hprose 1.4.1

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.
data/lib/hprose/io.rb ADDED
@@ -0,0 +1,929 @@
1
+ ############################################################
2
+ # #
3
+ # hprose #
4
+ # #
5
+ # Official WebSite: http://www.hprose.com/ #
6
+ # http://www.hprose.net/ #
7
+ # http://www.hprose.org/ #
8
+ # #
9
+ ############################################################
10
+
11
+ ############################################################
12
+ # #
13
+ # hprose/io.rb #
14
+ # #
15
+ # hprose io stream library for ruby #
16
+ # #
17
+ # LastModified: Mar 8, 2014 #
18
+ # Author: Ma Bingyao <andot@hprose.com> #
19
+ # #
20
+ ############################################################
21
+
22
+ require 'stringio'
23
+ require 'thread'
24
+ require 'uuidtools'
25
+ require 'hprose/common'
26
+
27
+ class String
28
+ def utf8?
29
+ return false if unpack('U*').find { |e| e > 0x10ffff } rescue return false
30
+ true
31
+ end
32
+ def ulength
33
+ (a = unpack('U*')) rescue return -1
34
+ return -1 if a.find { |e| e > 0x10ffff }
35
+ a.size + a.find_all { |e| e > 0xffff }.size
36
+ end
37
+ alias usize ulength
38
+ end
39
+
40
+ include UUIDTools
41
+
42
+ module Hprose
43
+ module Tags
44
+ # Serialize Tags
45
+ TagInteger = ?i.ord
46
+ TagLong = ?l.ord
47
+ TagDouble = ?d.ord
48
+ TagNull = ?n.ord
49
+ TagEmpty = ?e.ord
50
+ TagTrue = ?t.ord
51
+ TagFalse = ?f.ord
52
+ TagNaN = ?N.ord
53
+ TagInfinity = ?I.ord
54
+ TagDate = ?D.ord
55
+ TagTime = ?T.ord
56
+ TagUTC = ?Z.ord
57
+ TagBytes = ?b.ord
58
+ TagUTF8Char = ?u.ord
59
+ TagString = ?s.ord
60
+ TagGuid = ?g.ord
61
+ TagList = ?a.ord
62
+ TagMap = ?m.ord
63
+ TagClass = ?c.ord
64
+ TagObject = ?o.ord
65
+ TagRef = ?r.ord
66
+ # Serialize Marks
67
+ TagPos = ?+.ord
68
+ TagNeg = ?-.ord
69
+ TagSemicolon = ?;.ord
70
+ TagOpenbrace = ?{.ord
71
+ TagClosebrace = ?}.ord
72
+ TagQuote = ?".ord
73
+ TagPoint = ?..ord
74
+ # Protocol Tags
75
+ TagFunctions = ?F.ord
76
+ TagCall = ?C.ord
77
+ TagResult = ?R.ord
78
+ TagArgument = ?A.ord
79
+ TagError = ?E.ord
80
+ TagEnd = ?z.ord
81
+ # Number Tags
82
+ TagZero = ?0.ord
83
+ TagNine = ?9.ord
84
+ end # module Tags
85
+
86
+ module Stream
87
+ def readuntil(stream, char)
88
+ s = StringIO.new
89
+ while true do
90
+ c = stream.getbyte
91
+ break if c.nil? or (c == char)
92
+ s.putc(c)
93
+ end
94
+ result = s.string
95
+ s.close
96
+ return result
97
+ end
98
+ def readint(stream, char)
99
+ s = readuntil(stream, char)
100
+ return 0 if s == ''
101
+ return s.to_i
102
+ end
103
+ end # module Stream
104
+
105
+ class ClassManager
106
+ class << self
107
+ private
108
+ @@class_cache1 = {}
109
+ @@class_cache2 = {}
110
+ @@class_cache_lock = Mutex.new
111
+ def get_class(name)
112
+ name.split('.').inject(Object) {|x, y| x.const_get(y) } rescue return nil
113
+ end
114
+ def get_class2(name, ps, i, c)
115
+ if i < ps.size then
116
+ p = ps[i]
117
+ name[p] = c
118
+ cls = get_class2(name, ps, i + 1, '.')
119
+ if (i + 1 < ps.size) and (cls.nil?) then
120
+ cls = get_class2(name, ps, i + 1, '_')
121
+ end
122
+ return cls
123
+ else
124
+ return get_class(name)
125
+ end
126
+ end
127
+ def get_class_by_alias(name)
128
+ cls = nil
129
+ if cls.nil? then
130
+ ps = []
131
+ p = name.index('_')
132
+ while not p.nil?
133
+ ps.push(p)
134
+ p = name.index('_', p + 1)
135
+ end
136
+ cls = get_class2(name, ps, 0, '.')
137
+ if cls.nil? then
138
+ cls = get_class2(name, ps, 0, '_')
139
+ end
140
+ end
141
+ if cls.nil? then
142
+ return Object.const_set(name.to_sym, Class.new)
143
+ else
144
+ return cls
145
+ end
146
+ end
147
+ public
148
+ def register(cls, aliasname)
149
+ @@class_cache_lock.synchronize do
150
+ @@class_cache1[cls] = aliasname
151
+ @@class_cache2[aliasname] = cls
152
+ end
153
+ end
154
+
155
+ def getClass(aliasname)
156
+ return @@class_cache2[aliasname] if @@class_cache2.key?(aliasname)
157
+ cls = get_class_by_alias(aliasname)
158
+ register(cls, aliasname)
159
+ return cls
160
+ end
161
+
162
+ def getClassAlias(cls)
163
+ return @@class_cache1[cls] if @@class_cache1.key?(cls)
164
+ if cls == Struct then
165
+ aliasname = cls.to_s
166
+ aliasname['Struct::'] = '' unless aliasname['Struct::'].nil?
167
+ else
168
+ aliasname = cls.to_s.split('::').join('_')
169
+ end
170
+ register(cls, aliasname)
171
+ return aliasname
172
+ end
173
+ end
174
+ end
175
+
176
+ class RawReader
177
+ private
178
+ include Tags
179
+ include Stream
180
+ def read_number_raw(ostream)
181
+ ostream.write(readuntil(@stream, TagSemicolon))
182
+ ostream.putc(TagSemicolon)
183
+ end
184
+ def read_datetime_raw(ostream)
185
+ while true
186
+ c = @stream.getbyte
187
+ ostream.putc(c)
188
+ break if (c == TagSemicolon) or (c == TagUTC)
189
+ end
190
+ end
191
+ def read_utf8char_raw(ostream)
192
+ c = @stream.getbyte
193
+ ostream.putc(c)
194
+ if (c & 0xE0) == 0xC0 then
195
+ ostream.putc(@stream.getbyte())
196
+ elsif (c & 0xF0) == 0xE0 then
197
+ ostream.write(@stream.read(2))
198
+ elsif c > 0x7F then
199
+ raise Exception.exception('Bad utf-8 encoding')
200
+ end
201
+ end
202
+ def read_bytes_raw(ostream)
203
+ count = readuntil(@stream, TagQuote)
204
+ ostream.write(count)
205
+ ostream.putc(TagQuote)
206
+ count = ((count == '') ? 0 : count.to_i)
207
+ ostream.write(@stream.read(count + 1))
208
+ end
209
+ def read_string_raw(ostream)
210
+ count = readuntil(@stream, TagQuote)
211
+ ostream.write(count)
212
+ ostream.putc(TagQuote)
213
+ count = ((count == '') ? 0 : count.to_i)
214
+ i = 0
215
+ while i < count
216
+ c = @stream.getbyte
217
+ ostream.putc(c)
218
+ if (c & 0xE0) == 0xC0 then
219
+ ostream.putc(@stream.getbyte())
220
+ elsif (c & 0xF0) == 0xE0 then
221
+ ostream.write(@stream.read(2))
222
+ elsif (c & 0xF8) == 0xF0 then
223
+ ostream.write(@stream.read(3))
224
+ i += 1
225
+ end
226
+ i += 1
227
+ end
228
+ ostream.putc(@stream.getbyte())
229
+ end
230
+ def read_guid_raw(ostream)
231
+ ostream.write(@stream.read(38))
232
+ end
233
+ def read_complex_raw(ostream)
234
+ ostream.write(readuntil(@stream, TagOpenbrace))
235
+ ostream.write(TagOpenbrace)
236
+ tag = @stream.getbyte
237
+ while tag != TagClosebrace
238
+ read_raw(ostream, tag)
239
+ tag = @stream.getbyte
240
+ end
241
+ ostream.putc(tag)
242
+ end
243
+ public
244
+ def initialize(stream)
245
+ @stream = stream
246
+ end
247
+ attr_accessor :stream
248
+ def unexpected_tag(tag, expect_tag = nil)
249
+ if tag.nil? then
250
+ raise Exception.exception("No byte found in stream")
251
+ elsif expect_tag.nil? then
252
+ raise Exception.exception("Unexpected serialize tag '#{tag.chr}' in stream")
253
+ else
254
+ raise Exception.exception("Tag '#{expect_tag}' expected, but '#{tag.chr}' found in stream")
255
+ end
256
+ end
257
+ def read_raw(ostream = nil, tag = nil)
258
+ ostream = StringIO.new if ostream.nil?
259
+ tag = @stream.getbyte if tag.nil?
260
+ ostream.putc(tag)
261
+ case tag
262
+ when TagZero..TagNine, TagNull, TagEmpty, TagTrue, TagFalse, TagNaN then {}
263
+ when TagInfinity then ostream.putc(@stream.getbyte())
264
+ when TagInteger, TagLong, TagDouble, TagRef then read_number_raw(ostream)
265
+ when TagDate, TagTime then read_datetime_raw(ostream)
266
+ when TagUTF8Char then read_utf8char_raw(ostream)
267
+ when TagBytes then read_bytes_raw(ostream)
268
+ when TagString then read_string_raw(ostream)
269
+ when TagGuid then read_guid_raw(ostream)
270
+ when TagList, TagMap, TagObject then read_complex_raw(ostream)
271
+ when TagClass then read_complex_raw(ostream); read_raw(ostream)
272
+ when TagError then read_raw(ostream)
273
+ else unexpected_tag(tag)
274
+ end
275
+ return ostream
276
+ end
277
+ end # class RawReader
278
+
279
+ class Reader < RawReader
280
+ private
281
+ class FakeReaderRefer
282
+ def set(val)
283
+ end
284
+ def read(index)
285
+ raise Exception.exception("Unexpected serialize tag 'r' in stream")
286
+ end
287
+ def reset
288
+ end
289
+ end
290
+ class RealReaderRefer
291
+ def initialize()
292
+ @ref = []
293
+ end
294
+ def set(val)
295
+ @ref << val
296
+ end
297
+ def read(index)
298
+ @ref[index]
299
+ end
300
+ def reset
301
+ @ref.clear
302
+ end
303
+ end
304
+ def read_integer_without_tag
305
+ return readuntil(@stream, TagSemicolon).to_i
306
+ end
307
+ def read_long_without_tag
308
+ return readuntil(@stream, TagSemicolon).to_i
309
+ end
310
+ def read_double_without_tag
311
+ return readuntil(@stream, TagSemicolon).to_f
312
+ end
313
+ def read_infinity_without_tag
314
+ return (@stream.getbyte == TagPos) ? 1.0/0.0 : -1.0/0.0
315
+ end
316
+ def read_utf8char_without_tag
317
+ c = @stream.getbyte
318
+ sio = StringIO.new
319
+ sio.putc(c)
320
+ if ((c & 0xE0) == 0xC0) then
321
+ sio.putc(@stream.getbyte())
322
+ elsif ((c & 0xF0) == 0xE0) then
323
+ sio.write(@stream.read(2))
324
+ elsif c > 0x7F then
325
+ raise Exception.exception("Bad utf-8 encoding")
326
+ end
327
+ s = sio.string
328
+ sio.close
329
+ return s
330
+ end
331
+ def read_string_without_ref
332
+ sio = StringIO.new
333
+ count = readint(@stream, TagQuote)
334
+ i = 0
335
+ while i < count do
336
+ c = @stream.getbyte
337
+ sio.putc(c)
338
+ if ((c & 0xE0) == 0xC0) then
339
+ sio.putc(@stream.getbyte())
340
+ elsif ((c & 0xF0) == 0xE0) then
341
+ sio.write(@stream.read(2))
342
+ elsif ((c & 0xF8) == 0xF0) then
343
+ sio.write(@stream.read(3))
344
+ i += 1
345
+ end
346
+ i += 1
347
+ end
348
+ @stream.getbyte
349
+ s = sio.string
350
+ sio.close
351
+ return s
352
+ end
353
+ def read_class
354
+ cls = ClassManager.getClass(read_string_without_ref)
355
+ count = readint(@stream, TagOpenbrace)
356
+ fields = Array.new(count) { read_string }
357
+ @stream.getbyte
358
+ @classref << [cls, count, fields]
359
+ end
360
+ def read_usec
361
+ usec = 0
362
+ tag = @stream.getbyte
363
+ if tag == TagPoint then
364
+ usec = @stream.read(3).to_i * 1000
365
+ tag = @stream.getbyte
366
+ if (TagZero..TagNine) === tag then
367
+ usec = usec + (tag << @stream.read(2)).to_i
368
+ tag = @stream.getbyte
369
+ if (TagZero..TagNine) === tag then
370
+ @stream.read(2)
371
+ tag = @stream.getbyte
372
+ end
373
+ end
374
+ end
375
+ return tag, usec
376
+ end
377
+ def read_ref
378
+ return @refer.read(readint(@stream, TagSemicolon))
379
+ end
380
+ public
381
+ def initialize(stream, simple = false)
382
+ super(stream)
383
+ @classref = []
384
+ @refer = (simple ? FakeReaderRefer.new : RealReaderRefer.new)
385
+ end
386
+ def unserialize
387
+ tag = @stream.getbyte
388
+ return case tag
389
+ when TagZero..TagNine then tag - TagZero
390
+ when TagInteger then read_integer_without_tag
391
+ when TagLong then read_long_without_tag
392
+ when TagDouble then read_double_without_tag
393
+ when TagNull then nil
394
+ when TagEmpty then ""
395
+ when TagTrue then true
396
+ when TagFalse then false
397
+ when TagNaN then 0.0/0.0
398
+ when TagInfinity then read_infinity_without_tag
399
+ when TagDate then read_date_without_tag
400
+ when TagTime then read_time_without_tag
401
+ when TagBytes then read_bytes_without_tag
402
+ when TagUTF8Char then read_utf8char_without_tag
403
+ when TagString then read_string_without_tag
404
+ when TagGuid then read_guid_without_tag
405
+ when TagList then read_list_without_tag
406
+ when TagMap then read_map_without_tag
407
+ when TagClass then read_class; read_object_without_tag
408
+ when TagObject then read_object_without_tag
409
+ when TagRef then read_ref
410
+ when TagError then raise Exception.exception(read_string)
411
+ else unexpected_tag(tag)
412
+ end
413
+ end
414
+ def check_tag(expect_tag)
415
+ tag = @stream.getbyte
416
+ unexpected_tag(tag, expect_tag.chr) if tag != expect_tag
417
+ end
418
+ def check_tags(expect_tags)
419
+ tag = @stream.getbyte
420
+ unexpected_tag(tag, expect_tags.pack('c*')) unless expect_tags.include?(tag)
421
+ return tag
422
+ end
423
+ def read_integer
424
+ tag = @stream.getbyte
425
+ return case tag
426
+ when TagZero..TagNine then tag - TagZero
427
+ when TagInteger then read_integer_without_tag
428
+ else unexpected_tag(tag)
429
+ end
430
+ end
431
+ def read_long
432
+ tag = @stream.getbyte
433
+ return case tag
434
+ when TagZero..TagNine then tag - TagZero
435
+ when TagInteger, TagLong then read_long_without_tag
436
+ else unexpected_tag(tag)
437
+ end
438
+ end
439
+ def read_double
440
+ tag = @stream.getbyte
441
+ return case tag
442
+ when TagZero..TagNine then tag - TagZero
443
+ when TagInteger, TagLong, TagDouble then read_double_without_tag
444
+ when TagNaN then 0.0/0.0
445
+ when TagInfinity then read_infinity_without_tag
446
+ else unexpected_tag(tag)
447
+ end
448
+ end
449
+ def read_boolean
450
+ tag = check_tags([TagTrue, TagFalse])
451
+ return tag == TagTrue
452
+ end
453
+ def read_date_without_tag
454
+ year = @stream.read(4).to_i
455
+ month = @stream.read(2).to_i
456
+ day = @stream.read(2).to_i
457
+ tag = @stream.getbyte
458
+ if tag == TagTime then
459
+ hour = @stream.read(2).to_i
460
+ min = @stream.read(2).to_i
461
+ sec = @stream.read(2).to_i
462
+ tag, usec = read_usec
463
+ if tag == TagUTC then
464
+ date = Time.utc(year, month, day, hour, min, sec, usec)
465
+ else
466
+ date = Time.local(year, month, day, hour, min, sec, usec)
467
+ end
468
+ elsif tag == TagUTC then
469
+ date = Time.utc(year, month, day)
470
+ else
471
+ date = Time.local(year, month, day)
472
+ end
473
+ @refer.set(date)
474
+ return date
475
+ end
476
+ def read_date
477
+ tag = @stream.getbyte
478
+ return case tag
479
+ when TagNull then nil
480
+ when TagRef then read_ref
481
+ when TagDate then read_date_without_tag
482
+ else unexpected_tag(tag)
483
+ end
484
+ end
485
+ def read_time_without_tag
486
+ hour = @stream.read(2).to_i
487
+ min = @stream.read(2).to_i
488
+ sec = @stream.read(2).to_i
489
+ tag, usec = read_usec
490
+ if tag == TagUTC then
491
+ time = Time.utc(1970, 1, 1, hour, min, sec, usec)
492
+ else
493
+ time = Time.local(1970, 1, 1, hour, min, sec, usec)
494
+ end
495
+ @refer.set(time)
496
+ return time
497
+ end
498
+ def read_time
499
+ tag = @stream.getbyte
500
+ return case tag
501
+ when TagNull then nil
502
+ when TagRef then read_ref
503
+ when TagTime then read_time_without_tag
504
+ else unexpected_tag(tag)
505
+ end
506
+ end
507
+ def read_bytes_without_tag
508
+ bytes = @stream.read(readint(@stream, TagQuote))
509
+ @stream.getbyte
510
+ @refer.set(bytes)
511
+ return bytes
512
+ end
513
+ def read_bytes
514
+ tag = @stream.getbyte
515
+ return case tag
516
+ when TagNull then nil
517
+ when TagEmpty then ""
518
+ when TagRef then read_ref
519
+ when TagBytes then read_bytes_without_tag
520
+ else unexpected_tag(tag)
521
+ end
522
+ end
523
+ def read_string_without_tag
524
+ s = read_string_without_ref
525
+ @refer.set(s)
526
+ return s
527
+ end
528
+ def read_string
529
+ tag = @stream.getbyte
530
+ return case tag
531
+ when TagNull then nil
532
+ when TagEmpty then ""
533
+ when TagUTF8Char then read_utf8char_without_tag
534
+ when TagRef then read_ref
535
+ when TagString then read_string_without_tag
536
+ else unexpected_tag(tag)
537
+ end
538
+ end
539
+ def read_guid_without_tag
540
+ @stream.getbyte
541
+ guid = UUID.parse(@stream.read(36))
542
+ @stream.getbyte
543
+ @refer.set(guid)
544
+ return guid
545
+ end
546
+ def read_guid
547
+ tag = @stream.getbyte
548
+ return case tag
549
+ when TagNull then nil
550
+ when TagRef then read_ref
551
+ when TagGuid then read_guid_without_tag
552
+ else unexpected_tag(tag)
553
+ end
554
+ end
555
+ def read_list_without_tag
556
+ count = readint(@stream, TagOpenbrace)
557
+ list = Array.new(count)
558
+ @refer.set(list)
559
+ list.size.times do |i|
560
+ list[i] = unserialize
561
+ end
562
+ @stream.getbyte
563
+ return list
564
+ end
565
+ def read_list
566
+ tag = @stream.getbyte
567
+ return case tag
568
+ when TagNull then nil
569
+ when TagRef then read_ref
570
+ when TagList then read_list_without_tag
571
+ else unexpected_tag(tag)
572
+ end
573
+ end
574
+ def read_map_without_tag
575
+ map = {}
576
+ @refer.set(map)
577
+ readint(@stream, TagOpenbrace).times do
578
+ k = unserialize
579
+ v = unserialize
580
+ map[k] = v
581
+ end
582
+ @stream.getbyte
583
+ return map
584
+ end
585
+ def read_map
586
+ tag = @stream.getbyte
587
+ return case tag
588
+ when TagNull then nil
589
+ when TagRef then read_ref
590
+ when TagMap then read_map_without_tag
591
+ else unexpected_tag(tag)
592
+ end
593
+ end
594
+ def read_object_without_tag
595
+ cls, count, fields = @classref[readint(@stream, TagOpenbrace)]
596
+ obj = cls.new
597
+ @refer.set(obj)
598
+ vars = obj.instance_variables
599
+ count.times do |i|
600
+ key = fields[i]
601
+ var = '@' << key
602
+ value = unserialize
603
+ begin
604
+ obj[key] = value
605
+ rescue
606
+ unless vars.include?(var) then
607
+ cls.send(:attr_accessor, key)
608
+ cls.send(:public, key, key + '=')
609
+ end
610
+ obj.instance_variable_set(var.to_sym, value)
611
+ end
612
+ end
613
+ @stream.getbyte
614
+ return obj
615
+ end
616
+ def read_object
617
+ tag = @stream.getbyte
618
+ return case tag
619
+ when TagNull then nil
620
+ when TagRef then read_ref
621
+ when TagClass then read_class; read_object
622
+ when TagObject then read_object_without_tag
623
+ else unexpected_tag(tag)
624
+ end
625
+ end
626
+ def reset
627
+ @classref.clear
628
+ @refer.reset
629
+ end
630
+ end # class Reader
631
+
632
+ class Writer
633
+ private
634
+ include Tags
635
+ include Stream
636
+ class FakeWriterRefer
637
+ def set(val)
638
+ end
639
+ def write(stream, val)
640
+ false
641
+ end
642
+ def reset
643
+ end
644
+ end
645
+ class RealWriterRefer
646
+ include Tags
647
+ def initialize()
648
+ @ref = {}
649
+ @refcount = 0
650
+ end
651
+ def set(val)
652
+ @ref[val.object_id] = @refcount
653
+ @refcount += 1
654
+ end
655
+ def write(stream, val)
656
+ id = val.object_id
657
+ if @ref.key?(id) then
658
+ stream.putc(TagRef)
659
+ stream.write(@ref[id].to_s)
660
+ stream.putc(TagSemicolon)
661
+ return true
662
+ end
663
+ return false
664
+ end
665
+ def reset
666
+ @ref.clear
667
+ @refcount = 0
668
+ end
669
+ end
670
+ def write_class(classname, fields, vars)
671
+ count = fields.size
672
+ @stream.putc(TagClass)
673
+ @stream.write(classname.ulength.to_s)
674
+ @stream.putc(TagQuote)
675
+ @stream.write(classname)
676
+ @stream.putc(TagQuote)
677
+ @stream.write(count.to_s) if count > 0
678
+ @stream.putc(TagOpenbrace)
679
+ fields.each { |field| write_string(field) }
680
+ @stream.putc(TagClosebrace)
681
+ index = @fieldsref.size
682
+ @classref[classname] = index
683
+ @fieldsref << [fields, vars]
684
+ return index
685
+ end
686
+ def write_usec(usec)
687
+ if usec > 0 then
688
+ @stream.putc(TagPoint)
689
+ @stream.write(usec.div(1000).to_s.rjust(3, '0'))
690
+ @stream.write(usec.modulo(1000).to_s.rjust(3, '0')) if usec % 1000 > 0
691
+ end
692
+ end
693
+ protected
694
+ def write_ref(obj)
695
+ return @refer.write(@stream, obj)
696
+ end
697
+ public
698
+ def initialize(stream, simple = false)
699
+ @stream = stream
700
+ @classref = {}
701
+ @fieldsref = []
702
+ @refer = (simple ? FakeWriterRefer.new : RealWriterRefer.new)
703
+ end
704
+ attr_accessor :stream
705
+ def serialize(obj)
706
+ case obj
707
+ when NilClass then @stream.putc(TagNull)
708
+ when FalseClass then @stream.putc(TagFalse)
709
+ when TrueClass then @stream.putc(TagTrue)
710
+ when Fixnum then write_integer(obj)
711
+ when Bignum then write_long(obj)
712
+ when Float then write_double(obj)
713
+ when String then
714
+ len = obj.length
715
+ if len == 0 then
716
+ @stream.putc(TagEmpty)
717
+ elsif (len < 4) and (obj.ulength == 1) then
718
+ write_utf8char(obj)
719
+ elsif not write_ref(obj) then
720
+ if obj.utf8? then
721
+ write_string(obj)
722
+ else
723
+ write_bytes(obj)
724
+ end
725
+ end
726
+ when Symbol then write_string_with_ref(obj)
727
+ when UUID then write_guid_with_ref(obj)
728
+ when Time then write_date_with_ref(obj)
729
+ when Array, Range, MatchData then write_list_with_ref(obj)
730
+ when Hash then write_map_with_ref(obj)
731
+ when Binding, Class, Dir, Exception, IO, Numeric,
732
+ Method, Module, Proc, Regexp, Thread, ThreadGroup then
733
+ raise Exception.exception('This type is not supported to serialize')
734
+ else write_object_with_ref(obj)
735
+ end
736
+ end
737
+ def write_integer(integer)
738
+ if (0..9) === integer then
739
+ @stream.putc(integer.to_s)
740
+ else
741
+ @stream.putc((-2147483648..2147483647) === integer ? TagInteger : TagLong)
742
+ @stream.write(integer.to_s)
743
+ @stream.putc(TagSemicolon)
744
+ end
745
+ end
746
+ def write_long(long)
747
+ if (0..9) === long then
748
+ @stream.putc(long.to_s)
749
+ else
750
+ @stream.putc(TagLong)
751
+ @stream.write(long.to_s)
752
+ @stream.putc(TagSemicolon)
753
+ end
754
+ end
755
+ def write_double(double)
756
+ if double.nan? then
757
+ write_nan
758
+ elsif double.finite? then
759
+ @stream.putc(TagDouble)
760
+ @stream.write(double.to_s)
761
+ @stream.putc(TagSemicolon)
762
+ else
763
+ write_infinity(double > 0)
764
+ end
765
+ end
766
+ def write_nan
767
+ @stream.putc(TagNaN)
768
+ end
769
+ def write_infinity(positive = true)
770
+ @stream.putc(TagInfinity)
771
+ @stream.putc(positive ? TagPos : TagNeg)
772
+ end
773
+ def write_null
774
+ @stream.putc(TagNull)
775
+ end
776
+ def write_empty
777
+ @stream.putc(TagEmpty)
778
+ end
779
+ def write_boolean(bool)
780
+ @stream.putc(bool ? TagTrue : TagFalse)
781
+ end
782
+ def write_date(time)
783
+ @refer.set(time)
784
+ if time.hour == 0 and time.min == 0 and time.sec == 0 and time.usec == 0 then
785
+ @stream.putc(TagDate)
786
+ @stream.write(time.strftime('%Y%m%d'))
787
+ @stream.putc(time.utc? ? TagUTC : TagSemicolon)
788
+ elsif time.year == 1970 and time.mon == 1 and time.day == 1 then
789
+ @stream.putc(TagTime)
790
+ @stream.write(time.strftime('%H%M%S'))
791
+ write_usec(time.usec)
792
+ @stream.putc(time.utc? ? TagUTC : TagSemicolon)
793
+ else
794
+ @stream.putc(TagDate)
795
+ @stream.write(time.strftime('%Y%m%d' << TagTime << '%H%M%S'))
796
+ write_usec(time.usec)
797
+ @stream.putc(time.utc? ? TagUTC : TagSemicolon)
798
+ end
799
+ end
800
+ def write_date_with_ref(time)
801
+ write_date(time) unless write_ref(time)
802
+ end
803
+ alias write_time write_date
804
+ alias write_time_with_ref write_date_with_ref
805
+ def write_bytes(bytes)
806
+ @refer.set(bytes)
807
+ length = bytes.length
808
+ @stream.putc(TagBytes)
809
+ @stream.write(length.to_s) if length > 0
810
+ @stream.putc(TagQuote)
811
+ @stream.write(bytes)
812
+ @stream.putc(TagQuote)
813
+ end
814
+ def write_bytes_with_ref(bytes)
815
+ write_bytes(bytes) unless write_ref(bytes)
816
+ end
817
+ def write_utf8char(utf8char)
818
+ @stream.putc(TagUTF8Char)
819
+ @stream.write(utf8char)
820
+ end
821
+ def write_string(string)
822
+ @refer.set(string)
823
+ string = string.to_s
824
+ length = string.ulength
825
+ @stream.putc(TagString)
826
+ @stream.write(length.to_s) if length > 0
827
+ @stream.putc(TagQuote)
828
+ @stream.write(string)
829
+ @stream.putc(TagQuote)
830
+ end
831
+ def write_string_with_ref(string)
832
+ write_string(string) unless write_ref(string)
833
+ end
834
+ def write_guid(guid)
835
+ @refer.set(guid)
836
+ @stream.putc(TagGuid)
837
+ @stream.putc(TagOpenbrace)
838
+ @stream.write(guid.to_s)
839
+ @stream.putc(TagClosebrace)
840
+ end
841
+ def write_guid_with_ref(guid)
842
+ write_guid(guid) unless write_ref(guid)
843
+ end
844
+ def write_list(list)
845
+ @refer.set(list)
846
+ list = list.to_a
847
+ count = list.size
848
+ @stream.putc(TagList)
849
+ @stream.write(count.to_s) if count > 0
850
+ @stream.putc(TagOpenbrace)
851
+ count.times do |i|
852
+ serialize(list[i])
853
+ end
854
+ @stream.putc(TagClosebrace)
855
+ end
856
+ def write_list_with_ref(list)
857
+ write_list(list) unless write_ref(list)
858
+ end
859
+ def write_map(map)
860
+ @refer.set(map)
861
+ size = map.size
862
+ @stream.putc(TagMap)
863
+ @stream.write(size.to_s) if size > 0
864
+ @stream.putc(TagOpenbrace)
865
+ map.each do |key, value|
866
+ serialize(key)
867
+ serialize(value)
868
+ end
869
+ @stream.putc(TagClosebrace)
870
+ end
871
+ def write_map_with_ref(map)
872
+ write_map(map) unless write_ref(map)
873
+ end
874
+ def write_object(object)
875
+ classname = ClassManager.getClassAlias(object.class)
876
+ if @classref.key?(classname) then
877
+ index = @classref[classname]
878
+ fields, vars = @fieldsref[index]
879
+ else
880
+ if object.is_a?(Struct) then
881
+ vars = nil
882
+ fields = object.members
883
+ else
884
+ vars = object.instance_variables
885
+ fields = vars.map { |var| var.to_s.delete('@') }
886
+ end
887
+ index = write_class(classname, fields, vars)
888
+ end
889
+ @stream.putc(TagObject)
890
+ @stream.write(index.to_s)
891
+ @stream.putc(TagOpenbrace)
892
+ @refer.set(object)
893
+ if vars.nil? then
894
+ fields.each { |field| serialize(object[field]) }
895
+ else
896
+ vars.each { |var| serialize(object.instance_variable_get(var)) }
897
+ end
898
+ @stream.putc(TagClosebrace)
899
+ end
900
+ def write_object_with_ref(object)
901
+ write_object(object) unless write_ref(object)
902
+ end
903
+ def reset
904
+ @classref.clear
905
+ @fieldsref.clear
906
+ @refer.reset
907
+ end
908
+ end # class Writer
909
+
910
+ class Formatter
911
+ class << self
912
+ def serialize(variable, simple = false)
913
+ stream = StringIO.new
914
+ writer = Writer.new(stream, simple)
915
+ writer.serialize(variable)
916
+ s = stream.string
917
+ stream.close
918
+ return s
919
+ end
920
+ def unserialize(variable_representation, simple = false)
921
+ stream = StringIO.new(variable_representation, 'rb')
922
+ reader = Reader.new(stream, simple)
923
+ obj = reader.unserialize
924
+ stream.close
925
+ return obj
926
+ end
927
+ end # class
928
+ end # class Formatter
929
+ end # module Hprose