THRIFT-4386 Add Lua 5.3/5.4 support
Clint: lua
Patch: Thomas Bruggink

This closes #3012
diff --git a/lib/lua/TBinaryProtocol.lua b/lib/lua/TBinaryProtocol.lua
index 4b8e98a..9eacc4a 100644
--- a/lib/lua/TBinaryProtocol.lua
+++ b/lib/lua/TBinaryProtocol.lua
@@ -18,8 +18,8 @@
 --
 
 require 'TProtocol'
-require 'libluabpack'
-require 'libluabitwise'
+local libluabpack = require 'libluabpack'
+local libluabitwise = require 'libluabitwise'
 
 TBinaryProtocol = __TObject.new(TProtocolBase, {
   __type = 'TBinaryProtocol',
@@ -111,6 +111,11 @@
   self.trans:write(buff)
 end
 
+function TBinaryProtocol:writeUI32(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)
@@ -127,6 +132,13 @@
   self.trans:write(str)
 end
 
+function TBinaryProtocol:writeUuid(uuid)
+  self:writeUI32(uuid.two)
+  self:writeUI32(uuid.three)
+  self:writeUI32(uuid.zero)
+  self:writeUI32(uuid.one)
+end
+
 function TBinaryProtocol:readMessageBegin()
   local sz, ttype, name, seqid = self:readI32()
   if sz < 0 then
@@ -226,6 +238,12 @@
   return val
 end
 
+function TBinaryProtocol:readUI32()
+  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)
@@ -244,6 +262,19 @@
   return str
 end
 
+function TBinaryProtocol:readUuid()
+  local a = self:readUI32()
+  local b = self:readUI32()
+  local c = self:readUI32()
+  local d = self:readUI32()
+  return TUUID:new {
+    zero = c,
+    one = d,
+    two = a,
+    three = b
+  }
+end
+
 TBinaryProtocolFactory = TProtocolFactory:new{
   __type = 'TBinaryProtocolFactory',
   strictRead = false
diff --git a/lib/lua/TCompactProtocol.lua b/lib/lua/TCompactProtocol.lua
index 8ec7b3a..f01c56f 100644
--- a/lib/lua/TCompactProtocol.lua
+++ b/lib/lua/TCompactProtocol.lua
@@ -18,9 +18,9 @@
 --
 
 require 'TProtocol'
-require 'libluabpack'
-require 'libluabitwise'
-require 'liblualongnumber'
+local libluabpack = require 'libluabpack'
+local libluabitwise = require 'libluabitwise'
+local liblualongnumber = require 'liblualongnumber'
 
 TCompactProtocol = __TObject.new(TProtocolBase, {
   __type = 'TCompactProtocol',
@@ -61,7 +61,8 @@
   COMPACT_LIST          = 0x09,
   COMPACT_SET           = 0x0A,
   COMPACT_MAP           = 0x0B,
-  COMPACT_STRUCT        = 0x0C
+  COMPACT_STRUCT        = 0x0C,
+  COMPACT_UUID          = 0x0D,
 }
 
 TTypeToCompactType = {}
@@ -77,21 +78,23 @@
 TTypeToCompactType[TType.SET]    = TCompactType.COMPACT_SET
 TTypeToCompactType[TType.MAP]    = TCompactType.COMPACT_MAP
 TTypeToCompactType[TType.STRUCT] = TCompactType.COMPACT_STRUCT
+TTypeToCompactType[TType.UUID]   = TCompactType.COMPACT_UUID
 
 CompactTypeToTType = {}
-CompactTypeToTType[TType.STOP]                        = TType.STOP
-CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_TRUE] = TType.BOOL
+CompactTypeToTType[TType.STOP]                         = TType.STOP
+CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_TRUE]  = TType.BOOL
 CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_FALSE] = TType.BOOL
