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