1#!/usr/bin/env python
2
3#
4# Licensed to the Apache Software Foundation (ASF) under one
5# or more contributor license agreements. See the NOTICE file
6# distributed with this work for additional information
7# regarding copyright ownership. The ASF licenses this file
8# to you under the Apache License, Version 2.0 (the
9# "License"); you may not use this file except in compliance
10# with the License. You may obtain a copy of the License at
11#
12#   http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing,
15# software distributed under the License is distributed on an
16# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17# KIND, either express or implied. See the License for the
18# specific language governing permissions and limitations
19# under the License.
20#
21
22r"""
23PYTHONPATH=./gen-py:../../lib/py/build/lib... ./FastbinaryTest.py
24"""
25
26# TODO(dreiss): Test error cases.  Check for memory leaks.
27
28from __future__ import print_function
29
30import math
31import os
32import sys
33import timeit
34
35from copy import deepcopy
36from pprint import pprint
37
38from thrift.transport import TTransport
39from thrift.protocol.TBinaryProtocol import TBinaryProtocol, TBinaryProtocolAccelerated
40from thrift.protocol.TCompactProtocol import TCompactProtocol, TCompactProtocolAccelerated
41
42from DebugProtoTest import Srv
43from DebugProtoTest.ttypes import Backwards, Bonk, Empty, HolyMoley, OneOfEach, RandomStuff, Wrapper
44
45
46class TDevNullTransport(TTransport.TTransportBase):
47    def __init__(self):
48        pass
49
50    def isOpen(self):
51        return True
52
53
54ooe1 = OneOfEach()
55ooe1.im_true = True
56ooe1.im_false = False
57ooe1.a_bite = 0xd6
58ooe1.integer16 = 27000
59ooe1.integer32 = 1 << 24
60ooe1.integer64 = 6000 * 1000 * 1000
61ooe1.double_precision = math.pi
62ooe1.some_characters = "Debug THIS!"
63ooe1.zomg_unicode = u"\xd7\n\a\t"
64
65ooe2 = OneOfEach()
66ooe2.integer16 = 16
67ooe2.integer32 = 32
68ooe2.integer64 = 64
69ooe2.double_precision = (math.sqrt(5) + 1) / 2
70ooe2.some_characters = ":R (me going \"rrrr\")"
71ooe2.zomg_unicode = u"\xd3\x80\xe2\x85\xae\xce\x9d\x20"\
72                    u"\xd0\x9d\xce\xbf\xe2\x85\xbf\xd0\xbe"\
73                    u"\xc9\xa1\xd0\xb3\xd0\xb0\xcf\x81\xe2\x84\x8e"\
74                    u"\x20\xce\x91\x74\x74\xce\xb1\xe2\x85\xbd\xce\xba"\
75                    u"\xc7\x83\xe2\x80\xbc"
76
77if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
78    ooe1.zomg_unicode = ooe1.zomg_unicode.encode('utf8')
79    ooe2.zomg_unicode = ooe2.zomg_unicode.encode('utf8')
80
81hm = HolyMoley(**{"big": [], "contain": set(), "bonks": {}})
82hm.big.append(ooe1)
83hm.big.append(ooe2)
84hm.big[0].a_bite = 0x22
85hm.big[1].a_bite = 0x22
86
87hm.contain.add(("and a one", "and a two"))
88hm.contain.add(("then a one, two", "three!", "FOUR!"))
89if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
90    hm.contain.add((u"\xd7\n\a\t".encode('utf8'),))
91else:
92    hm.contain.add((u"\xd7\n\a\t",))
93hm.contain.add(())
94
95hm.bonks["nothing"] = []
96hm.bonks["something"] = [
97    Bonk(**{"type": 1, "message": "Wait."}),
98    Bonk(**{"type": 2, "message": "What?"}),
99]
100hm.bonks["poe"] = [
101    Bonk(**{"type": 3, "message": "quoth"}),
102    Bonk(**{"type": 4, "message": "the raven"}),
103    Bonk(**{"type": 5, "message": "nevermore"}),
104]
105
106rs = RandomStuff()
107rs.a = 1
108rs.b = 2
109rs.c = 3
110rs.myintlist = list(range(20))
111rs.maps = {1: Wrapper(**{"foo": Empty()}), 2: Wrapper(**{"foo": Empty()})}
112rs.bigint = 124523452435
113rs.triple = 3.14
114
115# make sure this splits two buffers in a buffered protocol
116rshuge = RandomStuff()
117rshuge.myintlist = list(range(10000))
118
119my_zero = Srv.Janky_result(**{"success": 5})
120
121
122class Test(object):
123    def __init__(self, fast, slow):
124        self._fast = fast
125        self._slow = slow
126
127    def _check_write(self, o):
128        trans_fast = TTransport.TMemoryBuffer()
129        trans_slow = TTransport.TMemoryBuffer()
130        prot_fast = self._fast(trans_fast, fallback=False)
131        prot_slow = self._slow(trans_slow)
132
133        o.write(prot_fast)
134        o.write(prot_slow)
135        ORIG = trans_slow.getvalue()
136        MINE = trans_fast.getvalue()
137        if ORIG != MINE:
138            print("actual  : %s\nexpected: %s" % (repr(MINE), repr(ORIG)))
139            raise Exception('write value mismatch')
140
141    def _check_read(self, o):
142        prot = self._slow(TTransport.TMemoryBuffer())
143        o.write(prot)
144
145        slow_version_binary = prot.trans.getvalue()
146
147        prot = self._fast(
148            TTransport.TMemoryBuffer(slow_version_binary), fallback=False)
149        c = o.__class__()
150        c.read(prot)
151        if c != o:
152            print("actual  : ")
153            pprint(repr(c))
154            print("expected: ")
155            pprint(repr(o))
156            raise Exception('read value mismatch')
157
158        prot = self._fast(
159            TTransport.TBufferedTransport(
160                TTransport.TMemoryBuffer(slow_version_binary)), fallback=False)
161        c = o.__class__()
162        c.read(prot)
163        if c != o:
164            print("actual  : ")
165            pprint(repr(c))
166            print("expected: ")
167            pprint(repr(o))
168            raise Exception('read value mismatch')
169
170    def do_test(self):
171        self._check_write(HolyMoley())
172        self._check_read(HolyMoley())
173
174        self._check_write(hm)
175        no_set = deepcopy(hm)
176        no_set.contain = set()
177        self._check_read(no_set)
178        self._check_read(hm)
179
180        self._check_write(rs)
181        self._check_read(rs)
182
183        self._check_write(rshuge)
184        self._check_read(rshuge)
185
186        self._check_write(my_zero)
187        self._check_read(my_zero)
188
189        self._check_read(Backwards(**{"first_tag2": 4, "second_tag1": 2}))
190
191        # One case where the serialized form changes, but only superficially.
192        o = Backwards(**{"first_tag2": 4, "second_tag1": 2})
193        trans_fast = TTransport.TMemoryBuffer()
194        trans_slow = TTransport.TMemoryBuffer()
195        prot_fast = self._fast(trans_fast, fallback=False)
196        prot_slow = self._slow(trans_slow)
197
198        o.write(prot_fast)
199        o.write(prot_slow)
200        ORIG = trans_slow.getvalue()
201        MINE = trans_fast.getvalue()
202        assert id(ORIG) != id(MINE)
203
204        prot = self._fast(TTransport.TMemoryBuffer(), fallback=False)
205        o.write(prot)
206        prot = self._slow(
207            TTransport.TMemoryBuffer(prot.trans.getvalue()))
208        c = o.__class__()
209        c.read(prot)
210        if c != o:
211            print("copy: ")
212            pprint(repr(c))
213            print("orig: ")
214            pprint(repr(o))
215
216
217def do_test(fast, slow):
218    Test(fast, slow).do_test()
219
220
221def do_benchmark(protocol, iters=5000, skip_slow=False):
222    setup = """
223from __main__ import hm, rs, TDevNullTransport
224from thrift.protocol.{0} import {0}{1}
225trans = TDevNullTransport()
226prot = {0}{1}(trans{2})
227"""
228
229    setup_fast = setup.format(protocol, 'Accelerated', ', fallback=False')
230    if not skip_slow:
231        setup_slow = setup.format(protocol, '', '')
232
233    print("Starting Benchmarks")
234
235    if not skip_slow:
236        print("HolyMoley Standard = %f" %
237              timeit.Timer('hm.write(prot)', setup_slow).timeit(number=iters))
238
239    print("HolyMoley Acceler. = %f" %
240          timeit.Timer('hm.write(prot)', setup_fast).timeit(number=iters))
241
242    if not skip_slow:
243        print("FastStruct Standard = %f" %
244              timeit.Timer('rs.write(prot)', setup_slow).timeit(number=iters))
245
246    print("FastStruct Acceler. = %f" %
247          timeit.Timer('rs.write(prot)', setup_fast).timeit(number=iters))
248
249
250if __name__ == '__main__':
251    print('Testing TBinaryAccelerated')
252    do_test(TBinaryProtocolAccelerated, TBinaryProtocol)
253    do_benchmark('TBinaryProtocol')
254    print('Testing TCompactAccelerated')
255    do_test(TCompactProtocolAccelerated, TCompactProtocol)
256    do_benchmark('TCompactProtocol')
257