Skip to main content

sacp_cbor/
value.rs

1use alloc::vec::Vec;
2
3use crate::profile::{validate_bignum_bytes, validate_int_safe_i64};
4use crate::CborError;
5
6/// A tagged bignum integer (CBOR tag 2 or 3).
7///
8/// SACP-CBOR/1 represents integers outside the safe range using CBOR tags 2 (positive) and 3 (negative),
9/// with a byte string magnitude encoded canonically (non-empty, no leading zeros).
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct BigInt {
12    negative: bool,
13    magnitude: Vec<u8>,
14}
15
16impl BigInt {
17    /// Construct a `BigInt` from sign and big-endian magnitude bytes.
18    ///
19    /// # Errors
20    ///
21    /// Returns an error if:
22    /// - the magnitude is empty or has a leading zero, or
23    /// - the represented integer would be within the safe integer range.
24    pub fn new(negative: bool, magnitude: Vec<u8>) -> Result<Self, CborError> {
25        validate_bignum_bytes(negative, &magnitude).map_err(|code| CborError::new(code, 0))?;
26        Ok(Self {
27            negative,
28            magnitude,
29        })
30    }
31
32    /// Sign flag: `true` if this represents a negative bignum (tag 3).
33    #[inline]
34    #[must_use]
35    pub const fn is_negative(&self) -> bool {
36        self.negative
37    }
38
39    /// Return the canonical big-endian magnitude bytes.
40    #[inline]
41    #[must_use]
42    pub fn magnitude(&self) -> &[u8] {
43        &self.magnitude
44    }
45}
46
47/// An integer value permitted by SACP-CBOR/1.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct CborInteger(IntegerRepr);
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum IntegerRepr {
53    Safe(i64),
54    Big(BigInt),
55}
56
57impl CborInteger {
58    /// Construct a safe-range integer.
59    ///
60    /// # Errors
61    ///
62    /// Returns `IntegerOutsideSafeRange` if the value is outside the safe range.
63    pub fn safe(value: i64) -> Result<Self, CborError> {
64        validate_int_safe_i64(value).map_err(|code| CborError::new(code, 0))?;
65        Ok(Self(IntegerRepr::Safe(value)))
66    }
67
68    /// Construct a bignum integer from sign and magnitude bytes.
69    ///
70    /// # Errors
71    ///
72    /// Returns an error if the magnitude is not canonical or is within the safe integer range.
73    pub fn big(negative: bool, magnitude: Vec<u8>) -> Result<Self, CborError> {
74        Ok(Self(IntegerRepr::Big(BigInt::new(negative, magnitude)?)))
75    }
76
77    /// Construct a bignum integer from an existing `BigInt`.
78    #[inline]
79    #[must_use]
80    pub const fn from_bigint(big: BigInt) -> Self {
81        Self(IntegerRepr::Big(big))
82    }
83
84    /// Returns `true` iff this is a safe-range integer.
85    #[inline]
86    #[must_use]
87    pub const fn is_safe(&self) -> bool {
88        matches!(self.0, IntegerRepr::Safe(_))
89    }
90
91    /// Returns `true` iff this is a bignum integer.
92    #[inline]
93    #[must_use]
94    pub const fn is_big(&self) -> bool {
95        matches!(self.0, IntegerRepr::Big(_))
96    }
97
98    /// Return the safe integer value if available.
99    #[inline]
100    #[must_use]
101    pub const fn as_i64(&self) -> Option<i64> {
102        match &self.0 {
103            IntegerRepr::Safe(v) => Some(*v),
104            IntegerRepr::Big(_) => None,
105        }
106    }
107
108    /// Return the underlying bignum if available.
109    #[inline]
110    #[must_use]
111    pub const fn as_bigint(&self) -> Option<&BigInt> {
112        match &self.0 {
113            IntegerRepr::Big(b) => Some(b),
114            IntegerRepr::Safe(_) => None,
115        }
116    }
117}
118
119impl From<BigInt> for CborInteger {
120    fn from(value: BigInt) -> Self {
121        Self(IntegerRepr::Big(value))
122    }
123}