Browse Source

Merge branch 'fast-buffer'

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
98683d285c
  1. 6
      benchmark/buffer_creation.js
  2. 42
      benchmark/fast_buffer2.js
  3. 6
      benchmark/fast_buffer2_creation.js
  4. 4
      benchmark/fast_buffer_creation.js
  5. 282
      lib/buffer.js
  6. 339
      src/node_buffer.cc
  7. 16
      src/node_buffer.h
  8. 84
      src/node_crypto.cc
  9. 20
      src/node_file.cc
  10. 55
      src/node_http_parser.cc
  11. 64
      src/node_net.cc
  12. 54
      test/simple/test-buffer.js

6
benchmark/buffer_creation.js

@ -1,6 +1,6 @@
SlowBuffer = require('buffer').SlowBuffer;
for (var i = 0; i < 1e6; i++) {
for (var i = 0; i < 9e7; i++) { b = new SlowBuffer(10);
b = new Buffer(10);
b[1] = 2 b[1] = 2
} }

42
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++

6
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;
}

4
benchmark/fast_buffer_creation.js

@ -0,0 +1,4 @@
for (var i = 0; i < 1e6; i++) {
b = new Buffer(10);
b[1] = 2;
}

282
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) { function toHex (n) {
if (n < 16) return "0" + n.toString(16); if (n < 16) return "0" + n.toString(16);
return 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 = [], var out = [],
len = this.length; len = this.length;
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
out[i] = toHex(this[i]); out[i] = toHex(this[i]);
} }
return "<Buffer " + out.join(" ") + ">"; return "<SlowBuffer " + out.join(" ") + ">";
}; };
Buffer.prototype.toString = function (encoding, start, stop) {
SlowBuffer.prototype.toString = function (encoding, start, end) {
encoding = String(encoding || 'utf8').toLowerCase(); encoding = String(encoding || 'utf8').toLowerCase();
start = +start || 0; start = +start || 0;
if (typeof stop == "undefined") stop = this.length; if (typeof end == "undefined") end = this.length;
// Fastpath empty strings // Fastpath empty strings
if (+stop == start) { if (+end == start) {
return ''; return '';
} }
switch (encoding) { switch (encoding) {
case 'utf8': case 'utf8':
case 'utf-8': case 'utf-8':
return this.utf8Slice(start, stop); return this.utf8Slice(start, end);
case 'ascii': case 'ascii':
return this.asciiSlice(start, stop); return this.asciiSlice(start, end);
case 'binary': case 'binary':
return this.binarySlice(start, stop); return this.binarySlice(start, end);
case 'base64': case 'base64':
return this.base64Slice(start, stop); return this.base64Slice(start, end);
default: default:
throw new Error('Unknown encoding'); throw new Error('Unknown encoding');
} }
}; };
Buffer.prototype.write = function (string, offset, encoding) {
SlowBuffer.prototype.write = function (string, offset, encoding) {
// Support both (string, offset, encoding) // Support both (string, offset, encoding)
// and the legacy (string, encoding, offset) // and the legacy (string, encoding, offset)
if (!isFinite(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
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 " + out.join(" ") + ">";
};
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');
}
}; };
Buffer.prototype.set = function (index, value) {
return this[index] = value; // 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);
}; };

339
src/node_buffer.cc

