Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 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
 
20
module Thrift
21
  class CompactProtocol < BaseProtocol
22
 
23
    PROTOCOL_ID = [0x82].pack('c').unpack('c').first
24
    VERSION = 1
25
    VERSION_MASK = 0x1f
26
    TYPE_MASK = 0xE0
27
    TYPE_SHIFT_AMOUNT = 5
28
 
29
    TSTOP = ["", Types::STOP, 0]
30
 
31
    # 
32
    # All of the on-wire type codes.
33
    # 
34
    class CompactTypes
35
      BOOLEAN_TRUE   = 0x01
36
      BOOLEAN_FALSE  = 0x02
37
      BYTE           = 0x03
38
      I16            = 0x04
39
      I32            = 0x05
40
      I64            = 0x06
41
      DOUBLE         = 0x07
42
      BINARY         = 0x08
43
      LIST           = 0x09
44
      SET            = 0x0A
45
      MAP            = 0x0B
46
      STRUCT         = 0x0C
47
 
48
      def self.is_bool_type?(b)
49
        (b & 0x0f) == BOOLEAN_TRUE || (b & 0x0f) == BOOLEAN_FALSE
50
      end
51
 
52
      COMPACT_TO_TTYPE = {
53
        Types::STOP   => Types::STOP,
54
        BOOLEAN_FALSE => Types::BOOL,
55
        BOOLEAN_TRUE  => Types::BOOL,
56
        BYTE          => Types::BYTE,
57
        I16           => Types::I16,
58
        I32           => Types::I32,
59
        I64           => Types::I64,
60
        DOUBLE        => Types::DOUBLE,
61
        BINARY        => Types::STRING,
62
        LIST          => Types::LIST,
63
        SET           => Types::SET,
64
        MAP           => Types::MAP,
65
        STRUCT        => Types::STRUCT
66
      }
67
 
68
      TTYPE_TO_COMPACT = {
69
        Types::STOP           => Types::STOP,
70
        Types::BOOL           => BOOLEAN_TRUE,
71
        Types::BYTE           => BYTE,
72
        Types::I16            => I16,
73
        Types::I32            => I32,
74
        Types::I64            => I64,
75
        Types::DOUBLE         => DOUBLE,
76
        Types::STRING         => BINARY,
77
        Types::LIST           => LIST,
78
        Types::SET            => SET,
79
        Types::MAP            => MAP,
80
        Types::STRUCT         => STRUCT
81
      }
82
 
83
      def self.get_ttype(compact_type)
84
        val = COMPACT_TO_TTYPE[compact_type & 0x0f]
85
        raise "don't know what type: #{compact_type & 0x0f}" unless val
86
        val
87
      end
88
 
89
      def self.get_compact_type(ttype)
90
        val = TTYPE_TO_COMPACT[ttype]
91
        raise "don't know what type: #{ttype & 0x0f}" unless val
92
        val
93
      end
94
    end
95
 
96
    def initialize(transport)
97
      super(transport)
98
 
99
      @last_field = [0]
100
      @boolean_value = nil
101
    end
102
 
103
    def write_message_begin(name, type, seqid)
104
      write_byte(PROTOCOL_ID)
105
      write_byte((VERSION & VERSION_MASK) | ((type << TYPE_SHIFT_AMOUNT) & TYPE_MASK))
106
      write_varint32(seqid)
107
      write_string(name)
108
      nil
109
    end
110
 
111
    def write_struct_begin(name)
112
      @last_field.push(0)
113
      nil
114
    end
115
 
116
    def write_struct_end
117
      @last_field.pop
118
      nil
119
    end
120
 
121
    def write_field_begin(name, type, id)
122
      if type == Types::BOOL
123
        # we want to possibly include the value, so we'll wait.
124
        @boolean_field = [type, id]
125
      else
126
        write_field_begin_internal(type, id)
127
      end
128
      nil
129
    end
130
 
131
    # 
132
    # The workhorse of writeFieldBegin. It has the option of doing a 
133
    # 'type override' of the type header. This is used specifically in the 
134
    # boolean field case.
135
    # 
136
    def write_field_begin_internal(type, id, type_override=nil)
137
      last_id = @last_field.pop
138
 
139
      # if there's a type override, use that.
140
      typeToWrite = type_override || CompactTypes.get_compact_type(type)
