Skip to content

Instantly share code, notes, and snippets.

@DutchGhost
Last active January 13, 2020 21:22
Show Gist options
  • Select an option

  • Save DutchGhost/30a67cd79b939c649c7c06119f30033e to your computer and use it in GitHub Desktop.

Select an option

Save DutchGhost/30a67cd79b939c649c7c06119f30033e to your computer and use it in GitHub Desktop.
Const buffers
#![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