1use crypto_bigint::{Choice, Encoding, Int, NonZero, Uint};
2
3use crate::{
4 Error, Result, Value, ValueKey,
5 integer::IntegerBytes,
6 tag,
7 util::{trim_leading_zeros, u64_from_slice},
8};
9
10fn pad_be_bytes(src: &[u8], dst: &mut [u8]) -> Result<()> {
13 if src.len() > dst.len() {
14 return Err(Error::Overflow);
15 }
16 dst.fill(0);
17 let offset = dst.len() - src.len();
18 dst[offset..].copy_from_slice(src);
19 Ok(())
20}
21
22impl<'a, const LIMBS: usize> From<Uint<LIMBS>> for Value<'a>
27where
28 Uint<LIMBS>: Encoding,
29{
30 fn from(value: Uint<LIMBS>) -> Self {
35 from_crypto_uint(&value)
36 }
37}
38
39impl<'a, const LIMBS: usize> From<&Uint<LIMBS>> for Value<'a>
40where
41 Uint<LIMBS>: Encoding,
42{
43 fn from(value: &Uint<LIMBS>) -> Self {
50 from_crypto_uint(value)
51 }
52}
53
54fn from_crypto_uint<'a, const LIMBS: usize>(value: &Uint<LIMBS>) -> Value<'a>
55where
56 Uint<LIMBS>: Encoding,
57{
58 let be = value.to_be_bytes();
59 let trimmed = trim_leading_zeros(be.as_ref());
60
61 if let Ok(number) = u64_from_slice(trimmed) {
62 Value::Unsigned(number)
63 } else {
64 Value::tag(tag::POS_BIG_INT, trimmed.to_vec())
65 }
66}
67
68impl<'a, const LIMBS: usize> From<Int<LIMBS>> for Value<'a>
73where
74 Uint<LIMBS>: Encoding,
75{
76 fn from(value: Int<LIMBS>) -> Self {
78 from_crypto_int(&value)
79 }
80}
81
82impl<'a, const LIMBS: usize> From<&Int<LIMBS>> for Value<'a>
83where
84 Uint<LIMBS>: Encoding,
85{
86 fn from(value: &Int<LIMBS>) -> Self {
93 from_crypto_int(value)
94 }
95}
96
97fn from_crypto_int<'a, const LIMBS: usize>(value: &Int<LIMBS>) -> Value<'a>
98where
99 Uint<LIMBS>: Encoding,
100{
101 let (magnitude, is_negative) = value.abs_sign();
102
103 if !bool::from(is_negative) {
104 from_crypto_uint(&magnitude)
105 } else {
106 let payload = magnitude.wrapping_sub(&Uint::ONE);
108 let be = payload.to_be_bytes();
109 let trimmed = trim_leading_zeros(be.as_ref());
110
111 if let Ok(number) = u64_from_slice(trimmed) {
112 Value::Negative(number)
113 } else {
114 Value::tag(tag::NEG_BIG_INT, trimmed.to_vec())
115 }
116 }
117}
118
119impl<'a, const LIMBS: usize> TryFrom<Value<'a>> for Uint<LIMBS>
124where
125 Uint<LIMBS>: Encoding,
126{
127 type Error = Error;
128
129 fn try_from(value: Value) -> Result<Self> {
135 to_crypto_uint(&value)
136 }
137}
138
139impl<'a, const LIMBS: usize> TryFrom<&Value<'a>> for Uint<LIMBS>
140where
141 Uint<LIMBS>: Encoding,
142{
143 type Error = Error;
144
145 fn try_from(value: &Value) -> Result<Self> {
146 to_crypto_uint(value)
147 }
148}
149
150fn to_crypto_uint<const LIMBS: usize>(value: &Value) -> Result<Uint<LIMBS>>
151where
152 Uint<LIMBS>: Encoding,
153{
154 match value.as_integer_bytes()? {
155 IntegerBytes::UnsignedOwned(bytes) => {
156 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
157 pad_be_bytes(&bytes, &mut buf)?;
158 Ok(Uint::from_be_slice(&buf))
159 }
160
161 IntegerBytes::NegativeOwned(_) => Err(Error::NegativeUnsigned),
162
163 IntegerBytes::UnsignedBorrowed(bytes) => {
164 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
165 pad_be_bytes(bytes, &mut buf)?;
166 Ok(Uint::from_be_slice(&buf))
167 }
168
169 IntegerBytes::NegativeBorrowed(_) => Err(Error::NegativeUnsigned),
170 }
171}
172
173impl<'a, const LIMBS: usize> TryFrom<Value<'a>> for Int<LIMBS>
178where
179 Uint<LIMBS>: Encoding,
180{
181 type Error = Error;
182
183 fn try_from(value: Value) -> Result<Self> {
188 to_crypto_int(&value)
189 }
190}
191
192impl<'a, const LIMBS: usize> TryFrom<&Value<'a>> for Int<LIMBS>
193where
194 Uint<LIMBS>: Encoding,
195{
196 type Error = Error;
197
198 fn try_from(value: &Value) -> Result<Self> {
199 to_crypto_int(value)
200 }
201}
202
203fn to_crypto_int<const LIMBS: usize>(value: &Value) -> Result<Int<LIMBS>>
204where
205 Uint<LIMBS>: Encoding,
206{
207 match value.as_integer_bytes()? {
208 IntegerBytes::UnsignedOwned(bytes) => {
209 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
210 pad_be_bytes(&bytes, &mut buf)?;
211 let magnitude = Uint::from_be_slice(&buf);
212
213 Int::new_from_abs_sign(magnitude, Choice::from(0))
214 .into_option()
215 .ok_or(Error::Overflow)
216 }
217
218 IntegerBytes::NegativeOwned(bytes) => {
219 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
221 pad_be_bytes(&bytes, &mut buf)?;
222 let payload = Uint::from_be_slice(&buf);
223 let magnitude = payload.wrapping_add(&Uint::ONE);
224
225 if magnitude.is_zero().into() {
228 return Err(Error::Overflow);
229 }
230
231 Int::new_from_abs_sign(magnitude, Choice::from(1))
232 .into_option()
233 .ok_or(Error::Overflow)
234 }
235
236 IntegerBytes::UnsignedBorrowed(bytes) => {
237 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
238 pad_be_bytes(bytes, &mut buf)?;
239 let magnitude = Uint::from_be_slice(&buf);
240
241 Int::new_from_abs_sign(magnitude, Choice::from(0))
242 .into_option()
243 .ok_or(Error::Overflow)
244 }
245
246 IntegerBytes::NegativeBorrowed(bytes) => {
247 let mut buf = vec![0_u8; Uint::<LIMBS>::BYTES];
249 pad_be_bytes(bytes, &mut buf)?;
250 let payload = Uint::from_be_slice(&buf);
251 let magnitude = payload.wrapping_add(&Uint::ONE);
252
253 if magnitude.is_zero().into() {
256 return Err(Error::Overflow);
257 }
258
259 Int::new_from_abs_sign(magnitude, Choice::from(1))
260 .into_option()
261 .ok_or(Error::Overflow)
262 }
263 }
264}
265
266impl<'a, const LIMBS: usize> From<NonZero<Uint<LIMBS>>> for Value<'a>
271where
272 Uint<LIMBS>: Encoding,
273{
274 fn from(value: NonZero<Uint<LIMBS>>) -> Self {
275 from_crypto_uint(&value)
276 }
277}
278
279impl<'a, const LIMBS: usize> From<&NonZero<Uint<LIMBS>>> for Value<'a>
280where
281 Uint<LIMBS>: Encoding,
282{
283 fn from(value: &NonZero<Uint<LIMBS>>) -> Self {
290 from_crypto_uint(value)
291 }
292}
293
294impl<'a, const LIMBS: usize> From<NonZero<Int<LIMBS>>> for Value<'a>
295where
296 Uint<LIMBS>: Encoding,
297{
298 fn from(value: NonZero<Int<LIMBS>>) -> Self {
299 from_crypto_int(&value)
300 }
301}
302
303impl<'a, const LIMBS: usize> From<&NonZero<Int<LIMBS>>> for Value<'a>
304where
305 Uint<LIMBS>: Encoding,
306{
307 fn from(value: &NonZero<Int<LIMBS>>) -> Self {
314 from_crypto_int(value)
315 }
316}
317
318macro_rules! impl_value_key {
323 ($($type:ty),* $(,)?) => { $(
324 impl<'a, const LIMBS: usize> From<$type> for ValueKey<'a>
325 where
326 Uint<LIMBS>: Encoding,
327 {
328 fn from(value: $type) -> Self {
329 Value::from(value).into()
330 }
331 }
332 )* }
333}
334
335impl_value_key!(Uint<LIMBS>, Int<LIMBS>, NonZero<Uint<LIMBS>>, NonZero<Int<LIMBS>>);
336
337#[cfg(test)]
342mod tests {
343 use super::*;
344 use crate::DataType;
345 use crypto_bigint::{U64, U128, U256};
346
347 type I128 = Int<{ U128::LIMBS }>;
348 type I256 = Int<{ U256::LIMBS }>;
349
350 fn roundtrip_uint<const L: usize>(n: Uint<L>) -> Uint<L>
351 where
352 Uint<L>: Encoding,
353 {
354 let encoded = Value::from(n).encode();
355 let decoded = Value::decode(&encoded).unwrap();
356 Uint::try_from(decoded).unwrap()
357 }
358
359 fn roundtrip_int<const L: usize>(n: Int<L>) -> Int<L>
360 where
361 Uint<L>: Encoding,
362 {
363 let encoded = Value::from(n).encode();
364 let decoded = Value::decode(&encoded).unwrap();
365 Int::try_from(decoded).unwrap()
366 }
367
368 #[test]
371 fn uint_zero() {
372 assert_eq!(roundtrip_uint(U256::ZERO), U256::ZERO);
373 }
374
375 #[test]
376 fn uint_small() {
377 let n = U256::from(42_u64);
378 assert_eq!(roundtrip_uint(n), n);
379 }
380
381 #[test]
382 fn uint_u64_max() {
383 let n = U256::from(u64::MAX);
384 let v = Value::from(n);
385 assert!(
386 matches!(v, Value::Unsigned(_)),
387 "u64::MAX should encode as plain Unsigned"
388 );
389 assert_eq!(Uint::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
390 }
391
392 #[test]
393 fn uint_large() {
394 let n = U256::from(u64::MAX).wrapping_add(&U256::from(2_u64));
396 let v = Value::from(n);
397 assert!(
398 matches!(&v, Value::Tag(2, _)),
399 "value > u64::MAX should encode as tag-2"
400 );
401 assert_eq!(Uint::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
402 }
403
404 #[test]
405 fn uint_max_u128() {
406 let n = U128::MAX;
407 let v = Value::from(n);
408 assert_eq!(U128::try_from(v).unwrap(), n);
409 }
410
411 #[test]
412 fn uint_max_u256() {
413 let n = U256::MAX;
414 let v = Value::from(n);
415 assert_eq!(Uint::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
416 }
417
418 #[test]
419 fn uint_overflow() {
420 let n = U256::from(u64::MAX).wrapping_add(&U256::ONE);
422 let v = Value::from(n);
423 assert_eq!(U64::try_from(v), Err(Error::Overflow));
424 }
425
426 #[test]
427 fn uint_negative_errors() {
428 let v = Value::from(-1);
429 assert_eq!(U256::try_from(v), Err(Error::NegativeUnsigned));
430 }
431
432 #[test]
433 fn uint_non_integer_errors() {
434 assert_eq!(
435 U256::try_from(Value::from("hello")),
436 Err(Error::IncompatibleType(DataType::Text))
437 );
438 assert_eq!(
439 U256::try_from(Value::null()),
440 Err(Error::IncompatibleType(DataType::Null))
441 );
442 }
443
444 #[test]
445 fn uint_from_u128_roundtrip() {
446 for x in [0_u128, 1, 42, u64::MAX.into(), u64::MAX as u128 + 1, u128::MAX] {
447 let expected = U256::from_u128(x);
448 let via_value = Value::from(x);
449 assert_eq!(
450 Uint::<{ U256::LIMBS }>::try_from(via_value).unwrap(),
451 expected,
452 "u128={x}"
453 );
454 }
455 }
456
457 #[test]
460 fn int_zero() {
461 assert_eq!(roundtrip_int(I256::ZERO), I256::ZERO);
462 }
463
464 #[test]
465 fn int_positive_small() {
466 let n = I256::from_i64(42);
467 assert_eq!(roundtrip_int(n), n);
468 }
469
470 #[test]
471 fn int_negative_one() {
472 let n = I256::MINUS_ONE;
473 assert_eq!(roundtrip_int(n), n);
474 }
475
476 #[test]
477 fn int_i64_min() {
478 let n = I256::from_i64(i64::MIN);
479 assert_eq!(roundtrip_int(n), n);
480 }
481
482 #[test]
483 fn int_i128_min() {
484 let n = I128::from_i128(i128::MIN);
485 let v = Value::from(n);
486 assert_eq!(I128::try_from(v).unwrap(), n);
487 }
488
489 #[test]
490 fn int_i128_max() {
491 let n = I128::from_i128(i128::MAX);
492 let v = Value::from(n);
493 assert_eq!(I128::try_from(v).unwrap(), n);
494 }
495
496 #[test]
497 fn int_large_positive() {
498 let big_uint = U256::from(u64::MAX).wrapping_add(&U256::from(2_u64));
500 let v = Value::from(big_uint);
501 let result = Int::<{ U256::LIMBS }>::try_from(v).unwrap();
502 let (mag, sign) = result.abs_sign();
503 assert!(!bool::from(sign));
504 assert_eq!(mag, big_uint);
505 assert_eq!(roundtrip_int(result), result);
506 }
507
508 #[test]
509 fn int_large_negative() {
510 let magnitude = U256::from(u64::MAX).wrapping_add(&U256::from(2_u64));
512 let n = Int::new_from_abs_sign(magnitude, Choice::from(1)).unwrap();
513 let v = Value::from(n);
514 assert!(matches!(&v, Value::Tag(3, _)));
515 assert_eq!(Int::<{ U256::LIMBS }>::try_from(v).unwrap(), n);
516 }
517
518 #[test]
519 fn int_non_integer_errors() {
520 assert_eq!(
521 I256::try_from(Value::from(0.5)),
522 Err(Error::IncompatibleType(DataType::Float16))
523 );
524 assert_eq!(
525 I256::try_from(Value::null()),
526 Err(Error::IncompatibleType(DataType::Null))
527 );
528 }
529
530 #[test]
533 fn nonzero_uint_roundtrip() {
534 let nz = NonZero::new(U256::from(42_u64)).unwrap();
535 let v = Value::from(nz);
536 assert_eq!(U256::try_from(v).unwrap(), U256::from(42_u64));
537 }
538
539 #[test]
540 fn nonzero_int_roundtrip() {
541 let nz = NonZero::new(I256::MINUS_ONE).unwrap();
542 let v = Value::from(nz);
543 assert_eq!(I256::try_from(v).unwrap(), I256::MINUS_ONE);
544 }
545
546 #[test]
549 fn int_and_uint_agree_on_positives() {
550 for x in [0_u64, 1, 42, u64::MAX] {
551 let vu = Value::from(U256::from(x));
552 let vi = Value::from(I256::from_i64(x as i64));
553 if x <= i64::MAX as u64 {
554 assert_eq!(vu, vi, "Uint/Int encoding differs for {x}");
555 }
556 }
557 }
558}