| 1 | // Copyright 2016 Brian Smith. |
| 2 | // |
| 3 | // Permission to use, copy, modify, and/or distribute this software for any |
| 4 | // purpose with or without fee is hereby granted, provided that the above |
| 5 | // copyright notice and this permission notice appear in all copies. |
| 6 | // |
| 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| 10 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 12 | // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 13 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 14 | |
| 15 | //! Bit lengths. |
| 16 | |
| 17 | use crate::{error::InputTooLongError, polyfill}; |
| 18 | |
| 19 | /// The length of something, in bits. |
| 20 | /// |
| 21 | /// This can represent a bit length that isn't a whole number of bytes. |
| 22 | #[derive (Clone, Copy, Debug, Eq, PartialEq, PartialOrd)] |
| 23 | #[repr (transparent)] |
| 24 | pub struct BitLength<T = usize>(T); |
| 25 | |
| 26 | pub(crate) trait FromByteLen<T>: Sized { |
| 27 | /// Constructs a `BitLength` from the given length in bytes. |
| 28 | /// |
| 29 | /// Fails if `bytes * 8` is too large for a `T`. |
| 30 | fn from_byte_len(bytes: T) -> Result<Self, InputTooLongError<T>>; |
| 31 | } |
| 32 | |
| 33 | impl FromByteLen<usize> for BitLength<usize> { |
| 34 | #[inline ] |
| 35 | fn from_byte_len(bytes: usize) -> Result<Self, InputTooLongError> { |
| 36 | match bytes.checked_mul(8) { |
| 37 | Some(bits: usize) => Ok(Self(bits)), |
| 38 | None => Err(InputTooLongError::new(imprecise_input_length:bytes)), |
| 39 | } |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | impl FromByteLen<u64> for BitLength<u64> { |
| 44 | #[inline ] |
| 45 | fn from_byte_len(bytes: u64) -> Result<Self, InputTooLongError<u64>> { |
| 46 | match bytes.checked_mul(8) { |
| 47 | Some(bits: u64) => Ok(Self(bits)), |
| 48 | None => Err(InputTooLongError::new(imprecise_input_length:bytes)), |
| 49 | } |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | impl FromByteLen<usize> for BitLength<u64> { |
| 54 | #[inline ] |
| 55 | fn from_byte_len(bytes: usize) -> Result<Self, InputTooLongError<usize>> { |
| 56 | match polyfill::u64_from_usize(bytes).checked_mul(8) { |
| 57 | Some(bits: u64) => Ok(Self(bits)), |
| 58 | None => Err(InputTooLongError::new(imprecise_input_length:bytes)), |
| 59 | } |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | impl<T> BitLength<T> { |
| 64 | /// Constructs a `BitLength` from the given length in bits. |
| 65 | #[inline ] |
| 66 | pub const fn from_bits(bits: T) -> Self { |
| 67 | Self(bits) |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | impl<T: Copy> BitLength<T> { |
| 72 | /// The number of bits this bit length represents, as the underlying type. |
| 73 | #[inline ] |
| 74 | pub fn as_bits(self) -> T { |
| 75 | self.0 |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | // Lengths measured in bits, where all arithmetic is guaranteed not to |
| 80 | // overflow. |
| 81 | impl BitLength<usize> { |
| 82 | #[cfg (feature = "alloc" )] |
| 83 | #[inline ] |
| 84 | pub(crate) fn half_rounded_up(&self) -> Self { |
| 85 | let round_up = self.0 & 1; |
| 86 | Self((self.0 / 2) + round_up) |
| 87 | } |
| 88 | |
| 89 | /// The bit length, rounded up to a whole number of bytes. |
| 90 | #[inline ] |
| 91 | pub const fn as_usize_bytes_rounded_up(&self) -> usize { |
| 92 | // Equivalent to (self.0 + 7) / 8, except with no potential for |
| 93 | // overflow and without branches. |
| 94 | |
| 95 | // Branchless round_up = if self.0 & 0b111 != 0 { 1 } else { 0 }; |
| 96 | let round_up = ((self.0 >> 2) | (self.0 >> 1) | self.0) & 1; |
| 97 | |
| 98 | (self.0 / 8) + round_up |
| 99 | } |
| 100 | |
| 101 | #[cfg (feature = "alloc" )] |
| 102 | #[inline ] |
| 103 | pub(crate) fn try_sub_1(self) -> Result<Self, crate::error::Unspecified> { |
| 104 | let sum = self.0.checked_sub(1).ok_or(crate::error::Unspecified)?; |
| 105 | Ok(Self(sum)) |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | impl BitLength<u64> { |
| 110 | pub fn to_be_bytes(self) -> [u8; 8] { |
| 111 | self.0.to_be_bytes() |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | #[cfg (any(target_pointer_width = "32" , target_pointer_width = "64" ))] |
| 116 | impl From<BitLength<usize>> for BitLength<u64> { |
| 117 | fn from(BitLength(value: usize): BitLength<usize>) -> Self { |
| 118 | BitLength(polyfill::u64_from_usize(value)) |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | impl TryFrom<BitLength<u64>> for BitLength<core::num::NonZeroU64> { |
| 123 | type Error = <core::num::NonZeroU64 as TryFrom<u64>>::Error; |
| 124 | |
| 125 | fn try_from(BitLength(value: u64): BitLength<u64>) -> Result<Self, Self::Error> { |
| 126 | value.try_into().map(op:BitLength) |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | const _TEST_AS_USIZE_BYTES_ROUNDED_UP_EVEN: () = |
| 131 | assert!(BitLength::from_bits(8192).as_usize_bytes_rounded_up() == 8192 / 8); |
| 132 | const _TEST_AS_USIZE_BYTES_ROUNDED_UP_ONE_BIT_HIGH: () = |
| 133 | assert!(BitLength::from_bits(8192 + 1).as_usize_bytes_rounded_up() == (8192 / 8) + 1); |
| 134 | const _TEST_AS_USIZE_BYTES_ROUNDED_UP_SEVEN_BITS_HIGH: () = |
| 135 | assert!(BitLength::from_bits(8192 + 7).as_usize_bytes_rounded_up() == (8192 / 8) + 1); |
| 136 | |