RocketAMF 0.0.5
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.rdoc +36 -0
- data/Rakefile +54 -0
- data/lib/rocketamf.rb +12 -0
- data/lib/rocketamf/class_mapping.rb +211 -0
- data/lib/rocketamf/common.rb +35 -0
- data/lib/rocketamf/constants.rb +47 -0
- data/lib/rocketamf/pure.rb +30 -0
- data/lib/rocketamf/pure/deserializer.rb +361 -0
- data/lib/rocketamf/pure/io_helpers.rb +94 -0
- data/lib/rocketamf/pure/remoting.rb +89 -0
- data/lib/rocketamf/pure/serializer.rb +327 -0
- data/lib/rocketamf/remoting.rb +133 -0
- data/lib/rocketamf/values/array_collection.rb +9 -0
- data/lib/rocketamf/values/messages.rb +133 -0
- data/lib/rocketamf/values/typed_hash.rb +13 -0
- data/lib/rocketamf/version.rb +9 -0
- data/spec/amf/class_mapping_set_spec.rb +34 -0
- data/spec/amf/class_mapping_spec.rb +120 -0
- data/spec/amf/deserializer_spec.rb +311 -0
- data/spec/amf/request_spec.rb +25 -0
- data/spec/amf/response_spec.rb +68 -0
- data/spec/amf/serializer_spec.rb +328 -0
- data/spec/amf/values/array_collection_spec.rb +6 -0
- data/spec/amf/values/messages_spec.rb +31 -0
- data/spec/fixtures/objects/amf0-boolean.bin +1 -0
- data/spec/fixtures/objects/amf0-date.bin +0 -0
- data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
- data/spec/fixtures/objects/amf0-hash.bin +0 -0
- data/spec/fixtures/objects/amf0-null.bin +1 -0
- data/spec/fixtures/objects/amf0-number.bin +0 -0
- data/spec/fixtures/objects/amf0-object.bin +0 -0
- data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
- data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
- data/spec/fixtures/objects/amf0-string.bin +0 -0
- data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
- data/spec/fixtures/objects/amf0-undefined.bin +1 -0
- data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
- data/spec/fixtures/objects/amf3-0.bin +0 -0
- data/spec/fixtures/objects/amf3-arrayRef.bin +1 -0
- data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
- data/spec/fixtures/objects/amf3-date.bin +0 -0
- data/spec/fixtures/objects/amf3-datesRef.bin +0 -0
- data/spec/fixtures/objects/amf3-dynObject.bin +2 -0
- data/spec/fixtures/objects/amf3-emptyArray.bin +1 -0
- data/spec/fixtures/objects/amf3-emptyArrayRef.bin +1 -0
- data/spec/fixtures/objects/amf3-emptyStringRef.bin +1 -0
- data/spec/fixtures/objects/amf3-false.bin +1 -0
- data/spec/fixtures/objects/amf3-graphMember.bin +0 -0
- data/spec/fixtures/objects/amf3-hash.bin +2 -0
- data/spec/fixtures/objects/amf3-largeMax.bin +0 -0
- data/spec/fixtures/objects/amf3-largeMin.bin +0 -0
- data/spec/fixtures/objects/amf3-max.bin +1 -0
- data/spec/fixtures/objects/amf3-min.bin +0 -0
- data/spec/fixtures/objects/amf3-mixedArray.bin +11 -0
- data/spec/fixtures/objects/amf3-null.bin +1 -0
- data/spec/fixtures/objects/amf3-objRef.bin +0 -0
- data/spec/fixtures/objects/amf3-primArray.bin +1 -0
- data/spec/fixtures/objects/amf3-string.bin +1 -0
- data/spec/fixtures/objects/amf3-stringRef.bin +0 -0
- data/spec/fixtures/objects/amf3-symbol.bin +1 -0
- data/spec/fixtures/objects/amf3-true.bin +1 -0
- data/spec/fixtures/objects/amf3-typedObject.bin +2 -0
- data/spec/fixtures/request/acknowledge-response.bin +0 -0
- data/spec/fixtures/request/amf0-error-response.bin +0 -0
- data/spec/fixtures/request/commandMessage.bin +0 -0
- data/spec/fixtures/request/remotingMessage.bin +0 -0
- data/spec/fixtures/request/simple-response.bin +0 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +32 -0
- metadata +133 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
module RocketAMF
|
2
|
+
module Pure
|
3
|
+
module ReadIOHelpers #:nodoc:
|
4
|
+
def read_int8 source
|
5
|
+
source.read(1).unpack('c').first
|
6
|
+
end
|
7
|
+
|
8
|
+
def read_word8 source
|
9
|
+
source.read(1).unpack('C').first
|
10
|
+
end
|
11
|
+
|
12
|
+
def read_double source
|
13
|
+
source.read(8).unpack('G').first
|
14
|
+
end
|
15
|
+
|
16
|
+
def read_word16_network source
|
17
|
+
source.read(2).unpack('n').first
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_int16_network source
|
21
|
+
str = source.read(2)
|
22
|
+
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
23
|
+
str.unpack('s').first
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_word32_network source
|
27
|
+
source.read(4).unpack('N').first
|
28
|
+
end
|
29
|
+
|
30
|
+
def byte_order
|
31
|
+
if [0x12345678].pack("L") == "\x12\x34\x56\x78"
|
32
|
+
:BigEndian
|
33
|
+
else
|
34
|
+
:LittleEndian
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def byte_order_little?
|
39
|
+
(byte_order == :LittleEndian) ? true : false;
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module WriteIOHelpers #:nodoc:
|
44
|
+
def pack_integer(integer)
|
45
|
+
integer = integer & 0x1fffffff
|
46
|
+
if(integer < 0x80)
|
47
|
+
[integer].pack('c')
|
48
|
+
elsif(integer < 0x4000)
|
49
|
+
[integer >> 7 & 0x7f | 0x80].pack('c')+
|
50
|
+
[integer & 0x7f].pack('c')
|
51
|
+
elsif(integer < 0x200000)
|
52
|
+
[integer >> 14 & 0x7f | 0x80].pack('c') +
|
53
|
+
[integer >> 7 & 0x7f | 0x80].pack('c') +
|
54
|
+
[integer & 0x7f].pack('c')
|
55
|
+
else
|
56
|
+
[integer >> 22 & 0x7f | 0x80].pack('c')+
|
57
|
+
[integer >> 15 & 0x7f | 0x80].pack('c')+
|
58
|
+
[integer >> 8 & 0x7f | 0x80].pack('c')+
|
59
|
+
[integer & 0xff].pack('c')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def pack_double(double)
|
64
|
+
[double].pack('G')
|
65
|
+
end
|
66
|
+
|
67
|
+
def pack_int8(val)
|
68
|
+
[val].pack('c')
|
69
|
+
end
|
70
|
+
|
71
|
+
def pack_int16_network(val)
|
72
|
+
[val].pack('n')
|
73
|
+
end
|
74
|
+
|
75
|
+
def pack_word32_network(val)
|
76
|
+
str = [val].pack('L')
|
77
|
+
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
78
|
+
str
|
79
|
+
end
|
80
|
+
|
81
|
+
def byte_order
|
82
|
+
if [0x12345678].pack("L") == "\x12\x34\x56\x78"
|
83
|
+
:BigEndian
|
84
|
+
else
|
85
|
+
:LittleEndian
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def byte_order_little?
|
90
|
+
(byte_order == :LittleEndian) ? true : false;
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rocketamf/pure/io_helpers'
|
2
|
+
|
3
|
+
module RocketAMF
|
4
|
+
module Pure
|
5
|
+
# Request deserialization module - provides a method that can be included into
|
6
|
+
# RocketAMF::Request for deserializing the given stream.
|
7
|
+
module Request
|
8
|
+
def populate_from_stream stream
|
9
|
+
stream = StringIO.new(stream) unless StringIO === stream
|
10
|
+
|
11
|
+
# Initialize
|
12
|
+
@amf_version = 0
|
13
|
+
@headers = []
|
14
|
+
@messages = []
|
15
|
+
|
16
|
+
# Read AMF version
|
17
|
+
@amf_version = read_word16_network stream
|
18
|
+
|
19
|
+
# Read in headers
|
20
|
+
header_count = read_word16_network stream
|
21
|
+
0.upto(header_count-1) do
|
22
|
+
name = stream.read(read_word16_network(stream))
|
23
|
+
must_understand = read_int8(stream) != 0
|
24
|
+
length = read_word32_network stream
|
25
|
+
data = RocketAMF.deserialize stream
|
26
|
+
@headers << RocketAMF::Header.new(name, must_understand, data)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Read in messages
|
30
|
+
message_count = read_word16_network stream
|
31
|
+
0.upto(message_count-1) do
|
32
|
+
target_uri = stream.read(read_word16_network(stream))
|
33
|
+
response_uri = stream.read(read_word16_network(stream))
|
34
|
+
length = read_word32_network stream
|
35
|
+
data = RocketAMF.deserialize stream
|
36
|
+
if data.is_a?(Array) && data.length == 1 && data[0].is_a?(::RocketAMF::Values::AbstractMessage)
|
37
|
+
data = data[0]
|
38
|
+
end
|
39
|
+
@messages << RocketAMF::Message.new(target_uri, response_uri, data)
|
40
|
+
end
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
include RocketAMF::Pure::ReadIOHelpers
|
47
|
+
end
|
48
|
+
|
49
|
+
# Response serialization module - provides a method that can be included into
|
50
|
+
# RocketAMF::Response for deserializing the given stream.
|
51
|
+
module Response
|
52
|
+
def serialize
|
53
|
+
stream = ""
|
54
|
+
|
55
|
+
# Write version
|
56
|
+
stream << pack_int16_network(@amf_version)
|
57
|
+
|
58
|
+
# Write headers
|
59
|
+
stream << pack_int16_network(@headers.length) # Header count
|
60
|
+
@headers.each do |h|
|
61
|
+
stream << pack_int16_network(h.name.length)
|
62
|
+
stream << h.name
|
63
|
+
stream << pack_int8(h.must_understand ? 1 : 0)
|
64
|
+
stream << pack_word32_network(-1)
|
65
|
+
stream << RocketAMF.serialize(h.data, 0)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Write messages
|
69
|
+
stream << pack_int16_network(@messages.length) # Message count
|
70
|
+
@messages.each do |m|
|
71
|
+
stream << pack_int16_network(m.target_uri.length)
|
72
|
+
stream << m.target_uri
|
73
|
+
|
74
|
+
stream << pack_int16_network(m.response_uri.length)
|
75
|
+
stream << m.response_uri
|
76
|
+
|
77
|
+
stream << pack_word32_network(-1)
|
78
|
+
stream << AMF0_AMF3_MARKER if @amf_version == 3
|
79
|
+
stream << RocketAMF.serialize(m.data, @amf_version)
|
80
|
+
end
|
81
|
+
|
82
|
+
stream
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
include RocketAMF::Pure::WriteIOHelpers
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,327 @@
|
|
1
|
+
require 'rocketamf/pure/io_helpers'
|
2
|
+
|
3
|
+
module RocketAMF
|
4
|
+
module Pure
|
5
|
+
# AMF0 implementation of serializer
|
6
|
+
class Serializer
|
7
|
+
def initialize
|
8
|
+
@ref_cache = SerializerCache.new :object
|
9
|
+
end
|
10
|
+
|
11
|
+
def version
|
12
|
+
0
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize obj, stream = ""
|
16
|
+
if obj.respond_to?(:to_amf)
|
17
|
+
stream << obj.to_amf(self)
|
18
|
+
elsif @ref_cache[obj] != nil
|
19
|
+
write_reference @ref_cache[obj], stream
|
20
|
+
elsif obj.is_a?(NilClass)
|
21
|
+
write_null stream
|
22
|
+
elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
|
23
|
+
write_boolean obj, stream
|
24
|
+
elsif obj.is_a?(Float) || obj.is_a?(Integer)
|
25
|
+
write_number obj, stream
|
26
|
+
elsif obj.is_a?(Symbol) || obj.is_a?(String)
|
27
|
+
write_string obj.to_s, stream
|
28
|
+
elsif obj.is_a?(Time)
|
29
|
+
write_date obj, stream
|
30
|
+
elsif obj.is_a?(Array)
|
31
|
+
write_array obj, stream
|
32
|
+
elsif obj.is_a?(Hash)
|
33
|
+
write_hash obj, stream
|
34
|
+
elsif obj.is_a?(Object)
|
35
|
+
write_object obj, stream
|
36
|
+
end
|
37
|
+
stream
|
38
|
+
end
|
39
|
+
|
40
|
+
def write_null stream
|
41
|
+
stream << AMF0_NULL_MARKER
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_boolean bool, stream
|
45
|
+
stream << AMF0_BOOLEAN_MARKER
|
46
|
+
stream << pack_int8(bool ? 1 : 0)
|
47
|
+
end
|
48
|
+
|
49
|
+
def write_number num, stream
|
50
|
+
stream << AMF0_NUMBER_MARKER
|
51
|
+
stream << pack_double(num)
|
52
|
+
end
|
53
|
+
|
54
|
+
def write_string str, stream
|
55
|
+
len = str.length
|
56
|
+
if len > 2**16-1
|
57
|
+
stream << AMF0_LONG_STRING_MARKER
|
58
|
+
stream << pack_word32_network(len)
|
59
|
+
else
|
60
|
+
stream << AMF0_STRING_MARKER
|
61
|
+
stream << pack_int16_network(len)
|
62
|
+
end
|
63
|
+
stream << str
|
64
|
+
end
|
65
|
+
|
66
|
+
def write_date date, stream
|
67
|
+
stream << AMF0_DATE_MARKER
|
68
|
+
|
69
|
+
date.utc unless date.utc?
|
70
|
+
seconds = (date.to_f * 1000).to_i
|
71
|
+
stream << pack_double(seconds)
|
72
|
+
|
73
|
+
stream << pack_int16_network(0)
|
74
|
+
end
|
75
|
+
|
76
|
+
def write_reference index, stream
|
77
|
+
stream << AMF0_REFERENCE_MARKER
|
78
|
+
stream << pack_int16_network(index)
|
79
|
+
end
|
80
|
+
|
81
|
+
def write_array array, stream
|
82
|
+
@ref_cache.add_obj array
|
83
|
+
stream << AMF0_STRICT_ARRAY_MARKER
|
84
|
+
stream << pack_word32_network(array.length)
|
85
|
+
array.each do |elem|
|
86
|
+
serialize elem, stream
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def write_hash hash, stream
|
91
|
+
@ref_cache.add_obj hash
|
92
|
+
stream << AMF0_HASH_MARKER
|
93
|
+
stream << pack_word32_network(hash.length)
|
94
|
+
write_prop_list hash, stream
|
95
|
+
end
|
96
|
+
|
97
|
+
def write_object obj, stream
|
98
|
+
@ref_cache.add_obj obj
|
99
|
+
|
100
|
+
# Is it a typed object?
|
101
|
+
class_name = ClassMapper.get_as_class_name obj
|
102
|
+
if class_name
|
103
|
+
stream << AMF0_TYPED_OBJECT_MARKER
|
104
|
+
stream << pack_int16_network(class_name.length)
|
105
|
+
stream << class_name
|
106
|
+
else
|
107
|
+
stream << AMF0_OBJECT_MARKER
|
108
|
+
end
|
109
|
+
|
110
|
+
write_prop_list obj, stream
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
include RocketAMF::Pure::WriteIOHelpers
|
115
|
+
def write_prop_list obj, stream
|
116
|
+
# Write prop list
|
117
|
+
props = ClassMapper.props_for_serialization obj
|
118
|
+
props.sort.each do |key, value| # Sort keys before writing
|
119
|
+
stream << pack_int16_network(key.length)
|
120
|
+
stream << key
|
121
|
+
serialize value, stream
|
122
|
+
end
|
123
|
+
|
124
|
+
# Write end
|
125
|
+
stream << pack_int16_network(0)
|
126
|
+
stream << AMF0_OBJECT_END_MARKER
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# AMF3 implementation of serializer
|
131
|
+
class AMF3Serializer
|
132
|
+
attr_reader :string_cache
|
133
|
+
|
134
|
+
def initialize
|
135
|
+
@string_cache = SerializerCache.new :string
|
136
|
+
@object_cache = SerializerCache.new :object
|
137
|
+
end
|
138
|
+
|
139
|
+
def version
|
140
|
+
3
|
141
|
+
end
|
142
|
+
|
143
|
+
def serialize obj, stream = ""
|
144
|
+
if obj.respond_to?(:to_amf)
|
145
|
+
stream << obj.to_amf(self)
|
146
|
+
elsif obj.is_a?(NilClass)
|
147
|
+
write_null stream
|
148
|
+
elsif obj.is_a?(TrueClass)
|
149
|
+
write_true stream
|
150
|
+
elsif obj.is_a?(FalseClass)
|
151
|
+
write_false stream
|
152
|
+
elsif obj.is_a?(Float)
|
153
|
+
write_float obj, stream
|
154
|
+
elsif obj.is_a?(Integer)
|
155
|
+
write_integer obj, stream
|
156
|
+
elsif obj.is_a?(Symbol) || obj.is_a?(String)
|
157
|
+
write_string obj.to_s, stream
|
158
|
+
elsif obj.is_a?(Time)
|
159
|
+
write_date obj, stream
|
160
|
+
elsif obj.is_a?(Array)
|
161
|
+
write_array obj, stream
|
162
|
+
elsif obj.is_a?(Hash) || obj.is_a?(Object)
|
163
|
+
write_object obj, stream
|
164
|
+
end
|
165
|
+
stream
|
166
|
+
end
|
167
|
+
|
168
|
+
def write_reference index, stream
|
169
|
+
header = index << 1 # shift value left to leave a low bit of 0
|
170
|
+
stream << pack_integer(header)
|
171
|
+
end
|
172
|
+
|
173
|
+
def write_null stream
|
174
|
+
stream << AMF3_NULL_MARKER
|
175
|
+
end
|
176
|
+
|
177
|
+
def write_true stream
|
178
|
+
stream << AMF3_TRUE_MARKER
|
179
|
+
end
|
180
|
+
|
181
|
+
def write_false stream
|
182
|
+
stream << AMF3_FALSE_MARKER
|
183
|
+
end
|
184
|
+
|
185
|
+
def write_integer int, stream
|
186
|
+
if int < MIN_INTEGER || int > MAX_INTEGER # Check valid range for 29 bits
|
187
|
+
write_float int.to_f, stream
|
188
|
+
else
|
189
|
+
stream << AMF3_INTEGER_MARKER
|
190
|
+
stream << pack_integer(int)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def write_float float, stream
|
195
|
+
stream << AMF3_DOUBLE_MARKER
|
196
|
+
stream << pack_double(float)
|
197
|
+
end
|
198
|
+
|
199
|
+
def write_string str, stream
|
200
|
+
stream << AMF3_STRING_MARKER
|
201
|
+
write_utf8_vr str, stream
|
202
|
+
end
|
203
|
+
|
204
|
+
def write_date date, stream
|
205
|
+
stream << AMF3_DATE_MARKER
|
206
|
+
if @object_cache[date] != nil
|
207
|
+
write_reference @object_cache[date], stream
|
208
|
+
else
|
209
|
+
# Cache date
|
210
|
+
@object_cache.add_obj date
|
211
|
+
|
212
|
+
# Build AMF string
|
213
|
+
date.utc unless date.utc?
|
214
|
+
seconds = (date.to_f * 1000).to_i
|
215
|
+
stream << pack_integer(AMF3_NULL_MARKER)
|
216
|
+
stream << pack_double(seconds)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def write_array array, stream
|
221
|
+
stream << AMF3_ARRAY_MARKER
|
222
|
+
if @object_cache[array] != nil
|
223
|
+
write_reference @object_cache[array], stream
|
224
|
+
else
|
225
|
+
# Cache array
|
226
|
+
@object_cache.add_obj array
|
227
|
+
|
228
|
+
# Build AMF string
|
229
|
+
header = array.length << 1 # make room for a low bit of 1
|
230
|
+
header = header | 1 # set the low bit to 1
|
231
|
+
stream << pack_integer(header)
|
232
|
+
stream << AMF3_CLOSE_DYNAMIC_ARRAY
|
233
|
+
array.each do |elem|
|
234
|
+
serialize elem, stream
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def write_object obj, stream
|
240
|
+
stream << AMF3_OBJECT_MARKER
|
241
|
+
if @object_cache[obj] != nil
|
242
|
+
write_reference @object_cache[obj], stream
|
243
|
+
else
|
244
|
+
# Cache object
|
245
|
+
@object_cache.add_obj obj
|
246
|
+
|
247
|
+
# Always serialize things as dynamic objects
|
248
|
+
stream << AMF3_DYNAMIC_OBJECT
|
249
|
+
|
250
|
+
# Write class name/anonymous
|
251
|
+
class_name = ClassMapper.get_as_class_name obj
|
252
|
+
if class_name
|
253
|
+
write_utf8_vr class_name, stream
|
254
|
+
else
|
255
|
+
stream << AMF3_ANONYMOUS_OBJECT
|
256
|
+
end
|
257
|
+
|
258
|
+
# Write out properties
|
259
|
+
props = ClassMapper.props_for_serialization obj
|
260
|
+
props.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
|
261
|
+
write_utf8_vr key.to_s, stream
|
262
|
+
serialize val, stream
|
263
|
+
end
|
264
|
+
|
265
|
+
# Write close
|
266
|
+
stream << AMF3_CLOSE_DYNAMIC_OBJECT
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
private
|
271
|
+
include RocketAMF::Pure::WriteIOHelpers
|
272
|
+
|
273
|
+
def write_utf8_vr str, stream
|
274
|
+
if str == ''
|
275
|
+
stream << AMF3_EMPTY_STRING
|
276
|
+
elsif @string_cache[str] != nil
|
277
|
+
write_reference @string_cache[str], stream
|
278
|
+
else
|
279
|
+
# Cache string
|
280
|
+
@string_cache.add_obj str
|
281
|
+
|
282
|
+
# Build AMF string
|
283
|
+
header = str.length << 1 # make room for a low bit of 1
|
284
|
+
header = header | 1 # set the low bit to 1
|
285
|
+
stream << pack_integer(header)
|
286
|
+
stream << str
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class SerializerCache #:nodoc:
|
292
|
+
def self.new type
|
293
|
+
if type == :string
|
294
|
+
StringCache.new
|
295
|
+
elsif type == :object
|
296
|
+
ObjectCache.new
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
class StringCache < Hash #:nodoc:
|
301
|
+
def initialize
|
302
|
+
@cache_index = 0
|
303
|
+
end
|
304
|
+
|
305
|
+
def add_obj str
|
306
|
+
self[str] = @cache_index
|
307
|
+
@cache_index += 1
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class ObjectCache < Hash #:nodoc:
|
312
|
+
def initialize
|
313
|
+
@cache_index = 0
|
314
|
+
end
|
315
|
+
|
316
|
+
def [] obj
|
317
|
+
super(obj.object_id)
|
318
|
+
end
|
319
|
+
|
320
|
+
def add_obj obj
|
321
|
+
self[obj.object_id] = @cache_index
|
322
|
+
@cache_index += 1
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|