|  | -- | 
|  | -- 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' | 
|  | require 'liblualongnumber' | 
|  |  | 
|  | TCompactProtocol = __TObject.new(TProtocolBase, { | 
|  | __type = 'TCompactProtocol', | 
|  | COMPACT_PROTOCOL_ID       = 0x82, | 
|  | COMPACT_VERSION           = 1, | 
|  | COMPACT_VERSION_MASK      = 0x1f, | 
|  | COMPACT_TYPE_MASK         = 0xE0, | 
|  | COMPACT_TYPE_BITS         = 0x07, | 
|  | COMPACT_TYPE_SHIFT_AMOUNT = 5, | 
|  |  | 
|  | -- Used to keep track of the last field for the current and previous structs, | 
|  | -- so we can do the delta stuff. | 
|  | lastField = {}, | 
|  | lastFieldId = 0, | 
|  | lastFieldIndex = 1, | 
|  |  | 
|  | -- If we encounter a boolean field begin, save the TField here so it can | 
|  | -- have the value incorporated. | 
|  | booleanFieldName    = "", | 
|  | booleanFieldId      = 0, | 
|  | booleanFieldPending = false, | 
|  |  | 
|  | -- If we read a field header, and it's a boolean field, save the boolean | 
|  | -- value here so that readBool can use it. | 
|  | boolValue          = false, | 
|  | boolValueIsNotNull = false, | 
|  | }) | 
|  |  | 
|  | TCompactType = { | 
|  | COMPACT_BOOLEAN_TRUE  = 0x01, | 
|  | COMPACT_BOOLEAN_FALSE = 0x02, | 
|  | COMPACT_BYTE          = 0x03, | 
|  | COMPACT_I16           = 0x04, | 
|  | COMPACT_I32           = 0x05, | 
|  | COMPACT_I64           = 0x06, | 
|  | COMPACT_DOUBLE        = 0x07, | 
|  | COMPACT_BINARY        = 0x08, | 
|  | COMPACT_LIST          = 0x09, | 
|  | COMPACT_SET           = 0x0A, | 
|  | COMPACT_MAP           = 0x0B, | 
|  | COMPACT_STRUCT        = 0x0C | 
|  | } | 
|  |  | 
|  | TTypeToCompactType = {} | 
|  | TTypeToCompactType[TType.STOP]   = TType.STOP | 
|  | TTypeToCompactType[TType.BOOL]   = TCompactType.COMPACT_BOOLEAN_TRUE | 
|  | TTypeToCompactType[TType.BYTE]   = TCompactType.COMPACT_BYTE | 
|  | TTypeToCompactType[TType.I16]    = TCompactType.COMPACT_I16 | 
|  | TTypeToCompactType[TType.I32]    = TCompactType.COMPACT_I32 | 
|  | TTypeToCompactType[TType.I64]    = TCompactType.COMPACT_I64 | 
|  | TTypeToCompactType[TType.DOUBLE] = TCompactType.COMPACT_DOUBLE | 
|  | TTypeToCompactType[TType.STRING] = TCompactType.COMPACT_BINARY | 
|  | TTypeToCompactType[TType.LIST]   = TCompactType.COMPACT_LIST | 
|  | TTypeToCompactType[TType.SET]    = TCompactType.COMPACT_SET | 
|  | TTypeToCompactType[TType.MAP]    = TCompactType.COMPACT_MAP | 
|  | TTypeToCompactType[TType.STRUCT] = TCompactType.COMPACT_STRUCT | 
|  |  | 
|  | CompactTypeToTType = {} | 
|  | 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 | 
|  |  | 
|  | function TCompactProtocol:resetLastField() | 
|  | self.lastField = {} | 
|  | self.lastFieldId = 0 | 
|  | self.lastFieldIndex = 1 | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:packCompactType(ktype, vtype) | 
|  | return libluabitwise.bor(libluabitwise.shiftl(ktype, 4), vtype) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeMessageBegin(name, ttype, seqid) | 
|  | self:writeByte(TCompactProtocol.COMPACT_PROTOCOL_ID) | 
|  | self:writeByte(libluabpack.packMesgType(TCompactProtocol.COMPACT_VERSION, | 
|  | TCompactProtocol.COMPACT_VERSION_MASK,ttype, | 
|  | TCompactProtocol.COMPACT_TYPE_SHIFT_AMOUNT, | 
|  | TCompactProtocol.COMPACT_TYPE_MASK)) | 
|  | self:writeVarint32(seqid) | 
|  | self:writeString(name) | 
|  | self:resetLastField() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeMessageEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeStructBegin(name) | 
|  | self.lastFieldIndex = self.lastFieldIndex + 1 | 
|  | self.lastField[self.lastFieldIndex] = self.lastFieldId | 
|  | self.lastFieldId = 0 | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeStructEnd() | 
|  | self.lastFieldIndex = self.lastFieldIndex - 1 | 
|  | self.lastFieldId = self.lastField[self.lastFieldIndex] | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeFieldBegin(name, ttype, id) | 
|  | if ttype == TType.BOOL then | 
|  | self.booleanFieldName = name | 
|  | self.booleanFieldId   = id | 
|  | self.booleanFieldPending = true | 
|  | else | 
|  | self:writeFieldBeginInternal(name, ttype, id, -1) | 
|  | end | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeFieldEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeFieldStop() | 
|  | self:writeByte(TType.STOP); | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeMapBegin(ktype, vtype, size) | 
|  | if size == 0 then | 
|  | self:writeByte(0) | 
|  | else | 
|  | self:writeVarint32(size) | 
|  | self:writeByte(self:packCompactType(TTypeToCompactType[ktype], TTypeToCompactType[vtype])) | 
|  | end | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeMapEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeListBegin(etype, size) | 
|  | self:writeCollectionBegin(etype, size) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeListEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeSetBegin(etype, size) | 
|  | self:writeCollectionBegin(etype, size) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeSetEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeBool(bool) | 
|  | local value = TCompactType.COMPACT_BOOLEAN_FALSE | 
|  | if bool then | 
|  | value = TCompactType.COMPACT_BOOLEAN_TRUE | 
|  | end | 
|  | print(value,self.booleanFieldPending,self.booleanFieldId) | 
|  | if self.booleanFieldPending then | 
|  | self:writeFieldBeginInternal(self.booleanFieldName, TType.BOOL, self.booleanFieldId, value) | 
|  | self.booleanFieldPending = false | 
|  | else | 
|  | self:writeByte(value) | 
|  | end | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeByte(byte) | 
|  | local buff = libluabpack.bpack('c', byte) | 
|  | self.trans:write(buff) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeI16(i16) | 
|  | self:writeVarint32(libluabpack.i32ToZigzag(i16)) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeI32(i32) | 
|  | self:writeVarint32(libluabpack.i32ToZigzag(i32)) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeI64(i64) | 
|  | self:writeVarint64(libluabpack.i64ToZigzag(i64)) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeDouble(dub) | 
|  | local buff = libluabpack.bpack('d', dub) | 
|  | self.trans:write(buff) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeString(str) | 
|  | -- Should be utf-8 | 
|  | self:writeBinary(str) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeBinary(str) | 
|  | -- Should be utf-8 | 
|  | self:writeVarint32(string.len(str)) | 
|  | self.trans:write(str) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeFieldBeginInternal(name, ttype, id, typeOverride) | 
|  | if typeOverride == -1 then | 
|  | typeOverride = TTypeToCompactType[ttype] | 
|  | end | 
|  | local offset = id - self.lastFieldId | 
|  | if id > self.lastFieldId and offset <= 15 then | 
|  | self:writeByte(libluabitwise.bor(libluabitwise.shiftl(offset, 4), typeOverride)) | 
|  | else | 
|  | self:writeByte(typeOverride) | 
|  | self:writeI16(id) | 
|  | end | 
|  | self.lastFieldId = id | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeCollectionBegin(etype, size) | 
|  | if size <= 14 then | 
|  | self:writeByte(libluabitwise.bor(libluabitwise.shiftl(size, 4), TTypeToCompactType[etype])) | 
|  | else | 
|  | self:writeByte(libluabitwise.bor(0xf0, TTypeToCompactType[etype])) | 
|  | self:writeVarint32(size) | 
|  | end | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeVarint32(i32) | 
|  | -- Should be utf-8 | 
|  | local str = libluabpack.toVarint32(i32) | 
|  | self.trans:write(str) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:writeVarint64(i64) | 
|  | -- Should be utf-8 | 
|  | local str = libluabpack.toVarint64(i64) | 
|  | self.trans:write(str) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readMessageBegin() | 
|  | local protocolId = self:readSignByte() | 
|  | if protocolId ~= self.COMPACT_PROTOCOL_ID then | 
|  | terror(TProtocolException:new{ | 
|  | message = "Expected protocol id " .. self.COMPACT_PROTOCOL_ID .. " but got " .. protocolId}) | 
|  | end | 
|  | local versionAndType = self:readSignByte() | 
|  | local version = libluabitwise.band(versionAndType, self.COMPACT_VERSION_MASK) | 
|  | local ttype = libluabitwise.band(libluabitwise.shiftr(versionAndType, | 
|  | self.COMPACT_TYPE_SHIFT_AMOUNT), self.COMPACT_TYPE_BITS) | 
|  | if version ~= self.COMPACT_VERSION then | 
|  | terror(TProtocolException:new{ | 
|  | message = "Expected version " .. self.COMPACT_VERSION .. " but got " .. version}) | 
|  | end | 
|  | local seqid = self:readVarint32() | 
|  | local name = self:readString() | 
|  | return name, ttype, seqid | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readMessageEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readStructBegin() | 
|  | self.lastField[self.lastFieldIndex] = self.lastFieldId | 
|  | self.lastFieldIndex = self.lastFieldIndex + 1 | 
|  | self.lastFieldId = 0 | 
|  | return nil | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readStructEnd() | 
|  | self.lastFieldIndex = self.lastFieldIndex - 1 | 
|  | self.lastFieldId = self.lastField[self.lastFieldIndex] | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readFieldBegin() | 
|  | local field_and_ttype = self:readSignByte() | 
|  | local ttype = self:getTType(field_and_ttype) | 
|  | if ttype == TType.STOP then | 
|  | return nil, ttype, 0 | 
|  | end | 
|  | -- mask off the 4 MSB of the type header. it could contain a field id delta. | 
|  | local modifier = libluabitwise.shiftr(libluabitwise.band(field_and_ttype, 0xf0), 4) | 
|  | local id = 0 | 
|  | if modifier == 0 then | 
|  | id = self:readI16() | 
|  | else | 
|  | id = self.lastFieldId + modifier | 
|  | end | 
|  | if ttype == TType.BOOL then | 
|  | boolValue = libluabitwise.band(field_and_ttype, 0x0f) == TCompactType.COMPACT_BOOLEAN_TRUE | 
|  | boolValueIsNotNull = true | 
|  | end | 
|  | self.lastFieldId = id | 
|  | return nil, ttype, id | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readFieldEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readMapBegin() | 
|  | local size = self:readVarint32() | 
|  | if size < 0 then | 
|  | return nil,nil,nil | 
|  | end | 
|  | local kvtype = self:readSignByte() | 
|  | local ktype = self:getTType(libluabitwise.shiftr(kvtype, 4)) | 
|  | local vtype = self:getTType(kvtype) | 
|  | return ktype, vtype, size | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readMapEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readListBegin() | 
|  | local size_and_type = self:readSignByte() | 
|  | local size = libluabitwise.band(libluabitwise.shiftr(size_and_type, 4), 0x0f) | 
|  | if size == 15 then | 
|  | size = self:readVarint32() | 
|  | end | 
|  | if size < 0 then | 
|  | return nil,nil | 
|  | end | 
|  | local etype = self:getTType(libluabitwise.band(size_and_type, 0x0f)) | 
|  | return etype, size | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readListEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readSetBegin() | 
|  | return self:readListBegin() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readSetEnd() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readBool() | 
|  | if boolValueIsNotNull then | 
|  | boolValueIsNotNull = true | 
|  | return boolValue | 
|  | end | 
|  | local val = self:readSignByte() | 
|  | if val == TCompactType.COMPACT_BOOLEAN_TRUE then | 
|  | return true | 
|  | end | 
|  | return false | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readByte() | 
|  | local buff = self.trans:readAll(1) | 
|  | local val = libluabpack.bunpack('c', buff) | 
|  | return val | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readSignByte() | 
|  | local buff = self.trans:readAll(1) | 
|  | local val = libluabpack.bunpack('C', buff) | 
|  | return val | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readI16() | 
|  | return self:readI32() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readI32() | 
|  | local v = self:readVarint32() | 
|  | local value = libluabpack.zigzagToI32(v) | 
|  | return value | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readI64() | 
|  | local value = self:readVarint64() | 
|  | return value | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readDouble() | 
|  | local buff = self.trans:readAll(8) | 
|  | local val = libluabpack.bunpack('d', buff) | 
|  | return val | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readString() | 
|  | return self:readBinary() | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readBinary() | 
|  | local size = self:readVarint32() | 
|  | if size <= 0 then | 
|  | return "" | 
|  | end | 
|  | return self.trans:readAll(size) | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readVarint32() | 
|  | local shiftl = 0 | 
|  | local result = 0 | 
|  | while true do | 
|  | b = self:readByte() | 
|  | result = libluabitwise.bor(result, | 
|  | libluabitwise.shiftl(libluabitwise.band(b, 0x7f), shiftl)) | 
|  | if libluabitwise.band(b, 0x80) ~= 0x80 then | 
|  | break | 
|  | end | 
|  | shiftl = shiftl + 7 | 
|  | end | 
|  | return result | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:readVarint64() | 
|  | local result = liblualongnumber.new | 
|  | local data = result(0) | 
|  | local shiftl = 0 | 
|  | while true do | 
|  | b = self:readByte() | 
|  | endFlag, data = libluabpack.fromVarint64(b, shiftl, data) | 
|  | shiftl = shiftl + 7 | 
|  | if endFlag == 0 then | 
|  | break | 
|  | end | 
|  | end | 
|  | return data | 
|  | end | 
|  |  | 
|  | function TCompactProtocol:getTType(ctype) | 
|  | return CompactTypeToTType[libluabitwise.band(ctype, 0x0f)] | 
|  | end | 
|  |  | 
|  | TCompactProtocolFactory = TProtocolFactory:new{ | 
|  | __type = 'TCompactProtocolFactory', | 
|  | } | 
|  |  | 
|  | function TCompactProtocolFactory: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 TCompactProtocol:new{ | 
|  | trans = trans | 
|  | } | 
|  | end |