-CompactTypeToTType[TCompactType.COMPACT_BYTE]         = TType.BYTE
-CompactTypeToTType[TCompactType.COMPACT_I16]          = TType.I16
-CompactTypeToTType[TCompactType.COMPACT_I32]          = TType.I32
-CompactTypeToTType[TCompactType.COMPACT_I64]          = TType.I64
-CompactTypeToTType[TCompactType.COMPACT_DOUBLE]       = TType.DOUBLE
-CompactTypeToTType[TCompactType.COMPACT_BINARY]       = TType.STRING
-CompactTypeToTType[TCompactType.COMPACT_LIST]         = TType.LIST
-CompactTypeToTType[TCompactType.COMPACT_SET]          = TType.SET
-CompactTypeToTType[TCompactType.COMPACT_MAP]          = TType.MAP
-CompactTypeToTType[TCompactType.COMPACT_STRUCT]       = TType.STRUCT
+CompactTypeToTType[TCompactType.COMPACT_BYTE]          = TType.BYTE
+CompactTypeToTType[TCompactType.COMPACT_I16]           = TType.I16
+CompactTypeToTType[TCompactType.COMPACT_I32]           = TType.I32
+CompactTypeToTType[TCompactType.COMPACT_I64]           = TType.I64
+CompactTypeToTType[TCompactType.COMPACT_DOUBLE]        = TType.DOUBLE
+CompactTypeToTType[TCompactType.COMPACT_BINARY]        = TType.STRING
+CompactTypeToTType[TCompactType.COMPACT_LIST]          = TType.LIST
+CompactTypeToTType[TCompactType.COMPACT_SET]           = TType.SET
+CompactTypeToTType[TCompactType.COMPACT_MAP]           = TType.MAP
+CompactTypeToTType[TCompactType.COMPACT_STRUCT]        = TType.STRUCT
+CompactTypeToTType[TCompactType.COMPACT_UUID]          = TType.UUID
 
 function TCompactProtocol:resetLastField()
   self.lastField = {}
@@ -197,6 +200,11 @@
   self:writeVarint32(libluabpack.i32ToZigzag(i32))
 end
 
+function TCompactProtocol:writeUI32(i32)
+  local buff = libluabpack.bpack('I', i32)
+  self.trans:write(buff)
+end
+
 function TCompactProtocol:writeI64(i64)
   self:writeVarint64(libluabpack.i64ToZigzag(i64))
 end
@@ -211,6 +219,13 @@
   self:writeBinary(str)
 end
 
+function TCompactProtocol:writeUuid(uuid)
+  self:writeUI32(uuid.two)
+  self:writeUI32(uuid.three)
+  self:writeUI32(uuid.zero)
+  self:writeUI32(uuid.one)
+end
+
 function TCompactProtocol:writeBinary(str)
   -- Should be utf-8
   self:writeVarint32(string.len(str))
@@ -385,6 +400,12 @@
   return value
 end
 
+function TCompactProtocol:readUI32()
+  local buff = self.trans:readAll(4)
+  local val = libluabpack.bunpack('I', buff)
+  return val
+end
+
 function TCompactProtocol:readI64()
   local value = self:readVarint64()
   return value
@@ -400,6 +421,19 @@
   return self:readBinary()
 end
 
+function TCompactProtocol:readUuid()
+  local a = self:readUI32()
+  local b = self:readUI32()
+  local c = self:readUI32()
+  local d = self:readUI32()
+  return TUUID:new {
+    zero = c,
+    one = d,
+    two = a,
+    three = b
+  }
+end
+
 function TCompactProtocol:readBinary()
   local size = self:readVarint32()
   if size <= 0 then
diff --git a/lib/lua/TFramedTransport.lua b/lib/lua/TFramedTransport.lua
index 768e2d9..4f41e83 100644
--- a/lib/lua/TFramedTransport.lua
+++ b/lib/lua/TFramedTransport.lua
@@ -18,7 +18,7 @@
 --
 
 require 'TTransport'
