use tact_parser::encoding::EncodingFile;
use tact_parser::utils::write_uint40_be;
fn create_test_encoding_file(entries: Vec<(Vec<u8>, Vec<Vec<u8>>, u64)>) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&[0x45, 0x4E]);
data.push(1);
data.push(16); data.push(16);
let entries_size = entries
.iter()
.map(|(ckey, ekeys, _size)| 1 + 5 + ckey.len() + ekeys.len() * ekeys[0].len())
.sum::<usize>();
let page_size_kb = (entries_size.div_ceil(1024)).max(1) as u16;
data.extend_from_slice(&page_size_kb.to_be_bytes()); data.extend_from_slice(&page_size_kb.to_be_bytes());
let page_count: u32 = if entries.is_empty() { 0 } else { 1 };
data.extend_from_slice(&page_count.to_be_bytes()); data.extend_from_slice(&0u32.to_be_bytes());
data.push(0);
data.extend_from_slice(&0u32.to_be_bytes());
if !entries.is_empty() {
data.extend_from_slice(&[0u8; 16]);
data.extend_from_slice(&[0u8; 16]);
let mut page_data = Vec::new();
for (ckey, ekeys, size) in &entries {
page_data.push(ekeys.len() as u8);
page_data.extend_from_slice(&write_uint40_be(*size));
page_data.extend_from_slice(ckey);
for ekey in ekeys {
page_data.extend_from_slice(ekey);
}
}
let page_size = page_size_kb as usize * 1024;
page_data.resize(page_size, 0);
data.extend_from_slice(&page_data);
}
data
}
#[test]
fn test_encoding_file_with_entries() {
let ckey1 = vec![1u8; 16];
let ekey1 = vec![2u8; 16];
let ekey2 = vec![3u8; 16];
let ckey2 = vec![4u8; 16];
let ekey3 = vec![5u8; 16];
let entries = vec![
(ckey1.clone(), vec![ekey1.clone(), ekey2.clone()], 1000),
(ckey2.clone(), vec![ekey3.clone()], 2000),
];
let data = create_test_encoding_file(entries);
let encoding = EncodingFile::parse(&data).unwrap();
let entry1 = encoding.lookup_by_ckey(&ckey1).unwrap();
assert_eq!(entry1.encoding_keys.len(), 2);
assert_eq!(entry1.encoding_keys[0], ekey1);
assert_eq!(entry1.encoding_keys[1], ekey2);
assert_eq!(entry1.size, 1000);
let entry2 = encoding.lookup_by_ckey(&ckey2).unwrap();
assert_eq!(entry2.encoding_keys.len(), 1);
assert_eq!(entry2.encoding_keys[0], ekey3);
assert_eq!(entry2.size, 2000);
assert_eq!(encoding.lookup_by_ekey(&ekey1), Some(&ckey1));
assert_eq!(encoding.lookup_by_ekey(&ekey2), Some(&ckey1));
assert_eq!(encoding.lookup_by_ekey(&ekey3), Some(&ckey2));
assert_eq!(encoding.get_ekey_for_ckey(&ckey1), Some(&ekey1));
assert_eq!(encoding.get_file_size(&ckey1), Some(1000));
assert_eq!(encoding.get_file_size(&ckey2), Some(2000));
assert_eq!(encoding.ckey_count(), 2);
assert_eq!(encoding.ekey_count(), 3);
}
#[test]
fn test_encoding_file_large_sizes() {
let ckey = vec![0xAAu8; 16];
let ekey = vec![0xBBu8; 16];
let large_size = 0xFF_FFFF_FFFF;
let entries = vec![(ckey.clone(), vec![ekey.clone()], large_size)];
let data = create_test_encoding_file(entries);
let encoding = EncodingFile::parse(&data).unwrap();
assert_eq!(encoding.get_file_size(&ckey), Some(large_size));
}
#[test]
fn test_encoding_file_empty_lookup() {
let data = create_test_encoding_file(vec![]);
let encoding = EncodingFile::parse(&data).unwrap();
let nonexistent_key = vec![0xFFu8; 16];
assert!(encoding.lookup_by_ckey(&nonexistent_key).is_none());
assert!(encoding.lookup_by_ekey(&nonexistent_key).is_none());
assert_eq!(encoding.get_ekey_for_ckey(&nonexistent_key), None);
assert_eq!(encoding.get_file_size(&nonexistent_key), None);
}
#[test]
fn test_encoding_header_endianness() {
let mut data = Vec::new();
data.extend_from_slice(&[0x45, 0x4E]);
data.push(1);
data.push(16);
data.push(16);
data.extend_from_slice(&[0x12, 0x34]); data.extend_from_slice(&[0x56, 0x78]);
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
data.push(0); data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
let encoding = EncodingFile::parse(&data).unwrap();
assert_eq!(encoding.header.ckey_page_size_kb, 0x1234);
assert_eq!(encoding.header.ekey_page_size_kb, 0x5678);
assert_eq!(encoding.header.ckey_page_count, 0);
assert_eq!(encoding.header.ekey_page_count, 0);
assert_eq!(encoding.header.espec_block_size, 0);
}
#[test]
fn test_multiple_ekeys_per_ckey() {
let ckey = vec![0x11u8; 16];
let ekeys = vec![
vec![0x21u8; 16],
vec![0x22u8; 16],
vec![0x23u8; 16],
vec![0x24u8; 16],
];
let entries = vec![(ckey.clone(), ekeys.clone(), 5000)];
let data = create_test_encoding_file(entries);
let encoding = EncodingFile::parse(&data).unwrap();
let entry = encoding.lookup_by_ckey(&ckey).unwrap();
assert_eq!(entry.encoding_keys.len(), 4);
for ekey in &ekeys {
assert_eq!(encoding.lookup_by_ekey(ekey), Some(&ckey));
}
assert_eq!(encoding.get_ekey_for_ckey(&ckey), Some(&ekeys[0]));
}
#[test]
fn test_40bit_integer_in_encoding() {
let test_sizes = [
0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF, 0x1234567890, 0xFFFFFFFFFF, ];
for (i, &size) in test_sizes.iter().enumerate() {
let ckey = vec![i as u8; 16];
let ekey = vec![(i + 100) as u8; 16];
let entries = vec![(ckey.clone(), vec![ekey.clone()], size)];
let data = create_test_encoding_file(entries);
let encoding = EncodingFile::parse(&data).unwrap();
assert_eq!(
encoding.get_file_size(&ckey),
Some(size),
"Failed for size {size:#x}"
);
}
}