Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 40 additions & 32 deletions sftool-lib/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ pub const ELF_MAGIC: &[u8] = &[0x7F, 0x45, 0x4C, 0x46]; // ELF file magic number

pub struct Utils;
impl Utils {
const HEX_SEGMENT_GAP_LIMIT: u32 = 0x1000;
const DEFAULT_HEX_SECTOR_SIZE: u32 = 0x1000;
const HEX_GAP_FILL_BYTE: u8 = 0xFF;

pub fn str_to_u32(s: &str) -> Result<u32> {
let s = s.trim();

Expand Down Expand Up @@ -230,21 +234,12 @@ impl Utils {
let absolute_address = current_base_address + offset as u32;

// Check if we need to start a new segment based on address continuity
let should_start_new_segment = if let Some(ref _temp_file) = current_temp_file {
let current_end_address = current_segment_start + current_file_offset;
let expected_start_address = absolute_address;

// If the new data is not continuous with existing data, start new segment
// Allow for some reasonable gap (e.g., 4KB) to be filled, but beyond that start new segment
let gap_size = if expected_start_address >= current_end_address {
expected_start_address - current_end_address
} else {
// Overlapping or backwards, definitely need new segment
u32::MAX
};

// If gap is too large (> 4KB), start new segment
gap_size > 0x1000
let should_start_new_segment = if current_temp_file.is_some() {
Self::should_start_new_hex_segment(
current_segment_start,
current_file_offset,
absolute_address,
)
} else {
false // No current file, will create one below
};
Expand Down Expand Up @@ -273,7 +268,7 @@ impl Utils {
// Fill gaps with 0xFF if they exist
if expected_file_offset > current_file_offset {
let gap_size = expected_file_offset - current_file_offset;
let fill_data = vec![0xFF; gap_size as usize];
let fill_data = vec![Self::HEX_GAP_FILL_BYTE; gap_size as usize];
temp_file.write_all(&fill_data)?;
current_file_offset = expected_file_offset;
}
Expand Down Expand Up @@ -350,21 +345,12 @@ impl Utils {
let absolute_address = current_base_address + offset as u32;

// Check if we need to start a new segment based on address continuity
let should_start_new_segment = if let Some(ref _temp_file) = current_temp_file {
let current_end_address = current_segment_start + current_file_offset;
let expected_start_address = absolute_address;

// If the new data is not continuous with existing data, start new segment
// Allow for some reasonable gap (e.g., 4KB) to be filled, but beyond that start new segment
let gap_size = if expected_start_address >= current_end_address {
expected_start_address - current_end_address
} else {
// Overlapping or backwards, definitely need new segment
u32::MAX
};

// If gap is too large (> 4KB), start new segment
gap_size > 0x1000
let should_start_new_segment = if current_temp_file.is_some() {
Self::should_start_new_hex_segment(
current_segment_start,
current_file_offset,
absolute_address,
)
} else {
false // No current file, will create one below
};
Expand Down Expand Up @@ -393,7 +379,7 @@ impl Utils {
// Fill gaps with 0xFF if they exist
if expected_file_offset > current_file_offset {
let gap_size = expected_file_offset - current_file_offset;
let fill_data = vec![0xFF; gap_size as usize];
let fill_data = vec![Self::HEX_GAP_FILL_BYTE; gap_size as usize];
temp_file.write_all(&fill_data)?;
current_file_offset = expected_file_offset;
}
Expand Down Expand Up @@ -521,6 +507,28 @@ impl Utils {
Ok(())
}

/// HEX段分段策略:
/// - 地址回退/重叠:分段
/// - 间隙 <= 4KB:不分段(以0xFF填充)
/// - 间隙 > 4KB:只有下一段起始地址为sector对齐时才分段
fn should_start_new_hex_segment(
current_segment_start: u32,
current_file_offset: u32,
next_address: u32,
) -> bool {
let current_end_address = current_segment_start.saturating_add(current_file_offset);
if next_address < current_end_address {
return true;
}

let gap_size = next_address - current_end_address;
if gap_size <= Self::HEX_SEGMENT_GAP_LIMIT {
return false;
}

next_address.is_multiple_of(Self::DEFAULT_HEX_SECTOR_SIZE)
}

/// 解析读取文件信息 (filename@address:size格式)
pub fn parse_read_file_info(file_spec: &str) -> Result<crate::ReadFlashFile> {
let Some((file_path, addr_size)) = file_spec.split_once('@') else {
Expand Down
54 changes: 54 additions & 0 deletions sftool-lib/tests/utils_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,33 @@ fn test_hex_non_continuous_segments_not_merged() {
assert_eq!(file_size_1, 4);
}

#[test]
fn test_hex_non_aligned_large_gap_segments_are_merged() {
// Test that a large-gap second segment is still merged when it is not sector aligned
// 0x1201FFF0 is not aligned to 4KB sector boundary.
let hex_content = ":020000041201E7\n:0400000001020304F2\n:04FFF0001122334463\n:00000001FF\n";

let mut temp_hex = NamedTempFile::new().unwrap();
temp_hex.write_all(hex_content.as_bytes()).unwrap();

let result = Utils::hex_to_write_flash_files(temp_hex.path()).unwrap();

// Should stay as a single segment because second block is not 4KB-aligned
assert_eq!(result.len(), 1);
assert_eq!(result[0].address, 0x12010000);

let file_size = result[0].file.metadata().unwrap().len() as usize;
assert_eq!(file_size, 0xFFF4);

let mut file_data = Vec::new();
let mut file = &result[0].file;
file.read_to_end(&mut file_data).unwrap();

assert_eq!(&file_data[0..4], &[0x01, 0x02, 0x03, 0x04]);
assert!(file_data[4..0xFFF0].iter().all(|&b| b == 0xFF));
assert_eq!(&file_data[0xFFF0..0xFFF4], &[0x11, 0x22, 0x33, 0x44]);
}

#[test]
fn test_hex_with_base_continuous_segments_merging() {
// Test continuous segment merging with base address override
Expand Down Expand Up @@ -459,6 +486,33 @@ fn test_hex_with_base_continuous_segments_merging() {
);
}

#[test]
fn test_hex_with_base_non_aligned_large_gap_segments_are_merged() {
// Test the same behavior via hex_with_base_to_write_flash_files path.
let hex_content = ":020000040801F1\n:0400000001020304F2\n:04FFF0001122334463\n:00000001FF\n";

let mut temp_hex = NamedTempFile::new().unwrap();
temp_hex.write_all(hex_content.as_bytes()).unwrap();

// 0x0801 will be replaced to 0x1201 when override is 0x12000000
let result =
Utils::hex_with_base_to_write_flash_files(temp_hex.path(), Some(0x12000000)).unwrap();

assert_eq!(result.len(), 1);
assert_eq!(result[0].address, 0x12010000);

let file_size = result[0].file.metadata().unwrap().len() as usize;
assert_eq!(file_size, 0xFFF4);

let mut file_data = Vec::new();
let mut file = &result[0].file;
file.read_to_end(&mut file_data).unwrap();

assert_eq!(&file_data[0..4], &[0x01, 0x02, 0x03, 0x04]);
assert!(file_data[4..0xFFF0].iter().all(|&b| b == 0xFF));
assert_eq!(&file_data[0xFFF0..0xFFF4], &[0x11, 0x22, 0x33, 0x44]);
}

#[test]
fn test_hex_continuous_with_gaps_still_merged() {
// Test that segments are merged even when there are internal gaps (filled with 0xFF)
Expand Down
Loading