141
 
142
      # check if we can use delta encoding for the field id
143
      if id > last_id && id - last_id <= 15
144
        # write them together
145
        write_byte((id - last_id) << 4 | typeToWrite)
146
      else
147
        # write them separate
148
        write_byte(typeToWrite)
149
        write_i16(id)
150
      end
151
 
152
      @last_field.push(id)
153
      nil
154
    end
155
 
156
    def write_field_stop
157
      write_byte(Types::STOP)
158
    end
159
 
160
    def write_map_begin(ktype, vtype, size)
161
      if (size == 0)
162
        write_byte(0)
163
      else
164
        write_varint32(size)
165
        write_byte(CompactTypes.get_compact_type(ktype) << 4 | CompactTypes.get_compact_type(vtype))
166
      end
167
    end
168
 
169
    def write_list_begin(etype, size)
170
      write_collection_begin(etype, size)
171
    end
172
 
173
    def write_set_begin(etype, size)
174
      write_collection_begin(etype, size);
175
    end
176
 
177
    def write_bool(bool)
178
      type = bool ? CompactTypes::BOOLEAN_TRUE : CompactTypes::BOOLEAN_FALSE
179
      unless @boolean_field.nil?
180
        # we haven't written the field header yet
181
        write_field_begin_internal(@boolean_field.first, @boolean_field.last, type)
182
        @boolean_field = nil
183
      else
184
        # we're not part of a field, so just write the value.
185
        write_byte(type)
186
      end
187
    end
188
 
189
    def write_byte(byte)
190
      @trans.write([byte].pack('c'))
191
    end
192
 
193
    def write_i16(i16)
194
      write_varint32(int_to_zig_zag(i16))
195
    end
196
 
197
    def write_i32(i32)
198
      write_varint32(int_to_zig_zag(i32))
199
    end
200
 
201
    def write_i64(i64)
202
      write_varint64(long_to_zig_zag(i64))
203
    end
204
 
205
    def write_double(dub)
206
      @trans.write([dub].pack("G").reverse)
207
    end
208
 
209
    def write_string(str)
210
      write_varint32(str.length)
211
      @trans.write(str)
212
    end
213
 
214
    def read_message_begin
215
      protocol_id = read_byte()
216
      if protocol_id != PROTOCOL_ID
217
        raise ProtocolException.new("Expected protocol id #{PROTOCOL_ID} but got #{protocol_id}")
218
      end
219
 
220
      version_and_type = read_byte()
221
      version = version_and_type & VERSION_MASK
222
      if (version != VERSION)
223
        raise ProtocolException.new("Expected version #{VERSION} but got #{version}");
224
      end
225
 
226
      type = (version_and_type >> TYPE_SHIFT_AMOUNT) & 0x03
227
      seqid = read_varint32()
228
      messageName = read_string()
229
      [messageName, type, seqid]
230
    end
231
 
232
    def read_struct_begin
233
      @last_field.push(0)
234
      ""
235
    end
236
 
237
    def read_struct_end
238
      @last_field.pop()
239
      nil
240
    end
241
 
242
    def read_field_begin
243
      type = read_byte()
244
 
245
      # if it's a stop, then we can return immediately, as the struct is over.
246
      if (type & 0x0f) == Types::STOP
247
        TSTOP
248
      else
249
        field_id = nil
250
 
251
        # mask off the 4 MSB of the type header. it could contain a field id delta.
252
        modifier = (type & 0xf0) >> 4
253
        if modifier == 0
254
          # not a delta. look ahead for the zigzag varint field id.
255
          field_id = read_i16()
256
        else
257
          # has a delta. add the delta to the last read field id.
258
          field_id = @last_field.pop + modifier
259
        end
260
 
261
        # if this happens to be a boolean field, the value is encoded in the type
262
        if CompactTypes.is_bool_type?(type)
263
          # save the boolean value in a special instance variable.
264
          @bool_value = (type & 0x0f) == CompactTypes::BOOLEAN_TRUE
265
        end
266
 
267
        # push the new field onto the field stack so we can keep the deltas going.
268
        @last_field.push(field_id)
269
        ["", CompactTypes.get_ttype(type & 0x0f), field_id]
270
      end
271
    end
272
 
273
    def read_map_begin
274
      size = read_varint32()
