thrift 0.23.0 → 0.25.0

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +203 -40
  3. data/ext/binary_protocol_accelerated.c +130 -40
  4. data/ext/bytes.c +14 -0
  5. data/ext/compact_protocol.c +184 -91
  6. data/ext/constants.h +3 -0
  7. data/ext/extconf.rb +12 -4
  8. data/ext/macros.h +11 -0
  9. data/ext/memory_buffer.c +67 -12
  10. data/ext/struct.c +192 -71
  11. data/ext/thrift_native.c +7 -0
  12. data/lib/thrift/bytes.rb +6 -3
  13. data/lib/thrift/client.rb +31 -33
  14. data/lib/thrift/exceptions.rb +11 -8
  15. data/lib/thrift/multiplexed_processor.rb +17 -8
  16. data/lib/thrift/processor.rb +25 -3
  17. data/lib/thrift/protocol/base_protocol.rb +66 -31
  18. data/lib/thrift/protocol/binary_protocol.rb +80 -37
  19. data/lib/thrift/protocol/binary_protocol_accelerated.rb +7 -2
  20. data/lib/thrift/protocol/compact_protocol.rb +204 -88
  21. data/lib/thrift/protocol/header_protocol.rb +15 -3
  22. data/lib/thrift/protocol/json_protocol.rb +158 -109
  23. data/lib/thrift/protocol/multiplexed_protocol.rb +3 -3
  24. data/lib/thrift/protocol/protocol_decorator.rb +6 -2
  25. data/lib/thrift/serializer/deserializer.rb +1 -0
  26. data/lib/thrift/serializer/serializer.rb +7 -0
  27. data/lib/thrift/server/base_server.rb +11 -2
  28. data/lib/thrift/server/nonblocking_server.rb +69 -20
  29. data/lib/thrift/server/rack_application.rb +69 -0
  30. data/lib/thrift/server/simple_server.rb +27 -15
  31. data/lib/thrift/server/thin_http_server.rb +21 -44
  32. data/lib/thrift/server/thread_pool_server.rb +21 -17
  33. data/lib/thrift/server/threaded_server.rb +22 -18
  34. data/lib/thrift/struct.rb +34 -18
  35. data/lib/thrift/struct_union.rb +37 -36
  36. data/lib/thrift/thrift_native.rb +2 -1
  37. data/lib/thrift/transport/base_server_transport.rb +3 -0
  38. data/lib/thrift/transport/base_transport.rb +26 -4
  39. data/lib/thrift/transport/buffered_transport.rb +5 -4
  40. data/lib/thrift/transport/framed_transport.rb +33 -7
  41. data/lib/thrift/transport/header_transport.rb +159 -33
  42. data/lib/thrift/transport/http_client_transport.rb +28 -14
  43. data/lib/thrift/transport/io_stream_transport.rb +3 -1
  44. data/lib/thrift/transport/memory_buffer_transport.rb +14 -9
  45. data/lib/thrift/transport/server_socket.rb +10 -8
  46. data/lib/thrift/transport/socket.rb +85 -48
  47. data/lib/thrift/transport/ssl_server_socket.rb +67 -6
  48. data/lib/thrift/transport/ssl_socket.rb +73 -14
  49. data/lib/thrift/transport/unix_server_socket.rb +6 -2
  50. data/lib/thrift/transport/unix_socket.rb +5 -6
  51. data/lib/thrift/types.rb +23 -22
  52. data/lib/thrift/union.rb +21 -11
  53. data/lib/thrift/uuid.rb +9 -8
  54. data/lib/thrift.rb +40 -39
  55. metadata +7 -266
  56. data/benchmark/Benchmark.thrift +0 -24
  57. data/benchmark/benchmark.rb +0 -285
  58. data/benchmark/client.rb +0 -117
  59. data/benchmark/server.rb +0 -120
  60. data/benchmark/thin_server.rb +0 -45
  61. data/lib/thrift/server/mongrel_http_server.rb +0 -60
  62. data/spec/BaseService.thrift +0 -27
  63. data/spec/ExtendedService.thrift +0 -25
  64. data/spec/Referenced.thrift +0 -44
  65. data/spec/ThriftNamespacedSpec.thrift +0 -53
  66. data/spec/ThriftSpec.thrift +0 -187
  67. data/spec/base_protocol_spec.rb +0 -224
  68. data/spec/base_transport_spec.rb +0 -387
  69. data/spec/binary_protocol_accelerated_spec.rb +0 -46
  70. data/spec/binary_protocol_spec.rb +0 -72
  71. data/spec/binary_protocol_spec_shared.rb +0 -445
  72. data/spec/bytes_spec.rb +0 -99
  73. data/spec/client_spec.rb +0 -166
  74. data/spec/compact_protocol_spec.rb +0 -196
  75. data/spec/constants_demo_spec.rb +0 -101
  76. data/spec/exception_spec.rb +0 -140
  77. data/spec/flat_spec.rb +0 -62
  78. data/spec/header_protocol_spec.rb +0 -475
  79. data/spec/header_transport_spec.rb +0 -386
  80. data/spec/http_client_spec.rb +0 -159
  81. data/spec/json_protocol_spec.rb +0 -552
  82. data/spec/namespaced_spec.rb +0 -66
  83. data/spec/nonblocking_server_spec.rb +0 -361
  84. data/spec/processor_spec.rb +0 -79
  85. data/spec/serializer_spec.rb +0 -66
  86. data/spec/server_socket_spec.rb +0 -84
  87. data/spec/server_spec.rb +0 -186
  88. data/spec/socket_spec.rb +0 -67
  89. data/spec/socket_spec_shared.rb +0 -167
  90. data/spec/spec_helper.rb +0 -69
  91. data/spec/ssl_server_socket_spec.rb +0 -45
  92. data/spec/ssl_socket_spec.rb +0 -87
  93. data/spec/struct_nested_containers_spec.rb +0 -190
  94. data/spec/struct_spec.rb +0 -397
  95. data/spec/support/header_protocol_helper.rb +0 -54
  96. data/spec/thin_http_server_spec.rb +0 -126
  97. data/spec/types_spec.rb +0 -117
  98. data/spec/union_spec.rb +0 -272
  99. data/spec/unix_socket_spec.rb +0 -115
  100. data/spec/uuid_validation_spec.rb +0 -238
  101. data/test/fuzz/Makefile.am +0 -173
  102. data/test/fuzz/README.md +0 -149
  103. data/test/fuzz/fuzz_common.rb +0 -95
  104. data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -22
  105. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -22
  106. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -22
  107. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -22
  108. data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -22
  109. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -22
  110. data/test/fuzz/fuzz_parse_json_protocol.rb +0 -22
  111. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -22
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -22
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -22
  114. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -22
  115. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -22
  116. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -22
  117. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -22
  118. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -22
  119. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -22
  120. data/test/fuzz/fuzz_tracer.rb +0 -28
