diff --git a/src/buffer/length.rs b/src/buffer/length.rs new file mode 100644 index 0000000..e198377 --- /dev/null +++ b/src/buffer/length.rs @@ -0,0 +1,93 @@ +use core::fmt::{self, Debug, Display}; +use core::cmp::Ordering; + +#[derive(Clone, Copy, Debug, Hash)] +pub enum Length { + Unbounded, + Bounded(usize), +} + +impl Length { + pub fn try_len(&self) -> Option { + match *self { + Length::Unbounded => None, + Length::Bounded(n) => Some(n), + } + } + + pub unsafe fn len(&self) -> usize { + match *self { + Length::Unbounded => { + panic!("attempt to convert `Length::Unbounded` to `usize`") + } + Length::Bounded(n) => n, + } + } + + pub fn is_bounded(&self) -> bool { + match *self { + Length::Unbounded => false, + Length::Bounded(_) => true, + } + } +} + +impl Display for Length { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(self, f) + } +} + +impl PartialEq for Length { + fn eq(&self, rhs: &Length) -> bool { + match (*self, *rhs) { + (Length::Unbounded, _) => false, + (_, Length::Unbounded) => false, + (Length::Bounded(a), Length::Bounded(ref b)) => a.eq(b), + } + } + + fn ne(&self, rhs: &Length) -> bool { + match (*self, *rhs) { + (Length::Unbounded, _) => false, + (_, Length::Unbounded) => false, + (Length::Bounded(a), Length::Bounded(ref b)) => a.ne(b), + } + } +} + +impl PartialEq for Length { + fn eq(&self, rhs: &usize) -> bool { + match *self { + Length::Unbounded => false, + Length::Bounded(n) => n.eq(rhs), + } + } + + fn ne(&self, rhs: &usize) -> bool { + match *self { + Length::Unbounded => false, + Length::Bounded(n) => n.eq(rhs), + } + } +} + +impl PartialOrd for Length { + fn partial_cmp(&self, rhs: &Length) -> Option { + match (*self, *rhs) { + (Length::Unbounded, Length::Unbounded) => None, + (Length::Unbounded, _) => Some(Ordering::Greater), + (_, Length::Unbounded) => Some(Ordering::Less), + (Length::Bounded(a), Length::Bounded(ref b)) => a.partial_cmp(b), + } + } +} + +impl PartialOrd for Length { + fn partial_cmp(&self, rhs: &usize) -> Option { + match *self { + Length::Unbounded => Some(Ordering::Greater), + Length::Bounded(n) => n.partial_cmp(rhs), + } + } +} diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs new file mode 100644 index 0000000..994c540 --- /dev/null +++ b/src/buffer/mod.rs @@ -0,0 +1,264 @@ +use core::ops::{Deref, DerefMut, Range}; + +use alloc::Vec; +use alloc::boxed::Box; +use alloc::borrow::{Cow, ToOwned}; + +pub mod length; + +use self::length::Length; + +pub trait Buffer +where + [T]: ToOwned, +{ + fn len(&self) -> Length; + fn commit(&mut self, slice: Option>); + unsafe fn slice_unchecked(&self, range: Range) -> &[T]; + + unsafe fn slice_unchecked_mut<'a>( + &'a self, + range: Range, + ) -> BufferSlice<'a, T> { + let index = range.start; + let slice = self.slice_unchecked(range); + BufferSlice::new(slice, index) + } + + fn slice(&self, range: Range) -> Option<&[T]> { + if self.len() >= range.end && self.len() > range.start { + unsafe { Some(self.slice_unchecked(range)) } + } else { + None + } + } + + fn slice_mut<'a>( + &'a mut self, + range: Range, + ) -> Option> { + if self.len() >= range.end && self.len() > range.start { + unsafe { Some(self.slice_unchecked_mut(range)) } + } else { + None + } + } +} + +pub struct BufferSlice<'a, T: 'a> +where + [T]: ToOwned, +{ + inner: Cow<'a, [T]>, + index: usize, +} + +impl BufferSlice<'static, T> +where + [T]: ToOwned, +{ + pub fn with_static(inner: &'static [T]) -> BufferSlice<'static, T> { + BufferSlice { + inner: Cow::Borrowed(inner), + index: 0, + } + } +} + +impl<'a, T> BufferSlice<'a, T> +where + [T]: ToOwned, +{ + pub fn new(inner: &'a [T], index: usize) -> BufferSlice<'a, T> { + BufferSlice { + inner: Cow::Borrowed(inner), + index, + } + } + + pub fn is_mutated(&self) -> bool { + match self.inner { + Cow::Borrowed(_) => false, + Cow::Owned(_) => true, + } + } + + #[inline] + pub fn at_index(&self) -> usize { + self.index + } +} + +impl<'a, T> BufferSlice<'a, T> +where + [T]: ToOwned>, +{ + pub fn commit(self) -> Option> { + if self.is_mutated() { + Some(BufferCommit::new(self.inner.into_owned(), self.index)) + } else { + None + } + } +} + +impl<'a, T> AsRef<[T]> for BufferSlice<'a, T> +where + [T]: ToOwned, +{ + fn as_ref(&self) -> &[T] { + self.inner.as_ref() + } +} + +impl<'a, T> AsMut<[T]> for BufferSlice<'a, T> +where + [T]: ToOwned, + <[T] as ToOwned>::Owned: AsMut<[T]>, +{ + fn as_mut(&mut self) -> &mut [T] { + self.inner.to_mut().as_mut() + } +} + +impl<'a, T> Deref for BufferSlice<'a, T> +where + [T]: ToOwned, +{ + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl<'a, T> DerefMut for BufferSlice<'a, T> +where + [T]: ToOwned, + <[T] as ToOwned>::Owned: AsMut<[T]>, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut() + } +} + +pub struct BufferCommit { + inner: Vec, + index: usize, +} + +impl BufferCommit { + pub fn with_vec(inner: Vec) -> BufferCommit { + BufferCommit { inner, index: 0 } + } + + pub fn new(inner: Vec, index: usize) -> BufferCommit { + BufferCommit { inner, index } + } + + pub fn into_inner(self) -> Vec { + self.inner + } + + #[inline] + pub fn at_index(&self) -> usize { + self.index + } +} + +impl AsRef<[T]> for BufferCommit { + fn as_ref(&self) -> &[T] { + self.inner.as_ref() + } +} + +impl AsMut<[T]> for BufferCommit { + fn as_mut(&mut self) -> &mut [T] { + self.inner.as_mut() + } +} + +impl Deref for BufferCommit { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl DerefMut for BufferCommit { + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut() + } +} + +default impl Buffer for B +where + T: Clone, + [T]: ToOwned, + B: AsRef<[T]> + AsMut<[T]>, +{ + fn len(&self) -> Length { + Length::Bounded(self.as_ref().len()) + } + + fn commit(&mut self, slice: Option>) { + slice.map(|slice| { + let index = slice.at_index(); + let end = index + slice.as_ref().len(); + // XXX: it would be much better to drop the contents of dst and + // move the contents of slice instead of cloning + let dst = &mut self.as_mut()[index..end]; + dst.clone_from_slice(slice.as_ref()); + }); + } + + unsafe fn slice_unchecked(&self, range: Range) -> &[T] { + self.as_ref().get_unchecked(range) + } +} + +impl<'a, T> Buffer for &'a mut [T] +where + T: Clone, + [T]: ToOwned, +{ +} + +impl Buffer for Vec +where + T: Clone, + [T]: ToOwned, +{ +} + +impl Buffer for Box<[T]> +where + T: Clone, + [T]: ToOwned, +{ +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn buffer() { + let mut buffer = vec![0; 1024]; + let commit = { + let mut slice = buffer.slice_mut(256..512).unwrap(); + slice.iter_mut().for_each(|x| *x = 1); + slice.commit() + }; + buffer.commit(commit); + + for (i, &x) in buffer.iter().enumerate() { + if i < 256 || i >= 512 { + assert_eq!(x, 0); + } else { + assert_eq!(x, 1); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index deaeaa1..1ce4792 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,18 @@ +#![feature(alloc)] +#![feature(specialization)] +#![feature(swap_with_slice)] #![cfg_attr(not(test), no_std)] +extern crate alloc; #[macro_use] extern crate bitflags; +#[cfg(test)] +extern crate core; pub mod error; pub mod sys; +pub mod buffer; +pub mod fs; #[cfg(test)] mod tests {