1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
/// Errors signaling bytes that don't represent valid data.
pub mod error;
mod primitives;
#[cfg(feature = "packbytes-derive")]
pub use packbytes_derive::{FromBytes, ToBytes, TryFromBytes};
use core::convert::Infallible;
use core::ops;
/// A private module to disallow implementing ByteArray on other types than byte arrays.
mod private {
pub trait ByteArray {}
impl<const N: usize> ByteArray for [u8; N] {}
}
/// A helper trait that is implemented only for byte arrays.
///
/// It is necessary since associated constants cannot be used in function signatures so far.
pub trait ByteArray:
private::ByteArray
+ ops::IndexMut<usize, Output = u8>
+ ops::IndexMut<ops::Range<usize>, Output = [u8]>
+ AsRef<[u8]>
+ AsMut<[u8]>
{
/// The length of this byte array.
const SIZE: usize;
/// Return the array with all zeros.
/// Cannot use `Default` as it is not implemented for all array sizes.
fn zeroed() -> Self;
}
impl<const N: usize> ByteArray for [u8; N] {
const SIZE: usize = N;
fn zeroed() -> Self {
[0; N]
}
}
/// Create a value from its representation as a packed stack byte array of a fixed size.
///
/// Most times, the method `from_bytes` should be used, as it ensures consistency by respecting
/// the byte order set by the `PREFERS_LE` associated constant.
pub trait FromBytes: Sized {
/// A byte array which can store a packed representation of this type.
type Bytes: ByteArray;
/// Is it preferred to represent this type as bytes in the little endian order?
const PREFERS_LE: bool = true;
/// Create a value of this type from its representation as a byte array in little endian.
fn from_le_bytes(bytes: Self::Bytes) -> Self;
/// Create a value of this type from its representation as a byte array in big endian.
fn from_be_bytes(bytes: Self::Bytes) -> Self;
/// Create a value of this type from its representation as a byte array in the preferred
/// byte order, set in the associated constant `PREFERS_LE`.
#[inline]
fn from_bytes(bytes: Self::Bytes) -> Self {
if Self::PREFERS_LE {
Self::from_le_bytes(bytes)
} else {
Self::from_be_bytes(bytes)
}
}
/// Create a value of this type from its representation as a byte array in native endian.
///
/// As the target platform’s native endianness is used, portable code likely wants to use
/// `from_le_bytes` or `from_be_bytes`, as appropriate instead.
#[inline]
fn from_ne_bytes(bytes: Self::Bytes) -> Self {
if cfg!(target_endian = "little") {
Self::from_le_bytes(bytes)
} else {
Self::from_be_bytes(bytes)
}
}
/// Read a byte representation of this type in the preferred byte order (set in the associated
/// constant `PREFERS_LE`) and create a value of this type from it.
#[cfg(feature = "std")]
#[inline]
fn read_packed<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
let mut bytes = Self::Bytes::zeroed();
reader.read_exact(bytes.as_mut())?;
Ok(Self::from_bytes(bytes))
}
}
/// Try to create a value from its representation as a packed stack byte array of a fixed size.
///
/// Most times, the method `try_from_bytes` should be used, as it ensures consistency by respecting
/// the byte order set by the `PREFERS_LE` associated constant.
pub trait TryFromBytes: Sized {
/// A byte array which can store a packed representation of this type.
type Bytes: ByteArray;
/// A type containing the failure of creating a value of the type from bytes.
type Error;
/// Is it preferred to represent this type as bytes in the little endian order?
const PREFERS_LE: bool = true;
/// Try to create a value of this type from its representation as a byte array in little endian.
fn try_from_le_bytes(bytes: Self::Bytes) -> Result<Self, Self::Error>;
/// Try to create a value of this type from its representation as a byte array in big endian.
fn try_from_be_bytes(bytes: Self::Bytes) -> Result<Self, Self::Error>;
/// Try to create a value of this type from its representation as a byte array in the preferred
/// byte order, set in the associated constant `PREFERS_LE`.
#[inline]
fn try_from_bytes(bytes: Self::Bytes) -> Result<Self, Self::Error> {
if Self::PREFERS_LE {
Self::try_from_le_bytes(bytes)
} else {
Self::try_from_be_bytes(bytes)
}
}
/// Try to create a value of this type from its representation as a byte array in native endian.
///
/// As the target platform’s native endianness is used, portable code likely wants to use
/// `try_from_le_bytes` or `try_from_be_bytes`, as appropriate instead.
#[inline]
fn try_from_ne_bytes(bytes: Self::Bytes) -> Result<Self, Self::Error> {
if cfg!(target_endian = "little") {
Self::try_from_le_bytes(bytes)
} else {
Self::try_from_be_bytes(bytes)
}
}
}
/// Pack this type into a stack byte array of a fixed size.
///
/// Most times, the method `to_bytes` should be used, as it ensures consistency by respecting
/// the byte order set by the `PREFERS_LE` associated constant.
pub trait ToBytes: Sized {
/// A byte array which can store a packed representation of this type.
type Bytes: ByteArray;
/// Is it preferred to represent this type as bytes in the little endian order?
const PREFERS_LE: bool = true;
/// Return the memory representation of this type as a byte array in little endian byte order.
fn to_le_bytes(self) -> Self::Bytes;
/// Return the memory representation of this type as a byte array in big endian byte order.
fn to_be_bytes(self) -> Self::Bytes;
/// Return the memory representation of this type as a byte array in the preferred
/// byte order, set in the associated constant `PREFERS_LE`.
#[inline]
fn to_bytes(self) -> Self::Bytes {
if Self::PREFERS_LE {
self.to_le_bytes()
} else {
self.to_be_bytes()
}
}
/// Return the memory representation of this type as a byte array in native endian byte order.
///
/// As the target platform’s native endianness is used, portable code likely wants to use
/// `to_le_bytes` or `to_be_bytes`, as appropriate instead.
#[inline]
fn to_ne_bytes(self) -> Self::Bytes {
if cfg!(target_endian = "little") {
self.to_le_bytes()
} else {
self.to_be_bytes()
}
}
/// Write the value of this type to a writer in preferred byte order, set by the associated
/// constant `PREFERS_LE`.
#[cfg(feature = "std")]
#[inline]
fn write_packed<W: std::io::Write>(self, writer: &mut W) -> std::io::Result<usize> {
writer.write(self.to_bytes().as_ref())
}
}
impl<B: ByteArray, T: FromBytes<Bytes = B>> TryFromBytes for T {
/// A byte array which can store a packed representation of this type.
type Bytes = B;
/// A type containing the failure of creating a value of the type from bytes.
type Error = Infallible;
/// Create a value of this type from its representation as a byte array in little endian.
#[inline]
fn try_from_le_bytes(bytes: B) -> Result<Self, Self::Error> {
Ok(Self::from_le_bytes(bytes))
}
/// Create a value of this type from its representation as a byte array in big endian.
#[inline]
fn try_from_be_bytes(bytes: B) -> Result<Self, Self::Error> {
Ok(Self::from_be_bytes(bytes))
}
}
impl<const N: usize> FromBytes for [u8; N] {
type Bytes = Self;
#[inline]
fn from_le_bytes(bytes: Self::Bytes) -> Self {
bytes
}
#[inline]
fn from_be_bytes(bytes: Self::Bytes) -> Self {
bytes
}
}
impl<const N: usize> ToBytes for [u8; N] {
type Bytes = Self;
#[inline]
fn to_le_bytes(self) -> Self::Bytes {
self
}
#[inline]
fn to_be_bytes(self) -> Self::Bytes {
self
}
}