thrift 0.22.0 → 0.24.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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +235 -17
  3. data/benchmark/benchmark.rb +23 -8
  4. data/benchmark/client.rb +50 -6
  5. data/benchmark/server.rb +46 -7
  6. data/benchmark/thin_server.rb +2 -0
  7. data/ext/binary_protocol_accelerated.c +154 -42
  8. data/ext/bytes.c +14 -0
  9. data/ext/compact_protocol.c +138 -45
  10. data/ext/constants.h +12 -0
  11. data/ext/extconf.rb +17 -8
  12. data/ext/memory_buffer.c +44 -7
  13. data/ext/protocol.c +29 -0
  14. data/ext/protocol.h +35 -0
  15. data/ext/struct.c +48 -17
  16. data/ext/thrift_native.c +28 -3
  17. data/lib/thrift/bytes.rb +70 -100
  18. data/lib/thrift/client.rb +54 -13
  19. data/lib/thrift/exceptions.rb +6 -5
  20. data/lib/thrift/multiplexed_processor.rb +20 -10
  21. data/lib/thrift/processor.rb +7 -6
  22. data/lib/thrift/protocol/base_protocol.rb +52 -21
  23. data/lib/thrift/protocol/binary_protocol.rb +65 -20
  24. data/lib/thrift/protocol/binary_protocol_accelerated.rb +6 -5
  25. data/lib/thrift/protocol/compact_protocol.rb +76 -41
  26. data/lib/thrift/protocol/header_protocol.rb +321 -0
  27. data/lib/thrift/protocol/json_protocol.rb +44 -27
  28. data/lib/thrift/protocol/multiplexed_protocol.rb +6 -5
  29. data/lib/thrift/protocol/protocol_decorator.rb +13 -4
  30. data/lib/thrift/serializer/deserializer.rb +6 -5
  31. data/lib/thrift/serializer/serializer.rb +5 -5
  32. data/lib/thrift/server/base_server.rb +5 -4
  33. data/lib/thrift/server/mongrel_http_server.rb +7 -6
  34. data/lib/thrift/server/nonblocking_server.rb +35 -9
  35. data/lib/thrift/server/simple_server.rb +13 -5
  36. data/lib/thrift/server/thin_http_server.rb +4 -3
  37. data/lib/thrift/server/thread_pool_server.rb +7 -6
  38. data/lib/thrift/server/threaded_server.rb +13 -5
  39. data/lib/thrift/struct.rb +12 -11
  40. data/lib/thrift/struct_union.rb +14 -9
  41. data/lib/thrift/thrift_native.rb +3 -2
  42. data/lib/thrift/transport/base_server_transport.rb +8 -5
  43. data/lib/thrift/transport/base_transport.rb +18 -14
  44. data/lib/thrift/transport/buffered_transport.rb +7 -6
  45. data/lib/thrift/transport/framed_transport.rb +8 -7
  46. data/lib/thrift/transport/header_transport.rb +562 -0
  47. data/lib/thrift/transport/http_client_transport.rb +2 -1
  48. data/lib/thrift/transport/io_stream_transport.rb +4 -3
  49. data/lib/thrift/transport/memory_buffer_transport.rb +13 -6
  50. data/lib/thrift/transport/server_socket.rb +14 -8
  51. data/lib/thrift/transport/socket.rb +126 -60
  52. data/lib/thrift/transport/ssl_server_socket.rb +4 -3
  53. data/lib/thrift/transport/ssl_socket.rb +45 -13
  54. data/lib/thrift/transport/unix_server_socket.rb +9 -5
  55. data/lib/thrift/transport/unix_socket.rb +7 -6
  56. data/lib/thrift/types.rb +10 -6
  57. data/lib/thrift/union.rb +15 -8
  58. data/lib/thrift/uuid.rb +50 -0
  59. data/lib/thrift.rb +4 -1
  60. data/spec/ThriftSpec.thrift +21 -1
  61. data/spec/base_protocol_spec.rb +21 -2
  62. data/spec/base_transport_spec.rb +48 -8
  63. data/spec/binary_protocol_accelerated_spec.rb +1 -0
  64. data/spec/binary_protocol_spec.rb +1 -2
  65. data/spec/binary_protocol_spec_shared.rb +206 -155
  66. data/spec/bytes_spec.rb +71 -114
  67. data/spec/client_spec.rb +86 -19
  68. data/spec/compact_protocol_spec.rb +153 -16
  69. data/spec/constants_demo_spec.rb +102 -0
  70. data/spec/exception_spec.rb +1 -1
  71. data/spec/flat_spec.rb +1 -0
  72. data/spec/header_protocol_spec.rb +476 -0
  73. data/spec/header_transport_spec.rb +431 -0
  74. data/spec/http_client_spec.rb +5 -6
  75. data/spec/json_protocol_spec.rb +69 -47
  76. data/spec/multiplexed_processor_spec.rb +75 -0
  77. data/spec/namespaced_spec.rb +1 -1
  78. data/spec/nonblocking_server_spec.rb +174 -8
  79. data/spec/processor_spec.rb +1 -1
  80. data/spec/recursion_depth_spec.rb +223 -0
  81. data/spec/serializer_spec.rb +1 -1
  82. data/spec/server_socket_spec.rb +38 -1
  83. data/spec/server_spec.rb +60 -9
  84. data/spec/socket_spec.rb +119 -13
  85. data/spec/socket_spec_shared.rb +73 -9
  86. data/spec/spec_helper.rb +2 -1
  87. data/spec/ssl_server_socket_spec.rb +52 -1
  88. data/spec/ssl_socket_spec.rb +181 -11
  89. data/spec/struct_nested_containers_spec.rb +2 -2
  90. data/spec/struct_spec.rb +114 -9
  91. data/spec/support/header_protocol_helper.rb +55 -0
  92. data/spec/thin_http_server_spec.rb +4 -18
  93. data/spec/types_spec.rb +26 -26
  94. data/spec/union_spec.rb +70 -11
  95. data/spec/unix_socket_spec.rb +17 -2
  96. data/spec/uuid_validation_spec.rb +239 -0
  97. data/test/fuzz/Makefile +779 -0
  98. data/test/fuzz/Makefile.am +173 -0
  99. data/test/fuzz/Makefile.in +775 -0
  100. data/test/fuzz/README.md +149 -0
  101. data/test/fuzz/fuzz_common.rb +96 -0
  102. data/{lib/thrift/core_ext.rb → test/fuzz/fuzz_parse_binary_protocol.rb} +4 -4
  103. data/{lib/thrift/core_ext/fixnum.rb → test/fuzz/fuzz_parse_binary_protocol_accelerated.rb} +7 -13
  104. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +23 -0
  105. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +23 -0
  106. data/test/fuzz/fuzz_parse_compact_protocol.rb +23 -0
  107. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +23 -0
  108. data/test/fuzz/fuzz_parse_json_protocol.rb +23 -0
  109. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +23 -0
  110. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +23 -0
  111. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +23 -0
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +23 -0
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +23 -0
  114. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +23 -0
  115. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +23 -0
  116. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +23 -0
  117. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +23 -0
  118. data/test/fuzz/fuzz_tracer.rb +29 -0
  119. metadata +105 -70
