1use crate::error::{Error, Result};
2use crate::medium::Medium;
3use std::io::{Read, Seek};
4use std::sync::{Arc, OnceLock};
5
6pub use crate::header::{
8 FileTypeIdentifier, Header, HeaderStructure, RegionTable, RegionTableEntry, RegionTableHeader,
9};
10
11pub use crate::bat::{Bat, BatEntry, BatState, PayloadBlockState, SectorBitmapState};
13
14pub use crate::metadata::{
16 EntryFlags, FileParameters, KeyValueEntry, LocatorHeader, Metadata, MetadataItems,
17 MetadataTable, ParentLocator, StandardItems, TableEntry, TableHeader,
18};
19
20pub use crate::log::{
22 DataDescriptor, DataSector, Descriptor, Entry, Log, LogEntryHeader, ZeroDescriptor,
23};
24
25pub struct Sections<'a, T = std::fs::File> {
30 header: Arc<[u8]>,
31 medium: &'a Medium<T>,
32 bat: OnceLock<Arc<[u8]>>,
33 metadata: OnceLock<Arc<[u8]>>,
34 log: OnceLock<Arc<[u8]>>,
35}
36
37impl<'a, T> Sections<'a, T>
38where
39 T: Read + Seek,
40{
41 pub(crate) fn new(header: Arc<[u8]>, medium: &'a Medium<T>) -> Result<Self> {
43 Header::new(&header)?;
44 Ok(Self {
45 header,
46 medium,
47 bat: OnceLock::new(),
48 metadata: OnceLock::new(),
49 log: OnceLock::new(),
50 })
51 }
52
53 pub fn header(&self) -> Result<Header<'_>> {
66 Header::new(&self.header)
67 }
68
69 pub fn bat(&self) -> Result<Bat<'_>> {
79 if self.metadata.get().is_none() {
80 let _ = self.metadata.set(self.medium.metadata_buf()?);
81 }
82 if self.bat.get().is_none() {
83 let _ = self.bat.set(self.medium.bat_buf()?);
84 }
85 let metadata = self
86 .metadata
87 .get()
88 .ok_or_else(|| Error::InvalidMetadata("metadata cache not loaded".into()))?;
89 let chunk_ratio = Self::compute_chunk_ratio(metadata)?;
90 let bat = self
91 .bat
92 .get()
93 .ok_or_else(|| Error::InvalidFile("BAT cache not loaded".into()))?;
94 Ok(Bat::new(bat, chunk_ratio))
95 }
96
97 pub fn metadata(&self) -> Result<Metadata<'_>> {
105 if self.metadata.get().is_none() {
106 let _ = self.metadata.set(self.medium.metadata_buf()?);
107 }
108 let metadata = self
109 .metadata
110 .get()
111 .ok_or_else(|| Error::InvalidMetadata("metadata cache not loaded".into()))?;
112 Metadata::new(metadata)
113 }
114
115 pub fn log(&self) -> Result<Log<'_>> {
123 if self.log.get().is_none() {
124 let _ = self.log.set(self.medium.log_buf()?);
125 }
126 let log = self
127 .log
128 .get()
129 .ok_or_else(|| Error::InvalidFile("log cache not loaded".into()))?;
130 Log::new(log)
131 }
132
133 fn compute_chunk_ratio(meta_buf: &[u8]) -> Result<u64> {
140 let metadata = Metadata::new(meta_buf)?;
141 let items = metadata.items();
142 let fp = items
143 .file_parameters()
144 .map_err(|_| Error::InvalidMetadata("FileParameters metadata item not found".into()))?;
145 let block_size = u64::from(fp.block_size());
146 if block_size == 0 {
147 return Err(Error::InvalidMetadata("block size must be non-zero".into()));
148 }
149 let logical_sector_size = u64::from(items.logical_sector_size().map_err(|_| {
150 Error::InvalidMetadata("LogicalSectorSize metadata item not found".into())
151 })?);
152 Ok(crate::common::compute_chunk_ratio(
153 block_size,
154 logical_sector_size,
155 ))
156 }
157}