Implement `Array` type

This allows us to have fixed size arrays right on the stack
next
Sayan Nandan 3 years ago
parent 5ff045bd93
commit 0067b7d1b7

@ -0,0 +1,446 @@
/*
* Created on Tue Jul 06 2021
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2021, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#![allow(dead_code)] // TODO(@ohsayan): Remove this once we're done
use core::borrow::Borrow;
use core::borrow::BorrowMut;
use core::cmp::Ordering;
use core::fmt;
use core::hash::Hash;
use core::hash::Hasher;
use core::iter::FromIterator;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::mem::MaybeUninit;
use core::ops;
use core::ptr;
use core::slice;
use serde::{de::SeqAccess, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
pub struct Array<T, const N: usize> {
stack: [MaybeUninit<T>; N],
/// no stack should be more than 16 bytes
init_len: u16,
}
pub struct LenScopeGuard<'a, T: Copy> {
real_ref: &'a mut T,
temp: T,
}
impl<'a, T: ops::AddAssign + Copy> LenScopeGuard<'a, T> {
pub fn new(real_ref: &'a mut T) -> Self {
let ret = *real_ref;
Self {
real_ref,
temp: ret,
}
}
pub fn incr(&mut self, val: T) {
self.temp += val;
}
pub fn get_temp(&self) -> T {
self.temp
}
}
impl<'a, T: Copy> Drop for LenScopeGuard<'a, T> {
fn drop(&mut self) {
*self.real_ref = self.temp;
}
}
// defy the compiler
struct UninitArray<T, const N: usize>(PhantomData<fn() -> T>);
impl<T, const N: usize> UninitArray<T, N> {
const VALUE: MaybeUninit<T> = MaybeUninit::uninit();
const ARRAY: [MaybeUninit<T>; N] = [Self::VALUE; N];
}
impl<T, const N: usize> Array<T, N> {
pub const fn new() -> Self {
Array {
stack: UninitArray::ARRAY,
init_len: 0,
}
}
pub const fn len(&self) -> usize {
self.init_len as usize
}
pub const fn capacity(&self) -> usize {
N
}
pub const fn is_full(&self) -> bool {
N == self.len()
}
pub const fn remaining_cap(&self) -> usize {
self.capacity() - self.len()
}
pub unsafe fn set_len(&mut self, len: usize) {
self.init_len = len as u16; // lossy cast, we maintain all invariants
}
unsafe fn as_mut_ptr(&mut self) -> *mut T {
self.stack.as_mut_ptr() as *mut _
}
unsafe fn as_ptr(&self) -> *const T {
self.stack.as_ptr() as *const _
}
pub unsafe fn push_unchecked(&mut self, element: T) {
let len = self.len();
ptr::write(self.as_mut_ptr().add(len), element);
self.set_len(len + 1);
}
pub fn push(&mut self, element: T) -> Result<(), ()> {
if self.capacity() < self.len() {
// so we can push it in
unsafe { self.push_unchecked(element) };
Ok(())
} else {
Err(())
}
}
pub fn push_panic(&mut self, element: T) {
self.push(element).unwrap();
}
pub fn pop(&mut self) -> Option<T> {
if self.len() == 0 {
// nothing here
None
} else {
unsafe {
let new_len = self.len() - 1;
self.set_len(new_len);
// len - 1 == offset
Some(ptr::read(self.as_ptr().add(new_len)))
}
}
}
pub fn truncate(&mut self, new_len: usize) {
let len = self.len();
if new_len < len {
// we need to drop off a part of the array
unsafe {
// drop_in_place will handle the ZST invariant for us
ptr::drop_in_place(slice::from_raw_parts_mut(
self.as_mut_ptr().add(new_len),
len - new_len,
))
}
}
}
pub fn clear(&mut self) {
self.truncate(0)
}
pub fn extend_from_slice(&mut self, slice: &[T]) -> Result<(), ()>
where
T: Copy,
{
if self.remaining_cap() < slice.len() {
// no more space here
return Err(());
}
let self_len = self.len();
let other_len = slice.len();
unsafe {
ptr::copy_nonoverlapping(slice.as_ptr(), self.as_mut_ptr().add(self_len), other_len);
self.set_len(self_len + other_len);
}
Ok(())
}
pub fn into_array(self) -> Result<[T; N], Self> {
if self.len() < self.capacity() {
// not fully initialized
Err(self)
} else {
unsafe {
Ok({
// make sure we don't do a double free or end up deleting the elements
let _self = ManuallyDrop::new(self);
ptr::read(_self.as_ptr() as *const [T; N])
})
}
}
}
// these operations are incredibly safe because we only pass the initialized part
// of the array
fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
}
fn as_slice_mut(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) }
}
}
impl<T, const N: usize> ops::Deref for Array<T, N> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T, const N: usize> ops::DerefMut for Array<T, N> {
fn deref_mut(&mut self) -> &mut [T] {
self.as_slice_mut()
}
}
impl<T, const N: usize> From<[T; N]> for Array<T, N> {
fn from(array: [T; N]) -> Self {
// do not double-free or destroy the elements
let array = ManuallyDrop::new(array);
let mut arr = Array::<T, N>::new();
unsafe {
// copy it over
let ptr = &*array as *const [T; N] as *const [MaybeUninit<T>; N];
ptr.copy_to_nonoverlapping(&mut arr.stack as *mut [MaybeUninit<T>; N], 1);
arr.set_len(N);
}
arr
}
}
impl<T, const N: usize> Drop for Array<T, N> {
fn drop(&mut self) {
self.clear()
}
}
pub struct ArrayIntoIter<T, const N: usize> {
state: usize,
a: Array<T, N>,
}
impl<T, const N: usize> Iterator for ArrayIntoIter<T, N> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.state == self.a.len() {
// reached end
None
} else {
let idx = self.state;
self.state += 1;
Some(unsafe { ptr::read(self.a.as_ptr().add(idx)) })
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.a.len() - self.state;
(l, Some(l))
}
}
impl<T, const N: usize> IntoIterator for Array<T, N> {
type Item = T;
type IntoIter = ArrayIntoIter<T, N>;
fn into_iter(self) -> Self::IntoIter {
ArrayIntoIter { state: 0, a: self }
}
}
impl<T, const N: usize> Array<T, N> {
pub unsafe fn extend_from_iter_unchecked<I>(&mut self, iterable: I)
where
I: IntoIterator<Item = T>,
{
// the ptr to start writing from
let mut ptr = Self::as_mut_ptr(self).add(self.len());
let mut guard = LenScopeGuard::new(&mut self.init_len);
let mut iter = iterable.into_iter();
loop {
if let Some(element) = iter.next() {
// write the element
ptr.write(element);
// move to the next location
ptr = ptr.add(1);
// tell the guard to increment
guard.incr(1);
} else {
return;
}
}
}
}
impl<T, const N: usize> Extend<T> for Array<T, N> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
unsafe { self.extend_from_iter_unchecked(iter) }
}
}
impl<T, const N: usize> FromIterator<T> for Array<T, N> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut arr = Array::new();
arr.extend(iter);
arr
}
}
impl<T, const N: usize> Clone for Array<T, N>
where
T: Clone,
{
fn clone(&self) -> Self {
self.iter().cloned().collect()
}
}
impl<T, const N: usize> Hash for Array<T, N>
where
T: Hash,
{
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
Hash::hash(&**self, hasher)
}
}
impl<T, const N: usize> PartialEq for Array<T, N>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T, const N: usize> Eq for Array<T, N> where T: Eq {}
impl<T, const N: usize> PartialOrd for Array<T, N>
where
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T, const N: usize> Ord for Array<T, N>
where
T: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T, const CAP: usize> Borrow<[T]> for Array<T, CAP> {
fn borrow(&self) -> &[T] {
self
}
}
impl<T, const CAP: usize> BorrowMut<[T]> for Array<T, CAP> {
fn borrow_mut(&mut self) -> &mut [T] {
self
}
}
impl<T, const CAP: usize> AsRef<[T]> for Array<T, CAP> {
fn as_ref(&self) -> &[T] {
self
}
}
impl<T, const CAP: usize> AsMut<[T]> for Array<T, CAP> {
fn as_mut(&mut self) -> &mut [T] {
self
}
}
impl<T, const CAP: usize> fmt::Debug for Array<T, CAP>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T, const N: usize> Serialize for Array<T, N>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_seq(self.iter())
}
}
struct ArrayVisitor<T, const N: usize> {
_marker: PhantomData<Array<T, N>>,
}
impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor<T, N>
where
T: Deserialize<'de>,
{
type Value = Array<T, N>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a sequence")
}
fn visit_seq<B>(self, mut seq: B) -> Result<Self::Value, B::Error>
where
B: SeqAccess<'de>,
{
let len = seq.size_hint().unwrap_or(0);
if len > N {
return Err(serde::de::Error::custom("Bad length"));
}
let mut array = Array::new();
while let Some(item) = seq.next_element()? {
unsafe {
// UNSAFE(@ohsayan): This is completely safe because we have checked len
array.push_unchecked(item)
}
}
Ok(array)
}
}
impl<'de, T, const N: usize> Deserialize<'de> for Array<T, N>
where
T: Deserialize<'de>,
{
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_seq(ArrayVisitor {
_marker: PhantomData,
})
}
}
#[test]
fn test_basic() {
let mut b: Array<u8, 11> = Array::new();
b.extend_from_slice("Hello World".as_bytes()).unwrap();
assert_eq!(
b,
Array::from([b'H', b'e', b'l', b'l', b'o', b' ', b'W', b'o', b'r', b'l', b'd'])
);
}

@ -26,12 +26,14 @@
#![allow(dead_code)] // TODO(@ohsayan): Remove this once we're done #![allow(dead_code)] // TODO(@ohsayan): Remove this once we're done
use crate::coredb::array::LenScopeGuard;
use core::alloc::Layout; use core::alloc::Layout;
use core::borrow::Borrow; use core::borrow::Borrow;
use core::borrow::BorrowMut; use core::borrow::BorrowMut;
use core::cmp; use core::cmp;
use core::fmt; use core::fmt;
use core::hash::{self, Hash}; use core::hash::{self, Hash};
use core::iter::FromIterator;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::mem; use core::mem;
use core::mem::ManuallyDrop; use core::mem::ManuallyDrop;
@ -505,33 +507,6 @@ impl<A: MemoryBlock> Drop for IArray<A> {
} }
} }
pub struct LenScopeGuard<'a> {
real_ref: &'a mut usize,
temp: usize,
}
impl<'a> LenScopeGuard<'a> {
pub fn new(real_ref: &'a mut usize) -> Self {
let ret = *real_ref;
Self {
real_ref,
temp: ret,
}
}
pub fn incr(&mut self) {
self.temp += 1;
}
pub fn get_temp(&self) -> usize {
self.temp
}
}
impl<'a> Drop for LenScopeGuard<'a> {
fn drop(&mut self) {
*self.real_ref = self.temp;
}
}
impl<A: MemoryBlock> Extend<A::LayoutItem> for IArray<A> { impl<A: MemoryBlock> Extend<A::LayoutItem> for IArray<A> {
fn extend<I: IntoIterator<Item = A::LayoutItem>>(&mut self, iterable: I) { fn extend<I: IntoIterator<Item = A::LayoutItem>>(&mut self, iterable: I) {
let mut iter = iterable.into_iter(); let mut iter = iterable.into_iter();
@ -545,7 +520,7 @@ impl<A: MemoryBlock> Extend<A::LayoutItem> for IArray<A> {
while len.get_temp() < cap { while len.get_temp() < cap {
if let Some(out) = iter.next() { if let Some(out) = iter.next() {
ptr::write(data_ptr.add(len.get_temp()), out); ptr::write(data_ptr.add(len.get_temp()), out);
len.incr(); len.incr(1);
} else { } else {
return; return;
} }
@ -608,6 +583,23 @@ where
} }
} }
impl<A: MemoryBlock> FromIterator<A::LayoutItem> for IArray<A> {
fn from_iter<I: IntoIterator<Item = A::LayoutItem>>(iter: I) -> Self {
let mut iarray = IArray::new();
iarray.extend(iter);
iarray
}
}
impl<'a, A: MemoryBlock> From<&'a [A::LayoutItem]> for IArray<A>
where
A::LayoutItem: Clone,
{
fn from(slice: &'a [A::LayoutItem]) -> Self {
slice.iter().cloned().collect()
}
}
// impl ser/de // impl ser/de
impl<A: MemoryBlock> Serialize for IArray<A> impl<A: MemoryBlock> Serialize for IArray<A>
where where
@ -658,6 +650,9 @@ where
} }
} }
unsafe impl<A: MemoryBlock> Send for IArray<A> where A::LayoutItem: Send {}
unsafe impl<A: MemoryBlock> Sync for IArray<A> where A::LayoutItem: Sync {}
#[test] #[test]
fn test_equality() { fn test_equality() {
let mut x = IArray::new_bytearray(); let mut x = IArray::new_bytearray();

@ -42,6 +42,7 @@ pub mod htable;
pub mod iarray; pub mod iarray;
pub mod lazy; pub mod lazy;
pub mod lock; pub mod lock;
pub mod array;
mod memstore; mod memstore;
/// This is a thread-safe database handle, which on cloning simply /// This is a thread-safe database handle, which on cloning simply

Loading…
Cancel
Save