1use std::io::{self, Read, Write};
41
42pub const FOOTER_SIZE: usize = 76;
43pub const MAGIC: [u8; 8] = *b"ONELF\x00\x01\x00";
44pub const END_MAGIC: [u8; 8] = *b"FLENONE\x00";
45
46bitflags! {
47 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
48 pub struct Flags: u16 {
49 const HAS_DICT = 1 << 0;
50 const MEMFD_HINT = 1 << 1;
51 const SHARUN_COMPAT = 1 << 2;
52 const STORED = 1 << 3;
56 }
57}
58
59#[derive(Debug, Clone)]
60pub struct Footer {
61 pub format_version: u16,
63 pub flags: Flags,
65 pub manifest_offset: u64,
67 pub manifest_compressed: u64,
69 pub manifest_original: u64,
71 pub payload_offset: u64,
73 pub payload_size: u64,
75 pub dict_offset: u64,
77 pub dict_size: u32,
79 pub manifest_checksum: [u8; 4],
81}
82
83impl Footer {
84 pub fn is_stored(&self) -> bool {
87 self.flags.contains(Flags::STORED)
88 }
89
90 pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
91 w.write_all(&MAGIC)?; w.write_all(&self.format_version.to_le_bytes())?; w.write_all(&self.flags.bits().to_le_bytes())?; w.write_all(&self.manifest_offset.to_le_bytes())?; w.write_all(&self.manifest_compressed.to_le_bytes())?; w.write_all(&self.manifest_original.to_le_bytes())?; w.write_all(&self.payload_offset.to_le_bytes())?; w.write_all(&self.payload_size.to_le_bytes())?; w.write_all(&self.dict_offset.to_le_bytes())?; w.write_all(&self.dict_size.to_le_bytes())?; w.write_all(&self.manifest_checksum)?; w.write_all(&END_MAGIC)?; Ok(()) }
105
106 pub fn read_from<R: Read>(r: &mut R) -> io::Result<Self> {
107 let mut buf = [0u8; FOOTER_SIZE];
108 r.read_exact(&mut buf)?;
109 Self::from_bytes(&buf)
110 }
111
112 pub fn from_bytes(buf: &[u8; FOOTER_SIZE]) -> io::Result<Self> {
113 if &buf[0..8] != &MAGIC {
114 return Err(io::Error::new(
115 io::ErrorKind::InvalidData,
116 "invalid onelf magic",
117 ));
118 }
119 if &buf[68..76] != &END_MAGIC {
120 return Err(io::Error::new(
121 io::ErrorKind::InvalidData,
122 "invalid onelf end magic",
123 ));
124 }
125
126 let format_version = u16::from_le_bytes(buf[8..10].try_into().unwrap());
127 if format_version != 1 {
128 return Err(io::Error::new(
129 io::ErrorKind::InvalidData,
130 format!("unsupported format version: {}", format_version),
131 ));
132 }
133
134 let flags_raw = u16::from_le_bytes(buf[10..12].try_into().unwrap());
135 let flags = Flags::from_bits_truncate(flags_raw);
136
137 Ok(Footer {
138 format_version,
139 flags,
140 manifest_offset: u64::from_le_bytes(buf[12..20].try_into().unwrap()),
141 manifest_compressed: u64::from_le_bytes(buf[20..28].try_into().unwrap()),
142 manifest_original: u64::from_le_bytes(buf[28..36].try_into().unwrap()),
143 payload_offset: u64::from_le_bytes(buf[36..44].try_into().unwrap()),
144 payload_size: u64::from_le_bytes(buf[44..52].try_into().unwrap()),
145 dict_offset: u64::from_le_bytes(buf[52..60].try_into().unwrap()),
146 dict_size: u32::from_le_bytes(buf[60..64].try_into().unwrap()),
147 manifest_checksum: buf[64..68].try_into().unwrap(),
148 })
149 }
150}