use crate::io::dwg::crc::{crc16, CRC16_SEED};
const MAX_CHUNK_TOTAL: usize = 2032;
pub fn write_handles(
handle_map: &[(u64, i64)],
section_offset: i32,
) -> Vec<u8> {
let mut sorted: Vec<(u64, i64)> = handle_map.to_vec();
sorted.sort_by_key(|&(h, _)| h);
let mut output = Vec::with_capacity(sorted.len() * 4 + 64);
let mut prev_handle: u64 = 0;
let mut prev_loc: i64 = 0;
let mut chunk_start = output.len();
output.push(0);
output.push(0);
let mut handle_buf = [0u8; 10];
let mut loc_buf = [0u8; 5];
for &(handle, position) in &sorted {
let handle_delta = handle - prev_handle;
let loc = position + section_offset as i64;
let loc_delta = loc - prev_loc;
let handle_size = encode_modular_short_unsigned(handle_delta, &mut handle_buf);
let loc_size = encode_modular_short_signed(loc_delta as i32, &mut loc_buf);
let chunk_total_len = output.len() - chunk_start; if chunk_total_len + handle_size + loc_size > MAX_CHUNK_TOTAL {
finalize_chunk(&mut output, chunk_start);
chunk_start = output.len();
output.push(0);
output.push(0);
let handle_delta = handle;
let loc = position + section_offset as i64;
let loc_delta = loc;
let handle_size = encode_modular_short_unsigned(handle_delta, &mut handle_buf);
let loc_size = encode_modular_short_signed(loc_delta as i32, &mut loc_buf);
output.extend_from_slice(&handle_buf[..handle_size]);
output.extend_from_slice(&loc_buf[..loc_size]);
} else {
output.extend_from_slice(&handle_buf[..handle_size]);
output.extend_from_slice(&loc_buf[..loc_size]);
}
prev_handle = handle;
prev_loc = loc;
}
finalize_chunk(&mut output, chunk_start);
let term_start = output.len();
output.push(0);
output.push(0);
finalize_chunk(&mut output, term_start);
output
}
fn finalize_chunk(output: &mut Vec<u8>, chunk_start: usize) {
let chunk_len = (output.len() - chunk_start) as u16;
output[chunk_start] = (chunk_len >> 8) as u8;
output[chunk_start + 1] = (chunk_len & 0xFF) as u8;
let crc = crc16(CRC16_SEED, &output[chunk_start..]);
output.push((crc >> 8) as u8);
output.push((crc & 0xFF) as u8);
}
fn encode_modular_short_unsigned(mut value: u64, buf: &mut [u8]) -> usize {
let mut i = 0;
while value >= 0x80 {
buf[i] = (value & 0x7F) as u8 | 0x80;
i += 1;
value >>= 7;
}
buf[i] = value as u8;
i + 1
}
fn encode_modular_short_signed(value: i32, buf: &mut [u8]) -> usize {
let mut i = 0;
if value < 0 {
let mut v = -value;
while v >= 64 {
buf[i] = (v as u8 & 0x7F) | 0x80;
i += 1;
v >>= 7;
}
buf[i] = v as u8 | 0x40;
i + 1
} else {
let mut v = value;
while v >= 64 {
buf[i] = (v as u8 & 0x7F) | 0x80;
i += 1;
v >>= 7;
}
buf[i] = v as u8;
i + 1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_modular_unsigned_small() {
let mut buf = [0u8; 10];
let n = encode_modular_short_unsigned(42, &mut buf);
assert_eq!(n, 1);
assert_eq!(buf[0], 42);
}
#[test]
fn test_encode_modular_unsigned_two_bytes() {
let mut buf = [0u8; 10];
let n = encode_modular_short_unsigned(200, &mut buf);
assert_eq!(n, 2);
assert_eq!(buf[0], (200 & 0x7F) as u8 | 0x80); assert_eq!(buf[1], (200 >> 7) as u8); }
#[test]
fn test_encode_modular_signed_positive() {
let mut buf = [0u8; 5];
let n = encode_modular_short_signed(10, &mut buf);
assert_eq!(n, 1);
assert_eq!(buf[0], 10);
}
#[test]
fn test_encode_modular_signed_negative() {
let mut buf = [0u8; 5];
let n = encode_modular_short_signed(-10, &mut buf);
assert_eq!(n, 1);
assert_eq!(buf[0], 10 | 0x40);
}
#[test]
fn test_encode_modular_signed_zero() {
let mut buf = [0u8; 5];
let n = encode_modular_short_signed(0, &mut buf);
assert_eq!(n, 1);
assert_eq!(buf[0], 0);
}
#[test]
fn test_write_handles_empty() {
let data = write_handles(&[], 0);
assert_eq!(data.len(), 8);
let term_start = data.len() - 4;
let term_size = u16::from_be_bytes([data[term_start], data[term_start + 1]]);
assert_eq!(term_size, 2); }
#[test]
fn test_write_handles_single_entry() {
let map = vec![(1u64, 100i64)];
let data = write_handles(&map, 0);
assert!(data.len() > 8);
let chunk_size = u16::from_be_bytes([data[0], data[1]]);
assert!(chunk_size > 2, "Chunk should contain data beyond header");
}
#[test]
fn test_write_handles_sorted() {
let map = vec![(5u64, 500i64), (1u64, 100i64), (3u64, 300i64)];
let data = write_handles(&map, 0);
assert!(data.len() > 8);
}
#[test]
fn test_write_handles_with_offset() {
let map = vec![(1u64, 100i64)];
let data_no_offset = write_handles(&map, 0);
let data_with_offset = write_handles(&map, 1000);
assert_ne!(data_no_offset, data_with_offset);
}
#[test]
fn test_chunk_crc_valid() {
let map = vec![(1u64, 100i64), (2u64, 200i64)];
let data = write_handles(&map, 0);
let chunk_size = u16::from_be_bytes([data[0], data[1]]) as usize;
let chunk_data = &data[0..chunk_size];
let chunk_crc_bytes = &data[chunk_size..chunk_size + 2];
let stored_crc = u16::from_be_bytes([chunk_crc_bytes[0], chunk_crc_bytes[1]]);
let computed_crc = crc16(CRC16_SEED, chunk_data);
assert_eq!(stored_crc, computed_crc, "Chunk CRC mismatch");
}
}