1 // Copyright Joyent, Inc. and other Node contributors.
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
24 #include "node_buffer.h"
29 #include "string_bytes.h"
30 #include "v8-profiler.h"
37 #define MIN(a, b) ((a) < (b) ? (a) : (b))
39 #define CHECK_NOT_OOB(r) \
40 do { if (!(r)) return ThrowRangeError("out of range index"); } while (0)
42 #define ARGS_THIS(argT) \
43 Local<Object> obj = argT; \
44 size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength(); \
45 char* obj_data = static_cast<char*>( \
46 obj->GetIndexedPropertiesExternalArrayData()); \
48 assert(obj_data != NULL);
50 #define SLICE_START_END(start_arg, end_arg, end_max) \
53 CHECK_NOT_OOB(ParseArrayIndex(start_arg, 0, &start)); \
54 CHECK_NOT_OOB(ParseArrayIndex(end_arg, end_max, &end)); \
55 if (end < start) end = start; \
56 CHECK_NOT_OOB(end <= end_max); \
57 size_t length = end - start;
62 using v8::ArrayBuffer;
65 using v8::FunctionCallbackInfo;
66 using v8::FunctionTemplate;
68 using v8::HandleScope;
77 bool HasInstance(Handle<Value> val) {
78 return val->IsObject() && HasInstance(val.As<Object>());
82 bool HasInstance(Handle<Object> obj) {
83 if (!obj->HasIndexedPropertiesInExternalArrayData())
85 v8::ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
86 return type == v8::kExternalUnsignedByteArray;
90 char* Data(Handle<Value> val) {
91 assert(val->IsObject());
92 // Use a fully qualified name here to work around a bug in gcc 4.2.
93 // It mistakes an unadorned call to Data() for the v8::String::Data type.
94 return node::Buffer::Data(val.As<Object>());
98 char* Data(Handle<Object> obj) {
99 assert(obj->HasIndexedPropertiesInExternalArrayData());
100 return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
104 size_t Length(Handle<Value> val) {
105 assert(val->IsObject());
106 return Length(val.As<Object>());
110 size_t Length(Handle<Object> obj) {
111 assert(obj->HasIndexedPropertiesInExternalArrayData());
112 return obj->GetIndexedPropertiesExternalArrayDataLength();
116 Local<Object> New(Handle<String> string, enum encoding enc) {
117 HandleScope scope(node_isolate);
119 size_t length = StringBytes::Size(string, enc);
121 Local<Object> buf = New(length);
122 char* data = Buffer::Data(buf);
123 StringBytes::Write(data, length, string, enc);
125 return scope.Close(buf);
129 Local<Object> New(size_t length) {
130 HandleScope handle_scope(node_isolate);
131 Environment* env = Environment::GetCurrent(node_isolate);
132 Local<Object> obj = Buffer::New(env, length);
133 return handle_scope.Close(obj);
137 // TODO(trevnorris): these have a flaw by needing to call the Buffer inst then
138 // Alloc. continue to look for a better architecture.
139 Local<Object> New(Environment* env, size_t length) {
140 HandleScope scope(node_isolate);
142 assert(length <= kMaxLength);
144 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
145 Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
147 // TODO(trevnorris): done like this to handle HasInstance since only checks
148 // if external array data has been set, but would like to use a better
149 // approach if v8 provided one.
152 data = static_cast<char*>(malloc(length));
154 FatalError("node::Buffer::New(size_t)", "Out Of Memory");
158 smalloc::Alloc(obj, data, length);
160 return scope.Close(obj);
164 Local<Object> New(const char* data, size_t length) {
165 HandleScope handle_scope(node_isolate);
166 Environment* env = Environment::GetCurrent(node_isolate);
167 Local<Object> obj = Buffer::New(env, data, length);
168 return handle_scope.Close(obj);
172 // TODO(trevnorris): for backwards compatibility this is left to copy the data,
173 // but for consistency w/ the other should use data. And a copy version renamed
174 // to something else.
175 Local<Object> New(Environment* env, const char* data, size_t length) {
176 HandleScope scope(node_isolate);
178 assert(length <= kMaxLength);
180 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
181 Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
183 // TODO(trevnorris): done like this to handle HasInstance since only checks
184 // if external array data has been set, but would like to use a better
185 // approach if v8 provided one.
188 new_data = static_cast<char*>(malloc(length));
189 if (new_data == NULL)
190 FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory");
191 memcpy(new_data, data, length);
196 smalloc::Alloc(obj, new_data, length);
198 return scope.Close(obj);
202 Local<Object> New(char* data,
204 smalloc::FreeCallback callback,
206 HandleScope handle_scope(node_isolate);
207 Environment* env = Environment::GetCurrent(node_isolate);
208 Local<Object> obj = Buffer::New(env, data, length, callback, hint);
209 return handle_scope.Close(obj);
213 Local<Object> New(Environment* env,
216 smalloc::FreeCallback callback,
218 HandleScope scope(node_isolate);
220 assert(length <= kMaxLength);
222 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
223 Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
225 smalloc::Alloc(obj, data, length, callback, hint);
227 return scope.Close(obj);
231 Local<Object> Use(char* data, uint32_t length) {
232 HandleScope handle_scope(node_isolate);
233 Environment* env = Environment::GetCurrent(node_isolate);
234 Local<Object> obj = Buffer::Use(env, data, length);
235 return handle_scope.Close(obj);
239 Local<Object> Use(Environment* env, char* data, uint32_t length) {
240 HandleScope scope(node_isolate);
242 assert(length <= kMaxLength);
244 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
245 Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
247 smalloc::Alloc(obj, data, length);
249 return scope.Close(obj);
253 template <encoding encoding>
254 void StringSlice(const FunctionCallbackInfo<Value>& args) {
255 HandleScope scope(node_isolate);
257 ARGS_THIS(args.This())
258 SLICE_START_END(args[0], args[1], obj_length)
260 args.GetReturnValue().Set(
261 StringBytes::Encode(obj_data + start, length, encoding));
265 void BinarySlice(const FunctionCallbackInfo<Value>& args) {
266 StringSlice<BINARY>(args);
270 void AsciiSlice(const FunctionCallbackInfo<Value>& args) {
271 StringSlice<ASCII>(args);
275 void Utf8Slice(const FunctionCallbackInfo<Value>& args) {
276 StringSlice<UTF8>(args);
280 void Ucs2Slice(const FunctionCallbackInfo<Value>& args) {
281 StringSlice<UCS2>(args);
285 void HexSlice(const FunctionCallbackInfo<Value>& args) {
286 StringSlice<HEX>(args);
290 void Base64Slice(const FunctionCallbackInfo<Value>& args) {
291 StringSlice<BASE64>(args);
295 // bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]);
296 void Copy(const FunctionCallbackInfo<Value> &args) {
297 HandleScope scope(node_isolate);
299 Local<Object> target = args[0]->ToObject();
301 if (!HasInstance(target))
302 return ThrowTypeError("first arg should be a Buffer");
304 ARGS_THIS(args.This())
305 size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength();
306 char* target_data = static_cast<char*>(
307 target->GetIndexedPropertiesExternalArrayData());
312 CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &target_start));
313 CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &source_start));
314 CHECK_NOT_OOB(ParseArrayIndex(args[3], obj_length, &source_end));
316 // Copy 0 bytes; we're done
317 if (target_start >= target_length || source_start >= source_end)
318 return args.GetReturnValue().Set(0);
320 if (source_start > obj_length)
321 return ThrowRangeError("out of range index");
323 if (source_end - source_start > target_length - target_start)
324 source_end = source_start + target_length - target_start;
326 uint32_t to_copy = MIN(MIN(source_end - source_start,
327 target_length - target_start),
328 obj_length - source_start);
330 memmove(target_data + target_start, obj_data + source_start, to_copy);
331 args.GetReturnValue().Set(to_copy);
335 // buffer.fill(value[, start][, end]);
336 void Fill(const FunctionCallbackInfo<Value>& args) {
337 HandleScope scope(node_isolate);
339 ARGS_THIS(args.This())
340 SLICE_START_END(args[1], args[2], obj_length)
341 args.GetReturnValue().Set(args.This());
343 if (args[0]->IsNumber()) {
344 int value = args[0]->Uint32Value() & 255;
345 memset(obj_data + start, value, length);
349 String::Utf8Value at(args[0]);
350 size_t at_length = at.length();
352 // optimize single ascii character case
353 if (at_length == 1) {
354 int value = static_cast<int>((*at)[0]);
355 memset(obj_data + start, value, length);
359 size_t in_there = at_length;
360 char* ptr = obj_data + start + at_length;
362 memcpy(obj_data + start, *at, MIN(at_length, length));
364 if (at_length >= length)
367 while (in_there < length - in_there) {
368 memcpy(ptr, obj_data + start, in_there);
373 if (in_there < length) {
374 memcpy(ptr, obj_data + start, length - in_there);
380 template <encoding encoding>
381 void StringWrite(const FunctionCallbackInfo<Value>& args) {
382 HandleScope scope(node_isolate);
384 ARGS_THIS(args.This())
386 if (!args[0]->IsString())
387 return ThrowTypeError("Argument must be a string");
389 Local<String> str = args[0]->ToString();
391 if (encoding == HEX && str->Length() % 2 != 0)
392 return ThrowTypeError("Invalid hex string");
397 CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset));
398 CHECK_NOT_OOB(ParseArrayIndex(args[2], obj_length - offset, &max_length));
400 max_length = MIN(obj_length - offset, max_length);
403 return args.GetReturnValue().Set(0);
405 if (encoding == UCS2)
406 max_length = max_length / 2;
408 if (offset >= obj_length)
409 return ThrowRangeError("Offset is out of bounds");
411 uint32_t written = StringBytes::Write(obj_data + offset,
416 args.GetReturnValue().Set(written);
420 void Base64Write(const FunctionCallbackInfo<Value>& args) {
421 StringWrite<BASE64>(args);
425 void BinaryWrite(const FunctionCallbackInfo<Value>& args) {
426 StringWrite<BINARY>(args);
430 void Utf8Write(const FunctionCallbackInfo<Value>& args) {
431 StringWrite<UTF8>(args);
435 void Ucs2Write(const FunctionCallbackInfo<Value>& args) {
436 StringWrite<UCS2>(args);
440 void HexWrite(const FunctionCallbackInfo<Value>& args) {
441 StringWrite<HEX>(args);
445 void AsciiWrite(const FunctionCallbackInfo<Value>& args) {
446 StringWrite<ASCII>(args);
450 static inline void Swizzle(char* start, unsigned int len) {
451 char* end = start + len - 1;
452 while (start < end) {
460 template <typename T, enum Endianness endianness>
461 void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
462 bool doAssert = !args[1]->BooleanValue();
465 CHECK_NOT_OOB(ParseArrayIndex(args[0], 0, &offset));
468 size_t len = Length(args.This());
469 if (offset + sizeof(T) > len || offset + sizeof(T) < offset)
470 return ThrowRangeError("Trying to read beyond buffer length");
475 char bytes[sizeof(T)];
479 const void* data = args.This()->GetIndexedPropertiesExternalArrayData();
480 const char* ptr = static_cast<const char*>(data) + offset;
481 memcpy(na.bytes, ptr, sizeof(na.bytes));
482 if (endianness != GetEndianness())
483 Swizzle(na.bytes, sizeof(na.bytes));
485 args.GetReturnValue().Set(na.val);
489 void ReadFloatLE(const FunctionCallbackInfo<Value>& args) {
490 ReadFloatGeneric<float, kLittleEndian>(args);
494 void ReadFloatBE(const FunctionCallbackInfo<Value>& args) {
495 ReadFloatGeneric<float, kBigEndian>(args);
499 void ReadDoubleLE(const FunctionCallbackInfo<Value>& args) {
500 ReadFloatGeneric<double, kLittleEndian>(args);
504 void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
505 ReadFloatGeneric<double, kBigEndian>(args);
509 template <typename T, enum Endianness endianness>
510 uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
511 bool doAssert = !args[2]->BooleanValue();
513 T val = static_cast<T>(args[0]->NumberValue());
516 if (!ParseArrayIndex(args[1], 0, &offset)) {
517 ThrowRangeError("out of range index");
522 size_t len = Length(args.This());
523 if (offset + sizeof(T) > len || offset + sizeof(T) < offset) {
524 ThrowRangeError("Trying to write beyond buffer length");
531 char bytes[sizeof(T)];
534 union NoAlias na = { val };
535 void* data = args.This()->GetIndexedPropertiesExternalArrayData();
536 char* ptr = static_cast<char*>(data) + offset;
537 if (endianness != GetEndianness())
538 Swizzle(na.bytes, sizeof(na.bytes));
539 memcpy(ptr, na.bytes, sizeof(na.bytes));
540 return offset + sizeof(na.bytes);
544 void WriteFloatLE(const FunctionCallbackInfo<Value>& args) {
545 args.GetReturnValue().Set(WriteFloatGeneric<float, kLittleEndian>(args));
549 void WriteFloatBE(const FunctionCallbackInfo<Value>& args) {
550 args.GetReturnValue().Set(WriteFloatGeneric<float, kBigEndian>(args));
554 void WriteDoubleLE(const FunctionCallbackInfo<Value>& args) {
555 args.GetReturnValue().Set(WriteFloatGeneric<double, kLittleEndian>(args));
559 void WriteDoubleBE(const FunctionCallbackInfo<Value>& args) {
560 args.GetReturnValue().Set(WriteFloatGeneric<double, kBigEndian>(args));
564 void ToArrayBuffer(const FunctionCallbackInfo<Value>& args) {
565 HandleScope scope(node_isolate);
567 ARGS_THIS(args.This());
568 void* adata = malloc(obj_length);
571 FatalError("node::Buffer::ToArrayBuffer("
572 "const FunctionCallbackInfo<v8::Value>&)",
576 memcpy(adata, obj_data, obj_length);
578 Local<ArrayBuffer> abuf = ArrayBuffer::New(adata, obj_length);
579 args.GetReturnValue().Set(abuf);
583 void ByteLength(const FunctionCallbackInfo<Value> &args) {
584 HandleScope scope(node_isolate);
586 if (!args[0]->IsString())
587 return ThrowTypeError("Argument must be a string");
589 Local<String> s = args[0]->ToString();
590 enum encoding e = ParseEncoding(args[1], UTF8);
592 uint32_t size = StringBytes::Size(s, e);
593 args.GetReturnValue().Set(size);
597 // pass Buffer object to load prototype methods
598 void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
599 HandleScope handle_scope(args.GetIsolate());
600 Environment* env = Environment::GetCurrent(args.GetIsolate());
602 assert(args[0]->IsFunction());
604 Local<Function> bv = args[0].As<Function>();
605 env->set_buffer_constructor_function(bv);
606 Local<Value> proto_v =
607 bv->Get(FIXED_ONE_BYTE_STRING(node_isolate, "prototype"));
609 assert(proto_v->IsObject());
611 Local<Object> proto = proto_v.As<Object>();
613 NODE_SET_METHOD(proto, "asciiSlice", AsciiSlice);
614 NODE_SET_METHOD(proto, "base64Slice", Base64Slice);
615 NODE_SET_METHOD(proto, "binarySlice", BinarySlice);
616 NODE_SET_METHOD(proto, "hexSlice", HexSlice);
617 NODE_SET_METHOD(proto, "ucs2Slice", Ucs2Slice);
618 NODE_SET_METHOD(proto, "utf8Slice", Utf8Slice);
620 NODE_SET_METHOD(proto, "asciiWrite", AsciiWrite);
621 NODE_SET_METHOD(proto, "base64Write", Base64Write);
622 NODE_SET_METHOD(proto, "binaryWrite", BinaryWrite);
623 NODE_SET_METHOD(proto, "hexWrite", HexWrite);
624 NODE_SET_METHOD(proto, "ucs2Write", Ucs2Write);
625 NODE_SET_METHOD(proto, "utf8Write", Utf8Write);
627 NODE_SET_METHOD(proto, "readDoubleBE", ReadDoubleBE);
628 NODE_SET_METHOD(proto, "readDoubleLE", ReadDoubleLE);
629 NODE_SET_METHOD(proto, "readFloatBE", ReadFloatBE);
630 NODE_SET_METHOD(proto, "readFloatLE", ReadFloatLE);
632 NODE_SET_METHOD(proto, "writeDoubleBE", WriteDoubleBE);
633 NODE_SET_METHOD(proto, "writeDoubleLE", WriteDoubleLE);
634 NODE_SET_METHOD(proto, "writeFloatBE", WriteFloatBE);
635 NODE_SET_METHOD(proto, "writeFloatLE", WriteFloatLE);
637 NODE_SET_METHOD(proto, "toArrayBuffer", ToArrayBuffer);
639 NODE_SET_METHOD(proto, "copy", Copy);
640 NODE_SET_METHOD(proto, "fill", Fill);
642 // for backwards compatibility
643 proto->Set(FIXED_ONE_BYTE_STRING(node_isolate, "offset"),
644 Uint32::New(0, node_isolate),
647 assert(args[1]->IsObject());
649 Local<Object> internal = args[1].As<Object>();
651 internal->Set(FIXED_ONE_BYTE_STRING(node_isolate, "byteLength"),
652 FunctionTemplate::New(ByteLength)->GetFunction());
656 void Initialize(Handle<Object> target,
657 Handle<Value> unused,
658 Handle<Context> context) {
659 Environment* env = Environment::GetCurrent(context);
660 target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "setupBufferJS"),
661 FunctionTemplate::New(SetupBufferJS)->GetFunction());
665 } // namespace Buffer
668 NODE_MODULE_CONTEXT_AWARE(node_buffer, node::Buffer::Initialize)