diff --git a/src/archive/ba2.rs b/src/archive/ba2.rs index e2b56ef0..e9990802 100644 --- a/src/archive/ba2.rs +++ b/src/archive/ba2.rs @@ -28,10 +28,10 @@ impl TryFrom<[u8; HEADER_SIZE - TYPE_ID.len()]> for Header { fn try_from(value: [u8; HEADER_SIZE - TYPE_ID.len()]) -> Result { let header = Self { type_id: TYPE_ID, - version: to_u32(&value)?, + version: to_u32(&value, 0)?, archive_type: to_archive_type(&value)?, - file_count: to_u32(&value[8..])?, - file_paths_offset: to_u64(&value[12..])?, + file_count: to_u32(&value, 8)?, + file_paths_offset: to_u64(&value, 12)?, }; // The header version is 1, 7 or 8 for Fallout 4 and 2 or 3 for Starfield. @@ -140,8 +140,11 @@ fn trim_slashes(mut path_bytes: &[u8]) -> &[u8] { } fn rsplit_on(slice: &[u8], needle: u8) -> Option<(&[u8], &[u8])> { - let index = slice.iter().rposition(|b| *b == needle)?; - Some((&slice[..index], &slice[index + 1..])) + let mut iter = slice.rsplitn(2, |b| *b == needle); + let second = iter.next()?; + let first = iter.next()?; + + Some((first, second)) } fn hash(value: &T) -> u64 { diff --git a/src/archive/bsa.rs b/src/archive/bsa.rs index a4fc0e93..04c7d07e 100644 --- a/src/archive/bsa.rs +++ b/src/archive/bsa.rs @@ -30,14 +30,14 @@ impl TryFrom<[u8; HEADER_SIZE - TYPE_ID.len()]> for Header { fn try_from(value: [u8; HEADER_SIZE - TYPE_ID.len()]) -> Result { let header = Self { type_id: TYPE_ID, - version: to_u32(&value)?, - records_offset: to_u32(&value[4..])?, - archive_flags: to_u32(&value[8..])?, - folder_count: to_u32(&value[12..])?, - total_file_count: to_u32(&value[16..])?, - total_folder_names_length: to_u32(&value[20..])?, - total_file_names_length: to_u32(&value[24..])?, - content_type_flags: to_u32(&value[28..])?, + version: to_u32(&value, 0)?, + records_offset: to_u32(&value, 4)?, + archive_flags: to_u32(&value, 8)?, + folder_count: to_u32(&value, 12)?, + total_file_count: to_u32(&value, 16)?, + total_folder_names_length: to_u32(&value, 20)?, + total_file_names_length: to_u32(&value, 24)?, + content_type_flags: to_u32(&value, 28)?, }; if to_usize(header.records_offset) != HEADER_SIZE { @@ -80,9 +80,9 @@ mod v103 { } Ok(FolderRecord { - name_hash: to_u64(value)?, - file_count: to_u32(&value[8..])?, - file_records_offset: to_u32(&value[12..])?, + name_hash: to_u64(value, 0)?, + file_count: to_u32(value, 8)?, + file_records_offset: to_u32(value, 12)?, }) } } @@ -106,9 +106,9 @@ mod v105 { } Ok(FolderRecord { - name_hash: to_u64(value)?, - file_count: to_u32(&value[8..])?, - file_records_offset: to_u32(&value[16..])?, + name_hash: to_u64(value, 0)?, + file_count: to_u32(value, 8)?, + file_records_offset: to_u32(value, 16)?, }) } } @@ -197,7 +197,7 @@ fn read_assets_with_header( .chunks_exact(FILE_RECORD_SIZE) .take(to_usize(folder_record.file_count)) { - let file_hash = to_u64(file_chunk)?; + let file_hash = to_u64(file_chunk, 0)?; if !file_hashes.insert(file_hash) { return Err(ArchiveParsingError::HashCollision { diff --git a/src/archive/find.rs b/src/archive/find.rs index 4c80319c..03df9083 100644 --- a/src/archive/find.rs +++ b/src/archive/find.rs @@ -111,8 +111,7 @@ fn find_associated_archives_with_arbitrary_suffixes( return false; } - let Ok(filename) = std::str::from_utf8(&archive_filename.as_bytes()[..plugin_stem_len]) - else { + let Some(filename) = archive_filename.get(..plugin_stem_len) else { return false; }; diff --git a/src/archive/parse.rs b/src/archive/parse.rs index c4d1c6ae..c81249e0 100644 --- a/src/archive/parse.rs +++ b/src/archive/parse.rs @@ -94,20 +94,27 @@ fn get_assets_in_archive( } } -pub(super) fn to_u32(bytes: &[u8]) -> Result { +pub(super) fn to_u32(bytes: &[u8], start_index: usize) -> Result { const ARRAY_SIZE: usize = to_usize(u32::BITS >> 3); - - <[u8; ARRAY_SIZE]>::try_from(&bytes[..ARRAY_SIZE]) - .map(u32::from_le_bytes) - .map_err(|_e| slice_too_small(bytes, ARRAY_SIZE)) + subarray::(bytes, start_index).map(u32::from_le_bytes) } -pub(super) fn to_u64(bytes: &[u8]) -> Result { +pub(super) fn to_u64(bytes: &[u8], start_index: usize) -> Result { const ARRAY_SIZE: usize = to_usize(u64::BITS >> 3); + subarray::(bytes, start_index).map(u64::from_le_bytes) +} - <[u8; ARRAY_SIZE]>::try_from(&bytes[..ARRAY_SIZE]) - .map(u64::from_le_bytes) - .map_err(|_e| slice_too_small(bytes, ARRAY_SIZE)) +fn subarray( + bytes: &[u8], + start_index: usize, +) -> Result<[u8; SIZE], ArchiveParsingError> { + let stop_index = start_index + SIZE; + + let bytes = bytes + .get(start_index..stop_index) + .ok_or_else(|| slice_too_small(bytes, stop_index))?; + + <[u8; SIZE]>::try_from(bytes).map_err(|_e| slice_too_small(bytes, SIZE)) } #[expect( diff --git a/src/lib.rs b/src/lib.rs index d86a83c4..77e327a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,7 @@ clippy::get_unwrap, clippy::host_endian_bytes, clippy::if_then_some_else_none, - // clippy::indexing_slicing, + clippy::indexing_slicing, clippy::infinite_loop, clippy::integer_division, clippy::integer_division_remainder_used, @@ -101,6 +101,7 @@ test, allow( clippy::assertions_on_result_states, + clippy::indexing_slicing, clippy::missing_asserts_for_indexing, clippy::panic, clippy::unwrap_used, diff --git a/src/metadata/metadata_document.rs b/src/metadata/metadata_document.rs index 88074188..72705097 100644 --- a/src/metadata/metadata_document.rs +++ b/src/metadata/metadata_document.rs @@ -335,9 +335,8 @@ impl std::default::Default for MetadataDocument { } fn replace_prelude(masterlist: String, prelude: &str) -> String { - let line_ending = detect_line_ending(&masterlist); if let Some((start, end)) = split_on_prelude(&masterlist) { - let prelude = indent_prelude(prelude, line_ending); + let prelude = indent_prelude(prelude); format!("{start}{prelude}{end}") } else { @@ -345,20 +344,6 @@ fn replace_prelude(masterlist: String, prelude: &str) -> String { } } -fn detect_line_ending(masterlist: &str) -> &'static str { - if let Some(pos) = masterlist.rfind('\n') { - if pos == 0 { - "\n" - } else if masterlist.as_bytes()[pos - 1] == b'\r' { - "\r\n" - } else { - "\n" - } - } else { - "\n" - } -} - fn split_on_prelude(masterlist: &str) -> Option<(&str, &str)> { let (prefix, remainder) = split_on_prelude_start(masterlist)?; @@ -396,17 +381,17 @@ fn split_on_prelude_start(masterlist: &str) -> Option<(&str, &str)> { // two steps and there's always the risk of a bug being introduced // in the middle. if let Some((prefix, remainder)) = masterlist.split_at_checked(index) { - return Some((prefix, remainder)) + return Some((prefix, remainder)); } } None } - } -fn indent_prelude(prelude: &str, line_ending: &str) -> String { +fn indent_prelude(prelude: &str) -> String { let prelude = ("\n ".to_owned() + &prelude.replace('\n', "\n ")) - .replace(&format!(" {line_ending}"), line_ending); + .replace(" \r\n", "\r\n") + .replace(" \n", "\n"); if prelude.ends_with("\n ") { prelude.trim_end_matches(' ').to_owned() diff --git a/src/sorting/plugins.rs b/src/sorting/plugins.rs index 8417bb4b..7c4bf892 100644 --- a/src/sorting/plugins.rs +++ b/src/sorting/plugins.rs @@ -996,8 +996,10 @@ fn get_plugins_in_groups( let mut keys = plugins_in_groups.keys().collect::>(); keys.sort(); for key in keys { - let plugin_names: Vec<_> = plugins_in_groups[key] - .iter() + let plugin_names: Vec<_> = plugins_in_groups + .get(key) + .into_iter() + .flatten() .map(|i| format!("\"{}\"", graph[*i].name())) .collect(); logging::debug!("\t{}: {}", key, plugin_names.join(", ")); @@ -1087,13 +1089,25 @@ impl<'a, 'b, 'c, 'd, 'e, T: SortingPlugin> GroupsPathVisitor<'a, 'b, 'c, 'd, 'e, edge_stack_index: usize, target_plugins: &[PluginNodeIndex], ) { - let from_plugins = self.edge_stack[edge_stack_index].1; + let Some([from_edge, edges @ ..]) = self.edge_stack.get(edge_stack_index..) else { + if is_log_enabled(LogLevel::Error) { + logging::error!( + "Unexpected invalid edge stack index {} for edge stack {:?}", + edge_stack_index, + self.edge_stack + .iter() + .map(|e| e.0.weight()) + .collect::>() + ); + } + return; + }; - let path_involves_user_metadata = self.edge_stack[edge_stack_index..] - .iter() + let path_involves_user_metadata = std::iter::once(from_edge) + .chain(edges.iter()) .any(|p| *p.0.weight() == EdgeType::UserLoadAfter); - for from_plugin in from_plugins { + for from_plugin in from_edge.1 { self.add_edges_from_plugin(*from_plugin, target_plugins, path_involves_user_metadata); } } diff --git a/src/version.rs b/src/version.rs index a2a7f2b3..5965889e 100644 --- a/src/version.rs +++ b/src/version.rs @@ -32,6 +32,10 @@ pub fn is_compatible(major: u32, minor: u32, _patch: u32) -> bool { clippy::as_conversions, reason = "Can't const-convert the byte to a u32 a safer way" )] +#[expect( + clippy::indexing_slicing, + reason = "Can't const-convert the byte to a u8 a safer way" +)] const fn parse_u32(value: &str) -> u32 { let mut acc = 0; let mut i = 0;