@@ -20,10 +20,12 @@
20
20
  #include <ruby.h>
21
21
  #include <stdbool.h>
22
22
  #include <stdint.h>
23
+ #include <string.h>
23
24
  #include <constants.h>
24
25
  #include <struct.h>
25
26
  #include <macros.h>
26
27
  #include <bytes.h>
28
+ #include <protocol.h>
27
29
 
28
30
  #define LAST_ID(obj) FIX2INT(rb_ary_pop(rb_ivar_get(obj, last_field_id)))
29
31
  #define SET_LAST_ID(obj, val) rb_ary_push(rb_ivar_get(obj, last_field_id), val)
@@ -58,6 +60,7 @@ static int CTYPE_LIST = 0x09;
58
60
  static int CTYPE_SET = 0x0A;
59
61
  static int CTYPE_MAP = 0x0B;
60
62
  static int CTYPE_STRUCT = 0x0C;
63
+ static int CTYPE_UUID = 0x0D;
61
64
 
62
65
  VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
63
66
 
@@ -86,6 +89,8 @@ static int get_compact_type(VALUE type_value) {
86
89
  return CTYPE_MAP;
87
90
  } else if (type == TTYPE_STRUCT) {
88
91
  return CTYPE_STRUCT;
92
+ } else if (type == TTYPE_UUID) {
93
+ return CTYPE_UUID;
89
94
  } else {
90
95
  char str[50];
91
96
  sprintf(str, "don't know what type: %d", type);
@@ -102,7 +107,7 @@ static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, V
102
107
  int id = FIX2INT(id_value);
103
108
  int last_id = LAST_ID(self);
104
109
  VALUE transport = GET_TRANSPORT(self);
105
-
110
+
106
111
  // if there's a type override, use that.
107
112
  int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type);
108
113
  // check if we can use delta encoding for the field id
@@ -119,21 +124,29 @@ static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, V
119
124
  SET_LAST_ID(self, id_value);
120
125
  }
121
126
 
122
- static int32_t int_to_zig_zag(int32_t n) {
123
- return (n << 1) ^ (n >> 31);
127
+ static uint32_t int_to_zig_zag(int32_t n) {
128
+ return (((uint32_t)n) << 1) ^ (0U - (uint32_t)(n < 0));
124
129
  }
125
130
 
126
131
  static uint64_t ll_to_zig_zag(int64_t n) {
127
- return (n << 1) ^ (n >> 63);
132
+ return (((uint64_t)n) << 1) ^ (0ULL - (uint64_t)(n < 0));
133
+ }
134
+
135
+ static uint32_t message_seqid_to_varint32(int32_t seqid) {
136
+ return seqid < 0 ? (uint32_t)((int64_t)seqid + (INT64_C(1) << 32)) : (uint32_t)seqid;
137
+ }
138
+
139
+ static int32_t message_seqid_from_varint32(uint32_t seqid) {
140
+ return seqid > INT32_MAX ? (int32_t)((int64_t)seqid - (INT64_C(1) << 32)) : (int32_t)seqid;
128
141
  }
129
142
 
130
143
  static void write_varint32(VALUE transport, uint32_t n) {
131
144
  while (true) {
132
- if ((n & ~0x7F) == 0) {
133
- write_byte_direct(transport, n & 0x7f);
145
+ if ((n & ~0x7FU) == 0U) {
146
+ write_byte_direct(transport, n & 0x7FU);
134
147
  break;
135
148
  } else {
136
- write_byte_direct(transport, (n & 0x7F) | 0x80);
149
+ write_byte_direct(transport, (n & 0x7FU) | 0x80U);
137
150
  n = n >> 7;
138
151
  }
139
152
  }
@@ -141,11 +154,11 @@ static void write_varint32(VALUE transport, uint32_t n) {
141
154
 
142
155
  static void write_varint64(VALUE transport, uint64_t n) {
143
156
  while (true) {
144
- if ((n & ~0x7F) == 0) {
145
- write_byte_direct(transport, n & 0x7f);
157
+ if ((n & ~0x7FULL) == 0ULL) {
158
+ write_byte_direct(transport, n & 0x7FULL);
146
159
  break;
147
160
  } else {
148
- write_byte_direct(transport, (n & 0x7F) | 0x80);
161
+ write_byte_direct(transport, (n & 0x7FULL) | 0x80ULL);
149
162
  n = n >> 7;
150
163
  }
151
164
  }
@@ -169,6 +182,7 @@ static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_
169
182
  VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32);
170
183
  VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str);
171
184
  VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf);
185
+ VALUE rb_thrift_compact_proto_write_uuid(VALUE self, VALUE uuid);
172
186
 
173
187
  VALUE rb_thrift_compact_proto_write_message_end(VALUE self) {
174
188
  return Qnil;
@@ -202,11 +216,12 @@ VALUE rb_thrift_compact_proto_write_set_end(VALUE self) {
202
216
 
203
217
  VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
204
218
  VALUE transport = GET_TRANSPORT(self);
219
+ int32_t seqid_value = FIX2INT(seqid);
205
220
  write_byte_direct(transport, PROTOCOL_ID);
206
221
  write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
207
- write_varint32(transport, FIX2INT(seqid));
222
+ write_varint32(transport, message_seqid_to_varint32(seqid_value));
208
223
  rb_thrift_compact_proto_write_string(self, name);
209
-
224
+
210
225
  return Qnil;
211
226
  }
212
227
 
@@ -320,6 +335,46 @@ VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf) {
320
335
  return Qnil;
321
336
  }
322
337
 
338
+ VALUE rb_thrift_compact_proto_write_uuid(VALUE self, VALUE uuid) {
339
+ if (NIL_P(uuid) || TYPE(uuid) != T_STRING) {
340
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("UUID must be a string")));
341
+ }
342
+
343
+ VALUE transport = GET_TRANSPORT(self);
344
+ char bytes[16];
345
+ const char* str = RSTRING_PTR(uuid);
346
+ long len = RSTRING_LEN(uuid);
347
+
348
+ // Parse UUID string (format: "550e8400-e29b-41d4-a716-446655440000")
349
+ // Expected length: 36 characters (32 hex + 4 hyphens)
350
+ if (len != 36 || str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-') {
351
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Invalid UUID format")));
352
+ }
353
+
354
+ // Parse hex string to bytes using direct conversion, skipping hyphens
355
+ int byte_idx = 0;
356
+ for (int i = 0; i < len && byte_idx < 16; i++) {
357
+ if (str[i] == '-') continue;
358
+ if (i + 1 >= len || str[i + 1] == '-') break;
359
+
360
+ // Convert two hex characters to one byte
361
+ int high = hex_char_to_int(str[i]);
362
+ int low = hex_char_to_int(str[i + 1]);
363
+
364
+ if (high < 0 || low < 0) break;
365
+
366
+ bytes[byte_idx++] = (unsigned char)((high << 4) | low);
367
+ i++; // skip next char since we processed two
368
+ }
369
+
370
+ if (byte_idx != 16) {
371
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Invalid UUID format")));
372
+ }
373
+
374
+ WRITE(transport, bytes, 16);
375
+ return Qnil;
376
+ }
377
+
323
378
  //---------------------------------------
324
379
  // interface reading methods
325
380
  //---------------------------------------
@@ -331,6 +386,7 @@ VALUE rb_thrift_compact_proto_read_binary(VALUE self);
331
386
  VALUE rb_thrift_compact_proto_read_byte(VALUE self);
332
387
  VALUE rb_thrift_compact_proto_read_i32(VALUE self);
333
388
  VALUE rb_thrift_compact_proto_read_i16(VALUE self);
389
+ VALUE rb_thrift_compact_proto_read_uuid(VALUE self);
334
390
 
335
391
  static int8_t get_ttype(int8_t ctype) {
336
392
  if (ctype == TTYPE_STOP) {
@@ -357,6 +413,8 @@ static int8_t get_ttype(int8_t ctype) {
357
413
  return TTYPE_MAP;
358
414
  } else if (ctype == CTYPE_STRUCT) {
359
415
  return TTYPE_STRUCT;
416
+ } else if (ctype == CTYPE_UUID) {
417
+ return TTYPE_UUID;
360
418
  } else {
361
419
  char str[50];
362
420
  sprintf(str, "don't know what type: %d", ctype);
@@ -370,37 +428,49 @@ static char read_byte_direct(VALUE self) {
370
428
  return (char)(FIX2INT(byte));
371
429
  }
372
430
 
373
- static int64_t zig_zag_to_ll(int64_t n) {
374
- return (((uint64_t)n) >> 1) ^ -(n & 1);
431
+ static int64_t zig_zag_to_ll(uint64_t n) {
432
+ return (int64_t)((n >> 1) ^ (0ULL - (n & 1ULL)));
375
433
  }
376
434
 
377
- static int32_t zig_zag_to_int(int32_t n) {
378
- return (((uint32_t)n) >> 1) ^ -(n & 1);
435
+ static int32_t zig_zag_to_int(uint32_t n) {
436
+ return (int32_t)((n >> 1) ^ (0U - (n & 1U)));
379
437
  }
380
438
 
381
- static int64_t read_varint64(VALUE self) {
382
- int shift = 0;
383
- int64_t result = 0;
384
- while (true) {
439
+ #define MAX_VARINT32_BYTES 5 /* ceil(32/7); matches protobuf wire format */
440
+ #define MAX_VARINT64_BYTES 10 /* ceil(64/7); matches protobuf wire format */
441
+
442
+ static uint64_t read_varint64(VALUE self) {
443
+ int i, shift = 0;
444
+ uint64_t result = 0;
445
+ for (i = 0; i < MAX_VARINT64_BYTES; i++) {
385
446
  int8_t b = read_byte_direct(self);
386
- result = result | ((uint64_t)(b & 0x7f) << shift);
447
+ result |= ((uint64_t)(b & 0x7f) << shift);
387
448
  if ((b & 0x80) != 0x80) {
388
- break;
449
+ return result;
389
450
  }
390
451
  shift += 7;
391
452
  }
392
- return result;
453
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int over 10 bytes.")));
454
+ return 0; /* unreachable */
393
455
  }
394
456
 
395
- static int16_t read_i16(VALUE self) {
396
- return zig_zag_to_int((int32_t)read_varint64(self));
457
+ static uint32_t read_varint32(VALUE self) {
458
+ int i, shift = 0;
459
+ uint32_t result = 0;
460
+ for (i = 0; i < MAX_VARINT32_BYTES; i++) {
461
+ int8_t b = read_byte_direct(self);
462
+ result |= ((uint32_t)(b & 0x7f) << shift);
463
+ if ((b & 0x80) != 0x80) {
464
+ return result;
465
+ }
466
+ shift += 7;
467
+ }
468
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int over 5 bytes.")));
469
+ return 0; /* unreachable */
397
470
  }
398
471
 
399
- static VALUE get_protocol_exception(VALUE code, VALUE message) {
400
- VALUE args[2];
401
- args[0] = code;
402
- args[1] = message;
403
- return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class);
472
+ static int16_t read_i16(VALUE self) {
473
+ return (int16_t)zig_zag_to_int(read_varint32(self));
404
474
  }
405
475
 
406
476
  VALUE rb_thrift_compact_proto_read_message_end(VALUE self) {
@@ -441,7 +511,7 @@ VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) {
441
511
  buf[len] = 0;
442
512
  rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
443
513
  }
444
-
514
+
445
515
  int8_t version_and_type = read_byte_direct(self);
446
516
  int8_t version = version_and_type & VERSION_MASK;
447
517
  if (version != VERSION) {
@@ -450,9 +520,9 @@ VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) {
450
520
  buf[len] = 0;
451
521
  rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
452
522
  }
453
-
523
+
454
524
  int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
455
- int32_t seqid = (int32_t)read_varint64(self);
525
+ int32_t seqid = message_seqid_from_varint32(read_varint32(self));
456
526
  VALUE messageName = rb_thrift_compact_proto_read_string(self);
457
527
  return rb_ary_new3(3, messageName, INT2FIX(type), INT2NUM(seqid));
458
528
  }
@@ -467,7 +537,7 @@ VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) {
467
537
 
468
538
  // mask off the 4 MSB of the type header. it could contain a field id delta.
469
539
  uint8_t modifier = ((type & 0xf0) >> 4);
470
-
540
+
471
541
  if (modifier == 0) {
472
542
  // not a delta. look ahead for the zigzag varint field id.
473
543
  (void) LAST_ID(self);
@@ -490,19 +560,19 @@ VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) {
490
560
  }
491
561
 
492
562
  VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) {
493
- int32_t size = (int32_t)read_varint64(self);
563
+ uint32_t size = read_varint32(self);
494
564
  uint8_t key_and_value_type = size == 0 ? 0 : read_byte_direct(self);
495
- return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), INT2FIX(size));
565
+ return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), UINT2NUM(size));
496
566
  }
497
567
 
498
568
  VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) {
499
569
  uint8_t size_and_type = read_byte_direct(self);
500
- int32_t size = (size_and_type >> 4) & 0x0f;
570
+ uint32_t size = (size_and_type >> 4) & 0x0f;
501
571
  if (size == 15) {
502
- size = (int32_t)read_varint64(self);
572
+ size = read_varint32(self);
503
573
  }
504
574
  uint8_t type = get_ttype(size_and_type & 0x0f);
505
- return rb_ary_new3(2, INT2FIX(type), INT2FIX(size));
575
+ return rb_ary_new3(2, INT2FIX(type), UINT2NUM(size));
506
576
  }
507
577
 
508
578
  VALUE rb_thrift_compact_proto_read_set_begin(VALUE self) {
@@ -528,7 +598,7 @@ VALUE rb_thrift_compact_proto_read_i16(VALUE self) {
528
598
  }
529
599
 
530
600
  VALUE rb_thrift_compact_proto_read_i32(VALUE self) {
531
- return INT2NUM(zig_zag_to_int((int32_t)read_varint64(self)));
601
+ return INT2NUM(zig_zag_to_int(read_varint32(self)));
532
602
  }
533
603
 
534
604
  VALUE rb_thrift_compact_proto_read_i64(VALUE self) {
@@ -561,11 +631,32 @@ VALUE rb_thrift_compact_proto_read_string(VALUE self) {
561
631
  }
562
632
 
563
633
  VALUE rb_thrift_compact_proto_read_binary(VALUE self) {
564
- int64_t size = read_varint64(self);
565
- return READ(self, size);
634
+ uint32_t size = read_varint32(self);
635
+ return rb_funcall(GET_TRANSPORT(self), read_all_method_id, 1, UINT2NUM(size));
636
+ }
637
+
638
+ VALUE rb_thrift_compact_proto_read_uuid(VALUE self) {
639
+ VALUE data = READ(self, 16);
640
+ const unsigned char* bytes = (const unsigned char*)RSTRING_PTR(data);
641
+
642
+ // Format as UUID string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
643
+ char uuid_str[37];
644
+ char* p = uuid_str;
645
+
646
+ for (int i = 0; i < 16; i++) {
647
+ *p++ = int_to_hex_char((bytes[i] >> 4) & 0x0F);
648
+ *p++ = int_to_hex_char(bytes[i] & 0x0F);
649
+ if (i == 3 || i == 5 || i == 7 || i == 9) {
650
+ *p++ = '-';
651
+ }
652
+ }
653
+
654
+ *p = '\0';
655
+
656
+ return rb_str_new(uuid_str, 36);
566
657
  }
567
658
 
568
- static void Init_constants() {
659
+ static void Init_constants(void) {
569
660
  thrift_compact_protocol_class = rb_const_get(thrift_module, rb_intern("CompactProtocol"));
570
661
  rb_global_variable(&thrift_compact_protocol_class);
571
662
 
@@ -582,7 +673,7 @@ static void Init_constants() {
582
673
  rbuf_ivar_id = rb_intern("@rbuf");
583
674
  }
584
675
 
585
- static void Init_rb_methods() {
676
+ static void Init_rb_methods(void) {
586
677
  rb_define_method(thrift_compact_protocol_class, "native?", rb_thrift_compact_proto_native_qmark, 0);
587
678
 
588
679
  rb_define_method(thrift_compact_protocol_class, "write_message_begin", rb_thrift_compact_proto_write_message_begin, 3);
@@ -599,6 +690,7 @@ static void Init_rb_methods() {
599
690
  rb_define_method(thrift_compact_protocol_class, "write_double", rb_thrift_compact_proto_write_double, 1);
600
691
  rb_define_method(thrift_compact_protocol_class, "write_string", rb_thrift_compact_proto_write_string, 1);
601
692
  rb_define_method(thrift_compact_protocol_class, "write_binary", rb_thrift_compact_proto_write_binary, 1);
693
+ rb_define_method(thrift_compact_protocol_class, "write_uuid", rb_thrift_compact_proto_write_uuid, 1);
602
694
 
603
695
  rb_define_method(thrift_compact_protocol_class, "write_message_end", rb_thrift_compact_proto_write_message_end, 0);
604
696
  rb_define_method(thrift_compact_protocol_class, "write_struct_begin", rb_thrift_compact_proto_write_struct_begin, 1);
@@ -622,6 +714,7 @@ static void Init_rb_methods() {
622
714
  rb_define_method(thrift_compact_protocol_class, "read_double", rb_thrift_compact_proto_read_double, 0);
623
715
  rb_define_method(thrift_compact_protocol_class, "read_string", rb_thrift_compact_proto_read_string, 0);
624
716
  rb_define_method(thrift_compact_protocol_class, "read_binary", rb_thrift_compact_proto_read_binary, 0);
717
+ rb_define_method(thrift_compact_protocol_class, "read_uuid", rb_thrift_compact_proto_read_uuid, 0);
625
718
 
626
719
  rb_define_method(thrift_compact_protocol_class, "read_message_end", rb_thrift_compact_proto_read_message_end, 0);
627
720
  rb_define_method(thrift_compact_protocol_class, "read_struct_begin", rb_thrift_compact_proto_read_struct_begin, 0);
@@ -632,7 +725,7 @@ static void Init_rb_methods() {
632
725
  rb_define_method(thrift_compact_protocol_class, "read_set_end", rb_thrift_compact_proto_read_set_end, 0);
633
726
  }
634
727
 
635
- void Init_compact_protocol() {
728
+ void Init_compact_protocol(void) {
636
729
  Init_constants();
637
730
  Init_rb_methods();
638
731
  }
data/ext/constants.h CHANGED
@@ -29,6 +29,7 @@ extern int TTYPE_MAP;
29
29
  extern int TTYPE_SET;
30
30
  extern int TTYPE_LIST;
31
31
  extern int TTYPE_STRUCT;
32
+ extern int TTYPE_UUID;
32
33
 
33
34
  extern ID validate_method_id;
34
35
  extern ID write_struct_begin_method_id;
@@ -49,6 +50,7 @@ extern ID write_list_begin_method_id;
49
50
  extern ID write_list_end_method_id;
50
51
  extern ID write_set_begin_method_id;
51
52
  extern ID write_set_end_method_id;
53
+ extern ID write_uuid_method_id;
52
54
  extern ID read_bool_method_id;
53
55
  extern ID read_byte_method_id;
54
56
  extern ID read_i16_method_id;
@@ -63,6 +65,7 @@ extern ID read_list_begin_method_id;
63
65
  extern ID read_list_end_method_id;
64
66
  extern ID read_set_begin_method_id;
65
67
  extern ID read_set_end_method_id;
68
+ extern ID read_uuid_method_id;
66
69
  extern ID read_struct_begin_method_id;
67
70
  extern ID read_struct_end_method_id;
68
71
  extern ID read_field_begin_method_id;
@@ -97,3 +100,12 @@ extern VALUE thrift_types_module;
97
100
  extern VALUE thrift_bytes_module;
98
101
  extern VALUE class_thrift_protocol;
99
102
  extern VALUE protocol_exception_class;
103
+
104
+ // protocol errors
105
+ extern int PROTOERR_UNKNOWN;
106
+ extern int PROTOERR_INVALID_DATA;
107
+ extern int PROTOERR_NEGATIVE_SIZE;
108
+ extern int PROTOERR_SIZE_LIMIT;
109
+ extern int PROTOERR_BAD_VERSION;
110
+ extern int PROTOERR_NOT_IMPLEMENTED;
111
+ extern int PROTOERR_DEPTH_LIMIT;
data/ext/extconf.rb CHANGED
@@ -1,4 +1,5 @@
1
- #
1
+ # frozen_string_literal: true
2
+ #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
4
5
  # distributed with this work for additional information
@@ -6,27 +7,35 @@
6
7
  # to you under the Apache License, Version 2.0 (the
7
8
  # "License"); you may not use this file except in compliance
8
9
  # with the License. You may obtain a copy of the License at
9
- #
10
+ #
10
11
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
+ #
12
13
  # Unless required by applicable law or agreed to in writing,
13
14
  # software distributed under the License is distributed on an
14
15
  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
16
  # KIND, either express or implied. See the License for the
16
17
  # specific language governing permissions and limitations
17
18
  # under the License.
18
- #
19
+ #
19
20
 
20
21
  if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
21
- File.open('Makefile', 'w'){|f| f.puts "all:\n\ninstall:\n" }
22
+ File.open('Makefile', 'w'){ |f| f.puts "all:\n\ninstall:\n" }
22
23
  else
23
24
  require 'mkmf'
24
- require 'rbconfig'
25
25
 
26
- $ARCH_FLAGS = RbConfig::CONFIG['CFLAGS'].scan( /(-arch )(\S+)/ ).map{|x,y| x + y + ' ' }.join('')
26
+ append_cflags(["-fsigned-char", "-g", "-O2", "-Wall", "-Werror", "-Werror=old-style-definition"])
27
27
 
28
+ # clang 21+ introduced -Wdefault-const-init-field-unsafe, which fires on
29
+ # Ruby 3.2's rstring.h (struct RString has a const field via RBasic).
30
+ # This is a Ruby header issue, not a Thrift bug, so suppress the warning here.
31
+ # append_cflags silently ignores flags unsupported by the compiler, so this
32
+ # is safe across all clang versions.
33
+ append_cflags("-Wno-default-const-init-field-unsafe")
28
34
 
29
- $CFLAGS = "-fsigned-char -g -O2 -Wall -Werror " + $ARCH_FLAGS
35
+ # Makes all symbols private by default to avoid unintended conflict
36
+ # with other gems. To explicitly export symbols you can use RUBY_FUNC_EXPORTED
37
+ # selectively, or entirely remove this flag.
38
+ append_cflags("-fvisibility=hidden")
30
39
 
31
40
  have_func("strlcpy", "string.h")
32
41
 
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
 
@@ -45,13 +49,13 @@ VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
45
49
 
46
50
  VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
47
51
  int length = FIX2INT(length_value);
48
-
52
+
49
53
  VALUE index_value = rb_ivar_get(self, index_ivar_id);
50
54
  int index = FIX2INT(index_value);
51
-
55
+
52
56
  VALUE buf = GET_BUF(self);
53
57
  VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value);
54
-
58
+
55
59
  index += length;
56
60
  if (index > RSTRING_LEN(buf)) {
57
61
  index = (int)RSTRING_LEN(buf);
@@ -69,6 +73,33 @@ VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
69
73
  return data;
70
74
  }
71
75
 
76
+ VALUE rb_thrift_memory_buffer_read_all(VALUE self, VALUE length_value) {
77
+ int length = FIX2INT(length_value);
78
+
79
+ if (RB_UNLIKELY(length < 0)) {
80
+ rb_exc_raise(rb_funcall(transport_exception_class, new_method_id, 2, transport_negative_size, rb_str_new2("Negative size")));
81
+ }
82
+
83
+ VALUE index_value = rb_ivar_get(self, index_ivar_id);
84
+ int index = FIX2INT(index_value);
85
+ VALUE buf = GET_BUF(self);
86
+
87
+ if (RB_UNLIKELY(length > RSTRING_LEN(buf) - index)) {
88
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
89
+ }
90
+
91
+ VALUE data = rb_str_subseq(buf, index, length);
92
+
93
+ index += length;
94
+ if (index >= GARBAGE_BUFFER_SIZE) {
95
+ rb_ivar_set(self, buf_ivar_id, rb_str_subseq(buf, index, RSTRING_LEN(buf) - index));
96
+ index = 0;
97
+ }
98
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
99
+
100
+ return data;
101
+ }
102
+
72
103
  VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
73
104
  VALUE index_value = rb_ivar_get(self, index_ivar_id);
74
105
  int index = FIX2INT(index_value);
@@ -118,17 +149,23 @@ VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, V
118
149
  return INT2FIX(i);
119
150
  }
120
151
 
121
- void Init_memory_buffer() {
152
+ void Init_memory_buffer(void) {
122
153
  VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport"));
123
154
  rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
124
155
  rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
156
+ rb_define_method(thrift_memory_buffer_class, "read_all", rb_thrift_memory_buffer_read_all, 1);
125
157
  rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0);
126
158
  rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2);
127
-
159
+
128
160
  buf_ivar_id = rb_intern("@buf");
129
161
  index_ivar_id = rb_intern("@index");
130
-
162
+
131
163
  slice_method_id = rb_intern("slice");
132
-
164
+ new_method_id = rb_intern("new");
165
+
133
166
  GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE")));
167
+ transport_exception_class = rb_const_get(thrift_module, rb_intern("TransportException"));
168
+ transport_negative_size = rb_const_get(transport_exception_class, rb_intern("NEGATIVE_SIZE"));
169
+ rb_global_variable(&transport_exception_class);
170
+ rb_global_variable(&transport_negative_size);
134
171
  }
data/ext/protocol.c CHANGED
@@ -0,0 +1,29 @@
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 <ruby.h>
21
+ #include <constants.h>
22
+ #include <protocol.h>
23
+
24
+ VALUE get_protocol_exception(VALUE code, VALUE message) {
25
+ VALUE args[2];
26
+ args[0] = code;
27
+ args[1] = message;
28
+ return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class);
29
+ }
data/ext/protocol.h CHANGED
@@ -0,0 +1,35 @@
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 <ruby.h>
21
+
22
+ VALUE get_protocol_exception(VALUE code, VALUE message);
23
+
24
+ // Efficient hex character to integer conversion
25
+ static inline int hex_char_to_int(char c) {
26
+ if (c >= '0' && c <= '9') return c - '0';
27
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
28
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
29
+ return -1; // invalid hex character
30
+ }
31
+
32
+ // Efficient integer to hex character conversion
33
+ static inline char int_to_hex_char(int val) {
34
+ return val < 10 ? ('0' + val) : ('a' + val - 10);
35
+ }