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/README.md +29 -0
- data/examples/client.rb +31 -0
- data/examples/server.rb +48 -0
- data/lib/hprose/client.rb +156 -0
- data/lib/hprose/common.rb +41 -0
- data/lib/hprose/httpclient.rb +141 -0
- data/lib/hprose/httpservice.rb +76 -0
- data/lib/hprose/io.rb +929 -0
- data/lib/hprose/service.rb +347 -0
- data/lib/hprose.rb +50 -0
- data/lib/hproseclient.rb +28 -0
- data/lib/hprosecommon.rb +30 -0
- data/lib/hproseio.rb +36 -0
- data/lib/hproseserver.rb +28 -0
- metadata +61 -0
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
|