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(arbitrary::Arbitrary, proptest_derive::Arbitrary))]
31#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
32#[cfg_attr(feature = "diesel", derive(diesel::AsExpression, diesel::FromSqlRow))]
33#[cfg_attr(feature = "diesel", diesel(sql_type = diesel::sql_types::Binary))]
34#[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
35#[repr(transparent)]
36pub struct FixedBytes<const N: usize>(#[into_iterator(owned, ref, ref_mut)] pub [u8; N]);
37
38crate::impl_fb_traits!(FixedBytes<N>, N, const);
39
40impl<const N: usize> Default for FixedBytes<N> {
41 #[inline]
42 fn default() -> Self {
43 Self::ZERO
44 }
45}
46
47impl<const N: usize> Default for &FixedBytes<N> {
48 #[inline]
49 fn default() -> Self {
50 &FixedBytes::ZERO
51 }
52}
53
54impl<const N: usize> From<&[u8; N]> for FixedBytes<N> {
55 #[inline]
56 fn from(bytes: &[u8; N]) -> Self {
57 Self(*bytes)
58 }
59}
60
61impl<const N: usize> From<&mut [u8; N]> for FixedBytes<N> {
62 #[inline]
63 fn from(bytes: &mut [u8; N]) -> Self {
64 Self(*bytes)
65 }
66}
67
68impl<const N: usize> TryFrom<&[u8]> for FixedBytes<N> {
71 type Error = core::array::TryFromSliceError;
72
73 #[inline]
74 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
75 <&Self>::try_from(slice).copied()
76 }
77}
78
79impl<const N: usize> TryFrom<&mut [u8]> for FixedBytes<N> {
82 type Error = core::array::TryFromSliceError;
83
84 #[inline]
85 fn try_from(slice: &mut [u8]) -> Result<Self, Self::Error> {
86 Self::try_from(&*slice)
87 }
88}
89
90impl<'a, const N: usize> TryFrom<&'a [u8]> for &'a FixedBytes<N> {
93 type Error = core::array::TryFromSliceError;
94
95 #[inline]
96 fn try_from(slice: &'a [u8]) -> Result<&'a FixedBytes<N>, Self::Error> {
97 <&[u8; N]>::try_from(slice).map(|array_ref| unsafe { core::mem::transmute(array_ref) })
99 }
100}
101
102impl<'a, const N: usize> TryFrom<&'a mut [u8]> for &'a mut FixedBytes<N> {
105 type Error = core::array::TryFromSliceError;
106
107 #[inline]
108 fn try_from(slice: &'a mut [u8]) -> Result<&'a mut FixedBytes<N>, Self::Error> {
109 <&mut [u8; N]>::try_from(slice).map(|array_ref| unsafe { core::mem::transmute(array_ref) })
111 }
112}
113
114macro_rules! fixed_bytes_uint_conversions {
118 ($($int:ty => $fb:ty),* $(,)?) => {$(
119 impl From<$int> for $fb {
120 #[inline]
123 fn from(value: $int) -> Self {
124 Self(value.to_be_bytes())
125 }
126 }
127
128 impl From<$fb> for $int {
129 #[inline]
132 fn from(value: $fb) -> Self {
133 Self::from_be_bytes(value.0)
134 }
135 }
136
137 const _: () = assert!(<$int>::BITS as usize == <$fb>::len_bytes() * 8);
138 )*};
139}
140
141fixed_bytes_uint_conversions! {
142 u8 => aliases::B8,
143 aliases::U8 => aliases::B8,
144 i8 => aliases::B8,
145 aliases::I8 => aliases::B8,
146
147 u16 => aliases::B16,
148 aliases::U16 => aliases::B16,
149 i16 => aliases::B16,
150 aliases::I16 => aliases::B16,
151
152 u32 => aliases::B32,
153 aliases::U32 => aliases::B32,
154 i32 => aliases::B32,
155 aliases::I32 => aliases::B32,
156
157 u64 => aliases::B64,
158 aliases::U64 => aliases::B64,
159 i64 => aliases::B64,
160 aliases::I64 => aliases::B64,
161
162 u128 => aliases::B128,
163 aliases::U128 => aliases::B128,
164 i128 => aliases::B128,
165 aliases::I128 => aliases::B128,
166
167 aliases::U160 => aliases::B160,
168 aliases::I160 => aliases::B160,
169
170 aliases::U256 => aliases::B256,
171 aliases::I256 => aliases::B256,
172
173 aliases::U512 => aliases::B512,
174 aliases::I512 => aliases::B512,
175
176}
177
178impl<const N: usize> From<FixedBytes<N>> for [u8; N] {
179 #[inline]
180 fn from(s: FixedBytes<N>) -> Self {
181 s.0
182 }
183}
184
185impl<const N: usize> AsRef<[u8; N]> for FixedBytes<N> {
186 #[inline]
187 fn as_ref(&self) -> &[u8; N] {
188 &self.0
189 }
190}
191
192impl<const N: usize> AsMut<[u8; N]> for FixedBytes<N> {
193 #[inline]
194 fn as_mut(&mut self) -> &mut [u8; N] {
195 &mut self.0
196 }
197}
198
199impl<const N: usize> AsRef<[u8]> for FixedBytes<N> {
200 #[inline]
201 fn as_ref(&self) -> &[u8] {
202 &self.0
203 }
204}
205
206impl<const N: usize> AsMut<[u8]> for FixedBytes<N> {
207 #[inline]
208 fn as_mut(&mut self) -> &mut [u8] {
209 &mut self.0
210 }
211}
212
213impl<const N: usize> fmt::Debug for FixedBytes<N> {
214 #[inline]
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 self.fmt_hex::<false>(f, true)
217 }
218}
219
220impl<const N: usize> fmt::Display for FixedBytes<N> {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 if N <= 4 || !f.alternate() {
224 return self.fmt_hex::<false>(f, true);
225 }
226
227 const SEP_LEN: usize = '…'.len_utf8();
229 let mut buf = [0; 2 + 4 + SEP_LEN + 4];
230 buf[0] = b'0';
231 buf[1] = b'x';
232 hex::encode_to_slice(&self.0[0..2], &mut buf[2..6]).unwrap();
233 '…'.encode_utf8(&mut buf[6..]);
234 hex::encode_to_slice(&self.0[N - 2..N], &mut buf[6 + SEP_LEN..]).unwrap();
235
236 f.write_str(unsafe { str::from_utf8_unchecked(&buf) })
238 }
239}
240
241impl<const N: usize> fmt::LowerHex for FixedBytes<N> {
242 #[inline]
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 self.fmt_hex::<false>(f, f.alternate())
245 }
246}
247
248impl<const N: usize> fmt::UpperHex for FixedBytes<N> {
249 #[inline]
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 self.fmt_hex::<true>(f, f.alternate())
252 }
253}
254
255impl<const N: usize> ops::BitAndAssign for FixedBytes<N> {
256 #[inline]
257 fn bitand_assign(&mut self, rhs: Self) {
258 *self &= &rhs;
259 }
260}
261
262impl<const N: usize> ops::BitOrAssign for FixedBytes<N> {
263 #[inline]
264 fn bitor_assign(&mut self, rhs: Self) {
265 *self |= &rhs;
266 }
267}
268
269impl<const N: usize> ops::BitXorAssign for FixedBytes<N> {
270 #[inline]
271 fn bitxor_assign(&mut self, rhs: Self) {
272 *self ^= &rhs;
273 }
274}
275
276impl<const N: usize> ops::BitAndAssign<&Self> for FixedBytes<N> {
277 #[inline]
278 fn bitand_assign(&mut self, rhs: &Self) {
279 iter::zip(self, rhs).for_each(|(a, b)| *a &= *b);
280 }
281}
282
283impl<const N: usize> ops::BitOrAssign<&Self> for FixedBytes<N> {
284 #[inline]
285 fn bitor_assign(&mut self, rhs: &Self) {
286 iter::zip(self, rhs).for_each(|(a, b)| *a |= *b);
287 }
288}
289
290impl<const N: usize> ops::BitXorAssign<&Self> for FixedBytes<N> {
291 #[inline]
292 fn bitxor_assign(&mut self, rhs: &Self) {
293 iter::zip(self, rhs).for_each(|(a, b)| *a ^= *b);
294 }
295}
296
297impl<const N: usize> ops::BitAnd for FixedBytes<N> {
298 type Output = Self;
299
300 #[inline]
301 fn bitand(mut self, rhs: Self) -> Self::Output {
302 self &= &rhs;
303 self
304 }
305}
306
307impl<const N: usize> ops::BitOr for FixedBytes<N> {
308 type Output = Self;
309
310 #[inline]
311 fn bitor(mut self, rhs: Self) -> Self::Output {
312 self |= &rhs;
313 self
314 }
315}
316
317impl<const N: usize> ops::BitXor for FixedBytes<N> {
318 type Output = Self;
319
320 #[inline]
321 fn bitxor(mut self, rhs: Self) -> Self::Output {
322 self ^= &rhs;
323 self
324 }
325}
326
327impl<const N: usize> ops::BitAnd<&Self> for FixedBytes<N> {
328 type Output = Self;
329
330 #[inline]
331 fn bitand(mut self, rhs: &Self) -> Self::Output {
332 self &= rhs;
333 self
334 }
335}
336
337impl<const N: usize> ops::BitOr<&Self> for FixedBytes<N> {
338 type Output = Self;
339
340 #[inline]
341 fn bitor(mut self, rhs: &Self) -> Self::Output {
342 self |= rhs;
343 self
344 }
345}
346
347impl<const N: usize> ops::BitXor<&Self> for FixedBytes<N> {
348 type Output = Self;
349
350 #[inline]
351 fn bitxor(mut self, rhs: &Self) -> Self::Output {
352 self ^= rhs;
353 self
354 }
355}
356
357impl<const N: usize> ops::Not for FixedBytes<N> {
358 type Output = Self;
359
360 #[inline]
361 fn not(mut self) -> Self::Output {
362 self.iter_mut().for_each(|byte| *byte = !*byte);
363 self
364 }
365}
366
367impl<const N: usize> str::FromStr for FixedBytes<N> {
368 type Err = hex::FromHexError;
369
370 #[inline]
371 fn from_str(s: &str) -> Result<Self, Self::Err> {
372 Self::from_hex(s)
373 }
374}
375
376#[cfg(feature = "rand")]
377impl<const N: usize> rand::distr::Distribution<FixedBytes<N>> for rand::distr::StandardUniform {
378 #[inline]
379 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> FixedBytes<N> {
380 FixedBytes::random_with(rng)
381 }
382}
383
384impl<const N: usize> FixedBytes<N> {
385 pub const ZERO: Self = Self([0u8; N]);
387
388 #[inline]
390 pub const fn new(bytes: [u8; N]) -> Self {
391 Self(bytes)
392 }
393
394 #[inline]
396 pub const fn with_last_byte(x: u8) -> Self {
397 let mut bytes = [0u8; N];
398 if N > 0 {
399 bytes[N - 1] = x;
400 }
401 Self(bytes)
402 }
403
404 #[inline]
406 pub const fn repeat_byte(byte: u8) -> Self {
407 Self([byte; N])
408 }
409
410 #[inline(always)]
412 pub const fn len_bytes() -> usize {
413 N
414 }
415
416 #[cfg(feature = "getrandom")]
421 #[inline]
422 #[track_caller]
423 pub fn random() -> Self {
424 let mut bytes = Self::ZERO;
425 bytes.randomize();
426 bytes
427 }
428
429 #[cfg(feature = "getrandom")]
434 #[inline]
435 pub fn try_random() -> Result<Self, getrandom::Error> {
436 let mut bytes = Self::ZERO;
437 bytes.try_randomize()?;
438 Ok(bytes)
439 }
440
441 #[cfg(feature = "rand")]
445 #[inline]
446 #[doc(alias = "random_using")]
447 pub fn random_with<R: rand::RngCore + ?Sized>(rng: &mut R) -> Self {
448 let mut bytes = Self::ZERO;
449 bytes.randomize_with(rng);
450 bytes
451 }
452
453 #[cfg(feature = "rand")]
455 #[inline]
456 pub fn try_random_with<R: rand::TryRngCore + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
457 let mut bytes = Self::ZERO;
458 bytes.try_randomize_with(rng)?;
459 Ok(bytes)
460 }
461
462 #[cfg(feature = "getrandom")]
466 #[inline]
467 #[track_caller]
468 pub fn randomize(&mut self) {
469 self.try_randomize().unwrap_or_else(|e| panic!("failed to fill with random bytes: {e}"));
470 }
471
472 #[inline]
477 #[cfg(feature = "getrandom")]
478 pub fn try_randomize(&mut self) -> Result<(), getrandom::Error> {
479 #[cfg(all(feature = "rand", feature = "std"))]
480 {
481 self.randomize_with(&mut rand::rng());
482 Ok(())
483 }
484 #[cfg(not(all(feature = "rand", feature = "std")))]
485 {
486 getrandom::fill(&mut self.0)
487 }
488 }
489
490 #[cfg(feature = "rand")]
492 #[inline]
493 #[doc(alias = "randomize_using")]
494 pub fn randomize_with<R: rand::RngCore + ?Sized>(&mut self, rng: &mut R) {
495 rng.fill_bytes(&mut self.0);
496 }
497
498 #[inline]
500 #[cfg(feature = "rand")]
501 pub fn try_randomize_with<R: rand::TryRngCore + ?Sized>(
502 &mut self,
503 rng: &mut R,
504 ) -> Result<(), R::Error> {
505 rng.try_fill_bytes(&mut self.0)
506 }
507
508 pub const fn concat_const<const M: usize, const Z: usize>(
517 self,
518 other: FixedBytes<M>,
519 ) -> FixedBytes<Z> {
520 assert!(N + M == Z, "Output size `Z` must equal the sum of the input sizes `N` and `M`");
521
522 let mut result = [0u8; Z];
523 let mut i = 0;
524 while i < Z {
525 result[i] = if i >= N { other.0[i - N] } else { self.0[i] };
526 i += 1;
527 }
528 FixedBytes(result)
529 }
530
531 #[inline]
543 #[track_caller]
544 pub fn from_slice(src: &[u8]) -> Self {
545 match Self::try_from(src) {
546 Ok(x) => x,
547 Err(_) => panic!("cannot convert a slice of length {} to FixedBytes<{N}>", src.len()),
548 }
549 }
550
551 #[inline]
562 #[track_caller]
563 pub fn left_padding_from(value: &[u8]) -> Self {
564 let len = value.len();
565 assert!(len <= N, "slice is too large. Expected <={N} bytes, got {len}");
566 let mut bytes = Self::ZERO;
567 bytes[N - len..].copy_from_slice(value);
568 bytes
569 }
570
571 #[inline]
582 #[track_caller]
583 pub fn right_padding_from(value: &[u8]) -> Self {
584 let len = value.len();
585 assert!(len <= N, "slice is too large. Expected <={N} bytes, got {len}");
586 let mut bytes = Self::ZERO;
587 bytes[..len].copy_from_slice(value);
588 bytes
589 }
590
591 #[inline]
593 pub const fn as_slice(&self) -> &[u8] {
594 &self.0
595 }
596
597 #[inline]
600 pub const fn as_mut_slice(&mut self) -> &mut [u8] {
601 &mut self.0
602 }
603
604 #[inline]
606 pub fn covers(&self, other: &Self) -> bool {
607 (*self & *other) == *other
608 }
609
610 pub const fn const_covers(self, other: Self) -> bool {
612 other.const_eq(&self.bit_and(other))
614 }
615
616 pub const fn const_eq(&self, other: &Self) -> bool {
618 let mut i = 0;
619 while i < N {
620 if self.0[i] != other.0[i] {
621 return false;
622 }
623 i += 1;
624 }
625 true
626 }
627
628 #[inline]
630 pub fn is_zero(&self) -> bool {
631 *self == Self::ZERO
632 }
633
634 #[inline]
636 pub const fn const_is_zero(&self) -> bool {
637 self.const_eq(&Self::ZERO)
638 }
639
640 pub const fn bit_and(self, rhs: Self) -> Self {
642 let mut ret = Self::ZERO;
643 let mut i = 0;
644 while i < N {
645 ret.0[i] = self.0[i] & rhs.0[i];
646 i += 1;
647 }
648 ret
649 }
650
651 pub const fn bit_or(self, rhs: Self) -> Self {
653 let mut ret = Self::ZERO;
654 let mut i = 0;
655 while i < N {
656 ret.0[i] = self.0[i] | rhs.0[i];
657 i += 1;
658 }
659 ret
660 }
661
662 pub const fn bit_xor(self, rhs: Self) -> Self {
664 let mut ret = Self::ZERO;
665 let mut i = 0;
666 while i < N {
667 ret.0[i] = self.0[i] ^ rhs.0[i];
668 i += 1;
669 }
670 ret
671 }
672
673 fn fmt_hex<const UPPER: bool>(&self, f: &mut fmt::Formatter<'_>, prefix: bool) -> fmt::Result {
674 let mut buf = hex::Buffer::<N, true>::new();
675 let s = if UPPER { buf.format_upper(self) } else { buf.format(self) };
676 f.write_str(unsafe { s.get_unchecked((!prefix as usize) * 2..) })
678 }
679}
680
681#[cfg(test)]
682mod tests {
683 use super::*;
684
685 macro_rules! test_fmt {
686 ($($fmt:literal, $hex:literal => $expected:literal;)+) => {$(
687 assert_eq!(
688 format!($fmt, fixed_bytes!($hex)),
689 $expected
690 );
691 )+};
692 }
693
694 #[test]
695 fn concat_const() {
696 const A: FixedBytes<2> = fixed_bytes!("0x0123");
697 const B: FixedBytes<2> = fixed_bytes!("0x4567");
698 const EXPECTED: FixedBytes<4> = fixed_bytes!("0x01234567");
699 const ACTUAL: FixedBytes<4> = A.concat_const(B);
700
701 assert_eq!(ACTUAL, EXPECTED);
702 }
703
704 #[test]
705 fn display() {
706 test_fmt! {
707 "{}", "0123456789abcdef" => "0x0123456789abcdef";
708 "{:#}", "0123" => "0x0123";
709 "{:#}", "01234567" => "0x01234567";
710 "{:#}", "0123456789" => "0x0123…6789";
711 }
712 }
713
714 #[test]
715 fn debug() {
716 test_fmt! {
717 "{:?}", "0123456789abcdef" => "0x0123456789abcdef";
718 "{:#?}", "0123456789abcdef" => "0x0123456789abcdef";
719 }
720 }
721
722 #[test]
723 fn lower_hex() {
724 test_fmt! {
725 "{:x}", "0123456789abcdef" => "0123456789abcdef";
726 "{:#x}", "0123456789abcdef" => "0x0123456789abcdef";
727 }
728 }
729
730 #[test]
731 fn upper_hex() {
732 test_fmt! {
733 "{:X}", "0123456789abcdef" => "0123456789ABCDEF";
734 "{:#X}", "0123456789abcdef" => "0x0123456789ABCDEF";
735 }
736 }
737
738 #[test]
739 fn left_padding_from() {
740 assert_eq!(FixedBytes::<4>::left_padding_from(&[0x01, 0x23]), fixed_bytes!("0x00000123"));
741
742 assert_eq!(
743 FixedBytes::<4>::left_padding_from(&[0x01, 0x23, 0x45, 0x67]),
744 fixed_bytes!("0x01234567")
745 );
746 }
747
748 #[test]
749 #[should_panic(expected = "slice is too large. Expected <=4 bytes, got 5")]
750 fn left_padding_from_too_large() {
751 FixedBytes::<4>::left_padding_from(&[0x01, 0x23, 0x45, 0x67, 0x89]);
752 }
753
754 #[test]
755 fn right_padding_from() {
756 assert_eq!(FixedBytes::<4>::right_padding_from(&[0x01, 0x23]), fixed_bytes!("0x01230000"));
757
758 assert_eq!(
759 FixedBytes::<4>::right_padding_from(&[0x01, 0x23, 0x45, 0x67]),
760 fixed_bytes!("0x01234567")
761 );
762 }
763
764 #[test]
765 #[should_panic(expected = "slice is too large. Expected <=4 bytes, got 5")]
766 fn right_padding_from_too_large() {
767 FixedBytes::<4>::right_padding_from(&[0x01, 0x23, 0x45, 0x67, 0x89]);
768 }
769}