RocketAMF-ouvrages 1.0.0

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.
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
+ }