structured_pkix/temporal/
precision.rs1use super::{LeapTable, Reading, Uncertainty};
3use crate::error::{Error, Result};
4use structured_asn1::memory::{MemoryError, OwnedVec, Work};
5
6#[derive(Debug)]
10pub struct ExactReading {
11 pub(super) reading: Reading,
12 tail: Option<OwnedVec<u8>>,
13}
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum CompareError {
17 Resource(MemoryError),
19 Uncertain(Uncertainty),
21}
22impl ExactReading {
23 pub(super) fn new<W: Work>(
24 reading: Reading,
25 fraction: Option<(&[u8], i128)>,
26 work: &W,
27 field: &'static str,
28 at: u64,
29 ) -> Result<Self> {
30 if !reading.has_subnanosecond_remainder() {
31 return Ok(Self {
32 reading,
33 tail: None,
34 });
35 }
36 let (mut digits, unit) =
37 fraction.expect("nonzero fractional remainder came from a fraction");
38 let failure = |cause| Error::resource(cause, field, at);
39 work.charge_work(
40 (digits.len() as u64)
41 .checked_mul(3)
42 .ok_or_else(|| failure(MemoryError::WorkExceeded))?,
43 )
44 .map_err(failure)?;
45 while digits.last() == Some(&b'0') {
49 digits = &digits[..digits.len() - 1];
50 }
51 debug_assert!(digits.len() > 9 && unit % 1_000_000_000 == 0);
52 let digits = &digits[9..];
53 let unit = unit / 1_000_000_000;
54 let mut tail = work.vector();
55 tail.try_reserve_exact(digits.len()).map_err(failure)?;
56 let mut carry = 0i128;
57 for digit in digits.iter().rev() {
58 let product = i128::from(digit - b'0') * unit + carry;
61 tail.try_push(u8::try_from(product % 10).expect("decimal remainder") + b'0')
62 .map_err(failure)?;
63 carry = product / 10;
64 }
65 tail.reverse();
66 while tail.last() == Some(&b'0') {
67 tail.pop();
68 }
69 debug_assert!(!tail.is_empty());
70 Ok(Self {
71 reading,
72 tail: Some(tail.into_owned()),
73 })
74 }
75 #[must_use]
77 pub fn reading(&self) -> Reading {
78 self.reading
79 }
80 pub fn try_copy_in<W: Work>(&self, work: &W, field: &'static str, at: u64) -> Result<Self> {
82 let tail = if let Some(tail) = &self.tail {
83 let mut copy = work.vector();
84 copy.try_extend_from_slice(tail)
85 .map_err(|cause| Error::resource(cause, field, at))?;
86 Some(copy.into_owned())
87 } else {
88 None
89 };
90 Ok(Self {
91 reading: self.reading,
92 tail,
93 })
94 }
95 pub fn compare<W: Work>(
98 &self,
99 other: &Self,
100 utc: &LeapTable<'_>,
101 work: &W,
102 ) -> core::result::Result<core::cmp::Ordering, CompareError> {
103 let bits = usize::BITS - utc.transitions().len().leading_zeros();
104 work.charge_work(32 + 6 * (u64::from(bits) + 1))
105 .map_err(CompareError::Resource)?;
106 let left = utc
107 .shift(self.reading.start(), 0)
108 .map_err(CompareError::Uncertain)?;
109 let right = utc
110 .shift(other.reading.start(), 0)
111 .map_err(CompareError::Uncertain)?;
112 if left != right {
113 return Ok(left.cmp(&right));
114 }
115 let a = self.tail.as_deref().unwrap_or(&[]);
116 let b = other.tail.as_deref().unwrap_or(&[]);
117 work.charge_work(1 + a.len().max(b.len()) as u64)
118 .map_err(CompareError::Resource)?;
119 for index in 0..a.len().max(b.len()) {
120 let left = a.get(index).copied().unwrap_or(b'0');
122 let right = b.get(index).copied().unwrap_or(b'0');
123 if left != right {
124 return Ok(left.cmp(&right));
125 }
126 }
127 Ok(core::cmp::Ordering::Equal)
128 }
129}