data/ext/memory_buffer.c CHANGED
@@ -28,11 +28,15 @@ ID index_ivar_id;
28
28
  ID slice_method_id;
29
29
 
30
30
  int GARBAGE_BUFFER_SIZE;
31
+ static VALUE transport_exception_class;
32
+ static VALUE transport_negative_size;
33
+ static ID new_method_id;
31
34
 
32
35
  #define GET_BUF(self) rb_ivar_get(self, buf_ivar_id)
33
36
 
34
37
  VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str);
35
38
  VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value);
39
+ VALUE rb_thrift_memory_buffer_read_all(VALUE self, VALUE length_value);
36
40
  VALUE rb_thrift_memory_buffer_read_byte(VALUE self);
37
41
  VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value);
38
42
 
@@ -44,23 +48,28 @@ VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
44
48
  }
45
49
 
46
50
  VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
47
- int length = FIX2INT(length_value);
51
+ long long length = NUM2LL(length_value);
52
+
53
+ if (RB_UNLIKELY(length < 0)) {
54
+ rb_exc_raise(rb_funcall(transport_exception_class, new_method_id, 2, transport_negative_size, rb_str_new2("Negative size")));
55
+ }
48
56
 
49
57
  VALUE index_value = rb_ivar_get(self, index_ivar_id);
50
- int index = FIX2INT(index_value);
58
+ long index = NUM2LONG(index_value);
51
59
 
52
60
  VALUE buf = GET_BUF(self);
53
61
  VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value);
54
62
 
55
- index += length;
56
- if (index > RSTRING_LEN(buf)) {
57
- index = (int)RSTRING_LEN(buf);
63
+ if (length > RSTRING_LEN(buf) - index) {
64
+ index = RSTRING_LEN(buf);
65
+ } else {
66
+ index += (long)length;
58
67
  }
59
68
  if (index >= GARBAGE_BUFFER_SIZE) {
60
- rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
69
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, LONG2NUM(index), LONG2NUM(RSTRING_LEN(buf) - 1)));
61
70
  index = 0;
62
71
  }
63
- rb_ivar_set(self, index_ivar_id, INT2FIX(index));
72
+ rb_ivar_set(self, index_ivar_id, LONG2NUM(index));
64
73
 