-require 'libluabpack'
+local libluabpack = require 'libluabpack'
 
 TFramedTransport = TTransportBase:new{
   __type = 'TFramedTransport',
diff --git a/lib/lua/THttpTransport.lua b/lib/lua/THttpTransport.lua
index 2951db7..e1318c3 100644
--- a/lib/lua/THttpTransport.lua
+++ b/lib/lua/THttpTransport.lua
@@ -160,12 +160,21 @@
   end
 end
 
+function THttpTransport:flushOneway()
+  self.wBuf = ''
+  self:writeHttpHeader(0)
+  self.trans:flush()
+end
+
 function THttpTransport:flush()
   -- If the write fails we still want wBuf to be clear
   local tmp = self.wBuf
   self.wBuf = ''
-  self:writeHttpHeader(string.len(tmp))
-  self.trans:write(tmp)
+  local dataLen = string.len(tmp)
+  self:writeHttpHeader(dataLen)
+  if dataLen > 0 then
+    self.trans:write(tmp)
+  end
   self.trans:flush()
 end
 
diff --git a/lib/lua/TJsonProtocol.lua b/lib/lua/TJsonProtocol.lua
index db08eec..cec8e26 100644
--- a/lib/lua/TJsonProtocol.lua
+++ b/lib/lua/TJsonProtocol.lua
@@ -18,8 +18,9 @@
 --
 
 require 'TProtocol'
-require 'libluabpack'
-require 'libluabitwise'
+local libluabpack = require 'libluabpack'
+local libluabitwise = require 'libluabitwise'
+local liblualongnumber = require 'liblualongnumber'
 
 TJSONProtocol = __TObject.new(TProtocolBase, {
   __type = 'TJSONProtocol',
@@ -42,6 +43,7 @@
 TTypeToString[TType.LIST]   = "lst"
 TTypeToString[TType.SET]    = "set"
 TTypeToString[TType.MAP]    = "map"
+TTypeToString[TType.UUID]   = "uid"
 
 StringToTType = {
   tf  = TType.BOOL,
@@ -54,7 +56,8 @@
   rec = TType.STRUCT,
   map = TType.MAP,
   set = TType.SET,
-  lst = TType.LIST
+  lst = TType.LIST,
+  uid = TType.UUID,
 }
 
 JSONNode = {
@@ -402,13 +405,17 @@
 end
 
 function TJSONProtocol:writeDouble(dub)
-  self:writeJSONDouble(string.format("%.16f", dub))
+  self:writeJSONDouble(string.format("%.20f", dub))
 end
 
 function TJSONProtocol:writeString(str)
   self:writeJSONString(str)
 end
 
+function TJSONProtocol:writeUuid(uuid)
+  self:writeJSONString(uuid:getString())
+end
+
 function TJSONProtocol:writeBinary(str)
   -- Should be utf-8
   self:writeJSONBase64(str)
@@ -706,6 +713,10 @@
   return self:readJSONString()
 end
 
+function TJSONProtocol:readUuid()
+  return TUUIDfromString(self:readJSONString())
+end
+
 function TJSONProtocol:readBinary()
   return self:readJSONBase64()
 end
diff --git a/lib/lua/TProtocol.lua b/lib/lua/TProtocol.lua
index 1306fb3..f7a993f 100644
--- a/lib/lua/TProtocol.lua
+++ b/lib/lua/TProtocol.lua
@@ -86,6 +86,7 @@
 function TProtocolBase:writeI64(i64) end
 function TProtocolBase:writeDouble(dub) end
 function TProtocolBase:writeString(str) end
+function TProtocolBase:writeUuid(uuid) end
 function TProtocolBase:readMessageBegin() end
 function TProtocolBase:readMessageEnd() end
 function TProtocolBase:readStructBegin() end
@@ -105,6 +106,7 @@
 function TProtocolBase:readI64() end
 function TProtocolBase:readDouble() end
 function TProtocolBase:readString() end
+function TProtocolBase:readUuid() end
 
 function TProtocolBase:skip(ttype)
   if ttype == TType.BOOL then
@@ -151,6 +153,8 @@
       self:skip(ettype)
     end
     self:readListEnd()
+  elseif ttype == TType.UUID then
+    self:readUuid()
   else
     terror(TProtocolException:new{
       message = 'Invalid data'
diff --git a/lib/lua/TSocket.lua b/lib/lua/TSocket.lua
index d71fc1f..47ca6f0 100644
--- a/lib/lua/TSocket.lua
+++ b/lib/lua/TSocket.lua
@@ -17,7 +17,7 @@
 --
 
 require 'TTransport'
-require 'libluasocket'
+local luasocket = require 'libluasocket'
 
 -- TSocketBase
 TSocketBase = TTransportBase:new{
diff --git a/lib/lua/TTransport.lua b/lib/lua/TTransport.lua
index 01c7e59..b7f6b83 100644
--- a/lib/lua/TTransport.lua
+++ b/lib/lua/TTransport.lua
@@ -76,6 +76,8 @@
   return buf
 end
 function TTransportBase:write(buf) end
+-- flushOneway is a NOOP for most transport types.
+function TTransportBase:flushOneway() end
 function TTransportBase:flush() end
 
 TServerTransportBase = __TObject:new{
diff --git a/lib/lua/Thrift.lua b/lib/lua/Thrift.lua
index 58daa22..b6e3628 100644
--- a/lib/lua/Thrift.lua
+++ b/lib/lua/Thrift.lua
@@ -23,6 +23,9 @@
 --setfenv(1, thrift)
 
 package.cpath = package.cpath .. ';bin/?.so' -- TODO FIX
+
+local libluabitwise = require 'libluabitwise'
+
 function ttype(obj)
   if type(obj) == 'table' and
     obj.__type and
@@ -66,8 +69,7 @@
   MAP    = 13,
   SET    = 14,
   LIST   = 15,
-  UTF8   = 16,
-  UTF16  = 17
+  UUID   = 16
 }
 
 TMessageType = {
@@ -233,6 +235,35 @@
   oprot:writeStructEnd()
 end
 
+TUUID = {
+  zero,
+  one,
+  two,
+  three
+}
+
+TUUID = __TObject:new{
+  __type = 'TUUID'
+}
+
+function TUUIDfromString(str)
+  local iterator = string.gmatch(str, "[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]")
+  return TUUID:new {
+    zero = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16)),
+    one = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16)),
+    two = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16)),
+    three = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16))
+  }
+end
+
+function TUUID:getString()
+  return string.format("%08x-%04x-%04x-%04x-%04x%08x", self.zero, libluabitwise.ushiftr(self.one, 16), libluabitwise.buand(self.one, 0xFFFF), libluabitwise.ushiftr(self.two, 16), libluabitwise.buand(self.two, 0xFFFF), self.three)
+end
+
+function TUUID:__tostring()
+  return "<TUUID: " .. self:getString() .. ">"
+end
+
 -- Basic Client (used in generated lua code)
 __TClient = __TObject:new{
   __type = '__TClient',
diff --git a/lib/lua/src/luabitwise.c b/lib/lua/src/luabitwise.c
index 2e07e17..ea9110a 100644
--- a/lib/lua/src/luabitwise.c
+++ b/lib/lua/src/luabitwise.c
@@ -27,6 +27,13 @@
   return 1;
 }
 
