Skip to main content

xrpl_common_stdlib/types/
uint.rs

1//! Generic unsigned integer types with configurable bit sizes
2
3use crate::fields::decoder::{FieldDecoder, FromCurrentTx, FromLedger, decode_exact};
4use crate::types::decode_error::DecodeError;
5
6/// A generic unsigned integer type with configurable byte size.
7///
8/// This type provides a zero-cost abstraction for fixed-size unsigned integers
9/// of arbitrary byte lengths. Common instantiations include UInt128, UInt160,
10/// UInt192, and UInt256.
11///
12/// # Type Parameters
13///
14/// * `N` - The size of the integer in bytes
15///
16/// ## Derived Traits
17///
18/// - `PartialEq, Eq`: Essential for comparisons and use in collections
19/// - `Debug, Clone`: Standard traits for development and consistency
20///
21/// Note: `Copy` is intentionally not derived because `N` can be arbitrarily large.
22#[repr(C)]
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct UInt<const N: usize>(pub [u8; N]);
25
26impl<const N: usize> From<[u8; N]> for UInt<N> {
27    fn from(bytes: [u8; N]) -> Self {
28        Self(bytes)
29    }
30}
31
32impl<const N: usize> UInt<N> {
33    /// Returns the inner bytes as a reference to the inner array.
34    pub fn as_bytes(&self) -> &[u8; N] {
35        &self.0
36    }
37}
38
39// Keep the existing constants for compatibility
40pub const UINT128_SIZE: usize = 16;
41pub const UINT160_SIZE: usize = 20;
42pub const UINT192_SIZE: usize = 24;
43pub const UINT256_SIZE: usize = 32;
44
45// Alias for Hash constants
46pub const HASH128_SIZE: usize = UINT128_SIZE;
47pub const HASH160_SIZE: usize = UINT160_SIZE;
48pub const HASH192_SIZE: usize = UINT192_SIZE;
49pub const HASH256_SIZE: usize = UINT256_SIZE;
50
51// Type aliases for common sizes
52pub type UInt128 = UInt<UINT128_SIZE>;
53pub type UInt160 = UInt<UINT160_SIZE>;
54pub type UInt192 = UInt<UINT192_SIZE>;
55pub type UInt256 = UInt<UINT256_SIZE>;
56
57// Alias for Hash types
58pub type Hash128 = UInt128;
59pub type Hash160 = UInt160;
60pub type Hash192 = UInt192;
61pub type Hash256 = UInt256;
62
63/// `FieldDecoder` for any fixed-width unsigned integer (`Hash128`/`Hash160`/`Hash192`/`Hash256`,
64/// and any other `UInt<N>` instantiation): decodes an `N`-byte buffer into `UInt<N>`, failing if
65/// the host wrote a different number of bytes.
66impl<const N: usize> FieldDecoder for UInt<N> {
67    type Buffer = [u8; N];
68
69    #[inline]
70    fn empty_buffer() -> Self::Buffer {
71        [0u8; N]
72    }
73
74    #[inline]
75    fn decode(buf: Self::Buffer, bytes_written: usize) -> core::result::Result<Self, DecodeError> {
76        decode_exact(buf, bytes_written)
77    }
78}
79
80impl FromCurrentTx for Hash128 {}
81impl FromCurrentTx for Hash160 {}
82impl FromCurrentTx for Hash192 {}
83impl FromCurrentTx for Hash256 {}
84impl FromLedger for Hash128 {}
85impl FromLedger for Hash160 {}
86impl FromLedger for Hash192 {}
87impl FromLedger for Hash256 {}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    // Tests for generic UInt<N>
94    #[test]
95    fn test_uint_creation_generic() {
96        // Create a UInt with 8 bytes
97        let bytes = [1u8, 2, 3, 4, 5, 6, 7, 8];
98        let uint = UInt::<8>(bytes);
99
100        // Verify the bytes
101        assert_eq!(uint.0, bytes);
102    }
103
104    #[test]
105    fn test_uint_from_bytes_generic() {
106        // Create a test byte array
107        let bytes = [0xAB, 0xCD, 0xEF, 0x12];
108
109        // Create a UInt from bytes using From trait
110        let uint = UInt::<4>::from(bytes);
111
112        // Verify the bytes
113        assert_eq!(uint.as_bytes(), &bytes);
114    }
115
116    #[test]
117    fn test_uint_as_bytes() {
118        // Create a UInt with specific bytes
119        let bytes = [0xFF, 0x00, 0xFF, 0x00, 0xAA, 0xBB];
120        let uint = UInt::<6>::from(bytes);
121
122        // Get the bytes back
123        let retrieved_bytes = uint.as_bytes();
124
125        // Verify they match
126        assert_eq!(retrieved_bytes, &bytes);
127    }
128
129    #[test]
130    fn test_uint_equality() {
131        // Create two identical UInts
132        let bytes1 = [1u8, 2, 3, 4];
133        let uint1 = UInt::<4>::from(bytes1);
134        let uint2 = UInt::<4>::from(bytes1);
135
136        // Create a different UInt
137        let bytes2 = [5u8, 6, 7, 8];
138        let uint3 = UInt::<4>::from(bytes2);
139
140        // Test equality
141        assert_eq!(uint1, uint2);
142        assert_ne!(uint1, uint3);
143    }
144
145    #[test]
146    #[allow(clippy::clone_on_copy)]
147    fn test_uint_clone() {
148        // Create a UInt
149        let bytes = [0x11, 0x22, 0x33, 0x44];
150        let uint1 = UInt::<4>::from(bytes);
151
152        // Clone it
153        let uint2 = uint1.clone();
154
155        // Verify they are equal
156        assert_eq!(uint1, uint2);
157        assert_eq!(uint1.as_bytes(), uint2.as_bytes());
158    }
159
160    #[test]
161    fn test_uint_copy() {
162        // Create a UInt
163        let bytes = [0xDE, 0xAD, 0xBE, 0xEF];
164        let uint1 = UInt::<4>::from(bytes);
165
166        // Copy it (implicit copy due to Copy trait)
167        let uint2 = uint1.clone();
168
169        // Both should be usable and equal
170        assert_eq!(uint1, uint2);
171        assert_eq!(uint1.as_bytes(), uint2.as_bytes());
172    }
173
174    #[test]
175    fn test_uint_debug() {
176        // Create a UInt
177        let bytes = [0x01, 0x02];
178        let uint = UInt::<2>::from(bytes);
179
180        // Verify Debug trait is implemented by using it in an assertion
181        // We can't use format! in no_std, but we can verify the trait exists
182        let _ = uint; // Debug trait is derived, so this test verifies compilation
183    }
184
185    // Tests for UInt128
186    #[test]
187    fn test_uint128_creation() {
188        // Create a UInt128 (16 bytes)
189        let bytes = [1u8; 16];
190        let uint128 = UInt128::from(bytes);
191
192        // Verify the bytes
193        assert_eq!(uint128.as_bytes(), &bytes);
194        assert_eq!(uint128.as_bytes().len(), UINT128_SIZE);
195    }
196
197    #[test]
198    fn test_uint128_all_zeros() {
199        // Create a UInt128 with all zeros
200        let bytes = [0u8; 16];
201        let uint128 = UInt128::from(bytes);
202
203        // Verify all bytes are zero
204        assert_eq!(uint128.as_bytes(), &[0u8; 16]);
205    }
206
207    #[test]
208    fn test_uint128_all_ones() {
209        // Create a UInt128 with all ones (max value)
210        let bytes = [0xFFu8; 16];
211        let uint128 = UInt128::from(bytes);
212
213        // Verify all bytes are 0xFF
214        assert_eq!(uint128.as_bytes(), &[0xFFu8; 16]);
215    }
216
217    // Tests for UInt160
218    #[test]
219    fn test_uint160_creation() {
220        // Create a UInt160 (20 bytes)
221        let bytes = [2u8; 20];
222        let uint160 = UInt160::from(bytes);
223
224        // Verify the bytes
225        assert_eq!(uint160.as_bytes(), &bytes);
226        assert_eq!(uint160.as_bytes().len(), UINT160_SIZE);
227    }
228
229    #[test]
230    fn test_uint160_pattern() {
231        // Create a UInt160 with a specific pattern
232        let mut bytes = [0u8; 20];
233        for (i, byte) in bytes.iter_mut().enumerate() {
234            *byte = (i % 256) as u8;
235        }
236        let uint160 = UInt160::from(bytes);
237
238        // Verify the pattern
239        assert_eq!(uint160.as_bytes(), &bytes);
240    }
241
242    // Tests for UInt192
243    #[test]
244    fn test_uint192_creation() {
245        // Create a UInt192 (24 bytes)
246        let bytes = [3u8; 24];
247        let uint192 = UInt192::from(bytes);
248
249        // Verify the bytes
250        assert_eq!(uint192.as_bytes(), &bytes);
251        assert_eq!(uint192.as_bytes().len(), UINT192_SIZE);
252    }
253
254    #[test]
255    fn test_uint192_alternating_pattern() {
256        // Create a UInt192 with alternating bytes
257        let mut bytes = [0u8; 24];
258        for (i, byte) in bytes.iter_mut().enumerate() {
259            *byte = if i % 2 == 0 { 0xAA } else { 0x55 };
260        }
261        let uint192 = UInt192::from(bytes);
262
263        // Verify the pattern
264        assert_eq!(uint192.as_bytes(), &bytes);
265    }
266
267    // Tests for UInt256
268    #[test]
269    fn test_uint256_creation() {
270        // Create a UInt256 (32 bytes)
271        let bytes = [4u8; 32];
272        let uint256 = UInt256::from(bytes);
273
274        // Verify the bytes
275        assert_eq!(uint256.as_bytes(), &bytes);
276        assert_eq!(uint256.as_bytes().len(), UINT256_SIZE);
277    }
278
279    #[test]
280    fn test_uint256_specific_value() {
281        // Create a UInt256 with a specific value
282        let mut bytes = [0u8; 32];
283        bytes[0] = 0x12;
284        bytes[15] = 0x34;
285        bytes[31] = 0x56;
286        let uint256 = UInt256::from(bytes);
287
288        // Verify specific bytes
289        assert_eq!(uint256.as_bytes()[0], 0x12);
290        assert_eq!(uint256.as_bytes()[15], 0x34);
291        assert_eq!(uint256.as_bytes()[31], 0x56);
292    }
293
294    // Tests for Hash type aliases
295    #[test]
296    fn test_hash128_alias() {
297        // Hash128 should be the same as UInt128
298        let bytes = [0xAB; 16];
299        let hash128 = Hash128::from(bytes);
300        let uint128 = UInt128::from(bytes);
301
302        // They should be equal
303        assert_eq!(hash128, uint128);
304        assert_eq!(hash128.as_bytes(), uint128.as_bytes());
305    }
306
307    #[test]
308    fn test_hash160_alias() {
309        // Hash160 should be the same as UInt160
310        let bytes = [0xCD; 20];
311        let hash160 = Hash160::from(bytes);
312        let uint160 = UInt160::from(bytes);
313
314        // They should be equal
315        assert_eq!(hash160, uint160);
316        assert_eq!(hash160.as_bytes(), uint160.as_bytes());
317    }
318
319    #[test]
320    fn test_hash192_alias() {
321        // Hash192 should be the same as UInt192
322        let bytes = [0xEF; 24];
323        let hash192 = Hash192::from(bytes);
324        let uint192 = UInt192::from(bytes);
325
326        // They should be equal
327        assert_eq!(hash192, uint192);
328        assert_eq!(hash192.as_bytes(), uint192.as_bytes());
329    }
330
331    #[test]
332    fn test_hash256_alias() {
333        // Hash256 should be the same as UInt256
334        let bytes = [0x12; 32];
335        let hash256 = Hash256::from(bytes);
336        let uint256 = UInt256::from(bytes);
337
338        // They should be equal
339        assert_eq!(hash256, uint256);
340        assert_eq!(hash256.as_bytes(), uint256.as_bytes());
341    }
342
343    // Tests for constants
344    #[test]
345    fn test_uint_constants() {
346        // Verify the size constants
347        assert_eq!(UINT128_SIZE, 16);
348        assert_eq!(UINT160_SIZE, 20);
349        assert_eq!(UINT192_SIZE, 24);
350        assert_eq!(UINT256_SIZE, 32);
351    }
352
353    #[test]
354    fn test_hash_constants() {
355        // Verify hash constants match uint constants
356        assert_eq!(HASH128_SIZE, UINT128_SIZE);
357        assert_eq!(HASH160_SIZE, UINT160_SIZE);
358        assert_eq!(HASH192_SIZE, UINT192_SIZE);
359        assert_eq!(HASH256_SIZE, UINT256_SIZE);
360
361        // Verify the actual values
362        assert_eq!(HASH128_SIZE, 16);
363        assert_eq!(HASH160_SIZE, 20);
364        assert_eq!(HASH192_SIZE, 24);
365        assert_eq!(HASH256_SIZE, 32);
366    }
367
368    // Edge case tests
369    #[test]
370    fn test_uint_single_byte() {
371        // Test with a single byte UInt
372        let bytes = [0x42];
373        let uint = UInt::<1>::from(bytes);
374
375        assert_eq!(uint.as_bytes(), &[0x42]);
376    }
377
378    #[test]
379    fn test_uint_large_size() {
380        // Test with a larger custom size
381        let bytes = [0x99; 64];
382        let uint = UInt::<64>::from(bytes);
383
384        assert_eq!(uint.as_bytes().len(), 64);
385        assert_eq!(uint.as_bytes(), &[0x99; 64]);
386    }
387
388    #[test]
389    fn test_uint_equality_different_sizes() {
390        // UInt<4> and UInt<8> are different types and cannot be compared
391        // This test just verifies they can coexist
392        let uint4 = UInt::<4>::from([1, 2, 3, 4]);
393        let uint8 = UInt::<8>::from([1, 2, 3, 4, 5, 6, 7, 8]);
394
395        // Just verify they exist and have correct sizes
396        assert_eq!(uint4.as_bytes().len(), 4);
397        assert_eq!(uint8.as_bytes().len(), 8);
398    }
399
400    #[test]
401    fn test_uint_repr_c() {
402        // Verify that UInt has the correct memory layout
403        // This is important for FFI compatibility
404        use core::mem;
405
406        // Size should be exactly N bytes
407        assert_eq!(mem::size_of::<UInt<16>>(), 16);
408        assert_eq!(mem::size_of::<UInt<20>>(), 20);
409        assert_eq!(mem::size_of::<UInt<24>>(), 24);
410        assert_eq!(mem::size_of::<UInt<32>>(), 32);
411
412        // Alignment should be 1 (byte-aligned)
413        assert_eq!(mem::align_of::<UInt<16>>(), 1);
414    }
415
416    #[test]
417    fn test_uint_from_array_direct() {
418        // Test creating UInt directly from array
419        let uint = UInt([0x01, 0x02, 0x03, 0x04]);
420
421        assert_eq!(uint.as_bytes(), &[0x01, 0x02, 0x03, 0x04]);
422    }
423
424    #[test]
425    fn test_multiple_uint_instances() {
426        // Test creating multiple instances with different values
427        let uint1 = UInt128::from([1u8; 16]);
428        let uint2 = UInt128::from([2u8; 16]);
429        let uint3 = UInt128::from([3u8; 16]);
430
431        // Verify they are all different
432        assert_ne!(uint1, uint2);
433        assert_ne!(uint2, uint3);
434        assert_ne!(uint1, uint3);
435
436        // Verify their values
437        assert_eq!(uint1.as_bytes(), &[1u8; 16]);
438        assert_eq!(uint2.as_bytes(), &[2u8; 16]);
439        assert_eq!(uint3.as_bytes(), &[3u8; 16]);
440    }
441}