use std::path::Path;
use crate::error::ChainError;
use crate::parser::locate_chain_ranges;
use crate::storage::{SharedBytes, is_gz_path};
#[cfg(all(feature = "index", not(feature = "gzip")))]
use crate::storage::gzip_feature_error;
#[cfg(feature = "gzip")]
use flate2::read::MultiGzDecoder;
#[cfg(feature = "mmap")]
use memmap2::MmapOptions;
#[derive(Debug, Clone, Copy)]
pub struct ChainSpan {
pub offset: usize,
pub len: usize,
}
pub struct ChainIndex {
bytes: SharedBytes,
spans: Vec<ChainSpan>,
}
impl ChainIndex {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, ChainError> {
let path = path.as_ref();
if is_gz_path(path) {
#[cfg(feature = "gzip")]
{
let file = std::fs::File::open(path)?;
let mut decoder = MultiGzDecoder::new(file);
let mut buffer = Vec::new();
use std::io::Read;
decoder.read_to_end(&mut buffer)?;
return ChainIndex::from_owned(buffer);
}
#[cfg(not(feature = "gzip"))]
{
return Err(gzip_feature_error());
}
}
#[cfg(feature = "mmap")]
{
let file = std::fs::File::open(path)?;
let mmap = unsafe { MmapOptions::new().map(&file)? };
return ChainIndex::from_bytes(SharedBytes::from_mmap(mmap));
}
#[cfg(not(feature = "mmap"))]
{
let mut buffer = Vec::new();
std::fs::File::open(path)?.read_to_end(&mut buffer)?;
ChainIndex::from_owned(buffer)
}
}
pub fn from_owned(bytes: Vec<u8>) -> Result<Self, ChainError> {
ChainIndex::from_bytes(SharedBytes::from_owned(bytes))
}
pub fn from_bytes(bytes: SharedBytes) -> Result<Self, ChainError> {
let spans = locate_chain_ranges(bytes.as_slice())?
.into_iter()
.map(|range| ChainSpan {
offset: range.start,
len: range.end - range.start,
})
.collect();
Ok(ChainIndex { bytes, spans })
}
pub fn spans(&self) -> &[ChainSpan] {
&self.spans
}
pub fn len(&self) -> usize {
self.spans.len()
}
pub fn is_empty(&self) -> bool {
self.spans.is_empty()
}
pub fn chain_bytes(&self, idx: usize) -> Option<&[u8]> {
let span = self.spans.get(idx)?;
let data = self.bytes.as_slice();
data.get(span.offset..span.offset + span.len)
}
}