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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/README.rdoc +52 -0
- data/Rakefile +59 -0
- data/benchmark.rb +86 -0
- data/doc/amf3-speification.pdf +0 -0
- data/ext/rocketamf_ext/class_mapping.c +483 -0
- data/ext/rocketamf_ext/constants.h +52 -0
- data/ext/rocketamf_ext/deserializer.c +776 -0
- data/ext/rocketamf_ext/deserializer.h +28 -0
- data/ext/rocketamf_ext/extconf.rb +18 -0
- data/ext/rocketamf_ext/remoting.c +184 -0
- data/ext/rocketamf_ext/rocketamf_ext.c +38 -0
- data/ext/rocketamf_ext/serializer.c +834 -0
- data/ext/rocketamf_ext/serializer.h +29 -0
- data/ext/rocketamf_ext/utility.h +4 -0
- data/lib/amf/common/hash_with_type.rb +20 -0
- data/lib/amf/ext.rb +22 -0
- data/lib/amf/pure/amf_constants.rb +42 -0
- data/lib/amf/pure/deserializer.rb +354 -0
- data/lib/amf/pure/errors/all_files.rb +2 -0
- data/lib/amf/pure/errors/amf_error.rb +5 -0
- data/lib/amf/pure/errors/amf_error_incomplete.rb +5 -0
- data/lib/amf/pure/helpers/all_files.rb +8 -0
- data/lib/amf/pure/helpers/cache_objects.rb +18 -0
- data/lib/amf/pure/helpers/cache_strings.rb +14 -0
- data/lib/amf/pure/helpers/io_helper_base.rb +19 -0
- data/lib/amf/pure/helpers/io_helper_read.rb +67 -0
- data/lib/amf/pure/helpers/io_helper_write.rb +49 -0
- data/lib/amf/pure/mapping/class_mapper.rb +159 -0
- data/lib/amf/pure/mapping/mapping_set.rb +49 -0
- data/lib/amf/pure/serializer.rb +318 -0
- data/lib/amf/pure.rb +16 -0
- data/lib/amf.rb +140 -0
- data/mrpin-amf.gemspec +24 -0
- data/spec/fixtures/objects/complex/amf3-associative-array.bin +1 -0
- data/spec/fixtures/objects/complex/amf3-byte-array.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-date.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-dictionary.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-dynamic-object.bin +2 -0
- data/spec/fixtures/objects/complex/amf3-empty-array.bin +1 -0
- data/spec/fixtures/objects/complex/amf3-empty-dictionary.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-hash.bin +2 -0
- data/spec/fixtures/objects/complex/amf3-mixed-array.bin +10 -0
- data/spec/fixtures/objects/complex/amf3-primitive-array.bin +1 -0
- data/spec/fixtures/objects/complex/amf3-typed-object.bin +2 -0
- data/spec/fixtures/objects/encoding/amf3-complex-encoded-string-array.bin +1 -0
- data/spec/fixtures/objects/encoding/amf3-encoded-string-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-array-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-byte-array-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-date-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-empty-array-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-empty-string-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-graph-member.bin +0 -0
- data/spec/fixtures/objects/references/amf3-object-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-string-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-trait-ref.bin +3 -0
- data/spec/fixtures/objects/simple/amf3-0.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-bigNum.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-false.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-float.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-large-max.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-large-min.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-max.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-min.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-null.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-string.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-symbol.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-true.bin +1 -0
- data/spec/helpers/class_mapping_test.rb +4 -0
- data/spec/helpers/class_mapping_test2.rb +3 -0
- data/spec/helpers/fixtures.rb +34 -0
- data/spec/helpers/other_class.rb +4 -0
- data/spec/helpers/ruby_class.rb +4 -0
- data/spec/helpers/test_ruby_class.rb +4 -0
- data/spec/spec-class_mapping.rb +98 -0
- data/spec/spec_deserializer.rb +239 -0
- data/spec/spec_fast_class_mapping.rb +147 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/spec_serializer.rb +267 -0
- metadata +146 -0
@@ -0,0 +1,834 @@
|
|
1
|
+
#include "serializer.h"
|
2
|
+
#include "constants.h"
|
3
|
+
#include "utility.h"
|
4
|
+
|
5
|
+
extern VALUE mRocketAMF;
|
6
|
+
extern VALUE mRocketAMFExt;
|
7
|
+
extern VALUE cSerializer;
|
8
|
+
extern VALUE cStringIO;
|
9
|
+
extern VALUE cDate;
|
10
|
+
extern VALUE cDateTime;
|
11
|
+
extern VALUE sym_class_name;
|
12
|
+
extern VALUE sym_members;
|
13
|
+
extern VALUE sym_externalizable;
|
14
|
+
extern VALUE sym_dynamic;
|
15
|
+
VALUE cArrayCollection;
|
16
|
+
ID id_haskey;
|
17
|
+
ID id_encode_amf;
|
18
|
+
ID id_is_array_collection;
|
19
|
+
ID id_use_array_collection;
|
20
|
+
ID id_get_as_class_name;
|
21
|
+
ID id_props_for_serialization;
|
22
|
+
ID id_utc;
|
23
|
+
ID id_to_f;
|
24
|
+
ID id_is_integer;
|
25
|
+
|
26
|
+
static VALUE ser0_serialize(VALUE self, VALUE obj);
|
27
|
+
static VALUE ser3_serialize(VALUE self, VALUE obj);
|
28
|
+
|
29
|
+
void ser_write_byte(AMF_SERIALIZER *ser, char byte) {
|
30
|
+
char bytes[2] = {byte, '\0'};
|
31
|
+
rb_str_buf_cat(ser->stream, bytes, 1);
|
32
|
+
}
|
33
|
+
|
34
|
+
void ser_write_int(AMF_SERIALIZER *ser, int num) {
|
35
|
+
char tmp[4];
|
36
|
+
int tmp_len;
|
37
|
+
|
38
|
+
num &= 0x1fffffff;
|
39
|
+
if (num < 0x80) {
|
40
|
+
tmp_len = 1;
|
41
|
+
tmp[0] = num;
|
42
|
+
} else if (num < 0x4000) {
|
43
|
+
tmp_len = 2;
|
44
|
+
tmp[0] = (num >> 7 & 0x7f) | 0x80;
|
45
|
+
tmp[1] = num & 0x7f;
|
46
|
+
} else if (num < 0x200000) {
|
47
|
+
tmp_len = 3;
|
48
|
+
tmp[0] = (num >> 14 & 0x7f) | 0x80;
|
49
|
+
tmp[1] = (num >> 7 & 0x7f) | 0x80;
|
50
|
+
tmp[2] = num & 0x7f;
|
51
|
+
} else if (num < 0x40000000) {
|
52
|
+
tmp_len = 4;
|
53
|
+
tmp[0] = (num >> 22 & 0x7f) | 0x80;
|
54
|
+
tmp[1] = (num >> 15 & 0x7f) | 0x80;
|
55
|
+
tmp[2] = (num >> 8 & 0x7f) | 0x80;
|
56
|
+
tmp[3] = (num & 0xff);
|
57
|
+
} else {
|
58
|
+
rb_raise(rb_eRangeError, "int %d out of range", num);
|
59
|
+
}
|
60
|
+
|
61
|
+
rb_str_buf_cat(ser->stream, tmp, tmp_len);
|
62
|
+
}
|
63
|
+
|
64
|
+
void ser_write_uint16(AMF_SERIALIZER *ser, long num) {
|
65
|
+
if(num > 0xffff) rb_raise(rb_eRangeError, "int %ld out of range", num);
|
66
|
+
char tmp[2] = {(num >> 8) & 0xff, num & 0xff};
|
67
|
+
rb_str_buf_cat(ser->stream, tmp, 2);
|
68
|
+
}
|
69
|
+
|
70
|
+
void ser_write_uint32(AMF_SERIALIZER *ser, long num) {
|
71
|
+
if(num > 0xffffffff) rb_raise(rb_eRangeError, "int %ld out of range", num);
|
72
|
+
char tmp[4] = {(num >> 24) & 0xff, (num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff};
|
73
|
+
rb_str_buf_cat(ser->stream, tmp, 4);
|
74
|
+
}
|
75
|
+
|
76
|
+
void ser_write_double(AMF_SERIALIZER *ser, double num) {
|
77
|
+
union aligned {
|
78
|
+
double dval;
|
79
|
+
char cval[8];
|
80
|
+
} d;
|
81
|
+
const char *number = d.cval;
|
82
|
+
d.dval = num;
|
83
|
+
|
84
|
+
#ifdef WORDS_BIGENDIAN
|
85
|
+
rb_str_buf_cat(ser->stream, number, 8);
|
86
|
+
#else
|
87
|
+
char netnum[8] = {number[7],number[6],number[5],number[4],number[3],number[2],number[1],number[0]};
|
88
|
+
rb_str_buf_cat(ser->stream, netnum, 8);
|
89
|
+
#endif
|
90
|
+
}
|
91
|
+
|
92
|
+
void ser_get_string(VALUE obj, VALUE encode, char** str, long* len) {
|
93
|
+
int type = TYPE(obj);
|
94
|
+
if(type == T_STRING) {
|
95
|
+
#ifdef HAVE_RB_STR_ENCODE
|
96
|
+
if(encode == Qtrue) {
|
97
|
+
rb_encoding *enc = rb_enc_get(obj);
|
98
|
+
if (enc != rb_ascii8bit_encoding()) {
|
99
|
+
rb_encoding *utf8 = rb_utf8_encoding();
|
100
|
+
if (enc != utf8) obj = rb_str_encode(obj, rb_enc_from_encoding(utf8), 0, Qnil);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
#endif
|
104
|
+
*str = RSTRING_PTR(obj);
|
105
|
+
*len = RSTRING_LEN(obj);
|
106
|
+
} else if(type == T_SYMBOL) {
|
107
|
+
*str = (char*)rb_id2name(SYM2ID(obj));
|
108
|
+
*len = strlen(*str);
|
109
|
+
} else if(obj == Qnil) {
|
110
|
+
*len = 0;
|
111
|
+
} else {
|
112
|
+
rb_raise(rb_eArgError, "Invalid type in ser_get_string: %d", type);
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
/*
|
117
|
+
* Write the given array in AMF0 notation
|
118
|
+
*/
|
119
|
+
static void ser0_write_array(VALUE self, VALUE ary) {
|
120
|
+
AMF_SERIALIZER *ser;
|
121
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
122
|
+
|
123
|
+
// Cache it
|
124
|
+
st_add_direct(ser->obj_cache, ary, LONG2FIX(ser->obj_index));
|
125
|
+
ser->obj_index++;
|
126
|
+
|
127
|
+
// Write it out
|
128
|
+
long i, len = RARRAY_LEN(ary);
|
129
|
+
ser_write_byte(ser, AMF0_STRICT_ARRAY_MARKER);
|
130
|
+
ser_write_uint32(ser, len);
|
131
|
+
for(i = 0; i < len; i++) {
|
132
|
+
ser0_serialize(self, RARRAY_PTR(ary)[i]);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
/*
|
137
|
+
* Supports writing strings and symbols. For hash keys, strings all have 16 bit
|
138
|
+
* lengths, so writing a type marker is unnecessary. In that case the third
|
139
|
+
* parameter should be set to Qfalse instead of Qtrue.
|
140
|
+
*/
|
141
|
+
static void ser0_write_string(AMF_SERIALIZER *ser, VALUE obj, VALUE write_marker) {
|
142
|
+
// Extract char array and length from object
|
143
|
+
char* str;
|
144
|
+
long len;
|
145
|
+
ser_get_string(obj, Qtrue, &str, &len);
|
146
|
+
|
147
|
+
// Write string
|
148
|
+
if(len > 0xffff) {
|
149
|
+
if(write_marker == Qtrue) ser_write_byte(ser, AMF0_LONG_STRING_MARKER);
|
150
|
+
ser_write_uint32(ser, len);
|
151
|
+
} else {
|
152
|
+
if(write_marker == Qtrue) ser_write_byte(ser, AMF0_STRING_MARKER);
|
153
|
+
ser_write_uint16(ser, len);
|
154
|
+
}
|
155
|
+
rb_str_buf_cat(ser->stream, str, len);
|
156
|
+
}
|
157
|
+
|
158
|
+
/*
|
159
|
+
* Hash iterator for object properties that writes the key and then serializes
|
160
|
+
* the value
|
161
|
+
*/
|
162
|
+
static int ser0_hash_iter(VALUE key, VALUE val, const VALUE args[1]) {
|
163
|
+
AMF_SERIALIZER *ser;
|
164
|
+
Data_Get_Struct(args[0], AMF_SERIALIZER, ser);
|
165
|
+
|
166
|
+
// Write key and value
|
167
|
+
ser0_write_string(ser, key, Qfalse); // Technically incorrect if key length is longer than a 16 bit string, but if you run into that you're screwed anyways
|
168
|
+
ser0_serialize(args[0], val);
|
169
|
+
|
170
|
+
return ST_CONTINUE;
|
171
|
+
}
|
172
|
+
|
173
|
+
/*
|
174
|
+
* Used for both hashes and objects. Takes the object and the props hash or Qnil,
|
175
|
+
* which forces a call to the class mapper for props for serialization. Prop
|
176
|
+
* sorting must be enabled by an explicit call to extconf.rb, so the tests will
|
177
|
+
* not pass typically on Ruby 1.8.
|
178
|
+
*/
|
179
|
+
static void ser0_write_object(VALUE self, VALUE obj, VALUE props) {
|
180
|
+
AMF_SERIALIZER *ser;
|
181
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
182
|
+
|
183
|
+
// Cache it
|
184
|
+
st_add_direct(ser->obj_cache, obj, LONG2FIX(ser->obj_index));
|
185
|
+
ser->obj_index++;
|
186
|
+
|
187
|
+
// Make a request for props hash unless we already have it
|
188
|
+
if(props == Qnil) {
|
189
|
+
props = rb_funcall(ser->class_mapper, id_props_for_serialization, 1, obj);
|
190
|
+
}
|
191
|
+
|
192
|
+
// Write header
|
193
|
+
VALUE class_name = rb_funcall(ser->class_mapper, id_get_as_class_name, 1, obj);
|
194
|
+
if(class_name != Qnil) {
|
195
|
+
ser_write_byte(ser, AMF0_TYPED_OBJECT_MARKER);
|
196
|
+
ser0_write_string(ser, class_name, Qfalse);
|
197
|
+
} else {
|
198
|
+
ser_write_byte(ser, AMF0_OBJECT_MARKER);
|
199
|
+
}
|
200
|
+
|
201
|
+
// Write out data
|
202
|
+
VALUE args[1] = {self};
|
203
|
+
#ifdef SORT_PROPS
|
204
|
+
// Sort is required prior to Ruby 1.9 to pass all the tests, as Ruby 1.8 hashes don't store insert order
|
205
|
+
VALUE sorted_props = rb_funcall(props, rb_intern("sort"), 0);
|
206
|
+
long i, len = RARRAY_LEN(sorted_props);
|
207
|
+
for(i = 0; i < len; i++) {
|
208
|
+
VALUE pair = RARRAY_PTR(sorted_props)[i];
|
209
|
+
ser0_hash_iter(RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1], args);
|
210
|
+
}
|
211
|
+
#else
|
212
|
+
rb_hash_foreach(props, ser0_hash_iter, (st_data_t)args);
|
213
|
+
#endif
|
214
|
+
|
215
|
+
ser_write_uint16(ser, 0);
|
216
|
+
ser_write_byte(ser, AMF0_OBJECT_END_MARKER);
|
217
|
+
}
|
218
|
+
|
219
|
+
static void ser0_write_time(VALUE self, VALUE time) {
|
220
|
+
AMF_SERIALIZER *ser;
|
221
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
222
|
+
|
223
|
+
ser_write_byte(ser, AMF0_DATE_MARKER);
|
224
|
+
|
225
|
+
// Write time
|
226
|
+
time = rb_obj_dup(time);
|
227
|
+
rb_funcall(time, id_utc, 0);
|
228
|
+
double tmp_num = NUM2DBL(rb_funcall(time, id_to_f, 0)) * 1000;
|
229
|
+
ser_write_double(ser, tmp_num);
|
230
|
+
ser_write_uint16(ser, 0); // Time zone
|
231
|
+
}
|
232
|
+
|
233
|
+
static void ser0_write_date(VALUE self, VALUE date) {
|
234
|
+
AMF_SERIALIZER *ser;
|
235
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
236
|
+
|
237
|
+
ser_write_byte(ser, AMF0_DATE_MARKER);
|
238
|
+
|
239
|
+
// Write time
|
240
|
+
double tmp_num = rb_str_to_dbl(rb_funcall(date, rb_intern("strftime"), 1, rb_str_new2("%Q")), Qfalse);
|
241
|
+
ser_write_double(ser, tmp_num);
|
242
|
+
ser_write_uint16(ser, 0); // Time zone
|
243
|
+
}
|
244
|
+
|
245
|
+
/*
|
246
|
+
* Serializes the object to a string and returns that string
|
247
|
+
*/
|
248
|
+
static VALUE ser0_serialize(VALUE self, VALUE obj) {
|
249
|
+
AMF_SERIALIZER *ser;
|
250
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
251
|
+
|
252
|
+
int type = TYPE(obj);
|
253
|
+
VALUE klass = Qnil;
|
254
|
+
if(type == T_OBJECT || type == T_DATA) {
|
255
|
+
klass = CLASS_OF(obj);
|
256
|
+
}
|
257
|
+
|
258
|
+
VALUE obj_index;
|
259
|
+
if(st_lookup(ser->obj_cache, obj, &obj_index)) {
|
260
|
+
ser_write_byte(ser, AMF0_REFERENCE_MARKER);
|
261
|
+
ser_write_uint16(ser, FIX2LONG(obj_index));
|
262
|
+
} else if(rb_respond_to(obj, id_encode_amf)) {
|
263
|
+
rb_funcall(obj, id_encode_amf, 1, self);
|
264
|
+
} else if(type == T_STRING || type == T_SYMBOL) {
|
265
|
+
ser0_write_string(ser, obj, Qtrue);
|
266
|
+
} else if(rb_obj_is_kind_of(obj, rb_cNumeric)) {
|
267
|
+
ser_write_byte(ser, AMF0_NUMBER_MARKER);
|
268
|
+
ser_write_double(ser, RFLOAT_VALUE(rb_Float(obj)));
|
269
|
+
} else if(type == T_NIL) {
|
270
|
+
ser_write_byte(ser, AMF0_NULL_MARKER);
|
271
|
+
} else if(type == T_TRUE || type == T_FALSE) {
|
272
|
+
ser_write_byte(ser, AMF0_BOOLEAN_MARKER);
|
273
|
+
ser_write_byte(ser, type == T_TRUE ? 1 : 0);
|
274
|
+
} else if(type == T_ARRAY) {
|
275
|
+
ser0_write_array(self, obj);
|
276
|
+
} else if(klass == rb_cTime) {
|
277
|
+
ser0_write_time(self, obj);
|
278
|
+
} else if(klass == cDate || klass == cDateTime) {
|
279
|
+
ser0_write_date(self, obj);
|
280
|
+
} else if(type == T_HASH || type == T_OBJECT) {
|
281
|
+
ser0_write_object(self, obj, Qnil);
|
282
|
+
}
|
283
|
+
|
284
|
+
return ser->stream;
|
285
|
+
}
|
286
|
+
|
287
|
+
/*
|
288
|
+
* Writes an AMF3 style string. Accepts strings, symbols, and nil, and handles
|
289
|
+
* all the necessary encoding and caching.
|
290
|
+
*/
|
291
|
+
static void ser3_write_utf8vr(AMF_SERIALIZER *ser, VALUE obj) {
|
292
|
+
// Extract char array and length from object
|
293
|
+
char* str;
|
294
|
+
long len;
|
295
|
+
ser_get_string(obj, Qtrue, &str, &len);
|
296
|
+
|
297
|
+
// Write string
|
298
|
+
VALUE str_index;
|
299
|
+
if(len == 0) {
|
300
|
+
ser_write_byte(ser, AMF3_EMPTY_STRING);
|
301
|
+
} else if(st_lookup(ser->str_cache, (st_data_t)str, &str_index)) {
|
302
|
+
ser_write_int(ser, FIX2INT(str_index) << 1);
|
303
|
+
} else {
|
304
|
+
st_add_direct(ser->str_cache, (st_data_t)strdup(str), LONG2FIX(ser->str_index));
|
305
|
+
ser->str_index++;
|
306
|
+
|
307
|
+
ser_write_int(ser, ((int)len) << 1 | 1);
|
308
|
+
rb_str_buf_cat(ser->stream, str, len);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
/*
|
313
|
+
* Writes Numeric conforming object using AMF3 notation
|
314
|
+
*/
|
315
|
+
static void ser3_write_numeric(AMF_SERIALIZER *ser, VALUE num) {
|
316
|
+
// Is it an integer in range?
|
317
|
+
if(rb_funcall(num, id_is_integer, 0) == Qtrue) {
|
318
|
+
// It's an integer internally, so now we need to check if it's in range
|
319
|
+
VALUE int_obj = rb_Integer(num);
|
320
|
+
if(TYPE(int_obj) == T_FIXNUM) {
|
321
|
+
long long_val = FIX2LONG(int_obj);
|
322
|
+
if(long_val < MIN_INTEGER || long_val > MAX_INTEGER) {
|
323
|
+
// Outside range, but we have a value already, so just cast to double
|
324
|
+
ser_write_byte(ser, AMF3_DOUBLE_MARKER);
|
325
|
+
ser_write_double(ser, (double)long_val);
|
326
|
+
} else {
|
327
|
+
// Inside valid integer range
|
328
|
+
ser_write_byte(ser, AMF3_INTEGER_MARKER);
|
329
|
+
ser_write_int(ser, (int)long_val);
|
330
|
+
}
|
331
|
+
return;
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
// It's either not an integer or out of range, so write as a double
|
336
|
+
ser_write_byte(ser, AMF3_DOUBLE_MARKER);
|
337
|
+
ser_write_double(ser, RFLOAT_VALUE(rb_Float(num)));
|
338
|
+
}
|
339
|
+
|
340
|
+
/*
|
341
|
+
* Writes the given array using AMF3 notation
|
342
|
+
*/
|
343
|
+
static void ser3_write_array(VALUE self, VALUE ary) {
|
344
|
+
AMF_SERIALIZER *ser;
|
345
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
346
|
+
|
347
|
+
// Is it an array collection?
|
348
|
+
VALUE is_ac = Qfalse;
|
349
|
+
if(rb_respond_to(ary, id_is_array_collection)) {
|
350
|
+
is_ac = rb_funcall(ary, id_is_array_collection, 0);
|
351
|
+
} else {
|
352
|
+
is_ac = rb_funcall(ser->class_mapper, id_use_array_collection, 0);
|
353
|
+
}
|
354
|
+
|
355
|
+
// Write type marker
|
356
|
+
ser_write_byte(ser, is_ac ? AMF3_OBJECT_MARKER : AMF3_ARRAY_MARKER);
|
357
|
+
|
358
|
+
// Write object ref, or cache it
|
359
|
+
VALUE obj_index;
|
360
|
+
if(st_lookup(ser->obj_cache, ary, &obj_index)) {
|
361
|
+
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
362
|
+
return;
|
363
|
+
} else {
|
364
|
+
st_add_direct(ser->obj_cache, ary, LONG2FIX(ser->obj_index));
|
365
|
+
ser->obj_index++;
|
366
|
+
if(is_ac) ser->obj_index++; // The array collection source array
|
367
|
+
}
|
368
|
+
|
369
|
+
// Write out traits and array marker if it's an array collection
|
370
|
+
if(is_ac) {
|
371
|
+
VALUE trait_index;
|
372
|
+
char array_collection_name[34] = "flex.messaging.io.ArrayCollection";
|
373
|
+
if(st_lookup(ser->trait_cache, (st_data_t)array_collection_name, &trait_index)) {
|
374
|
+
ser_write_int(ser, FIX2INT(trait_index) << 2 | 0x01);
|
375
|
+
} else {
|
376
|
+
st_add_direct(ser->trait_cache, (st_data_t)strdup(array_collection_name), LONG2FIX(ser->trait_index));
|
377
|
+
ser->trait_index++;
|
378
|
+
ser_write_byte(ser, 0x07); // Trait header
|
379
|
+
ser3_write_utf8vr(ser, rb_str_new2(array_collection_name));
|
380
|
+
}
|
381
|
+
ser_write_byte(ser, AMF3_ARRAY_MARKER);
|
382
|
+
}
|
383
|
+
|
384
|
+
// Write header
|
385
|
+
int header = ((int)RARRAY_LEN(ary)) << 1 | 1;
|
386
|
+
ser_write_int(ser, header);
|
387
|
+
ser_write_byte(ser, AMF3_CLOSE_DYNAMIC_ARRAY);
|
388
|
+
|
389
|
+
// Write contents
|
390
|
+
long i, len = RARRAY_LEN(ary);
|
391
|
+
for(i = 0; i < len; i++) {
|
392
|
+
ser3_serialize(self, RARRAY_PTR(ary)[i]);
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
/*
|
397
|
+
* AMF3 property hash write iterator. Checks the args->extra hash, if given,
|
398
|
+
* and skips properties that are keys in that hash.
|
399
|
+
*/
|
400
|
+
static int ser3_hash_iter(VALUE key, VALUE val, const VALUE args[2]) {
|
401
|
+
AMF_SERIALIZER *ser;
|
402
|
+
Data_Get_Struct(args[0], AMF_SERIALIZER, ser);
|
403
|
+
|
404
|
+
if(args[1] == Qnil || rb_funcall(args[1], id_haskey, 1, key) == Qfalse) {
|
405
|
+
// Write key and value
|
406
|
+
ser3_write_utf8vr(ser, key);
|
407
|
+
ser3_serialize(args[0], val);
|
408
|
+
}
|
409
|
+
return ST_CONTINUE;
|
410
|
+
}
|
411
|
+
|
412
|
+
/*
|
413
|
+
* Used for both hashes and objects. Takes the object and the props hash or Qnil,
|
414
|
+
* which forces a call to the class mapper for props for serialization. Prop
|
415
|
+
* sorting must be enabled by an explicit call to extconf.rb, so the tests will
|
416
|
+
* not pass typically on Ruby 1.8. If you need to have specific traits, you can
|
417
|
+
* also pass that in, or pass Qnil to use the default traits - dynamic with no
|
418
|
+
* defined members.
|
419
|
+
*/
|
420
|
+
static void ser3_write_object(VALUE self, VALUE obj, VALUE props, VALUE traits) {
|
421
|
+
AMF_SERIALIZER *ser;
|
422
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
423
|
+
long i;
|
424
|
+
|
425
|
+
// Write type marker
|
426
|
+
ser_write_byte(ser, AMF3_OBJECT_MARKER);
|
427
|
+
|
428
|
+
// Write object ref, or cache it
|
429
|
+
VALUE obj_index;
|
430
|
+
if(st_lookup(ser->obj_cache, obj, &obj_index)) {
|
431
|
+
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
432
|
+
return;
|
433
|
+
} else {
|
434
|
+
st_add_direct(ser->obj_cache, obj, LONG2FIX(ser->obj_index));
|
435
|
+
ser->obj_index++;
|
436
|
+
}
|
437
|
+
|
438
|
+
// Extract traits data, or use defaults
|
439
|
+
VALUE is_default = Qfalse;
|
440
|
+
VALUE class_name = Qnil;
|
441
|
+
VALUE members = Qnil;
|
442
|
+
long members_len = 0;
|
443
|
+
VALUE dynamic = Qtrue;
|
444
|
+
VALUE externalizable = Qfalse;
|
445
|
+
if(traits == Qnil) {
|
446
|
+
class_name = rb_funcall(ser->class_mapper, id_get_as_class_name, 1, obj);
|
447
|
+
if(class_name == Qnil) is_default = Qtrue;
|
448
|
+
} else {
|
449
|
+
class_name = rb_hash_aref(traits, sym_class_name);
|
450
|
+
members = rb_hash_aref(traits, sym_members);
|
451
|
+
if(members != Qnil) members_len = RARRAY_LEN(members);
|
452
|
+
dynamic = rb_hash_aref(traits, sym_dynamic);
|
453
|
+
externalizable = rb_hash_aref(traits, sym_externalizable);
|
454
|
+
}
|
455
|
+
|
456
|
+
// Handle trait caching
|
457
|
+
int did_ref = 0;
|
458
|
+
VALUE trait_index;
|
459
|
+
if(is_default == Qtrue || class_name != Qnil) {
|
460
|
+
const char *ref_class_name = is_default == Qtrue ? "__default__" : RSTRING_PTR(class_name);
|
461
|
+
if(st_lookup(ser->trait_cache, (st_data_t)ref_class_name, &trait_index)) {
|
462
|
+
ser_write_int(ser, FIX2INT(trait_index) << 2 | 0x01);
|
463
|
+
did_ref = 1;
|
464
|
+
} else {
|
465
|
+
st_add_direct(ser->trait_cache, (st_data_t)strdup(ref_class_name), LONG2FIX(ser->trait_index));
|
466
|
+
ser->trait_index++;
|
467
|
+
}
|
468
|
+
}
|
469
|
+
|
470
|
+
// Write traits outs if didn't write reference
|
471
|
+
if(!did_ref) {
|
472
|
+
// Write out trait header
|
473
|
+
int header = 0x03;
|
474
|
+
if(dynamic == Qtrue) header |= 0x02 << 2;
|
475
|
+
if(externalizable == Qtrue) header |= 0x01 << 2;
|
476
|
+
header |= ((int)members_len) << 4;
|
477
|
+
ser_write_int(ser, header);
|
478
|
+
|
479
|
+
// Write class name
|
480
|
+
ser3_write_utf8vr(ser, class_name);
|
481
|
+
|
482
|
+
// Write out members
|
483
|
+
for(i = 0; i < members_len; i++) {
|
484
|
+
ser3_write_utf8vr(ser, RARRAY_PTR(members)[i]);
|
485
|
+
}
|
486
|
+
}
|
487
|
+
|
488
|
+
// Raise exception if marked externalizable
|
489
|
+
if(externalizable == Qtrue) {
|
490
|
+
rb_funcall(obj, rb_intern("write_external"), 1, self);
|
491
|
+
return;
|
492
|
+
}
|
493
|
+
|
494
|
+
// Make a request for props hash unless we already have it
|
495
|
+
if(props == Qnil) {
|
496
|
+
props = rb_funcall(ser->class_mapper, id_props_for_serialization, 1, obj);
|
497
|
+
}
|
498
|
+
|
499
|
+
// Write sealed members
|
500
|
+
VALUE skipped_members = members_len ? rb_hash_new() : Qnil;
|
501
|
+
for(i = 0; i < members_len; i++) {
|
502
|
+
ser3_serialize(self, rb_hash_aref(props, RARRAY_PTR(members)[i]));
|
503
|
+
rb_hash_aset(skipped_members, RARRAY_PTR(members)[i], Qtrue);
|
504
|
+
}
|
505
|
+
|
506
|
+
// Write dynamic properties
|
507
|
+
if(dynamic == Qtrue) {
|
508
|
+
VALUE args[2] = {self, skipped_members};
|
509
|
+
#ifdef SORT_PROPS
|
510
|
+
// Sort is required prior to Ruby 1.9 to pass all the tests, as Ruby 1.8 hashes don't store insert order
|
511
|
+
VALUE sorted_props = rb_funcall(props, rb_intern("sort"), 0);
|
512
|
+
for(i = 0; i < RARRAY_LEN(sorted_props); i++) {
|
513
|
+
VALUE pair = RARRAY_PTR(sorted_props)[i];
|
514
|
+
ser3_hash_iter(RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1], args);
|
515
|
+
}
|
516
|
+
#else
|
517
|
+
rb_hash_foreach(props, ser3_hash_iter, (st_data_t)args);
|
518
|
+
#endif
|
519
|
+
|
520
|
+
ser_write_byte(ser, AMF3_CLOSE_DYNAMIC_OBJECT);
|
521
|
+
}
|
522
|
+
}
|
523
|
+
|
524
|
+
static void ser3_write_time(VALUE self, VALUE time_obj) {
|
525
|
+
AMF_SERIALIZER *ser;
|
526
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
527
|
+
|
528
|
+
ser_write_byte(ser, AMF3_DATE_MARKER);
|
529
|
+
|
530
|
+
// Write object ref, or cache it
|
531
|
+
VALUE obj_index;
|
532
|
+
if(st_lookup(ser->obj_cache, time_obj, &obj_index)) {
|
533
|
+
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
534
|
+
return;
|
535
|
+
} else {
|
536
|
+
st_add_direct(ser->obj_cache, time_obj, LONG2FIX(ser->obj_index));
|
537
|
+
ser->obj_index++;
|
538
|
+
}
|
539
|
+
|
540
|
+
// Write time
|
541
|
+
ser_write_byte(ser, AMF3_NULL_MARKER); // Ref header
|
542
|
+
time_obj = rb_obj_dup(time_obj);
|
543
|
+
rb_funcall(time_obj, id_utc, 0);
|
544
|
+
double tmp_num = NUM2DBL(rb_funcall(time_obj, id_to_f, 0)) * 1000;
|
545
|
+
ser_write_double(ser, tmp_num);
|
546
|
+
}
|
547
|
+
|
548
|
+
static void ser3_write_date(VALUE self, VALUE date) {
|
549
|
+
AMF_SERIALIZER *ser;
|
550
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
551
|
+
|
552
|
+
ser_write_byte(ser, AMF3_DATE_MARKER);
|
553
|
+
|
554
|
+
// Write object ref, or cache it
|
555
|
+
VALUE obj_index;
|
556
|
+
if(st_lookup(ser->obj_cache, date, &obj_index)) {
|
557
|
+
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
558
|
+
return;
|
559
|
+
} else {
|
560
|
+
st_add_direct(ser->obj_cache, date, LONG2FIX(ser->obj_index));
|
561
|
+
ser->obj_index++;
|
562
|
+
}
|
563
|
+
|
564
|
+
// Write time
|
565
|
+
ser_write_byte(ser, AMF3_NULL_MARKER); // Ref header
|
566
|
+
double tmp_num = rb_str_to_dbl(rb_funcall(date, rb_intern("strftime"), 1, rb_str_new2("%Q")), Qfalse);
|
567
|
+
ser_write_double(ser, tmp_num);
|
568
|
+
}
|
569
|
+
|
570
|
+
static void ser3_write_byte_array(VALUE self, VALUE ba) {
|
571
|
+
AMF_SERIALIZER *ser;
|
572
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
573
|
+
|
574
|
+
ser_write_byte(ser, AMF3_BYTE_ARRAY_MARKER);
|
575
|
+
|
576
|
+
// Write object ref, or cache it
|
577
|
+
VALUE obj_index;
|
578
|
+
if(st_lookup(ser->obj_cache, ba, &obj_index)) {
|
579
|
+
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
580
|
+
return;
|
581
|
+
} else {
|
582
|
+
st_add_direct(ser->obj_cache, ba, LONG2FIX(ser->obj_index));
|
583
|
+
ser->obj_index++;
|
584
|
+
}
|
585
|
+
|
586
|
+
// Write byte array
|
587
|
+
VALUE str = rb_funcall(ba, rb_intern("string"), 0);
|
588
|
+
int len = (int)(RSTRING_LEN(str) << 1); // Explicitly cast to int to avoid compiler warning
|
589
|
+
ser_write_int(ser, len | 1);
|
590
|
+
rb_str_buf_cat(ser->stream, RSTRING_PTR(str), RSTRING_LEN(str));
|
591
|
+
}
|
592
|
+
|
593
|
+
/*
|
594
|
+
* Serializes the object to a string and returns that string
|
595
|
+
*/
|
596
|
+
static VALUE ser3_serialize(VALUE self, VALUE obj) {
|
597
|
+
AMF_SERIALIZER *ser;
|
598
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
599
|
+
|
600
|
+
int type = TYPE(obj);
|
601
|
+
VALUE klass = Qnil;
|
602
|
+
if(type == T_OBJECT || type == T_DATA || type == T_ARRAY) {
|
603
|
+
klass = CLASS_OF(obj);
|
604
|
+
}
|
605
|
+
|
606
|
+
if(rb_respond_to(obj, id_encode_amf)) {
|
607
|
+
rb_funcall(obj, id_encode_amf, 1, self);
|
608
|
+
} else if(type == T_STRING || type == T_SYMBOL) {
|
609
|
+
ser_write_byte(ser, AMF3_STRING_MARKER);
|
610
|
+
ser3_write_utf8vr(ser, obj);
|
611
|
+
} else if(rb_obj_is_kind_of(obj, rb_cNumeric)) {
|
612
|
+
ser3_write_numeric(ser, obj);
|
613
|
+
} else if(type == T_NIL) {
|
614
|
+
ser_write_byte(ser, AMF3_NULL_MARKER);
|
615
|
+
} else if(type == T_TRUE) {
|
616
|
+
ser_write_byte(ser, AMF3_TRUE_MARKER);
|
617
|
+
} else if(type == T_FALSE) {
|
618
|
+
ser_write_byte(ser, AMF3_FALSE_MARKER);
|
619
|
+
} else if(type == T_ARRAY) {
|
620
|
+
ser3_write_array(self, obj);
|
621
|
+
} else if(type == T_HASH) {
|
622
|
+
ser3_write_object(self, obj, Qnil, Qnil);
|
623
|
+
} else if(klass == rb_cTime) {
|
624
|
+
ser3_write_time(self, obj);
|
625
|
+
} else if(klass == cDate || klass == cDateTime) {
|
626
|
+
ser3_write_date(self, obj);
|
627
|
+
} else if(klass == cStringIO) {
|
628
|
+
ser3_write_byte_array(self, obj);
|
629
|
+
} else if(type == T_OBJECT) {
|
630
|
+
ser3_write_object(self, obj, Qnil, Qnil);
|
631
|
+
}
|
632
|
+
|
633
|
+
return ser->stream;
|
634
|
+
}
|
635
|
+
|
636
|
+
/*
|
637
|
+
* Mark ruby objects for GC
|
638
|
+
*/
|
639
|
+
static void ser_mark(AMF_SERIALIZER *ser) {
|
640
|
+
if(!ser) return;
|
641
|
+
rb_gc_mark(ser->class_mapper);
|
642
|
+
rb_gc_mark(ser->stream);
|
643
|
+
}
|
644
|
+
|
645
|
+
/*
|
646
|
+
* Free cache tables, stream and the struct itself
|
647
|
+
*/
|
648
|
+
int ser_free_strtable_key(st_data_t key, st_data_t value, st_data_t ignored)
|
649
|
+
{
|
650
|
+
xfree((void *)key);
|
651
|
+
|
652
|
+
return ST_DELETE;
|
653
|
+
}
|
654
|
+
static inline void ser_free_cache(AMF_SERIALIZER *ser) {
|
655
|
+
if(ser->str_cache) {
|
656
|
+
st_foreach(ser->str_cache, ser_free_strtable_key, 0);
|
657
|
+
st_free_table(ser->str_cache);
|
658
|
+
ser->str_cache = NULL;
|
659
|
+
}
|
660
|
+
if(ser->trait_cache) {
|
661
|
+
st_foreach(ser->trait_cache, ser_free_strtable_key, 0);
|
662
|
+
st_free_table(ser->trait_cache);
|
663
|
+
ser->trait_cache = NULL;
|
664
|
+
}
|
665
|
+
if(ser->obj_cache) {
|
666
|
+
st_free_table(ser->obj_cache);
|
667
|
+
ser->obj_cache = NULL;
|
668
|
+
}
|
669
|
+
}
|
670
|
+
static void ser_free(AMF_SERIALIZER *ser) {
|
671
|
+
ser_free_cache(ser);
|
672
|
+
xfree(ser);
|
673
|
+
}
|
674
|
+
|
675
|
+
/*
|
676
|
+
* Create new struct and wrap with class
|
677
|
+
*/
|
678
|
+
static VALUE ser_alloc(VALUE klass) {
|
679
|
+
// Allocate struct
|
680
|
+
AMF_SERIALIZER *ser = ALLOC(AMF_SERIALIZER);
|
681
|
+
memset(ser, 0, sizeof(AMF_SERIALIZER));
|
682
|
+
return Data_Wrap_Struct(klass, ser_mark, ser_free, ser);
|
683
|
+
}
|
684
|
+
|
685
|
+
/*
|
686
|
+
* Initializer
|
687
|
+
*/
|
688
|
+
static VALUE ser_initialize(VALUE self, VALUE class_mapper) {
|
689
|
+
AMF_SERIALIZER *ser;
|
690
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
691
|
+
|
692
|
+
ser->class_mapper = class_mapper;
|
693
|
+
ser->depth = 0;
|
694
|
+
ser->stream = rb_str_buf_new(0);
|
695
|
+
|
696
|
+
return self;
|
697
|
+
}
|
698
|
+
|
699
|
+
/*
|
700
|
+
* call-seq:
|
701
|
+
* ser.version => int
|
702
|
+
*
|
703
|
+
* Returns the serializer version number, so that a custom encode_amf method
|
704
|
+
* knows which version to encode for
|
705
|
+
*/
|
706
|
+
static VALUE ser_version(VALUE self) {
|
707
|
+
AMF_SERIALIZER *ser;
|
708
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
709
|
+
return INT2FIX(ser->version);
|
710
|
+
}
|
711
|
+
|
712
|
+
/*
|
713
|
+
* call-seq:
|
714
|
+
* ser.stream => string
|
715
|
+
*
|
716
|
+
* Returns the string that the serializer is writing to
|
717
|
+
*/
|
718
|
+
static VALUE ser_stream(VALUE self) {
|
719
|
+
AMF_SERIALIZER *ser;
|
720
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
721
|
+
return ser->stream;
|
722
|
+
}
|
723
|
+
|
724
|
+
/*
|
725
|
+
* call-seq:
|
726
|
+
* ser.serialize(amf_ver, obj) => string
|
727
|
+
*
|
728
|
+
* Serialize the given object to the current stream and returns the stream
|
729
|
+
*/
|
730
|
+
VALUE ser_serialize(VALUE self, VALUE ver, VALUE obj) {
|
731
|
+
AMF_SERIALIZER *ser;
|
732
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
733
|
+
|
734
|
+
// Process version
|
735
|
+
int int_ver = FIX2INT(ver);
|
736
|
+
if(int_ver != 0 && int_ver != 3) rb_raise(rb_eArgError, "unsupported version %d", int_ver);
|
737
|
+
ser->version = int_ver;
|
738
|
+
|
739
|
+
// Initialize caches
|
740
|
+
if(ser->depth == 0) {
|
741
|
+
ser->obj_cache = st_init_numtable();
|
742
|
+
ser->obj_index = 0;
|
743
|
+
if(ser->version == 3) {
|
744
|
+
ser->str_cache = st_init_strtable();
|
745
|
+
ser->str_index = 0;
|
746
|
+
ser->trait_cache = st_init_strtable();
|
747
|
+
ser->trait_index = 0;
|
748
|
+
}
|
749
|
+
}
|
750
|
+
ser->depth++;
|
751
|
+
|
752
|
+
// Perform serialization
|
753
|
+
if(ser->version == 0) {
|
754
|
+
ser0_serialize(self, obj);
|
755
|
+
} else {
|
756
|
+
ser3_serialize(self, obj);
|
757
|
+
}
|
758
|
+
|
759
|
+
// Clean up
|
760
|
+
ser->depth--;
|
761
|
+
if(ser->depth == 0) ser_free_cache(ser);
|
762
|
+
|
763
|
+
return ser->stream;
|
764
|
+
}
|
765
|
+
|
766
|
+
/*
|
767
|
+
* call-seq:
|
768
|
+
* ser.write_array(ary) => ser
|
769
|
+
*
|
770
|
+
* Serializes the given array to the serializer stream
|
771
|
+
*/
|
772
|
+
static VALUE ser_write_array(VALUE self, VALUE ary) {
|
773
|
+
AMF_SERIALIZER *ser;
|
774
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
775
|
+
if(ser->version == 0) {
|
776
|
+
ser0_write_array(self, ary);
|
777
|
+
} else {
|
778
|
+
ser3_write_array(self, ary);
|
779
|
+
}
|
780
|
+
return self;
|
781
|
+
}
|
782
|
+
|
783
|
+
/*
|
784
|
+
* call-seq:
|
785
|
+
* ser.write_object(obj, props=nil) => ser
|
786
|
+
* ser.write_object(obj, props=nil, traits=nil) => ser
|
787
|
+
*
|
788
|
+
* Serializes the given object or hash to the serializer stream using
|
789
|
+
* the proper serializer version. If given a props hash, uses that
|
790
|
+
* instead of using the class mapper to calculate it. If given a traits
|
791
|
+
* hash for AMF3, uses that instead of the default dynamic traits with
|
792
|
+
* the mapped class name.
|
793
|
+
*/
|
794
|
+
static VALUE ser_write_object(int argc, VALUE *argv, VALUE self) {
|
795
|
+
AMF_SERIALIZER *ser;
|
796
|
+
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
797
|
+
|
798
|
+
// Check args and call implementation
|
799
|
+
VALUE obj;
|
800
|
+
VALUE props = Qnil;
|
801
|
+
VALUE traits = Qnil;
|
802
|
+
if(ser->version == 0) {
|
803
|
+
rb_scan_args(argc, argv, "11", &obj, &props);
|
804
|
+
ser0_write_object(self, obj, props);
|
805
|
+
} else {
|
806
|
+
rb_scan_args(argc, argv, "12", &obj, &props, &traits);
|
807
|
+
ser3_write_object(self, obj, props, traits);
|
808
|
+
}
|
809
|
+
|
810
|
+
return self;
|
811
|
+
}
|
812
|
+
|
813
|
+
void Init_rocket_amf_serializer() {
|
814
|
+
// Define Serializer
|
815
|
+
cSerializer = rb_define_class_under(mRocketAMFExt, "Serializer", rb_cObject);
|
816
|
+
rb_define_alloc_func(cSerializer, ser_alloc);
|
817
|
+
rb_define_method(cSerializer, "initialize", ser_initialize, 1);
|
818
|
+
rb_define_method(cSerializer, "version", ser_version, 0);
|
819
|
+
rb_define_method(cSerializer, "stream", ser_stream, 0);
|
820
|
+
rb_define_method(cSerializer, "serialize", ser_serialize, 2);
|
821
|
+
rb_define_method(cSerializer, "write_array", ser_write_array, 1);
|
822
|
+
rb_define_method(cSerializer, "write_object", ser_write_object, -1);
|
823
|
+
|
824
|
+
// Get refs to commonly used symbols and ids
|
825
|
+
id_haskey = rb_intern("has_key?");
|
826
|
+
id_encode_amf = rb_intern("encode_amf");
|
827
|
+
id_is_array_collection = rb_intern("is_array_collection?");
|
828
|
+
id_use_array_collection = rb_intern("use_array_collection");
|
829
|
+
id_get_as_class_name = rb_intern("get_as_class_name");
|
830
|
+
id_props_for_serialization = rb_intern("props_for_serialization");
|
831
|
+
id_utc = rb_intern("utc");
|
832
|
+
id_to_f = rb_intern("to_f");
|
833
|
+
id_is_integer = rb_intern("integer?");
|
834
|
+
}
|