Rev 30 | Blame | Compare with Previous | Last modification | View Log | RSS feed
## 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.#module Thriftclass CompactProtocol < BaseProtocolPROTOCOL_ID = [0x82].pack('c').unpack('c').firstVERSION = 1VERSION_MASK = 0x1fTYPE_MASK = 0xE0TYPE_SHIFT_AMOUNT = 5TSTOP = ["", Types::STOP, 0]## All of the on-wire type codes.#class CompactTypesBOOLEAN_TRUE = 0x01BOOLEAN_FALSE = 0x02BYTE = 0x03I16 = 0x04I32 = 0x05I64 = 0x06DOUBLE = 0x07BINARY = 0x08LIST = 0x09SET = 0x0AMAP = 0x0BSTRUCT = 0x0Cdef self.is_bool_type?(b)(b & 0x0f) == BOOLEAN_TRUE || (b & 0x0f) == BOOLEAN_FALSEendCOMPACT_TO_TTYPE = {Types::STOP => Types::STOP,BOOLEAN_FALSE => Types::BOOL,BOOLEAN_TRUE => Types::BOOL,BYTE => Types::BYTE,I16 => Types::I16,I32 => Types::I32,I64 => Types::I64,DOUBLE => Types::DOUBLE,BINARY => Types::STRING,LIST => Types::LIST,SET => Types::SET,MAP => Types::MAP,STRUCT => Types::STRUCT}TTYPE_TO_COMPACT = {Types::STOP => Types::STOP,Types::BOOL => BOOLEAN_TRUE,Types::BYTE => BYTE,Types::I16 => I16,Types::I32 => I32,Types::I64 => I64,Types::DOUBLE => DOUBLE,Types::STRING => BINARY,Types::LIST => LIST,Types::SET => SET,Types::MAP => MAP,Types::STRUCT => STRUCT}def self.get_ttype(compact_type)val = COMPACT_TO_TTYPE[compact_type & 0x0f]raise "don't know what type: #{compact_type & 0x0f}" unless valvalenddef self.get_compact_type(ttype)val = TTYPE_TO_COMPACT[ttype]raise "don't know what type: #{ttype & 0x0f}" unless valvalendenddef initialize(transport)super(transport)@last_field = [0]@boolean_value = nilenddef write_message_begin(name, type, seqid)write_byte(PROTOCOL_ID)write_byte((VERSION & VERSION_MASK) | ((type << TYPE_SHIFT_AMOUNT) & TYPE_MASK))write_varint32(seqid)write_string(name)nilenddef write_struct_begin(name)@last_field.push(0)nilenddef write_struct_end@last_field.popnilenddef write_field_begin(name, type, id)if type == Types::BOOL# we want to possibly include the value, so we'll wait.@boolean_field = [type, id]elsewrite_field_begin_internal(type, id)endnilend## The workhorse of writeFieldBegin. It has the option of doing a# 'type override' of the type header. This is used specifically in the# boolean field case.#def write_field_begin_internal(type, id, type_override=nil)last_id = @last_field.pop# if there's a type override, use that.typeToWrite = type_override || CompactTypes.get_compact_type(type)# check if we can use delta encoding for the field idif id > last_id && id - last_id <= 15# write them togetherwrite_byte((id - last_id) << 4 | typeToWrite)else# write them separatewrite_byte(typeToWrite)write_i16(id)end@last_field.push(id)nilenddef write_field_stopwrite_byte(Types::STOP)enddef write_map_begin(ktype, vtype, size)if (size == 0)write_byte(0)elsewrite_varint32(size)write_byte(CompactTypes.get_compact_type(ktype) << 4 | CompactTypes.get_compact_type(vtype))endenddef write_list_begin(etype, size)write_collection_begin(etype, size)enddef write_set_begin(etype, size)write_collection_begin(etype, size);enddef write_bool(bool)type = bool ? CompactTypes::BOOLEAN_TRUE : CompactTypes::BOOLEAN_FALSEunless @boolean_field.nil?# we haven't written the field header yetwrite_field_begin_internal(@boolean_field.first, @boolean_field.last, type)@boolean_field = nilelse# we're not part of a field, so just write the value.write_byte(type)endenddef write_byte(byte)@trans.write([byte].pack('c'))enddef write_i16(i16)write_varint32(int_to_zig_zag(i16))enddef write_i32(i32)write_varint32(int_to_zig_zag(i32))enddef write_i64(i64)write_varint64(long_to_zig_zag(i64))enddef write_double(dub)@trans.write([dub].pack("G").reverse)enddef write_string(str)write_varint32(str.length)@trans.write(str)enddef read_message_beginprotocol_id = read_byte()if protocol_id != PROTOCOL_IDraise ProtocolException.new("Expected protocol id #{PROTOCOL_ID} but got #{protocol_id}")endversion_and_type = read_byte()version = version_and_type & VERSION_MASKif (version != VERSION)raise ProtocolException.new("Expected version #{VERSION} but got #{version}");endtype = (version_and_type >> TYPE_SHIFT_AMOUNT) & 0x03seqid = read_varint32()messageName = read_string()[messageName, type, seqid]enddef read_struct_begin@last_field.push(0)""enddef read_struct_end@last_field.pop()nilenddef read_field_begintype = read_byte()# if it's a stop, then we can return immediately, as the struct is over.if (type & 0x0f) == Types::STOPTSTOPelsefield_id = nil# mask off the 4 MSB of the type header. it could contain a field id delta.modifier = (type & 0xf0) >> 4if modifier == 0# not a delta. look ahead for the zigzag varint field id.field_id = read_i16()else# has a delta. add the delta to the last read field id.field_id = @last_field.pop + modifierend# if this happens to be a boolean field, the value is encoded in the typeif CompactTypes.is_bool_type?(type)# save the boolean value in a special instance variable.@bool_value = (type & 0x0f) == CompactTypes::BOOLEAN_TRUEend# push the new field onto the field stack so we can keep the deltas going.@last_field.push(field_id)["", CompactTypes.get_ttype(type & 0x0f), field_id]endenddef read_map_beginsize = read_varint32()key_and_value_type = size == 0 ? 0 : read_byte()[CompactTypes.get_ttype(key_and_value_type >> 4), CompactTypes.get_ttype(key_and_value_type & 0xf), size]enddef read_list_beginsize_and_type = read_byte()size = (size_and_type >> 4) & 0x0fif size == 15size = read_varint32()endtype = CompactTypes.get_ttype(size_and_type)[type, size]enddef read_set_beginread_list_beginenddef read_boolunless @bool_value.nil?bv = @bool_value@bool_value = nilbvelseread_byte() == CompactTypes::BOOLEAN_TRUEendenddef read_bytedat = trans.read_all(1)val = dat[0]if (val > 0x7f)val = 0 - ((val - 1) ^ 0xff)endvalenddef read_i16zig_zag_to_int(read_varint32())enddef read_i32zig_zag_to_int(read_varint32())enddef read_i64zig_zag_to_long(read_varint64())enddef read_doubledat = trans.read_all(8)val = dat.reverse.unpack('G').firstvalenddef read_stringsize = read_varint32()trans.read_all(size)endprivate## Abstract method for writing the start of lists and sets. List and sets on# the wire differ only by the type indicator.#def write_collection_begin(elem_type, size)if size <= 14write_byte(size << 4 | CompactTypes.get_compact_type(elem_type))elsewrite_byte(0xf0 | CompactTypes.get_compact_type(elem_type))write_varint32(size)endenddef write_varint32(n)# int idx = 0;while trueif (n & ~0x7F) == 0# i32buf[idx++] = (byte)n;write_byte(n)break# return;else# i32buf[idx++] = (byte)((n & 0x7F) | 0x80);write_byte((n & 0x7F) | 0x80)n = n >> 7endend# trans_.write(i32buf, 0, idx);endSEVEN_BIT_MASK = 0x7FEVERYTHING_ELSE_MASK = ~SEVEN_BIT_MASKdef write_varint64(n)while trueif (n & EVERYTHING_ELSE_MASK) == 0 #TODO need to find a way to make this into a long...write_byte(n)breakelsewrite_byte((n & SEVEN_BIT_MASK) | 0x80)n >>= 7endendenddef read_varint32()read_varint64()enddef read_varint64()shift = 0result = 0while trueb = read_byte()result |= (b & 0x7f) << shiftbreak if (b & 0x80) != 0x80shift += 7endresultenddef int_to_zig_zag(n)(n << 1) ^ (n >> 31)enddef long_to_zig_zag(l)# puts "zz encoded #{l} to #{(l << 1) ^ (l >> 63)}"(l << 1) ^ (l >> 63)enddef zig_zag_to_int(n)(n >> 1) ^ -(n & 1)enddef zig_zag_to_long(n)(n >> 1) ^ -(n & 1)endendclass CompactProtocolFactory < BaseProtocolFactorydef get_protocol(trans)CompactProtocol.new(trans)endendend