use std::{fs, marker::PhantomData, ops::Deref, path::PathBuf, sync::Arc};
use rawdb::Database;
use crate::{Error, Result, Stamp, VecIndex, VecValue};
use super::{Format, HEADER_OFFSET, Header, ImportOptions, ReadOnlyBaseVec, SharedLen, WithPrev};
#[derive(Debug)]
pub(crate) struct ReadWriteBaseVec<I, T> {
pub(crate) read_only: ReadOnlyBaseVec<I, T>,
pub(super) pushed: WithPrev<Vec<T>>,
pub(super) previous_stored_len: usize,
pub(super) saved_stamped_changes: u16,
}
impl<I, T> Deref for ReadWriteBaseVec<I, T> {
type Target = ReadOnlyBaseVec<I, T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.read_only
}
}
impl<I, T> ReadWriteBaseVec<I, T>
where
I: VecIndex,
T: VecValue,
{
pub fn import(options: ImportOptions, format: Format) -> Result<Self> {
let region = options
.db
.create_region_if_needed(&vec_region_name_with::<I>(options.name))?;
let region_len = region.meta().len();
if region_len > 0 && region_len < HEADER_OFFSET {
return Err(Error::CorruptedRegion {
name: region.meta().id().to_string(),
region_len,
});
}
let header = if region_len == 0 {
Header::create_and_write(®ion, options.version, format)?
} else {
Header::import_and_verify(®ion, options.version, format)?
};
Ok(Self {
read_only: ReadOnlyBaseVec {
region,
header,
name: Arc::from(options.name),
stored_len: SharedLen::default(),
phantom: PhantomData,
},
pushed: WithPrev::default(),
previous_stored_len: 0,
saved_stamped_changes: options.saved_stamped_changes,
})
}
#[inline]
pub fn read_only_base(&self) -> ReadOnlyBaseVec<I, T> {
self.read_only.clone()
}
#[inline]
pub fn pushed(&self) -> &[T] {
self.pushed.current()
}
#[inline]
pub fn mut_pushed(&mut self) -> &mut Vec<T> {
self.pushed.current_mut()
}
#[inline]
pub fn reserve_pushed(&mut self, additional: usize) {
self.pushed.current_mut().reserve(additional);
}
#[inline]
pub fn prev_pushed(&self) -> &[T] {
self.pushed.previous()
}
#[inline]
pub fn len(&self) -> usize {
self.stored_len() + self.pushed().len()
}
#[inline]
pub fn update_stored_len(&self, val: usize) {
self.read_only.stored_len.set(val);
}
#[inline]
pub fn prev_stored_len(&self) -> usize {
self.previous_stored_len
}
#[inline(always)]
pub fn mut_prev_stored_len(&mut self) -> &mut usize {
&mut self.previous_stored_len
}
#[inline(always)]
pub fn saved_stamped_changes(&self) -> u16 {
self.saved_stamped_changes
}
#[inline]
pub fn mut_header(&mut self) -> &mut Header {
&mut self.read_only.header
}
#[inline]
pub fn db(&self) -> Database {
self.region.db()
}
#[inline]
pub fn db_path(&self) -> PathBuf {
self.db().path().to_path_buf()
}
pub fn write_header_if_needed(&mut self) -> Result<()> {
if self.read_only.header.modified() {
self.read_only.header.write(&self.read_only.region)?;
}
Ok(())
}
pub fn index_to_name(&self) -> String {
vec_region_name(&self.name, I::to_string())
}
pub fn remove(self) -> Result<()> {
self.read_only.region.remove()?;
Ok(())
}
#[inline]
pub fn fold_pushed<B, F: FnMut(B, T) -> B>(
&self,
from: usize,
to: usize,
init: B,
mut f: F,
) -> B {
let stored_len = self.stored_len();
let start = from.max(stored_len);
if start >= to {
return init;
}
let pushed = self.pushed();
let slice_from = start - stored_len;
let slice_to = (to - stored_len).min(pushed.len());
let ptr = pushed.as_ptr();
let mut acc = init;
let mut i = slice_from;
while i < slice_to {
acc = f(acc, unsafe { ptr.add(i).read() });
i += 1;
}
acc
}
#[inline]
pub fn try_fold_pushed<B, E, F: FnMut(B, T) -> std::result::Result<B, E>>(
&self,
from: usize,
to: usize,
init: B,
mut f: F,
) -> std::result::Result<B, E> {
let stored_len = self.stored_len();
let start = from.max(stored_len);
if start >= to {
return Ok(init);
}
let pushed = self.pushed();
let mut acc = init;
for v in &pushed[(start - stored_len)..(to - stored_len).min(pushed.len())] {
acc = f(acc, v.clone())?;
}
Ok(acc)
}
pub fn truncate_pushed(&mut self, index: usize) -> bool {
let stored_len = self.stored_len();
let len = stored_len + self.pushed().len();
if index >= len {
return false;
}
if index <= stored_len {
self.pushed.current_mut().clear();
} else {
self.pushed.current_mut().truncate(index - stored_len);
}
index < stored_len
}
pub fn reset_base(&mut self) -> Result<()> {
self.pushed.clear();
self.read_only.stored_len.set(0);
self.previous_stored_len = 0;
self.read_only.header.update_stamp(Stamp::default());
let changes_path = self.changes_path();
if changes_path.exists() {
fs::remove_dir_all(&changes_path)?;
}
Ok(())
}
pub fn reset_unsaved_base(&mut self) {
self.pushed.current_mut().clear();
}
}
pub fn vec_region_name_with<I: VecIndex>(name: &str) -> String {
vec_region_name(name, I::to_string())
}
pub fn vec_region_name(name: &str, index: &str) -> String {
format!("{name}/{index}")
}