Skip to content

Commit e5c4b13

Browse files
authored
feat(ext/file): file storage in the ext (#100)
1 parent 606a24f commit e5c4b13

File tree

2 files changed

+67
-47
lines changed

2 files changed

+67
-47
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ authors = ["the Andromeda team"]
77
edition = "2024"
88
license = "Mozilla Public License 2.0"
99
repository = "https://github.com/tryandromeda/andromeda"
10-
version = "0.1.0-draft9"
10+
version = "0.1.0-draft10"
1111

1212
[workspace.dependencies]
1313
andromeda-core = { path = "core" }

runtime/src/ext/file/mod.rs

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use andromeda_core::{Extension, ExtensionOp};
5+
use andromeda_core::{Extension, ExtensionOp, HostData, OpsStorage};
66
use nova_vm::{
77
ecmascript::{
88
builtins::ArgumentsList,
@@ -12,12 +12,9 @@ use nova_vm::{
1212
engine::context::{Bindable, GcScope},
1313
};
1414
use std::collections::HashMap;
15-
use std::sync::{Arc, Mutex};
1615
use uuid::Uuid;
1716

18-
// Resource table for managing blob data
19-
static BLOB_STORAGE: std::sync::OnceLock<Arc<Mutex<HashMap<String, BlobData>>>> =
20-
std::sync::OnceLock::new();
17+
use crate::RuntimeMacroTask;
2118

2219
// Internal blob data structure
2320
#[derive(Clone)]
@@ -27,6 +24,11 @@ struct BlobData {
2724
size: usize,
2825
}
2926

27+
// Extension resources structure
28+
struct FileExtResources {
29+
blob_storage: HashMap<String, BlobData>,
30+
}
31+
3032
#[derive(Default)]
3133
pub struct FileExt;
3234

@@ -86,7 +88,11 @@ impl FileExt {
8688
1,
8789
),
8890
],
89-
storage: None,
91+
storage: Some(Box::new(|storage: &mut OpsStorage| {
92+
storage.insert(FileExtResources {
93+
blob_storage: HashMap::new(),
94+
});
95+
})),
9096
files: vec![
9197
include_str!("./blob.ts"),
9298
include_str!("./file.ts"),
@@ -95,10 +101,13 @@ impl FileExt {
95101
}
96102
}
97103

98-
fn get_blob_storage() -> Arc<Mutex<HashMap<String, BlobData>>> {
99-
BLOB_STORAGE
100-
.get_or_init(|| Arc::new(Mutex::new(HashMap::new())))
101-
.clone()
104+
fn get_blob_storage_mut(agent: &Agent) -> std::cell::RefMut<'_, HashMap<String, BlobData>> {
105+
let host_data = agent.get_host_data();
106+
let host_data: &HostData<RuntimeMacroTask> = host_data.downcast_ref().unwrap();
107+
let storage = host_data.storage.borrow_mut();
108+
std::cell::RefMut::map(storage, |s| {
109+
&mut s.get_mut::<FileExtResources>().unwrap().blob_storage
110+
})
102111
}
103112

104113
pub fn internal_blob_create<'gc>(
@@ -146,8 +155,9 @@ impl FileExt {
146155
size,
147156
};
148157

149-
let storage = Self::get_blob_storage();
150-
storage.lock().unwrap().insert(blob_id.clone(), blob);
158+
let mut storage = Self::get_blob_storage_mut(agent);
159+
storage.insert(blob_id.clone(), blob);
160+
drop(storage);
151161

152162
let gc_no = gc.into_nogc();
153163
Ok(Value::from_string(agent, blob_id, gc_no).unbind())
@@ -189,24 +199,26 @@ impl FileExt {
189199
.to_string()
190200
};
191201

192-
let storage = Self::get_blob_storage();
193-
let storage_lock = storage.lock().unwrap();
202+
let storage = Self::get_blob_storage_mut(agent);
203+
204+
if let Some(blob) = storage.get(blob_id) {
205+
let blob_clone = blob.clone();
206+
drop(storage);
194207

195-
if let Some(blob) = storage_lock.get(blob_id) {
196208
let end = if end_arg.is_undefined() {
197-
blob.size
209+
blob_clone.size
198210
} else {
199211
(end_arg
200212
.to_number(agent, gc.reborrow())
201213
.unbind()?
202214
.into_f64(agent) as usize)
203-
.min(blob.size)
215+
.min(blob_clone.size)
204216
};
205217

206-
let start = start.min(blob.size);
218+
let start = start.min(blob_clone.size);
207219
let end = end.max(start);
208220

209-
let sliced_data = blob.data[start..end].to_vec();
221+
let sliced_data = blob_clone.data[start..end].to_vec();
210222
let new_blob_id = Uuid::new_v4().to_string();
211223

212224
let new_blob = BlobData {
@@ -215,14 +227,13 @@ impl FileExt {
215227
size: end - start,
216228
};
217229

218-
drop(storage_lock);
219-
storage
220-
.lock()
221-
.unwrap()
222-
.insert(new_blob_id.clone(), new_blob);
230+
let mut storage = Self::get_blob_storage_mut(agent);
231+
storage.insert(new_blob_id.clone(), new_blob);
232+
drop(storage);
223233

224234
Ok(Value::from_string(agent, new_blob_id, gc.nogc()).unbind())
225235
} else {
236+
drop(storage);
226237
Err(agent
227238
.throw_exception(
228239
ExceptionType::Error,
@@ -246,18 +257,19 @@ impl FileExt {
246257
.as_str(agent)
247258
.expect("String is not valid UTF-8");
248259

249-
let storage = Self::get_blob_storage();
250-
let storage_lock = storage.lock().unwrap();
260+
let storage = Self::get_blob_storage_mut(agent);
251261

252-
if let Some(blob) = storage_lock.get(blob_id) {
262+
if let Some(blob) = storage.get(blob_id) {
253263
let bytes_str = blob
254264
.data
255265
.iter()
256266
.map(|b| b.to_string())
257267
.collect::<Vec<_>>()
258268
.join(",");
269+
drop(storage);
259270
Ok(Value::from_string(agent, bytes_str, gc.nogc()).unbind())
260271
} else {
272+
drop(storage);
261273
Err(agent
262274
.throw_exception(
263275
ExceptionType::Error,
@@ -281,12 +293,14 @@ impl FileExt {
281293
.as_str(agent)
282294
.expect("String is not valid UTF-8");
283295

284-
let storage = Self::get_blob_storage();
285-
let storage_lock = storage.lock().unwrap();
296+
let storage = Self::get_blob_storage_mut(agent);
286297

287-
if let Some(blob) = storage_lock.get(blob_id) {
288-
Ok(Value::from_f64(agent, blob.size as f64, gc.nogc()).unbind())
298+
if let Some(blob) = storage.get(blob_id) {
299+
let size = blob.size;
300+
drop(storage);
301+
Ok(Value::from_f64(agent, size as f64, gc.nogc()).unbind())
289302
} else {
303+
drop(storage);
290304
Err(agent
291305
.throw_exception(
292306
ExceptionType::Error,
@@ -310,12 +324,14 @@ impl FileExt {
310324
.as_str(agent)
311325
.expect("String is not valid UTF-8");
312326

313-
let storage = Self::get_blob_storage();
314-
let storage_lock = storage.lock().unwrap();
327+
let storage = Self::get_blob_storage_mut(agent);
315328

316-
if let Some(blob) = storage_lock.get(blob_id) {
317-
Ok(Value::from_string(agent, blob.content_type.clone(), gc.nogc()).unbind())
329+
if let Some(blob) = storage.get(blob_id) {
330+
let content_type = blob.content_type.clone();
331+
drop(storage);
332+
Ok(Value::from_string(agent, content_type, gc.nogc()).unbind())
318333
} else {
334+
drop(storage);
319335
Err(agent
320336
.throw_exception(
321337
ExceptionType::Error,
@@ -339,10 +355,9 @@ impl FileExt {
339355
.as_str(agent)
340356
.expect("String is not valid UTF-8");
341357

342-
let storage = Self::get_blob_storage();
343-
let storage_lock = storage.lock().unwrap();
358+
let storage = Self::get_blob_storage_mut(agent);
344359

345-
if let Some(blob) = storage_lock.get(blob_id) {
360+
if let Some(blob) = storage.get(blob_id) {
346361
// Return the blob data as comma-separated bytes for now
347362
// TODO: return a ReadableStream
348363
let bytes_str = blob
@@ -351,8 +366,10 @@ impl FileExt {
351366
.map(|b| b.to_string())
352367
.collect::<Vec<_>>()
353368
.join(",");
369+
drop(storage);
354370
Ok(Value::from_string(agent, bytes_str, gc.nogc()).unbind())
355371
} else {
372+
drop(storage);
356373
Err(agent
357374
.throw_exception(
358375
ExceptionType::Error,
@@ -376,19 +393,20 @@ impl FileExt {
376393
.as_str(agent)
377394
.expect("String is not valid UTF-8");
378395

379-
let storage = Self::get_blob_storage();
380-
let storage_lock = storage.lock().unwrap();
396+
let storage = Self::get_blob_storage_mut(agent);
381397

382-
if let Some(blob) = storage_lock.get(blob_id) {
398+
if let Some(blob) = storage.get(blob_id) {
383399
// Return the blob data as comma-separated bytes
384400
let bytes_str = blob
385401
.data
386402
.iter()
387403
.map(|b| b.to_string())
388404
.collect::<Vec<_>>()
389405
.join(",");
406+
drop(storage);
390407
Ok(Value::from_string(agent, bytes_str, gc.nogc()).unbind())
391408
} else {
409+
drop(storage);
392410
Err(agent
393411
.throw_exception(
394412
ExceptionType::Error,
@@ -412,13 +430,14 @@ impl FileExt {
412430
.as_str(agent)
413431
.expect("String is not valid UTF-8");
414432

415-
let storage = Self::get_blob_storage();
416-
let storage_lock = storage.lock().unwrap();
433+
let storage = Self::get_blob_storage_mut(agent);
417434

418-
if let Some(blob) = storage_lock.get(blob_id) {
435+
if let Some(blob) = storage.get(blob_id) {
419436
let text = String::from_utf8_lossy(&blob.data).to_string();
437+
drop(storage);
420438
Ok(Value::from_string(agent, text, gc.nogc()).unbind())
421439
} else {
440+
drop(storage);
422441
Err(agent
423442
.throw_exception(
424443
ExceptionType::Error,
@@ -493,8 +512,9 @@ impl FileExt {
493512
size,
494513
};
495514

496-
let storage = Self::get_blob_storage();
497-
storage.lock().unwrap().insert(blob_id.clone(), blob);
515+
let mut storage = Self::get_blob_storage_mut(agent);
516+
storage.insert(blob_id.clone(), blob);
517+
drop(storage);
498518

499519
// Return combined data: blob_id:name:last_modified
500520
let result = format!("{blob_id}:{name}:{last_modified}");

0 commit comments

Comments
 (0)