use std::cmp::min;
use std::ops::Range;
use async_trait::async_trait;
use byteorder::{ByteOrder, LittleEndian};
use bytes::Bytes;
use lance_core::io::Reader;
use object_store::path::Path;
use prost::Message;
use snafu::{location, Location};
use crate::error::{Error, Result};
use crate::format::ProtoStruct;
use crate::io::ObjectStore;
pub use lance_core::io::Reader as ObjectReader;
#[derive(Debug)]
pub struct CloudObjectReader {
pub object_store: ObjectStore,
pub path: Path,
block_size: usize,
}
impl<'a> CloudObjectReader {
pub fn new(object_store: &'a ObjectStore, path: Path, block_size: usize) -> Result<Self> {
Ok(Self {
object_store: object_store.clone(),
path,
block_size,
})
}
}
#[async_trait]
impl ObjectReader for CloudObjectReader {
fn path(&self) -> &Path {
&self.path
}
fn block_size(&self) -> usize {
self.block_size
}
async fn size(&self) -> Result<usize> {
Ok(self.object_store.inner.head(&self.path).await?.size)
}
async fn get_range(&self, range: Range<usize>) -> Result<Bytes> {
Ok(self.object_store.inner.get_range(&self.path, range).await?)
}
}
pub(crate) async fn read_message<M: Message + Default>(
reader: &dyn Reader,
pos: usize,
) -> Result<M> {
let file_size = reader.size().await?;
if pos > file_size {
return Err(Error::IO {
message: "file size is too small".to_string(),
location: location!(),
});
}
let range = pos..min(pos + 4096, file_size);
let buf = reader.get_range(range.clone()).await?;
let msg_len = LittleEndian::read_u32(&buf) as usize;
if msg_len + 4 > buf.len() {
let remaining_range = range.end..min(4 + pos + msg_len, file_size);
let remaining_bytes = reader.get_range(remaining_range).await?;
let buf = [buf, remaining_bytes].concat();
assert!(buf.len() >= msg_len + 4);
Ok(M::decode(&buf[4..4 + msg_len])?)
} else {
Ok(M::decode(&buf[4..4 + msg_len])?)
}
}
pub(crate) async fn read_struct<
'm,
M: Message + Default + 'static,
T: ProtoStruct<Proto = M> + From<M>,
>(
reader: &dyn Reader,
pos: usize,
) -> Result<T> {
let msg = read_message::<M>(reader, pos).await?;
let obj = T::from(msg);
Ok(obj)
}