diff options
| author | tv <tv@krebsco.de> | 2026-10-31 18:07:07 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-31 18:07:07 +0100 |
| commit | 4a9f4507f3161791f80c8c25a6f0e9ccd3649c2c (patch) | |
| tree | 7de1e540b12879364105e275feb07a5b129251a5 /src/document | |
| parent | 4ea5867047d869ea6a33efd6a47a2adc2340114e (diff) | |
move all automerge references to document
Diffstat (limited to 'src/document')
| -rw-r--r-- | src/document/changes.rs | 22 | ||||
| -rw-r--r-- | src/document/file.rs | 9 | ||||
| -rw-r--r-- | src/document/mod.rs | 24 | ||||
| -rw-r--r-- | src/document/replica.rs | 2 |
4 files changed, 49 insertions, 8 deletions
diff --git a/src/document/changes.rs b/src/document/changes.rs index d369c79..655063b 100644 --- a/src/document/changes.rs +++ b/src/document/changes.rs @@ -8,6 +8,26 @@ use anyhow::{Context, Result, anyhow, bail}; use super::{Change, ChangeHash, Document}; +/// Those of `heads` that `doc` doesn't have. (Looking a change up needs the document +/// mutably.) +pub(crate) fn missing(doc: &mut Document, heads: &[ChangeHash]) -> Vec<ChangeHash> { + heads + .iter() + .copied() + .filter(|head| doc.get_change_by_hash(head).is_none()) + .collect() +} + +/// When the changes after `since` were committed, in Unix milliseconds by their +/// writers' clocks (`Replica::commit`); changes without a time (snapshots) left out. +pub(crate) fn commit_times(doc: &mut Document, since: &[ChangeHash]) -> Vec<i64> { + doc.get_changes(since) + .iter() + .map(|change| change.timestamp()) + .filter(|&time| time > 0) + .collect() +} + /// The change `hash` as the document encodes it; None if `doc` doesn't have it. pub(crate) fn change_bytes(doc: &mut Document, hash: &ChangeHash) -> Option<Vec<u8>> { doc.get_change_by_hash(hash) @@ -33,7 +53,7 @@ pub(crate) fn delta_bytes( from: &[ChangeHash], to: &[ChangeHash], ) -> Option<Vec<u8>> { - if to.iter().any(|head| doc.get_change_by_hash(head).is_none()) { + if !missing(doc, to).is_empty() { return None; } // Heads of `from` we don't have can't hide anything we have. diff --git a/src/document/file.rs b/src/document/file.rs index 47d7310..e03c2e6 100644 --- a/src/document/file.rs +++ b/src/document/file.rs @@ -99,7 +99,7 @@ fn parse(bytes: &[u8]) -> Result<(Loaded, usize)> { } let (record, after) = after.split_at(length); match &mut doc { - None => doc = Some(AutoCommit::load(record).context("the full save")?), + None => doc = Some(super::load(record).context("the full save")?), Some(doc) => { doc.load_incremental(record).context("an increment")?; } @@ -109,10 +109,7 @@ fn parse(bytes: &[u8]) -> Result<(Loaded, usize)> { let Some(doc) = doc else { bail!("no full save"); }; - let loaded = Loaded { - doc: doc.with_actor(ActorId::random()), - actors, - }; + let loaded = Loaded { doc, actors }; Ok((loaded, records.len())) } @@ -250,7 +247,7 @@ impl DocumentFile { .into_iter() .filter(|actor| *actor == current || present.contains(actor)) .collect(); - let save = copy.save(); + let save = super::save(&mut copy); let bytes = whole(&actors, &save)?; writer .files diff --git a/src/document/mod.rs b/src/document/mod.rs index 1612145..2230e47 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -6,6 +6,30 @@ /// Only `document` and `remake` use Automerge itself (`architecture.md`, The document). pub(crate) use automerge::{ActorId, AutoCommit as Document, Change, ChangeHash}; +/// The whole document as bytes, as the engine saves it: a backup, the document +/// file's full save, the size a joining node downloads. +pub(crate) fn save(doc: &mut Document) -> Vec<u8> { + doc.save() +} + +/// The changes after `since` as bytes that, appended to a save, load with it (the +/// document file's appends). +pub(crate) fn save_since(doc: &mut Document, since: &[ChangeHash]) -> Vec<u8> { + doc.save_after(since) +} + +/// A saved document (`save`), loaded to write to under a writer ID of its own: one ID +/// must never write in two places, and a save may be loaded on several nodes, or again +/// after the node that saved it wrote on. +pub(crate) fn load(bytes: &[u8]) -> anyhow::Result<Document> { + Ok(Document::load(bytes)?.with_actor(ActorId::random())) +} + +/// The actor (writer ID) `doc` writes under. +pub(crate) fn actor(doc: &Document) -> ActorId { + doc.get_actor().clone() +} + pub(crate) mod changes; pub(crate) mod file; pub(crate) mod lock; diff --git a/src/document/replica.rs b/src/document/replica.rs index 5d3d7a7..5a87016 100644 --- a/src/document/replica.rs +++ b/src/document/replica.rs @@ -101,7 +101,7 @@ impl fmt::Debug for Epoch { /// The heads are cached: `AutoCommit::get_heads` takes `&mut self` (it closes a pending /// transaction), so without the cache every keepalive and status read would need the /// write lock. Every path that changes the document under the write lock calls -/// [`Replica::refresh_heads`] before releasing it (`AppState::write_doc`, `mutate_doc`, +/// [`Replica::refresh_heads`] before releasing it (`AppState::mutate_doc`, /// `switch_epoch`, the sync apply); readers take the read lock and use /// [`Replica::heads`]. /// |
