RocketAMF-ouvrages 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/README.rdoc +47 -0
  2. data/Rakefile +59 -0
  3. data/RocketAMF.gemspec +20 -0
  4. data/benchmark.rb +74 -0
  5. data/ext/rocketamf_ext/class_mapping.c +484 -0
  6. data/ext/rocketamf_ext/constants.h +52 -0
  7. data/ext/rocketamf_ext/deserializer.c +770 -0
  8. data/ext/rocketamf_ext/deserializer.h +27 -0
  9. data/ext/rocketamf_ext/extconf.rb +18 -0
  10. data/ext/rocketamf_ext/remoting.c +184 -0
  11. data/ext/rocketamf_ext/rocketamf_ext.c +38 -0
  12. data/ext/rocketamf_ext/serializer.c +834 -0
  13. data/ext/rocketamf_ext/serializer.h +29 -0
  14. data/ext/rocketamf_ext/utility.h +4 -0
  15. data/lib/rocketamf.rb +216 -0
  16. data/lib/rocketamf/class_mapping.rb +237 -0
  17. data/lib/rocketamf/constants.rb +50 -0
  18. data/lib/rocketamf/ext.rb +28 -0
  19. data/lib/rocketamf/extensions.rb +22 -0
  20. data/lib/rocketamf/pure.rb +24 -0
  21. data/lib/rocketamf/pure/deserializer.rb +455 -0
  22. data/lib/rocketamf/pure/io_helpers.rb +94 -0
  23. data/lib/rocketamf/pure/remoting.rb +117 -0
  24. data/lib/rocketamf/pure/serializer.rb +474 -0
  25. data/lib/rocketamf/remoting.rb +196 -0
  26. data/lib/rocketamf/values/messages.rb +214 -0
  27. data/lib/rocketamf/values/typed_hash.rb +13 -0
  28. data/spec/class_mapping_spec.rb +110 -0
  29. data/spec/deserializer_spec.rb +449 -0
  30. data/spec/fast_class_mapping_spec.rb +144 -0
  31. data/spec/fixtures/objects/amf0-boolean.bin +1 -0
  32. data/spec/fixtures/objects/amf0-complex-encoded-string.bin +0 -0
  33. data/spec/fixtures/objects/amf0-date.bin +0 -0
  34. data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
  35. data/spec/fixtures/objects/amf0-hash.bin +0 -0
  36. data/spec/fixtures/objects/amf0-null.bin +1 -0
  37. data/spec/fixtures/objects/amf0-number.bin +0 -0
  38. data/spec/fixtures/objects/amf0-object.bin +0 -0
  39. data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
  40. data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
  41. data/spec/fixtures/objects/amf0-string.bin +0 -0
  42. data/spec/fixtures/objects/amf0-time.bin +0 -0
  43. data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
  44. data/spec/fixtures/objects/amf0-undefined.bin +1 -0
  45. data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
  46. data/spec/fixtures/objects/amf0-xml-doc.bin +0 -0
  47. data/spec/fixtures/objects/amf3-0.bin +0 -0
  48. data/spec/fixtures/objects/amf3-array-collection.bin +2 -0
  49. data/spec/fixtures/objects/amf3-array-ref.bin +1 -0
  50. data/spec/fixtures/objects/amf3-associative-array.bin +1 -0
  51. data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
  52. data/spec/fixtures/objects/amf3-byte-array-ref.bin +1 -0
  53. data/spec/fixtures/objects/amf3-byte-array.bin +0 -0
  54. data/spec/fixtures/objects/amf3-complex-array-collection.bin +6 -0
  55. data/spec/fixtures/objects/amf3-complex-encoded-string-array.bin +1 -0
  56. data/spec/fixtures/objects/amf3-date-ref.bin +0 -0
  57. data/spec/fixtures/objects/amf3-date.bin +0 -0
  58. data/spec/fixtures/objects/amf3-dictionary.bin +0 -0
  59. data/spec/fixtures/objects/amf3-dynamic-object.bin +2 -0
  60. data/spec/fixtures/objects/amf3-empty-array-ref.bin +1 -0
  61. data/spec/fixtures/objects/amf3-empty-array.bin +1 -0
  62. data/spec/fixtures/objects/amf3-empty-dictionary.bin +0 -0
  63. data/spec/fixtures/objects/amf3-empty-string-ref.bin +1 -0
  64. data/spec/fixtures/objects/amf3-encoded-string-ref.bin +0 -0
  65. data/spec/fixtures/objects/amf3-externalizable.bin +0 -0
  66. data/spec/fixtures/objects/amf3-false.bin +1 -0
  67. data/spec/fixtures/objects/amf3-float.bin +0 -0
  68. data/spec/fixtures/objects/amf3-graph-member.bin +0 -0
  69. data/spec/fixtures/objects/amf3-hash.bin +2 -0
  70. data/spec/fixtures/objects/amf3-large-max.bin +0 -0
  71. data/spec/fixtures/objects/amf3-large-min.bin +0 -0
  72. data/spec/fixtures/objects/amf3-max.bin +1 -0
  73. data/spec/fixtures/objects/amf3-min.bin +0 -0
  74. data/spec/fixtures/objects/amf3-mixed-array.bin +10 -0
  75. data/spec/fixtures/objects/amf3-null.bin +1 -0
  76. data/spec/fixtures/objects/amf3-object-ref.bin +0 -0
  77. data/spec/fixtures/objects/amf3-primitive-array.bin +1 -0
  78. data/spec/fixtures/objects/amf3-string-ref.bin +0 -0
  79. data/spec/fixtures/objects/amf3-string.bin +1 -0
  80. data/spec/fixtures/objects/amf3-symbol.bin +1 -0
  81. data/spec/fixtures/objects/amf3-trait-ref.bin +3 -0
  82. data/spec/fixtures/objects/amf3-true.bin +1 -0
  83. data/spec/fixtures/objects/amf3-typed-object.bin +2 -0
  84. data/spec/fixtures/objects/amf3-vector-double.bin +0 -0
  85. data/spec/fixtures/objects/amf3-vector-int.bin +0 -0
  86. data/spec/fixtures/objects/amf3-vector-object.bin +0 -0
  87. data/spec/fixtures/objects/amf3-vector-uint.bin +0 -0
  88. data/spec/fixtures/objects/amf3-xml-doc.bin +1 -0
  89. data/spec/fixtures/objects/amf3-xml-ref.bin +1 -0
  90. data/spec/fixtures/objects/amf3-xml.bin +1 -0
  91. data/spec/fixtures/request/acknowledge-response.bin +0 -0
  92. data/spec/fixtures/request/amf0-error-response.bin +0 -0
  93. data/spec/fixtures/request/blaze-response.bin +0 -0
  94. data/spec/fixtures/request/commandMessage.bin +0 -0
  95. data/spec/fixtures/request/flex-request.bin +0 -0
  96. data/spec/fixtures/request/multiple-simple-request.bin +0 -0
  97. data/spec/fixtures/request/remotingMessage.bin +0 -0
  98. data/spec/fixtures/request/simple-request.bin +0 -0
  99. data/spec/fixtures/request/simple-response.bin +0 -0
  100. data/spec/fixtures/request/unsupportedCommandMessage.bin +0 -0
  101. data/spec/messages_spec.rb +39 -0
  102. data/spec/remoting_spec.rb +196 -0
  103. data/spec/serializer_spec.rb +503 -0
  104. data/spec/spec_helper.rb +55 -0
  105. metadata +167 -0
