Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 12, 2025

📄 49% (0.49x) speedup for decode_hex in python/ccxt/static_dependencies/ethereum/utils/hexadecimal.py

⏱️ Runtime : 132 microseconds 88.3 microseconds (best of 250 runs)

📝 Explanation and details

The optimization achieves a 49% speedup by eliminating function call overhead and inlining critical checks in hot paths.

Key Optimizations Applied:

  1. Inlined is_text() function: Replaced is_text(value) calls with direct isinstance(value, str) checks. The line profiler shows is_text() was called 120 times with significant overhead (48,326 nanoseconds total). This eliminated both the function call and tuple lookup against text_types.

  2. Inlined prefix removal logic: Instead of calling remove_0x_prefix() and is_0x_prefixed(), the optimized code directly checks value.startswith(("0x", "0X")) and performs string slicing inline. This eliminates two nested function calls per decode_hex() invocation.

  3. Removed unnecessary HexStr wrapper: Eliminated the HexStr(value) conversion since HexStr is just a str subclass, avoiding object creation overhead.

Performance Impact Analysis:

The line profiler reveals the optimization targets were well-chosen:

  • Original decode_hex() spent 32.1% of time on is_text() check and 55.6% on remove_0x_prefix()
  • Optimized version eliminates these function calls, reducing total execution time from 866μs to 237μs

Test Case Benefits:

The optimization shows consistent improvements across all test scenarios:

  • Basic hex decoding: 60-80% faster for simple cases
  • Error cases: 20-55% faster even when exceptions are raised
  • Large strings: 30-40% faster for 1000-byte hex strings
  • Edge cases: Significant improvements for empty strings and prefix-only inputs

This optimization is particularly valuable for cryptocurrency libraries like CCXT where hex decoding is frequently performed in transaction processing and address validation workflows.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 63 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime

import binascii

function to test

from typing import Any

imports

import pytest # used for our unit tests
from ccxt.static_dependencies.ethereum.utils.hexadecimal import decode_hex

class HexStr(str):
pass
from ccxt.static_dependencies.ethereum.utils.hexadecimal import decode_hex

unit tests

--------------------

Basic Test Cases

--------------------

def test_decode_hex_basic_lowercase():
# Test decoding a basic lowercase hex string with 0x prefix
codeflash_output = decode_hex('0x68656c6c6f') # 2.40μs -> 1.42μs (69.3% faster)

def test_decode_hex_basic_uppercase():
# Test decoding a basic uppercase hex string with 0X prefix
codeflash_output = decode_hex('0X68656C6C6F') # 2.00μs -> 1.28μs (56.2% faster)

def test_decode_hex_no_prefix():
# Test decoding a hex string without any prefix
codeflash_output = decode_hex('68656c6c6f') # 1.61μs -> 900ns (78.6% faster)

def test_decode_hex_empty_string():
# Test decoding an empty string
codeflash_output = decode_hex('') # 1.55μs -> 940ns (64.6% faster)

def test_decode_hex_single_byte():
# Test decoding a single byte
codeflash_output = decode_hex('0x41') # 1.94μs -> 1.19μs (63.2% faster)

def test_decode_hex_even_length():
# Test decoding an even-length hex string
codeflash_output = decode_hex('0x41424344') # 1.85μs -> 1.09μs (68.7% faster)

def test_decode_hex_odd_length():
# Test decoding an odd-length hex string (should raise error)
with pytest.raises(binascii.Error):
decode_hex('0x123') # 2.40μs -> 1.70μs (41.3% faster)

def test_decode_hex_non_hex_characters():
# Test decoding a string with non-hex characters (should raise error)
with pytest.raises(binascii.Error):
decode_hex('0xZZZZ') # 2.42μs -> 1.67μs (44.9% faster)

def test_decode_hex_mixed_case():
# Test decoding a hex string with mixed case letters
codeflash_output = decode_hex('0xAaBbCcDd') # 1.94μs -> 1.24μs (56.2% faster)

--------------------

