mrpin-rocketamf 1.0.2

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