1 // Copyright Joyent, Inc. and other Node contributors.
2 //
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:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
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.
21 
22 #include "node.h"
23 #include "node_buffer.h"
24 #include "node_errors.h"
25 
26 #include "env-inl.h"
27 #include "string_bytes.h"
28 #include "string_search.h"
29 #include "util-inl.h"
30 #include "v8-profiler.h"
31 #include "v8.h"
32 
33 #include <string.h>
34 #include <limits.h>
35 
36 #define MIN(a, b) ((a) < (b) ? (a) : (b))
37 
38 #define THROW_AND_RETURN_UNLESS_BUFFER(env, obj)                            \
39   THROW_AND_RETURN_IF_NOT_BUFFER(env, obj, "argument")
40 
41 #define THROW_AND_RETURN_IF_OOB(r)                                          \
42   do {                                                                      \
43     if ((r).IsNothing()) return;                                            \
44     if (!(r).FromJust())                                                    \
45       return node::THROW_ERR_INDEX_OUT_OF_RANGE(env);                       \
46   } while (0)                                                               \
47 
48 #define SLICE_START_END(env, start_arg, end_arg, end_max)                   \
49   size_t start;                                                             \
50   size_t end;                                                               \
51   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, start_arg, 0, &start));      \
52   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, end_arg, end_max, &end));    \
53   if (end < start) end = start;                                             \
54   THROW_AND_RETURN_IF_OOB(Just(end <= end_max));                            \
55   size_t length = end - start;
56 
57 namespace node {
58 
59 // if true, all Buffer and SlowBuffer instances will automatically zero-fill
60 bool zero_fill_all_buffers = false;
61 
62 namespace {
63 
BufferMalloc(size_t length)64 inline void* BufferMalloc(size_t length) {
65   return zero_fill_all_buffers ? node::UncheckedCalloc(length) :
66                                  node::UncheckedMalloc(length);
67 }
68 
69 }  // namespace
70 
71 namespace Buffer {
72 
73 using v8::ArrayBuffer;
74 using v8::ArrayBufferCreationMode;
75 using v8::ArrayBufferView;
76 using v8::Context;
77 using v8::EscapableHandleScope;
78 using v8::FunctionCallbackInfo;
79 using v8::Integer;
80 using v8::Isolate;
81 using v8::Just;
82 using v8::Local;
83 using v8::Maybe;
84 using v8::MaybeLocal;
85 using v8::Nothing;
86 using v8::Object;
87 using v8::String;
88 using v8::Uint32;
89 using v8::Uint32Array;
90 using v8::Uint8Array;
91 using v8::Value;
92 using v8::WeakCallbackInfo;
93 
94 namespace {
95 
96 class CallbackInfo {
97  public:
98   static inline void Free(char* data, void* hint);
99   static inline CallbackInfo* New(Isolate* isolate,
100                                   Local<ArrayBuffer> object,
101                                   FreeCallback callback,
102                                   char* data,
103                                   void* hint = 0);
104  private:
105   static void WeakCallback(const WeakCallbackInfo<CallbackInfo>&);
106   inline void WeakCallback(Isolate* isolate);
107   inline CallbackInfo(Isolate* isolate,
108                       Local<ArrayBuffer> object,
109                       FreeCallback callback,
110                       char* data,
111                       void* hint);
112   Persistent<ArrayBuffer> persistent_;
113   FreeCallback const callback_;
114   char* const data_;
115   void* const hint_;
116   DISALLOW_COPY_AND_ASSIGN(CallbackInfo);
117 };
118 
119 
Free(char * data,void *)120 void CallbackInfo::Free(char* data, void*) {
121   ::free(data);
122 }
123 
124 
New(Isolate * isolate,Local<ArrayBuffer> object,FreeCallback callback,char * data,void * hint)125 CallbackInfo* CallbackInfo::New(Isolate* isolate,
126                                 Local<ArrayBuffer> object,
127                                 FreeCallback callback,
128                                 char* data,
129                                 void* hint) {
130   return new CallbackInfo(isolate, object, callback, data, hint);
131 }
132 
133 
CallbackInfo(Isolate * isolate,Local<ArrayBuffer> object,FreeCallback callback,char * data,void * hint)134 CallbackInfo::CallbackInfo(Isolate* isolate,
135                            Local<ArrayBuffer> object,
136                            FreeCallback callback,
137                            char* data,
138                            void* hint)
139     : persistent_(isolate, object),
140       callback_(callback),
141       data_(data),
142       hint_(hint) {
143   ArrayBuffer::Contents obj_c = object->GetContents();
144   CHECK_EQ(data_, static_cast<char*>(obj_c.Data()));
145   if (object->ByteLength() != 0)
146     CHECK_NOT_NULL(data_);
147 
148   persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
149   isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this));
150 }
151 
152 
WeakCallback(const WeakCallbackInfo<CallbackInfo> & data)153 void CallbackInfo::WeakCallback(
154     const WeakCallbackInfo<CallbackInfo>& data) {
155   CallbackInfo* self = data.GetParameter();
156   self->WeakCallback(data.GetIsolate());
157   delete self;
158 }
159 
160 
WeakCallback(Isolate * isolate)161 void CallbackInfo::WeakCallback(Isolate* isolate) {
162   callback_(data_, hint_);
163   int64_t change_in_bytes = -static_cast<int64_t>(sizeof(*this));
164   isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
165 }
166 
167 
168 // Parse index for external array data. An empty Maybe indicates
169 // a pending exception. `false` indicates that the index is out-of-bounds.
ParseArrayIndex(Environment * env,Local<Value> arg,size_t def,size_t * ret)170 inline MUST_USE_RESULT Maybe<bool> ParseArrayIndex(Environment* env,
171                                                    Local<Value> arg,
172                                                    size_t def,
173                                                    size_t* ret) {
174   if (arg->IsUndefined()) {
175     *ret = def;
176     return Just(true);
177   }
178 
179   int64_t tmp_i;
180   if (!arg->IntegerValue(env->context()).To(&tmp_i))
181     return Nothing<bool>();
182 
183   if (tmp_i < 0)
184     return Just(false);
185 
186   // Check that the result fits in a size_t.
187   const uint64_t kSizeMax = static_cast<uint64_t>(static_cast<size_t>(-1));
188   // coverity[pointless_expression]
189   if (static_cast<uint64_t>(tmp_i) > kSizeMax)
190     return Just(false);
191 
192   *ret = static_cast<size_t>(tmp_i);
193   return Just(true);
194 }
195 
196 }  // anonymous namespace
197 
198 // Buffer methods
199 
HasInstance(Local<Value> val)200 bool HasInstance(Local<Value> val) {
201   return val->IsArrayBufferView();
202 }
203 
204 
HasInstance(Local<Object> obj)205 bool HasInstance(Local<Object> obj) {
206   return obj->IsArrayBufferView();
207 }
208 
209 
Data(Local<Value> val)210 char* Data(Local<Value> val) {
211   CHECK(val->IsArrayBufferView());
212   Local<ArrayBufferView> ui = val.As<ArrayBufferView>();
213   ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents();
214   return static_cast<char*>(ab_c.Data()) + ui->ByteOffset();
215 }
216 
217 
Data(Local<Object> obj)218 char* Data(Local<Object> obj) {
219   CHECK(obj->IsArrayBufferView());
220   Local<ArrayBufferView> ui = obj.As<ArrayBufferView>();
221   ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents();
222   return static_cast<char*>(ab_c.Data()) + ui->ByteOffset();
223 }
224 
225 
Length(Local<Value> val)226 size_t Length(Local<Value> val) {
227   CHECK(val->IsArrayBufferView());
228   Local<ArrayBufferView> ui = val.As<ArrayBufferView>();
229   return ui->ByteLength();
230 }
231 
232 
Length(Local<Object> obj)233 size_t Length(Local<Object> obj) {
234   CHECK(obj->IsArrayBufferView());
235   Local<ArrayBufferView> ui = obj.As<ArrayBufferView>();
236   return ui->ByteLength();
237 }
238 
239 
New(Isolate * isolate,Local<String> string,enum encoding enc)240 MaybeLocal<Object> New(Isolate* isolate,
241                        Local<String> string,
242                        enum encoding enc) {
243   EscapableHandleScope scope(isolate);
244 
245   size_t length;
246   if (!StringBytes::Size(isolate, string, enc).To(&length))
247     return Local<Object>();
248   size_t actual = 0;
249   char* data = nullptr;
250 
251   if (length > 0) {
252     data = static_cast<char*>(BufferMalloc(length));
253 
254     if (data == nullptr)
255       return Local<Object>();
256 
257     actual = StringBytes::Write(isolate, data, length, string, enc);
258     CHECK(actual <= length);
259 
260     if (actual == 0) {
261       free(data);
262       data = nullptr;
263     } else if (actual < length) {
264       data = node::Realloc(data, actual);
265     }
266   }
267 
268   Local<Object> buf;
269   if (New(isolate, data, actual).ToLocal(&buf))
270     return scope.Escape(buf);
271 
272   // Object failed to be created. Clean up resources.
273   free(data);
274   return Local<Object>();
275 }
276 
277 
New(Isolate * isolate,size_t length)278 MaybeLocal<Object> New(Isolate* isolate, size_t length) {
279   EscapableHandleScope handle_scope(isolate);
280   Local<Object> obj;
281   Environment* env = Environment::GetCurrent(isolate);
282   CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
283   if (Buffer::New(env, length).ToLocal(&obj))
284     return handle_scope.Escape(obj);
285   return Local<Object>();
286 }
287 
288 
New(Environment * env,size_t length)289 MaybeLocal<Object> New(Environment* env, size_t length) {
290   EscapableHandleScope scope(env->isolate());
291 
292   // V8 currently only allows a maximum Typed Array index of max Smi.
293   if (length > kMaxLength) {
294     return Local<Object>();
295   }
296 
297   void* data;
298   if (length > 0) {
299     data = BufferMalloc(length);
300     if (data == nullptr)
301       return Local<Object>();
302   } else {
303     data = nullptr;
304   }
305 
306   Local<ArrayBuffer> ab =
307     ArrayBuffer::New(env->isolate(),
308         data,
309         length,
310         ArrayBufferCreationMode::kInternalized);
311   MaybeLocal<Uint8Array> ui = Buffer::New(env, ab, 0, length);
312 
313   if (ui.IsEmpty()) {
314     // Object failed to be created. Clean up resources.
315     free(data);
316   }
317 
318   return scope.Escape(ui.FromMaybe(Local<Uint8Array>()));
319 }
320 
321 
Copy(Isolate * isolate,const char * data,size_t length)322 MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) {
323   EscapableHandleScope handle_scope(isolate);
324   Environment* env = Environment::GetCurrent(isolate);
325   CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
326   Local<Object> obj;
327   if (Buffer::Copy(env, data, length).ToLocal(&obj))
328     return handle_scope.Escape(obj);
329   return Local<Object>();
330 }
331 
332 
Copy(Environment * env,const char * data,size_t length)333 MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) {
334   EscapableHandleScope scope(env->isolate());
335 
336   // V8 currently only allows a maximum Typed Array index of max Smi.
337   if (length > kMaxLength) {
338     return Local<Object>();
339   }
340 
341   void* new_data;
342   if (length > 0) {
343     CHECK_NOT_NULL(data);
344     new_data = node::UncheckedMalloc(length);
345     if (new_data == nullptr)
346       return Local<Object>();
347     memcpy(new_data, data, length);
348   } else {
349     new_data = nullptr;
350   }
351 
352   Local<ArrayBuffer> ab =
353     ArrayBuffer::New(env->isolate(),
354         new_data,
355         length,
356         ArrayBufferCreationMode::kInternalized);
357   MaybeLocal<Uint8Array> ui = Buffer::New(env, ab, 0, length);
358 
359   if (ui.IsEmpty()) {
360     // Object failed to be created. Clean up resources.
361     free(new_data);
362   }
363 
364   return scope.Escape(ui.FromMaybe(Local<Uint8Array>()));
365 }
366 
367 
New(Isolate * isolate,char * data,size_t length,FreeCallback callback,void * hint)368 MaybeLocal<Object> New(Isolate* isolate,
369                        char* data,
370                        size_t length,
371                        FreeCallback callback,
372                        void* hint) {
373   EscapableHandleScope handle_scope(isolate);
374   Environment* env = Environment::GetCurrent(isolate);
375   CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
376   Local<Object> obj;
377   if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj))
378     return handle_scope.Escape(obj);
379   return Local<Object>();
380 }
381 
382 
New(Environment * env,char * data,size_t length,FreeCallback callback,void * hint)383 MaybeLocal<Object> New(Environment* env,
384                        char* data,
385                        size_t length,
386                        FreeCallback callback,
387                        void* hint) {
388   EscapableHandleScope scope(env->isolate());
389 
390   if (length > kMaxLength) {
391     return Local<Object>();
392   }
393 
394   Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), data, length);
395   MaybeLocal<Uint8Array> ui = Buffer::New(env, ab, 0, length);
396 
397   if (ui.IsEmpty()) {
398     return Local<Object>();
399   }
400 
401   CallbackInfo::New(env->isolate(), ab, callback, data, hint);
402   return scope.Escape(ui.ToLocalChecked());
403 }
404 
405 
New(Isolate * isolate,char * data,size_t length)406 MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) {
407   EscapableHandleScope handle_scope(isolate);
408   Environment* env = Environment::GetCurrent(isolate);
409   CHECK_NOT_NULL(env);  // TODO(addaleax): Handle nullptr here.
410   Local<Object> obj;
411   if (Buffer::New(env, data, length).ToLocal(&obj))
412     return handle_scope.Escape(obj);
413   return Local<Object>();
414 }
415 
416 
New(Environment * env,char * data,size_t length)417 MaybeLocal<Object> New(Environment* env, char* data, size_t length) {
418   if (length > 0) {
419     CHECK_NOT_NULL(data);
420     CHECK(length <= kMaxLength);
421   }
422 
423   Local<ArrayBuffer> ab =
424       ArrayBuffer::New(env->isolate(),
425                        data,
426                        length,
427                        ArrayBufferCreationMode::kInternalized);
428   return Buffer::New(env, ab, 0, length).FromMaybe(Local<Object>());
429 }
430 
431 namespace {
432 
CreateFromString(const FunctionCallbackInfo<Value> & args)433 void CreateFromString(const FunctionCallbackInfo<Value>& args) {
434   CHECK(args[0]->IsString());
435   CHECK(args[1]->IsString());
436 
437   enum encoding enc = ParseEncoding(args.GetIsolate(),
438                                     args[1].As<String>(),
439                                     UTF8);
440   Local<Object> buf;
441   if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf))
442     args.GetReturnValue().Set(buf);
443 }
444 
445 
446 template <encoding encoding>
StringSlice(const FunctionCallbackInfo<Value> & args)447 void StringSlice(const FunctionCallbackInfo<Value>& args) {
448   Environment* env = Environment::GetCurrent(args);
449   Isolate* isolate = env->isolate();
450 
451   THROW_AND_RETURN_UNLESS_BUFFER(env, args.This());
452   SPREAD_BUFFER_ARG(args.This(), ts_obj);
453 
454   if (ts_obj_length == 0)
455     return args.GetReturnValue().SetEmptyString();
456 
457   SLICE_START_END(env, args[0], args[1], ts_obj_length)
458 
459   Local<Value> error;
460   MaybeLocal<Value> ret =
461       StringBytes::Encode(isolate,
462                           ts_obj_data + start,
463                           length,
464                           encoding,
465                           &error);
466   if (ret.IsEmpty()) {
467     CHECK(!error.IsEmpty());
468     isolate->ThrowException(error);
469     return;
470   }
471   args.GetReturnValue().Set(ret.ToLocalChecked());
472 }
473 
474 
475 // bytesCopied = copy(buffer, target[, targetStart][, sourceStart][, sourceEnd])
Copy(const FunctionCallbackInfo<Value> & args)476 void Copy(const FunctionCallbackInfo<Value> &args) {
477   Environment* env = Environment::GetCurrent(args);
478 
479   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
480   THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]);
481   Local<Object> buffer_obj = args[0].As<Object>();
482   Local<Object> target_obj = args[1].As<Object>();
483   SPREAD_BUFFER_ARG(buffer_obj, ts_obj);
484   SPREAD_BUFFER_ARG(target_obj, target);
485 
486   size_t target_start;
487   size_t source_start;
488   size_t source_end;
489 
490   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &target_start));
491   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &source_start));
492   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[4], ts_obj_length,
493                                           &source_end));
494 
495   // Copy 0 bytes; we're done
496   if (target_start >= target_length || source_start >= source_end)
497     return args.GetReturnValue().Set(0);
498 
499   if (source_start > ts_obj_length)
500     return node::THROW_ERR_INDEX_OUT_OF_RANGE(env);
501 
502   if (source_end - source_start > target_length - target_start)
503     source_end = source_start + target_length - target_start;
504 
505   uint32_t to_copy = MIN(MIN(source_end - source_start,
506                              target_length - target_start),
507                              ts_obj_length - source_start);
508 
509   memmove(target_data + target_start, ts_obj_data + source_start, to_copy);
510   args.GetReturnValue().Set(to_copy);
511 }
512 
513 
Fill(const FunctionCallbackInfo<Value> & args)514 void Fill(const FunctionCallbackInfo<Value>& args) {
515   Environment* env = Environment::GetCurrent(args);
516   Local<Context> ctx = env->context();
517 
518   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
519   SPREAD_BUFFER_ARG(args[0], ts_obj);
520 
521   uint32_t start;
522   if (!args[2]->Uint32Value(ctx).To(&start)) return;
523   uint32_t end;
524   if (!args[3]->Uint32Value(ctx).To(&end)) return;
525   size_t fill_length = end - start;
526   Local<String> str_obj;
527   size_t str_length;
528   enum encoding enc;
529 
530   // OOB Check. Throw the error in JS.
531   if (start > end || fill_length + start > ts_obj_length)
532     return args.GetReturnValue().Set(-2);
533 
534   // First check if Buffer has been passed.
535   if (Buffer::HasInstance(args[1])) {
536     SPREAD_BUFFER_ARG(args[1], fill_obj);
537     str_length = fill_obj_length;
538     memcpy(ts_obj_data + start, fill_obj_data, MIN(str_length, fill_length));
539     goto start_fill;
540   }
541 
542   // Then coerce everything that's not a string.
543   if (!args[1]->IsString()) {
544     uint32_t val;
545     if (!args[1]->Uint32Value(ctx).To(&val)) return;
546     int value = val & 255;
547     memset(ts_obj_data + start, value, fill_length);
548     return;
549   }
550 
551   str_obj = args[1]->ToString(env->context()).ToLocalChecked();
552   enc = ParseEncoding(env->isolate(), args[4], UTF8);
553 
554   // Can't use StringBytes::Write() in all cases. For example if attempting
555   // to write a two byte character into a one byte Buffer.
556   if (enc == UTF8) {
557     str_length = str_obj->Utf8Length(env->isolate());
558     node::Utf8Value str(env->isolate(), args[1]);
559     memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length));
560 
561   } else if (enc == UCS2) {
562     str_length = str_obj->Length() * sizeof(uint16_t);
563     node::TwoByteValue str(env->isolate(), args[1]);
564     if (IsBigEndian())
565       SwapBytes16(reinterpret_cast<char*>(&str[0]), str_length);
566 
567     memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length));
568 
569   } else {
570     // Write initial String to Buffer, then use that memory to copy remainder
571     // of string. Correct the string length for cases like HEX where less than
572     // the total string length is written.
573     str_length = StringBytes::Write(env->isolate(),
574                                     ts_obj_data + start,
575                                     fill_length,
576                                     str_obj,
577                                     enc,
578                                     nullptr);
579   }
580 
581  start_fill:
582 
583   if (str_length >= fill_length)
584     return;
585 
586   // If str_length is zero, then either an empty buffer was provided, or Write()
587   // indicated that no bytes could be written. If no bytes could be written,
588   // then return -1 because the fill value is invalid. This will trigger a throw
589   // in JavaScript. Silently failing should be avoided because it can lead to
590   // buffers with unexpected contents.
591   if (str_length == 0)
592     return args.GetReturnValue().Set(-1);
593 
594   size_t in_there = str_length;
595   char* ptr = ts_obj_data + start + str_length;
596 
597   while (in_there < fill_length - in_there) {
598     memcpy(ptr, ts_obj_data + start, in_there);
599     ptr += in_there;
600     in_there *= 2;
601   }
602 
603   if (in_there < fill_length) {
604     memcpy(ptr, ts_obj_data + start, fill_length - in_there);
605   }
606 }
607 
608 
609 template <encoding encoding>
StringWrite(const FunctionCallbackInfo<Value> & args)610 void StringWrite(const FunctionCallbackInfo<Value>& args) {
611   Environment* env = Environment::GetCurrent(args);
612 
613   THROW_AND_RETURN_UNLESS_BUFFER(env, args.This());
614   SPREAD_BUFFER_ARG(args.This(), ts_obj);
615 
616   THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument");
617 
618   Local<String> str = args[0]->ToString(env->context()).ToLocalChecked();
619 
620   size_t offset;
621   size_t max_length;
622 
623   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], 0, &offset));
624   if (offset > ts_obj_length) {
625     return node::THROW_ERR_BUFFER_OUT_OF_BOUNDS(
626         env, "\"offset\" is outside of buffer bounds");
627   }
628 
629   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], ts_obj_length - offset,
630                                           &max_length));
631 
632   max_length = MIN(ts_obj_length - offset, max_length);
633 
634   if (max_length == 0)
635     return args.GetReturnValue().Set(0);
636 
637   uint32_t written = StringBytes::Write(env->isolate(),
638                                         ts_obj_data + offset,
639                                         max_length,
640                                         str,
641                                         encoding,
642                                         nullptr);
643   args.GetReturnValue().Set(written);
644 }
645 
ByteLengthUtf8(const FunctionCallbackInfo<Value> & args)646 void ByteLengthUtf8(const FunctionCallbackInfo<Value> &args) {
647   Environment* env = Environment::GetCurrent(args);
648   CHECK(args[0]->IsString());
649 
650   // Fast case: avoid StringBytes on UTF8 string. Jump to v8.
651   args.GetReturnValue().Set(args[0].As<String>()->Utf8Length(env->isolate()));
652 }
653 
654 // Normalize val to be an integer in the range of [1, -1] since
655 // implementations of memcmp() can vary by platform.
normalizeCompareVal(int val,size_t a_length,size_t b_length)656 static int normalizeCompareVal(int val, size_t a_length, size_t b_length) {
657   if (val == 0) {
658     if (a_length > b_length)
659       return 1;
660     else if (a_length < b_length)
661       return -1;
662   } else {
663     if (val > 0)
664       return 1;
665     else
666       return -1;
667   }
668   return val;
669 }
670 
CompareOffset(const FunctionCallbackInfo<Value> & args)671 void CompareOffset(const FunctionCallbackInfo<Value> &args) {
672   Environment* env = Environment::GetCurrent(args);
673 
674   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
675   THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]);
676   SPREAD_BUFFER_ARG(args[0], ts_obj);
677   SPREAD_BUFFER_ARG(args[1], target);
678 
679   size_t target_start;
680   size_t source_start;
681   size_t source_end;
682   size_t target_end;
683 
684   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &target_start));
685   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], 0, &source_start));
686   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[4], target_length,
687                                           &target_end));
688   THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[5], ts_obj_length,
689                                           &source_end));
690 
691   if (source_start > ts_obj_length)
692     return node::THROW_ERR_INDEX_OUT_OF_RANGE(env);
693   if (target_start > target_length)
694     return node::THROW_ERR_INDEX_OUT_OF_RANGE(env);
695 
696   CHECK_LE(source_start, source_end);
697   CHECK_LE(target_start, target_end);
698 
699   size_t to_cmp = MIN(MIN(source_end - source_start,
700                       target_end - target_start),
701                       ts_obj_length - source_start);
702 
703   int val = normalizeCompareVal(to_cmp > 0 ?
704                                   memcmp(ts_obj_data + source_start,
705                                          target_data + target_start,
706                                          to_cmp) : 0,
707                                 source_end - source_start,
708                                 target_end - target_start);
709 
710   args.GetReturnValue().Set(val);
711 }
712 
Compare(const FunctionCallbackInfo<Value> & args)713 void Compare(const FunctionCallbackInfo<Value> &args) {
714   Environment* env = Environment::GetCurrent(args);
715 
716   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
717   THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]);
718   SPREAD_BUFFER_ARG(args[0], obj_a);
719   SPREAD_BUFFER_ARG(args[1], obj_b);
720 
721   size_t cmp_length = MIN(obj_a_length, obj_b_length);
722 
723   int val = normalizeCompareVal(cmp_length > 0 ?
724                                 memcmp(obj_a_data, obj_b_data, cmp_length) : 0,
725                                 obj_a_length, obj_b_length);
726   args.GetReturnValue().Set(val);
727 }
728 
729 
730 // Computes the offset for starting an indexOf or lastIndexOf search.
731 // Returns either a valid offset in [0...<length - 1>], ie inside the Buffer,
732 // or -1 to signal that there is no possible match.
IndexOfOffset(size_t length,int64_t offset_i64,int64_t needle_length,bool is_forward)733 int64_t IndexOfOffset(size_t length,
734                       int64_t offset_i64,
735                       int64_t needle_length,
736                       bool is_forward) {
737   int64_t length_i64 = static_cast<int64_t>(length);
738   if (offset_i64 < 0) {
739     if (offset_i64 + length_i64 >= 0) {
740       // Negative offsets count backwards from the end of the buffer.
741       return length_i64 + offset_i64;
742     } else if (is_forward || needle_length == 0) {
743       // indexOf from before the start of the buffer: search the whole buffer.
744       return 0;
745     } else {
746       // lastIndexOf from before the start of the buffer: no match.
747       return -1;
748     }
749   } else {
750     if (offset_i64 + needle_length <= length_i64) {
751       // Valid positive offset.
752       return offset_i64;
753     } else if (needle_length == 0) {
754       // Out of buffer bounds, but empty needle: point to end of buffer.
755       return length_i64;
756     } else if (is_forward) {
757       // indexOf from past the end of the buffer: no match.
758       return -1;
759     } else {
760       // lastIndexOf from past the end of the buffer: search the whole buffer.
761       return length_i64 - 1;
762     }
763   }
764 }
765 
IndexOfString(const FunctionCallbackInfo<Value> & args)766 void IndexOfString(const FunctionCallbackInfo<Value>& args) {
767   Environment* env = Environment::GetCurrent(args);
768   Isolate* isolate = env->isolate();
769 
770   CHECK(args[1]->IsString());
771   CHECK(args[2]->IsNumber());
772   CHECK(args[4]->IsBoolean());
773 
774   enum encoding enc = ParseEncoding(isolate, args[3], UTF8);
775 
776   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
777   SPREAD_BUFFER_ARG(args[0], ts_obj);
778 
779   Local<String> needle = args[1].As<String>();
780   int64_t offset_i64 = args[2].As<Integer>()->Value();
781   bool is_forward = args[4]->IsTrue();
782 
783   const char* haystack = ts_obj_data;
784   // Round down to the nearest multiple of 2 in case of UCS2.
785   const size_t haystack_length = (enc == UCS2) ?
786       ts_obj_length &~ 1 : ts_obj_length;  // NOLINT(whitespace/operators)
787 
788   size_t needle_length;
789   if (!StringBytes::Size(isolate, needle, enc).To(&needle_length)) return;
790 
791   int64_t opt_offset = IndexOfOffset(haystack_length,
792                                      offset_i64,
793                                      needle_length,
794                                      is_forward);
795 
796   if (needle_length == 0) {
797     // Match String#indexOf() and String#lastIndexOf() behavior.
798     args.GetReturnValue().Set(static_cast<double>(opt_offset));
799     return;
800   }
801 
802   if (haystack_length == 0) {
803     return args.GetReturnValue().Set(-1);
804   }
805 
806   if (opt_offset <= -1) {
807     return args.GetReturnValue().Set(-1);
808   }
809   size_t offset = static_cast<size_t>(opt_offset);
810   CHECK_LT(offset, haystack_length);
811   if ((is_forward && needle_length + offset > haystack_length) ||
812       needle_length > haystack_length) {
813     return args.GetReturnValue().Set(-1);
814   }
815 
816   size_t result = haystack_length;
817 
818   if (enc == UCS2) {
819     String::Value needle_value(isolate, needle);
820     if (*needle_value == nullptr)
821       return args.GetReturnValue().Set(-1);
822 
823     if (haystack_length < 2 || needle_value.length() < 1) {
824       return args.GetReturnValue().Set(-1);
825     }
826 
827     if (IsBigEndian()) {
828       StringBytes::InlineDecoder decoder;
829       if (decoder.Decode(env, needle, args[3], UCS2).IsNothing()) return;
830       const uint16_t* decoded_string =
831           reinterpret_cast<const uint16_t*>(decoder.out());
832 
833       if (decoded_string == nullptr)
834         return args.GetReturnValue().Set(-1);
835 
836       result = SearchString(reinterpret_cast<const uint16_t*>(haystack),
837                             haystack_length / 2,
838                             decoded_string,
839                             decoder.size() / 2,
840                             offset / 2,
841                             is_forward);
842     } else {
843       result = SearchString(reinterpret_cast<const uint16_t*>(haystack),
844                             haystack_length / 2,
845                             reinterpret_cast<const uint16_t*>(*needle_value),
846                             needle_value.length(),
847                             offset / 2,
848                             is_forward);
849     }
850     result *= 2;
851   } else if (enc == UTF8) {
852     String::Utf8Value needle_value(isolate, 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                           is_forward);
862   } else if (enc == LATIN1) {
863     uint8_t* needle_data = node::UncheckedMalloc<uint8_t>(needle_length);
864     if (needle_data == nullptr) {
865       return args.GetReturnValue().Set(-1);
866     }
867     needle->WriteOneByte(
868         isolate, needle_data, 0, needle_length, String::NO_NULL_TERMINATION);
869 
870     result = SearchString(reinterpret_cast<const uint8_t*>(haystack),
871                           haystack_length,
872                           needle_data,
873                           needle_length,
874                           offset,
875                           is_forward);
876     free(needle_data);
877   }
878 
879   args.GetReturnValue().Set(
880       result == haystack_length ? -1 : static_cast<int>(result));
881 }
882 
IndexOfBuffer(const FunctionCallbackInfo<Value> & args)883 void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) {
884   CHECK(args[1]->IsObject());
885   CHECK(args[2]->IsNumber());
886   CHECK(args[4]->IsBoolean());
887 
888   enum encoding enc = ParseEncoding(args.GetIsolate(),
889                                     args[3],
890                                     UTF8);
891 
892   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
893   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[1]);
894   SPREAD_BUFFER_ARG(args[0], ts_obj);
895   SPREAD_BUFFER_ARG(args[1], buf);
896   int64_t offset_i64 = args[2].As<Integer>()->Value();
897   bool is_forward = args[4]->IsTrue();
898 
899   const char* haystack = ts_obj_data;
900   const size_t haystack_length = ts_obj_length;
901   const char* needle = buf_data;
902   const size_t needle_length = buf_length;
903 
904   int64_t opt_offset = IndexOfOffset(haystack_length,
905                                      offset_i64,
906                                      needle_length,
907                                      is_forward);
908 
909   if (needle_length == 0) {
910     // Match String#indexOf() and String#lastIndexOf() behavior.
911     args.GetReturnValue().Set(static_cast<double>(opt_offset));
912     return;
913   }
914 
915   if (haystack_length == 0) {
916     return args.GetReturnValue().Set(-1);
917   }
918 
919   if (opt_offset <= -1) {
920     return args.GetReturnValue().Set(-1);
921   }
922   size_t offset = static_cast<size_t>(opt_offset);
923   CHECK_LT(offset, haystack_length);
924   if ((is_forward && needle_length + offset > haystack_length) ||
925       needle_length > haystack_length) {
926     return args.GetReturnValue().Set(-1);
927   }
928 
929   size_t result = haystack_length;
930 
931   if (enc == UCS2) {
932     if (haystack_length < 2 || needle_length < 2) {
933       return args.GetReturnValue().Set(-1);
934     }
935     result = SearchString(
936         reinterpret_cast<const uint16_t*>(haystack),
937         haystack_length / 2,
938         reinterpret_cast<const uint16_t*>(needle),
939         needle_length / 2,
940         offset / 2,
941         is_forward);
942     result *= 2;
943   } else {
944     result = SearchString(
945         reinterpret_cast<const uint8_t*>(haystack),
946         haystack_length,
947         reinterpret_cast<const uint8_t*>(needle),
948         needle_length,
949         offset,
950         is_forward);
951   }
952 
953   args.GetReturnValue().Set(
954       result == haystack_length ? -1 : static_cast<int>(result));
955 }
956 
IndexOfNumber(const FunctionCallbackInfo<Value> & args)957 void IndexOfNumber(const FunctionCallbackInfo<Value>& args) {
958   CHECK(args[1]->IsUint32());
959   CHECK(args[2]->IsNumber());
960   CHECK(args[3]->IsBoolean());
961 
962   THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
963   SPREAD_BUFFER_ARG(args[0], ts_obj);
964 
965   uint32_t needle = args[1].As<Uint32>()->Value();
966   int64_t offset_i64 = args[2].As<Integer>()->Value();
967   bool is_forward = args[3]->IsTrue();
968 
969   int64_t opt_offset = IndexOfOffset(ts_obj_length, offset_i64, 1, is_forward);
970   if (opt_offset <= -1 || ts_obj_length == 0) {
971     return args.GetReturnValue().Set(-1);
972   }
973   size_t offset = static_cast<size_t>(opt_offset);
974   CHECK_LT(offset, ts_obj_length);
975 
976   const void* ptr;
977   if (is_forward) {
978     ptr = memchr(ts_obj_data + offset, needle, ts_obj_length - offset);
979   } else {
980     ptr = node::stringsearch::MemrchrFill(ts_obj_data, needle, offset + 1);
981   }
982   const char* ptr_char = static_cast<const char*>(ptr);
983   args.GetReturnValue().Set(ptr ? static_cast<int>(ptr_char - ts_obj_data)
984                                 : -1);
985 }
986 
987 
Swap16(const FunctionCallbackInfo<Value> & args)988 void Swap16(const FunctionCallbackInfo<Value>& args) {
989   Environment* env = Environment::GetCurrent(args);
990   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
991   SPREAD_BUFFER_ARG(args[0], ts_obj);
992   SwapBytes16(ts_obj_data, ts_obj_length);
993   args.GetReturnValue().Set(args[0]);
994 }
995 
996 
Swap32(const FunctionCallbackInfo<Value> & args)997 void Swap32(const FunctionCallbackInfo<Value>& args) {
998   Environment* env = Environment::GetCurrent(args);
999   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
1000   SPREAD_BUFFER_ARG(args[0], ts_obj);
1001   SwapBytes32(ts_obj_data, ts_obj_length);
1002   args.GetReturnValue().Set(args[0]);
1003 }
1004 
1005 
Swap64(const FunctionCallbackInfo<Value> & args)1006 void Swap64(const FunctionCallbackInfo<Value>& args) {
1007   Environment* env = Environment::GetCurrent(args);
1008   THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
1009   SPREAD_BUFFER_ARG(args[0], ts_obj);
1010   SwapBytes64(ts_obj_data, ts_obj_length);
1011   args.GetReturnValue().Set(args[0]);
1012 }
1013 
1014 
1015 // Encode a single string to a UTF-8 Uint8Array (not Buffer).
1016 // Used in TextEncoder.prototype.encode.
EncodeUtf8String(const FunctionCallbackInfo<Value> & args)1017 static void EncodeUtf8String(const FunctionCallbackInfo<Value>& args) {
1018   Environment* env = Environment::GetCurrent(args);
1019   Isolate* isolate = env->isolate();
1020   CHECK_GE(args.Length(), 1);
1021   CHECK(args[0]->IsString());
1022 
1023   Local<String> str = args[0].As<String>();
1024   size_t length = str->Utf8Length(isolate);
1025   char* data = node::UncheckedMalloc(length);
1026   str->WriteUtf8(isolate,
1027                  data,
1028                  -1,  // We are certain that `data` is sufficiently large
1029                  nullptr,
1030                  String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
1031   auto array_buf = ArrayBuffer::New(
1032       isolate, data, length, ArrayBufferCreationMode::kInternalized);
1033   auto array = Uint8Array::New(array_buf, 0, length);
1034   args.GetReturnValue().Set(array);
1035 }
1036 
1037 
1038 // pass Buffer object to load prototype methods
SetupBufferJS(const FunctionCallbackInfo<Value> & args)1039 void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
1040   Environment* env = Environment::GetCurrent(args);
1041 
1042   CHECK(args[0]->IsObject());
1043   Local<Object> proto = args[0].As<Object>();
1044   env->set_buffer_prototype_object(proto);
1045 
1046   env->SetMethodNoSideEffect(proto, "asciiSlice", StringSlice<ASCII>);
1047   env->SetMethodNoSideEffect(proto, "base64Slice", StringSlice<BASE64>);
1048   env->SetMethodNoSideEffect(proto, "latin1Slice", StringSlice<LATIN1>);
1049   env->SetMethodNoSideEffect(proto, "hexSlice", StringSlice<HEX>);
1050   env->SetMethodNoSideEffect(proto, "ucs2Slice", StringSlice<UCS2>);
1051   env->SetMethodNoSideEffect(proto, "utf8Slice", StringSlice<UTF8>);
1052 
1053   env->SetMethod(proto, "asciiWrite", StringWrite<ASCII>);
1054   env->SetMethod(proto, "base64Write", StringWrite<BASE64>);
1055   env->SetMethod(proto, "latin1Write", StringWrite<LATIN1>);
1056   env->SetMethod(proto, "hexWrite", StringWrite<HEX>);
1057   env->SetMethod(proto, "ucs2Write", StringWrite<UCS2>);
1058   env->SetMethod(proto, "utf8Write", StringWrite<UTF8>);
1059 
1060   if (auto zero_fill_field = env->isolate_data()->zero_fill_field()) {
1061     CHECK(args[1]->IsObject());
1062     auto binding_object = args[1].As<Object>();
1063     auto array_buffer = ArrayBuffer::New(env->isolate(),
1064                                          zero_fill_field,
1065                                          sizeof(*zero_fill_field));
1066     auto name = FIXED_ONE_BYTE_STRING(env->isolate(), "zeroFill");
1067     auto value = Uint32Array::New(array_buffer, 0, 1);
1068     CHECK(binding_object->Set(env->context(), name, value).FromJust());
1069   }
1070 }
1071 
1072 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context)1073 void Initialize(Local<Object> target,
1074                 Local<Value> unused,
1075                 Local<Context> context) {
1076   Environment* env = Environment::GetCurrent(context);
1077 
1078   env->SetMethod(target, "setupBufferJS", SetupBufferJS);
1079   env->SetMethodNoSideEffect(target, "createFromString", CreateFromString);
1080 
1081   env->SetMethodNoSideEffect(target, "byteLengthUtf8", ByteLengthUtf8);
1082   env->SetMethod(target, "copy", Copy);
1083   env->SetMethodNoSideEffect(target, "compare", Compare);
1084   env->SetMethodNoSideEffect(target, "compareOffset", CompareOffset);
1085   env->SetMethod(target, "fill", Fill);
1086   env->SetMethodNoSideEffect(target, "indexOfBuffer", IndexOfBuffer);
1087   env->SetMethodNoSideEffect(target, "indexOfNumber", IndexOfNumber);
1088   env->SetMethodNoSideEffect(target, "indexOfString", IndexOfString);
1089 
1090   env->SetMethod(target, "swap16", Swap16);
1091   env->SetMethod(target, "swap32", Swap32);
1092   env->SetMethod(target, "swap64", Swap64);
1093 
1094   env->SetMethodNoSideEffect(target, "encodeUtf8String", EncodeUtf8String);
1095 
1096   target->Set(env->context(),
1097               FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"),
1098               Integer::NewFromUnsigned(env->isolate(), kMaxLength)).FromJust();
1099 
1100   target->Set(env->context(),
1101               FIXED_ONE_BYTE_STRING(env->isolate(), "kStringMaxLength"),
1102               Integer::New(env->isolate(), String::kMaxLength)).FromJust();
1103 }
1104 
1105 }  // anonymous namespace
1106 }  // namespace Buffer
1107 }  // namespace node
1108 
1109 NODE_BUILTIN_MODULE_CONTEXT_AWARE(buffer, node::Buffer::Initialize)
1110