+static int l_unot(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  a = ~a;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_xor(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -35,6 +42,15 @@
   return 1;
 }
 
+static int l_uxor(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a ^= b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
+
 static int l_and(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -43,6 +59,14 @@
   return 1;
 }
 
+static int l_uand(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a &= b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_or(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -51,6 +75,14 @@
   return 1;
 }
 
+static int l_uor(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a |= b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_shiftr(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -59,6 +91,14 @@
   return 1;
 }
 
+static int l_ushiftr(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a = a >> b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_shiftl(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -67,17 +107,36 @@
   return 1;
 }
 
+static int l_ushiftl(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a = a << b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static const struct luaL_Reg funcs[] = {
   {"band", l_and},
+  {"buand", l_uand},
   {"bor", l_or},
+  {"buor", l_uor},
   {"bxor", l_xor},
+  {"buxor", l_uxor},
   {"bnot", l_not},
+  {"bunot", l_unot},
   {"shiftl", l_shiftl},
+  {"ushiftl", l_ushiftl},
   {"shiftr", l_shiftr},
+  {"ushiftr", l_ushiftr},
   {NULL, NULL}
 };
 
 int luaopen_libluabitwise(lua_State *L) {
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, funcs, 0);
+#else
   luaL_register(L, "libluabitwise", funcs);
+#endif
   return 1;
 }
diff --git a/lib/lua/src/luabpack.c b/lib/lua/src/luabpack.c
index 077b6aa..cdfb72a 100644
--- a/lib/lua/src/luabpack.c
+++ b/lib/lua/src/luabpack.c
@@ -45,6 +45,7 @@
  *  c - Signed Byte
  *  s - Signed Short
  *  i - Signed Int
+ *  I - Unsigned Int
  *  l - Signed Long
  *  d - Double
  */
@@ -72,6 +73,12 @@
       luaL_addlstring(&buf, (void*)&data, sizeof(data));
       break;
     }
+    case 'I': {
+      uint32_t data = luaL_checkinteger(L, 2);
+      data = (uint32_t)htonl(data);
+      luaL_addlstring(&buf, (void*)&data, sizeof(data));
+      break;
+    }
     case 'l': {
       int64_t data = lualongnumber_checklong(L, 2);
       data = (int64_t)T_htonll(data);
@@ -97,6 +104,7 @@
  *  C - Unsigned Byte
  *  s - Signed Short
  *  i - Signed Int
+ *  I - Unsigned Int
  *  l - Signed Long
  *  d - Double
  */
@@ -144,6 +152,17 @@
       lua_pushnumber(L, val);
       break;
     }
+    /**
+     * unpack unsigned Int.
+     */
+    case 'I': {
+      uint32_t val;
+      luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+      memcpy(&val, data, sizeof(val));
+      val = (uint32_t)ntohl(val);
+      lua_pushnumber(L, val);
+      break;
+    }
     case 'l': {
       int64_t val;
       luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
@@ -303,6 +322,11 @@
 };
 
 int luaopen_libluabpack(lua_State *L) {
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, lua_bpack, 0);
+#else
   luaL_register(L, "libluabpack", lua_bpack);
+#endif
   return 1;
 }
diff --git a/lib/lua/src/lualongnumber.c b/lib/lua/src/lualongnumber.c
index 9001e4a..91df70a 100644
--- a/lib/lua/src/lualongnumber.c
+++ b/lib/lua/src/lualongnumber.c
@@ -223,6 +223,11 @@
   lua_pop(L, 1);
   set_methods(L, LONG_NUM_TYPE, methods);
 
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, funcs, 0);
+#else
   luaL_register(L, "liblualongnumber", funcs);
+#endif
   return 1;
 }
diff --git a/lib/lua/src/luasocket.c b/lib/lua/src/luasocket.c
index 6f63d3d..07524ab 100644
--- a/lib/lua/src/luasocket.c
+++ b/lib/lua/src/luasocket.c
@@ -185,8 +185,12 @@
   set_methods(L, SOCKET_GENERIC, methods_generic);
   set_methods(L, SOCKET_CLIENT, methods_client);
   set_methods(L, SOCKET_SERVER, methods_server);
-
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, funcs_luasocket, 0);
+#else
   luaL_register(L, "luasocket", funcs_luasocket);
