hessian2 2.0.5 → 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -10
  3. data/README.md +203 -199
  4. data/lib/hessian2.rb +14 -14
  5. data/lib/hessian2/client.rb +57 -57
  6. data/lib/hessian2/constants.rb +164 -164
  7. data/lib/hessian2/fault.rb +3 -3
  8. data/lib/hessian2/handler.rb +18 -18
  9. data/lib/hessian2/hessian_client.rb +3 -3
  10. data/lib/hessian2/parser.rb +619 -619
  11. data/lib/hessian2/type_wrapper.rb +49 -49
  12. data/lib/hessian2/version.rb +3 -3
  13. data/lib/hessian2/writer.rb +506 -504
  14. data/spec/binary_spec.rb +51 -51
  15. data/spec/boolean_spec.rb +26 -26
  16. data/spec/class_wrapper_spec.rb +52 -52
  17. data/spec/create_monkeys.rb +14 -14
  18. data/spec/date_spec.rb +45 -45
  19. data/spec/double_spec.rb +78 -78
  20. data/spec/int_spec.rb +54 -54
  21. data/spec/list_spec.rb +66 -66
  22. data/spec/long_spec.rb +68 -68
  23. data/spec/map_spec.rb +36 -36
  24. data/spec/null_spec.rb +17 -17
  25. data/spec/object_spec.rb +78 -78
  26. data/spec/ref_spec.rb +43 -43
  27. data/spec/spec_helper.rb +23 -23
  28. data/spec/string_spec.rb +61 -61
  29. data/spec/struct_wrapper_spec.rb +47 -47
  30. data/spec/type_wrapper_spec.rb +102 -102
  31. data/test/app.rb +24 -24
  32. data/test/async/em_http_asleep.rb +25 -25
  33. data/test/async/em_http_sleep.rb +25 -25
  34. data/test/async/monkey.asleep.rb +27 -27
  35. data/test/async/mysql2_aquery.rb +37 -37
  36. data/test/fault/monkey.undefined_method.rb +5 -5
  37. data/test/fault/monkey.wrong_arguments.rb +5 -5
  38. data/test/fiber_concurrency/em_http_asleep.rb +17 -17
  39. data/test/fiber_concurrency/em_http_sleep.rb +17 -17
  40. data/test/fiber_concurrency/monkey.asleep.fiber_aware.rb +18 -18
  41. data/test/fiber_concurrency/mysql2_query.rb +29 -29
  42. data/test/fiber_concurrency/net_http_asleep.rb +19 -19
  43. data/test/fiber_concurrency/net_http_sleep.rb +19 -19
  44. data/test/fibered_rainbows/Gemfile +15 -15
  45. data/test/fibered_rainbows/config.ru +11 -11
  46. data/test/fibered_rainbows/rainbows.rb +13 -13
  47. data/test/monkey_service.rb +16 -16
  48. data/test/prepare.rb +7 -7
  49. data/test/thread_concurrency/active_record_execute.rb +29 -29
  50. data/test/thread_concurrency/monkey.asleep.rb +22 -22
  51. data/test/thread_concurrency/net_http_asleep.rb +24 -24
  52. data/test/thread_concurrency/net_http_sleep.rb +24 -24
  53. data/test/threaded_rainbows/Gemfile +13 -13
  54. data/test/threaded_rainbows/config.ru +9 -9
  55. data/test/threaded_rainbows/rainbows.rb +13 -13
  56. metadata +46 -4
