use base62::DecodeError;
pub const WIDTH_U64: usize = 11;
pub fn encode_pad(n: u64, width: usize) -> String {
let s = base62::encode(n);
format!("{:0>width$}", s, width = width)
}
pub fn encode_time(ts: u64) -> String {
encode_pad(ts, WIDTH_U64)
}
pub fn encode_fast_pad<'a>(n: u64, width: usize, out: &'a mut [u8]) -> &'a str {
assert_eq!(
out.len(),
width,
"Output buffer length must be equal to the specified width"
);
out.fill(b'0');
let mut tmp = [0u8; 22];
let len = base62::encode_bytes(n, &mut tmp).unwrap();
if len > 0 {
let start = width - len;
out[start..].copy_from_slice(&tmp[..len]);
}
std::str::from_utf8(out).unwrap()
}
pub fn decode(s: &str) -> Result<u64, DecodeError> {
match base62::decode(s) {
Ok(value) => {
if value > u64::MAX as u128 {
Err(DecodeError::ArithmeticOverflow)
} else {
Ok(value as u64)
}
}
Err(e) => {
Err(e)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_pad_and_decode() {
let n = 0;
let encoded = encode_pad(n, 11);
assert_eq!(encoded, "00000000000");
assert_eq!(decode(&encoded).unwrap(), n);
let n = 1700;
let encoded = encode_pad(n, 11);
assert_eq!(encoded.len(), 11); assert_eq!(decode(&encoded).unwrap(), n);
let n = 1678886400000;
let encoded = encode_pad(n, 11);
assert_eq!(encoded.len(), 11); assert_eq!(decode(&encoded).unwrap(), n);
let n = u64::MAX;
let encoded = encode_pad(n, 11);
assert_eq!(encoded.len(), 11);
assert_eq!(decode(&encoded).unwrap(), n);
assert_eq!(decode("RQ").unwrap(), 1700);
}
#[test]
fn test_encode_time_convenience() {
assert_eq!(encode_time(1700).len(), WIDTH_U64);
assert_eq!(decode(&encode_time(1700)).unwrap(), 1700);
}
#[test]
fn test_encode_fast_pad() {
let mut buf = [0u8; WIDTH_U64];
let n = 0;
let s = encode_fast_pad(n, WIDTH_U64, &mut buf);
assert_eq!(s.len(), 11);
assert_eq!(decode(s).unwrap(), n);
let n = 1700;
let s = encode_fast_pad(n, WIDTH_U64, &mut buf);
assert_eq!(s.len(), 11);
assert_eq!(decode(s).unwrap(), n);
let n = 1678886400000;
let s = encode_fast_pad(n, WIDTH_U64, &mut buf);
assert_eq!(s.len(), 11);
assert_eq!(decode(s).unwrap(), n);
let n = u64::MAX;
let s = encode_fast_pad(n, WIDTH_U64, &mut buf);
assert_eq!(s.len(), 11);
assert_eq!(decode(s).unwrap(), n);
}
#[test]
#[should_panic]
fn test_fast_pad_panics_on_wrong_buffer_size() {
let mut buf = [0u8; 10]; encode_fast_pad(123, 11, &mut buf);
}
#[test]
fn test_lexicographical_sorting() {
let val1 = 1700;
let val2 = 17000;
let val3 = u64::MAX - 1000;
let key1 = encode_time(val1);
let key2 = encode_time(val2);
let key3 = encode_time(val3);
assert!(key1 < key2);
assert!(key2 < key3);
}
#[test]
fn test_descending_order_sorting() {
let ts1 = 1000;
let ts2 = 2000;
let key1_desc = encode_time(u64::MAX - ts1);
let key2_desc = encode_time(u64::MAX - ts2);
assert!(key1_desc > key2_desc);
let decoded_ts1 = u64::MAX - decode(&key1_desc).unwrap();
let decoded_ts2 = u64::MAX - decode(&key2_desc).unwrap();
assert_eq!(decoded_ts1, ts1);
assert_eq!(decoded_ts2, ts2);
}
#[test]
fn test_decode_invalid_character() {
let result = decode("invalid-char!");
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), DecodeError::InvalidBase62Byte(_, _)));
}
#[test]
fn test_decode_overflow() {
let overflow_string = "LygHa16AHYG";
let result = decode(overflow_string);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), DecodeError::ArithmeticOverflow);
}
}