diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 7ebfc089f3..522d3f520f 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -4,6 +4,8 @@ #include // malloc, free #include +#include // htons, htonl + #include namespace node { @@ -261,6 +263,70 @@ Handle Buffer::AsciiWrite(const Arguments &args) { } +// buffer.unpack(format, index); +// Starting at 'index', unpacks binary from the buffer into an array. +// 'format' is a string +// +// FORMAT RETURNS +// N uint32_t a 32bit unsigned integer in network byte order +// n uint16_t a 16bit unsigned integer in network byte order +// o uint8_t a 8bit unsigned integer +Handle Buffer::Unpack(const Arguments &args) { + HandleScope scope; + Buffer *buffer = ObjectWrap::Unwrap(args.This()); + + if (!args[0]->IsString()) { + return ThrowException(Exception::TypeError(String::New( + "Argument must be a string"))); + } + + String::AsciiValue format(args[0]->ToString()); + int index = args[1]->IntegerValue(); + +#define OUT_OF_BOUNDS ThrowException(Exception::Error(String::New("Out of bounds"))) + + Local array = Array::New(format.length()); + + uint8_t uint8; + uint16_t uint16; + uint32_t uint32; + + for (int i = 0; i < format.length(); i++) { + switch ((*format)[i]) { + // 32bit unsigned integer in network byte order + case 'N': + if (index + 3 >= buffer->length_) return OUT_OF_BOUNDS; + uint32 = htonl(*(uint32_t*)(buffer->data_ + index)); + array->Set(Integer::New(i), Integer::NewFromUnsigned(uint32)); + index += 4; + break; + + // 16bit unsigned integer in network byte order + case 'n': + if (index + 1 >= buffer->length_) return OUT_OF_BOUNDS; + uint16 = htons(*(uint16_t*)(buffer->data_ + index)); + array->Set(Integer::New(i), Integer::NewFromUnsigned(uint16)); + index += 2; + break; + + // a single octet, unsigned. + case 'o': + if (index >= buffer->length_) return OUT_OF_BOUNDS; + uint8 = (uint8_t)buffer->data_[index]; + array->Set(Integer::New(i), Integer::NewFromUnsigned(uint8)); + index += 1; + break; + + default: + return ThrowException(Exception::Error( + String::New("Unknown format character"))); + } + } + + return scope.Close(array); +} + + // var nbytes = Buffer.utf8Length("string") Handle Buffer::Utf8Length(const Arguments &args) { HandleScope scope; @@ -280,6 +346,7 @@ bool Buffer::HasInstance(Handle val) { } + void Buffer::Initialize(Handle target) { HandleScope scope; @@ -299,6 +366,7 @@ void Buffer::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write); NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "unpack", Buffer::Unpack); NODE_SET_METHOD(constructor_template->GetFunction(), "utf8Length", Buffer::Utf8Length); diff --git a/src/node_buffer.h b/src/node_buffer.h index d6b83fe00b..793c934615 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -46,6 +46,7 @@ class Buffer : public ObjectWrap { static v8::Handle AsciiWrite(const v8::Arguments &args); static v8::Handle Utf8Write(const v8::Arguments &args); static v8::Handle Utf8Length(const v8::Arguments &args); + static v8::Handle Unpack(const v8::Arguments &args); int AsciiWrite(char *string, int offset, int length); int Utf8Write(char *string, int offset, int length); diff --git a/test/mjsunit/test-buffer.js b/test/mjsunit/test-buffer.js index 50dba62871..c8792e8980 100644 --- a/test/mjsunit/test-buffer.js +++ b/test/mjsunit/test-buffer.js @@ -50,4 +50,26 @@ for (var j = 0; j < 10000; j++) { } +// unpack +var b = new process.Buffer(10); +b[0] = 0x00; +b[1] = 0x01; +b[2] = 0x03; +b[3] = 0x00; + +assert.deepEqual([0x0001], b.unpack('n', 0)); +assert.deepEqual([0x0001, 0x0300], b.unpack('nn', 0)); +assert.deepEqual([0x0103], b.unpack('n', 1)); +assert.deepEqual([0x0300], b.unpack('n', 2)); +assert.deepEqual([0x00010300], b.unpack('N', 0)); +assert.throws(function () { + b.unpack('N', 8); +}); + +b[4] = 0xDE; +b[5] = 0xAD; +b[6] = 0xBE; +b[7] = 0xEF; + +assert.deepEqual([0xDEADBEEF], b.unpack('N', 4));