Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+377
View File
@@ -0,0 +1,377 @@
/*!
A module for constants and various base utilities.
This module is a work-in-progress that may lead to helping us move off of
ranged integers. I'm not quite sure where this will go.
*/
use jcore::{
bounds::{self, const_check, Bounds, RawBoundsError},
constants as c,
};
use crate::Error;
/// Writes the definition of a single boundary type.
///
/// When only the name and type are given, then this copies the `WHAT`,
/// `MIN` and `MAX` definitions from `jcore`. This avoids copying the specific
/// range values and maintains a single point of truth.
macro_rules! define_one_boundary_type {
(
// The name of the boundary type.
$name:ident,
// The underlying primitive type. This is usually, but not always,
// the smallest signed primitive integer type that can represent both
// the minimum and maximum boundary values.
$ty:ident,
// A short human readable description that appears in error messages
// when the boundaries of this type are violated.
$what:expr,
// The minimum value.
$min:expr,
// The maximum value.
$max:expr $(,)?
) => {
pub(crate) struct $name(());
impl Bounds for $name {
const WHAT: &'static str = $what;
const MIN: Self::Primitive = $min;
const MAX: Self::Primitive = $max;
type Primitive = $ty;
type Error = BoundsError;
#[cold]
#[inline(never)]
fn error() -> BoundsError {
Self::error()
}
}
#[allow(dead_code)]
impl $name {
pub(crate) const MIN: $ty = <$name as Bounds>::MIN;
pub(crate) const MAX: $ty = <$name as Bounds>::MAX;
const LEN: i128 = Self::MAX as i128 - Self::MIN as i128 + 1;
#[cold]
pub(crate) const fn error() -> BoundsError {
BoundsError::$name(RawBoundsError::new())
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn check(
n: impl Into<i64>,
) -> Result<$ty, BoundsError> {
<$name as Bounds>::check(n)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) const fn checkc(n: i64) -> Result<$ty, BoundsError> {
match const_check::$ty(n) {
Ok(n) => Ok(n),
Err(err) => Err(BoundsError::$name(err)),
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn checked_add(
n1: $ty,
n2: $ty,
) -> Result<$ty, BoundsError> {
<$name as Bounds>::checked_add(n1, n2)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn checked_mul(
n1: $ty,
n2: $ty,
) -> Result<$ty, BoundsError> {
<$name as Bounds>::checked_mul(n1, n2)
}
#[cfg(test)]
pub(crate) fn arbitrary(g: &mut quickcheck::Gen) -> $ty {
use quickcheck::Arbitrary;
let mut n: $ty = <$ty>::arbitrary(g);
n = n.wrapping_rem_euclid(Self::LEN as $ty);
n += Self::MIN;
n
}
}
};
// A shortcut for defining a boundary type in `jiff` that is just a mirror
// of a boundary type defined in `jiff-core`. We do this instead of using
// the boundary type from `jiff-core` directly because we can't have a
// `From` impl on a `jiff-core` error type for `jiff::Error`. (Because
// that would make `jiff-core` a public dependency of `jiff`. Which means
// any semver incompatible release of `jiff-core` would require a semver
// incompatible release of `jiff`. Since `jiff-core` evolves more quickly
// than `jiff`, this would be bad juju.)
($name:ident, $ty:ident $(,)?) => {
define_one_boundary_type!(
$name,
$ty,
jcore::bounds::$name::WHAT,
jcore::bounds::$name::MIN,
jcore::bounds::$name::MAX,
);
};
}
/// This macro writes out the boiler plate to define a boundary type.
///
/// Specifically, it implements the `Bounds` trait and provides a few
/// concrete methods. The concrete methods are mostly wrappers around
/// the generic trait methods. They are provided so that callers don't
/// have to import the `Bounds` trait to use them.
macro_rules! define_bounds {
($((
$name:ident,
$ty:ident
$($tt:tt)*
)),* $(,)?) => {
$(
define_one_boundary_type!($name, $ty $($tt)*);
)*
/// An error that indicates a value is out of its intended range.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum BoundsError {
$($name(RawBoundsError<$name>),)*
}
impl core::fmt::Display for BoundsError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self {
$(BoundsError::$name(ref err) => err.fmt(f),)*
}
}
}
}
}
// This is where we define all of Jiff's ranges.
//
// Well, that's not quite true. Many of them are actually defined in
// `jiff-core`. For those, we still have an entry here that still produces
// a boundary type, but it reuses the minimum and maximum values from
// `jiff-core`. This is so `jiff` completely owns its own boundary types
// (to avoid a public dep on `jiff-core`) while simultaneously preserving a
// single point of truth for every range type.
define_bounds! {
(Century, i8, "century", 0, 99),
(CivilDayNanosecond, i64),
(CivilDaySecond, i32),
(Day, i8),
(DayOfYear, i16),
(Hour, i8),
(Hour12, i8, "hour (12 hour clock)", 1, 12),
(ISOWeek, i8),
(ISOYear, i16),
// This matches Temporal's range.
// See: https://github.com/tc39/proposal-temporal/issues/2458#issuecomment-1380742911
(Increment, i64, "rounding increment", 1, 1_000_000_000),
(Increment32, i32, "rounding increment", 1, 1_000_000_000),
// This is only used in parsing. A value of `60` gets clamped to `59`.
(LeapSecond, i8, "second", 0, 60),
(Microsecond, i16),
(Millisecond, i16),
(Minute, i8),
(Month, i8),
(Nanosecond, i16),
(NthWeekday, i32),
(OffsetHours, i8),
(OffsetMinutes, i8),
(OffsetSeconds, i8),
(OffsetTotalSeconds, i32),
(Second, i8),
(SignedDurationSeconds, i64, "signed duration seconds", i64::MIN, i64::MAX),
(SpanYears, i16, "years", -Self::MAX, (Year::LEN - 1) as i16),
(SpanMonths, i32, "months", -Self::MAX, SpanYears::MAX as i32 * 12),
(SpanWeeks, i32, "weeks", -Self::MAX, SpanDays::MAX / c::DAYS_PER_CIVIL_WEEK_32),
(SpanDays, i32, "days", -Self::MAX, SpanHours::MAX / c::HOURS_PER_CIVIL_DAY_32),
(SpanHours, i32, "hours", -Self::MAX, (SpanMinutes::MAX / c::MINS_PER_HOUR) as i32),
(SpanMinutes, i64, "minutes", -Self::MAX, SpanSeconds::MAX / c::SECS_PER_MIN),
(
SpanSeconds,
i64,
"seconds",
bounds::DeltaSeconds::MIN,
bounds::DeltaSeconds::MAX,
),
(
SpanMilliseconds,
i64,
"milliseconds",
-Self::MAX,
SpanSeconds::MAX * c::MILLIS_PER_SEC,
),
(
SpanMicroseconds,
i64,
"microseconds",
-Self::MAX,
SpanMilliseconds::MAX * c::MICROS_PER_MILLI,
),
(SpanMultiple, i64, "span multiple", i64::MIN + 1, i64::MAX),
// A range of the allowed number of nanoseconds.
//
// For this, we cannot cover the full span of supported time instants since
// `UnixSeconds::MAX * NANOSECONDS_PER_SECOND` cannot fit into 64-bits. We
// could use a `i128`, but it doesn't seem worth bloating up `Span`.
//
// Also note that our min is equal to -max, so that the total number of
// values in this range is one less than the number of distinct `i64`
// values. We do that so that the absolute value is always defined.
(SpanNanoseconds, i64, "nanoseconds", i64::MIN + 1, i64::MAX),
(SubsecNanosecond, i32),
(SignedSubsecNanosecond, i32),
(UnixEpochDays, i32),
(
UnixEpochMilliseconds,
i64,
"Unix timestamp milliseconds",
UnixEpochSeconds::MIN * c::MILLIS_PER_SEC,
UnixEpochSeconds::MAX * c::MILLIS_PER_SEC,
),
(
UnixEpochMicroseconds,
i64,
"Unix timestamp microseconds",
UnixEpochMilliseconds::MIN * c::MICROS_PER_MILLI,
UnixEpochMilliseconds::MAX * c::MICROS_PER_MILLI,
),
(UnixEpochSeconds, i64),
(WeekNum, i8, "week-number", 0, 53),
(WeekdayMondayZero, i8),
(WeekdayMondayOne, i8),
(WeekdaySundayZero, i8),
(WeekdaySundayOne, i8),
// The range of years supported by Jiff.
//
// This is ultimately where some of the other ranges (like `UnixSeconds`)
// were determined from. That is, the range of years is the primary point
// at which the space of supported time instants is derived from. If one
// wanted to expand this range, you'd need to change it here and then
// compute the corresponding min/max values for `UnixSeconds`. (Among other
// things... Increasing the supported Jiff range is far more complicated
// than just changing some ranges here.)
(Year, i16),
(YearCE, i16),
(YearBCE, i16),
(YearTwoDigit, i16, "year (2 digits)", 0, 99),
(
ZonedDayNanoseconds,
i64,
"nanoseconds (in one zoned datetime day)",
ZonedDaySeconds::MIN as i64 * c::NANOS_PER_SEC,
ZonedDaySeconds::MAX as i64 * c::NANOS_PER_SEC,
),
// The number of seconds permitted in a single day.
//
// This is mostly just a "sensible" cap on what is possible. We allow one
// day to span up to 7 civil days.
//
// It must also be at least 1 second long.
(
ZonedDaySeconds,
i32,
"seconds (in one zoned datetime day)",
1,
7 * c::SECS_PER_CIVIL_DAY_32,
),
}
impl From<BoundsError> for Error {
fn from(err: BoundsError) -> Error {
Error::bounds(err)
}
}
impl crate::error::IntoError for BoundsError {
fn into_error(self) -> Error {
self.into()
}
}
/// Like `BoundsError`, but maintained manually.
///
/// This is useful for range errors outside of the framework above.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum SpecialBoundsError {
SignedDurationFloatOutOfRangeF32,
SignedDurationFloatOutOfRangeF64,
SignedToUnsignedDuration,
}
impl core::fmt::Display for SpecialBoundsError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use self::SpecialBoundsError::*;
match *self {
SignedToUnsignedDuration => f.write_str(
"negative signed durations cannot be converted \
to an unsigned duration",
),
SignedDurationFloatOutOfRangeF32 => {
write!(
f,
"parameter 'floating point seconds' is not in \
the required range of {min}..={max}",
min = i64::MIN as f32,
max = i64::MAX as f32,
)
}
SignedDurationFloatOutOfRangeF64 => {
write!(
f,
"parameter 'floating point seconds' is not in \
the required range of {min}..={max}",
min = i64::MIN as f64,
max = i64::MAX as f64,
)
}
}
}
}
impl From<SpecialBoundsError> for Error {
fn from(err: SpecialBoundsError) -> Error {
Error::special_bounds(err)
}
}
impl crate::error::IntoError for SpecialBoundsError {
fn into_error(self) -> Error {
self.into()
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::*;
#[test]
fn size_of_bounds_error() {
// It's okay if this grows, but I wrote this test initially
// to confirm that a `BoundsError` is very small. (Since it's
// just a bunch of variants of ZSTs.)
assert_eq!(1, core::mem::size_of::<BoundsError>());
}
#[test]
fn basic_error_functionality() {
let err = Year::check(10_000).unwrap_err();
assert_eq!(
err.to_string(),
"parameter 'year' is not in the required range of -9999..=9999",
);
}
}
+106
View File
@@ -0,0 +1,106 @@
/*!
This module re-exports a "dumb" version of `std::borrow::Cow`.
It doesn't have any of the generic goodness and doesn't support dynamically
sized types. It's just either a `T` or a `&T`.
We have pretty simplistic needs, and we use this simpler type that works
in core-only mode.
*/
#[derive(Clone, Debug)]
pub(crate) enum DumbCow<'a, T> {
Owned(T),
Borrowed(&'a T),
}
impl<'a, T> DumbCow<'a, T> {
pub(crate) fn borrowed(&self) -> DumbCow<'_, T> {
match *self {
DumbCow::Owned(ref this) => DumbCow::Borrowed(this),
DumbCow::Borrowed(ref this) => DumbCow::Borrowed(this),
}
}
}
impl<'a, T> core::ops::Deref for DumbCow<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match *self {
DumbCow::Owned(ref t) => t,
DumbCow::Borrowed(t) => t,
}
}
}
impl<'a, T: core::fmt::Display> core::fmt::Display for DumbCow<'a, T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(core::ops::Deref::deref(self), f)
}
}
/// A `Cow` for strings, but can be used in core-only mode.
///
/// In core-only, the `Owned` variant doesn't exist.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) enum StringCow<'a> {
#[cfg(feature = "alloc")]
Owned(alloc::string::String),
Borrowed(&'a str),
}
impl<'a> StringCow<'a> {
/// Returns this `String` or `&str` as a `&str`.
///
/// Like `std::borrow::Cow`, the lifetime of the string slice returned
/// is tied to `StringCow`, and _not_ the original lifetime of the string
/// slice.
pub(crate) fn as_str<'s>(&'s self) -> &'s str {
match *self {
#[cfg(feature = "alloc")]
StringCow::Owned(ref s) => s,
StringCow::Borrowed(s) => s,
}
}
/// Converts this cow into an "owned" variant, copying if necessary.
///
/// If this cow is already an "owned" variant, then this is a no-op.
#[cfg(feature = "alloc")]
pub(crate) fn into_owned(self) -> StringCow<'static> {
use alloc::string::ToString;
match self {
StringCow::Owned(string) => StringCow::Owned(string),
StringCow::Borrowed(string) => {
StringCow::Owned(string.to_string())
}
}
}
}
impl<'a> core::ops::Deref for StringCow<'a> {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl<'a> core::fmt::Display for StringCow<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(self.as_str(), f)
}
}
#[cfg(feature = "alloc")]
impl From<alloc::string::String> for StringCow<'static> {
fn from(string: alloc::string::String) -> StringCow<'static> {
StringCow::Owned(string)
}
}
impl<'a> From<&'a str> for StringCow<'a> {
fn from(string: &'a str) -> StringCow<'a> {
StringCow::Borrowed(string)
}
}
+48
View File
@@ -0,0 +1,48 @@
use std::time::{Duration, Instant as MonotonicInstant};
/// A little helper for representing expiration time.
///
/// An overflowing expiration time is treated identically to a time that is
/// always expired.
///
/// When `None` internally, it implies that the expiration time is at some
/// arbitrary point in the past beyond all possible "time to live" values.
/// i.e., A `None` value invalidates the cache at the next failed lookup.
#[derive(Clone, Copy, Debug)]
pub(crate) struct Expiration(Option<MonotonicInstant>);
impl Expiration {
/// Returns an expiration time for which `is_expired` returns true after
/// the given duration has elapsed from this instant.
pub(crate) fn after(ttl: Duration) -> Expiration {
Expiration(
crate::now::monotonic_time().and_then(|now| now.checked_add(ttl)),
)
}
/// Returns an expiration time for which `is_expired` always returns true.
pub(crate) const fn expired() -> Expiration {
Expiration(None)
}
/// Whether expiration has occurred or not.
pub(crate) fn is_expired(self) -> bool {
self.0.map_or(true, |t| {
let Some(now) = crate::now::monotonic_time() else { return true };
now > t
})
}
}
impl core::fmt::Display for Expiration {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let maybe_duration = self.0.and_then(|instant| {
crate::now::monotonic_time()
.and_then(|now| instant.checked_duration_since(now))
});
match maybe_duration {
None => f.write_str("expired"),
Some(duration) => core::fmt::Debug::fmt(&duration, f),
}
}
}
+25
View File
@@ -0,0 +1,25 @@
/// Unwrap an `Option<T>` in a `const` context.
///
/// If it fails, panics with the given message.
macro_rules! unwrap {
($val:expr, $msg:expr$(,)?) => {
match $val {
Some(val) => val,
None => panic!($msg),
}
};
}
/// Unwrap a `Result<T, E>` in a `const` context.
///
/// If it fails, panics with the given message.
macro_rules! unwrapr {
($val:expr, $msg:expr$(,)?) => {
match $val {
Ok(val) => val,
Err(_) => panic!($msg),
}
};
}
pub(crate) use {unwrap, unwrapr};
+124
View File
@@ -0,0 +1,124 @@
/*!
Provides convenience routines for escaping raw bytes.
This was copied from `regex-automata` with a few light edits.
*/
use super::utf8;
/// Provides a convenient `Debug` implementation for a `u8`.
///
/// The `Debug` impl treats the byte as an ASCII, and emits a human
/// readable representation of it. If the byte isn't ASCII, then it's
/// emitted as a hex escape sequence.
#[derive(Clone, Copy)]
pub(crate) struct Byte(pub u8);
impl core::fmt::Display for Byte {
#[inline(never)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
if self.0 == b' ' {
return f.write_str(" ");
}
// 10 bytes is enough for any output from ascii::escape_default.
let mut bytes = [0u8; 10];
let mut len = 0;
for (i, mut b) in core::ascii::escape_default(self.0).enumerate() {
// capitalize \xab to \xAB
if i >= 2 && b'a' <= b && b <= b'f' {
b -= 32;
}
bytes[len] = b;
len += 1;
}
f.write_str(core::str::from_utf8(&bytes[..len]).unwrap())
}
}
impl core::fmt::Debug for Byte {
#[inline(never)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_str("\"")?;
core::fmt::Display::fmt(self, f)?;
f.write_str("\"")?;
Ok(())
}
}
/// Provides a convenient `Debug` implementation for `&[u8]`.
///
/// This generally works best when the bytes are presumed to be mostly
/// UTF-8, but will work for anything. For any bytes that aren't UTF-8,
/// they are emitted as hex escape sequences.
#[derive(Clone, Copy)]
pub(crate) struct Bytes<'a>(pub &'a [u8]);
impl<'a> core::fmt::Display for Bytes<'a> {
#[inline(never)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
// This is a sad re-implementation of a similar impl found in bstr.
let mut bytes = self.0;
while let Some(result) = utf8::decode(bytes) {
let ch = match result {
Ok(ch) => ch,
Err(err) => {
// The decode API guarantees `errant_bytes` is non-empty.
write!(f, r"\x{:02x}", err.as_slice()[0])?;
bytes = &bytes[1..];
continue;
}
};
bytes = &bytes[ch.len_utf8()..];
match ch {
'\0' => f.write_str(r"\0")?,
'\x01'..='\x7f' => {
core::fmt::Display::fmt(&(ch as u8).escape_ascii(), f)?;
}
_ => {
core::fmt::Display::fmt(&ch.escape_debug(), f)?;
}
}
}
Ok(())
}
}
impl<'a> core::fmt::Debug for Bytes<'a> {
#[inline(never)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_str("\"")?;
core::fmt::Display::fmt(self, f)?;
f.write_str("\"")?;
Ok(())
}
}
/// A helper for repeating a single byte utilizing `Byte`.
///
/// This is limited to repeating a byte up to `u8::MAX` times in order
/// to reduce its size overhead. And in practice, Jiff just doesn't
/// need more than this (at time of writing, 2025-11-29).
pub(crate) struct RepeatByte {
pub(crate) byte: u8,
pub(crate) count: u8,
}
impl core::fmt::Display for RepeatByte {
#[inline(never)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
for _ in 0..self.count {
core::fmt::Display::fmt(&Byte(self.byte), f)?;
}
Ok(())
}
}
impl core::fmt::Debug for RepeatByte {
#[inline(never)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_str("\"")?;
core::fmt::Display::fmt(self, f)?;
f.write_str("\"")?;
Ok(())
}
}
+70
View File
@@ -0,0 +1,70 @@
use std::{fs::File, path::Path};
use crate::Timestamp;
/// Returns the last modified time for the given file path as a Jiff timestamp.
///
/// If there was a problem accessing the last modified time or if it could not
/// fit in a Jiff timestamp, then a warning message is logged and `None` is
/// returned.
pub(crate) fn last_modified_from_path(path: &Path) -> Option<Timestamp> {
let file = match File::open(path) {
Ok(file) => file,
Err(_err) => {
warn!(
"failed to open file to get last modified time {}: {_err}",
path.display(),
);
return None;
}
};
last_modified_from_file(path, &file)
}
/// Returns the last modified time for the given file as a Jiff timestamp.
///
/// If there was a problem accessing the last modified time or if it could not
/// fit in a Jiff timestamp, then a warning message is logged and `None` is
/// returned.
///
/// The path given should be the path to the given file. It is used for
/// diagnostic purposes.
pub(crate) fn last_modified_from_file(
_path: &Path,
file: &File,
) -> Option<Timestamp> {
let md = match file.metadata() {
Ok(md) => md,
Err(_err) => {
warn!(
"failed to get metadata (for last modified time) \
for {}: {_err}",
_path.display(),
);
return None;
}
};
let systime = match md.modified() {
Ok(systime) => systime,
Err(_err) => {
warn!(
"failed to get last modified time for {}: {_err}",
_path.display()
);
return None;
}
};
let timestamp = match Timestamp::try_from(systime) {
Ok(timestamp) => timestamp,
Err(_err) => {
warn!(
"system time {systime:?} out of bounds \
for Jiff timestamp for last modified time \
from {}: {_err}",
_path.display(),
);
return None;
}
};
Some(timestamp)
}
+104
View File
@@ -0,0 +1,104 @@
/*!
A no-std module for some floating point operations.
These were vendored from the [`libm`] crate. For no-std specifically, I
wouldn't necessarily mind depending on `libm`, but I don't think there's a
way to express "only depend on a crate if some on-by-default feature is not
enabled."
We also very intentionally have very minimal floating point requirements. It's
used for `Span::total` where it's part of the API and thus necessary. It's also
used in floating point conversion routings to `jiff::SignedDuration`.
[`libm`]: https://github.com/rust-lang/libm
*/
pub(crate) trait Float {
fn round(self) -> Self;
fn trunc(self) -> Self;
fn fract(self) -> Self;
}
const TOINT64: f64 = 1. / f64::EPSILON;
impl Float for f64 {
fn round(self) -> f64 {
(self + copysign64(0.5 - 0.25 * f64::EPSILON, self)).trunc()
}
fn trunc(self) -> f64 {
let x = self;
let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120f === 2 ^ 120
let mut i: u64 = x.to_bits();
let mut e: i64 = (i >> 52 & 0x7ff) as i64 - 0x3ff + 12;
let m: u64;
if e >= 52 + 12 {
return x;
}
if e < 12 {
e = 1;
}
m = -1i64 as u64 >> e;
if (i & m) == 0 {
return x;
}
core::hint::black_box(x + x1p120);
i &= !m;
f64::from_bits(i)
}
fn fract(self) -> f64 {
self - self.trunc()
}
}
impl Float for f32 {
fn round(self) -> f32 {
(self + copysign32(0.5 - 0.25 * f32::EPSILON, self)).trunc()
}
fn trunc(self) -> f32 {
let x = self;
let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120
let mut i: u32 = x.to_bits();
let mut e: i32 = (i >> 23 & 0xff) as i32 - 0x7f + 9;
let m: u32;
if e >= 23 + 9 {
return x;
}
if e < 9 {
e = 1;
}
m = -1i32 as u32 >> e;
if (i & m) == 0 {
return x;
}
core::hint::black_box(x + x1p120);
i &= !m;
f32::from_bits(i)
}
fn fract(self) -> f32 {
self - self.trunc()
}
}
fn copysign64(x: f64, y: f64) -> f64 {
let mut ux = x.to_bits();
let uy = y.to_bits();
ux &= (!0) >> 1;
ux |= uy & (1 << 63);
f64::from_bits(ux)
}
fn copysign32(x: f32, y: f32) -> f32 {
let mut ux = x.to_bits();
let uy = y.to_bits();
ux &= 0x7fffffff;
ux |= uy & 0x80000000;
f32::from_bits(ux)
}
+18
View File
@@ -0,0 +1,18 @@
pub(crate) mod b;
pub(crate) mod borrow;
#[cfg(any(
feature = "tz-system",
feature = "tzdb-zoneinfo",
feature = "tzdb-concatenated"
))]
pub(crate) mod cache;
pub(crate) mod constant;
pub(crate) mod escape;
#[cfg(feature = "std")]
pub(crate) mod fs;
#[cfg(not(feature = "std"))]
pub(crate) mod libm;
pub(crate) mod parse;
pub(crate) mod round;
pub(crate) mod sync;
pub(crate) mod utf8;
+208
View File
@@ -0,0 +1,208 @@
use jcore::bounds::Bounds;
use crate::{
error::util::{ParseFractionError, ParseIntError},
Error,
};
/// Parses an `i64` number from the beginning to the end of the given slice of
/// ASCII digit characters.
///
/// If any byte in the given slice is not `[0-9]`, then this returns an error.
/// Similarly, if the number parsed does not fit into a `i64`, then this
/// returns an error. Notably, this routine does not permit parsing a negative
/// integer. (We use `i64` because everything in this crate uses signed
/// integers, and because a higher level routine might want to parse the sign
/// and then apply it to the result of this routine.)
#[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)
}
/// Like `self::i64`, but also does a boundary check for the given type.
///
/// # Errors
///
/// If the given slice is not a valid integer (i.e., overflow or contains
/// anything other than `[0-9]`) or is not in the bounds for the given `Bounds`
/// implementation, then an error is returned.
///
/// Note that the error can either be a parsing error or it can be a
/// boundary error.
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn bi64<B>(bytes: &[u8]) -> Result<B::Primitive, Error>
where
B: Bounds,
Error: From<B::Error>,
{
Ok(B::check(self::i64(bytes)?)?)
}
/// Parsed an optional `u64` that is a prefix of `bytes`.
///
/// If no digits (`[0-9]`) were found at the beginning of `bytes`, then `None`
/// is returned.
///
/// Note that this is safe to call on untrusted input. It will not attempt
/// to consume more input than could possibly fit into a parsed integer.
///
/// Since this returns a `u64`, it is possible that an integer that cannot
/// fit into an `i64` is returned. Callers should handle this. (Indeed,
/// `DurationUnits` handles this case.)
///
/// # Errors
///
/// When the parsed integer cannot fit into a `u64`.
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn u64_prefix(
bytes: &[u8],
) -> Result<(Option<u64>, &[u8]), ParseIntError> {
// Discovered via `u64::MAX.to_string().len()`.
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;
// OK because we confirmed `byte` is an ASCII digit.
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..]))
}
/// Parses a `u32` fractional number from the beginning to the end of the given
/// slice of ASCII digit characters.
///
/// The fraction's maximum precision is always 9 digits. The returned integer
/// will always be in units of `10^{max_precision}`. For example, this
/// will parse a fractional amount of seconds with a maximum precision of
/// nanoseconds.
///
/// If any byte in the given slice is not `[0-9]`, then this returns an error.
/// Notably, this routine does not permit parsing a negative integer.
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)
}
/// Parses an `OsStr` into a `&str` when `&[u8]` isn't easily available.
///
/// This is effectively `OsStr::to_str`, but with a slightly better error
/// message.
#[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))
}
/// Splits the given input into two slices at the given position.
///
/// If the position is greater than the length of the slice given, then this
/// returns `None`.
#[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))
}
}
/// Returns a function that converts two slices to an offset.
///
/// It takes the starting point as input and returns a function that, when
/// given an ending point (greater than or equal to the starting point), then
/// the corresponding pointers are subtracted and an offset relative to the
/// starting point is returned.
///
/// This is useful as a helper function in parsing routines that use slices
/// but want to report offsets.
///
/// # Panics
///
/// This may panic if the ending point is not a suffix slice of `start`.
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)
}
/// Returns a function that converts two slices to the slice between them.
///
/// This takes a starting point as input and returns a function that, when
/// given an ending point (greater than or equal to the starting point), it
/// returns a slice beginning at the starting point and ending just at the
/// ending point.
///
/// This is useful as a helper function in parsing routines.
///
/// # Panics
///
/// This may panic if the ending point is not a suffix slice of `start`.
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]
}
}
File diff suppressed because it is too large Load Diff
+48
View File
@@ -0,0 +1,48 @@
/*!
This module re-exports `Arc`.
That is, it provides some indirection for the case when `alloc::sync::Arc` is
unavailable.
It also defines a "dumb" `Arc` in core-only mode that doesn't actually do
anything (no indirection, no reference counting).
*/
#[cfg(all(feature = "alloc", not(target_has_atomic = "ptr")))]
pub(crate) use portable_atomic_util::Arc;
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
pub(crate) use alloc::sync::Arc;
/// A "fake" `Arc`.
///
/// Basically, it exposes the `Arc` APIs we use in Jiff, but doesn't
/// actually introduce indirection or reference counting. It's only used
/// in core-only mode and in effect results in inlining all data into its
/// container.
///
/// Not ideal, but we use `Arc` in very few places. One is `TimeZone`,
/// which ends up being pretty small in core-only mode since it doesn't
/// support carrying TZif data.
#[cfg(not(feature = "alloc"))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Arc<T>(T);
#[cfg(not(feature = "alloc"))]
impl<T> Arc<T> {
pub(crate) fn new(t: T) -> Arc<T> {
Arc(t)
}
pub(crate) fn get_mut(this: &mut Arc<T>) -> Option<&mut T> {
Some(&mut this.0)
}
}
#[cfg(not(feature = "alloc"))]
impl<T> core::ops::Deref for Arc<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
+122
View File
@@ -0,0 +1,122 @@
use core::cmp::Ordering;
/// Represents an invalid UTF-8 sequence.
///
/// This is an error returned by `decode`. It is guaranteed to
/// contain 1, 2 or 3 bytes.
pub(crate) struct Utf8Error {
bytes: [u8; 3],
len: u8,
}
impl Utf8Error {
#[cold]
#[inline(never)]
fn new(original_bytes: &[u8], err: core::str::Utf8Error) -> Utf8Error {
let len = err.error_len().unwrap_or_else(|| original_bytes.len());
// OK because the biggest invalid UTF-8
// sequence possible is 3.
debug_assert!(1 <= len && len <= 3);
let mut bytes = [0; 3];
bytes[..len].copy_from_slice(&original_bytes[..len]);
Utf8Error {
bytes,
// OK because the biggest invalid UTF-8
// sequence possible is 3.
len: u8::try_from(len).unwrap(),
}
}
/// Returns the slice of invalid UTF-8 bytes.
///
/// The slice returned is guaranteed to have length equivalent
/// to `Utf8Error::len`.
pub(crate) fn as_slice(&self) -> &[u8] {
&self.bytes[..self.len()]
}
/// Returns the length of the invalid UTF-8 sequence found.
///
/// This is guaranteed to be 1, 2 or 3.
pub(crate) fn len(&self) -> usize {
usize::from(self.len)
}
}
impl core::fmt::Display for Utf8Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"found invalid UTF-8 byte {errant_bytes:?} in format \
string (format strings must be valid UTF-8)",
errant_bytes = crate::util::escape::Bytes(self.as_slice()),
)
}
}
/// Decodes the next UTF-8 encoded codepoint from the given byte slice.
///
/// If no valid encoding of a codepoint exists at the beginning of the
/// given byte slice, then a 1-3 byte slice is returned (which is guaranteed
/// to be a prefix of `bytes`). That byte slice corresponds either to a single
/// invalid byte, or to a prefix of a valid UTF-8 encoding of a Unicode scalar
/// value (but which ultimately did not lead to a valid encoding).
///
/// This returns `None` if and only if `bytes` is empty.
///
/// This never panics.
///
/// *WARNING*: This is not designed for performance. If you're looking for
/// a fast UTF-8 decoder, this is not it. If you feel like you need one in
/// this crate, then please file an issue and discuss your use case.
pub(crate) fn decode(bytes: &[u8]) -> Option<Result<char, Utf8Error>> {
if bytes.is_empty() {
return None;
}
let string = match core::str::from_utf8(&bytes[..bytes.len().min(4)]) {
Ok(s) => s,
Err(ref err) if err.valid_up_to() > 0 => {
// OK because we just verified we have at least some
// valid UTF-8.
core::str::from_utf8(&bytes[..err.valid_up_to()]).unwrap()
}
// In this case, we want to return 1-3 bytes that make up a prefix of
// a potentially valid codepoint.
Err(err) => return Some(Err(Utf8Error::new(bytes, err))),
};
// OK because we guaranteed above that `string`
// must be non-empty. And thus, `str::chars` must
// yield at least one Unicode scalar value.
Some(Ok(string.chars().next().unwrap()))
}
/// Like std's `eq_ignore_ascii_case`, but returns a full `Ordering`.
#[inline]
pub(crate) fn cmp_ignore_ascii_case(s1: &str, s2: &str) -> Ordering {
cmp_ignore_ascii_case_bytes(s1.as_bytes(), s2.as_bytes())
}
/// Like std's `eq_ignore_ascii_case`, but returns a full `Ordering` on
/// `&[u8]`.
#[inline]
pub(crate) fn cmp_ignore_ascii_case_bytes(s1: &[u8], s2: &[u8]) -> Ordering {
// This function used to look like this:
//
// let it1 = s1.iter().map(|&b| b.to_ascii_lowercase());
// let it2 = s2.iter().map(|&b| b.to_ascii_lowercase());
// it1.cmp(it2)
//
// But the code below seems to do better in microbenchmarks.
let mut i = 0;
loop {
let b1 = s1.get(i).copied().map(|b| b.to_ascii_lowercase());
let b2 = s2.get(i).copied().map(|b| b.to_ascii_lowercase());
match (b1, b2) {
(None, None) => return Ordering::Equal,
(Some(_), None) => return Ordering::Greater,
(None, Some(_)) => return Ordering::Less,
(Some(b1), Some(b2)) if b1 == b2 => i += 1,
(Some(b1), Some(b2)) => return b1.cmp(&b2),
}
}
}