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