Last active
October 30, 2020 07:26
-
-
Save ohAitch/8301841ac7aa58aacf9acdac24a60e0d to your computer and use it in GitHub Desktop.
Kinda in the middle of figuring out the Either<Index,u32> aspect of the data representation
This file contains 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_panic)] | |
#![feature(const_in_array_repeat_expressions)] | |
#![feature(array_map)] | |
#![feature(untagged_unions,min_const_generics)] | |
use std::convert::{TryInto,TryFrom}; | |
use std::mem; | |
use core::fmt; | |
use core::fmt::{Debug}; | |
use core::ops::{IndexMut}; | |
#[macro_use] | |
mod sgbr { | |
#![allow(unused_macros)] | |
pub struct Sgbr<F: Fn()>(pub F); | |
impl<F: Fn()> Drop for Sgbr<F> { fn drop(&mut self){ | |
if std::thread::panicking() { self.0() } | |
} } | |
macro_rules! sgbr { | |
($($data:tt)*) => { let _raii = $crate::sgbr::Sgbr(|| {eprintln!($($data)*);});}; | |
} | |
} | |
//TODO 4096 maybe | |
const PAGE_SIZE: usize = 64; | |
const PAGES: usize = 1024; | |
type Elem = u32; | |
#[derive(Clone, Copy)] | |
struct Index(u16,u8,u8); | |
impl Index { | |
fn of(a:usize, b:usize, c:usize)-> Self { Self(a.try_into().unwrap(), b.try_into().unwrap(), c.try_into().unwrap())} | |
fn of32(a:u32, b:u32, c:u32)-> Self { Self(a.try_into().unwrap(), b.try_into().unwrap(), c.try_into().unwrap())} | |
} | |
impl std::ops::Add<u8> for Index { | |
type Output = Self; fn add(self, ofs: u8)-> Self { let mut s = self; s.2 += ofs; s } | |
} | |
impl std::ops::Sub<u8> for Index { | |
type Output = Self; fn sub(self, ofs: u8)-> Self { let mut s = self; s.2 -= ofs; s } | |
} | |
impl Debug for Index { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
write!(f,"{}-{}-{}", self.0, self.1, self.2) | |
} | |
} | |
impl Into<Elem> for Index { fn into(self) -> Elem { (self.0 as u32)*0x1_0000 ^ (self.1 as u32)*0x100 ^ (self.2 as u32) } } | |
impl From<Elem> for Index { fn from(e: Elem) -> Self { Self::of32(e>>16, e>>8 & 0xff, e&0xff) } } | |
//NOTE not strictly speaking cloneable, ideally this would be a sealed bit-clone | |
// trait that was only used by the allocator | |
#[derive(Clone,Default, Debug)] | |
struct Pair([Elem; 2]); | |
type List<const LEN: usize> = List_<[Elem; LEN]>; | |
#[derive(Clone, Debug)] | |
struct List_<T: ?Sized>{ //NOTE secretly, LEN: u8 | |
tail: Index, | |
used: u8, | |
data: T, | |
} | |
impl<T: Copy> List_<[T]>{ | |
fn overwrite<const LEN: usize>(&mut self, other: &List_<[T; LEN]>){ | |
assert!(self.data.len() == other.data.len()); | |
self.tail = other.tail; | |
self.used = other.used; | |
self.data.copy_from_slice(&other.data); | |
} | |
} | |
trait Meta { | |
fn len(&self)-> u8; fn list(&self)-> Option<&List_<[Elem]>>; | |
fn fits(&self, ix: Index)-> bool {(0..self.len()).contains(&ix.2)} | |
fn used(&self)-> u8 {if let Some(l) = self.list() {l.used} else {self.len()}} | |
fn is_used(&self, ix: Index)-> bool { | |
(0..self.used()).contains(&ix.2) | |
} | |
} | |
impl Meta for Pair { | |
fn len(&self)-> u8 {2} | |
fn list(&self)->Option<&List_<[Elem]>> { None } | |
} | |
impl<const N: usize> Meta for List<N> { | |
fn len(&self)-> u8 {N as u8} | |
fn list(&self)->Option<&List_<[Elem]>> { Some(self) } | |
} | |
trait Data { | |
fn data(&self)-> &[Elem]; | |
fn get_elem(&self, ix: Index)-> &Elem { &self.data()[ix.2 as usize]} | |
} | |
impl Data for Pair { fn data(&self)->&[Elem] { &self.0 } } | |
impl<const T: usize> Data for List<T> { | |
fn data(&self)->&[Elem] { &self.data } | |
} | |
trait DataMut: Data { fn data_mut(&mut self)-> &mut[Elem]; } | |
impl DataMut for Pair { fn data_mut(&mut self)->&mut[Elem] { &mut self.0 } } | |
impl<const T: usize> DataMut for List<T> { | |
fn data_mut(&mut self)->&mut[Elem] { &mut self.data } | |
} | |
trait Use { fn use_idx(&mut self, i: u8);} | |
impl Use for Pair {fn use_idx(&mut self, _:u8){}} | |
impl<const T: usize> Use for List<T>{ | |
fn use_idx(&mut self, i:u8){ | |
assert!(i<=self.used, "i={} self={:?}", i, self); | |
self.used=self.used.max(i+1) | |
} | |
} | |
#[derive(Copy, Clone, PartialEq, Debug)] | |
enum PageType {Pair, List2, List6, List14} //, List30, List62, List126, List254} | |
trait Taggable { fn tag(&self)-> PageType; } | |
impl Taggable for Pair { fn tag(&self)->PageType { PageType::Pair } } | |
impl Taggable for List<2> { fn tag(&self)->PageType { PageType::List2 } } | |
impl Taggable for List<6> { fn tag(&self)->PageType { PageType::List6 } } | |
impl Taggable for List<14> { fn tag(&self)->PageType { PageType::List14 } } | |
trait Container: Meta + Data + DataMut + Taggable + Use + Debug {} | |
impl<T: Meta+Data+DataMut+Taggable+Use+Debug> Container for T {} | |
impl PageType { | |
const fn max_idx(self)-> u8 { | |
let size = match self { | |
PageType::Pair => PAGE_SIZE/mem::size_of::<Pair>(), | |
PageType::List2 => PAGE_SIZE/mem::size_of::<List<2>>(), | |
PageType::List6 => PAGE_SIZE/mem::size_of::<List<6>>(), | |
PageType::List14 => PAGE_SIZE/mem::size_of::<List<14>>(), | |
}; | |
if size-1 > u8::MAX as usize { panic!("TODO u10 page indexes") }; | |
(size-1) as u8 | |
} | |
const fn next(self)-> Self { | |
match self { | |
PageType::Pair => PageType::List2, PageType::List2 => PageType::List6, | |
PageType::List6 => PageType::List14, PageType::List14 => PageType::List14, | |
} | |
} | |
} | |
macro_rules! page_arr { | |
($T:ty) => { | |
[[$T; PAGE_SIZE/mem::size_of::<$T>()]; PAGES] | |
} | |
} | |
union Pages { | |
pairs: page_arr!(Pair), | |
list2: page_arr!(List<2>), | |
list6: page_arr!(List<6>), | |
list14: page_arr!(List<14>), | |
/*list30: page_arr!(List<30>), | |
list62: page_arr!(List<62>), | |
list126: page_arr!(List<126>), | |
list254: page_arr!(List<254>),*/ | |
} | |
struct Store { | |
free: [bool; PAGES], //FIXME bitvec | |
full: [bool; PAGES], //FIXME bitvec | |
types: [Option<PageType>; PAGES], //TODO this is an arrayvec honestly | |
//FIXME u4 + hashtable backing? u8 + hashtable backing? also like sparsity / types mb | |
rc: [[u16; PAGE_SIZE/mem::size_of::<Pair>()]; PAGES], | |
pages: Pages, | |
} | |
impl core::ops::Index<Index> for Store { | |
type Output = Elem; | |
fn index(&self, ix: Index)-> &Elem { &self.grab(ix).unwrap().data()[ix.2 as usize] } | |
} | |
impl IndexMut<Index> for Store { | |
fn index_mut(&mut self, ix: Index)-> &mut Elem { | |
&mut self.grab_mut(ix).unwrap().data_mut()[ix.2 as usize] | |
} | |
} | |
impl Store { | |
fn alloc_page(&mut self, t: PageType) -> u16 { | |
let i = self.free.iter().position(|x| *x).expect("OOM"); | |
self.free[i] = false; | |
self.types[i] = Some(t); | |
u16::try_from(i).unwrap() | |
} | |
fn alloc(&mut self, t: PageType)-> Index { | |
for (page,full) in self.full.iter_mut().enumerate().filter(|(_,full)| !**full){ | |
match self.types[page] { | |
None => { | |
self.free[page] = false; | |
self.types[page] = Some(t); | |
return self.gain(Index::of(page,0,0)) | |
}, | |
Some(tne) if tne != t => continue, | |
Some(_) => { | |
let i: usize = self.rc[page].iter().position(|x|*x==0).expect("page was not full"); | |
let last: usize = t.max_idx().into(); | |
if !(0..=last).contains(&(i+1)) {*full=true}; | |
return self.gain(Index::of(page,i,0)) | |
}, | |
} | |
} | |
panic!("OOM") | |
} | |
fn gain(&mut self, idx: Index)-> Index { | |
let Index(page, list, _item) = idx; | |
self.rc[page as usize][list as usize] += 1; | |
// eprintln!("{} gain {:?}",page, self.rc[page as usize]); | |
idx | |
} | |
const fn new()-> Self { | |
// let mut self_ = | |
const W: usize = PAGE_SIZE/mem::size_of::<Pair>(); | |
const ZEROED: Pair = Pair([0,0]); | |
Self { | |
types: [None; PAGES], | |
pages: Pages { pairs: [[ZEROED; W]; PAGES] }, // basically mem::zeroed() | |
rc: [[0; W]; PAGES], | |
free: [true; PAGES], | |
full: [false; PAGES], | |
} | |
} | |
// fn lookup(page:&[impl Container], at: usize) -> &impl Container { | |
// &page[at] | |
// } | |
fn grab(&self, ix: Index)-> Option<&dyn Container> { Some({ | |
let Index(page, list, _item) = ix; | |
if self.rc[page as usize][list as usize] == 0 { None? }; | |
//SAFETY: as long as the types don't lie | |
match self.types[page as usize]? { | |
PageType::Pair => unsafe {&self.pages.pairs[page as usize][list as usize]}, | |
PageType::List2 => unsafe {&self.pages.list2[page as usize][list as usize]}, | |
PageType::List6 => unsafe {&self.pages.list6[page as usize][list as usize]}, | |
PageType::List14 => unsafe {&self.pages.list14[page as usize][list as usize]}, | |
} | |
}) | |
} | |
fn grab_mut(&mut self, ix: Index)-> Option<&mut dyn Container> { Some({ | |
let Index(page, list, _item) = ix; | |
//SAFETY: as long as the types don't lie | |
match self.types[page as usize]? { | |
PageType::Pair => unsafe {&mut self.pages.pairs[page as usize][list as usize]}, | |
PageType::List2 => unsafe {&mut self.pages.list2[page as usize][list as usize]}, | |
PageType::List6 => unsafe {&mut self.pages.list6[page as usize][list as usize]}, | |
PageType::List14 => unsafe {&mut self.pages.list14[page as usize][list as usize]}, | |
} | |
}) | |
} | |
fn grab_mut_list(&mut self, ix: Index)-> Option<&mut List_<[Elem]>> { Some({ | |
let Index(page, list, _item) = ix; | |
//SAFETY: as long as the types don't lie | |
match self.types[page as usize]? { | |
PageType::Pair => None?, // Not a list | |
PageType::List2 => unsafe {&mut self.pages.list2[page as usize][list as usize]}, | |
PageType::List6 => unsafe {&mut self.pages.list6[page as usize][list as usize]}, | |
PageType::List14 => unsafe {&mut self.pages.list14[page as usize][list as usize]}, | |
} | |
}) | |
} | |
fn copy(&mut self, from: Index, to: Index){ | |
fn do_copy<const N: usize>(arr: &mut [[impl Clone; N]], from: Index, to: Index){ | |
arr[to.0 as usize][to.1 as usize] = arr[from.0 as usize][from.1 as usize].clone() | |
} | |
assert!(self.types[from.0 as usize] == self.types[to.0 as usize]); | |
//SAFETY: as long as the types don't lie | |
match self.types[from.0 as usize].unwrap() { | |
PageType::Pair => unsafe {do_copy(&mut self.pages.pairs, from, to)}, | |
PageType::List2 => unsafe {do_copy(&mut self.pages.list2, from, to)}, | |
PageType::List6 => unsafe {do_copy(&mut self.pages.list6, from, to)}, | |
PageType::List14 => unsafe {do_copy(&mut self.pages.list14, from, to)}, | |
} | |
} | |
fn car(&self, ix: Index)-> Option<Elem>{ | |
let Index(_, _, item) = ix; | |
let cell = self.grab(ix)?; | |
if !cell.is_used(ix) { return None }; | |
if item == 0 { cell.list()?; }; // Pair has no tail | |
Some(cell.data()[item as usize]) | |
} | |
fn cdr(&self, ix: Index)-> Option<Result<Index,Elem>>{ Some({ | |
let Index(_, _, item) = ix; | |
if item > 0 {Ok(ix - 1)} | |
else { | |
let cell = self.grab(ix)?; | |
if !cell.is_used(ix) { None? }; | |
if let Some(list) = cell.list() { Ok(list.tail)} | |
else { Err(cell.data()[0]) } | |
} | |
})} | |
fn pair(&mut self, car: Elem, cdr: Elem)-> Index { | |
let n = self.alloc(PageType::Pair); | |
self[n] = cdr; | |
self[n+1] = car; | |
n+1 | |
} | |
fn cons(&mut self, car: Elem, ix: Index)-> Option<Index>{ Some({ | |
// #[derive(Debug)] struct Cons(Elem,Index); eprint!("{:?} ",Cons(car,ix)); | |
let cdr = self.grab(ix)?; | |
let next: Index = { | |
if ix.2 < u8::MAX && cdr.fits(ix+1) && | |
(!cdr.is_used(ix+1) || *cdr.get_elem(ix+1) == car) { | |
self.gain(ix+1) | |
} else if ix.2 == 0 && cdr.tag() == PageType::Pair { | |
// NOTE in theory shouldn't happen, you'd just use the elem | |
let n = self.alloc(PageType::Pair); | |
self.copy(ix,n); | |
n+1 | |
} else { | |
let tag = cdr.tag().next(); | |
let n = self.alloc(tag); | |
self.grab_mut_list(n).unwrap().tail = ix; | |
n | |
} | |
}; | |
self[next] = car; | |
self.grab_mut(next).unwrap().use_idx(next.2); | |
next | |
})} | |
// fn empty(&self, mut ix: Index)-> Option<bool> { | |
// loop { | |
// if self.car(ix)? != 0 { | |
// return Some(false); | |
// } | |
// match self.cdr(ix){ | |
// None => return Some(true), | |
// Some(iix) => ix = iix | |
// } | |
// } | |
// } | |
fn grab_page(&self, page: u16)-> Option<impl Iterator<Item=&dyn Container>+Clone> { Some({ | |
let p = page as usize; | |
std::iter::empty() | |
.chain(( | |
match self.types[p]? { | |
PageType::Pair => unsafe {&self.pages.pairs[p][..]}, _ => &[] | |
}).iter().map(|x|->&dyn Container {x})) | |
.chain(( | |
match self.types[p]? { | |
PageType::List2 => unsafe {&self.pages.list2[p][..]}, _ => &[] | |
}).iter().map(|x|->&dyn Container {x})) | |
.chain(( | |
match self.types[p]? { | |
PageType::List6 => unsafe {&self.pages.list6[p][..]}, _ => &[] | |
}).iter().map(|x|->&dyn Container {x})) | |
.chain(( | |
match self.types[p]? { | |
PageType::List14 => unsafe {&self.pages.list14[p][..]}, _ => &[] | |
}).iter().map(|x|->&dyn Container {x})) | |
// | |
// PageType::List2 => unsafe {&self.pages.list2[page as usize].iter().map(|x|->&dyn Container {x})}, | |
// PageType::List6 => unsafe {&self.pages.list6[page as usize].iter().map(|x|->&dyn Container {x})}, | |
// } | |
})} | |
fn get_iter(&self, i: Index) -> impl Iterator<Item=Elem> + Clone + '_ { | |
IndexIterator {store: &self, idx:Some(i)} | |
} | |
fn non_free_pages(&self) -> impl Iterator<Item=usize> + Clone + '_ { | |
self.free.iter().enumerate().filter(|(_,&x)| !x).map(|(i,_)|i) | |
} | |
} | |
struct Nullable<T>(Option<T>); | |
impl<T:Debug> Debug for Nullable<T> { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
Ok(if let Some(x) = &self.0 { x.fmt(f)? }) | |
} | |
} | |
#[derive(Debug)] | |
struct DebugRow<'a, T: Debug + Sized>{ | |
ty: PageType, | |
rc: &'a [u16], | |
x: T, | |
} | |
struct Listerator<D: Debug, I: Clone + Iterator<Item=D>>(I); | |
impl<D: Debug, I: Clone + Iterator<Item=D>> Debug for Listerator<D,I> { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
f.debug_list().entries(self.0.clone()).finish() | |
} | |
} | |
struct Maperator<K: Debug, V: Debug, I: Clone + Iterator<Item=(K,V)>>(I); | |
impl<K: Debug, V: Debug, I: Clone + Iterator<Item=(K,V)>> Debug for Maperator<K,V,I> { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
f.debug_map().entries(self.0.clone()).finish() | |
} | |
} | |
struct Lined<T>(T); | |
impl<T: Debug> Debug for Lined<T> { | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
self.0.fmt(f)?; | |
write!(f,"\n") | |
} | |
} | |
impl<T: Clone> Clone for Lined<T> {fn clone(&self) -> Self { Self(self.0.clone())}} | |
#[derive(Clone)] | |
struct IndexIterator<'a> { | |
store: &'a Store, | |
idx: Option<Index> | |
} | |
impl Iterator for IndexIterator<'_> { | |
type Item = Elem; | |
fn next(&mut self)-> Option<Elem>{ | |
let a = self.store.car(self.idx?); | |
let idx = self.store.cdr(self.idx?)?; | |
self.idx = idx.ok(); | |
match (a,idx) { | |
(Some(_), Ok(_))=> a, | |
(None, Err(e))=> Some(e), | |
_ => panic!("yo") | |
} | |
} | |
} | |
impl Debug for Store { | |
fn fmt<'a>(&'a self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
f.debug_map().entries( | |
self.non_free_pages().map(|i|{ | |
let page = self.grab_page(i as u16).unwrap(); | |
let page = page.enumerate().filter(move |(j,_)| self.rc[i][*j] != 0); | |
(i, Lined(DebugRow { | |
ty: self.types[i].unwrap(), | |
rc: &self.rc[i], | |
x: Maperator(page) | |
})) | |
}) | |
).finish() | |
} | |
} | |
struct Elems<'a>(&'a Store); | |
// impl AsRef<Store> for Elems<'_> { fn as_ref(&self)-> &Store {&self.0}} | |
impl Debug for Elems<'_> { | |
fn fmt<'a>(&'a self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
Maperator( | |
// (&[(1,2)]).iter().cloned() | |
self.0.non_free_pages().flat_map(|i|{ | |
let page = self.0.grab_page(i as u16).unwrap(); | |
let page = (0..page.count()).into_iter().filter(move |j| self.0.rc[i][*j] != 0); | |
page.flat_map(move |j|{ | |
let max = self.0.grab(Index::of(i,j,0)).unwrap().used().into(); | |
(0..max).into_iter().map( | |
move |k| (Index::of(i,j,k), Lined(Listerator(self.0.get_iter(Index::of(i,j,k))))) | |
) | |
}) | |
}) | |
).fmt(f) | |
} | |
} | |
macro_rules! nodbg { ($e:expr)=> {$e} } | |
fn _size<T>(_:T)-> usize { mem::size_of::<T>()} | |
fn main(){ | |
// dbg!(mem::size_of::<PageType>()); | |
let mut store = Box::new(Store::new()); | |
nodbg!(store.alloc_page(PageType::Pair)); | |
dbg!(store.alloc_page(PageType::List2)); | |
dbg!(store.pair(2,1)); | |
if true { | |
dbg!(store.car(Index(0,0,1)),store.cdr(Index(0,0,1))); | |
dbg!(store.car(Index(0,0,0)),store.cdr(Index(0,0,0))); | |
} | |
for _ in 1..10 { | |
store.alloc_page(PageType::Pair); | |
} | |
{ | |
dbg!(store.alloc_page(PageType::List2)); | |
dbg!(store.cons(2, Index(0,0,0))); | |
let pairs = format!{"{:?}", unsafe {&store.pages.pairs[0][..4]}}; dbg!(pairs); | |
let c3 = dbg!(store.cons(3, Index(0,0,0))).unwrap(); | |
let pairs = format!{"{:?}", unsafe {&store.pages.pairs[0][..4]}}; dbg!(pairs); | |
dbg!(Listerator(store.get_iter(c3))); | |
let c4 = dbg!(store.cons(4, c3)).unwrap(); | |
dbg!(Listerator(store.get_iter(c4)), store.grab(c4)); | |
// if true {return ()}; | |
let pairs = format!{"{:?}", unsafe {&store.pages.pairs[0][..4]}}; dbg!(pairs); | |
dbg!(store.alloc(PageType::Pair)); | |
nodbg!(unsafe{store.pages.pairs.len()}); | |
// dbg!(store.empty(Index(0,0,0))); | |
// dbg!({let l = store.pair(0,0); store.empty(l)}); | |
// dbg!(store.empty(Index(1,0,0))); | |
dbg!(format!{"{:?}", &store.rc[0]}); | |
let l6 = nodbg!(store.alloc(PageType::List6)); | |
let l6d = List::<6>{ tail: c3, used: 3, data: [4,5,6,77,0,0]}; | |
store.grab_mut_list(l6).unwrap().overwrite(&l6d); | |
// dbg!(store.grab(Index(0,0,0)), store.grab(Index(0,4,0)), store.grab(Index(1,0,2))); | |
// dbg!(size(store.types)); | |
// dbg!(mem::size_of::<Index>()); | |
// dbg!([PageType::Pair, PageType::List2, PageType::List6].map(|a|{mem::discriminant(&a)})); | |
store.gain(Index(3,3,0)); unsafe {store.pages.pairs[3][3] = Pair([13,13])}; | |
} | |
// eprintln!("{:?}",store); | |
// eprintln!("{:?}",Elems(&store)); | |
{ | |
let mut l = store.pair(18,19); | |
for i in (1..=17).rev() { | |
l = store.cons(i,l).unwrap() | |
} | |
eprintln!("l = {:?}",Listerator(store.get_iter(l))); | |
} | |
println!("{:?}",store); | |
println!("{:?}",Elems(&store)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment