use crate::{
buffer::{tip::Buffer, SyncState},
Blob, Buf, BufferPool, BufferPooler, Error, Handle, IoBufs,
};
use std::num::NonZeroUsize;
pub struct Write<B: Blob> {
blob: B,
buffer: Buffer,
sync_state: SyncState,
}
impl<B: Blob> Write<B> {
pub fn new(blob: B, size: u64, capacity: NonZeroUsize, pool: BufferPool) -> Self {
Self {
blob,
buffer: Buffer::new(size, capacity.get(), pool),
sync_state: SyncState::Dirty,
}
}
pub fn from_pooler(
pooler: &impl BufferPooler,
blob: B,
size: u64,
capacity: NonZeroUsize,
) -> Self {
Self::new(blob, size, capacity, pooler.storage_buffer_pool().clone())
}
pub const fn size(&self) -> u64 {
self.buffer.size()
}
pub async fn read_at(&self, offset: u64, len: usize) -> Result<IoBufs, Error> {
let end_offset = offset
.checked_add(len as u64)
.ok_or(Error::OffsetOverflow)?;
if end_offset > self.buffer.size() {
return Err(Error::BlobInsufficientLength);
}
if len == 0 {
return Ok(IoBufs::default());
}
if offset >= self.buffer.offset {
let start = (offset - self.buffer.offset) as usize;
let end = start + len;
return Ok(self.buffer.slice(start..end).into());
}
if end_offset <= self.buffer.offset {
return self.read_blob(offset, len).await;
}
let blob_len = (self.buffer.offset - offset) as usize;
let tip_len = len - blob_len;
let tip = self.buffer.slice(..tip_len);
let mut blob = self.read_blob(offset, blob_len).await?;
blob.append(tip);
Ok(blob)
}
async fn read_blob(&self, offset: u64, len: usize) -> Result<IoBufs, Error> {
Ok(self.blob.read_at(offset, len).await?.freeze())
}
pub async fn write_at(
&mut self,
offset: u64,
bufs: impl Into<IoBufs> + Send,
) -> Result<(), Error> {
let mut bufs = bufs.into();
offset
.checked_add(bufs.remaining() as u64)
.ok_or(Error::OffsetOverflow)?;
let mut current_offset = offset;
while bufs.has_remaining() {
let chunk = bufs.chunk();
let chunk_len = chunk.len();
if self.buffer.merge(chunk, current_offset) {
bufs.advance(chunk_len);
current_offset += chunk_len as u64;
continue;
}
let chunk_end = current_offset + chunk_len as u64;
if self.buffer.offset < chunk_end {
if let Some((old_buf, old_offset)) = self.buffer.take() {
self.sync_state
.write_at(&self.blob, old_offset, old_buf)
.await?;
if self.buffer.merge(chunk, current_offset) {
bufs.advance(chunk_len);
current_offset += chunk_len as u64;
continue;
}
}
}
let direct = bufs.split_to(chunk_len);
self.sync_state
.write_at(&self.blob, current_offset, direct)
.await?;
current_offset += chunk_len as u64;
self.buffer.offset = self.buffer.offset.max(current_offset);
}
Ok(())
}
pub async fn resize(&mut self, len: u64) -> Result<(), Error> {
if let Some((buf, offset)) = self.buffer.resize(len) {
self.sync_state.write_at(&self.blob, offset, buf).await?;
}
self.sync_state.resize(&self.blob, len).await?;
Ok(())
}
pub async fn sync(&mut self) -> Result<(), Error> {
if let Some((buf, offset)) = self.buffer.take() {
return self.write_blob_sync(offset, buf).await;
}
self.sync_blob().await
}
pub async fn start_sync(&mut self) -> Handle<()> {
if let Some((buf, offset)) = self.buffer.take() {
if let Err(err) = self.sync_state.write_at(&self.blob, offset, buf).await {
return Handle::ready(Err(err));
}
}
self.sync_state.start_sync(&self.blob).await
}
pub async fn wait_for_sync(&mut self) -> Result<(), Error> {
self.sync_state.wait_for_pending().await
}
async fn write_blob_sync(
&mut self,
offset: u64,
bufs: impl Into<IoBufs> + Send,
) -> Result<(), Error> {
self.sync_state
.write_at_sync(&self.blob, offset, bufs)
.await
}
async fn sync_blob(&mut self) -> Result<(), Error> {
self.sync_state.sync(&self.blob).await
}
}