@ -43,49 +43,6 @@ static Persistent<String> write_sym;
Persistent<FunctionTemplate> Buffer::constructor_template; Persistent<FunctionTemplate> 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) { static inline size_t base64_decoded_size(const char *src, size_t size) {
const char *const end = src + size; const char *const end = src + size;
const int remainder = size % 4; const int remainder = size % 4;
@ -110,39 +67,21 @@ static inline size_t base64_decoded_size(const char *src, size_t size) {
return 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> string, enum encoding enc) {
HandleScope scope;
~AsciiSliceExt() { if (enc == UTF8) {
//fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs); return string->Utf8Length();
blob_unref(blob_); } 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) { Buffer* Buffer::New(size_t size) {
HandleScope scope; HandleScope scope;
@ -153,6 +92,40 @@ Buffer* Buffer::New(size_t size) {
} }
char* Buffer::Data(Handle<Object> obj) {
if (obj->HasIndexedPropertiesInPixelData()) {
return (char*)obj->GetIndexedPropertiesPixelData();
}
HandleScope scope;
// Return true for "SlowBuffer"
if (constructor_template->HasInstance(obj)) {
return ObjectWrap::Unwrap<Buffer>(obj)->data();
}
// Not a buffer.
return NULL;
}
size_t Buffer::Length(Handle<Object> obj) {
if (obj->HasIndexedPropertiesInPixelData()) {
return (size_t)obj->GetIndexedPropertiesPixelDataLength();
}
HandleScope scope;
// Return true for "SlowBuffer"
if (constructor_template->HasInstance(obj)) {
return ObjectWrap::Unwrap<Buffer>(obj)->length();
}
// Not a buffer.
return 0;
}
Handle<Value> Buffer::New(const Arguments &args) { Handle<Value> Buffer::New(const Arguments &args) {
HandleScope scope; HandleScope scope;
@ -172,31 +145,6 @@ Handle<Value> Buffer::New(const Arguments &args) {
size_t length = args[0]->Uint32Value(); size_t length = args[0]->Uint32Value();
buffer = new Buffer(length); buffer = new Buffer(length);
} else if (args[0]->IsArray()) {
Local<Array> a = Local<Array>::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<String> 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<Buffer>(args[0]->ToObject());
SLICE_ARGS(args[1], args[2])
buffer = new Buffer(parent, start, end);
} else { } else {
return ThrowException(Exception::TypeError(String::New("Bad argument"))); return ThrowException(Exception::TypeError(String::New("Bad argument")));
} }
@ -207,65 +155,28 @@ Handle<Value> Buffer::New(const Arguments &args) {
buffer->length()); buffer->length());
args.This()->Set(length_symbol, Integer::New(buffer->length_)); args.This()->Set(length_symbol, Integer::New(buffer->length_));
if (args[0]->IsString()) {
if (write_sym.IsEmpty()) {
write_sym = Persistent<String>::New(String::NewSymbol("write"));
}
Local<Value> write_v = args.This()->Get(write_sym);
assert(write_v->IsFunction());
Local<Function> write = Local<Function>::Cast(write_v);
Local<Value> 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(); return args.This();
} }
Buffer::Buffer(size_t length) : ObjectWrap() { Buffer::Buffer(size_t length) : ObjectWrap() {
blob_ = blob_new(length);
off_ = 0; off_ = 0;
length_ = length; length_ = length;
data_ = new char[length_];
blob_ref(blob_); V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer) + length_);
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));
} }
Buffer::~Buffer() { Buffer::~Buffer() {
assert(blob_->refs > 0);
//fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs); //fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs);
blob_unref(blob_); delete data_;
V8::AdjustAmountOfExternalAllocatedMemory(-static_cast<long int>(sizeof(Buffer))); V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Buffer) + length_));
} }
char* Buffer::data() { char* Buffer::data() {
return blob_->data + off_; return data_;
} }
@ -274,7 +185,7 @@ Handle<Value> Buffer::BinarySlice(const Arguments &args) {
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
SLICE_ARGS(args[0], args[1]) SLICE_ARGS(args[0], args[1])
const char *data = const_cast<char*>(parent->data() + start); char *data = parent->data() + start;
//Local<String> string = String::New(data, end - start); //Local<String> string = String::New(data, end - start);
Local<Value> b = Encode(data, end - start, BINARY); Local<Value> b = Encode(data, end - start, BINARY);
@ -288,18 +199,9 @@ Handle<Value> Buffer::AsciiSlice(const Arguments &args) {
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
SLICE_ARGS(args[0], args[1]) SLICE_ARGS(args[0], args[1])
#if 0 char* data = parent->data() + start;
AsciiSliceExt *ext = new AsciiSliceExt(parent, start, end);
Local<String> 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<char*>(parent->data() + start);
Local<String> string = String::New(data, end - start); Local<String> string = String::New(data, end - start);
return scope.Close(string); return scope.Close(string);
} }
@ -308,7 +210,7 @@ Handle<Value> Buffer::Utf8Slice(const Arguments &args) {
HandleScope scope; HandleScope scope;
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
SLICE_ARGS(args[0], args[1]) SLICE_ARGS(args[0], args[1])
const char *data = const_cast<char*>(parent->data() + start); char *data = parent->data() + start;
Local<String> string = String::New(data, end - start); Local<String> string = String::New(data, end - start);
return scope.Close(string); return scope.Close(string);
} }
@ -400,15 +302,6 @@ Handle<Value> Buffer::Base64Slice(const Arguments &args) {
} }
Handle<Value> Buffer::Slice(const Arguments &args) {
HandleScope scope;
Local<Value> argv[3] = { args.This(), args[0], args[1] };
Local<Object> slice =
constructor_template->GetFunction()->NewInstance(3, argv);
return scope.Close(slice);
}
// var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd); // var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);
Handle<Value> Buffer::Copy(const Arguments &args) { Handle<Value> Buffer::Copy(const Arguments &args) {
HandleScope scope; HandleScope scope;
@ -452,25 +345,21 @@ Handle<Value> Buffer::Copy(const Arguments &args) {
"sourceEnd out of bounds"))); "sourceEnd out of bounds")));
} }
ssize_t to_copy = MIN(source_end - source_start, ssize_t to_copy = MIN(MIN(source_end - source_start,
target->length() - target_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 // need to use slightly slower memmove is the ranges might overlap
memmove((void*)(target->data() + target_start), memmove((void*)(target->data() + target_start),
(const void*)(source->data() + source_start), (const void*)(source->data() + source_start),
to_copy); to_copy);
} else {
memcpy((void*)(target->data() + target_start),
(const void*)(source->data() + source_start),
to_copy);
}
return scope.Close(Integer::New(to_copy)); return scope.Close(Integer::New(to_copy));
} }
// var charsWritten = buffer.utf8Write(string, offset); // var charsWritten = buffer.utf8Write(string, offset, [maxLength]);
Handle<Value> Buffer::Utf8Write(const Arguments &args) { Handle<Value> Buffer::Utf8Write(const Arguments &args) {
HandleScope scope; HandleScope scope;
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This()); Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
@ -482,19 +371,23 @@ Handle<Value> Buffer::Utf8Write(const Arguments &args) {
Local<String> s = args[0]->ToString(); Local<String> s = args[0]->ToString();
size_t offset = args[1]->Int32Value(); size_t offset = args[1]->Uint32Value();
if (s->Utf8Length() > 0 && offset >= buffer->length_) { if (s->Utf8Length() > 0 && offset >= buffer->length_) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
"Offset is out of bounds"))); "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 char_written;
int written = s->WriteUtf8((char*)p, int written = s->WriteUtf8(p,
buffer->length_ - offset, max_length,
&char_written, &char_written,
String::HINT_MANY_WRITES_EXPECTED); String::HINT_MANY_WRITES_EXPECTED);
@ -527,15 +420,20 @@ Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
"Offset is out of bounds"))); "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)); return scope.Close(Integer::New(written));
} }
// var bytesWritten = buffer.base64Write(string, offset); // var bytesWritten = buffer.base64Write(string, offset, [maxLength]);
Handle<Value> Buffer::Base64Write(const Arguments &args) { Handle<Value> Buffer::Base64Write(const Arguments &args) {
HandleScope scope; HandleScope scope;
@ -574,7 +472,7 @@ Handle<Value> Buffer::Base64Write(const Arguments &args) {
} }
char a, b, c, d; char a, b, c, d;
char *dst = buffer->data(); char* dst = buffer->data() + offset;
const char *src = *s; const char *src = *s;
const char *const srcEnd = src + s.length(); const char *const srcEnd = src + s.length();
@ -628,70 +526,6 @@ Handle<Value> 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<Value> Buffer::Unpack(const Arguments &args) {
HandleScope scope;
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(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 = 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") // var nbytes = Buffer.byteLength("string", "utf8")
Handle<Value> Buffer::ByteLength(const Arguments &args) { Handle<Value> Buffer::ByteLength(const Arguments &args) {
HandleScope scope; HandleScope scope;
@ -704,10 +538,7 @@ Handle<Value> Buffer::ByteLength(const Arguments &args) {
Local<String> s = args[0]->ToString(); Local<String> s = args[0]->ToString();
enum encoding e = ParseEncoding(args[1], UTF8); enum encoding e = ParseEncoding(args[1], UTF8);
Local<Integer> length = return scope.Close(Integer::New(node::ByteLength(s, e)));
Integer::New(e == UTF8 ? s->Utf8Length() : s->Length());
return scope.Close(length);
} }
@ -726,6 +557,18 @@ Handle<Value> Buffer::MakeFastBuffer(const Arguments &args) {
} }
bool Buffer::HasInstance(v8::Handle<v8::Value> val) {
if (!val->IsObject()) return false;
v8::Local<v8::Object> 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<Object> target) { void Buffer::Initialize(Handle<Object> target) {
HandleScope scope; HandleScope scope;
@ -736,13 +579,12 @@ void Buffer::Initialize(Handle<Object> target) {
Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New); Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New);
constructor_template = Persistent<FunctionTemplate>::New(t); constructor_template = Persistent<FunctionTemplate>::New(t);
constructor_template->InstanceTemplate()->SetInternalFieldCount(1); constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
constructor_template->SetClassName(String::NewSymbol("Buffer")); constructor_template->SetClassName(String::NewSymbol("SlowBuffer"));
// copy free // copy free
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice); NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice); NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice); 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); // TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
// copy // copy
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice); NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);
@ -751,7 +593,6 @@ void Buffer::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite); NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite); NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write); 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_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy);
NODE_SET_METHOD(constructor_template->GetFunction(), NODE_SET_METHOD(constructor_template->GetFunction(),
@ -761,7 +602,7 @@ void Buffer::Initialize(Handle<Object> target) {
"makeFastBuffer", "makeFastBuffer",
Buffer::MakeFastBuffer); Buffer::MakeFastBuffer);
target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction()); target->Set(String::NewSymbol("SlowBuffer"), constructor_template->GetFunction());
} }

