File Coverage

src/binary_protocol.h
Criterion Covered Total %
statement 31 39 79.4
branch 17 54 31.4
condition n/a
subroutine n/a
pod n/a
total 48 93 51.6


line stmt bran cond sub pod time code
1             #ifndef _BINARY_PROTOCOL_H
2             #define _BINARY_PROTOCOL_H
3              
4             #include <inttypes.h>
5             #include "simple_queue.h"
6              
7             struct field_entry {
8             int field_id;
9             SIMPLEQ_ENTRY(field_entry) entries;
10             };
11             SIMPLEQ_HEAD(fieldq, field_entry);
12              
13             typedef struct {
14             SV *transport; // Transport instance
15             TMemoryBuffer *mbuf; // XS MemoryBuffer instance, if we're using it
16              
17             // Data for compact protocol state
18             int bool_type;
19             int bool_id;
20             int bool_value_id;
21             int last_field_id;
22             struct fieldq *last_fields; // a stack, using INSERT_HEAD/REMOVE_HEAD
23             } TBinaryProtocol;
24              
25             // normal types
26             enum TType {
27             T_STOP = 0,
28             T_VOID = 1,
29             T_BOOL = 2,
30             T_BYTE = 3,
31             T_I08 = 3,
32             T_I16 = 6,
33             T_I32 = 8,
34             T_U64 = 9,
35             T_I64 = 10,
36             T_DOUBLE = 4,
37             T_STRING = 11,
38             T_UTF7 = 11,
39             T_STRUCT = 12,
40             T_MAP = 13,
41             T_SET = 14,
42             T_LIST = 15,
43             T_UTF8 = 16,
44             T_UTF16 = 17
45             };
46              
47             const int32_t VERSION_MASK = 0xffff0000;
48             const int32_t VERSION_1 = 0x80010000;
49             const int8_t T_CALL = 1;
50             const int8_t T_REPLY = 2;
51             const int8_t T_EXCEPTION = 3;
52             // tprotocolexception
53             const int INVALID_DATA = 1;
54             const int BAD_VERSION = 4;
55              
56             // compact types
57             enum CType {
58             CTYPE_BOOLEAN_TRUE = 0x01,
59             CTYPE_BOOLEAN_FALSE = 0x02,
60             CTYPE_BYTE = 0x03,
61             CTYPE_I16 = 0x04,
62             CTYPE_I32 = 0x05,
63             CTYPE_I64 = 0x06,
64             CTYPE_DOUBLE = 0x07,
65             CTYPE_BINARY = 0x08,
66             CTYPE_LIST = 0x09,
67             CTYPE_SET = 0x0A,
68             CTYPE_MAP = 0x0B,
69             CTYPE_STRUCT = 0x0C
70             };
71              
72             static const int8_t PROTOCOL_ID = (int8_t)0x82;
73             static const int8_t VERSION_N = 1;
74             static const int8_t VERSION_MASK_COMPACT = 0x1f; // 0001 1111
75             static const int8_t TYPE_MASK = (int8_t)0xE0; // 1110 0000
76             static const int32_t TYPE_SHIFT_AMOUNT = 5;
77              
78             #define is_bool_type(ctype) (((ctype) & 0x0F) == CTYPE_BOOLEAN_TRUE || ((ctype) & 0x0F) == CTYPE_BOOLEAN_FALSE)
79              
80             #define INT_TO_I32(dst, src, off) \
81             dst[3+off] = src & 0xff; \
82             dst[2+off] = (src >> 8) & 0xff; \
83             dst[1+off] = (src >> 16) & 0xff; \
84             dst[0+off] = (src >> 24) & 0xff
85              
86             #define INT_TO_I16(dst, src, off) \
87             dst[1+off] = src & 0xff; \
88             dst[0+off] = (src >> 8) & 0xff
89              
90             #define I32_TO_INT(dst, src, off) \
91             dst = ((uint8_t)src[3+off] | \
92             (((uint8_t)src[2+off]) << 8) | \
93             (((uint8_t)src[1+off]) << 16) | \
94             (((uint8_t)src[0+off]) << 24))
95              
96             #define I16_TO_INT(dst, src, off) \
97             dst = ((uint8_t)src[1+off] | \
98             (((uint8_t)src[0+off]) << 8))
99              
100             #define WRITE(p, str, len) \
101             if (likely(p->mbuf)) { \
102             buffer_append(p->mbuf->buffer, (void *)str, len); \
103             } \
104             else { \
105             dSP; ENTER; SAVETMPS; \
106             PUSHMARK(SP); \
107             XPUSHs(p->transport); \
108             XPUSHs(sv_2mortal(newSVpvn(str, len))); \
109             PUTBACK; \
110             call_method("write", G_DISCARD); \
111             FREETMPS; LEAVE; \
112             }
113              
114             // This macro uses an optimized direct buffer write if possible
115             #define WRITE_SV(p, sv) \
116             if (likely(p->mbuf)) { \
117             buffer_append(p->mbuf->buffer, (void *)SvPVX(sv), sv_len(sv)); \
118             } \
119             else { \
120             dSP; ENTER; SAVETMPS; \
121             PUSHMARK(SP); \
122             XPUSHs(p->transport); \
123             XPUSHs(sv); \
124             PUTBACK; \
125             call_method("write", G_DISCARD); \
126             FREETMPS; LEAVE; \
127             }
128              
129             // This macro uses an optimized direct buffer read if the XS MemoryBuffer
130             // code is in use. If another transport is being used, it calls through to
131             // the readAll method (slow)
132             #define READ_SV(p, dst, len) \
133             if (likely(p->mbuf)) { \
134             uint32_t avail = buffer_len(p->mbuf->buffer); \
135             if (avail < len) { \
136             THROW_SV("TTransportException", \
137             newSVpvf("Attempt to readAll(%" PRIu64 ") found only %d available", (uint64_t)len, avail)); \
138             } \
139             dst = newSVpvn( buffer_ptr(p->mbuf->buffer), len ); \
140             buffer_consume(p->mbuf->buffer, len); \
141             } \
142             else { \
143             dSP; ENTER; SAVETMPS; \
144             PUSHMARK(SP); \
145             XPUSHs(p->transport); \
146             XPUSHs(sv_2mortal(newSViv(len))); \
147             PUTBACK; \
148             call_method("readAll", G_SCALAR); \
149             SPAGAIN; \
150             dst = newSVsv(POPs); \
151             PUTBACK; \
152             FREETMPS; LEAVE; \
153             } \
154             sv_2mortal(dst)
155              
156             // These work for both 32-bit and 64-bit
157             #define UINT_TO_VARINT(len, dst, src, off) \
158             { \
159             len = 0; \
160             for (;;) { \
161             if ((src & ~0x7f) == 0) { \
162             dst[off + len++] = src; \
163             break; \
164             } \
165             else { \
166             dst[off + len++] = (src & 0x7f) | 0x80; \
167             src >>= 7; \
168             } \
169             } \
170             }
171              
172             // dst can be a uint32_t or uint64_t
173             #define READ_VARINT(p, dst) \
174             { \
175             dst = 0; \
176             int shift = 0; \
177             SV *b; \
178             char *bs; \
179             for (;;) { \
180             if (shift == 70) { \
181             dst = 0; \
182             break; \
183             } \
184             READ_SV(p, b, 1); \
185             bs = SvPVX(b); \
186             dst |= (uint64_t)(bs[0] & 0x7f) << shift; \
187             shift += 7; \
188             if (!(bs[0] & 0x80)) break; \
189             } \
190             }
191              
192 22           static int get_compact_type(int type) {
193 22           switch (type) {
194             case T_BOOL: return CTYPE_BOOLEAN_TRUE;
195 0           case T_BYTE: return CTYPE_BYTE;
196 1           case T_I16: return CTYPE_I16;
197 2           case T_I32: return CTYPE_I32;
198 0           case T_I64: return CTYPE_I64;
199 0           case T_DOUBLE: return CTYPE_DOUBLE;
200 12           case T_STRING: return CTYPE_BINARY;
201 3           case T_LIST: return CTYPE_LIST;
202 0           case T_SET: return CTYPE_SET;
203 2           case T_MAP: return CTYPE_MAP;
204 2           case T_STRUCT: return CTYPE_STRUCT;
205 0           default:
206             {
207 0           THROW_SV("Thrift::TException", newSVpvf("Cannot convert type %d to compact protocol", type));
208             }
209             }
210             }
211              
212 20 50         static int get_ttype(int ctype) {
213             switch (ctype) {
214             case T_STOP: return T_STOP;
215             case CTYPE_BOOLEAN_TRUE:
216             case CTYPE_BOOLEAN_FALSE: return T_BOOL;
217             case CTYPE_BYTE: return T_BYTE;
218             case CTYPE_I16: return T_I16;
219             case CTYPE_I32: return T_I32;
220             case CTYPE_I64: return T_I64;
221             case CTYPE_DOUBLE: return T_DOUBLE;
222             case CTYPE_BINARY: return T_STRING;
223             case CTYPE_LIST: return T_LIST;
224             case CTYPE_SET: return T_SET;
225             case CTYPE_MAP: return T_MAP;
226             case CTYPE_STRUCT: return T_STRUCT;
227 0           default:
228             {
229 0           THROW_SV("Thrift::TException", newSVpvf("Cannot convert type %d from compact protocol", ctype));
230             }
231             }
232             }
233              
234             static inline uint32_t
235             int_to_zigzag(int n)
236             {
237 7           return (uint32_t)((n << 1) ^ (n >> 31));
238             }
239              
240             static inline int32_t
241             zigzag_to_int(uint32_t n) {
242 5           return (n >> 1) ^ -(n & 1);
243             }
244              
245             static inline uint64_t
246             ll_to_zigzag(int64_t n)
247             {
248 5           return (uint64_t)((n << 1) ^ (n >> 63));
249             }
250              
251             static inline int64_t
252             zigzag_to_ll(uint64_t n)
253             {
254 1           return (int64_t)((n >> 1) ^ -(n & 1));
255             }
256              
257             static void
258 10           write_field_begin_internal(TBinaryProtocol *p, int type, int id, int type_override)
259             {
260             char data[4];
261 10 100         int type_to_write = type_override == -1 ? get_compact_type(type) : type_override;
262              
263             // check if we can use delta encoding for the field id
264 10 100         if (id > p->last_field_id && id - p->last_field_id <= 15) {
    100          
265             // write them together
266 8           data[0] = ((id - p->last_field_id) << 4) | type_to_write;
267 8 50         WRITE(p, data, 1);
    0          
    0          
    0          
    0          
268             }
269             else {
270             // write them separate
271             int varlen;
272 2           data[0] = type_to_write;
273             uint32_t uid = int_to_zigzag(id);
274              
275 3 100         UINT_TO_VARINT(varlen, data, uid, 1);
276 2 50         WRITE(p, data, varlen + 1);
    0          
    0          
    0          
    0          
277             }
278              
279 10           p->last_field_id = id;
280 10           }
281              
282             static void
283 7           write_collection_begin_internal(TBinaryProtocol *p, int elemtype, uint32_t size)
284             {
285             char data[6];
286              
287 7 100         if (size <= 14) {
288 4           data[0] = (size << 4) | get_compact_type(elemtype);
289 4 50         WRITE(p, data, 1);
    0          
    0          
    0          
    0          
290             }
291             else {
292             int varlen;
293 3           data[0] = 0xf0 | get_compact_type(elemtype);
294 6 100         UINT_TO_VARINT(varlen, data, size, 1);
295 3 50         WRITE(p, data, varlen + 1);
    0          
    0          
    0          
    0          
296             }
297 7           }
298              
299             #endif