use crate::{
error::{err, Error},
util::escape::{Byte, Bytes},
};
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn i64(bytes: &[u8]) -> Result<i64, Error> {
if bytes.is_empty() {
return Err(err!("invalid number, no digits found"));
}
let mut n: i64 = 0;
for &byte in bytes {
let digit = match byte.checked_sub(b'0') {
None => {
return Err(err!(
"invalid digit, expected 0-9 but got {}",
Byte(byte),
));
}
Some(digit) if digit > 9 => {
return Err(err!(
"invalid digit, expected 0-9 but got {}",
Byte(byte),
))
}
Some(digit) => {
debug_assert!((0..=9).contains(&digit));
i64::from(digit)
}
};
n = n.checked_mul(10).and_then(|n| n.checked_add(digit)).ok_or_else(
|| {
err!(
"number '{}' too big to parse into 64-bit integer",
Bytes(bytes),
)
},
)?;
}
Ok(n)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn u64_prefix(bytes: &[u8]) -> Result<(Option<u64>, &[u8]), Error> {
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_else(
#[inline(never)]
|| {
err!(
"number `{}` too big to parse into 64-bit integer",
Bytes(&bytes[..digit_count]),
)
},
)?;
}
if digit_count == 0 {
return Ok((None, bytes));
}
Ok((Some(n), &bytes[digit_count..]))
}
pub(crate) fn fraction(bytes: &[u8]) -> Result<u32, Error> {
const MAX_PRECISION: usize = 9;
if bytes.is_empty() {
return Err(err!("invalid fraction, no digits found"));
} else if bytes.len() > MAX_PRECISION {
return Err(err!(
"invalid fraction, too many digits \
(at most {MAX_PRECISION} are allowed"
));
}
let mut n: u32 = 0;
for &byte in bytes {
let digit = match byte.checked_sub(b'0') {
None => {
return Err(err!(
"invalid fractional digit, expected 0-9 but got {}",
Byte(byte),
));
}
Some(digit) if digit > 9 => {
return Err(err!(
"invalid fractional digit, expected 0-9 but got {}",
Byte(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(
|| {
err!(
"fractional '{}' too big to parse into 64-bit integer",
Bytes(bytes),
)
},
)?;
}
for _ in bytes.len()..MAX_PRECISION {
n = n.checked_mul(10).ok_or_else(|| {
err!(
"fractional '{}' too big to parse into 64-bit integer \
(too much precision supported)",
Bytes(bytes)
)
})?;
}
Ok(n)
}
#[cfg(feature = "tzdb-zoneinfo")]
pub(crate) fn os_str_utf8<'o, O>(os_str: &'o O) -> Result<&'o str, Error>
where
O: ?Sized + AsRef<std::ffi::OsStr>,
{
let os_str = os_str.as_ref();
os_str
.to_str()
.ok_or_else(|| err!("environment value {os_str:?} is not valid UTF-8"))
}
#[cfg(feature = "tz-system")]
pub(crate) fn os_str_bytes<'o, O>(os_str: &'o O) -> Result<&'o [u8], Error>
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))]
{
let string = os_str.to_str().ok_or_else(|| {
err!("environment value {os_str:?} is not valid UTF-8")
})?;
Ok(string.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]
}
}