use std::cell::{Cell, UnsafeCell};
use std::fmt::{self, Debug};
use std::iter::FromIterator;
use std::ops::Index;
const FIRST_CHUNK_SIZE: usize = 16;
pub struct AppendList<T> {
chunks: UnsafeCell<Vec<Vec<T>>>,
len: Cell<usize>,
}
impl<T> AppendList<T> {
fn chunks(&self) -> &[Vec<T>] {
unsafe { &*self.chunks.get() }
}
fn check_invariants(&self) {
#[cfg(test)]
{
if self.len.get() > 0 {
assert_eq!(index_chunk(self.len.get() - 1), self.chunks().len() - 1);
for chunk_id in 0..self.chunks().len() {
assert!(chunk_size(chunk_id) <= self.chunks()[chunk_id].capacity());
}
for chunk_id in 0..self.chunks().len() - 1 {
assert_eq!(chunk_size(chunk_id), self.chunks()[chunk_id].len());
}
assert_eq!(
self.chunks().last().unwrap().len(),
self.len.get() - chunk_start(self.chunks().len() - 1)
);
} else {
assert_eq!(0, self.chunks().len());
}
}
}
pub fn new() -> Self {
Self {
chunks: UnsafeCell::new(Vec::new()),
len: Cell::new(0),
}
}
pub fn push(&self, item: T) {
self.check_invariants();
let mut_chunks = unsafe { &mut *self.chunks.get() };
let new_index = self.len.get();
let chunk_id = index_chunk(new_index);
if chunk_id < mut_chunks.len() {
debug_assert_eq!(chunk_id, mut_chunks.len() - 1);
let chunk = &mut mut_chunks[chunk_id];
#[cfg(test)]
let prev_capacity = chunk.capacity();
chunk.push(item);
#[cfg(test)]
assert_eq!(prev_capacity, chunk.capacity());
} else {
debug_assert_eq!(chunk_id, mut_chunks.len());
let mut new_chunk = Vec::with_capacity(chunk_size(chunk_id));
debug_assert!(new_chunk.capacity() >= chunk_size(chunk_id));
new_chunk.push(item);
mut_chunks.push(new_chunk);
}
self.len.set(self.len.get() + 1);
self.check_invariants();
}
pub fn len(&self) -> usize {
self.check_invariants();
self.len.get()
}
pub fn get(&self, index: usize) -> Option<&T> {
self.check_invariants();
if index >= self.len() {
return None;
}
let chunk_id = index_chunk(index);
let chunk_start = chunk_start(chunk_id);
return Some(&self.chunks()[chunk_id][index - chunk_start]);
}
pub fn iter(&self) -> Iter<T> {
self.check_invariants();
Iter {
list: &self,
index: 0,
}
}
}
const fn chunk_size(chunk_id: usize) -> usize {
FIRST_CHUNK_SIZE << chunk_id
}
const fn chunk_start(chunk_id: usize) -> usize {
chunk_size(chunk_id) - FIRST_CHUNK_SIZE
}
const fn index_chunk(index: usize) -> usize {
floor_log2(index + FIRST_CHUNK_SIZE) - floor_log2(FIRST_CHUNK_SIZE)
}
#[inline]
const fn floor_log2(x: usize) -> usize {
const BITS_PER_BYTE: usize = 8;
BITS_PER_BYTE * std::mem::size_of::<usize>() - (x.leading_zeros() as usize) - 1
}
impl<T> Default for AppendList<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Index<usize> for AppendList<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
self.get(index)
.expect("AppendList indexed beyond its length")
}
}
impl<T> FromIterator<T> for AppendList<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let list = Self::new();
for item in iter {
list.push(item);
}
list
}
}
impl<'l, T> IntoIterator for &'l AppendList<T> {
type Item = &'l T;
type IntoIter = Iter<'l, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T: PartialEq> PartialEq for AppendList<T> {
fn eq(&self, other: &AppendList<T>) -> bool {
let mut s = self.iter();
let mut o = other.iter();
loop {
match (s.next(), o.next()) {
(Some(a), Some(b)) if a == b => {},
(None, None) => return true,
_ => return false,
}
}
}
}
impl<T: Debug> Debug for AppendList<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_list().entries(self.iter()).finish()
}
}
pub struct Iter<'l, T> {
list: &'l AppendList<T>,
index: usize,
}
impl<'l, T> Iterator for Iter<'l, T> {
type Item = &'l T;
fn next(&mut self) -> Option<Self::Item> {
let item = self.list.get(self.index);
self.index += 1;
item
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.list.len() - self.index;
(remaining, Some(remaining))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn from_iterator() {
let l: AppendList<i32> = (0..100).collect();
for i in 0..100 {
assert_eq!(l[i], i as i32);
}
}
#[test]
fn iterator() {
let l: AppendList<i32> = (0..100).collect();
let mut i1 = l.iter();
let mut i2 = l.into_iter();
for item in 0..100 {
assert_eq!(i1.next(), Some(&item));
assert_eq!(i2.next(), Some(&item));
}
assert_eq!(i1.next(), None);
assert_eq!(i2.next(), None);
}
#[test]
fn equality() {
let a = AppendList::new();
let b = AppendList::new();
assert_eq!(a, b);
a.push("foo");
assert_ne!(a, b);
b.push("foo");
assert_eq!(a, b);
a.push("bar");
a.push("baz");
assert_ne!(a, b);
}
#[test]
fn iterator_size_hint() {
let l: AppendList<i32> = AppendList::new();
let mut i = l.iter();
assert_eq!(i.size_hint(), (0, Some(0)));
l.push(1);
assert_eq!(i.size_hint(), (1, Some(1)));
l.push(2);
assert_eq!(i.size_hint(), (2, Some(2)));
i.next();
assert_eq!(i.size_hint(), (1, Some(1)));
l.push(3);
assert_eq!(i.size_hint(), (2, Some(2)));
i.next();
assert_eq!(i.size_hint(), (1, Some(1)));
i.next();
assert_eq!(i.size_hint(), (0, Some(0)));
}
#[test]
fn chunk_sizes_make_sense() {
assert_eq!(chunk_size(0), FIRST_CHUNK_SIZE);
let mut index = 0;
for chunk in 0..20 {
assert_eq!(chunk_start(chunk), index);
index += chunk_size(chunk);
}
}
#[test]
fn index_chunk_matches_up() {
for index in 0..1_000_000 {
let chunk_id = index_chunk(index);
assert!(index >= chunk_start(chunk_id));
assert!(index < chunk_start(chunk_id) + chunk_size(chunk_id));
}
}
#[test]
fn empty_list() {
let n: AppendList<usize> = AppendList::new();
assert_eq!(n.len(), 0);
assert_eq!(n.get(0), None);
let d: AppendList<usize> = AppendList::default();
assert_eq!(d.len(), 0);
assert_eq!(d.get(0), None);
}
#[test]
fn thousand_item_list() {
test_big_list(1_000);
}
#[test]
#[ignore]
fn million_item_list() {
test_big_list(1_000_000);
}
fn test_big_list(size: usize) {
let l = AppendList::new();
let mut refs = Vec::new();
for i in 0..size {
assert_eq!(l.len(), i);
l.push(i);
refs.push(l[i]);
assert_eq!(l.len(), i + 1);
}
for i in 0..size {
assert_eq!(Some(&refs[i]), l.get(i));
}
}
}