1use std::cell::RefCell;
4use std::collections::HashMap;
5use std::fmt::Debug;
6use std::rc::Rc;
7use std::sync::Arc;
8
9use async_trait::async_trait;
10use deno_core::JsBuffer;
11use deno_core::OpState;
12use deno_core::convert::Uint8Array;
13use deno_core::op2;
14use deno_core::parking_lot::Mutex;
15use deno_core::url::Url;
16use serde::Deserialize;
17use serde::Serialize;
18use uuid::Uuid;
19
20#[derive(Debug, thiserror::Error, deno_error::JsError)]
21pub enum BlobError {
22 #[class(type)]
23 #[error("Blob part not found")]
24 BlobPartNotFound,
25 #[class(type)]
26 #[error("start + len can not be larger than blob part size")]
27 SizeLargerThanBlobPart,
28 #[class(type)]
29 #[error("Blob URLs are not supported in this context")]
30 BlobURLsNotSupported,
31 #[class(generic)]
32 #[error(transparent)]
33 Url(#[from] deno_core::url::ParseError),
34}
35
36use crate::Location;
37
38pub type PartMap = HashMap<Uuid, Arc<dyn BlobPart + Send + Sync>>;
39
40#[derive(Default, Debug)]
41pub struct BlobStore {
42 parts: Mutex<PartMap>,
43 object_urls: Mutex<HashMap<Url, Arc<Blob>>>,
44}
45
46impl BlobStore {
47 pub fn insert_part(&self, part: Arc<dyn BlobPart + Send + Sync>) -> Uuid {
48 let id = Uuid::new_v4();
49 let mut parts = self.parts.lock();
50 parts.insert(id, part);
51 id
52 }
53
54 pub fn get_part(&self, id: &Uuid) -> Option<Arc<dyn BlobPart + Send + Sync>> {
55 let parts = self.parts.lock();
56 let part = parts.get(id);
57 part.cloned()
58 }
59
60 pub fn remove_part(
61 &self,
62 id: &Uuid,
63 ) -> Option<Arc<dyn BlobPart + Send + Sync>> {
64 let mut parts = self.parts.lock();
65 parts.remove(id)
66 }
67
68 pub fn get_object_url(&self, mut url: Url) -> Option<Arc<Blob>> {
69 let blob_store = self.object_urls.lock();
70 url.set_fragment(None);
71 blob_store.get(&url).cloned()
72 }
73
74 pub fn insert_object_url(
75 &self,
76 blob: Blob,
77 maybe_location: Option<Url>,
78 ) -> Url {
79 let origin = if let Some(location) = maybe_location {
80 location.origin().ascii_serialization()
81 } else {
82 "null".to_string()
83 };
84 let id = Uuid::new_v4();
85 let url = Url::parse(&format!("blob:{origin}/{id}")).unwrap();
86
87 let mut blob_store = self.object_urls.lock();
88 blob_store.insert(url.clone(), Arc::new(blob));
89
90 url
91 }
92
93 pub fn remove_object_url(&self, url: &Url) {
94 let mut blob_store = self.object_urls.lock();
95 blob_store.remove(url);
96 }
97
98 pub fn clear(&self) {
99 self.parts.lock().clear();
100 self.object_urls.lock().clear();
101 }
102}
103
104#[derive(Debug)]
105pub struct Blob {
106 pub media_type: String,
107
108 pub parts: Vec<Arc<dyn BlobPart + Send + Sync>>,
109}
110
111impl Blob {
112 pub async fn read_all(&self) -> Vec<u8> {
114 let size = self.size();
115 let mut bytes = Vec::with_capacity(size);
116
117 for part in &self.parts {
118 let chunk = part.read().await;
119 bytes.extend_from_slice(chunk);
120 }
121
122 assert_eq!(bytes.len(), size);
123
124 bytes
125 }
126
127 fn size(&self) -> usize {
128 let mut total = 0;
129 for part in &self.parts {
130 total += part.size()
131 }
132 total
133 }
134}
135
136#[async_trait]
137pub trait BlobPart: Debug {
138 async fn read<'a>(&'a self) -> &'a [u8];
140 fn size(&self) -> usize;
141}
142
143#[derive(Debug)]
144pub struct InMemoryBlobPart(Vec<u8>);
145
146impl From<Vec<u8>> for InMemoryBlobPart {
147 fn from(vec: Vec<u8>) -> Self {
148 Self(vec)
149 }
150}
151
152#[async_trait]
153impl BlobPart for InMemoryBlobPart {
154 async fn read<'a>(&'a self) -> &'a [u8] {
155 &self.0
156 }
157
158 fn size(&self) -> usize {
159 self.0.len()
160 }
161}
162
163#[derive(Debug)]
164pub struct SlicedBlobPart {
165 part: Arc<dyn BlobPart + Send + Sync>,
166 start: usize,
167 len: usize,
168}
169
170#[async_trait]
171impl BlobPart for SlicedBlobPart {
172 async fn read<'a>(&'a self) -> &'a [u8] {
173 let original = self.part.read().await;
174 &original[self.start..self.start + self.len]
175 }
176
177 fn size(&self) -> usize {
178 self.len
179 }
180}
181
182#[op2]
183#[serde]
184pub fn op_blob_create_part(
185 state: &mut OpState,
186 #[buffer] data: JsBuffer,
187) -> Uuid {
188 let blob_store = state.borrow::<Arc<BlobStore>>();
189 let part = InMemoryBlobPart(data.to_vec());
190 blob_store.insert_part(Arc::new(part))
191}
192
193#[derive(Deserialize)]
194#[serde(rename_all = "camelCase")]
195pub struct SliceOptions {
196 start: usize,
197 len: usize,
198}
199
200#[op2]
201#[serde]
202pub fn op_blob_slice_part(
203 state: &mut OpState,
204 #[serde] id: Uuid,
205 #[serde] options: SliceOptions,
206) -> Result<Uuid, BlobError> {
207 let blob_store = state.borrow::<Arc<BlobStore>>();
208 let part = blob_store
209 .get_part(&id)
210 .ok_or(BlobError::BlobPartNotFound)?;
211
212 let SliceOptions { start, len } = options;
213
214 let size = part.size();
215 if start + len > size {
216 return Err(BlobError::SizeLargerThanBlobPart);
217 }
218
219 let sliced_part = SlicedBlobPart { part, start, len };
220 let id = blob_store.insert_part(Arc::new(sliced_part));
221
222 Ok(id)
223}
224
225#[op2]
226pub async fn op_blob_read_part(
227 state: Rc<RefCell<OpState>>,
228 #[serde] id: Uuid,
229) -> Result<Uint8Array, BlobError> {
230 let part = {
231 let state = state.borrow();
232 let blob_store = state.borrow::<Arc<BlobStore>>();
233 blob_store.get_part(&id)
234 }
235 .ok_or(BlobError::BlobPartNotFound)?;
236 let buf = part.read().await;
237 Ok(Uint8Array::from(buf.to_vec()))
238}
239
240#[op2]
241pub fn op_blob_remove_part(state: &mut OpState, #[serde] id: Uuid) {
242 let blob_store = state.borrow::<Arc<BlobStore>>();
243 blob_store.remove_part(&id);
244}
245
246#[op2]
247#[string]
248pub fn op_blob_create_object_url(
249 state: &mut OpState,
250 #[string] media_type: String,
251 #[serde] part_ids: Vec<Uuid>,
252) -> Result<String, BlobError> {
253 let mut parts = Vec::with_capacity(part_ids.len());
254 let blob_store = state.borrow::<Arc<BlobStore>>();
255 for part_id in part_ids {
256 let part = blob_store
257 .get_part(&part_id)
258 .ok_or(BlobError::BlobPartNotFound)?;
259 parts.push(part);
260 }
261
262 let blob = Blob { media_type, parts };
263
264 let maybe_location = state.try_borrow::<Location>();
265 let blob_store = state.borrow::<Arc<BlobStore>>();
266
267 let url = blob_store
268 .insert_object_url(blob, maybe_location.map(|location| location.0.clone()));
269
270 Ok(url.into())
271}
272
273#[op2(fast)]
274pub fn op_blob_revoke_object_url(
275 state: &mut OpState,
276 #[string] url: &str,
277) -> Result<(), BlobError> {
278 let url = Url::parse(url)?;
279 let blob_store = state.borrow::<Arc<BlobStore>>();
280 blob_store.remove_object_url(&url);
281 Ok(())
282}
283
284#[derive(Serialize)]
285pub struct ReturnBlob {
286 pub media_type: String,
287 pub parts: Vec<ReturnBlobPart>,
288}
289
290#[derive(Serialize)]
291pub struct ReturnBlobPart {
292 pub uuid: Uuid,
293 pub size: usize,
294}
295
296#[op2]
297#[serde]
298pub fn op_blob_from_object_url(
299 state: &mut OpState,
300 #[string] url: String,
301) -> Result<Option<ReturnBlob>, BlobError> {
302 let url = Url::parse(&url)?;
303 if url.scheme() != "blob" {
304 return Ok(None);
305 }
306
307 let blob_store = state
308 .try_borrow::<Arc<BlobStore>>()
309 .ok_or(BlobError::BlobURLsNotSupported)?;
310 match blob_store.get_object_url(url) {
311 Some(blob) => {
312 let parts = blob
313 .parts
314 .iter()
315 .map(|part| ReturnBlobPart {
316 uuid: blob_store.insert_part(part.clone()),
317 size: part.size(),
318 })
319 .collect();
320 Ok(Some(ReturnBlob {
321 media_type: blob.media_type.clone(),
322 parts,
323 }))
324 }
325 _ => Ok(None),
326 }
327}