Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
initial blobs
Browse files Browse the repository at this point in the history
  • Loading branch information
ry committed Dec 29, 2009
1 parent 359d017 commit 0afed52
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <errno.h>
#include <dlfcn.h> /* dlopen(), dlsym() */

#include <node_blob.h>
#include <node_events.h>
#include <node_dns.h>
#include <node_net.h>
Expand Down Expand Up @@ -847,6 +848,7 @@ static Local<Object> 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
Expand Down
294 changes: 294 additions & 0 deletions src/node_blob.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
#include <assert.h>
#include <stdlib.h> // malloc, free
#include <v8.h>
#include <node.h>

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<String> length_symbol;
static Persistent<FunctionTemplate> 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<Object> 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<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 blob*>(ext->Value());
}


static void RootWeakCallback(Persistent<Value> value, void *data)
{
struct blob *blob = static_cast<struct blob*>(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> value, void *data)
{
struct blob *blob = static_cast<struct blob*>(data);
assert(blob->root != NULL); // this is a slice
assert(value == blob->handle);
blob->handle.Dispose();
blob_unref(blob->root);
}


static Handle<Value> 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<struct blob*>(d);

blob->length = length;
blob->offset = start_abs;
blob->weak = false;
blob->refs = 0;
blob->root = blob_root(parent);
blob->handle = Persistent<Object>::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<struct blob*>(d);

blob->offset = 0;
blob->length = length;
blob->weak = false;
blob->refs = 0;
blob->root = NULL;
blob->handle = Persistent<Object>::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<Value> 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 = String::NewExternal(s);

struct blob *root = blob_root(parent);
assert(root->refs > 0);

return scope.Close(string);
}

static Handle<Value> 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 =
String::New(reinterpret_cast<const char*>(&root->bytes + start_abs),
end_abs - start_abs);
return scope.Close(string);
}

static Handle<Value> 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);
}

void InitBlob(Handle<Object> target) {
HandleScope scope;

length_symbol = Persistent<String>::New(String::NewSymbol("length"));

Local<FunctionTemplate> t = FunctionTemplate::New(Constructor);
constructor_template = Persistent<FunctionTemplate>::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
12 changes: 12 additions & 0 deletions src/node_blob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef NODE_BLOB
#define NODE_BLOB

#include <v8.h>

namespace node {

void InitBlob(v8::Handle<v8::Object> target);

}

#endif // NODE_BLOB
1 change: 1 addition & 0 deletions wscript
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0afed52

Please sign in to comment.