|
@ -43,49 +43,6 @@ static Persistent<String> write_sym; |
|
|
Persistent<FunctionTemplate> Buffer::constructor_template; |
|
|
Persistent<FunctionTemplate> Buffer::constructor_template; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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; |
|
|
|
|
|
}; |
|
|
|
|
|
typedef struct Blob_ Blob; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline Blob * blob_new(size_t length) { |
|
|
|
|
|
Blob * blob = (Blob*) malloc(sizeof(Blob)); |
|
|
|
|
|
if (!blob) return NULL; |
|
|
|
|
|
|
|
|
|
|
|
blob->data = (char*) malloc(length); |
|
|
|
|
|
if (!blob->data) { |
|
|
|
|
|
free(blob); |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Blob) + length); |
|
|
|
|
|
blob->length = length; |
|
|
|
|
|
blob->refs = 0; |
|
|
|
|
|
return blob; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void blob_ref(Blob *blob) { |
|
|
|
|
|
blob->refs++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void blob_unref(Blob *blob) { |
|
|
|
|
|
assert(blob->refs > 0); |
|
|
|
|
|
if (--blob->refs == 0) { |
|
|
|
|
|
//fprintf(stderr, "free %d bytes\n", blob->length);
|
|
|
|
|
|
V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Blob) + blob->length)); |
|
|
|
|
|
free(blob->data); |
|
|
|
|
|
free(blob); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static inline size_t base64_decoded_size(const char *src, size_t size) { |
|
|
static inline size_t base64_decoded_size(const char *src, size_t size) { |
|
|
const char *const end = src + size; |
|
|
const char *const end = src + size; |
|
|
const int remainder = size % 4; |
|
|
const int remainder = size % 4; |
|
@ -110,39 +67,21 @@ static inline size_t base64_decoded_size(const char *src, size_t size) { |
|
|
return size; |
|
|
return size; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#if 0 |
|
|
|
|
|
// 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_); |
|
|
|
|
|
|
|
|
|
|
|
assert(start <= end); |
|
|
|
|
|
length_ = end - start; |
|
|
|
|
|
assert(start + length_ <= parent->length()); |
|
|
|
|
|
data_ = parent->data() + start; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static size_t ByteLength (Handle<String> string, enum encoding enc) { |
|
|
|
|
|
HandleScope scope; |
|
|
|
|
|
|
|
|
~AsciiSliceExt() { |
|
|
if (enc == UTF8) { |
|
|
//fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs);
|
|
|
return string->Utf8Length(); |
|
|
blob_unref(blob_); |
|
|
} else if (enc == BASE64) { |
|
|
|
|
|
String::Utf8Value v(string); |
|
|
|
|
|
return base64_decoded_size(*v, v.length()); |
|
|
|
|
|
} else { |
|
|
|
|
|
return string->Length(); |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char* data() const { return data_; } |
|
|
|
|
|
size_t length() const { return length_; } |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
const char *data_; |
|
|
|
|
|
size_t length_; |
|
|
|
|
|
Blob *blob_; |
|
|
|
|
|
}; |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
Buffer* Buffer::New(size_t size) { |
|
|
Buffer* Buffer::New(size_t size) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
|
|
|
|
|
@ -153,6 +92,40 @@ Buffer* Buffer::New(size_t size) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char* Buffer::Data(Handle<Object> obj) { |
|
|
|
|
|
if (obj->HasIndexedPropertiesInPixelData()) { |
|
|
|
|
|
return (char*)obj->GetIndexedPropertiesPixelData(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
HandleScope scope; |
|
|
|
|
|
|
|
|
|
|
|
// Return true for "SlowBuffer"
|
|
|
|
|
|
if (constructor_template->HasInstance(obj)) { |
|
|
|
|
|
return ObjectWrap::Unwrap<Buffer>(obj)->data(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Not a buffer.
|
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t Buffer::Length(Handle<Object> obj) { |
|
|
|
|
|
if (obj->HasIndexedPropertiesInPixelData()) { |
|
|
|
|
|
return (size_t)obj->GetIndexedPropertiesPixelDataLength(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
HandleScope scope; |
|
|
|
|
|
|
|
|
|
|
|
// Return true for "SlowBuffer"
|
|
|
|
|
|
if (constructor_template->HasInstance(obj)) { |
|
|
|
|
|
return ObjectWrap::Unwrap<Buffer>(obj)->length(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Not a buffer.
|
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Handle<Value> Buffer::New(const Arguments &args) { |
|
|
Handle<Value> Buffer::New(const Arguments &args) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
|
|
|
|
|
@ -172,31 +145,6 @@ Handle<Value> Buffer::New(const Arguments &args) { |
|
|
size_t length = args[0]->Uint32Value(); |
|
|
size_t length = args[0]->Uint32Value(); |
|
|
buffer = new Buffer(length); |
|
|
buffer = new Buffer(length); |
|
|
|
|
|
|
|
|
} else if (args[0]->IsArray()) { |
|
|
|
|
|
Local<Array> a = Local<Array>::Cast(args[0]); |
|
|
|
|
|
buffer = new Buffer(a->Length()); |
|
|
|
|
|
char *p = buffer->data(); |
|
|
|
|
|
for (int i = 0; i < a->Length(); i++) { |
|
|
|
|
|
p[i] = a->Get(i)->Uint32Value(); |
|
|
|
|
|
} |
|
|
|
|
|
} else if (args[0]->IsString()) { |
|
|
|
|
|
Local<String> s = args[0]->ToString(); |
|
|
|
|
|
enum encoding e = ParseEncoding(args[1], UTF8); |
|
|
|
|
|
int length = e == UTF8 ? s->Utf8Length() : s->Length(); |
|
|
|
|
|
|
|
|
|
|
|
// input gets base64-decoded, adjust buffer size
|
|
|
|
|
|
if (e == BASE64) { |
|
|
|
|
|
const String::AsciiValue data(s); |
|
|
|
|
|
length = base64_decoded_size(*data, data.length()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
buffer = new Buffer(length); |
|
|
|
|
|
} 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 { |
|
|
} else { |
|
|
return ThrowException(Exception::TypeError(String::New("Bad argument"))); |
|
|
return ThrowException(Exception::TypeError(String::New("Bad argument"))); |
|
|
} |
|
|
} |
|
@ -207,65 +155,28 @@ Handle<Value> Buffer::New(const Arguments &args) { |
|
|
buffer->length()); |
|
|
buffer->length()); |
|
|
args.This()->Set(length_symbol, Integer::New(buffer->length_)); |
|
|
args.This()->Set(length_symbol, Integer::New(buffer->length_)); |
|
|
|
|
|
|
|
|
if (args[0]->IsString()) { |
|
|
|
|
|
if (write_sym.IsEmpty()) { |
|
|
|
|
|
write_sym = Persistent<String>::New(String::NewSymbol("write")); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Local<Value> write_v = args.This()->Get(write_sym); |
|
|
|
|
|
assert(write_v->IsFunction()); |
|
|
|
|
|
Local<Function> write = Local<Function>::Cast(write_v); |
|
|
|
|
|
|
|
|
|
|
|
Local<Value> argv[2] = { args[0], args[1] }; |
|
|
|
|
|
|
|
|
|
|
|
TryCatch try_catch; |
|
|
|
|
|
|
|
|
|
|
|
write->Call(args.This(), 2, argv); |
|
|
|
|
|
|
|
|
|
|
|
if (try_catch.HasCaught()) { |
|
|
|
|
|
FatalException(try_catch); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return args.This(); |
|
|
return args.This(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Buffer::Buffer(size_t length) : ObjectWrap() { |
|
|
Buffer::Buffer(size_t length) : ObjectWrap() { |
|
|
blob_ = blob_new(length); |
|
|
|
|
|
off_ = 0; |
|
|
off_ = 0; |
|
|
length_ = length; |
|
|
length_ = length; |
|
|
|
|
|
data_ = new char[length_]; |
|
|
|
|
|
|
|
|
blob_ref(blob_); |
|
|
V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer) + length_); |
|
|
|
|
|
|
|
|
V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Buffer::Buffer(Buffer *parent, size_t start, size_t end) : ObjectWrap() { |
|
|
|
|
|
blob_ = parent->blob_; |
|
|
|
|
|
assert(blob_->refs > 0); |
|
|
|
|
|
blob_ref(blob_); |
|
|
|
|
|
|
|
|
|
|
|
assert(start <= end); |
|
|
|
|
|
off_ = parent->off_ + start; |
|
|
|
|
|
length_ = end - start; |
|
|
|
|
|
assert(length_ <= parent->length_); |
|
|
|
|
|
|
|
|
|
|
|
V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer)); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Buffer::~Buffer() { |
|
|
Buffer::~Buffer() { |
|
|
assert(blob_->refs > 0); |
|
|
|
|
|
//fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs);
|
|
|
//fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs);
|
|
|
blob_unref(blob_); |
|
|
delete data_; |
|
|
V8::AdjustAmountOfExternalAllocatedMemory(-static_cast<long int>(sizeof(Buffer))); |
|
|
V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Buffer) + length_)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char* Buffer::data() { |
|
|
char* Buffer::data() { |
|
|
return blob_->data + off_; |
|
|
return data_; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -274,7 +185,7 @@ Handle<Value> Buffer::BinarySlice(const Arguments &args) { |
|
|
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
|
|
|
|
|
|
const char *data = const_cast<char*>(parent->data() + start); |
|
|
char *data = parent->data() + start; |
|
|
//Local<String> string = String::New(data, end - start);
|
|
|
//Local<String> string = String::New(data, end - start);
|
|
|
|
|
|
|
|
|
Local<Value> b = Encode(data, end - start, BINARY); |
|
|
Local<Value> b = Encode(data, end - start, BINARY); |
|
@ -288,18 +199,9 @@ Handle<Value> Buffer::AsciiSlice(const Arguments &args) { |
|
|
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
|
|
|
|
|
|
#if 0 |
|
|
char* data = parent->data() + start; |
|
|
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); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
const char *data = const_cast<char*>(parent->data() + start); |
|
|
|
|
|
Local<String> string = String::New(data, end - start); |
|
|
Local<String> string = String::New(data, end - start); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return scope.Close(string); |
|
|
return scope.Close(string); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -308,7 +210,7 @@ Handle<Value> Buffer::Utf8Slice(const Arguments &args) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
SLICE_ARGS(args[0], args[1]) |
|
|
const char *data = const_cast<char*>(parent->data() + start); |
|
|
char *data = parent->data() + start; |
|
|
Local<String> string = String::New(data, end - start); |
|
|
Local<String> string = String::New(data, end - start); |
|
|
return scope.Close(string); |
|
|
return scope.Close(string); |
|
|
} |
|
|
} |
|
@ -400,15 +302,6 @@ Handle<Value> Buffer::Base64Slice(const Arguments &args) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);
|
|
|
// var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);
|
|
|
Handle<Value> Buffer::Copy(const Arguments &args) { |
|
|
Handle<Value> Buffer::Copy(const Arguments &args) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
@ -452,25 +345,21 @@ Handle<Value> Buffer::Copy(const Arguments &args) { |
|
|
"sourceEnd out of bounds"))); |
|
|
"sourceEnd out of bounds"))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
ssize_t to_copy = MIN(source_end - source_start, |
|
|
ssize_t to_copy = MIN(MIN(source_end - source_start, |
|
|
target->length() - target_start); |
|
|
target->length() - target_start), |
|
|
|
|
|
source->length() - source_start); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (target->blob_ == source->blob_) { |
|
|
// need to use slightly slower memmove is the ranges might overlap
|
|
|
// need to use slightly slower memmove is the ranges might overlap
|
|
|
memmove((void*)(target->data() + target_start), |
|
|
memmove((void*)(target->data() + target_start), |
|
|
(const void*)(source->data() + source_start), |
|
|
(const void*)(source->data() + source_start), |
|
|
to_copy); |
|
|
to_copy); |
|
|
|
|
|
} else { |
|
|
|
|
|
memcpy((void*)(target->data() + target_start), |
|
|
|
|
|
(const void*)(source->data() + source_start), |
|
|
|
|
|
to_copy); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return scope.Close(Integer::New(to_copy)); |
|
|
return scope.Close(Integer::New(to_copy)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// var charsWritten = buffer.utf8Write(string, offset);
|
|
|
// var charsWritten = buffer.utf8Write(string, offset, [maxLength]);
|
|
|
Handle<Value> Buffer::Utf8Write(const Arguments &args) { |
|
|
Handle<Value> Buffer::Utf8Write(const Arguments &args) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
@ -482,19 +371,23 @@ Handle<Value> Buffer::Utf8Write(const Arguments &args) { |
|
|
|
|
|
|
|
|
Local<String> s = args[0]->ToString(); |
|
|
Local<String> s = args[0]->ToString(); |
|
|
|
|
|
|
|
|
size_t offset = args[1]->Int32Value(); |
|
|
size_t offset = args[1]->Uint32Value(); |
|
|
|
|
|
|
|
|
if (s->Utf8Length() > 0 && offset >= buffer->length_) { |
|
|
if (s->Utf8Length() > 0 && offset >= buffer->length_) { |
|
|
return ThrowException(Exception::TypeError(String::New( |
|
|
return ThrowException(Exception::TypeError(String::New( |
|
|
"Offset is out of bounds"))); |
|
|
"Offset is out of bounds"))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const char *p = buffer->data() + offset; |
|
|
size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset |
|
|
|
|
|
: args[2]->Uint32Value(); |
|
|
|
|
|
max_length = MIN(buffer->length_ - offset, max_length); |
|
|
|
|
|
|
|
|
|
|
|
char* p = buffer->data() + offset; |
|
|
|
|
|
|
|
|
int char_written; |
|
|
int char_written; |
|
|
|
|
|
|
|
|
int written = s->WriteUtf8((char*)p, |
|
|
int written = s->WriteUtf8(p, |
|
|
buffer->length_ - offset, |
|
|
max_length, |
|
|
&char_written, |
|
|
&char_written, |
|
|
String::HINT_MANY_WRITES_EXPECTED); |
|
|
String::HINT_MANY_WRITES_EXPECTED); |
|
|
|
|
|
|
|
@ -527,15 +420,20 @@ Handle<Value> Buffer::AsciiWrite(const Arguments &args) { |
|
|
"Offset is out of bounds"))); |
|
|
"Offset is out of bounds"))); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const char *p = buffer->data() + offset; |
|
|
size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset |
|
|
|
|
|
: args[2]->Uint32Value(); |
|
|
|
|
|
max_length = MIN(s->Length(), MIN(buffer->length_ - offset, max_length)); |
|
|
|
|
|
|
|
|
size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset); |
|
|
char *p = buffer->data() + offset; |
|
|
|
|
|
|
|
|
int written = s->WriteAscii((char*)p, 0, towrite, String::HINT_MANY_WRITES_EXPECTED); |
|
|
int written = s->WriteAscii(p, |
|
|
|
|
|
0, |
|
|
|
|
|
max_length, |
|
|
|
|
|
String::HINT_MANY_WRITES_EXPECTED); |
|
|
return scope.Close(Integer::New(written)); |
|
|
return scope.Close(Integer::New(written)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// var bytesWritten = buffer.base64Write(string, offset);
|
|
|
// var bytesWritten = buffer.base64Write(string, offset, [maxLength]);
|
|
|
Handle<Value> Buffer::Base64Write(const Arguments &args) { |
|
|
Handle<Value> Buffer::Base64Write(const Arguments &args) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
|
|
|
|
|
|
@ -574,7 +472,7 @@ Handle<Value> Buffer::Base64Write(const Arguments &args) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
char a, b, c, d; |
|
|
char a, b, c, d; |
|
|
char *dst = buffer->data(); |
|
|
char* dst = buffer->data() + offset; |
|
|
const char *src = *s; |
|
|
const char *src = *s; |
|
|
const char *const srcEnd = src + s.length(); |
|
|
const char *const srcEnd = src + s.length(); |
|
|
|
|
|
|
|
@ -628,70 +526,6 @@ Handle<Value> Buffer::BinaryWrite(const Arguments &args) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// buffer.unpack(format, index);
|
|
|
|
|
|
// Starting at 'index', unpacks binary from the buffer into an array.
|
|
|
|
|
|
// 'format' is a string
|
|
|
|
|
|
//
|
|
|
|
|
|
// FORMAT RETURNS
|
|
|
|
|
|
// N uint32_t a 32bit unsigned integer in network byte order
|
|
|
|
|
|
// n uint16_t a 16bit unsigned integer in network byte order
|
|
|
|
|
|
// o uint8_t a 8bit unsigned integer
|
|
|
|
|
|
Handle<Value> Buffer::Unpack(const Arguments &args) { |
|
|
|
|
|
HandleScope scope; |
|
|
|
|
|
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This()); |
|
|
|
|
|
|
|
|
|
|
|
if (!args[0]->IsString()) { |
|
|
|
|
|
return ThrowException(Exception::TypeError(String::New( |
|
|
|
|
|
"Argument must be a string"))); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
String::AsciiValue format(args[0]->ToString()); |
|
|
|
|
|
uint32_t index = args[1]->Uint32Value(); |
|
|
|
|
|
|
|
|
|
|
|
#define OUT_OF_BOUNDS ThrowException(Exception::Error(String::New("Out of bounds"))) |
|
|
|
|
|
|
|
|
|
|
|
Local<Array> array = Array::New(format.length()); |
|
|
|
|
|
|
|
|
|
|
|
uint8_t uint8; |
|
|
|
|
|
uint16_t uint16; |
|
|
|
|
|
uint32_t uint32; |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < format.length(); i++) { |
|
|
|
|
|
switch ((*format)[i]) { |
|
|
|
|
|
// 32bit unsigned integer in network byte order
|
|
|
|
|
|
case 'N': |
|
|
|
|
|
if (index + 3 >= buffer->length_) return OUT_OF_BOUNDS; |
|
|
|
|
|
uint32 = htonl(*(uint32_t*)(buffer->data() + index)); |
|
|
|
|
|
array->Set(Integer::New(i), Integer::NewFromUnsigned(uint32)); |
|
|
|
|
|
index += 4; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
// 16bit unsigned integer in network byte order
|
|
|
|
|
|
case 'n': |
|
|
|
|
|
if (index + 1 >= buffer->length_) return OUT_OF_BOUNDS; |
|
|
|
|
|
uint16 = htons(*(uint16_t*)(buffer->data() + index)); |
|
|
|
|
|
array->Set(Integer::New(i), Integer::NewFromUnsigned(uint16)); |
|
|
|
|
|
index += 2; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
// a single octet, unsigned.
|
|
|
|
|
|
case 'o': |
|
|
|
|
|
if (index >= buffer->length_) return OUT_OF_BOUNDS; |
|
|
|
|
|
uint8 = (uint8_t)buffer->data()[index]; |
|
|
|
|
|
array->Set(Integer::New(i), Integer::NewFromUnsigned(uint8)); |
|
|
|
|
|
index += 1; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
return ThrowException(Exception::Error( |
|
|
|
|
|
String::New("Unknown format character"))); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return scope.Close(array); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// var nbytes = Buffer.byteLength("string", "utf8")
|
|
|
// var nbytes = Buffer.byteLength("string", "utf8")
|
|
|
Handle<Value> Buffer::ByteLength(const Arguments &args) { |
|
|
Handle<Value> Buffer::ByteLength(const Arguments &args) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
@ -704,10 +538,7 @@ Handle<Value> Buffer::ByteLength(const Arguments &args) { |
|
|
Local<String> s = args[0]->ToString(); |
|
|
Local<String> s = args[0]->ToString(); |
|
|
enum encoding e = ParseEncoding(args[1], UTF8); |
|
|
enum encoding e = ParseEncoding(args[1], UTF8); |
|
|
|
|
|
|
|
|
Local<Integer> length = |
|
|
return scope.Close(Integer::New(node::ByteLength(s, e))); |
|
|
Integer::New(e == UTF8 ? s->Utf8Length() : s->Length()); |
|
|
|
|
|
|
|
|
|
|
|
return scope.Close(length); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -726,6 +557,18 @@ Handle<Value> Buffer::MakeFastBuffer(const Arguments &args) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool Buffer::HasInstance(v8::Handle<v8::Value> val) { |
|
|
|
|
|
if (!val->IsObject()) return false; |
|
|
|
|
|
v8::Local<v8::Object> obj = val->ToObject(); |
|
|
|
|
|
|
|
|
|
|
|
if (obj->HasIndexedPropertiesInPixelData()) return true; |
|
|
|
|
|
|
|
|
|
|
|
// Return true for "SlowBuffer"
|
|
|
|
|
|
if (constructor_template->HasInstance(obj)) return true; |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Buffer::Initialize(Handle<Object> target) { |
|
|
void Buffer::Initialize(Handle<Object> target) { |
|
|
HandleScope scope; |
|
|
HandleScope scope; |
|
@ -736,13 +579,12 @@ void Buffer::Initialize(Handle<Object> target) { |
|
|
Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New); |
|
|
Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New); |
|
|
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("Buffer")); |
|
|
constructor_template->SetClassName(String::NewSymbol("SlowBuffer")); |
|
|
|
|
|
|
|
|
// copy free
|
|
|
// copy free
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Buffer::Slice); |
|
|
|
|
|
// TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
|
|
|
// TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
|
|
|
// copy
|
|
|
// copy
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice); |
|
@ -751,7 +593,6 @@ void Buffer::Initialize(Handle<Object> target) { |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "unpack", Buffer::Unpack); |
|
|
|
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy); |
|
|
NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy); |
|
|
|
|
|
|
|
|
NODE_SET_METHOD(constructor_template->GetFunction(), |
|
|
NODE_SET_METHOD(constructor_template->GetFunction(), |
|
@ -761,7 +602,7 @@ void Buffer::Initialize(Handle<Object> target) { |
|
|
"makeFastBuffer", |
|
|
"makeFastBuffer", |
|
|
Buffer::MakeFastBuffer); |
|
|
Buffer::MakeFastBuffer); |
|
|
|
|
|
|
|
|
target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction()); |
|
|
target->Set(String::NewSymbol("SlowBuffer"), constructor_template->GetFunction()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|