1pub use alloy::primitives::Uint;
2pub use alloy::primitives::U256;
3use std::ops::Deref;
4use std::str::FromStr;
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9mod convert;
10mod operation;
11mod primitive_ops;
12
13#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
56#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub struct SqlUint<const BITS: usize, const LIMBS: usize>(Uint<BITS, LIMBS>);
58pub type SqlU256 = SqlUint<256, 4>;
60
61impl<const BITS: usize, const LIMBS: usize> SqlUint<BITS, LIMBS> {
62 pub const ZERO: Self = SqlUint(Uint::ZERO);
68
69 pub fn inner(&self) -> &Uint<BITS, LIMBS> {
82 &self.0
83 }
84
85 pub fn into_inner(self) -> Uint<BITS, LIMBS> {
87 self.0
88 }
89}
90
91impl SqlU256 {
92 pub const ETHER: Self = Self(U256::from_limbs([0x0, 0x8AC7230489E80000, 0, 0]));
94
95 pub fn from_be_slice(bytes: &[u8]) -> Self {
97 Self(alloy::primitives::U256::from_be_slice(bytes))
98 }
99
100 pub fn as_u8(&self) -> Result<u8, &'static str> {
102 if self.0 > U256::from(u8::MAX) {
103 Err("SqlU256 value too large for u8")
104 } else {
105 Ok(self.0.to::<u8>())
106 }
107 }
108 pub fn as_u16(&self) -> Result<u16, &'static str> {
110 if self.0 > U256::from(u16::MAX) {
111 Err("SqlU256 value too large for u16")
112 } else {
113 Ok(self.0.to::<u16>())
114 }
115 }
116 pub fn as_u32(&self) -> Result<u32, &'static str> {
118 if self.0 > U256::from(u32::MAX) {
119 Err("SqlU256 value too large for u32")
120 } else {
121 Ok(self.0.to::<u32>())
122 }
123 }
124 pub fn as_u64(&self) -> Result<u64, &'static str> {
126 if self.0 > U256::from(u64::MAX) {
127 Err("SqlU256 value too large for u64")
128 } else {
129 Ok(self.0.to::<u64>())
130 }
131 }
132 pub fn as_u128(&self) -> Result<u128, &'static str> {
134 if self.0 > U256::from(u128::MAX) {
135 Err("SqlU256 value too large for u128")
136 } else {
137 Ok(self.0.to::<u128>())
138 }
139 }
140}
141
142impl<const BITS: usize, const LIMBS: usize> AsRef<Uint<BITS, LIMBS>> for SqlUint<BITS, LIMBS> {
143 fn as_ref(&self) -> &Uint<BITS, LIMBS> {
144 &self.0
145 }
146}
147impl<const BITS: usize, const LIMBS: usize> Deref for SqlUint<BITS, LIMBS> {
148 type Target = Uint<BITS, LIMBS>;
149
150 fn deref(&self) -> &Self::Target {
151 &self.0
152 }
153}
154
155impl<const BITS: usize, const LIMBS: usize> From<Uint<BITS, LIMBS>> for SqlUint<BITS, LIMBS> {
156 fn from(value: Uint<BITS, LIMBS>) -> Self {
157 SqlUint(value)
158 }
159}
160
161impl<const BITS: usize, const LIMBS: usize> From<SqlUint<BITS, LIMBS>> for Uint<BITS, LIMBS> {
162 fn from(value: SqlUint<BITS, LIMBS>) -> Self {
163 value.0
164 }
165}
166
167impl<const BITS: usize, const LIMBS: usize> FromStr for SqlUint<BITS, LIMBS> {
168 type Err = <Uint<BITS, LIMBS> as FromStr>::Err;
169
170 fn from_str(s: &str) -> Result<Self, Self::Err> {
171 Uint::from_str(s).map(SqlUint)
172 }
173}
174
175impl<const BITS: usize, const LIMBS: usize> std::fmt::Display for SqlUint<BITS, LIMBS> {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 write!(f, "0x{:x}", self.0)
178 }
179}
180
181impl<const BITS: usize, const LIMBS: usize> std::fmt::LowerHex for SqlUint<BITS, LIMBS> {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 self.0.fmt(f)
184 }
185}
186
187impl<const BITS: usize, const LIMBS: usize> std::fmt::UpperHex for SqlUint<BITS, LIMBS> {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 self.0.fmt(f)
190 }
191}
192
193impl Default for SqlU256 {
194 fn default() -> Self {
195 SqlU256::ZERO
196 }
197}
198
199impl PartialEq<u8> for crate::SqlU256 {
201 fn eq(&self, other: &u8) -> bool {
202 *self == crate::SqlU256::from(*other)
203 }
204}
205impl PartialEq<u16> for crate::SqlU256 {
206 fn eq(&self, other: &u16) -> bool {
207 *self == crate::SqlU256::from(*other)
208 }
209}
210impl PartialEq<u32> for crate::SqlU256 {
211 fn eq(&self, other: &u32) -> bool {
212 *self == crate::SqlU256::from(*other)
213 }
214}
215impl PartialEq<u64> for crate::SqlU256 {
216 fn eq(&self, other: &u64) -> bool {
217 *self == crate::SqlU256::from(*other)
218 }
219}
220impl PartialEq<u128> for crate::SqlU256 {
221 fn eq(&self, other: &u128) -> bool {
222 *self == crate::SqlU256::from(*other)
223 }
224}
225
226impl PartialOrd<u8> for crate::SqlU256 {
227 fn partial_cmp(&self, other: &u8) -> Option<std::cmp::Ordering> {
228 self.partial_cmp(&crate::SqlU256::from(*other))
229 }
230}
231impl PartialOrd<u16> for crate::SqlU256 {
232 fn partial_cmp(&self, other: &u16) -> Option<std::cmp::Ordering> {
233 self.partial_cmp(&crate::SqlU256::from(*other))
234 }
235}
236impl PartialOrd<u32> for crate::SqlU256 {
237 fn partial_cmp(&self, other: &u32) -> Option<std::cmp::Ordering> {
238 self.partial_cmp(&crate::SqlU256::from(*other))
239 }
240}
241impl PartialOrd<u64> for crate::SqlU256 {
242 fn partial_cmp(&self, other: &u64) -> Option<std::cmp::Ordering> {
243 self.partial_cmp(&crate::SqlU256::from(*other))
244 }
245}
246impl PartialOrd<u128> for crate::SqlU256 {
247 fn partial_cmp(&self, other: &u128) -> Option<std::cmp::Ordering> {
248 self.partial_cmp(&crate::SqlU256::from(*other))
249 }
250}
251
252impl PartialEq<crate::SqlU256> for u8 {
254 fn eq(&self, other: &crate::SqlU256) -> bool {
255 crate::SqlU256::from(*self) == *other
256 }
257}
258impl PartialEq<crate::SqlU256> for u16 {
259 fn eq(&self, other: &crate::SqlU256) -> bool {
260 crate::SqlU256::from(*self) == *other
261 }
262}
263impl PartialEq<crate::SqlU256> for u32 {
264 fn eq(&self, other: &crate::SqlU256) -> bool {
265 crate::SqlU256::from(*self) == *other
266 }
267}
268impl PartialEq<crate::SqlU256> for u64 {
269 fn eq(&self, other: &crate::SqlU256) -> bool {
270 crate::SqlU256::from(*self) == *other
271 }
272}
273impl PartialEq<crate::SqlU256> for u128 {
274 fn eq(&self, other: &crate::SqlU256) -> bool {
275 crate::SqlU256::from(*self) == *other
276 }
277}
278
279impl PartialOrd<crate::SqlU256> for u8 {
280 fn partial_cmp(&self, other: &crate::SqlU256) -> Option<std::cmp::Ordering> {
281 crate::SqlU256::from(*self).partial_cmp(other)
282 }
283}
284impl PartialOrd<crate::SqlU256> for u16 {
285 fn partial_cmp(&self, other: &crate::SqlU256) -> Option<std::cmp::Ordering> {
286 crate::SqlU256::from(*self).partial_cmp(other)
287 }
288}
289impl PartialOrd<crate::SqlU256> for u32 {
290 fn partial_cmp(&self, other: &crate::SqlU256) -> Option<std::cmp::Ordering> {
291 crate::SqlU256::from(*self).partial_cmp(other)
292 }
293}
294impl PartialOrd<crate::SqlU256> for u64 {
295 fn partial_cmp(&self, other: &crate::SqlU256) -> Option<std::cmp::Ordering> {
296 crate::SqlU256::from(*self).partial_cmp(other)
297 }
298}
299impl PartialOrd<crate::SqlU256> for u128 {
300 fn partial_cmp(&self, other: &crate::SqlU256) -> Option<std::cmp::Ordering> {
301 crate::SqlU256::from(*self).partial_cmp(other)
302 }
303}
304
305#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[cfg(feature = "serde")]
312 #[test]
313 fn test_serde() {
314 let value = "0x12345678";
315 let s_value = SqlUint::<32, 1>::from_str(value).unwrap();
316 let json = serde_json::to_string(&s_value).unwrap();
317 assert_eq!(json, "\"0x12345678\"");
318 let de: SqlUint<32, 1> = serde_json::from_str(&json).unwrap();
319 assert_eq!(s_value, de);
320 }
321
322 #[test]
323 fn test_creation_and_constants() {
324 let zero = SqlU256::ZERO;
326 assert_eq!(zero, SqlU256::from(0u64));
327
328 let value = SqlU256::from(U256::from(42u64));
330 assert_eq!(value, SqlU256::from(42u64));
331 }
332
333 #[test]
334 fn test_from_conversions() {
335 let u256_val = U256::from(123456789u64);
337 let sql_u256 = SqlU256::from(u256_val);
338 assert_eq!(sql_u256.inner(), &u256_val);
339
340 let back_to_u256: U256 = sql_u256.into();
342 assert_eq!(back_to_u256, u256_val);
343 }
344
345 #[test]
346 fn test_inner_and_deref() {
347 let sql_u256 = SqlU256::from(42u64);
348
349 let inner_ref: &U256 = sql_u256.inner();
351 assert_eq!(*inner_ref, U256::from(42u64));
352
353 let as_ref: &U256 = sql_u256.as_ref();
355 assert_eq!(*as_ref, U256::from(42u64));
356
357 let deref_val: U256 = *sql_u256;
359 assert_eq!(deref_val, U256::from(42u64));
360 }
361
362 #[test]
363 fn test_from_str_parsing() {
364 let from_decimal = SqlU256::from_str("123456789").unwrap();
366 assert_eq!(from_decimal, SqlU256::from(123456789u64));
367
368 let from_hex = SqlU256::from_str("0x75bcd15").unwrap();
370 assert_eq!(from_hex, SqlU256::from(123456789u64));
371
372 assert_eq!(from_decimal, from_hex);
374
375 let zero_decimal = SqlU256::from_str("0").unwrap();
377 let zero_hex = SqlU256::from_str("0x0").unwrap();
378 assert_eq!(zero_decimal, zero_hex);
379 assert_eq!(zero_decimal, SqlU256::ZERO);
380 }
381
382 #[test]
383 fn test_from_str_edge_cases() {
384 let max_hex = format!("0x{:x}", U256::MAX);
386 let max_sql = SqlU256::from_str(&max_hex).unwrap();
387 assert_eq!(max_sql.inner(), &U256::MAX);
388
389 let zero_cases = [
391 ("", "empty string"),
392 ("0", "zero"),
393 ("00", "double zero"),
394 ("0x", "just 0x prefix"),
395 ("0x0", "0x0"),
396 ("0x00", "0x00"),
397 ];
398
399 for (input, _desc) in zero_cases {
400 let result = SqlU256::from_str(input).unwrap();
401 assert_eq!(
402 result,
403 SqlU256::ZERO,
404 "Input '{}' should parse as zero",
405 input
406 );
407 }
408
409 assert!(SqlU256::from_str("not_a_number").is_err());
411 assert!(SqlU256::from_str("0xnot_hex").is_err());
412 assert!(SqlU256::from_str("123abc").is_err());
413 assert!(SqlU256::from_str("0x123xyz").is_err());
414 }
415
416 #[test]
417 fn test_display_formatting() {
418 let test_cases = [
419 (0u64, "0x0"),
420 (255u64, "0xff"),
421 (0xdeadbeef_u64, "0xdeadbeef"),
422 (123456789u64, "0x75bcd15"),
423 ];
424
425 for (input, expected) in test_cases {
426 let sql_u256 = SqlU256::from(input);
427 let display_str = format!("{}", sql_u256);
428 assert_eq!(display_str, expected);
429 }
430 }
431
432 #[test]
433 fn test_round_trip_consistency() {
434 let test_values = [
435 U256::from(0u64),
436 U256::from(1u64),
437 U256::from(255u64),
438 U256::from(0xdeadbeef_u64),
439 U256::from(123456789u64),
440 U256::MAX,
441 ];
442
443 for original_u256 in test_values {
444 let sql_u256 = SqlU256::from(original_u256);
445
446 let display_str = format!("{}", sql_u256);
448 let parsed_back = SqlU256::from_str(&display_str).unwrap();
449 assert_eq!(sql_u256, parsed_back);
450
451 let back_to_u256: U256 = sql_u256.into();
453 assert_eq!(back_to_u256, original_u256);
454 }
455 }
456
457 #[test]
458 fn test_equality_and_comparison() {
459 let a = SqlU256::from(100u64);
460 let b = SqlU256::from(100u64);
461 let c = SqlU256::from(200u64);
462
463 assert_eq!(a, b);
465 assert_ne!(a, c);
466
467 let zero1 = SqlU256::ZERO;
469 let zero2 = SqlU256::from(0u64);
470 assert_eq!(zero1, zero2);
471 }
472
473 #[test]
474 fn test_clone_and_copy() {
475 let original = SqlU256::from(42u64);
476
477 let cloned = original.clone();
479 assert_eq!(original, cloned);
480
481 let copied = original;
483 assert_eq!(original, copied);
484
485 assert_eq!(original, SqlU256::from(42u64));
487 }
488
489 #[test]
490 fn test_debug_formatting() {
491 let sql_u256 = SqlU256::from(42u64);
492 let debug_str = format!("{:?}", sql_u256);
493 assert!(debug_str.contains("SqlUint"));
495 }
496
497 #[test]
498 fn test_sql_u256_hash() {
499 use std::collections::{HashMap, HashSet};
500
501 let val1 = SqlU256::from(123u64);
502 let val2 = SqlU256::from(123u64);
503 let val3 = SqlU256::from(456u64);
504
505 use std::hash::{DefaultHasher, Hash, Hasher};
507
508 let mut hasher1 = DefaultHasher::new();
509 let mut hasher2 = DefaultHasher::new();
510 let mut hasher3 = DefaultHasher::new();
511
512 val1.hash(&mut hasher1);
513 val2.hash(&mut hasher2);
514 val3.hash(&mut hasher3);
515
516 assert_eq!(hasher1.finish(), hasher2.finish());
517 assert_ne!(hasher1.finish(), hasher3.finish());
518
519 let mut value_set = HashSet::new();
521 value_set.insert(val1);
522 value_set.insert(val2); value_set.insert(val3);
524 value_set.insert(SqlU256::ZERO);
525
526 assert_eq!(value_set.len(), 3);
527 assert!(value_set.contains(&val1));
528 assert!(value_set.contains(&val2));
529 assert!(value_set.contains(&val3));
530 assert!(value_set.contains(&SqlU256::ZERO));
531
532 let mut value_map = HashMap::new();
534 value_map.insert(val1, "First value");
535 value_map.insert(val2, "Same value"); value_map.insert(val3, "Different value");
537 value_map.insert(SqlU256::ZERO, "Zero value");
538
539 assert_eq!(value_map.len(), 3);
540 assert_eq!(value_map.get(&val1), Some(&"Same value"));
541 assert_eq!(value_map.get(&val2), Some(&"Same value"));
542 assert_eq!(value_map.get(&val3), Some(&"Different value"));
543 assert_eq!(value_map.get(&SqlU256::ZERO), Some(&"Zero value"));
544
545 let large1 =
547 SqlU256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
548 .unwrap();
549 let large2 = SqlU256::from_str(
550 "115792089237316195423570985008687907853269984665640564039457584007913129639935",
551 )
552 .unwrap(); let mut large_hasher1 = DefaultHasher::new();
555 let mut large_hasher2 = DefaultHasher::new();
556
557 large1.hash(&mut large_hasher1);
558 large2.hash(&mut large_hasher2);
559
560 assert_eq!(large_hasher1.finish(), large_hasher2.finish());
561 assert_eq!(large1, large2);
562 }
563
564 #[test]
565 fn test_sql_u256_hash_consistency_with_alloy_u256() {
566 use std::hash::{DefaultHasher, Hash, Hasher};
567
568 fn calculate_hash<T: Hash>(t: &T) -> u64 {
569 let mut hasher = DefaultHasher::new();
570 t.hash(&mut hasher);
571 hasher.finish()
572 }
573
574 let test_values = [
575 "0",
576 "42",
577 "1000000000000000000",
578 "0x75bcd15",
579 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
580 ];
581
582 for value_str in &test_values {
583 let alloy_u256 = U256::from_str(value_str).unwrap();
584 let sql_u256 = SqlU256::from_str(value_str).unwrap();
585
586 let alloy_hash = calculate_hash(&alloy_u256);
587 let sql_hash = calculate_hash(&sql_u256);
588
589 assert_eq!(
591 alloy_hash, sql_hash,
592 "Hash mismatch for value {}: alloy={}, sql={}",
593 value_str, alloy_hash, sql_hash
594 );
595 }
596
597 let original = U256::from(12345u64);
599 let sql_wrapped = SqlU256::from(original);
600 let converted_back: U256 = sql_wrapped.into();
601
602 assert_eq!(calculate_hash(&original), calculate_hash(&sql_wrapped));
603 assert_eq!(calculate_hash(&original), calculate_hash(&converted_back));
604 assert_eq!(
605 calculate_hash(&sql_wrapped),
606 calculate_hash(&converted_back)
607 );
608
609 assert_eq!(calculate_hash(&U256::ZERO), calculate_hash(&SqlU256::ZERO));
611
612 let max_alloy = U256::MAX;
614 let max_sql = SqlU256::from(max_alloy);
615 assert_eq!(calculate_hash(&max_alloy), calculate_hash(&max_sql));
616 }
617}