1 #include <node_buffer.h>
4 #include <stdlib.h> // malloc, free
7 #include <string.h> // memcpy
9 #include <arpa/inet.h> // htons, htonl
13 #define MIN(a,b) ((a) < (b) ? (a) : (b))
19 #define SLICE_ARGS(start_arg, end_arg) \
20 if (!start_arg->IsInt32() || !end_arg->IsInt32()) { \
21 return ThrowException(Exception::TypeError( \
22 String::New("Bad argument."))); \
24 int32_t start = start_arg->Int32Value(); \
25 int32_t end = end_arg->Int32Value(); \
26 if (start < 0 || end < 0) { \
27 return ThrowException(Exception::TypeError( \
28 String::New("Bad argument."))); \
30 if (!(start <= end)) { \
31 return ThrowException(Exception::Error( \
32 String::New("Must have start <= end"))); \
34 if ((size_t)end > parent->length_) { \
35 return ThrowException(Exception::Error( \
36 String::New("end cannot be longer than parent.length"))); \
40 static Persistent<String> length_symbol;
41 Persistent<FunctionTemplate> Buffer::constructor_template;
44 // Each javascript Buffer object is backed by a Blob object.
45 // the Blob is just a C-level chunk of bytes.
46 // It has a reference count.
52 typedef struct Blob_ Blob;
55 static inline Blob * blob_new(size_t length) {
56 Blob * blob = (Blob*) malloc(sizeof(Blob));
57 if (!blob) return NULL;
59 blob->data = (char*) malloc(length);
65 V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Blob) + length);
66 blob->length = length;
72 static inline void blob_ref(Blob *blob) {
77 static inline void blob_unref(Blob *blob) {
78 assert(blob->refs > 0);
79 if (--blob->refs == 0) {
80 //fprintf(stderr, "free %d bytes\n", blob->length);
81 V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Blob) + blob->length));
88 // When someone calls buffer.asciiSlice, data is not copied. Instead V8
89 // references in the underlying Blob with this ExternalAsciiStringResource.
90 class AsciiSliceExt: public String::ExternalAsciiStringResource {
93 AsciiSliceExt(Buffer *parent, size_t start, size_t end) {
94 blob_ = parent->blob();
98 length_ = end - start;
99 assert(start + length_ <= parent->length());
100 data_ = parent->data() + start;
105 //fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs);
110 const char* data() const { return data_; }
111 size_t length() const { return length_; }
121 Handle<Value> Buffer::New(const Arguments &args) {
125 if (args[0]->IsInt32()) {
126 // var buffer = new Buffer(1024);
127 size_t length = args[0]->Uint32Value();
128 buffer = new Buffer(length);
130 } else if (Buffer::HasInstance(args[0]) && args.Length() > 2) {
131 // var slice = new Buffer(buffer, 123, 130);
132 // args: parent, start, end
133 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
134 SLICE_ARGS(args[1], args[2])
135 buffer = new Buffer(parent, start, end);
137 return ThrowException(Exception::TypeError(String::New("Bad argument")));
140 buffer->Wrap(args.This());
141 args.This()->SetIndexedPropertiesToExternalArrayData(buffer->data(),
142 kExternalUnsignedByteArray,
144 args.This()->Set(length_symbol, Integer::New(buffer->length_));
149 Buffer::Buffer(size_t length) : ObjectWrap() {
150 blob_ = blob_new(length);
156 V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
160 Buffer::Buffer(Buffer *parent, size_t start, size_t end) : ObjectWrap() {
161 blob_ = parent->blob_;
162 assert(blob_->refs > 0);
165 assert(start <= end);
167 length_ = end - start;
168 assert(length_ <= parent->length_);
170 V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
175 assert(blob_->refs > 0);
176 //fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs);
178 V8::AdjustAmountOfExternalAllocatedMemory(-static_cast<long int>(sizeof(Buffer)));
182 char* Buffer::data() {
183 return blob_->data + off_;
187 Handle<Value> Buffer::BinarySlice(const Arguments &args) {
189 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
190 SLICE_ARGS(args[0], args[1])
192 const char *data = const_cast<char*>(parent->data() + start);
193 //Local<String> string = String::New(data, end - start);
195 Local<Value> b = Encode(data, end - start, BINARY);
197 return scope.Close(b);
201 Handle<Value> Buffer::AsciiSlice(const Arguments &args) {
203 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
204 SLICE_ARGS(args[0], args[1])
207 AsciiSliceExt *ext = new AsciiSliceExt(parent, start, end);
208 Local<String> string = String::NewExternal(ext);
209 // There should be at least two references to the blob now - the parent
211 assert(parent->blob_->refs >= 2);
214 const char *data = const_cast<char*>(parent->data() + start);
215 Local<String> string = String::New(data, end - start);
218 return scope.Close(string);
222 Handle<Value> Buffer::Utf8Slice(const Arguments &args) {
224 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
225 SLICE_ARGS(args[0], args[1])
226 const char *data = const_cast<char*>(parent->data() + start);
227 Local<String> string = String::New(data, end - start);
228 return scope.Close(string);
232 Handle<Value> Buffer::Slice(const Arguments &args) {
234 Local<Value> argv[3] = { args.This(), args[0], args[1] };
235 Local<Object> slice =
236 constructor_template->GetFunction()->NewInstance(3, argv);
237 return scope.Close(slice);
241 // var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);
242 Handle<Value> Buffer::Copy(const Arguments &args) {
245 Buffer *source = ObjectWrap::Unwrap<Buffer>(args.This());
247 if (!Buffer::HasInstance(args[0])) {
248 return ThrowException(Exception::TypeError(String::New(
249 "First arg should be a Buffer")));
252 Buffer *target = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
254 ssize_t target_start = args[1]->Int32Value();
255 ssize_t source_start = args[2]->Int32Value();
256 ssize_t source_end = args[3]->IsInt32() ? args[3]->Int32Value()
259 if (source_end < source_start) {
260 return ThrowException(Exception::Error(String::New(
261 "sourceEnd < sourceStart")));
264 if (target_start >= target->length()) {
265 return ThrowException(Exception::Error(String::New(
266 "targetStart out of bounds")));
269 if (source_start >= source->length()) {
270 return ThrowException(Exception::Error(String::New(
271 "sourceStart out of bounds")));
274 if (source_end > source->length()) {
275 return ThrowException(Exception::Error(String::New(
276 "sourceEnd out of bounds")));
279 ssize_t to_copy = MIN(source_end - source_start,
280 target->length() - target_start);
282 memcpy((void*)(target->data() + target_start),
283 (const void*)(source->data() + source_start),
286 return scope.Close(Integer::New(to_copy));
290 // var charsWritten = buffer.utf8Write(string, offset);
291 Handle<Value> Buffer::Utf8Write(const Arguments &args) {
293 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
295 if (!args[0]->IsString()) {
296 return ThrowException(Exception::TypeError(String::New(
297 "Argument must be a string")));
300 Local<String> s = args[0]->ToString();
302 size_t offset = args[1]->Int32Value();
304 if (offset >= buffer->length_) {
305 return ThrowException(Exception::TypeError(String::New(
306 "Offset is out of bounds")));
309 const char *p = buffer->data() + offset;
312 int written = s->WriteUtf8((char*)p, buffer->length_ - offset);
314 if (written > 0 && p[written-1] == '\0') written--;
316 return scope.Close(Integer::New(written));
320 // var charsWritten = buffer.asciiWrite(string, offset);
321 Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
324 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
326 if (!args[0]->IsString()) {
327 return ThrowException(Exception::TypeError(String::New(
328 "Argument must be a string")));
331 Local<String> s = args[0]->ToString();
333 size_t offset = args[1]->Int32Value();
335 if (offset >= buffer->length_) {
336 return ThrowException(Exception::TypeError(String::New(
337 "Offset is out of bounds")));
340 const char *p = buffer->data() + offset;
342 size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset);
345 int written = s->WriteAscii((char*)p, 0, towrite);
346 return scope.Close(Integer::New(written));
350 Handle<Value> Buffer::BinaryWrite(const Arguments &args) {
353 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
355 if (!args[0]->IsString()) {
356 return ThrowException(Exception::TypeError(String::New(
357 "Argument must be a string")));
360 Local<String> s = args[0]->ToString();
362 size_t offset = args[1]->Int32Value();
364 if (offset >= buffer->length_) {
365 return ThrowException(Exception::TypeError(String::New(
366 "Offset is out of bounds")));
369 char *p = (char*)buffer->data() + offset;
371 size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset);
373 int written = DecodeWrite(p, towrite, s, BINARY);
374 return scope.Close(Integer::New(written));
378 // buffer.unpack(format, index);
379 // Starting at 'index', unpacks binary from the buffer into an array.
380 // 'format' is a string
383 // N uint32_t a 32bit unsigned integer in network byte order
384 // n uint16_t a 16bit unsigned integer in network byte order
385 // o uint8_t a 8bit unsigned integer
386 Handle<Value> Buffer::Unpack(const Arguments &args) {
388 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
390 if (!args[0]->IsString()) {
391 return ThrowException(Exception::TypeError(String::New(
392 "Argument must be a string")));
395 String::AsciiValue format(args[0]->ToString());
396 uint32_t index = args[1]->Uint32Value();
398 #define OUT_OF_BOUNDS ThrowException(Exception::Error(String::New("Out of bounds")))
400 Local<Array> array = Array::New(format.length());
406 for (int i = 0; i < format.length(); i++) {
407 switch ((*format)[i]) {
408 // 32bit unsigned integer in network byte order
410 if (index + 3 >= buffer->length_) return OUT_OF_BOUNDS;
411 uint32 = htonl(*(uint32_t*)(buffer->data() + index));
412 array->Set(Integer::New(i), Integer::NewFromUnsigned(uint32));
416 // 16bit unsigned integer in network byte order
418 if (index + 1 >= buffer->length_) return OUT_OF_BOUNDS;
419 uint16 = htons(*(uint16_t*)(buffer->data() + index));
420 array->Set(Integer::New(i), Integer::NewFromUnsigned(uint16));
424 // a single octet, unsigned.
426 if (index >= buffer->length_) return OUT_OF_BOUNDS;
427 uint8 = (uint8_t)buffer->data()[index];
428 array->Set(Integer::New(i), Integer::NewFromUnsigned(uint8));
433 return ThrowException(Exception::Error(
434 String::New("Unknown format character")));
438 return scope.Close(array);
442 // var nbytes = Buffer.byteLength("string", "utf8")
443 Handle<Value> Buffer::ByteLength(const Arguments &args) {
446 if (!args[0]->IsString()) {
447 return ThrowException(Exception::TypeError(String::New(
448 "Argument must be a string")));
451 Local<String> s = args[0]->ToString();
452 enum encoding e = ParseEncoding(args[1], UTF8);
454 Local<Integer> length =
455 Integer::New(e == UTF8 ? s->Utf8Length() : s->Length());
457 return scope.Close(length);
461 void Buffer::Initialize(Handle<Object> target) {
464 length_symbol = Persistent<String>::New(String::NewSymbol("length"));
466 Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New);
467 constructor_template = Persistent<FunctionTemplate>::New(t);
468 constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
469 constructor_template->SetClassName(String::NewSymbol("Buffer"));
472 NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice);
473 NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
474 NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Buffer::Slice);
475 // TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
477 NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);
479 NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write);
480 NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite);
481 NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
482 NODE_SET_PROTOTYPE_METHOD(constructor_template, "unpack", Buffer::Unpack);
483 NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy);
485 NODE_SET_METHOD(constructor_template->GetFunction(),
489 target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction());