mrpin-amf 2.1.8

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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/README.rdoc +52 -0
  4. data/Rakefile +59 -0
  5. data/benchmark.rb +86 -0
  6. data/doc/amf3-speification.pdf +0 -0
  7. data/ext/rocketamf_ext/class_mapping.c +483 -0
  8. data/ext/rocketamf_ext/constants.h +52 -0
  9. data/ext/rocketamf_ext/deserializer.c +776 -0
  10. data/ext/rocketamf_ext/deserializer.h +28 -0
  11. data/ext/rocketamf_ext/extconf.rb +18 -0
  12. data/ext/rocketamf_ext/remoting.c +184 -0
  13. data/ext/rocketamf_ext/rocketamf_ext.c +38 -0
  14. data/ext/rocketamf_ext/serializer.c +834 -0
  15. data/ext/rocketamf_ext/serializer.h +29 -0
  16. data/ext/rocketamf_ext/utility.h +4 -0
  17. data/lib/amf/common/hash_with_type.rb +20 -0
  18. data/lib/amf/ext.rb +22 -0
  19. data/lib/amf/pure/amf_constants.rb +42 -0
  20. data/lib/amf/pure/deserializer.rb +354 -0
  21. data/lib/amf/pure/errors/all_files.rb +2 -0
  22. data/lib/amf/pure/errors/amf_error.rb +5 -0
  23. data/lib/amf/pure/errors/amf_error_incomplete.rb +5 -0
  24. data/lib/amf/pure/helpers/all_files.rb +8 -0
  25. data/lib/amf/pure/helpers/cache_objects.rb +18 -0
  26. data/lib/amf/pure/helpers/cache_strings.rb +14 -0
  27. data/lib/amf/pure/helpers/io_helper_base.rb +19 -0
  28. data/lib/amf/pure/helpers/io_helper_read.rb +67 -0
  29. data/lib/amf/pure/helpers/io_helper_write.rb +49 -0
  30. data/lib/amf/pure/mapping/class_mapper.rb +159 -0
  31. data/lib/amf/pure/mapping/mapping_set.rb +49 -0
  32. data/lib/amf/pure/serializer.rb +318 -0
  33. data/lib/amf/pure.rb +16 -0
  34. data/lib/amf.rb +140 -0
  35. data/mrpin-amf.gemspec +24 -0
  36. data/spec/fixtures/objects/complex/amf3-associative-array.bin +1 -0
  37. data/spec/fixtures/objects/complex/amf3-byte-array.bin +0 -0
  38. data/spec/fixtures/objects/complex/amf3-date.bin +0 -0
  39. data/spec/fixtures/objects/complex/amf3-dictionary.bin +0 -0
  40. data/spec/fixtures/objects/complex/amf3-dynamic-object.bin +2 -0
  41. data/spec/fixtures/objects/complex/amf3-empty-array.bin +1 -0
  42. data/spec/fixtures/objects/complex/amf3-empty-dictionary.bin +0 -0
  43. data/spec/fixtures/objects/complex/amf3-hash.bin +2 -0
  44. data/spec/fixtures/objects/complex/amf3-mixed-array.bin +10 -0
  45. data/spec/fixtures/objects/complex/amf3-primitive-array.bin +1 -0
  46. data/spec/fixtures/objects/complex/amf3-typed-object.bin +2 -0
  47. data/spec/fixtures/objects/encoding/amf3-complex-encoded-string-array.bin +1 -0
  48. data/spec/fixtures/objects/encoding/amf3-encoded-string-ref.bin +0 -0
  49. data/spec/fixtures/objects/references/amf3-array-ref.bin +1 -0
  50. data/spec/fixtures/objects/references/amf3-byte-array-ref.bin +1 -0
  51. data/spec/fixtures/objects/references/amf3-date-ref.bin +0 -0
  52. data/spec/fixtures/objects/references/amf3-empty-array-ref.bin +1 -0
  53. data/spec/fixtures/objects/references/amf3-empty-string-ref.bin +1 -0
  54. data/spec/fixtures/objects/references/amf3-graph-member.bin +0 -0
  55. data/spec/fixtures/objects/references/amf3-object-ref.bin +0 -0
  56. data/spec/fixtures/objects/references/amf3-string-ref.bin +0 -0
  57. data/spec/fixtures/objects/references/amf3-trait-ref.bin +3 -0
  58. data/spec/fixtures/objects/simple/amf3-0.bin +0 -0
  59. data/spec/fixtures/objects/simple/amf3-bigNum.bin +0 -0
  60. data/spec/fixtures/objects/simple/amf3-false.bin +1 -0
  61. data/spec/fixtures/objects/simple/amf3-float.bin +0 -0
  62. data/spec/fixtures/objects/simple/amf3-large-max.bin +0 -0
  63. data/spec/fixtures/objects/simple/amf3-large-min.bin +0 -0
  64. data/spec/fixtures/objects/simple/amf3-max.bin +1 -0
  65. data/spec/fixtures/objects/simple/amf3-min.bin +0 -0
  66. data/spec/fixtures/objects/simple/amf3-null.bin +1 -0
  67. data/spec/fixtures/objects/simple/amf3-string.bin +1 -0
  68. data/spec/fixtures/objects/simple/amf3-symbol.bin +1 -0
  69. data/spec/fixtures/objects/simple/amf3-true.bin +1 -0
  70. data/spec/helpers/class_mapping_test.rb +4 -0
  71. data/spec/helpers/class_mapping_test2.rb +3 -0
  72. data/spec/helpers/fixtures.rb +34 -0
  73. data/spec/helpers/other_class.rb +4 -0
  74. data/spec/helpers/ruby_class.rb +4 -0
  75. data/spec/helpers/test_ruby_class.rb +4 -0
  76. data/spec/spec-class_mapping.rb +98 -0
  77. data/spec/spec_deserializer.rb +239 -0
  78. data/spec/spec_fast_class_mapping.rb +147 -0
  79. data/spec/spec_helper.rb +8 -0
  80. data/spec/spec_serializer.rb +267 -0
  81. metadata +146 -0
@@ -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
+ }