Files @ 1bc57ab68e0e
Branch filter:

Location: CSY/reowolf/src/collections/scoped_buffer.rs

1bc57ab68e0e 6.2 KiB application/rls-services+xml Show Annotation Show as Raw Download as Raw
Max Henger
Merge branch 'feat-builtin-ip' into 'master'

feat: Builtin internet component

See merge request nl-cwi-csy/reowolf!6
/// scoped_buffer.rs
///
/// Solves the common pattern where we are performing some kind of recursive
/// pattern while using a temporary buffer. At the start, or during the
/// procedure, we push stuff into the buffer. At the end we take out what we
/// have put in.
///
/// It is unsafe because we're using pointers to circumvent borrowing rules in
/// the name of code cleanliness. The correctness of use is checked in debug
/// mode at runtime.

use std::iter::FromIterator;

macro_rules! hide {
    ($v:block) => {
        #[cfg(debug_assertions)] $v
    };
    ($v:expr) => {
        #[cfg(debug_assertions)] $v
    };
}

pub(crate) struct ScopedBuffer<T: Sized> {
    pub inner: Vec<T>,
}

impl<T: Sized> ScopedBuffer<T> {
    pub(crate) fn with_capacity(capacity: usize) -> Self {
        Self {
            inner: Vec::with_capacity(capacity),
        }
    }

    pub(crate) fn start_section(&mut self) -> ScopedSection<T> {
        let start_size = self.inner.len() as u32;
        ScopedSection {
            inner: &mut self.inner,
            start_size,
            #[cfg(debug_assertions)] cur_size: start_size,
        }
    }
}

impl<T: Clone> ScopedBuffer<T> {
    pub(crate) fn start_section_initialized(&mut self, initialize_with: &[T]) -> ScopedSection<T> {
        let start_size = self.inner.len() as u32;
        let _data_size = initialize_with.len() as u32;
        self.inner.extend_from_slice(initialize_with);
        ScopedSection{
            inner: &mut self.inner,
            start_size,
            #[cfg(debug_assertions)] cur_size: start_size + _data_size,
        }
    }
}

#[cfg(debug_assertions)]
impl<T: Sized> Drop for ScopedBuffer<T> {
    fn drop(&mut self) {
        // Make sure that everyone cleaned up the buffer neatly
        debug_assert!(self.inner.is_empty(), "dropped non-empty scoped buffer");
    }
}

/// A section of the buffer. Keeps track of where we started the section. When
/// done with the section one must call `into_vec` or `forget` to remove the
/// section from the underlying buffer. This will also be done upon dropping the
/// ScopedSection in case errors are being handled.
pub(crate) struct ScopedSection<T: Sized> {
    inner: *mut Vec<T>,
    start_size: u32,
    #[cfg(debug_assertions)] cur_size: u32,
}

impl<T: Sized> ScopedSection<T> {
    /// Pushes value into section
    #[inline]
    pub(crate) fn push(&mut self, value: T) {
        self.check_length();
        let vec = unsafe{&mut *self.inner};
        vec.push(value);
        hide!(self.cur_size += 1);
    }

    #[inline]
    pub(crate) fn len(&self) -> usize {
        self.check_length();
        let vec = unsafe{&mut *self.inner};
        return vec.len() - self.start_size as usize;
    }

    #[inline]
    #[allow(unused_mut)] // used in debug mode
    pub(crate) fn forget(mut self) {
        self.check_length();
        let vec = unsafe{&mut *self.inner};
        hide!(self.cur_size = self.start_size);
        vec.truncate(self.start_size as usize);
    }

    #[inline]
    #[allow(unused_mut)] // used in debug mode
    pub(crate) fn into_vec(mut self) -> Vec<T> {
        self.check_length();
        let vec = unsafe{&mut *self.inner};
        hide!(self.cur_size = self.start_size);
        let section = Vec::from_iter(vec.drain(self.start_size as usize..));
        section
    }

    #[inline]
    pub(crate) fn check_length(&self) {
        hide!({
            let vec = unsafe{&*self.inner};
            debug_assert_eq!(
                vec.len(), self.cur_size as usize,
                "incorrect use of ScopedSection: underlying storage vector has changed size"
            )
        })
    }
}

impl<T: Sized + PartialEq> ScopedSection<T> {
    #[inline]
    pub(crate) fn push_unique(&mut self, value: T) {
        self.check_length();
        let vec = unsafe{&mut *self.inner};
        for item in &vec[self.start_size as usize..] {
            if *item == value {
                // item already exists
                return;
            }
        }

        vec.push(value);
        hide!(self.cur_size += 1);
    }

    #[inline]
    pub(crate) fn contains(&self, value: &T) -> bool {
        self.check_length();
        let vec = unsafe{&*self.inner};
        for index in self.start_size as usize..vec.len() {
            if &vec[index] == value {
                return true;
            }
        }

        return false;
    }
}

impl<T: Copy> ScopedSection<T> {
    pub(crate) fn iter_copied(&self) -> ScopedIter<T> {
        return ScopedIter{
            inner: self.inner,
            cur_index: self.start_size,
            last_index: unsafe{ (*self.inner).len() as u32 },
        }
    }
}

impl<T> std::ops::Index<usize> for ScopedSection<T> {
    type Output = T;

    fn index(&self, index: usize) -> &Self::Output {
        let vec = unsafe{&*self.inner};
        return &vec[self.start_size as usize + index]
    }
}

impl<T> std::ops::IndexMut<usize> for ScopedSection<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        let vec = unsafe{&mut *self.inner};
        return &mut vec[self.start_size as usize + index]
    }
}

// note: this `Drop` impl used to be debug-only, requiring the programmer to
// call `into_vec` or `forget`. But this is rather error prone. So we'll check
// in debug mode, but always truncate in release mode (even though this is a
// noop in most cases).
impl<T: Sized> Drop for ScopedSection<T> {
    fn drop(&mut self) {
        let vec = unsafe{&mut *self.inner};
        hide!(debug_assert_eq!(vec.len(), self.cur_size as usize));
        vec.truncate(self.start_size as usize);
    }
}

/// Small utility for iterating over a section of the buffer. Same conditions as
/// the buffer apply: each time we retrieve an element the buffer must have the
/// same size as the moment of creation.
pub(crate) struct ScopedIter<T: Copy> {
    inner: *mut Vec<T>,
    cur_index: u32,
    last_index: u32,
}

impl<T: Copy> Iterator for ScopedIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        hide!(debug_assert_eq!(self.last_index as usize, unsafe { (*self.inner).len() }));
        if self.cur_index >= self.last_index {
            return None;
        }

        let vec = unsafe{ &*self.inner };
        let index = self.cur_index as usize;
        self.cur_index += 1;
        return Some(vec[index]);
    }
}