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