1#[cfg(feature = "mmap")]
4use super::collect::map_checked;
5use super::collect::{OutputLimit, collect_checked, path_origin, read_failure};
6#[cfg(all(feature = "gzip", feature = "mmap"))]
7use super::decode::map_gzip_reader;
8#[cfg(all(feature = "zstd", feature = "mmap"))]
9use super::decode::map_zstd_reader;
10use super::decode::{compressed_file_size, expand_gzip_reader, expand_zstd_reader, sniff_reader};
11#[cfg(not(feature = "mmap"))]
12use super::decode::{decompress, read_bounded};
13use super::{Compression, Limits};
14#[cfg(not(feature = "mmap"))]
15use crate::diagnostic::Code;
16use crate::diagnostic::Diagnostic;
17use std::fmt;
18use std::fs::File;
19use std::io::Read;
20use std::path::Path;
21use std::sync::Arc;
22
23#[cfg(feature = "mmap")]
24pub(super) const MMAP_MIN_BYTES: u64 = 1 << 20;
25
26#[derive(Clone, Copy, PartialEq, Eq, Debug)]
28pub enum InputKind {
29 Owned,
31 OwnerBacked,
33 #[cfg(feature = "mmap")]
35 Mapped,
36}
37
38trait ByteOwner: AsRef<[u8]> + fmt::Debug + Send + Sync {}
39
40impl<T> ByteOwner for T where T: AsRef<[u8]> + fmt::Debug + Send + Sync {}
41
42#[derive(Clone, Debug)]
43enum InputBytes {
44 Owned(Arc<Vec<u8>>),
45 OwnerBacked(Arc<dyn ByteOwner>),
46 #[cfg(feature = "mmap")]
47 Mapped(Arc<molframe_mmap::MappedFile>),
48}
49
50impl InputBytes {
51 fn as_slice(&self) -> &[u8] {
52 match self {
53 Self::Owned(bytes) => bytes.as_slice(),
54 Self::OwnerBacked(owner) => owner.as_ref().as_ref(),
55 #[cfg(feature = "mmap")]
56 Self::Mapped(mapped) => mapped.as_bytes(),
57 }
58 }
59
60 const fn kind(&self) -> InputKind {
61 match self {
62 Self::Owned(_) => InputKind::Owned,
63 Self::OwnerBacked(_) => InputKind::OwnerBacked,
64 #[cfg(feature = "mmap")]
65 Self::Mapped(_) => InputKind::Mapped,
66 }
67 }
68}
69
70#[derive(Clone, Debug)]
72pub struct InputBuffer {
73 bytes: InputBytes,
74 origin: Option<Arc<str>>,
75}
76
77impl InputBuffer {
78 #[must_use]
80 pub fn from_bytes(bytes: Vec<u8>) -> Self {
81 Self {
82 bytes: InputBytes::Owned(Arc::new(bytes)),
83 origin: None,
84 }
85 }
86
87 #[must_use]
93 pub fn from_owner<T>(owner: T) -> Self
94 where
95 T: AsRef<[u8]> + fmt::Debug + Send + Sync + 'static,
96 {
97 Self {
98 bytes: InputBytes::OwnerBacked(Arc::new(owner)),
99 origin: None,
100 }
101 }
102
103 pub fn open(path: impl AsRef<Path>, limits: Limits) -> Result<Self, Diagnostic> {
109 let path = path.as_ref();
110 let file = File::open(path)
111 .map_err(|error| read_failure("input could not be opened", Some(path), &error))?;
112 let compressed_size = file.metadata().map(|metadata| metadata.len());
113 #[cfg(feature = "mmap")]
114 let file_length = compressed_size.as_ref().copied().ok();
115
116 let (compression, reader) = sniff_reader(file, path)?;
117 let compressed = match compression {
118 Compression::None => 0,
119 Compression::Gzip | Compression::Zstd => compressed_file_size(compressed_size, path)?,
120 };
121 #[cfg(feature = "mmap")]
122 if compression != Compression::None
123 || file_length.is_some_and(|length| length >= MMAP_MIN_BYTES)
124 {
125 let mapped = match compression {
126 Compression::None => map_checked(
127 reader,
128 OutputLimit::uncompressed(limits),
129 "input could not be read",
130 Some(path),
131 )?,
132 #[cfg(feature = "gzip")]
133 Compression::Gzip => map_gzip_reader(reader, compressed, limits, Some(path))?,
134 #[cfg(not(feature = "gzip"))]
135 Compression::Gzip => return Err(super::decode::unsupported("gzip")),
136 #[cfg(feature = "zstd")]
137 Compression::Zstd => map_zstd_reader(reader, compressed, limits, Some(path))?,
138 #[cfg(not(feature = "zstd"))]
139 Compression::Zstd => return Err(super::decode::unsupported("zstd")),
140 };
141 return Ok(Self {
142 bytes: InputBytes::Mapped(Arc::new(mapped)),
143 origin: Some(path_origin(path)),
144 });
145 }
146
147 let bytes = match compression {
148 Compression::None => collect_checked(
149 reader,
150 OutputLimit::uncompressed(limits),
151 "input could not be read",
152 Some(path),
153 )?,
154 Compression::Gzip => expand_gzip_reader(reader, compressed, limits, Some(path))?,
155 Compression::Zstd => expand_zstd_reader(reader, compressed, limits, Some(path))?,
156 };
157 Ok(Self {
158 bytes: InputBytes::Owned(Arc::new(bytes)),
159 origin: Some(path_origin(path)),
160 })
161 }
162
163 #[cfg(feature = "mmap")]
184 #[must_use]
185 pub fn from_mapped(mapped: molframe_mmap::MappedFile) -> Self {
186 let _advised = mapped.advise_sequential();
187 Self {
188 bytes: InputBytes::Mapped(Arc::new(mapped)),
189 origin: None,
190 }
191 }
192
193 pub fn from_reader(reader: impl Read, limits: Limits) -> Result<Self, Diagnostic> {
199 #[cfg(feature = "mmap")]
200 {
201 Self::map_reader(reader, limits)
202 }
203 #[cfg(not(feature = "mmap"))]
204 {
205 let raw = read_bounded(reader, limits).map_err(|error| {
206 Diagnostic::new(Code::E7101)
207 .with_message("input stream could not be read")
208 .with_context("reason", error.to_string())
209 })?;
210 let bytes = decompress(raw, limits)?;
211 Ok(Self {
212 bytes: InputBytes::Owned(Arc::new(bytes)),
213 origin: None,
214 })
215 }
216 }
217
218 #[cfg(feature = "mmap")]
219 fn map_reader(reader: impl Read, limits: Limits) -> Result<Self, Diagnostic> {
220 let raw = map_checked(
221 reader,
222 OutputLimit::uncompressed(limits),
223 "input stream could not be read",
224 None,
225 )?;
226 #[cfg(any(feature = "gzip", feature = "zstd"))]
227 let compressed = u64::try_from(raw.as_bytes().len())
228 .map_err(|_| Limits::exceeded("compressed bytes", "more than u64::MAX"))?;
229 let bytes = match Compression::sniff(raw.as_bytes()) {
230 Compression::None => raw,
231 #[cfg(feature = "gzip")]
232 Compression::Gzip => map_gzip_reader(raw.as_bytes(), compressed, limits, None)?,
233 #[cfg(not(feature = "gzip"))]
234 Compression::Gzip => return Err(super::decode::unsupported("gzip")),
235 #[cfg(feature = "zstd")]
236 Compression::Zstd => map_zstd_reader(raw.as_bytes(), compressed, limits, None)?,
237 #[cfg(not(feature = "zstd"))]
238 Compression::Zstd => return Err(super::decode::unsupported("zstd")),
239 };
240 Ok(Self {
241 bytes: InputBytes::Mapped(Arc::new(bytes)),
242 origin: None,
243 })
244 }
245
246 #[must_use]
248 pub fn as_bytes(&self) -> &[u8] {
249 self.bytes.as_slice()
250 }
251
252 #[must_use]
254 pub fn len(&self) -> usize {
255 self.bytes.as_slice().len()
256 }
257
258 #[must_use]
260 pub fn is_empty(&self) -> bool {
261 self.bytes.as_slice().is_empty()
262 }
263
264 #[must_use]
266 pub const fn kind(&self) -> InputKind {
267 self.bytes.kind()
268 }
269
270 #[must_use]
272 pub fn origin(&self) -> Option<&str> {
273 self.origin.as_deref()
274 }
275
276 #[must_use]
278 pub fn with_origin(mut self, origin: &str) -> Self {
279 self.origin = Some(Arc::from(origin));
280 self
281 }
282}