use alloc::string::{FromUtf8Error, String};
use core::fmt::{Debug, Display, Formatter};
use bitflags::bitflags;
#[derive(Debug, Eq, PartialEq)]
pub struct Superblock {
pub num_inodes: u32,
pub num_blocks: u32,
pub num_superuser_reserved_blocks: u32,
pub num_unallocated_blocks: u32,
pub num_unallocated_inodes: u32,
pub superblock_block_number: u32,
pub block_size: u32,
pub fragment_size: u32,
pub blocks_per_group: u32,
pub fragments_per_group: u32,
pub inodes_per_group: u32,
pub last_mount_time: u32,
pub last_written_time: u32,
pub mounts_since_fsck: u16,
pub mounts_allowed_before_fsck: u16,
pub magic_number: u16,
pub state: State,
pub error_policy: ErrorPolicy,
pub version_minor: u16,
pub last_fsck: u32,
pub fsck_force_interval: u32,
pub os_id: u32,
pub version_major: u32,
pub uid_for_reserved_blocks: u16,
pub gid_for_reserved_blocks: u16,
pub extended: Option<SuperblockExtended>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SuperblockDecodeError {
InvalidMagicNumber,
ShortRead,
InvalidBlockSize,
InvalidFragmentSize,
InvalidData,
}
impl Display for SuperblockDecodeError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Debug::fmt(&self, f)
}
}
impl core::error::Error for SuperblockDecodeError {}
impl From<ShortReadError> for SuperblockDecodeError {
fn from(_: ShortReadError) -> Self {
Self::ShortRead
}
}
impl From<FromUtf8Error> for SuperblockDecodeError {
fn from(_: FromUtf8Error) -> Self {
Self::InvalidData
}
}
impl TryFrom<[u8; 1024]> for Superblock {
type Error = SuperblockDecodeError;
fn try_from(value: [u8; 1024]) -> Result<Self, Self::Error> {
let num_inodes = read_u32_le(&value[0..4])?;
let num_blocks = read_u32_le(&value[4..8])?;
let num_superuser_reserved_blocks = read_u32_le(&value[8..12])?;
let num_unallocated_blocks = read_u32_le(&value[12..16])?;
let num_unallocated_inodes = read_u32_le(&value[16..20])?;
let superblock_block_number = read_u32_le(&value[20..24])?;
let log2_block_size = read_u32_le(&value[24..28])?;
let block_size = u32::checked_shl(1024, log2_block_size)
.ok_or(SuperblockDecodeError::InvalidBlockSize)?;
let log2_fragment_size = read_u32_le(&value[28..32])?;
let fragment_size = u32::checked_shl(1024, log2_fragment_size)
.ok_or(SuperblockDecodeError::InvalidFragmentSize)?;
let blocks_per_group = read_u32_le(&value[32..36])?;
let fragments_per_group = read_u32_le(&value[36..40])?;
let inodes_per_group = read_u32_le(&value[40..44])?;
let last_mount_time = read_u32_le(&value[44..48])?;
let last_written_time = read_u32_le(&value[48..52])?;
let mounts_since_fsck = read_u16_le(&value[52..54])?;
let mounts_allowed_before_fsck = read_u16_le(&value[54..56])?;
let magic_number = read_u16_le(&value[56..58])?;
let state = State::from_bits_truncate(read_u16_le(&value[58..60])?);
let error_policy = ErrorPolicy::from_bits_truncate(read_u16_le(&value[60..62])?);
let version_minor = read_u16_le(&value[62..64])?;
let last_fsck = read_u32_le(&value[64..68])?;
let fsck_force_interval = read_u32_le(&value[68..72])?;
let os_id = read_u32_le(&value[72..76])?;
let version_major = read_u32_le(&value[76..80])?;
let uid_for_reserved_blocks = read_u16_le(&value[80..82])?;
let gid_for_reserved_blocks = read_u16_le(&value[82..84])?;
if blocks_per_group < 1 || inodes_per_group < 1 {
return Err(SuperblockDecodeError::InvalidData);
}
let check1 = (num_blocks + blocks_per_group - 1) / blocks_per_group;
let check2 = (num_inodes + inodes_per_group - 1) / inodes_per_group;
if check1 != check2 {
return Err(SuperblockDecodeError::InvalidData);
}
if magic_number != 0xEF53 {
return Err(SuperblockDecodeError::InvalidMagicNumber);
}
let extended = if version_major >= 1 {
Some(SuperblockExtended::try_from(value)?)
} else {
None
};
Ok(Self {
num_inodes,
num_blocks,
num_superuser_reserved_blocks,
num_unallocated_blocks,
num_unallocated_inodes,
superblock_block_number,
block_size,
fragment_size,
blocks_per_group,
fragments_per_group,
inodes_per_group,
last_mount_time,
last_written_time,
mounts_since_fsck,
mounts_allowed_before_fsck,
magic_number,
state,
error_policy,
version_minor,
last_fsck,
fsck_force_interval,
os_id,
version_major,
uid_for_reserved_blocks,
gid_for_reserved_blocks,
extended,
})
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Ext2FsId([u8; 16]);
#[derive(Debug, Eq, PartialEq)]
pub struct SuperblockExtended {
pub first_non_reserved_inode: u32,
pub inode_size: u16,
pub this_superblock_block_group: u16,
pub optional_features: OptionalFeatures,
pub required_features: RequiredFeatures,
pub write_required_features: ReadOnlyFeatures,
pub fsid: Ext2FsId,
pub volume_name: String,
pub last_mount_path: String,
pub compression: u32,
pub num_preallocate_blocks_file: u8,
pub num_preallocate_blocks_directory: u8,
}
impl TryFrom<[u8; 1024]> for SuperblockExtended {
type Error = SuperblockDecodeError;
fn try_from(value: [u8; 1024]) -> Result<Self, Self::Error> {
let first_non_reserved_inode = read_u32_le(&value[84..88])?;
let inode_size = read_u16_le(&value[88..90])?;
let this_superblock_block_group = read_u16_le(&value[90..92])?;
let optional_features = read_u32_le(&value[92..96])?;
let required_features = read_u32_le(&value[96..100])?;
let write_required_features = read_u32_le(&value[100..104])?;
let mut fsid = [0_u8; 16];
fsid.copy_from_slice(&value[104..120]);
let volume_name = read_c_str(&value[120..136])?;
let last_mount_path = read_c_str(&value[136..200])?;
let compression = read_u32_le(&value[200..204])?;
let num_preallocate_blocks_file = value[204];
let num_preallocate_blocks_directory = value[205];
Ok(Self {
first_non_reserved_inode,
inode_size,
this_superblock_block_group,
optional_features: OptionalFeatures::from_bits_truncate(optional_features),
required_features: RequiredFeatures::from_bits_truncate(required_features),
write_required_features: ReadOnlyFeatures::from_bits_truncate(write_required_features),
fsid: Ext2FsId(fsid),
volume_name,
last_mount_path,
compression,
num_preallocate_blocks_file,
num_preallocate_blocks_directory,
})
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct OptionalFeatures: u32 {
const PREALLOCATE_FOR_DIRECTORY = 0x0001;
const AFS_SERVER_INODES_EXIST = 0x0002;
const HAS_JOURNAL = 0x0004;
const INODES_EXTENDED_ATTRIBUTES = 0x0008;
const CAN_RESIZE = 0x0010;
const DIRECTORIES_USE_HASH_INDEX = 0x0020;
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct RequiredFeatures: u32 {
const COMPRESSION_USED = 0x0001;
const DIRECTORY_ENTRIES_HAVE_TYPE = 0x0002;
const NEEDS_JOURNAL_REPLAY = 0x0004;
const USES_JOURNAL_DEVICE = 0x0008;
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ReadOnlyFeatures: u32 {
const SPARSE_SUPERBLOCK_AND_GDTS = 0x0001;
const USE_64BIT_FILE_SIZE = 0x0002;
const DIRS_STORED_AS_BINARY_TREE = 0x0004;
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct State: u16 {
const CLEAN = 1;
const ERRONOUS = 2;
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ErrorPolicy: u16 {
const IGNORE = 1;
const REMOUNT_READ_ONLY = 2;
const KERNEL_PANIC = 3;
}
}
#[derive(Debug)]
struct ShortReadError;
fn read_u32_le(data: &[u8]) -> Result<u32, ShortReadError> {
if data.len() < 4 {
return Err(ShortReadError);
}
Ok(u32::from_le_bytes(data[0..4].try_into().unwrap()))
}
fn read_u16_le(data: &[u8]) -> Result<u16, ShortReadError> {
if data.len() < 2 {
return Err(ShortReadError);
}
Ok(u16::from_le_bytes(data[0..2].try_into().unwrap()))
}
fn read_c_str(data: &[u8]) -> Result<String, FromUtf8Error> {
let termination_index = data.iter().position(|&b| b == 0).unwrap_or(data.len() + 1);
String::from_utf8(data[0..termination_index].into())
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn test_superblock_try_from() {
let data = [
0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xca, 0x03,
0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x5d, 0x7f, 0x64, 0x00, 0x00, 0xff, 0xff,
0x53, 0xef, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc5, 0x5d, 0x7f, 0x64, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x77, 0x4b, 0x94, 0xce, 0x3d, 0x05, 0x4b, 0x71,
0x98, 0xfc, 0xfc, 0xf6, 0x37, 0xfd, 0x2b, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x38,
0x05, 0x06, 0x72, 0xb2, 0x44, 0xcd, 0xbb, 0x20, 0x4e, 0xa1, 0xcb, 0x69, 0xa6, 0x1d,
0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x5d,
0x7f, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
let sb = Superblock::try_from(data).unwrap();
assert_eq!(
Superblock {
num_inodes: 128,
num_blocks: 1024,
num_superuser_reserved_blocks: 51,
num_unallocated_blocks: 970,
num_unallocated_inodes: 117,
superblock_block_number: 1,
block_size: 1024,
fragment_size: 1024,
blocks_per_group: 8192,
fragments_per_group: 8192,
inodes_per_group: 128,
last_mount_time: 0,
last_written_time: 1686068678,
mounts_since_fsck: 0,
mounts_allowed_before_fsck: 65535,
magic_number: 61267,
state: State::CLEAN,
error_policy: ErrorPolicy::IGNORE,
version_minor: 0,
last_fsck: 1686068677,
fsck_force_interval: 0,
os_id: 0,
version_major: 1,
uid_for_reserved_blocks: 0,
gid_for_reserved_blocks: 0,
extended: Some(SuperblockExtended {
first_non_reserved_inode: 11,
inode_size: 256,
this_superblock_block_group: 0,
optional_features: OptionalFeatures::INODES_EXTENDED_ATTRIBUTES
| OptionalFeatures::CAN_RESIZE
| OptionalFeatures::DIRECTORIES_USE_HASH_INDEX,
required_features: RequiredFeatures::DIRECTORY_ENTRIES_HAVE_TYPE,
write_required_features: ReadOnlyFeatures::SPARSE_SUPERBLOCK_AND_GDTS
| ReadOnlyFeatures::USE_64BIT_FILE_SIZE,
fsid: Ext2FsId([
119, 75, 148, 206, 61, 5, 75, 113, 152, 252, 252, 246, 55, 253, 43, 72,
],),
volume_name: "".to_string(),
last_mount_path: "".to_string(),
compression: 0,
num_preallocate_blocks_file: 0,
num_preallocate_blocks_directory: 0,
},),
},
sb
);
}
#[test]
fn test_read_u32_le() {
for (expected, data) in [
(1, [0x01, 0x00, 0x00, 0x00]),
(2, [0x02, 0x00, 0x00, 0x00]),
(255, [0xFF, 0x00, 0x00, 0x00]),
(65280, [0x00, 0xFF, 0x00, 0x00]),
(4294967040, [0x00, 0xFF, 0xFF, 0xFF]),
(4294967295, [0xFF, 0xFF, 0xFF, 0xFF]),
] {
let actual = read_u32_le(&data).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn test_read_u32_le_short() {
assert!(read_u32_le(&[]).is_err());
assert!(read_u32_le(&[0]).is_err());
assert!(read_u32_le(&[0, 0]).is_err());
assert!(read_u32_le(&[0, 0, 0]).is_err());
assert!(read_u32_le(&[0, 0, 0, 0]).is_ok_and(|i| i == 0));
}
#[test]
fn test_read_u16_le() {
for (expected, data) in [
(1, [0x01, 0x00]),
(2, [0x02, 0x00]),
(255, [0xFF, 0x00]),
(65280, [0x00, 0xFF]),
(65535, [0xFF, 0xFF]),
] {
let actual = read_u16_le(&data).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn test_read_u16_le_short() {
assert!(read_u16_le(&[]).is_err());
assert!(read_u16_le(&[0]).is_err());
assert!(read_u16_le(&[0, 0]).is_ok_and(|i| i == 0));
}
}