Add basic row definition

next
Sayan Nandan 1 year ago
parent ac2ec6f71a
commit 05d93d2102
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -121,6 +121,15 @@ impl PrimaryIndexKey {
},
}
}
pub unsafe fn raw_clone(&self) -> Self {
Self {
tag: self.tag,
data: {
let (qw, nw) = self.data.dwordqn_load_qw_nw();
SpecialPaddedWord::new(qw, nw)
},
}
}
pub fn check(dc: &Datacell) -> bool {
dc.tag().tag_unique().is_unique()
}

@ -25,3 +25,4 @@
*/
mod key;
mod row;

@ -0,0 +1,105 @@
/*
* Created on Thu Apr 27 2023
*
* 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) 2023, 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/>.
*
*/
use {
super::key::PrimaryIndexKey,
crate::engine::{
data::cell::Datacell,
idx::{meta::hash::HasherNativeFx, mtchm::meta::TreeElement, IndexST},
sync::smart::RawRC,
},
parking_lot::RwLock,
std::mem::ManuallyDrop,
};
type DcFieldIndex = IndexST<Box<str>, Datacell, HasherNativeFx>;
pub struct Row {
pk: ManuallyDrop<PrimaryIndexKey>,
rc: RawRC<RwLock<DcFieldIndex>>,
}
impl TreeElement for Row {
type Key = PrimaryIndexKey;
type Value = RwLock<DcFieldIndex>;
fn key(&self) -> &Self::Key {
&self.pk
}
fn val(&self) -> &Self::Value {
self.rc.data()
}
fn new(k: Self::Key, v: Self::Value) -> Self {
Self::new(k, v)
}
}
impl Row {
pub fn new(pk: PrimaryIndexKey, data: RwLock<DcFieldIndex>) -> Self {
Self {
pk: ManuallyDrop::new(pk),
rc: unsafe {
// UNSAFE(@ohsayan): we free this up later
RawRC::new(data)
},
}
}
pub fn with_data_read<T>(&self, f: impl Fn(&DcFieldIndex) -> T) -> T {
let data = self.rc.data().read();
f(&data)
}
pub fn with_data_write<T>(&self, f: impl Fn(&mut DcFieldIndex) -> T) -> T {
let mut data = self.rc.data().write();
f(&mut data)
}
}
impl Clone for Row {
fn clone(&self) -> Self {
let rc = unsafe {
// UNSAFE(@ohsayan): we're calling this in the clone implementation
self.rc.rc_clone()
};
Self {
pk: unsafe {
// UNSAFE(@ohsayan): this is safe because of the refcount
ManuallyDrop::new(self.pk.raw_clone())
},
rc,
}
}
}
impl Drop for Row {
fn drop(&mut self) {
unsafe {
// UNSAFE(@ohsayan): we call in this the dtor itself
self.rc.rc_drop(|| {
// UNSAFE(@ohsayan): we rely on the correctness of the rc
ManuallyDrop::drop(&mut self.pk);
});
}
}
}

