1//! Interface for reading object files.
2//!
3//! ## Unified read API
4//!
5//! The [`Object`] trait provides a unified read API for accessing common features of
6//! object files, such as sections and symbols. There is an implementation of this
7//! trait for [`File`], which allows reading any file format, as well as implementations
8//! for each file format:
9//! [`ElfFile`](elf::ElfFile), [`MachOFile`](macho::MachOFile), [`CoffFile`](coff::CoffFile),
10//! [`PeFile`](pe::PeFile), [`WasmFile`](wasm::WasmFile), [`XcoffFile`](xcoff::XcoffFile).
11//!
12//! ## Low level read API
13//!
14//! The submodules for each file format define helpers that operate on the raw structs.
15//! These can be used instead of the unified API, or in conjunction with it to access
16//! details that are not available via the unified API.
17//!
18//! See the [submodules](#modules) for examples of the low level read API.
19//!
20//! ## Naming Convention
21//!
22//! Types that form part of the unified API for a file format are prefixed with the
23//! name of the file format.
24//!
25//! ## Example for unified read API
26//! ```no_run
27//! use object::{Object, ObjectSection};
28//! use std::error::Error;
29//! use std::fs;
30//!
31//! /// Reads a file and displays the name of each section.
32//! fn main() -> Result<(), Box<dyn Error>> {
33//! # #[cfg(feature = "std")] {
34//! let data = fs::read("path/to/binary")?;
35//! let file = object::File::parse(&*data)?;
36//! for section in file.sections() {
37//! println!("{}", section.name()?);
38//! }
39//! # }
40//! Ok(())
41//! }
42//! ```
43
44use alloc::borrow::Cow;
45use alloc::vec::Vec;
46use core::{fmt, result};
47
48use crate::common::*;
49
50mod read_ref;
51pub use read_ref::*;
52
53#[cfg(feature = "std")]
54mod read_cache;
55#[cfg(feature = "std")]
56pub use read_cache::*;
57
58mod util;
59pub use util::*;
60
61#[cfg(any(
62 feature = "coff",
63 feature = "elf",
64 feature = "macho",
65 feature = "pe",
66 feature = "wasm",
67 feature = "xcoff"
68))]
69mod any;
70#[cfg(any(
71 feature = "coff",
72 feature = "elf",
73 feature = "macho",
74 feature = "pe",
75 feature = "wasm",
76 feature = "xcoff"
77))]
78pub use any::*;
79
80#[cfg(feature = "archive")]
81pub mod archive;
82
83#[cfg(feature = "coff")]
84pub mod coff;
85
86#[cfg(feature = "elf")]
87pub mod elf;
88
89#[cfg(feature = "macho")]
90pub mod macho;
91
92#[cfg(feature = "pe")]
93pub mod pe;
94
95#[cfg(feature = "wasm")]
96pub mod wasm;
97
98#[cfg(feature = "xcoff")]
99pub mod xcoff;
100
101mod traits;
102pub use traits::*;
103
104mod private {
105 pub trait Sealed {}
106}
107
108/// The error type used within the read module.
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub struct Error(&'static str);
111
112impl fmt::Display for Error {
113 #[inline]
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 f.write_str(self.0)
116 }
117}
118
119#[cfg(feature = "std")]
120impl std::error::Error for Error {}
121
122/// The result type used within the read module.
123pub type Result<T> = result::Result<T, Error>;
124
125trait ReadError<T> {
126 fn read_error(self, error: &'static str) -> Result<T>;
127}
128
129impl<T> ReadError<T> for result::Result<T, ()> {
130 fn read_error(self, error: &'static str) -> Result<T> {
131 self.map_err(|()| Error(error))
132 }
133}
134
135impl<T> ReadError<T> for result::Result<T, Error> {
136 fn read_error(self, error: &'static str) -> Result<T> {
137 self.map_err(|_| Error(error))
138 }
139}
140
141impl<T> ReadError<T> for Option<T> {
142 fn read_error(self, error: &'static str) -> Result<T> {
143 self.ok_or(err:Error(error))
144 }
145}
146
147/// The native executable file for the target platform.
148#[cfg(all(
149 unix,
150 not(target_os = "macos"),
151 target_pointer_width = "32",
152 feature = "elf"
153))]
154pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile32<'data, crate::Endianness, R>;
155
156/// The native executable file for the target platform.
157#[cfg(all(
158 unix,
159 not(target_os = "macos"),
160 target_pointer_width = "64",
161 feature = "elf"
162))]
163pub type NativeFile<'data, R = &'data [u8]> = elf::ElfFile64<'data, crate::Endianness, R>;
164
165/// The native executable file for the target platform.
166#[cfg(all(target_os = "macos", target_pointer_width = "32", feature = "macho"))]
167pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile32<'data, crate::Endianness, R>;
168
169/// The native executable file for the target platform.
170#[cfg(all(target_os = "macos", target_pointer_width = "64", feature = "macho"))]
171pub type NativeFile<'data, R = &'data [u8]> = macho::MachOFile64<'data, crate::Endianness, R>;
172
173/// The native executable file for the target platform.
174#[cfg(all(target_os = "windows", target_pointer_width = "32", feature = "pe"))]
175pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile32<'data, R>;
176
177/// The native executable file for the target platform.
178#[cfg(all(target_os = "windows", target_pointer_width = "64", feature = "pe"))]
179pub type NativeFile<'data, R = &'data [u8]> = pe::PeFile64<'data, R>;
180
181/// The native executable file for the target platform.
182#[cfg(all(feature = "wasm", target_arch = "wasm32", feature = "wasm"))]
183pub type NativeFile<'data, R = &'data [u8]> = wasm::WasmFile<'data, R>;
184
185/// A file format kind.
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
187#[non_exhaustive]
188pub enum FileKind {
189 /// A Unix archive.
190 ///
191 /// See [`archive::ArchiveFile`].
192 #[cfg(feature = "archive")]
193 Archive,
194 /// A COFF object file.
195 ///
196 /// See [`coff::CoffFile`].
197 #[cfg(feature = "coff")]
198 Coff,
199 /// A COFF bigobj object file.
200 ///
201 /// This supports a larger number of sections.
202 ///
203 /// See [`coff::CoffBigFile`].
204 #[cfg(feature = "coff")]
205 CoffBig,
206 /// A Windows short import file.
207 ///
208 /// See [`coff::ImportFile`].
209 #[cfg(feature = "coff")]
210 CoffImport,
211 /// A dyld cache file containing Mach-O images.
212 ///
213 /// See [`macho::DyldCache`]
214 #[cfg(feature = "macho")]
215 DyldCache,
216 /// A 32-bit ELF file.
217 ///
218 /// See [`elf::ElfFile32`].
219 #[cfg(feature = "elf")]
220 Elf32,
221 /// A 64-bit ELF file.
222 ///
223 /// See [`elf::ElfFile64`].
224 #[cfg(feature = "elf")]
225 Elf64,
226 /// A 32-bit Mach-O file.
227 ///
228 /// See [`macho::MachOFile32`].
229 #[cfg(feature = "macho")]
230 MachO32,
231 /// A 64-bit Mach-O file.
232 ///
233 /// See [`macho::MachOFile64`].
234 #[cfg(feature = "macho")]
235 MachO64,
236 /// A 32-bit Mach-O fat binary.
237 ///
238 /// See [`macho::FatHeader::parse_arch32`].
239 #[cfg(feature = "macho")]
240 MachOFat32,
241 /// A 64-bit Mach-O fat binary.
242 ///
243 /// See [`macho::FatHeader::parse_arch64`].
244 #[cfg(feature = "macho")]
245 MachOFat64,
246 /// A 32-bit PE file.
247 ///
248 /// See [`pe::PeFile32`].
249 #[cfg(feature = "pe")]
250 Pe32,
251 /// A 64-bit PE file.
252 ///
253 /// See [`pe::PeFile64`].
254 #[cfg(feature = "pe")]
255 Pe64,
256 /// A Wasm file.
257 ///
258 /// See [`wasm::WasmFile`].
259 #[cfg(feature = "wasm")]
260 Wasm,
261 /// A 32-bit XCOFF file.
262 ///
263 /// See [`xcoff::XcoffFile32`].
264 #[cfg(feature = "xcoff")]
265 Xcoff32,
266 /// A 64-bit XCOFF file.
267 ///
268 /// See [`xcoff::XcoffFile64`].
269 #[cfg(feature = "xcoff")]
270 Xcoff64,
271}
272
273impl FileKind {
274 /// Determine a file kind by parsing the start of the file.
275 pub fn parse<'data, R: ReadRef<'data>>(data: R) -> Result<FileKind> {
276 Self::parse_at(data, 0)
277 }
278
279 /// Determine a file kind by parsing at the given offset.
280 pub fn parse_at<'data, R: ReadRef<'data>>(data: R, offset: u64) -> Result<FileKind> {
281 let magic = data
282 .read_bytes_at(offset, 16)
283 .read_error("Could not read file magic")?;
284 if magic.len() < 16 {
285 return Err(Error("File too short"));
286 }
287
288 let kind = match [magic[0], magic[1], magic[2], magic[3], magic[4], magic[5], magic[6], magic[7]] {
289 #[cfg(feature = "archive")]
290 [b'!', b'<', b'a', b'r', b'c', b'h', b'>', b'\n'] => FileKind::Archive,
291 #[cfg(feature = "macho")]
292 [b'd', b'y', b'l', b'd', b'_', b'v', b'1', b' '] => FileKind::DyldCache,
293 #[cfg(feature = "elf")]
294 [0x7f, b'E', b'L', b'F', 1, ..] => FileKind::Elf32,
295 #[cfg(feature = "elf")]
296 [0x7f, b'E', b'L', b'F', 2, ..] => FileKind::Elf64,
297 #[cfg(feature = "macho")]
298 [0xfe, 0xed, 0xfa, 0xce, ..]
299 | [0xce, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO32,
300 #[cfg(feature = "macho")]
301 | [0xfe, 0xed, 0xfa, 0xcf, ..]
302 | [0xcf, 0xfa, 0xed, 0xfe, ..] => FileKind::MachO64,
303 #[cfg(feature = "macho")]
304 [0xca, 0xfe, 0xba, 0xbe, ..] => FileKind::MachOFat32,
305 #[cfg(feature = "macho")]
306 [0xca, 0xfe, 0xba, 0xbf, ..] => FileKind::MachOFat64,
307 #[cfg(feature = "wasm")]
308 [0x00, b'a', b's', b'm', ..] => FileKind::Wasm,
309 #[cfg(feature = "pe")]
310 [b'M', b'Z', ..] if offset == 0 => {
311 // offset == 0 restriction is because optional_header_magic only looks at offset 0
312 match pe::optional_header_magic(data) {
313 Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR32_MAGIC) => {
314 FileKind::Pe32
315 }
316 Ok(crate::pe::IMAGE_NT_OPTIONAL_HDR64_MAGIC) => {
317 FileKind::Pe64
318 }
319 _ => return Err(Error("Unknown MS-DOS file")),
320 }
321 }
322 // TODO: more COFF machines
323 #[cfg(feature = "coff")]
324 // COFF arm
325 [0xc4, 0x01, ..]
326 // COFF arm64
327 | [0x64, 0xaa, ..]
328 // COFF arm64ec
329 | [0x41, 0xa6, ..]
330 // COFF x86
331 | [0x4c, 0x01, ..]
332 // COFF x86-64
333 | [0x64, 0x86, ..] => FileKind::Coff,
334 #[cfg(feature = "coff")]
335 [0x00, 0x00, 0xff, 0xff, 0x00, 0x00, ..] => FileKind::CoffImport,
336 #[cfg(feature = "coff")]
337 [0x00, 0x00, 0xff, 0xff, 0x02, 0x00, ..] if offset == 0 => {
338 // offset == 0 restriction is because anon_object_class_id only looks at offset 0
339 match coff::anon_object_class_id(data) {
340 Ok(crate::pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID) => FileKind::CoffBig,
341 _ => return Err(Error("Unknown anon object file")),
342 }
343 }
344 #[cfg(feature = "xcoff")]
345 [0x01, 0xdf, ..] => FileKind::Xcoff32,
346 #[cfg(feature = "xcoff")]
347 [0x01, 0xf7, ..] => FileKind::Xcoff64,
348 _ => return Err(Error("Unknown file magic")),
349 };
350 Ok(kind)
351 }
352}
353
354/// An object kind.
355///
356/// Returned by [`Object::kind`].
357#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
358#[non_exhaustive]
359pub enum ObjectKind {
360 /// The object kind is unknown.
361 Unknown,
362 /// Relocatable object.
363 Relocatable,
364 /// Executable.
365 Executable,
366 /// Dynamic shared object.
367 Dynamic,
368 /// Core.
369 Core,
370}
371
372/// The index used to identify a section in a file.
373#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
374pub struct SectionIndex(pub usize);
375
376/// The index used to identify a symbol in a symbol table.
377#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
378pub struct SymbolIndex(pub usize);
379
380/// The section where an [`ObjectSymbol`] is defined.
381#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
382#[non_exhaustive]
383pub enum SymbolSection {
384 /// The section is unknown.
385 Unknown,
386 /// The section is not applicable for this symbol (such as file symbols).
387 None,
388 /// The symbol is undefined.
389 Undefined,
390 /// The symbol has an absolute value.
391 Absolute,
392 /// The symbol is a zero-initialized symbol that will be combined with duplicate definitions.
393 Common,
394 /// The symbol is defined in the given section.
395 Section(SectionIndex),
396}
397
398impl SymbolSection {
399 /// Returns the section index for the section where the symbol is defined.
400 ///
401 /// May return `None` if the symbol is not defined in a section.
402 #[inline]
403 pub fn index(self) -> Option<SectionIndex> {
404 if let SymbolSection::Section(index: SectionIndex) = self {
405 Some(index)
406 } else {
407 None
408 }
409 }
410}
411
412/// An entry in a [`SymbolMap`].
413pub trait SymbolMapEntry {
414 /// The symbol address.
415 fn address(&self) -> u64;
416}
417
418/// A map from addresses to symbol information.
419///
420/// The symbol information depends on the chosen entry type, such as [`SymbolMapName`].
421///
422/// Returned by [`Object::symbol_map`].
423#[derive(Debug, Default, Clone)]
424pub struct SymbolMap<T: SymbolMapEntry> {
425 symbols: Vec<T>,
426}
427
428impl<T: SymbolMapEntry> SymbolMap<T> {
429 /// Construct a new symbol map.
430 ///
431 /// This function will sort the symbols by address.
432 pub fn new(mut symbols: Vec<T>) -> Self {
433 symbols.sort_by_key(|s| s.address());
434 SymbolMap { symbols }
435 }
436
437 /// Get the symbol before the given address.
438 pub fn get(&self, address: u64) -> Option<&T> {
439 let index = match self
440 .symbols
441 .binary_search_by_key(&address, |symbol| symbol.address())
442 {
443 Ok(index) => index,
444 Err(index) => index.checked_sub(1)?,
445 };
446 self.symbols.get(index)
447 }
448
449 /// Get all symbols in the map.
450 #[inline]
451 pub fn symbols(&self) -> &[T] {
452 &self.symbols
453 }
454}
455
456/// The type used for entries in a [`SymbolMap`] that maps from addresses to names.
457#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
458pub struct SymbolMapName<'data> {
459 address: u64,
460 name: &'data str,
461}
462
463impl<'data> SymbolMapName<'data> {
464 /// Construct a `SymbolMapName`.
465 pub fn new(address: u64, name: &'data str) -> Self {
466 SymbolMapName { address, name }
467 }
468
469 /// The symbol address.
470 #[inline]
471 pub fn address(&self) -> u64 {
472 self.address
473 }
474
475 /// The symbol name.
476 #[inline]
477 pub fn name(&self) -> &'data str {
478 self.name
479 }
480}
481
482impl<'data> SymbolMapEntry for SymbolMapName<'data> {
483 #[inline]
484 fn address(&self) -> u64 {
485 self.address
486 }
487}
488
489/// A map from addresses to symbol names and object files.
490///
491/// This is derived from STAB entries in Mach-O files.
492///
493/// Returned by [`Object::object_map`].
494#[derive(Debug, Default, Clone)]
495pub struct ObjectMap<'data> {
496 symbols: SymbolMap<ObjectMapEntry<'data>>,
497 objects: Vec<&'data [u8]>,
498}
499
500impl<'data> ObjectMap<'data> {
501 /// Get the entry containing the given address.
502 pub fn get(&self, address: u64) -> Option<&ObjectMapEntry<'data>> {
503 self.symbols
504 .get(address)
505 .filter(|entry: &&ObjectMapEntry<'_>| entry.size == 0 || address.wrapping_sub(entry.address) < entry.size)
506 }
507
508 /// Get all symbols in the map.
509 #[inline]
510 pub fn symbols(&self) -> &[ObjectMapEntry<'data>] {
511 self.symbols.symbols()
512 }
513
514 /// Get all objects in the map.
515 #[inline]
516 pub fn objects(&self) -> &[&'data [u8]] {
517 &self.objects
518 }
519}
520
521/// An [`ObjectMap`] entry.
522#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
523pub struct ObjectMapEntry<'data> {
524 address: u64,
525 size: u64,
526 name: &'data [u8],
527 object: usize,
528}
529
530impl<'data> ObjectMapEntry<'data> {
531 /// Get the symbol address.
532 #[inline]
533 pub fn address(&self) -> u64 {
534 self.address
535 }
536
537 /// Get the symbol size.
538 ///
539 /// This may be 0 if the size is unknown.
540 #[inline]
541 pub fn size(&self) -> u64 {
542 self.size
543 }
544
545 /// Get the symbol name.
546 #[inline]
547 pub fn name(&self) -> &'data [u8] {
548 self.name
549 }
550
551 /// Get the index of the object file name.
552 #[inline]
553 pub fn object_index(&self) -> usize {
554 self.object
555 }
556
557 /// Get the object file name.
558 #[inline]
559 pub fn object(&self, map: &ObjectMap<'data>) -> &'data [u8] {
560 map.objects[self.object]
561 }
562}
563
564impl<'data> SymbolMapEntry for ObjectMapEntry<'data> {
565 #[inline]
566 fn address(&self) -> u64 {
567 self.address
568 }
569}
570
571/// An imported symbol.
572///
573/// Returned by [`Object::imports`].
574#[derive(Debug, Clone, Copy, PartialEq, Eq)]
575pub struct Import<'data> {
576 library: ByteString<'data>,
577 // TODO: or ordinal
578 name: ByteString<'data>,
579}
580
581impl<'data> Import<'data> {
582 /// The symbol name.
583 #[inline]
584 pub fn name(&self) -> &'data [u8] {
585 self.name.0
586 }
587
588 /// The name of the library to import the symbol from.
589 #[inline]
590 pub fn library(&self) -> &'data [u8] {
591 self.library.0
592 }
593}
594
595/// An exported symbol.
596///
597/// Returned by [`Object::exports`].
598#[derive(Debug, Clone, Copy, PartialEq, Eq)]
599pub struct Export<'data> {
600 // TODO: and ordinal?
601 name: ByteString<'data>,
602 address: u64,
603}
604
605impl<'data> Export<'data> {
606 /// The symbol name.
607 #[inline]
608 pub fn name(&self) -> &'data [u8] {
609 self.name.0
610 }
611
612 /// The virtual address of the symbol.
613 #[inline]
614 pub fn address(&self) -> u64 {
615 self.address
616 }
617}
618
619/// PDB information from the debug directory in a PE file.
620#[derive(Debug, Clone, Copy, PartialEq, Eq)]
621pub struct CodeView<'data> {
622 guid: [u8; 16],
623 path: ByteString<'data>,
624 age: u32,
625}
626
627impl<'data> CodeView<'data> {
628 /// The path to the PDB as stored in CodeView.
629 #[inline]
630 pub fn path(&self) -> &'data [u8] {
631 self.path.0
632 }
633
634 /// The age of the PDB.
635 #[inline]
636 pub fn age(&self) -> u32 {
637 self.age
638 }
639
640 /// The GUID of the PDB.
641 #[inline]
642 pub fn guid(&self) -> [u8; 16] {
643 self.guid
644 }
645}
646
647/// The target referenced by a [`Relocation`].
648#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
649#[non_exhaustive]
650pub enum RelocationTarget {
651 /// The target is a symbol.
652 Symbol(SymbolIndex),
653 /// The target is a section.
654 Section(SectionIndex),
655 /// The offset is an absolute address.
656 Absolute,
657}
658
659/// A relocation entry.
660///
661/// Returned by [`Object::dynamic_relocations`] or [`ObjectSection::relocations`].
662#[derive(Debug)]
663pub struct Relocation {
664 kind: RelocationKind,
665 encoding: RelocationEncoding,
666 size: u8,
667 target: RelocationTarget,
668 addend: i64,
669 implicit_addend: bool,
670}
671
672impl Relocation {
673 /// The operation used to calculate the result of the relocation.
674 #[inline]
675 pub fn kind(&self) -> RelocationKind {
676 self.kind
677 }
678
679 /// Information about how the result of the relocation operation is encoded in the place.
680 #[inline]
681 pub fn encoding(&self) -> RelocationEncoding {
682 self.encoding
683 }
684
685 /// The size in bits of the place of the relocation.
686 ///
687 /// If 0, then the size is determined by the relocation kind.
688 #[inline]
689 pub fn size(&self) -> u8 {
690 self.size
691 }
692
693 /// The target of the relocation.
694 #[inline]
695 pub fn target(&self) -> RelocationTarget {
696 self.target
697 }
698
699 /// The addend to use in the relocation calculation.
700 #[inline]
701 pub fn addend(&self) -> i64 {
702 self.addend
703 }
704
705 /// Set the addend to use in the relocation calculation.
706 #[inline]
707 pub fn set_addend(&mut self, addend: i64) {
708 self.addend = addend
709 }
710
711 /// Returns true if there is an implicit addend stored in the data at the offset
712 /// to be relocated.
713 #[inline]
714 pub fn has_implicit_addend(&self) -> bool {
715 self.implicit_addend
716 }
717}
718
719/// A data compression format.
720#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
721#[non_exhaustive]
722pub enum CompressionFormat {
723 /// The data is uncompressed.
724 None,
725 /// The data is compressed, but the compression format is unknown.
726 Unknown,
727 /// ZLIB/DEFLATE.
728 ///
729 /// Used for ELF compression and GNU compressed debug information.
730 Zlib,
731 /// Zstandard.
732 ///
733 /// Used for ELF compression.
734 Zstandard,
735}
736
737/// A range in a file that may be compressed.
738///
739/// Returned by [`ObjectSection::compressed_file_range`].
740#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
741pub struct CompressedFileRange {
742 /// The data compression format.
743 pub format: CompressionFormat,
744 /// The file offset of the compressed data.
745 pub offset: u64,
746 /// The compressed data size.
747 pub compressed_size: u64,
748 /// The uncompressed data size.
749 pub uncompressed_size: u64,
750}
751
752impl CompressedFileRange {
753 /// Data that is uncompressed.
754 #[inline]
755 pub fn none(range: Option<(u64, u64)>) -> Self {
756 if let Some((offset, size)) = range {
757 CompressedFileRange {
758 format: CompressionFormat::None,
759 offset,
760 compressed_size: size,
761 uncompressed_size: size,
762 }
763 } else {
764 CompressedFileRange {
765 format: CompressionFormat::None,
766 offset: 0,
767 compressed_size: 0,
768 uncompressed_size: 0,
769 }
770 }
771 }
772
773 /// Convert to [`CompressedData`] by reading from the file.
774 pub fn data<'data, R: ReadRef<'data>>(self, file: R) -> Result<CompressedData<'data>> {
775 let data = file
776 .read_bytes_at(self.offset, self.compressed_size)
777 .read_error("Invalid compressed data size or offset")?;
778 Ok(CompressedData {
779 format: self.format,
780 data,
781 uncompressed_size: self.uncompressed_size,
782 })
783 }
784}
785
786/// Data that may be compressed.
787///
788/// Returned by [`ObjectSection::compressed_data`].
789#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
790pub struct CompressedData<'data> {
791 /// The data compression format.
792 pub format: CompressionFormat,
793 /// The compressed data.
794 pub data: &'data [u8],
795 /// The uncompressed data size.
796 pub uncompressed_size: u64,
797}
798
799impl<'data> CompressedData<'data> {
800 /// Data that is uncompressed.
801 #[inline]
802 pub fn none(data: &'data [u8]) -> Self {
803 CompressedData {
804 format: CompressionFormat::None,
805 data,
806 uncompressed_size: data.len() as u64,
807 }
808 }
809
810 /// Return the uncompressed data.
811 ///
812 /// Returns an error for invalid data or unsupported compression.
813 /// This includes if the data is compressed but the `compression` feature
814 /// for this crate is disabled.
815 pub fn decompress(self) -> Result<Cow<'data, [u8]>> {
816 match self.format {
817 CompressionFormat::None => Ok(Cow::Borrowed(self.data)),
818 #[cfg(feature = "compression")]
819 CompressionFormat::Zlib => {
820 use core::convert::TryInto;
821 let size = self
822 .uncompressed_size
823 .try_into()
824 .ok()
825 .read_error("Uncompressed data size is too large.")?;
826 let mut decompressed = Vec::with_capacity(size);
827 let mut decompress = flate2::Decompress::new(true);
828 decompress
829 .decompress_vec(
830 self.data,
831 &mut decompressed,
832 flate2::FlushDecompress::Finish,
833 )
834 .ok()
835 .read_error("Invalid zlib compressed data")?;
836 Ok(Cow::Owned(decompressed))
837 }
838 #[cfg(feature = "compression")]
839 CompressionFormat::Zstandard => {
840 use core::convert::TryInto;
841 use std::io::Read;
842 let size = self
843 .uncompressed_size
844 .try_into()
845 .ok()
846 .read_error("Uncompressed data size is too large.")?;
847 let mut decompressed = Vec::with_capacity(size);
848 let mut decoder = ruzstd::StreamingDecoder::new(self.data)
849 .ok()
850 .read_error("Invalid zstd compressed data")?;
851 decoder
852 .read_to_end(&mut decompressed)
853 .ok()
854 .read_error("Invalid zstd compressed data")?;
855 Ok(Cow::Owned(decompressed))
856 }
857 _ => Err(Error("Unsupported compressed data.")),
858 }
859 }
860}
861