65
74
  if (RSTRING_LEN(data) < length) {
66
75
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
@@ -69,6 +78,33 @@ VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
69
78
  return data;
70
79
  }
71
80
 
81
+ VALUE rb_thrift_memory_buffer_read_all(VALUE self, VALUE length_value) {
82
+ long long length = NUM2LL(length_value);
83
+
84
+ if (RB_UNLIKELY(length < 0)) {
85
+ rb_exc_raise(rb_funcall(transport_exception_class, new_method_id, 2, transport_negative_size, rb_str_new2("Negative size")));
86
+ }
87
+
88
+ VALUE index_value = rb_ivar_get(self, index_ivar_id);
89
+ long index = NUM2LONG(index_value);
90
+ VALUE buf = GET_BUF(self);
91
+
92
+ if (RB_UNLIKELY(length > RSTRING_LEN(buf) - index)) {
93
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
94
+ }
95
+
96
+ VALUE data = rb_str_subseq(buf, index, (long)length);
97
+
98
+ index += (long)length;
99
+ if (index >= GARBAGE_BUFFER_SIZE) {
100
+ rb_ivar_set(self, buf_ivar_id, rb_str_subseq(buf, index, RSTRING_LEN(buf) - index));
101
+ index = 0;
102
+ }
103
+ rb_ivar_set(self, index_ivar_id, LONG2NUM(index));
104
+
105
+ return data;
106
+ }
107
+
72
108
  VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
73
109
  VALUE index_value = rb_ivar_get(self, index_ivar_id);
74
110
  int index = FIX2INT(index_value);
@@ -77,7 +113,7 @@ VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
77
113
  if (index >= RSTRING_LEN(buf)) {
78
114
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
79
115
  }
80
- char byte = RSTRING_PTR(buf)[index++];
116
+ unsigned char byte = (unsigned char)RSTRING_PTR(buf)[index++];
81
117
 
82
118
  if (index >= GARBAGE_BUFFER_SIZE) {
83
119
  rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
@@ -85,8 +121,7 @@ VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
85
121
  }
86
122
  rb_ivar_set(self, index_ivar_id, INT2FIX(index));
87
123
 
88
- int result = (int) byte;
89
- return INT2FIX(result);
124
+ return INT2FIX(byte);
90
125
  }
91
126
 