16
src/node_buffer.h

@ -34,15 +34,13 @@ class Buffer : public ObjectWrap {
static void Initialize(v8::Handle<v8::Object> target); static void Initialize(v8::Handle<v8::Object> target);
static Buffer* New(size_t length); // public constructor static Buffer* New(size_t length); // public constructor
static inline bool HasInstance(v8::Handle<v8::Value> val) { static bool HasInstance(v8::Handle<v8::Value> val);
if (!val->IsObject()) return false;
v8::Local<v8::Object> obj = val->ToObject(); static char* Data(v8::Handle<v8::Object>);
return constructor_template->HasInstance(obj); static size_t Length(v8::Handle<v8::Object>);
}
char* data(); char* data();
size_t length() const { return length_; } size_t length() const { return length_; }
struct Blob_* blob() const { return blob_; }
int AsciiWrite(char *string, int offset, int length); int AsciiWrite(char *string, int offset, int length);
int Utf8Write(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(size_t length);
Buffer(Buffer *parent, size_t start, size_t end); Buffer(Buffer *parent, size_t start, size_t end);
size_t off_; // offset inside blob_ size_t off_;
size_t length_; // length inside blob_ size_t length_;
struct Blob_ *blob_; char* data_;
}; };

84
src/node_crypto.cc

