1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include "struct.h"
21 #include "constants.h"
22 #include "macros.h"
23 #include "strlcpy.h"
24 
25 VALUE thrift_union_class;
26 
27 ID setfield_id;
28 ID setvalue_id;
29 
30 ID to_s_method_id;
31 ID name_to_id_method_id;
32 static ID sorted_field_ids_method_id;
33 
34 #define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
35 #define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
36 
37 //-------------------------------------------
38 // Writing section
39 //-------------------------------------------
40 
41 // default fn pointers for protocol stuff here
42 
default_write_bool(VALUE protocol,VALUE value)43 VALUE default_write_bool(VALUE protocol, VALUE value) {
44   rb_funcall(protocol, write_boolean_method_id, 1, value);
45   return Qnil;
46 }
47 
default_write_byte(VALUE protocol,VALUE value)48 VALUE default_write_byte(VALUE protocol, VALUE value) {
49   rb_funcall(protocol, write_byte_method_id, 1, value);
50   return Qnil;
51 }
52 
default_write_i16(VALUE protocol,VALUE value)53 VALUE default_write_i16(VALUE protocol, VALUE value) {
54   rb_funcall(protocol, write_i16_method_id, 1, value);
55   return Qnil;
56 }
57 
default_write_i32(VALUE protocol,VALUE value)58 VALUE default_write_i32(VALUE protocol, VALUE value) {
59   rb_funcall(protocol, write_i32_method_id, 1, value);
60   return Qnil;
61 }
62 
default_write_i64(VALUE protocol,VALUE value)63 VALUE default_write_i64(VALUE protocol, VALUE value) {
64   rb_funcall(protocol, write_i64_method_id, 1, value);
65   return Qnil;
66 }
67 
default_write_double(VALUE protocol,VALUE value)68 VALUE default_write_double(VALUE protocol, VALUE value) {
69   rb_funcall(protocol, write_double_method_id, 1, value);
70   return Qnil;
71 }
72 
default_write_string(VALUE protocol,VALUE value)73 VALUE default_write_string(VALUE protocol, VALUE value) {
74   rb_funcall(protocol, write_string_method_id, 1, value);
75   return Qnil;
76 }
77 
default_write_binary(VALUE protocol,VALUE value)78 VALUE default_write_binary(VALUE protocol, VALUE value) {
79   rb_funcall(protocol, write_binary_method_id, 1, value);
80   return Qnil;
81 }
82 
default_write_list_begin(VALUE protocol,VALUE etype,VALUE length)83 VALUE default_write_list_begin(VALUE protocol, VALUE etype, VALUE length) {
84   rb_funcall(protocol, write_list_begin_method_id, 2, etype, length);
85   return Qnil;
86 }
87 
default_write_list_end(VALUE protocol)88 VALUE default_write_list_end(VALUE protocol) {
89   rb_funcall(protocol, write_list_end_method_id, 0);
90   return Qnil;
91 }
92 
default_write_set_begin(VALUE protocol,VALUE etype,VALUE length)93 VALUE default_write_set_begin(VALUE protocol, VALUE etype, VALUE length) {
94   rb_funcall(protocol, write_set_begin_method_id, 2, etype, length);
95   return Qnil;
96 }
97 
default_write_set_end(VALUE protocol)98 VALUE default_write_set_end(VALUE protocol) {
99   rb_funcall(protocol, write_set_end_method_id, 0);
100   return Qnil;
101 }
102 
default_write_map_begin(VALUE protocol,VALUE ktype,VALUE vtype,VALUE length)103 VALUE default_write_map_begin(VALUE protocol, VALUE ktype, VALUE vtype, VALUE length) {
104   rb_funcall(protocol, write_map_begin_method_id, 3, ktype, vtype, length);
105   return Qnil;
106 }
107 
default_write_map_end(VALUE protocol)108 VALUE default_write_map_end(VALUE protocol) {
109   rb_funcall(protocol, write_map_end_method_id, 0);
110   return Qnil;
111 }
112 
default_write_struct_begin(VALUE protocol,VALUE struct_name)113 VALUE default_write_struct_begin(VALUE protocol, VALUE struct_name) {
114   rb_funcall(protocol, write_struct_begin_method_id, 1, struct_name);
115   return Qnil;
116 }
117 
default_write_struct_end(VALUE protocol)118 VALUE default_write_struct_end(VALUE protocol) {
119   rb_funcall(protocol, write_struct_end_method_id, 0);
120   return Qnil;
121 }
122 
default_write_field_begin(VALUE protocol,VALUE name,VALUE type,VALUE id)123 VALUE default_write_field_begin(VALUE protocol, VALUE name, VALUE type, VALUE id) {
124   rb_funcall(protocol, write_field_begin_method_id, 3, name, type, id);
125   return Qnil;
126 }
127 
default_write_field_end(VALUE protocol)128 VALUE default_write_field_end(VALUE protocol) {
129   rb_funcall(protocol, write_field_end_method_id, 0);
130   return Qnil;
131 }
132 
default_write_field_stop(VALUE protocol)133 VALUE default_write_field_stop(VALUE protocol) {
134   rb_funcall(protocol, write_field_stop_method_id, 0);
135   return Qnil;
136 }
137 
default_read_field_begin(VALUE protocol)138 VALUE default_read_field_begin(VALUE protocol) {
139   return rb_funcall(protocol, read_field_begin_method_id, 0);
140 }
141 
default_read_field_end(VALUE protocol)142 VALUE default_read_field_end(VALUE protocol) {
143   return rb_funcall(protocol, read_field_end_method_id, 0);
144 }
145 
default_read_map_begin(VALUE protocol)146 VALUE default_read_map_begin(VALUE protocol) {
147   return rb_funcall(protocol, read_map_begin_method_id, 0);
148 }
149 
default_read_map_end(VALUE protocol)150 VALUE default_read_map_end(VALUE protocol) {
151   return rb_funcall(protocol, read_map_end_method_id, 0);
152 }
153 
default_read_list_begin(VALUE protocol)154 VALUE default_read_list_begin(VALUE protocol) {
155   return rb_funcall(protocol, read_list_begin_method_id, 0);
156 }
157 
default_read_list_end(VALUE protocol)158 VALUE default_read_list_end(VALUE protocol) {
159   return rb_funcall(protocol, read_list_end_method_id, 0);
160 }
161 
default_read_set_begin(VALUE protocol)162 VALUE default_read_set_begin(VALUE protocol) {
163   return rb_funcall(protocol, read_set_begin_method_id, 0);
164 }
165 
default_read_set_end(VALUE protocol)166 VALUE default_read_set_end(VALUE protocol) {
167   return rb_funcall(protocol, read_set_end_method_id, 0);
168 }
169 
default_read_byte(VALUE protocol)170 VALUE default_read_byte(VALUE protocol) {
171   return rb_funcall(protocol, read_byte_method_id, 0);
172 }
173 
default_read_bool(VALUE protocol)174 VALUE default_read_bool(VALUE protocol) {
175   return rb_funcall(protocol, read_bool_method_id, 0);
176 }
177 
default_read_i16(VALUE protocol)178 VALUE default_read_i16(VALUE protocol) {
179   return rb_funcall(protocol, read_i16_method_id, 0);
180 }
181 
default_read_i32(VALUE protocol)182 VALUE default_read_i32(VALUE protocol) {
183   return rb_funcall(protocol, read_i32_method_id, 0);
184 }
185 
default_read_i64(VALUE protocol)186 VALUE default_read_i64(VALUE protocol) {
187   return rb_funcall(protocol, read_i64_method_id, 0);
188 }
189 
default_read_double(VALUE protocol)190 VALUE default_read_double(VALUE protocol) {
191   return rb_funcall(protocol, read_double_method_id, 0);
192 }
193 
default_read_string(VALUE protocol)194 VALUE default_read_string(VALUE protocol) {
195   return rb_funcall(protocol, read_string_method_id, 0);
196 }
197 
default_read_binary(VALUE protocol)198 VALUE default_read_binary(VALUE protocol) {
199   return rb_funcall(protocol, read_binary_method_id, 0);
200 }
201 
default_read_struct_begin(VALUE protocol)202 VALUE default_read_struct_begin(VALUE protocol) {
203   return rb_funcall(protocol, read_struct_begin_method_id, 0);
204 }
205 
default_read_struct_end(VALUE protocol)206 VALUE default_read_struct_end(VALUE protocol) {
207   return rb_funcall(protocol, read_struct_end_method_id, 0);
208 }
209 
210 // end default protocol methods
211 
212 static VALUE rb_thrift_union_write (VALUE self, VALUE protocol);
213 static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
214 static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info);
215 
get_field_value(VALUE obj,VALUE field_name)216 VALUE get_field_value(VALUE obj, VALUE field_name) {
217   char name_buf[RSTRING_LEN(field_name) + 2];
218 
219   name_buf[0] = '@';
220   strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name) + 1);
221 
222   VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
223 
224   return value;
225 }
226 
write_container(int ttype,VALUE field_info,VALUE value,VALUE protocol)227 static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) {
228   int sz, i;
229 
230   if (ttype == TTYPE_MAP) {
231     VALUE keys;
232     VALUE key;
233     VALUE val;
234 
235     Check_Type(value, T_HASH);
236 
237     VALUE key_info = rb_hash_aref(field_info, key_sym);
238     VALUE keytype_value = rb_hash_aref(key_info, type_sym);
239     int keytype = FIX2INT(keytype_value);
240 
241     VALUE value_info = rb_hash_aref(field_info, value_sym);
242     VALUE valuetype_value = rb_hash_aref(value_info, type_sym);
243     int valuetype = FIX2INT(valuetype_value);
244 
245     keys = rb_funcall(value, keys_method_id, 0);
246 
247     sz = RARRAY_LEN(keys);
248 
249     default_write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
250 
251     for (i = 0; i < sz; i++) {
252       key = rb_ary_entry(keys, i);
253       val = rb_hash_aref(value, key);
254 
255       if (IS_CONTAINER(keytype)) {
256         write_container(keytype, key_info, key, protocol);
257       } else {
258         write_anything(keytype, key, protocol, key_info);
259       }
260 
261       if (IS_CONTAINER(valuetype)) {
262         write_container(valuetype, value_info, val, protocol);
263       } else {
264         write_anything(valuetype, val, protocol, value_info);
265       }
266     }
267 
268     default_write_map_end(protocol);
269   } else if (ttype == TTYPE_LIST) {
270     Check_Type(value, T_ARRAY);
271 
272     sz = RARRAY_LEN(value);
273 
274     VALUE element_type_info = rb_hash_aref(field_info, element_sym);
275     VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
276     int element_type = FIX2INT(element_type_value);
277 
278     default_write_list_begin(protocol, element_type_value, INT2FIX(sz));
279     for (i = 0; i < sz; ++i) {
280       VALUE val = rb_ary_entry(value, i);
281       if (IS_CONTAINER(element_type)) {
282         write_container(element_type, element_type_info, val, protocol);
283       } else {
284         write_anything(element_type, val, protocol, element_type_info);
285       }
286     }
287     default_write_list_end(protocol);
288   } else if (ttype == TTYPE_SET) {
289     VALUE items;
290 
291     if (TYPE(value) == T_ARRAY) {
292       items = value;
293     } else {
294       if (rb_cSet == CLASS_OF(value)) {
295         items = rb_funcall(value, entries_method_id, 0);
296       } else {
297         Check_Type(value, T_HASH);
298         items = rb_funcall(value, keys_method_id, 0);
299       }
300     }
301 
302     sz = RARRAY_LEN(items);
303 
304     VALUE element_type_info = rb_hash_aref(field_info, element_sym);
305     VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
306     int element_type = FIX2INT(element_type_value);
307 
308     default_write_set_begin(protocol, element_type_value, INT2FIX(sz));
309 
310     for (i = 0; i < sz; i++) {
311       VALUE val = rb_ary_entry(items, i);
312       if (IS_CONTAINER(element_type)) {
313         write_container(element_type, element_type_info, val, protocol);
314       } else {
315         write_anything(element_type, val, protocol, element_type_info);
316       }
317     }
318 
319     default_write_set_end(protocol);
320   } else {
321     rb_raise(rb_eNotImpError, "can't write container of type: %d", ttype);
322   }
323 }
324 
write_anything(int ttype,VALUE value,VALUE protocol,VALUE field_info)325 static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) {
326   if (ttype == TTYPE_BOOL) {
327     default_write_bool(protocol, value);
328   } else if (ttype == TTYPE_BYTE) {
329     default_write_byte(protocol, value);
330   } else if (ttype == TTYPE_I16) {
331     default_write_i16(protocol, value);
332   } else if (ttype == TTYPE_I32) {
333     default_write_i32(protocol, value);
334   } else if (ttype == TTYPE_I64) {
335     default_write_i64(protocol, value);
336   } else if (ttype == TTYPE_DOUBLE) {
337     default_write_double(protocol, value);
338   } else if (ttype == TTYPE_STRING) {
339     VALUE is_binary = rb_hash_aref(field_info, binary_sym);
340     if (is_binary != Qtrue) {
341       default_write_string(protocol, value);
342     } else {
343       default_write_binary(protocol, value);
344     }
345   } else if (IS_CONTAINER(ttype)) {
346     write_container(ttype, field_info, value, protocol);
347   } else if (ttype == TTYPE_STRUCT) {
348     if (rb_obj_is_kind_of(value, thrift_union_class)) {
349       rb_thrift_union_write(value, protocol);
350     } else {
351       rb_thrift_struct_write(value, protocol);
352     }
353   } else {
354     rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
355   }
356 }
357 
rb_thrift_struct_write(VALUE self,VALUE protocol)358 static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
359   // call validate
360   rb_funcall(self, validate_method_id, 0);
361 
362   // write struct begin
363   default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
364 
365   // iterate through all the fields here
366   VALUE struct_fields = STRUCT_FIELDS(self);
367   VALUE sorted_field_ids = rb_funcall(self, sorted_field_ids_method_id, 0);
368 
369   int i = 0;
370   for (i=0; i < RARRAY_LEN(sorted_field_ids); i++) {
371     VALUE field_id = rb_ary_entry(sorted_field_ids, i);
372 
373     VALUE field_info = rb_hash_aref(struct_fields, field_id);
374 
375     VALUE ttype_value = rb_hash_aref(field_info, type_sym);
376     int ttype = FIX2INT(ttype_value);
377     VALUE field_name = rb_hash_aref(field_info, name_sym);
378 
379     VALUE field_value = get_field_value(self, field_name);
380 
381     if (!NIL_P(field_value)) {
382       default_write_field_begin(protocol, field_name, ttype_value, field_id);
383 
384       write_anything(ttype, field_value, protocol, field_info);
385 
386       default_write_field_end(protocol);
387     }
388   }
389 
390   default_write_field_stop(protocol);
391 
392   // write struct end
393   default_write_struct_end(protocol);
394 
395   return Qnil;
396 }
397 
398 //-------------------------------------------
399 // Reading section
400 //-------------------------------------------
401 
402 static VALUE rb_thrift_union_read(VALUE self, VALUE protocol);
403 static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
404 static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size);
405 static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size);
406 
set_field_value(VALUE obj,VALUE field_name,VALUE value)407 static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
408   char name_buf[RSTRING_LEN(field_name) + 2];
409 
410   name_buf[0] = '@';
411   strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name)+1);
412 
413   rb_ivar_set(obj, rb_intern(name_buf), value);
414 }
415 
416 // Helper method to skip the contents of a map (assumes the map header has been read).
skip_map_contents(VALUE protocol,VALUE key_type_value,VALUE value_type_value,int size)417 static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size) {
418   int i;
419   for (i = 0; i < size; i++) {
420     rb_funcall(protocol, skip_method_id, 1, key_type_value);
421     rb_funcall(protocol, skip_method_id, 1, value_type_value);
422   }
423 }
424 
425 // Helper method to skip the contents of a list or set (assumes the list/set header has been read).
skip_list_or_set_contents(VALUE protocol,VALUE element_type_value,int size)426 static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size) {
427   int i;
428   for (i = 0; i < size; i++) {
429     rb_funcall(protocol, skip_method_id, 1, element_type_value);
430   }
431 }
432 
read_anything(VALUE protocol,int ttype,VALUE field_info)433 static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
434   VALUE result = Qnil;
435 
436   if (ttype == TTYPE_BOOL) {
437     result = default_read_bool(protocol);
438   } else if (ttype == TTYPE_BYTE) {
439     result = default_read_byte(protocol);
440   } else if (ttype == TTYPE_I16) {
441     result = default_read_i16(protocol);
442   } else if (ttype == TTYPE_I32) {
443     result = default_read_i32(protocol);
444   } else if (ttype == TTYPE_I64) {
445     result = default_read_i64(protocol);
446   } else if (ttype == TTYPE_STRING) {
447     VALUE is_binary = rb_hash_aref(field_info, binary_sym);
448     if (is_binary != Qtrue) {
449       result = default_read_string(protocol);
450     } else {
451       result = default_read_binary(protocol);
452     }
453   } else if (ttype == TTYPE_DOUBLE) {
454     result = default_read_double(protocol);
455   } else if (ttype == TTYPE_STRUCT) {
456     VALUE klass = rb_hash_aref(field_info, class_sym);
457     result = rb_class_new_instance(0, NULL, klass);
458 
459     if (rb_obj_is_kind_of(result, thrift_union_class)) {
460       rb_thrift_union_read(result, protocol);
461     } else {
462       rb_thrift_struct_read(result, protocol);
463     }
464   } else if (ttype == TTYPE_MAP) {
465     int i;
466 
467     VALUE map_header = default_read_map_begin(protocol);
468     int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
469     int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
470     int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
471 
472     // Check the declared key and value types against the expected ones and skip the map contents
473     // if the types don't match.
474     VALUE key_info = rb_hash_aref(field_info, key_sym);
475     VALUE value_info = rb_hash_aref(field_info, value_sym);
476 
477     if (!NIL_P(key_info) && !NIL_P(value_info)) {
478       int specified_key_type = FIX2INT(rb_hash_aref(key_info, type_sym));
479       int specified_value_type = FIX2INT(rb_hash_aref(value_info, type_sym));
480       if (num_entries == 0 || (specified_key_type == key_ttype && specified_value_type == value_ttype)) {
481         result = rb_hash_new();
482 
483         for (i = 0; i < num_entries; ++i) {
484           VALUE key, val;
485 
486           key = read_anything(protocol, key_ttype, key_info);
487           val = read_anything(protocol, value_ttype, value_info);
488 
489           rb_hash_aset(result, key, val);
490         }
491       } else {
492         skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
493       }
494     } else {
495       skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
496     }
497 
498     default_read_map_end(protocol);
499   } else if (ttype == TTYPE_LIST) {
500     int i;
501 
502     VALUE list_header = default_read_list_begin(protocol);
503     int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
504     int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
505 
506     // Check the declared element type against the expected one and skip the list contents
507     // if the types don't match.
508     VALUE element_info = rb_hash_aref(field_info, element_sym);
509     if (!NIL_P(element_info)) {
510       int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
511       if (specified_element_type == element_ttype) {
512         result = rb_ary_new2(num_elements);
513 
514         for (i = 0; i < num_elements; ++i) {
515           rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
516         }
517       } else {
518         skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
519       }
520     } else {
521       skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
522     }
523 
524     default_read_list_end(protocol);
525   } else if (ttype == TTYPE_SET) {
526     VALUE items;
527     int i;
528 
529     VALUE set_header = default_read_set_begin(protocol);
530     int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
531     int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
532 
533     // Check the declared element type against the expected one and skip the set contents
534     // if the types don't match.
535     VALUE element_info = rb_hash_aref(field_info, element_sym);
536     if (!NIL_P(element_info)) {
537       int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
538       if (specified_element_type == element_ttype) {
539         items = rb_ary_new2(num_elements);
540 
541         for (i = 0; i < num_elements; ++i) {
542           rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
543         }
544 
545         result = rb_class_new_instance(1, &items, rb_cSet);
546       } else {
547         skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
548       }
549     } else {
550       skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
551     }
552 
553     default_read_set_end(protocol);
554   } else {
555     rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
556   }
557 
558   return result;
559 }
560 
rb_thrift_struct_read(VALUE self,VALUE protocol)561 static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
562   // read struct begin
563   default_read_struct_begin(protocol);
564 
565   VALUE struct_fields = STRUCT_FIELDS(self);
566 
567   // read each field
568   while (true) {
569     VALUE field_header = default_read_field_begin(protocol);
570     VALUE field_type_value = rb_ary_entry(field_header, 1);
571     int field_type = FIX2INT(field_type_value);
572 
573     if (field_type == TTYPE_STOP) {
574       break;
575     }
576 
577     // make sure we got a type we expected
578     VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
579 
580     if (!NIL_P(field_info)) {
581       int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
582       if (field_type == specified_type) {
583         // read the value
584         VALUE name = rb_hash_aref(field_info, name_sym);
585         set_field_value(self, name, read_anything(protocol, field_type, field_info));
586       } else {
587         rb_funcall(protocol, skip_method_id, 1, field_type_value);
588       }
589     } else {
590       rb_funcall(protocol, skip_method_id, 1, field_type_value);
591     }
592 
593     // read field end
594     default_read_field_end(protocol);
595   }
596 
597   // read struct end
598   default_read_struct_end(protocol);
599 
600   // call validate
601   rb_funcall(self, validate_method_id, 0);
602 
603   return Qnil;
604 }
605 
606 
607 // --------------------------------
608 // Union section
609 // --------------------------------
610 
rb_thrift_union_read(VALUE self,VALUE protocol)611 static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) {
612   // read struct begin
613   default_read_struct_begin(protocol);
614 
615   VALUE struct_fields = STRUCT_FIELDS(self);
616 
617   VALUE field_header = default_read_field_begin(protocol);
618   VALUE field_type_value = rb_ary_entry(field_header, 1);
619   int field_type = FIX2INT(field_type_value);
620 
621   // make sure we got a type we expected
622   VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
623 
624   if (!NIL_P(field_info)) {
625     int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
626     if (field_type == specified_type) {
627       // read the value
628       VALUE name = rb_hash_aref(field_info, name_sym);
629       rb_iv_set(self, "@setfield", rb_str_intern(name));
630       rb_iv_set(self, "@value", read_anything(protocol, field_type, field_info));
631     } else {
632       rb_funcall(protocol, skip_method_id, 1, field_type_value);
633     }
634   } else {
635     rb_funcall(protocol, skip_method_id, 1, field_type_value);
636   }
637 
638   // read field end
639   default_read_field_end(protocol);
640 
641   field_header = default_read_field_begin(protocol);
642   field_type_value = rb_ary_entry(field_header, 1);
643   field_type = FIX2INT(field_type_value);
644 
645   if (field_type != TTYPE_STOP) {
646     rb_raise(rb_eRuntimeError, "too many fields in union!");
647   }
648 
649   // read struct end
650   default_read_struct_end(protocol);
651 
652   // call validate
653   rb_funcall(self, validate_method_id, 0);
654 
655   return Qnil;
656 }
657 
rb_thrift_union_write(VALUE self,VALUE protocol)658 static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
659   // call validate
660   rb_funcall(self, validate_method_id, 0);
661 
662   // write struct begin
663   default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
664 
665   VALUE struct_fields = STRUCT_FIELDS(self);
666 
667   VALUE setfield = rb_ivar_get(self, setfield_id);
668   VALUE setvalue = rb_ivar_get(self, setvalue_id);
669   VALUE field_id = rb_funcall(self, name_to_id_method_id, 1, rb_funcall(setfield, to_s_method_id, 0));
670 
671   VALUE field_info = rb_hash_aref(struct_fields, field_id);
672 
673   if(NIL_P(field_info)) {
674     rb_raise(rb_eRuntimeError, "set_field is not valid for this union!");
675   }
676 
677   VALUE ttype_value = rb_hash_aref(field_info, type_sym);
678   int ttype = FIX2INT(ttype_value);
679 
680   default_write_field_begin(protocol, setfield, ttype_value, field_id);
681 
682   write_anything(ttype, setvalue, protocol, field_info);
683 
684   default_write_field_end(protocol);
685 
686   default_write_field_stop(protocol);
687 
688   // write struct end
689   default_write_struct_end(protocol);
690 
691   return Qnil;
692 }
693 
Init_struct()694 void Init_struct() {
695   VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
696 
697   rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
698   rb_define_method(struct_module, "read", rb_thrift_struct_read, 1);
699 
700   thrift_union_class = rb_const_get(thrift_module, rb_intern("Union"));
701 
702   rb_define_method(thrift_union_class, "write", rb_thrift_union_write, 1);
703   rb_define_method(thrift_union_class, "read", rb_thrift_union_read, 1);
704 
705   setfield_id = rb_intern("@setfield");
706   setvalue_id = rb_intern("@value");
707 
708   to_s_method_id = rb_intern("to_s");
709   name_to_id_method_id = rb_intern("name_to_id");
710   sorted_field_ids_method_id = rb_intern("sorted_field_ids");
711 }
712