cbor_core/ext/
num_bigint.rs1use num_bigint::{BigInt, BigUint, Sign};
2
3use crate::{
4 Error, Result, Value, ValueKey, tag,
5 util::{trim_leading_zeros, u64_from_slice},
6};
7
8impl<'a> From<BigUint> for Value<'a> {
13 fn from(value: BigUint) -> Self {
18 from_big_uint(&value)
19 }
20}
21
22impl<'a> From<&BigUint> for Value<'a> {
23 fn from(value: &BigUint) -> Self {
30 from_big_uint(value)
31 }
32}
33
34fn from_big_uint<'a>(value: &BigUint) -> Value<'a> {
35 let bytes = value.to_bytes_be();
36 let trimmed = trim_leading_zeros(&bytes);
37
38 if let Ok(number) = u64_from_slice(trimmed) {
39 Value::Unsigned(number)
40 } else if bytes.len() == trimmed.len() {
41 Value::tag(tag::POS_BIG_INT, bytes) } else {
43 Value::tag(tag::POS_BIG_INT, trimmed.to_vec()) }
45}
46
47impl<'a> From<BigInt> for Value<'a> {
52 fn from(value: BigInt) -> Self {
54 from_big_int(&value)
55 }
56}
57
58impl<'a> From<&BigInt> for Value<'a> {
59 fn from(value: &BigInt) -> Self {
66 from_big_int(value)
67 }
68}
69
70fn from_big_int<'a>(value: &BigInt) -> Value<'a> {
71 let magnitude = value.magnitude();
72 match value.sign() {
73 Sign::NoSign | Sign::Plus => magnitude.into(),
74 Sign::Minus => {
75 let bytes = (magnitude - 1_u32).to_bytes_be();
76 let trimmed = trim_leading_zeros(&bytes);
77
78 if let Ok(number) = u64_from_slice(trimmed) {
79 Value::Negative(number)
80 } else if bytes.len() == trimmed.len() {
81 Value::tag(tag::NEG_BIG_INT, bytes) } else {
83 Value::tag(tag::NEG_BIG_INT, trimmed.to_vec()) }
85 }
86 }
87}
88
89impl<'a> From<BigUint> for ValueKey<'a> {
94 fn from(value: BigUint) -> Self {
95 Value::from(value).into()
96 }
97}
98
99impl<'a> From<BigInt> for ValueKey<'a> {
100 fn from(value: BigInt) -> Self {
101 Value::from(value).into()
102 }
103}
104
105impl<'a> TryFrom<Value<'a>> for BigUint {
110 type Error = Error;
111
112 fn try_from(value: Value<'a>) -> Result<Self> {
117 to_num_biguint(&value)
118 }
119}
120
121impl<'a> TryFrom<&Value<'a>> for BigUint {
122 type Error = Error;
123
124 fn try_from(value: &Value<'a>) -> Result<Self> {
129 to_num_biguint(value)
130 }
131}
132
133fn to_num_biguint(value: &Value<'_>) -> Result<BigUint> {
134 match value.as_integer_bytes()? {
135 crate::integer::IntegerBytes::UnsignedOwned(bytes) => Ok(BigUint::from(u64::from_be_bytes(bytes))),
136 crate::integer::IntegerBytes::NegativeOwned(_) => Err(Error::NegativeUnsigned),
137 crate::integer::IntegerBytes::UnsignedBorrowed(bytes) => Ok(BigUint::from_bytes_be(bytes)),
138 crate::integer::IntegerBytes::NegativeBorrowed(_) => Err(Error::NegativeUnsigned),
139 }
140}
141
142impl<'a> TryFrom<Value<'a>> for BigInt {
147 type Error = Error;
148
149 fn try_from(value: Value<'a>) -> Result<Self> {
153 to_num_bigint(&value)
154 }
155}
156
157impl<'a> TryFrom<&Value<'a>> for BigInt {
158 type Error = Error;
159
160 fn try_from(value: &Value<'a>) -> Result<Self> {
164 to_num_bigint(value)
165 }
166}
167
168fn to_num_bigint(value: &Value<'_>) -> Result<BigInt> {
169 match value.as_integer_bytes()? {
170 crate::integer::IntegerBytes::UnsignedOwned(bytes) => Ok(BigUint::from_bytes_be(&bytes).into()),
171 crate::integer::IntegerBytes::NegativeOwned(bytes) => Ok(BigInt::from(!u64::from_be_bytes(bytes) as i64)),
172 crate::integer::IntegerBytes::UnsignedBorrowed(bytes) => Ok(BigUint::from_bytes_be(bytes).into()),
173 crate::integer::IntegerBytes::NegativeBorrowed(bytes) => {
174 let payload = BigUint::from_bytes_be(bytes);
176 Ok(-(BigInt::from(payload) + 1_i32))
177 }
178 }
179}
180
181#[cfg(test)]
186mod tests {
187 use super::*;
188 use crate::DataType;
189
190 fn roundtrip_biguint(n: BigUint) -> BigUint {
191 let encoded = Value::from(n).encode();
192 let decoded = Value::decode(&encoded).unwrap();
193 BigUint::try_from(decoded).unwrap()
194 }
195
196 fn roundtrip_bigint(n: BigInt) -> BigInt {
197 BigInt::try_from(Value::from(n)).unwrap()
198 }
199
200 #[test]
201 fn biguint_zero() {
202 assert_eq!(roundtrip_biguint(BigUint::ZERO), BigUint::ZERO);
203 }
204
205 #[test]
206 fn biguint_small() {
207 let n = BigUint::from(42_u32);
208 assert_eq!(roundtrip_biguint(n.clone()), n);
209 }
210
211 #[test]
212 fn biguint_u64_max() {
213 let n = BigUint::from(u64::MAX);
214 let v = Value::from(n.clone());
215 assert!(
216 matches!(v, Value::Unsigned(_)),
217 "u64::MAX should encode as plain Unsigned"
218 );
219 assert_eq!(BigUint::try_from(v).unwrap(), n);
220 }
221
222 #[test]
223 fn biguint_u128_max() {
224 let n = BigUint::from(u128::MAX);
225 let v = Value::from(n.clone());
226 assert!(
227 matches!(&v, Value::Tag(2, _)),
228 "u128::MAX should encode as tag-2 bigint"
229 );
230 assert_eq!(BigUint::try_from(v).unwrap(), n);
231 }
232
233 #[test]
234 fn biguint_from_u128_roundtrip() {
235 for x in [0_u128, 1, 42, u64::MAX as u128, u64::MAX as u128 + 1, u128::MAX] {
236 let expected = BigUint::from(x);
237 let via_value = Value::from(x); assert_eq!(BigUint::try_from(via_value).unwrap(), expected, "u128={x}");
239 }
240 }
241
242 #[test]
243 fn biguint_negative_value_errors() {
244 let v = Value::from(-1);
245 assert_eq!(BigUint::try_from(v), Err(Error::NegativeUnsigned));
246
247 let v = Value::from(i128::MIN);
248 assert_eq!(BigUint::try_from(v), Err(Error::NegativeUnsigned));
249 }
250
251 #[test]
252 fn biguint_non_integer_errors() {
253 assert_eq!(
254 BigUint::try_from(Value::from("hello")),
255 Err(Error::IncompatibleType(DataType::Text))
256 );
257 assert_eq!(
258 BigUint::try_from(Value::null()),
259 Err(Error::IncompatibleType(DataType::Null))
260 );
261 }
262
263 #[test]
264 fn bigint_zero() {
265 assert_eq!(roundtrip_bigint(BigInt::ZERO), BigInt::ZERO);
266 }
267
268 #[test]
269 fn bigint_positive_small() {
270 let n = BigInt::from(42);
271 assert_eq!(roundtrip_bigint(n.clone()), n);
272 }
273
274 #[test]
275 fn bigint_negative_one() {
276 let n = BigInt::from(-1);
277 assert_eq!(roundtrip_bigint(n.clone()), n);
278 }
279
280 #[test]
281 fn bigint_i64_min() {
282 let n = BigInt::from(i64::MIN);
283 assert_eq!(roundtrip_bigint(n.clone()), n);
284 }
285
286 #[test]
287 fn bigint_u128_max() {
288 let n = BigInt::from(u128::MAX);
289 let v = Value::from(n.clone());
290 assert!(matches!(&v, Value::Tag(2, _)));
291 assert_eq!(BigInt::try_from(v).unwrap(), n);
292 }
293
294 #[test]
295 fn bigint_i128_min() {
296 let n = BigInt::from(i128::MIN);
297 let v = Value::from(n.clone());
298 assert!(matches!(&v, Value::Tag(3, _)));
299 assert_eq!(BigInt::try_from(v).unwrap(), n);
300 }
301
302 #[test]
303 fn bigint_from_u128_roundtrip() {
304 for x in [0_u128, 1, 42, u64::MAX as u128, u64::MAX as u128 + 1, u128::MAX] {
305 let expected = BigInt::from(x);
306 let via_value = Value::from(x);
307 assert_eq!(BigInt::try_from(via_value).unwrap(), expected, "u128={x}");
308 }
309 }
310
311 #[test]
312 fn bigint_from_i128_roundtrip() {
313 for x in [
314 0,
315 1,
316 -1,
317 42,
318 -42,
319 i64::MIN as i128,
320 i64::MAX as i128,
321 i128::MIN,
322 i128::MAX,
323 ] {
324 let expected = BigInt::from(x);
325 let via_value = Value::from(x);
326 assert_eq!(BigInt::try_from(via_value).unwrap(), expected, "i128={x}");
327 }
328 }
329
330 #[test]
331 fn bigint_non_integer_errors() {
332 assert_eq!(
333 BigInt::try_from(Value::from(0.5)),
334 Err(Error::IncompatibleType(DataType::Float16))
335 );
336 assert_eq!(
337 BigInt::try_from(Value::null()),
338 Err(Error::IncompatibleType(DataType::Null))
339 );
340 }
341
342 #[test]
345 fn bigint_and_biguint_agree_on_positives() {
346 for x in [0u128, 1, u64::MAX as u128, u128::MAX] {
347 let vu = Value::from(BigUint::from(x));
348 let vi = Value::from(BigInt::from(x));
349 assert_eq!(vu, vi, "BigUint/BigInt encoding differs for {x}");
350 }
351 }
352}