Skip to main content

coven_format/
writes_slot.rs

1//! A writes slot's body codec, inner-write identities and ordering checks
2//! (D9).
3
4use crate::error::{exact_length, require, Error, Rule};
5use crate::path::ObjectPath;
6use crate::sequence::SlotHeader;
7use crate::value::positive;
8use crate::write::WriteHeader;
9
10/// The body's head: the slot header frame, then how many inner writes follow.
11pub const HEAD_LEN: usize = SlotHeader::ENCODED_LEN + 8;
12
13/// Encode the head of a body holding `count` inner writes, at least one.
14pub fn encode_head(header: &SlotHeader, path: &ObjectPath, count: u64) -> Result<Vec<u8>, Error> {
15    positive(count)?;
16    let mut bytes = header.encode(path)?;
17    bytes.extend_from_slice(&count.to_be_bytes());
18    Ok(bytes)
19}
20
21/// Decode exactly a body's head: its slot header and positive write count.
22pub fn decode_head(bytes: &[u8], path: &ObjectPath) -> Result<(SlotHeader, u64), Error> {
23    exact_length(bytes.len(), HEAD_LEN)?;
24    let (header, count) = bytes.split_at(SlotHeader::ENCODED_LEN);
25    let header = SlotHeader::decode(header, path)?;
26    let count = u64::from_be_bytes(count.try_into().expect("eight bytes"));
27    positive(count)?;
28    Ok((header, count))
29}
30
31/// The length field before each inner write.
32pub fn encode_write_length(length: u64) -> [u8; 8] {
33    length.to_be_bytes()
34}
35
36/// Decode an inner write's length field, refusing an empty write or one
37/// longer than the `remaining` body bytes after the field.
38pub fn decode_write_length(bytes: [u8; 8], remaining: u64) -> Result<u64, Error> {
39    let length = u64::from_be_bytes(bytes);
40    require(length > 0 && length <= remaining, "inner write length", Rule::StreamLength)?;
41    Ok(length)
42}
43
44/// Exact crypto identity of an inner write, independent of its containing slot.
45pub fn identity(
46    store: coven_foundation::id_source::StoreId,
47    write: coven_merge::WriteId,
48) -> Result<String, Error> {
49    crate::value::positive(write.number)?;
50    Ok(format!("coven/write/{store}/{}/{}", write.device.0, write.number))
51}
52
53/// Check one authenticated inner header's device, causal prefix and increasing
54/// number within a writes slot.
55pub fn check_write(
56    slot: &SlotHeader,
57    previous: Option<u64>,
58    header: &WriteHeader,
59) -> Result<(), Error> {
60    slot.check_write(header)?;
61    require(
62        previous.is_none_or(|number| number < header.position.number),
63        "batch write order",
64        Rule::Order,
65    )
66}
67
68#[cfg(test)]
69#[path = "writes_slot_tests.rs"]
70mod tests;