//! [`Transducer`] — the conversion strategy used by //! [`crate::host_store::HostStore`] on content-bearing methods. //! //! A transducer maps between a file's logical (user-visible) bytes and the //! bytes actually persisted on disk. The persistence layer invokes it inside //! the few [`fskit_rs::Filesystem`] methods that handle user content //! (currently `read` and `write`); every other method bypasses it. //! //! "Transducer" is borrowed from automata theory: a state machine that //! converts an input stream into an output stream. The current trait shape is //! whole-payload `bytes -> bytes` and our two impls happen to be pure, but //! the name leaves room for stateful, side-effecting implementations later //! (e.g. a CRDT transducer that records local edits as operations and //! rematerializes them on read). /// Maps file content between its logical (user-visible) form and the bytes /// actually written to disk. Whole-file: implementations get the full /// payload and return the full transformed payload; the persistence layer /// handles all offset/splice math. /// /// Implementations may be stateful and side-effecting; the `&self` receiver /// is intentional — anything mutable should live behind interior mutability /// (e.g. `Mutex`) so the transducer can be cloned across connections. pub trait Transducer: Send + Sync + Clone + 'static { /// Logical bytes -> bytes to persist on disk. fn encode(&self, logical: &[u8]) -> Vec; /// Persisted bytes -> logical bytes to return to the caller. fn decode(&self, on_disk: &[u8]) -> Vec; } /// Pass-through transducer: on-disk bytes equal logical bytes. #[derive(Clone, Copy, Debug, Default)] pub struct Identity; impl Transducer for Identity { fn encode(&self, logical: &[u8]) -> Vec { logical.to_vec() } fn decode(&self, on_disk: &[u8]) -> Vec { on_disk.to_vec() } } /// Reverses every byte sequence passing through it. `encode == decode`, /// since reversing twice yields the original. #[derive(Clone, Copy, Debug, Default)] pub struct Reverse; impl Transducer for Reverse { fn encode(&self, logical: &[u8]) -> Vec { logical.iter().rev().copied().collect() } fn decode(&self, on_disk: &[u8]) -> Vec { on_disk.iter().rev().copied().collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn identity_round_trip() { let t = Identity; let bytes: &[u8] = b"hello world"; assert_eq!(t.decode(&t.encode(bytes)), bytes); assert_eq!(t.encode(bytes), bytes); } #[test] fn reverse_round_trip() { let t = Reverse; let bytes: &[u8] = b"hello"; assert_eq!(t.encode(bytes), b"olleh"); assert_eq!(t.decode(b"olleh"), b"hello"); assert_eq!(t.decode(&t.encode(bytes)), bytes); } }