Edge Test Cases

--------------------

def test_decode_hex_non_string_input():
# Test passing non-string input (should raise TypeError)
with pytest.raises(TypeError):
decode_hex(12345) # 936ns -> 751ns (24.6% faster)

def test_decode_hex_bytes_input():
# Test passing bytes input (should raise TypeError)
with pytest.raises(TypeError):
decode_hex(b'68656c6c6f') # 876ns -> 692ns (26.6% faster)

def test_decode_hex_none_input():
# Test passing None input (should raise TypeError)
with pytest.raises(TypeError):
decode_hex(None) # 823ns -> 700ns (17.6% faster)

def test_decode_hex_only_prefix():
# Test passing only the prefix (should return empty bytes)
codeflash_output = decode_hex('0x') # 2.41μs -> 1.33μs (80.5% faster)

def test_decode_hex_prefix_and_space():
# Test passing prefix and spaces (should raise error)
with pytest.raises(binascii.Error):
decode_hex('0x ') # 2.50μs -> 1.72μs (45.6% faster)

def test_decode_hex_with_whitespace():
# Test hex with whitespace inside (should raise error)
with pytest.raises(binascii.Error):
decode_hex('0x68 65 6c') # 2.52μs -> 1.74μs (44.6% faster)

def test_decode_hex_with_newline():
# Test hex with newline character (should raise error)
with pytest.raises(binascii.Error):
decode_hex('0x6865\n6c6f') # 2.23μs -> 1.65μs (35.3% faster)

def test_decode_hex_with_leading_trailing_spaces():
# Test hex with leading/trailing spaces (should raise error)
with pytest.raises(binascii.Error):
decode_hex(' 68656c6c6f ') # 2.09μs -> 1.46μs (43.0% faster)

def test_decode_hex_with_special_characters():
# Test hex with special non-hex characters (should raise error)
with pytest.raises(binascii.Error):
decode_hex('0x68$56c6c6f') # 2.31μs -> 1.64μs (41.3% faster)

def test_decode_hex_long_prefix():
# Test hex with more than one "0x" prefix (should treat as part of hex, likely error)
with pytest.raises(binascii.Error):
decode_hex('0x0x68656c6c6f') # 2.35μs -> 1.59μs (48.3% faster)

def test_decode_hex_zero_length():
# Test decoding a zero-length string (should return empty bytes)
codeflash_output = decode_hex('') # 1.58μs -> 988ns (59.7% faster)

def test_decode_hex_prefix_only_uppercase():
# Test decoding only prefix in uppercase (should return empty bytes)
codeflash_output = decode_hex('0X') # 1.90μs -> 1.15μs (65.4% faster)

def test_decode_hex_non_ascii_hex():
# Test decoding a hex string with non-ascii characters (should raise error)
with pytest.raises(UnicodeEncodeError):
decode_hex('0x6865é6c6f') # 3.79μs -> 2.95μs (28.4% faster)

--------------------

Large Scale Test Cases

--------------------

def test_decode_hex_large_even_length():
# Test decoding a large even-length hex string (1000 bytes)
hex_str = '0x' + 'ab' * 1000 # 2000 hex digits, 1000 bytes
expected_bytes = b'\xab' * 1000
codeflash_output = decode_hex(hex_str) # 3.15μs -> 2.42μs (30.1% faster)

def test_decode_hex_large_no_prefix():
# Test decoding a large hex string without prefix
hex_str = 'cd' * 1000
expected_bytes = b'\xcd' * 1000
codeflash_output = decode_hex(hex_str) # 2.45μs -> 1.81μs (35.2% faster)

def test_decode_hex_large_uppercase():
# Test decoding a large uppercase hex string
hex_str = '0X' + 'EF' * 1000
expected_bytes = b'\xef' * 1000
codeflash_output = decode_hex(hex_str) # 2.96μs -> 2.13μs (38.8% faster)

