#![expect(
clippy::cast_possible_truncation,
reason = "u64 -> usize for decoded string lengths"
)]
use std::io::{self, Read, Write};
use super::encoded_uint_vector::{decode_uint_vector_u64, encode_uint_vector_u64};
pub fn encode_string_vector(strings: &[&[u8]], w: &mut dyn Write) -> io::Result<()> {
let mut offset = 0u64;
let mut offsets = Vec::with_capacity(strings.len());
for s in strings {
offset += s.len() as u64;
offsets.push(offset);
}
encode_uint_vector_u64(&offsets, w)?;
for s in strings {
w.write_all(s)?;
}
Ok(())
}
pub fn decode_string_vector(r: &mut dyn Read) -> io::Result<Vec<Vec<u8>>> {
let offsets = decode_uint_vector_u64(r)?;
if offsets.is_empty() {
return Ok(Vec::new());
}
let total_len = *offsets.last().unwrap_or(&0);
if total_len > 500_000_000 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"string vector data too large",
));
}
let mut data = vec![0u8; total_len as usize];
r.read_exact(&mut data)?;
let mut result = Vec::with_capacity(offsets.len());
let mut start = 0usize;
for &end in &offsets {
let end = end as usize;
if start <= end && end <= data.len() {
result.push(data[start..end].to_vec());
} else {
result.push(Vec::new());
}
start = end;
}
Ok(result)
}
#[derive(Debug)]
pub struct StringVectorBuilder {
blocks: Vec<Vec<u8>>,
}
impl StringVectorBuilder {
pub(crate) fn new() -> Self {
Self { blocks: Vec::new() }
}
pub(crate) fn add(&mut self, data: Vec<u8>) {
self.blocks.push(data);
}
pub(crate) fn encode(&self, w: &mut dyn Write) -> io::Result<()> {
let refs: Vec<&[u8]> = self.blocks.iter().map(Vec::as_slice).collect();
encode_string_vector(&refs, w)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty() {
let mut buf = Vec::new();
encode_string_vector(&[], &mut buf).unwrap();
let decoded = decode_string_vector(&mut buf.as_slice()).unwrap();
assert!(decoded.is_empty());
}
#[test]
fn test_single_string() {
let strings: Vec<&[u8]> = vec![b"hello"];
let mut buf = Vec::new();
encode_string_vector(&strings, &mut buf).unwrap();
let decoded = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(decoded, vec![b"hello".to_vec()]);
}
#[test]
fn test_multiple_strings() {
let strings: Vec<&[u8]> = vec![b"abc", b"", b"de", b"fghij"];
let mut buf = Vec::new();
encode_string_vector(&strings, &mut buf).unwrap();
let decoded = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(
decoded,
vec![
b"abc".to_vec(),
b"".to_vec(),
b"de".to_vec(),
b"fghij".to_vec(),
]
);
}
#[test]
fn test_builder() {
let mut builder = StringVectorBuilder::new();
builder.add(vec![1, 2, 3]);
builder.add(vec![4, 5]);
builder.add(vec![]);
builder.add(vec![6]);
let mut buf = Vec::new();
builder.encode(&mut buf).unwrap();
let decoded = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(decoded, vec![vec![1, 2, 3], vec![4, 5], vec![], vec![6]]);
}
fn test_encoded_string_vector(input: &[&[u8]], expected_bytes: usize) {
let mut buf = Vec::new();
encode_string_vector(input, &mut buf).unwrap();
assert_eq!(
expected_bytes,
buf.len(),
"encoded byte size mismatch for {} strings",
input.len()
);
let decoded = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(input.len(), decoded.len());
for (i, (expected, actual)) in input.iter().zip(&decoded).enumerate() {
assert_eq!(*expected, actual.as_slice(), "string {i} mismatch");
}
}
#[test]
fn test_empty_vector() {
test_encoded_string_vector(&[], 1);
}
#[test]
fn test_empty_string() {
test_encoded_string_vector(&[b""], 2);
}
#[test]
fn test_repeated_empty_strings() {
test_encoded_string_vector(&[b"", b"", b""], 4);
}
#[test]
fn test_one_string() {
test_encoded_string_vector(&[b"apples"], 8);
}
#[test]
fn test_two_strings() {
test_encoded_string_vector(&[b"fuji", b"mutsu"], 12);
}
#[test]
fn test_two_big_strings() {
let s1 = vec![b'x'; 10_000];
let s2 = vec![b'y'; 100_000];
test_encoded_string_vector(&[&s1, &s2], 110_007);
}
#[test]
fn test_subscript_operator() {
let strings: Vec<&[u8]> = vec![b"pink lady", b"gala"];
let mut buf = Vec::new();
encode_string_vector(&strings, &mut buf).unwrap();
let decoded = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(2, decoded.len());
assert_eq!(b"pink lady", decoded[0].as_slice());
assert_eq!(b"gala", decoded[1].as_slice());
}
#[test]
fn test_reinitialize() {
let input1: Vec<&[u8]> = vec![b"abcd", b"edfg"];
let input2: Vec<&[u8]> = vec![b"hij", b"klm", b"nop", b"qrs"];
let input3: Vec<&[u8]> = vec![b"tu"];
let mut buf = Vec::new();
encode_string_vector(&input1, &mut buf).unwrap();
let d1 = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(2, d1.len());
assert_eq!(b"abcd", d1[0].as_slice());
buf.clear();
encode_string_vector(&input2, &mut buf).unwrap();
let d2 = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(4, d2.len());
assert_eq!(b"hij", d2[0].as_slice());
buf.clear();
encode_string_vector(&input3, &mut buf).unwrap();
let d3 = decode_string_vector(&mut buf.as_slice()).unwrap();
assert_eq!(1, d3.len());
assert_eq!(b"tu", d3[0].as_slice());
}
}