1use crate::aliases;
2use core::{fmt, iter, ops, str};
3use derive_more::{Deref, DerefMut, From, Index, IndexMut, IntoIterator};
4use hex::FromHex;
5
6#[derive(
16 Clone,
17 Copy,
18 PartialEq,
19 Eq,
20 PartialOrd,
21 Ord,
22 Hash,
23 Deref,
24 DerefMut,
25 From,
26 Index,
27 IndexMut,
28 IntoIterator,
29)]
30#[cfg_attr(feature = "arbitrary", derive(derive_arbitrary::Arbitrary, proptest_derive::Arbitrary))]
31#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
32#[repr(transparent)]
33pub struct FixedBytes<const N: usize>(#[into_iterator(owned, ref, ref_mut)] pub [u8; N]);
34
35crate::impl_fb_traits!(FixedBytes<N>, N, const);
36
37impl<const N: usize> Default for FixedBytes<N> {
38 #[inline]
39 fn default() -> Self {
40 Self::ZERO
41 }
42}
43
44impl<const N: usize> Default for &FixedBytes<N> {
45 #[inline]
46 fn default() -> Self {
47 &FixedBytes::ZERO
48 }
49}
50
51impl<const N: usize> From<&[u8; N]> for FixedBytes<N> {
52 #[inline]
53 fn from(bytes: &[u8; N]) -> Self {
54 Self(*bytes)
55 }
56}
57
58impl<const N: usize> From<&mut [u8; N]> for FixedBytes<N> {
59 #[inline]
60 fn from(bytes: &mut [u8; N]) -> Self {
61 Self(*bytes)
62 }
63}
64
65impl<const N: usize> TryFrom<&[u8]> for FixedBytes<N> {
68 type Error = core::array::TryFromSliceError;
69
70 #[inline]
71 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
72 <&Self>::try_from(slice).copied()
73 }
74}
75
76impl<const N: usize> TryFrom<&mut [u8]> for FixedBytes<N> {
79 type Error = core::array::TryFromSliceError;
80
81 #[inline]
82 fn try_from(slice: &mut [u8]) -> Result<Self, Self::Error> {
83 Self::try_from(&*slice)
84 }
85}
86
87impl<'a, const N: usize> TryFrom<&'a [u8]> for &'a FixedBytes<N> {
90 type Error = core::array::TryFromSliceError;
91
92 #[inline]
93 fn try_from(slice: &'a [u8]) -> Result<&'a FixedBytes<N>, Self::Error> {
94 <&[u8; N]>::try_from(slice).map(|array_ref| unsafe { core::mem::transmute(array_ref) })
96 }
97}
98
99impl<'a, const N: usize> TryFrom<&'a mut [u8]> for &'a mut FixedBytes<N> {
102 type Error = core::array::TryFromSliceError;
103
104 #[inline]
105 fn try_from(slice: &'a mut [u8]) -> Result<&'a mut FixedBytes<N>, Self::Error> {
106 <&mut [u8; N]>::try_from(slice).map(|array_ref| unsafe { core::mem::transmute(array_ref) })
108 }
109}
110
111macro_rules! fixed_bytes_uint_conversions {
115 ($($int:ty => $fb:ty),* $(,)?) => {$(
116 impl From<$int> for $fb {
117 #[inline]
120 fn from(value: $int) -> Self {
121 Self(value.to_be_bytes())
122 }
123 }
124
125 impl From<$fb> for $int {
126 #[inline]
129 fn from(value: $fb) -> Self {
130 Self::from_be_bytes(value.0)
131 }
132 }
133
134 const _: () = assert!(<$int>::BITS as usize == <$fb>::len_bytes() * 8);
135 )*};
136}
137
138fixed_bytes_uint_conversions! {
139 u8 => aliases::B8,
140 aliases::U8 => aliases::B8,
141 i8 => aliases::B8,
142 aliases::I8 => aliases::B8,
143
144 u16 => aliases::B16,
145 aliases::U16 => aliases::B16,
146 i16 => aliases::B16,
147 aliases::I16 => aliases::B16,
148
149 u32 => aliases::B32,
150 aliases::U32 => aliases::B32,
151 i32 => aliases::B32,
152 aliases::I32 => aliases::B32,
153
154 u64 => aliases::B64,
155 aliases::U64 => aliases::B64,
156 i64 => aliases::B64,
157 aliases::I64 => aliases::B64,
158
159 u128 => aliases::B128,
160 aliases::U128 => aliases::B128,
161 i128 => aliases::B128,
162 aliases::I128 => aliases::B128,
163
164 aliases::U160 => aliases::B160,
165 aliases::I160 => aliases::B160,
166
167 aliases::U256 => aliases::B256,
168 aliases::I256 => aliases::B256,
169
170 aliases::U512 => aliases::B512,
171 aliases::I512 => aliases::B512,
172
173}
174
175impl<const N: usize> From<FixedBytes<N>> for [u8; N] {
176 #[inline]
177 fn from(s: FixedBytes<N>) -> Self {
178 s.0
179 }
180}
181
182impl<const N: usize> AsRef<[u8; N]> for FixedBytes<N> {
183 #[inline]
184 fn as_ref(&self) -> &[u8; N] {
185 &self.0
186 }
187}
188
189impl<const N: usize> AsMut<[u8; N]> for FixedBytes<N> {
190 #[inline]
191 fn as_mut(&mut self) -> &mut [u8; N] {
192 &mut self.0
193 }
194}
195
196impl<const N: usize> AsRef<[u8]> for FixedBytes<N> {
197 #[inline]
198 fn as_ref(&self) -> &[u8] {
199 &self.0
200 }
201}
202
203impl<const N: usize> AsMut<[u8]> for FixedBytes<N> {
204 #[inline]
205 fn as_mut(&mut self) -> &mut [u8] {
206 &mut self.0
207 }
208}
209
210impl<const N: usize> fmt::Debug for FixedBytes<N> {
211 #[inline]
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 self.fmt_hex::<false>(f, true)
214 }
215}
216
217impl<const N: usize> fmt::Display for FixedBytes<N> {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 if N <= 4 || !f.alternate() {
221 return self.fmt_hex::<false>(f, true);
222 }
223
224 const SEP_LEN: usize = '…'.len_utf8();
226 let mut buf = [0; 2 + 4 + SEP_LEN + 4];
227 buf[0] = b'0';
228 buf[1] = b'x';
229 hex::encode_to_slice(&self.0[0..2], &mut buf[2..6]).unwrap();
230 '…'.encode_utf8(&mut buf[6..]);
231 hex::encode_to_slice(&self.0[N - 2..N], &mut buf[6 + SEP_LEN..]).unwrap();
232
233 f.write_str(unsafe { str::from_utf8_unchecked(&buf) })
235 }
236}
237
238impl<const N: usize> fmt::LowerHex for FixedBytes<N> {
239 #[inline]
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 self.fmt_hex::<false>(f, f.alternate())
242 }
243}
244
245impl<const N: usize> fmt::UpperHex for FixedBytes<N> {
246 #[inline]
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 self.fmt_hex::<true>(f, f.alternate())
249 }
250}
251
252impl<const N: usize> ops::BitAnd for FixedBytes<N> {
253 type Output = Self;
254
255 #[inline]
256 fn bitand(mut self, rhs: Self) -> Self::Output {
257 self &= rhs;
258 self
259 }
260}
261
262impl<const N: usize> ops::BitAndAssign for FixedBytes<N> {
263 #[inline]
264 fn bitand_assign(&mut self, rhs: Self) {
265 iter::zip(self, &rhs).for_each(|(a, b)| *a &= *b);
267 }
268}
269
270impl<const N: usize> ops::BitOr for FixedBytes<N> {
271 type Output = Self;
272
273 #[inline]
274 fn bitor(mut self, rhs: Self) -> Self::Output {
275 self |= rhs;
276 self
277 }
278}
279
280impl<const N: usize> ops::BitOrAssign for FixedBytes<N> {
281 #[inline]
282 fn bitor_assign(&mut self, rhs: Self) {
283 iter::zip(self, &rhs).for_each(|(a, b)| *a |= *b);
285 }
286}
287
288impl<const N: usize> ops::BitXor for FixedBytes<N> {
289 type Output = Self;
290
291 #[inline]
292 fn bitxor(mut self, rhs: Self) -> Self::Output {
293 self ^= rhs;
294 self
295 }
296}
297
298impl<const N: usize> ops::BitXorAssign for FixedBytes<N> {
299 #[inline]
300 fn bitxor_assign(&mut self, rhs: Self) {
301 iter::zip(self, &rhs).for_each(|(a, b)| *a ^= *b);
303 }
304}
305
306impl<const N: usize> ops::Not for FixedBytes<N> {
307 type Output = Self;
308
309 #[inline]
310 fn not(mut self) -> Self::Output {
311 self.iter_mut().for_each(|byte| *byte = !*byte);
312 self
313 }
314}
315
316impl<const N: usize> str::FromStr for FixedBytes<N> {
317 type Err = hex::FromHexError;
318
319 #[inline]
320 fn from_str(s: &str) -> Result<Self, Self::Err> {
321 Self::from_hex(s)
322 }
323}
324
325#[cfg(feature = "rand")]
326impl<const N: usize> rand::distributions::Distribution<FixedBytes<N>>
327 for rand::distributions::Standard
328{
329 #[inline]
330 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> FixedBytes<N> {
331 FixedBytes::random_with(rng)
332 }
333}
334
335impl<const N: usize> FixedBytes<N> {
336 pub const ZERO: Self = Self([0u8; N]);
338
339 #[inline]
341 pub const fn new(bytes: [u8; N]) -> Self {
342 Self(bytes)
343 }
344
345 #[inline]
347 pub const fn with_last_byte(x: u8) -> Self {
348 let mut bytes = [0u8; N];
349 if N > 0 {
350 bytes[N - 1] = x;
351 }
352 Self(bytes)
353 }
354
355 #[inline]
357 pub const fn repeat_byte(byte: u8) -> Self {
358 Self([byte; N])
359 }
360
361 #[inline(always)]
363 pub const fn len_bytes() -> usize {
364 N
365 }
366
367 #[cfg(feature = "getrandom")]
372 #[inline]
373 #[track_caller]
374 pub fn random() -> Self {
375 #[allow(clippy::uninit_assumed_init)]
377 let mut bytes = unsafe { core::mem::MaybeUninit::<Self>::uninit().assume_init() };
378 bytes.randomize();
379 bytes
380 }
381
382 #[cfg(feature = "getrandom")]
387 #[inline]
388 pub fn try_random() -> Result<Self, getrandom::Error> {
389 #[allow(clippy::uninit_assumed_init)]
391 let mut bytes = unsafe { core::mem::MaybeUninit::<Self>::uninit().assume_init() };
392 bytes.try_randomize()?;
393 Ok(bytes)
394 }
395
396 #[cfg(feature = "rand")]
400 #[inline]
401 #[doc(alias = "random_using")]
402 pub fn random_with<R: rand::RngCore + ?Sized>(rng: &mut R) -> Self {
403 #[allow(clippy::uninit_assumed_init)]
405 let mut bytes = unsafe { core::mem::MaybeUninit::<Self>::uninit().assume_init() };
406 bytes.randomize_with(rng);
407 bytes
408 }
409
410 #[cfg(feature = "getrandom")]
414 #[inline]
415 #[track_caller]
416 pub fn randomize(&mut self) {
417 self.try_randomize().unwrap_or_else(|e| panic!("failed to fill with random bytes: {e}"));
418 }
419
420 #[inline]
425 #[cfg(feature = "getrandom")]
426 pub fn try_randomize(&mut self) -> Result<(), getrandom::Error> {
427 #[cfg(all(feature = "rand", feature = "std"))]
428 {
429 self.randomize_with(&mut rand::thread_rng());
430 Ok(())
431 }
432 #[cfg(not(all(feature = "rand", feature = "std")))]
433 {
434 getrandom::getrandom(&mut self.0)
435 }
436 }
437
438 #[cfg(feature = "rand")]
440 #[inline]
441 #[doc(alias = "randomize_using")]
442 pub fn randomize_with<R: rand::RngCore + ?Sized>(&mut self, rng: &mut R) {
443 rng.fill_bytes(&mut self.0);
444 }
445
446 pub const fn concat_const<const M: usize, const Z: usize>(
455 self,
456 other: FixedBytes<M>,
457 ) -> FixedBytes<Z> {
458 assert!(N + M == Z, "Output size `Z` must equal the sum of the input sizes `N` and `M`");
459
460 let mut result = [0u8; Z];
461 let mut i = 0;
462 while i < Z {
463 result[i] = if i >= N { other.0[i - N] } else { self.0[i] };
464 i += 1;
465 }
466 FixedBytes(result)
467 }
468
469 #[inline]
481 #[track_caller]
482 pub fn from_slice(src: &[u8]) -> Self {
483 match Self::try_from(src) {
484 Ok(x) => x,
485 Err(_) => panic!("cannot convert a slice of length {} to FixedBytes<{N}>", src.len()),
486 }
487 }
488
489 #[inline]
500 #[track_caller]
501 pub fn left_padding_from(value: &[u8]) -> Self {
502 let len = value.len();
503 assert!(len <= N, "slice is too large. Expected <={N} bytes, got {len}");
504 let mut bytes = Self::ZERO;
505 bytes[N - len..].copy_from_slice(value);
506 bytes
507 }
508
509 #[inline]
520 #[track_caller]
521 pub fn right_padding_from(value: &[u8]) -> Self {
522 let len = value.len();
523 assert!(len <= N, "slice is too large. Expected <={N} bytes, got {len}");
524 let mut bytes = Self::ZERO;
525 bytes[..len].copy_from_slice(value);
526 bytes
527 }
528
529 #[inline]
531 pub const fn as_slice(&self) -> &[u8] {
532 &self.0
533 }
534
535 #[inline]
538 pub fn as_mut_slice(&mut self) -> &mut [u8] {
539 &mut self.0
540 }
541
542 #[inline]
544 pub fn covers(&self, other: &Self) -> bool {
545 (*self & *other) == *other
546 }
547
548 pub const fn const_covers(self, other: Self) -> bool {
550 other.const_eq(&self.bit_and(other))
552 }
553
554 pub const fn const_eq(&self, other: &Self) -> bool {
556 let mut i = 0;
557 while i < N {
558 if self.0[i] != other.0[i] {
559 return false;
560 }
561 i += 1;
562 }
563 true
564 }
565
566 #[inline]
568 pub fn is_zero(&self) -> bool {
569 *self == Self::ZERO
570 }
571
572 #[inline]
574 pub const fn const_is_zero(&self) -> bool {
575 self.const_eq(&Self::ZERO)
576 }
577
578 pub const fn bit_and(self, rhs: Self) -> Self {
580 let mut ret = Self::ZERO;
581 let mut i = 0;
582 while i < N {
583 ret.0[i] = self.0[i] & rhs.0[i];
584 i += 1;
585 }
586 ret
587 }
588
589 pub const fn bit_or(self, rhs: Self) -> Self {
591 let mut ret = Self::ZERO;
592 let mut i = 0;
593 while i < N {
594 ret.0[i] = self.0[i] | rhs.0[i];
595 i += 1;
596 }
597 ret
598 }
599
600 pub const fn bit_xor(self, rhs: Self) -> Self {
602 let mut ret = Self::ZERO;
603 let mut i = 0;
604 while i < N {
605 ret.0[i] = self.0[i] ^ rhs.0[i];
606 i += 1;
607 }
608 ret
609 }
610
611 fn fmt_hex<const UPPER: bool>(&self, f: &mut fmt::Formatter<'_>, prefix: bool) -> fmt::Result {
612 let mut buf = hex::Buffer::<N, true>::new();
613 let s = if UPPER { buf.format_upper(self) } else { buf.format(self) };
614 f.write_str(unsafe { s.get_unchecked((!prefix as usize) * 2..) })
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 macro_rules! test_fmt {
624 ($($fmt:literal, $hex:literal => $expected:literal;)+) => {$(
625 assert_eq!(
626 format!($fmt, fixed_bytes!($hex)),
627 $expected
628 );
629 )+};
630 }
631
632 #[test]
633 fn concat_const() {
634 const A: FixedBytes<2> = fixed_bytes!("0x0123");
635 const B: FixedBytes<2> = fixed_bytes!("0x4567");
636 const EXPECTED: FixedBytes<4> = fixed_bytes!("0x01234567");
637 const ACTUAL: FixedBytes<4> = A.concat_const(B);
638
639 assert_eq!(ACTUAL, EXPECTED);
640 }
641
642 #[test]
643 fn display() {
644 test_fmt! {
645 "{}", "0123456789abcdef" => "0x0123456789abcdef";
646 "{:#}", "0123" => "0x0123";
647 "{:#}", "01234567" => "0x01234567";
648 "{:#}", "0123456789" => "0x0123…6789";
649 }
650 }
651
652 #[test]
653 fn debug() {
654 test_fmt! {
655 "{:?}", "0123456789abcdef" => "0x0123456789abcdef";
656 "{:#?}", "0123456789abcdef" => "0x0123456789abcdef";
657 }
658 }
659
660 #[test]
661 fn lower_hex() {
662 test_fmt! {
663 "{:x}", "0123456789abcdef" => "0123456789abcdef";
664 "{:#x}", "0123456789abcdef" => "0x0123456789abcdef";
665 }
666 }
667
668 #[test]
669 fn upper_hex() {
670 test_fmt! {
671 "{:X}", "0123456789abcdef" => "0123456789ABCDEF";
672 "{:#X}", "0123456789abcdef" => "0x0123456789ABCDEF";
673 }
674 }
675
676 #[test]
677 fn left_padding_from() {
678 assert_eq!(FixedBytes::<4>::left_padding_from(&[0x01, 0x23]), fixed_bytes!("0x00000123"));
679
680 assert_eq!(
681 FixedBytes::<4>::left_padding_from(&[0x01, 0x23, 0x45, 0x67]),
682 fixed_bytes!("0x01234567")
683 );
684 }
685
686 #[test]
687 #[should_panic(expected = "slice is too large. Expected <=4 bytes, got 5")]
688 fn left_padding_from_too_large() {
689 FixedBytes::<4>::left_padding_from(&[0x01, 0x23, 0x45, 0x67, 0x89]);
690 }
691
692 #[test]
693 fn right_padding_from() {
694 assert_eq!(FixedBytes::<4>::right_padding_from(&[0x01, 0x23]), fixed_bytes!("0x01230000"));
695
696 assert_eq!(
697 FixedBytes::<4>::right_padding_from(&[0x01, 0x23, 0x45, 0x67]),
698 fixed_bytes!("0x01234567")
699 );
700 }
701
702 #[test]
703 #[should_panic(expected = "slice is too large. Expected <=4 bytes, got 5")]
704 fn right_padding_from_too_large() {
705 FixedBytes::<4>::right_padding_from(&[0x01, 0x23, 0x45, 0x67, 0x89]);
706 }
707}