+#endif
   return 1;
 }
 
diff --git a/lib/lua/src/usocket.c b/lib/lua/src/usocket.c
index 21c0bac..27103a0 100644
--- a/lib/lua/src/usocket.c
+++ b/lib/lua/src/usocket.c
@@ -205,21 +205,35 @@
   return socket_wait(sock, WAIT_MODE_C, timeout);
 }
 
+#define SEND_RETRY_COUNT 5
 T_ERRCODE socket_send(
   p_socket sock, const char *data, size_t len, int timeout) {
   int err, put = 0;
   if (*sock < 0) {
     return CLOSED;
   }
-  do {
-    put = send(*sock, data, len, 0);
-    if (put > 0) {
-      return SUCCESS;
-    }
-  } while ((err = errno) == EINTR);
+  for(int i = 0; i < SEND_RETRY_COUNT; i++) {
+    do {
+      size_t l = len - put;
+      put = send(*sock, data + put, l, 0);
+      if (put > 0) {
+        if(put == l) {
+          return SUCCESS;
+        }
+        // Not all data was delivered, we need to try again.
+        err = EAGAIN;
+        break;
+      }
+    } while ((err = errno) == EINTR);
 
-  if (err == EAGAIN) {
-    return socket_wait(sock, WAIT_MODE_W, timeout);
+    if (err == EAGAIN) {
+      err = socket_wait(sock, WAIT_MODE_W, timeout);
+      // Check if the socket is available again and try to resend.
+      if(err == SUCCESS) {
+        continue;
+      }
+    }
+    break;
   }
 
   return err;