thrift 0.24.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 +107 -4
  3. data/ext/binary_protocol_accelerated.c +52 -17
  4. data/ext/compact_protocol.c +135 -70
  5. data/ext/constants.h +3 -0
  6. data/ext/extconf.rb +4 -4
  7. data/ext/macros.h +11 -0
  8. data/ext/memory_buffer.c +35 -17
  9. data/ext/struct.c +185 -64
  10. data/ext/thrift_native.c +6 -0
  11. data/lib/thrift/bytes.rb +2 -2
  12. data/lib/thrift/client.rb +31 -22
  13. data/lib/thrift/exceptions.rb +10 -8
  14. data/lib/thrift/multiplexed_processor.rb +5 -6
  15. data/lib/thrift/processor.rb +24 -3
  16. data/lib/thrift/protocol/base_protocol.rb +53 -27
  17. data/lib/thrift/protocol/binary_protocol.rb +54 -40
  18. data/lib/thrift/protocol/binary_protocol_accelerated.rb +6 -2
  19. data/lib/thrift/protocol/compact_protocol.rb +193 -88
  20. data/lib/thrift/protocol/header_protocol.rb +14 -3
  21. data/lib/thrift/protocol/json_protocol.rb +146 -104
  22. data/lib/thrift/protocol/multiplexed_protocol.rb +2 -3
  23. data/lib/thrift/protocol/protocol_decorator.rb +5 -2
  24. data/lib/thrift/serializer/serializer.rb +6 -0
  25. data/lib/thrift/server/base_server.rb +10 -2
  26. data/lib/thrift/server/nonblocking_server.rb +49 -26
  27. data/lib/thrift/server/rack_application.rb +69 -0
  28. data/lib/thrift/server/simple_server.rb +27 -23
  29. data/lib/thrift/server/thin_http_server.rb +20 -44
  30. data/lib/thrift/server/thread_pool_server.rb +20 -17
  31. data/lib/thrift/server/threaded_server.rb +22 -26
  32. data/lib/thrift/struct.rb +33 -18
  33. data/lib/thrift/struct_union.rb +36 -30
  34. data/lib/thrift/transport/base_transport.rb +23 -5
  35. data/lib/thrift/transport/buffered_transport.rb +4 -4
  36. data/lib/thrift/transport/framed_transport.rb +32 -7
  37. data/lib/thrift/transport/header_transport.rb +130 -50
  38. data/lib/thrift/transport/http_client_transport.rb +27 -14
  39. data/lib/thrift/transport/io_stream_transport.rb +2 -1
  40. data/lib/thrift/transport/memory_buffer_transport.rb +8 -10
  41. data/lib/thrift/transport/server_socket.rb +4 -5
  42. data/lib/thrift/transport/socket.rb +20 -22
  43. data/lib/thrift/transport/ssl_server_socket.rb +64 -4
  44. data/lib/thrift/transport/ssl_socket.rb +32 -5
  45. data/lib/thrift/transport/unix_server_socket.rb +1 -1
  46. data/lib/thrift/transport/unix_socket.rb +4 -6
  47. data/lib/thrift/types.rb +22 -22
  48. data/lib/thrift/union.rb +20 -11
  49. data/lib/thrift/uuid.rb +8 -8
  50. data/lib/thrift.rb +39 -39
  51. metadata +5 -230
  52. data/benchmark/Benchmark.thrift +0 -24
  53. data/benchmark/benchmark.rb +0 -286
  54. data/benchmark/client.rb +0 -118
  55. data/benchmark/server.rb +0 -121
  56. data/benchmark/thin_server.rb +0 -46
  57. data/lib/thrift/server/mongrel_http_server.rb +0 -61
  58. data/spec/BaseService.thrift +0 -27
  59. data/spec/ExtendedService.thrift +0 -25
  60. data/spec/Referenced.thrift +0 -44
  61. data/spec/ThriftNamespacedSpec.thrift +0 -53
  62. data/spec/ThriftSpec.thrift +0 -203
  63. data/spec/base_protocol_spec.rb +0 -244
  64. data/spec/base_transport_spec.rb +0 -428
  65. data/spec/binary_protocol_accelerated_spec.rb +0 -47
  66. data/spec/binary_protocol_spec.rb +0 -73
  67. data/spec/binary_protocol_spec_shared.rb +0 -509
  68. data/spec/bytes_spec.rb +0 -117
  69. data/spec/client_spec.rb +0 -167
  70. data/spec/compact_protocol_spec.rb +0 -295
  71. data/spec/constants_demo_spec.rb +0 -102
  72. data/spec/exception_spec.rb +0 -141
  73. data/spec/flat_spec.rb +0 -63
  74. data/spec/header_protocol_spec.rb +0 -476
  75. data/spec/header_transport_spec.rb +0 -431
  76. data/spec/http_client_spec.rb +0 -160
  77. data/spec/json_protocol_spec.rb +0 -574
  78. data/spec/multiplexed_processor_spec.rb +0 -75
  79. data/spec/namespaced_spec.rb +0 -67
  80. data/spec/nonblocking_server_spec.rb +0 -429
  81. data/spec/processor_spec.rb +0 -80
  82. data/spec/recursion_depth_spec.rb +0 -223
  83. data/spec/serializer_spec.rb +0 -67
  84. data/spec/server_socket_spec.rb +0 -121
  85. data/spec/server_spec.rb +0 -238
  86. data/spec/socket_spec.rb +0 -174
  87. data/spec/socket_spec_shared.rb +0 -168
  88. data/spec/spec_helper.rb +0 -70
  89. data/spec/ssl_server_socket_spec.rb +0 -85
  90. data/spec/ssl_socket_spec.rb +0 -248
  91. data/spec/struct_nested_containers_spec.rb +0 -191
  92. data/spec/struct_spec.rb +0 -398
  93. data/spec/support/header_protocol_helper.rb +0 -55
  94. data/spec/thin_http_server_spec.rb +0 -127
  95. data/spec/types_spec.rb +0 -118
  96. data/spec/union_spec.rb +0 -273
  97. data/spec/unix_socket_spec.rb +0 -131
  98. data/spec/uuid_validation_spec.rb +0 -239
  99. data/test/fuzz/Makefile +0 -779
  100. data/test/fuzz/Makefile.am +0 -173
  101. data/test/fuzz/Makefile.in +0 -775
  102. data/test/fuzz/README.md +0 -149
  103. data/test/fuzz/fuzz_common.rb +0 -96
  104. data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -23
  105. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -23
  106. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -23
  107. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -23
  108. data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -23
  109. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -23
  110. data/test/fuzz/fuzz_parse_json_protocol.rb +0 -23
  111. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -23
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -23
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -23
  114. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -23
  115. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -23
  116. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -23
  117. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -23
  118. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -23
  119. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -23
  120. data/test/fuzz/fuzz_tracer.rb +0 -29
data/ext/constants.h CHANGED
@@ -77,14 +77,17 @@ extern ID skip_method_id;
77
77
  extern ID write_method_id;
78
78
  extern ID read_all_method_id;
79
79
  extern ID read_into_buffer_method_id;
80
+ extern ID reset_message_size_method_id;
80
81
  extern ID force_binary_encoding_id;
81
82
  extern ID convert_to_utf8_byte_buffer_id;
82
83
  extern ID convert_to_string_id;
83
84
 
84
85
  extern ID fields_const_id;
85
86
  extern ID transport_ivar_id;
87
+ extern ID reset_message_size_ivar_id;
86
88
  extern ID strict_read_ivar_id;
87
89
  extern ID strict_write_ivar_id;
90
+ extern ID max_string_size_ivar_id;
88
91
 
89
92
  extern VALUE type_sym;
90
93
  extern VALUE name_sym;
data/ext/extconf.rb CHANGED
@@ -18,10 +18,10 @@
18
18
  # under the License.
19
19
  #
20
20
 
21
- if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
22
- File.open('Makefile', 'w'){ |f| f.puts "all:\n\ninstall:\n" }
21
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
22
+ File.open("Makefile", "w") { |f| f.puts "all:\n\ninstall:\n" }
23
23
  else
24
- require 'mkmf'
24
+ require "mkmf"
25
25
 
