1use crypto_bigint::{Choice, Encoding, Int, NonZero, Uint};
2
3use crate::{
4 Error, Result, Tag, Value,
5 integer::IntegerBytes,
6 util::{trim_leading_zeros, u64_from_slice},
7};
8
9fn pad_be_bytes(src: &[u8], dst: &mut [u8]) -> Result<()> {
12 if src.len() > dst.len() {
13 return Err(Error::Overflow);
14 }
15 dst.fill(0);
16 let offset = dst.len() - src.len();
17 dst[offset..].copy_from_slice(src);
18 Ok(())
19}
20
21impl<const LIMBS: usize> From<Uint<LIMBS>> for Value
26where
27 Uint<LIMBS>: Encoding,
28{
29 fn from(value: Uint<LIMBS>) -> Self {
34 from_crypto_uint(&value)
35 }
36}
37
38impl<const LIMBS: usize> From<&Uint<LIMBS>> for Value
39where
40 Uint<LIMBS>: Encoding,
41{
42 fn from(value: &Uint<LIMBS>) -> Self {
43 from_crypto_uint(value)
44 }
45}
46
47fn from_crypto_uint<const LIMBS: usize>(value: &Uint<LIMBS>) -> Value
48where
49 Uint<LIMBS>: Encoding,
50{
51 let be = value.to_be_bytes();
52 let trimmed = trim_leading_zeros(be.as_ref());
53
54 if let Ok(number) = u64_from_slice(trimmed) {
55 Value::Unsigned(number)
56 } else {
57 Value::tag(Tag::POS_BIG_INT, trimmed)
58 }
59}
60
61impl<const LIMBS: usize> From<Int<LIMBS>> for Value
66where
67 Uint<LIMBS>: Encoding,
68{
69 fn from(value: Int<LIMBS>) -> Self {
71 from_crypto_int(&value)
72 }
73}
74
75impl<const LIMBS: usize> From<&Int<LIMBS>> for Value
76where
77 Uint<LIMBS>: Encoding,
78{
79 fn from(value: &Int<LIMBS>) -> Self {
80 from_crypto_int(value)
81 }
82}
83
84fn from_crypto_int<const LIMBS: usize>(value: &Int<LIMBS>) -> Value
85where
86 Uint<LIMBS>: Encoding,
87{
88 let (magnitude, is_negative) = value.abs_sign();
89
90 if !bool::from(is_negative) {
91 from_crypto_uint(&magnitude)
92 } else {
93 let payload = magnitude.wrapping_sub(&Uint::ONE);
95 let be = payload.to_be_bytes();
96 let trimmed = trim_leading_zeros(be.as_ref());
97
98 if let Ok(number) = u64_from_slice(trimmed) {
99 Value::Negative(number)
100 } else {
101 Value::tag(Tag::NEG_BIG_INT, trimmed)
102 }
103 }
104}
105
106impl<const LIMBS: usize> TryFrom<Value> for Uint<LIMBS>
111where
112 Uint<LIMBS>: Encoding,
113{
114 type Error = Error;
115
116 fn try_from(value: Value) -> Result<Self> {
122 to_crypto_uint(&value)
123 }
124}
125
126impl<const LIMBS: usize> TryFrom<&Value> for Uint<LIMBS>
127where
128 Uint<LIMBS>: Encoding,
129{
130 type Error = Error;
131
132 fn try_from(value: &Value) -> Result<Self> {
133 to_crypto_uint(value)
134 }
135}
136
137fn to_crypto_uint<const LIMBS: usize>(value: &Value) -> Result<Uint<LIMBS>>
138where
139 Uint<LIMBS>: Encoding,
140{
141 match value.as_integer_bytes()? {
142 IntegerBytes::UnsignedOwned(bytes) => {
143 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
144 pad_be_bytes(&bytes, &mut buf)?;
145 Ok(Uint::from_be_slice(&buf))
146 }
147
148 IntegerBytes::NegativeOwned(_) => Err(Error::NegativeUnsigned),
149
150 IntegerBytes::UnsignedBorrowed(bytes) => {
151 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
152 pad_be_bytes(bytes, &mut buf)?;
153 Ok(Uint::from_be_slice(&buf))
154 }
155
156 IntegerBytes::NegativeBorrowed(_) => Err(Error::NegativeUnsigned),
157 }
158}
159
160impl<const LIMBS: usize> TryFrom<Value> for Int<LIMBS>
165where
166 Uint<LIMBS>: Encoding,
167{
168 type Error = Error;
169
170 fn try_from(value: Value) -> Result<Self> {
175 to_crypto_int(&value)
176 }
177}
178
179impl<const LIMBS: usize> TryFrom<&Value> for Int<LIMBS>
180where
181 Uint<LIMBS>: Encoding,
182{
183 type Error = Error;
184
185 fn try_from(value: &Value) -> Result<Self> {
186 to_crypto_int(value)
187 }
188}
189
190fn to_crypto_int<const LIMBS: usize>(value: &Value) -> Result<Int<LIMBS>>
191where
192 Uint<LIMBS>: Encoding,
193{
194 match value.as_integer_bytes()? {
195 IntegerBytes::UnsignedOwned(bytes) => {
196 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
197 pad_be_bytes(&bytes, &mut buf)?;
198 let magnitude = Uint::from_be_slice(&buf);
199
200 Int::new_from_abs_sign(magnitude, Choice::from(0))
201 .into_option()
202 .ok_or(Error::Overflow)
203 }
204
205 IntegerBytes::NegativeOwned(bytes) => {
206 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
208 pad_be_bytes(&bytes, &mut buf)?;
209 let payload = Uint::from_be_slice(&buf);
210 let magnitude = payload.wrapping_add(&Uint::ONE);
211
212 if magnitude.is_zero().into() {
215 return Err(Error::Overflow);
216 }
217
218 Int::new_from_abs_sign(magnitude, Choice::from(1))
219 .into_option()
220 .ok_or(Error::Overflow)
221 }
222
223 IntegerBytes::UnsignedBorrowed(bytes) => {
224 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
225 pad_be_bytes(bytes, &mut buf)?;
226 let magnitude = Uint::from_be_slice(&buf);
227
228 Int::new_from_abs_sign(magnitude, Choice::from(0))
229 .into_option()
230 .ok_or(Error::Overflow)
231 }
232
233 IntegerBytes::NegativeBorrowed(bytes) => {
234 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
236 pad_be_bytes(bytes, &mut buf)?;
237 let payload = Uint::from_be_slice(&buf);
238 let magnitude = payload.wrapping_add(&Uint::ONE);
239
240 if magnitude.is_zero().into() {
243 return Err(Error::Overflow);
244 }
245
246 Int::new_from_abs_sign(magnitude, Choice::from(1))
247 .into_option()
248 .ok_or(Error::Overflow)
249 }
250 }
251}
252
253impl<const LIMBS: usize> From<NonZero<Uint<LIMBS>>> for Value
258where
259 Uint<LIMBS>: Encoding,
260{
261 fn from(value: NonZero<Uint<LIMBS>>) -> Self {
262 from_crypto_uint(&value)
263 }
264}
265
266impl<const LIMBS: usize> From<&NonZero<Uint<LIMBS>>> for Value
267where
268 Uint<LIMBS>: Encoding,
269{
270 fn from(value: &NonZero<Uint<LIMBS>>) -> Self {
271 from_crypto_uint(value)
272 }
273}
274
275impl<const LIMBS: usize> From<NonZero<Int<LIMBS>>> for Value
276where
277 Uint<LIMBS>: Encoding,
278{
279 fn from(value: NonZero<Int<LIMBS>>) -> Self {
280 from_crypto_int(&value)
281 }
282}
283
284impl<const LIMBS: usize> From<&NonZero<Int<LIMBS>>> for Value
285where
286 Uint<LIMBS>: Encoding,
287{
288 fn from(value: &NonZero<Int<LIMBS>>) -> Self {
289 from_crypto_int(value)
290 }
291}
292
293#[cfg(test)]
298mod tests {
299 use super::*;
300 use crate::DataType;
301 use crypto_bigint::{U64, U128, U256};
302
303 type I128 = Int<{ U128::LIMBS }>;
304 type I256 = Int<{ U256::LIMBS }>;
305
306 fn roundtrip_uint<const L: usize>(n: Uint<L>) -> Uint<L>
307 where
308 Uint<L>: Encoding,
309 {
310 let encoded = Value::from(n).encode();
311 let decoded = Value::decode(encoded).unwrap();
312 Uint::try_from(decoded).unwrap()
313 }
314
315 fn roundtrip_int<const L: usize>(n: Int<L>) -> Int<L>
316 where
317 Uint<L>: Encoding,
318 {
319 let encoded = Value::from(n).encode();
320 let decoded = Value::decode(encoded).unwrap();
321 Int::try_from(decoded).unwrap()
322 }
323
324 #[test]
327 fn uint_zero() {
328 assert_eq!(roundtrip_uint(U256::ZERO), U256::ZERO);
329 }
330
331 #[test]
332 fn uint_small() {
333 let n = U256::from(42_u64);
334 assert_eq!(roundtrip_uint(n), n);
335 }
336
337 #[test]
338 fn uint_u64_max() {
339 let n = U256::from(u64::MAX);
340 let v = Value::from(n);
341 assert!(
342 matches!(v, Value::Unsigned(_)),
343 "u64::MAX should encode as plain Unsigned"
344 );
345 assert_eq!(Uint::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
346 }
347
348 #[test]
349 fn uint_large() {
350 let n = U256::from(u64::MAX).wrapping_add(&U256::from(2_u64));
352 let v = Value::from(n);
353 assert!(
354 matches!(&v, Value::Tag(2, _)),
355 "value > u64::MAX should encode as tag-2"
356 );
357 assert_eq!(Uint::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
358 }
359
360 #[test]
361 fn uint_max_u128() {
362 let n = U128::MAX;
363 let v = Value::from(n);
364 assert_eq!(U128::try_from(v).unwrap(), n);
365 }
366
367 #[test]
368 fn uint_max_u256() {
369 let n = U256::MAX;
370 let v = Value::from(n);
371 assert_eq!(Uint::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
372 }
373
374 #[test]
375 fn uint_overflow() {
376 let n = U256::from(u64::MAX).wrapping_add(&U256::ONE);
378 let v = Value::from(n);
379 assert_eq!(U64::try_from(v), Err(Error::Overflow));
380 }
381
382 #[test]
383 fn uint_negative_errors() {
384 let v = Value::from(-1);
385 assert_eq!(U256::try_from(v), Err(Error::NegativeUnsigned));
386 }
387
388 #[test]
389 fn uint_non_integer_errors() {
390 assert_eq!(
391 U256::try_from(Value::from("hello")),
392 Err(Error::IncompatibleType(DataType::Text))
393 );
394 assert_eq!(
395 U256::try_from(Value::null()),
396 Err(Error::IncompatibleType(DataType::Null))
397 );
398 }
399
400 #[test]
401 fn uint_from_u128_roundtrip() {
402 for x in [0_u128, 1, 42, u64::MAX.into(), u64::MAX as u128 + 1, u128::MAX] {
403 let expected = U256::from_u128(x);
404 let via_value = Value::from(x);
405 assert_eq!(
406 Uint::<{ U256::LIMBS }>::try_from(via_value).unwrap(),
407 expected,
408 "u128={x}"
409 );
410 }
411 }
412
413 #[test]
416 fn int_zero() {
417 assert_eq!(roundtrip_int(I256::ZERO), I256::ZERO);
418 }
419
420 #[test]
421 fn int_positive_small() {
422 let n = I256::from_i64(42);
423 assert_eq!(roundtrip_int(n), n);
424 }
425
426 #[test]
427 fn int_negative_one() {
428 let n = I256::MINUS_ONE;
429 assert_eq!(roundtrip_int(n), n);
430 }
431
432 #[test]
433 fn int_i64_min() {
434 let n = I256::from_i64(i64::MIN);
435 assert_eq!(roundtrip_int(n), n);
436 }
437
438 #[test]
439 fn int_i128_min() {
440 let n = I128::from_i128(i128::MIN);
441 let v = Value::from(n);
442 assert_eq!(I128::try_from(v).unwrap(), n);
443 }
444
445 #[test]
446 fn int_i128_max() {
447 let n = I128::from_i128(i128::MAX);
448 let v = Value::from(n);
449 assert_eq!(I128::try_from(v).unwrap(), n);
450 }
451
452 #[test]
453 fn int_large_positive() {
454 let big_uint = U256::from(u64::MAX).wrapping_add(&U256::from(2_u64));
456 let v = Value::from(big_uint);
457 let result = Int::<{ U256::LIMBS }>::try_from(v).unwrap();
458 let (mag, sign) = result.abs_sign();
459 assert!(!bool::from(sign));
460 assert_eq!(mag, big_uint);
461 assert_eq!(roundtrip_int(result), result);
462 }
463
464 #[test]
465 fn int_large_negative() {
466 let magnitude = U256::from(u64::MAX).wrapping_add(&U256::from(2_u64));
468 let n = Int::new_from_abs_sign(magnitude, Choice::from(1)).unwrap();
469 let v = Value::from(n);
470 assert!(matches!(&v, Value::Tag(3, _)));
471 assert_eq!(Int::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
472 }
473
474 #[test]
475 fn int_non_integer_errors() {
476 assert_eq!(
477 I256::try_from(Value::from(0.5)),
478 Err(Error::IncompatibleType(DataType::Float16))
479 );
480 assert_eq!(
481 I256::try_from(Value::null()),
482 Err(Error::IncompatibleType(DataType::Null))
483 );
484 }
485
486 #[test]
489 fn nonzero_uint_roundtrip() {
490 let nz = NonZero::new(U256::from(42_u64)).unwrap();
491 let v = Value::from(nz);
492 assert_eq!(U256::try_from(v).unwrap(), U256::from(42_u64));
493 }
494
495 #[test]
496 fn nonzero_int_roundtrip() {
497 let nz = NonZero::new(I256::MINUS_ONE).unwrap();
498 let v = Value::from(nz);
499 assert_eq!(I256::try_from(v).unwrap(), I256::MINUS_ONE);
500 }
501
502 #[test]
505 fn int_and_uint_agree_on_positives() {
506 for x in [0_u64, 1, 42, u64::MAX] {
507 let vu = Value::from(U256::from(x));
508 let vi = Value::from(I256::from_i64(x as i64));
509 if x <= i64::MAX as u64 {
510 assert_eq!(vu, vi, "Uint/Int encoding differs for {x}");
511 }
512 }
513 }
514}