src: replace naive search in Buffer::IndexOf
[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 "string_bytes.h"
7 #include "string_search.h"
8 #include "util.h"
9 #include "util-inl.h"
10 #include "v8-profiler.h"
11 #include "v8.h"
12
13 #include <string.h>
14 #include <limits.h>
15
16 #define BUFFER_ID 0xB0E4
17
18 #define MIN(a, b) ((a) < (b) ? (a) : (b))
19
20 #define CHECK_NOT_OOB(r)                                                    \
21   do {                                                                      \
22     if (!(r)) return env->ThrowRangeError("out of range index");            \
23   } while (0)
24
25 #define THROW_AND_RETURN_UNLESS_BUFFER(env, obj)                            \
26   do {                                                                      \
27     if (!HasInstance(obj))                                                  \
28       return env->ThrowTypeError("argument should be a Buffer");            \
29   } while (0)
30
31 #define SPREAD_ARG(val, name)                                                 \
32   CHECK((val)->IsUint8Array());                                               \
33   Local<Uint8Array> name = (val).As<Uint8Array>();                            \
34   ArrayBuffer::Contents name##_c = name->Buffer()->GetContents();             \
35   const size_t name##_offset = name->ByteOffset();                            \
36   const size_t name##_length = name->ByteLength();                            \
37   char* const name##_data =                                                   \
38       static_cast<char*>(name##_c.Data()) + name##_offset;                    \
39   if (name##_length > 0)                                                      \
40     CHECK_NE(name##_data, nullptr);
41
42 #define SLICE_START_END(start_arg, end_arg, end_max)                        \
43   size_t start;                                                             \
44   size_t end;                                                               \
45   CHECK_NOT_OOB(ParseArrayIndex(start_arg, 0, &start));                     \
46   CHECK_NOT_OOB(ParseArrayIndex(end_arg, end_max, &end));                   \
47   if (end < start) end = start;                                             \
48   CHECK_NOT_OOB(end <= end_max);                                            \
49   size_t length = end - start;
50
51 namespace node {
52 namespace Buffer {
53
54 using v8::ArrayBuffer;
55 using v8::ArrayBufferCreationMode;
56 using v8::Context;
57 using v8::EscapableHandleScope;
58 using v8::Function;
59 using v8::FunctionCallbackInfo;
60 using v8::FunctionTemplate;
61 using v8::HandleScope;
62 using v8::Integer;
63 using v8::Isolate;
64 using v8::Local;
65 using v8::Maybe;
66 using v8::MaybeLocal;
67 using v8::Number;
68 using v8::Object;
69 using v8::Persistent;
70 using v8::String;
71 using v8::Uint32;
72 using v8::Uint32Array;
73 using v8::Uint8Array;
74 using v8::Value;
75 using v8::WeakCallbackData;
76
77
78 class CallbackInfo {
79  public:
80   static inline void Free(char* data, void* hint);
81   static inline CallbackInfo* New(Isolate* isolate,
82                                   Local<Object> object,
83                                   FreeCallback callback,
84                                   void* hint = 0);
85   inline void Dispose(Isolate* isolate);
86   inline Persistent<Object>* persistent();
87  private:
88   static void WeakCallback(const WeakCallbackData<Object, CallbackInfo>&);
89   inline void WeakCallback(Isolate* isolate, Local<Object> object);
90   inline CallbackInfo(Isolate* isolate,
91                       Local<Object> object,
92                       FreeCallback callback,
93                       void* hint);
94   ~CallbackInfo();
95   Persistent<Object> persistent_;
96   FreeCallback const callback_;
97   void* const hint_;
98   DISALLOW_COPY_AND_ASSIGN(CallbackInfo);
99 };
100
101
102 void CallbackInfo::Free(char* data, void*) {
103   ::free(data);
104 }
105
106
107 CallbackInfo* CallbackInfo::New(Isolate* isolate,
108                                 Local<Object> object,
109                                 FreeCallback callback,
110                                 void* hint) {
111   return new CallbackInfo(isolate, object, callback, hint);
112 }
113
114
115 void CallbackInfo::Dispose(Isolate* isolate) {
116   WeakCallback(isolate, PersistentToLocal(isolate, persistent_));
117 }
118
119
120 Persistent<Object>* CallbackInfo::persistent() {
121   return &persistent_;
122 }
123
124
125 CallbackInfo::CallbackInfo(Isolate* isolate,
126                            Local<Object> object,
127                            FreeCallback callback,
128                            void* hint)
129     : persistent_(isolate, object),
130       callback_(callback),
131       hint_(hint) {
132   persistent_.SetWeak(this, WeakCallback);
133   persistent_.SetWrapperClassId(BUFFER_ID);
134   persistent_.MarkIndependent();
135   isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this));
136 }
137
138
139 CallbackInfo::~CallbackInfo() {
140   persistent_.Reset();
141 }
142
143
144 void CallbackInfo::WeakCallback(
145     const WeakCallbackData<Object, CallbackInfo>& data) {
146   data.GetParameter()->WeakCallback(data.GetIsolate(), data.GetValue());
147 }
148
149
150 void CallbackInfo::WeakCallback(Isolate* isolate, Local<Object> object) {
151   SPREAD_ARG(object, obj);
152   CHECK_EQ(obj_offset, 0);
153   CHECK_EQ(obj_c.ByteLength(), obj_length);
154
155   obj->Buffer()->Neuter();
156   callback_(obj_data, hint_);
157   int64_t change_in_bytes = -static_cast<int64_t>(sizeof(*this));
158   isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
159
160   delete this;
161 }
162
163
164 // Buffer methods
165
166 bool HasInstance(Local<Value> val) {
167   return val->IsUint8Array();
168 }
169
170
171 bool HasInstance(Local<Object> obj) {
172   return obj->IsUint8Array();
173 }
174
175
176 char* Data(Local<Value> val) {
177   CHECK(val->IsUint8Array());
178   Local<Uint8Array> ui = val.As<Uint8Array>();
179   ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents();
180   return static_cast<char*>(ab_c.Data()) + ui->ByteOffset();
181 }
182
183
184 char* Data(Local<Object> obj) {
185   CHECK(obj->IsUint8Array());
186   Local<Uint8Array> ui = obj.As<Uint8Array>();
187   ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents();
188   return static_cast<char*>(ab_c.Data()) + ui->ByteOffset();
189 }
190
191
192 size_t Length(Local<Value> val) {
193   CHECK(val->IsUint8Array());
194   Local<Uint8Array> ui = val.As<Uint8Array>();
195   return ui->ByteLength();
196 }
197
198
199 size_t Length(Local<Object> obj) {
200   CHECK(obj->IsUint8Array());
201   Local<Uint8Array> ui = obj.As<Uint8Array>();
202   return ui->ByteLength();
203 }
204
205
206 MaybeLocal<Object> New(Isolate* isolate,
207                        Local<String> string,
208                        enum encoding enc) {
209   EscapableHandleScope scope(isolate);
210
211   size_t length = StringBytes::Size(isolate, string, enc);
212   char* data = static_cast<char*>(malloc(length));
213
214   if (data == nullptr)
215     return Local<Object>();
216
217   size_t actual = StringBytes::Write(isolate, data, length, string, enc);
218   CHECK(actual <= length);
219
220   if (actual < length) {
221     data = static_cast<char*>(realloc(data, actual));
222     CHECK_NE(data, nullptr);
223   }
224
225   Local<Object> buf;
226   if (New(isolate, data, actual).ToLocal(&buf))
227     return scope.Escape(buf);
228
229   // Object failed to be created. Clean up resources.
230   free(data);
231   return Local<Object>();
232 }
233
234
235 MaybeLocal<Object> New(Isolate* isolate, size_t length) {
236   EscapableHandleScope handle_scope(isolate);
237   Local<Object> obj;
238   if (Buffer::New(Environment::GetCurrent(isolate), length).ToLocal(&obj))
239     return handle_scope.Escape(obj);
240   return Local<Object>();
241 }
242
243
244 MaybeLocal<Object> New(Environment* env, size_t length) {
245   EscapableHandleScope scope(env->isolate());
246
247   // V8 currently only allows a maximum Typed Array index of max Smi.
248   if (length > kMaxLength) {
249     return Local<Object>();
250   }
251
252   void* data;
253   if (length > 0) {
254     data = malloc(length);
255     if (data == nullptr)
256       return Local<Object>();
257   } else {
258     data = nullptr;
259   }
260
261   Local<ArrayBuffer> ab =
262     ArrayBuffer::New(env->isolate(),
263         data,
264         length,
265         ArrayBufferCreationMode::kInternalized);
266   Local<Uint8Array> ui = Uint8Array::New(ab, 0, length);
267   Maybe<bool> mb =
268       ui->SetPrototype(env->context(), env->buffer_prototype_object());
269   if (mb.FromMaybe(false))
270     return scope.Escape(ui);
271
272   // Object failed to be created. Clean up resources.
273   free(data);
274   return Local<Object>();
275 }
276
277
278 MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) {
279   Environment* env = Environment::GetCurrent(isolate);
280   EscapableHandleScope handle_scope(env->isolate());
281   Local<Object> obj;
282   if (Buffer::Copy(env, data, length).ToLocal(&obj))
283     return handle_scope.Escape(obj);
284   return Local<Object>();
285 }
286
287
288 MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) {
289   EscapableHandleScope scope(env->isolate());
290
291   // V8 currently only allows a maximum Typed Array index of max Smi.
292   if (length > kMaxLength) {
293     return Local<Object>();
294   }
295
296   void* new_data;
297   if (length > 0) {
298     CHECK_NE(data, nullptr);
299     new_data = malloc(length);
300     if (new_data == nullptr)
301       return Local<Object>();
302     memcpy(new_data, data, length);
303   } else {
304     new_data = nullptr;
305   }
306
307   Local<ArrayBuffer> ab =
308     ArrayBuffer::New(env->isolate(),
309         new_data,
310         length,
311         ArrayBufferCreationMode::kInternalized);
312   Local<Uint8Array> ui = Uint8Array::New(ab, 0, length);
313   Maybe<bool> mb =
314       ui->SetPrototype(env->context(), env->buffer_prototype_object());
315   if (mb.FromMaybe(false))
316     return scope.Escape(ui);
317
318   // Object failed to be created. Clean up resources.
319   free(new_data);
320   return Local<Object>();
321 }
322
323
324 MaybeLocal<Object> New(Isolate* isolate,
325                        char* data,
326                        size_t length,
327                        FreeCallback callback,
328                        void* hint) {
329   Environment* env = Environment::GetCurrent(isolate);
330   EscapableHandleScope handle_scope(env->isolate());
331   Local<Object> obj;
332   if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj))
333     return handle_scope.Escape(obj);
334   return Local<Object>();
335 }
336
337
338 MaybeLocal<Object> New(Environment* env,
339                        char* data,
340                        size_t length,
341                        FreeCallback callback,
342                        void* hint) {
343   EscapableHandleScope scope(env->isolate());
344
345   if (length > kMaxLength) {
346     return Local<Object>();
347   }
348
349   Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), data, length);
350   Local<Uint8Array> ui = Uint8Array::New(ab, 0, length);
351   Maybe<bool> mb =
352       ui->SetPrototype(env->context(), env->buffer_prototype_object());
353
354   if (!mb.FromMaybe(false))
355     return Local<Object>();
356
357   CallbackInfo::New(env->isolate(), ab, callback, hint);
358   return scope.Escape(ui);
359 }
360
361
362 MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) {
363   Environment* env = Environment::GetCurrent(isolate);
364   EscapableHandleScope handle_scope(env->isolate());
365   Local<Object> obj;
366   if (Buffer::New(env, data, length).ToLocal(&obj))
367     return handle_scope.Escape(obj);
368   return Local<Object>();
369 }
370
371
372 MaybeLocal<Object> New(Environment* env, char* data, size_t length) {
373   EscapableHandleScope scope(env->isolate());
374
375   if (length > 0) {
376     CHECK_NE(data, nullptr);
377     CHECK(length <= kMaxLength);
378   }
379
380   Local<ArrayBuffer> ab =
381       ArrayBuffer::New(env->isolate(),
382                        data,
383                        length,
384                        ArrayBufferCreationMode::kInternalized);
385   Local<Uint8Array> ui = Uint8Array::New(ab, 0, length);
386   Maybe<bool> mb =
387       ui->SetPrototype(env->context(), env->buffer_prototype_object());
388   if (mb.FromMaybe(false))
389     return scope.Escape(ui);
390   return Local<Object>();
391 }
392
393
394 void CreateFromString(const FunctionCallbackInfo<Value>& args) {
395   CHECK(args[0]->IsString());
396   CHECK(args[1]->IsString());
397
398   enum encoding enc = ParseEncoding(args.GetIsolate(),
399                                     args[1].As<String>(),
400                                     UTF8);
401   Local<Object> buf;
402   if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf))
403     args.GetReturnValue().Set(buf);
404 }
405
406
407 void CreateFromArrayBuffer(const FunctionCallbackInfo<Value>& args) {
408   Environment* env = Environment::GetCurrent(args);
409   if (!args[0]->IsArrayBuffer())
410     return env->ThrowTypeError("argument is not an ArrayBuffer");
411   Local<ArrayBuffer> ab = args[0].As<ArrayBuffer>();
412   Local<Uint8Array> ui = Uint8Array::New(ab, 0, ab->ByteLength());
413   Maybe<bool> mb =
414       ui->SetPrototype(env->context(), env->buffer_prototype_object());
415   if (!mb.FromMaybe(false))
416     return env->ThrowError("Unable to set Object prototype");
417   args.GetReturnValue().Set(ui);
418 }
419
420
421 template <encoding encoding>
422 void StringSlice(const FunctionCallbackInfo<Value>& args) {
423   Environment* env = Environment::GetCurrent(args);
424   Isolate* isolate = env->isolate();
425
426   THROW_AND_RETURN_UNLESS_BUFFER(env, args.This());
427   SPREAD_ARG(args.This(), ts_obj);
428
429   if (ts_obj_length == 0)
430     return args.GetReturnValue().SetEmptyString();
431
432   SLICE_START_END(args[0], args[1], ts_obj_length)
433
434   args.GetReturnValue().Set(
435       StringBytes::Encode(isolate, ts_obj_data + start, length, encoding));
436 }
437
438
439 template <>
440 void StringSlice<UCS2>(const FunctionCallbackInfo<Value>& args) {
441   Environment* env = Environment::GetCurrent(args);
442
443   THROW_AND_RETURN_UNLESS_BUFFER(env, args.This());
444   SPREAD_ARG(args.This(), ts_obj);
445
446   if (ts_obj_length == 0)
447     return args.GetReturnValue().SetEmptyString();
448
449   SLICE_START_END(args[0], args[1], ts_obj_length)
450   length /= 2;
451
452   const char* data = ts_obj_data + start;
453   const uint16_t* buf;
454   bool release = false;
455
456   // Node's "ucs2" encoding expects LE character data inside a Buffer, so we
457   // need to reorder on BE platforms.  See http://nodejs.org/api/buffer.html
458   // regarding Node's "ucs2" encoding specification.
459   const bool aligned = (reinterpret_cast<uintptr_t>(data) % sizeof(*buf) == 0);
460   if (IsLittleEndian() && aligned) {
461     buf = reinterpret_cast<const uint16_t*>(data);
462   } else {
463     // Make a copy to avoid unaligned accesses in v8::String::NewFromTwoByte().
464     uint16_t* copy = new uint16_t[length];
465     for (size_t i = 0, k = 0; i < length; i += 1, k += 2) {
466       // Assumes that the input is little endian.
467       const uint8_t lo = static_cast<uint8_t>(data[k + 0]);
468       const uint8_t hi = static_cast<uint8_t>(data[k + 1]);
469       copy[i] = lo | hi << 8;
470     }
471     buf = copy;
472     release = true;
473   }
474
475   args.GetReturnValue().Set(StringBytes::Encode(env->isolate(), buf, length));
476
477   if (release)
478     delete[] buf;
479 }
480
481
482 void BinarySlice(const FunctionCallbackInfo<Value>& args) {
483   StringSlice<BINARY>(args);
484 }
485
486
487 void AsciiSlice(const FunctionCallbackInfo<Value>& args) {
488   StringSlice<ASCII>(args);
489 }
490
491
492 void Utf8Slice(const FunctionCallbackInfo<Value>& args) {
493   StringSlice<UTF8>(args);
494 }
495
496
497 void Ucs2Slice(const FunctionCallbackInfo<Value>& args) {
498   StringSlice<UCS2>(args);
499 }
500
501
502 void HexSlice(const FunctionCallbackInfo<Value>& args) {
503   StringSlice<HEX>(args);
504 }
505
506
507 void Base64Slice(const FunctionCallbackInfo<Value>& args) {
508   StringSlice<BASE64>(args);
509 }
510
511
512 // bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]);
513 void Copy(const FunctionCallbackInfo<Value> &args) {
514   Environment* env = Environment::GetCurrent(args);
515
516   THROW_AND_RETURN_UNLESS_BUFFER(env, args.This());
517   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
518   Local<Object> target_obj = args[0].As<Object>();
519   SPREAD_ARG(args.This(), ts_obj);
520   SPREAD_ARG(target_obj, target);
521
522   size_t target_start;
523   size_t source_start;
524   size_t source_end;
525
526   CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &target_start));
527   CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &source_start));
528   CHECK_NOT_OOB(ParseArrayIndex(args[3], ts_obj_length, &source_end));
529
530   // Copy 0 bytes; we're done
531   if (target_start >= target_length || source_start >= source_end)
532     return args.GetReturnValue().Set(0);
533
534   if (source_start > ts_obj_length)
535     return env->ThrowRangeError("out of range index");
536
537   if (source_end - source_start > target_length - target_start)
538     source_end = source_start + target_length - target_start;
539
540   uint32_t to_copy = MIN(MIN(source_end - source_start,
541                              target_length - target_start),
542                              ts_obj_length - source_start);
543
544   memmove(target_data + target_start, ts_obj_data + source_start, to_copy);
545   args.GetReturnValue().Set(to_copy);
546 }
547
548
549 void Fill(const FunctionCallbackInfo<Value>& args) {
550   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
551   SPREAD_ARG(args[0], ts_obj);
552
553   size_t start = args[2]->Uint32Value();
554   size_t end = args[3]->Uint32Value();
555   size_t length = end - start;
556   CHECK(length + start <= ts_obj_length);
557
558   if (args[1]->IsNumber()) {
559     int value = args[1]->Uint32Value() & 255;
560     memset(ts_obj_data + start, value, length);
561     return;
562   }
563
564   node::Utf8Value str(args.GetIsolate(), args[1]);
565   size_t str_length = str.length();
566   size_t in_there = str_length;
567   char* ptr = ts_obj_data + start + str_length;
568
569   if (str_length == 0)
570     return;
571
572   memcpy(ts_obj_data + start, *str, MIN(str_length, length));
573
574   if (str_length >= length)
575     return;
576
577   while (in_there < length - in_there) {
578     memcpy(ptr, ts_obj_data + start, in_there);
579     ptr += in_there;
580     in_there *= 2;
581   }
582
583   if (in_there < length) {
584     memcpy(ptr, ts_obj_data + start, length - in_there);
585     in_there = length;
586   }
587 }
588
589
590 template <encoding encoding>
591 void StringWrite(const FunctionCallbackInfo<Value>& args) {
592   Environment* env = Environment::GetCurrent(args);
593
594   THROW_AND_RETURN_UNLESS_BUFFER(env, args.This());
595   SPREAD_ARG(args.This(), ts_obj);
596
597   if (!args[0]->IsString())
598     return env->ThrowTypeError("Argument must be a string");
599
600   Local<String> str = args[0]->ToString(env->isolate());
601
602   if (encoding == HEX && str->Length() % 2 != 0)
603     return env->ThrowTypeError("Invalid hex string");
604
605   size_t offset;
606   size_t max_length;
607
608   CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset));
609   CHECK_NOT_OOB(ParseArrayIndex(args[2], ts_obj_length - offset, &max_length));
610
611   max_length = MIN(ts_obj_length - offset, max_length);
612
613   if (max_length == 0)
614     return args.GetReturnValue().Set(0);
615
616   if (offset >= ts_obj_length)
617     return env->ThrowRangeError("Offset is out of bounds");
618
619   uint32_t written = StringBytes::Write(env->isolate(),
620                                         ts_obj_data + offset,
621                                         max_length,
622                                         str,
623                                         encoding,
624                                         nullptr);
625   args.GetReturnValue().Set(written);
626 }
627
628
629 void Base64Write(const FunctionCallbackInfo<Value>& args) {
630   StringWrite<BASE64>(args);
631 }
632
633
634 void BinaryWrite(const FunctionCallbackInfo<Value>& args) {
635   StringWrite<BINARY>(args);
636 }
637
638
639 void Utf8Write(const FunctionCallbackInfo<Value>& args) {
640   StringWrite<UTF8>(args);
641 }
642
643
644 void Ucs2Write(const FunctionCallbackInfo<Value>& args) {
645   StringWrite<UCS2>(args);
646 }
647
648
649 void HexWrite(const FunctionCallbackInfo<Value>& args) {
650   StringWrite<HEX>(args);
651 }
652
653
654 void AsciiWrite(const FunctionCallbackInfo<Value>& args) {
655   StringWrite<ASCII>(args);
656 }
657
658
659 static inline void Swizzle(char* start, unsigned int len) {
660   char* end = start + len - 1;
661   while (start < end) {
662     char tmp = *start;
663     *start++ = *end;
664     *end-- = tmp;
665   }
666 }
667
668
669 template <typename T, enum Endianness endianness>
670 void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
671   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
672   SPREAD_ARG(args[0], ts_obj);
673
674   uint32_t offset = args[1]->Uint32Value();
675   CHECK_LE(offset + sizeof(T), ts_obj_length);
676
677   union NoAlias {
678     T val;
679     char bytes[sizeof(T)];
680   };
681
682   union NoAlias na;
683   const char* ptr = static_cast<const char*>(ts_obj_data) + offset;
684   memcpy(na.bytes, ptr, sizeof(na.bytes));
685   if (endianness != GetEndianness())
686     Swizzle(na.bytes, sizeof(na.bytes));
687
688   args.GetReturnValue().Set(na.val);
689 }
690
691
692 void ReadFloatLE(const FunctionCallbackInfo<Value>& args) {
693   ReadFloatGeneric<float, kLittleEndian>(args);
694 }
695
696
697 void ReadFloatBE(const FunctionCallbackInfo<Value>& args) {
698   ReadFloatGeneric<float, kBigEndian>(args);
699 }
700
701
702 void ReadDoubleLE(const FunctionCallbackInfo<Value>& args) {
703   ReadFloatGeneric<double, kLittleEndian>(args);
704 }
705
706
707 void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
708   ReadFloatGeneric<double, kBigEndian>(args);
709 }
710
711
712 template <typename T, enum Endianness endianness>
713 uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
714   SPREAD_ARG(args[0], ts_obj);
715
716   T val = args[1]->NumberValue();
717   uint32_t offset = args[2]->Uint32Value();
718   CHECK_LE(offset + sizeof(T), ts_obj_length);
719
720   union NoAlias {
721     T val;
722     char bytes[sizeof(T)];
723   };
724
725   union NoAlias na = { val };
726   char* ptr = static_cast<char*>(ts_obj_data) + offset;
727   if (endianness != GetEndianness())
728     Swizzle(na.bytes, sizeof(na.bytes));
729   memcpy(ptr, na.bytes, sizeof(na.bytes));
730   return offset + sizeof(na.bytes);
731 }
732
733
734 void WriteFloatLE(const FunctionCallbackInfo<Value>& args) {
735   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
736   args.GetReturnValue().Set(WriteFloatGeneric<float, kLittleEndian>(args));
737 }
738
739
740 void WriteFloatBE(const FunctionCallbackInfo<Value>& args) {
741   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
742   args.GetReturnValue().Set(WriteFloatGeneric<float, kBigEndian>(args));
743 }
744
745
746 void WriteDoubleLE(const FunctionCallbackInfo<Value>& args) {
747   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
748   args.GetReturnValue().Set(WriteFloatGeneric<double, kLittleEndian>(args));
749 }
750
751
752 void WriteDoubleBE(const FunctionCallbackInfo<Value>& args) {
753   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
754   args.GetReturnValue().Set(WriteFloatGeneric<double, kBigEndian>(args));
755 }
756
757
758 void ByteLengthUtf8(const FunctionCallbackInfo<Value> &args) {
759   CHECK(args[0]->IsString());
760
761   // Fast case: avoid StringBytes on UTF8 string. Jump to v8.
762   args.GetReturnValue().Set(args[0].As<String>()->Utf8Length());
763 }
764
765
766 void Compare(const FunctionCallbackInfo<Value> &args) {
767   Environment* env = Environment::GetCurrent(args);
768
769   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
770   THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]);
771   SPREAD_ARG(args[0], obj_a);
772   SPREAD_ARG(args[1], obj_b);
773
774   size_t cmp_length = MIN(obj_a_length, obj_b_length);
775
776   int val = cmp_length > 0 ? memcmp(obj_a_data, obj_b_data, cmp_length) : 0;
777
778   // Normalize val to be an integer in the range of [1, -1] since
779   // implementations of memcmp() can vary by platform.
780   if (val == 0) {
781     if (obj_a_length > obj_b_length)
782       val = 1;
783     else if (obj_a_length < obj_b_length)
784       val = -1;
785   } else {
786     if (val > 0)
787       val = 1;
788     else
789       val = -1;
790   }
791
792   args.GetReturnValue().Set(val);
793 }
794
795
796 void IndexOfString(const FunctionCallbackInfo<Value>& args) {
797   ASSERT(args[1]->IsString());
798   ASSERT(args[2]->IsNumber());
799
800   enum encoding enc = ParseEncoding(args.GetIsolate(),
801                                     args[3],
802                                     UTF8);
803
804   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
805   SPREAD_ARG(args[0], ts_obj);
806
807   Local<String> needle = args[1].As<String>();
808   const char* haystack = ts_obj_data;
809   const size_t haystack_length = ts_obj_length;
810   const size_t needle_length = needle->Utf8Length();
811
812
813   if (needle_length == 0 || haystack_length == 0) {
814     return args.GetReturnValue().Set(-1);
815   }
816
817   int64_t offset_i64 = args[2]->IntegerValue();
818   size_t offset = 0;
819
820   if (offset_i64 < 0) {
821     if (offset_i64 + static_cast<int64_t>(haystack_length) < 0) {
822       offset = 0;
823     } else {
824       offset = static_cast<size_t>(haystack_length + offset_i64);
825     }
826   } else {
827     offset = static_cast<size_t>(offset_i64);
828   }
829
830   if (haystack_length < offset || needle_length + offset > haystack_length) {
831     return args.GetReturnValue().Set(-1);
832   }
833
834   size_t result = haystack_length;
835
836   if (enc == UCS2) {
837     String::Value needle_value(needle);
838     if (*needle_value == nullptr)
839       return args.GetReturnValue().Set(-1);
840
841     if (haystack_length < 2 || needle_value.length() < 1) {
842       return args.GetReturnValue().Set(-1);
843     }
844
845     result = SearchString(reinterpret_cast<const uint16_t*>(haystack),
846                           haystack_length / 2,
847                           reinterpret_cast<const uint16_t*>(*needle_value),
848                           needle_value.length(),
849                           offset / 2);
850     result *= 2;
851   } else if (enc == UTF8) {
852     String::Utf8Value needle_value(needle);
853     if (*needle_value == nullptr)
854       return args.GetReturnValue().Set(-1);
855
856     result = SearchString(reinterpret_cast<const uint8_t*>(haystack),
857                           haystack_length,
858                           reinterpret_cast<const uint8_t*>(*needle_value),
859                           needle_length,
860                           offset);
861   } else if (enc == BINARY) {
862     uint8_t* needle_data = static_cast<uint8_t*>(malloc(needle_length));
863     if (needle_data == nullptr) {
864       return args.GetReturnValue().Set(-1);
865     }
866     needle->WriteOneByte(
867         needle_data, 0, needle_length, String::NO_NULL_TERMINATION);
868
869     result = SearchString(reinterpret_cast<const uint8_t*>(haystack),
870                           haystack_length,
871                           needle_data,
872                           needle_length,
873                           offset);
874     free(needle_data);
875   }
876
877   args.GetReturnValue().Set(
878       result == haystack_length ? -1 : static_cast<int>(result));
879 }
880
881 void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) {
882   ASSERT(args[1]->IsObject());
883   ASSERT(args[2]->IsNumber());
884
885   enum encoding enc = ParseEncoding(args.GetIsolate(),
886                                     args[3],
887                                     UTF8);
888
889   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
890   SPREAD_ARG(args[0], ts_obj);
891   SPREAD_ARG(args[1], buf);
892
893   if (buf_length > 0)
894     CHECK_NE(buf_data, nullptr);
895
896   const char* haystack = ts_obj_data;
897   const size_t haystack_length = ts_obj_length;
898   const char* needle = buf_data;
899   const size_t needle_length = buf_length;
900
901   if (needle_length == 0 || haystack_length == 0) {
902     return args.GetReturnValue().Set(-1);
903   }
904
905   int64_t offset_i64 = args[2]->IntegerValue();
906   size_t offset = 0;
907
908   if (offset_i64 < 0) {
909     if (offset_i64 + static_cast<int64_t>(haystack_length) < 0)
910       offset = 0;
911     else
912       offset = static_cast<size_t>(haystack_length + offset_i64);
913   } else {
914     offset = static_cast<size_t>(offset_i64);
915   }
916
917   if (haystack_length < offset || needle_length + offset > haystack_length) {
918     return args.GetReturnValue().Set(-1);
919   }
920
921   size_t result = haystack_length;
922
923   if (enc == UCS2) {
924     if (haystack_length < 2 || needle_length < 2) {
925       return args.GetReturnValue().Set(-1);
926     }
927     result = SearchString(
928         reinterpret_cast<const uint16_t*>(haystack),
929         haystack_length / 2,
930         reinterpret_cast<const uint16_t*>(needle),
931         needle_length / 2,
932         offset / 2);
933     result *= 2;
934   } else {
935     result = SearchString(
936         reinterpret_cast<const uint8_t*>(haystack),
937         haystack_length,
938         reinterpret_cast<const uint8_t*>(needle),
939         needle_length,
940         offset);
941   }
942
943   args.GetReturnValue().Set(
944       result == haystack_length ? -1 : static_cast<int>(result));
945 }
946
947 void IndexOfNumber(const FunctionCallbackInfo<Value>& args) {
948   ASSERT(args[1]->IsNumber());
949   ASSERT(args[2]->IsNumber());
950
951   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
952   SPREAD_ARG(args[0], ts_obj);
953
954   uint32_t needle = args[1]->Uint32Value();
955   int64_t offset_i64 = args[2]->IntegerValue();
956   size_t offset;
957
958   if (offset_i64 < 0) {
959     if (offset_i64 + static_cast<int64_t>(ts_obj_length) < 0)
960       offset = 0;
961     else
962       offset = static_cast<size_t>(ts_obj_length + offset_i64);
963   } else {
964     offset = static_cast<size_t>(offset_i64);
965   }
966
967   if (ts_obj_length == 0 || offset + 1 > ts_obj_length)
968     return args.GetReturnValue().Set(-1);
969
970   void* ptr = memchr(ts_obj_data + offset, needle, ts_obj_length - offset);
971   char* ptr_char = static_cast<char*>(ptr);
972   args.GetReturnValue().Set(ptr ? static_cast<int>(ptr_char - ts_obj_data)
973                                 : -1);
974 }
975
976
977 // pass Buffer object to load prototype methods
978 void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
979   Environment* env = Environment::GetCurrent(args);
980
981   CHECK(args[0]->IsObject());
982   Local<Object> proto = args[0].As<Object>();
983   env->set_buffer_prototype_object(proto);
984
985   env->SetMethod(proto, "asciiSlice", AsciiSlice);
986   env->SetMethod(proto, "base64Slice", Base64Slice);
987   env->SetMethod(proto, "binarySlice", BinarySlice);
988   env->SetMethod(proto, "hexSlice", HexSlice);
989   env->SetMethod(proto, "ucs2Slice", Ucs2Slice);
990   env->SetMethod(proto, "utf8Slice", Utf8Slice);
991
992   env->SetMethod(proto, "asciiWrite", AsciiWrite);
993   env->SetMethod(proto, "base64Write", Base64Write);
994   env->SetMethod(proto, "binaryWrite", BinaryWrite);
995   env->SetMethod(proto, "hexWrite", HexWrite);
996   env->SetMethod(proto, "ucs2Write", Ucs2Write);
997   env->SetMethod(proto, "utf8Write", Utf8Write);
998
999   env->SetMethod(proto, "copy", Copy);
1000
1001   CHECK(args[1]->IsObject());
1002   Local<Object> bObj = args[1].As<Object>();
1003
1004   uint32_t* const fields = env->array_buffer_allocator_info()->fields();
1005   uint32_t const fields_count =
1006       env->array_buffer_allocator_info()->fields_count();
1007
1008   Local<ArrayBuffer> array_buffer =
1009       ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count);
1010
1011   bObj->Set(String::NewFromUtf8(env->isolate(), "flags"),
1012             Uint32Array::New(array_buffer, 0, fields_count));
1013 }
1014
1015
1016 void Initialize(Local<Object> target,
1017                 Local<Value> unused,
1018                 Local<Context> context) {
1019   Environment* env = Environment::GetCurrent(context);
1020
1021   env->SetMethod(target, "setupBufferJS", SetupBufferJS);
1022   env->SetMethod(target, "createFromString", CreateFromString);
1023   env->SetMethod(target, "createFromArrayBuffer", CreateFromArrayBuffer);
1024
1025   env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8);
1026   env->SetMethod(target, "compare", Compare);
1027   env->SetMethod(target, "fill", Fill);
1028   env->SetMethod(target, "indexOfBuffer", IndexOfBuffer);
1029   env->SetMethod(target, "indexOfNumber", IndexOfNumber);
1030   env->SetMethod(target, "indexOfString", IndexOfString);
1031
1032   env->SetMethod(target, "readDoubleBE", ReadDoubleBE);
1033   env->SetMethod(target, "readDoubleLE", ReadDoubleLE);
1034   env->SetMethod(target, "readFloatBE", ReadFloatBE);
1035   env->SetMethod(target, "readFloatLE", ReadFloatLE);
1036
1037   env->SetMethod(target, "writeDoubleBE", WriteDoubleBE);
1038   env->SetMethod(target, "writeDoubleLE", WriteDoubleLE);
1039   env->SetMethod(target, "writeFloatBE", WriteFloatBE);
1040   env->SetMethod(target, "writeFloatLE", WriteFloatLE);
1041
1042   target->Set(env->context(),
1043               FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"),
1044               Integer::NewFromUnsigned(env->isolate(), kMaxLength)).FromJust();
1045
1046   target->Set(env->context(),
1047               FIXED_ONE_BYTE_STRING(env->isolate(), "kStringMaxLength"),
1048               Integer::New(env->isolate(), String::kMaxLength)).FromJust();
1049 }
1050
1051
1052 }  // namespace Buffer
1053 }  // namespace node
1054
1055 NODE_MODULE_CONTEXT_AWARE_BUILTIN(buffer, node::Buffer::Initialize)