Skip to main content

musefs_format/ogg/
b64.rs

1//! Incremental base64 serving for embedded art: given a requested window of the
2//! *output* base64 of an image, compute the bounded raw-input range to read and
3//! how to trim the re-encoded result. base64 encodes each 3 input bytes into 4
4//! output chars independently, so any output window `[o, o+len)` depends only on
5//! input bytes `[⌊o/4⌋·3 .. ⌈(o+len)/4⌉·3)` (clipped to the image length, whose
6//! final partial group yields the canonical `=` padding).
7
8use base64::Engine;
9
10/// The raw-input read plan for an output base64 window.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct B64Window {
13    /// First raw input byte to read.
14    pub in_start: u64,
15    /// Number of raw input bytes to read (clipped to the image length).
16    pub in_len: u64,
17    /// Leading base64 chars to drop after encoding the read bytes.
18    pub skip: usize,
19}
20
21/// Compute the input read plan to serve output base64 chars `[out_offset,
22/// out_offset+take)` of `base64(image)`, where the image is `img_total` bytes.
23pub fn b64_window(out_offset: u64, take: u64, img_total: u64) -> B64Window {
24    debug_assert!(take > 0);
25    let g0 = out_offset / 4;
26    let g1 = (out_offset + take - 1) / 4;
27    let in_start = g0 * 3;
28    let in_end = ((g1 + 1) * 3).min(img_total);
29    B64Window {
30        in_start,
31        in_len: in_end.saturating_sub(in_start),
32        skip: crate::convert::usize_from(out_offset - g0 * 4),
33    }
34}
35
36/// Encode `raw` (the bytes named by a `B64Window`) and return exactly `take`
37/// output chars starting at `skip`.
38pub fn encode_b64_slice(raw: &[u8], skip: usize, take: usize) -> Vec<u8> {
39    let enc = base64::engine::general_purpose::STANDARD.encode(raw);
40    enc.as_bytes()[skip..skip + take].to_vec()
41}
42
43/// Total base64 output length for an image of `img_total` bytes, or `None` if it
44/// overflows `u64`. Only an adversarial `img_total` can overflow; every real
45/// image is far below this.
46pub fn b64_len_checked(img_total: u64) -> Option<u64> {
47    img_total.div_ceil(3).checked_mul(4)
48}
49
50/// Total base64 output length for an image of `img_total` bytes.
51pub fn b64_len(img_total: u64) -> u64 {
52    b64_len_checked(img_total).expect("base64 output length fits u64")
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use base64::Engine;
59
60    fn full_b64(img: &[u8]) -> Vec<u8> {
61        base64::engine::general_purpose::STANDARD
62            .encode(img)
63            .into_bytes()
64    }
65
66    #[test]
67    fn any_window_matches_substring_of_full_encode() {
68        // Cover image lengths that hit every length-mod-3 case and various windows.
69        for &img_total in &[0u64, 1, 2, 3, 4, 5, 6, 7, 100, 257, 1024] {
70            let img: Vec<u8> = (0..img_total)
71                .map(|i| u8::try_from((i * 7 + 3) % 256).unwrap())
72                .collect();
73            let full = full_b64(&img);
74            assert_eq!(crate::convert::usize_from(b64_len(img_total)), full.len());
75            if full.is_empty() {
76                continue;
77            }
78            for o in 0..full.len() as u64 {
79                for take in 1..=(full.len() as u64 - o) {
80                    let w = b64_window(o, take, img_total);
81                    let raw = &img[crate::convert::usize_from(w.in_start)
82                        ..crate::convert::usize_from(w.in_start + w.in_len)];
83                    let got = encode_b64_slice(raw, w.skip, crate::convert::usize_from(take));
84                    assert_eq!(
85                        got,
86                        &full[crate::convert::usize_from(o)..crate::convert::usize_from(o + take)],
87                        "img_total={img_total} o={o} take={take}"
88                    );
89                }
90            }
91        }
92    }
93
94    #[test]
95    fn b64_window_fields_are_exact_at_group_boundaries() {
96        // out_offset and take chosen so the -1 and /4 in g1 are observable.
97        // g0 = out_offset/4, g1 = (out_offset+take-1)/4,
98        // in_start = g0*3, in_end = min((g1+1)*3, img_total), skip = out_offset - g0*4.
99        let img_total = 1024u64;
100
101        // take=1 at offset 0: g1 = 0 (with -1). The +1 mutant gives g1=0 too here,
102        // so choose offset 3 take=1: g0=0,g1=0 vs +1 mutant g1=1 -> in_len differs.
103        let w = b64_window(3, 1, img_total);
104        assert_eq!(
105            w,
106            B64Window {
107                in_start: 0,
108                in_len: 3,
109                skip: 3
110            }
111        );
112
113        // take exactly fills group 0 (offset 0, take 4): g1=0; mutant take+1 -> g1=1.
114        let w = b64_window(0, 4, img_total);
115        assert_eq!(
116            w,
117            B64Window {
118                in_start: 0,
119                in_len: 3,
120                skip: 0
121            }
122        );
123
124        // offset 4 take 4 -> g0=1,g1=1 -> in_start=3,in_len=3,skip=0; /4->*4 mutant
125        // makes g1 huge -> in_len clamps to img_total-3 (differs).
126        let w = b64_window(4, 4, img_total);
127        assert_eq!(
128            w,
129            B64Window {
130                in_start: 3,
131                in_len: 3,
132                skip: 0
133            }
134        );
135
136        // Window spanning two groups: offset 2 take 6 -> g0=0,g1=1 -> in 0..6.
137        let w = b64_window(2, 6, img_total);
138        assert_eq!(
139            w,
140            B64Window {
141                in_start: 0,
142                in_len: 6,
143                skip: 2
144            }
145        );
146    }
147
148    #[test]
149    fn b64_window_is_overflow_free_at_the_max_validated_boundary() {
150        // For any layout that passes RegionLayout::validate, an OggArtSlice
151        // satisfies offset + len <= b64_len(art_total) AND b64_len_checked(art_total)
152        // is Some. Under those bounds b64_window's internal +/* cannot overflow.
153        // Pin the worst case: the largest art_total whose b64_len still fits u64,
154        // reading the final 4 output chars. In debug, any intermediate overflow
155        // would panic here.
156        let art_total = u64::MAX / 4 * 3; // b64_len_checked(art_total) is Some
157        assert!(b64_len_checked(art_total).is_some());
158        let total = b64_len(art_total);
159        let w = b64_window(total - 4, 4, art_total);
160        assert!(w.in_start <= art_total);
161        assert!(w.in_len <= art_total);
162    }
163}