Use proc macro for uninit array magic

The proc macro does some magic to give us a const array with the full
size without having to manually write it. Magic!
next
Sayan Nandan 3 years ago
parent fd3e06beda
commit 17d1c472b6

@ -8,6 +8,7 @@ build = "build.rs"
[dependencies]
# internal deps
skytable = { git="https://github.com/skytable/client-rust", branch="next", default-features=false }
sky_macros = { path="../sky-macros" }
# external deps
tokio = { version="1.7.0", features=["full"] }
bytes = "1.0.1"
@ -38,7 +39,6 @@ cc = "1.0.68"
[dev-dependencies]
# internal deps
sky_macros = { path="../sky-macros" }
skytable = { git="https://github.com/skytable/client-rust", features=["async", "aio-ssl"], default-features=false, branch="next" }
# external deps
tokio = { version="1.6.1", features=["test-util"] }

@ -105,6 +105,12 @@ impl<T, const N: usize> Array<T, N> {
init_len: 0,
}
}
pub const fn from_const(array: [MaybeUninit<T>; N], init_len: u16) -> Self {
Self {
stack: array,
init_len,
}
}
/// This literally turns [T; M] into [T; N]. How can you expect it to be safe?
/// This function is extremely unsafe. I mean, I don't even know how to call it safe.
/// There's one way though: make M == N. This will panic in debug mode if M > N. In

@ -61,22 +61,30 @@ use crate::coredb::htable::Coremap;
use crate::coredb::htable::Data;
use crate::coredb::SnapshotStatus;
use crate::kvengine::KVEngine;
use core::mem::MaybeUninit;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
const DEFAULT_ARRAY: [u8; 7] = [b'd', b'e', b'f', b'a', b'u', b'l', b't'];
#[sky_macros::array]
const DEFAULT_ARRAY: [MaybeUninit<u8>; 64] = [b'd', b'e', b'f', b'a', b'u', b'l', b't'];
/// The `DEFAULT` array (with the rest uninit)
pub const DEFAULT: Array<u8, 64> = Array::from_const(DEFAULT_ARRAY, 7);
#[test]
fn test_def_macro_sanity() {
// just make sure our macro is working as expected
let mut def = DEFAULT.clone();
def.push(b'?');
assert_eq!(
def.into_iter().map(char::from).collect::<String>(),
"default?".to_owned()
);
}
/// typedef for the namespace/keyspace IDs. We don't need too much fancy here,
/// no atomic pointers and all. Just a nice array. With amazing gurantees
type NsKsTblId = Array<u8, 64>;
macro_rules! defaultid {
() => {{
unsafe {
let mut array = Array::new();
array.extend_from_slice_unchecked(&DEFAULT_ARRAY[..]);
array
}
}};
}
mod cluster {
/// This is for the future where every node will be allocated a shard
@ -149,7 +157,7 @@ impl Memstore {
Self {
namespaces: {
let n = Coremap::new();
n.true_if_insert(defaultid!(), Arc::new(Namespace::empty_default()));
n.true_if_insert(DEFAULT, Arc::new(Namespace::empty_default()));
Arc::new(n)
},
}
@ -198,7 +206,7 @@ impl Namespace {
Self {
keyspaces: {
let ks = Coremap::new();
ks.true_if_insert(defaultid!(), Arc::new(Keyspace::empty_default()));
ks.true_if_insert(DEFAULT, Arc::new(Keyspace::empty_default()));
ks
},
shard_range: cluster::ClusterShardRange::default(),
@ -215,7 +223,7 @@ impl Namespace {
}
/// Drop a keyspace if it is not in use **and** it is empty and not the default
pub fn drop_keyspace(&self, keyspace_idenitifer: NsKsTblId) -> Result<(), DdlError> {
if keyspace_idenitifer.eq(&defaultid!()) {
if keyspace_idenitifer.eq(&DEFAULT) {
// can't delete default keyspace
Err(DdlError::ProtectedObject)
} else if self.keyspaces.contains_key(&keyspace_idenitifer) {

@ -247,3 +247,100 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
pub fn dbtest(args: TokenStream, item: TokenStream) -> TokenStream {
parse_test_module(args, item)
}
#[proc_macro_attribute]
pub fn array(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
if !args.is_empty() {
syn::Error::new(proc_macro2::Span::call_site(), "Expected 0 arguments")
.to_compile_error()
.into()
} else {
// fine, so there's something
let item = item.to_string();
if !(item.starts_with("const") || item.starts_with("pub const") || item.starts_with("let"))
{
syn::Error::new_spanned(item, "Expected a `const` or `let` declaration")
.to_compile_error()
.into()
} else {
// fine, so it's [let|pub|pub const] : [ty; len] = [1, 2, 3, 4];
let item = item.trim();
let ret: Vec<&str> = item.split('=').collect();
if ret.len() != 2 {
syn::Error::new_spanned(item, "Expected a `const` or `let` assignment")
.to_compile_error()
.into()
} else {
// so we have the form we expect
let (declaration, expression) = (ret[0], ret[1]);
let expression = expression.trim().replace(" ;", "");
if !(expression.starts_with('[') && expression.ends_with(']')) {
syn::Error::new_spanned(declaration, "Expected an array")
.to_compile_error()
.into()
} else {
let expression = &expression[1..expression.len() - 1];
// so we have the raw numbers, separated by commas
let count_provided = expression.split(',').count();
let declarations: Vec<&str> = declaration.split(':').collect();
if declarations.len() != 2 {
syn::Error::new_spanned(declaration, "Expected a type")
.to_compile_error()
.into()
} else {
// so we have two parts, let's look at the second part: [ty; len]
let starts_ends =
declarations[1].starts_with('[') && declarations[1].ends_with(']');
let ret: Vec<&str> = declarations[1].split(';').collect();
if ret.len() != 2 || starts_ends {
syn::Error::new_spanned(declaration, "Expected [T; N]")
.to_compile_error()
.into()
} else {
// so we have [T; N], let's make it T; N
let len = declarations[1].len();
// decl hash T; N
let decl = &declarations[1][1..len - 1];
let expr: Vec<&str> = decl.split(';').collect();
let (_, count) = (expr[0], expr[1].replace(']', ""));
let count = count.trim();
let count = match count.parse::<usize>() {
Ok(cnt) => cnt,
Err(_) => {
return syn::Error::new_spanned(
count,
"Expected `[T; N]` where `N` is a positive integer",
)
.to_compile_error()
.into()
}
};
let repeats = count - count_provided;
// we have uninit, uninit, uninit, uninit, uninit,
let repeat_str = "core::mem::MaybeUninit::uninit(),".repeat(repeats);
// expression has 1, 2, 3, 4
let expression: String = expression
.split(',')
.map(|s| {
let mut st = String::new();
st.push_str("MaybeUninit::new(");
st.push_str(&s);
st.push(')');
st.push(',');
st
})
.collect();
// remove the trailing comma
let expression = &expression[..expression.len() - 1];
// let's join them
let ret = "[".to_owned() + expression + "," + &repeat_str + "];";
let ret = declaration.to_owned() + "=" + &ret;
ret.parse().unwrap()
}
}
}
}
}
}
}

Loading…
Cancel
Save