@ -0,0 +1,241 @@
/*
* Created on Sat Apr 29 2023
*
* 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) 2023, 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/>.
*
*/
use std::hash::{BuildHasher, Hasher};
pub type Hasher32Fx = HasherRawFx<u32>;
pub type Hasher64Fx = HasherRawFx<u64>;
pub type HasherNativeFx = HasherRawFx<usize>;
const ROTATE: u32 = 5;
const PRIME32: u32 = 0x9E3779B9; // golden
const PRIME64: u64 = 0x517CC1B727220A95; // archimedes (obtained from rustc)
pub trait WriteNumeric {
fn self_u32(self) -> u32;
}
macro_rules! impl_numeric_writes {
($($ty:ty),*) => {
$(impl WriteNumeric for $ty { fn self_u32(self) -> u32 { self as u32 } })*
};
}
impl_numeric_writes!(u8, i8, u16, i16, u32, i32);
pub trait HashWord: Sized {
const STATE: Self;
fn fin(&self) -> u64;
fn h_bytes(&mut self, bytes: &[u8]);
fn h_quad(&mut self, quad: u64);
fn h_word(&mut self, v: impl WriteNumeric);
}
impl HashWord for u32 {
const STATE: Self = 0;
fn fin(&self) -> u64 {
(*self) as _
}
fn h_bytes(&mut self, mut bytes: &[u8]) {
let mut state = *self;
while bytes.len() >= 4 {
// no need for ptr am; let opt with loop invariant
state = self::hash32(
state,
u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
);
bytes = &bytes[4..];
}
if bytes.len() >= 2 {
state = self::hash32(state, u16::from_ne_bytes([bytes[0], bytes[1]]) as u32);
bytes = &bytes[2..];
}
if !bytes.is_empty() {
state = self::hash32(state, bytes[0] as u32);
}
*self = state;
}
fn h_quad(&mut self, quad: u64) {
let mut state = *self;
let [x, y]: [u32; 2] = unsafe { core::mem::transmute(quad.to_ne_bytes()) };
state = self::hash32(state, x);
state = self::hash32(state, y);
*self = state;
}
fn h_word(&mut self, v: impl WriteNumeric) {
*self = self::hash32(*self, v.self_u32());
}
}
impl HashWord for u64 {
const STATE: Self = 0;
fn fin(&self) -> u64 {
(*self) as _
}
fn h_bytes(&mut self, mut bytes: &[u8]) {
let mut state = *self;
while bytes.len() >= 8 {
state = self::hash64(
state,
u64::from_ne_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]),
);
bytes = &bytes[8..];
}
if bytes.len() >= 4 {
state = self::hash64(
state,
u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as u64,
);
bytes = &bytes[4..];
}
if bytes.len() >= 2 {
state = self::hash64(state, u16::from_ne_bytes([bytes[0], bytes[1]]) as u64);
bytes = &bytes[2..];
}
if !bytes.is_empty() {
state = self::hash64(state, bytes[0] as u64);
}
*self = state;
}
fn h_quad(&mut self, quad: u64) {
*self = self::hash64(*self, quad);
}
fn h_word(&mut self, v: impl WriteNumeric) {
*self = self::hash64(*self, v.self_u32() as _)
}
}
impl HashWord for usize {
const STATE: Self = 0;
fn fin(&self) -> u64 {
(*self) as _
}
fn h_bytes(&mut self, bytes: &[u8]) {
if cfg!(target_pointer_width = "32") {
let mut slf = *self as u32;
<u32 as HashWord>::h_bytes(&mut slf, bytes);
*self = slf as usize;
} else {
let mut slf = *self as u64;
<u64 as HashWord>::h_bytes(&mut slf, bytes);
*self = slf as usize;
}
}
fn h_quad(&mut self, quad: u64) {
if cfg!(target_pointer_width = "32") {
let mut slf = *self as u32;
<u32 as HashWord>::h_quad(&mut slf, quad);
*self = slf as usize;
} else {
let mut slf = *self as u64;
<u64 as HashWord>::h_quad(&mut slf, quad);
*self = slf as usize;
}
}
fn h_word(&mut self, v: impl WriteNumeric) {
if cfg!(target_pointer_width = "32") {
let mut slf = *self as u32;
<u32 as HashWord>::h_word(&mut slf, v);
*self = slf as usize;
} else {
let mut slf = *self as u64;
<u64 as HashWord>::h_word(&mut slf, v);
*self = slf as usize;
}
}
}
fn hash32(state: u32, word: u32) -> u32 {
(state.rotate_left(ROTATE) ^ word).wrapping_mul(PRIME32)
}
fn hash64(state: u64, word: u64) -> u64 {
(state.rotate_left(ROTATE) ^ word).wrapping_mul(PRIME64)
}
#[derive(Debug)]
pub struct HasherRawFx<T>(T);
impl<T: HashWord> HasherRawFx<T> {
pub const fn new() -> Self {
Self(T::STATE)
}
}
impl<T: HashWord> Hasher for HasherRawFx<T> {
fn finish(&self) -> u64 {
self.0.fin()
}
fn write(&mut self, bytes: &[u8]) {
T::h_bytes(&mut self.0, bytes)
}
fn write_u8(&mut self, i: u8) {
T::h_word(&mut self.0, i)
}
fn write_u16(&mut self, i: u16) {
T::h_word(&mut self.0, i)
}
fn write_u32(&mut self, i: u32) {
T::h_word(&mut self.0, i)
}
fn write_u64(&mut self, i: u64) {
T::h_quad(&mut self.0, i)
}
fn write_u128(&mut self, i: u128) {
let [a, b]: [u64; 2] = unsafe { core::mem::transmute(i) };
T::h_quad(&mut self.0, a);
T::h_quad(&mut self.0, b);
}
fn write_usize(&mut self, i: usize) {
if cfg!(target_pointer_width = "32") {
T::h_word(&mut self.0, i as u32);
} else {
T::h_quad(&mut self.0, i as u64);
}
}
}
impl<T: HashWord> BuildHasher for HasherRawFx<T> {
type Hasher = Self;
fn build_hasher(&self) -> Self::Hasher {
Self::new()
}
}
impl<T: HashWord> Default for HasherRawFx<T> {
fn default() -> Self {
Self::new()
}
}

