e4e6d738fa74c71ef7d76ad4391d06755f58ff26
[platform/upstream/nodejs.git] / src / node_buffer.cc
1 #include "node.h"
2 #include "node_buffer.h"
3
4 #include "env.h"
5 #include "env-inl.h"
6 #include "smalloc.h"
7 #include "string_bytes.h"
8 #include "v8-profiler.h"
9 #include "v8.h"
10
11 #include <string.h>
12 #include <limits.h>
13
14 #define MIN(a, b) ((a) < (b) ? (a) : (b))
15
16 #define CHECK_NOT_OOB(r)                                                    \
17   do {                                                                      \
18     if (!(r)) return env->ThrowRangeError("out of range index");            \
19   } while (0)
20
21 #define ARGS_THIS(argT)                                                     \
22   Local<Object> obj = argT;                                                 \
23   size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength();   \
24   char* obj_data = static_cast<char*>(                                      \
25     obj->GetIndexedPropertiesExternalArrayData());                          \
26   if (obj_length > 0)                                                       \
27     CHECK_NE(obj_data, nullptr);
28
29 #define SLICE_START_END(start_arg, end_arg, end_max)                        \
30   size_t start;                                                             \
31   size_t end;                                                               \
32   CHECK_NOT_OOB(ParseArrayIndex(start_arg, 0, &start));                     \
33   CHECK_NOT_OOB(ParseArrayIndex(end_arg, end_max, &end));                   \
34   if (end < start) end = start;                                             \
35   CHECK_NOT_OOB(end <= end_max);                                            \
36   size_t length = end - start;
37
38 namespace node {
39 namespace Buffer {
40
41 using v8::Context;
42 using v8::EscapableHandleScope;
43 using v8::Function;
44 using v8::FunctionCallbackInfo;
45 using v8::FunctionTemplate;
46 using v8::Handle;
47 using v8::HandleScope;
48 using v8::Isolate;
49 using v8::Local;
50 using v8::Number;
51 using v8::Object;
52 using v8::String;
53 using v8::Uint32;
54 using v8::Value;
55
56
57 bool HasInstance(Handle<Value> val) {
58   return val->IsObject() && HasInstance(val.As<Object>());
59 }
60
61
62 bool HasInstance(Handle<Object> obj) {
63   if (!obj->HasIndexedPropertiesInExternalArrayData())
64     return false;
65   v8::ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
66   return type == v8::kExternalUint8Array;
67 }
68
69
70 char* Data(Handle<Value> val) {
71   CHECK(val->IsObject());
72   // Use a fully qualified name here to work around a bug in gcc 4.2.
73   // It mistakes an unadorned call to Data() for the v8::String::Data type.
74   return node::Buffer::Data(val.As<Object>());
75 }
76
77
78 char* Data(Handle<Object> obj) {
79   CHECK(obj->HasIndexedPropertiesInExternalArrayData());
80   return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
81 }
82
83
84 size_t Length(Handle<Value> val) {
85   CHECK(val->IsObject());
86   return Length(val.As<Object>());
87 }
88
89
90 size_t Length(Handle<Object> obj) {
91   CHECK(obj->HasIndexedPropertiesInExternalArrayData());
92   return obj->GetIndexedPropertiesExternalArrayDataLength();
93 }
94
95
96 Local<Object> New(Isolate* isolate, Handle<String> string, enum encoding enc) {
97   EscapableHandleScope scope(isolate);
98
99   size_t length = StringBytes::Size(isolate, string, enc);
100
101   Local<Object> buf = New(length);
102   char* data = Buffer::Data(buf);
103   StringBytes::Write(isolate, data, length, string, enc);
104
105   return scope.Escape(buf);
106 }
107
108
109 Local<Object> New(Isolate* isolate, size_t length) {
110   EscapableHandleScope handle_scope(isolate);
111   Local<Object> obj = Buffer::New(Environment::GetCurrent(isolate), length);
112   return handle_scope.Escape(obj);
113 }
114
115
116 // TODO(trevnorris): these have a flaw by needing to call the Buffer inst then
117 // Alloc. continue to look for a better architecture.
118 Local<Object> New(Environment* env, size_t length) {
119   EscapableHandleScope scope(env->isolate());
120
121   CHECK_LE(length, kMaxLength);
122
123   Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length);
124   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
125
126   // TODO(trevnorris): done like this to handle HasInstance since only checks
127   // if external array data has been set, but would like to use a better
128   // approach if v8 provided one.
129   char* data;
130   if (length > 0) {
131     data = static_cast<char*>(malloc(length));
132     if (data == nullptr)
133       FatalError("node::Buffer::New(size_t)", "Out Of Memory");
134   } else {
135     data = nullptr;
136   }
137   smalloc::Alloc(env, obj, data, length);
138
139   return scope.Escape(obj);
140 }
141
142
143 Local<Object> New(Isolate* isolate, const char* data, size_t length) {
144   Environment* env = Environment::GetCurrent(isolate);
145   EscapableHandleScope handle_scope(env->isolate());
146   Local<Object> obj = Buffer::New(env, data, length);
147   return handle_scope.Escape(obj);
148 }
149
150
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(Environment* env, const char* data, size_t length) {
155   EscapableHandleScope scope(env->isolate());
156
157   CHECK_LE(length, kMaxLength);
158
159   Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length);
160   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
161
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.
165   char* new_data;
166   if (length > 0) {
167     new_data = static_cast<char*>(malloc(length));
168     if (new_data == nullptr)
169       FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory");
170     memcpy(new_data, data, length);
171   } else {
172     new_data = nullptr;
173   }
174
175   smalloc::Alloc(env, obj, new_data, length);
176
177   return scope.Escape(obj);
178 }
179
180
181 Local<Object> New(Isolate* isolate,
182                   char* data,
183                   size_t length,
184                   smalloc::FreeCallback callback,
185                   void* hint) {
186   Environment* env = Environment::GetCurrent(isolate);
187   EscapableHandleScope handle_scope(env->isolate());
188   Local<Object> obj = Buffer::New(env, data, length, callback, hint);
189   return handle_scope.Escape(obj);
190 }
191
192
193 Local<Object> New(Environment* env,
194                   char* data,
195                   size_t length,
196                   smalloc::FreeCallback callback,
197                   void* hint) {
198   EscapableHandleScope scope(env->isolate());
199
200   CHECK_LE(length, kMaxLength);
201
202   Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length);
203   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
204
205   smalloc::Alloc(env, obj, data, length, callback, hint);
206
207   return scope.Escape(obj);
208 }
209
210
211 Local<Object> Use(Isolate* isolate, char* data, uint32_t length) {
212   Environment* env = Environment::GetCurrent(isolate);
213   EscapableHandleScope handle_scope(env->isolate());
214   Local<Object> obj = Buffer::Use(env, data, length);
215   return handle_scope.Escape(obj);
216 }
217
218
219 Local<Object> Use(Environment* env, char* data, uint32_t length) {
220   EscapableHandleScope scope(env->isolate());
221
222   CHECK_LE(length, kMaxLength);
223
224   Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length);
225   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
226
227   smalloc::Alloc(env, obj, data, length);
228
229   return scope.Escape(obj);
230 }
231
232
233 template <encoding encoding>
234 void StringSlice(const FunctionCallbackInfo<Value>& args) {
235   Environment* env = Environment::GetCurrent(args);
236
237   ARGS_THIS(args.This())
238   SLICE_START_END(args[0], args[1], obj_length)
239
240   args.GetReturnValue().Set(
241       StringBytes::Encode(env->isolate(), obj_data + start, length, encoding));
242 }
243
244
245 template <>
246 void StringSlice<UCS2>(const FunctionCallbackInfo<Value>& args) {
247   Environment* env = Environment::GetCurrent(args);
248
249   ARGS_THIS(args.This())
250   SLICE_START_END(args[0], args[1], obj_length)
251   length /= 2;
252
253   const char* data = obj_data + start;
254   const uint16_t* buf;
255   bool release = false;
256
257   // Node's "ucs2" encoding expects LE character data inside a Buffer, so we
258   // need to reorder on BE platforms.  See http://nodejs.org/api/buffer.html
259   // regarding Node's "ucs2" encoding specification.
260   const bool aligned = (reinterpret_cast<uintptr_t>(data) % sizeof(*buf) == 0);
261   if (IsLittleEndian() && aligned) {
262     buf = reinterpret_cast<const uint16_t*>(data);
263   } else {
264     // Make a copy to avoid unaligned accesses in v8::String::NewFromTwoByte().
265     uint16_t* copy = new uint16_t[length];
266     for (size_t i = 0, k = 0; i < length; i += 1, k += 2) {
267       // Assumes that the input is little endian.
268       const uint8_t lo = static_cast<uint8_t>(data[k + 0]);
269       const uint8_t hi = static_cast<uint8_t>(data[k + 1]);
270       copy[i] = lo | hi << 8;
271     }
272     buf = copy;
273     release = true;
274   }
275
276   args.GetReturnValue().Set(StringBytes::Encode(env->isolate(), buf, length));
277
278   if (release)
279     delete[] buf;
280 }
281
282
283 void BinarySlice(const FunctionCallbackInfo<Value>& args) {
284   StringSlice<BINARY>(args);
285 }
286
287
288 void AsciiSlice(const FunctionCallbackInfo<Value>& args) {
289   StringSlice<ASCII>(args);
290 }
291
292
293 void Utf8Slice(const FunctionCallbackInfo<Value>& args) {
294   StringSlice<UTF8>(args);
295 }
296
297
298 void Ucs2Slice(const FunctionCallbackInfo<Value>& args) {
299   StringSlice<UCS2>(args);
300 }
301
302
303 void HexSlice(const FunctionCallbackInfo<Value>& args) {
304   StringSlice<HEX>(args);
305 }
306
307
308 void Base64Slice(const FunctionCallbackInfo<Value>& args) {
309   StringSlice<BASE64>(args);
310 }
311
312
313 // bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]);
314 void Copy(const FunctionCallbackInfo<Value> &args) {
315   Environment* env = Environment::GetCurrent(args);
316
317   Local<Object> target = args[0]->ToObject(env->isolate());
318
319   if (!HasInstance(target))
320     return env->ThrowTypeError("first arg should be a Buffer");
321
322   ARGS_THIS(args.This())
323   size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength();
324   char* target_data = static_cast<char*>(
325       target->GetIndexedPropertiesExternalArrayData());
326   size_t target_start;
327   size_t source_start;
328   size_t source_end;
329
330   CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &target_start));
331   CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &source_start));
332   CHECK_NOT_OOB(ParseArrayIndex(args[3], obj_length, &source_end));
333
334   // Copy 0 bytes; we're done
335   if (target_start >= target_length || source_start >= source_end)
336     return args.GetReturnValue().Set(0);
337
338   if (source_start > obj_length)
339     return env->ThrowRangeError("out of range index");
340
341   if (source_end - source_start > target_length - target_start)
342     source_end = source_start + target_length - target_start;
343
344   uint32_t to_copy = MIN(MIN(source_end - source_start,
345                              target_length - target_start),
346                              obj_length - source_start);
347
348   memmove(target_data + target_start, obj_data + source_start, to_copy);
349   args.GetReturnValue().Set(to_copy);
350 }
351
352
353 void Fill(const FunctionCallbackInfo<Value>& args) {
354   ARGS_THIS(args[0].As<Object>())
355
356   size_t start = args[2]->Uint32Value();
357   size_t end = args[3]->Uint32Value();
358   size_t length = end - start;
359   CHECK(length + start <= obj_length);
360
361   if (args[1]->IsNumber()) {
362     int value = args[1]->Uint32Value() & 255;
363     memset(obj_data + start, value, length);
364     return;
365   }
366
367   node::Utf8Value str(args.GetIsolate(), args[1]);
368   size_t str_length = str.length();
369   size_t in_there = str_length;
370   char* ptr = obj_data + start + str_length;
371
372   if (str_length == 0)
373     return;
374
375   memcpy(obj_data + start, *str, MIN(str_length, length));
376
377   if (str_length >= length)
378     return;
379
380   while (in_there < length - in_there) {
381     memcpy(ptr, obj_data + start, in_there);
382     ptr += in_there;
383     in_there *= 2;
384   }
385
386   if (in_there < length) {
387     memcpy(ptr, obj_data + start, length - in_there);
388     in_there = length;
389   }
390 }
391
392
393 template <encoding encoding>
394 void StringWrite(const FunctionCallbackInfo<Value>& args) {
395   Environment* env = Environment::GetCurrent(args);
396
397   ARGS_THIS(args.This())
398
399   if (!args[0]->IsString())
400     return env->ThrowTypeError("Argument must be a string");
401
402   Local<String> str = args[0]->ToString(env->isolate());
403
404   if (encoding == HEX && str->Length() % 2 != 0)
405     return env->ThrowTypeError("Invalid hex string");
406
407   size_t offset;
408   size_t max_length;
409
410   CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset));
411   CHECK_NOT_OOB(ParseArrayIndex(args[2], obj_length - offset, &max_length));
412
413   max_length = MIN(obj_length - offset, max_length);
414
415   if (max_length == 0)
416     return args.GetReturnValue().Set(0);
417
418   if (encoding == UCS2)
419     max_length = max_length / 2;
420
421   if (offset >= obj_length)
422     return env->ThrowRangeError("Offset is out of bounds");
423
424   uint32_t written = StringBytes::Write(env->isolate(),
425                                         obj_data + offset,
426                                         max_length,
427                                         str,
428                                         encoding,
429                                         nullptr);
430   args.GetReturnValue().Set(written);
431 }
432
433
434 void Base64Write(const FunctionCallbackInfo<Value>& args) {
435   StringWrite<BASE64>(args);
436 }
437
438
439 void BinaryWrite(const FunctionCallbackInfo<Value>& args) {
440   StringWrite<BINARY>(args);
441 }
442
443
444 void Utf8Write(const FunctionCallbackInfo<Value>& args) {
445   StringWrite<UTF8>(args);
446 }
447
448
449 void Ucs2Write(const FunctionCallbackInfo<Value>& args) {
450   StringWrite<UCS2>(args);
451 }
452
453
454 void HexWrite(const FunctionCallbackInfo<Value>& args) {
455   StringWrite<HEX>(args);
456 }
457
458
459 void AsciiWrite(const FunctionCallbackInfo<Value>& args) {
460   StringWrite<ASCII>(args);
461 }
462
463
464 static inline void Swizzle(char* start, unsigned int len) {
465   char* end = start + len - 1;
466   while (start < end) {
467     char tmp = *start;
468     *start++ = *end;
469     *end-- = tmp;
470   }
471 }
472
473
474 template <typename T, enum Endianness endianness>
475 void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
476   ARGS_THIS(args[0].As<Object>());
477
478   uint32_t offset = args[1]->Uint32Value();
479   CHECK_LE(offset + sizeof(T), obj_length);
480
481   union NoAlias {
482     T val;
483     char bytes[sizeof(T)];
484   };
485
486   union NoAlias na;
487   const char* ptr = static_cast<const char*>(obj_data) + offset;
488   memcpy(na.bytes, ptr, sizeof(na.bytes));
489   if (endianness != GetEndianness())
490     Swizzle(na.bytes, sizeof(na.bytes));
491
492   args.GetReturnValue().Set(na.val);
493 }
494
495
496 void ReadFloatLE(const FunctionCallbackInfo<Value>& args) {
497   ReadFloatGeneric<float, kLittleEndian>(args);
498 }
499
500
501 void ReadFloatBE(const FunctionCallbackInfo<Value>& args) {
502   ReadFloatGeneric<float, kBigEndian>(args);
503 }
504
505
506 void ReadDoubleLE(const FunctionCallbackInfo<Value>& args) {
507   ReadFloatGeneric<double, kLittleEndian>(args);
508 }
509
510
511 void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
512   ReadFloatGeneric<double, kBigEndian>(args);
513 }
514
515
516 template <typename T, enum Endianness endianness>
517 uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
518   ARGS_THIS(args[0].As<Object>())
519
520   T val = args[1]->NumberValue();
521   uint32_t offset = args[2]->Uint32Value();
522   CHECK_LE(offset + sizeof(T), obj_length);
523
524   union NoAlias {
525     T val;
526     char bytes[sizeof(T)];
527   };
528
529   union NoAlias na = { val };
530   char* ptr = static_cast<char*>(obj_data) + offset;
531   if (endianness != GetEndianness())
532     Swizzle(na.bytes, sizeof(na.bytes));
533   memcpy(ptr, na.bytes, sizeof(na.bytes));
534   return offset + sizeof(na.bytes);
535 }
536
537
538 void WriteFloatLE(const FunctionCallbackInfo<Value>& args) {
539   args.GetReturnValue().Set(WriteFloatGeneric<float, kLittleEndian>(args));
540 }
541
542
543 void WriteFloatBE(const FunctionCallbackInfo<Value>& args) {
544   args.GetReturnValue().Set(WriteFloatGeneric<float, kBigEndian>(args));
545 }
546
547
548 void WriteDoubleLE(const FunctionCallbackInfo<Value>& args) {
549   args.GetReturnValue().Set(WriteFloatGeneric<double, kLittleEndian>(args));
550 }
551
552
553 void WriteDoubleBE(const FunctionCallbackInfo<Value>& args) {
554   args.GetReturnValue().Set(WriteFloatGeneric<double, kBigEndian>(args));
555 }
556
557
558 void ByteLength(const FunctionCallbackInfo<Value> &args) {
559   Environment* env = Environment::GetCurrent(args);
560
561   if (!args[0]->IsString())
562     return env->ThrowTypeError("Argument must be a string");
563
564   Local<String> s = args[0]->ToString(env->isolate());
565   enum encoding e = ParseEncoding(env->isolate(), args[1], UTF8);
566
567   uint32_t size = StringBytes::Size(env->isolate(), s, e);
568   args.GetReturnValue().Set(size);
569 }
570
571
572 void Compare(const FunctionCallbackInfo<Value> &args) {
573   Local<Object> obj_a = args[0].As<Object>();
574   char* obj_a_data =
575       static_cast<char*>(obj_a->GetIndexedPropertiesExternalArrayData());
576   size_t obj_a_len = obj_a->GetIndexedPropertiesExternalArrayDataLength();
577
578   Local<Object> obj_b = args[1].As<Object>();
579   char* obj_b_data =
580       static_cast<char*>(obj_b->GetIndexedPropertiesExternalArrayData());
581   size_t obj_b_len = obj_b->GetIndexedPropertiesExternalArrayDataLength();
582
583   size_t cmp_length = MIN(obj_a_len, obj_b_len);
584
585   int32_t val = memcmp(obj_a_data, obj_b_data, cmp_length);
586
587   // Normalize val to be an integer in the range of [1, -1] since
588   // implementations of memcmp() can vary by platform.
589   if (val == 0) {
590     if (obj_a_len > obj_b_len)
591       val = 1;
592     else if (obj_a_len < obj_b_len)
593       val = -1;
594   } else {
595     if (val > 0)
596       val = 1;
597     else
598       val = -1;
599   }
600
601   args.GetReturnValue().Set(val);
602 }
603
604
605 // pass Buffer object to load prototype methods
606 void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
607   Environment* env = Environment::GetCurrent(args);
608
609   CHECK(args[0]->IsFunction());
610
611   Local<Function> bv = args[0].As<Function>();
612   env->set_buffer_constructor_function(bv);
613   Local<Value> proto_v = bv->Get(env->prototype_string());
614
615   CHECK(proto_v->IsObject());
616
617   Local<Object> proto = proto_v.As<Object>();
618
619   env->SetMethod(proto, "asciiSlice", AsciiSlice);
620   env->SetMethod(proto, "base64Slice", Base64Slice);
621   env->SetMethod(proto, "binarySlice", BinarySlice);
622   env->SetMethod(proto, "hexSlice", HexSlice);
623   env->SetMethod(proto, "ucs2Slice", Ucs2Slice);
624   env->SetMethod(proto, "utf8Slice", Utf8Slice);
625
626   env->SetMethod(proto, "asciiWrite", AsciiWrite);
627   env->SetMethod(proto, "base64Write", Base64Write);
628   env->SetMethod(proto, "binaryWrite", BinaryWrite);
629   env->SetMethod(proto, "hexWrite", HexWrite);
630   env->SetMethod(proto, "ucs2Write", Ucs2Write);
631   env->SetMethod(proto, "utf8Write", Utf8Write);
632
633   env->SetMethod(proto, "copy", Copy);
634
635   // for backwards compatibility
636   proto->ForceSet(env->offset_string(),
637                   Uint32::New(env->isolate(), 0),
638                   v8::ReadOnly);
639 }
640
641
642 void Initialize(Handle<Object> target,
643                 Handle<Value> unused,
644                 Handle<Context> context) {
645   Environment* env = Environment::GetCurrent(context);
646
647   env->SetMethod(target, "setupBufferJS", SetupBufferJS);
648
649   env->SetMethod(target, "byteLength", ByteLength);
650   env->SetMethod(target, "byteLength", ByteLength);
651   env->SetMethod(target, "compare", Compare);
652   env->SetMethod(target, "fill", Fill);
653
654   env->SetMethod(target, "readDoubleBE", ReadDoubleBE);
655   env->SetMethod(target, "readDoubleLE", ReadDoubleLE);
656   env->SetMethod(target, "readFloatBE", ReadFloatBE);
657   env->SetMethod(target, "readFloatLE", ReadFloatLE);
658
659   env->SetMethod(target, "writeDoubleBE", WriteDoubleBE);
660   env->SetMethod(target, "writeDoubleLE", WriteDoubleLE);
661   env->SetMethod(target, "writeFloatBE", WriteFloatBE);
662   env->SetMethod(target, "writeFloatLE", WriteFloatLE);
663 }
664
665
666 }  // namespace Buffer
667 }  // namespace node
668
669 NODE_MODULE_CONTEXT_AWARE_BUILTIN(buffer, node::Buffer::Initialize)