Skip to content

Commit cd42f56

Browse files
committed
buffer: optimize Buffer.prototype.write(s, 'hex')
Move the implementation to C++ land. This is similar to commit 3f65916 but this time for the write() function and the Buffer(s, 'hex') constructor. Speeds up the benchmark below about 24x (2.6s vs 1:02m). var s = 'f'; for (var i = 0; i < 26; ++i) s += s; // 64 MB Buffer(s, 'hex');
1 parent 916aeba commit cd42f56

3 files changed

Lines changed: 68 additions & 30 deletions

File tree

lib/buffer.js

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,36 +74,6 @@ SlowBuffer.prototype.toString = function(encoding, start, end) {
7474
};
7575

7676

77-
SlowBuffer.prototype.hexWrite = function(string, offset, length) {
78-
offset = +offset || 0;
79-
var remaining = this.length - offset;
80-
if (!length) {
81-
length = remaining;
82-
} else {
83-
length = +length;
84-
if (length > remaining) {
85-
length = remaining;
86-
}
87-
}
88-
89-
// must be an even number of digits
90-
var strLen = string.length;
91-
if (strLen % 2) {
92-
throw new TypeError('Invalid hex string');
93-
}
94-
if (length > strLen / 2) {
95-
length = strLen / 2;
96-
}
97-
for (var i = 0; i < length; i++) {
98-
var byte = parseInt(string.substr(i * 2, 2), 16);
99-
if (isNaN(byte)) throw new TypeError('Invalid hex string');
100-
this[offset + i] = byte;
101-
}
102-
SlowBuffer._charsWritten = i * 2;
103-
return i;
104-
};
105-
106-
10777
SlowBuffer.prototype.write = function(string, offset, length, encoding) {
10878
// Support both (string, offset, length, encoding)
10979
// and the legacy (string, encoding, offset, length)

src/node_buffer.cc

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,72 @@ Handle<Value> Buffer::Ucs2Write(const Arguments &args) {
545545
}
546546

547547

548+
inline unsigned hex2bin(char c) {
549+
if (c >= '0' && c <= '9') return c - '0';
550+
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
551+
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
552+
return static_cast<unsigned>(-1);
553+
}
554+
555+
556+
Handle<Value> Buffer::HexWrite(const Arguments& args) {
557+
HandleScope scope;
558+
Buffer* parent = ObjectWrap::Unwrap<Buffer>(args.This());
559+
560+
if (args[0]->IsString() == false) {
561+
return ThrowTypeError("Argument must be a string");
562+
}
563+
564+
Local<String> s = args[0].As<String>();
565+
566+
if (s->Length() % 2 != 0) {
567+
return ThrowTypeError("Invalid hex string");
568+
}
569+
570+
uint32_t start = args[1]->Uint32Value();
571+
uint32_t size = args[2]->Uint32Value();
572+
uint32_t end = start + size;
573+
574+
if (start >= parent->length_) {
575+
Local<Integer> val = Integer::New(0, node_isolate);
576+
constructor_template->GetFunction()->Set(chars_written_sym, val);
577+
return scope.Close(val);
578+
}
579+
580+
if (end < start || end > parent->length_) { // Overflow + bounds check.
581+
end = parent->length_;
582+
size = parent->length_ - start;
583+
}
584+
585+
if (size == 0) {
586+
Local<Integer> val = Integer::New(0, node_isolate);
587+
constructor_template->GetFunction()->Set(chars_written_sym, val);
588+
return scope.Close(val);
589+
}
590+
591+
char* dst = parent->data_ + start;
592+
String::AsciiValue string(s);
593+
const char* src = *string;
594+
uint32_t max = string.length() / 2;
595+
596+
if (max > size) {
597+
max = size;
598+
}
599+
600+
for (uint32_t i = 0; i < max; ++i) {
601+
unsigned a = hex2bin(src[i * 2 + 0]);
602+
unsigned b = hex2bin(src[i * 2 + 1]);
603+
if (!~a || !~b) return ThrowTypeError("Invalid hex string");
604+
dst[i] = a * 16 + b;
605+
}
606+
607+
constructor_template->GetFunction()->Set(chars_written_sym,
608+
Integer::New(max * 2, node_isolate));
609+
610+
return scope.Close(Integer::New(max, node_isolate));
611+
}
612+
613+
548614
// var charsWritten = buffer.asciiWrite(string, offset);
549615
Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
550616
HandleScope scope;
@@ -950,6 +1016,7 @@ void Buffer::Initialize(Handle<Object> target) {
9501016
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
9511017
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write);
9521018
NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Write", Buffer::Ucs2Write);
1019+
NODE_SET_PROTOTYPE_METHOD(constructor_template, "hexWrite", Buffer::HexWrite);
9531020
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatLE", Buffer::ReadFloatLE);
9541021
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatBE", Buffer::ReadFloatBE);
9551022
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readDoubleLE", Buffer::ReadDoubleLE);

src/node_buffer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class NODE_EXTERN Buffer: public ObjectWrap {
124124
static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);
125125
static v8::Handle<v8::Value> Utf8Write(const v8::Arguments &args);
126126
static v8::Handle<v8::Value> Ucs2Write(const v8::Arguments &args);
127+
static v8::Handle<v8::Value> HexWrite(const v8::Arguments &args);
127128
static v8::Handle<v8::Value> ReadFloatLE(const v8::Arguments &args);
128129
static v8::Handle<v8::Value> ReadFloatBE(const v8::Arguments &args);
129130
static v8::Handle<v8::Value> ReadDoubleLE(const v8::Arguments &args);

0 commit comments

Comments
 (0)