hessian2 2.0.5 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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