in src/bun.js/bindings/BunObject.cpp [71:213]
static inline JSC::EncodedJSValue flattenArrayOfBuffersIntoArrayBufferOrUint8Array(JSGlobalObject* lexicalGlobalObject, JSValue arrayValue, size_t maxLength, bool asUint8Array)
{
auto& vm = lexicalGlobalObject->vm();
if (arrayValue.isUndefinedOrNull() || !arrayValue) {
return JSC::JSValue::encode(JSC::JSArrayBuffer::create(vm, lexicalGlobalObject->arrayBufferStructure(), JSC::ArrayBuffer::create(static_cast<size_t>(0), 1)));
}
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto array = JSC::jsDynamicCast<JSC::JSArray*>(arrayValue);
if (UNLIKELY(!array)) {
throwTypeError(lexicalGlobalObject, throwScope, "Argument must be an array"_s);
return JSValue::encode(jsUndefined());
}
size_t arrayLength = array->length();
const auto returnEmptyArrayBufferView = [&]() -> EncodedJSValue {
if (asUint8Array) {
return JSValue::encode(
JSC::JSUint8Array::create(
lexicalGlobalObject,
lexicalGlobalObject->m_typedArrayUint8.get(lexicalGlobalObject),
0));
}
RELEASE_AND_RETURN(throwScope, JSValue::encode(JSC::JSArrayBuffer::create(vm, lexicalGlobalObject->arrayBufferStructure(), JSC::ArrayBuffer::create(static_cast<size_t>(0), 1))));
};
if (arrayLength < 1) {
return returnEmptyArrayBufferView();
}
size_t byteLength = 0;
bool any_buffer = false;
bool any_typed = false;
// Use an argument buffer to avoid calling `getIndex` more than once per element.
// This is a small optimization
MarkedArgumentBuffer args;
args.ensureCapacity(arrayLength);
if (UNLIKELY(args.hasOverflowed())) {
throwOutOfMemoryError(lexicalGlobalObject, throwScope);
return JSValue::encode({});
}
for (size_t i = 0; i < arrayLength; i++) {
auto element = array->getIndex(lexicalGlobalObject, i);
RETURN_IF_EXCEPTION(throwScope, {});
if (auto* typedArray = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(element)) {
if (UNLIKELY(typedArray->isDetached())) {
throwTypeError(lexicalGlobalObject, throwScope, "ArrayBufferView is detached"_s);
return JSValue::encode(jsUndefined());
}
size_t current = typedArray->byteLength();
any_typed = true;
byteLength += current;
if (current > 0) {
args.append(typedArray);
}
} else if (auto* arrayBuffer = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(element)) {
auto* impl = arrayBuffer->impl();
if (UNLIKELY(!impl)) {
throwTypeError(lexicalGlobalObject, throwScope, "ArrayBuffer is detached"_s);
return JSValue::encode(jsUndefined());
}
size_t current = impl->byteLength();
any_buffer = true;
if (current > 0) {
args.append(arrayBuffer);
}
byteLength += current;
} else {
throwTypeError(lexicalGlobalObject, throwScope, "Expected TypedArray"_s);
return JSValue::encode(jsUndefined());
}
}
byteLength = std::min(byteLength, maxLength);
if (byteLength == 0) {
return returnEmptyArrayBufferView();
}
auto buffer = JSC::ArrayBuffer::tryCreateUninitialized(byteLength, 1);
if (UNLIKELY(!buffer)) {
throwTypeError(lexicalGlobalObject, throwScope, "Failed to allocate ArrayBuffer"_s);
return JSValue::encode(jsUndefined());
}
size_t remain = byteLength;
auto* head = reinterpret_cast<char*>(buffer->data());
if (!any_buffer) {
for (size_t i = 0; i < args.size(); i++) {
auto element = args.at(i);
RETURN_IF_EXCEPTION(throwScope, {});
auto* view = JSC::jsCast<JSC::JSArrayBufferView*>(element);
size_t length = std::min(remain, view->byteLength());
memcpy(head, view->vector(), length);
remain -= length;
head += length;
}
} else if (!any_typed) {
for (size_t i = 0; i < args.size(); i++) {
auto element = args.at(i);
RETURN_IF_EXCEPTION(throwScope, {});
auto* view = JSC::jsCast<JSC::JSArrayBuffer*>(element);
size_t length = std::min(remain, view->impl()->byteLength());
memcpy(head, view->impl()->data(), length);
remain -= length;
head += length;
}
} else {
for (size_t i = 0; i < args.size(); i++) {
auto element = args.at(i);
RETURN_IF_EXCEPTION(throwScope, {});
size_t length = 0;
if (auto* view = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(element)) {
length = std::min(remain, view->impl()->byteLength());
memcpy(head, view->impl()->data(), length);
} else {
auto* typedArray = JSC::jsCast<JSC::JSArrayBufferView*>(element);
length = std::min(remain, typedArray->byteLength());
memcpy(head, typedArray->vector(), length);
}
remain -= length;
head += length;
}
}
if (asUint8Array) {
auto uint8array = JSC::JSUint8Array::create(lexicalGlobalObject, lexicalGlobalObject->m_typedArrayUint8.get(lexicalGlobalObject), WTFMove(buffer), 0, byteLength);
return JSValue::encode(uint8array);
}
RELEASE_AND_RETURN(throwScope, JSValue::encode(JSC::JSArrayBuffer::create(vm, lexicalGlobalObject->arrayBufferStructure(), WTFMove(buffer))));
}