use std::{
convert::TryInto,
io::{prelude::*, BufReader, Read, SeekFrom},
};
use crate::{error::Error, Result};
const HEADER_SIZE: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct Header {
lines: usize,
}
impl Header {
pub(crate) fn encode(&self) -> [u8; HEADER_SIZE] {
let enc: [u8; HEADER_SIZE] = self.lines.to_le_bytes().try_into().unwrap();
enc
}
pub fn decode<R: Read + Seek>(reader: &mut BufReader<R>) -> Result<Self> {
reader.seek(SeekFrom::Start(0))?;
let mut header: [u8; 8] = [0; 8];
reader.read_exact(&mut header)?;
let lines = usize::from_le_bytes(header);
Ok(Header { lines })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Index {
inner: Vec<u64>,
len_bytes: usize,
}
impl Index {
pub fn new(line: Vec<u64>) -> Index {
Self {
len_bytes: HEADER_SIZE + line.len() * 8 + 1,
inner: line,
}
}
pub fn build<R: Read + Unpin + Seek>(reader: &mut BufReader<R>) -> Result<Self> {
reader.seek(SeekFrom::Start(0)).unwrap();
let mut line_index: Vec<u64> = Vec::new();
let mut curr_offset: u64 = 0;
let mut buff = Vec::with_capacity(1000);
loop {
let last_offset = curr_offset;
buff.clear();
let n = reader.read_until(b'\n', &mut buff)?;
if n == 0 || buff.is_empty() {
break;
}
line_index.push(last_offset);
curr_offset += n as u64;
}
reader.seek(SeekFrom::Start(0)).unwrap();
Ok(Self {
inner: line_index,
len_bytes: 0,
})
}
pub fn encode(&self) -> Vec<u8> {
let mut out: Vec<_> = self
.inner
.iter()
.map(|i| i.to_le_bytes())
.flatten()
.collect();
out.push(b'\n');
out
}
pub fn decode<R: Read + Unpin + Seek>(
reader: &mut BufReader<R>,
header: &Header,
) -> Result<Self> {
reader.seek(SeekFrom::Start(HEADER_SIZE as u64))?;
let mut inner: Vec<u64> = Vec::new();
let mut buff: [u8; 8] = [0; 8];
for _ in 0..header.lines {
reader.read_exact(&mut buff)?;
inner.push(u64::from_le_bytes(
buff.try_into().map_err(|_| Error::MalformedIndex)?,
));
}
Ok(Index::new(inner))
}
pub fn zero_len(self) -> Self {
let mut s = self;
s.len_bytes = 0;
s
}
pub(crate) fn get_header(&self) -> Header {
Header {
lines: self.inner.len(),
}
}
#[inline]
pub fn get(&self, pos: usize) -> Result<u64> {
self.inner.get(pos).ok_or(Error::OutOfBounds).map(|i| *i)
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn len_bytes(&self) -> usize {
self.len_bytes
}
pub fn is_empty(&self) -> bool {
self.len_bytes() == 0
}
pub(super) fn parse_index<R: Read + Unpin + Seek>(reader: &mut BufReader<R>) -> Result<Index> {
let header = Header::decode(reader)?;
let index = Index::decode(reader, &header)?;
Ok(index)
}
}