Last active
January 13, 2020 21:22
-
-
Save DutchGhost/30a67cd79b939c649c7c06119f30033e to your computer and use it in GitHub Desktop.
Const buffers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #![feature(const_generics)] | |
| #![feature(untagged_unions)] | |
| #![feature(const_fn)] | |
| #![feature(const_fn_union)] | |
| #![feature(const_mut_refs)] | |
| #![feature(const_if_match)] | |
| #![feature(const_panic)] | |
| #![feature(const_raw_ptr_deref)] | |
| use std::mem::{self, ManuallyDrop, MaybeUninit}; | |
| #[repr(C)] | |
| union Init<T, const N: usize> { | |
| uninit: MaybeUninit<[MaybeUninit<T>; N]>, | |
| init: [MaybeUninit<T>; N], | |
| } | |
| pub struct ArrayBuf<T, const N: usize> { | |
| data: [MaybeUninit<T>; N], | |
| len: usize, | |
| } | |
| impl<T, const N: usize> ArrayBuf<T, N> { | |
| const fn data_uninit() -> Self { | |
| let uninit = MaybeUninit::uninit(); | |
| let not_yet_init = Init { uninit }; | |
| unsafe { | |
| Self { | |
| data: not_yet_init.init, | |
| len: 0, | |
| } | |
| } | |
| } | |
| } | |
| impl<T, const N: usize> ArrayBuf<T, N> { | |
| pub const fn new() -> Self { | |
| Self::data_uninit() | |
| } | |
| pub const fn len(&self) -> usize { | |
| self.len | |
| } | |
| pub const fn is_empty(&self) -> bool { | |
| self.len == 0 | |
| } | |
| pub const fn is_not_empty(&self) -> bool { | |
| self.len > 0 | |
| } | |
| pub const fn is_not_full(&self) -> bool { | |
| self.len < N | |
| } | |
| pub const fn is_full(&self) -> bool { | |
| !self.is_not_full() | |
| } | |
| pub const unsafe fn set_len(&mut self, new_len: usize) { | |
| self.len = new_len; | |
| } | |
| pub const unsafe fn push_unchecked(&mut self, data: T) { | |
| debug_assert!(self.is_not_full()); | |
| let len = self.len; | |
| self.data[len] = MaybeUninit::new(data); | |
| self.set_len(len + 1); | |
| } | |
| pub const fn try_push(&mut self, data: T) -> Result<(), ManuallyDrop<T>> { | |
| if self.is_full() { | |
| Err(ManuallyDrop::new(data)) | |
| } else { | |
| unsafe { | |
| self.push_unchecked(data); | |
| Ok(()) | |
| } | |
| } | |
| } | |
| pub const fn push(&mut self, data: T) { | |
| match self.try_push(data) { | |
| Ok(_) => return, | |
| Err(_) => { | |
| panic!("ArrayBuf::push called trough an ArrayBuf already at maximum capacity!") | |
| } | |
| } | |
| } | |
| pub const fn first(&self) -> Option<&T> { | |
| if self.is_empty() { | |
| None | |
| } else { | |
| unsafe { | |
| let first_ptr = &self.data as *const _ as *const T; | |
| Some(&*first_ptr) | |
| } | |
| } | |
| } | |
| pub const unsafe fn pop_unchecked(&mut self) -> T | |
| where | |
| T: Copy, | |
| { | |
| debug_assert!(self.is_not_empty()); | |
| let len = self.len - 1; | |
| let data = &self.data[len] as *const _ as *const T; | |
| self.set_len(len); | |
| *data | |
| } | |
| pub const fn pop(&mut self) -> Option<T> | |
| where | |
| T: Copy | |
| { | |
| if self.is_not_empty() { | |
| Some(unsafe { self.pop_unchecked() }) | |
| } else { | |
| None | |
| } | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| const fn test_buf(first: i32) -> Option<i32> { | |
| let mut buf = ArrayBuf::<i32, 5>::new(); | |
| buf.push(first); | |
| buf.push(20); | |
| buf.push(30); | |
| buf.push(40); | |
| buf.push(50); | |
| match buf.pop() { | |
| Some(popped) => assert!(popped == 50), | |
| None => panic!(), | |
| } | |
| assert!(buf.len() == 4); | |
| match buf.first() { | |
| Some(n) => Some(*n), | |
| None => None, | |
| } | |
| } | |
| #[test] | |
| fn it_works() { | |
| const first: Option<i32> = test_buf(10); | |
| assert_eq!(first, Some(10)); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment