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 #include <Python.h>
21 #include "types.h"
22 #include "binary.h"
23 #include "compact.h"
24 #include <limits>
25 #include <stdint.h>
26 
27 // TODO(dreiss): defval appears to be unused.  Look into removing it.
28 // TODO(dreiss): Make parse_spec_args recursive, and cache the output
29 //               permanently in the object.  (Malloc and orphan.)
30 // TODO(dreiss): Why do we need cStringIO for reading, why not just char*?
31 //               Can cStringIO let us work with a BufferedTransport?
32 // TODO(dreiss): Don't ignore the rv from cwrite (maybe).
33 
34 // Doing a benchmark shows that interning actually makes a difference, amazingly.
35 
36 /** Pointer to interned string to speed up attribute lookup. */
37 PyObject* INTERN_STRING(TFrozenDict);
38 PyObject* INTERN_STRING(cstringio_buf);
39 PyObject* INTERN_STRING(cstringio_refill);
40 static PyObject* INTERN_STRING(string_length_limit);
41 static PyObject* INTERN_STRING(container_length_limit);
42 static PyObject* INTERN_STRING(trans);
43 
44 namespace apache {
45 namespace thrift {
46 namespace py {
47 
48 template <typename T>
encode_impl(PyObject * args)49 static PyObject* encode_impl(PyObject* args) {
50   if (!args)
51     return nullptr;
52 
53   PyObject* enc_obj = nullptr;
54   PyObject* type_args = nullptr;
55   if (!PyArg_ParseTuple(args, "OO", &enc_obj, &type_args)) {
56     return nullptr;
57   }
58   if (!enc_obj || !type_args) {
59     return nullptr;
60   }
61 
62   T protocol;
63   if (!protocol.prepareEncodeBuffer() || !protocol.encodeValue(enc_obj, T_STRUCT, type_args)) {
64     return nullptr;
65   }
66 
67   return protocol.getEncodedValue();
68 }
69 
as_long_then_delete(PyObject * value,long default_value)70 static inline long as_long_then_delete(PyObject* value, long default_value) {
71   ScopedPyObject scope(value);
72   long v = PyInt_AsLong(value);
73   if (INT_CONV_ERROR_OCCURRED(v)) {
74     PyErr_Clear();
75     return default_value;
76   }
77   return v;
78 }
79 
80 template <typename T>
decode_impl(PyObject * args)81 static PyObject* decode_impl(PyObject* args) {
82   PyObject* output_obj = nullptr;
83   PyObject* oprot = nullptr;
84   PyObject* typeargs = nullptr;
85   if (!PyArg_ParseTuple(args, "OOO", &output_obj, &oprot, &typeargs)) {
86     return nullptr;
87   }
88 
89   T protocol;
90   int32_t default_limit = (std::numeric_limits<int32_t>::max)();
91   protocol.setStringLengthLimit(
92       as_long_then_delete(PyObject_GetAttr(oprot, INTERN_STRING(string_length_limit)),
93                           default_limit));
94   protocol.setContainerLengthLimit(
95       as_long_then_delete(PyObject_GetAttr(oprot, INTERN_STRING(container_length_limit)),
96                           default_limit));
97   ScopedPyObject transport(PyObject_GetAttr(oprot, INTERN_STRING(trans)));
98   if (!transport) {
99     return nullptr;
100   }
101 
102   StructTypeArgs parsedargs;
103   if (!parse_struct_args(&parsedargs, typeargs)) {
104     return nullptr;
105   }
106 
107   if (!protocol.prepareDecodeBufferFromTransport(transport.get())) {
108     return nullptr;
109   }
110 
111   return protocol.readStruct(output_obj, parsedargs.klass, parsedargs.spec);
112 }
113 }
114 }
115 }
116 
117 using namespace apache::thrift::py;
118 
119 /* -- PYTHON MODULE SETUP STUFF --- */
120 
121 extern "C" {
122 
encode_binary(PyObject *,PyObject * args)123 static PyObject* encode_binary(PyObject*, PyObject* args) {
124   return encode_impl<BinaryProtocol>(args);
125 }
126 
decode_binary(PyObject *,PyObject * args)127 static PyObject* decode_binary(PyObject*, PyObject* args) {
128   return decode_impl<BinaryProtocol>(args);
129 }
130 
encode_compact(PyObject *,PyObject * args)131 static PyObject* encode_compact(PyObject*, PyObject* args) {
132   return encode_impl<CompactProtocol>(args);
133 }
134 
decode_compact(PyObject *,PyObject * args)135 static PyObject* decode_compact(PyObject*, PyObject* args) {
136   return decode_impl<CompactProtocol>(args);
137 }
138 
139 static PyMethodDef ThriftFastBinaryMethods[] = {
140     {"encode_binary", encode_binary, METH_VARARGS, ""},
141     {"decode_binary", decode_binary, METH_VARARGS, ""},
142     {"encode_compact", encode_compact, METH_VARARGS, ""},
143     {"decode_compact", decode_compact, METH_VARARGS, ""},
144     {nullptr, nullptr, 0, nullptr} /* Sentinel */
145 };
146 
147 #if PY_MAJOR_VERSION >= 3
148 
149 static struct PyModuleDef ThriftFastBinaryDef = {PyModuleDef_HEAD_INIT,
150                                                  "thrift.protocol.fastbinary",
151                                                  nullptr,
152                                                  0,
153                                                  ThriftFastBinaryMethods,
154                                                  nullptr,
155                                                  nullptr,
156                                                  nullptr,
157                                                  nullptr};
158 
159 #define INITERROR return nullptr;
160 
PyInit_fastbinary()161 PyObject* PyInit_fastbinary() {
162 
163 #else
164 
165 #define INITERROR return;
166 
167 void initfastbinary() {
168 
169   PycString_IMPORT;
170   if (PycStringIO == nullptr)
171     INITERROR
172 
173 #endif
174 
175 #define INIT_INTERN_STRING(value)                                                                  \
176   do {                                                                                             \
177     INTERN_STRING(value) = PyString_InternFromString(#value);                                      \
178     if (!INTERN_STRING(value))                                                                     \
179       INITERROR                                                                                    \
180   } while (0)
181 
182   INIT_INTERN_STRING(TFrozenDict);
183   INIT_INTERN_STRING(cstringio_buf);
184   INIT_INTERN_STRING(cstringio_refill);
185   INIT_INTERN_STRING(string_length_limit);
186   INIT_INTERN_STRING(container_length_limit);
187   INIT_INTERN_STRING(trans);
188 #undef INIT_INTERN_STRING
189 
190   PyObject* module =
191 #if PY_MAJOR_VERSION >= 3
192       PyModule_Create(&ThriftFastBinaryDef);
193 #else
194       Py_InitModule("thrift.protocol.fastbinary", ThriftFastBinaryMethods);
195 #endif
196   if (module == nullptr)
197     INITERROR;
198 
199 #if PY_MAJOR_VERSION >= 3
200   return module;
201 #endif
202 }
203 }
204