#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
#include "src/common/message-template.h"
#include "src/logging/counters.h"
#include "src/objects/elements-kind.h"
#include "src/objects/elements.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/option-utils.h"
#include "src/objects/simd.h"
#include "third_party/simdutf/simdutf.h"
namespace v8::internal {
BUILTIN(TypedArrayPrototypeBuffer) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSTypedArray, typed_array,
"get %TypedArray%.prototype.buffer");
return *typed_array->GetBuffer(isolate);
}
namespace {
int64_t CapRelativeIndex(double relative, int64_t minimum, int64_t maximum) {
DCHECK(!std::isnan(relative));
return static_cast<int64_t>(
relative < 0 ? std::max<double>(relative + maximum, minimum)
: std::min<double>(relative, maximum));
}
}
BUILTIN(TypedArrayPrototypeCopyWithin) {
HandleScope scope(isolate);
DirectHandle<JSTypedArray> array;
const char* method_name = "%TypedArray%.prototype.copyWithin";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array,
JSTypedArray::Validate(isolate, args.receiver(), method_name));
int64_t len = array->GetLength();
int64_t to = 0;
int64_t from = 0;
int64_t final = len;
if (V8_LIKELY(args.length() > 1)) {
double num;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, num, Object::IntegerValue(isolate, args.at<Object>(1)));
to = CapRelativeIndex(num, 0, len);
if (args.length() > 2) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
from = CapRelativeIndex(num, 0, len);
DirectHandle<Object> end = args.atOrUndefined(isolate, 3);
if (!IsUndefined(*end, isolate)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
Object::IntegerValue(isolate, end));
final = CapRelativeIndex(num, 0, len);
}
}
}
int64_t count = std::min<int64_t>(final - from, len - to);
if (count <= 0) return *array;
if (V8_UNLIKELY(array->WasDetached())) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
if (V8_UNLIKELY(array->is_backed_by_rab())) {
bool out_of_bounds = false;
int64_t new_len = array->GetLengthOrOutOfBounds(out_of_bounds);
if (out_of_bounds) {
const MessageTemplate message = MessageTemplate::kDetachedOperation;
DirectHandle<String> operation =
isolate->factory()->NewStringFromAsciiChecked(method_name);
THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewTypeError(message, operation));
}
if (new_len < len) {
if (final > new_len) {
final = new_len;
}
count = std::min<int64_t>(final - from, new_len - to);
if (count <= 0) {
return *array;
}
}
}
DCHECK_GE(from, 0);
DCHECK_LT(from, len);
DCHECK_GE(to, 0);
DCHECK_LT(to, len);
DCHECK_GE(len - count, 0);
size_t element_size = array->element_size();
SBXCHECK_LE(element_size * len, TypedArray::kMaxByteLength);
to = to * element_size;
from = from * element_size;
count = count * element_size;
uint8_t* data = static_cast<uint8_t*>(array->DataPtr());
if (array->buffer()->is_shared()) {
base::Relaxed_Memmove(reinterpret_cast<base::Atomic8*>(data + to),
reinterpret_cast<base::Atomic8*>(data + from), count);
} else {
std::memmove(data + to, data + from, count);
}
return *array;
}
BUILTIN(TypedArrayPrototypeFill) {
HandleScope scope(isolate);
DirectHandle<JSTypedArray> array;
const char* method_name = "%TypedArray%.prototype.fill";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array,
JSTypedArray::Validate(isolate, args.receiver(), method_name));
ElementsKind kind = array->GetElementsKind();
int64_t len = array->GetLength();
DirectHandle<Object> obj_value = args.atOrUndefined(isolate, 1);
if (IsBigIntTypedArrayElementsKind(kind)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
BigInt::FromObject(isolate, obj_value));
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
Object::ToNumber(isolate, obj_value));
}
int64_t start = 0;
int64_t end = len;
if (args.length() > 2) {
DirectHandle<Object> num = args.atOrUndefined(isolate, 2);
double double_num;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, double_num,
Object::IntegerValue(isolate, num));
start = CapRelativeIndex(double_num, 0, len);
num = args.atOrUndefined(isolate, 3);
if (!IsUndefined(*num, isolate)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, double_num,
Object::IntegerValue(isolate, num));
end = CapRelativeIndex(double_num, 0, len);
}
}
if (V8_UNLIKELY(array->WasDetached())) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
if (V8_UNLIKELY(array->IsVariableLength())) {
if (array->IsOutOfBounds()) {
const MessageTemplate message = MessageTemplate::kDetachedOperation;
DirectHandle<String> operation =
isolate->factory()->NewStringFromAsciiChecked(method_name);
THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewTypeError(message, operation));
}
end = std::min(end, static_cast<int64_t>(array->GetLength()));
}
int64_t count = end - start;
if (count <= 0) return *array;
DCHECK_GE(start, 0);
DCHECK_LT(start, len);
DCHECK_GE(end, 0);
DCHECK_LE(end, len);
DCHECK_LE(count, len);
RETURN_RESULT_OR_FAILURE(isolate, ElementsAccessor::ForKind(kind)->Fill(
isolate, array, obj_value, start, end));
}
BUILTIN(TypedArrayPrototypeIncludes) {
HandleScope scope(isolate);
DirectHandle<JSTypedArray> array;
const char* method_name = "%TypedArray%.prototype.includes";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array,
JSTypedArray::Validate(isolate, args.receiver(), method_name));
if (args.length() < 2) return ReadOnlyRoots(isolate).false_value();
int64_t len = array->GetLength();
if (len == 0) return ReadOnlyRoots(isolate).false_value();
int64_t index = 0;
if (args.length() > 2) {
double num;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
index = CapRelativeIndex(num, 0, len);
}
DirectHandle<Object> search_element = args.atOrUndefined(isolate, 1);
ElementsAccessor* elements = array->GetElementsAccessor();
Maybe<bool> result =
elements->IncludesValue(isolate, array, search_element, index, len);
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return *isolate->factory()->ToBoolean(result.FromJust());
}
BUILTIN(TypedArrayPrototypeIndexOf) {
HandleScope scope(isolate);
DirectHandle<JSTypedArray> array;
const char* method_name = "%TypedArray%.prototype.indexOf";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array,
JSTypedArray::Validate(isolate, args.receiver(), method_name));
int64_t len = array->GetLength();
if (len == 0) return Smi::FromInt(-1);
int64_t index = 0;
if (args.length() > 2) {
double num;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
index = CapRelativeIndex(num, 0, len);
}
if (V8_UNLIKELY(array->WasDetached())) return Smi::FromInt(-1);
if (V8_UNLIKELY(array->IsVariableLength() && array->IsOutOfBounds())) {
return Smi::FromInt(-1);
}
DirectHandle<Object> search_element = args.atOrUndefined(isolate, 1);
ElementsAccessor* elements = array->GetElementsAccessor();
Maybe<int64_t> result =
elements->IndexOfValue(isolate, array, search_element, index, len);
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return *isolate->factory()->NewNumberFromInt64(result.FromJust());
}
BUILTIN(TypedArrayPrototypeLastIndexOf) {
HandleScope scope(isolate);
DirectHandle<JSTypedArray> array;
const char* method_name = "%TypedArray%.prototype.lastIndexOf";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array,
JSTypedArray::Validate(isolate, args.receiver(), method_name));
int64_t len = array->GetLength();
if (len == 0) return Smi::FromInt(-1);
int64_t index = len - 1;
if (args.length() > 2) {
double num;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, num, Object::IntegerValue(isolate, args.at<Object>(2)));
index = std::min<int64_t>(CapRelativeIndex(num, -1, len), len - 1);
}
if (index < 0) return Smi::FromInt(-1);
if (V8_UNLIKELY(array->WasDetached())) return Smi::FromInt(-1);
if (V8_UNLIKELY(array->IsVariableLength() && array->IsOutOfBounds())) {
return Smi::FromInt(-1);
}
DirectHandle<Object> search_element = args.atOrUndefined(isolate, 1);
ElementsAccessor* elements = array->GetElementsAccessor();
Maybe<int64_t> result =
elements->LastIndexOfValue(array, search_element, index);
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return *isolate->factory()->NewNumberFromInt64(result.FromJust());
}
BUILTIN(TypedArrayPrototypeReverse) {
HandleScope scope(isolate);
DirectHandle<JSTypedArray> array;
const char* method_name = "%TypedArray%.prototype.reverse";
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, array,
JSTypedArray::Validate(isolate, args.receiver(), method_name));
ElementsAccessor* elements = array->GetElementsAccessor();
elements->Reverse(*array);
return *array;
}
namespace {
std::vector<std::tuple<const char*, size_t, simdutf::base64_options>>
SimdutfBase64OptionsVector() {
return {{"base64", 6, simdutf::base64_options::base64_default},
{"base64url", 9, simdutf::base64_options::base64_url}};
}
std::vector<
std::tuple<const char*, size_t, simdutf::last_chunk_handling_options>>
SimdutfLastChunkHandlingOptionsVector() {
return {{"loose", 5, simdutf::last_chunk_handling_options::loose},
{"strict", 6, simdutf::last_chunk_handling_options::strict},
{"stop-before-partial", 19,
simdutf::last_chunk_handling_options::stop_before_partial}};
}
template <typename T>
Maybe<T> MapOptionToEnum(
Isolate* isolate, DirectHandle<String> option_string,
const std::vector<std::tuple<const char*, size_t, T>>& allowed_options) {
option_string = String::Flatten(isolate, option_string);
{
DisallowGarbageCollection no_gc;
String::FlatContent option_content = option_string->GetFlatContent(no_gc);
if (option_content.IsOneByte()) {
const unsigned char* option_string_to_compare =
option_content.ToOneByteVector().data();
size_t length = option_content.ToOneByteVector().size();
for (auto& [str_val, str_size, enum_val] : allowed_options) {
if (str_size == length &&
CompareCharsEqual(option_string_to_compare, str_val, str_size)) {
return Just<T>(enum_val);
}
}
} else {
const base::uc16* option_string_to_compare =
option_content.ToUC16Vector().data();
size_t length = option_content.ToUC16Vector().size();
for (auto& [str_val, str_size, enum_val] : allowed_options) {
if (str_size == length &&
CompareCharsEqual(option_string_to_compare, str_val, str_size)) {
return Just<T>(enum_val);
}
}
}
}
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kInvalidOption, option_string));
return Nothing<T>();
}
Maybe<std::pair<simdutf::base64_options, simdutf::last_chunk_handling_options>>
HandleOptionsBag(Isolate* isolate, DirectHandle<Object> options) {
simdutf::base64_options alphabet;
simdutf::last_chunk_handling_options last_chunk_handling;
DirectHandle<Object> opt_alphabet;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, opt_alphabet,
JSObject::ReadFromOptionsBag(
options, isolate->factory()->alphabet_string(), isolate),
(Nothing<std::pair<simdutf::base64_options,
simdutf::last_chunk_handling_options>>()));
if (IsUndefined(*opt_alphabet)) {
alphabet = simdutf::base64_default;
} else if (!IsString(*opt_alphabet)) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kInvalidOption, opt_alphabet));
return Nothing<std::pair<simdutf::base64_options,
simdutf::last_chunk_handling_options>>();
} else {
DirectHandle<String> alphabet_string = Cast<String>(opt_alphabet);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, alphabet,
MapOptionToEnum(isolate, alphabet_string, SimdutfBase64OptionsVector()),
(Nothing<std::pair<simdutf::base64_options,
simdutf::last_chunk_handling_options>>()));
}
DirectHandle<Object> opt_last_chunk_handling;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, opt_last_chunk_handling,
JSObject::ReadFromOptionsBag(
options, isolate->factory()->last_chunk_handling_string(), isolate),
(Nothing<std::pair<simdutf::base64_options,
simdutf::last_chunk_handling_options>>()));
if (IsUndefined(*opt_last_chunk_handling)) {
last_chunk_handling = simdutf::last_chunk_handling_options::loose;
} else if (!IsString(*opt_last_chunk_handling)) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kInvalidOption, opt_last_chunk_handling));
return Nothing<std::pair<simdutf::base64_options,
simdutf::last_chunk_handling_options>>();
} else {
DirectHandle<String> last_chunk_handling_string =
Cast<String>(opt_last_chunk_handling);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, last_chunk_handling,
MapOptionToEnum(isolate, last_chunk_handling_string,
SimdutfLastChunkHandlingOptionsVector()),
(Nothing<std::pair<simdutf::base64_options,
simdutf::last_chunk_handling_options>>()));
}
return Just(std::make_pair(alphabet, last_chunk_handling));
}
MessageTemplate ToMessageTemplate(simdutf::error_code error) {
switch (error) {
case simdutf::error_code::INVALID_BASE64_CHARACTER:
return MessageTemplate::kInvalidBase64Character;
case simdutf::error_code::BASE64_INPUT_REMAINDER:
return MessageTemplate::kBase64InputRemainder;
case simdutf::error_code::BASE64_EXTRA_BITS:
return MessageTemplate::kBase64ExtraBits;
default:
UNREACHABLE();
}
}
template <typename T>
std::unique_ptr<char[]> ArrayBufferFromBase64(
T input_vector, size_t input_length, simdutf::base64_options alphabet,
simdutf::last_chunk_handling_options last_chunk_handling,
simdutf::result& simd_result, size_t& output_length) {
output_length = simdutf::maximal_binary_length_from_base64(
reinterpret_cast<const T>(input_vector), input_length);
std::unique_ptr<char[]> output = std::make_unique<char[]>(output_length);
simd_result = simdutf::base64_to_binary_safe(
reinterpret_cast<const T>(input_vector), input_length, output.get(),
output_length, alphabet, last_chunk_handling,
true);
return output;
}
template <typename T>
simdutf::result ArrayBufferSetFromBase64(
T input_vector, size_t input_length, size_t array_length,
simdutf::base64_options alphabet,
simdutf::last_chunk_handling_options last_chunk_handling,
DirectHandle<JSTypedArray> typed_array, size_t& output_length) {
output_length = array_length;
simdutf::result simd_result;
if (typed_array->buffer()->is_shared()) {
simd_result = simdutf::atomic_base64_to_binary_safe(
reinterpret_cast<const T>(input_vector), input_length,
reinterpret_cast<char*>(typed_array->DataPtr()), output_length,
alphabet, last_chunk_handling, true);
} else {
simd_result = simdutf::base64_to_binary_safe(
reinterpret_cast<const T>(input_vector), input_length,
reinterpret_cast<char*>(typed_array->DataPtr()), output_length,
alphabet, last_chunk_handling, true);
}
return simd_result;
}
}
BUILTIN(Uint8ArrayFromBase64) {
HandleScope scope(isolate);
isolate->CountUsage(v8::Isolate::kUint8ArrayToFromBase64AndHex);
const char method_name[] = "Uint8Array.fromBase64";
DirectHandle<Object> input = args.atOrUndefined(isolate, 1);
if (!IsString(*input)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kArgumentIsNonString,
isolate->factory()->input_string()));
}
DirectHandle<String> input_string =
String::Flatten(isolate, Cast<String>(input));
DirectHandle<Object> input_options = args.atOrUndefined(isolate, 2);
DirectHandle<Object> options;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, options, GetOptionsObject(isolate, input_options, method_name));
std::pair<simdutf::base64_options, simdutf::last_chunk_handling_options>
options_pair;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, options_pair,
HandleOptionsBag(isolate, options));
size_t input_length;
size_t output_length = 0;
simdutf::result simd_result;
DirectHandle<JSArrayBuffer> buffer;
std::unique_ptr<char[]> output;
{
DisallowGarbageCollection no_gc;
String::FlatContent input_content = input_string->GetFlatContent(no_gc);
if (input_content.IsOneByte()) {
const unsigned char* input_vector =
input_content.ToOneByteVector().data();
input_length = input_content.ToOneByteVector().size();
output = ArrayBufferFromBase64(
reinterpret_cast<const char*>(input_vector), input_length,
options_pair.first, options_pair.second, simd_result, output_length);
} else {
const base::uc16* input_vector = input_content.ToUC16Vector().data();
input_length = input_content.ToUC16Vector().size();
output = ArrayBufferFromBase64(
reinterpret_cast<const char16_t*>(input_vector), input_length,
options_pair.first, options_pair.second, simd_result, output_length);
}
}
MaybeDirectHandle<JSArrayBuffer> result_buffer =
isolate->factory()->NewJSArrayBufferAndBackingStore(
output_length, InitializedFlag::kUninitialized);
if (!result_buffer.ToHandle(&buffer)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kOutOfMemory,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
memcpy(buffer->backing_store(), output.get(), output_length);
if (simd_result.error != simdutf::error_code::SUCCESS) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(ToMessageTemplate(simd_result.error)));
}
DirectHandle<JSTypedArray> result_typed_array =
isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0,
output_length);
return *result_typed_array;
}
BUILTIN(Uint8ArrayPrototypeSetFromBase64) {
HandleScope scope(isolate);
const char method_name[] = "Uint8Array.prototype.setFromBase64";
isolate->CountUsage(v8::Isolate::kUint8ArrayToFromBase64AndHex);
CHECK_RECEIVER(JSTypedArray, uint8array, method_name);
ElementsKind elements_kind = uint8array->GetElementsKind();
if (elements_kind != ElementsKind::UINT8_ELEMENTS &&
elements_kind != ElementsKind::RAB_GSAB_UINT8_ELEMENTS) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
DirectHandle<Object> input = args.atOrUndefined(isolate, 1);
if (!IsString(*input)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kArgumentIsNonString,
isolate->factory()->input_string()));
}
DirectHandle<String> input_string =
String::Flatten(isolate, Cast<String>(input));
DirectHandle<Object> input_options = args.atOrUndefined(isolate, 2);
DirectHandle<Object> options;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, options, GetOptionsObject(isolate, input_options, method_name));
std::pair<simdutf::base64_options, simdutf::last_chunk_handling_options>
options_pair;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, options_pair,
HandleOptionsBag(isolate, options));
bool out_of_bounds = false;
size_t array_length = uint8array->GetLengthOrOutOfBounds(out_of_bounds);
if (out_of_bounds || uint8array->WasDetached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
if (array_length == 0) {
return *isolate->factory()->NewJSUint8ArraySetFromResult(
handle(Smi::zero(), isolate), handle(Smi::zero(), isolate));
}
size_t input_length;
size_t output_length;
simdutf::result simd_result;
{
DisallowGarbageCollection no_gc;
String::FlatContent input_content = input_string->GetFlatContent(no_gc);
if (input_content.IsOneByte()) {
const unsigned char* input_vector =
input_content.ToOneByteVector().data();
input_length = input_content.ToOneByteVector().size();
simd_result = ArrayBufferSetFromBase64(
reinterpret_cast<const char*>(input_vector), input_length,
array_length, options_pair.first, options_pair.second, uint8array,
output_length);
} else {
const base::uc16* input_vector = input_content.ToUC16Vector().data();
input_length = input_content.ToUC16Vector().size();
simd_result = ArrayBufferSetFromBase64(
reinterpret_cast<const char16_t*>(input_vector), input_length,
array_length, options_pair.first, options_pair.second, uint8array,
output_length);
}
}
DCHECK_LE(output_length, array_length);
if (simd_result.error != simdutf::error_code::SUCCESS &&
simd_result.error != simdutf::error_code::OUTPUT_BUFFER_TOO_SMALL) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(ToMessageTemplate(simd_result.error)));
}
return *isolate->factory()->NewJSUint8ArraySetFromResult(
isolate->factory()->NewNumberFromSize(simd_result.count),
isolate->factory()->NewNumberFromSize(output_length));
}
BUILTIN(Uint8ArrayPrototypeToBase64) {
HandleScope scope(isolate);
const char method_name[] = "Uint8Array.prototype.toBase64";
isolate->CountUsage(v8::Isolate::kUint8ArrayToFromBase64AndHex);
CHECK_RECEIVER(JSTypedArray, uint8array, method_name);
ElementsKind elements_kind = uint8array->GetElementsKind();
if (elements_kind != ElementsKind::UINT8_ELEMENTS &&
elements_kind != ElementsKind::RAB_GSAB_UINT8_ELEMENTS) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
DirectHandle<Object> input_options = args.atOrUndefined(isolate, 1);
DirectHandle<Object> options;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, options, GetOptionsObject(isolate, input_options, method_name));
DirectHandle<Object> opt_alphabet;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, opt_alphabet,
JSObject::ReadFromOptionsBag(
options, isolate->factory()->alphabet_string(), isolate));
simdutf::base64_options alphabet;
if (IsUndefined(*opt_alphabet)) {
alphabet = simdutf::base64_options::base64_default;
} else if (!IsString(*opt_alphabet)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidOption, opt_alphabet));
} else {
DirectHandle<String> alphabet_string = Cast<String>(opt_alphabet);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, alphabet,
MapOptionToEnum(isolate, alphabet_string,
SimdutfBase64OptionsVector()));
}
DirectHandle<Object> omit_padding_object;
bool omit_padding = false;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, omit_padding_object,
JSObject::ReadFromOptionsBag(
options, isolate->factory()->NewStringFromAsciiChecked("omitPadding"),
isolate));
if (Object::BooleanValue(*omit_padding_object, isolate)) {
omit_padding = true;
}
bool out_of_bounds = false;
size_t length = uint8array->GetLengthOrOutOfBounds(out_of_bounds);
if (out_of_bounds || uint8array->WasDetached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
if (alphabet == simdutf::base64_options::base64_default &&
omit_padding == true) {
alphabet = simdutf::base64_options::base64_default_no_padding;
} else if (alphabet == simdutf::base64_options::base64_url &&
omit_padding == false) {
alphabet = simdutf::base64_options::base64_url_with_padding;
}
size_t output_length = simdutf::base64_length_from_binary(length, alphabet);
if (output_length > String::kMaxLength) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidStringLength));
}
if (output_length == 0) {
return *isolate->factory()->empty_string();
}
DirectHandle<SeqOneByteString> output;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, output,
isolate->factory()->NewRawOneByteString(static_cast<int>(output_length)));
{
DisallowGarbageCollection no_gc;
size_t simd_result_size;
if (uint8array->buffer()->is_shared()) {
simd_result_size = simdutf::atomic_binary_to_base64(
reinterpret_cast<const char*>(uint8array->DataPtr()), length,
reinterpret_cast<char*>(output->GetChars(no_gc)), alphabet);
} else {
simd_result_size = simdutf::binary_to_base64(
reinterpret_cast<const char*>(uint8array->DataPtr()), length,
reinterpret_cast<char*>(output->GetChars(no_gc)), alphabet);
}
DCHECK_EQ(simd_result_size, output_length);
USE(simd_result_size);
}
return *output;
}
BUILTIN(Uint8ArrayFromHex) {
HandleScope scope(isolate);
const char method_name[] = "Uint8Array.fromHex";
isolate->CountUsage(v8::Isolate::kUint8ArrayToFromBase64AndHex);
DirectHandle<Object> input = args.atOrUndefined(isolate, 1);
if (!IsString(*input)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kArgumentIsNonString,
isolate->factory()->input_string()));
}
DirectHandle<String> input_string =
String::Flatten(isolate, Cast<String>(input));
DirectHandle<JSArrayBuffer> buffer;
size_t input_length = input_string->length();
if (input_length % 2 != 0) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(MessageTemplate::kInvalidHexString));
}
size_t output_length = (input_length / 2);
MaybeDirectHandle<JSArrayBuffer> result_buffer =
isolate->factory()->NewJSArrayBufferAndBackingStore(
output_length, InitializedFlag::kUninitialized);
if (!result_buffer.ToHandle(&buffer)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kOutOfMemory,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
bool result;
{
DisallowGarbageCollection no_gc;
String::FlatContent input_content = input_string->GetFlatContent(no_gc);
if (input_content.IsOneByte()) {
base::Vector<const uint8_t> input_vector =
input_content.ToOneByteVector();
result = ArrayBufferFromHex(
input_vector, false,
static_cast<uint8_t*>(buffer->backing_store()), output_length);
} else {
base::Vector<const base::uc16> input_vector =
input_content.ToUC16Vector();
result = ArrayBufferFromHex(
input_vector, false,
static_cast<uint8_t*>(buffer->backing_store()), output_length);
}
}
if (!result) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(MessageTemplate::kInvalidHexString));
}
DirectHandle<JSTypedArray> result_typed_array =
isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0,
output_length);
return *result_typed_array;
}
BUILTIN(Uint8ArrayPrototypeSetFromHex) {
HandleScope scope(isolate);
const char method_name[] = "Uint8Array.prototype.setFromHex";
isolate->CountUsage(v8::Isolate::kUint8ArrayToFromBase64AndHex);
CHECK_RECEIVER(JSTypedArray, uint8array, method_name);
ElementsKind elements_kind = uint8array->GetElementsKind();
if (elements_kind != ElementsKind::UINT8_ELEMENTS &&
elements_kind != ElementsKind::RAB_GSAB_UINT8_ELEMENTS) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
DirectHandle<Object> input = args.atOrUndefined(isolate, 1);
if (!IsString(*input)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kArgumentIsNonString,
isolate->factory()->input_string()));
}
DirectHandle<String> input_string =
String::Flatten(isolate, Cast<String>(input));
bool out_of_bounds = false;
size_t array_length = uint8array->GetLengthOrOutOfBounds(out_of_bounds);
if (out_of_bounds || uint8array->WasDetached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
size_t input_length = input_string->length();
if (input_length % 2 != 0) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(MessageTemplate::kInvalidHexString));
}
if (array_length == 0) {
return *isolate->factory()->NewJSUint8ArraySetFromResult(
handle(Smi::zero(), isolate), handle(Smi::zero(), isolate));
}
size_t output_length = (input_length / 2);
output_length = std::min(output_length, array_length);
bool result;
{
DisallowGarbageCollection no_gc;
String::FlatContent input_content = input_string->GetFlatContent(no_gc);
if (input_content.IsOneByte()) {
base::Vector<const uint8_t> input_vector =
input_content.ToOneByteVector();
result = ArrayBufferFromHex(
input_vector, uint8array->buffer()->is_shared(),
static_cast<uint8_t*>(uint8array->DataPtr()), output_length);
} else {
base::Vector<const base::uc16> input_vector =
input_content.ToUC16Vector();
result = ArrayBufferFromHex(
input_vector, uint8array->buffer()->is_shared(),
static_cast<uint8_t*>(uint8array->DataPtr()), output_length);
}
}
if (!result) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(MessageTemplate::kInvalidHexString));
}
return *isolate->factory()->NewJSUint8ArraySetFromResult(
isolate->factory()->NewNumberFromSize(output_length * 2),
isolate->factory()->NewNumberFromSize(output_length));
}
BUILTIN(Uint8ArrayPrototypeToHex) {
HandleScope scope(isolate);
const char method_name[] = "Uint8Array.prototype.toHex";
isolate->CountUsage(v8::Isolate::kUint8ArrayToFromBase64AndHex);
CHECK_RECEIVER(JSTypedArray, uint8array, method_name);
ElementsKind elements_kind = uint8array->GetElementsKind();
if (elements_kind != ElementsKind::UINT8_ELEMENTS &&
elements_kind != ElementsKind::RAB_GSAB_UINT8_ELEMENTS) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
bool out_of_bounds = false;
size_t length = uint8array->GetLengthOrOutOfBounds(out_of_bounds);
if (out_of_bounds || uint8array->WasDetached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
isolate->factory()->NewStringFromAsciiChecked(
method_name)));
}
if (length > String::kMaxLength / 2) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidStringLength));
}
if (length == 0) {
return *isolate->factory()->empty_string();
}
DirectHandle<SeqOneByteString> output;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, output,
isolate->factory()->NewRawOneByteString(static_cast<int>(length) * 2));
return Uint8ArrayToHex(reinterpret_cast<const char*>(uint8array->DataPtr()),
length, uint8array->buffer()->is_shared(), output);
}
}