blob: 2f0d083978aba3f51b331e544c97f423033a4106 [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_PROTOCOL_TCC
21#define THRIFT_PY_PROTOCOL_TCC
22
Nobuaki Sukegawaf7a8d942016-03-01 01:41:47 +090023#include <iterator>
24
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +090025#define CHECK_RANGE(v, min, max) (((v) <= (max)) && ((v) >= (min)))
26#define INIT_OUTBUF_SIZE 128
27
Nobuaki Sukegawa7af189a2016-02-11 16:21:01 +090028#if PY_MAJOR_VERSION < 3
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +090029#include <cStringIO.h>
Nobuaki Sukegawa7af189a2016-02-11 16:21:01 +090030#else
31#include <algorithm>
32#endif
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +090033
34namespace apache {
35namespace thrift {
36namespace py {
37
Nobuaki Sukegawa7af189a2016-02-11 16:21:01 +090038#if PY_MAJOR_VERSION < 3
39
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +090040namespace detail {
41
42inline bool input_check(PyObject* input) {
43 return PycStringIO_InputCheck(input);
44}
45
46inline EncodeBuffer* new_encode_buffer(size_t size) {
47 if (!PycStringIO) {
48 PycString_IMPORT;
49 }
50 if (!PycStringIO) {
51 return NULL;
52 }
53 return PycStringIO->NewOutput(size);
54}
55
56inline int read_buffer(PyObject* buf, char** output, int len) {
57 if (!PycStringIO) {
58 PycString_IMPORT;
59 }
60 if (!PycStringIO) {
61 PyErr_SetString(PyExc_ImportError, "failed to import native cStringIO");
62 return -1;
63 }
64 return PycStringIO->cread(buf, output, len);
65}
66}
67
68template <typename Impl>
69inline ProtocolBase<Impl>::~ProtocolBase() {
70 if (output_) {
71 Py_CLEAR(output_);
72 }
73}
74
75template <typename Impl>
76inline bool ProtocolBase<Impl>::isUtf8(PyObject* typeargs) {
77 return PyString_Check(typeargs) && !strncmp(PyString_AS_STRING(typeargs), "UTF8", 4);
78}
79
80template <typename Impl>
81PyObject* ProtocolBase<Impl>::getEncodedValue() {
82 if (!PycStringIO) {
83 PycString_IMPORT;
84 }
85 if (!PycStringIO) {
86 return NULL;
87 }
88 return PycStringIO->cgetvalue(output_);
89}
90
91template <typename Impl>
92inline bool ProtocolBase<Impl>::writeBuffer(char* data, size_t size) {
93 if (!PycStringIO) {
94 PycString_IMPORT;
95 }
96 if (!PycStringIO) {
97 PyErr_SetString(PyExc_ImportError, "failed to import native cStringIO");
98 return false;
99 }
100 int len = PycStringIO->cwrite(output_, data, size);
101 if (len < 0) {
102 PyErr_SetString(PyExc_IOError, "failed to write to cStringIO object");
103 return false;
104 }
105 if (len != size) {
Nobuaki Sukegawa7efa4fb2016-02-19 00:59:57 +0900106 PyErr_Format(PyExc_EOFError, "write length mismatch: expected %lu got %d", size, len);
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900107 return false;
108 }
109 return true;
110}
111
Nobuaki Sukegawa7af189a2016-02-11 16:21:01 +0900112#else
113
114namespace detail {
115
116inline bool input_check(PyObject* input) {
117 // TODO: Check for BytesIO type
118 return true;
119}
120
121inline EncodeBuffer* new_encode_buffer(size_t size) {
122 EncodeBuffer* buffer = new EncodeBuffer;
123 buffer->buf.reserve(size);
124 buffer->pos = 0;
125 return buffer;
126}
127
128struct bytesio {
129 PyObject_HEAD
130#if PY_MINOR_VERSION < 5
131 char* buf;
132#else
133 PyObject* buf;
134#endif
135 Py_ssize_t pos;
136 Py_ssize_t string_size;
137};
138
139inline int read_buffer(PyObject* buf, char** output, int len) {
140 bytesio* buf2 = reinterpret_cast<bytesio*>(buf);
141#if PY_MINOR_VERSION < 5
142 *output = buf2->buf + buf2->pos;
143#else
144 *output = PyBytes_AS_STRING(buf2->buf) + buf2->pos;
145#endif
146 Py_ssize_t pos0 = buf2->pos;
147 buf2->pos = std::min(buf2->pos + static_cast<Py_ssize_t>(len), buf2->string_size);
148 return static_cast<int>(buf2->pos - pos0);
149}
150}
151
152template <typename Impl>
153inline ProtocolBase<Impl>::~ProtocolBase() {
154 if (output_) {
155 delete output_;
156 }
157}
158
159template <typename Impl>
160inline bool ProtocolBase<Impl>::isUtf8(PyObject* typeargs) {
161 // while condition for py2 is "arg == 'UTF8'", it should be "arg != 'BINARY'" for py3.
162 // HACK: check the length and don't bother reading the value
163 return !PyUnicode_Check(typeargs) || PyUnicode_GET_LENGTH(typeargs) != 6;
164}
165
166template <typename Impl>
167PyObject* ProtocolBase<Impl>::getEncodedValue() {
168 return PyBytes_FromStringAndSize(output_->buf.data(), output_->buf.size());
169}
170
171template <typename Impl>
172inline bool ProtocolBase<Impl>::writeBuffer(char* data, size_t size) {
173 size_t need = size + output_->pos;
174 if (output_->buf.capacity() < need) {
175 try {
176 output_->buf.reserve(need);
177 } catch (std::bad_alloc& ex) {
178 PyErr_SetString(PyExc_MemoryError, "Failed to allocate write buffer");
179 return false;
180 }
181 }
182 std::copy(data, data + size, std::back_inserter(output_->buf));
183 return true;
184}
185
186#endif
187
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900188namespace detail {
189
190#define DECLARE_OP_SCOPE(name, op) \
191 template <typename Impl> \
192 struct name##Scope { \
193 Impl* impl; \
194 bool valid; \
195 name##Scope(Impl* thiz) : impl(thiz), valid(impl->op##Begin()) {} \
196 ~name##Scope() { \
197 if (valid) \
198 impl->op##End(); \
199 } \
200 operator bool() { return valid; } \
201 }; \
202 template <typename Impl, template <typename> class T> \
203 name##Scope<Impl> op##Scope(T<Impl>* thiz) { \
204 return name##Scope<Impl>(static_cast<Impl*>(thiz)); \
205 }
206DECLARE_OP_SCOPE(WriteStruct, writeStruct)
207DECLARE_OP_SCOPE(ReadStruct, readStruct)
208#undef DECLARE_OP_SCOPE
209
210inline bool check_ssize_t_32(Py_ssize_t len) {
211 // error from getting the int
212 if (INT_CONV_ERROR_OCCURRED(len)) {
213 return false;
214 }
Nobuaki Sukegawaf7a8d942016-03-01 01:41:47 +0900215 if (!CHECK_RANGE(len, 0, std::numeric_limits<int32_t>::max())) {
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900216 PyErr_SetString(PyExc_OverflowError, "size out of range: exceeded INT32_MAX");
217 return false;
218 }
219 return true;
220}
221}
222
223template <typename T>
224bool parse_pyint(PyObject* o, T* ret, int32_t min, int32_t max) {
225 long val = PyInt_AsLong(o);
226
227 if (INT_CONV_ERROR_OCCURRED(val)) {
228 return false;
229 }
230 if (!CHECK_RANGE(val, min, max)) {
231 PyErr_SetString(PyExc_OverflowError, "int out of range");
232 return false;
233 }
234
235 *ret = static_cast<T>(val);
236 return true;
237}
238
239template <typename Impl>
240inline bool ProtocolBase<Impl>::checkType(TType got, TType expected) {
241 if (expected != got) {
242 PyErr_SetString(PyExc_TypeError, "got wrong ttype while reading field");
243 return false;
244 }
245 return true;
246}
247
248template <typename Impl>
249bool ProtocolBase<Impl>::checkLengthLimit(int32_t len, long limit) {
250 if (len < 0) {
Nobuaki Sukegawa7efa4fb2016-02-19 00:59:57 +0900251 PyErr_Format(PyExc_OverflowError, "negative length: %ld", limit);
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900252 return false;
253 }
254 if (len > limit) {
Nobuaki Sukegawa7efa4fb2016-02-19 00:59:57 +0900255 PyErr_Format(PyExc_OverflowError, "size exceeded specified limit: %ld", limit);
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900256 return false;
257 }
258 return true;
259}
260
261template <typename Impl>
262bool ProtocolBase<Impl>::readBytes(char** output, int len) {
263 if (len < 0) {
264 PyErr_Format(PyExc_ValueError, "attempted to read negative length: %d", len);
265 return false;
266 }
267 // TODO(dreiss): Don't fear the malloc. Think about taking a copy of
268 // the partial read instead of forcing the transport
269 // to prepend it to its buffer.
270
271 int rlen = detail::read_buffer(input_.stringiobuf.get(), output, len);
272
273 if (rlen == len) {
274 return true;
275 } else if (rlen == -1) {
276 return false;
277 } else {
278 // using building functions as this is a rare codepath
Nobuaki Sukegawa7af189a2016-02-11 16:21:01 +0900279 ScopedPyObject newiobuf(PyObject_CallFunction(input_.refill_callable.get(), refill_signature,
280 *output, rlen, len, NULL));
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900281 if (!newiobuf) {
282 return false;
283 }
284
285 // must do this *AFTER* the call so that we don't deref the io buffer
286 input_.stringiobuf.reset(newiobuf.release());
287
288 rlen = detail::read_buffer(input_.stringiobuf.get(), output, len);
289
290 if (rlen == len) {
291 return true;
292 } else if (rlen == -1) {
293 return false;
294 } else {
295 // TODO(dreiss): This could be a valid code path for big binary blobs.
296 PyErr_SetString(PyExc_TypeError, "refill claimed to have refilled the buffer, but didn't!!");
297 return false;
298 }
299 }
300}
301
302template <typename Impl>
303bool ProtocolBase<Impl>::prepareDecodeBufferFromTransport(PyObject* trans) {
304 if (input_.stringiobuf) {
305 PyErr_SetString(PyExc_ValueError, "decode buffer is already initialized");
306 return false;
307 }
308
309 ScopedPyObject stringiobuf(PyObject_GetAttr(trans, INTERN_STRING(cstringio_buf)));
310 if (!stringiobuf) {
311 return false;
312 }
313 if (!detail::input_check(stringiobuf.get())) {
314 PyErr_SetString(PyExc_TypeError, "expecting stringio input_");
315 return false;
316 }
317
318 ScopedPyObject refill_callable(PyObject_GetAttr(trans, INTERN_STRING(cstringio_refill)));
319 if (!refill_callable) {
320 return false;
321 }
322 if (!PyCallable_Check(refill_callable.get())) {
323 PyErr_SetString(PyExc_TypeError, "expecting callable");
324 return false;
325 }
326
327 input_.stringiobuf.swap(stringiobuf);
328 input_.refill_callable.swap(refill_callable);
329 return true;
330}
331
332template <typename Impl>
333bool ProtocolBase<Impl>::prepareEncodeBuffer() {
334 output_ = detail::new_encode_buffer(INIT_OUTBUF_SIZE);
335 return output_ != NULL;
336}
337
338template <typename Impl>
339bool ProtocolBase<Impl>::encodeValue(PyObject* value, TType type, PyObject* typeargs) {
340 /*
341 * Refcounting Strategy:
342 *
343 * We assume that elements of the thrift_spec tuple are not going to be
344 * mutated, so we don't ref count those at all. Other than that, we try to
345 * keep a reference to all the user-created objects while we work with them.
346 * encodeValue assumes that a reference is already held. The *caller* is
347 * responsible for handling references
348 */
349
350 switch (type) {
351
352 case T_BOOL: {
353 int v = PyObject_IsTrue(value);
354 if (v == -1) {
355 return false;
356 }
357 impl()->writeBool(v);
358 return true;
359 }
360 case T_I08: {
361 int8_t val;
362
Nobuaki Sukegawaf7a8d942016-03-01 01:41:47 +0900363 if (!parse_pyint(value, &val, std::numeric_limits<int8_t>::min(),
364 std::numeric_limits<int8_t>::max())) {
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900365 return false;
366 }
367
368 impl()->writeI8(val);
369 return true;
370 }
371 case T_I16: {
372 int16_t val;
373
Nobuaki Sukegawaf7a8d942016-03-01 01:41:47 +0900374 if (!parse_pyint(value, &val, std::numeric_limits<int16_t>::min(),
375 std::numeric_limits<int16_t>::max())) {
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900376 return false;
377 }
378
379 impl()->writeI16(val);
380 return true;
381 }
382 case T_I32: {
383 int32_t val;
384
Nobuaki Sukegawaf7a8d942016-03-01 01:41:47 +0900385 if (!parse_pyint(value, &val, std::numeric_limits<int32_t>::min(),
386 std::numeric_limits<int32_t>::max())) {
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900387 return false;
388 }
389
390 impl()->writeI32(val);
391 return true;
392 }
393 case T_I64: {
394 int64_t nval = PyLong_AsLongLong(value);
395
396 if (INT_CONV_ERROR_OCCURRED(nval)) {
397 return false;
398 }
399
Nobuaki Sukegawaf7a8d942016-03-01 01:41:47 +0900400 if (!CHECK_RANGE(nval, std::numeric_limits<int64_t>::min(),
401 std::numeric_limits<int64_t>::max())) {
Nobuaki Sukegawa6525f6a2016-02-11 13:58:39 +0900402 PyErr_SetString(PyExc_OverflowError, "int out of range");
403 return false;
404 }
405
406 impl()->writeI64(nval);
407 return true;
408 }
409
410 case T_DOUBLE: {
411 double nval = PyFloat_AsDouble(value);
412 if (nval == -1.0 && PyErr_Occurred()) {
413 return false;
414 }
415
416 impl()->writeDouble(nval);
417 return true;
418 }
419
420 case T_STRING: {
421 if (PyUnicode_Check(value)) {
422 value = PyUnicode_AsUTF8String(value);
423 if (!value) {
424 return false;
425 }
426 }
427
428 Py_ssize_t len = PyBytes_Size(value);
429 if (!detail::check_ssize_t_32(len)) {
430 return false;
431 }
432
433 impl()->writeString(value, static_cast<int32_t>(len));
434 return true;
435 }
436
437 case T_LIST:
438 case T_SET: {
439 SetListTypeArgs parsedargs;
440 if (!parse_set_list_args(&parsedargs, typeargs)) {
441 return false;
442 }
443
444 Py_ssize_t len = PyObject_Length(value);
445 if (!detail::check_ssize_t_32(len)) {
446 return false;
447 }
448
449 if (!impl()->writeListBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
450 return false;
451 }
452 ScopedPyObject iterator(PyObject_GetIter(value));
453 if (!iterator) {
454 return false;
455 }
456
457 while (PyObject* rawItem = PyIter_Next(iterator.get())) {
458 ScopedPyObject item(rawItem);
459 if (!encodeValue(item.get(), parsedargs.element_type, parsedargs.typeargs)) {
460 return false;
461 }
462 }
463
464 return true;
465 }
466
467 case T_MAP: {
468 Py_ssize_t len = PyDict_Size(value);
469 if (!detail::check_ssize_t_32(len)) {
470 return false;
471 }
472
473 MapTypeArgs parsedargs;
474 if (!parse_map_args(&parsedargs, typeargs)) {
475 return false;
476 }
477
478 if (!impl()->writeMapBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
479 return false;
480 }
481 Py_ssize_t pos = 0;
482 PyObject* k = NULL;
483 PyObject* v = NULL;
484 // TODO(bmaurer): should support any mapping, not just dicts
485 while (PyDict_Next(value, &pos, &k, &v)) {
486 if (!encodeValue(k, parsedargs.ktag, parsedargs.ktypeargs)
487 || !encodeValue(v, parsedargs.vtag, parsedargs.vtypeargs)) {
488 return false;
489 }
490 }
491 return true;
492 }
493
494 case T_STRUCT: {
495 StructTypeArgs parsedargs;
496 if (!parse_struct_args(&parsedargs, typeargs)) {
497 return false;
498 }
499
500 Py_ssize_t nspec = PyTuple_Size(parsedargs.spec);
501 if (nspec == -1) {
502 PyErr_SetString(PyExc_TypeError, "spec is not a tuple");
503 return false;
504 }
505
506 detail::WriteStructScope<Impl> scope = detail::writeStructScope(this);
507 if (!scope) {
508 return false;
509 }
510 for (Py_ssize_t i = 0; i < nspec; i++) {
511 PyObject* spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, i);
512 if (spec_tuple == Py_None) {
513 continue;
514 }
515
516 StructItemSpec parsedspec;
517 if (!parse_struct_item_spec(&parsedspec, spec_tuple)) {
518 return false;
519 }
520
521 ScopedPyObject instval(PyObject_GetAttr(value, parsedspec.attrname));
522
523 if (!instval) {
524 return false;
525 }
526
527 if (instval.get() == Py_None) {
528 continue;
529 }
530
531 bool res = impl()->writeField(instval.get(), parsedspec);
532 if (!res) {
533 return false;
534 }
535 }
536 impl()->writeFieldStop();
537 return true;
538 }
539
540 case T_STOP:
541 case T_VOID:
542 case T_UTF16:
543 case T_UTF8:
544 case T_U64:
545 default:
546 PyErr_Format(PyExc_TypeError, "Unexpected TType for encodeValue: %d", type);
547 return false;
548 }
549
550 return true;
551}
552
553template <typename Impl>
554bool ProtocolBase<Impl>::skip(TType type) {
555 switch (type) {
556 case T_BOOL:
557 return impl()->skipBool();
558 case T_I08:
559 return impl()->skipByte();
560 case T_I16:
561 return impl()->skipI16();
562 case T_I32:
563 return impl()->skipI32();
564 case T_I64:
565 return impl()->skipI64();
566 case T_DOUBLE:
567 return impl()->skipDouble();
568
569 case T_STRING: {
570 return impl()->skipString();
571 }
572
573 case T_LIST:
574 case T_SET: {
575 TType etype = T_STOP;
576 int32_t len = impl()->readListBegin(etype);
577 if (len < 0) {
578 return false;
579 }
580 for (int32_t i = 0; i < len; i++) {
581 if (!skip(etype)) {
582 return false;
583 }
584 }
585 return true;
586 }
587
588 case T_MAP: {
589 TType ktype = T_STOP;
590 TType vtype = T_STOP;
591 int32_t len = impl()->readMapBegin(ktype, vtype);
592 if (len < 0) {
593 return false;
594 }
595 for (int32_t i = 0; i < len; i++) {
596 if (!skip(ktype) || !skip(vtype)) {
597 return false;
598 }
599 }
600 return true;
601 }
602
603 case T_STRUCT: {
604 detail::ReadStructScope<Impl> scope = detail::readStructScope(this);
605 if (!scope) {
606 return false;
607 }
608 while (true) {
609 TType type = T_STOP;
610 int16_t tag;
611 if (!impl()->readFieldBegin(type, tag)) {
612 return false;
613 }
614 if (type == T_STOP) {
615 return true;
616 }
617 if (!skip(type)) {
618 return false;
619 }
620 }
621 return true;
622 }
623
624 case T_STOP:
625 case T_VOID:
626 case T_UTF16:
627 case T_UTF8:
628 case T_U64:
629 default:
630 PyErr_Format(PyExc_TypeError, "Unexpected TType for skip: %d", type);
631 return false;
632 }
633
634 return true;
635}
636
637// Returns a new reference.
638template <typename Impl>
639PyObject* ProtocolBase<Impl>::decodeValue(TType type, PyObject* typeargs) {
640 switch (type) {
641
642 case T_BOOL: {
643 bool v = 0;
644 if (!impl()->readBool(v)) {
645 return NULL;
646 }
647 if (v) {
648 Py_RETURN_TRUE;
649 } else {
650 Py_RETURN_FALSE;
651 }
652 }
653 case T_I08: {
654 int8_t v = 0;
655 if (!impl()->readI8(v)) {
656 return NULL;
657 }
658 return PyInt_FromLong(v);
659 }
660 case T_I16: {
661 int16_t v = 0;
662 if (!impl()->readI16(v)) {
663 return NULL;
664 }
665 return PyInt_FromLong(v);
666 }
667 case T_I32: {
668 int32_t v = 0;
669 if (!impl()->readI32(v)) {
670 return NULL;
671 }
672 return PyInt_FromLong(v);
673 }
674
675 case T_I64: {
676 int64_t v = 0;
677 if (!impl()->readI64(v)) {
678 return NULL;
679 }
680 // TODO(dreiss): Find out if we can take this fastpath always when
681 // sizeof(long) == sizeof(long long).
682 if (CHECK_RANGE(v, LONG_MIN, LONG_MAX)) {
683 return PyInt_FromLong((long)v);
684 }
685 return PyLong_FromLongLong(v);
686 }
687
688 case T_DOUBLE: {
689 double v = 0.0;
690 if (!impl()->readDouble(v)) {
691 return NULL;
692 }
693 return PyFloat_FromDouble(v);
694 }
695
696 case T_STRING: {
697 char* buf = NULL;
698 int len = impl()->readString(&buf);
699 if (len < 0) {
700 return NULL;
701 }
702 if (isUtf8(typeargs)) {
703 return PyUnicode_DecodeUTF8(buf, len, 0);
704 } else {
705 return PyBytes_FromStringAndSize(buf, len);
706 }
707 }
708
709 case T_LIST:
710 case T_SET: {
711 SetListTypeArgs parsedargs;
712 if (!parse_set_list_args(&parsedargs, typeargs)) {
713 return NULL;
714 }
715
716 TType etype = T_STOP;
717 int32_t len = impl()->readListBegin(etype);
718 if (len < 0) {
719 return NULL;
720 }
721 if (len > 0 && !checkType(etype, parsedargs.element_type)) {
722 return NULL;
723 }
724
725 bool use_tuple = type == T_LIST && parsedargs.immutable;
726 ScopedPyObject ret(use_tuple ? PyTuple_New(len) : PyList_New(len));
727 if (!ret) {
728 return NULL;
729 }
730
731 for (int i = 0; i < len; i++) {
732 PyObject* item = decodeValue(etype, parsedargs.typeargs);
733 if (!item) {
734 return NULL;
735 }
736 if (use_tuple) {
737 PyTuple_SET_ITEM(ret.get(), i, item);
738 } else {
739 PyList_SET_ITEM(ret.get(), i, item);
740 }
741 }
742
743 // TODO(dreiss): Consider biting the bullet and making two separate cases
744 // for list and set, avoiding this post facto conversion.
745 if (type == T_SET) {
746 PyObject* setret;
747 setret = parsedargs.immutable ? PyFrozenSet_New(ret.get()) : PySet_New(ret.get());
748 return setret;
749 }
750 return ret.release();
751 }
752
753 case T_MAP: {
754 MapTypeArgs parsedargs;
755 if (!parse_map_args(&parsedargs, typeargs)) {
756 return NULL;
757 }
758
759 TType ktype = T_STOP;
760 TType vtype = T_STOP;
761 uint32_t len = impl()->readMapBegin(ktype, vtype);
762 if (len > 0 && (!checkType(ktype, parsedargs.ktag) || !checkType(vtype, parsedargs.vtag))) {
763 return NULL;
764 }
765
766 ScopedPyObject ret(PyDict_New());
767 if (!ret) {
768 return NULL;
769 }
770
771 for (uint32_t i = 0; i < len; i++) {
772 ScopedPyObject k(decodeValue(ktype, parsedargs.ktypeargs));
773 if (!k) {
774 return NULL;
775 }
776 ScopedPyObject v(decodeValue(vtype, parsedargs.vtypeargs));
777 if (!v) {
778 return NULL;
779 }
780 if (PyDict_SetItem(ret.get(), k.get(), v.get()) == -1) {
781 return NULL;
782 }
783 }
784
785 if (parsedargs.immutable) {
786 if (!ThriftModule) {
787 ThriftModule = PyImport_ImportModule("thrift.Thrift");
788 }
789 if (!ThriftModule) {
790 return NULL;
791 }
792
793 ScopedPyObject cls(PyObject_GetAttr(ThriftModule, INTERN_STRING(TFrozenDict)));
794 if (!cls) {
795 return NULL;
796 }
797
798 ScopedPyObject arg(PyTuple_New(1));
799 PyTuple_SET_ITEM(arg.get(), 0, ret.release());
800 ret.reset(PyObject_CallObject(cls.get(), arg.get()));
801 }
802
803 return ret.release();
804 }
805
806 case T_STRUCT: {
807 StructTypeArgs parsedargs;
808 if (!parse_struct_args(&parsedargs, typeargs)) {
809 return NULL;
810 }
811 return readStruct(Py_None, parsedargs.klass, parsedargs.spec);
812 }
813
814 case T_STOP:
815 case T_VOID:
816 case T_UTF16:
817 case T_UTF8:
818 case T_U64:
819 default:
820 PyErr_Format(PyExc_TypeError, "Unexpected TType for decodeValue: %d", type);
821 return NULL;
822 }
823}
824
825template <typename Impl>
826PyObject* ProtocolBase<Impl>::readStruct(PyObject* output, PyObject* klass, PyObject* spec_seq) {
827 int spec_seq_len = PyTuple_Size(spec_seq);
828 bool immutable = output == Py_None;
829 ScopedPyObject kwargs;
830 if (spec_seq_len == -1) {
831 return NULL;
832 }
833
834 if (immutable) {
835 kwargs.reset(PyDict_New());
836 if (!kwargs) {
837 PyErr_SetString(PyExc_TypeError, "failed to prepare kwargument storage");
838 return NULL;
839 }
840 }
841
842 detail::ReadStructScope<Impl> scope = detail::readStructScope(this);
843 if (!scope) {
844 return NULL;
845 }
846 while (true) {
847 TType type = T_STOP;
848 int16_t tag;
849 if (!impl()->readFieldBegin(type, tag)) {
850 return NULL;
851 }
852 if (type == T_STOP) {
853 break;
854 }
855 if (tag < 0 || tag >= spec_seq_len) {
856 if (!skip(type)) {
857 PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field");
858 return NULL;
859 }
860 continue;
861 }
862
863 PyObject* item_spec = PyTuple_GET_ITEM(spec_seq, tag);
864 if (item_spec == Py_None) {
865 if (!skip(type)) {
866 PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field");
867 return NULL;
868 }
869 continue;
870 }
871 StructItemSpec parsedspec;
872 if (!parse_struct_item_spec(&parsedspec, item_spec)) {
873 return NULL;
874 }
875 if (parsedspec.type != type) {
876 if (!skip(type)) {
877 PyErr_Format(PyExc_TypeError, "struct field had wrong type: expected %d but got %d",
878 parsedspec.type, type);
879 return NULL;
880 }
881 continue;
882 }
883
884 ScopedPyObject fieldval(decodeValue(parsedspec.type, parsedspec.typeargs));
885 if (!fieldval) {
886 return NULL;
887 }
888
889 if ((immutable && PyDict_SetItem(kwargs.get(), parsedspec.attrname, fieldval.get()) == -1)
890 || (!immutable && PyObject_SetAttr(output, parsedspec.attrname, fieldval.get()) == -1)) {
891 return NULL;
892 }
893 }
894 if (immutable) {
895 ScopedPyObject args(PyTuple_New(0));
896 if (!args) {
897 PyErr_SetString(PyExc_TypeError, "failed to prepare argument storage");
898 return NULL;
899 }
900 return PyObject_Call(klass, args.get(), kwargs.get());
901 }
902 Py_INCREF(output);
903 return output;
904}
905}
906}
907}
908#endif // THRIFT_PY_PROTOCOL_H