macro_rules! code_name { () => { "debt64" }}
macro_rules! version { () => { "2.0.0" }}
pub const NAME: &'static str = "Debt64";
pub const CODE_NAME: &'static str = code_name!();
pub const VERSION: &'static str = version!();
pub const RELEASE_DATE: (u16, u8, u8) = (2019, 3, 28);
pub const UUID: &'static str = "6e07c48e-cc38-45d0-8265-4bc1a82def63";
pub const TAG: &'static str = concat!(code_name!(), "::6e07c48e::", version!());
#[test]
fn test_crate_version() {
assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
}
mod encoder;
mod decoder;
pub use encoder::*;
pub use decoder::*;
pub mod version_info;
const BASE62_DIGITS: [char; 62] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
];
const PAD_CHAR: char = '=';
#[derive(Debug, Eq, PartialEq)]
struct LastChars {
first: char,
last: char,
}
#[derive(Debug)]
pub enum Kind {
Standard,
IMAP,
MIME,
URL,
FreenetURL,
}
impl Kind {
const STANDARD_LAST_CHARS: LastChars = LastChars { first: '+', last: '/' };
const IMAP_LAST_CHARS: LastChars = LastChars { first: '+', last: ',' };
const MIME_LAST_CHARS: LastChars = LastChars { first: '+', last: '/' };
const URL_LAST_CHARS: LastChars = LastChars { first: '-', last: '_' };
const FREENET_URL_LAST_CHARS: LastChars = LastChars { first: '~', last: '-' };
const MIME_LINE_SEPARATORS: [char; 2] = ['\r', '\n'];
fn last_chars(&self) -> &LastChars {
match *self {
Kind::Standard => &Self::STANDARD_LAST_CHARS,
Kind::IMAP => &Self::IMAP_LAST_CHARS,
Kind::MIME => &Self::MIME_LAST_CHARS,
Kind::URL => &Self::URL_LAST_CHARS,
Kind::FreenetURL => &Self::FREENET_URL_LAST_CHARS,
}
}
fn line_separators(&self) -> Option<&[char]> {
match *self {
Kind::Standard => None,
Kind::IMAP => None,
Kind::MIME => Some(&Self::MIME_LINE_SEPARATORS),
Kind::URL => None,
Kind::FreenetURL => None,
}
}
fn max_line_len(&self) -> Option<usize> {
match *self {
Kind::Standard => None,
Kind::IMAP => None,
Kind::MIME => Some(76),
Kind::URL => None,
Kind::FreenetURL => None,
}
}
fn must_use_pad(&self) -> bool {
match *self {
Kind::Standard => true,
Kind::IMAP => false,
Kind::MIME => true,
Kind::URL => false,
Kind::FreenetURL => true,
}
}
fn can_use_pad(&self) -> bool {
match *self {
Kind::Standard => true,
Kind::IMAP => false,
Kind::MIME => true,
Kind::URL => true,
Kind::FreenetURL => true,
}
}
fn get_char(index: usize, last_chars: &LastChars) -> &char {
match index {
0...61 => &BASE62_DIGITS[index],
62 => &last_chars.first,
_ => &last_chars.last,
}
}
fn allows_invalid_chars(&self) -> bool {
match *self {
Kind::Standard => false,
Kind::IMAP => false,
Kind::MIME => true,
Kind::URL => false,
Kind::FreenetURL => false,
}
}
pub fn estimate_encoding_capacity(&self, bytes: impl AsRef<[u8]>) -> usize {
let result = bytes.as_ref().len();
if result == 0 {
return 0;
}
let result = (result / 3).saturating_mul(4).saturating_add(match result % 3 {
0 => 0,
other => match self.must_use_pad() {
true => 4,
false => other + 1,
},
});
match (self.max_line_len().as_ref(), self.line_separators().as_ref()) {
(Some(max_line_len), Some(line_separators)) => result.saturating_add(
(result / max_line_len).saturating_sub(if result % max_line_len == 0 { 1 } else { 0 }).saturating_mul(line_separators.len())
),
_ => result,
}
}
pub fn estimate_decoding_capacity(&self, bytes: impl AsRef<[u8]>) -> usize {
let result = bytes.as_ref().len();
if result == 0 {
return 0;
}
let line_separators = match (self.max_line_len().as_ref(), self.line_separators().as_ref()) {
(Some(max_line_len), Some(line_separators)) => {
let line_len = max_line_len.saturating_add(line_separators.len());
(result / line_len).saturating_mul(line_separators.len())
},
_ => 0,
};
let result = result.saturating_sub(line_separators);
let result = (result / 4).saturating_mul(3).saturating_add(match result % 4 {
0 | 1 => 0,
2 => 1,
_ => 2,
});
result
}
}
#[test]
fn test_base62_digits() {
for (i, b) in (b'A'..b'Z').enumerate() {
assert_eq!(b as char, BASE62_DIGITS[i]);
}
for (i, b) in (b'a'..b'z').enumerate() {
assert_eq!(b as char, BASE62_DIGITS[i + 26]);
}
for (i, b) in (b'0'..b'9').enumerate() {
assert_eq!(b as char, BASE62_DIGITS[i + 52]);
}
}
#[test]
fn test_kind_last_chars() {
assert_eq!(Kind::Standard.last_chars(), &LastChars { first: '+', last: '/' });
assert_eq!(Kind::IMAP.last_chars(), &LastChars { first: '+', last: ',' });
assert_eq!(Kind::MIME.last_chars(), &LastChars { first: '+', last: '/' });
assert_eq!(Kind::URL.last_chars(), &LastChars { first: '-', last: '_' });
assert_eq!(Kind::FreenetURL.last_chars(), &LastChars { first: '~', last: '-' });
}
#[test]
fn test_kind_line_separators() {
assert_eq!(Kind::Standard.line_separators(), None);
assert_eq!(Kind::IMAP.line_separators(), None);
assert_eq!(Kind::MIME.line_separators().unwrap(), &['\r', '\n']);
assert_eq!(Kind::URL.line_separators(), None);
assert_eq!(Kind::FreenetURL.line_separators(), None);
}
#[test]
fn test_kind_max_line_len() {
assert_eq!(Kind::Standard.max_line_len(), None);
assert_eq!(Kind::IMAP.max_line_len(), None);
assert_eq!(Kind::MIME.max_line_len(), Some(76));
assert_eq!(Kind::URL.max_line_len(), None);
assert_eq!(Kind::FreenetURL.max_line_len(), None);
}
#[test]
fn test_kind_pad_char() {
assert_eq!(Kind::Standard.must_use_pad(), true);
assert_eq!(Kind::Standard.can_use_pad(), true);
assert_eq!(Kind::IMAP.must_use_pad(), false);
assert_eq!(Kind::IMAP.can_use_pad(), false);
assert_eq!(Kind::MIME.must_use_pad(), true);
assert_eq!(Kind::MIME.can_use_pad(), true);
assert_eq!(Kind::URL.must_use_pad(), false);
assert_eq!(Kind::URL.can_use_pad(), true);
assert_eq!(Kind::FreenetURL.must_use_pad(), true);
assert_eq!(Kind::FreenetURL.can_use_pad(), true);
}
#[test]
fn test_kind_invalid_chars() {
assert_eq!(Kind::Standard.allows_invalid_chars(), false);
assert_eq!(Kind::IMAP.allows_invalid_chars(), false);
assert_eq!(Kind::MIME.allows_invalid_chars(), true);
assert_eq!(Kind::URL.allows_invalid_chars(), false);
assert_eq!(Kind::FreenetURL.allows_invalid_chars(), false);
}