]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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 <stdbool.h> | |
22 | #include <stdint.h> | |
23 | #include <constants.h> | |
24 | #include <struct.h> | |
25 | #include <macros.h> | |
26 | #include <bytes.h> | |
27 | ||
28 | #define LAST_ID(obj) FIX2INT(rb_ary_pop(rb_ivar_get(obj, last_field_id))) | |
29 | #define SET_LAST_ID(obj, val) rb_ary_push(rb_ivar_get(obj, last_field_id), val) | |
30 | ||
31 | VALUE rb_thrift_compact_proto_native_qmark(VALUE self) { | |
32 | return Qtrue; | |
33 | } | |
34 | ||
35 | static ID last_field_id; | |
36 | static ID boolean_field_id; | |
37 | static ID bool_value_id; | |
38 | static ID rbuf_ivar_id; | |
39 | ||
40 | static int VERSION; | |
41 | static int VERSION_MASK; | |
42 | static int TYPE_MASK; | |
43 | static int TYPE_BITS; | |
44 | static int TYPE_SHIFT_AMOUNT; | |
45 | static int PROTOCOL_ID; | |
46 | ||
47 | static VALUE thrift_compact_protocol_class; | |
48 | ||
49 | static int CTYPE_BOOLEAN_TRUE = 0x01; | |
50 | static int CTYPE_BOOLEAN_FALSE = 0x02; | |
51 | static int CTYPE_BYTE = 0x03; | |
52 | static int CTYPE_I16 = 0x04; | |
53 | static int CTYPE_I32 = 0x05; | |
54 | static int CTYPE_I64 = 0x06; | |
55 | static int CTYPE_DOUBLE = 0x07; | |
56 | static int CTYPE_BINARY = 0x08; | |
57 | static int CTYPE_LIST = 0x09; | |
58 | static int CTYPE_SET = 0x0A; | |
59 | static int CTYPE_MAP = 0x0B; | |
60 | static int CTYPE_STRUCT = 0x0C; | |
61 | ||
62 | VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16); | |
63 | ||
64 | // TODO: implement this | |
65 | static int get_compact_type(VALUE type_value) { | |
66 | int type = FIX2INT(type_value); | |
67 | if (type == TTYPE_BOOL) { | |
68 | return CTYPE_BOOLEAN_TRUE; | |
69 | } else if (type == TTYPE_BYTE) { | |
70 | return CTYPE_BYTE; | |
71 | } else if (type == TTYPE_I16) { | |
72 | return CTYPE_I16; | |
73 | } else if (type == TTYPE_I32) { | |
74 | return CTYPE_I32; | |
75 | } else if (type == TTYPE_I64) { | |
76 | return CTYPE_I64; | |
77 | } else if (type == TTYPE_DOUBLE) { | |
78 | return CTYPE_DOUBLE; | |
79 | } else if (type == TTYPE_STRING) { | |
80 | return CTYPE_BINARY; | |
81 | } else if (type == TTYPE_LIST) { | |
82 | return CTYPE_LIST; | |
83 | } else if (type == TTYPE_SET) { | |
84 | return CTYPE_SET; | |
85 | } else if (type == TTYPE_MAP) { | |
86 | return CTYPE_MAP; | |
87 | } else if (type == TTYPE_STRUCT) { | |
88 | return CTYPE_STRUCT; | |
89 | } else { | |
90 | char str[50]; | |
91 | sprintf(str, "don't know what type: %d", type); | |
92 | rb_raise(rb_eStandardError, "%s", str); | |
93 | return 0; | |
94 | } | |
95 | } | |
96 | ||
97 | static void write_byte_direct(VALUE transport, int8_t b) { | |
98 | WRITE(transport, (char*)&b, 1); | |
99 | } | |
100 | ||
101 | static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, VALUE type_override) { | |
102 | int id = FIX2INT(id_value); | |
103 | int last_id = LAST_ID(self); | |
104 | VALUE transport = GET_TRANSPORT(self); | |
105 | ||
106 | // if there's a type override, use that. | |
107 | int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type); | |
108 | // check if we can use delta encoding for the field id | |
109 | int diff = id - last_id; | |
110 | if (diff > 0 && diff <= 15) { | |
111 | // write them together | |
112 | write_byte_direct(transport, diff << 4 | (type_to_write & 0x0f)); | |
113 | } else { | |
114 | // write them separate | |
115 | write_byte_direct(transport, type_to_write & 0x0f); | |
116 | rb_thrift_compact_proto_write_i16(self, id_value); | |
117 | } | |
118 | ||
119 | SET_LAST_ID(self, id_value); | |
120 | } | |
121 | ||
122 | static int32_t int_to_zig_zag(int32_t n) { | |
123 | return (n << 1) ^ (n >> 31); | |
124 | } | |
125 | ||
126 | static uint64_t ll_to_zig_zag(int64_t n) { | |
127 | return (n << 1) ^ (n >> 63); | |
128 | } | |
129 | ||
130 | static void write_varint32(VALUE transport, uint32_t n) { | |
131 | while (true) { | |
132 | if ((n & ~0x7F) == 0) { | |
133 | write_byte_direct(transport, n & 0x7f); | |
134 | break; | |
135 | } else { | |
136 | write_byte_direct(transport, (n & 0x7F) | 0x80); | |
137 | n = n >> 7; | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
142 | static void write_varint64(VALUE transport, uint64_t n) { | |
143 | while (true) { | |
144 | if ((n & ~0x7F) == 0) { | |
145 | write_byte_direct(transport, n & 0x7f); | |
146 | break; | |
147 | } else { | |
148 | write_byte_direct(transport, (n & 0x7F) | 0x80); | |
149 | n = n >> 7; | |
150 | } | |
151 | } | |
152 | } | |
153 | ||
154 | static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_value) { | |
155 | int size = FIX2INT(size_value); | |
156 | if (size <= 14) { | |
157 | write_byte_direct(transport, size << 4 | get_compact_type(elem_type)); | |
158 | } else { | |
159 | write_byte_direct(transport, 0xf0 | get_compact_type(elem_type)); | |
160 | write_varint32(transport, size); | |
161 | } | |
162 | } | |
163 | ||
164 | ||
165 | //-------------------------------- | |
166 | // interface writing methods | |
167 | //-------------------------------- | |
168 | ||
169 | VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32); | |
170 | VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str); | |
171 | VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf); | |
172 | ||
173 | VALUE rb_thrift_compact_proto_write_message_end(VALUE self) { | |
174 | return Qnil; | |
175 | } | |
176 | ||
177 | VALUE rb_thrift_compact_proto_write_struct_begin(VALUE self, VALUE name) { | |
178 | rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0)); | |
179 | return Qnil; | |
180 | } | |
181 | ||
182 | VALUE rb_thrift_compact_proto_write_struct_end(VALUE self) { | |
183 | rb_ary_pop(rb_ivar_get(self, last_field_id)); | |
184 | return Qnil; | |
185 | } | |
186 | ||
187 | VALUE rb_thrift_compact_proto_write_field_end(VALUE self) { | |
188 | return Qnil; | |
189 | } | |
190 | ||
191 | VALUE rb_thrift_compact_proto_write_map_end(VALUE self) { | |
192 | return Qnil; | |
193 | } | |
194 | ||
195 | VALUE rb_thrift_compact_proto_write_list_end(VALUE self) { | |
196 | return Qnil; | |
197 | } | |
198 | ||
199 | VALUE rb_thrift_compact_proto_write_set_end(VALUE self) { | |
200 | return Qnil; | |
201 | } | |
202 | ||
203 | VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) { | |
204 | VALUE transport = GET_TRANSPORT(self); | |
205 | write_byte_direct(transport, PROTOCOL_ID); | |
206 | write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK)); | |
207 | write_varint32(transport, FIX2INT(seqid)); | |
208 | rb_thrift_compact_proto_write_string(self, name); | |
209 | ||
210 | return Qnil; | |
211 | } | |
212 | ||
213 | VALUE rb_thrift_compact_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) { | |
214 | if (FIX2INT(type) == TTYPE_BOOL) { | |
215 | // we want to possibly include the value, so we'll wait. | |
216 | rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, id)); | |
217 | } else { | |
218 | write_field_begin_internal(self, type, id, Qnil); | |
219 | } | |
220 | ||
221 | return Qnil; | |
222 | } | |
223 | ||
224 | VALUE rb_thrift_compact_proto_write_field_stop(VALUE self) { | |
225 | write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP); | |
226 | return Qnil; | |
227 | } | |
228 | ||
229 | VALUE rb_thrift_compact_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size_value) { | |
230 | int size = FIX2INT(size_value); | |
231 | VALUE transport = GET_TRANSPORT(self); | |
232 | if (size == 0) { | |
233 | write_byte_direct(transport, 0); | |
234 | } else { | |
235 | write_varint32(transport, size); | |
236 | write_byte_direct(transport, get_compact_type(ktype) << 4 | get_compact_type(vtype)); | |
237 | } | |
238 | return Qnil; | |
239 | } | |
240 | ||
241 | VALUE rb_thrift_compact_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) { | |
242 | write_collection_begin(GET_TRANSPORT(self), etype, size); | |
243 | return Qnil; | |
244 | } | |
245 | ||
246 | VALUE rb_thrift_compact_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) { | |
247 | write_collection_begin(GET_TRANSPORT(self), etype, size); | |
248 | return Qnil; | |
249 | } | |
250 | ||
251 | VALUE rb_thrift_compact_proto_write_bool(VALUE self, VALUE b) { | |
252 | int8_t type = b == Qtrue ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE; | |
253 | VALUE boolean_field = rb_ivar_get(self, boolean_field_id); | |
254 | if (NIL_P(boolean_field)) { | |
255 | // we're not part of a field, so just write the value. | |
256 | write_byte_direct(GET_TRANSPORT(self), type); | |
257 | } else { | |
258 | // we haven't written the field header yet | |
259 | write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), rb_ary_entry(boolean_field, 1), INT2FIX(type)); | |
260 | rb_ivar_set(self, boolean_field_id, Qnil); | |
261 | } | |
262 | return Qnil; | |
263 | } | |
264 | ||
265 | VALUE rb_thrift_compact_proto_write_byte(VALUE self, VALUE byte) { | |
266 | CHECK_NIL(byte); | |
267 | write_byte_direct(GET_TRANSPORT(self), FIX2INT(byte)); | |
268 | return Qnil; | |
269 | } | |
270 | ||
271 | VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16) { | |
272 | rb_thrift_compact_proto_write_i32(self, i16); | |
273 | return Qnil; | |
274 | } | |
275 | ||
276 | VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32) { | |
277 | CHECK_NIL(i32); | |
278 | write_varint32(GET_TRANSPORT(self), int_to_zig_zag(NUM2INT(i32))); | |
279 | return Qnil; | |
280 | } | |
281 | ||
282 | VALUE rb_thrift_compact_proto_write_i64(VALUE self, VALUE i64) { | |
283 | CHECK_NIL(i64); | |
284 | write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(NUM2LL(i64))); | |
285 | return Qnil; | |
286 | } | |
287 | ||
288 | VALUE rb_thrift_compact_proto_write_double(VALUE self, VALUE dub) { | |
289 | CHECK_NIL(dub); | |
290 | // Unfortunately, bitwise_cast doesn't work in C. Bad C! | |
291 | union { | |
292 | double f; | |
293 | int64_t l; | |
294 | } transfer; | |
295 | transfer.f = RFLOAT_VALUE(rb_Float(dub)); | |
296 | char buf[8]; | |
297 | buf[0] = transfer.l & 0xff; | |
298 | buf[1] = (transfer.l >> 8) & 0xff; | |
299 | buf[2] = (transfer.l >> 16) & 0xff; | |
300 | buf[3] = (transfer.l >> 24) & 0xff; | |
301 | buf[4] = (transfer.l >> 32) & 0xff; | |
302 | buf[5] = (transfer.l >> 40) & 0xff; | |
303 | buf[6] = (transfer.l >> 48) & 0xff; | |
304 | buf[7] = (transfer.l >> 56) & 0xff; | |
305 | WRITE(GET_TRANSPORT(self), buf, 8); | |
306 | return Qnil; | |
307 | } | |
308 | ||
309 | VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str) { | |
310 | str = convert_to_utf8_byte_buffer(str); | |
311 | rb_thrift_compact_proto_write_binary(self, str); | |
312 | return Qnil; | |
313 | } | |
314 | ||
315 | VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf) { | |
316 | buf = force_binary_encoding(buf); | |
317 | VALUE transport = GET_TRANSPORT(self); | |
318 | write_varint32(transport, RSTRING_LEN(buf)); | |
319 | WRITE(transport, StringValuePtr(buf), RSTRING_LEN(buf)); | |
320 | return Qnil; | |
321 | } | |
322 | ||
323 | //--------------------------------------- | |
324 | // interface reading methods | |
325 | //--------------------------------------- | |
326 | ||
327 | #define is_bool_type(ctype) (((ctype) & 0x0F) == CTYPE_BOOLEAN_TRUE || ((ctype) & 0x0F) == CTYPE_BOOLEAN_FALSE) | |
328 | ||
329 | VALUE rb_thrift_compact_proto_read_string(VALUE self); | |
330 | VALUE rb_thrift_compact_proto_read_binary(VALUE self); | |
331 | VALUE rb_thrift_compact_proto_read_byte(VALUE self); | |
332 | VALUE rb_thrift_compact_proto_read_i32(VALUE self); | |
333 | VALUE rb_thrift_compact_proto_read_i16(VALUE self); | |
334 | ||
335 | static int8_t get_ttype(int8_t ctype) { | |
336 | if (ctype == TTYPE_STOP) { | |
337 | return TTYPE_STOP; | |
338 | } else if (ctype == CTYPE_BOOLEAN_TRUE || ctype == CTYPE_BOOLEAN_FALSE) { | |
339 | return TTYPE_BOOL; | |
340 | } else if (ctype == CTYPE_BYTE) { | |
341 | return TTYPE_BYTE; | |
342 | } else if (ctype == CTYPE_I16) { | |
343 | return TTYPE_I16; | |
344 | } else if (ctype == CTYPE_I32) { | |
345 | return TTYPE_I32; | |
346 | } else if (ctype == CTYPE_I64) { | |
347 | return TTYPE_I64; | |
348 | } else if (ctype == CTYPE_DOUBLE) { | |
349 | return TTYPE_DOUBLE; | |
350 | } else if (ctype == CTYPE_BINARY) { | |
351 | return TTYPE_STRING; | |
352 | } else if (ctype == CTYPE_LIST) { | |
353 | return TTYPE_LIST; | |
354 | } else if (ctype == CTYPE_SET) { | |
355 | return TTYPE_SET; | |
356 | } else if (ctype == CTYPE_MAP) { | |
357 | return TTYPE_MAP; | |
358 | } else if (ctype == CTYPE_STRUCT) { | |
359 | return TTYPE_STRUCT; | |
360 | } else { | |
361 | char str[50]; | |
362 | sprintf(str, "don't know what type: %d", ctype); | |
363 | rb_raise(rb_eStandardError, "%s", str); | |
364 | return 0; | |
365 | } | |
366 | } | |
367 | ||
368 | static char read_byte_direct(VALUE self) { | |
369 | VALUE byte = rb_funcall(GET_TRANSPORT(self), read_byte_method_id, 0); | |
370 | return (char)(FIX2INT(byte)); | |
371 | } | |
372 | ||
373 | static int64_t zig_zag_to_ll(int64_t n) { | |
374 | return (((uint64_t)n) >> 1) ^ -(n & 1); | |
375 | } | |
376 | ||
377 | static int32_t zig_zag_to_int(int32_t n) { | |
378 | return (((uint32_t)n) >> 1) ^ -(n & 1); | |
379 | } | |
380 | ||
381 | static int64_t read_varint64(VALUE self) { | |
382 | int shift = 0; | |
383 | int64_t result = 0; | |
384 | while (true) { | |
385 | int8_t b = read_byte_direct(self); | |
386 | result = result | ((uint64_t)(b & 0x7f) << shift); | |
387 | if ((b & 0x80) != 0x80) { | |
388 | break; | |
389 | } | |
390 | shift += 7; | |
391 | } | |
392 | return result; | |
393 | } | |
394 | ||
395 | static int16_t read_i16(VALUE self) { | |
396 | return zig_zag_to_int((int32_t)read_varint64(self)); | |
397 | } | |
398 | ||
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); | |
404 | } | |
405 | ||
406 | VALUE rb_thrift_compact_proto_read_message_end(VALUE self) { | |
407 | return Qnil; | |
408 | } | |
409 | ||
410 | VALUE rb_thrift_compact_proto_read_struct_begin(VALUE self) { | |
411 | rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0)); | |
412 | return Qnil; | |
413 | } | |
414 | ||
415 | VALUE rb_thrift_compact_proto_read_struct_end(VALUE self) { | |
416 | rb_ary_pop(rb_ivar_get(self, last_field_id)); | |
417 | return Qnil; | |
418 | } | |
419 | ||
420 | VALUE rb_thrift_compact_proto_read_field_end(VALUE self) { | |
421 | return Qnil; | |
422 | } | |
423 | ||
424 | VALUE rb_thrift_compact_proto_read_map_end(VALUE self) { | |
425 | return Qnil; | |
426 | } | |
427 | ||
428 | VALUE rb_thrift_compact_proto_read_list_end(VALUE self) { | |
429 | return Qnil; | |
430 | } | |
431 | ||
432 | VALUE rb_thrift_compact_proto_read_set_end(VALUE self) { | |
433 | return Qnil; | |
434 | } | |
435 | ||
436 | VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) { | |
437 | int8_t protocol_id = read_byte_direct(self); | |
438 | if (protocol_id != PROTOCOL_ID) { | |
439 | char buf[100]; | |
440 | int len = sprintf(buf, "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id); | |
441 | buf[len] = 0; | |
442 | rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf))); | |
443 | } | |
444 | ||
445 | int8_t version_and_type = read_byte_direct(self); | |
446 | int8_t version = version_and_type & VERSION_MASK; | |
447 | if (version != VERSION) { | |
448 | char buf[100]; | |
449 | int len = sprintf(buf, "Expected version id %d but got %d", version, VERSION); | |
450 | buf[len] = 0; | |
451 | rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf))); | |
452 | } | |
453 | ||
454 | int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS; | |
455 | int32_t seqid = read_varint64(self); | |
456 | VALUE messageName = rb_thrift_compact_proto_read_string(self); | |
457 | return rb_ary_new3(3, messageName, INT2FIX(type), INT2NUM(seqid)); | |
458 | } | |
459 | ||
460 | VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) { | |
461 | int8_t type = read_byte_direct(self); | |
462 | // if it's a stop, then we can return immediately, as the struct is over. | |
463 | if ((type & 0x0f) == TTYPE_STOP) { | |
464 | return rb_ary_new3(3, Qnil, INT2FIX(0), INT2FIX(0)); | |
465 | } else { | |
466 | int field_id = 0; | |
467 | ||
468 | // mask off the 4 MSB of the type header. it could contain a field id delta. | |
469 | uint8_t modifier = ((type & 0xf0) >> 4); | |
470 | ||
471 | if (modifier == 0) { | |
472 | // not a delta. look ahead for the zigzag varint field id. | |
473 | (void) LAST_ID(self); | |
474 | field_id = read_i16(self); | |
475 | } else { | |
476 | // has a delta. add the delta to the last read field id. | |
477 | field_id = LAST_ID(self) + modifier; | |
478 | } | |
479 | ||
480 | // if this happens to be a boolean field, the value is encoded in the type | |
481 | if (is_bool_type(type)) { | |
482 | // save the boolean value in a special instance variable. | |
483 | rb_ivar_set(self, bool_value_id, (type & 0x0f) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse); | |
484 | } | |
485 | ||
486 | // push the new field onto the field stack so we can keep the deltas going. | |
487 | SET_LAST_ID(self, INT2FIX(field_id)); | |
488 | return rb_ary_new3(3, Qnil, INT2FIX(get_ttype(type & 0x0f)), INT2FIX(field_id)); | |
489 | } | |
490 | } | |
491 | ||
492 | VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) { | |
493 | int32_t size = read_varint64(self); | |
494 | 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)); | |
496 | } | |
497 | ||
498 | VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) { | |
499 | uint8_t size_and_type = read_byte_direct(self); | |
500 | int32_t size = (size_and_type >> 4) & 0x0f; | |
501 | if (size == 15) { | |
502 | size = read_varint64(self); | |
503 | } | |
504 | uint8_t type = get_ttype(size_and_type & 0x0f); | |
505 | return rb_ary_new3(2, INT2FIX(type), INT2FIX(size)); | |
506 | } | |
507 | ||
508 | VALUE rb_thrift_compact_proto_read_set_begin(VALUE self) { | |
509 | return rb_thrift_compact_proto_read_list_begin(self); | |
510 | } | |
511 | ||
512 | VALUE rb_thrift_compact_proto_read_bool(VALUE self) { | |
513 | VALUE bool_value = rb_ivar_get(self, bool_value_id); | |
514 | if (NIL_P(bool_value)) { | |
515 | return read_byte_direct(self) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse; | |
516 | } else { | |
517 | rb_ivar_set(self, bool_value_id, Qnil); | |
518 | return bool_value; | |
519 | } | |
520 | } | |
521 | ||
522 | VALUE rb_thrift_compact_proto_read_byte(VALUE self) { | |
523 | return INT2FIX(read_byte_direct(self)); | |
524 | } | |
525 | ||
526 | VALUE rb_thrift_compact_proto_read_i16(VALUE self) { | |
527 | return INT2FIX(read_i16(self)); | |
528 | } | |
529 | ||
530 | VALUE rb_thrift_compact_proto_read_i32(VALUE self) { | |
531 | return INT2NUM(zig_zag_to_int(read_varint64(self))); | |
532 | } | |
533 | ||
534 | VALUE rb_thrift_compact_proto_read_i64(VALUE self) { | |
535 | return LL2NUM(zig_zag_to_ll(read_varint64(self))); | |
536 | } | |
537 | ||
538 | VALUE rb_thrift_compact_proto_read_double(VALUE self) { | |
539 | union { | |
540 | double f; | |
541 | int64_t l; | |
542 | } transfer; | |
543 | VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id); | |
544 | rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8)); | |
545 | uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[0])) | |
546 | | (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 8) | |
547 | | (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 16) | |
548 | | (((uint8_t)(RSTRING_PTR(rbuf)[3])) << 24); | |
549 | uint64_t hi = (((uint8_t)(RSTRING_PTR(rbuf)[4]))) | |
550 | | (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 8) | |
551 | | (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 16) | |
552 | | (((uint8_t)(RSTRING_PTR(rbuf)[7])) << 24); | |
553 | transfer.l = (hi << 32) | lo; | |
554 | ||
555 | return rb_float_new(transfer.f); | |
556 | } | |
557 | ||
558 | VALUE rb_thrift_compact_proto_read_string(VALUE self) { | |
559 | VALUE buffer = rb_thrift_compact_proto_read_binary(self); | |
560 | return convert_to_string(buffer); | |
561 | } | |
562 | ||
563 | VALUE rb_thrift_compact_proto_read_binary(VALUE self) { | |
564 | int64_t size = read_varint64(self); | |
565 | return READ(self, size); | |
566 | } | |
567 | ||
568 | static void Init_constants() { | |
569 | thrift_compact_protocol_class = rb_const_get(thrift_module, rb_intern("CompactProtocol")); | |
570 | ||
571 | VERSION = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION"))); | |
572 | VERSION_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION_MASK"))); | |
573 | TYPE_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_MASK"))); | |
574 | TYPE_BITS = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_BITS"))); | |
575 | TYPE_SHIFT_AMOUNT = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_SHIFT_AMOUNT"))); | |
576 | PROTOCOL_ID = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("PROTOCOL_ID"))); | |
577 | ||
578 | last_field_id = rb_intern("@last_field"); | |
579 | boolean_field_id = rb_intern("@boolean_field"); | |
580 | bool_value_id = rb_intern("@bool_value"); | |
581 | rbuf_ivar_id = rb_intern("@rbuf"); | |
582 | } | |
583 | ||
584 | static void Init_rb_methods() { | |
585 | rb_define_method(thrift_compact_protocol_class, "native?", rb_thrift_compact_proto_native_qmark, 0); | |
586 | ||
587 | rb_define_method(thrift_compact_protocol_class, "write_message_begin", rb_thrift_compact_proto_write_message_begin, 3); | |
588 | rb_define_method(thrift_compact_protocol_class, "write_field_begin", rb_thrift_compact_proto_write_field_begin, 3); | |
589 | rb_define_method(thrift_compact_protocol_class, "write_field_stop", rb_thrift_compact_proto_write_field_stop, 0); | |
590 | rb_define_method(thrift_compact_protocol_class, "write_map_begin", rb_thrift_compact_proto_write_map_begin, 3); | |
591 | rb_define_method(thrift_compact_protocol_class, "write_list_begin", rb_thrift_compact_proto_write_list_begin, 2); | |
592 | rb_define_method(thrift_compact_protocol_class, "write_set_begin", rb_thrift_compact_proto_write_set_begin, 2); | |
593 | rb_define_method(thrift_compact_protocol_class, "write_byte", rb_thrift_compact_proto_write_byte, 1); | |
594 | rb_define_method(thrift_compact_protocol_class, "write_bool", rb_thrift_compact_proto_write_bool, 1); | |
595 | rb_define_method(thrift_compact_protocol_class, "write_i16", rb_thrift_compact_proto_write_i16, 1); | |
596 | rb_define_method(thrift_compact_protocol_class, "write_i32", rb_thrift_compact_proto_write_i32, 1); | |
597 | rb_define_method(thrift_compact_protocol_class, "write_i64", rb_thrift_compact_proto_write_i64, 1); | |
598 | rb_define_method(thrift_compact_protocol_class, "write_double", rb_thrift_compact_proto_write_double, 1); | |
599 | rb_define_method(thrift_compact_protocol_class, "write_string", rb_thrift_compact_proto_write_string, 1); | |
600 | rb_define_method(thrift_compact_protocol_class, "write_binary", rb_thrift_compact_proto_write_binary, 1); | |
601 | ||
602 | rb_define_method(thrift_compact_protocol_class, "write_message_end", rb_thrift_compact_proto_write_message_end, 0); | |
603 | rb_define_method(thrift_compact_protocol_class, "write_struct_begin", rb_thrift_compact_proto_write_struct_begin, 1); | |
604 | rb_define_method(thrift_compact_protocol_class, "write_struct_end", rb_thrift_compact_proto_write_struct_end, 0); | |
605 | rb_define_method(thrift_compact_protocol_class, "write_field_end", rb_thrift_compact_proto_write_field_end, 0); | |
606 | rb_define_method(thrift_compact_protocol_class, "write_map_end", rb_thrift_compact_proto_write_map_end, 0); | |
607 | rb_define_method(thrift_compact_protocol_class, "write_list_end", rb_thrift_compact_proto_write_list_end, 0); | |
608 | rb_define_method(thrift_compact_protocol_class, "write_set_end", rb_thrift_compact_proto_write_set_end, 0); | |
609 | ||
610 | ||
611 | rb_define_method(thrift_compact_protocol_class, "read_message_begin", rb_thrift_compact_proto_read_message_begin, 0); | |
612 | rb_define_method(thrift_compact_protocol_class, "read_field_begin", rb_thrift_compact_proto_read_field_begin, 0); | |
613 | rb_define_method(thrift_compact_protocol_class, "read_map_begin", rb_thrift_compact_proto_read_map_begin, 0); | |
614 | rb_define_method(thrift_compact_protocol_class, "read_list_begin", rb_thrift_compact_proto_read_list_begin, 0); | |
615 | rb_define_method(thrift_compact_protocol_class, "read_set_begin", rb_thrift_compact_proto_read_set_begin, 0); | |
616 | rb_define_method(thrift_compact_protocol_class, "read_byte", rb_thrift_compact_proto_read_byte, 0); | |
617 | rb_define_method(thrift_compact_protocol_class, "read_bool", rb_thrift_compact_proto_read_bool, 0); | |
618 | rb_define_method(thrift_compact_protocol_class, "read_i16", rb_thrift_compact_proto_read_i16, 0); | |
619 | rb_define_method(thrift_compact_protocol_class, "read_i32", rb_thrift_compact_proto_read_i32, 0); | |
620 | rb_define_method(thrift_compact_protocol_class, "read_i64", rb_thrift_compact_proto_read_i64, 0); | |
621 | rb_define_method(thrift_compact_protocol_class, "read_double", rb_thrift_compact_proto_read_double, 0); | |
622 | rb_define_method(thrift_compact_protocol_class, "read_string", rb_thrift_compact_proto_read_string, 0); | |
623 | rb_define_method(thrift_compact_protocol_class, "read_binary", rb_thrift_compact_proto_read_binary, 0); | |
624 | ||
625 | rb_define_method(thrift_compact_protocol_class, "read_message_end", rb_thrift_compact_proto_read_message_end, 0); | |
626 | rb_define_method(thrift_compact_protocol_class, "read_struct_begin", rb_thrift_compact_proto_read_struct_begin, 0); | |
627 | rb_define_method(thrift_compact_protocol_class, "read_struct_end", rb_thrift_compact_proto_read_struct_end, 0); | |
628 | rb_define_method(thrift_compact_protocol_class, "read_field_end", rb_thrift_compact_proto_read_field_end, 0); | |
629 | rb_define_method(thrift_compact_protocol_class, "read_map_end", rb_thrift_compact_proto_read_map_end, 0); | |
630 | rb_define_method(thrift_compact_protocol_class, "read_list_end", rb_thrift_compact_proto_read_list_end, 0); | |
631 | rb_define_method(thrift_compact_protocol_class, "read_set_end", rb_thrift_compact_proto_read_set_end, 0); | |
632 | } | |
633 | ||
634 | void Init_compact_protocol() { | |
635 | Init_constants(); | |
636 | Init_rb_methods(); | |
637 | } |