|
@ -24,88 +24,92 @@ using namespace v8; |
|
|
static Persistent<String> length_symbol; |
|
|
static Persistent<String> length_symbol; |
|
|
static Persistent<FunctionTemplate> constructor_template; |
|
|
static Persistent<FunctionTemplate> constructor_template; |
|
|
|
|
|
|
|
|
/* A blob 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 one can slice the blob into substrings or |
|
|
* individual bytes with [] and slice it into substrings or sub-buffers |
|
|
* subblobs without copying memory. |
|
|
* without copying memory. |
|
|
* |
|
|
* |
|
|
* blob.asciiSlide(0, 3) // return an ascii encoded string - no memory iscopied
|
|
|
* // return an ascii encoded string - no memory iscopied
|
|
|
* blob.slice(0, 3) // returns another blob - no memory is copied
|
|
|
* buffer.asciiSlide(0, 3) |
|
|
* |
|
|
* |
|
|
* Interally, each javascript blob object is backed by a "struct blob" object. |
|
|
* // returns another buffer - no memory is copied
|
|
|
* These "struct blob" objects are either the root object (in the case that |
|
|
* buffer.slice(0, 3) |
|
|
* blob->root == NULL) or slice objects (in which case blob->root != NULL). |
|
|
* |
|
|
* The root blob is only GCed once all it's slices are GCed. |
|
|
* Interally, each javascript buffer object is backed by a "struct buffer" |
|
|
|
|
|
* object. These "struct buffer" objects are either a root buffer (in the |
|
|
|
|
|
* case that buffer->root == NULL) or slice objects (in which case |
|
|
|
|
|
* buffer->root != NULL). A root buffer is only GCed once all its slices |
|
|
|
|
|
* are GCed. |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
|
struct blob { |
|
|
struct buffer { |
|
|
Persistent<Object> handle; // both
|
|
|
Persistent<Object> handle; // both
|
|
|
bool weak; // both
|
|
|
bool weak; // both
|
|
|
struct blob *root; // both (NULL for root)
|
|
|
struct buffer *root; // both (NULL for root)
|
|
|
size_t offset; // both (0 for root)
|
|
|
size_t offset; // both (0 for root)
|
|
|
size_t length; // both
|
|
|
size_t length; // both
|
|
|
unsigned int refs; // root only
|
|
|
unsigned int refs; // root only
|
|
|
char bytes[1]; // root only
|
|
|
char bytes[1]; // root only
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline struct blob* blob_root(blob *blob) { |
|
|
static inline struct buffer* buffer_root(buffer *buffer) { |
|
|
return blob->root ? blob->root : blob; |
|
|
return buffer->root ? buffer->root : buffer; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* Determines the absolute position for a relative offset */ |
|
|
/* Determines the absolute position for a relative offset */ |
|
|
static inline size_t blob_abs_off(blob *blob, size_t off) { |
|
|
static inline size_t buffer_abs_off(buffer *buffer, size_t off) { |
|
|
struct blob *root = blob_root(blob); |
|
|
struct buffer *root = buffer_root(buffer); |
|
|
off += root->offset; |
|
|
off += root->offset; |
|
|
return MIN(root->length, off); |
|
|
return MIN(root->length, off); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void blob_ref(struct blob *blob) { |
|
|
static inline void buffer_ref(struct buffer *buffer) { |
|
|
assert(blob->root == NULL); |
|
|
assert(buffer->root == NULL); |
|
|
blob->refs++; |
|
|
buffer->refs++; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void blob_unref(struct blob *blob) { |
|
|
static inline void buffer_unref(struct buffer *buffer) { |
|
|
assert(blob->root == NULL); |
|
|
assert(buffer->root == NULL); |
|
|
assert(blob->refs > 0); |
|
|
assert(buffer->refs > 0); |
|
|
blob->refs--; |
|
|
buffer->refs--; |
|
|
if (blob->refs == 0 && blob->weak) free(blob); |
|
|
if (buffer->refs == 0 && buffer->weak) free(buffer); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline struct blob* Unwrap(Handle<Value> val) { |
|
|
static inline struct buffer* Unwrap(Handle<Value> val) { |
|
|
assert(val->IsObject()); |
|
|
assert(val->IsObject()); |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
Local<Object> obj = val->ToObject(); |
|
|
Local<Object> obj = val->ToObject(); |
|
|
assert(obj->InternalFieldCount() == 1); |
|
|
assert(obj->InternalFieldCount() == 1); |
|
|
Local<External> ext = Local<External>::Cast(obj->GetInternalField(0)); |
|
|
Local<External> ext = Local<External>::Cast(obj->GetInternalField(0)); |
|
|
return static_cast<struct blob*>(ext->Value()); |
|
|
return static_cast<struct buffer*>(ext->Value()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void RootWeakCallback(Persistent<Value> value, void *data) |
|
|
static void RootWeakCallback(Persistent<Value> value, void *data) |
|
|
{ |
|
|
{ |
|
|
struct blob *blob = static_cast<struct blob*>(data); |
|
|
struct buffer *buffer = static_cast<struct buffer*>(data); |
|
|
assert(blob->root == NULL); // this is the root
|
|
|
assert(buffer->root == NULL); // this is the root
|
|
|
assert(value == blob->handle); |
|
|
assert(value == buffer->handle); |
|
|
blob->handle.Dispose(); |
|
|
buffer->handle.Dispose(); |
|
|
if (blob->refs) { |
|
|
if (buffer->refs) { |
|
|
blob->weak = true; |
|
|
buffer->weak = true; |
|
|
} else { |
|
|
} else { |
|
|
free(blob); |
|
|
free(buffer); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SliceWeakCallback(Persistent<Value> value, void *data) |
|
|
static void SliceWeakCallback(Persistent<Value> value, void *data) |
|
|
{ |
|
|
{ |
|
|
struct blob *blob = static_cast<struct blob*>(data); |
|
|
struct buffer *buffer = static_cast<struct buffer*>(data); |
|
|
assert(blob->root != NULL); // this is a slice
|
|
|
assert(buffer->root != NULL); // this is a slice
|
|
|
assert(value == blob->handle); |
|
|
assert(value == buffer->handle); |
|
|
blob->handle.Dispose(); |
|
|
buffer->handle.Dispose(); |
|
|
blob_unref(blob->root); |
|
|
buffer_unref(buffer->root); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -113,20 +117,20 @@ static Handle<Value> Constructor(const Arguments &args) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
|
|
|
|
|
|
size_t length; |
|
|
size_t length; |
|
|
struct blob *blob; |
|
|
struct buffer *buffer; |
|
|
|
|
|
|
|
|
if (constructor_template->HasInstance(args[0])) { |
|
|
if (constructor_template->HasInstance(args[0])) { |
|
|
// slice slice
|
|
|
// slice slice
|
|
|
SLICE_ARGS(args[1], args[2]) |
|
|
SLICE_ARGS(args[1], args[2]) |
|
|
|
|
|
|
|
|
struct blob *parent = Unwrap(args[0]); |
|
|
struct buffer *parent = Unwrap(args[0]); |
|
|
|
|
|
|
|
|
size_t start_abs = blob_abs_off(parent, start); |
|
|
size_t start_abs = buffer_abs_off(parent, start); |
|
|
size_t end_abs = blob_abs_off(parent, end); |
|
|
size_t end_abs = buffer_abs_off(parent, end); |
|
|
assert(start_abs <= end_abs); |
|
|
assert(start_abs <= end_abs); |
|
|
length = end_abs - start_abs; |
|
|
length = end_abs - start_abs; |
|
|
|
|
|
|
|
|
void *d = malloc(sizeof(struct blob)); |
|
|
void *d = malloc(sizeof(struct buffer)); |
|
|
|
|
|
|
|
|
if (!d) { |
|
|
if (!d) { |
|
|
V8::LowMemoryNotification(); |
|
|
V8::LowMemoryNotification(); |
|
@ -135,17 +139,17 @@ static Handle<Value> Constructor(const Arguments &args) { |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
blob = static_cast<struct blob*>(d); |
|
|
buffer = static_cast<struct buffer*>(d); |
|
|
|
|
|
|
|
|
blob->length = length; |
|
|
buffer->length = length; |
|
|
blob->offset = start_abs; |
|
|
buffer->offset = start_abs; |
|
|
blob->weak = false; |
|
|
buffer->weak = false; |
|
|
blob->refs = 0; |
|
|
buffer->refs = 0; |
|
|
blob->root = blob_root(parent); |
|
|
buffer->root = buffer_root(parent); |
|
|
blob->handle = Persistent<Object>::New(args.This()); |
|
|
buffer->handle = Persistent<Object>::New(args.This()); |
|
|
blob->handle.MakeWeak(blob, SliceWeakCallback); |
|
|
buffer->handle.MakeWeak(buffer, SliceWeakCallback); |
|
|
|
|
|
|
|
|
blob_ref(blob->root); |
|
|
buffer_ref(buffer->root); |
|
|
} else { |
|
|
} else { |
|
|
// Root slice
|
|
|
// Root slice
|
|
|
|
|
|
|
|
@ -157,7 +161,7 @@ static Handle<Value> Constructor(const Arguments &args) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// TODO alignment. modify the length?
|
|
|
// TODO alignment. modify the length?
|
|
|
void *d = malloc(sizeof(struct blob) + length - 1); |
|
|
void *d = malloc(sizeof(struct buffer) + length - 1); |
|
|
|
|
|
|
|
|
if (!d) { |
|
|
if (!d) { |
|
|
V8::LowMemoryNotification(); |
|
|
V8::LowMemoryNotification(); |
|
@ -165,23 +169,23 @@ static Handle<Value> Constructor(const Arguments &args) { |
|
|
String::New("Could not allocate enough memory"))); |
|
|
String::New("Could not allocate enough memory"))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
blob = static_cast<struct blob*>(d); |
|
|
buffer = static_cast<struct buffer*>(d); |
|
|
|
|
|
|
|
|
blob->offset = 0; |
|
|
buffer->offset = 0; |
|
|
blob->length = length; |
|
|
buffer->length = length; |
|
|
blob->weak = false; |
|
|
buffer->weak = false; |
|
|
blob->refs = 0; |
|
|
buffer->refs = 0; |
|
|
blob->root = NULL; |
|
|
buffer->root = NULL; |
|
|
blob->handle = Persistent<Object>::New(args.This()); |
|
|
buffer->handle = Persistent<Object>::New(args.This()); |
|
|
blob->handle.MakeWeak(blob, RootWeakCallback); |
|
|
buffer->handle.MakeWeak(buffer, RootWeakCallback); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
args.This()->SetInternalField(0, v8::External::New(blob)); |
|
|
args.This()->SetInternalField(0, v8::External::New(buffer)); |
|
|
|
|
|
|
|
|
struct blob *root = blob_root(blob); |
|
|
struct buffer *root = buffer_root(buffer); |
|
|
|
|
|
|
|
|
args.This()-> |
|
|
args.This()-> |
|
|
SetIndexedPropertiesToExternalArrayData(&root->bytes + blob->offset, |
|
|
SetIndexedPropertiesToExternalArrayData(&root->bytes + buffer->offset, |
|
|
kExternalUnsignedByteArray, |
|
|
kExternalUnsignedByteArray, |
|
|
length); |
|
|
length); |
|
|
|
|
|
|
|
@ -194,16 +198,16 @@ static Handle<Value> Constructor(const Arguments &args) { |
|
|
class AsciiSliceExt: public String::ExternalAsciiStringResource { |
|
|
class AsciiSliceExt: public String::ExternalAsciiStringResource { |
|
|
public: |
|
|
public: |
|
|
|
|
|
|
|
|
AsciiSliceExt(struct blob *root, size_t start, size_t end) |
|
|
AsciiSliceExt(struct buffer *root, size_t start, size_t end) |
|
|
{ |
|
|
{ |
|
|
data_ = root->bytes + start; |
|
|
data_ = root->bytes + start; |
|
|
len_ = end - start; |
|
|
len_ = end - start; |
|
|
root_ = root; |
|
|
root_ = root; |
|
|
blob_ref(root_); |
|
|
buffer_ref(root_); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
~AsciiSliceExt() { |
|
|
~AsciiSliceExt() { |
|
|
blob_unref(root_); |
|
|
buffer_unref(root_); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const char* data() const { |
|
|
const char* data() const { |
|
@ -217,7 +221,7 @@ class AsciiSliceExt: public String::ExternalAsciiStringResource { |
|
|
private: |
|
|
private: |
|
|
const char *data_; |
|
|
const char *data_; |
|
|
size_t len_; |
|
|
size_t len_; |
|
|
struct blob *root_; |
|
|
struct buffer *root_; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
static Handle<Value> AsciiSlice(const Arguments &args) { |
|
|
static Handle<Value> AsciiSlice(const Arguments &args) { |
|
@ -226,17 +230,17 @@ static Handle<Value> AsciiSlice(const Arguments &args) { |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
|
|
|
|
|
|
assert(args.This()->InternalFieldCount() == 1); |
|
|
assert(args.This()->InternalFieldCount() == 1); |
|
|
struct blob *parent = Unwrap(args.This()); |
|
|
struct buffer *parent = Unwrap(args.This()); |
|
|
|
|
|
|
|
|
size_t start_abs = blob_abs_off(parent, start); |
|
|
size_t start_abs = buffer_abs_off(parent, start); |
|
|
size_t end_abs = blob_abs_off(parent, end); |
|
|
size_t end_abs = buffer_abs_off(parent, end); |
|
|
|
|
|
|
|
|
assert(start_abs <= end_abs); |
|
|
assert(start_abs <= end_abs); |
|
|
|
|
|
|
|
|
AsciiSliceExt *s = new AsciiSliceExt(blob_root(parent), start_abs, end_abs); |
|
|
AsciiSliceExt *s = new AsciiSliceExt(buffer_root(parent), start_abs, end_abs); |
|
|
Local<String> string = String::NewExternal(s); |
|
|
Local<String> string = String::NewExternal(s); |
|
|
|
|
|
|
|
|
struct blob *root = blob_root(parent); |
|
|
struct buffer *root = buffer_root(parent); |
|
|
assert(root->refs > 0); |
|
|
assert(root->refs > 0); |
|
|
|
|
|
|
|
|
return scope.Close(string); |
|
|
return scope.Close(string); |
|
@ -247,12 +251,12 @@ static Handle<Value> Utf8Slice(const Arguments &args) { |
|
|
|
|
|
|
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
|
|
|
|
|
|
struct blob *parent = Unwrap(args.This()); |
|
|
struct buffer *parent = Unwrap(args.This()); |
|
|
size_t start_abs = blob_abs_off(parent, start); |
|
|
size_t start_abs = buffer_abs_off(parent, start); |
|
|
size_t end_abs = blob_abs_off(parent, end); |
|
|
size_t end_abs = buffer_abs_off(parent, end); |
|
|
assert(start_abs <= end_abs); |
|
|
assert(start_abs <= end_abs); |
|
|
|
|
|
|
|
|
struct blob *root = blob_root(parent); |
|
|
struct buffer *root = buffer_root(parent); |
|
|
|
|
|
|
|
|
Local<String> string = |
|
|
Local<String> string = |
|
|
String::New(reinterpret_cast<const char*>(&root->bytes + start_abs), |
|
|
String::New(reinterpret_cast<const char*>(&root->bytes + start_abs), |
|
@ -271,7 +275,7 @@ static Handle<Value> Slice(const Arguments &args) { |
|
|
return scope.Close(slice); |
|
|
return scope.Close(slice); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void InitBlob(Handle<Object> target) { |
|
|
void InitBuffer(Handle<Object> target) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
|
|
|
|
|
|
length_symbol = Persistent<String>::New(String::NewSymbol("length")); |
|
|
length_symbol = Persistent<String>::New(String::NewSymbol("length")); |
|
@ -279,7 +283,7 @@ void InitBlob(Handle<Object> target) { |
|
|
Local<FunctionTemplate> t = FunctionTemplate::New(Constructor); |
|
|
Local<FunctionTemplate> t = FunctionTemplate::New(Constructor); |
|
|
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("Blob")); |
|
|
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", AsciiSlice); |
|
@ -288,7 +292,7 @@ void InitBlob(Handle<Object> target) { |
|
|
// copy
|
|
|
// copy
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Utf8Slice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Utf8Slice); |
|
|
|
|
|
|
|
|
target->Set(String::NewSymbol("Blob"), constructor_template->GetFunction()); |
|
|
target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} // namespace node
|
|
|
} // namespace node
|