diff --git a/src/node.cc b/src/node.cc index 5c77e932ef..7af78bb2b2 100644 --- a/src/node.cc +++ b/src/node.cc @@ -10,6 +10,7 @@ #include #include /* dlopen(), dlsym() */ +#include #include #include #include @@ -847,6 +848,7 @@ static Local Load(int argc, char *argv[]) { // Initialize the C++ modules..................filename of module + InitBlob(process); // stdio.cc Stdio::Initialize(process); // stdio.cc Timer::Initialize(process); // timer.cc SignalHandler::Initialize(process); // signal_handler.cc diff --git a/src/node_blob.cc b/src/node_blob.cc new file mode 100644 index 0000000000..e86b5706c0 --- /dev/null +++ b/src/node_blob.cc @@ -0,0 +1,294 @@ +#include +#include // malloc, free +#include +#include + +namespace node { + +using namespace v8; + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +#define SLICE_ARGS(start_arg, end_arg) \ + if (!start_arg->IsInt32() || !end_arg->IsInt32()) { \ + return ThrowException(Exception::TypeError( \ + String::New("Bad argument."))); \ + } \ + int32_t start = start_arg->Int32Value(); \ + int32_t end = end_arg->Int32Value(); \ + if (start < 0 || end < 0) { \ + return ThrowException(Exception::TypeError( \ + String::New("Bad argument."))); \ + } + +static Persistent length_symbol; +static Persistent constructor_template; + +/* A blob 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 + * individual bytes with [] and one can slice the blob into substrings or + * subblobs without copying memory. + * + * blob.asciiSlide(0, 3) // return an ascii encoded string - no memory iscopied + * blob.slice(0, 3) // returns another blob - no memory is copied + * + * Interally, each javascript blob object is backed by a "struct blob" object. + * These "struct blob" objects are either the root object (in the case that + * 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. + */ + +struct blob { + Persistent handle; // both + bool weak; // both + struct blob *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 +}; + + +static inline struct blob* blob_root(blob *blob) { + return blob->root ? blob->root : blob; +} + +/* Determines the absolute position for a relative offset */ +static inline size_t blob_abs_off(blob *blob, size_t off) { + struct blob *root = blob_root(blob); + off += root->offset; + return MIN(root->length, off); +} + + +static inline void blob_ref(struct blob *blob) { + assert(blob->root == NULL); + blob->refs++; +} + + +static inline void blob_unref(struct blob *blob) { + assert(blob->root == NULL); + assert(blob->refs > 0); + blob->refs--; + if (blob->refs == 0 && blob->weak) free(blob); +} + + +static inline struct blob* Unwrap(Handle val) { + assert(val->IsObject()); + HandleScope scope; + Local obj = val->ToObject(); + assert(obj->InternalFieldCount() == 1); + Local ext = Local::Cast(obj->GetInternalField(0)); + return static_cast(ext->Value()); +} + + +static void RootWeakCallback(Persistent value, void *data) +{ + struct blob *blob = static_cast(data); + assert(blob->root == NULL); // this is the root + assert(value == blob->handle); + blob->handle.Dispose(); + if (blob->refs) { + blob->weak = true; + } else { + free(blob); + } +} + + +static void SliceWeakCallback(Persistent value, void *data) +{ + struct blob *blob = static_cast(data); + assert(blob->root != NULL); // this is a slice + assert(value == blob->handle); + blob->handle.Dispose(); + blob_unref(blob->root); +} + + +static Handle Constructor(const Arguments &args) { + HandleScope scope; + + size_t length; + struct blob *blob; + + if (constructor_template->HasInstance(args[0])) { + // slice slice + SLICE_ARGS(args[1], args[2]) + + struct blob *parent = Unwrap(args[0]); + + size_t start_abs = blob_abs_off(parent, start); + size_t end_abs = blob_abs_off(parent, end); + assert(start_abs <= end_abs); + length = end_abs - start_abs; + + void *d = malloc(sizeof(struct blob)); + + if (!d) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error( + String::New("Could not allocate enough memory"))); + + } + + blob = static_cast(d); + + blob->length = length; + blob->offset = start_abs; + blob->weak = false; + blob->refs = 0; + blob->root = blob_root(parent); + blob->handle = Persistent::New(args.This()); + blob->handle.MakeWeak(blob, SliceWeakCallback); + + blob_ref(blob->root); + } else { + // Root slice + + 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 blob) + length - 1); + + if (!d) { + V8::LowMemoryNotification(); + return ThrowException(Exception::Error( + String::New("Could not allocate enough memory"))); + } + + blob = static_cast(d); + + blob->offset = 0; + blob->length = length; + blob->weak = false; + blob->refs = 0; + blob->root = NULL; + blob->handle = Persistent::New(args.This()); + blob->handle.MakeWeak(blob, RootWeakCallback); + } + + args.This()->SetInternalField(0, v8::External::New(blob)); + + struct blob *root = blob_root(blob); + + args.This()-> + SetIndexedPropertiesToExternalArrayData(&root->bytes + blob->offset, + kExternalUnsignedByteArray, + length); + + args.This()->Set(length_symbol, Integer::New(length)); + + return args.This(); +} + + +class AsciiSliceExt: public String::ExternalAsciiStringResource { + public: + + AsciiSliceExt(struct blob *root, size_t start, size_t end) + { + data_ = root->bytes + start; + len_ = end - start; + root_ = root; + blob_ref(root_); + } + + ~AsciiSliceExt() { + blob_unref(root_); + } + + const char* data() const { + return data_; + } + + size_t length() const { + return len_; + } + + private: + const char *data_; + size_t len_; + struct blob *root_; +}; + +static Handle AsciiSlice(const Arguments &args) { + HandleScope scope; + + SLICE_ARGS(args[0], args[1]) + + assert(args.This()->InternalFieldCount() == 1); + struct blob *parent = Unwrap(args.This()); + + size_t start_abs = blob_abs_off(parent, start); + size_t end_abs = blob_abs_off(parent, end); + + assert(start_abs <= end_abs); + + AsciiSliceExt *s = new AsciiSliceExt(blob_root(parent), start_abs, end_abs); + Local string = String::NewExternal(s); + + struct blob *root = blob_root(parent); + assert(root->refs > 0); + + return scope.Close(string); +} + +static Handle Utf8Slice(const Arguments &args) { + HandleScope scope; + + SLICE_ARGS(args[0], args[1]) + + struct blob *parent = Unwrap(args.This()); + size_t start_abs = blob_abs_off(parent, start); + size_t end_abs = blob_abs_off(parent, end); + assert(start_abs <= end_abs); + + struct blob *root = blob_root(parent); + + Local string = + String::New(reinterpret_cast(&root->bytes + start_abs), + end_abs - start_abs); + return scope.Close(string); +} + +static Handle Slice(const Arguments &args) { + HandleScope scope; + + Local argv[3] = { args.This(), args[0], args[1] }; + + Local slice = + constructor_template->GetFunction()->NewInstance(3, argv); + + return scope.Close(slice); +} + +void InitBlob(Handle target) { + HandleScope scope; + + length_symbol = Persistent::New(String::NewSymbol("length")); + + Local t = FunctionTemplate::New(Constructor); + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("Blob")); + + // copy free + NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", AsciiSlice); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Slice); + // TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice); + // copy + NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Utf8Slice); + + target->Set(String::NewSymbol("Blob"), constructor_template->GetFunction()); +} + +} // namespace node diff --git a/src/node_blob.h b/src/node_blob.h new file mode 100644 index 0000000000..cb88c50d92 --- /dev/null +++ b/src/node_blob.h @@ -0,0 +1,12 @@ +#ifndef NODE_BLOB +#define NODE_BLOB + +#include + +namespace node { + +void InitBlob(v8::Handle target); + +} + +#endif // NODE_BLOB diff --git a/wscript b/wscript index 03341b74d9..e74b9326a4 100644 --- a/wscript +++ b/wscript @@ -322,6 +322,7 @@ def build(bld): node.target = "node" node.source = """ src/node.cc + src/node_blob.cc src/node_child_process.cc src/node_constants.cc src/node_dns.cc