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}