use crate::config::Compression;
use crate::framer::{Codec, ObjectFramer};
use crate::offset::{self, Position};
use crate::split::SplitRange;
use crate::trim::RangeTrim;
use bytes::Bytes;
use spate_core::framing::RecordFramer;
use std::io;
use std::sync::Arc;
pub const MAX_ORDINAL: u32 = offset::MAX_ORDINAL;
pub const MAX_RECORD_INDEX: u64 = offset::MAX_RECORD_INDEX;
#[must_use]
pub fn encode_position(ordinal: u32, record: u64) -> Option<i64> {
Position { ordinal, record }.encode().ok()
}
#[must_use]
pub fn decode_position(offset: i64) -> (u32, u64) {
let position = Position::decode(offset);
(position.ordinal, position.record)
}
pub fn frame_object<M>(
compression: Compression,
key: &str,
chunks: &[&[u8]],
make_framer: M,
) -> (usize, io::Result<()>)
where
M: Fn() -> Box<dyn RecordFramer> + Send + Sync + 'static,
{
let mut framer = ObjectFramer::new(Arc::new(make_framer));
let mut records = 0;
let mut outcome = framer.begin_object(Codec::resolve(compression, key));
for chunk in chunks {
if outcome.is_err() {
break;
}
outcome = framer.push_chunk(chunk);
while framer.pop_record().is_some() {
records += 1;
}
}
if outcome.is_ok() {
outcome = framer.finish_object();
while framer.pop_record().is_some() {
records += 1;
}
}
(records, outcome)
}
#[must_use]
pub fn read_range(
object: &[u8],
start: u64,
end: u64,
delimiter: u8,
range_bytes: u64,
chunk_bytes: u64,
) -> Option<Vec<u8>> {
let size = object.len() as u64;
if start >= end || end > size {
return None;
}
let mut trim = RangeTrim::new(SplitRange::new(start, end, delimiter), size);
let mut pos = trim.first_byte();
let mut owned = Vec::new();
while let Some(window_end) = trim.next_window(pos, range_bytes, chunk_bytes) {
assert!(window_end > pos, "an empty window at byte {pos}");
let window = Bytes::copy_from_slice(&object[pos as usize..window_end as usize]);
owned.extend_from_slice(&trim.feed(pos, window));
pos = window_end;
}
Some(owned)
}