THRIFT-1681: Add Lua Support Patch: Dave Watson

Github Pull Request: This closes #92
diff --git a/lib/lua/TBinaryProtocol.lua b/lib/lua/TBinaryProtocol.lua
new file mode 100644
index 0000000..df13d61
--- /dev/null
+++ b/lib/lua/TBinaryProtocol.lua
@@ -0,0 +1,264 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+require 'TProtocol'
+require 'libluabpack'
+require 'libluabitwise'
+
+TBinaryProtocol = __TObject.new(TProtocolBase, {
+  __type = 'TBinaryProtocol',
+  VERSION_MASK = -65536, -- 0xffff0000
+  VERSION_1    = -2147418112, -- 0x80010000
+  TYPE_MASK    = 0x000000ff,
+  strictRead   = false,
+  strictWrite  = true
+})
+
+function TBinaryProtocol:writeMessageBegin(name, ttype, seqid)
+  if self.stirctWrite then
+    self:writeI32(libluabitwise.bor(TBinaryProtocol.VERSION_1, ttype))
+    self:writeString(name)
+    self:writeI32(seqid)
+  else
+    self:writeString(name)
+    self:writeByte(ttype)
+    self:writeI32(seqid)
+  end
+end
+
+function TBinaryProtocol:writeMessageEnd()
+end
+
+function TBinaryProtocol:writeStructBegin(name)
+end
+
+function TBinaryProtocol:writeStructEnd()
+end
+
+function TBinaryProtocol:writeFieldBegin(name, ttype, id)
+  self:writeByte(ttype)
+  self:writeI16(id)
+end
+
+function TBinaryProtocol:writeFieldEnd()
+end
+
+function TBinaryProtocol:writeFieldStop()
+  self:writeByte(TType.STOP);
+end
+
+function TBinaryProtocol:writeMapBegin(ktype, vtype, size)
+  self:writeByte(ktype)
+  self:writeByte(vtype)
+  self:writeI32(size)
+end
+
+function TBinaryProtocol:writeMapEnd()
+end
+
+function TBinaryProtocol:writeListBegin(etype, size)
+  self:writeByte(etype)
+  self:writeI32(size)
+end
+
+function TBinaryProtocol:writeListEnd()
+end
+
+function TBinaryProtocol:writeSetBegin(etype, size)
+  self:writeByte(etype)
+  self:writeI32(size)
+end
+
+function TBinaryProtocol:writeSetEnd()
+end
+
+function TBinaryProtocol:writeBool(bool)
+  if bool then
+    self:writeByte(1)
+  else
+    self:writeByte(0)
+  end
+end
+
+function TBinaryProtocol:writeByte(byte)
+  local buff = libluabpack.bpack('c', byte)
+  self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeI16(i16)
+  local buff = libluabpack.bpack('s', i16)
+  self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeI32(i32)
+  local buff = libluabpack.bpack('i', i32)
+  self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeI64(i64)
+  local buff = libluabpack.bpack('l', i64)
+  self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeDouble(dub)
+  local buff = libluabpack.bpack('d', dub)
+  self.trans:write(buff)
+end
+
+function TBinaryProtocol:writeString(str)
+  -- Should be utf-8
+  self:writeI32(string.len(str))
+  self.trans:write(str)
+end
+
+function TBinaryProtocol:readMessageBegin()
+  local sz, ttype, name, seqid = self:readI32()
+  if sz < 0 then
+    local version = libluabitwise.band(sz, TBinaryProtocol.VERSION_MASK)
+    if version ~= TBinaryProtocol.VERSION_1 then
+      terror(TProtocolException:new{
+        message = 'Bad version in readMessageBegin: ' .. sz
+      })
+    end
+    ttype = libluabitwise.band(sz, TBinaryProtocol.TYPE_MASK)
+    name = self:readString()
+    seqid = self:readI32()
+  else
+    if self.strictRead then
+      terror(TProtocolException:new{message = 'No protocol version header'})
+    end
+    name = self.trans:readAll(sz)
+    ttype = self:readByte()
+    seqid = self:readI32()
+  end
+  return name, ttype, seqid
+end
+
+function TBinaryProtocol:readMessageEnd()
+end
+
+function TBinaryProtocol:readStructBegin()
+  return nil
+end
+
+function TBinaryProtocol:readStructEnd()
+end
+
+function TBinaryProtocol:readFieldBegin()
+  local ttype = self:readByte()
+  if ttype == TType.STOP then
+    return nil, ttype, 0
+  end
+  local id = self:readI16()
+  return nil, ttype, id
+end
+
+function TBinaryProtocol:readFieldEnd()
+end
+
+function TBinaryProtocol:readMapBegin()
+  local ktype = self:readByte()
+  local vtype = self:readByte()
+  local size = self:readI32()
+  return ktype, vtype, size
+end
+
+function TBinaryProtocol:readMapEnd()
+end
+
+function TBinaryProtocol:readListBegin()
+  local etype = self:readByte()
+  local size = self:readI32()
+  return etype, size
+end
+
+function TBinaryProtocol:readListEnd()
+end
+
+function TBinaryProtocol:readSetBegin()
+  local etype = self:readByte()
+  local size = self:readI32()
+  return etype, size
+end
+
+function TBinaryProtocol:readSetEnd()
+end
+
+function TBinaryProtocol:readBool()
+  local byte = self:readByte()
+  if byte == 0 then
+    return false
+  end
+  return true
+end
+
+function TBinaryProtocol:readByte()
+  local buff = self.trans:readAll(1)
+  local val = libluabpack.bunpack('c', buff)
+  return val
+end
+
+function TBinaryProtocol:readI16()
+  local buff = self.trans:readAll(2)
+  local val = libluabpack.bunpack('s', buff)
+  return val
+end
+
+function TBinaryProtocol:readI32()
+  local buff = self.trans:readAll(4)
+  local val = libluabpack.bunpack('i', buff)
+  return val
+end
+
+function TBinaryProtocol:readI64()
+  local buff = self.trans:readAll(8)
+  local val = libluabpack.bunpack('l', buff)
+  return val
+end
+
+function TBinaryProtocol:readDouble()
+  local buff = self.trans:readAll(8)
+  local val = libluabpack.bunpack('d', buff)
+  return val
+end
+
+function TBinaryProtocol:readString()
+  local len = self:readI32()
+  local str = self.trans:readAll(len)
+  return str
+end
+
+TBinaryProtocolFactory = TProtocolFactory:new{
+  __type = 'TBinaryProtocolFactory',
+  strictRead = false
+}
+
+function TBinaryProtocolFactory:getProtocol(trans)
+  -- TODO Enforce that this must be a transport class (ie not a bool)
+  if not trans then
+    terror(TProtocolException:new{
+      message = 'Must supply a transport to ' .. ttype(self)
+    })
+  end
+  return TBinaryProtocol:new{
+    trans = trans,
+    strictRead = self.strictRead,
+    strictWrite = true
+  }
+end