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