cchecksum 0.4.0__cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ from eth_typing import AnyAddress, ChecksumAddress, HexAddress
2
+ from typing import Iterable, List, Union
3
+
4
+ def to_checksum_address(value: Union[AnyAddress, str, bytes]) -> ChecksumAddress:
5
+ """
6
+ Convert an address to its EIP-55 checksum format.
7
+
8
+ This function takes an address in any supported format and returns it in the
9
+ checksummed format as defined by EIP-55. It uses a custom Cython implementation
10
+ for the checksum conversion to optimize performance.
11
+
12
+ Args:
13
+ value: The address to be converted. It can be in any format supported by
14
+ :func:`eth_utils.to_normalized_address`.
15
+
16
+ Raises:
17
+ ValueError: If the input address is not in a recognized format.
18
+ TypeError: If the input is not a string, bytes, or any address type.
19
+
20
+ Examples:
21
+ >>> to_checksum_address("0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb")
22
+ '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB'
23
+
24
+ >>> to_checksum_address(b'\xb4~<\xd87\xdd\xf8\xe4\xc5\x7f\x05\xd7\n\xb8e\xden\x19;\xbb')
25
+ '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB'
26
+
27
+ See Also:
28
+ - :func:`eth_utils.to_checksum_address` for the standard implementation.
29
+ - :func:`to_normalized_address` for converting to a normalized address before checksumming.
30
+ """
31
+
32
+ def to_checksum_address_many(
33
+ values: Union[
34
+ bytes,
35
+ bytearray,
36
+ memoryview,
37
+ Iterable[Union[AnyAddress, str, bytes, bytearray]],
38
+ ],
39
+ ) -> List[ChecksumAddress]:
40
+ """
41
+ Convert multiple addresses to EIP-55 checksum format.
42
+
43
+ Accepts a sequence of address-like inputs (str/bytes/bytearray) or a packed
44
+ bytes-like object containing concatenated 20-byte addresses.
45
+ """
46
+
47
+ def to_normalized_address_no_0x(value: Union[AnyAddress, str, bytes]) -> bytes:
48
+ """
49
+ Converts an address to its normalized hexadecimal representation without the '0x' prefix.
50
+
51
+ This function ensures that the address is in a consistent lowercase hexadecimal
52
+ format, which is useful for further processing or validation.
53
+
54
+ Args:
55
+ value: The address to be normalized.
56
+
57
+ Raises:
58
+ ValueError: If the input address is not in a recognized format.
59
+ TypeError: If the input is not a string, bytes, or any address type.
60
+
61
+ Examples:
62
+ >>> to_normalized_address("0xB47E3CD837DDF8E4C57F05D70AB865DE6E193BBB")
63
+ '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb'
64
+
65
+ >>> to_normalized_address(b'\xb4~<\xd87\xdd\xf8\xe4\xc5\x7f\x05\xd7\n\xb8e\xden\x19;\xbb')
66
+ '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb'
67
+
68
+ See Also:
69
+ - :func:`eth_utils.to_normalized_address` for the standard implementation.
70
+ """
71
+
72
+ def cchecksum(norm_address_no_0x: bytes, address_hash_hex_no_0x: bytes) -> ChecksumAddress: ...
@@ -0,0 +1,549 @@
1
+ # cython: boundscheck=False
2
+ # cython: wraparound=False
3
+
4
+ from cpython.bytes cimport PyBytes_GET_SIZE
5
+ from cpython.sequence cimport PySequence_Fast, PySequence_Fast_GET_ITEM, PySequence_Fast_GET_SIZE
6
+ from cpython.unicode cimport PyUnicode_AsEncodedString, PyUnicode_DecodeASCII
7
+ from cython.parallel cimport prange
8
+ from libc.stddef cimport size_t
9
+ from libc.string cimport memcpy
10
+
11
+ from eth_typing import AnyAddress, ChecksumAddress
12
+
13
+
14
+ cdef const unsigned char* hexdigits = b"0123456789abcdef"
15
+
16
+
17
+ # this was ripped out of eth_utils and optimized a little bit
18
+
19
+ cdef extern from "keccak.h":
20
+ void keccak_256(const unsigned char* data, size_t len, unsigned char* out) nogil
21
+
22
+
23
+ cpdef unicode to_checksum_address(value: Union[AnyAddress, str, bytes]):
24
+ """
25
+ Convert an address to its EIP-55 checksum format.
26
+
27
+ This function takes an address in any supported format and returns it in the
28
+ checksummed format as defined by EIP-55. It uses a custom Cython implementation
29
+ for the checksum conversion to optimize performance.
30
+
31
+ Args:
32
+ value: The address to be converted. It can be in any format supported by
33
+ :func:`eth_utils.to_normalized_address`.
34
+
35
+ Raises:
36
+ ValueError: If the input address is not in a recognized format.
37
+ TypeError: If the input is not a string, bytes, or any address type.
38
+
39
+ Examples:
40
+ >>> to_checksum_address("0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb")
41
+ '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB'
42
+
43
+ >>> to_checksum_address(b'\xb4~<\xd87\xdd\xf8\xe4\xc5\x7f\x05\xd7\n\xb8e\xden\x19;\xbb')
44
+ '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB'
45
+
46
+ See Also:
47
+ - :func:`eth_utils.to_checksum_address` for the standard implementation.
48
+ - :func:`to_normalized_address` for converting to a normalized address before checksumming.
49
+ """
50
+ cdef bytes hex_address_bytes
51
+ cdef const unsigned char* hex_address_bytestr
52
+ cdef unsigned char c
53
+ cdef unsigned char hash_out[32]
54
+
55
+ cdef unsigned char hash_buffer[80]
56
+
57
+ # Create a buffer for our result
58
+ # 2 for "0x" prefix and 40 for the address itself
59
+ cdef char result_buffer[42]
60
+ result_buffer[0] = 48 # '0'
61
+ result_buffer[1] = 120 # 'x'
62
+
63
+ if isinstance(value, str):
64
+ hex_address_bytes = lowercase_ascii_and_validate(PyUnicode_AsEncodedString(value, b"ascii", NULL))
65
+ hex_address_bytestr = hex_address_bytes
66
+
67
+ elif isinstance(value, (bytes, bytearray)):
68
+ hex_address_bytes = hexlify(value)
69
+ hex_address_bytestr = hex_address_bytes
70
+ num_bytes = PyBytes_GET_SIZE(hex_address_bytes)
71
+
72
+ with nogil:
73
+ for i in range(num_bytes):
74
+ c = hex_address_bytestr[i]
75
+
76
+ if not is_hex_lower(c):
77
+ raise ValueError(
78
+ f"Unknown format {repr(value)}, attempted to normalize to '0x{hex_address_bytes.decode()}'"
79
+ )
80
+
81
+ else:
82
+ raise TypeError(
83
+ f"Unsupported type: '{repr(type(value))}'. Must be one of: bool, str, bytes, bytearray or int."
84
+ )
85
+
86
+ if PyBytes_GET_SIZE(hex_address_bytes) != 40:
87
+ raise ValueError(
88
+ f"Unknown format {repr(value)}, attempted to normalize to '0x{hex_address_bytes.decode()}'"
89
+ )
90
+
91
+ with nogil:
92
+ keccak_256(hex_address_bytestr, 40, hash_out)
93
+ hexlify_c_string_to_buffer(hash_out, hash_buffer, 32)
94
+ populate_result_buffer(result_buffer, hex_address_bytestr, hash_buffer)
95
+
96
+ # It is faster to decode a buffer with a known size ie buffer[:42]
97
+ return result_buffer[:42].decode('ascii')
98
+
99
+
100
+ cpdef list to_checksum_address_many(object values):
101
+ """
102
+ Convert multiple addresses to EIP-55 checksum format.
103
+
104
+ Accepts a sequence of address-like inputs (str/bytes/bytearray) or a packed
105
+ bytes-like object containing concatenated 20-byte addresses.
106
+ """
107
+ cdef Py_ssize_t i, n, packed_len
108
+ cdef bytes hex_address_bytes
109
+ cdef const unsigned char* hex_address_bytestr
110
+ cdef object item
111
+ cdef object seq
112
+ cdef const unsigned char[:] packed_view
113
+ cdef const unsigned char* packed_ptr
114
+ cdef unsigned char* norm_ptr
115
+ cdef unsigned char* hash_ptr
116
+ cdef char* result_ptr
117
+ cdef bytearray norm_hex
118
+ cdef bytearray hashes
119
+ cdef bytearray results
120
+ cdef list output
121
+
122
+ if isinstance(values, (bytes, bytearray, memoryview)):
123
+ packed_view = values
124
+
125
+ if packed_view.ndim != 1 or packed_view.itemsize != 1 or packed_view.strides[0] != 1:
126
+ raise TypeError("Packed addresses must be a contiguous 1-D view of bytes.")
127
+
128
+ packed_len = packed_view.shape[0]
129
+ if packed_len % 20 != 0:
130
+ raise ValueError("Packed address bytes length must be a multiple of 20.")
131
+
132
+ n = packed_len // 20
133
+ if n == 0:
134
+ return []
135
+
136
+ norm_hex = bytearray(n * 40)
137
+ hashes = bytearray(n * 32)
138
+ results = bytearray(n * 42)
139
+ norm_ptr = norm_hex
140
+ hash_ptr = hashes
141
+ result_ptr = results
142
+ packed_ptr = &packed_view[0]
143
+
144
+ with nogil:
145
+ for i in range(n):
146
+ hexlify_c_string_to_buffer(packed_ptr + (i * 20), norm_ptr + (i * 40), 20)
147
+
148
+ for i in prange(n, schedule="static"):
149
+ keccak_256(norm_ptr + (i * 40), 40, hash_ptr + (i * 32))
150
+ checksum_address_to_buffer(
151
+ result_ptr + (i * 42),
152
+ norm_ptr + (i * 40),
153
+ hash_ptr + (i * 32),
154
+ )
155
+
156
+ output = [None] * n
157
+ for i in range(n):
158
+ output[i] = PyUnicode_DecodeASCII(result_ptr + (i * 42), 42, NULL)
159
+ return output
160
+
161
+ if isinstance(values, str):
162
+ raise TypeError("to_checksum_address_many expects a sequence of addresses, not a str.")
163
+
164
+ seq = PySequence_Fast(values, "to_checksum_address_many expects a sequence of addresses.")
165
+ n = PySequence_Fast_GET_SIZE(seq)
166
+ if n == 0:
167
+ return []
168
+
169
+ norm_hex = bytearray(n * 40)
170
+ hashes = bytearray(n * 32)
171
+ results = bytearray(n * 42)
172
+ norm_ptr = norm_hex
173
+ hash_ptr = hashes
174
+ result_ptr = results
175
+
176
+ for i in range(n):
177
+ item = <object>PySequence_Fast_GET_ITEM(seq, i)
178
+
179
+ if isinstance(item, str):
180
+ hex_address_bytes = lowercase_ascii_and_validate(PyUnicode_AsEncodedString(item, b"ascii", NULL))
181
+ hex_address_bytestr = hex_address_bytes
182
+ elif isinstance(item, (bytes, bytearray)):
183
+ hex_address_bytes = hexlify(item)
184
+ hex_address_bytestr = hex_address_bytes
185
+ else:
186
+ raise TypeError(
187
+ f"Unsupported type: '{repr(type(item))}'. Must be one of: bool, str, bytes, bytearray or int."
188
+ )
189
+
190
+ if PyBytes_GET_SIZE(hex_address_bytes) != 40:
191
+ raise ValueError(
192
+ f"Unknown format {repr(item)}, attempted to normalize to '0x{hex_address_bytes.decode()}'"
193
+ )
194
+
195
+ memcpy(norm_ptr + (i * 40), hex_address_bytestr, 40)
196
+
197
+ with nogil:
198
+ for i in prange(n, schedule="static"):
199
+ keccak_256(norm_ptr + (i * 40), 40, hash_ptr + (i * 32))
200
+ checksum_address_to_buffer(
201
+ result_ptr + (i * 42),
202
+ norm_ptr + (i * 40),
203
+ hash_ptr + (i * 32),
204
+ )
205
+
206
+ output = [None] * n
207
+ for i in range(n):
208
+ output[i] = PyUnicode_DecodeASCII(result_ptr + (i * 42), 42, NULL)
209
+ return output
210
+
211
+
212
+ cpdef bytes hexlify(const unsigned char[:] src_buffer):
213
+ return bytes(hexlify_unsafe(src_buffer, len(src_buffer)))
214
+
215
+
216
+ cdef const unsigned char[:] hexlify_unsafe(const unsigned char[:] src_buffer, Py_ssize_t num_bytes) noexcept:
217
+ """Make sure your `num_bytes` is correct or ting go boom"""
218
+ cdef unsigned char[:] result_buffer = bytearray(num_bytes * 2) # contiguous and writeable
219
+ with nogil:
220
+ hexlify_memview_to_buffer(src_buffer, result_buffer, num_bytes)
221
+ return result_buffer
222
+
223
+
224
+ cdef inline void hexlify_memview_to_buffer(
225
+ const unsigned char[:] src_buffer,
226
+ unsigned char[:] result_buffer,
227
+ Py_ssize_t num_bytes,
228
+ ) noexcept nogil:
229
+ cdef Py_ssize_t i
230
+ cdef unsigned char c
231
+ for i in range(num_bytes):
232
+ c = src_buffer[i]
233
+ result_buffer[2*i] = hexdigits[c >> 4]
234
+ result_buffer[2*i+1] = hexdigits[c & 0x0F]
235
+
236
+
237
+ cdef inline void hexlify_c_string_to_buffer(
238
+ const unsigned char* src_buffer,
239
+ unsigned char* result_buffer,
240
+ Py_ssize_t num_bytes,
241
+ ) noexcept nogil:
242
+ cdef Py_ssize_t i
243
+ cdef unsigned char c
244
+ for i in range(num_bytes):
245
+ c = src_buffer[i]
246
+ result_buffer[2*i] = hexdigits[c >> 4]
247
+ result_buffer[2*i+1] = hexdigits[c & 0x0F]
248
+
249
+
250
+ cdef inline void checksum_address_to_buffer(
251
+ char* buffer,
252
+ const unsigned char* norm_address_no_0x,
253
+ const unsigned char* address_hash_bytes,
254
+ ) noexcept nogil:
255
+ cdef Py_ssize_t i
256
+ cdef unsigned char hash_byte
257
+ cdef unsigned char hash_nibble
258
+ cdef unsigned char c
259
+
260
+ buffer[0] = 48 # '0'
261
+ buffer[1] = 120 # 'x'
262
+
263
+ for i in range(40):
264
+ c = norm_address_no_0x[i]
265
+ hash_byte = address_hash_bytes[i >> 1]
266
+ if i & 1:
267
+ hash_nibble = hash_byte & 0x0F
268
+ else:
269
+ hash_nibble = hash_byte >> 4
270
+ if hash_nibble < 8:
271
+ buffer[i + 2] = c
272
+ else:
273
+ buffer[i + 2] = get_char(c)
274
+
275
+
276
+ cdef void populate_result_buffer(
277
+ char[42] buffer,
278
+ const unsigned char* norm_address_no_0x,
279
+ const unsigned char* address_hash_hex_no_0x,
280
+ ) noexcept nogil:
281
+ """
282
+ Computes the checksummed version of an Ethereum address.
283
+
284
+ This function takes a normalized Ethereum address (without the '0x' prefix) and its corresponding
285
+ hash (also without the '0x' prefix) and returns the checksummed address as per the Ethereum
286
+ Improvement Proposal 55 (EIP-55).
287
+
288
+ Args:
289
+ norm_address_no_0x: The normalized Ethereum address without the '0x' prefix.
290
+ address_hash_hex_no_0x: The hash of the address, also without the '0x' prefix.
291
+
292
+ Returns:
293
+ The checksummed Ethereum address with the '0x' prefix.
294
+
295
+ See Also:
296
+ - :func:`eth_utils.to_checksum_address`: A utility function for converting addresses to their checksummed form.
297
+ """
298
+ # Handle character casing based on the hash value
299
+ # `if address_hash_hex_no_0x[x] < 56`
300
+ # '0' to '7' have ASCII values 48 to 55
301
+ if address_hash_hex_no_0x[0] < 56:
302
+ buffer[2] = norm_address_no_0x[0]
303
+ else:
304
+ buffer[2] = get_char(norm_address_no_0x[0])
305
+ if address_hash_hex_no_0x[1] < 56:
306
+ buffer[3] = norm_address_no_0x[1]
307
+ else:
308
+ buffer[3] = get_char(norm_address_no_0x[1])
309
+ if address_hash_hex_no_0x[2] < 56:
310
+ buffer[4] = norm_address_no_0x[2]
311
+ else:
312
+ buffer[4] = get_char(norm_address_no_0x[2])
313
+ if address_hash_hex_no_0x[3] < 56:
314
+ buffer[5] = norm_address_no_0x[3]
315
+ else:
316
+ buffer[5] = get_char(norm_address_no_0x[3])
317
+ if address_hash_hex_no_0x[4] < 56:
318
+ buffer[6] = norm_address_no_0x[4]
319
+ else:
320
+ buffer[6] = get_char(norm_address_no_0x[4])
321
+ if address_hash_hex_no_0x[5] < 56:
322
+ buffer[7] = norm_address_no_0x[5]
323
+ else:
324
+ buffer[7] = get_char(norm_address_no_0x[5])
325
+ if address_hash_hex_no_0x[6] < 56:
326
+ buffer[8] = norm_address_no_0x[6]
327
+ else:
328
+ buffer[8] = get_char(norm_address_no_0x[6])
329
+ if address_hash_hex_no_0x[7] < 56:
330
+ buffer[9] = norm_address_no_0x[7]
331
+ else:
332
+ buffer[9] = get_char(norm_address_no_0x[7])
333
+ if address_hash_hex_no_0x[8] < 56:
334
+ buffer[10] = norm_address_no_0x[8]
335
+ else:
336
+ buffer[10] = get_char(norm_address_no_0x[8])
337
+ if address_hash_hex_no_0x[9] < 56:
338
+ buffer[11] = norm_address_no_0x[9]
339
+ else:
340
+ buffer[11] = get_char(norm_address_no_0x[9])
341
+ if address_hash_hex_no_0x[10] < 56:
342
+ buffer[12] = norm_address_no_0x[10]
343
+ else:
344
+ buffer[12] = get_char(norm_address_no_0x[10])
345
+ if address_hash_hex_no_0x[11] < 56:
346
+ buffer[13] = norm_address_no_0x[11]
347
+ else:
348
+ buffer[13] = get_char(norm_address_no_0x[11])
349
+ if address_hash_hex_no_0x[12] < 56:
350
+ buffer[14] = norm_address_no_0x[12]
351
+ else:
352
+ buffer[14] = get_char(norm_address_no_0x[12])
353
+ if address_hash_hex_no_0x[13] < 56:
354
+ buffer[15] = norm_address_no_0x[13]
355
+ else:
356
+ buffer[15] = get_char(norm_address_no_0x[13])
357
+ if address_hash_hex_no_0x[14] < 56:
358
+ buffer[16] = norm_address_no_0x[14]
359
+ else:
360
+ buffer[16] = get_char(norm_address_no_0x[14])
361
+ if address_hash_hex_no_0x[15] < 56:
362
+ buffer[17] = norm_address_no_0x[15]
363
+ else:
364
+ buffer[17] = get_char(norm_address_no_0x[15])
365
+ if address_hash_hex_no_0x[16] < 56:
366
+ buffer[18] = norm_address_no_0x[16]
367
+ else:
368
+ buffer[18] = get_char(norm_address_no_0x[16])
369
+ if address_hash_hex_no_0x[17] < 56:
370
+ buffer[19] = norm_address_no_0x[17]
371
+ else:
372
+ buffer[19] = get_char(norm_address_no_0x[17])
373
+ if address_hash_hex_no_0x[18] < 56:
374
+ buffer[20] = norm_address_no_0x[18]
375
+ else:
376
+ buffer[20] = get_char(norm_address_no_0x[18])
377
+ if address_hash_hex_no_0x[19] < 56:
378
+ buffer[21] = norm_address_no_0x[19]
379
+ else:
380
+ buffer[21] = get_char(norm_address_no_0x[19])
381
+ if address_hash_hex_no_0x[20] < 56:
382
+ buffer[22] = norm_address_no_0x[20]
383
+ else:
384
+ buffer[22] = get_char(norm_address_no_0x[20])
385
+ if address_hash_hex_no_0x[21] < 56:
386
+ buffer[23] = norm_address_no_0x[21]
387
+ else:
388
+ buffer[23] = get_char(norm_address_no_0x[21])
389
+ if address_hash_hex_no_0x[22] < 56:
390
+ buffer[24] = norm_address_no_0x[22]
391
+ else:
392
+ buffer[24] = get_char(norm_address_no_0x[22])
393
+ if address_hash_hex_no_0x[23] < 56:
394
+ buffer[25] = norm_address_no_0x[23]
395
+ else:
396
+ buffer[25] = get_char(norm_address_no_0x[23])
397
+ if address_hash_hex_no_0x[24] < 56:
398
+ buffer[26] = norm_address_no_0x[24]
399
+ else:
400
+ buffer[26] = get_char(norm_address_no_0x[24])
401
+ if address_hash_hex_no_0x[25] < 56:
402
+ buffer[27] = norm_address_no_0x[25]
403
+ else:
404
+ buffer[27] = get_char(norm_address_no_0x[25])
405
+ if address_hash_hex_no_0x[26] < 56:
406
+ buffer[28] = norm_address_no_0x[26]
407
+ else:
408
+ buffer[28] = get_char(norm_address_no_0x[26])
409
+ if address_hash_hex_no_0x[27] < 56:
410
+ buffer[29] = norm_address_no_0x[27]
411
+ else:
412
+ buffer[29] = get_char(norm_address_no_0x[27])
413
+ if address_hash_hex_no_0x[28] < 56:
414
+ buffer[30] = norm_address_no_0x[28]
415
+ else:
416
+ buffer[30] = get_char(norm_address_no_0x[28])
417
+ if address_hash_hex_no_0x[29] < 56:
418
+ buffer[31] = norm_address_no_0x[29]
419
+ else:
420
+ buffer[31] = get_char(norm_address_no_0x[29])
421
+ if address_hash_hex_no_0x[30] < 56:
422
+ buffer[32] = norm_address_no_0x[30]
423
+ else:
424
+ buffer[32] = get_char(norm_address_no_0x[30])
425
+ if address_hash_hex_no_0x[31] < 56:
426
+ buffer[33] = norm_address_no_0x[31]
427
+ else:
428
+ buffer[33] = get_char(norm_address_no_0x[31])
429
+ if address_hash_hex_no_0x[32] < 56:
430
+ buffer[34] = norm_address_no_0x[32]
431
+ else:
432
+ buffer[34] = get_char(norm_address_no_0x[32])
433
+ if address_hash_hex_no_0x[33] < 56:
434
+ buffer[35] = norm_address_no_0x[33]
435
+ else:
436
+ buffer[35] = get_char(norm_address_no_0x[33])
437
+ if address_hash_hex_no_0x[34] < 56:
438
+ buffer[36] = norm_address_no_0x[34]
439
+ else:
440
+ buffer[36] = get_char(norm_address_no_0x[34])
441
+ if address_hash_hex_no_0x[35] < 56:
442
+ buffer[37] = norm_address_no_0x[35]
443
+ else:
444
+ buffer[37] = get_char(norm_address_no_0x[35])
445
+ if address_hash_hex_no_0x[36] < 56:
446
+ buffer[38] = norm_address_no_0x[36]
447
+ else:
448
+ buffer[38] = get_char(norm_address_no_0x[36])
449
+ if address_hash_hex_no_0x[37] < 56:
450
+ buffer[39] = norm_address_no_0x[37]
451
+ else:
452
+ buffer[39] = get_char(norm_address_no_0x[37])
453
+ if address_hash_hex_no_0x[38] < 56:
454
+ buffer[40] = norm_address_no_0x[38]
455
+ else:
456
+ buffer[40] = get_char(norm_address_no_0x[38])
457
+ if address_hash_hex_no_0x[39] < 56:
458
+ buffer[41] = norm_address_no_0x[39]
459
+ else:
460
+ buffer[41] = get_char(norm_address_no_0x[39])
461
+
462
+
463
+ cdef inline unsigned char get_char(unsigned char c) noexcept nogil:
464
+ """This checks if `address_char` falls in the ASCII range for lowercase hexadecimal
465
+ characters ('a' to 'f'), which correspond to ASCII values 97 to 102. If it does,
466
+ the character is capitalized.
467
+ """
468
+ if c == 97: # a
469
+ return 65 # A
470
+ elif c == 98: # b
471
+ return 66 # B
472
+ elif c == 99: # c
473
+ return 67 # C
474
+ elif c == 100: # d
475
+ return 68 # D
476
+ elif c == 101: # e
477
+ return 69 # E
478
+ elif c == 102: # f
479
+ return 70 # F
480
+ else:
481
+ return c
482
+
483
+
484
+ cdef inline bint is_hex_lower(unsigned char c) noexcept nogil:
485
+ return (48 <= c <= 57) or (97 <= c <= 102)
486
+
487
+
488
+ cdef unsigned char* lowercase_ascii_and_validate(bytes src):
489
+ cdef Py_ssize_t src_len, range_start, i
490
+ cdef unsigned char* c_string
491
+ cdef unsigned char c
492
+
493
+ src_len = PyBytes_GET_SIZE(src)
494
+ c_string = src
495
+
496
+ with nogil:
497
+ # if c_string[0] == b"0" and c_string[1] in (b"X", b"x")
498
+ if c_string[0] == 48 and c_string[1] in (88, 120):
499
+ range_start = 2
500
+ else:
501
+ range_start = 0
502
+
503
+ for i in range(range_start, src_len):
504
+ c = c_string[i]
505
+
506
+ if 65 <= c <= 90:
507
+ c += 32
508
+ c_string[i] = c
509
+
510
+ if c == 48: # 0
511
+ pass
512
+ elif c == 49: # 1
513
+ pass
514
+ elif c == 50: # 2
515
+ pass
516
+ elif c == 51: # 3
517
+ pass
518
+ elif c == 52: # 4
519
+ pass
520
+ elif c == 53: # 5
521
+ pass
522
+ elif c == 54: # 6
523
+ pass
524
+ elif c == 55: # 7
525
+ pass
526
+ elif c == 56: # 8
527
+ pass
528
+ elif c == 57: # 9
529
+ pass
530
+ elif c == 97: # a
531
+ pass
532
+ elif c == 98: # b
533
+ pass
534
+ elif c == 99: # c
535
+ pass
536
+ elif c == 100: # d
537
+ pass
538
+ elif c == 101: # e
539
+ pass
540
+ elif c == 102: # f
541
+ pass
542
+ else:
543
+ with gil:
544
+ raise ValueError("when sending a str, it must be a hex string. " f"Got: {repr(src.decode('ascii'))}")
545
+
546
+ return c_string[range_start:]
547
+
548
+
549
+ del AnyAddress, ChecksumAddress