92
127
  VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value) {
@@ -96,15 +131,29 @@ VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, V
96
131
  VALUE buf = GET_BUF(self);
97
132
 
98
133
  index = FIX2INT(rb_ivar_get(self, index_ivar_id));
99
- while (i < size) {
134
+ if (size > 0) {
100
135
  if (index >= RSTRING_LEN(buf)) {
136
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
101
137
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
102
138
  }
103
- char byte = RSTRING_PTR(buf)[index++];
139
+ StringValue(buffer_value);
140
+ if (RSTRING_LEN(buffer_value) == 0) {
141
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
142
+ rb_raise(rb_eIndexError, "index 0 out of string");
143
+ }
144
+ rb_str_modify(buffer_value);
145
+ }
104
146
 
147
+ while (i < size) {
148
+ if (index >= RSTRING_LEN(buf)) {
149
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
150
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
151
+ }
105
152
  if (i >= RSTRING_LEN(buffer_value)) {
153
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
106
154
  rb_raise(rb_eIndexError, "index %d out of string", i);
107
155
  }
156
+ char byte = RSTRING_PTR(buf)[index++];
108
157
  ((char*)RSTRING_PTR(buffer_value))[i] = byte;
109
158
  i++;
110
159
  }
@@ -122,6 +171,7 @@ void Init_memory_buffer(void) {
122
171
  VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport"));
123
172
  rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
124
173
  rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
174
+ rb_define_method(thrift_memory_buffer_class, "read_all", rb_thrift_memory_buffer_read_all, 1);
125
175
  rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0);
126
176
  rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2);
127
177
 
@@ -129,6 +179,11 @@ void Init_memory_buffer(void) {
129
179
  index_ivar_id = rb_intern("@index");
130
180
 
131
181
  slice_method_id = rb_intern("slice");
182
+ new_method_id = rb_intern("new");
132
183
 
133
184
  GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE")));
185
+ transport_exception_class = rb_const_get(thrift_module, rb_intern("TransportException"));
186
+ transport_negative_size = rb_const_get(transport_exception_class, rb_intern("NEGATIVE_SIZE"));
187
+ rb_global_variable(&transport_exception_class);
188
+ rb_global_variable(&transport_negative_size);
134
189
  }
data/ext/struct.c CHANGED
@@ -17,6 +17,8 @@
17
17
  * under the License.
18
18
  */
19
19
 
20
+ #include <stdint.h>
21
+
20
22
  #include "struct.h"
21
23
  #include "constants.h"
22
24
  #include "macros.h"
@@ -31,20 +33,63 @@ ID setvalue_id;
31
33
  ID to_s_method_id;
32
34
  ID name_to_id_method_id;
33
35
  static ID sorted_field_ids_method_id;
36
+ static ID validate_container_size_method_id;
37
+ static VALUE default_sym;
34
38
 
35
39
  #define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
36
40
  #define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
41
+ // Default budget when callers do not provide a remaining depth.
42
+ static int recursion_limit;
37
43
 
38
- static VALUE new_container_array(int size) {
39
- if (size < 0) {
44
+ static void validate_recursion_depth(int remaining_depth) {
45
+ if (RB_UNLIKELY(remaining_depth <= 0)) {
40
46
  rb_exc_raise(
41
47
  get_protocol_exception(
42
- INT2FIX(PROTOERR_NEGATIVE_SIZE),
43
- rb_str_new2("Negative container size")
48
+ INT2FIX(PROTOERR_DEPTH_LIMIT),
49
+ rb_str_new2("Maximum recursion depth exceeded")
44
50
  )
45
51
  );
46
52
  }
53
+ }
54
+
55
+ static int parse_recursive_args(int argc, const VALUE *argv, VALUE *protocol) {
56
+ if (RB_UNLIKELY(argc < 1 || argc > 2)) {
57
+ rb_error_arity(argc, 1, 2);
58
+ }
59
+
60
+ *protocol = argv[0];
61
+ int remaining_depth = argc == 1 ? recursion_limit : NUM2INT(argv[1]);
62
+ validate_recursion_depth(remaining_depth);
63
+ return remaining_depth;
64
+ }
65
+
66
+ static int container_size(VALUE protocol, VALUE size) {
67
+ if (RB_LIKELY(FIXNUM_P(size))) {
68
+ long value = FIX2LONG(size);
69
+ if (RB_UNLIKELY(value < 0)) {
70
+ rb_exc_raise(
71
+ get_protocol_exception(
72
+ INT2FIX(PROTOERR_NEGATIVE_SIZE),
73
+ rb_str_new2("Negative size")
74
+ )
75
+ );
76
+ }
77
+ if (RB_UNLIKELY((unsigned long)value > INT32_MAX)) {
78
+ rb_exc_raise(
79
+ get_protocol_exception(
80
+ INT2FIX(PROTOERR_SIZE_LIMIT),
81
+ rb_str_new2("Container size limit exceeded")
82
+ )
83
+ );
84
+ }
85
+ return (int)value;
86
+ }
87
+
88
+ rb_funcall(protocol, validate_container_size_method_id, 1, size);
89
+ return NUM2INT(size);
90
+ }
47
91
 
92
+ static VALUE new_container_array(int size) {
48
93
  return rb_ary_new2(size > 1024 ? 1024 : size);
49
94
  }
50
95
 
@@ -232,28 +277,28 @@ VALUE default_read_struct_end(VALUE protocol) {
232
277
 
233
278
  // end default protocol methods
234
279
 
235
- static VALUE rb_thrift_union_write (VALUE self, VALUE protocol);
236
- static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
237
- static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info);
280
+ static VALUE rb_thrift_struct_write_recursive(VALUE self, VALUE protocol, int remaining_depth);
281
+ static VALUE rb_thrift_union_write_recursive(VALUE self, VALUE protocol, int remaining_depth);
282
+ static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info, int remaining_depth);
238
283
 
239
- VALUE get_field_value(VALUE obj, VALUE field_name) {
284
+ static inline ID field_ivar_id(VALUE field_name) {
240
285
  char name_buf[RSTRING_LEN(field_name) + 2];
241
286
 
242
287
  name_buf[0] = '@';
243
288
  strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name) + 1);
244
289
 
245
- VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
290
+ return rb_intern(name_buf);
291
+ }
246
292
 
247
- return value;
293
+ VALUE get_field_value(VALUE obj, VALUE field_name) {
294
+ return rb_ivar_get(obj, field_ivar_id(field_name));
248
295
  }
249
296
 
250
- static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) {
297
+ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol, int remaining_depth) {
251
298
  long sz, i;
252
299
 
253
300
  if (ttype == TTYPE_MAP) {
254
301
  VALUE keys;
255
- VALUE key;
256
- VALUE val;
257
302
 
258
303
  Check_Type(value, T_HASH);
259
304
 
@@ -272,19 +317,19 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
272
317
  default_write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
273
318
 
274
319
  for (i = 0; i < sz; i++) {
275
- key = rb_ary_entry(keys, i);
276
- val = rb_hash_aref(value, key);
320
+ VALUE key = rb_ary_entry(keys, i);
321
+ VALUE val = rb_hash_aref(value, key);
277
322
 
278
323
  if (IS_CONTAINER(keytype)) {
279
- write_container(keytype, key_info, key, protocol);
324
+ write_container(keytype, key_info, key, protocol, remaining_depth);
280
325
  } else {
281
- write_anything(keytype, key, protocol, key_info);
326
+ write_anything(keytype, key, protocol, key_info, remaining_depth);
282
327
  }
283
328
 
284
329
  if (IS_CONTAINER(valuetype)) {
285
- write_container(valuetype, value_info, val, protocol);
330
+ write_container(valuetype, value_info, val, protocol, remaining_depth);
286
331
  } else {
287
- write_anything(valuetype, val, protocol, value_info);
332
+ write_anything(valuetype, val, protocol, value_info, remaining_depth);
288
333
  }
289
334
  }
290
335
 
@@ -302,9 +347,9 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
302
347
  for (i = 0; i < sz; ++i) {
303
348
  VALUE val = rb_ary_entry(value, i);
304
349
  if (IS_CONTAINER(element_type)) {
305
- write_container(element_type, element_type_info, val, protocol);
350
+ write_container(element_type, element_type_info, val, protocol, remaining_depth);
306
351
  } else {
307
- write_anything(element_type, val, protocol, element_type_info);
352
+ write_anything(element_type, val, protocol, element_type_info, remaining_depth);
308
353
  }
309
354
  }
310
355
  default_write_list_end(protocol);
@@ -314,7 +359,7 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
314
359
  if (TYPE(value) == T_ARRAY) {
315
360
  items = value;
316
361
  } else {
317
- if (rb_cSet == CLASS_OF(value)) {
362
+ if (rb_obj_is_kind_of(value, rb_cSet)) {
318
363
  items = rb_funcall(value, entries_method_id, 0);
319
364
  } else {
320
365
  Check_Type(value, T_HASH);
@@ -333,9 +378,9 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
333
378
  for (i = 0; i < sz; i++) {
334
379
  VALUE val = rb_ary_entry(items, i);
335
380
  if (IS_CONTAINER(element_type)) {
336
- write_container(element_type, element_type_info, val, protocol);
381
+ write_container(element_type, element_type_info, val, protocol, remaining_depth);
337
382
  } else {
338
- write_anything(element_type, val, protocol, element_type_info);
383
+ write_anything(element_type, val, protocol, element_type_info, remaining_depth);
339
384
  }
340
385
  }
341
386
 
@@ -345,7 +390,7 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
345
390
  }
346
391
  }
347
392
 
348
- static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) {
393
+ static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info, int remaining_depth) {
349
394
  if (ttype == TTYPE_BOOL) {
350
395
  default_write_bool(protocol, value);
351
396
  } else if (ttype == TTYPE_BYTE) {
@@ -368,19 +413,21 @@ static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_i
368
413
  } else if (ttype == TTYPE_UUID) {
369
414
  default_write_uuid(protocol, value);
370
415
  } else if (IS_CONTAINER(ttype)) {
371
- write_container(ttype, field_info, value, protocol);
416
+ write_container(ttype, field_info, value, protocol, remaining_depth);
372
417
  } else if (ttype == TTYPE_STRUCT) {
418
+ remaining_depth--;
419
+ validate_recursion_depth(remaining_depth);
373
420
  if (rb_obj_is_kind_of(value, thrift_union_class)) {
374
- rb_thrift_union_write(value, protocol);
421
+ rb_thrift_union_write_recursive(value, protocol, remaining_depth);
375
422
  } else {
376
- rb_thrift_struct_write(value, protocol);
423
+ rb_thrift_struct_write_recursive(value, protocol, remaining_depth);
377
424
  }
378
425
  } else {
379
426
  rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
380
427
  }
381
428
  }
382
429
 
383
- static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
430
+ static VALUE rb_thrift_struct_write_recursive(VALUE self, VALUE protocol, int remaining_depth) {
384
431
  // call validate
385
432
  rb_funcall(self, validate_method_id, 0);
386
433
 
@@ -406,7 +453,7 @@ static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
406
453
  if (!NIL_P(field_value)) {
407
454
  default_write_field_begin(protocol, field_name, ttype_value, field_id);
408
455
 
409
- write_anything(ttype, field_value, protocol, field_info);
456
+ write_anything(ttype, field_value, protocol, field_info, remaining_depth);
410
457
 
411
458
  default_write_field_end(protocol);
412
459
  }
@@ -420,22 +467,60 @@ static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
420
467
  return Qnil;
421
468
  }
422
469
 
470
+ // cppcheck-suppress constParameterCallback
471
+ static VALUE rb_thrift_struct_write(int argc, VALUE *argv, VALUE self) {
472
+ VALUE protocol;
473
+ int remaining_depth = parse_recursive_args(argc, argv, &protocol);
474
+ return rb_thrift_struct_write_recursive(self, protocol, remaining_depth);
475
+ }
476
+
423
477
  //-------------------------------------------
424
478
  // Reading section
425
479
  //-------------------------------------------
426
480
 
427
- static VALUE rb_thrift_union_read(VALUE self, VALUE protocol);
428
- static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
481
+ static VALUE rb_thrift_union_read_recursive(VALUE self, VALUE protocol, int remaining_depth);
482
+ static VALUE rb_thrift_struct_read_recursive(VALUE self, VALUE protocol, int remaining_depth);
429
483
  static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size);
430
484
  static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size);
