grit_lib/
git_binary_base85.rs1use thiserror::Error;
4
5const EN85: &[u8; 85] =
6 b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
7
8#[derive(Clone, Debug, Error, PartialEq, Eq)]
10pub enum DecodeError {
11 #[error("truncated base85 line")]
13 TruncatedLine,
14 #[error("invalid base85 alphabet byte {0}")]
16 InvalidAlphabetByte(u8),
17 #[error("base85 overflow")]
19 Overflow,
20 #[error("trailing base85 data")]
22 TrailingData,
23}
24
25fn prep_decode_table() -> [u8; 256] {
26 let mut de85 = [0u8; 256];
27 for (i, &c) in EN85.iter().enumerate() {
28 de85[c as usize] = (i + 1) as u8;
29 }
30 de85
31}
32
33#[must_use]
35pub fn encode(mut data: &[u8]) -> String {
36 let mut result = String::new();
37 while !data.is_empty() {
38 let mut acc: u32 = 0;
39 let mut cnt = 24i32;
40 while cnt >= 0 {
41 let ch = u32::from(data[0]);
42 data = &data[1..];
43 acc |= ch << cnt;
44 if data.is_empty() {
45 break;
46 }
47 cnt -= 8;
48 }
49 let mut buf = [0u8; 5];
50 for i in (0..5).rev() {
51 let val = (acc % 85) as usize;
52 acc /= 85;
53 buf[i] = EN85[val];
54 }
55 result.extend(buf.iter().map(|b| char::from(*b)));
56 }
57 result
58}
59
60pub fn decode_body(buffer: &[u8], mut out_len: usize) -> Result<Vec<u8>, DecodeError> {
64 static DE85: std::sync::OnceLock<[u8; 256]> = std::sync::OnceLock::new();
65 let de85 = DE85.get_or_init(prep_decode_table);
66
67 let mut dst = Vec::with_capacity(out_len);
68 let mut pos = 0usize;
69
70 while out_len > 0 {
71 let mut acc: u32 = 0;
72 for _ in 0..4 {
73 let ch = *buffer.get(pos).ok_or(DecodeError::TruncatedLine)?;
74 pos += 1;
75 let de = de85[ch as usize];
76 if de == 0 {
77 return Err(DecodeError::InvalidAlphabetByte(ch));
78 }
79 acc = acc
80 .checked_mul(85)
81 .and_then(|a| a.checked_add(u32::from(de - 1)))
82 .ok_or(DecodeError::Overflow)?;
83 }
84 let ch = *buffer.get(pos).ok_or(DecodeError::TruncatedLine)?;
85 pos += 1;
86 let de = de85[ch as usize];
87 if de == 0 {
88 return Err(DecodeError::InvalidAlphabetByte(ch));
89 }
90 acc = acc
91 .checked_mul(85)
92 .and_then(|a| a.checked_add(u32::from(de - 1)))
93 .ok_or(DecodeError::Overflow)?;
94
95 let chunk = out_len.min(4);
96 out_len -= chunk;
97 let mut a = acc;
98 for _ in 0..chunk {
99 a = a.rotate_left(8);
100 dst.push(a as u8);
101 }
102 }
103
104 if pos != buffer.len() {
105 return Err(DecodeError::TrailingData);
106 }
107 Ok(dst)
108}