Skip to content
File

Blob: src/workerd/jsg/buffersource.c++

5.6 KB
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 
7namespace workerd::jsg {
8 
9namespace {
10auto 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 
16size_t getByteLength(auto& handle) {
17 return handle->IsArrayBuffer() ? handle.template As<v8::ArrayBuffer>()->ByteLength()
18 : handle.template As<v8::ArrayBufferView>()->ByteLength();
19}
20 
21size_t getByteOffset(auto& handle) {
22 return handle->IsArrayBuffer() ? 0 : handle.template As<v8::ArrayBufferView>()->ByteOffset();
23}
24 
25auto 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 
34bool 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 
40bool 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 
48Value createHandle(Lock& js, BackingStore& backingStore) {
49 return js.withinHandleScope([&] { return js.v8Ref(backingStore.createHandle(js)); });
50}
51 
52} // namespace
53 
54void GcVisitor::visit(BufferSource& value) {
55 visit(value.handle);
56}
57 
58BackingStore::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 
76bool BackingStore::operator==(const BackingStore& other) {
77 return backingStore == other.backingStore && byteLength == other.byteLength &&
78 byteOffset == other.byteOffset;
79}
80 
81kj::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 
89kj::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 
99BufferSource::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 
108BufferSource::BufferSource(Lock& js, BackingStore&& backingStore)
109 : handle(createHandle(js, backingStore)),
110 maybeBackingStore(kj::mv(backingStore)) {}
111 
112BackingStore 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 
129bool BufferSource::canDetach(Lock& js) {
130 if (isDetached()) return false;
131 return isDetachable(handle.getHandle(js));
132}
133 
134v8::Local<v8::Value> BufferSource::getHandle(Lock& js) {
135 return handle.getHandle(js);
136}
137 
138JsBufferSource 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 
150void 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 
157BufferSource 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