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"
26 #include "string_bytes.h"
29 #include "v8-profiler.h"
35 #define MIN(a, b) ((a) < (b) ? (a) : (b))
37 #define CHECK_NOT_OOB(r) \
38 do { if (!(r)) return ThrowRangeError("out of range index"); } while (0)
40 #define ARGS_THIS(argT) \
41 Local<Object> obj = argT; \
42 size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength(); \
43 char* obj_data = static_cast<char*>( \
44 obj->GetIndexedPropertiesExternalArrayData());
46 #define SLICE_START_END(start_arg, end_arg, end_max) \
49 CHECK_NOT_OOB(ParseArrayIndex(start_arg, 0, &start)); \
50 CHECK_NOT_OOB(ParseArrayIndex(end_arg, end_max, &end)); \
51 if (end < start) end = start; \
52 CHECK_NOT_OOB(end <= end_max); \
53 size_t length = end - start;
59 using v8::FunctionCallbackInfo;
60 using v8::FunctionTemplate;
62 using v8::HandleScope;
71 static Persistent<Function> p_buffer_fn;
74 bool HasInstance(Handle<Value> val) {
75 return val->IsObject() && HasInstance(val.As<Object>());
79 bool HasInstance(Handle<Object> obj) {
80 if (!obj->HasIndexedPropertiesInExternalArrayData())
82 v8::ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
83 return type == v8::kExternalUnsignedByteArray;
87 char* Data(Handle<Value> val) {
88 assert(val->IsObject());
89 // Use a fully qualified name here to work around a bug in gcc 4.2.
90 // It mistakes an unadorned call to Data() for the v8::String::Data type.
91 return node::Buffer::Data(val.As<Object>());
95 char* Data(Handle<Object> obj) {
96 assert(obj->HasIndexedPropertiesInExternalArrayData());
97 return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
101 size_t Length(Handle<Value> val) {
102 assert(val->IsObject());
103 return Length(val.As<Object>());
107 size_t Length(Handle<Object> obj) {
108 assert(obj->HasIndexedPropertiesInExternalArrayData());
109 return obj->GetIndexedPropertiesExternalArrayDataLength();
113 Local<Object> New(Handle<String> string, enum encoding enc) {
114 HandleScope scope(node_isolate);
116 size_t length = StringBytes::Size(string, enc);
118 Local<Object> buf = New(length);
119 char* data = Buffer::Data(buf);
120 StringBytes::Write(data, length, string, enc);
122 return scope.Close(buf);
126 Local<Object> New(size_t length) {
127 HandleScope scope(node_isolate);
129 assert(length <= kMaxLength);
131 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
132 Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg);
134 // TODO(trevnorris): done like this to handle HasInstance since only checks
135 // if external array data has been set, but would like to use a better
136 // approach if v8 provided one.
139 data = static_cast<char*>(malloc(length));
141 FatalError("node::Buffer::New(size_t)", "Out Of Memory");
145 smalloc::Alloc(obj, data, length);
147 return scope.Close(obj);
151 // TODO(trevnorris): for backwards compatibility this is left to copy the data,
152 // but for consistency w/ the other should use data. And a copy version renamed
153 // to something else.
154 Local<Object> New(const char* data, size_t length) {
155 HandleScope scope(node_isolate);
157 assert(length <= kMaxLength);
159 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
160 Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg);
162 // TODO(trevnorris): done like this to handle HasInstance since only checks
163 // if external array data has been set, but would like to use a better
164 // approach if v8 provided one.
167 new_data = static_cast<char*>(malloc(length));
168 if (new_data == NULL)
169 FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory");
170 memcpy(new_data, data, length);
175 smalloc::Alloc(obj, new_data, length);
177 return scope.Close(obj);
181 Local<Object> New(char* data,
183 smalloc::FreeCallback callback,
185 HandleScope scope(node_isolate);
187 assert(length <= kMaxLength);
189 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
190 Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg);
192 smalloc::Alloc(obj, data, length, callback, hint);
194 return scope.Close(obj);
198 Local<Object> Use(char* data, uint32_t length) {
199 HandleScope scope(node_isolate);
201 assert(length <= kMaxLength);
203 Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
204 Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg);
206 smalloc::Alloc(obj, data, length);
208 return scope.Close(obj);
212 template <encoding encoding>
213 void StringSlice(const FunctionCallbackInfo<Value>& args) {
214 HandleScope scope(node_isolate);
216 ARGS_THIS(args.This())
217 SLICE_START_END(args[0], args[1], obj_length)
219 args.GetReturnValue().Set(
220 StringBytes::Encode(obj_data + start, length, encoding));
224 void BinarySlice(const FunctionCallbackInfo<Value>& args) {
225 StringSlice<BINARY>(args);
229 void AsciiSlice(const FunctionCallbackInfo<Value>& args) {
230 StringSlice<ASCII>(args);
234 void Utf8Slice(const FunctionCallbackInfo<Value>& args) {
235 StringSlice<UTF8>(args);
239 void Ucs2Slice(const FunctionCallbackInfo<Value>& args) {
240 StringSlice<UCS2>(args);
244 void HexSlice(const FunctionCallbackInfo<Value>& args) {
245 StringSlice<HEX>(args);
249 void Base64Slice(const FunctionCallbackInfo<Value>& args) {
250 StringSlice<BASE64>(args);
254 // bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]);
255 void Copy(const FunctionCallbackInfo<Value> &args) {
256 HandleScope scope(node_isolate);
258 Local<Object> target = args[0]->ToObject();
260 if (!HasInstance(target))
261 return ThrowTypeError("first arg should be a Buffer");
263 ARGS_THIS(args.This())
264 size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength();
265 char* target_data = static_cast<char*>(
266 target->GetIndexedPropertiesExternalArrayData());
271 CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &target_start));
272 CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &source_start));
273 CHECK_NOT_OOB(ParseArrayIndex(args[3], obj_length, &source_end));
275 // Copy 0 bytes; we're done
276 if (target_start >= target_length || source_start >= source_end)
277 return args.GetReturnValue().Set(0);
279 if (source_start > obj_length)
280 return ThrowRangeError("out of range index");
282 if (source_end - source_start > target_length - target_start)
283 source_end = source_start + target_length - target_start;
285 uint32_t to_copy = MIN(MIN(source_end - source_start,
286 target_length - target_start),
287 obj_length - source_start);
289 memmove(target_data + target_start, obj_data + source_start, to_copy);
290 args.GetReturnValue().Set(to_copy);
294 // buffer.fill(value[, start][, end]);
295 void Fill(const FunctionCallbackInfo<Value>& args) {
296 HandleScope scope(node_isolate);
298 ARGS_THIS(args.This())
299 SLICE_START_END(args[1], args[2], obj_length)
300 args.GetReturnValue().Set(args.This());
302 if (args[0]->IsNumber()) {
303 int value = args[0]->Uint32Value() & 255;
304 memset(obj_data + start, value, length);
308 String::Utf8Value at(args[0]);
309 size_t at_length = at.length();
311 // optimize single ascii character case
312 if (at_length == 1) {
313 int value = static_cast<int>((*at)[0]);
314 memset(obj_data + start, value, length);
318 size_t in_there = at_length;
319 char* ptr = obj_data + start + at_length;
321 memcpy(obj_data + start, *at, MIN(at_length, length));
323 if (at_length >= length)
326 while (in_there < length - in_there) {
327 memcpy(ptr, obj_data + start, in_there);
332 if (in_there < length) {
333 memcpy(ptr, obj_data + start, length - in_there);
339 template <encoding encoding>
340 void StringWrite(const FunctionCallbackInfo<Value>& args) {
341 HandleScope scope(node_isolate);
343 ARGS_THIS(args.This())
345 if (!args[0]->IsString())
346 return ThrowTypeError("Argument must be a string");
348 Local<String> str = args[0]->ToString();
350 if (encoding == HEX && str->Length() % 2 != 0)
351 return ThrowTypeError("Invalid hex string");
356 CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset));
357 CHECK_NOT_OOB(ParseArrayIndex(args[2], obj_length - offset, &max_length));
359 max_length = MIN(obj_length - offset, max_length);
362 return args.GetReturnValue().Set(0);
364 if (encoding == UCS2)
365 max_length = max_length / 2;
367 if (offset >= obj_length)
368 return ThrowRangeError("Offset is out of bounds");
370 uint32_t written = StringBytes::Write(obj_data + offset,
375 args.GetReturnValue().Set(written);
379 void Base64Write(const FunctionCallbackInfo<Value>& args) {
380 StringWrite<BASE64>(args);
384 void BinaryWrite(const FunctionCallbackInfo<Value>& args) {
385 StringWrite<BINARY>(args);
389 void Utf8Write(const FunctionCallbackInfo<Value>& args) {
390 StringWrite<UTF8>(args);
394 void Ucs2Write(const FunctionCallbackInfo<Value>& args) {
395 StringWrite<UCS2>(args);
399 void HexWrite(const FunctionCallbackInfo<Value>& args) {
400 StringWrite<HEX>(args);
404 void AsciiWrite(const FunctionCallbackInfo<Value>& args) {
405 StringWrite<ASCII>(args);
409 static inline void Swizzle(char* start, unsigned int len) {
410 char* end = start + len - 1;
411 while (start < end) {
419 template <typename T, enum Endianness endianness>
420 void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
421 bool doAssert = !args[1]->BooleanValue();
424 CHECK_NOT_OOB(ParseArrayIndex(args[0], 0, &offset));
427 size_t len = Length(args.This());
428 if (offset + sizeof(T) > len || offset + sizeof(T) < offset)
429 return ThrowRangeError("Trying to read beyond buffer length");
434 char bytes[sizeof(T)];
438 const void* data = args.This()->GetIndexedPropertiesExternalArrayData();
439 const char* ptr = static_cast<const char*>(data) + offset;
440 memcpy(na.bytes, ptr, sizeof(na.bytes));
441 if (endianness != GetEndianness()) Swizzle(na.bytes, sizeof(na.bytes));
443 args.GetReturnValue().Set(na.val);
447 void ReadFloatLE(const FunctionCallbackInfo<Value>& args) {
448 ReadFloatGeneric<float, kLittleEndian>(args);
452 void ReadFloatBE(const FunctionCallbackInfo<Value>& args) {
453 ReadFloatGeneric<float, kBigEndian>(args);
457 void ReadDoubleLE(const FunctionCallbackInfo<Value>& args) {
458 ReadFloatGeneric<double, kLittleEndian>(args);
462 void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
463 ReadFloatGeneric<double, kBigEndian>(args);
467 template <typename T, enum Endianness endianness>
468 uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
469 bool doAssert = !args[2]->BooleanValue();
471 T val = static_cast<T>(args[0]->NumberValue());
474 if (!ParseArrayIndex(args[1], 0, &offset)) {
475 ThrowRangeError("out of range index");
480 size_t len = Length(args.This());
481 if (offset + sizeof(T) > len || offset + sizeof(T) < offset) {
482 ThrowRangeError("Trying to write beyond buffer length");
489 char bytes[sizeof(T)];
492 union NoAlias na = { val };
493 void* data = args.This()->GetIndexedPropertiesExternalArrayData();
494 char* ptr = static_cast<char*>(data) + offset;
495 if (endianness != GetEndianness()) Swizzle(na.bytes, sizeof(na.bytes));
496 memcpy(ptr, na.bytes, sizeof(na.bytes));
497 return offset + sizeof(na.bytes);
501 void WriteFloatLE(const FunctionCallbackInfo<Value>& args) {
502 args.GetReturnValue().Set(WriteFloatGeneric<float, kLittleEndian>(args));
506 void WriteFloatBE(const FunctionCallbackInfo<Value>& args) {
507 args.GetReturnValue().Set(WriteFloatGeneric<float, kBigEndian>(args));
511 void WriteDoubleLE(const FunctionCallbackInfo<Value>& args) {
512 args.GetReturnValue().Set(WriteFloatGeneric<double, kLittleEndian>(args));
516 void WriteDoubleBE(const FunctionCallbackInfo<Value>& args) {
517 args.GetReturnValue().Set(WriteFloatGeneric<double, kBigEndian>(args));
521 void ByteLength(const FunctionCallbackInfo<Value> &args) {
522 HandleScope scope(node_isolate);
524 if (!args[0]->IsString())
525 return ThrowTypeError("Argument must be a string");
527 Local<String> s = args[0]->ToString();
528 enum encoding e = ParseEncoding(args[1], UTF8);
530 uint32_t size = StringBytes::Size(s, e);
531 args.GetReturnValue().Set(size);
535 // pass Buffer object to load prototype methods
536 void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
537 HandleScope scope(node_isolate);
539 assert(args[0]->IsFunction());
541 Local<Function> bv = args[0].As<Function>();
542 p_buffer_fn.Reset(node_isolate, bv);
543 Local<Value> proto_v =
544 bv->Get(FIXED_ONE_BYTE_STRING(node_isolate, "prototype"));
546 assert(proto_v->IsObject());
548 Local<Object> proto = proto_v.As<Object>();
550 NODE_SET_METHOD(proto, "asciiSlice", AsciiSlice);
551 NODE_SET_METHOD(proto, "base64Slice", Base64Slice);
552 NODE_SET_METHOD(proto, "binarySlice", BinarySlice);
553 NODE_SET_METHOD(proto, "hexSlice", HexSlice);
554 NODE_SET_METHOD(proto, "ucs2Slice", Ucs2Slice);
555 NODE_SET_METHOD(proto, "utf8Slice", Utf8Slice);
557 NODE_SET_METHOD(proto, "asciiWrite", AsciiWrite);
558 NODE_SET_METHOD(proto, "base64Write", Base64Write);
559 NODE_SET_METHOD(proto, "binaryWrite", BinaryWrite);
560 NODE_SET_METHOD(proto, "hexWrite", HexWrite);
561 NODE_SET_METHOD(proto, "ucs2Write", Ucs2Write);
562 NODE_SET_METHOD(proto, "utf8Write", Utf8Write);
564 NODE_SET_METHOD(proto, "readDoubleBE", ReadDoubleBE);
565 NODE_SET_METHOD(proto, "readDoubleLE", ReadDoubleLE);
566 NODE_SET_METHOD(proto, "readFloatBE", ReadFloatBE);
567 NODE_SET_METHOD(proto, "readFloatLE", ReadFloatLE);
569 NODE_SET_METHOD(proto, "writeDoubleBE", WriteDoubleBE);
570 NODE_SET_METHOD(proto, "writeDoubleLE", WriteDoubleLE);
571 NODE_SET_METHOD(proto, "writeFloatBE", WriteFloatBE);
572 NODE_SET_METHOD(proto, "writeFloatLE", WriteFloatLE);
574 NODE_SET_METHOD(proto, "copy", Copy);
575 NODE_SET_METHOD(proto, "fill", Fill);
577 // for backwards compatibility
578 proto->Set(FIXED_ONE_BYTE_STRING(node_isolate, "offset"),
579 Uint32::New(0, node_isolate),
582 assert(args[1]->IsObject());
584 Local<Object> internal = args[1].As<Object>();
586 internal->Set(FIXED_ONE_BYTE_STRING(node_isolate, "byteLength"),
587 FunctionTemplate::New(ByteLength)->GetFunction());
591 void Initialize(Handle<Object> target) {
592 HandleScope scope(node_isolate);
594 target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "setupBufferJS"),
595 FunctionTemplate::New(SetupBufferJS)->GetFunction());
599 } // namespace Buffer
602 NODE_MODULE(node_buffer, node::Buffer::Initialize)