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}