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