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 #ifndef THRIFT_PY_PROTOCOL_TCC
21 #define THRIFT_PY_PROTOCOL_TCC
22
23 #include <iterator>
24
25 #define CHECK_RANGE(v, min, max) (((v) <= (max)) && ((v) >= (min)))
26 #define INIT_OUTBUF_SIZE 128
27
28 #if PY_MAJOR_VERSION < 3
29 #include <cStringIO.h>
30 #else
31 #include <algorithm>
32 #endif
33
34 namespace apache {
35 namespace thrift {
36 namespace py {
37
38 #if PY_MAJOR_VERSION < 3
39
40 namespace detail {
41
input_check(PyObject * input)42 inline bool input_check(PyObject* input) {
43 return PycStringIO_InputCheck(input);
44 }
45
new_encode_buffer(size_t size)46 inline EncodeBuffer* new_encode_buffer(size_t size) {
47 if (!PycStringIO) {
48 PycString_IMPORT;
49 }
50 if (!PycStringIO) {
51 return nullptr;
52 }
53 return PycStringIO->NewOutput(size);
54 }
55
read_buffer(PyObject * buf,char ** output,int len)56 inline 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
68 template <typename Impl>
~ProtocolBase()69 inline ProtocolBase<Impl>::~ProtocolBase() {
70 if (output_) {
71 Py_CLEAR(output_);
72 }
73 }
74
75 template <typename Impl>
isUtf8(PyObject * typeargs)76 inline bool ProtocolBase<Impl>::isUtf8(PyObject* typeargs) {
77 return PyString_Check(typeargs) && !strncmp(PyString_AS_STRING(typeargs), "UTF8", 4);
78 }
79
80 template <typename Impl>
getEncodedValue()81 PyObject* ProtocolBase<Impl>::getEncodedValue() {
82 if (!PycStringIO) {
83 PycString_IMPORT;
84 }
85 if (!PycStringIO) {
86 return nullptr;
87 }
88 return PycStringIO->cgetvalue(output_);
89 }
90
91 template <typename Impl>
writeBuffer(char * data,size_t size)92 inline 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 (static_cast<size_t>(len) != size) {
106 PyErr_Format(PyExc_EOFError, "write length mismatch: expected %lu got %d", size, len);
107 return false;
108 }
109 return true;
110 }
111
112 #else
113
114 namespace detail {
115
116 inline bool input_check(PyObject* input) {
117 // TODO: Check for BytesIO type
118 return true;
119 }
120
121 inline 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
128 struct 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
139 inline 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
152 template <typename Impl>
153 inline ProtocolBase<Impl>::~ProtocolBase() {
154 if (output_) {
155 delete output_;
156 }
157 }
158
159 template <typename Impl>
160 inline 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
166 template <typename Impl>
167 PyObject* ProtocolBase<Impl>::getEncodedValue() {
168 return PyBytes_FromStringAndSize(output_->buf.data(), output_->buf.size());
169 }
170
171 template <typename Impl>
172 inline 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&) {
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
188 namespace 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 }
DECLARE_OP_SCOPE(WriteStruct,writeStruct)206 DECLARE_OP_SCOPE(WriteStruct, writeStruct)
207 DECLARE_OP_SCOPE(ReadStruct, readStruct)
208 #undef DECLARE_OP_SCOPE
209
210 inline 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 }
215 if (!CHECK_RANGE(len, 0, (std::numeric_limits<int32_t>::max)())) {
216 PyErr_SetString(PyExc_OverflowError, "size out of range: exceeded INT32_MAX");
217 return false;
218 }
219 return true;
220 }
221 }
222
223 template <typename T>
parse_pyint(PyObject * o,T * ret,int32_t min,int32_t max)224 bool 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
239 template <typename Impl>
checkType(TType got,TType expected)240 inline 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
248 template <typename Impl>
checkLengthLimit(int32_t len,long limit)249 bool ProtocolBase<Impl>::checkLengthLimit(int32_t len, long limit) {
250 if (len < 0) {
251 PyErr_Format(PyExc_OverflowError, "negative length: %ld", limit);
252 return false;
253 }
254 if (len > limit) {
255 PyErr_Format(PyExc_OverflowError, "size exceeded specified limit: %ld", limit);
256 return false;
257 }
258 return true;
259 }
260
261 template <typename Impl>
readBytes(char ** output,int len)262 bool 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
279 ScopedPyObject newiobuf(PyObject_CallFunction(input_.refill_callable.get(), refill_signature,
280 *output, rlen, len, nullptr));
281 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
302 template <typename Impl>
prepareDecodeBufferFromTransport(PyObject * trans)303 bool 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
332 template <typename Impl>
prepareEncodeBuffer()333 bool ProtocolBase<Impl>::prepareEncodeBuffer() {
334 output_ = detail::new_encode_buffer(INIT_OUTBUF_SIZE);
335 return output_ != nullptr;
336 }
337
338 template <typename Impl>
encodeValue(PyObject * value,TType type,PyObject * typeargs)339 bool 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
363 if (!parse_pyint(value, &val, (std::numeric_limits<int8_t>::min)(),
364 (std::numeric_limits<int8_t>::max)())) {
365 return false;
366 }
367
368 impl()->writeI8(val);
369 return true;
370 }
371 case T_I16: {
372 int16_t val;
373
374 if (!parse_pyint(value, &val, (std::numeric_limits<int16_t>::min)(),
375 (std::numeric_limits<int16_t>::max)())) {
376 return false;
377 }
378
379 impl()->writeI16(val);
380 return true;
381 }
382 case T_I32: {
383 int32_t val;
384
385 if (!parse_pyint(value, &val, (std::numeric_limits<int32_t>::min)(),
386 (std::numeric_limits<int32_t>::max)())) {
387 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
400 if (!CHECK_RANGE(nval, (std::numeric_limits<int64_t>::min)(),
401 (std::numeric_limits<int64_t>::max)())) {
402 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 ScopedPyObject nval;
422
423 if (PyUnicode_Check(value)) {
424 nval.reset(PyUnicode_AsUTF8String(value));
425 if (!nval) {
426 return false;
427 }
428 } else {
429 Py_INCREF(value);
430 nval.reset(value);
431 }
432
433 Py_ssize_t len = PyBytes_Size(nval.get());
434 if (!detail::check_ssize_t_32(len)) {
435 return false;
436 }
437
438 impl()->writeString(nval.get(), static_cast<int32_t>(len));
439 return true;
440 }
441
442 case T_LIST:
443 case T_SET: {
444 SetListTypeArgs parsedargs;
445 if (!parse_set_list_args(&parsedargs, typeargs)) {
446 return false;
447 }
448
449 Py_ssize_t len = PyObject_Length(value);
450 if (!detail::check_ssize_t_32(len)) {
451 return false;
452 }
453
454 if (!impl()->writeListBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
455 return false;
456 }
457 ScopedPyObject iterator(PyObject_GetIter(value));
458 if (!iterator) {
459 return false;
460 }
461
462 while (PyObject* rawItem = PyIter_Next(iterator.get())) {
463 ScopedPyObject item(rawItem);
464 if (!encodeValue(item.get(), parsedargs.element_type, parsedargs.typeargs)) {
465 return false;
466 }
467 }
468
469 return true;
470 }
471
472 case T_MAP: {
473 Py_ssize_t len = PyDict_Size(value);
474 if (!detail::check_ssize_t_32(len)) {
475 return false;
476 }
477
478 MapTypeArgs parsedargs;
479 if (!parse_map_args(&parsedargs, typeargs)) {
480 return false;
481 }
482
483 if (!impl()->writeMapBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
484 return false;
485 }
486 Py_ssize_t pos = 0;
487 PyObject* k = nullptr;
488 PyObject* v = nullptr;
489 // TODO(bmaurer): should support any mapping, not just dicts
490 while (PyDict_Next(value, &pos, &k, &v)) {
491 if (!encodeValue(k, parsedargs.ktag, parsedargs.ktypeargs)
492 || !encodeValue(v, parsedargs.vtag, parsedargs.vtypeargs)) {
493 return false;
494 }
495 }
496 return true;
497 }
498
499 case T_STRUCT: {
500 StructTypeArgs parsedargs;
501 if (!parse_struct_args(&parsedargs, typeargs)) {
502 return false;
503 }
504
505 Py_ssize_t nspec = PyTuple_Size(parsedargs.spec);
506 if (nspec == -1) {
507 PyErr_SetString(PyExc_TypeError, "spec is not a tuple");
508 return false;
509 }
510
511 detail::WriteStructScope<Impl> scope = detail::writeStructScope(this);
512 if (!scope) {
513 return false;
514 }
515 for (Py_ssize_t i = 0; i < nspec; i++) {
516 PyObject* spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, i);
517 if (spec_tuple == Py_None) {
518 continue;
519 }
520
521 StructItemSpec parsedspec;
522 if (!parse_struct_item_spec(&parsedspec, spec_tuple)) {
523 return false;
524 }
525
526 ScopedPyObject instval(PyObject_GetAttr(value, parsedspec.attrname));
527
528 if (!instval) {
529 return false;
530 }
531
532 if (instval.get() == Py_None) {
533 continue;
534 }
535
536 bool res = impl()->writeField(instval.get(), parsedspec);
537 if (!res) {
538 return false;
539 }
540 }
541 impl()->writeFieldStop();
542 return true;
543 }
544
545 case T_STOP:
546 case T_VOID:
547 case T_UTF16:
548 case T_UTF8:
549 case T_U64:
550 default:
551 PyErr_Format(PyExc_TypeError, "Unexpected TType for encodeValue: %d", type);
552 return false;
553 }
554
555 return true;
556 }
557
558 template <typename Impl>
skip(TType type)559 bool ProtocolBase<Impl>::skip(TType type) {
560 switch (type) {
561 case T_BOOL:
562 return impl()->skipBool();
563 case T_I08:
564 return impl()->skipByte();
565 case T_I16:
566 return impl()->skipI16();
567 case T_I32:
568 return impl()->skipI32();
569 case T_I64:
570 return impl()->skipI64();
571 case T_DOUBLE:
572 return impl()->skipDouble();
573
574 case T_STRING: {
575 return impl()->skipString();
576 }
577
578 case T_LIST:
579 case T_SET: {
580 TType etype = T_STOP;
581 int32_t len = impl()->readListBegin(etype);
582 if (len < 0) {
583 return false;
584 }
585 for (int32_t i = 0; i < len; i++) {
586 if (!skip(etype)) {
587 return false;
588 }
589 }
590 return true;
591 }
592
593 case T_MAP: {
594 TType ktype = T_STOP;
595 TType vtype = T_STOP;
596 int32_t len = impl()->readMapBegin(ktype, vtype);
597 if (len < 0) {
598 return false;
599 }
600 for (int32_t i = 0; i < len; i++) {
601 if (!skip(ktype) || !skip(vtype)) {
602 return false;
603 }
604 }
605 return true;
606 }
607
608 case T_STRUCT: {
609 detail::ReadStructScope<Impl> scope = detail::readStructScope(this);
610 if (!scope) {
611 return false;
612 }
613 while (true) {
614 TType type = T_STOP;
615 int16_t tag;
616 if (!impl()->readFieldBegin(type, tag)) {
617 return false;
618 }
619 if (type == T_STOP) {
620 return true;
621 }
622 if (!skip(type)) {
623 return false;
624 }
625 }
626 return true;
627 }
628
629 case T_STOP:
630 case T_VOID:
631 case T_UTF16:
632 case T_UTF8:
633 case T_U64:
634 default:
635 PyErr_Format(PyExc_TypeError, "Unexpected TType for skip: %d", type);
636 return false;
637 }
638
639 return true;
640 }
641
642 // Returns a new reference.
643 template <typename Impl>
decodeValue(TType type,PyObject * typeargs)644 PyObject* ProtocolBase<Impl>::decodeValue(TType type, PyObject* typeargs) {
645 switch (type) {
646
647 case T_BOOL: {
648 bool v = 0;
649 if (!impl()->readBool(v)) {
650 return nullptr;
651 }
652 if (v) {
653 Py_RETURN_TRUE;
654 } else {
655 Py_RETURN_FALSE;
656 }
657 }
658 case T_I08: {
659 int8_t v = 0;
660 if (!impl()->readI8(v)) {
661 return nullptr;
662 }
663 return PyInt_FromLong(v);
664 }
665 case T_I16: {
666 int16_t v = 0;
667 if (!impl()->readI16(v)) {
668 return nullptr;
669 }
670 return PyInt_FromLong(v);
671 }
672 case T_I32: {
673 int32_t v = 0;
674 if (!impl()->readI32(v)) {
675 return nullptr;
676 }
677 return PyInt_FromLong(v);
678 }
679
680 case T_I64: {
681 int64_t v = 0;
682 if (!impl()->readI64(v)) {
683 return nullptr;
684 }
685 // TODO(dreiss): Find out if we can take this fastpath always when
686 // sizeof(long) == sizeof(long long).
687 if (CHECK_RANGE(v, LONG_MIN, LONG_MAX)) {
688 return PyInt_FromLong((long)v);
689 }
690 return PyLong_FromLongLong(v);
691 }
692
693 case T_DOUBLE: {
694 double v = 0.0;
695 if (!impl()->readDouble(v)) {
696 return nullptr;
697 }
698 return PyFloat_FromDouble(v);
699 }
700
701 case T_STRING: {
702 char* buf = nullptr;
703 int len = impl()->readString(&buf);
704 if (len < 0) {
705 return nullptr;
706 }
707 if (isUtf8(typeargs)) {
708 return PyUnicode_DecodeUTF8(buf, len, "replace");
709 } else {
710 return PyBytes_FromStringAndSize(buf, len);
711 }
712 }
713
714 case T_LIST:
715 case T_SET: {
716 SetListTypeArgs parsedargs;
717 if (!parse_set_list_args(&parsedargs, typeargs)) {
718 return nullptr;
719 }
720
721 TType etype = T_STOP;
722 int32_t len = impl()->readListBegin(etype);
723 if (len < 0) {
724 return nullptr;
725 }
726 if (len > 0 && !checkType(etype, parsedargs.element_type)) {
727 return nullptr;
728 }
729
730 bool use_tuple = type == T_LIST && parsedargs.immutable;
731 ScopedPyObject ret(use_tuple ? PyTuple_New(len) : PyList_New(len));
732 if (!ret) {
733 return nullptr;
734 }
735
736 for (int i = 0; i < len; i++) {
737 PyObject* item = decodeValue(etype, parsedargs.typeargs);
738 if (!item) {
739 return nullptr;
740 }
741 if (use_tuple) {
742 PyTuple_SET_ITEM(ret.get(), i, item);
743 } else {
744 PyList_SET_ITEM(ret.get(), i, item);
745 }
746 }
747
748 // TODO(dreiss): Consider biting the bullet and making two separate cases
749 // for list and set, avoiding this post facto conversion.
750 if (type == T_SET) {
751 PyObject* setret;
752 setret = parsedargs.immutable ? PyFrozenSet_New(ret.get()) : PySet_New(ret.get());
753 return setret;
754 }
755 return ret.release();
756 }
757
758 case T_MAP: {
759 MapTypeArgs parsedargs;
760 if (!parse_map_args(&parsedargs, typeargs)) {
761 return nullptr;
762 }
763
764 TType ktype = T_STOP;
765 TType vtype = T_STOP;
766 uint32_t len = impl()->readMapBegin(ktype, vtype);
767 if (len > 0 && (!checkType(ktype, parsedargs.ktag) || !checkType(vtype, parsedargs.vtag))) {
768 return nullptr;
769 }
770
771 ScopedPyObject ret(PyDict_New());
772 if (!ret) {
773 return nullptr;
774 }
775
776 for (uint32_t i = 0; i < len; i++) {
777 ScopedPyObject k(decodeValue(ktype, parsedargs.ktypeargs));
778 if (!k) {
779 return nullptr;
780 }
781 ScopedPyObject v(decodeValue(vtype, parsedargs.vtypeargs));
782 if (!v) {
783 return nullptr;
784 }
785 if (PyDict_SetItem(ret.get(), k.get(), v.get()) == -1) {
786 return nullptr;
787 }
788 }
789
790 if (parsedargs.immutable) {
791 if (!ThriftModule) {
792 ThriftModule = PyImport_ImportModule("thrift.Thrift");
793 }
794 if (!ThriftModule) {
795 return nullptr;
796 }
797
798 ScopedPyObject cls(PyObject_GetAttr(ThriftModule, INTERN_STRING(TFrozenDict)));
799 if (!cls) {
800 return nullptr;
801 }
802
803 ScopedPyObject arg(PyTuple_New(1));
804 PyTuple_SET_ITEM(arg.get(), 0, ret.release());
805 ret.reset(PyObject_CallObject(cls.get(), arg.get()));
806 }
807
808 return ret.release();
809 }
810
811 case T_STRUCT: {
812 StructTypeArgs parsedargs;
813 if (!parse_struct_args(&parsedargs, typeargs)) {
814 return nullptr;
815 }
816 return readStruct(Py_None, parsedargs.klass, parsedargs.spec);
817 }
818
819 case T_STOP:
820 case T_VOID:
821 case T_UTF16:
822 case T_UTF8:
823 case T_U64:
824 default:
825 PyErr_Format(PyExc_TypeError, "Unexpected TType for decodeValue: %d", type);
826 return nullptr;
827 }
828 }
829
830 template <typename Impl>
readStruct(PyObject * output,PyObject * klass,PyObject * spec_seq)831 PyObject* ProtocolBase<Impl>::readStruct(PyObject* output, PyObject* klass, PyObject* spec_seq) {
832 int spec_seq_len = PyTuple_Size(spec_seq);
833 bool immutable = output == Py_None;
834 ScopedPyObject kwargs;
835 if (spec_seq_len == -1) {
836 return nullptr;
837 }
838
839 if (immutable) {
840 kwargs.reset(PyDict_New());
841 if (!kwargs) {
842 PyErr_SetString(PyExc_TypeError, "failed to prepare kwargument storage");
843 return nullptr;
844 }
845 }
846
847 detail::ReadStructScope<Impl> scope = detail::readStructScope(this);
848 if (!scope) {
849 return nullptr;
850 }
851 while (true) {
852 TType type = T_STOP;
853 int16_t tag;
854 if (!impl()->readFieldBegin(type, tag)) {
855 return nullptr;
856 }
857 if (type == T_STOP) {
858 break;
859 }
860 if (tag < 0 || tag >= spec_seq_len) {
861 if (!skip(type)) {
862 PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field");
863 return nullptr;
864 }
865 continue;
866 }
867
868 PyObject* item_spec = PyTuple_GET_ITEM(spec_seq, tag);
869 if (item_spec == Py_None) {
870 if (!skip(type)) {
871 PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field");
872 return nullptr;
873 }
874 continue;
875 }
876 StructItemSpec parsedspec;
877 if (!parse_struct_item_spec(&parsedspec, item_spec)) {
878 return nullptr;
879 }
880 if (parsedspec.type != type) {
881 if (!skip(type)) {
882 PyErr_Format(PyExc_TypeError, "struct field had wrong type: expected %d but got %d",
883 parsedspec.type, type);
884 return nullptr;
885 }
886 continue;
887 }
888
889 ScopedPyObject fieldval(decodeValue(parsedspec.type, parsedspec.typeargs));
890 if (!fieldval) {
891 return nullptr;
892 }
893
894 if ((immutable && PyDict_SetItem(kwargs.get(), parsedspec.attrname, fieldval.get()) == -1)
895 || (!immutable && PyObject_SetAttr(output, parsedspec.attrname, fieldval.get()) == -1)) {
896 return nullptr;
897 }
898 }
899 if (immutable) {
900 ScopedPyObject args(PyTuple_New(0));
901 if (!args) {
902 PyErr_SetString(PyExc_TypeError, "failed to prepare argument storage");
903 return nullptr;
904 }
905 return PyObject_Call(klass, args.get(), kwargs.get());
906 }
907 Py_INCREF(output);
908 return output;
909 }
910 }
911 }
912 }
913 #endif // THRIFT_PY_PROTOCOL_H
914