275
      key_and_value_type = size == 0 ? 0 : read_byte()
276
      [CompactTypes.get_ttype(key_and_value_type >> 4), CompactTypes.get_ttype(key_and_value_type & 0xf), size]
277
    end
278
 
279
    def read_list_begin
280
      size_and_type = read_byte()
281
      size = (size_and_type >> 4) & 0x0f
282
      if size == 15
283
        size = read_varint32()
284
      end
285
      type = CompactTypes.get_ttype(size_and_type)
286
      [type, size]
287
    end
288
 
289
    def read_set_begin
290
      read_list_begin
291
    end
292
 
293
    def read_bool
294
      unless @bool_value.nil?
295
        bv = @bool_value
296
        @bool_value = nil
297
        bv
298
      else
299
        read_byte() == CompactTypes::BOOLEAN_TRUE
300
      end
301
    end
302
 
303
    def read_byte
304
      dat = trans.read_all(1)
305
      val = dat[0]
306
      if (val > 0x7f)
307
        val = 0 - ((val - 1) ^ 0xff)
308
      end
309
      val
310
    end
311
 
312
    def read_i16
313
      zig_zag_to_int(read_varint32())
314
    end
315
 
316
    def read_i32
317
      zig_zag_to_int(read_varint32())
318
    end
319
 
320
    def read_i64
321
      zig_zag_to_long(read_varint64())
322
    end
323
 
324
    def read_double
325
      dat = trans.read_all(8)
326
      val = dat.reverse.unpack('G').first
327
      val
328
    end
329
 
330
    def read_string
331
      size = read_varint32()
332
      trans.read_all(size)
333
    end
334
 
335
 
336
    private
337
 
338
    # 
339
    # Abstract method for writing the start of lists and sets. List and sets on 
340
    # the wire differ only by the type indicator.
341
    # 
342
    def write_collection_begin(elem_type, size)
343
      if size <= 14
344
        write_byte(size << 4 | CompactTypes.get_compact_type(elem_type))
345
      else
346
        write_byte(0xf0 | CompactTypes.get_compact_type(elem_type))
347
        write_varint32(size)
348
      end
349
    end
350
 
351
    def write_varint32(n)
352
      # int idx = 0;
353
      while true
354
        if (n & ~0x7F) == 0
355
          # i32buf[idx++] = (byte)n;
356
          write_byte(n)
357
          break
358
          # return;
359
        else
360
          # i32buf[idx++] = (byte)((n & 0x7F) | 0x80);
361
          write_byte((n & 0x7F) | 0x80)
362
          n = n >> 7
363
        end
364
      end
365
      # trans_.write(i32buf, 0, idx);
366
    end
367
 
368
    SEVEN_BIT_MASK = 0x7F
369
    EVERYTHING_ELSE_MASK = ~SEVEN_BIT_MASK
370
 
371
    def write_varint64(n)
372
      while true
373
        if (n & EVERYTHING_ELSE_MASK) == 0 #TODO need to find a way to make this into a long...
374
          write_byte(n)
375
          break
376
        else
377
          write_byte((n & SEVEN_BIT_MASK) | 0x80)
378
          n >>= 7
379
        end
380
      end
381
    end
382
 
383
    def read_varint32()
384
      read_varint64()
385
    end
386
 
387
    def read_varint64()
388
      shift = 0
389
      result = 0
390
      while true
391
        b = read_byte()
392
        result |= (b & 0x7f) << shift
393
        break if (b & 0x80) != 0x80
394
        shift += 7
395
      end
396
      result
397
    end
398
 
399
    def int_to_zig_zag(n)
400
      (n << 1) ^ (n >> 31)
401
    end
402
 
403
    def long_to_zig_zag(l)
404
      # puts "zz encoded #{l} to #{(l << 1) ^ (l >> 63)}"
405
      (l << 1) ^ (l >> 63)
406
    end
407
 
408
    def zig_zag_to_int(n)
409
      (n >> 1) ^ -(n & 1)
410
    end
411
 
412
    def zig_zag_to_long(n)
413
      (n >> 1) ^ -(n & 1)
414
    end
415
  end
416
 
417
  class CompactProtocolFactory < BaseProtocolFactory
418
    def get_protocol(trans)
419
      CompactProtocol.new(trans)
420
    end
421
  end
422
end