1#![cfg_attr(not(any(feature = "std", test)), no_std)]
67#[cfg(feature = "alloc")]
68extern crate alloc;
69pub mod bin;
70#[cfg(feature = "yaml")]
71mod text;
72mod util;
73
74#[cfg(feature = "alloc")]
75use alloc::{
76 borrow::{Cow, ToOwned},
77 collections::BTreeMap,
78};
79#[cfg(not(feature = "alloc"))]
80pub use bin::ResTblReader;
81use thiserror_no_std::Error;
82use util::Name;
83
84pub type Result<T> = core::result::Result<T, Error>;
86
87#[derive(Debug, Error)]
89pub enum Error {
90 #[error("Insufficient data: found {0} bytes, expected {1}")]
91 InsufficientData(usize, &'static str),
92 #[error("Invalid magic: {0:?}, expected \"RESTBL\"")]
93 InvalidMagic([u8; 6]),
94 #[error("Invalid table size: {0}, expected {1}")]
95 InvalidTableSize(usize, usize),
96 #[error(transparent)]
97 Utf8Error(#[from] core::str::Utf8Error),
98 #[error("Buffer too small for output: found {0} bytes, requires at least {1}")]
99 InsufficientBuffer(usize, usize),
100 #[cfg(feature = "std")]
101 #[error(transparent)]
102 IoError(#[from] std::io::Error),
103 #[cfg(all(feature = "alloc", feature = "yaml"))]
104 #[error("Invalid YAML line: {0}")]
105 YamlError(alloc::string::String),
106 #[cfg(feature = "yaml")]
107 #[error("Invalid number in YAML line: {0}")]
108 YamlInvalidNumber(#[from] core::num::ParseIntError),
109}
110
111#[derive(Debug)]
114pub enum TableIndex<'a> {
115 HashIndex(u32),
116 #[cfg(feature = "alloc")]
117 StringIndex(Cow<'a, str>),
118 #[cfg(not(feature = "alloc"))]
119 StringIndex(&'a str),
120}
121
122impl From<u32> for TableIndex<'_> {
123 fn from(value: u32) -> Self {
124 TableIndex::HashIndex(value)
125 }
126}
127
128impl<'a> From<&'a str> for TableIndex<'a> {
129 fn from(value: &'a str) -> Self {
130 #[cfg(feature = "alloc")]
131 {
132 TableIndex::StringIndex(value.into())
133 }
134 #[cfg(not(feature = "alloc"))]
135 {
136 TableIndex::StringIndex(value)
137 }
138 }
139}
140
141impl<'a> From<&'a Name> for TableIndex<'a> {
142 fn from(value: &'a Name) -> Self {
143 #[cfg(feature = "alloc")]
144 {
145 TableIndex::StringIndex(Cow::Borrowed(value.as_str()))
146 }
147 #[cfg(not(feature = "alloc"))]
148 {
149 TableIndex::StringIndex(value.as_str())
150 }
151 }
152}
153
154#[cfg(feature = "alloc")]
155impl From<Name> for TableIndex<'_> {
156 fn from(value: Name) -> Self {
157 TableIndex::StringIndex(Cow::Owned(value.as_str().to_owned()))
158 }
159}
160
161#[cfg(feature = "alloc")]
162impl From<alloc::string::String> for TableIndex<'_> {
163 fn from(value: alloc::string::String) -> Self {
164 TableIndex::StringIndex(value.into())
165 }
166}
167
168#[cfg(feature = "alloc")]
173#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
174#[derive(Debug, Default, Clone, PartialEq)]
175pub struct ResourceSizeTable {
176 pub crc_table: BTreeMap<u32, u32>,
177 pub name_table: BTreeMap<Name, u32>,
178}
179
180#[cfg(feature = "alloc")]
181impl ResourceSizeTable {
182 pub fn new() -> Self {
184 Self::default()
185 }
186
187 pub fn from_parser(parser: &bin::ResTblReader<'_>) -> Self {
189 let mut crc_table = BTreeMap::new();
190 let mut name_table = BTreeMap::new();
191 for entry in parser.iter() {
192 match entry {
193 bin::TableEntry::Hash(entry) => crc_table.insert(entry.hash(), entry.value()),
194 bin::TableEntry::Name(entry) => name_table.insert(entry.name(), entry.value()),
195 };
196 }
197 ResourceSizeTable {
198 crc_table,
199 name_table,
200 }
201 }
202
203 #[inline(always)]
205 pub fn len(&self) -> usize {
206 self.crc_table.len() + self.name_table.len()
207 }
208
209 #[inline(always)]
210 pub fn is_empty(&self) -> bool {
211 self.len() == 0
212 }
213
214 pub fn contains<'i, I: Into<TableIndex<'i>>>(&self, needle: I) -> bool {
217 fn inner(tbl: &ResourceSizeTable, needle: TableIndex) -> bool {
218 match needle {
219 TableIndex::HashIndex(hash) => tbl.crc_table.contains_key(&hash),
220 TableIndex::StringIndex(name) => {
221 tbl.name_table.contains_key(&Name::from(name.as_ref())) || {
222 let hash = util::hash_name(&name);
223 tbl.crc_table.contains_key(&hash)
224 }
225 }
226 }
227 }
228 inner(self, needle.into())
229 }
230
231 pub fn get<'i, I: Into<TableIndex<'i>>>(&self, needle: I) -> Option<u32> {
235 fn inner(tbl: &ResourceSizeTable, needle: TableIndex) -> Option<u32> {
236 match needle {
237 TableIndex::HashIndex(hash) => tbl.crc_table.get(&hash),
238 TableIndex::StringIndex(name) => {
239 tbl.name_table.get(&Name::from(name.as_ref())).or_else(|| {
240 let hash = util::hash_name(&name);
241 tbl.crc_table.get(&hash)
242 })
243 }
244 }
245 .copied()
246 }
247 inner(self, needle.into())
248 }
249
250 pub fn get_mut<'i, I: Into<TableIndex<'i>>>(&mut self, needle: I) -> Option<&mut u32> {
254 fn inner<'a>(
255 tbl: &'a mut ResourceSizeTable,
256 needle: TableIndex<'_>,
257 ) -> Option<&'a mut u32> {
258 match needle {
259 TableIndex::HashIndex(hash) => tbl.crc_table.get_mut(&hash),
260 TableIndex::StringIndex(name) => tbl
261 .name_table
262 .get_mut(&Name::from(name.as_ref()))
263 .or_else(|| {
264 let hash = util::hash_name(&name);
265 tbl.crc_table.get_mut(&hash)
266 }),
267 }
268 }
269 inner(self, needle.into())
270 }
271
272 pub fn set<'i, I: Into<TableIndex<'i>>>(&mut self, res: I, value: u32) -> Option<u32> {
276 fn inner(tbl: &mut ResourceSizeTable, needle: TableIndex, value: u32) -> Option<u32> {
277 match needle {
278 TableIndex::HashIndex(hash) => tbl.crc_table.insert(hash, value),
279 TableIndex::StringIndex(name) => {
280 match tbl.name_table.entry(Name::from(name.as_ref())) {
281 alloc::collections::btree_map::Entry::Occupied(mut e) => {
282 Some(e.insert(value))
283 }
284 alloc::collections::btree_map::Entry::Vacant(_) => {
285 let hash = util::hash_name(&name);
286 tbl.crc_table.insert(hash, value)
287 }
288 }
289 }
290 }
291 }
292 inner(self, res.into(), value)
293 }
294
295 pub fn remove<'i, I: Into<TableIndex<'i>>>(&mut self, res: I) -> Option<u32> {
299 fn inner(tbl: &mut ResourceSizeTable, needle: TableIndex<'_>) -> Option<u32> {
300 match needle {
301 TableIndex::HashIndex(hash) => tbl.crc_table.remove(&hash),
302 TableIndex::StringIndex(name) => tbl
303 .name_table
304 .remove(&Name::from(name.as_ref()))
305 .or_else(|| {
306 let hash = util::hash_name(&name);
307 tbl.crc_table.remove(&hash)
308 }),
309 }
310 }
311 inner(self, res.into())
312 }
313
314 pub fn extend<'i, N: Into<TableIndex<'i>>, I: Iterator<Item = (N, u32)>>(&mut self, iter: I) {
316 fn inner<'i, I: Iterator<Item = (TableIndex<'i>, u32)>>(
317 tbl: &mut ResourceSizeTable,
318 iter: I,
319 ) {
320 for (k, v) in iter {
321 tbl.set(k, v);
322 }
323 }
324 inner(self, iter.map(|(k, v)| (k.into(), v)))
325 }
326}
327
328#[cfg(test)]
329mod test {
330 pub(crate) static DATA: &[u8] =
331 include_bytes!("../test/ResourceSizeTable.Product.110.rsizetable");
332}