1-- 2-- Licensed to the Apache Software Foundation (ASF) under one 3-- or more contributor license agreements. See the NOTICE file 4-- distributed with this work for additional information 5-- regarding copyright ownership. The ASF licenses this file 6-- to you under the Apache License, Version 2.0 (the 7-- "License"); you may not use this file except in compliance 8-- with the License. You may obtain a copy of the License at 9-- 10-- http://www.apache.org/licenses/LICENSE-2.0 11-- 12-- Unless required by applicable law or agreed to in writing, 13-- software distributed under the License is distributed on an 14-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15-- KIND, either express or implied. See the License for the 16-- specific language governing permissions and limitations 17-- under the License. 18-- 19 20require 'TProtocol' 21require 'libluabpack' 22require 'libluabitwise' 23require 'liblualongnumber' 24 25TCompactProtocol = __TObject.new(TProtocolBase, { 26 __type = 'TCompactProtocol', 27 COMPACT_PROTOCOL_ID = 0x82, 28 COMPACT_VERSION = 1, 29 COMPACT_VERSION_MASK = 0x1f, 30 COMPACT_TYPE_MASK = 0xE0, 31 COMPACT_TYPE_BITS = 0x07, 32 COMPACT_TYPE_SHIFT_AMOUNT = 5, 33 34 -- Used to keep track of the last field for the current and previous structs, 35 -- so we can do the delta stuff. 36 lastField = {}, 37 lastFieldId = 0, 38 lastFieldIndex = 1, 39 40 -- If we encounter a boolean field begin, save the TField here so it can 41 -- have the value incorporated. 42 booleanFieldName = "", 43 booleanFieldId = 0, 44 booleanFieldPending = false, 45 46 -- If we read a field header, and it's a boolean field, save the boolean 47 -- value here so that readBool can use it. 48 boolValue = false, 49 boolValueIsNotNull = false, 50}) 51 52TCompactType = { 53 COMPACT_BOOLEAN_TRUE = 0x01, 54 COMPACT_BOOLEAN_FALSE = 0x02, 55 COMPACT_BYTE = 0x03, 56 COMPACT_I16 = 0x04, 57 COMPACT_I32 = 0x05, 58 COMPACT_I64 = 0x06, 59 COMPACT_DOUBLE = 0x07, 60 COMPACT_BINARY = 0x08, 61 COMPACT_LIST = 0x09, 62 COMPACT_SET = 0x0A, 63 COMPACT_MAP = 0x0B, 64 COMPACT_STRUCT = 0x0C 65} 66 67TTypeToCompactType = {} 68TTypeToCompactType[TType.STOP] = TType.STOP 69TTypeToCompactType[TType.BOOL] = TCompactType.COMPACT_BOOLEAN_TRUE 70TTypeToCompactType[TType.BYTE] = TCompactType.COMPACT_BYTE 71TTypeToCompactType[TType.I16] = TCompactType.COMPACT_I16 72TTypeToCompactType[TType.I32] = TCompactType.COMPACT_I32 73TTypeToCompactType[TType.I64] = TCompactType.COMPACT_I64 74TTypeToCompactType[TType.DOUBLE] = TCompactType.COMPACT_DOUBLE 75TTypeToCompactType[TType.STRING] = TCompactType.COMPACT_BINARY 76TTypeToCompactType[TType.LIST] = TCompactType.COMPACT_LIST 77TTypeToCompactType[TType.SET] = TCompactType.COMPACT_SET 78TTypeToCompactType[TType.MAP] = TCompactType.COMPACT_MAP 79TTypeToCompactType[TType.STRUCT] = TCompactType.COMPACT_STRUCT 80 81CompactTypeToTType = {} 82CompactTypeToTType[TType.STOP] = TType.STOP 83CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_TRUE] = TType.BOOL 84CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_FALSE] = TType.BOOL 85CompactTypeToTType[TCompactType.COMPACT_BYTE] = TType.BYTE 86CompactTypeToTType[TCompactType.COMPACT_I16] = TType.I16 87CompactTypeToTType[TCompactType.COMPACT_I32] = TType.I32 88CompactTypeToTType[TCompactType.COMPACT_I64] = TType.I64 89CompactTypeToTType[TCompactType.COMPACT_DOUBLE] = TType.DOUBLE 90CompactTypeToTType[TCompactType.COMPACT_BINARY] = TType.STRING 91CompactTypeToTType[TCompactType.COMPACT_LIST] = TType.LIST 92CompactTypeToTType[TCompactType.COMPACT_SET] = TType.SET 93CompactTypeToTType[TCompactType.COMPACT_MAP] = TType.MAP 94CompactTypeToTType[TCompactType.COMPACT_STRUCT] = TType.STRUCT 95 96function TCompactProtocol:resetLastField() 97 self.lastField = {} 98 self.lastFieldId = 0 99 self.lastFieldIndex = 1 100end 101 102function TCompactProtocol:packCompactType(ktype, vtype) 103 return libluabitwise.bor(libluabitwise.shiftl(ktype, 4), vtype) 104end 105 106function TCompactProtocol:writeMessageBegin(name, ttype, seqid) 107 self:writeByte(TCompactProtocol.COMPACT_PROTOCOL_ID) 108 self:writeByte(libluabpack.packMesgType(TCompactProtocol.COMPACT_VERSION, 109 TCompactProtocol.COMPACT_VERSION_MASK,ttype, 110 TCompactProtocol.COMPACT_TYPE_SHIFT_AMOUNT, 111 TCompactProtocol.COMPACT_TYPE_MASK)) 112 self:writeVarint32(seqid) 113 self:writeString(name) 114 self:resetLastField() 115end 116 117function TCompactProtocol:writeMessageEnd() 118end 119 120function TCompactProtocol:writeStructBegin(name) 121 self.lastField[self.lastFieldIndex] = self.lastFieldId 122 self.lastFieldIndex = self.lastFieldIndex + 1 123 self.lastFieldId = 0 124end 125 126function TCompactProtocol:writeStructEnd() 127 self.lastFieldIndex = self.lastFieldIndex - 1 128 self.lastFieldId = self.lastField[self.lastFieldIndex] 129end 130 131function TCompactProtocol:writeFieldBegin(name, ttype, id) 132 if ttype == TType.BOOL then 133 self.booleanFieldName = name 134 self.booleanFieldId = id 135 self.booleanFieldPending = true 136 else 137 self:writeFieldBeginInternal(name, ttype, id, -1) 138 end 139end 140 141function TCompactProtocol:writeFieldEnd() 142end 143 144function TCompactProtocol:writeFieldStop() 145 self:writeByte(TType.STOP); 146end 147 148function TCompactProtocol:writeMapBegin(ktype, vtype, size) 149 if size == 0 then 150 self:writeByte(0) 151 else 152 self:writeVarint32(size) 153 self:writeByte(self:packCompactType(TTypeToCompactType[ktype], TTypeToCompactType[vtype])) 154 end 155end 156 157function TCompactProtocol:writeMapEnd() 158end 159 160function TCompactProtocol:writeListBegin(etype, size) 161 self:writeCollectionBegin(etype, size) 162end 163 164function TCompactProtocol:writeListEnd() 165end 166 167function TCompactProtocol:writeSetBegin(etype, size) 168 self:writeCollectionBegin(etype, size) 169end 170 171function TCompactProtocol:writeSetEnd() 172end 173 174function TCompactProtocol:writeBool(bool) 175 local value = TCompactType.COMPACT_BOOLEAN_FALSE 176 if bool then 177 value = TCompactType.COMPACT_BOOLEAN_TRUE 178 end 179 if self.booleanFieldPending then 180 self:writeFieldBeginInternal(self.booleanFieldName, TType.BOOL, self.booleanFieldId, value) 181 self.booleanFieldPending = false 182 else 183 self:writeByte(value) 184 end 185end 186 187function TCompactProtocol:writeByte(byte) 188 local buff = libluabpack.bpack('c', byte) 189 self.trans:write(buff) 190end 191 192function TCompactProtocol:writeI16(i16) 193 self:writeVarint32(libluabpack.i32ToZigzag(i16)) 194end 195 196function TCompactProtocol:writeI32(i32) 197 self:writeVarint32(libluabpack.i32ToZigzag(i32)) 198end 199 200function TCompactProtocol:writeI64(i64) 201 self:writeVarint64(libluabpack.i64ToZigzag(i64)) 202end 203 204function TCompactProtocol:writeDouble(dub) 205 local buff = libluabpack.bpack('d', dub) 206 self.trans:write(buff) 207end 208 209function TCompactProtocol:writeString(str) 210 -- Should be utf-8 211 self:writeBinary(str) 212end 213 214function TCompactProtocol:writeBinary(str) 215 -- Should be utf-8 216 self:writeVarint32(string.len(str)) 217 self.trans:write(str) 218end 219 220function TCompactProtocol:writeFieldBeginInternal(name, ttype, id, typeOverride) 221 if typeOverride == -1 then 222 typeOverride = TTypeToCompactType[ttype] 223 end 224 local offset = id - self.lastFieldId 225 if id > self.lastFieldId and offset <= 15 then 226 self:writeByte(libluabitwise.bor(libluabitwise.shiftl(offset, 4), typeOverride)) 227 else 228 self:writeByte(typeOverride) 229 self:writeI16(id) 230 end 231 self.lastFieldId = id 232end 233 234function TCompactProtocol:writeCollectionBegin(etype, size) 235 if size <= 14 then 236 self:writeByte(libluabitwise.bor(libluabitwise.shiftl(size, 4), TTypeToCompactType[etype])) 237 else 238 self:writeByte(libluabitwise.bor(0xf0, TTypeToCompactType[etype])) 239 self:writeVarint32(size) 240 end 241end 242 243function TCompactProtocol:writeVarint32(i32) 244 -- Should be utf-8 245 local str = libluabpack.toVarint32(i32) 246 self.trans:write(str) 247end 248 249function TCompactProtocol:writeVarint64(i64) 250 -- Should be utf-8 251 local str = libluabpack.toVarint64(i64) 252 self.trans:write(str) 253end 254 255function TCompactProtocol:readMessageBegin() 256 local protocolId = self:readSignByte() 257 if protocolId ~= self.COMPACT_PROTOCOL_ID then 258 terror(TProtocolException:new{ 259 message = "Expected protocol id " .. self.COMPACT_PROTOCOL_ID .. " but got " .. protocolId}) 260 end 261 local versionAndType = self:readSignByte() 262 local version = libluabitwise.band(versionAndType, self.COMPACT_VERSION_MASK) 263 local ttype = libluabitwise.band(libluabitwise.shiftr(versionAndType, 264 self.COMPACT_TYPE_SHIFT_AMOUNT), self.COMPACT_TYPE_BITS) 265 if version ~= self.COMPACT_VERSION then 266 terror(TProtocolException:new{ 267 message = "Expected version " .. self.COMPACT_VERSION .. " but got " .. version}) 268 end 269 local seqid = self:readVarint32() 270 local name = self:readString() 271 return name, ttype, seqid 272end 273 274function TCompactProtocol:readMessageEnd() 275end 276 277function TCompactProtocol:readStructBegin() 278 self.lastField[self.lastFieldIndex] = self.lastFieldId 279 self.lastFieldIndex = self.lastFieldIndex + 1 280 self.lastFieldId = 0 281 return nil 282end 283 284function TCompactProtocol:readStructEnd() 285 self.lastFieldIndex = self.lastFieldIndex - 1 286 self.lastFieldId = self.lastField[self.lastFieldIndex] 287end 288 289function TCompactProtocol:readFieldBegin() 290 local field_and_ttype = self:readSignByte() 291 local ttype = self:getTType(field_and_ttype) 292 if ttype == TType.STOP then 293 return nil, ttype, 0 294 end 295 local modifier = libluabitwise.shiftr(field_and_ttype, 4) 296 local id = 0 297 if modifier == 0 then 298 id = self:readI16() 299 else 300 id = self.lastFieldId + modifier 301 end 302 local type = libluabitwise.band(field_and_ttype, 0x0f) 303 if type == TCompactType.COMPACT_BOOLEAN_TRUE then 304 self.boolValue = true 305 self.boolValueIsNotNull = true 306 elseif type == TCompactType.COMPACT_BOOLEAN_FALSE then 307 self.boolValue = false 308 self.boolValueIsNotNull = true 309 end 310 self.lastFieldId = id 311 return nil, ttype, id 312end 313 314function TCompactProtocol:readFieldEnd() 315end 316 317function TCompactProtocol:readMapBegin() 318 local size = self:readVarint32() 319 local kvtype = 0 320 if size > 0 then 321 kvtype = self:readSignByte() 322 end 323 local ktype = self:getTType(libluabitwise.shiftr(kvtype, 4)) 324 local vtype = self:getTType(kvtype) 325 return ktype, vtype, size 326end 327 328function TCompactProtocol:readMapEnd() 329end 330 331function TCompactProtocol:readListBegin() 332 local size_and_type = self:readSignByte() 333 local size = libluabitwise.band(libluabitwise.shiftr(size_and_type, 4), 0x0f) 334 if size == 15 then 335 size = self:readVarint32() 336 end 337 if size < 0 then 338 return nil,nil 339 end 340 local etype = self:getTType(libluabitwise.band(size_and_type, 0x0f)) 341 return etype, size 342end 343 344function TCompactProtocol:readListEnd() 345end 346 347function TCompactProtocol:readSetBegin() 348 return self:readListBegin() 349end 350 351function TCompactProtocol:readSetEnd() 352end 353 354function TCompactProtocol:readBool() 355 if self.boolValueIsNotNull then 356 self.boolValueIsNotNull = false 357 return self.boolValue 358 end 359 local val = self:readSignByte() 360 if val == TCompactType.COMPACT_BOOLEAN_TRUE then 361 return true 362 end 363 return false 364end 365 366function TCompactProtocol:readByte() 367 local buff = self.trans:readAll(1) 368 local val = libluabpack.bunpack('c', buff) 369 return val 370end 371 372function TCompactProtocol:readSignByte() 373 local buff = self.trans:readAll(1) 374 local val = libluabpack.bunpack('C', buff) 375 return val 376end 377 378function TCompactProtocol:readI16() 379 return self:readI32() 380end 381 382function TCompactProtocol:readI32() 383 local v = self:readVarint32() 384 local value = libluabpack.zigzagToI32(v) 385 return value 386end 387 388function TCompactProtocol:readI64() 389 local value = self:readVarint64() 390 return value 391end 392 393function TCompactProtocol:readDouble() 394 local buff = self.trans:readAll(8) 395 local val = libluabpack.bunpack('d', buff) 396 return val 397end 398 399function TCompactProtocol:readString() 400 return self:readBinary() 401end 402 403function TCompactProtocol:readBinary() 404 local size = self:readVarint32() 405 if size <= 0 then 406 return "" 407 end 408 return self.trans:readAll(size) 409end 410 411function TCompactProtocol:readVarint32() 412 local shiftl = 0 413 local result = 0 414 while true do 415 b = self:readByte() 416 result = libluabitwise.bor(result, 417 libluabitwise.shiftl(libluabitwise.band(b, 0x7f), shiftl)) 418 if libluabitwise.band(b, 0x80) ~= 0x80 then 419 break 420 end 421 shiftl = shiftl + 7 422 end 423 return result 424end 425 426function TCompactProtocol:readVarint64() 427 local result = liblualongnumber.new 428 local data = result(0) 429 local shiftl = 0 430 while true do 431 b = self:readSignByte() 432 endFlag, data = libluabpack.fromVarint64(b, shiftl, data) 433 shiftl = shiftl + 7 434 if endFlag == 0 then 435 break 436 end 437 end 438 return data 439end 440 441function TCompactProtocol:getTType(ctype) 442 return CompactTypeToTType[libluabitwise.band(ctype, 0x0f)] 443end 444 445TCompactProtocolFactory = TProtocolFactory:new{ 446 __type = 'TCompactProtocolFactory', 447} 448 449function TCompactProtocolFactory:getProtocol(trans) 450 -- TODO Enforce that this must be a transport class (ie not a bool) 451 if not trans then 452 terror(TProtocolException:new{ 453 message = 'Must supply a transport to ' .. ttype(self) 454 }) 455 end 456 return TCompactProtocol:new{ 457 trans = trans 458 } 459end 460