Skip to main content

macroonz_compiler/identity/
encode.rs

1//! The one canonical framing, and the two citation encodings written with it.
2//!
3//! Every canonical encoding anywhere in this crate — a captured tree, a planned membership, a rendered unit, a transcript — is written through the two primitives here.
4//! One framing rather than one per home is what keeps the concatenation collision from being reintroduced locally: a home that invented its own length spelling would admit two byte strings for one value without anything else noticing.
5
6use super::{OwnerFact, OwnerIdentity, Profile};
7
8/// Append one length as eight big-endian bytes.
9///
10/// A fixed width rather than a varint, because a canonical encoding that admitted two spellings of one length would admit two preimages for one value.
11pub fn encode_length(length: usize, into: &mut Vec<u8>) {
12    into.extend_from_slice(&u64::try_from(length).unwrap_or(u64::MAX).to_be_bytes());
13}
14
15/// Append one length-prefixed byte string: the eight-byte length, then the bytes.
16///
17/// Without the prefix, two members could be split at a different boundary and encode identically — the concatenation collision the prefix removes outright.
18pub fn encode_bytes(material: &[u8], into: &mut Vec<u8>) {
19    encode_length(material.len(), into);
20    into.extend_from_slice(material);
21}
22
23impl Profile {
24    /// Appends this profile's canonical bytes: the stem of whoever owns the grammar, its declared name, then its version position in four big-endian bytes.
25    ///
26    /// Seated with the type on purpose: every identity home that commits to a profile writes it through this one road, so a lawful grammar edit moves every identity family at once rather than splitting the homes that restated the spelling from the homes that did not.
27    pub fn encode_into(self, into: &mut Vec<u8>) {
28        encode_bytes(self.stem().as_bytes(), into);
29        encode_bytes(self.name().as_bytes(), into);
30        into.extend_from_slice(&self.version().position().to_be_bytes());
31    }
32}
33
34impl OwnerFact {
35    /// The canonical bytes of this citation, for a transcript to be taken over.
36    #[must_use]
37    pub fn citation_bytes(&self) -> Vec<u8> {
38        let mut bytes = Vec::new();
39        encode_bytes(self.home.as_bytes(), &mut bytes);
40        encode_bytes(self.name.as_bytes(), &mut bytes);
41        bytes
42    }
43}
44
45impl OwnerIdentity {
46    /// The canonical bytes of this citation, for a transcript to be taken over.
47    ///
48    /// The subject is framed ahead of the identity, so one consumer's thirty-two bytes cited under two subjects are two citations rather than one.
49    #[must_use]
50    pub fn citation_bytes(&self) -> Vec<u8> {
51        let mut bytes = Vec::new();
52        encode_bytes(self.subject.as_bytes(), &mut bytes);
53        encode_bytes(&self.bytes, &mut bytes);
54        bytes
55    }
56}