Skip to main content

hadris_fat/
io.rs

1io_transform! {
2
3// Re-export I/O traits from the parent sync/async module.
4// Other I/O files use `super::io::Read` etc. which resolves through these re-exports.
5pub use super::super::{Read, Write, Seek, ReadExt, Error, ErrorKind, SeekFrom, Parsable, Writable};
6pub use super::super::IoResult;
7
8/// Create an I/O error from an ErrorKind.
9///
10/// This helper works in both std and no-std modes.
11#[cfg(feature = "std")]
12pub fn error_from_kind(kind: ErrorKind) -> Error {
13    Error::new(kind, "")
14}
15
16/// Creates a portable I/O error from its classification in `no_std` builds.
17#[cfg(not(feature = "std"))]
18pub fn error_from_kind(kind: ErrorKind) -> Error {
19    Error::from_kind(kind)
20}
21
22/// A Type Representing a FAT Sector
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
24pub(crate) struct Sector<T = usize>(pub T);
25
26/// Converts a typed sector number into a byte offset.
27pub trait SectorLike {
28    /// Converts this sector number using `bytes_per_sector`.
29    fn to_bytes(self, bytes_per_sector: usize) -> usize;
30}
31
32macro_rules! sector_impl {
33    ($ty:ty) => {
34        impl SectorLike for Sector<$ty> {
35            fn to_bytes(self, bytes_per_sector: usize) -> usize {
36                (self.0 as usize) * bytes_per_sector
37            }
38        }
39    };
40}
41sector_impl!(u8);
42sector_impl!(u16);
43sector_impl!(u32);
44sector_impl!(u64);
45sector_impl!(usize);
46
47/// Represents a cluster number in a FAT filesystem.
48/// Clusters are the allocation units for file data, starting at cluster 2.
49#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
50pub struct Cluster<T = usize>(pub T);
51
52impl Cluster<usize> {
53    /// Combines the high and low words stored in a directory entry.
54    pub fn from_parts(high: u16, low: u16) -> Self {
55        Self((high as usize) << 16 | (low as usize))
56    }
57}
58
59pub(crate) trait ClusterLike {
60    fn to_bytes(self, data_start: usize, bytes_per_cluster: usize) -> usize;
61}
62
63macro_rules! cluster_impl {
64    ($ty:ty) => {
65        impl ClusterLike for Cluster<$ty> {
66            fn to_bytes(self, data_start: usize, bytes_per_cluster: usize) -> usize {
67                data_start + (self.0 as usize - 2) * bytes_per_cluster
68            }
69        }
70    };
71}
72cluster_impl!(u8);
73cluster_impl!(u16);
74cluster_impl!(u32);
75cluster_impl!(u64);
76cluster_impl!(usize);
77
78/// Seekable data source with FAT sector and cluster geometry.
79pub struct SectorCursor<DATA: Seek> {
80    pub(crate) data: DATA,
81    pub(crate) sector_size: usize,
82    pub(crate) cluster_size: usize,
83}
84
85impl<DATA: Seek> SectorCursor<DATA> {
86    /// Creates a cursor with the supplied sector and cluster sizes.
87    pub const fn new(data: DATA, sector_size: usize, cluster_size: usize) -> Self {
88        Self {
89            data,
90            sector_size,
91            cluster_size,
92        }
93    }
94
95    /// Seeks to the beginning of a sector.
96    pub async fn seek_sector(&mut self, sector: impl SectorLike) -> hadris_io::Result<u64> {
97        self.seek(SeekFrom::Start(sector.to_bytes(self.sector_size) as u64))
98            .await
99            .map_err(hadris_io::Error::erase)
100    }
101}
102
103impl<T> Seek for SectorCursor<T>
104where
105    T: Seek,
106{
107    type Error = <T as Seek>::Error;
108
109    async fn seek(&mut self, pos: hadris_io::SeekFrom) -> hadris_io::Result<u64, Self::Error> {
110        self.data.seek(pos).await
111    }
112
113    async fn stream_position(&mut self) -> hadris_io::Result<u64, Self::Error> {
114        self.data.stream_position().await
115    }
116
117    async fn seek_relative(&mut self, offset: i64) -> hadris_io::Result<(), Self::Error> {
118        self.data.seek_relative(offset).await
119    }
120}
121
122impl<T> Read for SectorCursor<T>
123where
124    T: Read + Seek,
125{
126    type Error = <T as Read>::Error;
127
128    async fn read(&mut self, buf: &mut [u8]) -> hadris_io::Result<usize, Self::Error> {
129        self.data.read(buf).await
130    }
131
132    async fn read_exact(&mut self, buf: &mut [u8]) -> hadris_io::Result<()> {
133        self.data.read_exact(buf).await
134    }
135}
136
137#[cfg(feature = "write")]
138impl<T> Write for SectorCursor<T>
139where
140    T: Write + Seek,
141{
142    type Error = <T as Write>::Error;
143
144    async fn write(&mut self, buf: &[u8]) -> hadris_io::Result<usize, Self::Error> {
145        self.data.write(buf).await
146    }
147
148    async fn flush(&mut self) -> hadris_io::Result<(), Self::Error> {
149        self.data.flush().await
150    }
151
152    async fn write_all(&mut self, buf: &[u8]) -> hadris_io::Result<()> {
153        self.data.write_all(buf).await
154    }
155}
156
157} // end io_transform!