hprose 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
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