File
Blob: src/workerd/jsg/buffersource.c++
| 1 | // Copyright (c) 2017-2022 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #include "buffersource.h" |
| 6 | |
| 7 | namespace workerd::jsg { |
| 8 | |
| 9 | namespace { |
| 10 | auto getBacking(auto& handle) { |
| 11 | auto buffer = handle->IsArrayBuffer() ? handle.template As<v8::ArrayBuffer>() |
| 12 | : handle.template As<v8::ArrayBufferView>()->Buffer(); |
| 13 | return buffer->GetBackingStore(); |
| 14 | } |
| 15 | |
| 16 | size_t getByteLength(auto& handle) { |
| 17 | return handle->IsArrayBuffer() ? handle.template As<v8::ArrayBuffer>()->ByteLength() |
| 18 | : handle.template As<v8::ArrayBufferView>()->ByteLength(); |
| 19 | } |
| 20 | |
| 21 | size_t getByteOffset(auto& handle) { |
| 22 | return handle->IsArrayBuffer() ? 0 : handle.template As<v8::ArrayBufferView>()->ByteOffset(); |
| 23 | } |
| 24 | |
| 25 | auto determineElementSize(auto& handle) { |
| 26 | #define V(Type, size, _) \ |
| 27 | if (handle->Is##Type()) return size; |
| 28 | JSG_ARRAY_BUFFER_VIEW_TYPES(V) |
| 29 | #undef V |
| 30 | KJ_ASSERT(handle->IsDataView() || handle->IsArrayBuffer()); |
| 31 | return 1; |
| 32 | } |
| 33 | |
| 34 | bool isDetachable(auto handle) { |
| 35 | auto buffer = handle->IsArrayBuffer() ? handle.template As<v8::ArrayBuffer>() |
| 36 | : handle.template As<v8::ArrayBufferView>()->Buffer(); |
| 37 | return buffer->IsDetachable(); |
| 38 | } |
| 39 | |
| 40 | bool determineIsIntegerType(auto& handle) { |
| 41 | #define V(Type, _, integerView) \ |
| 42 | if (handle->Is##Type()) return integerView; |
| 43 | JSG_ARRAY_BUFFER_VIEW_TYPES(V); |
| 44 | #undef V |
| 45 | return false; |
| 46 | } |
| 47 | |
| 48 | Value createHandle(Lock& js, BackingStore& backingStore) { |
| 49 | return js.withinHandleScope([&] { return js.v8Ref(backingStore.createHandle(js)); }); |
| 50 | } |
| 51 | |
| 52 | } // namespace |
| 53 | |
| 54 | void GcVisitor::visit(BufferSource& value) { |
| 55 | visit(value.handle); |
| 56 | } |
| 57 | |
| 58 | BackingStore::BackingStore(std::shared_ptr<v8::BackingStore> backingStore, |
| 59 | size_t byteLength, |
| 60 | size_t byteOffset, |
| 61 | size_t elementSize, |
| 62 | BufferSourceViewConstructor ctor, |
| 63 | bool integerType) |
| 64 | : backingStore(kj::mv(backingStore)), |
| 65 | byteLength(byteLength), |
| 66 | byteOffset(byteOffset), |
| 67 | elementSize(elementSize), |
| 68 | ctor(ctor), |
| 69 | integerType(integerType) { |
| 70 | KJ_REQUIRE(this->backingStore != nullptr); |
| 71 | KJ_REQUIRE(this->byteLength <= this->backingStore->ByteLength()); |
| 72 | KJ_REQUIRE(this->byteLength % this->elementSize == 0, |
| 73 | kj::str("byteLength must be a multiple of ", this->elementSize, ".")); |
| 74 | } |
| 75 | |
| 76 | bool BackingStore::operator==(const BackingStore& other) { |
| 77 | return backingStore == other.backingStore && byteLength == other.byteLength && |
| 78 | byteOffset == other.byteOffset; |
| 79 | } |
| 80 | |
| 81 | kj::Maybe<BufferSource> BufferSource::tryAlloc(Lock& js, size_t size) { |
| 82 | v8::Local<v8::ArrayBuffer> buffer; |
| 83 | if (v8::ArrayBuffer::MaybeNew(js.v8Isolate, size).ToLocal(&buffer)) { |
| 84 | return BufferSource(js, v8::Uint8Array::New(buffer, 0, size).As<v8::Value>()); |
| 85 | } |
| 86 | return kj::none; |
| 87 | } |
| 88 | |
| 89 | kj::Maybe<BufferSource> BufferSource::tryAllocUnsafe(Lock& js, size_t size) { |
| 90 | v8::Local<v8::ArrayBuffer> buffer; |
| 91 | if (v8::ArrayBuffer::MaybeNew( |
| 92 | js.v8Isolate, size, v8::BackingStoreInitializationMode::kUninitialized) |
| 93 | .ToLocal(&buffer)) { |
| 94 | return BufferSource(js, v8::Uint8Array::New(buffer, 0, size).As<v8::Value>()); |
| 95 | } |
| 96 | return kj::none; |
| 97 | } |
| 98 | |
| 99 | BufferSource::BufferSource(Lock& js, v8::Local<v8::Value> handle) |
| 100 | : handle(js.v8Ref(handle)), |
| 101 | maybeBackingStore(BackingStore(getBacking(handle), |
| 102 | getByteLength(handle), |
| 103 | getByteOffset(handle), |
| 104 | determineElementSize(handle), |
| 105 | determineConstructor(handle), |
| 106 | determineIsIntegerType(handle))) {} |
| 107 | |
| 108 | BufferSource::BufferSource(Lock& js, BackingStore&& backingStore) |
| 109 | : handle(createHandle(js, backingStore)), |
| 110 | maybeBackingStore(kj::mv(backingStore)) {} |
| 111 | |
| 112 | BackingStore BufferSource::detach(Lock& js, kj::Maybe<v8::Local<v8::Value>> maybeKey) { |
| 113 | auto theHandle = handle.getHandle(js); |
| 114 | JSG_REQUIRE(isDetachable(theHandle), TypeError, |
| 115 | "This BufferSource does not have a detachable backing store."); |
| 116 | auto backingStore = kj::mv(JSG_REQUIRE_NONNULL( |
| 117 | maybeBackingStore, TypeError, "This BufferSource has already been detached.")); |
| 118 | maybeBackingStore = kj::none; |
| 119 | |
| 120 | v8::Local<v8::Value> key = maybeKey.orDefault(v8::Local<v8::Value>()); |
| 121 | |
| 122 | auto buffer = theHandle->IsArrayBuffer() ? theHandle.As<v8::ArrayBuffer>() |
| 123 | : theHandle.As<v8::ArrayBufferView>()->Buffer(); |
| 124 | jsg::check(buffer->Detach(key)); |
| 125 | |
| 126 | return kj::mv(backingStore); |
| 127 | } |
| 128 | |
| 129 | bool BufferSource::canDetach(Lock& js) { |
| 130 | if (isDetached()) return false; |
| 131 | return isDetachable(handle.getHandle(js)); |
| 132 | } |
| 133 | |
| 134 | v8::Local<v8::Value> BufferSource::getHandle(Lock& js) { |
| 135 | return handle.getHandle(js); |
| 136 | } |
| 137 | |
| 138 | JsBufferSource BufferSource::getJsHandle(Lock& js) { |
| 139 | auto handle = getHandle(js); |
| 140 | if (handle->IsArrayBuffer()) { |
| 141 | return JsBufferSource(JsArrayBuffer(handle.As<v8::ArrayBuffer>())); |
| 142 | } else if (handle->IsSharedArrayBuffer()) { |
| 143 | return JsBufferSource(handle.As<v8::SharedArrayBuffer>()); |
| 144 | } else if (handle->IsArrayBufferView()) { |
| 145 | return JsBufferSource(JsArrayBufferView(handle.As<v8::ArrayBufferView>())); |
| 146 | } |
| 147 | KJ_UNREACHABLE; |
| 148 | } |
| 149 | |
| 150 | void BufferSource::setDetachKey(Lock& js, v8::Local<v8::Value> key) { |
| 151 | auto handle = getHandle(js); |
| 152 | auto buffer = handle->IsArrayBuffer() ? handle.As<v8::ArrayBuffer>() |
| 153 | : handle.As<v8::ArrayBufferView>()->Buffer(); |
| 154 | buffer->SetDetachKey(key); |
| 155 | } |
| 156 | |
| 157 | BufferSource BufferSource::wrap( |
| 158 | Lock& js, void* data, size_t size, BackingStore::Disposer disposer, void* ctx) { |
| 159 | return BufferSource(js, BackingStore::wrap(data, size, disposer, ctx)); |
| 160 | } |
| 161 | |
| 162 | } // namespace workerd::jsg |