1use std::error::Error;
4use std::fmt::{self, Display};
5
6mod internal;
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub enum DecodeError {
9 InvalidByte(usize, u8),
10 InvalidChunk(usize),
11 InvalidLength(usize),
12 InvalidTail,
13}
14
15impl Display for DecodeError {
16 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17 match self {
18 DecodeError::InvalidLength(length) => {
19 write!(f, "Z85 data length ({}) is not multiple of five", length)
20 }
21 DecodeError::InvalidByte(position, byte) => write!(
22 f,
23 "Z85 data has an invalid byte (0x{:02X}) at ({}) ",
24 byte, position
25 ),
26 DecodeError::InvalidChunk(position) => write!(
27 f,
28 "Z85 data has an invalid 5-bytes chunk at ({}) ",
29 position
30 ),
31 DecodeError::InvalidTail => write!(f, "Z85 data has an invalid padding chunk"),
32 }
33 }
34}
35
36impl Error for DecodeError {}
37
38impl DecodeError {
40 fn add_offset(&self, chunk_count: usize) -> Self {
41 use DecodeError::*;
42 let offset = chunk_count * 5;
43 match self {
44 InvalidByte(index, byte) => InvalidByte(index + offset, *byte),
45 InvalidChunk(index) => InvalidChunk(index + offset),
46 _ => *self,
47 }
48 }
49}
50
51pub fn encode<T: AsRef<[u8]>>(input: T) -> String {
53 let input = input.as_ref();
54 let length = input.len();
55 if length == 0 {
56 return String::with_capacity(0);
57 }
58 let tail_size = length % 4;
59 let chunked_size = length - tail_size;
60 let mut out = Vec::with_capacity(length / 4 * 5 + 5);
61 for chunk in input[..chunked_size].chunks(4) {
62 let z85_chunk = internal::encode_chunk(chunk);
63 out.extend_from_slice(&z85_chunk);
64 }
65 if tail_size > 0 {
66 let bintail = &input[chunked_size..];
67 let tail = internal::encode_tail(bintail);
68 out.extend_from_slice(&tail);
69 }
70
71 unsafe { String::from_utf8_unchecked(out) }
74}
75
76pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
78 let input = input.as_ref();
79 let length = input.len();
80 if length == 0 {
81 return Ok(Vec::with_capacity(0));
82 }
83 if length % 5 != 0 {
84 return Err(DecodeError::InvalidLength(length));
85 }
86 let has_tail = input[length - 5] == b'#';
87 let chunked_size = if has_tail { length - 5 } else { length };
88 let mut out = Vec::with_capacity(length / 5 * 4);
89 for (chunk_count, chunk) in input[..chunked_size].chunks(5).enumerate() {
90 match internal::decode_chunk(chunk) {
91 Err(decode_error) => return Err(decode_error.add_offset(chunk_count)),
92 Ok(binchunk) => out.extend_from_slice(&binchunk),
93 }
94 }
95 if has_tail {
96 let last_chunk = &input[chunked_size..];
97 match internal::decode_tail(last_chunk) {
98 Err(decode_error) => return Err(decode_error.add_offset(length - 5)),
99 Ok(bintail) => bintail.append_to_vec(&mut out),
100 }
101 }
102 Ok(out)
103}