@ -322,21 +322,23 @@ Handle<Value> SecureStream::ReadInject(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> 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(); size_t off = args[1]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[2]->Int32Value(); size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); 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 (bytes_written < 0) {
if (errno == EAGAIN || errno == EINTR) return Null(); if (errno == EAGAIN || errno == EINTR) return Null();
@ -362,16 +364,18 @@ Handle<Value> SecureStream::ReadExtract(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> 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(); size_t off = args[1]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[2]->Int32Value(); size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
@ -393,7 +397,7 @@ Handle<Value> SecureStream::ReadExtract(const Arguments& args) {
return scope.Close(Integer::New(0)); 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) { if (bytes_read < 0) {
int err = SSL_get_error(ss->pSSL, bytes_read); int err = SSL_get_error(ss->pSSL, bytes_read);
if (err == SSL_ERROR_WANT_READ) { if (err == SSL_ERROR_WANT_READ) {
@ -445,21 +449,23 @@ Handle<Value> SecureStream::WriteExtract(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> 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(); size_t off = args[1]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[2]->Int32Value(); size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); 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)); return scope.Close(Integer::New(bytes_read));
} }
@ -480,16 +486,18 @@ Handle<Value> SecureStream::WriteInject(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> 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(); size_t off = args[1]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[2]->Int32Value(); size_t len = args[2]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
@ -503,7 +511,7 @@ Handle<Value> SecureStream::WriteInject(const Arguments& args) {
} }
return scope.Close(Integer::New(0)); 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)); return scope.Close(Integer::New(bytes_written));
} }
@ -1001,8 +1009,11 @@ class Cipher : public ObjectWrap {
unsigned char *out=0; unsigned char *out=0;
int out_len=0; int out_len=0;
if (Buffer::HasInstance(args[0])) { if (Buffer::HasInstance(args[0])) {
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> buffer_obj = args[0]->ToObject();
int r = cipher->CipherUpdate(buffer->data(), buffer->length(), &out, &out_len); 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 { } else {
char* buf = new char[len]; char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc); 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 // if alloc_buf then buf must be deleted later
bool alloc_buf = false; bool alloc_buf = false;
if (Buffer::HasInstance(args[0])) { if (Buffer::HasInstance(args[0])) {
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> buffer_obj = args[0]->ToObject();
buf = buffer->data(); char *buffer_data = Buffer::Data(buffer_obj);
len = buffer->length(); size_t buffer_length = Buffer::Length(buffer_obj);
buf = buffer_data;
len = buffer_length;
} else { } else {
alloc_buf = true; alloc_buf = true;
buf = new char[len]; buf = new char[len];
@ -1664,8 +1678,11 @@ class Hmac : public ObjectWrap {
} }
if( Buffer::HasInstance(args[0])) { if( Buffer::HasInstance(args[0])) {
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> buffer_obj = args[0]->ToObject();
int r = hmac->HmacUpdate(buffer->data(), buffer->length()); char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
int r = hmac->HmacUpdate(buffer_data, buffer_length);
} else { } else {
char* buf = new char[len]; char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc); ssize_t written = DecodeWrite(buf, len, args[0], enc);
@ -1811,8 +1828,11 @@ class Hash : public ObjectWrap {
if (Buffer::HasInstance(args[0])) { if (Buffer::HasInstance(args[0])) {
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> buffer_obj = args[0]->ToObject();
int r = hash->HashUpdate(buffer->data(), buffer->length()); char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
int r = hash->HashUpdate(buffer_data, buffer_length);
} else { } else {
char* buf = new char[len]; char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc); ssize_t written = DecodeWrite(buf, len, args[0], enc);
@ -1981,8 +2001,11 @@ class Sign : public ObjectWrap {
} }
if (Buffer::HasInstance(args[0])) { if (Buffer::HasInstance(args[0])) {
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> buffer_obj = args[0]->ToObject();
int r = sign->SignUpdate(buffer->data(), buffer->length()); char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
int r = sign->SignUpdate(buffer_data, buffer_length);
} else { } else {
char* buf = new char[len]; char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc); ssize_t written = DecodeWrite(buf, len, args[0], enc);
@ -2180,8 +2203,11 @@ class Verify : public ObjectWrap {
} }
if(Buffer::HasInstance(args[0])) { if(Buffer::HasInstance(args[0])) {
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> buffer_obj = args[0]->ToObject();
int r = verify->VerifyUpdate(buffer->data(), buffer->length()); char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
int r = verify->VerifyUpdate(buffer_data, buffer_length);
} else { } else {
char* buf = new char[len]; char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc); ssize_t written = DecodeWrite(buf, len, args[0], enc);

20
src/node_file.cc

@ -627,23 +627,25 @@ static Handle<Value> Write(const Arguments& args) {
String::New("Second argument needs to be a buffer"))); String::New("Second argument needs to be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> 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(); size_t off = args[2]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
ssize_t len = args[3]->Int32Value(); ssize_t len = args[3]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
off_t pos = GET_OFFSET(args[4]); off_t pos = GET_OFFSET(args[4]);
char * buf = (char*)buffer->data() + off; char * buf = (char*)buffer_data + off;
Local<Value> cb = args[5]; Local<Value> cb = args[5];
if (cb->IsFunction()) { if (cb->IsFunction()) {
@ -688,23 +690,25 @@ static Handle<Value> Read(const Arguments& args) {
String::New("Second argument needs to be a buffer"))); String::New("Second argument needs to be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> 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(); size_t off = args[2]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
len = args[3]->Int32Value(); len = args[3]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
pos = GET_OFFSET(args[4]); pos = GET_OFFSET(args[4]);
buf = (char*)buffer->data() + off; buf = buffer_data + off;
cb = args[5]; cb = args[5];

55
src/node_http_parser.cc

@ -68,6 +68,15 @@ static Persistent<String> upgrade_sym;
static struct http_parser_settings settings; 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<Value>* current_buffer;
static char* current_buffer_data;
static size_t current_buffer_len;
// Callback prototype for http_cb // Callback prototype for http_cb
#define DEFINE_HTTP_CB(name) \ #define DEFINE_HTTP_CB(name) \
static int name(http_parser *p) { \ static int name(http_parser *p) { \
@ -88,16 +97,16 @@ static struct http_parser_settings settings;
#define DEFINE_HTTP_DATA_CB(name) \ #define DEFINE_HTTP_DATA_CB(name) \
static int name(http_parser *p, const char *at, size_t length) { \ static int name(http_parser *p, const char *at, size_t length) { \
Parser *parser = static_cast<Parser*>(p->data); \ Parser *parser = static_cast<Parser*>(p->data); \
assert(parser->buffer_); \ assert(current_buffer); \
Local<Value> cb_value = parser->handle_->Get(name##_sym); \ Local<Value> cb_value = parser->handle_->Get(name##_sym); \
if (!cb_value->IsFunction()) return 0; \ if (!cb_value->IsFunction()) return 0; \
Local<Function> cb = Local<Function>::Cast(cb_value); \ Local<Function> cb = Local<Function>::Cast(cb_value); \
Local<Value> argv[3] = { Local<Value>::New(parser->buffer_->handle_) \ Local<Value> argv[3] = { *current_buffer \
, Integer::New(at - parser->buffer_->data()) \ , Integer::New(at - current_buffer_data) \
, Integer::New(length) \ , Integer::New(length) \
}; \ }; \
Local<Value> ret = cb->Call(parser->handle_, 3, argv); \ Local<Value> ret = cb->Call(parser->handle_, 3, argv); \
assert(parser->buffer_); \ assert(current_buffer); \
if (ret.IsEmpty()) { \ if (ret.IsEmpty()) { \
parser->got_exception_ = true; \ parser->got_exception_ = true; \
return -1; \ return -1; \
@ -137,12 +146,10 @@ method_to_str(unsigned short m) {
class Parser : public ObjectWrap { class Parser : public ObjectWrap {
public: public:
Parser(enum http_parser_type type) : ObjectWrap() { Parser(enum http_parser_type type) : ObjectWrap() {
buffer_ = NULL;
Init(type); Init(type);
} }
~Parser() { ~Parser() {
assert(buffer_ == NULL && "Destroying a parser while it's parsing");
} }
DEFINE_HTTP_CB(on_message_begin) DEFINE_HTTP_CB(on_message_begin)
@ -214,7 +221,7 @@ class Parser : public ObjectWrap {
} }
parser->Wrap(args.This()); parser->Wrap(args.This());
assert(!parser->buffer_); assert(!current_buffer);
return args.This(); return args.This();
} }
@ -225,41 +232,50 @@ class Parser : public ObjectWrap {
Parser *parser = ObjectWrap::Unwrap<Parser>(args.This()); Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
assert(!parser->buffer_); assert(!current_buffer);
if (parser->buffer_) { assert(!current_buffer_data);
if (current_buffer) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("Already parsing a buffer"))); String::New("Already parsing a buffer")));
} }
if (!Buffer::HasInstance(args[0])) { Local<Value> buffer_v = args[0];
if (!Buffer::HasInstance(buffer_v)) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("Argument should be a buffer"))); String::New("Argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); Local<Object> 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(); size_t off = args[1]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_len) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[2]->Int32Value(); size_t len = args[2]->Int32Value();
if (off+len > buffer->length()) { if (off+len > buffer_len) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
// Assign 'buffer_' while we parse. The callbacks will access that varible. // 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; parser->got_exception_ = false;
size_t nparsed = 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 // Unassign the 'buffer_' variable
assert(parser->buffer_); assert(current_buffer);
parser->buffer_ = NULL; current_buffer = NULL;
current_buffer_data = NULL;
// If there was an exception in one of the callbacks // If there was an exception in one of the callbacks
if (parser->got_exception_) return Local<Value>(); if (parser->got_exception_) return Local<Value>();
@ -282,7 +298,7 @@ class Parser : public ObjectWrap {
Parser *parser = ObjectWrap::Unwrap<Parser>(args.This()); Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
assert(!parser->buffer_); assert(!current_buffer);
parser->got_exception_ = false; parser->got_exception_ = false;
http_parser_execute(&(parser->parser_), &settings, NULL, 0); http_parser_execute(&(parser->parser_), &settings, NULL, 0);
@ -313,13 +329,10 @@ class Parser : public ObjectWrap {
private: private:
void Init (enum http_parser_type type) { void Init (enum http_parser_type type) {
assert(buffer_ == NULL); // don't call this during Execute()
http_parser_init(&parser_, type); http_parser_init(&parser_, type);
parser_.data = this; parser_.data = this;
} }
Buffer * buffer_; // The buffer currently being parsed.
bool got_exception_; bool got_exception_;
http_parser parser_; http_parser parser_;
}; };

64
src/node_net.cc

@ -538,21 +538,23 @@ static Handle<Value> Read(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> 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(); size_t off = args[2]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[3]->Int32Value(); size_t len = args[3]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); 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 (bytes_read < 0) {
if (errno == EAGAIN || errno == EINTR) return Null(); if (errno == EAGAIN || errno == EINTR) return Null();
@ -583,16 +585,18 @@ static Handle<Value> RecvFrom(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> 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(); size_t off = args[2]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[3]->Int32Value(); size_t len = args[3]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
@ -602,7 +606,7 @@ static Handle<Value> RecvFrom(const Arguments& args) {
struct sockaddr_storage address_storage; struct sockaddr_storage address_storage;
socklen_t addrlen = sizeof(struct sockaddr_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); (struct sockaddr*) &address_storage, &addrlen);
if (bytes_read < 0) { if (bytes_read < 0) {
@ -639,22 +643,24 @@ static Handle<Value> RecvMsg(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> 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(); size_t off = args[2]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[3]->Int32Value(); size_t len = args[3]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
struct iovec iov[1]; 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; iov[0].iov_len = len;
struct msghdr msg; struct msghdr msg;
@ -732,21 +738,23 @@ static Handle<Value> Write(const Arguments& args) {
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> 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(); size_t off = args[2]->Int32Value();
if (off >= buffer->length()) { if (off >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
size_t len = args[3]->Int32Value(); size_t len = args[3]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); 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 (written < 0) {
if (errno == EAGAIN || errno == EINTR) { if (errno == EAGAIN || errno == EINTR) {
@ -790,7 +798,9 @@ static Handle<Value> SendMsg(const Arguments& args) {
String::New("Expected either a string or a buffer"))); String::New("Expected either a string or a buffer")));
} }
Buffer *buf = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> buffer_obj = args[1]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
size_t offset = 0; size_t offset = 0;
if (args.Length() >= 3 && !args[2]->IsUndefined()) { if (args.Length() >= 3 && !args[2]->IsUndefined()) {
@ -800,13 +810,13 @@ static Handle<Value> SendMsg(const Arguments& args) {
} }
offset = args[2]->Uint32Value(); offset = args[2]->Uint32Value();
if (offset >= buf->length()) { if (offset >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset into buffer too large"))); 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.Length() >= 4 && !args[3]->IsUndefined()) {
if (!args[3]->IsUint32()) { if (!args[3]->IsUint32()) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
@ -814,13 +824,13 @@ static Handle<Value> SendMsg(const Arguments& args) {
} }
length = args[3]->Uint32Value(); length = args[3]->Uint32Value();
if (offset + length > buf->length()) { if (offset + length > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("offset + length beyond buffer length"))); String::New("offset + length beyond buffer length")));
} }
} }
iov.iov_base = buf->data() + offset; iov.iov_base = buffer_data + offset;
iov.iov_len = length; iov.iov_len = length;
int fd_to_send = -1; int fd_to_send = -1;
@ -911,7 +921,9 @@ static Handle<Value> SendTo(const Arguments& args) {
String::New("Expected either a string or a buffer"))); String::New("Expected either a string or a buffer")));
} }
Buffer *buf = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Local<Object> buffer_obj = args[1]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
size_t offset = 0; size_t offset = 0;
if (args.Length() >= 3 && !args[2]->IsUndefined()) { if (args.Length() >= 3 && !args[2]->IsUndefined()) {
@ -921,13 +933,13 @@ static Handle<Value> SendTo(const Arguments& args) {
} }
offset = args[2]->Uint32Value(); offset = args[2]->Uint32Value();
if (offset >= buf->length()) { if (offset >= buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Offset into buffer too large"))); 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.Length() >= 4 && !args[3]->IsUndefined()) {
if (!args[3]->IsUint32()) { if (!args[3]->IsUint32()) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
@ -935,7 +947,7 @@ static Handle<Value> SendTo(const Arguments& args) {
} }
length = args[3]->Uint32Value(); length = args[3]->Uint32Value();
if (offset + length > buf->length()) { if (offset + length > buffer_length) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("offset + length beyond buffer length"))); String::New("offset + length beyond buffer length")));
} }
@ -954,7 +966,7 @@ static Handle<Value> SendTo(const Arguments& args) {
Handle<Value> error = ParseAddressArgs(args[5], args[6], false); Handle<Value> error = ParseAddressArgs(args[5], args[6], false);
if (!error.IsEmpty()) return ThrowException(error); 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 (written < 0) {
if (errno == EAGAIN || errno == EINTR) return Null(); if (errno == EAGAIN || errno == EINTR) return Null();

54
test/simple/test-buffer.js

@ -19,11 +19,13 @@ for (var i = 0; i < 1024; i++) {
} }
var c = new Buffer(512); var c = new Buffer(512);
console.log("c.length == %d", c.length);
assert.strictEqual(512, c.length);
// copy 512 bytes, from 0 to 512. // copy 512 bytes, from 0 to 512.
var copied = b.copy(c, 0, 0, 512); var copied = b.copy(c, 0, 0, 512);
console.log("copied " + copied + " bytes from b into c"); 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++) { for (var i = 0; i < c.length; i++) {
common.print('.'); common.print('.');
assert.equal(i % 256, c[i]); 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); var asciiSlice = b.toString('ascii', 0, asciiString.length);
assert.equal(asciiString, asciiSlice); assert.equal(asciiString, asciiSlice);
var written = b.asciiWrite(asciiString, offset); var written = b.write(asciiString, offset, 'ascii');
assert.equal(asciiString.length, written); assert.equal(asciiString.length, written);
var asciiSlice = b.toString('ascii', offset, offset+asciiString.length); var asciiSlice = b.toString('ascii', offset, offset+asciiString.length);
assert.equal(asciiString, asciiSlice); 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 // Bug regression test
var testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語 var testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語
var buffer = new Buffer(32); 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); console.log('bytes written to buffer: ' + size);
var slice = buffer.toString('utf8', 0, size); var slice = buffer.toString('utf8', 0, size);
assert.equal(slice, testValue); assert.equal(slice, testValue);
@ -237,9 +215,11 @@ assert.equal(d[1], 42);
assert.equal(d[2], 255); assert.equal(d[2], 255);
var e = new Buffer('über'); var e = new Buffer('über');
console.error("uber: '%s'", e.toString());
assert.deepEqual(e, new Buffer([195, 188, 98, 101, 114])); assert.deepEqual(e, new Buffer([195, 188, 98, 101, 114]));
var f = new Buffer('über', 'ascii'); 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])); 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); b = new Buffer(1024);
bytesWritten = b.write(expected, 0, 'base64'); bytesWritten = b.write(expected, 0, 'base64');
assert.equal(quote, b.toString('ascii', 0, quote.length));
assert.equal(quote.length, bytesWritten); assert.equal(quote.length, bytesWritten);
assert.equal(quote, b.toString('ascii', 0, quote.length));
assert.equal(new Buffer('', 'base64').toString(), ''); assert.equal(new Buffer('', 'base64').toString(), '');
assert.equal(new Buffer('K', '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[2], 0x2e);
assert.equal(dot[3], 0x00); assert.equal(dot[3], 0x00);
assert.equal(dot.toString('base64'), '//4uAA=='); 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);

Loading…
Cancel
Save