431
485
 
432
- static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
433
- char name_buf[RSTRING_LEN(field_name) + 2];
486
+ static inline void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
487
+ rb_ivar_set(obj, field_ivar_id(field_name), value);
488
+ }
434
489
 
435
- name_buf[0] = '@';
436
- strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name)+1);
490
+ /* Ruby's rescue and iteration APIs require addressable callback functions. */
491
+ static VALUE duplicate_default_value(VALUE value) {
492
+ return rb_obj_dup(value);
493
+ }
437
494
 
438
- rb_ivar_set(obj, rb_intern(name_buf), value);
495
+ static VALUE retain_default_value(VALUE value, VALUE exception) {
496
+ (void)exception;
497
+ return value;
498
+ }
499
+
500
+ static int reset_struct_field(VALUE field_id, VALUE field_info, VALUE self) {
501
+ (void)field_id;
502
+
503
+ VALUE field_name = rb_hash_aref(field_info, name_sym);
504
+ ID ivar_id = field_ivar_id(field_name);
505
+ VALUE default_value = rb_hash_aref(field_info, default_sym);
506
+
507
+ if (NIL_P(default_value)) {
508
+ if (!NIL_P(rb_ivar_get(self, ivar_id))) {
509
+ rb_ivar_set(self, ivar_id, Qnil);
510
+ }
511
+ } else {
512
+ VALUE value = rb_rescue2(
513
+ duplicate_default_value,
514
+ default_value,
515
+ retain_default_value,
516
+ default_value,
517
+ rb_eStandardError,
518
+ (VALUE)0
519
+ );
520
+ rb_ivar_set(self, ivar_id, value);
521
+ }
522
+
523
+ return ST_CONTINUE;
439
524
  }
