blob: 3e98a12059374d41b22c8d5c706c31e81581aeae [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/native_io/native_io_file_utils.h"
#include "base/numerics/safe_conversions.h"
#include "base/sequence_checker.h"
#include "base/types/pass_key.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "v8/include/v8.h"
namespace blink {
int NativeIOOperationSize(const DOMArrayBufferView& buffer) {
// On 32-bit platforms, clamp operation sizes to 2^31-1.
return base::saturated_cast<int>(buffer.byteLength());
}
DOMArrayBufferView* TransferToNewArrayBufferView(
v8::Isolate* isolate,
NotShared<DOMArrayBufferView> source) {
size_t offset = source->byteOffset();
size_t length = source->byteLength() / source->TypeSize();
ArrayBufferContents target_contents;
// Avoid transferring a non-detachable ArrayBuffer, to prevent copying and
// ensure source detachment.
if (!source->buffer()->IsDetachable(isolate) ||
!source->buffer()->Transfer(isolate, target_contents)) {
return nullptr;
}
DOMArrayBuffer* target_buffer =
DOMArrayBuffer::Create(std::move(target_contents));
DOMArrayBufferView* target;
switch (source->GetType()) {
case DOMArrayBufferView::kTypeInt8:
target = DOMInt8Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeUint8:
target = DOMUint8Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeUint8Clamped:
target = DOMUint8ClampedArray::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeInt16:
target = DOMInt16Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeUint16:
target = DOMUint16Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeInt32:
target = DOMInt32Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeUint32:
target = DOMUint32Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeFloat32:
target = DOMFloat32Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeFloat64:
target = DOMFloat64Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeBigInt64:
target = DOMBigInt64Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeBigUint64:
target = DOMBigUint64Array::Create(target_buffer, offset, length);
break;
case DOMArrayBufferView::kTypeDataView:
target = DOMDataView::Create(target_buffer, offset, length);
break;
}
return target;
}
// static
std::unique_ptr<NativeIODataBuffer> NativeIODataBuffer::Create(
ScriptState* script_state,
NotShared<DOMArrayBufferView> source) {
DCHECK(script_state);
DCHECK(source);
DOMArrayBufferView::ViewType type = source->GetType();
size_t offset = source->byteOffset();
size_t byte_length = source->byteLength();
size_t length = byte_length / source->TypeSize();
// Explicitly fail if the source buffer is not detachable. On its own,
// Transfer() copies non-detachable input buffers.
DOMArrayBuffer* buffer = source->buffer();
v8::Isolate* isolate = script_state->GetIsolate();
if (!buffer->IsDetachable(isolate))
return nullptr;
ArrayBufferContents contents;
if (!buffer->Transfer(isolate, contents))
return nullptr;
DCHECK(source->IsDetached());
return std::make_unique<NativeIODataBuffer>(
std::move(contents), type, offset,
#if DCHECK_IS_ON()
byte_length,
#endif // DCHECK_IS_ON()
length, base::PassKey<NativeIODataBuffer>());
}
NativeIODataBuffer::NativeIODataBuffer(ArrayBufferContents contents,
DOMArrayBufferView::ViewType type,
size_t offset,
#if DCHECK_IS_ON()
size_t byte_length,
#endif // DCHECK_IS_ON()
size_t length,
base::PassKey<NativeIODataBuffer>)
: contents_(std::move(contents)),
type_(type),
offset_(offset),
#if DCHECK_IS_ON()
byte_length_(byte_length),
#endif // DCHECK_IS_ON()
length_(length) {
DCHECK(IsValid());
DCHECK(!contents_.IsShared());
// DataLength() returns 0 when called on an invalid ArrayBufferContents
// (backing an empty array). This works as expected.
DCHECK_LE(offset, contents_.DataLength());
#if DCHECK_IS_ON()
DCHECK_LE(length, byte_length);
DCHECK_LE(byte_length, contents_.DataLength() - offset);
#endif // DCHECK_IS_ON()
}
NativeIODataBuffer::~NativeIODataBuffer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
bool NativeIODataBuffer::IsValid() const {
// The ArrayBufferContents is not shared when this instance is constructed. It
// should not become shared while the instance is valid, because no other code
// can gain access and make it shared.
//
// ArrayBufferContents::IsShared() returns false for invalid instances, which
// works out well for this check.
DCHECK(!contents_.IsShared());
// Transferring the data out of an empty ArrayBuffer yields an invalid
// ArrayBufferContents.
return length_ == 0 || contents_.IsValid();
}
NotShared<DOMArrayBufferView> NativeIODataBuffer::Take() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsValid());
DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(std::move(contents_));
DOMArrayBufferView* view = nullptr;
switch (type_) {
case DOMArrayBufferView::kTypeInt8:
view = DOMInt8Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeUint8:
view = DOMUint8Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeUint8Clamped:
view = DOMUint8ClampedArray::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeInt16:
view = DOMInt16Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeUint16:
view = DOMUint16Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeInt32:
view = DOMInt32Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeUint32:
view = DOMUint32Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeFloat32:
view = DOMFloat32Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeFloat64:
view = DOMFloat64Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeBigInt64:
view = DOMBigInt64Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeBigUint64:
view = DOMBigUint64Array::Create(array_buffer, offset_, length_);
break;
case DOMArrayBufferView::kTypeDataView:
view = DOMDataView::Create(array_buffer, offset_, length_);
break;
}
return NotShared<DOMArrayBufferView>(view);
}
} // namespace blink