Browse Source

Reimplment Buffers

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
bf803f478b
  1. 2
      src/node.cc
  2. 381
      src/node_buffer.cc
  3. 66
      src/node_buffer.h
  4. 70
      src/node_http_parser.cc
  5. 51
      src/node_io_watcher.cc
  6. 38
      src/node_net2.cc
  7. 5
      src/node_object_wrap.h
  8. 21
      test/mjsunit/test-buffer.js
  9. 2
      wscript

2
src/node.cc

@ -989,7 +989,7 @@ static Local<Object> Load(int argc, char *argv[]) {
// Initialize the C++ modules..................filename of module // Initialize the C++ modules..................filename of module
InitBuffer(process); // buffer.cc Buffer::Initialize(process); // buffer.cc
IOWatcher::Initialize(process); // io_watcher.cc IOWatcher::Initialize(process); // io_watcher.cc
IdleWatcher::Initialize(process); // idle_watcher.cc IdleWatcher::Initialize(process); // idle_watcher.cc
Timer::Initialize(process); // timer.cc Timer::Initialize(process); // timer.cc

381
src/node_buffer.cc

@ -10,254 +10,198 @@ namespace node {
using namespace v8; using namespace v8;
#define SLICE_ARGS(start_arg, end_arg) \ #define SLICE_ARGS(start_arg, end_arg) \
if (!start_arg->IsInt32() || !end_arg->IsInt32()) { \ if (!start_arg->IsInt32() || !end_arg->IsInt32()) { \
return ThrowException(Exception::TypeError( \ return ThrowException(Exception::TypeError( \
String::New("Bad argument."))); \ String::New("Bad argument."))); \
} \ } \
int32_t start = start_arg->Int32Value(); \ int32_t start = start_arg->Int32Value(); \
int32_t end = end_arg->Int32Value(); \ int32_t end = end_arg->Int32Value(); \
if (start < 0 || end < 0) { \ if (start < 0 || end < 0) { \
return ThrowException(Exception::TypeError( \ return ThrowException(Exception::TypeError( \
String::New("Bad argument."))); \ String::New("Bad argument."))); \
} \
if (!(start <= end)) { \
return ThrowException(Exception::Error( \
String::New("Must have start <= end"))); \
} \
if ((size_t)end > parent->length_) { \
return ThrowException(Exception::Error( \
String::New("end cannot be longer than parent.length"))); \
} }
static Persistent<String> length_symbol;
static Persistent<FunctionTemplate> constructor_template;
bool IsBuffer(v8::Handle<v8::Value> val) { static Persistent<String> length_symbol;
if (!val->IsObject()) return false; Persistent<FunctionTemplate> Buffer::constructor_template;
Local<Object> obj = val->ToObject();
return constructor_template->HasInstance(obj);
}
/* Determines the absolute position for a relative offset */ // Each javascript Buffer object is backed by a Blob object.
size_t buffer_abs_off(buffer *buffer, size_t off) { // the Blob is just a C-level chunk of bytes.
struct buffer *root = buffer_root(buffer); // It has a reference count.
off += buffer->offset; struct Blob_ {
return MIN(root->length, off); unsigned int refs;
size_t length;
char data[1];
};
typedef struct Blob_ Blob;
static inline Blob * blob_new(size_t length) {
size_t s = sizeof(Blob) - 1 + length;
Blob * blob = (Blob*) malloc(s);
if (!blob) return NULL;
V8::AdjustAmountOfExternalAllocatedMemory(s);
blob->length = length;
blob->refs = 0;
//fprintf(stderr, "alloc %d bytes\n", length);
return blob;
} }
void buffer_ref(struct buffer *buffer) { static inline void blob_ref(Blob *blob) {
buffer_root(buffer)->refs++; blob->refs++;
} }
struct buffer* BufferUnwrap(v8::Handle<v8::Value> val) { static inline void blob_unref(Blob *blob) {
assert(val->IsObject()); assert(blob->refs > 0);
HandleScope scope; if (--blob->refs == 0) {
Local<Object> obj = val->ToObject(); //fprintf(stderr, "free %d bytes\n", blob->length);
assert(obj->InternalFieldCount() == 1); size_t s = sizeof(Blob) - 1 + blob->length;
Local<External> ext = Local<External>::Cast(obj->GetInternalField(0)); V8::AdjustAmountOfExternalAllocatedMemory(-s);
return static_cast<struct buffer*>(ext->Value()); free(blob);
}
} }
static void RootWeakCallback(Persistent<Value> value, void *data) // When someone calls buffer.asciiSlice, data is not copied. Instead V8
{ // references in the underlying Blob with this ExternalAsciiStringResource.
struct buffer *buffer = static_cast<struct buffer*>(data); class AsciiSliceExt: public String::ExternalAsciiStringResource {
assert(buffer->root == NULL); // this is the root friend class Buffer;
assert(value == buffer->handle); public:
value.ClearWeak(); AsciiSliceExt(Buffer *parent, size_t start, size_t end) {
if (buffer->refs) { blob_ = parent->blob();
buffer->weak = true; blob_ref(blob_);
} else {
buffer->handle.Dispose(); assert(start <= end);
free(buffer); length_ = end - start;
assert(length_ <= parent->length());
data_ = parent->data() + start;
} }
}
void buffer_unref(struct buffer *buffer) { ~AsciiSliceExt() {
struct buffer * root = buffer_root(buffer); //fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs);
assert(root->refs > 0); blob_unref(blob_);
root->refs--;
if (root->refs == 0 && root->weak) {
root->handle.MakeWeak(root, RootWeakCallback);
} }
}
static void SliceWeakCallback(Persistent<Value> value, void *data) const char* data() const { return data_; }
{ size_t length() const { return length_; }
struct buffer *buffer = static_cast<struct buffer*>(data);
assert(buffer->root != NULL); // this is a slice private:
assert(value == buffer->handle); const char *data_;
buffer->handle.Dispose(); size_t length_;
buffer_unref(buffer->root); Blob *blob_;
} };
static Handle<Value> Constructor(const Arguments &args) { Handle<Value> Buffer::New(const Arguments &args) {
HandleScope scope; HandleScope scope;
size_t length; Buffer *buffer;
struct buffer *buffer; if (args[0]->IsInt32()) {
// var buffer = new Buffer(1024);
size_t length = args[0]->Uint32Value();
buffer = new Buffer(length);
if (constructor_template->HasInstance(args[0])) { } else if (Buffer::HasInstance(args[0]) && args.Length() > 2) {
// slice slice // 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]) SLICE_ARGS(args[1], args[2])
buffer = new Buffer(parent, start, end);
struct buffer *parent = BufferUnwrap(args[0]);
size_t start_abs = buffer_abs_off(parent, start);
size_t end_abs = buffer_abs_off(parent, end);
assert(start_abs <= end_abs);
length = end_abs - start_abs;
void *d = malloc(sizeof(struct buffer));
if (!d) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
buffer = static_cast<struct buffer*>(d);
buffer->length = length;
buffer->offset = start_abs;
buffer->weak = false;
buffer->refs = 0;
buffer->root = buffer_root(parent);
buffer->handle = Persistent<Object>::New(args.This());
buffer->handle.MakeWeak(buffer, SliceWeakCallback);
buffer_ref(buffer->root);
} else { } else {
// Root slice return ThrowException(Exception::TypeError(String::New("Bad argument")));
length = args[0]->Uint32Value();
if (length < 1) {
return ThrowException(Exception::TypeError(
String::New("Bad argument. Length must be positive")));
}
// TODO alignment. modify the length?
void *d = malloc(sizeof(struct buffer) + length - 1);
if (!d) {
V8::LowMemoryNotification();
return ThrowException(Exception::Error(
String::New("Could not allocate enough memory")));
}
buffer = static_cast<struct buffer*>(d);
buffer->offset = 0;
buffer->length = length;
buffer->weak = false;
buffer->refs = 0;
buffer->root = NULL;
buffer->handle = Persistent<Object>::New(args.This());
buffer->handle.MakeWeak(buffer, RootWeakCallback);
} }
args.This()->SetInternalField(0, v8::External::New(buffer)); buffer->Wrap(args.This());
args.This()->SetIndexedPropertiesToExternalArrayData((void*)buffer->data_,
struct buffer *root = buffer_root(buffer); kExternalUnsignedByteArray,
buffer->length_);
args.This()->Set(length_symbol, Integer::New(buffer->length_));
return args.This();
}
args.This()->
SetIndexedPropertiesToExternalArrayData(&root->bytes + buffer->offset,
kExternalUnsignedByteArray,
length);
args.This()->Set(length_symbol, Integer::New(length)); Buffer::Buffer(size_t length) : ObjectWrap() {
blob_ = blob_new(length);
length_ = length;
data_ = blob_->data;
blob_ref(blob_);
return args.This(); V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
} }
class AsciiSliceExt: public String::ExternalAsciiStringResource { Buffer::Buffer(Buffer *parent, size_t start, size_t end) : ObjectWrap() {
public: blob_ = parent->blob_;
assert(blob_->refs > 0);
blob_ref(blob_);
AsciiSliceExt(struct buffer *root, size_t start, size_t end) assert(start <= end);
{ length_ = end - start;
data_ = root->bytes + start; assert(length_ <= parent->length_);
len_ = end - start; data_ = parent->data_ + start;
root_ = root;
buffer_ref(root_);
}
~AsciiSliceExt() { V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
buffer_unref(root_); }
}
const char* data() const {
return data_;
}
size_t length() const { Buffer::~Buffer() {
return len_; assert(blob_->refs > 0);
} //fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs);
blob_unref(blob_);
V8::AdjustAmountOfExternalAllocatedMemory(-sizeof(Buffer));
}
private:
const char *data_;
size_t len_;
struct buffer *root_;
};
static Handle<Value> AsciiSlice(const Arguments &args) { Handle<Value> Buffer::AsciiSlice(const Arguments &args) {
HandleScope scope; HandleScope scope;
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
SLICE_ARGS(args[0], args[1]) SLICE_ARGS(args[0], args[1])
AsciiSliceExt *ext = new AsciiSliceExt(parent, start, end);
assert(args.This()->InternalFieldCount() == 1); Local<String> string = String::NewExternal(ext);
struct buffer *parent = BufferUnwrap(args.This()); // There should be at least two references to the blob now - the parent
// and the slice.
size_t start_abs = buffer_abs_off(parent, start); assert(parent->blob_->refs >= 2);
size_t end_abs = buffer_abs_off(parent, end);
assert(start_abs <= end_abs);
AsciiSliceExt *s = new AsciiSliceExt(buffer_root(parent), start_abs, end_abs);
Local<String> string = String::NewExternal(s);
struct buffer *root = buffer_root(parent);
assert(root->refs > 0);
return scope.Close(string); return scope.Close(string);
} }
static Handle<Value> Utf8Slice(const Arguments &args) {
HandleScope scope;
Handle<Value> Buffer::Utf8Slice(const Arguments &args) {
HandleScope scope;
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
SLICE_ARGS(args[0], args[1]) SLICE_ARGS(args[0], args[1])
const char *data = reinterpret_cast<const char*>(parent->data_ + start);
struct buffer *parent = BufferUnwrap(args.This()); Local<String> string = String::New(data, end - start);
size_t start_abs = buffer_abs_off(parent, start);
size_t end_abs = buffer_abs_off(parent, end);
assert(start_abs <= end_abs);
struct buffer *root = buffer_root(parent);
Local<String> string =
String::New(reinterpret_cast<const char*>(&root->bytes + start_abs),
end_abs - start_abs);
return scope.Close(string); return scope.Close(string);
} }
static Handle<Value> Slice(const Arguments &args) {
HandleScope scope;
Handle<Value> Buffer::Slice(const Arguments &args) {
HandleScope scope;
Local<Value> argv[3] = { args.This(), args[0], args[1] }; Local<Value> argv[3] = { args.This(), args[0], args[1] };
Local<Object> slice = Local<Object> slice =
constructor_template->GetFunction()->NewInstance(3, argv); constructor_template->GetFunction()->NewInstance(3, argv);
return scope.Close(slice); return scope.Close(slice);
} }
// var charsWritten = buffer.utf8Write(string, offset, length); // var charsWritten = buffer.utf8Write(string, offset);
static Handle<Value> Utf8Write(const Arguments &args) { Handle<Value> Buffer::Utf8Write(const Arguments &args) {
HandleScope scope; HandleScope scope;
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
struct buffer *buffer = BufferUnwrap(args.This());
if (!args[0]->IsString()) { if (!args[0]->IsString()) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
@ -268,30 +212,28 @@ static Handle<Value> Utf8Write(const Arguments &args) {
size_t offset = args[1]->Int32Value(); size_t offset = args[1]->Int32Value();
char *p = buffer_p(buffer, offset); if (offset >= buffer->length_) {
if (buffer_p(buffer, offset) == NULL) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
"Offset is out of bounds"))); "Offset is out of bounds")));
} }
size_t toWrite = args[2]->Int32Value(); const char *p = buffer->data_ + offset;
if (buffer_remaining(buffer, offset) < toWrite) { if (s->Length() + offset > buffer->length_) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
"Length is out of bounds"))); "Not enough space in Buffer for string")));
} }
int written = s->WriteUtf8(p, toWrite); int written = s->WriteUtf8((char*)p);
return scope.Close(Integer::New(written)); return scope.Close(Integer::New(written));
} }
// var charsWritten = buffer.asciiWrite(string, offset, length); // var charsWritten = buffer.asciiWrite(string, offset);
static Handle<Value> AsciiWrite(const Arguments &args) { Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
HandleScope scope; HandleScope scope;
struct buffer *buffer = BufferUnwrap(args.This()); Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
if (!args[0]->IsString()) { if (!args[0]->IsString()) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
@ -302,30 +244,25 @@ static Handle<Value> AsciiWrite(const Arguments &args) {
size_t offset = args[1]->Int32Value(); size_t offset = args[1]->Int32Value();
char *p = buffer_p(buffer, offset); if (offset >= buffer->length_) {
if (buffer_p(buffer, offset) == NULL) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
"Offset is out of bounds"))); "Offset is out of bounds")));
} }
size_t toWrite = args[2]->Int32Value(); const char *p = buffer->data_ + offset;
if (buffer_remaining(buffer, offset) < toWrite) { if (s->Length() + offset > buffer->length_) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
"Length is out of bounds"))); "Not enough space in Buffer for string")));
} }
// TODO Expose the second argument of WriteAscii? int written = s->WriteAscii((char*)p);
// Could avoid doing slices when the string doesn't fit in a buffer. V8
// slice() does copy the string, so exposing that argument would help.
int written = s->WriteAscii(p, 0, toWrite);
return scope.Close(Integer::New(written)); return scope.Close(Integer::New(written));
} }
static Handle<Value> Utf8Length(const Arguments &args) { // var nbytes = Buffer.utf8Length("string")
Handle<Value> Buffer::Utf8Length(const Arguments &args) {
HandleScope scope; HandleScope scope;
if (!args[0]->IsString()) { if (!args[0]->IsString()) {
return ThrowException(Exception::TypeError(String::New( return ThrowException(Exception::TypeError(String::New(
@ -336,29 +273,37 @@ static Handle<Value> Utf8Length(const Arguments &args) {
} }
void InitBuffer(Handle<Object> target) { bool Buffer::HasInstance(Handle<Value> val) {
if (!val->IsObject()) return false;
Local<Object> obj = val->ToObject();
return constructor_template->HasInstance(obj);
}
void Buffer::Initialize(Handle<Object> target) {
HandleScope scope; HandleScope scope;
length_symbol = Persistent<String>::New(String::NewSymbol("length")); length_symbol = Persistent<String>::New(String::NewSymbol("length"));
Local<FunctionTemplate> t = FunctionTemplate::New(Constructor); 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("Buffer"));
// copy free // copy free
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", AsciiSlice); NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Slice); 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", Utf8Slice); NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Utf8Write); NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", AsciiWrite); NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite);
NODE_SET_METHOD(constructor_template->GetFunction(), "utf8Length", Utf8Length); NODE_SET_METHOD(constructor_template->GetFunction(), "utf8Length", Buffer::Utf8Length);
target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction()); target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction());
} }
} // namespace node } // namespace node

66
src/node_buffer.h

@ -1,12 +1,12 @@
#ifndef NODE_BUFFER #ifndef NODE_BUFFER_H_
#define NODE_BUFFER #define NODE_BUFFER_H_
#include <node.h>
#include <node_object_wrap.h>
#include <v8.h> #include <v8.h>
namespace node { namespace node {
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* A buffer is a chunk of memory stored outside the V8 heap, mirrored by an /* A buffer is a chunk of memory stored outside the V8 heap, mirrored by an
* object in javascript. The object is not totally opaque, one can access * object in javascript. The object is not totally opaque, one can access
* individual bytes with [] and slice it into substrings or sub-buffers * individual bytes with [] and slice it into substrings or sub-buffers
@ -25,40 +25,42 @@ namespace node {
* are GCed. * are GCed.
*/ */
struct buffer {
v8::Persistent<v8::Object> handle; // both
bool weak; // both
struct buffer *root; // both (NULL for root)
size_t offset; // both (0 for root)
size_t length; // both
unsigned int refs; // root only
char bytes[1]; // root only
};
void InitBuffer(v8::Handle<v8::Object> target); struct Blob_;
class Buffer : public ObjectWrap {
public:
static void Initialize(v8::Handle<v8::Object> target);
static bool HasInstance(v8::Handle<v8::Value> val);
struct buffer* BufferUnwrap(v8::Handle<v8::Value> val); const char* data() const { return data_; }
bool IsBuffer(v8::Handle<v8::Value> val); size_t length() const { return length_; }
struct Blob_* blob() const { return blob_; }
static inline struct buffer * buffer_root(struct buffer *buffer) { protected:
return buffer->root ? buffer->root : buffer; static v8::Persistent<v8::FunctionTemplate> constructor_template;
} static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> Slice(const v8::Arguments &args);
static v8::Handle<v8::Value> AsciiSlice(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Slice(const v8::Arguments &args);
static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Write(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Length(const v8::Arguments &args);
static inline char * buffer_p(struct buffer *buffer, size_t off) { int AsciiWrite(char *string, int offset, int length);
struct buffer *root = buffer_root(buffer); int Utf8Write(char *string, int offset, int length);
if (buffer->offset + off >= root->length) return NULL;
return reinterpret_cast<char*>(&(root->bytes) + buffer->offset + off);
}
static inline size_t buffer_remaining(struct buffer *buffer, size_t off) { private:
struct buffer *root = buffer_root(buffer); Buffer(size_t length);
char *end = reinterpret_cast<char*>(&(root->bytes) + root->length); Buffer(Buffer *parent, size_t start, size_t end);
return end - buffer_p(buffer, off); ~Buffer();
}
const char *data_;
size_t length_;
struct Blob_ *blob_;
};
void buffer_ref(struct buffer *buffer);
void buffer_unref(struct buffer *buffer);
} // namespace node buffer } // namespace node buffer
#endif // NODE_BUFFER #endif // NODE_BUFFER_H_

70
src/node_http_parser.cc

@ -65,41 +65,35 @@ static Persistent<String> version_minor_sym;
static Persistent<String> should_keep_alive_sym; static Persistent<String> should_keep_alive_sym;
// 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) { \
Parser *parser = static_cast<Parser*>(p->data); \ Parser *parser = static_cast<Parser*>(p->data); \
\ \
HandleScope scope; \ HandleScope scope; \
\ \
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> ret = cb->Call(parser->handle_, 0, NULL); \ Local<Value> ret = cb->Call(parser->handle_, 0, NULL); \
return ret.IsEmpty() ? -1 : 0; \ return ret.IsEmpty() ? -1 : 0; \
} }
// Callback prototype for http_data_cb // Callback prototype for http_data_cb
#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); \
\ HandleScope scope; \
HandleScope scope; \ assert(parser->buffer_); \
\ Local<Value> cb_value = parser->handle_->Get(name##_sym); \
assert(parser->buffer_); \ if (!cb_value->IsFunction()) return 0; \
struct buffer * root = buffer_root(parser->buffer_); \ Local<Function> cb = Local<Function>::Cast(cb_value); \
char * base = buffer_p(root, 0); \ Local<Value> argv[3] = { Local<Value>::New(parser->buffer_->handle_) \
\ , Integer::New(at - parser->buffer_->data()) \
Local<Value> cb_value = parser->handle_->Get(name##_sym); \ , Integer::New(length) \
if (!cb_value->IsFunction()) return 0; \ }; \
Local<Function> cb = Local<Function>::Cast(cb_value); \ Local<Value> ret = cb->Call(parser->handle_, 3, argv); \
\ assert(parser->buffer_); \
Local<Value> argv[3] = { Local<Value>::New(root->handle) \ return ret.IsEmpty() ? -1 : 0; \
, Integer::New(at - base) \
, Integer::New(length) \
}; \
Local<Value> ret = cb->Call(parser->handle_, 3, argv); \
assert(parser->buffer_); \
return ret.IsEmpty() ? -1 : 0; \
} }
@ -218,21 +212,21 @@ class Parser : public ObjectWrap {
String::New("Already parsing a buffer"))); String::New("Already parsing a buffer")));
} }
if (!IsBuffer(args[0])) { if (!Buffer::HasInstance(args[0])) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("Argument should be a buffer"))); String::New("Argument should be a buffer")));
} }
struct buffer * buffer = BufferUnwrap(args[0]); Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
size_t off = args[1]->Int32Value(); size_t off = args[1]->Int32Value();
if (buffer_p(buffer, off) == NULL) { 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 (buffer_remaining(buffer, off) < len) { 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")));
} }
@ -243,7 +237,7 @@ class Parser : public ObjectWrap {
parser->buffer_ = buffer; parser->buffer_ = buffer;
size_t nparsed = size_t nparsed =
http_parser_execute(&(parser->parser_), buffer_p(buffer, off), len); http_parser_execute(&parser->parser_, buffer->data()+off, len);
// Unassign the 'buffer_' variable // Unassign the 'buffer_' variable
assert(parser->buffer_); assert(parser->buffer_);
@ -318,7 +312,7 @@ class Parser : public ObjectWrap {
parser_.data = this; parser_.data = this;
} }
struct buffer * buffer_; // The buffer currently being parsed. Buffer * buffer_; // The buffer currently being parsed.
http_parser parser_; http_parser parser_;
}; };

