jiff/util/
b.rs

1/*!
2A module for constants and various base utilities.
3
4This module is a work-in-progress that may lead to helping us move off of
5ranged integers. I'm not quite sure where this will go.
6*/
7
8#![allow(dead_code)]
9
10use crate::{Error, SignedDuration};
11
12pub(crate) const DAYS_PER_WEEK: i64 = 7;
13pub(crate) const HOURS_PER_CIVIL_DAY: i64 = 24;
14pub(crate) const MINS_PER_CIVIL_DAY: i64 = HOURS_PER_CIVIL_DAY * MINS_PER_HOUR;
15pub(crate) const MINS_PER_HOUR: i64 = 60;
16pub(crate) const SECS_PER_WEEK: i64 = DAYS_PER_WEEK * SECS_PER_CIVIL_DAY;
17pub(crate) const SECS_PER_CIVIL_DAY: i64 = HOURS_PER_CIVIL_DAY * SECS_PER_HOUR;
18pub(crate) const SECS_PER_HOUR: i64 = SECS_PER_MIN * MINS_PER_HOUR;
19pub(crate) const SECS_PER_MIN: i64 = 60;
20pub(crate) const MILLIS_PER_CIVIL_DAY: i64 =
21    SECS_PER_CIVIL_DAY * MILLIS_PER_SEC;
22pub(crate) const MILLIS_PER_SEC: i64 = 1_000;
23pub(crate) const MICROS_PER_CIVIL_DAY: i64 =
24    SECS_PER_CIVIL_DAY * MICROS_PER_SEC;
25pub(crate) const MICROS_PER_SEC: i64 = MILLIS_PER_SEC * MICROS_PER_MILLI;
26pub(crate) const MICROS_PER_MILLI: i64 = 1_000;
27pub(crate) const NANOS_PER_WEEK: i64 = DAYS_PER_WEEK * NANOS_PER_CIVIL_DAY;
28pub(crate) const NANOS_PER_CIVIL_DAY: i64 =
29    HOURS_PER_CIVIL_DAY * NANOS_PER_HOUR;
30pub(crate) const NANOS_PER_HOUR: i64 = MINS_PER_HOUR * NANOS_PER_MIN;
31pub(crate) const NANOS_PER_MIN: i64 = SECS_PER_MIN * NANOS_PER_SEC;
32pub(crate) const NANOS_PER_SEC: i64 = MILLIS_PER_SEC * NANOS_PER_MILLI;
33pub(crate) const NANOS_PER_MILLI: i64 = MICROS_PER_MILLI * NANOS_PER_MICRO;
34pub(crate) const NANOS_PER_MICRO: i64 = 1_000;
35
36pub(crate) const DAYS_PER_WEEK_32: i32 = 7;
37pub(crate) const HOURS_PER_CIVIL_DAY_32: i32 = 24;
38pub(crate) const MINS_PER_HOUR_32: i32 = 60;
39pub(crate) const SECS_PER_CIVIL_DAY_32: i32 =
40    HOURS_PER_CIVIL_DAY_32 * SECS_PER_HOUR_32;
41pub(crate) const SECS_PER_HOUR_32: i32 = SECS_PER_MIN_32 * MINS_PER_HOUR_32;
42pub(crate) const SECS_PER_MIN_32: i32 = 60;
43pub(crate) const MILLIS_PER_SEC_32: i32 = 1_000;
44pub(crate) const MICROS_PER_SEC_32: i32 =
45    MILLIS_PER_SEC_32 * MICROS_PER_MILLI_32;
46pub(crate) const MICROS_PER_MILLI_32: i32 = 1_000;
47pub(crate) const NANOS_PER_SEC_32: i32 =
48    MILLIS_PER_SEC_32 * NANOS_PER_MILLI_32;
49pub(crate) const NANOS_PER_MILLI_32: i32 =
50    MICROS_PER_MILLI_32 * NANOS_PER_MICRO_32;
51pub(crate) const NANOS_PER_MICRO_32: i32 = 1_000;
52
53/// This macro writes out the boiler plate to define a boundary type.
54///
55/// Specifically, it implements the `Bounds` trait and provides a few
56/// concrete methods. The concrete methods are mostly wrappers around
57/// the generic trait methods. They are provided so that callers don't
58/// have to import the `Bounds` trait to use them.
59macro_rules! define_bounds {
60    ($((
61        // The name of the boundary type.
62        $name:ident,
63        // The underlying primitive type. This is usually, but not always,
64        // the smallest signed primitive integer type that can represent both
65        // the minimum and maximum boundary values.
66        $ty:ident,
67        // A short human readable description that appears in error messages
68        // when the boundaries of this type are violated.
69        $what:expr,
70        // The minimum value.
71        $min:expr,
72        // The maximum value.
73        $max:expr $(,)?
74    )),* $(,)?) => {
75        $(
76            pub(crate) struct $name(());
77
78            impl Bounds for $name {
79                const WHAT: &'static str = $what;
80                const MIN: Self::Primitive = $min;
81                const MAX: Self::Primitive = $max;
82                type Primitive = $ty;
83
84                #[cold]
85                #[inline(never)]
86                fn error() -> BoundsError {
87                    BoundsError::$name(RawBoundsError::new())
88                }
89            }
90
91            impl $name {
92                pub(crate) const MIN: $ty = <$name as Bounds>::MIN;
93                pub(crate) const MAX: $ty = <$name as Bounds>::MAX;
94                const LEN: i128 = Self::MAX as i128 - Self::MIN as i128 + 1;
95
96                #[cold]
97                pub(crate) const fn error() -> BoundsError {
98                    BoundsError::$name(RawBoundsError::new())
99                }
100
101                #[cfg_attr(feature = "perf-inline", inline(always))]
102                pub(crate) fn check(n: impl Into<i64>) -> Result<$ty, BoundsError> {
103                    <$name as Bounds>::check(n)
104                }
105
106                #[cfg_attr(feature = "perf-inline", inline(always))]
107                pub(crate) const fn checkc(n: i64) -> Result<$ty, BoundsError> {
108                    match self::checkc::$ty(n) {
109                        Ok(n) => Ok(n),
110                        Err(err) => Err(BoundsError::$name(err)),
111                    }
112                }
113
114                #[cfg_attr(feature = "perf-inline", inline(always))]
115                pub(crate) fn check128(n: impl Into<i128>) -> Result<$ty, BoundsError> {
116                    <$name as Bounds>::check128(n)
117                }
118
119                #[cfg_attr(feature = "perf-inline", inline(always))]
120                pub(crate) fn parse(bytes: &[u8]) -> Result<$ty, Error> {
121                    <$name as Bounds>::parse(bytes)
122                }
123
124                #[cfg_attr(feature = "perf-inline", inline(always))]
125                pub(crate) fn checked_add(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
126                    <$name as Bounds>::checked_add(n1, n2)
127                }
128
129                #[cfg_attr(feature = "perf-inline", inline(always))]
130                pub(crate) fn checked_sub(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
131                    <$name as Bounds>::checked_sub(n1, n2)
132                }
133
134                #[cfg_attr(feature = "perf-inline", inline(always))]
135                pub(crate) fn checked_mul(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
136                    <$name as Bounds>::checked_mul(n1, n2)
137                }
138
139                #[cfg(test)]
140                pub(crate) fn arbitrary(g: &mut quickcheck::Gen) -> $ty {
141                    use quickcheck::Arbitrary;
142
143                    let mut n: $ty = <$ty>::arbitrary(g);
144                    n = n.wrapping_rem_euclid(Self::LEN as $ty);
145                    n += Self::MIN;
146                    n
147                }
148            }
149        )*
150
151        /// An error that indicates a value is out of its intended range.
152        #[derive(Clone, Debug)]
153        pub(crate) enum BoundsError {
154            $($name(RawBoundsError<$name>),)*
155        }
156
157        impl core::fmt::Display for BoundsError {
158            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
159                match *self {
160                    $(BoundsError::$name(ref err) => err.fmt(f),)*
161                }
162            }
163        }
164    }
165}
166
167define_bounds! {
168    (Century, i8, "century", 0, 99),
169    (
170        CivilDayNanosecond,
171        i64,
172        "nanoseconds (in one civil day)",
173        0,
174        NANOS_PER_CIVIL_DAY - 1,
175    ),
176    (
177        CivilDaySecond,
178        i32,
179        "seconds (in one civil day)",
180        0,
181        SECS_PER_CIVIL_DAY_32 - 1,
182    ),
183    (Day, i8, "day", 1, 31),
184    (DayOfYear, i16, "day-of-year", 1, 366),
185    (Hour, i8, "hour", 0, 23),
186    (Hour12, i8, "hour (12 hour clock)", 1, 12),
187    (ISOWeek, i8, "iso-week", 1, 53),
188    (ISOYear, i16, "iso-year", -9999, 9999),
189    // This matches Temporal's range.
190    // See: https://github.com/tc39/proposal-temporal/issues/2458#issuecomment-1380742911
191    (Increment, i64, "rounding increment", 1, 1_000_000_000),
192    (Increment32, i32, "rounding increment", 1, 1_000_000_000),
193    // This is only used in parsing. A value of `60` gets clamped to `59`.
194    (LeapSecond, i8, "second", 0, 60),
195    (Microsecond, i16, "microsecond", 0, 999),
196    (Millisecond, i16, "millisecond", 0, 999),
197    (Minute, i8, "minute", 0, 59),
198    (Month, i8, "month", 1, 12),
199    (Nanosecond, i16, "nanosecond", 0, 999),
200    (NthWeekday, i32, "nth weekday", SpanWeeks::MIN, SpanWeeks::MAX),
201    // The number of hours allowed in a time zone offset.
202    //
203    // This number was somewhat arbitrarily chosen. In part because it's bigger
204    // than any current offset in actual use by a wide margin, and in part
205    // because POSIX `TZ` strings require the ability to store offsets in the
206    // range `-24:59:59..=25:59:59`. Note though that we make the range a
207    // little bigger with `-25:59:59..=25:59:59` so that negating an offset
208    // always produces a valid offset.
209    //
210    // Note that RFC 8536 actually allows offsets to be much bigger, namely,
211    // in the range `(-2^31, 2^31)`, where both ends are _exclusive_ (`-2^31`
212    // is explicitly disallowed, and `2^31` overflows a signed 32-bit
213    // integer). But RFC 8536 does say that it *should* be in the range
214    // `[-89999, 93599]`, which matches POSIX. In order to keep our offset
215    // small, we stick roughly to what POSIX requires.
216    //
217    // Note that we support a slightly bigger range of offsets than Temporal.
218    // Temporal seems to support only up to 23 hours, but we go up to 25 hours.
219    // This is done to support POSIX time zone strings, which also require 25
220    // hours (plus the maximal minute/second components).
221    (OffsetHours, i8, "time zone offset hours", -25, 25),
222    (OffsetMinutes, i8, "time zone offset minutes", -59, 59),
223    (OffsetSeconds, i8, "time zone offset seconds", -59, 59),
224    (
225        OffsetTotalSeconds,
226        i32,
227        "time zone offset total seconds",
228        -Self::MAX,
229        (OffsetHours::MAX as i32 * SECS_PER_HOUR_32)
230        + (OffsetMinutes::MAX as i32 * MINS_PER_HOUR_32)
231        + OffsetSeconds::MAX as i32,
232    ),
233    (Second, i8, "second", 0, 59),
234    (SignedDurationSeconds, i64, "signed duration seconds", i64::MIN, i64::MAX),
235    (SpanYears, i16, "years", -Self::MAX, (Year::LEN - 1) as i16),
236    (SpanMonths, i32, "months", -Self::MAX, SpanYears::MAX as i32 * 12),
237    (SpanWeeks, i32, "weeks", -Self::MAX, SpanDays::MAX / DAYS_PER_WEEK_32),
238    (SpanDays, i32, "days", -Self::MAX, SpanHours::MAX / HOURS_PER_CIVIL_DAY_32),
239    (SpanHours, i32, "hours", -Self::MAX, (SpanMinutes::MAX / MINS_PER_HOUR) as i32),
240    (SpanMinutes, i64, "minutes", -Self::MAX, SpanSeconds::MAX / SECS_PER_MIN),
241    // The maximum number of seconds that can be expressed with a span.
242    //
243    // All of our span types (except for years and months, since they have
244    // variable length even in civil datetimes) are defined in terms of this
245    // constant. The way it's defined is a little odd, so let's break it down.
246    //
247    // Firstly, a span of seconds should be able to represent at least the
248    // complete span supported by `Timestamp`. Thus, it's based off of
249    // `UnixSeconds::LEN`. That is, a span should be able to represent the
250    // value `UnixSeconds::MAX - UnixSeconds::MIN`.
251    //
252    // Secondly, a span should also be able to account for any amount of
253    // possible time that a time zone offset might add or subtract to an
254    // `Timestamp`. This also means it can account for any difference between
255    // two `civil::DateTime` values.
256    //
257    // Thirdly, we would like our span to be divisible by
258    // `SECONDS_PER_CIVIL_DAY`. This isn't strictly required, but it makes
259    // defining boundaries a little smoother. If it weren't divisible, then the
260    // lower bounds on some types would need to be adjusted by one.
261    //
262    // Note that neither the existence of this constant nor defining our
263    // spans based on it impacts the correctness of doing arithmetic on zoned
264    // instants. Arithmetic on zoned instants still uses "civil" spans, but the
265    // length of time for some units (like a day) might vary. The arithmetic
266    // for zoned instants accounts for this explicitly. But it still must obey
267    // the limits set here.
268    (
269        SpanSeconds,
270        i64,
271        "seconds",
272        -Self::MAX,
273        next_multiple_of(
274            UnixSeconds::LEN as i64
275                + OffsetTotalSeconds::MAX as i64
276                + SECS_PER_CIVIL_DAY,
277            SECS_PER_CIVIL_DAY,
278        ),
279    ),
280    (
281        SpanMilliseconds,
282        i64,
283        "milliseconds",
284        -Self::MAX,
285        SpanSeconds::MAX * MILLIS_PER_SEC,
286    ),
287    (
288        SpanMicroseconds,
289        i64,
290        "microseconds",
291        -Self::MAX,
292        SpanMilliseconds::MAX * MICROS_PER_MILLI,
293    ),
294    (SpanMultiple, i64, "span multiple", i64::MIN  + 1, i64::MAX),
295    // A range of the allowed number of nanoseconds.
296    //
297    // For this, we cannot cover the full span of supported time instants since
298    // `UnixSeconds::MAX * NANOSECONDS_PER_SECOND` cannot fit into 64-bits. We
299    // could use a `i128`, but it doesn't seem worth it.
300    //
301    // Also note that our min is equal to -max, so that the total number of
302    // values in this range is one less than the number of distinct `i64`
303    // values. We do that so that the absolute value is always defined.
304    (SpanNanoseconds, i64, "nanoseconds", i64::MIN + 1, i64::MAX),
305    (SubsecNanosecond, i32, "subsecond nanosecond", 0, NANOS_PER_SEC_32 - 1),
306    (
307        SignedSubsecNanosecond,
308        i32,
309        "subsecond nanosecond",
310        -SubsecNanosecond::MAX,
311        SubsecNanosecond::MAX,
312    ),
313    // The number of days from the Unix epoch for the Gregorian calendar.
314    //
315    // The range supported is based on the range of Unix timestamps that we
316    // support.
317    //
318    // While I had originally used the "rate die" concept from Calendrical
319    // Calculations, I found [Howard Hinnant's formulation][date-algorithms]
320    // much more straight-forward.
321    //
322    // [date-algorithms]: http://howardhinnant.github.io/date_algorithms.html
323    (
324        UnixEpochDays,
325        i32,
326        "Unix epoch days",
327        (UnixSeconds::MIN+ OffsetTotalSeconds::MIN as i64).div_euclid(SECS_PER_CIVIL_DAY) as i32,
328        (UnixSeconds::MAX + OffsetTotalSeconds::MAX as i64).div_euclid(SECS_PER_CIVIL_DAY) as i32,
329    ),
330    (
331        UnixMilliseconds,
332        i64,
333        "Unix timestamp milliseconds",
334        UnixSeconds::MIN * MILLIS_PER_SEC,
335        UnixSeconds::MAX * MILLIS_PER_SEC,
336    ),
337    (
338        UnixMicroseconds,
339        i64,
340        "Unix timestamp microseconds",
341        UnixMilliseconds::MIN * MICROS_PER_MILLI,
342        UnixMilliseconds::MAX * MICROS_PER_MILLI,
343    ),
344    // The range of Unix seconds supported by Jiff.
345    //
346    // This range should correspond to the first second of `Year::MIN` up
347    // through (and including) the last second of `Year::MAX`. Actually
348    // computing that is non-trivial, however, it can be computed easily enough
349    // using Unix programs like `date`:
350    //
351    // ```text
352    // $ TZ=0 date -d 'Mon Jan  1 12:00:00 AM  -9999' +'%s'
353    // date: invalid date ‘Mon Jan  1 12:00:00 AM  -9999’
354    // $ TZ=0 date -d 'Fri Dec 31 23:59:59  9999' +'%s'
355    // 253402300799
356    // ```
357    //
358    // Well, almost easily enough. `date` apparently doesn't support negative
359    // years. But it does support negative timestamps:
360    //
361    // ```text
362    // $ TZ=0 date -d '@-377705116800'
363    // Mon Jan  1 12:00:00 AM  -9999
364    // $ TZ=0 date -d '@253402300799'
365    // Fri Dec 31 11:59:59 PM  9999
366    // ```
367    //
368    // With that said, we actually end up restricting the range a bit more
369    // than what's above. Namely, what's above is what we support for civil
370    // datetimes. Because of time zones, we need to choose whether all
371    // `Timestamp` values can be infallibly converted to `civil::DateTime`
372    // values, or whether all `civil::DateTime` values can be infallibly
373    // converted to `Timestamp` values. I chose the former because getting
374    // a civil datetime is important for formatting. If I didn't choose the
375    // former, there would be some instants that could not be formatted. Thus,
376    // we make room by shrinking the range of allowed instants by precisely the
377    // maximum supported time zone offset.
378    (
379        UnixSeconds,
380        i64,
381        "Unix timestamp seconds",
382        -377705116800 - OffsetTotalSeconds::MIN as i64,
383        253402300799 - OffsetTotalSeconds::MAX as i64,
384    ),
385    (WeekNum, i8, "week-number", 0, 53),
386    (WeekdayMondayZero, i8, "weekday (Monday 0-indexed)", 0, 6),
387    (WeekdayMondayOne, i8, "weekday (Monday 1-indexed)", 1, 7),
388    (WeekdaySundayZero, i8, "weekday (Sunday 0-indexed)", 0, 6),
389    (WeekdaySundayOne, i8, "weekday (Sunday 1-indexed)", 1, 7),
390    // The range of years supported by Jiff.
391    //
392    // This is ultimately where some of the other ranges (like `UnixSeconds`)
393    // were determined from. That is, the range of years is the primary point
394    // at which the space of supported time instants is derived from. If one
395    // wanted to expand this range, you'd need to change it here and then
396    // compute the corresponding min/max values for `UnixSeconds`. (Among other
397    // things... Increasing the supported Jiff range is far more complicated
398    // than just changing some ranges here.)
399    (Year, i16, "year", -9999, 9999),
400    (YearCE, i16, "CE year", 1, Year::MAX),
401    (YearBCE, i16, "BCE year", 1, Year::MAX + 1),
402    (YearTwoDigit, i16, "year (2 digits)", 0, 99),
403    (
404        ZonedDayNanoseconds,
405        i64,
406        "nanoseconds (in one zoned datetime day)",
407        ZonedDaySeconds::MIN as i64 * NANOS_PER_SEC,
408        ZonedDaySeconds::MAX as i64 * NANOS_PER_SEC,
409    ),
410    // The number of seconds permitted in a single day.
411    //
412    // This is mostly just a "sensible" cap on what is possible. We allow one
413    // day to span up to 7 civil days.
414    //
415    // It must also be at least 1 second long.
416    (
417        ZonedDaySeconds,
418        i32,
419        "seconds (in one zoned datetime day)",
420        1,
421        7 * SECS_PER_CIVIL_DAY_32,
422    ),
423}
424
425/// An interface for defining boundaries on integer values.
426pub(crate) trait Bounds: Sized {
427    /// A short human readable description of the values represented by these
428    /// bounds.
429    const WHAT: &'static str;
430
431    /// The minimum boundary value.
432    const MIN: Self::Primitive;
433
434    /// The maximum boundary value.
435    const MAX: Self::Primitive;
436
437    /// The primitive integer representation for this boundary type.
438    ///
439    /// This is generally the smallest primitive integer type that fits the
440    /// minimum and maximum allowed values.
441    // MSRV: Ideally this would be a private trait. On newer versions
442    // of Rust (not sure when it started exactly), it's allowed but
443    // comes with a warn-by-default lint. I would like it to be private
444    // to avoid accidentally using it elsewhere, since it makes casts
445    // between integers very easy.
446    type Primitive: Primitive;
447
448    // We provide `check` and `check128` to avoid manifesting 128-bit integers
449    // in the vast majority of cases. While in theory the compiler should be
450    // able to see through it, this is such a primitive and common operation
451    // used throughout Jiff, that we specialize the overwhelmingly common case
452    // for 64-bit integers under the presumption that 64-bit integers (and
453    // smaller) are either always fast enough or are slower in environments
454    // where we care less about performance.
455
456    /// Create an error when a value is outside the bounds for this type.
457    fn error() -> BoundsError;
458
459    /// Converts the 64-bit integer provided into the primitive representation
460    /// of these bounds.
461    ///
462    /// # Errors
463    ///
464    /// This returns an error if the given integer does not fit in the bounds
465    /// prescribed by this trait implementation.
466    ///
467    /// # Panics
468    ///
469    /// This panics when `debug_assertions` are enabled if the bounds of
470    /// this implementation exceed what is representable in an `i64`. In
471    /// this case, callers must use `check128`.
472    #[cfg_attr(feature = "perf-inline", inline(always))]
473    fn check(n: impl Into<i64>) -> Result<Self::Primitive, BoundsError> {
474        // These asserts confirm that we only call this routine when our
475        // bounds fit into an i64. Otherwise, the `as_i64()` casts below
476        // are incorrect.
477        debug_assert!((i128::from(i64::MIN)..=i128::from(i64::MAX))
478            .contains(&Self::MIN.as_i128()));
479        debug_assert!((i128::from(i64::MIN)..=i128::from(i64::MAX))
480            .contains(&Self::MAX.as_i128()));
481
482        let n = n.into();
483        if !(Self::MIN.as_i64() <= n && n <= Self::MAX.as_i64()) {
484            return Err(Self::error());
485        }
486        Ok(Self::Primitive::from_i64(n))
487    }
488
489    /// Converts the 128-bit integer provided into the primitive representation
490    /// of these bounds.
491    ///
492    /// # Errors
493    ///
494    /// This returns an error if the given integer does not fit in the bounds
495    /// prescribed by this trait implementation.
496    #[cfg_attr(feature = "perf-inline", inline(always))]
497    fn check128(n: impl Into<i128>) -> Result<Self::Primitive, BoundsError> {
498        let n = n.into();
499        if !(Self::MIN.as_i128() <= n && n <= Self::MAX.as_i128()) {
500            return Err(Self::error());
501        }
502        Ok(Self::Primitive::from_i128(n))
503    }
504
505    /// Checks whether the given integer, in the same primitive representation
506    /// as this boundary type, is in bounds.
507    ///
508    /// # Errors
509    ///
510    /// This returns an error if the given integer does not fit in the bounds
511    /// prescribed by this trait implementation.
512    #[cfg_attr(feature = "perf-inline", inline(always))]
513    fn check_self(n: Self::Primitive) -> Result<Self::Primitive, BoundsError> {
514        if !(Self::MIN <= n && n <= Self::MAX) {
515            return Err(Self::error());
516        }
517        Ok(n)
518    }
519
520    /// Parses a 64-bit integer from the beginning to the end of the given
521    /// slice of bytes.
522    ///
523    /// Note that this can never parse a negative integer since it doesn't
524    /// look for a sign. On success, the integer returned is always positive.
525    ///
526    /// # Errors
527    ///
528    /// If the given slice is not a valid integer (i.e., overflow or contains
529    /// anything other than `[0-9]`) or is not in the bounds for this trait
530    /// implementation, then an error is returned.
531    ///
532    /// Note that the error can either be a parsing error or it can be a
533    /// boundary error.
534    #[cfg_attr(feature = "perf-inline", inline(always))]
535    fn parse(bytes: &[u8]) -> Result<Self::Primitive, Error> {
536        Ok(Self::check(crate::util::parse::i64(bytes)?)?)
537    }
538
539    /// Performs checked addition using this boundary type's primitive
540    /// representation.
541    ///
542    /// # Errors
543    ///
544    /// If the result exceeds the boundaries of the primitive type or of the
545    /// declared range for this type, then an error is returned.
546    #[cfg_attr(feature = "perf-inline", inline(always))]
547    fn checked_add(
548        n1: Self::Primitive,
549        n2: Self::Primitive,
550    ) -> Result<Self::Primitive, BoundsError> {
551        Self::check_self(n1.checked_add(n2).ok_or_else(Self::error)?)
552    }
553
554    /// Performs checked subtraction using this boundary type's primitive
555    /// representation.
556    ///
557    /// # Errors
558    ///
559    /// If the result exceeds the boundaries of the primitive type or of the
560    /// declared range for this type, then an error is returned.
561    #[cfg_attr(feature = "perf-inline", inline(always))]
562    fn checked_sub(
563        n1: Self::Primitive,
564        n2: Self::Primitive,
565    ) -> Result<Self::Primitive, BoundsError> {
566        Self::check_self(n1.checked_sub(n2).ok_or_else(Self::error)?)
567    }
568
569    /// Performs checked multiplication using this boundary type's primitive
570    /// representation.
571    ///
572    /// # Errors
573    ///
574    /// If the result exceeds the boundaries of the primitive type or of the
575    /// declared range for this type, then an error is returned.
576    #[cfg_attr(feature = "perf-inline", inline(always))]
577    fn checked_mul(
578        n1: Self::Primitive,
579        n2: Self::Primitive,
580    ) -> Result<Self::Primitive, BoundsError> {
581        Self::check_self(n1.checked_mul(n2).ok_or_else(Self::error)?)
582    }
583}
584
585/// A simple trait for making `int as int` usable in a generic context.
586///
587/// All of these methods require callers to ensure the cast is correct.
588pub(crate) trait Primitive:
589    Clone
590    + Copy
591    + Eq
592    + PartialEq
593    + PartialOrd
594    + Ord
595    + core::fmt::Debug
596    + core::fmt::Display
597{
598    fn as_i8(self) -> i8;
599    fn as_i16(self) -> i16;
600    fn as_i32(self) -> i32;
601    fn as_i64(self) -> i64;
602    fn as_i128(self) -> i128;
603
604    fn from_i8(n: i8) -> Self;
605    fn from_i16(n: i16) -> Self;
606    fn from_i32(n: i32) -> Self;
607    fn from_i64(n: i64) -> Self;
608    fn from_i128(n: i128) -> Self;
609
610    fn checked_add(self, n: Self) -> Option<Self>;
611    fn checked_sub(self, n: Self) -> Option<Self>;
612    fn checked_mul(self, n: Self) -> Option<Self>;
613}
614
615macro_rules! impl_primitive {
616    ($($intty:ty),*) => {
617        $(
618            impl Primitive for $intty {
619                fn as_i8(self) -> i8 { self as i8 }
620                fn as_i16(self) -> i16 { self as i16 }
621                fn as_i32(self) -> i32 { self as i32 }
622                fn as_i64(self) -> i64 { self as i64 }
623                fn as_i128(self) -> i128 { self as i128 }
624
625                fn from_i8(n: i8) -> Self { n as $intty }
626                fn from_i16(n: i16) -> Self { n as $intty }
627                fn from_i32(n: i32) -> Self { n as $intty }
628                fn from_i64(n: i64) -> Self { n as $intty }
629                fn from_i128(n: i128) -> Self { n as $intty }
630
631                fn checked_add(self, n: $intty) -> Option<$intty> {
632                    <$intty>::checked_add(self, n)
633                }
634
635                fn checked_sub(self, n: $intty) -> Option<$intty> {
636                    <$intty>::checked_sub(self, n)
637                }
638
639                fn checked_mul(self, n: $intty) -> Option<$intty> {
640                    <$intty>::checked_mul(self, n)
641                }
642            }
643        )*
644    }
645}
646
647impl_primitive!(i8, i16, i32, i64, i128);
648
649impl From<BoundsError> for Error {
650    fn from(err: BoundsError) -> Error {
651        Error::bounds(err)
652    }
653}
654
655impl crate::error::IntoError for BoundsError {
656    fn into_error(self) -> Error {
657        self.into()
658    }
659}
660
661pub(crate) struct RawBoundsError<B>(core::marker::PhantomData<B>);
662
663impl<B> RawBoundsError<B> {
664    const fn new() -> RawBoundsError<B> {
665        RawBoundsError(core::marker::PhantomData)
666    }
667}
668
669impl<B> Clone for RawBoundsError<B> {
670    fn clone(&self) -> RawBoundsError<B> {
671        RawBoundsError::new()
672    }
673}
674
675impl<B, P> core::fmt::Debug for RawBoundsError<B>
676where
677    B: Bounds<Primitive = P>,
678    P: core::fmt::Debug,
679{
680    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
681        f.debug_struct("RawBoundsError")
682            .field("what", &B::WHAT)
683            .field("min", &B::MIN)
684            .field("max", &B::MAX)
685            .finish()
686    }
687}
688
689impl<B, P> core::fmt::Display for RawBoundsError<B>
690where
691    B: Bounds<Primitive = P>,
692    P: core::fmt::Display,
693{
694    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
695        write!(
696            f,
697            "parameter '{what}' is not in the required range of {min}..={max}",
698            what = B::WHAT,
699            min = B::MIN,
700            max = B::MAX,
701        )
702    }
703}
704
705/// Like `BoundsError`, but maintained manually.
706///
707/// This is useful for range errors outside of the framework above.
708#[derive(Clone, Debug)]
709pub(crate) enum SpecialBoundsError {
710    UnixNanoseconds,
711    SignedDurationFloatOutOfRangeF32,
712    SignedDurationFloatOutOfRangeF64,
713    SignedToUnsignedDuration,
714}
715
716impl core::fmt::Display for SpecialBoundsError {
717    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
718        use self::SpecialBoundsError::*;
719
720        let (what, min, max) = match *self {
721            UnixNanoseconds => (
722                "Unix timestamp nanoseconds",
723                UnixMicroseconds::MIN as i128 * (NANOS_PER_MICRO as i128),
724                UnixMicroseconds::MAX as i128 * (NANOS_PER_MICRO as i128),
725            ),
726            SignedToUnsignedDuration => {
727                return f.write_str(
728                    "negative signed durations cannot be converted \
729                     to an unsigned duration",
730                );
731            }
732            SignedDurationFloatOutOfRangeF32 => {
733                return write!(
734                    f,
735                    "parameter 'floating point seconds' is not in \
736                     the required range of {min}..={max}",
737                    min = i64::MIN as f32,
738                    max = i64::MAX as f32,
739                );
740            }
741            SignedDurationFloatOutOfRangeF64 => {
742                return write!(
743                    f,
744                    "parameter 'floating point seconds' is not in \
745                     the required range of {min}..={max}",
746                    min = i64::MIN as f64,
747                    max = i64::MAX as f64,
748                );
749            }
750        };
751        write!(
752            f,
753            "parameter '{what}' is not in the required range of {min}..={max}",
754        )
755    }
756}
757
758impl From<SpecialBoundsError> for Error {
759    fn from(err: SpecialBoundsError) -> Error {
760        Error::special_bounds(err)
761    }
762}
763
764impl crate::error::IntoError for SpecialBoundsError {
765    fn into_error(self) -> Error {
766        self.into()
767    }
768}
769
770/// A representation of a numeric sign.
771///
772/// Its `Display` impl emits the ASCII minus sign, `-` when this
773/// is negative. It emits the empty string in all other cases.
774#[derive(
775    Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord,
776)]
777#[repr(i8)]
778pub(crate) enum Sign {
779    #[default]
780    Zero = 0,
781    Positive = 1,
782    Negative = -1,
783}
784
785impl Sign {
786    pub(crate) fn is_zero(self) -> bool {
787        matches!(self, Sign::Zero)
788    }
789
790    pub(crate) fn is_positive(self) -> bool {
791        matches!(self, Sign::Positive)
792    }
793
794    pub(crate) fn is_negative(self) -> bool {
795        matches!(self, Sign::Negative)
796    }
797
798    pub(crate) fn signum(self) -> i8 {
799        self.as_i8()
800    }
801
802    pub(crate) fn as_i8(self) -> i8 {
803        self as i8
804    }
805
806    pub(crate) fn as_i16(self) -> i16 {
807        i16::from(self.as_i8())
808    }
809
810    pub(crate) fn as_i32(self) -> i32 {
811        i32::from(self.as_i8())
812    }
813
814    pub(crate) fn as_i64(self) -> i64 {
815        i64::from(self.as_i8())
816    }
817
818    pub(crate) fn as_i128(self) -> i128 {
819        i128::from(self.as_i8())
820    }
821
822    pub(crate) fn from_ordinals<T: Ord>(t1: T, t2: T) -> Sign {
823        use core::cmp::Ordering::*;
824        match t1.cmp(&t2) {
825            Less => Sign::Negative,
826            Equal => Sign::Zero,
827            Greater => Sign::Positive,
828        }
829    }
830}
831
832impl core::ops::Neg for Sign {
833    type Output = Sign;
834
835    fn neg(self) -> Sign {
836        match self {
837            Sign::Positive => Sign::Negative,
838            Sign::Zero => Sign::Zero,
839            Sign::Negative => Sign::Positive,
840        }
841    }
842}
843
844impl From<i8> for Sign {
845    fn from(n: i8) -> Sign {
846        Sign::from(i64::from(n))
847    }
848}
849
850impl From<i16> for Sign {
851    fn from(n: i16) -> Sign {
852        Sign::from(i64::from(n))
853    }
854}
855
856impl From<i32> for Sign {
857    fn from(n: i32) -> Sign {
858        Sign::from(i64::from(n))
859    }
860}
861
862impl From<i64> for Sign {
863    fn from(n: i64) -> Sign {
864        if n == 0 {
865            Sign::Zero
866        } else if n > 0 {
867            Sign::Positive
868        } else {
869            Sign::Negative
870        }
871    }
872}
873
874impl From<i128> for Sign {
875    fn from(n: i128) -> Sign {
876        if n == 0 {
877            Sign::Zero
878        } else if n > 0 {
879            Sign::Positive
880        } else {
881            Sign::Negative
882        }
883    }
884}
885
886impl From<f64> for Sign {
887    fn from(n: f64) -> Sign {
888        use core::num::FpCategory::*;
889
890        // This is a little odd, but we want +/- 0 to
891        // always have a sign of zero, so as to be consistent
892        // with how we deal with signed integers.
893        //
894        // As for NaN... It should generally be a bug if
895        // Jiff ever materializes a NaN. Notably, I do not
896        // believe there are any APIs in which a float is
897        // given from the caller. Jiff only ever uses them
898        // internally or returns them. So if we get a NaN,
899        // it's on us. If we do, just assign it a zero sign?
900        if matches!(n.classify(), Nan | Zero) {
901            Sign::Zero
902        } else if n.is_sign_positive() {
903            Sign::Positive
904        } else {
905            Sign::Negative
906        }
907    }
908}
909
910impl From<SignedDuration> for Sign {
911    fn from(n: SignedDuration) -> Sign {
912        if n.is_zero() {
913            Sign::Zero
914        } else if n.is_positive() {
915            Sign::Positive
916        } else {
917            Sign::Negative
918        }
919    }
920}
921
922impl core::ops::Mul<Sign> for Sign {
923    type Output = Sign;
924    fn mul(self, rhs: Sign) -> Sign {
925        match (self, rhs) {
926            (Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
927            (Sign::Positive, Sign::Positive) => Sign::Positive,
928            (Sign::Negative, Sign::Negative) => Sign::Positive,
929            (Sign::Positive, Sign::Negative) => Sign::Negative,
930            (Sign::Negative, Sign::Positive) => Sign::Negative,
931        }
932    }
933}
934
935impl core::ops::Mul<i8> for Sign {
936    type Output = i8;
937    fn mul(self, n: i8) -> i8 {
938        self.as_i8() * n
939    }
940}
941
942impl core::ops::Mul<Sign> for i8 {
943    type Output = i8;
944    fn mul(self, n: Sign) -> i8 {
945        self * n.as_i8()
946    }
947}
948
949impl core::ops::Mul<i16> for Sign {
950    type Output = i16;
951    fn mul(self, n: i16) -> i16 {
952        self.as_i16() * n
953    }
954}
955
956impl core::ops::Mul<Sign> for i16 {
957    type Output = i16;
958    fn mul(self, n: Sign) -> i16 {
959        self * n.as_i16()
960    }
961}
962
963impl core::ops::Mul<i32> for Sign {
964    type Output = i32;
965    fn mul(self, n: i32) -> i32 {
966        self.as_i32() * n
967    }
968}
969
970impl core::ops::Mul<Sign> for i32 {
971    type Output = i32;
972    fn mul(self, n: Sign) -> i32 {
973        self * n.as_i32()
974    }
975}
976
977impl core::ops::Mul<i64> for Sign {
978    type Output = i64;
979    fn mul(self, n: i64) -> i64 {
980        self.as_i64() * n
981    }
982}
983
984impl core::ops::Mul<Sign> for i64 {
985    type Output = i64;
986    fn mul(self, n: Sign) -> i64 {
987        self * n.as_i64()
988    }
989}
990
991impl core::ops::Mul<i128> for Sign {
992    type Output = i128;
993    fn mul(self, n: i128) -> i128 {
994        self.as_i128() * n
995    }
996}
997
998impl core::ops::Mul<Sign> for i128 {
999    type Output = i128;
1000    fn mul(self, n: Sign) -> i128 {
1001        self * n.as_i128()
1002    }
1003}
1004
1005impl core::fmt::Display for Sign {
1006    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1007        if self.is_negative() {
1008            f.write_str("-")
1009        } else {
1010            Ok(())
1011        }
1012    }
1013}
1014
1015mod checkc {
1016    use super::{Bounds, RawBoundsError};
1017
1018    #[cfg_attr(feature = "perf-inline", inline(always))]
1019    pub(super) const fn i8<B>(n: i64) -> Result<i8, RawBoundsError<B>>
1020    where
1021        B: Bounds<Primitive = i8>,
1022    {
1023        // These asserts confirm that we only call this routine
1024        // when our bounds fit into an i64. Otherwise, the
1025        // `as` casts below are incorrect.
1026        debug_assert!(
1027            (i64::MIN as i128) <= (B::MIN as i128)
1028                && (B::MIN as i128) <= (i64::MAX as i128),
1029        );
1030        debug_assert!(
1031            (i64::MIN as i128) <= (B::MAX as i128)
1032                && (B::MAX as i128) <= (i64::MAX as i128),
1033        );
1034
1035        if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
1036            return Err(RawBoundsError::new());
1037        }
1038        Ok(n as i8)
1039    }
1040
1041    #[cfg_attr(feature = "perf-inline", inline(always))]
1042    pub(super) const fn i16<B>(n: i64) -> Result<i16, RawBoundsError<B>>
1043    where
1044        B: Bounds<Primitive = i16>,
1045    {
1046        // These asserts confirm that we only call this routine
1047        // when our bounds fit into an i64. Otherwise, the
1048        // `as` casts below are incorrect.
1049        debug_assert!(
1050            (i64::MIN as i128) <= (B::MIN as i128)
1051                && (B::MIN as i128) <= (i64::MAX as i128),
1052        );
1053        debug_assert!(
1054            (i64::MIN as i128) <= (B::MAX as i128)
1055                && (B::MAX as i128) <= (i64::MAX as i128),
1056        );
1057
1058        if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
1059            return Err(RawBoundsError::new());
1060        }
1061        Ok(n as i16)
1062    }
1063
1064    #[cfg_attr(feature = "perf-inline", inline(always))]
1065    pub(super) const fn i32<B>(n: i64) -> Result<i32, RawBoundsError<B>>
1066    where
1067        B: Bounds<Primitive = i32>,
1068    {
1069        // These asserts confirm that we only call this routine
1070        // when our bounds fit into an i64. Otherwise, the
1071        // `as` casts below are incorrect.
1072        debug_assert!(
1073            (i64::MIN as i128) <= (B::MIN as i128)
1074                && (B::MIN as i128) <= (i64::MAX as i128),
1075        );
1076        debug_assert!(
1077            (i64::MIN as i128) <= (B::MAX as i128)
1078                && (B::MAX as i128) <= (i64::MAX as i128),
1079        );
1080
1081        if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
1082            return Err(RawBoundsError::new());
1083        }
1084        Ok(n as i32)
1085    }
1086
1087    #[cfg_attr(feature = "perf-inline", inline(always))]
1088    pub(super) const fn i64<B>(n: i64) -> Result<i64, RawBoundsError<B>>
1089    where
1090        B: Bounds<Primitive = i64>,
1091    {
1092        // These asserts confirm that we only call this routine
1093        // when our bounds fit into an i64. Otherwise, the
1094        // `as` casts below are incorrect.
1095        debug_assert!(
1096            (i64::MIN as i128) <= (B::MIN as i128)
1097                && (B::MIN as i128) <= (i64::MAX as i128),
1098        );
1099        debug_assert!(
1100            (i64::MIN as i128) <= (B::MAX as i128)
1101                && (B::MAX as i128) <= (i64::MAX as i128),
1102        );
1103
1104        if !(B::MIN <= n && n <= B::MAX) {
1105            return Err(RawBoundsError::new());
1106        }
1107        Ok(n)
1108    }
1109}
1110
1111/// Computes the next multiple of `rhs` that is greater than or equal to `lhs`.
1112///
1113/// Taken from:
1114/// https://github.com/rust-lang/rust/blob/eff958c59e8c07ba0515e164b825c9001b242294/library/core/src/num/int_macros.rs
1115const fn next_multiple_of(lhs: i64, rhs: i64) -> i64 {
1116    // This would otherwise fail when calculating `r` when self == T::MIN.
1117    if rhs == -1 {
1118        return lhs;
1119    }
1120
1121    let r = lhs % rhs;
1122    let m = if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) { r + rhs } else { r };
1123    if m == 0 {
1124        lhs
1125    } else {
1126        lhs + (rhs - m)
1127    }
1128}
1129
1130#[cfg(test)]
1131mod tests {
1132    use alloc::string::ToString;
1133
1134    use super::*;
1135
1136    #[test]
1137    fn size_of_bounds_error() {
1138        // It's okay if this grows, but I wrote this test initially
1139        // to confirm that a `BoundsError` is very small. (Since it's
1140        // just a bunch of variants of ZSTs.)
1141        assert_eq!(1, core::mem::size_of::<BoundsError>());
1142    }
1143
1144    #[test]
1145    fn basic_error_functionality() {
1146        let err = Year::check(10_000).unwrap_err();
1147        assert_eq!(
1148            err.to_string(),
1149            "parameter 'year' is not in the required range of -9999..=9999",
1150        );
1151    }
1152}