Skip to content

Commit

Permalink
Add Clear() for python Builder
Browse files Browse the repository at this point in the history
  • Loading branch information
razvanalex committed Dec 12, 2023
1 parent 5ba80c2 commit 6a6dce1
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 16 deletions.
14 changes: 14 additions & 0 deletions python/flatbuffers/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,20 @@ def __init__(self, initialSize=1024):
## @endcond
self.finished = False

def Clear(self) -> None:
## @cond FLATBUFFERS_INTERNAL
self.current_vtable = None
self.head = UOffsetTFlags.py_type(len(self.Bytes))
self.minalign = 1
self.objectEnd = None
self.vtables = {}
self.nested = False
self.forceDefaults = False
self.sharedStrings = {}
self.vectorNumElems = None
## @endcond
self.finished = False

def Output(self):
"""Return the portion of the buffer that has been used for writing data.
Expand Down
16 changes: 8 additions & 8 deletions tests/PythonTest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function run_tests() {
JYTHONPATH=${runtime_library_dir}:${gen_code_path} \
COMPARE_GENERATED_TO_GO=0 \
COMPARE_GENERATED_TO_JAVA=0 \
$1 py_test.py $2 $3 $4 $5
$1 py_test.py $2 $3 $4 $5 $6
if [ $1 = python3 ]; then
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=${runtime_library_dir}:${gen_code_path} \
Expand All @@ -52,12 +52,12 @@ function run_tests() {
}

# Run test suite with these interpreters. The arguments are benchmark counts.
run_tests python2.6 100 100 100 false
run_tests python2.7 100 100 100 false
run_tests python2.7 100 100 100 true
run_tests python3 100 100 100 false
run_tests python3 100 100 100 true
run_tests pypy 100 100 100 false
run_tests python2.6 100 100 100 100 false
run_tests python2.7 100 100 100 100 false
run_tests python2.7 100 100 100 100 true
run_tests python3 100 100 100 100 false
run_tests python3 100 100 100 100 true
run_tests pypy 100 100 100 100 false

# NOTE: We'd like to support python2.5 in the future.

Expand All @@ -77,7 +77,7 @@ if $(which coverage >/dev/null); then

PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=${runtime_library_dir}:${gen_code_path} \
coverage run --source=flatbuffers,MyGame py_test.py 0 0 0 false > /dev/null
coverage run --source=flatbuffers,MyGame py_test.py 0 0 0 0 false > /dev/null

echo
cov_result=`coverage report --omit="*flatbuffers/vendor*,*py_test*" \
Expand Down
68 changes: 60 additions & 8 deletions tests/py_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2017,10 +2017,10 @@ def test_some_floats(self):
])


def make_monster_from_generated_code(sizePrefix=False, file_identifier=None):
def make_monster_from_generated_code(b=None, sizePrefix=False, file_identifier=None):
""" Use generated code to build the example Monster. """

b = flatbuffers.Builder(0)
if b is None:
b = flatbuffers.Builder(0)
string = b.CreateString('MyMonster')
test1 = b.CreateString('test1')
test2 = b.CreateString('test2')
Expand Down Expand Up @@ -2767,6 +2767,43 @@ def test_nested_union_tables(self):
self.assertEqual(nestUnionDecodeTFromBuf2.data.test3.b, nestUnion.data.test3.b)


class TestBuilderClear(unittest.TestCase):

def test_consistency(self):
""" Checks if clear resets the state of the builder. """
b = flatbuffers.Builder(0)

# Add some data to the buffer
off1 = b.CreateString('a' * 1024)
want = b.Bytes[b.Head():]

# Reset the builder
b.Clear()

# Readd the same data into the buffer
off2 = b.CreateString('a' * 1024)
got = b.Bytes[b.Head():]

# Expect to get the same data into the buffer at the same offset
self.assertEqual(off1, off2)
self.assertEqual(want, got)

def test_repeated_clear_after_builder_reuse(self):
init_buf = None
init_off = None
b = flatbuffers.Builder(0)

for i in range(5):
buf, off = make_monster_from_generated_code(b)
b.Clear()

if i > 0:
self.assertEqual(init_buf, buf)
self.assertEqual(init_off, off)
else:
init_buf = buf
init_off = off

def CheckAgainstGoldDataGo():
try:
gen_buf, gen_off = make_monster_from_generated_code()
Expand Down Expand Up @@ -2912,6 +2949,18 @@ def BenchmarkMakeMonsterFromGeneratedCode(count, length):
(count, length, duration, rate, data_rate)))


def BenchmarkBuilderClear(count, length):
b = flatbuffers.Builder(length)
duration = timeit.timeit(stmt=lambda: make_monster_from_generated_code(b),
number=count)
rate = float(count) / duration
data = float(length * count) / float(1024 * 1024)
data_rate = data / float(duration)

print(('built %d %d-byte flatbuffers (reused buffer) in %.2fsec:'
' %.2f/sec, %.2fMB/sec' % (count, length, duration, rate, data_rate)))


def backward_compatible_run_tests(**kwargs):
if PY_VERSION < (2, 6):
sys.stderr.write('Python version less than 2.6 are not supported')
Expand Down Expand Up @@ -2940,20 +2989,20 @@ def backward_compatible_run_tests(**kwargs):
def main():
import os
import sys
if not len(sys.argv) == 5:
if not len(sys.argv) == 6:
sys.stderr.write('Usage: %s <benchmark vtable count> '
'<benchmark read count> <benchmark build count> '
'<is_onefile>\n' % sys.argv[0])
'<benchmark clear builder> <is_onefile>\n' % sys.argv[0])
sys.stderr.write(' Provide COMPARE_GENERATED_TO_GO=1 to check'
'for bytewise comparison to Go data.\n')
sys.stderr.write(' Provide COMPARE_GENERATED_TO_JAVA=1 to check'
'for bytewise comparison to Java data.\n')
sys.stderr.flush()
sys.exit(1)

kwargs = dict(argv=sys.argv[:-4])
kwargs = dict(argv=sys.argv[:-5])

create_namespace_shortcut(sys.argv[4].lower() == 'true')
create_namespace_shortcut(sys.argv[5].lower() == 'true')

# show whether numpy is present, as it changes the test logic:
try:
Expand All @@ -2978,6 +3027,7 @@ def main():
bench_vtable = int(sys.argv[1])
bench_traverse = int(sys.argv[2])
bench_build = int(sys.argv[3])
bench_clear = int(sys.argv[4])
if bench_vtable:
BenchmarkVtableDeduplication(bench_vtable)
if bench_traverse:
Expand All @@ -2986,7 +3036,9 @@ def main():
if bench_build:
buf, off = make_monster_from_generated_code()
BenchmarkMakeMonsterFromGeneratedCode(bench_build, len(buf))

if bench_clear:
buf, off = make_monster_from_generated_code()
BenchmarkBuilderClear(bench_build, len(buf))

if __name__ == '__main__':
main()

0 comments on commit 6a6dce1

Please sign in to comment.