Skip to main content

blockworx_store/storage/
memory.rs

1//! A container in a map, for a session with nothing to write to and for
2//! tests on every target.
3//!
4//! The browser's facts are knobs here, so the paths that only origin-private
5//! storage takes in earnest are driven natively: futures that are not ready
6//! when they are made and a lock the host keeps rather than the container
7//! ([`Memory::deferred`]), and a write that will not land
8//! ([`Memory::refusing`]).
9
10use core::future::Future;
11use core::pin::Pin;
12use core::task::{Context, Poll};
13use std::cell::{Cell, RefCell};
14use std::collections::{BTreeMap, BTreeSet};
15use std::rc::Rc;
16
17use super::{Entry, Name, Residency, Storage};
18use crate::lock::{Claim, Holding};
19use crate::record::WallTime;
20
21/// A container's bytes, keyed by entry name, dying with the process.
22///
23/// A handle on a map rather than the map itself: cloning one hands a second
24/// container the same bytes, which is what re-opening a container means
25/// where there is no directory to point at twice.
26#[derive(Clone, Debug)]
27pub struct Memory(Rc<Held>);
28
29#[derive(Debug)]
30struct Held {
31    name: RefCell<Name>,
32    residency: Residency,
33    pace: Pace,
34    files: RefCell<BTreeMap<String, Vec<u8>>>,
35    dirs: RefCell<BTreeSet<String>>,
36    refused: RefCell<Option<Entry>>,
37    /// Whether this map keeps its own lock, and whether that lock is held.
38    /// `None` for one whose lock is the container's own entry.
39    own_lock: Cell<Option<bool>>,
40    reads: Cell<usize>,
41    wrote: RefCell<Vec<Entry>>,
42}
43
44/// Whether this map's futures are ready the moment they are made.
45#[derive(Clone, Copy, PartialEq, Eq, Debug)]
46enum Pace {
47    Ready,
48    Deferred,
49}
50
51impl Memory {
52    /// Read as a directory is: an entry at a time, as it is asked for.
53    pub fn new(name: &str) -> Self {
54        Self::with(name, Residency::Lazy, Pace::Ready)
55    }
56
57    /// Read whole at open, with futures that are ready anyway — the
58    /// residency without the waiting.
59    pub fn resident(name: &str) -> Self {
60        Self::with(name, Residency::Resident, Pace::Ready)
61    }
62
63    /// A map as asynchronous as origin-private storage is: every future
64    /// answers `Pending` once before it completes, so nothing may resolve
65    /// one by polling it — and, as [`Storage::release`] requires of such a
66    /// storage, a lock of its own rather than an entry it could not remove
67    /// as it is dropped.
68    pub fn deferred(name: &str) -> Self {
69        let deferred = Self::with(name, Residency::Resident, Pace::Deferred);
70        deferred.0.own_lock.set(Some(false));
71        deferred
72    }
73
74    /// A map that will not write `at`, so a storage that fails partway
75    /// through what it was handed can be stood up without breaking one.
76    #[must_use]
77    pub fn refusing(self, at: Entry) -> Self {
78        *self.0.refused.borrow_mut() = Some(at);
79        self
80    }
81
82    fn with(name: &str, residency: Residency, pace: Pace) -> Self {
83        Self(Rc::new(Held {
84            name: RefCell::new(Name::new(name).unwrap_or_else(|| Name(name.to_owned()))),
85            residency,
86            pace,
87            files: RefCell::default(),
88            dirs: RefCell::default(),
89            refused: RefCell::default(),
90            own_lock: Cell::new(None),
91            reads: Cell::new(0),
92            wrote: RefCell::default(),
93        }))
94    }
95
96    /// How many entries have been read out of this map — the count that
97    /// must stop moving once a resident container is open.
98    pub fn reads(&self) -> usize {
99        self.0.reads.get()
100    }
101
102    /// What has been written to this map, in the order it landed.
103    pub fn wrote(&self) -> Vec<Entry> {
104        self.0.wrote.borrow().clone()
105    }
106
107    /// Answer `Pending` once where this map is the deferred kind, so a
108    /// caller that resolves its futures by polling them is caught.
109    async fn paced(&self) {
110        if let Pace::Deferred = self.0.pace {
111            Wait(false).await;
112        }
113    }
114
115    fn read_one(&self) {
116        self.0.reads.set(self.0.reads.get() + 1);
117    }
118
119    fn wrote_one(&self, at: &Entry) -> std::io::Result<()> {
120        if self.0.refused.borrow().as_ref() == Some(at) {
121            return Err(std::io::Error::new(
122                std::io::ErrorKind::PermissionDenied,
123                format!("{at} is an entry this map refuses to write"),
124            ));
125        }
126        self.0.wrote.borrow_mut().push(at.clone());
127        Ok(())
128    }
129}
130
131/// A future that answers `Pending` once before it is ready.
132struct Wait(bool);
133
134impl Future for Wait {
135    type Output = ();
136
137    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
138        if self.0 {
139            return Poll::Ready(());
140        }
141        self.0 = true;
142        cx.waker().wake_by_ref();
143        Poll::Pending
144    }
145}
146
147impl Default for Memory {
148    fn default() -> Self {
149        Self::new("session.bwx")
150    }
151}
152
153/// The name `dir` holds `key` under, or `None` where `key` is not in it.
154/// The root holds everything with no separator left in it.
155fn within<'a>(dir: &str, key: &'a str) -> Option<&'a str> {
156    let rest = if dir.is_empty() {
157        key
158    } else {
159        key.strip_prefix(dir)?.strip_prefix('/')?
160    };
161    (!rest.contains('/')).then_some(rest)
162}
163
164impl Storage for Memory {
165    fn name(&self) -> Name {
166        self.0.name.borrow().clone()
167    }
168
169    fn names(&self, at: &Entry) -> String {
170        format!("{at} of {}", self.0.name.borrow())
171    }
172
173    fn residency(&self) -> Residency {
174        self.0.residency
175    }
176
177    async fn read<'a>(&'a self, at: &'a Entry) -> std::io::Result<Option<Vec<u8>>> {
178        self.paced().await;
179        self.read_one();
180        Ok(self.0.files.borrow().get(at.as_str()).cloned())
181    }
182
183    async fn exists<'a>(&'a self, at: &'a Entry) -> std::io::Result<bool> {
184        self.paced().await;
185        self.read_one();
186        Ok(self.0.files.borrow().contains_key(at.as_str()))
187    }
188
189    async fn write<'a>(&'a self, at: &'a Entry, bytes: &'a [u8]) -> std::io::Result<()> {
190        self.paced().await;
191        self.wrote_one(at)?;
192        self.0
193            .files
194            .borrow_mut()
195            .insert(at.as_str().to_owned(), bytes.to_vec());
196        Ok(())
197    }
198
199    async fn append<'a>(&'a self, at: &'a Entry, bytes: &'a [u8]) -> std::io::Result<()> {
200        self.paced().await;
201        self.wrote_one(at)?;
202        self.0
203            .files
204            .borrow_mut()
205            .entry(at.as_str().to_owned())
206            .or_default()
207            .extend_from_slice(bytes);
208        Ok(())
209    }
210
211    async fn truncate<'a>(&'a self, at: &'a Entry, len: u64) -> std::io::Result<()> {
212        self.paced().await;
213        self.wrote_one(at)?;
214        if let Some(bytes) = self.0.files.borrow_mut().get_mut(at.as_str()) {
215            bytes.truncate(len as usize);
216        }
217        Ok(())
218    }
219
220    async fn list<'a>(&'a self, dir: &'a Entry) -> std::io::Result<Vec<String>> {
221        self.paced().await;
222        self.read_one();
223        let mut names: Vec<String> = self
224            .0
225            .dirs
226            .borrow()
227            .iter()
228            .filter_map(|key| within(dir.as_str(), key))
229            .map(ToOwned::to_owned)
230            .collect();
231        names.extend(
232            self.0
233                .files
234                .borrow()
235                .keys()
236                .filter_map(|key| within(dir.as_str(), key))
237                .map(ToOwned::to_owned),
238        );
239        Ok(names)
240    }
241
242    async fn remove<'a>(&'a self, at: &'a Entry) -> std::io::Result<()> {
243        self.paced().await;
244        let gone = self.0.files.borrow_mut().remove(at.as_str()).is_some()
245            || self.0.dirs.borrow_mut().remove(at.as_str());
246        if gone {
247            return Ok(());
248        }
249        Err(std::io::Error::from(std::io::ErrorKind::NotFound))
250    }
251
252    async fn create_dir<'a>(&'a self, dir: &'a Entry) -> std::io::Result<()> {
253        self.paced().await;
254        if !dir.as_str().is_empty() {
255            self.0.dirs.borrow_mut().insert(dir.as_str().to_owned());
256        }
257        Ok(())
258    }
259
260    async fn rename<'a>(&'a mut self, to: &'a Name) -> std::io::Result<()> {
261        self.paced().await;
262        *self.0.name.borrow_mut() = to.clone();
263        Ok(())
264    }
265
266    async fn discard(&self) -> std::io::Result<()> {
267        self.paced().await;
268        self.0.files.borrow_mut().clear();
269        self.0.dirs.borrow_mut().clear();
270        Ok(())
271    }
272
273    async fn claim(&self, now: WallTime) -> std::io::Result<Claim> {
274        self.paced().await;
275        match self.0.own_lock.get() {
276            None => crate::lock::claim(self, now).await,
277            Some(true) => Ok(Claim::Held(Holding::Elsewhere)),
278            Some(false) => {
279                self.0.own_lock.set(Some(true));
280                Ok(Claim::Taken)
281            }
282        }
283    }
284
285    fn release(&self) {
286        match self.0.own_lock.get() {
287            None => crate::lock::release(self),
288            Some(_) => self.0.own_lock.set(Some(false)),
289        }
290    }
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296    use crate::storage::ready_now;
297
298    const MANIFEST: Entry = Entry::fixed("manifest.jsonl");
299
300    #[test]
301    fn entries_round_trip_and_list_under_the_directory_that_holds_them() {
302        let memory = Memory::new("doc.bwx");
303        ready_now(memory.create_dir(&Entry::fixed("revs"))).expect("the directory");
304        ready_now(memory.append(&MANIFEST, b"one\n")).expect("a row");
305        ready_now(memory.append(&MANIFEST, b"two\n")).expect("another row");
306        let rev = Entry::under("revs", "000001.json");
307        ready_now(memory.write(&rev, b"a document")).expect("a rev");
308
309        assert_eq!(
310            ready_now(memory.read(&MANIFEST)).expect("it reads back"),
311            Some(b"one\ntwo\n".to_vec()),
312        );
313        let mut root = ready_now(memory.list(&Entry::ROOT)).expect("it lists");
314        root.sort();
315        assert_eq!(root, ["manifest.jsonl", "revs"]);
316        assert_eq!(
317            ready_now(memory.list(&Entry::fixed("revs"))).expect("it lists"),
318            ["000001.json"],
319            "a nested entry was listed at the root, or not under its directory",
320        );
321
322        ready_now(memory.truncate(&MANIFEST, 4)).expect("the truncate");
323        assert_eq!(
324            ready_now(memory.read(&MANIFEST)).expect("it reads back"),
325            Some(b"one\n".to_vec()),
326        );
327        ready_now(memory.remove(&rev)).expect("the rev goes");
328        assert_eq!(
329            ready_now(memory.read(&rev)).expect("the read answers"),
330            None
331        );
332    }
333
334    #[test]
335    fn a_map_is_read_as_it_is_asked_and_can_be_asked_to_be_read_whole() {
336        assert_eq!(Memory::new("a").residency(), Residency::Lazy);
337        assert_eq!(Memory::resident("a").residency(), Residency::Resident);
338        assert_eq!(Memory::deferred("a").residency(), Residency::Resident);
339    }
340
341    /// Two handles on one map are one container's bytes seen twice — how a
342    /// container is re-opened where there is no directory to point at.
343    #[test]
344    fn a_clone_is_the_same_map_and_not_a_copy_of_it() {
345        let first = Memory::new("doc.bwx");
346        let second = first.clone();
347        ready_now(first.write(&MANIFEST, b"a row\n")).expect("the write lands");
348
349        assert_eq!(
350            ready_now(second.read(&MANIFEST)).expect("it reads back"),
351            Some(b"a row\n".to_vec()),
352        );
353    }
354
355    /// The promise the synchronous read path rests on, broken deliberately:
356    /// a deferred map's futures cannot be resolved by polling them once.
357    #[test]
358    fn a_deferred_map_refuses_to_be_read_as_if_it_were_ready() {
359        let deferred = Memory::deferred("doc.bwx");
360        assert_eq!(
361            ready_now(deferred.read(&MANIFEST))
362                .expect_err("a deferred future must not resolve in one poll")
363                .kind(),
364            std::io::ErrorKind::WouldBlock,
365        );
366        assert_eq!(
367            crate::fixture::block_on(deferred.read(&MANIFEST)).expect("driven, it answers"),
368            None,
369        );
370    }
371
372    #[test]
373    fn a_refused_entry_is_the_only_one_that_does_not_write() {
374        let memory = Memory::new("doc.bwx").refusing(MANIFEST);
375        assert_eq!(
376            ready_now(memory.append(&MANIFEST, b"a row\n"))
377                .expect_err("the refusal")
378                .kind(),
379            std::io::ErrorKind::PermissionDenied,
380        );
381        ready_now(memory.write(&Entry::fixed("document.json"), b"{}")).expect("another entry");
382        assert_eq!(memory.wrote(), [Entry::fixed("document.json")]);
383    }
384}