em_hessian2 2.0.0

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