Implement `Array` type
This allows us to have fixed size arrays right on the stacknext
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'])
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue