use std::io::Write;
use crate::error::Result;
pub const CODEC_MAGIC: i32 = 0x3fd7_6c17;
#[must_use]
pub fn header_length(codec_name: &str) -> usize {
9 + codec_name.len()
}
pub struct CodecOutput<Sink: Write> {
sink: Sink,
written: u64,
}
impl<Sink: Write> CodecOutput<Sink> {
pub const fn new(sink: Sink) -> Self {
Self { sink, written: 0 }
}
#[must_use]
pub const fn position(&self) -> u64 {
self.written
}
pub fn into_inner(self) -> Sink {
self.sink
}
pub fn write_byte(&mut self, byte: u8) -> Result<()> {
self.sink.write_all(&[byte])?;
self.written += 1;
Ok(())
}
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
self.sink.write_all(bytes)?;
self.written += bytes.len() as u64;
Ok(())
}
pub fn write_int(&mut self, value: i32) -> Result<()> {
self.write_bytes(&value.to_be_bytes())
}
pub fn write_long(&mut self, value: i64) -> Result<()> {
self.write_bytes(&value.to_be_bytes())
}
pub fn write_vint(&mut self, value: i32) -> Result<()> {
let mut remaining = value as u32;
while remaining & !0x7F != 0 {
self.write_byte(((remaining & 0x7F) | 0x80) as u8)?;
remaining >>= 7;
}
self.write_byte(remaining as u8)
}
pub fn write_vlong(&mut self, value: i64) -> Result<()> {
if value < 0 {
return Err(crate::error::Error::InvalidFormat {
details: format!(
"a Lucene vlong is non-negative and {value} is not; the block-packed \
writer's own signed form is the one that accepts a negative value"
),
});
}
self.write_vlong_signed(value)
}
pub fn write_vlong_signed(&mut self, value: i64) -> Result<()> {
let mut remaining = value as u64;
let mut written = 0;
while remaining & !0x7F != 0 && written < 8 {
self.write_byte(((remaining & 0x7F) | 0x80) as u8)?;
remaining >>= 7;
written += 1;
}
self.write_byte(remaining as u8)
}
pub fn write_string(&mut self, value: &str) -> Result<()> {
let bytes = value.as_bytes();
self.write_vint(i32::try_from(bytes.len()).map_err(|_| {
crate::error::Error::InvalidFormat {
details: format!("a string of {} bytes does not fit a vint", bytes.len()),
}
})?)?;
self.write_bytes(bytes)
}
pub fn write_string_map<'entry>(
&mut self,
entries: impl IntoIterator<Item = (&'entry str, &'entry str)>,
) -> Result<()> {
let entries: Vec<(&str, &str)> = entries.into_iter().collect();
self.write_int(i32::try_from(entries.len()).unwrap_or(i32::MAX))?;
for (key, value) in entries {
self.write_string(key)?;
self.write_string(value)?;
}
Ok(())
}
pub fn write_string_set<'value>(
&mut self,
values: impl IntoIterator<Item = &'value str>,
) -> Result<()> {
let values: Vec<&str> = values.into_iter().collect();
self.write_int(i32::try_from(values.len()).unwrap_or(i32::MAX))?;
for value in values {
self.write_string(value)?;
}
Ok(())
}
pub fn write_header(&mut self, codec_name: &str, version: i32) -> Result<()> {
if !codec_name.is_ascii() || codec_name.len() >= 128 {
return Err(crate::error::Error::InvalidFormat {
details: format!(
"a codec name is ASCII and under 128 characters; {codec_name:?} is not, \
and header_length would be wrong for it"
),
});
}
self.write_int(CODEC_MAGIC)?;
self.write_string(codec_name)?;
self.write_int(version)
}
}