add smart, delayed update `Buffer`

pull/3/head
Szymon Walter 2018-03-19 00:02:31 +01:00
parent 43e400ef08
commit e91533440e
3 changed files with 365 additions and 0 deletions

93
src/buffer/length.rs Normal file
View File

@ -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<usize> {
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<usize> 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<Ordering> {
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<usize> for Length {
fn partial_cmp(&self, rhs: &usize) -> Option<Ordering> {
match *self {
Length::Unbounded => Some(Ordering::Greater),
Length::Bounded(n) => n.partial_cmp(rhs),
}
}
}

264
src/buffer/mod.rs Normal file
View File

@ -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<T>
where
[T]: ToOwned,
{
fn len(&self) -> Length;
fn commit(&mut self, slice: Option<BufferCommit<T>>);
unsafe fn slice_unchecked(&self, range: Range<usize>) -> &[T];
unsafe fn slice_unchecked_mut<'a>(
&'a self,
range: Range<usize>,
) -> BufferSlice<'a, T> {
let index = range.start;
let slice = self.slice_unchecked(range);
BufferSlice::new(slice, index)
}
fn slice(&self, range: Range<usize>) -> 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<usize>,
) -> Option<BufferSlice<'a, T>> {
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<T> 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<Owned = Vec<T>>,
{
pub fn commit(self) -> Option<BufferCommit<T>> {
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<T> {
inner: Vec<T>,
index: usize,
}
impl<T> BufferCommit<T> {
pub fn with_vec(inner: Vec<T>) -> BufferCommit<T> {
BufferCommit { inner, index: 0 }
}
pub fn new(inner: Vec<T>, index: usize) -> BufferCommit<T> {
BufferCommit { inner, index }
}
pub fn into_inner(self) -> Vec<T> {
self.inner
}
#[inline]
pub fn at_index(&self) -> usize {
self.index
}
}
impl<T> AsRef<[T]> for BufferCommit<T> {
fn as_ref(&self) -> &[T] {
self.inner.as_ref()
}
}
impl<T> AsMut<[T]> for BufferCommit<T> {
fn as_mut(&mut self) -> &mut [T] {
self.inner.as_mut()
}
}
impl<T> Deref for BufferCommit<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<T> DerefMut for BufferCommit<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
default impl<T, B> Buffer<T> 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<BufferCommit<T>>) {
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<usize>) -> &[T] {
self.as_ref().get_unchecked(range)
}
}
impl<'a, T> Buffer<T> for &'a mut [T]
where
T: Clone,
[T]: ToOwned,
{
}
impl<T> Buffer<T> for Vec<T>
where
T: Clone,
[T]: ToOwned,
{
}
impl<T> Buffer<T> 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);
}
}
}
}

View File

@ -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 {