440
525
 
441
526
  // Helper method to skip the contents of a map (assumes the map header has been read).
@@ -455,7 +540,7 @@ static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value,
455
540
  }
456
541
  }
457
542
 
458
- static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
543
+ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info, int remaining_depth) {
459
544
  VALUE result = Qnil;
460
545
 
461
546
  if (ttype == TTYPE_BOOL) {
@@ -482,23 +567,19 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
482
567
  } else if (ttype == TTYPE_STRUCT) {
483
568
  VALUE klass = rb_hash_aref(field_info, class_sym);
484
569
  result = rb_class_new_instance(0, NULL, klass);
570
+ remaining_depth--;
571
+ validate_recursion_depth(remaining_depth);
485
572
 
486
573
  if (rb_obj_is_kind_of(result, thrift_union_class)) {
487
- rb_thrift_union_read(result, protocol);
574
+ rb_thrift_union_read_recursive(result, protocol, remaining_depth);
488
575
  } else {
489
- rb_thrift_struct_read(result, protocol);
576
+ rb_thrift_struct_read_recursive(result, protocol, remaining_depth);
490
577
  }
491
578
  } else if (ttype == TTYPE_MAP) {
492
- int i;
493
-
494
579
  VALUE map_header = default_read_map_begin(protocol);
495
580
  int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
496
581
  int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
497
- int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
498
-
499
- if (num_entries < 0) {
500
- rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_NEGATIVE_SIZE), rb_str_new2("Negative container size")));
501
- }
582
+ int num_entries = container_size(protocol, rb_ary_entry(map_header, 2));
502
583
 
503
584
  // Check the declared key and value types against the expected ones and skip the map contents
504
585
  // if the types don't match.
@@ -511,11 +592,11 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
511
592
  if (num_entries == 0 || (specified_key_type == key_ttype && specified_value_type == value_ttype)) {
512
593
  result = rb_hash_new();
513
594
 
514
- for (i = 0; i < num_entries; ++i) {
595
+ for (int i = 0; i < num_entries; ++i) {
515
596
  VALUE key, val;
516
597
 
517
- key = read_anything(protocol, key_ttype, key_info);
518
- val = read_anything(protocol, value_ttype, value_info);
598
+ key = read_anything(protocol, key_ttype, key_info, remaining_depth);
599
+ val = read_anything(protocol, value_ttype, value_info, remaining_depth);
519
600
 
520
601
  rb_hash_aset(result, key, val);
521
602
  }
@@ -528,11 +609,9 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
528
609
 
529
610
  default_read_map_end(protocol);
530
611
  } else if (ttype == TTYPE_LIST) {
531
- int i;
532
-
533
612
  VALUE list_header = default_read_list_begin(protocol);
534
613
  int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
535
- int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
614
+ int num_elements = container_size(protocol, rb_ary_entry(list_header, 1));
536
615
 
537
616
  // Check the declared element type against the expected one and skip the list contents
538
617
  // if the types don't match.
@@ -542,8 +621,8 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
542
621
  if (specified_element_type == element_ttype) {
543
622
  result = new_container_array(num_elements);
544
623
 
545
- for (i = 0; i < num_elements; ++i) {
546
- rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
624
+ for (int i = 0; i < num_elements; ++i) {
625
+ rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym), remaining_depth));
547
626
  }
548
627
  } else {
549
628
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
@@ -555,11 +634,10 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
555
634
  default_read_list_end(protocol);
556
635
  } else if (ttype == TTYPE_SET) {
557
636
  VALUE items;
558
- int i;
559
637
 
560
638
  VALUE set_header = default_read_set_begin(protocol);
561
639
  int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
562
- int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
640
+ int num_elements = container_size(protocol, rb_ary_entry(set_header, 1));
563
641
 
564
642
  // Check the declared element type against the expected one and skip the set contents
565
643
  // if the types don't match.
@@ -569,8 +647,8 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
569
647
  if (specified_element_type == element_ttype) {
570
648
  items = new_container_array(num_elements);
571
649
 
572
- for (i = 0; i < num_elements; ++i) {
573
- rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
650
+ for (int i = 0; i < num_elements; ++i) {
651
+ rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym), remaining_depth));
574
652
  }
575
653
 
576
654
  result = rb_class_new_instance(1, &items, rb_cSet);
@@ -589,12 +667,17 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
589
667
  return result;
590
668
  }
591
669
 
