diff --git a/server/src/coredb/array.rs b/server/src/coredb/array.rs new file mode 100644 index 00000000..42c45d5b --- /dev/null +++ b/server/src/coredb/array.rs @@ -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 + * + * 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 . + * +*/ + +#![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 { + stack: [MaybeUninit; 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(PhantomData T>); + +impl UninitArray { + const VALUE: MaybeUninit = MaybeUninit::uninit(); + const ARRAY: [MaybeUninit; N] = [Self::VALUE; N]; +} + +impl Array { + 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 { + 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 ops::Deref for Array { + type Target = [T]; + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl ops::DerefMut for Array { + fn deref_mut(&mut self) -> &mut [T] { + self.as_slice_mut() + } +} + +impl From<[T; N]> for Array { + fn from(array: [T; N]) -> Self { + // do not double-free or destroy the elements + let array = ManuallyDrop::new(array); + let mut arr = Array::::new(); + unsafe { + // copy it over + let ptr = &*array as *const [T; N] as *const [MaybeUninit; N]; + ptr.copy_to_nonoverlapping(&mut arr.stack as *mut [MaybeUninit; N], 1); + arr.set_len(N); + } + arr + } +} + +impl Drop for Array { + fn drop(&mut self) { + self.clear() + } +} + +pub struct ArrayIntoIter { + state: usize, + a: Array, +} + +impl Iterator for ArrayIntoIter { + type Item = T; + fn next(&mut self) -> Option { + 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) { + let l = self.a.len() - self.state; + (l, Some(l)) + } +} + +impl IntoIterator for Array { + type Item = T; + type IntoIter = ArrayIntoIter; + fn into_iter(self) -> Self::IntoIter { + ArrayIntoIter { state: 0, a: self } + } +} + +impl Array { + pub unsafe fn extend_from_iter_unchecked(&mut self, iterable: I) + where + I: IntoIterator, + { + // 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 Extend for Array { + fn extend>(&mut self, iter: I) { + unsafe { self.extend_from_iter_unchecked(iter) } + } +} + +impl FromIterator for Array { + fn from_iter>(iter: I) -> Self { + let mut arr = Array::new(); + arr.extend(iter); + arr + } +} + +impl Clone for Array +where + T: Clone, +{ + fn clone(&self) -> Self { + self.iter().cloned().collect() + } +} + +impl Hash for Array +where + T: Hash, +{ + fn hash(&self, hasher: &mut H) + where + H: Hasher, + { + Hash::hash(&**self, hasher) + } +} + +impl PartialEq for Array +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl Eq for Array where T: Eq {} + +impl PartialOrd for Array +where + T: PartialOrd, +{ + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } +} + +impl Ord for Array +where + T: Ord, +{ + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(&**other) + } +} + +impl Borrow<[T]> for Array { + fn borrow(&self) -> &[T] { + self + } +} + +impl BorrowMut<[T]> for Array { + fn borrow_mut(&mut self) -> &mut [T] { + self + } +} +impl AsRef<[T]> for Array { + fn as_ref(&self) -> &[T] { + self + } +} + +impl AsMut<[T]> for Array { + fn as_mut(&mut self) -> &mut [T] { + self + } +} + +impl fmt::Debug for Array +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (**self).fmt(f) + } +} + +impl Serialize for Array +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq(self.iter()) + } +} + +struct ArrayVisitor { + _marker: PhantomData>, +} + +impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor +where + T: Deserialize<'de>, +{ + type Value = Array; + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a sequence") + } + fn visit_seq(self, mut seq: B) -> Result + 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 +where + T: Deserialize<'de>, +{ + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_seq(ArrayVisitor { + _marker: PhantomData, + }) + } +} + +#[test] +fn test_basic() { + let mut b: Array = 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']) + ); +} diff --git a/server/src/coredb/iarray.rs b/server/src/coredb/iarray.rs index 2f1da031..71e297f1 100644 --- a/server/src/coredb/iarray.rs +++ b/server/src/coredb/iarray.rs @@ -26,12 +26,14 @@ #![allow(dead_code)] // TODO(@ohsayan): Remove this once we're done +use crate::coredb::array::LenScopeGuard; use core::alloc::Layout; use core::borrow::Borrow; use core::borrow::BorrowMut; use core::cmp; use core::fmt; use core::hash::{self, Hash}; +use core::iter::FromIterator; use core::marker::PhantomData; use core::mem; use core::mem::ManuallyDrop; @@ -505,33 +507,6 @@ impl Drop for IArray { } } -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 Extend for IArray { fn extend>(&mut self, iterable: I) { let mut iter = iterable.into_iter(); @@ -545,7 +520,7 @@ impl Extend for IArray { while len.get_temp() < cap { if let Some(out) = iter.next() { ptr::write(data_ptr.add(len.get_temp()), out); - len.incr(); + len.incr(1); } else { return; } @@ -608,6 +583,23 @@ where } } +impl FromIterator for IArray { + fn from_iter>(iter: I) -> Self { + let mut iarray = IArray::new(); + iarray.extend(iter); + iarray + } +} + +impl<'a, A: MemoryBlock> From<&'a [A::LayoutItem]> for IArray +where + A::LayoutItem: Clone, +{ + fn from(slice: &'a [A::LayoutItem]) -> Self { + slice.iter().cloned().collect() + } +} + // impl ser/de impl Serialize for IArray where @@ -658,6 +650,9 @@ where } } +unsafe impl Send for IArray where A::LayoutItem: Send {} +unsafe impl Sync for IArray where A::LayoutItem: Sync {} + #[test] fn test_equality() { let mut x = IArray::new_bytearray(); diff --git a/server/src/coredb/mod.rs b/server/src/coredb/mod.rs index 0ff8394d..bd7ca946 100644 --- a/server/src/coredb/mod.rs +++ b/server/src/coredb/mod.rs @@ -42,6 +42,7 @@ pub mod htable; pub mod iarray; pub mod lazy; pub mod lock; +pub mod array; mod memstore; /// This is a thread-safe database handle, which on cloning simply