diff --git a/benchmark/buffer_creation.js b/benchmark/buffer_creation.js index 44ae32e315..3bc711e3ba 100644 --- a/benchmark/buffer_creation.js +++ b/benchmark/buffer_creation.js @@ -1,6 +1,6 @@ +SlowBuffer = require('buffer').SlowBuffer; - -for (var i = 0; i < 9e7; i++) { - b = new Buffer(10); +for (var i = 0; i < 1e6; i++) { + b = new SlowBuffer(10); b[1] = 2 } diff --git a/benchmark/fast_buffer2.js b/benchmark/fast_buffer2.js new file mode 100644 index 0000000000..861ae3baae --- /dev/null +++ b/benchmark/fast_buffer2.js @@ -0,0 +1,42 @@ +var SlowBuffer = require('buffer').SlowBuffer; +var POOLSIZE = 8*1024; +var pool; + +function allocPool () { + pool = new SlowBuffer(POOLSIZE); + pool.used = 0; +} + +function FastBuffer (length) { + this.length = length; + + if (length > POOLSIZE) { + // Big buffer, just alloc one. + this.parent = new Buffer(length); + this.offset = 0; + } else { + // Small buffer. + if (!pool || pool.length - pool.used < length) allocPool(); + this.parent = pool; + this.offset = pool.used; + pool.used += length; + } + + // HERE HERE HERE + SlowBuffer.makeFastBuffer(this.parent, this, this.offset, this.length); +} + +exports.FastBuffer = FastBuffer; + +FastBuffer.prototype.get = function (i) { + if (i < 0 || i >= this.length) throw new Error("oob"); + return this.parent[this.offset + i]; +}; + +FastBuffer.prototype.set = function (i, v) { + if (i < 0 || i >= this.length) throw new Error("oob"); + return this.parent[this.offset + i] = v; +}; + +// TODO define slice, toString, write, etc. +// slice should not use c++ diff --git a/benchmark/fast_buffer2_creation.js b/benchmark/fast_buffer2_creation.js new file mode 100644 index 0000000000..877f5695d2 --- /dev/null +++ b/benchmark/fast_buffer2_creation.js @@ -0,0 +1,6 @@ + +FastBuffer = require('./fast_buffer2').FastBuffer; +for (var i = 0; i < 1e6; i++) { + b = new FastBuffer(10); + b[1] = 2; +} diff --git a/benchmark/fast_buffer_creation.js b/benchmark/fast_buffer_creation.js new file mode 100644 index 0000000000..fbd0c7579a --- /dev/null +++ b/benchmark/fast_buffer_creation.js @@ -0,0 +1,4 @@ +for (var i = 0; i < 1e6; i++) { + b = new Buffer(10); + b[1] = 2; +} diff --git a/lib/buffer.js b/lib/buffer.js index 85c0bfece8..3fb35f6a2c 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -1,55 +1,53 @@ -var Buffer = process.binding('buffer').Buffer; +var SlowBuffer = process.binding('buffer').SlowBuffer; -exports.Buffer = Buffer; function toHex (n) { if (n < 16) return "0" + n.toString(16); return n.toString(16); } -Buffer.isBuffer = function (b) { - return b instanceof Buffer; -}; -Buffer.prototype.inspect = function () { +SlowBuffer.prototype.inspect = function () { var out = [], len = this.length; for (var i = 0; i < len; i++) { out[i] = toHex(this[i]); } - return ""; + return ""; }; -Buffer.prototype.toString = function (encoding, start, stop) { + +SlowBuffer.prototype.toString = function (encoding, start, end) { encoding = String(encoding || 'utf8').toLowerCase(); start = +start || 0; - if (typeof stop == "undefined") stop = this.length; + if (typeof end == "undefined") end = this.length; // Fastpath empty strings - if (+stop == start) { + if (+end == start) { return ''; } switch (encoding) { case 'utf8': case 'utf-8': - return this.utf8Slice(start, stop); + return this.utf8Slice(start, end); case 'ascii': - return this.asciiSlice(start, stop); + return this.asciiSlice(start, end); case 'binary': - return this.binarySlice(start, stop); + return this.binarySlice(start, end); case 'base64': - return this.base64Slice(start, stop); + return this.base64Slice(start, end); default: throw new Error('Unknown encoding'); } }; -Buffer.prototype.write = function (string, offset, encoding) { + +SlowBuffer.prototype.write = function (string, offset, encoding) { // Support both (string, offset, encoding) // and the legacy (string, encoding, offset) if (!isFinite(offset)) { @@ -80,10 +78,256 @@ Buffer.prototype.write = function (string, offset, encoding) { } }; -Buffer.prototype.get = function (index) { - return this[index]; + +// slice(start, end) +SlowBuffer.prototype.slice = function (start, end) { + if (end > this.length) { + throw new Error("oob"); + } + if (start > end) { + throw new Error("oob"); + } + + return new Buffer(this, end - start, +start); }; -Buffer.prototype.set = function (index, value) { - return this[index] = value; + +// Buffer + +function Buffer (subject, encoding, offset) { + if (!(this instanceof Buffer)) { + return new Buffer(subject, encoding, offset); + } + + var type; + + // Are we slicing? + if (typeof offset === 'number') { + this.length = encoding; + this.parent = subject; + this.offset = offset; + } else { + // Find the length + switch (type = typeof subject) { + case 'number': + this.length = subject; + break; + + case 'string': + this.length = Buffer.byteLength(subject, encoding); + break; + + case 'object': // Assume object is an array + this.length = subject.length; + break; + + default: + throw new Error("First argument need to be an number, array or string."); + } + + if (this.length > Buffer.poolSize) { + // Big buffer, just alloc one. + this.parent = new SlowBuffer(this.length); + this.offset = 0; + + } else { + // Small buffer. + if (!pool || pool.length - pool.used < this.length) allocPool(); + this.parent = pool; + this.offset = pool.used; + pool.used += this.length; + } + + // Assume object is an array + if (Array.isArray(subject)) { + for (var i = 0; i < this.length; i++) { + this.parent[i + this.offset] = subject[i]; + } + } else if (type == 'string') { + // We are a string + this.write(subject, 0, encoding); + } + } + + SlowBuffer.makeFastBuffer(this.parent, this, this.offset, this.length); +} + +exports.SlowBuffer = SlowBuffer; +exports.Buffer = Buffer; + +Buffer.poolSize = 8*1024; +var pool; + +function allocPool () { + pool = new SlowBuffer(Buffer.poolSize); + pool.used = 0; +} + + +// Static methods +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer; +}; + + +// Inspect +Buffer.prototype.inspect = function inspect() { + var out = [], + len = this.length; + for (var i = 0; i < len; i++) { + out[i] = toHex(this.parent[i + this.offset]); + } + return ""; +}; + + +Buffer.prototype.get = function get (i) { + if (i < 0 || i >= this.length) throw new Error("oob"); + return this.parent[this.offset + i]; +}; + + +Buffer.prototype.set = function set (i, v) { + if (i < 0 || i >= this.length) throw new Error("oob"); + return this.parent[this.offset + i] = v; +}; + + +// write(string, offset = 0, encoding = 'utf8') +Buffer.prototype.write = function write (string, offset, encoding) { + if (!isFinite(offset)) { + var swap = encoding; + encoding = offset; + offset = swap; + } + + offset = +offset || 0; + encoding = String(encoding || 'utf8').toLowerCase(); + + // Make sure we are not going to overflow + var maxLength = this.length - offset; + + var ret; + switch (encoding) { + case 'utf8': + case 'utf-8': + ret = this.parent.utf8Write(string, this.offset + offset, maxLength); + break; + + case 'ascii': + ret = this.parent.asciiWrite(string, this.offset + offset, maxLength); + break; + + case 'binary': + ret = this.parent.binaryWrite(string, this.offset + offset, maxLength); + break; + + case 'base64': + // Warning: maxLength not taken into account in base64Write + ret = this.parent.base64Write(string, this.offset + offset, maxLength); + break; + + default: + throw new Error('Unknown encoding'); + } + + Buffer._charsWritten = SlowBuffer._charsWritten; + + return ret; +}; + + +// toString(encoding, start=0, end=buffer.length) +Buffer.prototype.toString = function (encoding, start, end) { + encoding = String(encoding || 'utf8').toLowerCase(); + + if (typeof start == 'undefined' || start < 0) { + start = 0; + } else if (start > this.length) { + start = this.length; + } + + if (typeof end == "undefined" || end > this.length) { + end = this.length; + } else if (end < 0) { + end = 0; + } + + start = start + this.offset; + end = end + this.offset; + + switch (encoding) { + case 'utf8': + case 'utf-8': + return this.parent.utf8Slice(start, end); + + case 'ascii': + return this.parent.asciiSlice(start, end); + + case 'binary': + return this.parent.binarySlice(start, end); + + case 'base64': + return this.parent.base64Slice(start, end); + + default: + throw new Error('Unknown encoding'); + } +}; + + +// byteLength +Buffer.byteLength = SlowBuffer.byteLength; + + +// copy(targetBuffer, targetStart, sourceStart, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, target_start, start, end) { + var source = this; + start || (start = 0); + end || (end = this.length); + + if (end < start) throw new Error("sourceEnd < sourceStart"); + + // Copy 0 bytes; we're done + if (end === start) return 0; + if (target.length == 0 || source.length == 0) return 0; + + if (target_start < 0 || target_start >= target.length) { + throw new Error("targetStart out of bounds"); + } + + if (start < 0 || start >= source.length) { + throw new Error("sourceStart out of bounds"); + } + + if (end < 0 || end > source.length) { + throw new Error("sourceEnd out of bounds"); + } + + // Are we oob? + if (end > this.length) { + end = this.length; + } + + if (target.length - target_start < end - start) { + end = target.length - target_start + start; + } + + return this.parent.copy(target.parent, + target_start + target.offset, + start + this.offset, + end + this.offset); +}; + + +// slice(start, end) +Buffer.prototype.slice = function (start, end) { + if (end > this.length) { + throw new Error("oob"); + } + if (start > end) { + throw new Error("oob"); + } + + return new Buffer(this.parent, end - start, +start + this.offset); }; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 599c83b6de..10ddbaf72b 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -43,49 +43,6 @@ static Persistent write_sym; Persistent Buffer::constructor_template; -// Each javascript Buffer object is backed by a Blob object. -// the Blob is just a C-level chunk of bytes. -// It has a reference count. -struct Blob_ { - unsigned int refs; - size_t length; - char *data; -}; -typedef struct Blob_ Blob; - - -static inline Blob * blob_new(size_t length) { - Blob * blob = (Blob*) malloc(sizeof(Blob)); - if (!blob) return NULL; - - blob->data = (char*) malloc(length); - if (!blob->data) { - free(blob); - return NULL; - } - - V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Blob) + length); - blob->length = length; - blob->refs = 0; - return blob; -} - - -static inline void blob_ref(Blob *blob) { - blob->refs++; -} - - -static inline void blob_unref(Blob *blob) { - assert(blob->refs > 0); - if (--blob->refs == 0) { - //fprintf(stderr, "free %d bytes\n", blob->length); - V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Blob) + blob->length)); - free(blob->data); - free(blob); - } -} - static inline size_t base64_decoded_size(const char *src, size_t size) { const char *const end = src + size; const int remainder = size % 4; @@ -110,39 +67,21 @@ static inline size_t base64_decoded_size(const char *src, size_t size) { return size; } -#if 0 -// When someone calls buffer.asciiSlice, data is not copied. Instead V8 -// references in the underlying Blob with this ExternalAsciiStringResource. -class AsciiSliceExt: public String::ExternalAsciiStringResource { - friend class Buffer; - public: - AsciiSliceExt(Buffer *parent, size_t start, size_t end) { - blob_ = parent->blob(); - blob_ref(blob_); - - assert(start <= end); - length_ = end - start; - assert(start + length_ <= parent->length()); - data_ = parent->data() + start; - } +static size_t ByteLength (Handle string, enum encoding enc) { + HandleScope scope; - ~AsciiSliceExt() { - //fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs); - blob_unref(blob_); + if (enc == UTF8) { + return string->Utf8Length(); + } else if (enc == BASE64) { + String::Utf8Value v(string); + return base64_decoded_size(*v, v.length()); + } else { + return string->Length(); } +} - const char* data() const { return data_; } - size_t length() const { return length_; } - - private: - const char *data_; - size_t length_; - Blob *blob_; -}; -#endif - Buffer* Buffer::New(size_t size) { HandleScope scope; @@ -153,6 +92,40 @@ Buffer* Buffer::New(size_t size) { } +char* Buffer::Data(Handle obj) { + if (obj->HasIndexedPropertiesInPixelData()) { + return (char*)obj->GetIndexedPropertiesPixelData(); + } + + HandleScope scope; + + // Return true for "SlowBuffer" + if (constructor_template->HasInstance(obj)) { + return ObjectWrap::Unwrap(obj)->data(); + } + + // Not a buffer. + return NULL; +} + + +size_t Buffer::Length(Handle obj) { + if (obj->HasIndexedPropertiesInPixelData()) { + return (size_t)obj->GetIndexedPropertiesPixelDataLength(); + } + + HandleScope scope; + + // Return true for "SlowBuffer" + if (constructor_template->HasInstance(obj)) { + return ObjectWrap::Unwrap(obj)->length(); + } + + // Not a buffer. + return 0; +} + + Handle Buffer::New(const Arguments &args) { HandleScope scope; @@ -172,31 +145,6 @@ Handle Buffer::New(const Arguments &args) { size_t length = args[0]->Uint32Value(); buffer = new Buffer(length); - } else if (args[0]->IsArray()) { - Local a = Local::Cast(args[0]); - buffer = new Buffer(a->Length()); - char *p = buffer->data(); - for (int i = 0; i < a->Length(); i++) { - p[i] = a->Get(i)->Uint32Value(); - } - } else if (args[0]->IsString()) { - Local s = args[0]->ToString(); - enum encoding e = ParseEncoding(args[1], UTF8); - int length = e == UTF8 ? s->Utf8Length() : s->Length(); - - // input gets base64-decoded, adjust buffer size - if (e == BASE64) { - const String::AsciiValue data(s); - length = base64_decoded_size(*data, data.length()); - } - - buffer = new Buffer(length); - } else if (Buffer::HasInstance(args[0]) && args.Length() > 2) { - // var slice = new Buffer(buffer, 123, 130); - // args: parent, start, end - Buffer *parent = ObjectWrap::Unwrap(args[0]->ToObject()); - SLICE_ARGS(args[1], args[2]) - buffer = new Buffer(parent, start, end); } else { return ThrowException(Exception::TypeError(String::New("Bad argument"))); } @@ -207,65 +155,28 @@ Handle Buffer::New(const Arguments &args) { buffer->length()); args.This()->Set(length_symbol, Integer::New(buffer->length_)); - if (args[0]->IsString()) { - if (write_sym.IsEmpty()) { - write_sym = Persistent::New(String::NewSymbol("write")); - } - - Local write_v = args.This()->Get(write_sym); - assert(write_v->IsFunction()); - Local write = Local::Cast(write_v); - - Local argv[2] = { args[0], args[1] }; - - TryCatch try_catch; - - write->Call(args.This(), 2, argv); - - if (try_catch.HasCaught()) { - FatalException(try_catch); - } - } - return args.This(); } Buffer::Buffer(size_t length) : ObjectWrap() { - blob_ = blob_new(length); off_ = 0; length_ = length; + data_ = new char[length_]; - blob_ref(blob_); - - V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer)); -} - - -Buffer::Buffer(Buffer *parent, size_t start, size_t end) : ObjectWrap() { - blob_ = parent->blob_; - assert(blob_->refs > 0); - blob_ref(blob_); - - assert(start <= end); - off_ = parent->off_ + start; - length_ = end - start; - assert(length_ <= parent->length_); - - V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer)); + V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer) + length_); } Buffer::~Buffer() { - assert(blob_->refs > 0); //fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs); - blob_unref(blob_); - V8::AdjustAmountOfExternalAllocatedMemory(-static_cast(sizeof(Buffer))); + delete data_; + V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Buffer) + length_)); } char* Buffer::data() { - return blob_->data + off_; + return data_; } @@ -274,7 +185,7 @@ Handle Buffer::BinarySlice(const Arguments &args) { Buffer *parent = ObjectWrap::Unwrap(args.This()); SLICE_ARGS(args[0], args[1]) - const char *data = const_cast(parent->data() + start); + char *data = parent->data() + start; //Local string = String::New(data, end - start); Local b = Encode(data, end - start, BINARY); @@ -288,18 +199,9 @@ Handle Buffer::AsciiSlice(const Arguments &args) { Buffer *parent = ObjectWrap::Unwrap(args.This()); SLICE_ARGS(args[0], args[1]) -#if 0 - AsciiSliceExt *ext = new AsciiSliceExt(parent, start, end); - Local string = String::NewExternal(ext); - // There should be at least two references to the blob now - the parent - // and the slice. - assert(parent->blob_->refs >= 2); -#endif - - const char *data = const_cast(parent->data() + start); + char* data = parent->data() + start; Local string = String::New(data, end - start); - return scope.Close(string); } @@ -308,7 +210,7 @@ Handle Buffer::Utf8Slice(const Arguments &args) { HandleScope scope; Buffer *parent = ObjectWrap::Unwrap(args.This()); SLICE_ARGS(args[0], args[1]) - const char *data = const_cast(parent->data() + start); + char *data = parent->data() + start; Local string = String::New(data, end - start); return scope.Close(string); } @@ -400,15 +302,6 @@ Handle Buffer::Base64Slice(const Arguments &args) { } -Handle Buffer::Slice(const Arguments &args) { - HandleScope scope; - Local argv[3] = { args.This(), args[0], args[1] }; - Local slice = - constructor_template->GetFunction()->NewInstance(3, argv); - return scope.Close(slice); -} - - // var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd); Handle Buffer::Copy(const Arguments &args) { HandleScope scope; @@ -452,25 +345,21 @@ Handle Buffer::Copy(const Arguments &args) { "sourceEnd out of bounds"))); } - ssize_t to_copy = MIN(source_end - source_start, - target->length() - target_start); + ssize_t to_copy = MIN(MIN(source_end - source_start, + target->length() - target_start), + source->length() - source_start); + - if (target->blob_ == source->blob_) { - // need to use slightly slower memmove is the ranges might overlap - memmove((void*)(target->data() + target_start), - (const void*)(source->data() + source_start), - to_copy); - } else { - memcpy((void*)(target->data() + target_start), - (const void*)(source->data() + source_start), - to_copy); - } + // need to use slightly slower memmove is the ranges might overlap + memmove((void*)(target->data() + target_start), + (const void*)(source->data() + source_start), + to_copy); return scope.Close(Integer::New(to_copy)); } -// var charsWritten = buffer.utf8Write(string, offset); +// var charsWritten = buffer.utf8Write(string, offset, [maxLength]); Handle Buffer::Utf8Write(const Arguments &args) { HandleScope scope; Buffer *buffer = ObjectWrap::Unwrap(args.This()); @@ -482,19 +371,23 @@ Handle Buffer::Utf8Write(const Arguments &args) { Local s = args[0]->ToString(); - size_t offset = args[1]->Int32Value(); + size_t offset = args[1]->Uint32Value(); if (s->Utf8Length() > 0 && offset >= buffer->length_) { return ThrowException(Exception::TypeError(String::New( "Offset is out of bounds"))); } - const char *p = buffer->data() + offset; + size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset + : args[2]->Uint32Value(); + max_length = MIN(buffer->length_ - offset, max_length); + + char* p = buffer->data() + offset; int char_written; - int written = s->WriteUtf8((char*)p, - buffer->length_ - offset, + int written = s->WriteUtf8(p, + max_length, &char_written, String::HINT_MANY_WRITES_EXPECTED); @@ -527,15 +420,20 @@ Handle Buffer::AsciiWrite(const Arguments &args) { "Offset is out of bounds"))); } - const char *p = buffer->data() + offset; + size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset + : args[2]->Uint32Value(); + max_length = MIN(s->Length(), MIN(buffer->length_ - offset, max_length)); - size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset); + char *p = buffer->data() + offset; - int written = s->WriteAscii((char*)p, 0, towrite, String::HINT_MANY_WRITES_EXPECTED); + int written = s->WriteAscii(p, + 0, + max_length, + String::HINT_MANY_WRITES_EXPECTED); return scope.Close(Integer::New(written)); } -// var bytesWritten = buffer.base64Write(string, offset); +// var bytesWritten = buffer.base64Write(string, offset, [maxLength]); Handle Buffer::Base64Write(const Arguments &args) { HandleScope scope; @@ -574,7 +472,7 @@ Handle Buffer::Base64Write(const Arguments &args) { } char a, b, c, d; - char *dst = buffer->data(); + char* dst = buffer->data() + offset; const char *src = *s; const char *const srcEnd = src + s.length(); @@ -628,70 +526,6 @@ Handle Buffer::BinaryWrite(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()); - uint32_t index = args[1]->Uint32Value(); - -#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.byteLength("string", "utf8") Handle Buffer::ByteLength(const Arguments &args) { HandleScope scope; @@ -704,10 +538,7 @@ Handle Buffer::ByteLength(const Arguments &args) { Local s = args[0]->ToString(); enum encoding e = ParseEncoding(args[1], UTF8); - Local length = - Integer::New(e == UTF8 ? s->Utf8Length() : s->Length()); - - return scope.Close(length); + return scope.Close(Integer::New(node::ByteLength(s, e))); } @@ -726,6 +557,18 @@ Handle Buffer::MakeFastBuffer(const Arguments &args) { } +bool Buffer::HasInstance(v8::Handle val) { + if (!val->IsObject()) return false; + v8::Local obj = val->ToObject(); + + if (obj->HasIndexedPropertiesInPixelData()) return true; + + // Return true for "SlowBuffer" + if (constructor_template->HasInstance(obj)) return true; + + return false; +} + void Buffer::Initialize(Handle target) { HandleScope scope; @@ -736,13 +579,12 @@ void Buffer::Initialize(Handle target) { Local t = FunctionTemplate::New(Buffer::New); constructor_template = Persistent::New(t); constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Buffer")); + constructor_template->SetClassName(String::NewSymbol("SlowBuffer")); // copy free NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice); NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice); NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Buffer::Slice); // TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice); // copy NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice); @@ -751,7 +593,6 @@ void Buffer::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite); NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite); NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "unpack", Buffer::Unpack); NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy); NODE_SET_METHOD(constructor_template->GetFunction(), @@ -761,7 +602,7 @@ void Buffer::Initialize(Handle target) { "makeFastBuffer", Buffer::MakeFastBuffer); - target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction()); + target->Set(String::NewSymbol("SlowBuffer"), constructor_template->GetFunction()); } diff --git a/src/node_buffer.h b/src/node_buffer.h index e1c037120a..b55b1d3ec2 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -34,15 +34,13 @@ class Buffer : public ObjectWrap { static void Initialize(v8::Handle target); static Buffer* New(size_t length); // public constructor - static inline bool HasInstance(v8::Handle val) { - if (!val->IsObject()) return false; - v8::Local obj = val->ToObject(); - return constructor_template->HasInstance(obj); - } + static bool HasInstance(v8::Handle val); + + static char* Data(v8::Handle); + static size_t Length(v8::Handle); char* data(); size_t length() const { return length_; } - struct Blob_* blob() const { return blob_; } int AsciiWrite(char *string, int offset, int length); int Utf8Write(char *string, int offset, int length); @@ -68,9 +66,9 @@ class Buffer : public ObjectWrap { Buffer(size_t length); Buffer(Buffer *parent, size_t start, size_t end); - size_t off_; // offset inside blob_ - size_t length_; // length inside blob_ - struct Blob_ *blob_; + size_t off_; + size_t length_; + char* data_; }; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 2fa9ddb52d..2934dfe7fa 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -322,21 +322,23 @@ Handle SecureStream::ReadInject(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[0]->ToObject()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[1]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } - int bytes_written = BIO_write(ss->pbioRead, (char*)buffer->data() + off, len); + int bytes_written = BIO_write(ss->pbioRead, (char*)buffer_data + off, len); if (bytes_written < 0) { if (errno == EAGAIN || errno == EINTR) return Null(); @@ -362,16 +364,18 @@ Handle SecureStream::ReadExtract(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[0]->ToObject()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[1]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } @@ -393,7 +397,7 @@ Handle SecureStream::ReadExtract(const Arguments& args) { return scope.Close(Integer::New(0)); } - bytes_read = SSL_read(ss->pSSL, (char*)buffer->data() + off, len); + bytes_read = SSL_read(ss->pSSL, (char*)buffer_data + off, len); if (bytes_read < 0) { int err = SSL_get_error(ss->pSSL, bytes_read); if (err == SSL_ERROR_WANT_READ) { @@ -445,21 +449,23 @@ Handle SecureStream::WriteExtract(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[0]->ToObject()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[1]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } - int bytes_read = BIO_read(ss->pbioWrite, (char*)buffer->data() + off, len); + int bytes_read = BIO_read(ss->pbioWrite, (char*)buffer_data + off, len); return scope.Close(Integer::New(bytes_read)); } @@ -480,16 +486,18 @@ Handle SecureStream::WriteInject(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[0]->ToObject()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[1]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } @@ -503,7 +511,7 @@ Handle SecureStream::WriteInject(const Arguments& args) { } return scope.Close(Integer::New(0)); } - int bytes_written = SSL_write(ss->pSSL, (char*)buffer->data() + off, len); + int bytes_written = SSL_write(ss->pSSL, (char*)buffer_data + off, len); return scope.Close(Integer::New(bytes_written)); } @@ -1001,8 +1009,11 @@ class Cipher : public ObjectWrap { unsigned char *out=0; int out_len=0; if (Buffer::HasInstance(args[0])) { - Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); - int r = cipher->CipherUpdate(buffer->data(), buffer->length(), &out, &out_len); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + + int r = cipher->CipherUpdate(buffer_data, buffer_length, &out, &out_len); } else { char* buf = new char[len]; ssize_t written = DecodeWrite(buf, len, args[0], enc); @@ -1344,9 +1355,12 @@ class Decipher : public ObjectWrap { // if alloc_buf then buf must be deleted later bool alloc_buf = false; if (Buffer::HasInstance(args[0])) { - Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); - buf = buffer->data(); - len = buffer->length(); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + + buf = buffer_data; + len = buffer_length; } else { alloc_buf = true; buf = new char[len]; @@ -1664,8 +1678,11 @@ class Hmac : public ObjectWrap { } if( Buffer::HasInstance(args[0])) { - Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); - int r = hmac->HmacUpdate(buffer->data(), buffer->length()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + + int r = hmac->HmacUpdate(buffer_data, buffer_length); } else { char* buf = new char[len]; ssize_t written = DecodeWrite(buf, len, args[0], enc); @@ -1811,8 +1828,11 @@ class Hash : public ObjectWrap { if (Buffer::HasInstance(args[0])) { - Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); - int r = hash->HashUpdate(buffer->data(), buffer->length()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + + int r = hash->HashUpdate(buffer_data, buffer_length); } else { char* buf = new char[len]; ssize_t written = DecodeWrite(buf, len, args[0], enc); @@ -1981,8 +2001,11 @@ class Sign : public ObjectWrap { } if (Buffer::HasInstance(args[0])) { - Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); - int r = sign->SignUpdate(buffer->data(), buffer->length()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + + int r = sign->SignUpdate(buffer_data, buffer_length); } else { char* buf = new char[len]; ssize_t written = DecodeWrite(buf, len, args[0], enc); @@ -2180,8 +2203,11 @@ class Verify : public ObjectWrap { } if(Buffer::HasInstance(args[0])) { - Buffer *buffer = ObjectWrap::Unwrap(args[0]->ToObject()); - int r = verify->VerifyUpdate(buffer->data(), buffer->length()); + Local buffer_obj = args[0]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); + + int r = verify->VerifyUpdate(buffer_data, buffer_length); } else { char* buf = new char[len]; ssize_t written = DecodeWrite(buf, len, args[0], enc); diff --git a/src/node_file.cc b/src/node_file.cc index 18c3704642..56876493b4 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -626,24 +626,26 @@ static Handle Write(const Arguments& args) { return ThrowException(Exception::Error( String::New("Second argument needs to be a buffer"))); } - - Buffer * buffer = ObjectWrap::Unwrap(args[1]->ToObject()); + + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[2]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } ssize_t len = args[3]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } off_t pos = GET_OFFSET(args[4]); - char * buf = (char*)buffer->data() + off; + char * buf = (char*)buffer_data + off; Local cb = args[5]; if (cb->IsFunction()) { @@ -688,23 +690,25 @@ static Handle Read(const Arguments& args) { String::New("Second argument needs to be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[1]->ToObject()); + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[2]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } len = args[3]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } pos = GET_OFFSET(args[4]); - buf = (char*)buffer->data() + off; + buf = buffer_data + off; cb = args[5]; diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 0ca04bd143..1524f4abd3 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -68,6 +68,15 @@ static Persistent upgrade_sym; static struct http_parser_settings settings; + +// This is a hack to get the current_buffer to the callbacks with the least +// amount of overhead. Nothing else will run while http_parser_execute() +// runs, therefore this pointer can be set and used for the execution. +static Local* current_buffer; +static char* current_buffer_data; +static size_t current_buffer_len; + + // Callback prototype for http_cb #define DEFINE_HTTP_CB(name) \ static int name(http_parser *p) { \ @@ -88,16 +97,16 @@ static struct http_parser_settings settings; #define DEFINE_HTTP_DATA_CB(name) \ static int name(http_parser *p, const char *at, size_t length) { \ Parser *parser = static_cast(p->data); \ - assert(parser->buffer_); \ + assert(current_buffer); \ Local cb_value = parser->handle_->Get(name##_sym); \ if (!cb_value->IsFunction()) return 0; \ Local cb = Local::Cast(cb_value); \ - Local argv[3] = { Local::New(parser->buffer_->handle_) \ - , Integer::New(at - parser->buffer_->data()) \ + Local argv[3] = { *current_buffer \ + , Integer::New(at - current_buffer_data) \ , Integer::New(length) \ }; \ Local ret = cb->Call(parser->handle_, 3, argv); \ - assert(parser->buffer_); \ + assert(current_buffer); \ if (ret.IsEmpty()) { \ parser->got_exception_ = true; \ return -1; \ @@ -137,12 +146,10 @@ method_to_str(unsigned short m) { class Parser : public ObjectWrap { public: Parser(enum http_parser_type type) : ObjectWrap() { - buffer_ = NULL; Init(type); } ~Parser() { - assert(buffer_ == NULL && "Destroying a parser while it's parsing"); } DEFINE_HTTP_CB(on_message_begin) @@ -214,7 +221,7 @@ class Parser : public ObjectWrap { } parser->Wrap(args.This()); - assert(!parser->buffer_); + assert(!current_buffer); return args.This(); } @@ -225,41 +232,50 @@ class Parser : public ObjectWrap { Parser *parser = ObjectWrap::Unwrap(args.This()); - assert(!parser->buffer_); - if (parser->buffer_) { + assert(!current_buffer); + assert(!current_buffer_data); + + if (current_buffer) { return ThrowException(Exception::TypeError( String::New("Already parsing a buffer"))); } - if (!Buffer::HasInstance(args[0])) { + Local buffer_v = args[0]; + + if (!Buffer::HasInstance(buffer_v)) { return ThrowException(Exception::TypeError( String::New("Argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[0]->ToObject()); + Local buffer_obj = buffer_v->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_len = Buffer::Length(buffer_obj); size_t off = args[1]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_len) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); - if (off+len > buffer->length()) { + if (off+len > buffer_len) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } // Assign 'buffer_' while we parse. The callbacks will access that varible. - parser->buffer_ = buffer; + current_buffer = &buffer_v; + current_buffer_data = buffer_data; + current_buffer_len = buffer_len; parser->got_exception_ = false; size_t nparsed = - http_parser_execute(&parser->parser_, &settings, buffer->data()+off, len); + http_parser_execute(&parser->parser_, &settings, buffer_data + off, len); // Unassign the 'buffer_' variable - assert(parser->buffer_); - parser->buffer_ = NULL; + assert(current_buffer); + current_buffer = NULL; + current_buffer_data = NULL; // If there was an exception in one of the callbacks if (parser->got_exception_) return Local(); @@ -282,7 +298,7 @@ class Parser : public ObjectWrap { Parser *parser = ObjectWrap::Unwrap(args.This()); - assert(!parser->buffer_); + assert(!current_buffer); parser->got_exception_ = false; http_parser_execute(&(parser->parser_), &settings, NULL, 0); @@ -313,13 +329,10 @@ class Parser : public ObjectWrap { private: void Init (enum http_parser_type type) { - assert(buffer_ == NULL); // don't call this during Execute() http_parser_init(&parser_, type); - parser_.data = this; } - Buffer * buffer_; // The buffer currently being parsed. bool got_exception_; http_parser parser_; }; diff --git a/src/node_net.cc b/src/node_net.cc index d4448aa1ae..4f458c000e 100644 --- a/src/node_net.cc +++ b/src/node_net.cc @@ -538,21 +538,23 @@ static Handle Read(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[1]->ToObject()); + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[2]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[3]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } - ssize_t bytes_read = read(fd, (char*)buffer->data() + off, len); + ssize_t bytes_read = read(fd, (char*)buffer_data + off, len); if (bytes_read < 0) { if (errno == EAGAIN || errno == EINTR) return Null(); @@ -583,16 +585,18 @@ static Handle RecvFrom(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[1]->ToObject()); + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[2]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[3]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } @@ -602,7 +606,7 @@ static Handle RecvFrom(const Arguments& args) { struct sockaddr_storage address_storage; socklen_t addrlen = sizeof(struct sockaddr_storage); - ssize_t bytes_read = recvfrom(fd, (char*)buffer->data() + off, len, flags, + ssize_t bytes_read = recvfrom(fd, (char*)buffer_data + off, len, flags, (struct sockaddr*) &address_storage, &addrlen); if (bytes_read < 0) { @@ -639,22 +643,24 @@ static Handle RecvMsg(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[1]->ToObject()); + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[2]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[3]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } struct iovec iov[1]; - iov[0].iov_base = (char*)buffer->data() + off; + iov[0].iov_base = (char*)buffer_data + off; iov[0].iov_len = len; struct msghdr msg; @@ -732,21 +738,23 @@ static Handle Write(const Arguments& args) { String::New("Second argument should be a buffer"))); } - Buffer * buffer = ObjectWrap::Unwrap(args[1]->ToObject()); + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t off = args[2]->Int32Value(); - if (off >= buffer->length()) { + if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[3]->Int32Value(); - if (off + len > buffer->length()) { + if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("Length is extends beyond buffer"))); } - ssize_t written = write(fd, (char*)buffer->data() + off, len); + ssize_t written = write(fd, buffer_data + off, len); if (written < 0) { if (errno == EAGAIN || errno == EINTR) { @@ -790,7 +798,9 @@ static Handle SendMsg(const Arguments& args) { String::New("Expected either a string or a buffer"))); } - Buffer *buf = ObjectWrap::Unwrap(args[1]->ToObject()); + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t offset = 0; if (args.Length() >= 3 && !args[2]->IsUndefined()) { @@ -800,13 +810,13 @@ static Handle SendMsg(const Arguments& args) { } offset = args[2]->Uint32Value(); - if (offset >= buf->length()) { + if (offset >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset into buffer too large"))); } } - size_t length = buf->length() - offset; + size_t length = buffer_length - offset; if (args.Length() >= 4 && !args[3]->IsUndefined()) { if (!args[3]->IsUint32()) { return ThrowException(Exception::TypeError( @@ -814,13 +824,13 @@ static Handle SendMsg(const Arguments& args) { } length = args[3]->Uint32Value(); - if (offset + length > buf->length()) { + if (offset + length > buffer_length) { return ThrowException(Exception::Error( String::New("offset + length beyond buffer length"))); } } - iov.iov_base = buf->data() + offset; + iov.iov_base = buffer_data + offset; iov.iov_len = length; int fd_to_send = -1; @@ -911,7 +921,9 @@ static Handle SendTo(const Arguments& args) { String::New("Expected either a string or a buffer"))); } - Buffer *buf = ObjectWrap::Unwrap(args[1]->ToObject()); + Local buffer_obj = args[1]->ToObject(); + char *buffer_data = Buffer::Data(buffer_obj); + size_t buffer_length = Buffer::Length(buffer_obj); size_t offset = 0; if (args.Length() >= 3 && !args[2]->IsUndefined()) { @@ -921,13 +933,13 @@ static Handle SendTo(const Arguments& args) { } offset = args[2]->Uint32Value(); - if (offset >= buf->length()) { + if (offset >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset into buffer too large"))); } } - size_t length = buf->length() - offset; + size_t length = buffer_length - offset; if (args.Length() >= 4 && !args[3]->IsUndefined()) { if (!args[3]->IsUint32()) { return ThrowException(Exception::TypeError( @@ -935,7 +947,7 @@ static Handle SendTo(const Arguments& args) { } length = args[3]->Uint32Value(); - if (offset + length > buf->length()) { + if (offset + length > buffer_length) { return ThrowException(Exception::Error( String::New("offset + length beyond buffer length"))); } @@ -954,7 +966,7 @@ static Handle SendTo(const Arguments& args) { Handle error = ParseAddressArgs(args[5], args[6], false); if (!error.IsEmpty()) return ThrowException(error); - ssize_t written = sendto(fd, buf->data() + offset, length, flags, addr, addrlen); + ssize_t written = sendto(fd, buffer_data + offset, length, flags, addr, addrlen); if (written < 0) { if (errno == EAGAIN || errno == EINTR) return Null(); diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index 7a3f1549d9..933b48ec46 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -19,11 +19,13 @@ for (var i = 0; i < 1024; i++) { } var c = new Buffer(512); +console.log("c.length == %d", c.length); +assert.strictEqual(512, c.length); // copy 512 bytes, from 0 to 512. var copied = b.copy(c, 0, 0, 512); console.log("copied " + copied + " bytes from b into c"); -assert.strictEqual(512, copied); +assert.equal(512, copied); for (var i = 0; i < c.length; i++) { common.print('.'); assert.equal(i % 256, c[i]); @@ -159,7 +161,7 @@ for (var j = 0; j < 500; j++) { var asciiSlice = b.toString('ascii', 0, asciiString.length); assert.equal(asciiString, asciiSlice); - var written = b.asciiWrite(asciiString, offset); + var written = b.write(asciiString, offset, 'ascii'); assert.equal(asciiString.length, written); var asciiSlice = b.toString('ascii', offset, offset+asciiString.length); assert.equal(asciiString, asciiSlice); @@ -183,35 +185,11 @@ for (var j = 0; j < 100; j++) { } -// unpack - -var b = new 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)); - // Bug regression test var testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語 var buffer = new Buffer(32); -var size = buffer.utf8Write(testValue, 0); +var size = buffer.write(testValue, 0, 'utf8'); console.log('bytes written to buffer: ' + size); var slice = buffer.toString('utf8', 0, size); assert.equal(slice, testValue); @@ -237,9 +215,11 @@ assert.equal(d[1], 42); assert.equal(d[2], 255); var e = new Buffer('über'); +console.error("uber: '%s'", e.toString()); assert.deepEqual(e, new Buffer([195, 188, 98, 101, 114])); var f = new Buffer('über', 'ascii'); +console.error("f.length: %d (should be 4)", f.length); assert.deepEqual(f, new Buffer([252, 98, 101, 114])); @@ -255,8 +235,8 @@ assert.equal(expected, (new Buffer(quote)).toString('base64')); b = new Buffer(1024); bytesWritten = b.write(expected, 0, 'base64'); -assert.equal(quote, b.toString('ascii', 0, quote.length)); assert.equal(quote.length, bytesWritten); +assert.equal(quote, b.toString('ascii', 0, quote.length)); assert.equal(new Buffer('', 'base64').toString(), ''); assert.equal(new Buffer('K', 'base64').toString(), ''); @@ -314,3 +294,21 @@ assert.equal(dot[1], 0xfe); assert.equal(dot[2], 0x2e); assert.equal(dot[3], 0x00); assert.equal(dot.toString('base64'), '//4uAA=='); + + +// Creating buffers larger than pool size. +l = Buffer.poolSize + 5; +s = "" +for (i = 0; i < l; i++) { + s += "h"; +} + +b = new Buffer(s); + +for (i = 0; i < l; i++) { + assert.equal("h".charCodeAt(0), b[i]); +} + +sb = b.toString(); +assert.equal(sb.length, s.length); +assert.equal(sb, s);