~jeffa/filo

a6af6e4f8d7a3f78e12695e86e1a7b18b7e24a78 — Jeff a month ago 215be9c custom_buffer
Rewrite extend method to use custom buffer
1 files changed, 41 insertions(+), 36 deletions(-)

M src/filo.rs
M src/filo.rs => src/filo.rs +41 -36
@@ 13,10 13,11 @@ use std::{
    path::PathBuf,
};

const DEFAULT_BUF_SIZE: usize = 8 * 1024;

pub struct Filo<T> {
    file: File,
    reader: BufReader<File>,
    writer: BufWriter<File>,
    buffer: Vec<u8>,
    len: usize,
    _item: PhantomData<T>,
}


@@ 28,15 29,13 @@ impl<T: FiloRw> Filo<T> {
            .write(true)
            .create(true)
            .open(path)?;
        let reader = BufReader::with_capacity(T::SIZE, file.try_clone()?);
        let writer = BufWriter::new(file.try_clone()?);
        let buffer = Vec::with_capacity(DEFAULT_BUF_SIZE);
        let len = 0;
        let _item = PhantomData;

        Ok(Filo {
            file,
            reader,
            writer,
            buffer,
            len,
            _item,
        })


@@ 54,7 53,7 @@ impl<T: FiloRw> Filo<T> {
    /// performance.
    pub fn push(&mut self, item: T) -> Result<(), io::Error>
    where
        [u8; T::SIZE]: Sized,
        [u8; T::FILO_FMT_SIZE]: Sized,
    {
        let new_index = self.len + 1;
        let write_result = self.write_index(item, new_index);


@@ 66,14 65,18 @@ impl<T: FiloRw> Filo<T> {
        write_result
    }

    /// Reads the entire underlying file and sets the Filo's "len" field to the actual number of
    /// items that are stored. This will also clean up any trailing bytes caused by a partially
    /// failed write.
    /// Ensures that this instance of Filo and the file it manages are correct.
    ///
    /// This currently performs two tasks:
    ///
    /// - Reads the entire underlying file and sets the Filo's "len" field to the actual number of
    ///   items that are stored.
    /// - Cleans up any trailing bytes caused by a partially failed write.
    ///
    /// This method can potentially take a long time, depending on the number of items. By extending
    /// the buffer to count many items at once, performance is improved compared to reading one item
    /// at a time. However, the time complexity is still linear (i.e. O(n)).
    pub fn sync_len(&self) {
    pub fn sync(&self) {
        unimplemented!();
    }



@@ 82,9 85,14 @@ impl<T: FiloRw> Filo<T> {
        unimplemented!();
    }

    /// Extends a Filo with the contents of an iterator.
    /// Extends the Filo with the contents of an iterator.
    ///
    /// This method will short circuit, stopping on the first operation that errors. The fallible
    /// part of this method is [File::write_all].
    ///
    /// This method will short circuit, stopping on the first operation that errors.
    /// This method will attempt to write every item in the given iterator, even if the buffer does
    /// not have enough space to do so. It will clear the buffer and recursively call itself until
    /// the iterator is empty.
    ///
    /// You should call [sync_len] if this method fails.
    ///


@@ 93,48 101,45 @@ impl<T: FiloRw> Filo<T> {
    /// buffer to ensure that every byte is written to the file.
    pub fn extend(&mut self, items: impl Iterator<Item = T>) -> Result<(), io::Error>
    where
        [u8; T::SIZE]: Sized,
        [u8; T::FILO_FMT_SIZE]: Sized,
    {
        let mut extend_len = 0;

        for item in items {
            let bytes = item.to_storable();
            let write_result = self.writer.write(&bytes);

            match write_result {
                Ok(_) => extend_len += 1,
                Err(err) => {
                    self.sync_len();
                    return Err(err);
                }
            let bytes = item.to_filo_fmt();
            let new_buffer_len = self.buffer.len() + bytes.len();

            if new_buffer_len > self.buffer.capacity() {
                break;
            }
        }

        let flush_result = self.writer.flush();
            self.buffer.extend_from_slice(&bytes);

        match flush_result {
            Ok(_) => {},
            Err(err) => {
                self.sync_len();
                return Err(err);
            },
            extend_len += 1;
        }

        self.len += extend_len;
        self.file.write_all(&self.buffer)?;
        self.buffer.clear();

        Ok(())
        if let Some(item) = items.next() {
            let remaining_items = std::iter::once(item).chain(items);

            self.extend(remaining_items)
        } else {
            Ok(())
        }
    }

    /// Returns an [Option] containing the item at the given index or None if the index does not
    /// exist.
    pub fn read_index(&mut self, index: usize) -> Result<T, io::Error> {
        let byte_position = (index * T::SIZE) as u64;
        let byte_position = (index * T::FILO_FMT_SIZE) as u64;

        self.reader.seek(SeekFrom::Start(byte_position))?;

        let (item, data_len) = {
            let data = self.reader.fill_buf()?;
            let item = T::from_storable(data);
            let item = T::from_filo_fmt(data);

            (item, data.len())
        };


@@ 149,7 154,7 @@ impl<T: FiloRw> Filo<T> {
    /// If the given index is greater than the
    pub fn write_index(&mut self, item: T, index: usize) -> Result<(), io::Error>
    where
        [u8; T::SIZE]: Sized,
        [u8; T::FILO_FMT_SIZE]: Sized,
    {
        unimplemented!();
    }