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