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`. Returns `None` when the encoded output is
38/// shorter than `skip + take` — i.e. `raw` was shorter than the window the
39/// caller resolved against `art_total` (a truncated art blob). A checked read
40/// rather than a panic, so the serve path can surface this as `BackingChanged`
41/// like the other base64-art arms (#526).
42pub fn encode_b64_slice(raw: &[u8], skip: usize, take: usize) -> Option<Vec<u8>> {
43    let enc = base64::engine::general_purpose::STANDARD.encode(raw);
44    let end = skip.checked_add(take)?;
45    enc.as_bytes().get(skip..end).map(<[u8]>::to_vec)
46}
47
48/// Total base64 output length for an image of `img_total` bytes, or `None` if it
49/// overflows `u64`. Only an adversarial `img_total` can overflow; every real
50/// image is far below this.
51pub fn b64_len_checked(img_total: u64) -> Option<u64> {
52    img_total.div_ceil(3).checked_mul(4)
53}
54
55/// Total base64 output length for an image of `img_total` bytes.
56pub fn b64_len(img_total: u64) -> u64 {
57    b64_len_checked(img_total).expect("base64 output length fits u64")
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use base64::Engine;
64
65    fn full_b64(img: &[u8]) -> Vec<u8> {
66        base64::engine::general_purpose::STANDARD
67            .encode(img)
68            .into_bytes()
69    }
70
71    #[test]
72    fn any_window_matches_substring_of_full_encode() {
73        // Cover image lengths that hit every length-mod-3 case and various windows.
74        for &img_total in &[0u64, 1, 2, 3, 4, 5, 6, 7, 100, 257, 1024] {
75            let img: Vec<u8> = (0..img_total)
76                .map(|i| u8::try_from((i * 7 + 3) % 256).unwrap())
77                .collect();
78            let full = full_b64(&img);
79            assert_eq!(crate::convert::usize_from(b64_len(img_total)), full.len());
80            if full.is_empty() {
81                continue;
82            }
83            for o in 0..full.len() as u64 {
84                for take in 1..=(full.len() as u64 - o) {
85                    let w = b64_window(o, take, img_total);
86                    let raw = &img[crate::convert::usize_from(w.in_start)
87                        ..crate::convert::usize_from(w.in_start + w.in_len)];
88                    let got = encode_b64_slice(raw, w.skip, crate::convert::usize_from(take))
89                        .expect("window lies within the encoded output");
90                    assert_eq!(
91                        got,
92                        &full[crate::convert::usize_from(o)..crate::convert::usize_from(o + take)],
93                        "img_total={img_total} o={o} take={take}"
94                    );
95                }
96            }
97        }
98    }
99
100    #[test]
101    fn encode_b64_slice_returns_none_when_window_exceeds_output() {
102        // A 3-byte blob encodes to exactly 4 base64 chars ("YWJj"). A window that
103        // runs past that — as it would for an art blob shorter than its resolved
104        // `art_total` — must return None rather than panic on an out-of-range
105        // slice (#526).
106        assert_eq!(encode_b64_slice(b"abc", 0, 4), Some(b"YWJj".to_vec()));
107        assert_eq!(encode_b64_slice(b"abc", 2, 4), None);
108        assert_eq!(encode_b64_slice(b"abc", 5, 1), None);
109    }
110
111    #[test]
112    fn b64_window_fields_are_exact_at_group_boundaries() {
113        // out_offset and take chosen so the -1 and /4 in g1 are observable.
114        // g0 = out_offset/4, g1 = (out_offset+take-1)/4,
115        // in_start = g0*3, in_end = min((g1+1)*3, img_total), skip = out_offset - g0*4.
116        let img_total = 1024u64;
117
118        // take=1 at offset 0: g1 = 0 (with -1). The +1 mutant gives g1=0 too here,
119        // so choose offset 3 take=1: g0=0,g1=0 vs +1 mutant g1=1 -> in_len differs.
120        let w = b64_window(3, 1, img_total);
121        assert_eq!(
122            w,
123            B64Window {
124                in_start: 0,
125                in_len: 3,
126                skip: 3
127            }
128        );
129
130        // take exactly fills group 0 (offset 0, take 4): g1=0; mutant take+1 -> g1=1.
131        let w = b64_window(0, 4, img_total);
132        assert_eq!(
133            w,
134            B64Window {
135                in_start: 0,
136                in_len: 3,
137                skip: 0
138            }
139        );
140
141        // offset 4 take 4 -> g0=1,g1=1 -> in_start=3,in_len=3,skip=0; /4->*4 mutant
142        // makes g1 huge -> in_len clamps to img_total-3 (differs).
143        let w = b64_window(4, 4, img_total);
144        assert_eq!(
145            w,
146            B64Window {
147                in_start: 3,
148                in_len: 3,
149                skip: 0
150            }
151        );
152
153        // Window spanning two groups: offset 2 take 6 -> g0=0,g1=1 -> in 0..6.
154        let w = b64_window(2, 6, img_total);
155        assert_eq!(
156            w,
157            B64Window {
158                in_start: 0,
159                in_len: 6,
160                skip: 2
161            }
162        );
163    }
164
165    #[test]
166    fn b64_window_is_overflow_free_at_the_max_validated_boundary() {
167        // For any layout that passes RegionLayout::validate, an OggArtSlice
168        // satisfies offset + len <= b64_len(art_total) AND b64_len_checked(art_total)
169        // is Some. Under those bounds b64_window's internal +/* cannot overflow.
170        // Pin the worst case: the largest art_total whose b64_len still fits u64,
171        // reading the final 4 output chars. In debug, any intermediate overflow
172        // would panic here.
173        let art_total = u64::MAX / 4 * 3; // b64_len_checked(art_total) is Some
174        assert!(b64_len_checked(art_total).is_some());
175        let total = b64_len(art_total);
176        let w = b64_window(total - 4, 4, art_total);
177        assert!(w.in_start <= art_total);
178        assert!(w.in_len <= art_total);
179    }
180}