26
26
  append_cflags(["-fsigned-char", "-g", "-O2", "-Wall", "-Werror", "-Werror=old-style-definition"])
27
27
 
@@ -39,5 +39,5 @@ else
39
39
 
40
40
  have_func("strlcpy", "string.h")
41
41
 
42
- create_makefile 'thrift_native'
42
+ create_makefile "thrift_native"
43
43
  end
data/ext/macros.h CHANGED
@@ -24,6 +24,17 @@
24
24
  #define CHECK_NIL(obj) if (NIL_P(obj)) { rb_raise(rb_eStandardError, "nil argument not allowed!");}
25
25
  #define READ(obj, length) rb_funcall(GET_TRANSPORT(obj), read_all_method_id, 1, INT2FIX(length))
26
26
 
27
+ // Refuse a declared string length over the protocol's @max_string_size (nil
28
+ // means no limit) before it becomes a read size.
29
+ #define CHECK_STRING_SIZE(obj, size) \
30
+ do { \
31
+ VALUE max_string_size_ = rb_ivar_get(obj, max_string_size_ivar_id); \
32
+ if (!NIL_P(max_string_size_) && RB_UNLIKELY((long long)(size) > NUM2LL(max_string_size_))) { \
33
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_SIZE_LIMIT), \
34
+ rb_sprintf("String size %lld larger than the maximum %lld", (long long)(size), NUM2LL(max_string_size_)))); \
35
+ } \
36
+ } while (0)
37
+
27
38
  #ifndef RFLOAT_VALUE
28
39
  # define RFLOAT_VALUE(v) RFLOAT(rb_Float(v))->value
29
40
  #endif
data/ext/memory_buffer.c CHANGED
@@ -48,23 +48,28 @@ VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
48
48
  }
49
49
 
50
50
  VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
51
- 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
+ }
52
56
 
53
57
  VALUE index_value = rb_ivar_get(self, index_ivar_id);
54
- int index = FIX2INT(index_value);
58
+ long index = NUM2LONG(index_value);
55
59
 
56
60
  VALUE buf = GET_BUF(self);
57
61
  VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value);
58
62
 
59
- index += length;
60
- if (index > RSTRING_LEN(buf)) {
61
- index = (int)RSTRING_LEN(buf);
63
+ if (length > RSTRING_LEN(buf) - index) {
64
+ index = RSTRING_LEN(buf);
65
+ } else {
66
+ index += (long)length;
62
67
  }
63
68
  if (index >= GARBAGE_BUFFER_SIZE) {
64
- 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)));
65
70
  index = 0;
66
71
  }
67
- rb_ivar_set(self, index_ivar_id, INT2FIX(index));
72
+ rb_ivar_set(self, index_ivar_id, LONG2NUM(index));
68
73
 
69
74
  if (RSTRING_LEN(data) < length) {
70
75
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
@@ -74,28 +79,28 @@ VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
74
79
  }
75
80
 
76
81
  VALUE rb_thrift_memory_buffer_read_all(VALUE self, VALUE length_value) {
77
- int length = FIX2INT(length_value);
82
+ long long length = NUM2LL(length_value);
78
83
 
79
84
  if (RB_UNLIKELY(length < 0)) {
80
85
  rb_exc_raise(rb_funcall(transport_exception_class, new_method_id, 2, transport_negative_size, rb_str_new2("Negative size")));
81
86
  }
82
87
 
83
88
  VALUE index_value = rb_ivar_get(self, index_ivar_id);
84
- int index = FIX2INT(index_value);
89
+ long index = NUM2LONG(index_value);
85
90
  VALUE buf = GET_BUF(self);
86
91
 
87
92
  if (RB_UNLIKELY(length > RSTRING_LEN(buf) - index)) {
88
93
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
89
94
  }
90
95
 
91
- VALUE data = rb_str_subseq(buf, index, length);
96
+ VALUE data = rb_str_subseq(buf, index, (long)length);
92
97
 
93
- index += length;
98
+ index += (long)length;
94
99
  if (index >= GARBAGE_BUFFER_SIZE) {
95
100
  rb_ivar_set(self, buf_ivar_id, rb_str_subseq(buf, index, RSTRING_LEN(buf) - index));
96
101
  index = 0;
97
102
  }
98
- rb_ivar_set(self, index_ivar_id, INT2FIX(index));
103
+ rb_ivar_set(self, index_ivar_id, LONG2NUM(index));
99
104
 
100
105
  return data;
101
106
  }
@@ -108,7 +113,7 @@ VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
108
113
  if (index >= RSTRING_LEN(buf)) {
109
114
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
110
115
  }
111
- char byte = RSTRING_PTR(buf)[index++];
116
+ unsigned char byte = (unsigned char)RSTRING_PTR(buf)[index++];
112
117
 
113
118
  if (index >= GARBAGE_BUFFER_SIZE) {
114
119
  rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
@@ -116,8 +121,7 @@ VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
116
121
  }
117
122
  rb_ivar_set(self, index_ivar_id, INT2FIX(index));
118
123
 
119
- int result = (int) byte;
120
- return INT2FIX(result);
124
+ return INT2FIX(byte);
121
125
  }
122
126
 
123
127
  VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value) {
@@ -127,15 +131,29 @@ VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, V
127
131
  VALUE buf = GET_BUF(self);
128
132
 
129
133
  index = FIX2INT(rb_ivar_get(self, index_ivar_id));
130
- while (i < size) {
134
+ if (size > 0) {
131
135
  if (index >= RSTRING_LEN(buf)) {
136
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
132
137
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
133
138
  }
134
- 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
+ }
135
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
+ }
136
152
  if (i >= RSTRING_LEN(buffer_value)) {
153
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
137
154
  rb_raise(rb_eIndexError, "index %d out of string", i);
138
155
  }
156
+ char byte = RSTRING_PTR(buf)[index++];
139
157
  ((char*)RSTRING_PTR(buffer_value))[i] = byte;
140
158
  i++;
141
159
  }
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,23 +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 void validate_container_size(int size) {
39
- if (RB_UNLIKELY(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
  }
47
53
  }
48
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
+ }
91
+
49
92
  static VALUE new_container_array(int size) {
50
- validate_container_size(size);
51
93
  return rb_ary_new2(size > 1024 ? 1024 : size);
52
94
  }
53
95
 
@@ -235,22 +277,24 @@ VALUE default_read_struct_end(VALUE protocol) {
235
277
 
236
278
  // end default protocol methods
237
279
 
238
- static VALUE rb_thrift_union_write (VALUE self, VALUE protocol);
239
- static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
240
- 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);
241
283
 
242
- VALUE get_field_value(VALUE obj, VALUE field_name) {
284
+ static inline ID field_ivar_id(VALUE field_name) {
243
285
  char name_buf[RSTRING_LEN(field_name) + 2];
244
286
 
245
287
  name_buf[0] = '@';
246
288
  strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name) + 1);
247
289
 
248
- VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
290
+ return rb_intern(name_buf);
291
+ }
249
292
 
250
- return value;
293
+ VALUE get_field_value(VALUE obj, VALUE field_name) {
294
+ return rb_ivar_get(obj, field_ivar_id(field_name));
251
295
  }
252
296
 
