based64/lib.rs
1//!BASE64 library for chads
2//!
3//!## Features
4//!
5//!- `alloc` - Enables usage of heap based collections;
6//!
7//!## API
8//!
9//!- [raw](raw) - Contains functions to work with raw pointers. Mostly unsafe.
10//!- [uninit](uninit) - Contains functions to work with unintialized slices.
11//!- [vec](vec) - Contains high level functions that returns `Vec`. Requires `alloc` feature.
12//!- [string](string) - Contains high level functions that returns `String`. Requires `alloc` feature.
13//!- [Codec](Codec) - Wrapper that allows to pre-built lookup table for decoding. Useful if you want to safe tiny bit on building lookup table.
14
15#![no_std]
16#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
17#![cfg_attr(rustfmt, rustfmt_skip)]
18
19pub mod raw;
20pub mod uninit;
21#[cfg(feature = "alloc")]
22pub mod vec;
23#[cfg(feature = "alloc")]
24pub mod string;
25
26use core::mem;
27
28///Base64 padding character
29pub const PAD: u8 = b'=';
30///Default character table used by based64
31pub static STANDARD_TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
32///Alternative table URL safe.
33pub static URL_TABLE: &[u8; 64] = b"ABCDEFGHIJLKMNOPQRSTUVWXYZabcdefghijlkmnopqrstuvwxyz0123456789-_";
34///Codec which uses `STANDARD_TABLE`
35pub static STANDARD_CODEC: Codec<'static> = Codec::new(STANDARD_TABLE);
36///Codec which uses `URL_TABLE`
37pub static URL_CODEC: Codec<'static> = Codec::new(URL_TABLE);
38
39#[inline]
40///Validates custom character table by requiring user to provide table of specific size
41///containing only ASCII characters.
42pub const fn assert_valid_character_table(table: &[u8; 64]) -> bool {
43 let mut idx = 0;
44 while idx < table.len() {
45 let ch = table[idx];
46 if !ch.is_ascii() || ch == PAD {
47 return false
48 }
49
50 idx += 1;
51 }
52
53 true
54}
55
56const REVERSE_TABLE_SIZE: usize = (u8::max_value() as usize) + 1;
57#[inline(always)]
58const fn build_reverse_table(table: &[u8; 64]) -> [i8; REVERSE_TABLE_SIZE] {
59 let mut reverse_table = [-1i8; (u8::max_value() as usize) + 1];
60
61 let mut idx = 0;
62 loop {
63 let byte = table[idx] as usize;
64 reverse_table[byte] = idx as i8;
65 idx += 1;
66
67 if idx >= table.len() {
68 break;
69 }
70 }
71
72 reverse_table
73}
74
75#[inline(always)]
76///Returns number of bytes necessary to encode input of provided size (including padding).
77///
78///On overflow returns wrapped value.
79pub const fn encode_len(input: usize) -> usize {
80 input.wrapping_mul(4).wrapping_div(3).wrapping_add(3) & !3
81}
82
83///Returns number of bytes necessary to decode provided input.
84///
85///In case of not rounded input, assumes the worst.
86pub const fn decode_len(input: &[u8]) -> usize {
87 let len = input.len();
88 if len == 0 {
89 return 0
90 }
91
92 let unused_num = len & 3;
93 if unused_num != 0 {
94 //unpadded (probably)
95 len.wrapping_div(4).wrapping_mul(3) + unused_num
96 } else {
97 //padded so it is simply
98 //len / 4 * 3
99 let result = len.wrapping_div(4).wrapping_mul(3);
100 if input[len - 1] != PAD {
101 result
102 } else if input[len - 2] != PAD {
103 result - 1
104 } else if input[len - 3] != PAD {
105 result - 2
106 } else {
107 result - 3
108 }
109 }
110}
111
112///Encoding function writing to slice.
113///
114///# Arguments
115///
116///- `src` - Input to encode;
117///- `dst` - Output to write;
118///
119///# Result
120///
121///Returns `Some` if successful, containing number of bytes written.
122///
123///Returns `None` if data cannot be encoded due to insufficient buffer size or size calculation overflow happens.
124#[inline]
125pub fn encode(table: &[u8; 64], src: &[u8], dst: &mut [u8]) -> Option<usize> {
126 unsafe {
127 uninit::encode(table, src, mem::transmute(dst))
128 }
129}
130
131///Decoding function writing to slice.
132///
133///# Arguments
134///
135///- `src` - Input to decode;
136///- `dst` - Output to write;
137///
138///# Result
139///Returns `Some` if successful, containing number of bytes written.
140///
141///Returns `None` if data cannot be encoded due to insufficient buffer size or invalid input.
142#[inline]
143pub fn decode(table: &[u8; 64], src: &[u8], dst: &mut [u8]) -> Option<usize> {
144 unsafe {
145 uninit::decode(table, src, mem::transmute(dst))
146 }
147}
148
149///BASE64 codec
150#[derive(Copy, Clone)]
151pub struct Codec<'a> {
152 table: &'a [u8; 64],
153 reverse: [i8; REVERSE_TABLE_SIZE]
154}
155
156impl<'a> Codec<'a> {
157 #[inline(always)]
158 ///Creates new codec, validating that table contains only ASCII characters.
159 pub const fn new(table: &'a [u8; 64]) -> Self {
160 assert!(assert_valid_character_table(table));
161 Self {
162 table,
163 reverse: build_reverse_table(table),
164 }
165 }
166
167 #[inline(always)]
168 ///Access prebuilt instance of codec with `STANDARD_TABLE`
169 pub fn standard() -> &'static Codec<'static> {
170 &STANDARD_CODEC
171 }
172
173 #[inline(always)]
174 ///Access prebuilt instance of codec with `URL_TABLE`
175 pub fn url_usafe() -> &'static Codec<'static> {
176 &URL_CODEC
177 }
178}
179
180impl<'a> Codec<'a> {
181 ///Encoding function writing to slice.
182 ///
183 ///# Arguments
184 ///
185 ///- `src` - Input to encode;
186 ///- `dst` - Output to write;
187 ///
188 ///# Result
189 ///
190 ///Returns `Some` if successful, containing number of bytes written.
191 ///
192 ///Returns `None` if data cannot be encoded due to insufficient buffer size or size calculation overflow happens.
193 #[inline(always)]
194 pub fn encode_to(&self, src: &[u8], dst: &mut [u8]) -> Option<usize> {
195 encode(self.table, src, dst)
196 }
197
198 ///Decoding function writing to slice.
199 ///
200 ///# Arguments
201 ///
202 ///- `src` - Input to decode;
203 ///- `dst` - Output to write;
204 ///
205 ///# Result
206 ///Returns `Some` if successful, containing number of bytes written.
207 ///
208 ///Returns `None` if data cannot be encoded due to insufficient buffer size or invalid input.
209 #[inline]
210 pub fn decode_to(&self, src: &[u8], dst: &mut [u8]) -> Option<usize> {
211 unsafe {
212 self.decode_to_uninit(src, mem::transmute(dst))
213 }
214 }
215}