mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
4 changed files with 309 additions and 0 deletions
@ -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
|
@ -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
|
Loading…
Reference in new issue