@ -24,6 +24,8 @@
*
*/
pub mod hash;
use core::{
borrow::Borrow,
hash::{BuildHasher, Hash},

@ -27,7 +27,7 @@
#![deny(unreachable_patterns)]
pub mod meta;
mod mtchm;
pub mod mtchm;
mod stdhm;
mod stord;
#[cfg(test)]

@ -25,9 +25,9 @@
*/
mod access;
pub(super) mod imp;
pub mod imp;
mod iter;
pub(super) mod meta;
pub mod meta;
mod patch;
#[cfg(test)]
mod tests;

@ -200,21 +200,18 @@ impl<T> Deref for SliceRC<T> {
unsafe impl<T: Send> Send for SliceRC<T> {}
unsafe impl<T: Sync> Sync for SliceRC<T> {}
/// The core atomic reference counter implementation. All smart pointers use this inside
/// A simple rc
pub struct EArc {
rc: NonNull<AtomicUsize>,
base: RawRC<()>,
}
impl EArc {
/// Create a new [`EArc`] instance
///
/// ## Safety
///
/// While this is **not unsafe** in the eyes of the language specification for safety, it still does violate a very common
/// bug: memory leaks and we don't want that. So, it is upto the caller to clean this up
unsafe fn new() -> Self {
/// Clean up your own memory, sir
pub unsafe fn new() -> Self {
Self {
rc: NonNull::new_unchecked(Box::into_raw(Box::new(AtomicUsize::new(0)))),
base: RawRC::new(()),
}
}
}
@ -222,14 +219,61 @@ impl EArc {
impl EArc {
/// ## Safety
///
/// Only call when you follow the appropriate ground rules for safety
unsafe fn _rc(&self) -> &AtomicUsize {
self.rc.as_ref()
/// Only call in an actual [`Clone`] context
pub unsafe fn rc_clone(&self) -> Self {
Self {
base: self.base.rc_clone(),
}
}
/// ## Safety
///
/// Only call in dtor context
pub unsafe fn rc_drop(&mut self, dropfn: impl FnMut()) {
self.base.rc_drop(dropfn)
}
}
/// The core atomic reference counter implementation. All smart pointers use this inside
pub struct RawRC<T> {
hptr: NonNull<RawRCData<T>>,
}
struct RawRCData<T> {
rc: AtomicUsize,
data: T,
}
impl<T> RawRC<T> {
/// Create a new [`RawRC`] instance
///
/// ## Safety
///
/// While this is **not unsafe** in the eyes of the language specification for safety, it still does violate a very common
/// bug: memory leaks and we don't want that. So, it is upto the caller to clean this up
pub unsafe fn new(data: T) -> Self {
Self {
hptr: NonNull::new_unchecked(Box::leak(Box::new(RawRCData {
rc: AtomicUsize::new(1),
data,
}))),
}
}
pub fn data(&self) -> &T {
unsafe {
// UNSAFE(@ohsayan): we believe in the power of barriers!
&(*self.hptr.as_ptr()).data
}
}
fn _rc(&self) -> &AtomicUsize {
unsafe {
// UNSAFE(@ohsayan): we believe in the power of barriers!
&(*self.hptr.as_ptr()).rc
}
}
/// ## Safety
///
/// Only call in an actual [`Clone`] context
unsafe fn rc_clone(&self) -> Self {
pub unsafe fn rc_clone(&self) -> Self {
let new_rc = self._rc().fetch_add(1, ORD_RLX);
if new_rc > (isize::MAX) as usize {
// some incredibly degenerate case; this won't ever happen but who knows if some fella decided to have atomic overflow fun?
@ -237,18 +281,10 @@ impl EArc {
}
Self { ..*self }
}
#[cold]
#[inline(never)]
unsafe fn rc_drop_slow(&mut self, mut dropfn: impl FnMut()) {
// deallocate object
dropfn();
// deallocate rc
drop(Box::from_raw(self.rc.as_ptr()));
}
/// ## Safety
///
/// Only call in dtor context
unsafe fn rc_drop(&mut self, dropfn: impl FnMut()) {
pub unsafe fn rc_drop(&mut self, dropfn: impl FnMut()) {
if self._rc().fetch_sub(1, ORD_REL) != 1 {
// not the last man alive
return;
@ -257,4 +293,12 @@ impl EArc {
atomic::fence(ORD_ACQ);
self.rc_drop_slow(dropfn);
}
#[cold]
#[inline(never)]
unsafe fn rc_drop_slow(&mut self, mut dropfn: impl FnMut()) {
// deallocate object
dropfn();
// deallocate rc
drop(Box::from_raw(self.hptr.as_ptr()));
}
}

Loading…
Cancel
Save