592
- static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
670
+ static VALUE rb_thrift_struct_read_recursive(VALUE self, VALUE protocol, int remaining_depth) {
671
+ VALUE struct_fields = STRUCT_FIELDS(self);
672
+
673
+ if (RHASH_SIZE(struct_fields) > 0 && rb_ivar_count(self) > 0) {
674
+ rb_check_frozen(self);
675
+ rb_hash_foreach(struct_fields, reset_struct_field, self);
676
+ }
677
+
593
678
  // read struct begin
594
679
  default_read_struct_begin(protocol);
595
680
 
596
- VALUE struct_fields = STRUCT_FIELDS(self);
597
-
598
681
  // read each field
599
682
  while (true) {
600
683
  VALUE field_header = default_read_field_begin(protocol);
@@ -613,7 +696,7 @@ static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
613
696
  if (field_type == specified_type) {
614
697
  // read the value
615
698
  VALUE name = rb_hash_aref(field_info, name_sym);
616
- set_field_value(self, name, read_anything(protocol, field_type, field_info));
699
+ set_field_value(self, name, read_anything(protocol, field_type, field_info, remaining_depth));
617
700
  } else {
618
701
  rb_funcall(protocol, skip_method_id, 1, field_type_value);
619
702
  }
@@ -634,12 +717,28 @@ static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
634
717
  return Qnil;
635
718
  }
636
719
 
720
+ // cppcheck-suppress constParameterCallback
721
+ static VALUE rb_thrift_struct_read(int argc, VALUE *argv, VALUE self) {
722
+ VALUE protocol;
723
+ int remaining_depth = parse_recursive_args(argc, argv, &protocol);
724
+ return rb_thrift_struct_read_recursive(self, protocol, remaining_depth);
725
+ }
726
+
637
727
 
638
728
  // --------------------------------
639
729
  // Union section
640
730
  // --------------------------------
641
731
 
642
- static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) {
732
+ static VALUE rb_thrift_union_read_recursive(VALUE self, VALUE protocol, int remaining_depth) {
733
+ rb_check_frozen(self);
734
+
735
+ if (!NIL_P(rb_ivar_get(self, setfield_id))) {
736
+ rb_ivar_set(self, setfield_id, Qnil);
737
+ }
738
+ if (!NIL_P(rb_ivar_get(self, setvalue_id))) {
739
+ rb_ivar_set(self, setvalue_id, Qnil);
740
+ }
741
+
643
742
  // read struct begin
644
743
  default_read_struct_begin(protocol);
645
744
 
@@ -657,8 +756,9 @@ static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) {
657
756
  if (field_type == specified_type) {
658
757
  // read the value
659
758
  VALUE name = rb_hash_aref(field_info, name_sym);
759
+ VALUE value = read_anything(protocol, field_type, field_info, remaining_depth);
660
760
  rb_iv_set(self, "@setfield", rb_str_intern(name));
661
- rb_iv_set(self, "@value", read_anything(protocol, field_type, field_info));
761
+ rb_iv_set(self, "@value", value);
662
762
  } else {
663
763
  rb_funcall(protocol, skip_method_id, 1, field_type_value);
664
764
  }
@@ -686,7 +786,14 @@ static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) {
686
786
  return Qnil;
687
787
  }
688
788
 
689
- static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
789
+ // cppcheck-suppress constParameterCallback
790
+ static VALUE rb_thrift_union_read(int argc, VALUE *argv, VALUE self) {
791
+ VALUE protocol;
792
+ int remaining_depth = parse_recursive_args(argc, argv, &protocol);
793
+ return rb_thrift_union_read_recursive(self, protocol, remaining_depth);
794
+ }
795
+
796
+ static VALUE rb_thrift_union_write_recursive(VALUE self, VALUE protocol, int remaining_depth) {
690
797
  // call validate
691
798
  rb_funcall(self, validate_method_id, 0);
692
799
 
@@ -710,7 +817,7 @@ static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
710
817
 
711
818
  default_write_field_begin(protocol, setfield, ttype_value, field_id);
712
819
 
713
- write_anything(ttype, setvalue, protocol, field_info);
820
+ write_anything(ttype, setvalue, protocol, field_info, remaining_depth);
714
821
 
715
822
  default_write_field_end(protocol);
716
823
 
@@ -722,17 +829,26 @@ static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
722
829
  return Qnil;
723
830
  }
724
831
 
832
+ // cppcheck-suppress constParameterCallback
833
+ static VALUE rb_thrift_union_write(int argc, VALUE *argv, VALUE self) {
834
+ VALUE protocol;
835
+ int remaining_depth = parse_recursive_args(argc, argv, &protocol);
836
+ return rb_thrift_union_write_recursive(self, protocol, remaining_depth);
837
+ }
838
+
725
839
  void Init_struct(void) {
726
840
  VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
727
841
 
728
- rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
729
- rb_define_method(struct_module, "read", rb_thrift_struct_read, 1);
842
+ recursion_limit = FIX2INT(rb_const_get(thrift_module, rb_intern("DEFAULT_RECURSION_DEPTH")));
843
+
844
+ rb_define_method(struct_module, "write", rb_thrift_struct_write, -1);
845
+ rb_define_method(struct_module, "read", rb_thrift_struct_read, -1);
730
846
 
731
847
  thrift_union_class = rb_const_get(thrift_module, rb_intern("Union"));
732
848
  rb_global_variable(&thrift_union_class);
733
849
 
734
- rb_define_method(thrift_union_class, "write", rb_thrift_union_write, 1);
735
- rb_define_method(thrift_union_class, "read", rb_thrift_union_read, 1);
850
+ rb_define_method(thrift_union_class, "write", rb_thrift_union_write, -1);
851
+ rb_define_method(thrift_union_class, "read", rb_thrift_union_read, -1);
736
852
 
737
853
  setfield_id = rb_intern("@setfield");
738
854
  rb_global_variable(&setfield_id);
@@ -748,4 +864,9 @@ void Init_struct(void) {
748
864
 
749
865
  sorted_field_ids_method_id = rb_intern("sorted_field_ids");
750
866
  rb_global_variable(&sorted_field_ids_method_id);
867
+
868
+ validate_container_size_method_id = rb_intern("validate_container_size");
869
+
870
+ default_sym = ID2SYM(rb_intern("default"));
871
+ rb_global_variable(&default_sym);
751
872
  }