blob: 20b48b931b7450454c7bd06a61114de29228d664 [file] [log] [blame]
Bryan Duxburyc0166282009-02-02 00:48:17 +00001#include <struct.h>
2#include <constants.h>
3
Bryan Duxbury1e80d442009-02-03 18:16:54 +00004#ifndef HAVE_STRLCPY
5
6static
7size_t
8strlcpy (char *dst, const char *src, size_t dst_sz)
9{
10 size_t n;
11
12 for (n = 0; n < dst_sz; n++) {
13 if ((*dst++ = *src++) == '\0')
14 break;
15 }
16
17 if (n < dst_sz)
18 return n;
19 if (n > 0)
20 *(dst - 1) = '\0';
21 return n + strlen (src);
22}
23
24#endif
25
Bryan Duxburyc0166282009-02-02 00:48:17 +000026static native_proto_method_table *mt;
Bryan Duxburyd815c212009-03-19 18:57:43 +000027static native_proto_method_table *default_mt;
28static VALUE last_proto_class = Qnil;
Bryan Duxburyc0166282009-02-02 00:48:17 +000029
30#define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
31#define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
32
Bryan Duxburyd815c212009-03-19 18:57:43 +000033static void set_native_proto_function_pointers(VALUE protocol) {
34 VALUE method_table_object = rb_const_get(CLASS_OF(protocol), rb_intern("@native_method_table"));
35 // TODO: check nil?
36 Data_Get_Struct(method_table_object, native_proto_method_table, mt);
37}
38
39static void check_native_proto_method_table(VALUE protocol) {
40 VALUE protoclass = CLASS_OF(protocol);
41 if (protoclass != last_proto_class) {
42 last_proto_class = protoclass;
43 if (rb_funcall(protocol, native_qmark_method_id, 0) == Qtrue) {
44 set_native_proto_function_pointers(protocol);
45 } else {
46 mt = default_mt;
47 }
48 }
49}
50
Bryan Duxburyc0166282009-02-02 00:48:17 +000051//-------------------------------------------
52// Writing section
53//-------------------------------------------
54
55// default fn pointers for protocol stuff here
56
57VALUE default_write_bool(VALUE protocol, VALUE value) {
58 rb_funcall(protocol, write_boolean_method_id, 1, value);
59 return Qnil;
60}
61
62VALUE default_write_byte(VALUE protocol, VALUE value) {
63 rb_funcall(protocol, write_byte_method_id, 1, value);
64 return Qnil;
65}
66
67VALUE default_write_i16(VALUE protocol, VALUE value) {
68 rb_funcall(protocol, write_i16_method_id, 1, value);
69 return Qnil;
70}
71
72VALUE default_write_i32(VALUE protocol, VALUE value) {
73 rb_funcall(protocol, write_i32_method_id, 1, value);
74 return Qnil;
75}
76
77VALUE default_write_i64(VALUE protocol, VALUE value) {
78 rb_funcall(protocol, write_i64_method_id, 1, value);
79 return Qnil;
80}
81
82VALUE default_write_double(VALUE protocol, VALUE value) {
83 rb_funcall(protocol, write_double_method_id, 1, value);
84 return Qnil;
85}
86
87VALUE default_write_string(VALUE protocol, VALUE value) {
88 rb_funcall(protocol, write_string_method_id, 1, value);
89 return Qnil;
90}
91
92VALUE default_write_list_begin(VALUE protocol, VALUE etype, VALUE length) {
93 rb_funcall(protocol, write_list_begin_method_id, 2, etype, length);
94 return Qnil;
95}
96
97VALUE default_write_list_end(VALUE protocol) {
98 rb_funcall(protocol, write_list_end_method_id, 0);
99 return Qnil;
100}
101
102VALUE default_write_set_begin(VALUE protocol, VALUE etype, VALUE length) {
103 rb_funcall(protocol, write_set_begin_method_id, 2, etype, length);
104 return Qnil;
105}
106
107VALUE default_write_set_end(VALUE protocol) {
108 rb_funcall(protocol, write_set_end_method_id, 0);
109 return Qnil;
110}
111
112VALUE default_write_map_begin(VALUE protocol, VALUE ktype, VALUE vtype, VALUE length) {
113 rb_funcall(protocol, write_map_begin_method_id, 3, ktype, vtype, length);
114 return Qnil;
115}
116
117VALUE default_write_map_end(VALUE protocol) {
118 rb_funcall(protocol, write_map_end_method_id, 0);
119 return Qnil;
120}
121
122VALUE default_write_struct_begin(VALUE protocol, VALUE struct_name) {
123 rb_funcall(protocol, write_struct_begin_method_id, 1, struct_name);
124 return Qnil;
125}
126
127VALUE default_write_struct_end(VALUE protocol) {
128 rb_funcall(protocol, write_struct_end_method_id, 0);
129 return Qnil;
130}
131
132VALUE default_write_field_begin(VALUE protocol, VALUE name, VALUE type, VALUE id) {
133 rb_funcall(protocol, write_field_begin_method_id, 3, name, type, id);
134 return Qnil;
135}
136
137VALUE default_write_field_end(VALUE protocol) {
138 rb_funcall(protocol, write_field_end_method_id, 0);
139 return Qnil;
140}
141
142VALUE default_write_field_stop(VALUE protocol) {
143 rb_funcall(protocol, write_field_stop_method_id, 0);
144 return Qnil;
145}
146
147VALUE default_read_field_begin(VALUE protocol) {
148 return rb_funcall(protocol, read_field_begin_method_id, 0);
149}
150
151VALUE default_read_field_end(VALUE protocol) {
152 return rb_funcall(protocol, read_field_end_method_id, 0);
153}
154
155VALUE default_read_map_begin(VALUE protocol) {
156 return rb_funcall(protocol, read_map_begin_method_id, 0);
157}
158
159VALUE default_read_map_end(VALUE protocol) {
160 return rb_funcall(protocol, read_map_end_method_id, 0);
161}
162
163VALUE default_read_list_begin(VALUE protocol) {
164 return rb_funcall(protocol, read_list_begin_method_id, 0);
165}
166
167VALUE default_read_list_end(VALUE protocol) {
168 return rb_funcall(protocol, read_list_end_method_id, 0);
169}
170
171VALUE default_read_set_begin(VALUE protocol) {
172 return rb_funcall(protocol, read_set_begin_method_id, 0);
173}
174
175VALUE default_read_set_end(VALUE protocol) {
176 return rb_funcall(protocol, read_set_end_method_id, 0);
177}
178
179VALUE default_read_byte(VALUE protocol) {
180 return rb_funcall(protocol, read_byte_method_id, 0);
181}
182
183VALUE default_read_bool(VALUE protocol) {
184 return rb_funcall(protocol, read_bool_method_id, 0);
185}
186
187VALUE default_read_i16(VALUE protocol) {
188 return rb_funcall(protocol, read_i16_method_id, 0);
189}
190
191VALUE default_read_i32(VALUE protocol) {
192 return rb_funcall(protocol, read_i32_method_id, 0);
193}
194
195VALUE default_read_i64(VALUE protocol) {
196 return rb_funcall(protocol, read_i64_method_id, 0);
197}
198
199VALUE default_read_double(VALUE protocol) {
200 return rb_funcall(protocol, read_double_method_id, 0);
201}
202
203VALUE default_read_string(VALUE protocol) {
204 return rb_funcall(protocol, read_string_method_id, 0);
205}
206
207VALUE default_read_struct_begin(VALUE protocol) {
208 return rb_funcall(protocol, read_struct_begin_method_id, 0);
209}
210
211VALUE default_read_struct_end(VALUE protocol) {
212 return rb_funcall(protocol, read_struct_end_method_id, 0);
213}
214
215static void set_default_proto_function_pointers() {
Bryan Duxburyd815c212009-03-19 18:57:43 +0000216 default_mt = ALLOC(native_proto_method_table);
Bryan Duxburyc0166282009-02-02 00:48:17 +0000217
Bryan Duxburyd815c212009-03-19 18:57:43 +0000218 default_mt->write_field_begin = default_write_field_begin;
219 default_mt->write_field_stop = default_write_field_stop;
220 default_mt->write_map_begin = default_write_map_begin;
221 default_mt->write_map_end = default_write_map_end;
222 default_mt->write_list_begin = default_write_list_begin;
223 default_mt->write_list_end = default_write_list_end;
224 default_mt->write_set_begin = default_write_set_begin;
225 default_mt->write_set_end = default_write_set_end;
226 default_mt->write_byte = default_write_byte;
227 default_mt->write_bool = default_write_bool;
228 default_mt->write_i16 = default_write_i16;
229 default_mt->write_i32 = default_write_i32;
230 default_mt->write_i64 = default_write_i64;
231 default_mt->write_double = default_write_double;
232 default_mt->write_string = default_write_string;
233 default_mt->write_struct_begin = default_write_struct_begin;
234 default_mt->write_struct_end = default_write_struct_end;
235 default_mt->write_field_end = default_write_field_end;
Bryan Duxburyc0166282009-02-02 00:48:17 +0000236
Bryan Duxburyd815c212009-03-19 18:57:43 +0000237 default_mt->read_struct_begin = default_read_struct_begin;
238 default_mt->read_struct_end = default_read_struct_end;
239 default_mt->read_field_begin = default_read_field_begin;
240 default_mt->read_field_end = default_read_field_end;
241 default_mt->read_map_begin = default_read_map_begin;
242 default_mt->read_map_end = default_read_map_end;
243 default_mt->read_list_begin = default_read_list_begin;
244 default_mt->read_list_end = default_read_list_end;
245 default_mt->read_set_begin = default_read_set_begin;
246 default_mt->read_set_end = default_read_set_end;
247 default_mt->read_byte = default_read_byte;
248 default_mt->read_bool = default_read_bool;
249 default_mt->read_i16 = default_read_i16;
250 default_mt->read_i32 = default_read_i32;
251 default_mt->read_i64 = default_read_i64;
252 default_mt->read_double = default_read_double;
253 default_mt->read_string = default_read_string;
Bryan Duxburyc0166282009-02-02 00:48:17 +0000254}
255
256// end default protocol methods
257
258
259static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
260static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info);
261
262VALUE get_field_value(VALUE obj, VALUE field_name) {
263 char name_buf[RSTRING(field_name)->len + 1];
264
265 name_buf[0] = '@';
266 strlcpy(&name_buf[1], RSTRING(field_name)->ptr, sizeof(name_buf));
267
268 VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
269
270 return value;
271}
272
273static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) {
274 int sz, i;
275
276 if (ttype == TTYPE_MAP) {
277 VALUE keys;
278 VALUE key;
279 VALUE val;
280
281 Check_Type(value, T_HASH);
282
283 VALUE key_info = rb_hash_aref(field_info, key_sym);
284 VALUE keytype_value = rb_hash_aref(key_info, type_sym);
285 int keytype = FIX2INT(keytype_value);
286
287 VALUE value_info = rb_hash_aref(field_info, value_sym);
288 VALUE valuetype_value = rb_hash_aref(value_info, type_sym);
289 int valuetype = FIX2INT(valuetype_value);
290
291 keys = rb_funcall(value, keys_method_id, 0);
292
293 sz = RARRAY(keys)->len;
294
295 mt->write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
296
297 for (i = 0; i < sz; i++) {
298 key = rb_ary_entry(keys, i);
299 val = rb_hash_aref(value, key);
300
301 if (IS_CONTAINER(keytype)) {
302 write_container(keytype, key_info, key, protocol);
303 } else {
304 write_anything(keytype, key, protocol, key_info);
305 }
306
307 if (IS_CONTAINER(valuetype)) {
308 write_container(valuetype, value_info, val, protocol);
309 } else {
310 write_anything(valuetype, val, protocol, value_info);
311 }
312 }
313
314 mt->write_map_end(protocol);
315 } else if (ttype == TTYPE_LIST) {
316 Check_Type(value, T_ARRAY);
317
318 sz = RARRAY(value)->len;
319
320 VALUE element_type_info = rb_hash_aref(field_info, element_sym);
321 VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
322 int element_type = FIX2INT(element_type_value);
323
324 mt->write_list_begin(protocol, element_type_value, INT2FIX(sz));
325 for (i = 0; i < sz; ++i) {
326 VALUE val = rb_ary_entry(value, i);
327 if (IS_CONTAINER(element_type)) {
328 write_container(element_type, element_type_info, val, protocol);
329 } else {
330 write_anything(element_type, val, protocol, element_type_info);
331 }
332 }
333 mt->write_list_end(protocol);
334 } else if (ttype == TTYPE_SET) {
335 VALUE items;
336
337 if (TYPE(value) == T_ARRAY) {
338 items = value;
339 } else {
340 if (rb_cSet == CLASS_OF(value)) {
341 items = rb_funcall(value, entries_method_id, 0);
342 } else {
343 Check_Type(value, T_HASH);
344 items = rb_funcall(value, keys_method_id, 0);
345 }
346 }
347
348 sz = RARRAY(items)->len;
349
350 VALUE element_type_info = rb_hash_aref(field_info, element_sym);
351 VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
352 int element_type = FIX2INT(element_type_value);
353
354 mt->write_set_begin(protocol, element_type_value, INT2FIX(sz));
355
356 for (i = 0; i < sz; i++) {
357 VALUE val = rb_ary_entry(items, i);
358 if (IS_CONTAINER(element_type)) {
359 write_container(element_type, element_type_info, val, protocol);
360 } else {
361 write_anything(element_type, val, protocol, element_type_info);
362 }
363 }
364
365 mt->write_set_end(protocol);
366 } else {
367 rb_raise(rb_eNotImpError, "can't write container of type: %d", ttype);
368 }
369}
370
371static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) {
372 if (ttype == TTYPE_BOOL) {
373 mt->write_bool(protocol, value);
374 } else if (ttype == TTYPE_BYTE) {
375 mt->write_byte(protocol, value);
376 } else if (ttype == TTYPE_I16) {
377 mt->write_i16(protocol, value);
378 } else if (ttype == TTYPE_I32) {
379 mt->write_i32(protocol, value);
380 } else if (ttype == TTYPE_I64) {
381 mt->write_i64(protocol, value);
382 } else if (ttype == TTYPE_DOUBLE) {
383 mt->write_double(protocol, value);
384 } else if (ttype == TTYPE_STRING) {
385 mt->write_string(protocol, value);
386 } else if (IS_CONTAINER(ttype)) {
387 write_container(ttype, field_info, value, protocol);
388 } else if (ttype == TTYPE_STRUCT) {
389 rb_thrift_struct_write(value, protocol);
390 } else {
391 rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
392 }
393}
394
395static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
396 // call validate
397 rb_funcall(self, validate_method_id, 0);
398
Bryan Duxburyd815c212009-03-19 18:57:43 +0000399 // if (rb_funcall(protocol, native_qmark_method_id, 0) == Qtrue) {
400 // set_native_proto_function_pointers(protocol);
401 // } else {
402 // set_default_proto_function_pointers();
403 // }
404 check_native_proto_method_table(protocol);
Bryan Duxburyc0166282009-02-02 00:48:17 +0000405
406 // write struct begin
407 mt->write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
408
409 // iterate through all the fields here
410 VALUE struct_fields = STRUCT_FIELDS(self);
411 VALUE struct_field_ids_unordered = rb_funcall(struct_fields, keys_method_id, 0);
412 VALUE struct_field_ids_ordered = rb_funcall(struct_field_ids_unordered, sort_method_id, 0);
413
414 int i = 0;
415 for (i=0; i < RARRAY(struct_field_ids_ordered)->len; i++) {
416 VALUE field_id = rb_ary_entry(struct_field_ids_ordered, i);
417 VALUE field_info = rb_hash_aref(struct_fields, field_id);
418
419 VALUE ttype_value = rb_hash_aref(field_info, type_sym);
420 int ttype = FIX2INT(ttype_value);
421 VALUE field_name = rb_hash_aref(field_info, name_sym);
422 VALUE field_value = get_field_value(self, field_name);
423
424 if (!NIL_P(field_value)) {
425 mt->write_field_begin(protocol, field_name, ttype_value, field_id);
426
427 write_anything(ttype, field_value, protocol, field_info);
428
429 mt->write_field_end(protocol);
430 }
431 }
432
433 mt->write_field_stop(protocol);
434
435 // write struct end
436 mt->write_struct_end(protocol);
437
438 return Qnil;
439}
440
441//-------------------------------------------
442// Reading section
443//-------------------------------------------
444
445static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
446
447static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
448 char name_buf[RSTRING(field_name)->len + 1];
449
450 name_buf[0] = '@';
451 strlcpy(&name_buf[1], RSTRING(field_name)->ptr, sizeof(name_buf));
452
453 rb_ivar_set(obj, rb_intern(name_buf), value);
454}
455
456static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
457 VALUE result = Qnil;
458
459 if (ttype == TTYPE_BOOL) {
460 result = mt->read_bool(protocol);
461 } else if (ttype == TTYPE_BYTE) {
462 result = mt->read_byte(protocol);
463 } else if (ttype == TTYPE_I16) {
464 result = mt->read_i16(protocol);
465 } else if (ttype == TTYPE_I32) {
466 result = mt->read_i32(protocol);
467 } else if (ttype == TTYPE_I64) {
468 result = mt->read_i64(protocol);
469 } else if (ttype == TTYPE_STRING) {
470 result = mt->read_string(protocol);
471 } else if (ttype == TTYPE_DOUBLE) {
472 result = mt->read_double(protocol);
473 } else if (ttype == TTYPE_STRUCT) {
474 VALUE klass = rb_hash_aref(field_info, class_sym);
475 result = rb_class_new_instance(0, NULL, klass);
476 rb_thrift_struct_read(result, protocol);
477 } else if (ttype == TTYPE_MAP) {
478 int i;
479
480 VALUE map_header = mt->read_map_begin(protocol);
481 int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
482 int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
483 int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
484
485 VALUE key_info = rb_hash_aref(field_info, key_sym);
486 VALUE value_info = rb_hash_aref(field_info, value_sym);
487
488 result = rb_hash_new();
489
490 for (i = 0; i < num_entries; ++i) {
491 VALUE key, val;
492
493 key = read_anything(protocol, key_ttype, key_info);
494 val = read_anything(protocol, value_ttype, value_info);
495
496 rb_hash_aset(result, key, val);
497 }
498
499 mt->read_map_end(protocol);
500 } else if (ttype == TTYPE_LIST) {
501 int i;
502
503 VALUE list_header = mt->read_list_begin(protocol);
504 int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
505 int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
506 result = rb_ary_new2(num_elements);
507
508 for (i = 0; i < num_elements; ++i) {
509 rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
510 }
511
512
513 mt->read_list_end(protocol);
514 } else if (ttype == TTYPE_SET) {
515 VALUE items;
516 int i;
517
518 VALUE set_header = mt->read_set_begin(protocol);
519 int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
520 int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
521 items = rb_ary_new2(num_elements);
522
523 for (i = 0; i < num_elements; ++i) {
524 rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
525 }
526
527
528 mt->read_set_end(protocol);
529
530 result = rb_class_new_instance(1, &items, rb_cSet);
531 } else {
532 rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
533 }
534
535 return result;
536}
537
538static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
Bryan Duxburyd815c212009-03-19 18:57:43 +0000539 check_native_proto_method_table(protocol);
540
Bryan Duxburyc0166282009-02-02 00:48:17 +0000541 // read struct begin
542 mt->read_struct_begin(protocol);
543
544 VALUE struct_fields = STRUCT_FIELDS(self);
545
546 // read each field
547 while (true) {
548 VALUE field_header = rb_funcall(protocol, read_field_begin_method_id, 0);
549 VALUE field_type_value = rb_ary_entry(field_header, 1);
550 int field_type = FIX2INT(field_type_value);
551
552 if (field_type == TTYPE_STOP) {
553 break;
554 }
555
556 // make sure we got a type we expected
557 VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
558
559 if (!NIL_P(field_info)) {
560 int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
561 if (field_type == specified_type) {
562 // read the value
563 VALUE name = rb_hash_aref(field_info, name_sym);
564 set_field_value(self, name, read_anything(protocol, field_type, field_info));
565 } else {
566 rb_funcall(protocol, skip_method_id, 1, field_type_value);
567 }
568 } else {
569 rb_funcall(protocol, skip_method_id, 1, field_type_value);
570 }
571
572 // read field end
573 mt->read_field_end(protocol);
574 }
575
576 // read struct end
577 mt->read_struct_end(protocol);
578
579 return Qnil;
580}
581
582void Init_struct() {
583 VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
584
585 rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
586 rb_define_method(struct_module, "read", rb_thrift_struct_read, 1);
587
588 set_default_proto_function_pointers();
589}
590