def test_decode_hex_large_mixed_case():
# Test decoding a large mixed-case hex string
hex_str = '0x' + ''.join(['aB' if i % 2 == 0 else 'Cd' for i in range(1000)])
expected_bytes = b''.join([b'\xab' if i % 2 == 0 else b'\xcd' for i in range(1000)])
codeflash_output = decode_hex(hex_str) # 3.00μs -> 2.17μs (38.8% faster)

def test_decode_hex_large_odd_length():
# Test decoding a large odd-length hex string (should raise error)
hex_str = '0x' + 'ab' * 999 + 'a' # 19992+1 = 19992+1 = 19992+1 = 19992+1 = 1999*2+1 = odd
with pytest.raises(binascii.Error):
decode_hex(hex_str) # 2.73μs -> 1.86μs (46.8% faster)

def test_decode_hex_large_non_hex():
# Test decoding a large string with a non-hex character in the middle (should raise error)
hex_str = '0x' + 'ab' * 500 + 'ZZ' + 'cd' * 499
with pytest.raises(binascii.Error):
decode_hex(hex_str) # 3.07μs -> 2.25μs (36.7% faster)

def test_decode_hex_large_with_spaces():
# Test decoding a large string with spaces (should raise error)
hex_str = '0x' + ('ab ' * 500)
with pytest.raises(binascii.Error):
decode_hex(hex_str.strip()) # 2.48μs -> 1.65μs (50.8% faster)

def test_decode_hex_large_with_newline():
# Test decoding a large string with a newline (should raise error)
hex_str = '0x' + ('ab' * 500) + '\n' + ('cd' * 499)
with pytest.raises(binascii.Error):
decode_hex(hex_str) # 2.38μs -> 1.73μs (37.0% faster)

codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

#------------------------------------------------
import binascii

function to test

from typing import Any

imports

import pytest # used for our unit tests
from ccxt.static_dependencies.ethereum.utils.hexadecimal import decode_hex

class HexStr(str):
pass
from ccxt.static_dependencies.ethereum.utils.hexadecimal import decode_hex

unit tests

--- Basic Test Cases ---

def test_decode_hex_basic_lowercase():
# Test decoding a simple lowercase hex string
codeflash_output = decode_hex("68656c6c6f") # 1.65μs -> 1.01μs (63.4% faster)

def test_decode_hex_basic_uppercase():
# Test decoding a simple uppercase hex string
codeflash_output = decode_hex("48656C6C6F") # 1.51μs -> 842ns (78.9% faster)

def test_decode_hex_with_0x_prefix_lowercase():
# Test decoding a hex string with '0x' prefix (lowercase)
codeflash_output = decode_hex("0x68656c6c6f") # 1.93μs -> 1.16μs (66.7% faster)

def test_decode_hex_with_0x_prefix_uppercase():
# Test decoding a hex string with '0X' prefix (uppercase)
codeflash_output = decode_hex("0X68656c6c6f") # 1.88μs -> 1.17μs (60.5% faster)

def test_decode_hex_with_mixed_case():
# Test decoding a hex string with mixed case
codeflash_output = decode_hex("0x48656c6c6F") # 1.79μs -> 1.05μs (70.6% faster)

def test_decode_hex_empty_string():
# Test decoding an empty string
codeflash_output = decode_hex("") # 1.50μs -> 847ns (76.7% faster)

def test_decode_hex_empty_string_with_0x():
# Test decoding an empty string with '0x' prefix
codeflash_output = decode_hex("0x") # 1.80μs -> 1.03μs (75.8% faster)

def test_decode_hex_single_byte():
# Test decoding a single byte
codeflash_output = decode_hex("ff") # 1.54μs -> 930ns (65.4% faster)

def test_decode_hex_single_byte_with_0x():
# Test decoding a single byte with '0x' prefix
codeflash_output = decode_hex("0xff") # 1.88μs -> 1.14μs (65.3% faster)

--- Edge Test Cases ---

def test_decode_hex_non_str_input_int():
# Test that non-str input raises TypeError (int)
with pytest.raises(TypeError):
decode_hex(123) # 995ns -> 747ns (33.2% faster)

