blob: 5bba23761244863b4e9c16316774be9a2e76e9b3 [file] [log] [blame]
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +09001/*
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#ifndef THRIFT_PY_COMPACT_H
21#define THRIFT_PY_COMPACT_H
22
23#include <Python.h>
24#include "ext/protocol.h"
25#include "ext/endian.h"
26#include <stdint.h>
27#include <stack>
28
29namespace apache {
30namespace thrift {
31namespace py {
32
33class CompactProtocol : public ProtocolBase<CompactProtocol> {
34public:
35 CompactProtocol() { readBool_.exists = false; }
36
37 virtual ~CompactProtocol() {}
38
39 void writeI8(int8_t val) { writeBuffer(reinterpret_cast<char*>(&val), 1); }
40
41 void writeI16(int16_t val) { writeVarint(toZigZag(val)); }
42
43 int writeI32(int32_t val) { return writeVarint(toZigZag(val)); }
44
45 void writeI64(int64_t val) { writeVarint64(toZigZag64(val)); }
46
47 void writeDouble(double dub) {
48 union {
49 double f;
50 int64_t t;
51 } transfer;
52 transfer.f = htolell(dub);
53 writeBuffer(reinterpret_cast<char*>(&transfer.t), sizeof(int64_t));
54 }
55
56 void writeBool(int v) { writeByte(static_cast<uint8_t>(v ? CT_BOOLEAN_TRUE : CT_BOOLEAN_FALSE)); }
57
58 void writeString(PyObject* value, int32_t len) {
59 writeVarint(len);
60 writeBuffer(PyBytes_AS_STRING(value), len);
61 }
62
63 bool writeListBegin(PyObject* value, const SetListTypeArgs& args, int32_t len) {
64 int ctype = toCompactType(args.element_type);
65 if (len <= 14) {
66 writeByte(static_cast<uint8_t>(len << 4 | ctype));
67 } else {
68 writeByte(0xf0 | ctype);
69 writeVarint(len);
70 }
71 return true;
72 }
73
74 bool writeMapBegin(PyObject* value, const MapTypeArgs& args, int32_t len) {
75 if (len == 0) {
76 writeByte(0);
77 return true;
78 }
79 int ctype = toCompactType(args.ktag) << 4 | toCompactType(args.vtag);
80 writeVarint(len);
81 writeByte(ctype);
82 return true;
83 }
84
85 bool writeStructBegin() {
86 writeTags_.push(0);
87 return true;
88 }
89 bool writeStructEnd() {
90 writeTags_.pop();
91 return true;
92 }
93
94 bool writeField(PyObject* value, const StructItemSpec& spec) {
95 if (spec.type == T_BOOL) {
96 doWriteFieldBegin(spec, PyObject_IsTrue(value) ? CT_BOOLEAN_TRUE : CT_BOOLEAN_FALSE);
97 return true;
98 } else {
99 doWriteFieldBegin(spec, toCompactType(spec.type));
100 return encodeValue(value, spec.type, spec.typeargs);
101 }
102 }
103
104 void writeFieldStop() { writeByte(0); }
105
106 bool readBool(bool& val) {
107 if (readBool_.exists) {
108 readBool_.exists = false;
109 val = readBool_.value;
110 return true;
111 }
112 char* buf;
113 if (!readBytes(&buf, 1)) {
114 return false;
115 }
116 val = buf[0] == CT_BOOLEAN_TRUE;
117 return true;
118 }
119 bool readI8(int8_t& val) {
120 char* buf;
121 if (!readBytes(&buf, 1)) {
122 return false;
123 }
124 val = buf[0];
125 return true;
126 }
127
128 bool readI16(int16_t& val) {
129 uint16_t uval;
130 if (readVarint<uint16_t, 3>(uval)) {
131 val = fromZigZag<int16_t, uint16_t>(uval);
132 return true;
133 }
134 return false;
135 }
136
137 bool readI32(int32_t& val) {
138 uint32_t uval;
139 if (readVarint<uint32_t, 5>(uval)) {
140 val = fromZigZag<int32_t, uint32_t>(uval);
141 return true;
142 }
143 return false;
144 }
145
146 bool readI64(int64_t& val) {
147 uint64_t uval;
148 if (readVarint<uint64_t, 10>(uval)) {
149 val = fromZigZag<int64_t, uint64_t>(uval);
150 return true;
151 }
152 return false;
153 }
154
155 bool readDouble(double& val) {
156 union {
157 int64_t f;
158 double t;
159 } transfer;
160
161 char* buf;
162 if (!readBytes(&buf, 8)) {
163 return false;
164 }
165 transfer.f = letohll(*reinterpret_cast<int64_t*>(buf));
166 val = transfer.t;
167 return true;
168 }
169
170 int32_t readString(char** buf) {
171 uint32_t len;
172 if (!readVarint<uint32_t, 5>(len) || !checkLengthLimit(len, stringLimit())) {
173 return -1;
174 }
175 if (len == 0) {
176 return 0;
177 }
178 if (!readBytes(buf, len)) {
179 return -1;
180 }
181 return len;
182 }
183
184 int32_t readListBegin(TType& etype) {
185 uint8_t b;
186 if (!readByte(b)) {
187 return -1;
188 }
189 etype = getTType(b & 0xf);
190 if (etype == -1) {
191 return -1;
192 }
193 uint32_t len = (b >> 4) & 0xf;
194 if (len == 15 && !readVarint<uint32_t, 5>(len)) {
195 return -1;
196 }
197 if (!checkLengthLimit(len, containerLimit())) {
198 return -1;
199 }
200 return len;
201 }
202
203 int32_t readMapBegin(TType& ktype, TType& vtype) {
204 uint32_t len;
205 if (!readVarint<uint32_t, 5>(len) || !checkLengthLimit(len, containerLimit())) {
206 return -1;
207 }
208 if (len != 0) {
209 uint8_t kvType;
210 if (!readByte(kvType)) {
211 return -1;
212 }
213 ktype = getTType(kvType >> 4);
214 vtype = getTType(kvType & 0xf);
215 if (ktype == -1 || vtype == -1) {
216 return -1;
217 }
218 }
219 return len;
220 }
221
222 bool readStructBegin() {
223 readTags_.push(0);
224 return true;
225 }
226 bool readStructEnd() {
227 readTags_.pop();
228 return true;
229 }
230 bool readFieldBegin(TType& type, int16_t& tag);
231
232 bool skipBool() {
233 bool val;
234 return readBool(val);
235 }
236#define SKIPBYTES(n) \
237 do { \
238 if (!readBytes(&dummy_buf_, (n))) { \
239 return false; \
240 } \
241 return true; \
242 } while (0)
243 bool skipByte() { SKIPBYTES(1); }
244 bool skipDouble() { SKIPBYTES(8); }
245 bool skipI16() {
246 int16_t val;
247 return readI16(val);
248 }
249 bool skipI32() {
250 int32_t val;
251 return readI32(val);
252 }
253 bool skipI64() {
254 int64_t val;
255 return readI64(val);
256 }
257 bool skipString() {
258 uint32_t len;
259 if (!readVarint<uint32_t, 5>(len)) {
260 return false;
261 }
262 SKIPBYTES(len);
263 }
264#undef SKIPBYTES
265
266private:
267 enum Types {
268 CT_STOP = 0x00,
269 CT_BOOLEAN_TRUE = 0x01,
270 CT_BOOLEAN_FALSE = 0x02,
271 CT_BYTE = 0x03,
272 CT_I16 = 0x04,
273 CT_I32 = 0x05,
274 CT_I64 = 0x06,
275 CT_DOUBLE = 0x07,
276 CT_BINARY = 0x08,
277 CT_LIST = 0x09,
278 CT_SET = 0x0A,
279 CT_MAP = 0x0B,
280 CT_STRUCT = 0x0C
281 };
282
283 static const uint8_t TTypeToCType[];
284
285 TType getTType(uint8_t type);
286
287 int toCompactType(TType type) {
288 int i = static_cast<int>(type);
289 return i < 16 ? TTypeToCType[i] : -1;
290 }
291
292 uint32_t toZigZag(int32_t val) { return (val >> 31) ^ (val << 1); }
293
294 uint64_t toZigZag64(int64_t val) { return (val >> 63) ^ (val << 1); }
295
296 int writeVarint(uint32_t val) {
297 int cnt = 1;
298 while (val & ~0x7fU) {
299 writeByte(static_cast<char>((val & 0x7fU) | 0x80U));
300 val >>= 7;
301 ++cnt;
302 }
303 writeByte(static_cast<char>(val));
304 return cnt;
305 }
306
307 int writeVarint64(uint64_t val) {
308 int cnt = 1;
309 while (val & ~0x7fULL) {
310 writeByte(static_cast<char>((val & 0x7fULL) | 0x80ULL));
311 val >>= 7;
312 ++cnt;
313 }
314 writeByte(static_cast<char>(val));
315 return cnt;
316 }
317
318 template <typename T, int Max>
319 bool readVarint(T& result) {
320 uint8_t b;
321 T val = 0;
322 int shift = 0;
323 for (int i = 0; i < Max; ++i) {
324 if (!readByte(b)) {
325 return false;
326 }
327 if (b & 0x80) {
328 val |= static_cast<T>(b & 0x7f) << shift;
329 } else {
330 val |= static_cast<T>(b) << shift;
331 result = val;
332 return true;
333 }
334 shift += 7;
335 }
336 PyErr_Format(PyExc_OverflowError, "varint exceeded %d bytes", Max);
337 return false;
338 }
339
340 template <typename S, typename U>
341 S fromZigZag(U val) {
342 return (val >> 1) ^ static_cast<U>(-static_cast<S>(val & 1));
343 }
344
345 void doWriteFieldBegin(const StructItemSpec& spec, int ctype) {
346 int diff = spec.tag - writeTags_.top();
347 if (diff > 0 && diff <= 15) {
348 writeByte(static_cast<uint8_t>(diff << 4 | ctype));
349 } else {
350 writeByte(static_cast<uint8_t>(ctype));
351 writeI16(spec.tag);
352 }
353 writeTags_.top() = spec.tag;
354 }
355
356 std::stack<int> writeTags_;
357 std::stack<int> readTags_;
358 struct {
359 bool exists;
360 bool value;
361 } readBool_;
362 char* dummy_buf_;
363};
364}
365}
366}
367#endif // THRIFT_PY_COMPACT_H