hessian2 2.0.2 → 2.0.3

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