def test_decode_hex_non_str_input_bytes():
# Test that non-str input raises TypeError (bytes)
with pytest.raises(TypeError):
decode_hex(b"68656c6c6f") # 874ns -> 716ns (22.1% faster)

def test_decode_hex_non_str_input_list():
# Test that non-str input raises TypeError (list)
with pytest.raises(TypeError):
decode_hex(["68", "65", "6c", "6c", "6f"]) # 1.05μs -> 670ns (57.5% faster)

def test_decode_hex_odd_length_string():
# Test decoding a hex string of odd length should raise binascii.Error
with pytest.raises(binascii.Error):
decode_hex("abc") # 2.39μs -> 1.55μs (54.3% faster)

def test_decode_hex_odd_length_with_0x():
# Test decoding an odd length hex string with '0x' prefix should raise binascii.Error
with pytest.raises(binascii.Error):
decode_hex("0xabc") # 2.46μs -> 1.62μs (51.7% faster)

def test_decode_hex_non_hex_characters():
# Test decoding a string with non-hex characters should raise binascii.Error
with pytest.raises(binascii.Error):
decode_hex("zzzz") # 2.14μs -> 1.48μs (44.3% faster)

def test_decode_hex_non_hex_characters_with_0x():
# Test decoding a string with non-hex characters and '0x' prefix should raise binascii.Error
with pytest.raises(binascii.Error):
decode_hex("0xzzzz") # 2.40μs -> 1.61μs (48.6% faster)

def test_decode_hex_zero_length_with_0x():
# Test decoding zero-length string with '0x' prefix
codeflash_output = decode_hex("0x") # 1.94μs -> 1.16μs (67.7% faster)

def test_decode_hex_zero_length_with_0X():
# Test decoding zero-length string with '0X' prefix
codeflash_output = decode_hex("0X") # 1.78μs -> 1.09μs (63.4% faster)

def test_decode_hex_leading_trailing_spaces():
# Test decoding a hex string with spaces should raise binascii.Error
with pytest.raises(binascii.Error):
decode_hex(" 68656c6c6f ") # 2.00μs -> 1.40μs (42.7% faster)

def test_decode_hex_with_newline():
# Test decoding a hex string with newline should raise binascii.Error
with pytest.raises(binascii.Error):
decode_hex("68656c6c6f\n") # 2.00μs -> 1.34μs (48.9% faster)

def test_decode_hex_with_special_characters():
# Test decoding a hex string with special characters should raise binascii.Error
with pytest.raises(binascii.Error):
decode_hex("0x68!56c6c6f") # 2.42μs -> 1.55μs (55.9% faster)

def test_decode_hex_with_only_0x():
# Test decoding a string that is only '0x'
codeflash_output = decode_hex("0x") # 1.88μs -> 1.10μs (70.8% faster)

def test_decode_hex_with_only_0X():
# Test decoding a string that is only '0X'
codeflash_output = decode_hex("0X") # 1.82μs -> 1.05μs (73.3% faster)

def test_decode_hex_with_leading_zeros():
# Test decoding a hex string with leading zeros
codeflash_output = decode_hex("000001") # 1.58μs -> 964ns (63.6% faster)

def test_decode_hex_with_leading_zeros_and_0x():
# Test decoding a hex string with leading zeros and '0x' prefix
codeflash_output = decode_hex("0x000001") # 1.91μs -> 1.08μs (75.9% faster)

def test_decode_hex_with_trailing_zeros():
# Test decoding a hex string with trailing zeros
codeflash_output = decode_hex("010000") # 1.45μs -> 922ns (56.7% faster)

--- Large Scale Test Cases ---

def test_decode_hex_large_string():
# Test decoding a large hex string (1000 bytes)
hex_str = "aa" * 1000 # 1000 bytes of 0xaa
expected = b"\xaa" * 1000
codeflash_output = decode_hex(hex_str) # 2.51μs -> 1.85μs (35.2% faster)

