Skip to content

Commit 4158272

Browse files
chjjevanlucas
authored andcommitted
buffer: optimize hex_decode
PR-URL: #7602 Reviewed-By: Anna Henningsen <[email protected]>
1 parent 0858e62 commit 4158272

File tree

3 files changed

+93
-13
lines changed

3 files changed

+93
-13
lines changed

benchmark/buffers/buffer-hex.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
5+
const bench = common.createBenchmark(main, {
6+
len: [0, 1, 64, 1024],
7+
n: [1e7]
8+
});
9+
10+
function main(conf) {
11+
const len = conf.len | 0;
12+
const n = conf.n | 0;
13+
const buf = Buffer.alloc(len);
14+
15+
for (let i = 0; i < buf.length; i++)
16+
buf[i] = i & 0xff;
17+
18+
const hex = buf.toString('hex');
19+
20+
bench.start();
21+
22+
for (let i = 0; i < n; i += 1)
23+
Buffer.from(hex, 'hex');
24+
25+
bench.end(n);
26+
}

src/string_bytes.cc

+24-13
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,27 @@ const int8_t unbase64_table[256] =
143143
};
144144

145145

146-
template <typename TypeName>
147-
unsigned hex2bin(TypeName c) {
148-
if (c >= '0' && c <= '9')
149-
return c - '0';
150-
if (c >= 'A' && c <= 'F')
151-
return 10 + (c - 'A');
152-
if (c >= 'a' && c <= 'f')
153-
return 10 + (c - 'a');
154-
return static_cast<unsigned>(-1);
155-
}
146+
static const int8_t unhex_table[256] =
147+
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
148+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
149+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
150+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
151+
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
152+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
153+
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
154+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
155+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
156+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
157+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
158+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
159+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
160+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
161+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
162+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
163+
};
164+
165+
#define unhex(x) \
166+
static_cast<unsigned>(unhex_table[static_cast<uint8_t>(x)])
156167

157168

158169
template <typename TypeName>
@@ -162,11 +173,11 @@ size_t hex_decode(char* buf,
162173
const size_t srcLen) {
163174
size_t i;
164175
for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) {
165-
unsigned a = hex2bin(src[i * 2 + 0]);
166-
unsigned b = hex2bin(src[i * 2 + 1]);
176+
unsigned a = unhex(src[i * 2 + 0]);
177+
unsigned b = unhex(src[i * 2 + 1]);
167178
if (!~a || !~b)
168179
return i;
169-
buf[i] = a * 16 + b;
180+
buf[i] = (a << 4) | b;
170181
}
171182

172183
return i;

test/parallel/test-buffer-badhex.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const Buffer = require('buffer').Buffer;
5+
6+
// Test hex strings and bad hex strings
7+
{
8+
const buf1 = Buffer.alloc(4);
9+
assert.strictEqual(buf1.length, 4);
10+
assert.deepStrictEqual(buf1, new Buffer([0, 0, 0, 0]));
11+
assert.strictEqual(buf1.write('abcdxx', 0, 'hex'), 2);
12+
assert.deepStrictEqual(buf1, new Buffer([0xab, 0xcd, 0x00, 0x00]));
13+
assert.strictEqual(buf1.toString('hex'), 'abcd0000');
14+
assert.strictEqual(buf1.write('abcdef01', 0, 'hex'), 4);
15+
assert.deepStrictEqual(buf1, new Buffer([0xab, 0xcd, 0xef, 0x01]));
16+
assert.strictEqual(buf1.toString('hex'), 'abcdef01');
17+
18+
const buf2 = Buffer.from(buf1.toString('hex'), 'hex');
19+
assert.strictEqual(buf1.toString('hex'), buf2.toString('hex'));
20+
21+
const buf3 = Buffer.alloc(5);
22+
assert.strictEqual(buf3.write('abcdxx', 1, 'hex'), 2);
23+
assert.strictEqual(buf3.toString('hex'), '00abcd0000');
24+
25+
const buf4 = Buffer.alloc(4);
26+
assert.deepStrictEqual(buf4, new Buffer([0, 0, 0, 0]));
27+
assert.strictEqual(buf4.write('xxabcd', 0, 'hex'), 0);
28+
assert.deepStrictEqual(buf4, new Buffer([0, 0, 0, 0]));
29+
assert.strictEqual(buf4.write('xxab', 1, 'hex'), 0);
30+
assert.deepStrictEqual(buf4, new Buffer([0, 0, 0, 0]));
31+
assert.strictEqual(buf4.write('cdxxab', 0, 'hex'), 1);
32+
assert.deepStrictEqual(buf4, new Buffer([0xcd, 0, 0, 0]));
33+
34+
const buf5 = Buffer.alloc(256);
35+
for (let i = 0; i < 256; i++)
36+
buf5[i] = i;
37+
38+
const hex = buf5.toString('hex');
39+
assert.deepStrictEqual(Buffer.from(hex, 'hex'), buf5);
40+
41+
const badHex = hex.slice(0, 256) + 'xx' + hex.slice(256, 510);
42+
assert.deepStrictEqual(Buffer.from(badHex, 'hex'), buf5.slice(0, 128));
43+
}

0 commit comments

Comments
 (0)