51
src/node_io_watcher.cc

@ -11,7 +11,7 @@ namespace node {
using namespace v8; using namespace v8;
Persistent<FunctionTemplate> IOWatcher::constructor_template; Persistent<FunctionTemplate> IOWatcher::constructor_template;
static Persistent<String> callback_symbol; Persistent<String> callback_symbol;
void IOWatcher::Initialize(Handle<Object> target) { void IOWatcher::Initialize(Handle<Object> target) {
HandleScope scope; HandleScope scope;
@ -34,7 +34,6 @@ void IOWatcher::Initialize(Handle<Object> target) {
void IOWatcher::Callback(EV_P_ ev_io *w, int revents) { void IOWatcher::Callback(EV_P_ ev_io *w, int revents) {
IOWatcher *io = static_cast<IOWatcher*>(w->data); IOWatcher *io = static_cast<IOWatcher*>(w->data);
assert(w == &io->watcher_); assert(w == &io->watcher_);
assert(!(revents & EV_ERROR));
HandleScope scope; HandleScope scope;
Local<Value> callback_v = io->handle_->Get(callback_symbol); Local<Value> callback_v = io->handle_->Get(callback_symbol);
@ -51,9 +50,7 @@ void IOWatcher::Callback(EV_P_ ev_io *w, int revents) {
argv[0] = Local<Value>::New(revents & EV_READ ? True() : False()); argv[0] = Local<Value>::New(revents & EV_READ ? True() : False());
argv[1] = Local<Value>::New(revents & EV_WRITE ? True() : False()); argv[1] = Local<Value>::New(revents & EV_WRITE ? True() : False());
io->Ref();
callback->Call(io->handle_, 2, argv); callback->Call(io->handle_, 2, argv);
io->Unref();
if (try_catch.HasCaught()) { if (try_catch.HasCaught()) {
FatalException(try_catch); FatalException(try_catch);
@ -62,8 +59,9 @@ void IOWatcher::Callback(EV_P_ ev_io *w, int revents) {
// //
// var io = new process.IOWatcher(); // var io = new process.IOWatcher(function (readable, writable) {
// io.callback = function (readable, writable) { ... }; //
// });
// io.set(fd, true, false); // io.set(fd, true, false);
// io.start(); // io.start();
// //
@ -71,24 +69,34 @@ Handle<Value> IOWatcher::New(const Arguments& args) {
HandleScope scope; HandleScope scope;
IOWatcher *s = new IOWatcher(); IOWatcher *s = new IOWatcher();
s->Wrap(args.This()); s->Wrap(args.This());
return args.This(); return args.This();
} }
Handle<Value> IOWatcher::Start(const Arguments& args) {
HandleScope scope;
IOWatcher *io = ObjectWrap::Unwrap<IOWatcher>(args.Holder());
ev_io_start(EV_DEFAULT_UC_ &io->watcher_);
io->Ref();
return Undefined();
}
Handle<Value> IOWatcher::Set(const Arguments& args) { Handle<Value> IOWatcher::Set(const Arguments& args) {
HandleScope scope; HandleScope scope;
IOWatcher *io = ObjectWrap::Unwrap<IOWatcher>(args.Holder());
if (!args[0]->IsInt32()) { if (!args[0]->IsInt32()) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("First arg should be a file descriptor."))); String::New("First arg should be a file descriptor.")));
} }
IOWatcher *io = ObjectWrap::Unwrap<IOWatcher>(args.This());
int fd = args[0]->Int32Value(); int fd = args[0]->Int32Value();
if (!args[1]->IsBoolean()) { if (!args[1]->IsBoolean()) {
@ -112,38 +120,19 @@ Handle<Value> IOWatcher::Set(const Arguments& args) {
return Undefined(); return Undefined();
} }
Handle<Value> IOWatcher::Start(const Arguments& args) {
HandleScope scope;
IOWatcher *io = ObjectWrap::Unwrap<IOWatcher>(args.This());
io->Start();
return Undefined();
}
Handle<Value> IOWatcher::Stop(const Arguments& args) { Handle<Value> IOWatcher::Stop(const Arguments& args) {
HandleScope scope; HandleScope scope;
IOWatcher *io = ObjectWrap::Unwrap<IOWatcher>(args.This()); IOWatcher *io = ObjectWrap::Unwrap<IOWatcher>(args.Holder());
io->Stop(); io->Stop();
return Undefined(); return Undefined();
} }
void IOWatcher::Start () {
if (!ev_is_active(&watcher_)) {
ev_io_start(EV_DEFAULT_UC_ &watcher_);
Ref();
}
assert(ev_is_active(&watcher_));
}
void IOWatcher::Stop () { void IOWatcher::Stop () {
if (ev_is_active(&watcher_)) { if (watcher_.active) {
ev_io_stop(EV_DEFAULT_UC_ &watcher_); ev_io_stop(EV_DEFAULT_UC_ &watcher_);
Unref(); Unref();
} }
assert(!ev_is_active(&watcher_));
} }

38
src/node_net2.cc

@ -488,28 +488,26 @@ static Handle<Value> Read(const Arguments& args) {
FD_ARG(args[0]) FD_ARG(args[0])
if (!IsBuffer(args[1])) { if (!Buffer::HasInstance(args[1])) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
struct buffer * buffer = BufferUnwrap(args[1]); Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject());
size_t off = args[2]->Int32Value(); size_t off = args[2]->Int32Value();
if (buffer_p(buffer, off) == NULL) { 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 (buffer_remaining(buffer, off) < len) { 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, ssize_t bytes_read = read(fd, (char*)buffer->data() + off, len);
buffer_p(buffer, off),
buffer_remaining(buffer, off));
if (bytes_read < 0) { if (bytes_read < 0) {
if (errno == EAGAIN || errno == EINTR) return Null(); if (errno == EAGAIN || errno == EINTR) return Null();
@ -533,21 +531,21 @@ static Handle<Value> RecvMsg(const Arguments& args) {
FD_ARG(args[0]) FD_ARG(args[0])
if (!IsBuffer(args[1])) { if (!Buffer::HasInstance(args[1])) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
struct buffer * buffer = BufferUnwrap(args[1]); Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject());
size_t off = args[2]->Int32Value(); size_t off = args[2]->Int32Value();
if (buffer_p(buffer, off) == NULL) { 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 (buffer_remaining(buffer, off) < len) { 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")));
} }
@ -555,7 +553,7 @@ static Handle<Value> RecvMsg(const Arguments& args) {
int received_fd; int received_fd;
struct iovec iov[1]; struct iovec iov[1];
iov[0].iov_base = buffer_p(buffer, 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;
@ -606,28 +604,26 @@ static Handle<Value> Write(const Arguments& args) {
FD_ARG(args[0]) FD_ARG(args[0])
if (!IsBuffer(args[1])) { if (!Buffer::HasInstance(args[1])) {
return ThrowException(Exception::TypeError( return ThrowException(Exception::TypeError(
String::New("Second argument should be a buffer"))); String::New("Second argument should be a buffer")));
} }
struct buffer * buffer = BufferUnwrap(args[1]); Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject());
size_t off = args[2]->Int32Value(); size_t off = args[2]->Int32Value();
char *p = buffer_p(buffer, off); if (off >= buffer->length()) {
if (p == NULL) {
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();
size_t remaining = buffer_remaining(buffer, off); if (off + len > buffer->length()) {
if (remaining < len) {
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, p, len); ssize_t written = write(fd, (char*)buffer->data() + off, len);
if (written < 0) { if (written < 0) {
if (errno == EAGAIN || errno == EINTR) return Null(); if (errno == EAGAIN || errno == EINTR) return Null();
@ -662,9 +658,9 @@ static Handle<Value> SendFD(const Arguments& args) {
struct iovec iov[1]; struct iovec iov[1];
char control_msg[CMSG_SPACE(sizeof(fd_to_send))]; char control_msg[CMSG_SPACE(sizeof(fd_to_send))];
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
char *dummy = "d"; // Need to send at least a byte of data in the message static char dummy = 'd'; // Need to send at least a byte of data in the message
iov[0].iov_base = dummy; iov[0].iov_base = &dummy;
iov[0].iov_len = 1; iov[0].iov_len = 1;
msg.msg_iov = iov; msg.msg_iov = iov;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;

5
src/node_object_wrap.h

@ -21,7 +21,8 @@ class ObjectWrap {
} }
} }
protected: v8::Persistent<v8::Object> handle_; // ro
template <class T> template <class T>
static inline T* Unwrap (v8::Handle<v8::Object> handle) static inline T* Unwrap (v8::Handle<v8::Object> handle)
{ {
@ -31,6 +32,7 @@ class ObjectWrap {
handle->GetInternalField(0))->Value()); handle->GetInternalField(0))->Value());
} }
protected:
inline void Wrap (v8::Handle<v8::Object> handle) inline void Wrap (v8::Handle<v8::Object> handle)
{ {
assert(handle_.IsEmpty()); assert(handle_.IsEmpty());
@ -72,7 +74,6 @@ class ObjectWrap {
if (refs_ == 0 && handle_.IsNearDeath()) delete this; if (refs_ == 0 && handle_.IsNearDeath()) delete this;
} }
v8::Persistent<v8::Object> handle_; // ro
int refs_; // ro int refs_; // ro
private: private:

21
test/mjsunit/test-buffer.js

@ -16,16 +16,28 @@ for (var i = 0; i < 1024; i++) {
assert.equal(i % 256, b[i]); assert.equal(i % 256, b[i]);
} }
for (var j = 0; j < 10000; j++) { var asciiString = "hello world";
var asciiString = "hello world"; var offset = 100;
for (var j = 0; j < 50000; j++) {
for (var i = 0; i < asciiString.length; i++) { for (var i = 0; i < asciiString.length; i++) {
b[i] = asciiString.charCodeAt(i); b[i] = asciiString.charCodeAt(i);
} }
var asciiSlice = b.asciiSlice(0, asciiString.length); var asciiSlice = b.asciiSlice(0, asciiString.length);
assert.equal(asciiString, asciiSlice);
var written = b.asciiWrite(asciiString, offset);
assert.equal(asciiString.length, written);
var asciiSlice = b.asciiSlice(offset, offset+asciiString.length);
assert.equal(asciiString, asciiSlice); assert.equal(asciiString, asciiSlice);
var sliceA = b.slice(offset, offset+asciiString.length);
var sliceB = b.slice(offset, offset+asciiString.length);
for (var i = 0; i < asciiString.length; i++) {
assert.equal(sliceA[i], sliceB[i]);
}
// TODO utf8 slice tests
} }
@ -36,3 +48,6 @@ for (var j = 0; j < 10000; j++) {
assert.equal(b[100+i], slice[i]); assert.equal(b[100+i], slice[i]);
} }
} }

2
wscript

@ -345,8 +345,8 @@ def build(bld):
node.source = """ node.source = """
src/node.cc src/node.cc
src/node_buffer.cc src/node_buffer.cc
src/node_net2.cc
src/node_http_parser.cc src/node_http_parser.cc
src/node_net2.cc
src/node_io_watcher.cc src/node_io_watcher.cc
src/node_child_process.cc src/node_child_process.cc
src/node_constants.cc src/node_constants.cc

Loading…
Cancel
Save