Skip to content

Commit 3f65916

Browse files
committed
buffer: optimize Buffer.prototype.toString('hex')
Move the implementation to C++ land. The old JS implementation used string concatenation, was dog slow and consumed copious amounts of memory for large buffers. Example: var buf = Buffer(0x1000000); // 16 MB buf.toString('hex') // Used 3+ GB of memory. The new implementation operates in O(n) time and space. Fixes nodejs#4700.
1 parent c7c1ed0 commit 3f65916

File tree

3 files changed

+22
-15
lines changed

3 files changed

+22
-15
lines changed

lib/buffer.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,6 @@ function toHex(n) {
3535
}
3636

3737

38-
SlowBuffer.prototype.hexSlice = function(start, end) {
39-
var len = this.length;
40-
41-
if (!start || start < 0) start = 0;
42-
if (!end || end < 0 || end > len) end = len;
43-
44-
var out = '';
45-
for (var i = start; i < end; i++) {
46-
out += toHex(this[i]);
47-
}
48-
return out;
49-
};
50-
51-
52-
5338
SlowBuffer.prototype.toString = function(encoding, start, end) {
5439
encoding = String(encoding || 'utf8').toLowerCase();
5540
start = +start || 0;

src/node_buffer.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,26 @@ Handle<Value> Buffer::Ucs2Slice(const Arguments &args) {
277277
}
278278

279279

280+
Handle<Value> Buffer::HexSlice(const Arguments &args) {
281+
HandleScope scope;
282+
Buffer* parent = ObjectWrap::Unwrap<Buffer>(args.This());
283+
SLICE_ARGS(args[0], args[1])
284+
char* src = parent->data_ + start;
285+
uint32_t dstlen = (end - start) * 2;
286+
if (dstlen == 0) return scope.Close(String::Empty(node_isolate));
287+
char* dst = new char[dstlen];
288+
for (uint32_t i = 0, k = 0; k < dstlen; i += 1, k += 2) {
289+
static const char hex[] = "0123456789abcdef";
290+
uint8_t val = static_cast<uint8_t>(src[i]);
291+
dst[k + 0] = hex[val >> 4];
292+
dst[k + 1] = hex[val & 15];
293+
}
294+
Local<String> string = String::New(dst, dstlen);
295+
delete[] dst;
296+
return scope.Close(string);
297+
}
298+
299+
280300
// supports regular and URL-safe base64
281301
static const int unbase64_table[] =
282302
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-2,-1,-1
@@ -920,6 +940,7 @@ void Buffer::Initialize(Handle<Object> target) {
920940
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
921941
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice);
922942
NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Slice", Buffer::Ucs2Slice);
943+
NODE_SET_PROTOTYPE_METHOD(constructor_template, "hexSlice", Buffer::HexSlice);
923944
// TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
924945
// copy
925946
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);

src/node_buffer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class NODE_EXTERN Buffer: public ObjectWrap {
118118
static v8::Handle<v8::Value> Base64Slice(const v8::Arguments &args);
119119
static v8::Handle<v8::Value> Utf8Slice(const v8::Arguments &args);
120120
static v8::Handle<v8::Value> Ucs2Slice(const v8::Arguments &args);
121+
static v8::Handle<v8::Value> HexSlice(const v8::Arguments &args);
121122
static v8::Handle<v8::Value> BinaryWrite(const v8::Arguments &args);
122123
static v8::Handle<v8::Value> Base64Write(const v8::Arguments &args);
123124
static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);

0 commit comments

Comments
 (0)