1 /**
2 * Copied from node_buffer.cc
3 * see http://www.nodejs.org/
4 *
5 * Node's license follows:
6 *
7 * Copyright 2009, 2010 Ryan Lienhart Dahl. All rights reserved.
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27 #include "node_buffer.h"
28
29 #include <assert.h>
30 #include <stdlib.h> // malloc, free
31 #include <v8.h>
32
33 #include <string.h> // memcpy
34
35 #include <arpa/inet.h> // htons, htonl
36
37 #include "logging.h"
38 #include "node_util.h"
39 #include "util.h"
40
41 //#define BUFFER_DEBUG
42 #ifdef BUFFER_DEBUG
43
44 #define DBG(...) LOGD(__VA_ARGS__)
45
46 #else
47
48 #define DBG(...)
49
50 #endif
51
52 #define MIN(a,b) ((a) < (b) ? (a) : (b))
53
54 using namespace v8;
55
56 #define SLICE_ARGS(start_arg, end_arg) \
57 if (!start_arg->IsInt32() || !end_arg->IsInt32()) { \
58 return ThrowException(Exception::TypeError( \
59 v8::String::New("Bad argument."))); \
60 } \
61 int32_t start = start_arg->Int32Value(); \
62 int32_t end = end_arg->Int32Value(); \
63 if (start < 0 || end < 0) { \
64 return ThrowException(Exception::TypeError( \
65 v8::String::New("Bad argument."))); \
66 } \
67 if (!(start <= end)) { \
68 return ThrowException(Exception::Error( \
69 v8::String::New("Must have start <= end"))); \
70 } \
71 if ((size_t)end > parent->length_) { \
72 return ThrowException(Exception::Error( \
73 v8::String::New("end cannot be longer than parent.length"))); \
74 }
75
76 static Persistent<String> length_symbol;
77 static Persistent<String> chars_written_sym;
78 static Persistent<String> write_sym;
79 Persistent<FunctionTemplate> Buffer::constructor_template;
80
81
82 // Each javascript Buffer object is backed by a Blob object.
83 // the Blob is just a C-level chunk of bytes.
84 // It has a reference count.
85 struct Blob_ {
86 unsigned int refs;
87 size_t length;
88 char *data;
89 };
90 typedef struct Blob_ Blob;
91
92
blob_new(size_t length)93 static inline Blob * blob_new(size_t length) {
94 DBG("blob_new E");
95 Blob * blob = (Blob*) malloc(sizeof(Blob));
96 if (!blob) return NULL;
97
98 blob->data = (char*) malloc(length);
99 if (!blob->data) {
100 DBG("blob_new X no memory for data");
101 free(blob);
102 return NULL;
103 }
104
105 V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Blob) + length);
106 blob->length = length;
107 blob->refs = 0;
108 DBG("blob_new X");
109 return blob;
110 }
111
112
blob_ref(Blob * blob)113 static inline void blob_ref(Blob *blob) {
114 blob->refs++;
115 }
116
117
blob_unref(Blob * blob)118 static inline void blob_unref(Blob *blob) {
119 assert(blob->refs > 0);
120 if (--blob->refs == 0) {
121 DBG("blob_unref == 0");
122 //fprintf(stderr, "free %d bytes\n", blob->length);
123 V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Blob) + blob->length));
124 free(blob->data);
125 free(blob);
126 DBG("blob_unref blob and its data freed");
127 }
128 }
129
130 #if 0
131 // When someone calls buffer.asciiSlice, data is not copied. Instead V8
132 // references in the underlying Blob with this ExternalAsciiStringResource.
133 class AsciiSliceExt: public String::ExternalAsciiStringResource {
134 friend class Buffer;
135 public:
136 AsciiSliceExt(Buffer *parent, size_t start, size_t end) {
137 blob_ = parent->blob();
138 blob_ref(blob_);
139
140 assert(start <= end);
141 length_ = end - start;
142 assert(start + length_ <= parent->length());
143 data_ = parent->data() + start;
144 }
145
146
147 ~AsciiSliceExt() {
148 //fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs);
149 blob_unref(blob_);
150 }
151
152
153 const char* data() const { return data_; }
154 size_t length() const { return length_; }
155
156 private:
157 const char *data_;
158 size_t length_;
159 Blob *blob_;
160 };
161 #endif
162
New(size_t size)163 Buffer* Buffer::New(size_t size) {
164 DBG("Buffer::New(size) E");
165 HandleScope scope;
166
167 Local<Value> arg = Integer::NewFromUnsigned(size);
168 Local<Object> b = constructor_template->GetFunction()->NewInstance(1, &arg);
169
170 DBG("Buffer::New(size) X");
171 return ObjectWrap::Unwrap<Buffer>(b);
172 }
173
174
New(const Arguments & args)175 Handle<Value> Buffer::New(const Arguments &args) {
176 DBG("Buffer::New(args) E");
177 HandleScope scope;
178
179 Buffer *buffer;
180 if ((args.Length() == 0) || args[0]->IsInt32()) {
181 size_t length = 0;
182 if (args[0]->IsInt32()) {
183 length = args[0]->Uint32Value();
184 }
185 buffer = new Buffer(length);
186 } else if (args[0]->IsArray()) {
187 Local<Array> a = Local<Array>::Cast(args[0]);
188 buffer = new Buffer(a->Length());
189 char *p = buffer->data();
190 for (unsigned int i = 0; i < a->Length(); i++) {
191 p[i] = a->Get(i)->Uint32Value();
192 }
193 } else if (args[0]->IsString()) {
194 Local<String> s = args[0]->ToString();
195 enum encoding e = ParseEncoding(args[1], UTF8);
196 int length = e == UTF8 ? s->Utf8Length() : s->Length();
197 buffer = new Buffer(length);
198 } else if (Buffer::HasInstance(args[0]) && args.Length() > 2) {
199 // var slice = new Buffer(buffer, 123, 130);
200 // args: parent, start, end
201 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
202 SLICE_ARGS(args[1], args[2])
203 buffer = new Buffer(parent, start, end);
204 } else {
205 DBG("Buffer::New(args) X Bad argument");
206 return ThrowException(Exception::TypeError(String::New("Bad argument")));
207 }
208
209 buffer->Wrap(args.This());
210 args.This()->SetIndexedPropertiesToExternalArrayData(buffer->data(),
211 kExternalUnsignedByteArray,
212 buffer->length());
213 args.This()->Set(length_symbol, Integer::New(buffer->length_));
214
215 if (args[0]->IsString()) {
216 if (write_sym.IsEmpty()) {
217 write_sym = Persistent<String>::New(String::NewSymbol("write"));
218 }
219
220 Local<Value> write_v = args.This()->Get(write_sym);
221 assert(write_v->IsFunction());
222 Local<Function> write = Local<Function>::Cast(write_v);
223
224 Local<Value> argv[2] = { args[0], args[1] };
225
226 TryCatch try_catch;
227
228 write->Call(args.This(), 2, argv);
229
230 if (try_catch.HasCaught()) {
231 ReportException(&try_catch);
232 }
233 }
234
235 DBG("Buffer::New(args) X");
236 return args.This();
237 }
238
239
Buffer(size_t length)240 Buffer::Buffer(size_t length) : ObjectWrap() {
241 DBG("Buffer::Buffer(length) E");
242 blob_ = blob_new(length);
243 off_ = 0;
244 length_ = length;
245
246 blob_ref(blob_);
247
248 V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
249 DBG("Buffer::Buffer(length) X");
250 }
251
252
Buffer(Buffer * parent,size_t start,size_t end)253 Buffer::Buffer(Buffer *parent, size_t start, size_t end) : ObjectWrap() {
254 DBG("Buffer::Buffer(parent, start, end) E");
255 blob_ = parent->blob_;
256 assert(blob_->refs > 0);
257 blob_ref(blob_);
258
259 assert(start <= end);
260 off_ = parent->off_ + start;
261 length_ = end - start;
262 assert(length_ <= parent->length_);
263
264 V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
265 DBG("Buffer::Buffer(parent, start, end) X");
266 }
267
268
~Buffer()269 Buffer::~Buffer() {
270 DBG("Buffer::~Buffer() E");
271 assert(blob_->refs > 0);
272 //fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs);
273 blob_unref(blob_);
274 V8::AdjustAmountOfExternalAllocatedMemory(-static_cast<long int>(sizeof(Buffer)));
275 DBG("Buffer::~Buffer() X");
276 }
277
278
data()279 char* Buffer::data() {
280 char *p = blob_->data + off_;
281 DBG("Buffer::data() EX p=%p", p);
282 return p;
283 }
284
NewBlob(size_t length)285 void Buffer::NewBlob(size_t length) {
286 DBG("Buffer::NewBlob(length) E");
287 blob_unref(blob_);
288 blob_ = blob_new(length);
289 off_ = 0;
290 length_ = length;
291
292 blob_ref(blob_);
293
294 V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
295 DBG("Buffer::NewBlob(length) X");
296 }
297
298
BinarySlice(const Arguments & args)299 Handle<Value> Buffer::BinarySlice(const Arguments &args) {
300 DBG("Buffer::BinarySlice(args) E");
301 HandleScope scope;
302 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
303 SLICE_ARGS(args[0], args[1])
304
305 const char *data = const_cast<char*>(parent->data() + start);
306 //Local<String> string = String::New(data, end - start);
307
308 Local<Value> b = Encode(data, end - start, BINARY);
309
310 DBG("Buffer::BinarySlice(args) X");
311 return scope.Close(b);
312 }
313
314
AsciiSlice(const Arguments & args)315 Handle<Value> Buffer::AsciiSlice(const Arguments &args) {
316 DBG("Buffer::AsciiSlice(args) E");
317 HandleScope scope;
318 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
319 SLICE_ARGS(args[0], args[1])
320
321 #if 0
322 AsciiSliceExt *ext = new AsciiSliceExt(parent, start, end);
323 Local<String> string = String::NewExternal(ext);
324 // There should be at least two references to the blob now - the parent
325 // and the slice.
326 assert(parent->blob_->refs >= 2);
327 #endif
328
329 const char *data = const_cast<char*>(parent->data() + start);
330 Local<String> string = String::New(data, end - start);
331
332
333 DBG("Buffer::AsciiSlice(args) X");
334 return scope.Close(string);
335 }
336
337
Utf8Slice(const Arguments & args)338 Handle<Value> Buffer::Utf8Slice(const Arguments &args) {
339 DBG("Buffer::Utf8Slice(args) E");
340 HandleScope scope;
341 Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
342 SLICE_ARGS(args[0], args[1])
343 const char *data = const_cast<char*>(parent->data() + start);
344 Local<String> string = String::New(data, end - start);
345 DBG("Buffer::Utf8Slice(args) X");
346 return scope.Close(string);
347 }
348
349
Slice(const Arguments & args)350 Handle<Value> Buffer::Slice(const Arguments &args) {
351 DBG("Buffer::Slice(args) E");
352 HandleScope scope;
353 Local<Value> argv[3] = { args.This(), args[0], args[1] };
354 Local<Object> slice =
355 constructor_template->GetFunction()->NewInstance(3, argv);
356 DBG("Buffer::Slice(args) X");
357 return scope.Close(slice);
358 }
359
360
361 // var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);
Copy(const Arguments & args)362 Handle<Value> Buffer::Copy(const Arguments &args) {
363 DBG("Buffer::Copy(args) E");
364 HandleScope scope;
365
366 Buffer *source = ObjectWrap::Unwrap<Buffer>(args.This());
367
368 if (!Buffer::HasInstance(args[0])) {
369 DBG("Buffer::Copy(args) X arg[0] not buffer");
370 return ThrowException(Exception::TypeError(String::New(
371 "First arg should be a Buffer")));
372 }
373
374 Buffer *target = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
375
376 ssize_t target_start = args[1]->Int32Value();
377 ssize_t source_start = args[2]->Int32Value();
378 ssize_t source_end = args[3]->IsInt32() ? args[3]->Int32Value()
379 : source->length();
380
381 if (source_end < source_start) {
382 DBG("Buffer::Copy(args) X end < start");
383 return ThrowException(Exception::Error(String::New(
384 "sourceEnd < sourceStart")));
385 }
386
387 if (target_start < 0 || ((size_t)target_start) > target->length()) {
388 DBG("Buffer::Copy(args) X targetStart bad");
389 return ThrowException(Exception::Error(String::New(
390 "targetStart out of bounds")));
391 }
392
393 if (source_start < 0 || ((size_t)source_start) > source->length()) {
394 DBG("Buffer::Copy(args) X base source start");
395 return ThrowException(Exception::Error(String::New(
396 "sourceStart out of bounds")));
397 }
398
399 if (source_end < 0 || ((size_t)source_end) > source->length()) {
400 DBG("Buffer::Copy(args) X bad source");
401 return ThrowException(Exception::Error(String::New(
402 "sourceEnd out of bounds")));
403 }
404
405 ssize_t to_copy = MIN(source_end - source_start,
406 target->length() - target_start);
407
408 memcpy((void*)(target->data() + target_start),
409 (const void*)(source->data() + source_start),
410 to_copy);
411
412 DBG("Buffer::Copy(args) X");
413 return scope.Close(Integer::New(to_copy));
414 }
415
416
417 // var charsWritten = buffer.utf8Write(string, offset);
Utf8Write(const Arguments & args)418 Handle<Value> Buffer::Utf8Write(const Arguments &args) {
419 DBG("Buffer::Utf8Write(args) X");
420 HandleScope scope;
421 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
422
423 if (!args[0]->IsString()) {
424 DBG("Buffer::Utf8Write(args) X arg[0] not string");
425 return ThrowException(Exception::TypeError(String::New(
426 "Argument must be a string")));
427 }
428
429 Local<String> s = args[0]->ToString();
430
431 size_t offset = args[1]->Int32Value();
432
433 if (offset >= buffer->length_) {
434 DBG("Buffer::Utf8Write(args) X offset bad");
435 return ThrowException(Exception::TypeError(String::New(
436 "Offset is out of bounds")));
437 }
438
439 const char *p = buffer->data() + offset;
440
441 int char_written;
442
443 int written = s->WriteUtf8((char*)p,
444 buffer->length_ - offset,
445 &char_written,
446 String::HINT_MANY_WRITES_EXPECTED);
447
448 constructor_template->GetFunction()->Set(chars_written_sym,
449 Integer::New(char_written));
450
451 if (written > 0 && p[written-1] == '\0') written--;
452
453 DBG("Buffer::Utf8Write(args) X");
454 return scope.Close(Integer::New(written));
455 }
456
457
458 // var charsWritten = buffer.asciiWrite(string, offset);
AsciiWrite(const Arguments & args)459 Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
460 DBG("Buffer::AsciiWrite(args) E");
461 HandleScope scope;
462
463 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
464
465 if (!args[0]->IsString()) {
466 DBG("Buffer::AsciiWrite(args) X arg[0] not string");
467 return ThrowException(Exception::TypeError(String::New(
468 "Argument must be a string")));
469 }
470
471 Local<String> s = args[0]->ToString();
472
473 size_t offset = args[1]->Int32Value();
474
475 if (offset >= buffer->length_) {
476 DBG("Buffer::AsciiWrite(args) X bad offset");
477 return ThrowException(Exception::TypeError(String::New(
478 "Offset is out of bounds")));
479 }
480
481 const char *p = buffer->data() + offset;
482
483 size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset);
484
485 int written = s->WriteAscii((char*)p, 0, towrite, String::HINT_MANY_WRITES_EXPECTED);
486 DBG("Buffer::AsciiWrite(args) X");
487 return scope.Close(Integer::New(written));
488 }
489
490
BinaryWrite(const Arguments & args)491 Handle<Value> Buffer::BinaryWrite(const Arguments &args) {
492 DBG("Buffer::BinaryWrite(args) E");
493 HandleScope scope;
494
495 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
496
497 if (!args[0]->IsString()) {
498 DBG("Buffer::BinaryWrite(args) X arg[0] not string");
499 return ThrowException(Exception::TypeError(String::New(
500 "Argument must be a string")));
501 }
502
503 Local<String> s = args[0]->ToString();
504
505 size_t offset = args[1]->Int32Value();
506
507 if (offset >= buffer->length_) {
508 DBG("Buffer::BinaryWrite(args) X offset bad");
509 return ThrowException(Exception::TypeError(String::New(
510 "Offset is out of bounds")));
511 }
512
513 char *p = (char*)buffer->data() + offset;
514
515 size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset);
516
517 int written = DecodeWrite(p, towrite, s, BINARY);
518 DBG("Buffer::BinaryWrite(args) X");
519 return scope.Close(Integer::New(written));
520 }
521
522
523 // buffer.unpack(format, index);
524 // Starting at 'index', unpacks binary from the buffer into an array.
525 // 'format' is a string
526 //
527 // FORMAT RETURNS
528 // N uint32_t a 32bit unsigned integer in network byte order
529 // n uint16_t a 16bit unsigned integer in network byte order
530 // o uint8_t a 8bit unsigned integer
Unpack(const Arguments & args)531 Handle<Value> Buffer::Unpack(const Arguments &args) {
532 DBG("Buffer::Unpack(args) E");
533 HandleScope scope;
534 Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
535
536 if (!args[0]->IsString()) {
537 DBG("Buffer::Unpack(args) X arg[0] not string");
538 return ThrowException(Exception::TypeError(String::New(
539 "Argument must be a string")));
540 }
541
542 String::AsciiValue format(args[0]->ToString());
543 uint32_t index = args[1]->Uint32Value();
544
545 #define OUT_OF_BOUNDS ThrowException(Exception::Error(String::New("Out of bounds")))
546
547 Local<Array> array = Array::New(format.length());
548
549 uint8_t uint8;
550 uint16_t uint16;
551 uint32_t uint32;
552
553 for (int i = 0; i < format.length(); i++) {
554 switch ((*format)[i]) {
555 // 32bit unsigned integer in network byte order
556 case 'N':
557 if (index + 3 >= buffer->length_) return OUT_OF_BOUNDS;
558 uint32 = htonl(*(uint32_t*)(buffer->data() + index));
559 array->Set(Integer::New(i), Integer::NewFromUnsigned(uint32));
560 index += 4;
561 break;
562
563 // 16bit unsigned integer in network byte order
564 case 'n':
565 if (index + 1 >= buffer->length_) return OUT_OF_BOUNDS;
566 uint16 = htons(*(uint16_t*)(buffer->data() + index));
567 array->Set(Integer::New(i), Integer::NewFromUnsigned(uint16));
568 index += 2;
569 break;
570
571 // a single octet, unsigned.
572 case 'o':
573 if (index >= buffer->length_) return OUT_OF_BOUNDS;
574 uint8 = (uint8_t)buffer->data()[index];
575 array->Set(Integer::New(i), Integer::NewFromUnsigned(uint8));
576 index += 1;
577 break;
578
579 default:
580 DBG("Buffer::Unpack(args) X unknown format character");
581 return ThrowException(Exception::Error(
582 String::New("Unknown format character")));
583 }
584 }
585
586 DBG("Buffer::Unpack(args) X");
587 return scope.Close(array);
588 }
589
590
591 // var nbytes = Buffer.byteLength("string", "utf8")
ByteLength(const Arguments & args)592 Handle<Value> Buffer::ByteLength(const Arguments &args) {
593 DBG("Buffer::ByteLength(args) E");
594 HandleScope scope;
595
596 if (!args[0]->IsString()) {
597 DBG("Buffer::ByteLength(args) X arg[0] not a string");
598 return ThrowException(Exception::TypeError(String::New(
599 "Argument must be a string")));
600 }
601
602 Local<String> s = args[0]->ToString();
603 enum encoding e = ParseEncoding(args[1], UTF8);
604
605 Local<Integer> length =
606 Integer::New(e == UTF8 ? s->Utf8Length() : s->Length());
607
608 DBG("Buffer::ByteLength(args) X");
609 return scope.Close(length);
610 }
611
InitializeObjectTemplate(Handle<ObjectTemplate> target)612 void Buffer::InitializeObjectTemplate(Handle<ObjectTemplate> target) {
613 DBG("InitializeObjectTemplate(target) E:");
614 HandleScope scope;
615
616 length_symbol = Persistent<String>::New(String::NewSymbol("length"));
617 chars_written_sym = Persistent<String>::New(String::NewSymbol("_charsWritten"));
618
619 Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New);
620 constructor_template = Persistent<FunctionTemplate>::New(t);
621 constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
622 constructor_template->SetClassName(String::NewSymbol("Buffer"));
623
624 // copy free
625 SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice);
626 SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
627 SET_PROTOTYPE_METHOD(constructor_template, "slice", Buffer::Slice);
628 // TODO SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
629 // copy
630 SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);
631
632 SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write);
633 SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite);
634 SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
635 SET_PROTOTYPE_METHOD(constructor_template, "unpack", Buffer::Unpack);
636 SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy);
637
638 SET_PROTOTYPE_METHOD(constructor_template, "byteLength", Buffer::ByteLength);
639
640 target->Set(String::NewSymbol("Buffer"), constructor_template);
641 DBG("InitializeObjectTemplate(target) X:");
642 }
643