#![no_std]
#![allow(clippy::doc_markdown)]
#![allow(clippy::wildcard_imports)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::items_after_statements)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_alias))]
#[cfg(feature = "alloc")]
extern crate alloc;
use core::error;
use core::fmt;
use core::marker;
use core::slice;
use core::str;
#[cfg(feature = "alloc")]
pub(crate) mod __private {
pub use alloc::string::String;
pub use alloc::vec;
pub use alloc::vec::Vec;
}
#[cfg(feature = "alloc")]
pub(crate) use __private::*;
#[cfg(feature = "check")]
pub mod checksum {
use sha2::Sha256;
use super::*;
pub const BYTE_LENGTH: usize = 4;
pub type Checksum = [u8; BYTE_LENGTH];
#[inline]
#[must_use]
pub const fn compute(bytes: &[u8], version: u8) -> Checksum {
let buffer = Sha256::new().update(&[version]).update(bytes).finalize();
let hash = Sha256::new().update(&buffer).finalize();
from_slice(&hash)
}
#[inline]
#[must_use]
pub const fn from_slice(bytes: &[u8]) -> Checksum {
let mut sum = [0u8; BYTE_LENGTH];
__internal::memcpy(&mut sum, 0, bytes, 0, BYTE_LENGTH);
sum
}
}
pub(crate) const ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
pub(crate) const BYTE_MAP: [i8; 128] = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
-1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20,
21, 0, 22, 23, 24, 25, 26, -1, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1,
10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, 22, 23, 24, 25,
26, -1, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Error {
BufferTooSmall { min: usize, len: usize },
InvalidDataSize { expected: usize, got: usize },
InvalidCharacter { char: char, index: usize },
MissingPrefix { char: char, got: Option<char> },
#[cfg(feature = "check")]
InvalidVersion { expected: &'static str, version: u8 },
#[cfg(feature = "check")]
InsufficientData { min: usize, len: usize },
#[cfg(feature = "check")]
ChecksumMismatch {
expected: checksum::Checksum,
got: checksum::Checksum,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BufferTooSmall { min, len } => {
write!(f, "Buffer size '{len}' is less than required '{min}'")
}
Self::InvalidDataSize { expected, got } => {
write!(f, "Invalid data size '{got}', expected: '{expected}'")
}
Self::InvalidCharacter { char, index } => {
write!(f, "Invalid character '{char}' at position {index}")
}
Self::MissingPrefix { char, got } => {
write!(f, "Expected prefix '{char}', found '{got:?}'")
}
#[cfg(feature = "check")]
Self::InvalidVersion { expected, version } => {
write!(f, "Invalid version byte '{version}': {expected}")
}
#[cfg(feature = "check")]
Self::InsufficientData { min, len } => {
write!(f, "Input size '{len}' is less than required '{min}'")
}
#[cfg(feature = "check")]
Self::ChecksumMismatch { expected, got } => {
write!(f, "Expected checksum '{expected:?}', got '{got:?}'")
}
}
}
}
impl error::Error for Error {}
pub type Result<T> = core::result::Result<T, Error>;
pub trait Encoding<const PREFIX: bool> {}
pub mod en {
use super::*;
pub struct Default;
impl<const PREFIX: bool> Encoding<PREFIX> for Default {}
#[cfg(feature = "check")]
mod __check {
use super::*;
pub struct Check;
impl<const PREFIX: bool> Encoding<PREFIX> for Check {}
}
#[cfg(feature = "check")]
pub use __check::*;
}
pub struct Buffer<
const LEN: usize,
const PREFIX: bool = false,
E: Encoding<PREFIX> = en::Default,
> {
__raw: [u8; LEN],
__pos: usize,
__marker: marker::PhantomData<E>,
}
impl<const LEN: usize, const PREFIX: bool, E: Encoding<PREFIX>>
Buffer<LEN, PREFIX, E>
{
pub const EMPTY: Self = Self {
__raw: [0u8; LEN],
__pos: 0,
__marker: marker::PhantomData,
};
const fn new(__raw: [u8; LEN], __pos: usize) -> Self {
Self {
__raw,
__pos,
__marker: marker::PhantomData,
}
}
#[inline]
#[must_use]
pub const fn pos(&self) -> usize {
self.__pos
}
#[inline]
#[must_use]
pub const fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.__pos) }
}
#[inline]
#[must_use]
pub const fn as_ptr(&self) -> *const u8 {
self.__raw.as_ptr()
}
}
impl<const N: usize> Buffer<N, false, en::Default> {
#[inline]
#[must_use]
pub const fn encode<const M: usize>(src: &[u8; M]) -> Self {
const { assert!(N >= encoded_len(M), "Size 'N' is too small") }
let mut __raw = [0u8; N];
let __pos = __internal::en(src, 0, M, &mut __raw, 0, None);
Self::new(__raw, __pos)
}
#[inline]
pub const fn try_encode<const M: usize>(src: &[u8; M]) -> Result<Self> {
let capacity = encoded_len(M);
if N < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: N,
});
}
Ok(Self::encode(src))
}
#[inline]
#[must_use]
pub const fn decode(src: &[u8]) -> Self {
assert!(N >= decoded_len(src.len()), "Size 'N' is too small");
let mut __raw = [0u8; N];
let __pos = match __internal::de(src, 0, src.len(), &mut __raw, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char: _, index: _ }) => {
panic!("Input contains invalid characters")
}
_ => unreachable!(),
};
Self::new(__raw, __pos)
}
#[inline]
pub const fn try_decode(src: &[u8]) -> Result<Self> {
let capacity = decoded_len(src.len());
if N < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: N,
});
}
Ok(Self::decode(src))
}
}
impl<const N: usize> Buffer<N, true, en::Default> {
#[inline]
#[must_use]
pub const fn encode<const M: usize>(src: &[u8; M], prefix: char) -> Self {
const { assert!(N > encoded_len(M), "Size 'N' is too small") }
assert!(prefix.is_ascii(), "Prefix must be an ASCII character");
let mut __raw = [0u8; N];
__raw[0] = prefix as u8;
let __pos = __internal::en(src, 0, M, &mut __raw, 1, None) + 1;
Self::new(__raw, __pos)
}
#[inline]
pub const fn try_encode<const M: usize>(
src: &[u8; M],
prefix: char,
) -> Result<Self> {
const { assert!(N > encoded_len(M), "Size 'N' is too small") }
if !prefix.is_ascii() {
return Err(Error::InvalidCharacter {
char: prefix,
index: 0,
});
}
Ok(Self::encode(src, prefix))
}
#[inline]
#[must_use]
pub const fn decode(src: &[u8], prefix: char) -> Self {
assert!(N >= decoded_len(src.len() - 1), "Size 'N' is too small");
assert!(prefix.is_ascii(), "Prefix must be an ASCII character");
assert!(!src.is_empty(), "Input must contain min. 1 character");
assert!(src[0] == prefix as u8, "Input must start with prefix");
let mut __raw = [0u8; N];
let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char: _, index: _ }) => {
panic!("Input contains invalid characters")
}
_ => unreachable!(),
};
Self::new(__raw, __pos)
}
#[inline]
pub const fn try_decode(src: &[u8], prefix: char) -> Result<Self> {
if src.is_empty() {
return Err(Error::MissingPrefix {
char: prefix,
got: None,
});
}
if !prefix.is_ascii() {
return Err(Error::InvalidCharacter {
char: prefix,
index: 0,
});
}
if src[0] != prefix as u8 {
return Err(Error::MissingPrefix {
char: prefix,
got: Some(src[0] as char),
});
}
let capacity = decoded_len(src.len() - 1); if N < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: N,
});
}
let mut __raw = [0u8; N];
let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char, index }) => {
return Err(Error::InvalidCharacter {
char,
index: index + 1,
});
}
Err(e) => return Err(e),
};
Ok(Self::new(__raw, __pos))
}
}
#[cfg(feature = "check")]
impl<const N: usize> Buffer<N, false, en::Check> {
#[inline]
#[must_use]
pub const fn encode<const M: usize>(src: &[u8; M], version: u8) -> Self {
const { assert!(N >= encoded_check_len(M), "Size 'N' is too small") }
assert!(version < 32, "Version must be < 32");
let mut __raw = [0u8; N];
__raw[0] = ALPHABET[version as usize];
let sum = checksum::compute(src, version);
let __pos = __internal::en(src, 0, M, &mut __raw, 1, Some(sum)) + 1;
Self::new(__raw, __pos)
}
#[inline]
pub const fn try_encode<const M: usize>(
src: &[u8; M],
version: u8,
) -> Result<Self> {
const { assert!(N >= encoded_check_len(M), "Size 'N' is too small") }
if version >= 32 {
return Err(Error::InvalidVersion {
expected: "must be < 32",
version,
});
}
Ok(Self::encode(src, version))
}
#[inline]
#[must_use]
pub const fn decode(src: &[u8]) -> (Self, u8) {
assert!(N >= decoded_check_len(src.len()), "Size 'N' is too small");
assert!(src.len() >= 2, "Input must contain min. 2 characters");
let mut __raw = [0u8; N];
let mut buffer = [0u8; 1];
let _ = match __internal::de(&[src[0]], 0, 1, &mut buffer, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char: _, index: _ }) => {
panic!("Input must not contain invalid characters")
}
_ => unreachable!(),
};
let version = buffer[0];
assert!(version < 32, "Version must be < 32");
let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char: _, index: _ }) => {
panic!("Input must not contain invalid characters")
}
_ => unreachable!(),
};
let __pos = __pos - checksum::BYTE_LENGTH;
let mut sum = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
let mut expected = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
assert!(__internal::memcmp(&expected, &sum, 4), "Checksum mismatch");
(Self::new(__raw, __pos), version)
}
#[inline]
pub const fn try_decode(src: &[u8]) -> Result<(Self, u8)> {
let capacity = decoded_check_len(src.len());
if N < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: N,
});
}
if src.len() < 2 {
return Err(Error::InsufficientData {
min: 2,
len: src.len(),
});
}
let mut __raw = [0u8; N];
let mut buffer = [0u8; 1];
let _ = match __internal::de(&[src[0]], 0, 1, &mut buffer, 0) {
Ok(pos) => pos,
Err(err) => return Err(err),
};
let version = buffer[0];
if version >= 32 {
return Err(Error::InvalidVersion {
expected: "must be < 32",
version,
});
}
let __pos = match __internal::de(src, 1, src.len() - 1, &mut __raw, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char, index }) => {
return Err(Error::InvalidCharacter {
char,
index: index + 1,
});
}
Err(e) => return Err(e),
};
let __pos = __pos - checksum::BYTE_LENGTH;
let mut sum = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
let mut expected = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
if !__internal::memcmp(&expected, &sum, checksum::BYTE_LENGTH) {
return Err(Error::ChecksumMismatch { expected, got: sum });
}
Ok((Self::new(__raw, __pos), version))
}
}
#[cfg(feature = "check")]
impl<const N: usize> Buffer<N, true, en::Check> {
#[inline]
#[must_use]
pub const fn encode<const M: usize>(
src: &[u8; M],
prefix: char,
version: u8,
) -> Self {
const { assert!(N > encoded_check_len(M), "Size 'N' is too small") }
assert!(version < 32, "Version must be < 32");
let mut __raw = [0u8; N];
__raw[0] = prefix as u8;
__raw[1] = ALPHABET[version as usize];
let sum = checksum::compute(src, version);
let __pos = __internal::en(src, 0, M, &mut __raw, 2, Some(sum)) + 2;
Self::new(__raw, __pos)
}
#[inline]
pub const fn try_encode<const M: usize>(
src: &[u8; M],
prefix: char,
version: u8,
) -> Result<Self> {
const { assert!(N > encoded_check_len(M), "Size 'N' is too small") }
if version >= 32 {
return Err(Error::InvalidVersion {
expected: "must be < 32",
version,
});
}
let mut __raw = [0u8; N];
__raw[0] = prefix as u8;
__raw[1] = ALPHABET[version as usize];
let sum = checksum::compute(src, version);
let __pos = __internal::en(src, 0, M, &mut __raw, 2, Some(sum)) + 2;
Ok(Self::new(__raw, __pos))
}
#[inline]
#[must_use]
pub const fn decode(src: &[u8], prefix: char) -> (Self, u8) {
assert!(N >= decoded_check_len(src.len() - 1), "'N' is too small");
assert!(prefix.is_ascii(), "Prefix must be an ASCII character");
assert!(src.len() >= 3, "Input must contain min. 3 characters");
assert!(src[0] == prefix as u8, "Input must start with prefix");
let mut buffer = [0u8; 1];
let _ = match __internal::de(&[src[1]], 0, 1, &mut buffer, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char: _, index: _ }) => {
panic!("Input must not contain invalid characters")
}
_ => unreachable!(),
};
let version = buffer[0];
assert!(version < 32, "Version must be < 32");
let mut __raw = [0u8; N];
let pos = match __internal::de(src, 2, src.len() - 2, &mut __raw, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char: _, index: _ }) => {
panic!("Input must not contain invalid characters")
}
_ => unreachable!(),
};
let __pos = pos - checksum::BYTE_LENGTH;
let mut sum = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
let mut expected = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
assert!(__internal::memcmp(&expected, &sum, 4), "Checksum mismatch");
(Self::new(__raw, __pos), version)
}
#[inline]
pub const fn try_decode(src: &[u8], prefix: char) -> Result<(Self, u8)> {
let capacity = decoded_check_len(src.len() - prefix.len_utf8());
if N < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: N,
});
}
if !prefix.is_ascii() {
return Err(Error::InvalidCharacter {
char: prefix,
index: 0,
});
}
if src.is_empty() {
return Err(Error::MissingPrefix {
char: prefix,
got: None,
});
}
if src.len() < 3 {
return Err(Error::InsufficientData {
min: 3,
len: src.len(),
});
}
if src[0] != prefix as u8 {
return Err(Error::MissingPrefix {
char: prefix,
got: Some(src[0] as char),
});
}
let mut buffer = [0u8; 1];
let _ = match __internal::de(&[src[1]], 0, 1, &mut buffer, 0) {
Ok(pos) => pos,
Err(err) => return Err(err),
};
let version = buffer[0];
if version >= 32 {
return Err(Error::InvalidVersion {
expected: "must be < 32",
version,
});
}
let mut __raw = [0u8; N];
let mut __pos =
match __internal::de(src, 2, src.len() - 2, &mut __raw, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char, index }) => {
return Err(Error::InvalidCharacter {
char,
index: index + 2,
});
}
Err(e) => return Err(e),
};
__pos -= checksum::BYTE_LENGTH;
let mut sum = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut sum, 0, &__raw, __pos, checksum::BYTE_LENGTH);
let mut expected = [0u8; checksum::BYTE_LENGTH];
__internal::memcpy(&mut expected, 0, &__raw, __pos, 4);
if !__internal::memcmp(&expected, &sum, checksum::BYTE_LENGTH) {
return Err(Error::ChecksumMismatch { expected, got: sum });
}
Ok((Self::new(__raw, __pos), version))
}
}
#[inline]
#[must_use]
pub const fn encoded_len(n: usize) -> usize {
(n * 8 + 4) / 5
}
#[inline]
#[must_use]
#[cfg(feature = "check")]
pub const fn encoded_check_len(n: usize) -> usize {
1 + encoded_len(n + 4)
}
#[inline]
#[must_use]
pub const fn decoded_len(n: usize) -> usize {
n
}
#[inline]
#[must_use]
#[cfg(feature = "check")]
pub const fn decoded_check_len(n: usize) -> usize {
n
}
#[inline]
#[must_use]
#[cfg(feature = "alloc")]
pub fn encode<B>(src: B) -> String
where
B: AsRef<[u8]>,
{
let src = src.as_ref();
let capacity = encoded_len(src.len());
let mut dst = vec![0u8; capacity];
let offset = encode_into(src, &mut dst).unwrap();
dst.truncate(offset);
String::from_utf8(dst).unwrap()
}
#[inline]
#[cfg(feature = "alloc")]
pub fn decode(str: &str) -> Result<Vec<u8>> {
let bytes = str.as_bytes();
let capacity = decoded_len(bytes.len());
let mut dst = vec![0u8; capacity];
let offset = decode_into(bytes, &mut dst)?;
dst.truncate(offset);
Ok(dst)
}
#[inline]
#[must_use]
#[cfg(feature = "alloc")]
pub fn encode_prefixed<B>(src: B, prefix: char) -> String
where
B: AsRef<[u8]>,
{
let src = src.as_ref();
let encoded = encode(src);
let capacity = prefix.len_utf8() + encoded.len();
let mut dst = String::with_capacity(capacity);
dst.push(prefix);
dst.push_str(&encoded);
dst
}
#[inline]
#[cfg(feature = "alloc")]
pub fn decode_prefixed(str: &str, prefix: char) -> Result<Vec<u8>> {
if !str.starts_with(prefix) {
return Err(Error::MissingPrefix {
char: prefix,
got: str.chars().next(),
});
}
match decode(&str[prefix.len_utf8()..]) {
Ok(bytes) => Ok(bytes),
Err(Error::InvalidCharacter { char, index }) => {
Err(Error::InvalidCharacter {
char,
index: index + prefix.len_utf8(),
})
}
Err(e) => Err(e),
}
}
#[inline]
#[cfg(all(feature = "alloc", feature = "check"))]
pub fn encode_check<B>(src: B, version: u8) -> Result<String>
where
B: AsRef<[u8]>,
{
let src = src.as_ref();
let capacity = encoded_check_len(src.len());
let mut dst = vec![0u8; capacity];
let offset = encode_check_into(src, &mut dst, version)?;
dst.truncate(offset);
Ok(String::from_utf8(dst).unwrap())
}
#[inline]
#[cfg(all(feature = "alloc", feature = "check"))]
pub fn decode_check(str: &str) -> Result<(Vec<u8>, u8)> {
let bytes = str.as_bytes();
let capacity = decoded_check_len(bytes.len());
let mut dst = vec![0u8; capacity];
let (offset, version) = decode_check_into(bytes, &mut dst)?;
dst.truncate(offset);
Ok((dst, version))
}
#[inline]
#[cfg(all(feature = "alloc", feature = "check"))]
pub fn encode_check_prefixed<B>(
src: B,
prefix: char,
version: u8,
) -> Result<String>
where
B: AsRef<[u8]>,
{
let src = src.as_ref();
let encoded = encode_check(src, version)?;
let capacity = prefix.len_utf8() + encoded.len();
let mut dst = String::with_capacity(capacity);
dst.push(prefix);
dst.push_str(&encoded);
Ok(dst)
}
#[inline]
#[cfg(all(feature = "alloc", feature = "check"))]
pub fn decode_check_prefixed(str: &str, prefix: char) -> Result<(Vec<u8>, u8)> {
if !str.starts_with(prefix) {
return Err(Error::MissingPrefix {
char: prefix,
got: str.chars().next(),
});
}
match decode_check(&str[prefix.len_utf8()..]) {
Ok(bytes) => Ok(bytes),
Err(Error::InvalidCharacter { char, index }) => {
Err(Error::InvalidCharacter {
char,
index: index + prefix.len_utf8(),
})
}
Err(e) => Err(e),
}
}
#[inline]
pub fn encode_into(src: &[u8], dst: &mut [u8]) -> Result<usize> {
let capacity = encoded_len(src.len());
if dst.len() < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: dst.len(),
});
}
let offset = __internal::en(src, 0, src.len(), &mut dst[..], 0, None);
Ok(offset)
}
#[inline]
pub fn decode_into(src: &[u8], dst: &mut [u8]) -> Result<usize> {
let capacity = decoded_len(src.len());
if dst.len() < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: dst.len(),
});
}
__internal::de(src, 0, src.len(), dst, 0)
}
#[inline]
#[cfg(feature = "check")]
pub fn encode_check_into(
src: &[u8],
dst: &mut [u8],
version: u8,
) -> Result<usize> {
let capacity = encoded_check_len(src.len());
if dst.len() < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: dst.len(),
});
}
if version >= 32 {
return Err(Error::InvalidVersion {
expected: "must be < 32",
version,
});
}
let mut offset = 0;
dst[offset] = ALPHABET[version as usize];
offset += 1;
let sum = checksum::compute(src, version);
offset +=
__internal::en(src, 0, src.len(), &mut dst[offset..], 0, Some(sum));
Ok(offset)
}
#[inline]
#[cfg(feature = "check")]
#[allow(clippy::missing_panics_doc)]
pub fn decode_check_into(src: &[u8], dst: &mut [u8]) -> Result<(usize, u8)> {
let capacity = decoded_check_len(src.len());
if dst.len() < capacity {
return Err(Error::BufferTooSmall {
min: capacity,
len: dst.len(),
});
}
if src.len() < 2 {
return Err(Error::InsufficientData {
min: 2,
len: src.len(),
});
}
let (tag, payload) = src.split_first().unwrap();
let mut buffer = [0u8; 1];
let _ = __internal::de(&[*tag], 0, 1, &mut buffer, 0)?;
let version = buffer[0];
if version >= 32 {
return Err(Error::InvalidVersion {
expected: "must be < 32",
version,
});
}
let mut offset = match __internal::de(payload, 0, payload.len(), dst, 0) {
Ok(pos) => pos,
Err(Error::InvalidCharacter { char, index }) => {
return Err(Error::InvalidCharacter {
char,
index: index + 1,
});
}
Err(e) => return Err(e),
};
offset -= checksum::BYTE_LENGTH;
let sum =
checksum::from_slice(&dst[offset..offset + checksum::BYTE_LENGTH]);
let expected = checksum::compute(&dst[..offset], version);
if !__internal::memcmp(&expected, &sum, checksum::BYTE_LENGTH) {
return Err(Error::ChecksumMismatch { expected, got: sum });
}
Ok((offset, version))
}
#[allow(dead_code)]
mod __internal {
use super::*;
#[inline]
#[must_use]
pub(crate) const fn en(
src: &[u8],
src_offset: usize,
src_len: usize,
dst: &mut [u8],
dst_offset: usize,
checksum: Option<[u8; 4]>,
) -> usize {
const MASK_5: u16 = 0x1F;
const SHIFT_5: u16 = 5;
let mut carry = 0;
let mut carry_bits = 0;
let mut dst_pos = dst_offset;
let mut leading_zeros = 0;
while leading_zeros < src_len && src[src_offset + leading_zeros] == 0 {
leading_zeros += 1;
}
if let Some(sum) = checksum {
let mut checksum_pos = sum.len();
while checksum_pos > 0 {
checksum_pos -= 1;
carry |= (sum[checksum_pos] as u16) << carry_bits;
carry_bits += 8;
while carry_bits >= SHIFT_5 {
dst[dst_pos] = ALPHABET[(carry & MASK_5) as usize];
dst_pos += 1;
carry >>= SHIFT_5;
carry_bits -= SHIFT_5;
}
}
}
let mut input_pos = src_offset + src_len;
while input_pos > src_offset {
input_pos -= 1;
carry |= (src[input_pos] as u16) << carry_bits;
carry_bits += 8;
while carry_bits >= SHIFT_5 {
dst[dst_pos] = ALPHABET[(carry & MASK_5) as usize];
dst_pos += 1;
carry >>= SHIFT_5;
carry_bits -= SHIFT_5;
}
}
if carry_bits > 0 && carry > 0 {
dst[dst_pos] = ALPHABET[(carry & MASK_5) as usize];
dst_pos += 1;
}
while dst_pos > dst_offset && dst[dst_pos - 1] == ALPHABET[0] {
dst_pos -= 1;
}
let mut j = 0;
while j < leading_zeros {
dst[dst_pos] = ALPHABET[0];
dst_pos += 1;
j += 1;
}
if dst_pos > dst_offset {
let mut lhs = dst_offset;
let mut rhs = dst_pos - 1;
while lhs < rhs {
let temp = dst[lhs];
dst[lhs] = dst[rhs];
dst[rhs] = temp;
lhs += 1;
rhs -= 1;
}
}
dst_pos - dst_offset
}
#[inline]
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) const fn de(
src: &[u8],
src_offset: usize,
src_len: usize,
dst: &mut [u8],
dst_offset: usize,
) -> Result<usize> {
const MASK_8: u16 = 0xFF;
const SHIFT_8: u16 = 8;
let mut carry = 0;
let mut carry_bits = 0;
let mut dst_pos = dst_offset;
let mut leading_zeros = 0;
while leading_zeros < src_len
&& src[src_offset + leading_zeros] == ALPHABET[0]
{
leading_zeros += 1;
}
let mut input_pos = src_offset + src_len;
while input_pos > src_offset {
input_pos -= 1;
let byte = src[input_pos];
if byte >= 128 {
return Err(Error::InvalidCharacter {
char: byte as char,
index: input_pos - src_offset,
});
}
let index = BYTE_MAP[byte as usize];
if index < 0 {
return Err(Error::InvalidCharacter {
char: byte as char,
index: input_pos - src_offset,
});
}
carry |= (index as u16) << carry_bits;
carry_bits += 5;
while carry_bits >= SHIFT_8 {
dst[dst_pos] = (carry & MASK_8) as u8;
dst_pos += 1;
carry >>= SHIFT_8;
carry_bits -= SHIFT_8;
}
}
if carry_bits > 0 {
dst[dst_pos] = carry as u8;
dst_pos += 1;
}
while dst_pos > dst_offset && dst[dst_pos - 1] == 0 {
dst_pos -= 1;
}
let mut j = 0;
while j < leading_zeros {
dst[dst_pos] = 0;
dst_pos += 1;
j += 1;
}
if dst_pos > dst_offset {
let mut lhs = dst_offset;
let mut rhs = dst_pos - 1;
while lhs < rhs {
let temp = dst[lhs];
dst[lhs] = dst[rhs];
dst[rhs] = temp;
lhs += 1;
rhs -= 1;
}
}
Ok(dst_pos - dst_offset)
}
#[inline]
pub(crate) const fn memcpy(
dst: &mut [u8],
dst_offset: usize,
src: &[u8],
src_offset: usize,
n: usize,
) {
let mut i = 0;
while i < n {
dst[dst_offset + i] = src[src_offset + i];
i += 1;
}
}
#[inline]
#[must_use]
pub(crate) const fn memcmp(lhs: &[u8], rhs: &[u8], n: usize) -> bool {
let mut i = 0;
while i < n {
if lhs[i] != rhs[i] {
return false;
}
i += 1;
}
true
}
}