use crate::error::util::{ParseFractionError, ParseIntError};
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn i64(bytes: &[u8]) -> Result<i64, ParseIntError> {
if bytes.is_empty() {
return Err(ParseIntError::NoDigitsFound);
}
let mut n: i64 = 0;
for &byte in bytes {
if !(b'0' <= byte && byte <= b'9') {
return Err(ParseIntError::InvalidDigit(byte));
}
let digit = i64::from(byte - b'0');
n = n
.checked_mul(10)
.and_then(|n| n.checked_add(digit))
.ok_or(ParseIntError::TooBig)?;
}
Ok(n)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn u64_prefix(
bytes: &[u8],
) -> Result<(Option<u64>, &[u8]), ParseIntError> {
const MAX_U64_DIGITS: usize = 20;
let mut digit_count = 0;
let mut n: u64 = 0;
while digit_count <= MAX_U64_DIGITS {
let Some(&byte) = bytes.get(digit_count) else { break };
if !byte.is_ascii_digit() {
break;
}
digit_count += 1;
let digit = u64::from(byte - b'0');
n = n
.checked_mul(10)
.and_then(|n| n.checked_add(digit))
.ok_or(ParseIntError::TooBig)?;
}
if digit_count == 0 {
return Ok((None, bytes));
}
Ok((Some(n), &bytes[digit_count..]))
}
pub(crate) fn fraction(bytes: &[u8]) -> Result<u32, ParseFractionError> {
if bytes.is_empty() {
return Err(ParseFractionError::NoDigitsFound);
} else if bytes.len() > ParseFractionError::MAX_PRECISION {
return Err(ParseFractionError::TooManyDigits);
}
let mut n: u32 = 0;
for &byte in bytes {
let digit = match byte.checked_sub(b'0') {
None => {
return Err(ParseFractionError::InvalidDigit(byte));
}
Some(digit) if digit > 9 => {
return Err(ParseFractionError::InvalidDigit(byte));
}
Some(digit) => {
debug_assert!((0..=9).contains(&digit));
u32::from(digit)
}
};
n = n
.checked_mul(10)
.and_then(|n| n.checked_add(digit))
.ok_or_else(|| ParseFractionError::TooBig)?;
}
for _ in bytes.len()..ParseFractionError::MAX_PRECISION {
n = n.checked_mul(10).ok_or_else(|| ParseFractionError::TooBig)?;
}
Ok(n)
}
#[cfg(any(feature = "tz-system", feature = "tzdb-zoneinfo"))]
pub(crate) fn os_str_utf8<'o, O>(
os_str: &'o O,
) -> Result<&'o str, crate::error::util::OsStrUtf8Error>
where
O: ?Sized + AsRef<std::ffi::OsStr>,
{
let os_str = os_str.as_ref();
os_str
.to_str()
.ok_or_else(|| crate::error::util::OsStrUtf8Error::from(os_str))
}
#[cfg(feature = "tz-system")]
pub(crate) fn os_str_bytes<'o, O>(
os_str: &'o O,
) -> Result<&'o [u8], crate::error::util::OsStrUtf8Error>
where
O: ?Sized + AsRef<std::ffi::OsStr>,
{
let os_str = os_str.as_ref();
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
Ok(os_str.as_bytes())
}
#[cfg(not(unix))]
{
Ok(os_str_utf8(os_str)?.as_bytes())
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn split(input: &[u8], at: usize) -> Option<(&[u8], &[u8])> {
if at > input.len() {
None
} else {
Some(input.split_at(at))
}
}
pub(crate) fn offseter<'a>(
start: &'a [u8],
) -> impl Fn(&'a [u8]) -> usize + 'a {
move |end| (end.as_ptr() as usize) - (start.as_ptr() as usize)
}
pub(crate) fn slicer<'a>(
start: &'a [u8],
) -> impl Fn(&'a [u8]) -> &'a [u8] + 'a {
let mkoffset = offseter(start);
move |end| {
let offset = mkoffset(end);
&start[..offset]
}
}