|
|
@ -20,244 +20,188 @@ using namespace v8; |
|
|
|
if (start < 0 || end < 0) { \ |
|
|
|
return ThrowException(Exception::TypeError( \ |
|
|
|
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) { |
|
|
|
if (!val->IsObject()) return false; |
|
|
|
Local<Object> obj = val->ToObject(); |
|
|
|
return constructor_template->HasInstance(obj); |
|
|
|
} |
|
|
|
static Persistent<String> length_symbol; |
|
|
|
Persistent<FunctionTemplate> Buffer::constructor_template; |
|
|
|
|
|
|
|
|
|
|
|
/* Determines the absolute position for a relative offset */ |
|
|
|
size_t buffer_abs_off(buffer *buffer, size_t off) { |
|
|
|
struct buffer *root = buffer_root(buffer); |
|
|
|
off += buffer->offset; |
|
|
|
return MIN(root->length, off); |
|
|
|
} |
|
|
|
// 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[1]; |
|
|
|
}; |
|
|
|
typedef struct Blob_ Blob; |
|
|
|
|
|
|
|
|
|
|
|
void buffer_ref(struct buffer *buffer) { |
|
|
|
buffer_root(buffer)->refs++; |
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct buffer* BufferUnwrap(v8::Handle<v8::Value> val) { |
|
|
|
assert(val->IsObject()); |
|
|
|
HandleScope scope; |
|
|
|
Local<Object> obj = val->ToObject(); |
|
|
|
assert(obj->InternalFieldCount() == 1); |
|
|
|
Local<External> ext = Local<External>::Cast(obj->GetInternalField(0)); |
|
|
|
return static_cast<struct buffer*>(ext->Value()); |
|
|
|
static inline void blob_ref(Blob *blob) { |
|
|
|
blob->refs++; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void RootWeakCallback(Persistent<Value> value, void *data) |
|
|
|
{ |
|
|
|
struct buffer *buffer = static_cast<struct buffer*>(data); |
|
|
|
assert(buffer->root == NULL); // this is the root
|
|
|
|
assert(value == buffer->handle); |
|
|
|
value.ClearWeak(); |
|
|
|
if (buffer->refs) { |
|
|
|
buffer->weak = true; |
|
|
|
} else { |
|
|
|
buffer->handle.Dispose(); |
|
|
|
free(buffer); |
|
|
|
static inline void blob_unref(Blob *blob) { |
|
|
|
assert(blob->refs > 0); |
|
|
|
if (--blob->refs == 0) { |
|
|
|
//fprintf(stderr, "free %d bytes\n", blob->length);
|
|
|
|
size_t s = sizeof(Blob) - 1 + blob->length; |
|
|
|
V8::AdjustAmountOfExternalAllocatedMemory(-s); |
|
|
|
free(blob); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void buffer_unref(struct buffer *buffer) { |
|
|
|
struct buffer * root = buffer_root(buffer); |
|
|
|
assert(root->refs > 0); |
|
|
|
root->refs--; |
|
|
|
if (root->refs == 0 && root->weak) { |
|
|
|
root->handle.MakeWeak(root, RootWeakCallback); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 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_); |
|
|
|
|
|
|
|
static void SliceWeakCallback(Persistent<Value> value, void *data) |
|
|
|
{ |
|
|
|
struct buffer *buffer = static_cast<struct buffer*>(data); |
|
|
|
assert(buffer->root != NULL); // this is a slice
|
|
|
|
assert(value == buffer->handle); |
|
|
|
buffer->handle.Dispose(); |
|
|
|
buffer_unref(buffer->root); |
|
|
|
assert(start <= end); |
|
|
|
length_ = end - start; |
|
|
|
assert(length_ <= parent->length()); |
|
|
|
data_ = parent->data() + start; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static Handle<Value> Constructor(const Arguments &args) { |
|
|
|
HandleScope scope; |
|
|
|
|
|
|
|
size_t length; |
|
|
|
struct buffer *buffer; |
|
|
|
|
|
|
|
if (constructor_template->HasInstance(args[0])) { |
|
|
|
// slice slice
|
|
|
|
SLICE_ARGS(args[1], args[2]) |
|
|
|
|
|
|
|
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"))); |
|
|
|
|
|
|
|
~AsciiSliceExt() { |
|
|
|
//fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs);
|
|
|
|
blob_unref(blob_); |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
const char* data() const { return data_; } |
|
|
|
size_t length() const { return length_; } |
|
|
|
|
|
|
|
buffer_ref(buffer->root); |
|
|
|
} else { |
|
|
|
// Root slice
|
|
|
|
private: |
|
|
|
const char *data_; |
|
|
|
size_t length_; |
|
|
|
Blob *blob_; |
|
|
|
}; |
|
|
|
|
|
|
|
length = args[0]->Uint32Value(); |
|
|
|
|
|
|
|
if (length < 1) { |
|
|
|
return ThrowException(Exception::TypeError( |
|
|
|
String::New("Bad argument. Length must be positive"))); |
|
|
|
} |
|
|
|
Handle<Value> Buffer::New(const Arguments &args) { |
|
|
|
HandleScope scope; |
|
|
|
|
|
|
|
// TODO alignment. modify the length?
|
|
|
|
void *d = malloc(sizeof(struct buffer) + length - 1); |
|
|
|
Buffer *buffer; |
|
|
|
if (args[0]->IsInt32()) { |
|
|
|
// var buffer = new Buffer(1024);
|
|
|
|
size_t length = args[0]->Uint32Value(); |
|
|
|
buffer = new Buffer(length); |
|
|
|
|
|
|
|
if (!d) { |
|
|
|
V8::LowMemoryNotification(); |
|
|
|
return ThrowException(Exception::Error( |
|
|
|
String::New("Could not allocate enough memory"))); |
|
|
|
} 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 { |
|
|
|
return ThrowException(Exception::TypeError(String::New("Bad argument"))); |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
buffer->Wrap(args.This()); |
|
|
|
args.This()->SetIndexedPropertiesToExternalArrayData((void*)buffer->data_, |
|
|
|
kExternalUnsignedByteArray, |
|
|
|
buffer->length_); |
|
|
|
args.This()->Set(length_symbol, Integer::New(buffer->length_)); |
|
|
|
return args.This(); |
|
|
|
} |
|
|
|
|
|
|
|
args.This()->SetInternalField(0, v8::External::New(buffer)); |
|
|
|
|
|
|
|
struct buffer *root = buffer_root(buffer); |
|
|
|
|
|
|
|
args.This()-> |
|
|
|
SetIndexedPropertiesToExternalArrayData(&root->bytes + buffer->offset, |
|
|
|
kExternalUnsignedByteArray, |
|
|
|
length); |
|
|
|
Buffer::Buffer(size_t length) : ObjectWrap() { |
|
|
|
blob_ = blob_new(length); |
|
|
|
length_ = length; |
|
|
|
data_ = blob_->data; |
|
|
|
blob_ref(blob_); |
|
|
|
|
|
|
|
args.This()->Set(length_symbol, Integer::New(length)); |
|
|
|
|
|
|
|
return args.This(); |
|
|
|
V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer)); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class AsciiSliceExt: public String::ExternalAsciiStringResource { |
|
|
|
public: |
|
|
|
Buffer::Buffer(Buffer *parent, size_t start, size_t end) : ObjectWrap() { |
|
|
|
blob_ = parent->blob_; |
|
|
|
assert(blob_->refs > 0); |
|
|
|
blob_ref(blob_); |
|
|
|
|
|
|
|
AsciiSliceExt(struct buffer *root, size_t start, size_t end) |
|
|
|
{ |
|
|
|
data_ = root->bytes + start; |
|
|
|
len_ = end - start; |
|
|
|
root_ = root; |
|
|
|
buffer_ref(root_); |
|
|
|
} |
|
|
|
assert(start <= end); |
|
|
|
length_ = end - start; |
|
|
|
assert(length_ <= parent->length_); |
|
|
|
data_ = parent->data_ + start; |
|
|
|
|
|
|
|
~AsciiSliceExt() { |
|
|
|
buffer_unref(root_); |
|
|
|
V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer)); |
|
|
|
} |
|
|
|
|
|
|
|
const char* data() const { |
|
|
|
return data_; |
|
|
|
} |
|
|
|
|
|
|
|
size_t length() const { |
|
|
|
return len_; |
|
|
|
Buffer::~Buffer() { |
|
|
|
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; |
|
|
|
|
|
|
|
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
|
|
|
|
|
assert(args.This()->InternalFieldCount() == 1); |
|
|
|
struct buffer *parent = BufferUnwrap(args.This()); |
|
|
|
|
|
|
|
size_t start_abs = buffer_abs_off(parent, start); |
|
|
|
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); |
|
|
|
|
|
|
|
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); |
|
|
|
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]) |
|
|
|
|
|
|
|
struct buffer *parent = BufferUnwrap(args.This()); |
|
|
|
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); |
|
|
|
const char *data = reinterpret_cast<const char*>(parent->data_ + start); |
|
|
|
Local<String> string = String::New(data, end - start); |
|
|
|
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<Object> slice = |
|
|
|
constructor_template->GetFunction()->NewInstance(3, argv); |
|
|
|
|
|
|
|
return scope.Close(slice); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// var charsWritten = buffer.utf8Write(string, offset, length);
|
|
|
|
static Handle<Value> Utf8Write(const Arguments &args) { |
|
|
|
// var charsWritten = buffer.utf8Write(string, offset);
|
|
|
|
Handle<Value> Buffer::Utf8Write(const Arguments &args) { |
|
|
|
HandleScope scope; |
|
|
|
|
|
|
|
struct buffer *buffer = BufferUnwrap(args.This()); |
|
|
|
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
|
|
|
|
|
if (!args[0]->IsString()) { |
|
|
|
return ThrowException(Exception::TypeError(String::New( |
|
|
@ -268,30 +212,28 @@ static Handle<Value> Utf8Write(const Arguments &args) { |
|
|
|
|
|
|
|
size_t offset = args[1]->Int32Value(); |
|
|
|
|
|
|
|
char *p = buffer_p(buffer, offset); |
|
|
|
if (buffer_p(buffer, offset) == NULL) { |
|
|
|
if (offset >= buffer->length_) { |
|
|
|
return ThrowException(Exception::TypeError(String::New( |
|
|
|
"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( |
|
|
|
"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)); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// var charsWritten = buffer.asciiWrite(string, offset, length);
|
|
|
|
static Handle<Value> AsciiWrite(const Arguments &args) { |
|
|
|
// var charsWritten = buffer.asciiWrite(string, offset);
|
|
|
|
Handle<Value> Buffer::AsciiWrite(const Arguments &args) { |
|
|
|
HandleScope scope; |
|
|
|
|
|
|
|
struct buffer *buffer = BufferUnwrap(args.This()); |
|
|
|
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
|
|
|
|
|
if (!args[0]->IsString()) { |
|
|
|
return ThrowException(Exception::TypeError(String::New( |
|
|
@ -302,30 +244,25 @@ static Handle<Value> AsciiWrite(const Arguments &args) { |
|
|
|
|
|
|
|
size_t offset = args[1]->Int32Value(); |
|
|
|
|
|
|
|
char *p = buffer_p(buffer, offset); |
|
|
|
if (buffer_p(buffer, offset) == NULL) { |
|
|
|
if (offset >= buffer->length_) { |
|
|
|
return ThrowException(Exception::TypeError(String::New( |
|
|
|
"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( |
|
|
|
"Length is out of bounds"))); |
|
|
|
"Not enough space in Buffer for string"))); |
|
|
|
} |
|
|
|
|
|
|
|
// TODO Expose the second argument of WriteAscii?
|
|
|
|
// 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); |
|
|
|
|
|
|
|
int written = s->WriteAscii((char*)p); |
|
|
|
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; |
|
|
|
if (!args[0]->IsString()) { |
|
|
|
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; |
|
|
|
|
|
|
|
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->InstanceTemplate()->SetInternalFieldCount(1); |
|
|
|
constructor_template->SetClassName(String::NewSymbol("Buffer")); |
|
|
|
|
|
|
|
// copy free
|
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", AsciiSlice); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Slice); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Buffer::Slice); |
|
|
|
// TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
|
|
|
|
// copy
|
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Utf8Slice); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice); |
|
|
|
|
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Utf8Write); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", AsciiWrite); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write); |
|
|
|
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()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} // namespace node
|
|
|
|