@@ -0,0 +1,52 @@
1
+ // AMF0 Type Markers
2
+ #define AMF0_NUMBER_MARKER 0x00
3
+ #define AMF0_BOOLEAN_MARKER 0x01
4
+ #define AMF0_STRING_MARKER 0x02
5
+ #define AMF0_OBJECT_MARKER 0x03
6
+ #define AMF0_MOVIE_CLIP_MARKER 0x04
7
+ #define AMF0_NULL_MARKER 0x05
8
+ #define AMF0_UNDEFINED_MARKER 0x06
9
+ #define AMF0_REFERENCE_MARKER 0x07
10
+ #define AMF0_HASH_MARKER 0x08
11
+ #define AMF0_OBJECT_END_MARKER 0x09
12
+ #define AMF0_STRICT_ARRAY_MARKER 0x0A
13
+ #define AMF0_DATE_MARKER 0x0B
14
+ #define AMF0_LONG_STRING_MARKER 0x0C
15
+ #define AMF0_UNSUPPORTED_MARKER 0x0D
16
+ #define AMF0_RECORDSET_MARKER 0x0E
17
+ #define AMF0_XML_MARKER 0x0F
18
+ #define AMF0_TYPED_OBJECT_MARKER 0x10
19
+ #define AMF0_AMF3_MARKER 0x11
20
+
21
+ // AMF3 Type Markers
22
+ #define AMF3_UNDEFINED_MARKER 0x00
23
+ #define AMF3_NULL_MARKER 0x01
24
+ #define AMF3_FALSE_MARKER 0x02
25
+ #define AMF3_TRUE_MARKER 0x03
26
+ #define AMF3_INTEGER_MARKER 0x04
27
+ #define AMF3_DOUBLE_MARKER 0x05
28
+ #define AMF3_STRING_MARKER 0x06
29
+ #define AMF3_XML_DOC_MARKER 0x07
30
+ #define AMF3_DATE_MARKER 0x08
31
+ #define AMF3_ARRAY_MARKER 0x09
32
+ #define AMF3_OBJECT_MARKER 0x0A
33
+ #define AMF3_XML_MARKER 0x0B
34
+ #define AMF3_BYTE_ARRAY_MARKER 0x0C
35
+ #define AMF3_VECTOR_INT_MARKER 0x0D
36
+ #define AMF3_VECTOR_UINT_MARKER 0x0E
37
+ #define AMF3_VECTOR_DOUBLE_MARKER 0x0F
38
+ #define AMF3_VECTOR_OBJECT_MARKER 0x10
39
+ #define AMF3_DICT_MARKER 0x11
40
+
41
+ // Other AMF3 Markers
42
+ #define AMF3_EMPTY_STRING 0x01
43
+ #define AMF3_DYNAMIC_OBJECT 0x0B
44
+ #define AMF3_CLOSE_DYNAMIC_OBJECT 0x01
45
+ #define AMF3_CLOSE_DYNAMIC_ARRAY 0x01
46
+
47
+ // Other Constants
48
+ #define MAX_INTEGER 268435455
49
+ #define MIN_INTEGER -268435456
50
+ #define INITIAL_STREAM_LENGTH 128 // Initial buffer length for serializer output
51
+ #define MAX_STREAM_LENGTH 10*1024*1024 // Let's cap it at 10MB for now
52
+ #define MAX_ARRAY_PREALLOC 100000
@@ -0,0 +1,770 @@
1
+ #include "deserializer.h"
2
+ #include "constants.h"
3
+
4
+ #define DES_BOUNDS_CHECK(des, i) if(des->pos + (i) > des->size || des->pos + (i) < des->pos) rb_raise(rb_eRangeError, "reading %lu bytes is beyond end of source: %ld (pos), %ld (size)", (unsigned long)(i), des->pos, des->size);
5
+
6
+ extern VALUE mRocketAMF;
7
+ extern VALUE mRocketAMFExt;
8
+ extern VALUE cDeserializer;
9
+ extern VALUE cStringIO;
10
+ extern VALUE sym_class_name;
11
+ extern VALUE sym_members;
12
+ extern VALUE sym_externalizable;
13
+ extern VALUE sym_dynamic;
14
+ ID id_get_ruby_obj;
15
+ ID id_populate_ruby_obj;
16
+
17
+ static VALUE des0_deserialize(VALUE self, char type);
18
+ static VALUE des3_deserialize(VALUE self);
19
+
20
+ char des_read_byte(AMF_DESERIALIZER *des) {
21
+ DES_BOUNDS_CHECK(des, 1);
22
+ des->pos++;
23
+ return des->stream[des->pos-1];
24
+ }
25
+
26
+ int des_read_uint16(AMF_DESERIALIZER *des) {
27
+ DES_BOUNDS_CHECK(des, 2);
28
+ const unsigned char *str = (unsigned char*)(des->stream) + des->pos;
29
+ des->pos += 2;
30
+ return ((str[0] << 8) | str[1]);
31
+ }
32
+
33
+ unsigned int des_read_uint32(AMF_DESERIALIZER *des) {
34
+ DES_BOUNDS_CHECK(des, 4);
35
+ const unsigned char *str = (unsigned char*)(des->stream) + des->pos;
36
+ des->pos += 4;
37
+ return ((str[0] << 24) | (str[1] << 16) | (str[2] << 8) | str[3]);
38
+ }
39
+
40
+ /*
41
+ * Read a network double
42
+ */
43
+ double des_read_double(AMF_DESERIALIZER *des) {
44
+ DES_BOUNDS_CHECK(des, 8);
45
+ union aligned {
46
+ double dval;
47
+ char cval[8];
48
+ } d;
49
+ const char *str = des->stream + des->pos;
50
+ des->pos +=8;
51
+
52
+ #ifdef WORDS_BIGENDIAN
53
+ memcpy(d.cval, str, 8);
54
+ #else
55
+ d.cval[0] = str[7];
56
+ d.cval[1] = str[6];
57
+ d.cval[2] = str[5];
58
+ d.cval[3] = str[4];
59
+ d.cval[4] = str[3];
60
+ d.cval[5] = str[2];
61
+ d.cval[6] = str[1];
62
+ d.cval[7] = str[0];
63
+ #endif
64
+ return d.dval;
65
+ }
66
+
67
+ /*
68
+ * Read an AMF3 style integer
69
+ */
70
+ int des_read_int(AMF_DESERIALIZER *des) {
71
+ int result = 0, byte_cnt = 0;
72
+ DES_BOUNDS_CHECK(des, 1);
73
+ unsigned char byte = des->stream[des->pos++];
74
+
75
+ while(byte & 0x80 && byte_cnt < 3) {
76
+ result <<= 7;
77
+ result |= byte & 0x7f;
78
+ DES_BOUNDS_CHECK(des, 1);
79
+ byte = des->stream[des->pos++];
80
+ byte_cnt++;
81
+ }
82
+
83
+ if (byte_cnt < 3) {
84
+ result <<= 7;
85
+ result |= byte & 0x7F;
86
+ } else {
87
+ result <<= 8;
88
+ result |= byte & 0xff;
89
+ }
90
+
91
+ if (result & 0x10000000) {
92
+ result -= 0x20000000;
93
+ }
94
+
95
+ return result;
96
+ }
97
+
98
+ /*
99
+ * Read a string and then force the encoding to UTF 8 if running ruby 1.9
100
+ */
101
+ VALUE des_read_string(AMF_DESERIALIZER *des, unsigned int len) {
102
+ DES_BOUNDS_CHECK(des, len);
103
+ VALUE str = rb_str_new(des->stream + des->pos, len);
104
+ #ifdef HAVE_RB_STR_ENCODE
105
+ rb_encoding *utf8 = rb_utf8_encoding();
106
+ rb_enc_associate(str, utf8);
107
+ ENC_CODERANGE_CLEAR(str);
108
+ #endif
109
+ des->pos += len;
110
+ return str;
111
+ }
112
+
113
+ /*
114
+ * Set the source of the amf reader to a StringIO object, creating a new one to
115
+ * wrap the source if it's only a string
116
+ */
117
+ void des_set_src(AMF_DESERIALIZER *des, VALUE src) {
118
+ VALUE klass = CLASS_OF(src);
119
+ if(klass == cStringIO) {
120
+ VALUE str = rb_funcall(src, rb_intern("string"), 0);
121
+ des->src = src;
122
+ des->stream = RSTRING_PTR(str);
123
+ des->pos = NUM2LONG(rb_funcall(src, rb_intern("pos"), 0));
124
+ des->size = RSTRING_LEN(str);
125
+ } else if(klass == rb_cString) {
126
+ VALUE args[1] = {src};
127
+ des->src = rb_class_new_instance(1, args, cStringIO);
128
+ des->stream = RSTRING_PTR(src);
129
+ des->pos = 0;
130
+ des->size = RSTRING_LEN(src);
131
+ } else {
132
+ rb_raise(rb_eArgError, "Invalid source type to deserialize from");
133
+ }
134
+
135
+ if(des->pos >= des->size) rb_raise(rb_eRangeError, "already at the end of the source");
136
+ }
137
+
138
+ /*
139
+ * Create AMF3 deserializer and copy source data over to it, before calling
140
+ * AMF3 internal deserialize function
141
+ */
142
+ static VALUE des0_read_amf3(VALUE self) {
143
+ AMF_DESERIALIZER *des;
144
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
145
+ des->version = 3;
146
+ des->str_cache = rb_ary_new();
147
+ des->trait_cache = rb_ary_new();
148
+ return des3_deserialize(self);
149
+ }
150
+
151
+ /*
152
+ * Reads an AMF0 hash
153
+ */
154
+ static void des0_read_props(VALUE self, VALUE hash) {
155
+ AMF_DESERIALIZER *des;
156
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
157
+
158
+ while(1) {
159
+ int len = des_read_uint16(des);
160
+ if(len == 0) {
161
+ des_read_byte(des); // Read type byte
162
+ return;
163
+ } else {
164
+ VALUE key = des_read_string(des, len);
165
+ char type = des_read_byte(des);
166
+ rb_hash_aset(hash, key, des0_deserialize(self, type));
167
+ }
168
+ }
169
+ }
170
+
171
+ static VALUE des0_read_object(VALUE self) {
172
+ AMF_DESERIALIZER *des;
173
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
174
+
175
+ // Create object and add to cache
176
+ VALUE obj = rb_funcall(des->class_mapper, id_get_ruby_obj, 1, rb_str_new(NULL, 0));
177
+ rb_ary_push(des->obj_cache, obj);
178
+
179
+ // Populate object
180
+ VALUE props = rb_hash_new();
181
+ des0_read_props(self, props);
182
+ rb_funcall(des->class_mapper, id_populate_ruby_obj, 2, obj, props);
183
+
184
+ return obj;
185
+ }
186
+
187
+ static VALUE des0_read_typed_object(VALUE self) {
188
+ AMF_DESERIALIZER *des;
189
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
190
+
191
+ // Create object and add to cache
192
+ VALUE class_name = des_read_string(des, des_read_uint16(des));
193
+ VALUE obj = rb_funcall(des->class_mapper, id_get_ruby_obj, 1, class_name);
194
+ rb_ary_push(des->obj_cache, obj);
195
+
196
+ // Populate object
197
+ VALUE props = rb_hash_new();
198
+ des0_read_props(self, props);
199
+ rb_funcall(des->class_mapper, id_populate_ruby_obj, 2, obj, props);
200
+
201
+ return obj;
202
+ }
203
+
204
+ static VALUE des0_read_hash(VALUE self) {
205
+ AMF_DESERIALIZER *des;
206
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
207
+ des_read_uint32(des); // Hash size, but there's no optimization I can perform with this
208
+ VALUE obj = rb_hash_new();
209
+ rb_ary_push(des->obj_cache, obj);
210
+ des0_read_props(self, obj);
211
+ return obj;
212
+ }
213
+
214
+ static VALUE des0_read_array(VALUE self) {
215
+ AMF_DESERIALIZER *des;
216
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
217
+
218
+ // Limit size of pre-allocation to force remote user to actually send data,
219
+ // rather than just sending a size of 2**32-1 and nothing afterwards to
220
+ // crash the server
221
+ unsigned int len = des_read_uint32(des);
222
+ VALUE ary = rb_ary_new2(len < MAX_ARRAY_PREALLOC ? len : MAX_ARRAY_PREALLOC);
223
+ rb_ary_push(des->obj_cache, ary);
224
+
225
+ unsigned int i;
226
+ for(i = 0; i < len; i++) {
227
+ rb_ary_push(ary, des0_deserialize(self, des_read_byte(des)));
228
+ }
229
+
230
+ return ary;
231
+ }
232
+
233
+ static VALUE des0_read_time(VALUE self) {
234
+ AMF_DESERIALIZER *des;
235
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
236
+ double milli = des_read_double(des);
237
+ des_read_uint16(des); // Timezone - unused
238
+ time_t sec = milli/1000.0;
239
+ time_t micro = (milli-sec*1000)*1000;
240
+ return rb_time_new(sec, micro);
241
+ }
242
+
243
+ /*
244
+ * Internal C deserialize call. Takes deserializer and a char for the type
245
+ * marker.
246
+ */
247
+ static VALUE des0_deserialize(VALUE self, char type) {
248
+ AMF_DESERIALIZER *des;
249
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
250
+
251
+ long tmp;
252
+ VALUE ret = Qnil;
253
+ switch(type) {
254
+ case AMF0_STRING_MARKER:
255
+ ret = des_read_string(des, des_read_uint16(des));
256
+ break;
257
+ case AMF0_AMF3_MARKER:
258
+ ret = des0_read_amf3(self);
259
+ break;
260
+ case AMF0_NUMBER_MARKER:
261
+ ret = rb_float_new(des_read_double(des));
262
+ break;
263
+ case AMF0_BOOLEAN_MARKER:
264
+ ret = des_read_byte(des) == 0 ? Qfalse : Qtrue;
265
+ break;
266
+ case AMF0_NULL_MARKER:
267
+ case AMF0_UNDEFINED_MARKER:
268
+ case AMF0_UNSUPPORTED_MARKER:
269
+ ret = Qnil;
270
+ break;
271
+ case AMF0_OBJECT_MARKER:
272
+ ret = des0_read_object(self);
273
+ break;
274
+ case AMF0_TYPED_OBJECT_MARKER:
275
+ ret = des0_read_typed_object(self);
276
+ break;
277
+ case AMF0_HASH_MARKER:
278
+ ret = des0_read_hash(self);
279
+ break;
280
+ case AMF0_STRICT_ARRAY_MARKER:
281
+ ret = des0_read_array(self);
282
+ break;
283
+ case AMF0_REFERENCE_MARKER:
284
+ tmp = des_read_uint16(des);
285
+ if(tmp >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "reference index beyond end");
286
+ ret = RARRAY_PTR(des->obj_cache)[tmp];
287
+ break;
288
+ case AMF0_DATE_MARKER:
289
+ ret = des0_read_time(self);
290
+ break;
291
+ case AMF0_XML_MARKER:
292
+ case AMF0_LONG_STRING_MARKER:
293
+ ret = des_read_string(des, des_read_uint32(des));
294
+ break;
295
+ default:
296
+ rb_raise(rb_eRuntimeError, "Not supported: %d", type);
297
+ break;
298
+ }
299
+
300
+ return ret;
301
+ }
302
+
303
+ static VALUE des3_read_string(AMF_DESERIALIZER *des) {
304
+ int header = des_read_int(des);
305
+ if((header & 1) == 0) {
306
+ header >>= 1;
307
+ if(header >= RARRAY_LEN(des->str_cache)) rb_raise(rb_eRangeError, "str reference index beyond end");
308
+ return RARRAY_PTR(des->str_cache)[header];
309
+ } else {
310
+ VALUE str = des_read_string(des, header >> 1);
311
+ if(RSTRING_LEN(str) > 0) rb_ary_push(des->str_cache, str);
312
+ return str;
313
+ }
314
+ }
315
+
316
+ /*
317
+ * Same as des3_read_string, but XML uses the object cache, rather than the
318
+ * string cache
319
+ */
320
+ static VALUE des3_read_xml(VALUE self) {
321
+ AMF_DESERIALIZER *des;
322
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
323
+
324
+ int header = des_read_int(des);
325
+ if((header & 1) == 0) {
326
+ header >>= 1;
327
+ if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
328
+ return RARRAY_PTR(des->obj_cache)[header];
329
+ } else {
330
+ VALUE str = des_read_string(des, header >> 1);
331
+ if(RSTRING_LEN(str) > 0) rb_ary_push(des->obj_cache, str);
332
+ return str;
333
+ }
334
+ }
335
+
336
+ static VALUE des3_read_object(VALUE self) {
337
+ AMF_DESERIALIZER *des;
338
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
339
+
340
+ int header = des_read_int(des);
341
+ if((header & 1) == 0) {
342
+ header >>= 1;
343
+ if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
344
+ return RARRAY_PTR(des->obj_cache)[header];
345
+ } else {
346
+ VALUE externalizable, dynamic, members, class_name, traits;
347
+ long i, members_len;
348
+
349
+ // Parse traits
350
+ header >>= 1;
351
+ if((header & 1) == 0) {
352
+ header >>= 1;
353
+ if(header >= RARRAY_LEN(des->trait_cache)) rb_raise(rb_eRangeError, "trait reference index beyond end");
354
+ traits = RARRAY_PTR(des->trait_cache)[header];
355
+ externalizable = rb_hash_aref(traits, sym_externalizable);
356
+ dynamic = rb_hash_aref(traits, sym_dynamic);
357
+ members = rb_hash_aref(traits, sym_members);
358
+ members_len = members == Qnil ? 0 : RARRAY_LEN(members);
359
+ class_name = rb_hash_aref(traits, sym_class_name);
360
+ } else {
361
+ externalizable = (header & 2) != 0 ? Qtrue : Qfalse;
362
+ dynamic = (header & 4) != 0 ? Qtrue : Qfalse;
363
+ members_len = header >> 3;
364
+ class_name = des3_read_string(des);
365
+
366
+ members = rb_ary_new2(members_len);
367
+ for(i = 0; i < members_len; i++) rb_ary_push(members, des3_read_string(des));
368
+
369
+ traits = rb_hash_new();
370
+ rb_hash_aset(traits, sym_externalizable, externalizable);
371
+ rb_hash_aset(traits, sym_dynamic, dynamic);
372
+ rb_hash_aset(traits, sym_members, members);
373
+ rb_hash_aset(traits, sym_class_name, class_name);
374
+ rb_ary_push(des->trait_cache, traits);
375
+ }
376
+
377
+ // Optimization for deserializing ArrayCollection
378
+ if(strcmp(RSTRING_PTR(class_name), "flex.messaging.io.ArrayCollection") == 0) {
379
+ VALUE arr = des3_deserialize(self); // Adds ArrayCollection array to object cache automatically
380
+ rb_ary_push(des->obj_cache, arr); // Add again for ArrayCollection source array
381
+ return arr;
382
+ }
383
+
384
+ VALUE obj = rb_funcall(des->class_mapper, id_get_ruby_obj, 1, class_name);
385
+ rb_ary_push(des->obj_cache, obj);
386
+
387
+ if(externalizable == Qtrue) {
388
+ rb_funcall(des->src, rb_intern("pos="), 1, LONG2NUM(des->pos)); // Update source StringIO pos
389
+ rb_funcall(obj, rb_intern("read_external"), 1, self);
390
+ des->pos = NUM2LONG(rb_funcall(des->src, rb_intern("pos"), 0)); // Update from source
391
+ return obj;
392
+ }
393
+
394
+ VALUE props = rb_hash_new();
395
+ for(i = 0; i < members_len; i++) {
396
+ rb_hash_aset(props, RARRAY_PTR(members)[i], des3_deserialize(self));
397
+ }
398
+
399
+ VALUE dynamic_props = Qnil;
400
+ if(dynamic == Qtrue) {
401
+ dynamic_props = rb_hash_new();
402
+ while(1) {
403
+ VALUE key = des3_read_string(des);
404
+ if(RSTRING_LEN(key) == 0) break;
405
+ rb_hash_aset(dynamic_props, key, des3_deserialize(self));
406
+ }
407
+ }
408
+
409
+ rb_funcall(des->class_mapper, id_populate_ruby_obj, 3, obj, props, dynamic_props);
410
+
411
+ return obj;
412
+ }
413
+ }
414
+
415
+ static VALUE des3_read_array(VALUE self) {
416
+ AMF_DESERIALIZER *des;
417
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
418
+
419
+ int i;
420
+ int header = des_read_int(des);
421
+ if((header & 1) == 0) {
422
+ header >>= 1;
423
+ if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
424
+ return RARRAY_PTR(des->obj_cache)[header];
425
+ } else {
426
+ header >>= 1;
427
+ VALUE obj;
428
+ VALUE key = des3_read_string(des);
429
+ if(key == Qnil) rb_raise(rb_eRangeError, "key is Qnil");
430
+ if(RSTRING_LEN(key) != 0) {
431
+ obj = rb_hash_new();
432
+ rb_ary_push(des->obj_cache, obj);
433
+ while(RSTRING_LEN(key) != 0) {
434
+ rb_hash_aset(obj, key, des3_deserialize(self));
435
+ key = des3_read_string(des);
436
+ }
437
+ for(i = 0; i < header; i++) {
438
+ rb_hash_aset(obj, INT2FIX(i), des3_deserialize(self));
439
+ }
440
+ } else {
441
+ // Limit size of pre-allocation to force remote user to actually send data,
442
+ // rather than just sending a size of 2**32-1 and nothing afterwards to
443
+ // crash the server
444
+ obj = rb_ary_new2(header < MAX_ARRAY_PREALLOC ? header : MAX_ARRAY_PREALLOC);
445
+ rb_ary_push(des->obj_cache, obj);
446
+ for(i = 0; i < header; i++) {
447
+ rb_ary_push(obj, des3_deserialize(self));
448
+ }
449
+ }
450
+ return obj;
451
+ }
452
+ }
453
+
454
+ static VALUE des3_read_time(VALUE self) {
455
+ AMF_DESERIALIZER *des;
456
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
457
+
458
+ int header = des_read_int(des);
459
+ if((header & 1) == 0) {
460
+ header >>= 1;
461
+ if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
462
+ return RARRAY_PTR(des->obj_cache)[header];
463
+ } else {
464
+ double milli = des_read_double(des);
465
+ time_t sec = milli/1000.0;
466
+ time_t micro = (milli-sec*1000)*1000;
467
+ VALUE time = rb_time_new(sec, micro);
468
+ rb_ary_push(des->obj_cache, time);
469
+ return time;
470
+ }
471
+ }
472
+
473
+ static VALUE des3_read_byte_array(VALUE self) {
474
+ AMF_DESERIALIZER *des;
475
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
476
+
477
+ int header = des_read_int(des);
478
+ if((header & 1) == 0) {
479
+ header >>= 1;
480
+ if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
481
+ return RARRAY_PTR(des->obj_cache)[header];
482
+ } else {
483
+ header >>= 1;
484
+ VALUE args[1] = {des_read_string(des, header)};
485
+ #ifdef HAVE_RB_STR_ENCODE
486
+ // Need to force encoding to ASCII-8BIT
487
+ rb_encoding *ascii = rb_ascii8bit_encoding();
488
+ rb_enc_associate(args[0], ascii);
489
+ ENC_CODERANGE_CLEAR(args[0]);
490
+ #endif
491
+ VALUE ba = rb_class_new_instance(1, args, cStringIO);
492
+ rb_ary_push(des->obj_cache, ba);
493
+ return ba;
494
+ }
495
+ }
496
+
497
+ static VALUE des3_read_dict(VALUE self) {
498
+ AMF_DESERIALIZER *des;
499
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
500
+
501
+ int header = des_read_int(des);
502
+ if((header & 1) == 0) {
503
+ header >>= 1;
504
+ if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
505
+ return RARRAY_PTR(des->obj_cache)[header];
506
+ } else {
507
+ header >>= 1;
508
+
509
+ VALUE dict = rb_hash_new();
510
+ rb_ary_push(des->obj_cache, dict);
511
+
512
+ des_read_byte(des); // Weak Keys: Not supported in ruby
513
+
514
+ int i;
515
+ for(i = 0; i < header; i++) {
516
+ VALUE key = des3_deserialize(self);
517
+ VALUE val = des3_deserialize(self);
518
+ rb_hash_aset(dict, key, val);
519
+ }
520
+
521
+ return dict;
522
+ }
523
+ }
524
+
525
+ static VALUE des3_read_vec(VALUE self, char type) {
526
+ AMF_DESERIALIZER *des;
527
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
528
+
529
+ int header = des_read_int(des);
530
+ if((header & 1) == 0) {
531
+ header >>= 1;
532
+ if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
533
+ return RARRAY_PTR(des->obj_cache)[header];
534
+ } else {
535
+ header >>= 1;
536
+
537
+ // Limit size of pre-allocation to force remote user to actually send data,
538
+ // rather than just sending a size of 2**32-1 and nothing afterwards to
539
+ // crash the server
540
+ VALUE vec = rb_ary_new2(header < MAX_ARRAY_PREALLOC ? header : MAX_ARRAY_PREALLOC);
541
+ rb_ary_push(des->obj_cache, vec);
542
+
543
+ des_read_byte(des); // Fixed Length: Not supported in ruby
544
+
545
+ // On 32-bit ARCH, FIXNUM has a limit of 2**31-1, resulting in truncation of large ints/uints
546
+ int i;
547
+ switch(type) {
548
+ case AMF3_VECTOR_INT_MARKER:
549
+ for(i = 0; i < header; i++) {
550
+ int ival = des_read_uint32(des);
551
+ rb_ary_push(vec, INT2FIX(ival));
552
+ }
553
+ break;
554
+ case AMF3_VECTOR_UINT_MARKER:
555
+ for(i = 0; i < header; i++) {
556
+ rb_ary_push(vec, INT2FIX(des_read_uint32(des)));
557
+ }
558
+ break;
559
+ case AMF3_VECTOR_DOUBLE_MARKER:
560
+ for(i = 0; i < header; i++) {
561
+ rb_ary_push(vec, rb_float_new(des_read_double(des)));
562
+ }
563
+ break;
564
+ case AMF3_VECTOR_OBJECT_MARKER:
565
+ des3_read_string(des); // Class name of objects - ignored
566
+ for(i = 0; i < header; i++) {
567
+ rb_ary_push(vec, des3_deserialize(self));
568
+ }
569
+ break;
570
+ }
571
+ return vec;
572
+ }
573
+ }
574
+
575
+ /*
576
+ * Internal deserialize call - unlike des0_deserialize, it reads the type
577
+ * itself, due to minor changes in the specs that make that modification
578
+ * unnecessary.
579
+ */
580
+ static VALUE des3_deserialize(VALUE self) {
581
+ AMF_DESERIALIZER *des;
582
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
583
+
584
+ char type = des_read_byte(des);
585
+ VALUE ret = Qnil;
586
+ switch(type) {
587
+ case AMF3_UNDEFINED_MARKER:
588
+ case AMF3_NULL_MARKER:
589
+ ret = Qnil;
590
+ break;
591
+ case AMF3_FALSE_MARKER:
592
+ ret = Qfalse;
593
+ break;
594
+ case AMF3_TRUE_MARKER:
595
+ ret = Qtrue;
596
+ break;
597
+ case AMF3_INTEGER_MARKER:
598
+ ret = INT2FIX(des_read_int(des));
599
+ break;
600
+ case AMF3_DOUBLE_MARKER:
601
+ ret = rb_float_new(des_read_double(des));
602
+ break;
603
+ case AMF3_STRING_MARKER:
604
+ ret = des3_read_string(des);
605
+ break;
606
+ case AMF3_ARRAY_MARKER:
607
+ ret = des3_read_array(self);
608
+ break;
609
+ case AMF3_OBJECT_MARKER:
610
+ ret = des3_read_object(self);
611
+ break;
612
+ case AMF3_DATE_MARKER:
613
+ ret = des3_read_time(self);
614
+ break;
615
+ case AMF3_XML_DOC_MARKER:
616
+ case AMF3_XML_MARKER:
617
+ ret = des3_read_xml(self);
618
+ break;
619
+ case AMF3_BYTE_ARRAY_MARKER:
620
+ ret = des3_read_byte_array(self);
621
+ break;
622
+ case AMF3_VECTOR_INT_MARKER:
623
+ case AMF3_VECTOR_UINT_MARKER:
624
+ case AMF3_VECTOR_DOUBLE_MARKER:
625
+ case AMF3_VECTOR_OBJECT_MARKER:
626
+ ret = des3_read_vec(self, type);
627
+ break;
628
+ case AMF3_DICT_MARKER:
629
+ ret = des3_read_dict(self);
630
+ break;
631
+ default:
632
+ rb_raise(rb_eRuntimeError, "Not supported: %d", type);
633
+ break;
634
+ }
635
+
636
+ return ret;
637
+ }
638
+
639
+ /*
640
+ * Mark the reader and its source. If caches are populated mark them as well.
641
+ */
642
+ static void des_mark(AMF_DESERIALIZER *des) {
643
+ if(!des) return;
644
+ rb_gc_mark(des->class_mapper);
645
+ rb_gc_mark(des->src);
646
+ if(des->obj_cache) rb_gc_mark(des->obj_cache);
647
+ if(des->str_cache) rb_gc_mark(des->str_cache);
648
+ if(des->trait_cache) rb_gc_mark(des->trait_cache);
649
+ }
650
+
651
+ /*
652
+ * Free the reader. Don't need to free anything but the struct because we didn't
653
+ * alloc anything - source is from the ruby source object.
654
+ */
655
+ static void des_free(AMF_DESERIALIZER *des) {
656
+ xfree(des);
657
+ }
658
+
659
+ /*
660
+ * Create new struct and wrap with class
661
+ */
662
+ static VALUE des_alloc(VALUE klass) {
663
+ AMF_DESERIALIZER *des = ALLOC(AMF_DESERIALIZER);
664
+ memset(des, 0, sizeof(AMF_DESERIALIZER));
665
+ return Data_Wrap_Struct(klass, des_mark, des_free, des);
666
+ }
667
+
668
+ /*
669
+ * Initializer
670
+ */
671
+ static VALUE des_initialize(VALUE self, VALUE class_mapper) {
672
+ AMF_DESERIALIZER *des;
673
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
674
+ des->class_mapper = class_mapper;
675
+ return self;
676
+ }
677
+
678
+ /*
679
+ * call-seq:
680
+ * ser.stream => StringIO
681
+ *
682
+ * Returns the source that the deserializer is reading from
683
+ */
684
+ static VALUE des_source(VALUE self) {
685
+ AMF_DESERIALIZER *des;
686
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
687
+ return des->src;
688
+ }
689
+
690
+ /*
691
+ * call-seq:
692
+ * des.deserialize(amf_ver, str) => obj
693
+ * des.deserialize(amf_ver, StringIO) => obj
694
+ *
695
+ * Deserialize the string or StringIO from AMF to a ruby object.
696
+ */
697
+ VALUE des_deserialize(VALUE self, VALUE ver, VALUE src) {
698
+ AMF_DESERIALIZER *des;
699
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
700
+
701
+ // Process version
702
+ int int_ver = FIX2INT(ver);
703
+ if(int_ver != 0 && int_ver != 3) rb_raise(rb_eArgError, "unsupported version %d", int_ver);
704
+ des->version = int_ver;
705
+
706
+ // Process source
707
+ if(src != Qnil) {
708
+ des_set_src(des, src);
709
+ } else if(!des->src) {
710
+ rb_raise(rb_eArgError, "Missing deserialization source");
711
+ }
712
+
713
+ // Deserialize from source
714
+ VALUE ret;
715
+ if(des->version == 0) {
716
+ des->obj_cache = rb_ary_new();
717
+ ret = des0_deserialize(self, des_read_byte(des));
718
+ } else {
719
+ des->obj_cache = rb_ary_new();
720
+ des->str_cache = rb_ary_new();
721
+ des->trait_cache = rb_ary_new();
722
+ ret = des3_deserialize(self);
723
+ }
724
+
725
+ // Update source position
726
+ rb_funcall(des->src, rb_intern("pos="), 1, LONG2NUM(des->pos)); // Update source StringIO pos
727
+
728
+ return ret;
729
+ }
730
+
731
+ /*
732
+ * call-seq:
733
+ * des.read_object => obj
734
+ *
735
+ * Reads an object from the deserializer's stream and returns it.
736
+ */
737
+ VALUE des_read_object(VALUE self) {
738
+ AMF_DESERIALIZER *des;
739
+ Data_Get_Struct(self, AMF_DESERIALIZER, des);
740
+
741
+ // Update internal pos from source in case they've modified it
742
+ des->pos = NUM2LONG(rb_funcall(des->src, rb_intern("pos"), 0));
743
+
744
+ // Deserialize
745
+ VALUE ret;
746
+ if(des->version == 0) {
747
+ ret = des0_deserialize(self, des_read_byte(des));
748
+ } else {
749
+ ret = des3_deserialize(self);
750
+ }
751
+
752
+ // Update source position
753
+ rb_funcall(des->src, rb_intern("pos="), 1, LONG2NUM(des->pos)); // Update source StringIO pos
754
+
755
+ return ret;
756
+ }
757
+
758
+ void Init_rocket_amf_deserializer() {
759
+ // Define Deserializer
760
+ cDeserializer = rb_define_class_under(mRocketAMFExt, "Deserializer", rb_cObject);
761
+ rb_define_alloc_func(cDeserializer, des_alloc);
762
+ rb_define_method(cDeserializer, "initialize", des_initialize, 1);
763
+ rb_define_method(cDeserializer, "source", des_source, 0);
764
+ rb_define_method(cDeserializer, "deserialize", des_deserialize, 2);
765
+ rb_define_method(cDeserializer, "read_object", des_read_object, 0);
766
+
767
+ // Get refs to commonly used symbols and ids
768
+ id_get_ruby_obj = rb_intern("get_ruby_obj");
769
+ id_populate_ruby_obj = rb_intern("populate_ruby_obj");
770
+ }