Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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');
  • Loading branch information...
commit cd42f56178c929da84046e750b9306b5656e3144 1 parent 916aeba
@bnoordhuis bnoordhuis authored
Showing with 68 additions and 30 deletions.
  1. +0 −30 lib/buffer.js
  2. +67 −0 src/node_buffer.cc
  3. +1 −0  src/node_buffer.h
View
30 lib/buffer.js
@@ -74,36 +74,6 @@ SlowBuffer.prototype.toString = function(encoding, start, end) {
};
-SlowBuffer.prototype.hexWrite = function(string, offset, length) {
- offset = +offset || 0;
- var remaining = this.length - offset;
- if (!length) {
- length = remaining;
- } else {
- length = +length;
- if (length > remaining) {
- length = remaining;
- }
- }
-
- // must be an even number of digits
- var strLen = string.length;
- if (strLen % 2) {
- throw new TypeError('Invalid hex string');
- }
- if (length > strLen / 2) {
- length = strLen / 2;
- }
- for (var i = 0; i < length; i++) {
- var byte = parseInt(string.substr(i * 2, 2), 16);
- if (isNaN(byte)) throw new TypeError('Invalid hex string');
- this[offset + i] = byte;
- }
- SlowBuffer._charsWritten = i * 2;
- return i;
-};
-
-
SlowBuffer.prototype.write = function(string, offset, length, encoding) {
// Support both (string, offset, length, encoding)
// and the legacy (string, encoding, offset, length)
View
67 src/node_buffer.cc
@@ -545,6 +545,72 @@ Handle<Value> Buffer::Ucs2Write(const Arguments &args) {
}
+inline unsigned hex2bin(char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
+ if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
+ return static_cast<unsigned>(-1);
+}
+
+
+Handle<Value> Buffer::HexWrite(const Arguments& args) {
+ HandleScope scope;
+ Buffer* parent = ObjectWrap::Unwrap<Buffer>(args.This());
+
+ if (args[0]->IsString() == false) {
+ return ThrowTypeError("Argument must be a string");
+ }
+
+ Local<String> s = args[0].As<String>();
+
+ if (s->Length() % 2 != 0) {
+ return ThrowTypeError("Invalid hex string");
+ }
+
+ uint32_t start = args[1]->Uint32Value();
+ uint32_t size = args[2]->Uint32Value();
+ uint32_t end = start + size;
+
+ if (start >= parent->length_) {
+ Local<Integer> val = Integer::New(0, node_isolate);
+ constructor_template->GetFunction()->Set(chars_written_sym, val);
+ return scope.Close(val);
+ }
+
+ if (end < start || end > parent->length_) { // Overflow + bounds check.
+ end = parent->length_;
+ size = parent->length_ - start;
+ }
+
+ if (size == 0) {
+ Local<Integer> val = Integer::New(0, node_isolate);
+ constructor_template->GetFunction()->Set(chars_written_sym, val);
+ return scope.Close(val);
+ }
+
+ char* dst = parent->data_ + start;
+ String::AsciiValue string(s);
+ const char* src = *string;
+ uint32_t max = string.length() / 2;
+
+ if (max > size) {
+ max = size;
+ }
+
+ for (uint32_t i = 0; i < max; ++i) {
+ unsigned a = hex2bin(src[i * 2 + 0]);
+ unsigned b = hex2bin(src[i * 2 + 1]);
+ if (!~a || !~b) return ThrowTypeError("Invalid hex string");
+ dst[i] = a * 16 + b;
+ }
+
+ constructor_template->GetFunction()->Set(chars_written_sym,
+ Integer::New(max * 2, node_isolate));
+
+ return scope.Close(Integer::New(max, node_isolate));
+}
+
+
// var charsWritten = buffer.asciiWrite(string, offset);
Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
HandleScope scope;
@@ -950,6 +1016,7 @@ void Buffer::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Write", Buffer::Ucs2Write);
+ NODE_SET_PROTOTYPE_METHOD(constructor_template, "hexWrite", Buffer::HexWrite);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatLE", Buffer::ReadFloatLE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readFloatBE", Buffer::ReadFloatBE);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "readDoubleLE", Buffer::ReadDoubleLE);
View
1  src/node_buffer.h
@@ -124,6 +124,7 @@ class NODE_EXTERN Buffer: public ObjectWrap {
static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Write(const v8::Arguments &args);
static v8::Handle<v8::Value> Ucs2Write(const v8::Arguments &args);
+ static v8::Handle<v8::Value> HexWrite(const v8::Arguments &args);
static v8::Handle<v8::Value> ReadFloatLE(const v8::Arguments &args);
static v8::Handle<v8::Value> ReadFloatBE(const v8::Arguments &args);
static v8::Handle<v8::Value> ReadDoubleLE(const v8::Arguments &args);
Please sign in to comment.
Something went wrong with that request. Please try again.