module Hessian2::Writer
Public Instance Methods
call(method, args)
click to toggle source
# File lib/hessian2/writer.rb, line 8 def call(method, args) refs, crefs, trefs = {}, {}, {} out = [ 'H', '2', '0', 'C' ].pack('ahha') out << write_string(method) out << write_int(args.size) args.each{|arg| out << write(arg, refs, crefs, trefs) } out end
print_string(str)
click to toggle source
# File lib/hessian2/writer.rb, line 79 def print_string(str) return str.b if String.method_defined?(:b) arr, i = Array.new(str.bytesize), 0 str.unpack('U*').each do |c| if c < 0x80 # 0xxxxxxx arr[i] = c elsif c < 0x800 # 110xxxxx 10xxxxxx arr[i] = 0xc0 + ((c >> 6) & 0x1f) arr[i += 1] = 0x80 + (c & 0x3f) else # 1110xxxx 10xxxxxx 10xxxxxx arr[i] = 0xe0 + ((c >> 12) & 0xf) arr[i += 1] = 0x80 + ((c >> 6) & 0x3f) arr[i += 1] = 0x80 + (c & 0x3f) end i += 1 end arr.pack('C*') end
reply(val)
click to toggle source
# File lib/hessian2/writer.rb, line 19 def reply(val) [ 'H', '2', '0', 'R' ].pack('ahha') << write(val) end
write(val, refs = {}, crefs = {}, trefs = {})
click to toggle source
# File lib/hessian2/writer.rb, line 33 def write(val, refs = {}, crefs = {}, trefs = {}) case val when StructWrapper # object to values-array write_struct_wrapper(val, refs, crefs, trefs) when ClassWrapper # class definition for statically typed languages write_class_wrapper(val, refs, crefs, trefs) when TypeWrapper write_type_wrapper(val, refs, crefs, trefs) when TrueClass [ BC_TRUE ].pack('C') when FalseClass [ BC_FALSE ].pack('C') when Time if val.usec == 0 && val.sec == 0 # date in minutes [ BC_DATE_MINUTE, val.to_i / 60 ].pack('CL>') else [ BC_DATE, val.to_i * 1000 + val.usec / 1000 ].pack('CQ>') # date end when Float, BigDecimal write_float(val) when Fixnum write_int(val) when Array write_array(val, refs, crefs, trefs) when Bignum if val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs [ BC_LONG_INT, val ].pack('Cl>') else # long [ BC_LONG, val ].pack('Cq>') end when Integer write_int(val) when Hash write_hash(val, refs, crefs, trefs) when NilClass write_nil when String write_string(val) when Symbol write_string(val.to_s) else write_object(val, refs, crefs, trefs) end end
write_array(arr, refs = {}, crefs = {}, trefs = {})
click to toggle source
# File lib/hessian2/writer.rb, line 101 def write_array(arr, refs = {}, crefs = {}, trefs = {}) idx = refs[arr.object_id] return write_ref(idx) if idx refs[arr.object_id] = refs.size len = arr.count if len <= LIST_DIRECT_MAX # [x78-7f] value* str = [ BC_LIST_DIRECT_UNTYPED + len ].pack('C') else # x58 int value* str = [ BC_LIST_FIXED_UNTYPED ].pack('C') << write_int(len) end arr.each do |ele| str << write(ele, refs, crefs, trefs) end str end
write_binary(str)
click to toggle source
# File lib/hessian2/writer.rb, line 121 def write_binary(str) chunks, i, len = [], 0, str.size while len > 0x8000 chunks << [ BC_BINARY_CHUNK, 0x8000 ].pack('Cn') << str[i...(i += 0x8000)] len -= 0x8000 end final = str[i..-1] if len <= BINARY_DIRECT_MAX chunks << [ BC_BINARY_DIRECT + len ].pack('C') << final elsif len <= BINARY_SHORT_MAX chunks << [ BC_BINARY_SHORT + (len >> 8), len ].pack('CC') << final else chunks << [ BC_BINARY, len ].pack('Cn') << final end chunks.join end
write_class_wrapper(val, refs, crefs, trefs)
click to toggle source
# File lib/hessian2/writer.rb, line 141 def write_class_wrapper(val, refs, crefs, trefs) return write_nil unless val.values idx = refs[val.object_id] return write_ref(idx) if idx refs[val.object_id] = refs.size if val.is_multi? type = '[' << val.klass if trefs.include?(type) tstr = write_int(trefs[type]) else trefs[type] = trefs.size # store a type tstr = write_string(type) end return [ BC_LIST_DIRECT ].pack('C') << tstr if val.values.size == 0 end cref = crefs[val.klass] if cref cidx = cref.first fields = cref.last str = '' else fstr = val.fields.map{|f| write_string(f) }.join str = [ BC_OBJECT_DEF ].pack('C') << write_string(val.klass) << write_int(val.fields.size) << fstr cidx = crefs.size crefs[val.klass] = [cidx, val.fields] # store a class definition end if cidx <= OBJECT_DIRECT_MAX cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C') else cstr = [ BC_OBJECT ].pack('C') << write_int(cidx) end if val.is_multi? len = val.values.size if len <= LIST_DIRECT_MAX # [x70-77] type value* str << [ BC_LIST_DIRECT + len ].pack('C') << tstr else # 'V' type int value* str << [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len) end val.values.each do |ele| if ele ele_idx = refs[ele.object_id] if ele_idx str << (cstr + write_ref(ele_idx)) else refs[ele.object_id] = refs.size str << (cstr + ele.map{|v| write(v, refs, crefs, trefs)}.join) end else str << write_nil end end else str << (cstr + val.values.map{|v| write(v, refs, crefs, trefs)}.join) end str end
write_fault(e)
click to toggle source
# File lib/hessian2/writer.rb, line 24 def write_fault(e) val = { code: e.class.to_s, message: e.message, detail: e.backtrace } [ 'F' ].pack('a') << write_hash(val) end
write_float(val)
click to toggle source
# File lib/hessian2/writer.rb, line 208 def write_float(val) case val.infinite? when 1 return [ BC_DOUBLE, Float::INFINITY ].pack('CG') when -1 return [ BC_DOUBLE, -Float::INFINITY ].pack('CG') else return [ BC_DOUBLE, Float::NAN ].pack('CG') if val.nan? return [ BC_DOUBLE_ZERO ].pack('C') if val.zero? # double zero return [ BC_DOUBLE_ONE ].pack('C') if val == 1 # double one ival = val.to_i if ival == val return [ BC_DOUBLE_BYTE, ival ].pack('Cc') if ival >= -0x80 && ival <= 0x7f # double octet return [ BC_DOUBLE_SHORT, (ival >> 8), ival ].pack('Ccc') if ival >= -0x8000 && ival <= 0x7fff # double short end mval = val * 1000 if mval.finite? mills = mval.to_i if mills >= -0x80_000_000 && mills <= 0x7f_fff_fff && 0.001 * mills == val return [ BC_DOUBLE_MILL, mills ].pack('Cl>') # double mill end end [ BC_DOUBLE, val ].pack('CG') # double end end
write_hash(hash, refs = {}, crefs = {}, trefs = {})
click to toggle source
# File lib/hessian2/writer.rb, line 238 def write_hash(hash, refs = {}, crefs = {}, trefs = {}) idx = refs[hash.object_id] return write_ref(idx) if idx refs[hash.object_id] = refs.size str = [ BC_MAP_UNTYPED ].pack('C') hash.each do |k, v| str << write(k, refs, crefs, trefs) str << write(v, refs, crefs, trefs) end str << [ BC_END ].pack('C') end
write_int(val)
click to toggle source
# File lib/hessian2/writer.rb, line 253 def write_int(val) if val >= INT_DIRECT_MIN && val <= INT_DIRECT_MAX # single octet integers [ BC_INT_ZERO + val ].pack('c') elsif val >= INT_BYTE_MIN && val <= INT_BYTE_MAX # two octet integers [ BC_INT_BYTE_ZERO + (val >> 8), val ].pack('cc') elsif val >= INT_SHORT_MIN && val <= INT_SHORT_MAX # three octet integers [ BC_INT_SHORT_ZERO + (val >> 16), (val >> 8), val].pack('ccc') elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # integer [ BC_INT, val ].pack('Cl>') else [ BC_LONG, val ].pack('Cq>') end end
write_long(val)
click to toggle source
# File lib/hessian2/writer.rb, line 268 def write_long(val) if val >= LONG_DIRECT_MIN && val <= LONG_DIRECT_MAX # single octet longs [ BC_LONG_ZERO + val ].pack('c') elsif val >= LONG_BYTE_MIN && val <= LONG_BYTE_MAX # two octet longs [ BC_LONG_BYTE_ZERO + (val >> 8), val ].pack('cc') elsif val >= LONG_SHORT_MIN && val <= LONG_SHORT_MAX # three octet longs [ BC_LONG_SHORT_ZERO + (val >> 16), (val >> 8), val ].pack('ccc') elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs [ BC_LONG_INT, val ].pack('Cl>') else [ BC_LONG, val ].pack('Cq>') end end
write_nil()
click to toggle source
# File lib/hessian2/writer.rb, line 283 def write_nil [ BC_NULL ].pack('C') end
write_object(object, refs = {}, crefs = {}, trefs = {})
click to toggle source
# File lib/hessian2/writer.rb, line 288 def write_object(object, refs = {}, crefs = {}, trefs = {}) return write_nil unless object return write_array(object, refs, crefs, trefs) if object.respond_to?(:count) && (object.respond_to?(:sql) || object.respond_to?(:to_sql)) idx = refs[object.object_id] return write_ref(idx) if idx refs[object.object_id] = refs.size klass = object.class.to_s cref = crefs[klass] if cref cidx = cref.first fields = cref.last str = '' else fields = get_fields(object) fstr = fields.map{|f| write_string(f) }.join cidx = crefs.size crefs[klass] = [cidx, fields] str = [ BC_OBJECT_DEF ].pack('C') << write_string(klass) << write_int(fields.size) << fstr end if cidx <= OBJECT_DIRECT_MAX cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C') else cstr = [ BC_OBJECT ].pack('C') << write_int(cidx) end str << write_values(object, cstr, fields, refs, crefs, trefs) str end
write_ref(val)
click to toggle source
# File lib/hessian2/writer.rb, line 325 def write_ref(val) [ BC_REF ].pack('C') << write_int(val) end
write_string(str)
click to toggle source
# File lib/hessian2/writer.rb, line 342 def write_string(str) chunks, i, len = '', 0, str.size while len > 0x8000 chunks << [ BC_STRING_CHUNK, 0x8000 ].pack('Cn') << print_string(str[i...(i += 0x8000)]) len -= 0x8000 end final = str[i..-1] chunks << if len <= STRING_DIRECT_MAX [ BC_STRING_DIRECT + len ].pack('C') elsif len <= STRING_SHORT_MAX [ BC_STRING_SHORT + (len >> 8), len ].pack('CC') else [ BC_STRING, len ].pack('Cn') end chunks << print_string(final) end
write_struct_wrapper(val, refs, crefs, trefs)
click to toggle source
# File lib/hessian2/writer.rb, line 330 def write_struct_wrapper(val, refs, crefs, trefs) return write_nil unless val.values idx = refs[val.object_id] return write_ref(idx) if idx refs[val.object_id] = refs.size write_array(val.values, refs, crefs, trefs) end
write_type_wrapped_array(arr, tstr, eletype, refs = {}, crefs = {}, trefs = {})
click to toggle source
# File lib/hessian2/writer.rb, line 362 def write_type_wrapped_array(arr, tstr, eletype, refs = {}, crefs = {}, trefs = {}) len = arr.size return [ BC_LIST_DIRECT ].pack('C') << tstr if len == 0 if len <= LIST_DIRECT_MAX # [x70-77] type value* str = [ BC_LIST_DIRECT + len ].pack('C') << tstr else # 'V' type int value* str = [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len) end case eletype when 'L' arr.each do |ele| str << write_long(Integer(ele)) end when 'I' arr.each do |ele| str << write_int(Integer(ele)) end when 'B' arr.each do |ele| str << write_binary(ele) end else arr.each do |ele| idx = refs[ele.object_id] if idx str << write_ref(idx) else refs[ele.object_id] = refs.size str << write_type_wrapped_object(ele, tstr, refs, crefs, trefs) end end end str end
write_type_wrapped_object(object, tstr, refs = {}, crefs = {}, trefs = {})
click to toggle source
# File lib/hessian2/writer.rb, line 401 def write_type_wrapped_object(object, tstr, refs = {}, crefs = {}, trefs = {}) return write_nil unless object str = [ BC_MAP ].pack('C') << tstr if object.is_a?(Hash) object.each do |k, v| str << write(k, refs, crefs, trefs) str << write(v, refs, crefs, trefs) end elsif object.instance_variable_get(:@attributes).is_a?(Hash) object.attributes.each do |k, v| str << write(k, refs, crefs, trefs) str << write(v, refs, crefs, trefs) end elsif object.is_a?(ClassWrapper) object.fields.each_with_index do |f, i| str << write(f, refs, crefs, trefs) str << write(object.values[i], refs, crefs, trefs) end elsif object.is_a?(TypeWrapper) object.object.each do |k, v| str << write(k, refs, crefs, trefs) str << write(v, refs, crefs, trefs) end else object.instance_variables.each do |var| str << write(var[1..-1], refs, crefs, trefs) str << write(object.instance_variable_get(var), refs, crefs, trefs) end end str << [ BC_END ].pack('C') end
write_type_wrapper(val, refs, crefs, trefs)
click to toggle source
# File lib/hessian2/writer.rb, line 437 def write_type_wrapper(val, refs, crefs, trefs) return write_nil unless val.object idx = refs[val.object_id] return write_ref(idx) if idx refs[val.object_id] = refs.size type = val.is_multi? ? ('[' << val.hessian_type) : val.hessian_type if trefs.include?(type) tstr = write_int(trefs[type]) else trefs[type] = trefs.size tstr = write_string(type) end if val.is_multi? write_type_wrapped_array(val.object, tstr, val.hessian_type, refs, crefs, trefs) else case val.hessian_type when 'L' write_long(Integer(val.object)) when 'I' write_int(Integer(val.object)) when 'B' write_binary(val.object) else write_type_wrapped_object(val.object, tstr, refs, crefs, trefs) end end end
Private Instance Methods
get_fields(object)
click to toggle source
# File lib/hessian2/writer.rb, line 472 def get_fields(object) fields = if object.is_a?(Hash) object.keys.map{|k| k.to_sym } elsif object.instance_variable_get(:@values).is_a?(Hash) object.values.keys elsif object.instance_variable_get(:@attributes).is_a?(Hash) object.attributes.keys else object.instance_variables.map{|k| k[1..-1].to_sym } end raise "fields should not be empty: #{object.inspect}" if fields.empty? fields end
write_values(object, cstr, fields, refs, crefs, trefs)
click to toggle source
# File lib/hessian2/writer.rb, line 489 def write_values(object, cstr, fields, refs, crefs, trefs) return write_nil unless object vstr = if object.is_a?(Hash) fields.map{|f| write(object[f] || object[f.to_s], refs, crefs, trefs) }.join elsif object.instance_variable_get(:@values).is_a?(Hash) fields.map{|f| write(object.values[f], refs, crefs, trefs) }.join elsif object.instance_variable_get(:@attributes).is_a?(Hash) fields.map{|f| write(object.attributes[f], refs, crefs, trefs) }.join else fields.map{|f| write(object.instance_variable_get(f.to_s.prepend('@')), refs, crefs, trefs) }.join end cstr + vstr end