add smart, delayed update Buffer
This commit is contained in:
parent
43e400ef08
commit
e91533440e
93
src/buffer/length.rs
Normal file
93
src/buffer/length.rs
Normal 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
264
src/buffer/mod.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,18 @@
|
||||||
|
#![feature(alloc)]
|
||||||
|
#![feature(specialization)]
|
||||||
|
#![feature(swap_with_slice)]
|
||||||
#![cfg_attr(not(test), no_std)]
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
|
pub mod buffer;
|
||||||
|
pub mod fs;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
Reference in a new issue