def test_decode_hex_large_string_with_0x():
# Test decoding a large hex string (1000 bytes) with '0x' prefix
hex_str = "0x" + ("aa" * 1000)
expected = b"\xaa" * 1000
codeflash_output = decode_hex(hex_str) # 3.07μs -> 2.33μs (32.0% faster)

def test_decode_hex_large_random_bytes():
# Test decoding a large hex string of random bytes
import random
random_bytes = bytes(random.getrandbits(8) for _ in range(1000))
hex_str = random_bytes.hex()
codeflash_output = decode_hex(hex_str) # 2.48μs -> 1.82μs (36.5% faster)

def test_decode_hex_large_random_bytes_with_0x():
# Test decoding a large hex string of random bytes with '0x' prefix
import random
random_bytes = bytes(random.getrandbits(8) for _ in range(1000))
hex_str = "0x" + random_bytes.hex()
codeflash_output = decode_hex(hex_str) # 3.02μs -> 2.20μs (37.5% faster)

def test_decode_hex_large_string_with_uppercase():
# Test decoding a large uppercase hex string (1000 bytes)
hex_str = "FF" * 1000
expected = b"\xff" * 1000
codeflash_output = decode_hex(hex_str) # 2.51μs -> 1.83μs (37.1% faster)

def test_decode_hex_large_string_with_mixed_case():
# Test decoding a large mixed-case hex string (1000 bytes)
hex_str = "".join(["aA" if i % 2 == 0 else "Bb" for i in range(500)])
expected = b"".join([b"\xaa" if i % 2 == 0 else b"\xbb" for i in range(500)])
codeflash_output = decode_hex(hex_str) # 2.07μs -> 1.36μs (52.8% faster)

def test_decode_hex_large_string_with_0x_and_mixed_case():
# Test decoding a large mixed-case hex string (1000 bytes) with '0x' prefix
hex_str = "0x" + "".join(["aA" if i % 2 == 0 else "Bb" for i in range(500)])
expected = b"".join([b"\xaa" if i % 2 == 0 else b"\xbb" for i in range(500)])
codeflash_output = decode_hex(hex_str) # 2.70μs -> 1.86μs (44.7% faster)

codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-decode_hex-mhw6e57e and push.

Codeflash Static Badge

The optimization achieves a **49% speedup** by eliminating function call overhead and inlining critical checks in hot paths.

**Key Optimizations Applied:**

1. **Inlined `is_text()` function**: Replaced `is_text(value)` calls with direct `isinstance(value, str)` checks. The line profiler shows `is_text()` was called 120 times with significant overhead (48,326 nanoseconds total). This eliminated both the function call and tuple lookup against `text_types`.

2. **Inlined prefix removal logic**: Instead of calling `remove_0x_prefix()` and `is_0x_prefixed()`, the optimized code directly checks `value.startswith(("0x", "0X"))` and performs string slicing inline. This eliminates two nested function calls per `decode_hex()` invocation.

3. **Removed unnecessary `HexStr` wrapper**: Eliminated the `HexStr(value)` conversion since `HexStr` is just a `str` subclass, avoiding object creation overhead.

**Performance Impact Analysis:**

The line profiler reveals the optimization targets were well-chosen:
- Original `decode_hex()` spent 32.1% of time on `is_text()` check and 55.6% on `remove_0x_prefix()` 
- Optimized version eliminates these function calls, reducing total execution time from 866μs to 237μs

**Test Case Benefits:**

The optimization shows consistent improvements across all test scenarios:
- **Basic hex decoding**: 60-80% faster for simple cases
- **Error cases**: 20-55% faster even when exceptions are raised
- **Large strings**: 30-40% faster for 1000-byte hex strings
- **Edge cases**: Significant improvements for empty strings and prefix-only inputs

This optimization is particularly valuable for cryptocurrency libraries like CCXT where hex decoding is frequently performed in transaction processing and address validation workflows.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 12, 2025 15:49
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Nov 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant