Skip to content

Commit 62b1b8f

Browse files
committed
revert: remove _hexdigits lookup table, back to ternary
The hex digit lookup table caused a performance regression on larger digests (branch prediction beats memory load). Revert to the original (c > 9) ? c + 'a' - 10 : c + '0' ternary pattern.
1 parent 2185ac1 commit 62b1b8f

1 file changed

Lines changed: 56 additions & 25 deletions

File tree

src/_xxhash.c

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
#define XXH128_DIGESTSIZE 16
4545
#define XXH128_BLOCKSIZE 64
4646

47-
static const char _hexdigits[] = "0123456789abcdef";
4847

4948

5049
/* Get a buffer from an object. Rejects str with hashlib-compatible error. */
@@ -219,9 +218,13 @@ static PyObject *xxh32_hexdigest(PyObject *self, PyObject *const *args,
219218
char retbuf[XXH32_DIGESTSIZE * 2];
220219
int i, j;
221220
for (i = j = 0; i < XXH32_DIGESTSIZE; i++) {
222-
unsigned char byte = digest[i];
223-
retbuf[j++] = _hexdigits[byte >> 4];
224-
retbuf[j++] = _hexdigits[byte & 0xf];
221+
unsigned char c;
222+
c = (digest[i] >> 4) & 0xf;
223+
c = (c > 9) ? c + 'a' - 10 : c + '0';
224+
retbuf[j++] = c;
225+
c = (digest[i] & 0xf);
226+
c = (c > 9) ? c + 'a' - 10 : c + '0';
227+
retbuf[j++] = c;
225228
}
226229

227230
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -285,9 +288,13 @@ static PyObject *xxh64_hexdigest(PyObject *self, PyObject *const *args,
285288
char retbuf[XXH64_DIGESTSIZE * 2];
286289
int i, j;
287290
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
288-
unsigned char byte = digest[i];
289-
retbuf[j++] = _hexdigits[byte >> 4];
290-
retbuf[j++] = _hexdigits[byte & 0xf];
291+
unsigned char c;
292+
c = (digest[i] >> 4) & 0xf;
293+
c = (c > 9) ? c + 'a' - 10 : c + '0';
294+
retbuf[j++] = c;
295+
c = (digest[i] & 0xf);
296+
c = (c > 9) ? c + 'a' - 10 : c + '0';
297+
retbuf[j++] = c;
291298
}
292299

293300
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -351,9 +358,13 @@ static PyObject *xxh3_64_hexdigest(PyObject *self, PyObject *const *args,
351358
char retbuf[XXH64_DIGESTSIZE * 2];
352359
int i, j;
353360
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
354-
unsigned char byte = digest[i];
355-
retbuf[j++] = _hexdigits[byte >> 4];
356-
retbuf[j++] = _hexdigits[byte & 0xf];
361+
unsigned char c;
362+
c = (digest[i] >> 4) & 0xf;
363+
c = (c > 9) ? c + 'a' - 10 : c + '0';
364+
retbuf[j++] = c;
365+
c = (digest[i] & 0xf);
366+
c = (c > 9) ? c + 'a' - 10 : c + '0';
367+
retbuf[j++] = c;
357368
}
358369

359370
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -432,9 +443,13 @@ static PyObject *xxh3_128_hexdigest(PyObject *self, PyObject *const *args,
432443
char retbuf[XXH128_DIGESTSIZE * 2];
433444
int i, j;
434445
for (i = j = 0; i < XXH128_DIGESTSIZE; i++) {
435-
unsigned char byte = digest[i];
436-
retbuf[j++] = _hexdigits[byte >> 4];
437-
retbuf[j++] = _hexdigits[byte & 0xf];
446+
unsigned char c;
447+
c = (digest[i] >> 4) & 0xf;
448+
c = (c > 9) ? c + 'a' - 10 : c + '0';
449+
retbuf[j++] = c;
450+
c = (digest[i] & 0xf);
451+
c = (c > 9) ? c + 'a' - 10 : c + '0';
452+
retbuf[j++] = c;
438453
}
439454

440455
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -607,9 +622,13 @@ static PyObject *PYXXH32_hexdigest(PYXXH32Object *self)
607622
XXH32_canonicalFromHash((XXH32_canonical_t *)digest, intdigest);
608623

609624
for (i = j = 0; i < XXH32_DIGESTSIZE; i++) {
610-
unsigned char byte = digest[i];
611-
retbuf[j++] = _hexdigits[byte >> 4];
612-
retbuf[j++] = _hexdigits[byte & 0xf];
625+
unsigned char c;
626+
c = (digest[i] >> 4) & 0xf;
627+
c = (c > 9) ? c + 'a' - 10 : c + '0';
628+
retbuf[j++] = c;
629+
c = (digest[i] & 0xf);
630+
c = (c > 9) ? c + 'a' - 10 : c + '0';
631+
retbuf[j++] = c;
613632
}
614633

615634
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -955,9 +974,13 @@ static PyObject *PYXXH64_hexdigest(PYXXH64Object *self)
955974
XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest);
956975

957976
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
958-
unsigned char byte = digest[i];
959-
retbuf[j++] = _hexdigits[byte >> 4];
960-
retbuf[j++] = _hexdigits[byte & 0xf];
977+
unsigned char c;
978+
c = (digest[i] >> 4) & 0xf;
979+
c = (c > 9) ? c + 'a' - 10 : c + '0';
980+
retbuf[j++] = c;
981+
c = (digest[i] & 0xf);
982+
c = (c > 9) ? c + 'a' - 10 : c + '0';
983+
retbuf[j++] = c;
961984
}
962985

963986
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -1304,9 +1327,13 @@ static PyObject *PYXXH3_64_hexdigest(PYXXH3_64Object *self)
13041327
XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest);
13051328

13061329
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
1307-
unsigned char byte = digest[i];
1308-
retbuf[j++] = _hexdigits[byte >> 4];
1309-
retbuf[j++] = _hexdigits[byte & 0xf];
1330+
unsigned char c;
1331+
c = (digest[i] >> 4) & 0xf;
1332+
c = (c > 9) ? c + 'a' - 10 : c + '0';
1333+
retbuf[j++] = c;
1334+
c = (digest[i] & 0xf);
1335+
c = (c > 9) ? c + 'a' - 10 : c + '0';
1336+
retbuf[j++] = c;
13101337
}
13111338

13121339
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -1660,9 +1687,13 @@ static PyObject *PYXXH3_128_hexdigest(PYXXH3_128Object *self)
16601687
XXH128_canonicalFromHash((XXH128_canonical_t *)digest, intdigest);
16611688

16621689
for (i = j = 0; i < XXH128_DIGESTSIZE; i++) {
1663-
unsigned char byte = digest[i];
1664-
retbuf[j++] = _hexdigits[byte >> 4];
1665-
retbuf[j++] = _hexdigits[byte & 0xf];
1690+
unsigned char c;
1691+
c = (digest[i] >> 4) & 0xf;
1692+
c = (c > 9) ? c + 'a' - 10 : c + '0';
1693+
retbuf[j++] = c;
1694+
c = (digest[i] & 0xf);
1695+
c = (c > 9) ? c + 'a' - 10 : c + '0';
1696+
retbuf[j++] = c;
16661697
}
16671698

16681699
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));

0 commit comments

Comments
 (0)