Skip to content

Commit 7d6d7fb

Browse files
committed
Added UUID support in Ruby library
1 parent 2fec153 commit 7d6d7fb

21 files changed

+652
-29
lines changed

LANGUAGES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,8 @@ Thrift's core protocol is TBinary, supported by all languages except for JavaScr
312312
<td align=left><a href="https://github.com/apache/thrift/blob/master/lib/rb/README.md">Ruby</a></td>
313313
<!-- Since -----------------><td>0.2.0</td>
314314
<!-- Build Systems ---------><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cred.png" alt=""/></td>
315-
<!-- Language Levels -------><td>2.4.0</td><td>2.5.1p57</td>
316-
<!-- Field types -----------><td><img src="/doc/images/cred.png" alt=""/></td>
315+
<!-- Language Levels -------><td>2.7.8</td><td>4.0.0</td>
316+
<!-- Field types -----------><td><img src="/doc/images/cgrn.png" alt="Yes"/></td>
317317
<!-- Low-Level Transports --><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cred.png" alt=""/></td><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cred.png" alt=""/></td><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cgrn.png" alt="Yes"/></td>
318318
<!-- Transport Wrappers ----><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cred.png" alt=""/></td><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cred.png" alt=""/></td>
319319
<!-- Protocols -------------><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cgrn.png" alt="Yes"/></td><td><img src="/doc/images/cgrn.png" alt="Yes"/></td>

compiler/cpp/src/thrift/generate/t_rb_generator.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,9 @@ t_rb_ofstream& t_rb_generator::render_const_value(t_rb_ofstream& out,
430430
case t_base_type::TYPE_STRING:
431431
out << "%q\"" << get_escaped_string(value) << '"';
432432
break;
433+
case t_base_type::TYPE_UUID:
434+
out << "%q\"" << get_escaped_string(value) << '"';
435+
break;
433436
case t_base_type::TYPE_BOOL:
434437
out << (value->get_integer() > 0 ? "true" : "false");
435438
break;
@@ -1175,6 +1178,8 @@ string t_rb_generator::type_to_enum(t_type* type) {
11751178
return "::Thrift::Types::I64";
11761179
case t_base_type::TYPE_DOUBLE:
11771180
return "::Thrift::Types::DOUBLE";
1181+
case t_base_type::TYPE_UUID:
1182+
return "::Thrift::Types::UUID";
11781183
default:
11791184
throw "compiler error: unhandled type";
11801185
}

lib/rb/ext/binary_protocol_accelerated.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <ruby.h>
2121
#include <stdbool.h>
2222
#include <stdint.h>
23+
#include <string.h>
2324
#include <constants.h>
2425
#include <struct.h>
2526
#include <macros.h>
@@ -86,6 +87,19 @@ static void write_string_direct(VALUE trans, VALUE str) {
8687
rb_funcall(trans, write_method_id, 1, str);
8788
}
8889

90+
// Efficient hex character to integer conversion
91+
static inline int hex_char_to_int(char c) {
92+
if (c >= '0' && c <= '9') return c - '0';
93+
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
94+
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
95+
return -1; // invalid hex character
96+
}
97+
98+
// Efficient integer to hex character conversion
99+
static inline char int_to_hex_char(int val) {
100+
return val < 10 ? ('0' + val) : ('a' + val - 10);
101+
}
102+
89103
//--------------------------------
90104
// interface writing methods
91105
//--------------------------------
@@ -228,6 +242,58 @@ VALUE rb_thrift_binary_proto_write_binary(VALUE self, VALUE buf) {
228242
return Qnil;
229243
}
230244

245+
VALUE rb_thrift_binary_proto_write_uuid(VALUE self, VALUE uuid) {
246+
CHECK_NIL(uuid);
247+
248+
if (TYPE(uuid) != T_STRING) {
249+
rb_raise(rb_eStandardError, "UUID must be a string");
250+
}
251+
252+
VALUE trans = GET_TRANSPORT(self);
253+
char bytes[16];
254+
const char* str = RSTRING_PTR(uuid);
255+
long len = RSTRING_LEN(uuid);
256+
257+
// Parse UUID string (format: "550e8400-e29b-41d4-a716-446655440000")
258+
// Expected length: 36 characters (32 hex + 4 hyphens)
259+
if (len != 36 || str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-') {
260+
VALUE exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException"));
261+
VALUE invalid_data = rb_const_get(exception_class, rb_intern("INVALID_DATA"));
262+
VALUE args[2];
263+
args[0] = invalid_data;
264+
args[1] = rb_str_new2("Invalid UUID format");
265+
rb_exc_raise(rb_class_new_instance(2, args, exception_class));
266+
}
267+
268+
// Parse hex string to bytes using direct conversion, skipping hyphens
269+
int byte_idx = 0;
270+
for (int i = 0; i < len && byte_idx < 16; i++) {
271+
if (str[i] == '-') continue;
272+
273+
// Convert two hex characters to one byte
274+
int high = hex_char_to_int(str[i]);
275+
int low = hex_char_to_int(str[i + 1]);
276+
277+
if (high < 0 || low < 0) {
278+
VALUE exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException"));
279+
VALUE invalid_data = rb_const_get(exception_class, rb_intern("INVALID_DATA"));
280+
VALUE args[2];
281+
args[0] = invalid_data;
282+
args[1] = rb_str_new2("Invalid hex character in UUID");
283+
rb_exc_raise(rb_class_new_instance(2, args, exception_class));
284+
}
285+
286+
bytes[byte_idx++] = (unsigned char)((high << 4) | low);
287+
i++; // skip next char since we processed two
288+
}
289+
290+
// Write LSB first (bytes 8-15), then MSB (bytes 0-7)
291+
WRITE(trans, (char*)&bytes[8], 8);
292+
WRITE(trans, (char*)&bytes[0], 8);
293+
294+
return Qnil;
295+
}
296+
231297
//---------------------------------------
232298
// interface reading methods
233299
//---------------------------------------
@@ -400,6 +466,40 @@ VALUE rb_thrift_binary_proto_read_binary(VALUE self) {
400466
return READ(self, size);
401467
}
402468

469+
VALUE rb_thrift_binary_proto_read_uuid(VALUE self) {
470+
// Read 16 bytes directly into a single buffer: LSB (8 bytes) then MSB (8 bytes)
471+
unsigned char bytes[16];
472+
VALUE data = READ(self, 16);
473+
memcpy(bytes, RSTRING_PTR(data), 16);
474+
475+
// Format as UUID string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
476+
// MSB first (bytes 8-15), then LSB (bytes 0-7)
477+
char uuid_str[37];
478+
char* p = uuid_str;
479+
480+
// Format MSB (bytes 8-15)
481+
for (int i = 8; i < 16; i++) {
482+
*p++ = int_to_hex_char((bytes[i] >> 4) & 0x0F);
483+
*p++ = int_to_hex_char(bytes[i] & 0x0F);
484+
if (i == 11 || i == 13 || i == 15) {
485+
*p++ = '-';
486+
}
487+
}
488+
489+
// Format LSB (bytes 0-7)
490+
for (int i = 0; i < 8; i++) {
491+
*p++ = int_to_hex_char((bytes[i] >> 4) & 0x0F);
492+
*p++ = int_to_hex_char(bytes[i] & 0x0F);
493+
if (i == 1) {
494+
*p++ = '-';
495+
}
496+
}
497+
498+
*p = '\0';
499+
500+
return rb_str_new(uuid_str, 36);
501+
}
502+
403503
void Init_binary_protocol_accelerated() {
404504
VALUE thrift_binary_protocol_class = rb_const_get(thrift_module, rb_intern("BinaryProtocol"));
405505

@@ -425,6 +525,7 @@ void Init_binary_protocol_accelerated() {
425525
rb_define_method(bpa_class, "write_double", rb_thrift_binary_proto_write_double, 1);
426526
rb_define_method(bpa_class, "write_string", rb_thrift_binary_proto_write_string, 1);
427527
rb_define_method(bpa_class, "write_binary", rb_thrift_binary_proto_write_binary, 1);
528+
rb_define_method(bpa_class, "write_uuid", rb_thrift_binary_proto_write_uuid, 1);
428529
// unused methods
429530
rb_define_method(bpa_class, "write_message_end", rb_thrift_binary_proto_write_message_end, 0);
430531
rb_define_method(bpa_class, "write_struct_begin", rb_thrift_binary_proto_write_struct_begin, 1);
@@ -447,6 +548,7 @@ void Init_binary_protocol_accelerated() {
447548
rb_define_method(bpa_class, "read_double", rb_thrift_binary_proto_read_double, 0);
448549
rb_define_method(bpa_class, "read_string", rb_thrift_binary_proto_read_string, 0);
449550
rb_define_method(bpa_class, "read_binary", rb_thrift_binary_proto_read_binary, 0);
551+
rb_define_method(bpa_class, "read_uuid", rb_thrift_binary_proto_read_uuid, 0);
450552
// unused methods
451553
rb_define_method(bpa_class, "read_message_end", rb_thrift_binary_proto_read_message_end, 0);
452554
rb_define_method(bpa_class, "read_struct_begin", rb_thrift_binary_proto_read_struct_begin, 0);

lib/rb/ext/compact_protocol.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ static int CTYPE_LIST = 0x09;
5858
static int CTYPE_SET = 0x0A;
5959
static int CTYPE_MAP = 0x0B;
6060
static int CTYPE_STRUCT = 0x0C;
61+
static int CTYPE_UUID = 0x0D;
6162

6263
VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
6364

@@ -86,6 +87,8 @@ static int get_compact_type(VALUE type_value) {
8687
return CTYPE_MAP;
8788
} else if (type == TTYPE_STRUCT) {
8889
return CTYPE_STRUCT;
90+
} else if (type == TTYPE_UUID) {
91+
return CTYPE_UUID;
8992
} else {
9093
char str[50];
9194
sprintf(str, "don't know what type: %d", type);
@@ -357,6 +360,8 @@ static int8_t get_ttype(int8_t ctype) {
357360
return TTYPE_MAP;
358361
} else if (ctype == CTYPE_STRUCT) {
359362
return TTYPE_STRUCT;
363+
} else if (ctype == CTYPE_UUID) {
364+
return TTYPE_UUID;
360365
} else {
361366
char str[50];
362367
sprintf(str, "don't know what type: %d", ctype);

lib/rb/ext/constants.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ extern int TTYPE_MAP;
2929
extern int TTYPE_SET;
3030
extern int TTYPE_LIST;
3131
extern int TTYPE_STRUCT;
32+
extern int TTYPE_UUID;
3233

3334
extern ID validate_method_id;
3435
extern ID write_struct_begin_method_id;
@@ -49,6 +50,7 @@ extern ID write_list_begin_method_id;
4950
extern ID write_list_end_method_id;
5051
extern ID write_set_begin_method_id;
5152
extern ID write_set_end_method_id;
53+
extern ID write_uuid_method_id;
5254
extern ID read_bool_method_id;
5355
extern ID read_byte_method_id;
5456
extern ID read_i16_method_id;
@@ -63,6 +65,7 @@ extern ID read_list_begin_method_id;
6365
extern ID read_list_end_method_id;
6466
extern ID read_set_begin_method_id;
6567
extern ID read_set_end_method_id;
68+
extern ID read_uuid_method_id;
6669
extern ID read_struct_begin_method_id;
6770
extern ID read_struct_end_method_id;
6871
extern ID read_field_begin_method_id;

lib/rb/ext/struct.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ VALUE default_write_string(VALUE protocol, VALUE value) {
7575
return Qnil;
7676
}
7777

78+
VALUE default_write_uuid(VALUE protocol, VALUE value) {
79+
rb_funcall(protocol, write_uuid_method_id, 1, value);
80+
return Qnil;
81+
}
82+
7883
VALUE default_write_binary(VALUE protocol, VALUE value) {
7984
rb_funcall(protocol, write_binary_method_id, 1, value);
8085
return Qnil;
@@ -195,6 +200,10 @@ VALUE default_read_string(VALUE protocol) {
195200
return rb_funcall(protocol, read_string_method_id, 0);
196201
}
197202

203+
VALUE default_read_uuid(VALUE protocol) {
204+
return rb_funcall(protocol, read_uuid_method_id, 0);
205+
}
206+
198207
VALUE default_read_binary(VALUE protocol) {
199208
return rb_funcall(protocol, read_binary_method_id, 0);
200209
}
@@ -342,6 +351,8 @@ static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_i
342351
} else {
343352
default_write_binary(protocol, value);
344353
}
354+
} else if (ttype == TTYPE_UUID) {
355+
default_write_uuid(protocol, value);
345356
} else if (IS_CONTAINER(ttype)) {
346357
write_container(ttype, field_info, value, protocol);
347358
} else if (ttype == TTYPE_STRUCT) {
@@ -452,6 +463,8 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
452463
}
453464
} else if (ttype == TTYPE_DOUBLE) {
454465
result = default_read_double(protocol);
466+
} else if (ttype == TTYPE_UUID) {
467+
result = default_read_uuid(protocol);
455468
} else if (ttype == TTYPE_STRUCT) {
456469
VALUE klass = rb_hash_aref(field_info, class_sym);
457470
result = rb_class_new_instance(0, NULL, klass);

lib/rb/ext/thrift_native.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ int TTYPE_MAP;
4343
int TTYPE_SET;
4444
int TTYPE_LIST;
4545
int TTYPE_STRUCT;
46+
int TTYPE_UUID;
4647

4748
// method ids
4849
ID validate_method_id;
@@ -57,6 +58,7 @@ ID write_i32_method_id;
5758
ID write_i64_method_id;
5859
ID write_double_method_id;
5960
ID write_string_method_id;
61+
ID write_uuid_method_id;
6062
ID write_binary_method_id;
6163
ID write_map_begin_method_id;
6264
ID write_map_end_method_id;
@@ -70,6 +72,7 @@ ID read_i16_method_id;
7072
ID read_i32_method_id;
7173
ID read_i64_method_id;
7274
ID read_string_method_id;
75+
ID read_uuid_method_id;
7376
ID read_binary_method_id;
7477
ID read_double_method_id;
7578
ID read_map_begin_method_id;
@@ -138,6 +141,7 @@ void Init_thrift_native() {
138141
TTYPE_SET = FIX2INT(rb_const_get(thrift_types_module, rb_intern("SET")));
139142
TTYPE_LIST = FIX2INT(rb_const_get(thrift_types_module, rb_intern("LIST")));
140143
TTYPE_STRUCT = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRUCT")));
144+
TTYPE_UUID = FIX2INT(rb_const_get(thrift_types_module, rb_intern("UUID")));
141145

142146
// method ids
143147
validate_method_id = rb_intern("validate");
@@ -152,6 +156,7 @@ void Init_thrift_native() {
152156
write_i64_method_id = rb_intern("write_i64");
153157
write_double_method_id = rb_intern("write_double");
154158
write_string_method_id = rb_intern("write_string");
159+
write_uuid_method_id = rb_intern("write_uuid");
155160
write_binary_method_id = rb_intern("write_binary");
156161
write_map_begin_method_id = rb_intern("write_map_begin");
157162
write_map_end_method_id = rb_intern("write_map_end");
@@ -165,10 +170,11 @@ void Init_thrift_native() {
165170
read_i32_method_id = rb_intern("read_i32");
166171
read_i64_method_id = rb_intern("read_i64");
167172
read_string_method_id = rb_intern("read_string");
173+
read_uuid_method_id = rb_intern("read_uuid");
168174
read_binary_method_id = rb_intern("read_binary");
169175
read_double_method_id = rb_intern("read_double");
170176
read_map_begin_method_id = rb_intern("read_map_begin");
171-
read_map_end_method_id = rb_intern("read_map_end");
177+
read_map_end_method_id = rb_intern("read_map_end");
172178
read_list_begin_method_id = rb_intern("read_list_begin");
173179
read_list_end_method_id = rb_intern("read_list_end");
174180
read_set_begin_method_id = rb_intern("read_set_begin");
@@ -192,7 +198,7 @@ void Init_thrift_native() {
192198
fields_const_id = rb_intern("FIELDS");
193199
transport_ivar_id = rb_intern("@trans");
194200
strict_read_ivar_id = rb_intern("@strict_read");
195-
strict_write_ivar_id = rb_intern("@strict_write");
201+
strict_write_ivar_id = rb_intern("@strict_write");
196202

197203
// cached symbols
198204
type_sym = ID2SYM(rb_intern("type"));

lib/rb/lib/thrift/protocol/base_protocol.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ def write_binary(buf)
137137
raise NotImplementedError
138138
end
139139

140+
# Writes a UUID as 16 bytes.
141+
#
142+
# uuid - The UUID string to write (e.g. "550e8400-e29b-41d4-a716-446655440000").
143+
#
144+
# Returns nothing.
145+
def write_uuid(uuid)
146+
raise NotImplementedError
147+
end
148+
140149
def read_message_begin
141150
raise NotImplementedError
142151
end
@@ -212,6 +221,13 @@ def read_binary
212221
raise NotImplementedError
213222
end
214223

224+
# Reads a UUID as 16 bytes and returns it as a string.
225+
#
226+
# Returns a String (e.g. "550e8400-e29b-41d4-a716-446655440000").
227+
def read_uuid
228+
raise NotImplementedError
229+
end
230+
215231
# Writes a field based on the field information, field ID and value.
216232
#
217233
# field_info - A Hash containing the definition of the field:
@@ -276,6 +292,8 @@ def write_type(field_info, value)
276292
else
277293
write_string(value)
278294
end
295+
when Types::UUID
296+
write_uuid(value)
279297
when Types::STRUCT
280298
value.write(self)
281299
else
@@ -316,6 +334,8 @@ def read_type(field_info)
316334
else
317335
read_string
318336
end
337+
when Types::UUID
338+
read_uuid
319339
else
320340
raise NotImplementedError
321341
end
@@ -337,6 +357,8 @@ def skip(type)
337357
read_double
338358
when Types::STRING
339359
read_string
360+
when Types::UUID
361+
read_uuid
340362
when Types::STRUCT
341363
read_struct_begin
342364
while true

0 commit comments

Comments
 (0)