253
- 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) {
254
298
  long sz, i;
255
299
 
256
300
  if (ttype == TTYPE_MAP) {
@@ -277,15 +321,15 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
277
321
  VALUE val = rb_hash_aref(value, key);
278
322
 
279
323
  if (IS_CONTAINER(keytype)) {
280
- write_container(keytype, key_info, key, protocol);
324
+ write_container(keytype, key_info, key, protocol, remaining_depth);
281
325
  } else {
282
- write_anything(keytype, key, protocol, key_info);
326
+ write_anything(keytype, key, protocol, key_info, remaining_depth);
283
327
  }
284
328
 
285
329
  if (IS_CONTAINER(valuetype)) {
286
- write_container(valuetype, value_info, val, protocol);
330
+ write_container(valuetype, value_info, val, protocol, remaining_depth);
287
331
  } else {
288
- write_anything(valuetype, val, protocol, value_info);
332
+ write_anything(valuetype, val, protocol, value_info, remaining_depth);
289
333
  }
290
334
  }
291
335
 
@@ -303,9 +347,9 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
303
347
  for (i = 0; i < sz; ++i) {
304
348
  VALUE val = rb_ary_entry(value, i);
305
349
  if (IS_CONTAINER(element_type)) {
306
- write_container(element_type, element_type_info, val, protocol);
350
+ write_container(element_type, element_type_info, val, protocol, remaining_depth);
307
351
  } else {
308
- write_anything(element_type, val, protocol, element_type_info);
352
+ write_anything(element_type, val, protocol, element_type_info, remaining_depth);
309
353
  }
310
354
  }
311
355
  default_write_list_end(protocol);
@@ -315,7 +359,7 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
315
359
  if (TYPE(value) == T_ARRAY) {
316
360
  items = value;
317
361
  } else {
318
- if (rb_cSet == CLASS_OF(value)) {
362
+ if (rb_obj_is_kind_of(value, rb_cSet)) {
319
363
  items = rb_funcall(value, entries_method_id, 0);
320
364
  } else {
321
365
  Check_Type(value, T_HASH);
@@ -334,9 +378,9 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
334
378
  for (i = 0; i < sz; i++) {
335
379
  VALUE val = rb_ary_entry(items, i);
336
380
  if (IS_CONTAINER(element_type)) {
337
- write_container(element_type, element_type_info, val, protocol);
381
+ write_container(element_type, element_type_info, val, protocol, remaining_depth);
338
382
  } else {
339
- write_anything(element_type, val, protocol, element_type_info);
383
+ write_anything(element_type, val, protocol, element_type_info, remaining_depth);
340
384
  }
341
385
  }
342
386
 
@@ -346,7 +390,7 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
346
390
  }
347
391
  }
348
392
 
349
- 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) {
350
394
  if (ttype == TTYPE_BOOL) {
351
395
  default_write_bool(protocol, value);
352
396
  } else if (ttype == TTYPE_BYTE) {
@@ -369,19 +413,21 @@ static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_i
369
413
  } else if (ttype == TTYPE_UUID) {
370
414
  default_write_uuid(protocol, value);
371
415
  } else if (IS_CONTAINER(ttype)) {
372
- write_container(ttype, field_info, value, protocol);
416
+ write_container(ttype, field_info, value, protocol, remaining_depth);
373
417
  } else if (ttype == TTYPE_STRUCT) {
418
+ remaining_depth--;
419
+ validate_recursion_depth(remaining_depth);
374
420
  if (rb_obj_is_kind_of(value, thrift_union_class)) {
375
- rb_thrift_union_write(value, protocol);
421
+ rb_thrift_union_write_recursive(value, protocol, remaining_depth);
376
422
  } else {
377
- rb_thrift_struct_write(value, protocol);
423
+ rb_thrift_struct_write_recursive(value, protocol, remaining_depth);
378
424
  }
379
425
  } else {
380
426
  rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
381
427
  }
382
428
  }
383
429
 
384
- 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) {
385
431
  // call validate
386
432
  rb_funcall(self, validate_method_id, 0);
387
433
 
@@ -407,7 +453,7 @@ static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
407
453
  if (!NIL_P(field_value)) {
408
454
  default_write_field_begin(protocol, field_name, ttype_value, field_id);
409
455
 
410
- write_anything(ttype, field_value, protocol, field_info);
456
+ write_anything(ttype, field_value, protocol, field_info, remaining_depth);
411
457
 
412
458
  default_write_field_end(protocol);
413
459
  }
@@ -421,22 +467,60 @@ static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
421
467
  return Qnil;
422
468
  }
423
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
+
424
477
  //-------------------------------------------
425
478
  // Reading section
426
479
  //-------------------------------------------
427
480
 
428
- static VALUE rb_thrift_union_read(VALUE self, VALUE protocol);
429
- 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);
430
483
  static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size);
