use crate::alphabet::{ALPHABET, ID_SIZE_BYTES, ID_SIZE_CHARS, MAX_RELATIVE_MS};
use crate::error::OrionIdError;
pub fn encode_sortable64(bytes: &[u8]) -> Result<String, OrionIdError> {
if bytes.len() != ID_SIZE_BYTES {
return Err(OrionIdError::InvalidLength);
}
let alphabet = ALPHABET.as_bytes();
let mut out = String::with_capacity(ID_SIZE_CHARS);
for chunk in bytes.chunks_exact(3) {
let n = (u32::from(chunk[0]) << 16) | (u32::from(chunk[1]) << 8) | u32::from(chunk[2]);
out.push(char::from(alphabet[((n >> 18) & 63) as usize]));
out.push(char::from(alphabet[((n >> 12) & 63) as usize]));
out.push(char::from(alphabet[((n >> 6) & 63) as usize]));
out.push(char::from(alphabet[(n & 63) as usize]));
}
Ok(out)
}
pub fn decode_sortable64(id: &str) -> Result<[u8; ID_SIZE_BYTES], OrionIdError> {
if id.len() != ID_SIZE_CHARS {
return Err(OrionIdError::InvalidLength);
}
let input = id.as_bytes();
let mut out = [0u8; ID_SIZE_BYTES];
let mut cursor = 0usize;
for chunk in input.chunks_exact(4) {
let v0 = u32::from(decode_value(chunk[0])?);
let v1 = u32::from(decode_value(chunk[1])?);
let v2 = u32::from(decode_value(chunk[2])?);
let v3 = u32::from(decode_value(chunk[3])?);
let n = (v0 << 18) | (v1 << 12) | (v2 << 6) | v3;
out[cursor] = ((n >> 16) & 0xff) as u8;
out[cursor + 1] = ((n >> 8) & 0xff) as u8;
out[cursor + 2] = (n & 0xff) as u8;
cursor += 3;
}
Ok(out)
}
#[inline]
const fn decode_value(byte: u8) -> Result<u8, OrionIdError> {
match byte {
b'0'..=b'9' => Ok(byte - b'0'),
b'A'..=b'Z' => Ok(10 + byte - b'A'),
b'_' => Ok(36),
b'a'..=b'z' => Ok(37 + byte - b'a'),
b'~' => Ok(63),
_ => Err(OrionIdError::InvalidCharacter),
}
}
#[inline]
pub(crate) fn read_uint48_be(bytes: &[u8; ID_SIZE_BYTES], offset: usize) -> u128 {
(u128::from(bytes[offset]) << 40)
| (u128::from(bytes[offset + 1]) << 32)
| (u128::from(bytes[offset + 2]) << 24)
| (u128::from(bytes[offset + 3]) << 16)
| (u128::from(bytes[offset + 4]) << 8)
| u128::from(bytes[offset + 5])
}
#[inline]
pub(crate) fn write_uint48_be(
out: &mut [u8; ID_SIZE_BYTES],
offset: usize,
value: u128,
) -> Result<(), OrionIdError> {
if value > MAX_RELATIVE_MS {
return Err(OrionIdError::TimestampOverflow);
}
out[offset] = ((value >> 40) & 0xff) as u8;
out[offset + 1] = ((value >> 32) & 0xff) as u8;
out[offset + 2] = ((value >> 24) & 0xff) as u8;
out[offset + 3] = ((value >> 16) & 0xff) as u8;
out[offset + 4] = ((value >> 8) & 0xff) as u8;
out[offset + 5] = (value & 0xff) as u8;
Ok(())
}
pub(crate) fn to_hex(bytes: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut out = String::with_capacity(bytes.len() * 2);
for &byte in bytes {
out.push(char::from(HEX[(byte >> 4) as usize]));
out.push(char::from(HEX[(byte & 0x0f) as usize]));
}
out
}