From 1634d7d858f258bf4e465c3ee2267639674afc1f Mon Sep 17 00:00:00 2001 From: Martin Gallwey Date: Mon, 29 Jun 2026 18:04:58 +0100 Subject: [PATCH] Optimize integer serialization with native builtins Optimize wire-protocol integer serialization and deserialization utilities in pynuodb/crypt.py by replacing manual byte-shifting loops with native C-level `int.to_bytes()` and `int.from_bytes()` functions. Detailed changes: - toSignedByteString: Migrated to `int.to_bytes(..., signed=True)`. Uses the expression `((-value - 1).bit_length() + 8) // 8` for negative numbers to precisely calculate the minimal big-endian two's-complement byte-width without over-allocating an extra byte for border cases. - fromSignedByteString: Replaced manual backward bitwise loop parsing with a direct `int.from_bytes(..., signed=True)` call. - toByteString: Streamlined non-negative integer conversions using `int.to_bytes()`. Preserves original byte-identical behavior for 0, -1, and includes an explicit legacy fallback loop for other negative values to prevent unexpected edge cases. These changes eliminate high-overhead Python loops from critical data parsing paths, significantly improving throughput on integer-heavy protocol streams --- pynuodb/crypt.py | 68 +++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/pynuodb/crypt.py b/pynuodb/crypt.py index 028f3a4..11cbb6e 100644 --- a/pynuodb/crypt.py +++ b/pynuodb/crypt.py @@ -142,50 +142,54 @@ def fromHex(hexStr): def toSignedByteString(value): # type: (int) -> bytearray - """Convert an integer into bytes.""" - result = bytearray() - if value == 0 or value == -1: - result.append(value & 0xFF) + """Convert an integer into the minimal big-endian two's-complement bytes. + + Uses int.to_bytes (C-level) instead of a Python byte-shift loop. For + negative values that are not exact -2**k the standard byte-length formula + (bit_length+8)//8 would over-allocate by one; the (-v-1).bit_length() + expression below computes the right minimal width without that case split. + """ + if value == 0: + return bytearray(b'\x00') + if value == -1: + return bytearray(b'\xff') + if value > 0: + nbytes = (value.bit_length() + 8) // 8 else: - while value != 0 and value != -1: - result.append(value & 0xFF) - value >>= 8 - # Zero pad if positive - if value == 0 and (result[-1] & 0x80) == 0x80: - result.append(0x00) - elif value == -1 and (result[-1] & 0x80) == 0x00: - result.append(0xFF) - result.reverse() - return result + nbytes = ((-value - 1).bit_length() + 8) // 8 + return bytearray(value.to_bytes(nbytes, 'big', signed=True)) def fromSignedByteString(data): # type: (bytearray) -> int """Convert bytes into a signed integer.""" - if data: - is_neg = (data[0] & 0x80) >> 7 - else: - is_neg = 0 - result = 0 - shiftCount = 0 - for b in reversed(data): - result = result | (((b & 0xFF) ^ (is_neg * 0xFF)) << shiftCount) - shiftCount += 8 - - return ((-1)**is_neg) * (result + is_neg) + return int.from_bytes(data, 'big', signed=True) def toByteString(bigInt): # type: (int) -> bytearray - """Convert an integer into bytes.""" + """Convert a non-negative integer into the minimal big-endian bytes. + + The legacy implementation also accepted negative inputs and produced a + quirky truncated two's-complement representation, but no current caller + invokes it that way (lengths, scales, message IDs, SRP primes are all + non-negative). We preserve the original behaviour for 0 and -1 (one byte + of 0x00 / 0xff) and fall back to the old algorithm for any other negative + value so external semantics stay byte-identical. + """ + if bigInt == 0: + return bytearray(b'\x00') + if bigInt == -1: + return bytearray(b'\xff') + if bigInt > 0: + nbytes = (bigInt.bit_length() + 7) // 8 + return bytearray(bigInt.to_bytes(nbytes, 'big')) + # Negative-other-than-(-1) fallback (unused in practice). result = bytearray() - if bigInt == -1 or bigInt == 0: + while bigInt != 0 and bigInt != -1: result.append(bigInt & 0xFF) - else: - while bigInt != 0 and bigInt != -1: - result.append(bigInt & 0xFF) - bigInt >>= 8 - result.reverse() + bigInt >>= 8 + result.reverse() return result