431
484
  static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size);
432
485
 
433
- static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
434
- 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
+ }
435
489
 
436
- name_buf[0] = '@';
437
- 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
+ }
494
+
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
+ }
438
522
 
439
- rb_ivar_set(obj, rb_intern(name_buf), value);
523
+ return ST_CONTINUE;
440
524
  }
441
525
 
442
526
  // Helper method to skip the contents of a map (assumes the map header has been read).
@@ -456,7 +540,7 @@ static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value,
456
540
  }
457
541
  }
458
542
 
459
- 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) {
460
544
  VALUE result = Qnil;
461
545
 
462
546
  if (ttype == TTYPE_BOOL) {
@@ -483,21 +567,19 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
483
567
  } else if (ttype == TTYPE_STRUCT) {
484
568
  VALUE klass = rb_hash_aref(field_info, class_sym);
485
569
  result = rb_class_new_instance(0, NULL, klass);
570
+ remaining_depth--;
571
+ validate_recursion_depth(remaining_depth);
486
572
 
487
573
  if (rb_obj_is_kind_of(result, thrift_union_class)) {
488
- rb_thrift_union_read(result, protocol);
574
+ rb_thrift_union_read_recursive(result, protocol, remaining_depth);
489
575
  } else {
490
- rb_thrift_struct_read(result, protocol);
576
+ rb_thrift_struct_read_recursive(result, protocol, remaining_depth);
491
577
  }
492
578
  } else if (ttype == TTYPE_MAP) {
493
579
  VALUE map_header = default_read_map_begin(protocol);
494
580
  int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
495
581
  int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
496
- int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
497
-
498
- if (num_entries < 0) {
499
- rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_NEGATIVE_SIZE), rb_str_new2("Negative container size")));
500
- }
582
+ int num_entries = container_size(protocol, rb_ary_entry(map_header, 2));
501
583
 
502
584
  // Check the declared key and value types against the expected ones and skip the map contents
503
585
  // if the types don't match.
@@ -513,8 +595,8 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
513
595
  for (int i = 0; i < num_entries; ++i) {
514
596
  VALUE key, val;
515
597
 
516
- key = read_anything(protocol, key_ttype, key_info);
517
- 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);
518
600
 
519
601
  rb_hash_aset(result, key, val);
520
602
  }
@@ -529,7 +611,7 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
529
611
  } else if (ttype == TTYPE_LIST) {
530
612
  VALUE list_header = default_read_list_begin(protocol);
531
613
  int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
532
- int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
614
+ int num_elements = container_size(protocol, rb_ary_entry(list_header, 1));
533
615
 
534
616
  // Check the declared element type against the expected one and skip the list contents
535
617
  // if the types don't match.
@@ -540,14 +622,12 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
540
622
  result = new_container_array(num_elements);
541
623
 
542
624
  for (int i = 0; i < num_elements; ++i) {
543
- rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
625
+ rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym), remaining_depth));
544
626
  }
545
627
  } else {
546
- validate_container_size(num_elements);
547
628
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
548
629
  }
549
630
  } else {
550
- validate_container_size(num_elements);
551
631
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
552
632
  }
553
633
 
@@ -557,7 +637,7 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
557
637
 
558
638
  VALUE set_header = default_read_set_begin(protocol);
559
639
  int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
560
- int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
640
+ int num_elements = container_size(protocol, rb_ary_entry(set_header, 1));
561
641
 
562
642
  // Check the declared element type against the expected one and skip the set contents
563
643
  // if the types don't match.
@@ -568,16 +648,14 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
568
648
  items = new_container_array(num_elements);
569
649
 
570
650
  for (int i = 0; i < num_elements; ++i) {
571
- rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
651
+ rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym), remaining_depth));
572
652
  }
573
653
 
574
654
  result = rb_class_new_instance(1, &items, rb_cSet);
575
655
  } else {
576
- validate_container_size(num_elements);
577
656
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
578
657
  }
579
658
  } else {
580
- validate_container_size(num_elements);
581
659
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
582
660
  }
583
661
 
@@ -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
  }