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