@@ -1,49 +1,49 @@
1
- module Hessian2
2
- class TypeWrapper
3
- attr_accessor :object
4
- attr_reader :hessian_type, :is_multi
5
-
6
- def initialize(type, object)
7
- if type.is_a?(Array)
8
- is_multi = true
9
- hessian_type = unify_type(type.first)
10
- elsif type.is_a?(String)
11
- if type.include?('[')
12
- is_multi = true
13
- hessian_type = unify_type(type.delete('[]'))
14
- else
15
- is_multi = false
16
- hessian_type = unify_type(type)
17
- end
18
- else
19
- is_multi = false
20
- hessian_type = unify_type(type)
21
- end
22
-
23
- @object, @hessian_type, @is_multi = object, hessian_type, is_multi
24
- end
25
-
26
-
27
- def is_multi?
28
- @is_multi
29
- end
30
-
31
-
32
- private
33
-
34
- def unify_type(type)
35
- case type
36
- when 'L', 'l', 'Long', 'long', :long
37
- 'L'
38
- when 'I', 'i', 'Integer', 'int', :int
39
- 'I'
40
- when 'B', 'b', 'Binary', 'bin', :bin
41
- 'B'
42
- else
43
- type
44
- end
45
- end
46
-
47
-
48
- end
49
- end
1
+ module Hessian2
2
+ class TypeWrapper
3
+ attr_accessor :object
4
+ attr_reader :hessian_type, :is_multi
5
+
6
+ def initialize(type, object)
7
+ if type.is_a?(Array)
8
+ is_multi = true
9
+ hessian_type = unify_type(type.first)
10
+ elsif type.is_a?(String)
11
+ if type.include?('[')
12
+ is_multi = true
13
+ hessian_type = unify_type(type.delete('[]'))
14
+ else
15
+ is_multi = false
16
+ hessian_type = unify_type(type)
17
+ end
18
+ else
19
+ is_multi = false
20
+ hessian_type = unify_type(type)
21
+ end
22
+
23
+ @object, @hessian_type, @is_multi = object, hessian_type, is_multi
24
+ end
25
+
26
+
27
+ def is_multi?
28
+ @is_multi
29
+ end
30
+
31
+
32
+ private
33
+
34
+ def unify_type(type)
35
+ case type
36
+ when 'L', 'l', 'Long', 'long', :long
37
+ 'L'
38
+ when 'I', 'i', 'Integer', 'int', :int
39
+ 'I'
40
+ when 'B', 'b', 'Binary', 'bin', :bin
41
+ 'B'
42
+ else
43
+ type
44
+ end
45
+ end
46
+
47
+
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
- module Hessian2
2
- VERSION = "2.0.5"
3
- end
1
+ module Hessian2
2
+ VERSION = "2.0.6"
3
+ end
@@ -1,504 +1,506 @@
1
- require 'hessian2/constants'
2
- require 'bigdecimal'
3
-
4
- module Hessian2
5
- module Writer
6
- include Constants
7
-
8
- def call(method, args)
9
- refs, crefs, trefs = {}, {}, {}
10
- out = [ 'H', '2', '0', 'C' ].pack('ahha')
11
- out << write_string(method)
12
- out << write_int(args.size)
13
- args.each{|arg| out << write(arg, refs, crefs, trefs) }
14
-
15
- out
16
- end
17
-
18
-
19
- def reply(val)
20
- [ 'H', '2', '0', 'R' ].pack('ahha') << write(val)
21
- end
22
-
23
-
24
- def write_fault(e)
25
- val = {
26
- code: e.class.to_s,
27
- message: e.message,
28
- detail: e.backtrace }
29
- [ 'F' ].pack('a') << write_hash(val)
30
- end
31
-
32
-
33
- def write(val, refs = {}, crefs = {}, trefs = {})
34
- case val
35
- when StructWrapper # object to values-array
36
- write_struct_wrapper(val, refs, crefs, trefs)
37
- when ClassWrapper # class definition for statically typed languages
38
- write_class_wrapper(val, refs, crefs, trefs)
39
- when TypeWrapper
40
- write_type_wrapper(val, refs, crefs, trefs)
41
- when TrueClass
42
- [ BC_TRUE ].pack('C')
43
- when FalseClass
44
- [ BC_FALSE ].pack('C')
45
- when Time
46
- if val.usec == 0 && val.sec == 0 # date in minutes
47
- [ BC_DATE_MINUTE, val.to_i / 60 ].pack('CL>')
48
- else
49
- [ BC_DATE, val.to_i * 1000 + val.usec / 1000 ].pack('CQ>') # date
50
- end
51
- when Float, BigDecimal
52
- write_float(val)
53
- when Fixnum
54
- write_int(val)
55
- when Array
56
- write_array(val, refs, crefs, trefs)
57
- when Bignum
58
- if val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
59
- [ BC_LONG_INT, val ].pack('Cl>')
60
- else # long
61
- [ BC_LONG, val ].pack('Cq>')
62
- end
63
- when Hash
64
- write_hash(val, refs, crefs, trefs)
65
- when NilClass
66
- write_nil
67
- when String
68
- write_string(val)
69
- when Symbol
70
- write_string(val.to_s)
71
- else
72
- write_object(val, refs, crefs, trefs)
73
- end
74
- end
75
-
76
-
77
- def print_string(str)
78
- return str.b if String.method_defined?(:b)
79
-
80
- arr, i = Array.new(str.bytesize), 0
81
- str.unpack('U*').each do |c|
82
- if c < 0x80 # 0xxxxxxx
83
- arr[i] = c
84
- elsif c < 0x800 # 110xxxxx 10xxxxxx
85
- arr[i] = 0xc0 + ((c >> 6) & 0x1f)
86
- arr[i += 1] = 0x80 + (c & 0x3f)
87
- else # 1110xxxx 10xxxxxx 10xxxxxx
88
- arr[i] = 0xe0 + ((c >> 12) & 0xf)
89
- arr[i += 1] = 0x80 + ((c >> 6) & 0x3f)
90
- arr[i += 1] = 0x80 + (c & 0x3f)
91
- end
92
- i += 1
93
- end
94
-
95
- arr.pack('C*')
96
- end
97
-
98
-
99
- def write_array(arr, refs = {}, crefs = {}, trefs = {})
100
- idx = refs[arr.object_id]
101
- return write_ref(idx) if idx
102
-
103
- refs[arr.object_id] = refs.size
104
- len = arr.count
105
- if len <= LIST_DIRECT_MAX # [x78-7f] value*
106
- str = [ BC_LIST_DIRECT_UNTYPED + len ].pack('C')
107
- else # x58 int value*
108
- str = [ BC_LIST_FIXED_UNTYPED ].pack('C') << write_int(len)
109
- end
110
-
111
- arr.each do |ele|
112
- str << write(ele, refs, crefs, trefs)
113
- end
114
-
115
- str
116
- end
117
-
118
-
119
- def write_binary(str)
120
- chunks, i, len = [], 0, str.size
121
- while len > 0x8000
122
- chunks << [ BC_BINARY_CHUNK, 0x8000 ].pack('Cn') << str[i...(i += 0x8000)]
123
- len -= 0x8000
124
- end
125
-
126
- final = str[i..-1]
127
- if len <= BINARY_DIRECT_MAX
128
- chunks << [ BC_BINARY_DIRECT + len ].pack('C') << final
129
- elsif len <= BINARY_SHORT_MAX
130
- chunks << [ BC_BINARY_SHORT + (len >> 8), len ].pack('CC') << final
131
- else
132
- chunks << [ BC_BINARY, len ].pack('Cn') << final
133
- end
134
-
135
- chunks.join
136
- end
137
-
138
-
139
- def write_class_wrapper(val, refs, crefs, trefs)
140
- return write_nil unless val.values
141
-
142
- idx = refs[val.object_id]
143
- return write_ref(idx) if idx
144
-
145
- refs[val.object_id] = refs.size
146
-
147
- if val.is_multi?
148
- type = '[' << val.klass
149
- if trefs.include?(type)
150
- tstr = write_int(trefs[type])
151
- else
152
- trefs[type] = trefs.size # store a type
153
- tstr = write_string(type)
154
- end
155
- return [ BC_LIST_DIRECT ].pack('C') << tstr if val.values.size == 0
156
- end
157
-
158
- cref = crefs[val.klass]
159
- if cref
160
- cidx = cref.first
161
- fields = cref.last
162
- str = ''
163
- else
164
- fstr = val.fields.map{|f| write_string(f) }.join
165
-
166
- str = [ BC_OBJECT_DEF ].pack('C') << write_string(val.klass) << write_int(val.fields.size) << fstr
167
- cidx = crefs.size
168
- crefs[val.klass] = [cidx, val.fields] # store a class definition
169
- end
170
-
171
- if cidx <= OBJECT_DIRECT_MAX
172
- cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
173
- else
174
- cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
175
- end
176
-
177
- if val.is_multi?
178
- len = val.values.size
179
- if len <= LIST_DIRECT_MAX # [x70-77] type value*
180
- str << [ BC_LIST_DIRECT + len ].pack('C') << tstr
181
- else # 'V' type int value*
182
- str << [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
183
- end
184
-
185
- val.values.each do |ele|
186
- if ele
187
- ele_idx = refs[ele.object_id]
188
- if ele_idx
189
- str << (cstr + write_ref(ele_idx))
190
- else
191
- refs[ele.object_id] = refs.size
192
- str << (cstr + ele.map{|v| write(v, refs, crefs, trefs)}.join)
193
- end
194
- else
195
- str << write_nil
196
- end
197
- end
198
- else
199
- str << (cstr + val.values.map{|v| write(v, refs, crefs, trefs)}.join)
200
- end
201
-
202
- str
203
- end
204
-
205
-
206
- def write_float(val)
207
- case val.infinite?
208
- when 1
209
- return [ BC_DOUBLE, Float::INFINITY ].pack('CG')
210
- when -1
211
- return [ BC_DOUBLE, -Float::INFINITY ].pack('CG')
212
- else
213
- return [ BC_DOUBLE, Float::NAN ].pack('CG') if val.nan?
214
- return [ BC_DOUBLE_ZERO ].pack('C') if val.zero? # double zero
215
- return [ BC_DOUBLE_ONE ].pack('C') if val == 1 # double one
216
-
217
- ival = val.to_i
218
- if ival == val
219
- return [ BC_DOUBLE_BYTE, ival ].pack('Cc') if ival >= -0x80 && ival <= 0x7f # double octet
220
- return [ BC_DOUBLE_SHORT, (ival >> 8), ival ].pack('Ccc') if ival >= -0x8000 && ival <= 0x7fff # double short
221
- end
222
-
223
- mval = val * 1000
224
- if mval.finite?
225
- mills = mval.to_i
226
- if mills >= -0x80_000_000 && mills <= 0x7f_fff_fff && 0.001 * mills == val
227
- return [ BC_DOUBLE_MILL, mills ].pack('Cl>') # double mill
228
- end
229
- end
230
-
231
- [ BC_DOUBLE, val ].pack('CG') # double
232
- end
233
- end
234
-
235
-
236
- def write_hash(hash, refs = {}, crefs = {}, trefs = {})
237
- idx = refs[hash.object_id]
238
- return write_ref(idx) if idx
239
-
240
- refs[hash.object_id] = refs.size
241
- str = [ BC_MAP_UNTYPED ].pack('C')
242
- hash.each do |k, v|
243
- str << write(k, refs, crefs, trefs)
244
- str << write(v, refs, crefs, trefs)
245
- end
246
-
247
- str << [ BC_END ].pack('C')
248
- end
249
-
250
-
251
- def write_int(val)
252
- if val >= INT_DIRECT_MIN && val <= INT_DIRECT_MAX # single octet integers
253
- [ BC_INT_ZERO + val ].pack('c')
254
- elsif val >= INT_BYTE_MIN && val <= INT_BYTE_MAX # two octet integers
255
- [ BC_INT_BYTE_ZERO + (val >> 8), val ].pack('cc')
256
- elsif val >= INT_SHORT_MIN && val <= INT_SHORT_MAX # three octet integers
257
- [ BC_INT_SHORT_ZERO + (val >> 16), (val >> 8), val].pack('ccc')
258
- elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # integer
259
- [ BC_INT, val ].pack('Cl>')
260
- else
261
- [ BC_LONG, val ].pack('Cq>')
262
- end
263
- end
264
-
265
-
266
- def write_long(val)
267
- if val >= LONG_DIRECT_MIN && val <= LONG_DIRECT_MAX # single octet longs
268
- [ BC_LONG_ZERO + val ].pack('c')
269
- elsif val >= LONG_BYTE_MIN && val <= LONG_BYTE_MAX # two octet longs
270
- [ BC_LONG_BYTE_ZERO + (val >> 8), val ].pack('cc')
271
- elsif val >= LONG_SHORT_MIN && val <= LONG_SHORT_MAX # three octet longs
272
- [ BC_LONG_SHORT_ZERO + (val >> 16), (val >> 8), val ].pack('ccc')
273
- elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
274
- [ BC_LONG_INT, val ].pack('Cl>')
275
- else
276
- [ BC_LONG, val ].pack('Cq>')
277
- end
278
- end
279
-
280
-
281
- def write_nil
282
- [ BC_NULL ].pack('C')
283
- end
284
-
285
-
286
- def write_object(object, refs = {}, crefs = {}, trefs = {})
287
- return write_nil unless object
288
-
289
- return write_array(object, refs, crefs, trefs) if object.respond_to?(:count) && (object.respond_to?(:sql) || object.respond_to?(:to_sql))
290
-
291
- idx = refs[object.object_id]
292
- return write_ref(idx) if idx
293
-
294
- refs[object.object_id] = refs.size
295
-
296
- klass = object.class.to_s
297
- cref = crefs[klass]
298
- if cref
299
- cidx = cref.first
300
- fields = cref.last
301
- str = ''
302
- else
303
- fields = get_fields(object)
304
- fstr = fields.map{|f| write_string(f) }.join
305
- cidx = crefs.size
306
- crefs[klass] = [cidx, fields]
307
-
308
- str = [ BC_OBJECT_DEF ].pack('C') << write_string(klass) << write_int(fields.size) << fstr
309
- end
310
-
311
- if cidx <= OBJECT_DIRECT_MAX
312
- cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
313
- else
314
- cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
315
- end
316
-
317
- str << write_values(object, cstr, fields, refs, crefs, trefs)
318
-
319
- str
320
- end
321
-
322
-
323
- def write_ref(val)
324
- [ BC_REF ].pack('C') << write_int(val)
325
- end
326
-
327
-
328
- def write_struct_wrapper(val, refs, crefs, trefs)
329
- return write_nil unless val.values
330
-
331
- idx = refs[val.object_id]
332
- return write_ref(idx) if idx
333
-
334
- refs[val.object_id] = refs.size
335
-
336
- write_array(val.values, refs, crefs, trefs)
337
- end
338
-
339
-
340
- def write_string(str)
341
- chunks, i, len = '', 0, str.size
342
- while len > 0x8000
343
- chunks << [ BC_STRING_CHUNK, 0x8000 ].pack('Cn') << print_string(str[i...(i += 0x8000)])
344
- len -= 0x8000
345
- end
346
-
347
- final = str[i..-1]
348
- chunks << if len <= STRING_DIRECT_MAX
349
- [ BC_STRING_DIRECT + len ].pack('C')
350
- elsif len <= STRING_SHORT_MAX
351
- [ BC_STRING_SHORT + (len >> 8), len ].pack('CC')
352
- else
353
- [ BC_STRING, len ].pack('Cn')
354
- end
355
-
356
- chunks << print_string(final)
357
- end
358
-
359
-
360
- def write_type_wrapped_array(arr, tstr, eletype, refs = {}, crefs = {}, trefs = {})
361
- len = arr.size
362
- return [ BC_LIST_DIRECT ].pack('C') << tstr if len == 0
363
-
364
- if len <= LIST_DIRECT_MAX # [x70-77] type value*
365
- str = [ BC_LIST_DIRECT + len ].pack('C') << tstr
366
- else # 'V' type int value*
367
- str = [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
368
- end
369
-
370
- case eletype
371
- when 'L'
372
- arr.each do |ele|
373
- str << write_long(Integer(ele))
374
- end
375
- when 'I'
376
- arr.each do |ele|
377
- str << write_int(Integer(ele))
378
- end
379
- when 'B'
380
- arr.each do |ele|
381
- str << write_binary(ele)
382
- end
383
- else
384
- arr.each do |ele|
385
- idx = refs[ele.object_id]
386
- if idx
387
- str << write_ref(idx)
388
- else
389
- refs[ele.object_id] = refs.size
390
- str << write_type_wrapped_object(ele, tstr, refs, crefs, trefs)
391
- end
392
- end
393
- end
394
-
395
- str
396
- end
397
-
398
-
399
- def write_type_wrapped_object(object, tstr, refs = {}, crefs = {}, trefs = {})
400
- return write_nil unless object
401
-
402
- str = [ BC_MAP ].pack('C') << tstr
403
-
404
- if object.is_a?(Hash)
405
- object.each do |k, v|
406
- str << write(k, refs, crefs, trefs)
407
- str << write(v, refs, crefs, trefs)
408
- end
409
- elsif object.instance_variable_get(:@attributes).is_a?(Hash)
410
- object.attributes.each do |k, v|
411
- str << write(k, refs, crefs, trefs)
412
- str << write(v, refs, crefs, trefs)
413
- end
414
- elsif object.is_a?(ClassWrapper)
415
- object.fields.each_with_index do |f, i|
416
- str << write(f, refs, crefs, trefs)
417
- str << write(object.values[i], refs, crefs, trefs)
418
- end
419
- elsif object.is_a?(TypeWrapper)
420
- object.object.each do |k, v|
421
- str << write(k, refs, crefs, trefs)
422
- str << write(v, refs, crefs, trefs)
423
- end
424
- else
425
- object.instance_variables.each do |var|
426
- str << write(var[1..-1], refs, crefs, trefs)
427
- str << write(object.instance_variable_get(var), refs, crefs, trefs)
428
- end
429
- end
430
-
431
- str << [ BC_END ].pack('C')
432
- end
433
-
434
-
435
- def write_type_wrapper(val, refs, crefs, trefs)
436
- return write_nil unless val.object
437
-
438
- idx = refs[val.object_id]
439
- return write_ref(idx) if idx
440
-
441
- refs[val.object_id] = refs.size
442
-
443
- type = val.is_multi? ? ('[' << val.hessian_type) : val.hessian_type
444
- if trefs.include?(type)
445
- tstr = write_int(trefs[type])
446
- else
447
- trefs[type] = trefs.size
448
- tstr = write_string(type)
449
- end
450
-
451
- if val.is_multi?
452
- write_type_wrapped_array(val.object, tstr, val.hessian_type, refs, crefs, trefs)
453
- else
454
- case val.hessian_type
455
- when 'L'
456
- write_long(Integer(val.object))
457
- when 'I'
458
- write_int(Integer(val.object))
459
- when 'B'
460
- write_binary(val.object)
461
- else
462
- write_type_wrapped_object(val.object, tstr, refs, crefs, trefs)
463
- end
464
- end
465
- end
466
-
467
-
468
- private
469
-
470
- def get_fields(object)
471
- fields = if object.is_a?(Hash)
472
- object.keys.map{|k| k.to_sym }
473
- elsif object.instance_variable_get(:@values).is_a?(Hash)
474
- object.values.keys
475
- elsif object.instance_variable_get(:@attributes).is_a?(Hash)
476
- object.attributes.keys
477
- else
478
- object.instance_variables.map{|k| k[1..-1].to_sym }
479
- end
480
-
481
- raise "fields should not be empty: #{object.inspect}" if fields.empty?
482
-
483
- fields
484
- end
485
-
486
-
487
- def write_values(object, cstr, fields, refs, crefs, trefs)
488
- return write_nil unless object
489
-
490
- vstr = if object.is_a?(Hash)
491
- fields.map{|f| write(object[f] || object[f.to_s], refs, crefs, trefs) }.join
492
- elsif object.instance_variable_get(:@values).is_a?(Hash)
493
- fields.map{|f| write(object.values[f], refs, crefs, trefs) }.join
494
- elsif object.instance_variable_get(:@attributes).is_a?(Hash)
495
- fields.map{|f| write(object.attributes[f], refs, crefs, trefs) }.join
496
- else
497
- fields.map{|f| write(object.instance_variable_get(f.to_s.prepend('@')), refs, crefs, trefs) }.join
498
- end
499
-
500
- cstr + vstr
501
- end
502
-
503
- end
504
- end
1
+ require 'hessian2/constants'
2
+ require 'bigdecimal'
3
+
4
+ module Hessian2
5
+ module Writer
6
+ include Constants
7
+
8
+ def call(method, args)
9
+ refs, crefs, trefs = {}, {}, {}
10
+ out = [ 'H', '2', '0', 'C' ].pack('ahha')
11
+ out << write_string(method)
12
+ out << write_int(args.size)
13
+ args.each{|arg| out << write(arg, refs, crefs, trefs) }
14
+
15
+ out
16
+ end
17
+
18
+
19
+ def reply(val)
20
+ [ 'H', '2', '0', 'R' ].pack('ahha') << write(val)
21
+ end
22
+
23
+
24
+ def write_fault(e)
25
+ val = {
26
+ code: e.class.to_s,
27
+ message: e.message,
28
+ detail: e.backtrace }
29
+ [ 'F' ].pack('a') << write_hash(val)
30
+ end
31
+
32
+
33
+ def write(val, refs = {}, crefs = {}, trefs = {})
34
+ case val
35
+ when StructWrapper # object to values-array
36
+ write_struct_wrapper(val, refs, crefs, trefs)
37
+ when ClassWrapper # class definition for statically typed languages
38
+ write_class_wrapper(val, refs, crefs, trefs)
39
+ when TypeWrapper
40
+ write_type_wrapper(val, refs, crefs, trefs)
41
+ when TrueClass
42
+ [ BC_TRUE ].pack('C')
43
+ when FalseClass
44
+ [ BC_FALSE ].pack('C')
45
+ when Time
46
+ if val.usec == 0 && val.sec == 0 # date in minutes
47
+ [ BC_DATE_MINUTE, val.to_i / 60 ].pack('CL>')
48
+ else
49
+ [ BC_DATE, val.to_i * 1000 + val.usec / 1000 ].pack('CQ>') # date
50
+ end
51
+ when Float, BigDecimal
52
+ write_float(val)
53
+ when Fixnum
54
+ write_int(val)
55
+ when Array
56
+ write_array(val, refs, crefs, trefs)
57
+ when Bignum
58
+ if val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
59
+ [ BC_LONG_INT, val ].pack('Cl>')
60
+ else # long
61
+ [ BC_LONG, val ].pack('Cq>')
62
+ end
63
+ when Integer
64
+ write_int(val)
65
+ when Hash
66
+ write_hash(val, refs, crefs, trefs)
67
+ when NilClass
68
+ write_nil
69
+ when String
70
+ write_string(val)
71
+ when Symbol
72
+ write_string(val.to_s)
73
+ else
74
+ write_object(val, refs, crefs, trefs)
75
+ end
76
+ end
77
+
78
+
79
+ def print_string(str)
80
+ return str.b if String.method_defined?(:b)
81
+
82
+ arr, i = Array.new(str.bytesize), 0
83
+ str.unpack('U*').each do |c|
84
+ if c < 0x80 # 0xxxxxxx
85
+ arr[i] = c
86
+ elsif c < 0x800 # 110xxxxx 10xxxxxx
87
+ arr[i] = 0xc0 + ((c >> 6) & 0x1f)
88
+ arr[i += 1] = 0x80 + (c & 0x3f)
89
+ else # 1110xxxx 10xxxxxx 10xxxxxx
90
+ arr[i] = 0xe0 + ((c >> 12) & 0xf)
91
+ arr[i += 1] = 0x80 + ((c >> 6) & 0x3f)
92
+ arr[i += 1] = 0x80 + (c & 0x3f)
93
+ end
94
+ i += 1
95
+ end
96
+
97
+ arr.pack('C*')
98
+ end
99
+
100
+
101
+ def write_array(arr, refs = {}, crefs = {}, trefs = {})
102
+ idx = refs[arr.object_id]
103
+ return write_ref(idx) if idx
104
+
105
+ refs[arr.object_id] = refs.size
106
+ len = arr.count
107
+ if len <= LIST_DIRECT_MAX # [x78-7f] value*
108
+ str = [ BC_LIST_DIRECT_UNTYPED + len ].pack('C')
109
+ else # x58 int value*
110
+ str = [ BC_LIST_FIXED_UNTYPED ].pack('C') << write_int(len)
111
+ end
112
+
113
+ arr.each do |ele|
114
+ str << write(ele, refs, crefs, trefs)
115
+ end
116
+
117
+ str
118
+ end
119
+
120
+
121
+ def write_binary(str)
122
+ chunks, i, len = [], 0, str.size
123
+ while len > 0x8000
124
+ chunks << [ BC_BINARY_CHUNK, 0x8000 ].pack('Cn') << str[i...(i += 0x8000)]
125
+ len -= 0x8000
126
+ end
127
+
128
+ final = str[i..-1]
129
+ if len <= BINARY_DIRECT_MAX
130
+ chunks << [ BC_BINARY_DIRECT + len ].pack('C') << final
131
+ elsif len <= BINARY_SHORT_MAX
132
+ chunks << [ BC_BINARY_SHORT + (len >> 8), len ].pack('CC') << final
133
+ else
134
+ chunks << [ BC_BINARY, len ].pack('Cn') << final
135
+ end
136
+
137
+ chunks.join
138
+ end
139
+
140
+
141
+ def write_class_wrapper(val, refs, crefs, trefs)
142
+ return write_nil unless val.values
143
+
144
+ idx = refs[val.object_id]
145
+ return write_ref(idx) if idx
146
+
147
+ refs[val.object_id] = refs.size
148
+
149
+ if val.is_multi?
150
+ type = '[' << val.klass
151
+ if trefs.include?(type)
152
+ tstr = write_int(trefs[type])
153
+ else
154
+ trefs[type] = trefs.size # store a type
155
+ tstr = write_string(type)
156
+ end
157
+ return [ BC_LIST_DIRECT ].pack('C') << tstr if val.values.size == 0
158
+ end
159
+
160
+ cref = crefs[val.klass]
161
+ if cref
162
+ cidx = cref.first
163
+ fields = cref.last
164
+ str = ''
165
+ else
166
+ fstr = val.fields.map{|f| write_string(f) }.join
167
+
168
+ str = [ BC_OBJECT_DEF ].pack('C') << write_string(val.klass) << write_int(val.fields.size) << fstr
169
+ cidx = crefs.size
170
+ crefs[val.klass] = [cidx, val.fields] # store a class definition
171
+ end
172
+
173
+ if cidx <= OBJECT_DIRECT_MAX
174
+ cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
175
+ else
176
+ cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
177
+ end
178
+
179
+ if val.is_multi?
180
+ len = val.values.size
181
+ if len <= LIST_DIRECT_MAX # [x70-77] type value*
182
+ str << [ BC_LIST_DIRECT + len ].pack('C') << tstr
183
+ else # 'V' type int value*
184
+ str << [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
185
+ end
186
+
187
+ val.values.each do |ele|
188
+ if ele
189
+ ele_idx = refs[ele.object_id]
190
+ if ele_idx
191
+ str << (cstr + write_ref(ele_idx))
192
+ else
193
+ refs[ele.object_id] = refs.size
194
+ str << (cstr + ele.map{|v| write(v, refs, crefs, trefs)}.join)
195
+ end
196
+ else
197
+ str << write_nil
198
+ end
199
+ end
200
+ else
201
+ str << (cstr + val.values.map{|v| write(v, refs, crefs, trefs)}.join)
202
+ end
203
+
204
+ str
205
+ end
206
+
207
+
208
+ def write_float(val)
209
+ case val.infinite?
210
+ when 1
211
+ return [ BC_DOUBLE, Float::INFINITY ].pack('CG')
212
+ when -1
213
+ return [ BC_DOUBLE, -Float::INFINITY ].pack('CG')
214
+ else
215
+ return [ BC_DOUBLE, Float::NAN ].pack('CG') if val.nan?
216
+ return [ BC_DOUBLE_ZERO ].pack('C') if val.zero? # double zero
217
+ return [ BC_DOUBLE_ONE ].pack('C') if val == 1 # double one
218
+
219
+ ival = val.to_i
220
+ if ival == val
221
+ return [ BC_DOUBLE_BYTE, ival ].pack('Cc') if ival >= -0x80 && ival <= 0x7f # double octet
222
+ return [ BC_DOUBLE_SHORT, (ival >> 8), ival ].pack('Ccc') if ival >= -0x8000 && ival <= 0x7fff # double short
223
+ end
224
+
225
+ mval = val * 1000
226
+ if mval.finite?
227
+ mills = mval.to_i
228
+ if mills >= -0x80_000_000 && mills <= 0x7f_fff_fff && 0.001 * mills == val
229
+ return [ BC_DOUBLE_MILL, mills ].pack('Cl>') # double mill
230
+ end
231
+ end
232
+
233
+ [ BC_DOUBLE, val ].pack('CG') # double
234
+ end
235
+ end
236
+
237
+
238
+ def write_hash(hash, refs = {}, crefs = {}, trefs = {})
239
+ idx = refs[hash.object_id]
240
+ return write_ref(idx) if idx
241
+
242
+ refs[hash.object_id] = refs.size
243
+ str = [ BC_MAP_UNTYPED ].pack('C')
244
+ hash.each do |k, v|
245
+ str << write(k, refs, crefs, trefs)
246
+ str << write(v, refs, crefs, trefs)
247
+ end
248
+
249
+ str << [ BC_END ].pack('C')
250
+ end
251
+
252
+
253
+ def write_int(val)
254
+ if val >= INT_DIRECT_MIN && val <= INT_DIRECT_MAX # single octet integers
255
+ [ BC_INT_ZERO + val ].pack('c')
256
+ elsif val >= INT_BYTE_MIN && val <= INT_BYTE_MAX # two octet integers
257
+ [ BC_INT_BYTE_ZERO + (val >> 8), val ].pack('cc')
258
+ elsif val >= INT_SHORT_MIN && val <= INT_SHORT_MAX # three octet integers
259
+ [ BC_INT_SHORT_ZERO + (val >> 16), (val >> 8), val].pack('ccc')
260
+ elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # integer
261
+ [ BC_INT, val ].pack('Cl>')
262
+ else
263
+ [ BC_LONG, val ].pack('Cq>')
264
+ end
265
+ end
266
+
267
+
268
+ def write_long(val)
269
+ if val >= LONG_DIRECT_MIN && val <= LONG_DIRECT_MAX # single octet longs
270
+ [ BC_LONG_ZERO + val ].pack('c')
271
+ elsif val >= LONG_BYTE_MIN && val <= LONG_BYTE_MAX # two octet longs
272
+ [ BC_LONG_BYTE_ZERO + (val >> 8), val ].pack('cc')
273
+ elsif val >= LONG_SHORT_MIN && val <= LONG_SHORT_MAX # three octet longs
274
+ [ BC_LONG_SHORT_ZERO + (val >> 16), (val >> 8), val ].pack('ccc')
275
+ elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
276
+ [ BC_LONG_INT, val ].pack('Cl>')
277
+ else
278
+ [ BC_LONG, val ].pack('Cq>')
279
+ end
280
+ end
281
+
282
+
283
+ def write_nil
284
+ [ BC_NULL ].pack('C')
285
+ end
286
+
287
+
288
+ def write_object(object, refs = {}, crefs = {}, trefs = {})
289
+ return write_nil unless object
290
+
291
+ return write_array(object, refs, crefs, trefs) if object.respond_to?(:count) && (object.respond_to?(:sql) || object.respond_to?(:to_sql))
292
+
293
+ idx = refs[object.object_id]
294
+ return write_ref(idx) if idx
295
+
296
+ refs[object.object_id] = refs.size
297
+
298
+ klass = object.class.to_s
299
+ cref = crefs[klass]
300
+ if cref
301
+ cidx = cref.first
302
+ fields = cref.last
303
+ str = ''
304
+ else
305
+ fields = get_fields(object)
306
+ fstr = fields.map{|f| write_string(f) }.join
307
+ cidx = crefs.size
308
+ crefs[klass] = [cidx, fields]
309
+
310
+ str = [ BC_OBJECT_DEF ].pack('C') << write_string(klass) << write_int(fields.size) << fstr
311
+ end
312
+
313
+ if cidx <= OBJECT_DIRECT_MAX
314
+ cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
315
+ else
316
+ cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
317
+ end
318
+
319
+ str << write_values(object, cstr, fields, refs, crefs, trefs)
320
+
321
+ str
322
+ end
323
+
324
+
325
+ def write_ref(val)
326
+ [ BC_REF ].pack('C') << write_int(val)
327
+ end
328
+
329
+
330
+ def write_struct_wrapper(val, refs, crefs, trefs)
331
+ return write_nil unless val.values
332
+
333
+ idx = refs[val.object_id]
334
+ return write_ref(idx) if idx
335
+
336
+ refs[val.object_id] = refs.size
337
+
338
+ write_array(val.values, refs, crefs, trefs)
339
+ end
340
+
341
+
342
+ def write_string(str)
343
+ chunks, i, len = '', 0, str.size
344
+ while len > 0x8000
345
+ chunks << [ BC_STRING_CHUNK, 0x8000 ].pack('Cn') << print_string(str[i...(i += 0x8000)])
346
+ len -= 0x8000
347
+ end
348
+
349
+ final = str[i..-1]
350
+ chunks << if len <= STRING_DIRECT_MAX
351
+ [ BC_STRING_DIRECT + len ].pack('C')
352
+ elsif len <= STRING_SHORT_MAX
353
+ [ BC_STRING_SHORT + (len >> 8), len ].pack('CC')
354
+ else
355
+ [ BC_STRING, len ].pack('Cn')
356
+ end
357
+
358
+ chunks << print_string(final)
359
+ end
360
+
361
+
362
+ def write_type_wrapped_array(arr, tstr, eletype, refs = {}, crefs = {}, trefs = {})
363
+ len = arr.size
364
+ return [ BC_LIST_DIRECT ].pack('C') << tstr if len == 0
365
+
366
+ if len <= LIST_DIRECT_MAX # [x70-77] type value*
367
+ str = [ BC_LIST_DIRECT + len ].pack('C') << tstr
368
+ else # 'V' type int value*
369
+ str = [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
370
+ end
371
+
372
+ case eletype
373
+ when 'L'
374
+ arr.each do |ele|
375
+ str << write_long(Integer(ele))
376
+ end
377
+ when 'I'
378
+ arr.each do |ele|
379
+ str << write_int(Integer(ele))
380
+ end
381
+ when 'B'
382
+ arr.each do |ele|
383
+ str << write_binary(ele)
384
+ end
385
+ else
386
+ arr.each do |ele|
387
+ idx = refs[ele.object_id]
388
+ if idx
389
+ str << write_ref(idx)
390
+ else
391
+ refs[ele.object_id] = refs.size
392
+ str << write_type_wrapped_object(ele, tstr, refs, crefs, trefs)
393
+ end
394
+ end
395
+ end
396
+
397
+ str
398
+ end
399
+
400
+
401
+ def write_type_wrapped_object(object, tstr, refs = {}, crefs = {}, trefs = {})
402
+ return write_nil unless object
403
+
404
+ str = [ BC_MAP ].pack('C') << tstr
405
+
406
+ if object.is_a?(Hash)
407
+ object.each do |k, v|
408
+ str << write(k, refs, crefs, trefs)
409
+ str << write(v, refs, crefs, trefs)
410
+ end
411
+ elsif object.instance_variable_get(:@attributes).is_a?(Hash)
412
+ object.attributes.each do |k, v|
413
+ str << write(k, refs, crefs, trefs)
414
+ str << write(v, refs, crefs, trefs)
415
+ end
416
+ elsif object.is_a?(ClassWrapper)
417
+ object.fields.each_with_index do |f, i|
418
+ str << write(f, refs, crefs, trefs)
419
+ str << write(object.values[i], refs, crefs, trefs)
420
+ end
421
+ elsif object.is_a?(TypeWrapper)
422
+ object.object.each do |k, v|
423
+ str << write(k, refs, crefs, trefs)
424
+ str << write(v, refs, crefs, trefs)
425
+ end
426
+ else
427
+ object.instance_variables.each do |var|
428
+ str << write(var[1..-1], refs, crefs, trefs)
429
+ str << write(object.instance_variable_get(var), refs, crefs, trefs)
430
+ end
431
+ end
432
+
433
+ str << [ BC_END ].pack('C')
434
+ end
435
+
436
+
437
+ def write_type_wrapper(val, refs, crefs, trefs)
438
+ return write_nil unless val.object
439
+
440
+ idx = refs[val.object_id]
441
+ return write_ref(idx) if idx
442
+
443
+ refs[val.object_id] = refs.size
444
+
445
+ type = val.is_multi? ? ('[' << val.hessian_type) : val.hessian_type
446
+ if trefs.include?(type)
447
+ tstr = write_int(trefs[type])
448
+ else
449
+ trefs[type] = trefs.size
450
+ tstr = write_string(type)
451
+ end
452
+
453
+ if val.is_multi?
454
+ write_type_wrapped_array(val.object, tstr, val.hessian_type, refs, crefs, trefs)
455
+ else
456
+ case val.hessian_type
457
+ when 'L'
458
+ write_long(Integer(val.object))
459
+ when 'I'
460
+ write_int(Integer(val.object))
461
+ when 'B'
462
+ write_binary(val.object)
463
+ else
464
+ write_type_wrapped_object(val.object, tstr, refs, crefs, trefs)
465
+ end
466
+ end
467
+ end
468
+
469
+
470
+ private
471
+
472
+ def get_fields(object)
473
+ fields = if object.is_a?(Hash)
474
+ object.keys.map{|k| k.to_sym }
475
+ elsif object.instance_variable_get(:@values).is_a?(Hash)
476
+ object.values.keys
477
+ elsif object.instance_variable_get(:@attributes).is_a?(Hash)
478
+ object.attributes.keys
479
+ else
480
+ object.instance_variables.map{|k| k[1..-1].to_sym }
481
+ end
482
+
483
+ raise "fields should not be empty: #{object.inspect}" if fields.empty?
484
+
485
+ fields
486
+ end
487
+
488
+
489
+ def write_values(object, cstr, fields, refs, crefs, trefs)
490
+ return write_nil unless object
491
+
492
+ vstr = if object.is_a?(Hash)
493
+ fields.map{|f| write(object[f] || object[f.to_s], refs, crefs, trefs) }.join
494
+ elsif object.instance_variable_get(:@values).is_a?(Hash)
495
+ fields.map{|f| write(object.values[f], refs, crefs, trefs) }.join
496
+ elsif object.instance_variable_get(:@attributes).is_a?(Hash)
497
+ fields.map{|f| write(object.attributes[f], refs, crefs, trefs) }.join
498
+ else
499
+ fields.map{|f| write(object.instance_variable_get(f.to_s.prepend('@')), refs, crefs, trefs) }.join
500
+ end
501
+
502
+ cstr + vstr
503
+ end
504
+
505
+ end
506
+ end