v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-typed-array.cc
Go to the documentation of this file.
1// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/base/logging.h"
14#include "src/objects/simd.h"
15#include "third_party/simdutf/simdutf.h"
16
17namespace v8 {
18namespace internal {
19
20// -----------------------------------------------------------------------------
21// ES6 section 22.2 TypedArray Objects
22
23// ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer
24BUILTIN(TypedArrayPrototypeBuffer) {
25 HandleScope scope(isolate);
26 CHECK_RECEIVER(JSTypedArray, typed_array,
27 "get %TypedArray%.prototype.buffer");
28 return *typed_array->GetBuffer();
29}
30
31namespace {
32
33int64_t CapRelativeIndex(double relative, int64_t minimum, int64_t maximum) {
34 DCHECK(!std::isnan(relative));
35 return static_cast<int64_t>(
36 relative < 0 ? std::max<double>(relative + maximum, minimum)
37 : std::min<double>(relative, maximum));
38}
39
40} // namespace
41
42BUILTIN(TypedArrayPrototypeCopyWithin) {
43 HandleScope scope(isolate);
44
46 const char* method_name = "%TypedArray%.prototype.copyWithin";
48 isolate, array,
49 JSTypedArray::Validate(isolate, args.receiver(), method_name));
50
51 int64_t len = array->GetLength();
52 int64_t to = 0;
53 int64_t from = 0;
54 int64_t final = len;
55
56 if (V8_LIKELY(args.length() > 1)) {
57 double num;
59 isolate, num, Object::IntegerValue(isolate, args.at<Object>(1)));
60 to = CapRelativeIndex(num, 0, len);
61
62 if (args.length() > 2) {
64 isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
65 from = CapRelativeIndex(num, 0, len);
66
67 DirectHandle<Object> end = args.atOrUndefined(isolate, 3);
68 if (!IsUndefined(*end, isolate)) {
70 isolate, num, Object::IntegerValue(isolate, end));
71 final = CapRelativeIndex(num, 0, len);
72 }
73 }
74 }
75
76 int64_t count = std::min<int64_t>(final - from, len - to);
77 if (count <= 0) return *array;
78
79 // TypedArray buffer may have been transferred/detached during parameter
80 // processing above.
81 if (V8_UNLIKELY(array->WasDetached())) {
83 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
84 isolate->factory()->NewStringFromAsciiChecked(
85 method_name)));
86 }
87
88 if (V8_UNLIKELY(array->is_backed_by_rab())) {
89 bool out_of_bounds = false;
90 int64_t new_len = array->GetLengthOrOutOfBounds(out_of_bounds);
91 if (out_of_bounds) {
92 const MessageTemplate message = MessageTemplate::kDetachedOperation;
93 DirectHandle<String> operation =
94 isolate->factory()->NewStringFromAsciiChecked(method_name);
95 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewTypeError(message, operation));
96 }
97 if (new_len < len) {
98 // We don't need to account for growing, since we only copy an already
99 // determined number of elements and growing won't change it. If to >
100 // new_len or from > new_len, the count below will be < 0, so we don't
101 // need to check them separately.
102 if (final > new_len) {
103 final = new_len;
104 }
105 count = std::min<int64_t>(final - from, new_len - to);
106 if (count <= 0) {
107 return *array;
108 }
109 }
110 }
111
112 // Ensure processed indexes are within array bounds
113 DCHECK_GE(from, 0);
114 DCHECK_LT(from, len);
115 DCHECK_GE(to, 0);
116 DCHECK_LT(to, len);
117 DCHECK_GE(len - count, 0);
118
119 size_t element_size = array->element_size();
120 to = to * element_size;
121 from = from * element_size;
122 count = count * element_size;
123
124 uint8_t* data = static_cast<uint8_t*>(array->DataPtr());
125 if (array->buffer()->is_shared()) {
126 base::Relaxed_Memmove(reinterpret_cast<base::Atomic8*>(data + to),
127 reinterpret_cast<base::Atomic8*>(data + from), count);
128 } else {
129 std::memmove(data + to, data + from, count);
130 }
131
132 return *array;
133}
134
135// ES#sec-%typedarray%.prototype.fill
136BUILTIN(TypedArrayPrototypeFill) {
137 HandleScope scope(isolate);
138
139 // 1. Let O be the this value.
140 // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).
142 const char* method_name = "%TypedArray%.prototype.fill";
144 isolate, array,
145 JSTypedArray::Validate(isolate, args.receiver(), method_name));
146 ElementsKind kind = array->GetElementsKind();
147
148 // 3. Let len be TypedArrayLength(taRecord).
149 int64_t len = array->GetLength();
150
151 DirectHandle<Object> obj_value = args.atOrUndefined(isolate, 1);
153 // 4. If O.[[ContentType]] is bigint, set value to ? ToBigInt(value).
154 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
155 BigInt::FromObject(isolate, obj_value));
156 } else {
157 // 5. Otherwise, set value to ? ToNumber(value).
158 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
159 Object::ToNumber(isolate, obj_value));
160 }
161
162 int64_t start = 0;
163 int64_t end = len;
164
165 if (args.length() > 2) {
166 // 6. Let relativeStart be ? ToIntegerOrInfinity(start).
167 DirectHandle<Object> num = args.atOrUndefined(isolate, 2);
168 double double_num;
170 isolate, double_num, Object::IntegerValue(isolate, num));
171
172 // 7. If relativeStart = -∞, let startIndex be 0.
173 // 8. Else if relativeStart < 0, let startIndex be max(len + relativeStart,
174 // 0).
175 // 9. Else, let startIndex be min(relativeStart, len).
176 start = CapRelativeIndex(double_num, 0, len);
177
178 // 10. If end is undefined, let relativeEnd be len; else let relativeEnd be
179 // ? ToIntegerOrInfinity(end).
180 num = args.atOrUndefined(isolate, 3);
181 if (!IsUndefined(*num, isolate)) {
182 // 11. If relativeEnd = -∞, let endIndex be 0.
183 // 12. Else if relativeEnd < 0, let endIndex be max(len + relativeEnd, 0).
184 // 13. Else, let endIndex be min(relativeEnd, len).
186 isolate, double_num, Object::IntegerValue(isolate, num));
187 end = CapRelativeIndex(double_num, 0, len);
188 }
189 }
190
191 // 14. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).
192 // 15. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError
193 // exception.
194 if (V8_UNLIKELY(array->WasDetached())) {
196 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
197 isolate->factory()->NewStringFromAsciiChecked(
198 method_name)));
199 }
200
201 if (V8_UNLIKELY(array->IsVariableLength())) {
202 if (array->IsOutOfBounds()) {
203 const MessageTemplate message = MessageTemplate::kDetachedOperation;
204 DirectHandle<String> operation =
205 isolate->factory()->NewStringFromAsciiChecked(method_name);
206 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewTypeError(message, operation));
207 }
208 // 16. Set len to TypedArrayLength(taRecord).
209 // 17. Set endIndex to min(endIndex, len).
210 end = std::min(end, static_cast<int64_t>(array->GetLength()));
211 }
212
213 int64_t count = end - start;
214 if (count <= 0) return *array;
215
216 // Ensure processed indexes are within array bounds
217 DCHECK_GE(start, 0);
218 DCHECK_LT(start, len);
219 DCHECK_GE(end, 0);
220 DCHECK_LE(end, len);
221 DCHECK_LE(count, len);
222
223 // 19. Repeat, while k < endIndex,
224 // a. Let Pk be ! ToString(𝔽(k)).
225 // b. Perform ! Set(O, Pk, value, true).
226 // c. Set k to k + 1.
227 // 20. Return O.
229 array, obj_value, start, end));
230}
231
232BUILTIN(TypedArrayPrototypeIncludes) {
233 HandleScope scope(isolate);
234
236 const char* method_name = "%TypedArray%.prototype.includes";
238 isolate, array,
239 JSTypedArray::Validate(isolate, args.receiver(), method_name));
240
241 if (args.length() < 2) return ReadOnlyRoots(isolate).false_value();
242
243 int64_t len = array->GetLength();
244 if (len == 0) return ReadOnlyRoots(isolate).false_value();
245
246 int64_t index = 0;
247 if (args.length() > 2) {
248 double num;
250 isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
251 index = CapRelativeIndex(num, 0, len);
252 }
253
254 DirectHandle<Object> search_element = args.atOrUndefined(isolate, 1);
255 ElementsAccessor* elements = array->GetElementsAccessor();
257 elements->IncludesValue(isolate, array, search_element, index, len);
258 MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
259 return *isolate->factory()->ToBoolean(result.FromJust());
260}
261
262BUILTIN(TypedArrayPrototypeIndexOf) {
263 HandleScope scope(isolate);
264
266 const char* method_name = "%TypedArray%.prototype.indexOf";
268 isolate, array,
269 JSTypedArray::Validate(isolate, args.receiver(), method_name));
270
271 int64_t len = array->GetLength();
272 if (len == 0) return Smi::FromInt(-1);
273
274 int64_t index = 0;
275 if (args.length() > 2) {
276 double num;
278 isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
279 index = CapRelativeIndex(num, 0, len);
280 }
281
282 if (V8_UNLIKELY(array->WasDetached())) return Smi::FromInt(-1);
283
284 if (V8_UNLIKELY(array->IsVariableLength() && array->IsOutOfBounds())) {
285 return Smi::FromInt(-1);
286 }
287
288 DirectHandle<Object> search_element = args.atOrUndefined(isolate, 1);
289 ElementsAccessor* elements = array->GetElementsAccessor();
291 elements->IndexOfValue(isolate, array, search_element, index, len);
292 MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
293 return *isolate->factory()->NewNumberFromInt64(result.FromJust());
294}
295
296BUILTIN(TypedArrayPrototypeLastIndexOf) {
297 HandleScope scope(isolate);
298
300 const char* method_name = "%TypedArray%.prototype.lastIndexOf";
302 isolate, array,
303 JSTypedArray::Validate(isolate, args.receiver(), method_name));
304
305 int64_t len = array->GetLength();
306 if (len == 0) return Smi::FromInt(-1);
307
308 int64_t index = len - 1;
309 if (args.length() > 2) {
310 double num;
312 isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
313 // Set a negative value (-1) for returning -1 if num is negative and
314 // len + num is still negative. Upper bound is len - 1.
315 index = std::min<int64_t>(CapRelativeIndex(num, -1, len), len - 1);
316 }
317
318 if (index < 0) return Smi::FromInt(-1);
319
320 if (V8_UNLIKELY(array->WasDetached())) return Smi::FromInt(-1);
321 if (V8_UNLIKELY(array->IsVariableLength() && array->IsOutOfBounds())) {
322 return Smi::FromInt(-1);
323 }
324
325 DirectHandle<Object> search_element = args.atOrUndefined(isolate, 1);
326 ElementsAccessor* elements = array->GetElementsAccessor();
328 elements->LastIndexOfValue(array, search_element, index);
329 MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
330 return *isolate->factory()->NewNumberFromInt64(result.FromJust());
331}
332
333BUILTIN(TypedArrayPrototypeReverse) {
334 HandleScope scope(isolate);
335
337 const char* method_name = "%TypedArray%.prototype.reverse";
339 isolate, array,
340 JSTypedArray::Validate(isolate, args.receiver(), method_name));
341
342 ElementsAccessor* elements = array->GetElementsAccessor();
343 elements->Reverse(*array);
344 return *array;
345}
346
347namespace {
348
349std::vector<std::tuple<const char*, size_t, simdutf::base64_options>>
350SimdutfBase64OptionsVector() {
351 return {{"base64", 6, simdutf::base64_options::base64_default},
352 {"base64url", 9, simdutf::base64_options::base64_url}};
353}
354
355template <typename T>
356Maybe<T> MapOptionToEnum(
357 Isolate* isolate, DirectHandle<String> option_string,
358 const std::vector<std::tuple<const char*, size_t, T>>& allowed_options) {
359 option_string = String::Flatten(isolate, option_string);
360
361 {
363 String::FlatContent option_content = option_string->GetFlatContent(no_gc);
364
365 if (option_content.IsOneByte()) {
366 const unsigned char* option_string_to_compare =
367 option_content.ToOneByteVector().data();
368 size_t length = option_content.ToOneByteVector().size();
369
370 for (auto& [str_val, str_size, enum_val] : allowed_options) {
371 if (str_size == length &&
372 CompareCharsEqual(option_string_to_compare, str_val, str_size)) {
373 return Just<T>(enum_val);
374 }
375 }
376 } else {
377 const base::uc16* option_string_to_compare =
378 option_content.ToUC16Vector().data();
379 size_t length = option_content.ToUC16Vector().size();
380
381 for (auto& [str_val, str_size, enum_val] : allowed_options) {
382 if (str_size == length &&
383 CompareCharsEqual(option_string_to_compare, str_val, str_size)) {
384 return Just<T>(enum_val);
385 }
386 }
387 }
388 }
389
390 isolate->Throw(*isolate->factory()->NewTypeError(
391 MessageTemplate::kInvalidOption, option_string));
392 return Nothing<T>();
393}
394
395MessageTemplate ToMessageTemplate(simdutf::error_code error) {
396 switch (error) {
397 case simdutf::error_code::INVALID_BASE64_CHARACTER:
398 return MessageTemplate::kInvalidBase64Character;
399 case simdutf::error_code::BASE64_INPUT_REMAINDER:
400 return MessageTemplate::kBase64InputRemainder;
401 case simdutf::error_code::BASE64_EXTRA_BITS:
402 return MessageTemplate::kBase64ExtraBits;
403 default:
404 UNREACHABLE();
405 }
406}
407
408template <typename T>
409Maybe<simdutf::result> ArrayBufferFromBase64(
410 Isolate* isolate, T input_vector, size_t input_length,
411 size_t& output_length, simdutf::base64_options alphabet,
412 simdutf::last_chunk_handling_options last_chunk_handling,
413 DirectHandle<JSArrayBuffer>& buffer) {
414 const char method_name[] = "Uint8Array.fromBase64";
415
416 output_length = simdutf::maximal_binary_length_from_base64(
417 reinterpret_cast<const T>(input_vector), input_length);
418 std::unique_ptr<char[]> output = std::make_unique<char[]>(output_length);
419 simdutf::result simd_result = simdutf::base64_to_binary_safe(
420 reinterpret_cast<const T>(input_vector), input_length, output.get(),
421 output_length, alphabet, last_chunk_handling);
422
423 {
425 MaybeDirectHandle<JSArrayBuffer> result_buffer =
426 isolate->factory()->NewJSArrayBufferAndBackingStore(
427 output_length, InitializedFlag::kUninitialized);
428 if (!result_buffer.ToHandle(&buffer)) {
429 isolate->Throw(*isolate->factory()->NewRangeError(
430 MessageTemplate::kOutOfMemory,
431 isolate->factory()->NewStringFromAsciiChecked(method_name)));
433 }
434
435 memcpy(buffer->backing_store(), output.get(), output_length);
436 }
437 return Just<simdutf::result>(simd_result);
438}
439
440} // namespace
441
442// https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.frombase64
443BUILTIN(Uint8ArrayFromBase64) {
444 HandleScope scope(isolate);
445
446 // 1. If string is not a String, throw a TypeError exception.
447 DirectHandle<Object> input = args.atOrUndefined(isolate, 1);
448 if (!IsString(*input)) {
450 isolate, NewTypeError(MessageTemplate::kArgumentIsNonString,
451 isolate->factory()->input_string()));
452 }
453
454 DirectHandle<String> input_string =
455 String::Flatten(isolate, Cast<String>(input));
456
457 // 2. Let opts be ? GetOptionsObject(options).
458 DirectHandle<Object> options = args.atOrUndefined(isolate, 2);
459
460 // 3. Let alphabet be ? Get(opts, "alphabet").
461 DirectHandle<Object> opt_alphabet;
463 isolate, opt_alphabet,
465 options, isolate->factory()->alphabet_string(), isolate));
466
467 // 4. If alphabet is undefined, set alphabet to "base64".
468 simdutf::base64_options alphabet;
469 if (IsUndefined(*opt_alphabet)) {
470 alphabet = simdutf::base64_default;
471 } else if (!IsString(*opt_alphabet)) {
473 isolate, NewTypeError(MessageTemplate::kInvalidOption, opt_alphabet));
474 } else {
475 // 5. If alphabet is neither "base64" nor "base64url", throw a TypeError
476 // exception.
477 DirectHandle<String> alphabet_string = Cast<String>(opt_alphabet);
478
480 isolate, alphabet,
481 MapOptionToEnum(isolate, alphabet_string,
482 SimdutfBase64OptionsVector()));
483 }
484
485 // 6. Let lastChunkHandling be ? Get(opts, "lastChunkHandling").
486 DirectHandle<Object> opt_last_chunk_handling;
488 isolate, opt_last_chunk_handling,
490 options, isolate->factory()->last_chunk_handling_string(), isolate));
491
492 // 7. If lastChunkHandling is undefined, set lastChunkHandling to "loose".
493 simdutf::last_chunk_handling_options last_chunk_handling;
494 if (IsUndefined(*opt_last_chunk_handling)) {
495 last_chunk_handling = simdutf::last_chunk_handling_options::loose;
496 } else if (!IsString(*opt_last_chunk_handling)) {
498 isolate,
499 NewTypeError(MessageTemplate::kInvalidOption, opt_last_chunk_handling));
500 } else {
501 // 8. If lastChunkHandling is not one of "loose", "strict", or
502 // "stop-before-partial", throw a TypeError exception.
503 DirectHandle<String> last_chunk_handling_string =
504 Cast<String>(opt_last_chunk_handling);
505
506 std::vector<
507 std::tuple<const char*, size_t, simdutf::last_chunk_handling_options>>
508 last_chunk_handling_vector = {
509 {"loose", 5, simdutf::last_chunk_handling_options::loose},
510 {"strict", 6, simdutf::last_chunk_handling_options::strict},
511 {"stop-before-partial", 19,
512 simdutf::last_chunk_handling_options::stop_before_partial}};
514 isolate, last_chunk_handling,
515 MapOptionToEnum(isolate, last_chunk_handling_string,
516 last_chunk_handling_vector));
517 }
518
519 // 9. Let result be ? FromBase64(string, alphabet, lastChunkHandling).
520 size_t input_length;
521 size_t output_length;
522 simdutf::result simd_result;
524 {
526 String::FlatContent input_content = input_string->GetFlatContent(no_gc);
527 if (input_content.IsOneByte()) {
528 const unsigned char* input_vector =
529 input_content.ToOneByteVector().data();
530 input_length = input_content.ToOneByteVector().size();
532 isolate, simd_result,
533 ArrayBufferFromBase64(isolate,
534 reinterpret_cast<const char*>(input_vector),
535 input_length, output_length, alphabet,
536 last_chunk_handling, buffer));
537 } else {
538 const base::uc16* input_vector = input_content.ToUC16Vector().data();
539 input_length = input_content.ToUC16Vector().size();
541 isolate, simd_result,
542 ArrayBufferFromBase64(isolate,
543 reinterpret_cast<const char16_t*>(input_vector),
544 input_length, output_length, alphabet,
545 last_chunk_handling, buffer));
546 }
547 }
548
549 // 10. If result.[[Error]] is not none, then
550 // a. Throw result.[[Error]].
551 if (simd_result.error != simdutf::error_code::SUCCESS) {
553 isolate, NewSyntaxError(ToMessageTemplate(simd_result.error)));
554 }
555
556 // 11. Let resultLength be the length of result.[[Bytes]].
557 // 12. Let ta be ? AllocateTypedArray("Uint8Array", %Uint8Array%,
558 // "%Uint8Array.prototype%", resultLength).
559 // 13. Set the value at each index of
560 // ta.[[ViewedArrayBuffer]].[[ArrayBufferData]] to the value at the
561 // corresponding index of result.[[Bytes]].
562 // 14. Return ta.
563 DirectHandle<JSTypedArray> result_typed_array =
564 isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0,
565 output_length);
566 return *result_typed_array;
567}
568
569// https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64
570BUILTIN(Uint8ArrayPrototypeToBase64) {
571 HandleScope scope(isolate);
572 const char method_name[] = "Uint8Array.prototype.toBase64";
573
574 // 1. Let O be the this value.
575 // 2. Perform ? ValidateUint8Array(O).
576 CHECK_RECEIVER(JSTypedArray, uint8array, method_name);
577 if (uint8array->GetElementsKind() != ElementsKind::UINT8_ELEMENTS) {
579 isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
580 isolate->factory()->NewStringFromAsciiChecked(
581 method_name)));
582 }
583
584 // 3. Let opts be ? GetOptionsObject(options).
585 DirectHandle<Object> options = args.atOrUndefined(isolate, 1);
586
587 // 4. Let alphabet be ? Get(opts, "alphabet").
588 DirectHandle<Object> opt_alphabet;
590 isolate, opt_alphabet,
592 options, isolate->factory()->alphabet_string(), isolate));
593
594 // 5. If alphabet is undefined, set alphabet to "base64".
595 simdutf::base64_options alphabet;
596 if (IsUndefined(*opt_alphabet)) {
597 alphabet = simdutf::base64_options::base64_default;
598 } else if (!IsString(*opt_alphabet)) {
600 isolate, NewTypeError(MessageTemplate::kInvalidOption, opt_alphabet));
601 } else {
602 // 6. If alphabet is neither "base64" nor "base64url", throw a TypeError
603 // exception.
604 DirectHandle<String> alphabet_string = Cast<String>(opt_alphabet);
605
607 isolate, alphabet,
608 MapOptionToEnum(isolate, alphabet_string,
609 SimdutfBase64OptionsVector()));
610 }
611
612 // 7. Let omitPadding be ToBoolean(? Get(opts, "omitPadding")).
613 DirectHandle<Object> omit_padding_object;
614 bool omit_padding = false;
616 isolate, omit_padding_object,
618 options, isolate->factory()->NewStringFromAsciiChecked("omitPadding"),
619 isolate));
620
621 if (Object::BooleanValue(*omit_padding_object, isolate)) {
622 omit_padding = true;
623 }
624
625 bool out_of_bounds = false;
626 size_t length = uint8array->GetLengthOrOutOfBounds(out_of_bounds);
627
628 if (out_of_bounds || uint8array->WasDetached()) {
630 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
631 isolate->factory()->NewStringFromAsciiChecked(
632 method_name)));
633 }
634
635 if (alphabet == simdutf::base64_options::base64_default &&
636 omit_padding == true) {
637 alphabet = simdutf::base64_options::base64_default_no_padding;
638 } else if (alphabet == simdutf::base64_options::base64_url &&
639 omit_padding == false) {
640 alphabet = simdutf::base64_options::base64_url_with_padding;
641 }
642
643 size_t output_length = simdutf::base64_length_from_binary(length, alphabet);
644
645 if (output_length > String::kMaxLength) {
647 isolate, NewTypeError(MessageTemplate::kInvalidStringLength));
648 }
649
650 if (output_length == 0) {
651 return *isolate->factory()->empty_string();
652 }
653
656 isolate, output,
657 isolate->factory()->NewRawOneByteString(static_cast<int>(output_length)));
658 {
660 // 8. Let toEncode be ? GetUint8ArrayBytes(O).
661 // 9. If alphabet is "base64", then
662 // a. Let outAscii be the sequence of code points which results from
663 // encoding toEncode according to the base64 encoding specified in
664 // section 4 of RFC 4648. Padding is included if and only if omitPadding
665 // is false.
666 // 10. Else,
667 // a. Assert: alphabet is "base64url".
668 // b. Let outAscii be the sequence of code points which results from
669 // encoding toEncode according to the base64url encoding specified in
670 // section 5 of RFC 4648. Padding is included if and only if omitPadding
671 // is false.
672 // 11. Return CodePointsToString(outAscii).
673
674 // TODO(rezvan): Make sure to add a path for SharedArrayBuffers when
675 // simdutf library got updated. Also, add a test for it.
676 size_t simd_result_size = simdutf::binary_to_base64(
677 std::bit_cast<const char*>(uint8array->GetBuffer()->backing_store()),
678 length, reinterpret_cast<char*>(output->GetChars(no_gc)), alphabet);
679 DCHECK_EQ(simd_result_size, output_length);
680 USE(simd_result_size);
681 }
682
683 // output_length is the correct length of the output, with padding or
684 // without padding, so we do not need to modify the output based on
685 // padding here.
686 return *output;
687}
688
689// https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.fromhex
690BUILTIN(Uint8ArrayFromHex) {
691 HandleScope scope(isolate);
692 const char method_name[] = "Uint8Array.fromHex";
693
694 // 1. If string is not a String, throw a TypeError exception.
695 DirectHandle<Object> input = args.atOrUndefined(isolate, 1);
696 if (!IsString(*input)) {
698 isolate, NewTypeError(MessageTemplate::kArgumentIsNonString,
699 isolate->factory()->input_string()));
700 }
701
702 DirectHandle<String> input_string =
703 String::Flatten(isolate, Cast<String>(input));
704
705 // 2. Let result be FromHex(string).
706 // 3. If result.[[Error]] is not none, then
707 // a. Throw result.[[Error]].
708 // 4. Let resultLength be the length of result.[[Bytes]].
710
711 size_t input_length = input_string->length();
712 if (input_length % 2 != 0) {
714 isolate, NewSyntaxError(MessageTemplate::kInvalidHexString));
715 }
716
717 size_t output_length = (input_length / 2);
718
720 isolate->factory()->NewJSArrayBufferAndBackingStore(
721 output_length, InitializedFlag::kUninitialized);
722
723 if (!result_buffer.ToHandle(&buffer)) {
725 isolate, NewRangeError(MessageTemplate::kOutOfMemory,
726 isolate->factory()->NewStringFromAsciiChecked(
727 method_name)));
728 }
729
730 bool result;
731 {
733 String::FlatContent input_content = input_string->GetFlatContent(no_gc);
734
735 if (input_content.IsOneByte()) {
736 base::Vector<const uint8_t> input_vector =
737 input_content.ToOneByteVector();
738 result = ArrayBufferFromHex(input_vector, buffer, output_length);
739 } else {
740 base::Vector<const base::uc16> input_vector =
741 input_content.ToUC16Vector();
742 result = ArrayBufferFromHex(input_vector, buffer, output_length);
743 }
744 }
745
746 if (!result) {
748 isolate, NewSyntaxError(MessageTemplate::kInvalidHexString));
749 }
750 // 5. Let ta be ? AllocateTypedArray("Uint8Array", %Uint8Array%,
751 // "%Uint8Array.prototype%", resultLength).
752 // 6. Set the value at each index of
753 // ta.[[ViewedArrayBuffer]].[[ArrayBufferData]] to the value at the
754 // corresponding index of result.[[Bytes]].
755 // 7. Return ta.
756 DirectHandle<JSTypedArray> result_typed_array =
757 isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0,
758 output_length);
759 return *result_typed_array;
760}
761
762// https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tohex
763BUILTIN(Uint8ArrayPrototypeToHex) {
764 HandleScope scope(isolate);
765 const char method_name[] = "Uint8Array.prototype.toHex";
766
767 // 1. Let O be the this value.
768 // 2. Perform ? ValidateUint8Array(O).
769 CHECK_RECEIVER(JSTypedArray, uint8array, method_name);
770 if (uint8array->GetElementsKind() != ElementsKind::UINT8_ELEMENTS) {
772 isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
773 isolate->factory()->NewStringFromAsciiChecked(
774 method_name)));
775 }
776
777 // 3. Let toEncode be ? GetUint8ArrayBytes(O).
778 bool out_of_bounds = false;
779 size_t length = uint8array->GetLengthOrOutOfBounds(out_of_bounds);
780
781 if (out_of_bounds || uint8array->WasDetached()) {
783 isolate, NewTypeError(MessageTemplate::kDetachedOperation,
784 isolate->factory()->NewStringFromAsciiChecked(
785 method_name)));
786 }
787
788 if (length > String::kMaxLength / 2) {
790 isolate, NewTypeError(MessageTemplate::kInvalidStringLength));
791 }
792
793 if (length == 0) {
794 return *isolate->factory()->empty_string();
795 }
796
797 const char* bytes =
798 std::bit_cast<const char*>(uint8array->GetBuffer()->backing_store());
799
800 // 4. Let out be the empty String.
803 isolate, output,
804 isolate->factory()->NewRawOneByteString(static_cast<int>(length) * 2));
805 // 5. For each byte byte of toEncode, do
806 // a. Let hex be Number::toString(𝔽(byte), 16).
807 // b. Set hex to StringPad(hex, 2, "0", start).
808 // c. Set out to the string-concatenation of out and hex.
809 // 6. Return out.
810 return Uint8ArrayToHex(bytes, length, output);
811}
812
813} // namespace internal
814} // namespace v8
#define CHECK_RECEIVER(Type, name, method)
#define BUILTIN(name)
Builtins::Kind kind
Definition builtins.cc:40
constexpr size_t size() const
Definition vector.h:70
constexpr T * data() const
Definition vector.h:100
static ElementsAccessor * ForKind(ElementsKind elements_kind)
Definition elements.h:29
virtual Maybe< bool > IncludesValue(Isolate *isolate, DirectHandle< JSObject > receiver, DirectHandle< Object > value, size_t start, size_t length)=0
virtual Maybe< int64_t > LastIndexOfValue(DirectHandle< JSObject > receiver, DirectHandle< Object > value, size_t start)=0
virtual void Reverse(Tagged< JSObject > receiver)=0
virtual Maybe< int64_t > IndexOfValue(Isolate *isolate, DirectHandle< JSObject > receiver, DirectHandle< Object > value, size_t start, size_t length)=0
static MaybeDirectHandle< Object > ReadFromOptionsBag(DirectHandle< Object > options, DirectHandle< String > option_name, Isolate *isolate)
static MaybeDirectHandle< JSTypedArray > Validate(Isolate *isolate, DirectHandle< Object > receiver, const char *method_name)
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(DirectHandle< S > *out) const
static V8_WARN_UNUSED_RESULT HandleType< Number >::MaybeType ToNumber(Isolate *isolate, HandleType< T > input)
static V8_EXPORT_PRIVATE bool BooleanValue(Tagged< Object > obj, IsolateT *isolate)
static V8_WARN_UNUSED_RESULT Maybe< double > IntegerValue(Isolate *isolate, HandleType< T > input)
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
base::Vector< const uint8_t > ToOneByteVector() const
Definition string.h:139
base::Vector< const base::uc16 > ToUC16Vector() const
Definition string.h:145
static const uint32_t kMaxLength
Definition string.h:511
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
int start
int end
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:284
#define THROW_NEW_ERROR_RETURN_FAILURE(isolate, call)
Definition isolate.h:294
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
#define RETURN_RESULT_OR_FAILURE(isolate, call)
Definition isolate.h:264
#define MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:448
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
ZoneVector< RpoNumber > & result
STL namespace.
void Relaxed_Memmove(volatile Atomic8 *dst, volatile const Atomic8 *src, size_t bytes)
Definition atomicops.h:388
char Atomic8
Definition atomicops.h:57
uint16_t uc16
Definition strings.h:18
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
PerThreadAssertScopeDebugOnly< true, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > AllowGarbageCollection
bool CompareCharsEqual(const lchar *lhs, const rchar *rhs, size_t chars)
Definition utils.h:509
bool ArrayBufferFromHex(base::Vector< T > &input_vector, DirectHandle< JSArrayBuffer > buffer, size_t output_length)
Definition simd.cc:1035
bool IsBigIntTypedArrayElementsKind(ElementsKind kind)
@ kExternalUint8Array
Definition globals.h:2454
Tagged< Object > Uint8ArrayToHex(const char *bytes, size_t length, DirectHandle< SeqOneByteString > string_output)
Definition simd.cc:599
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Maybe< T > Nothing()
Definition v8-maybe.h:112
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
#define V8_LIKELY(condition)
Definition v8config.h:661
#define V8_UNLIKELY(condition)
Definition v8config.h:660