Skip to content

Instantly share code, notes, and snippets.

@Kerollmops
Last active October 4, 2019 08:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Kerollmops/702b329ce83df7cc2813e30b893d7d28 to your computer and use it in GitHub Desktop.
Save Kerollmops/702b329ce83df7cc2813e30b893d7d28 to your computer and use it in GitHub Desktop.
This little bit of code could makes the lmdb usage really easy and fast
use std::{io, marker, str};
use std::borrow::Cow;
use zerocopy::{LayoutVerified, AsBytes, FromBytes};
use serde::{Serialize, Deserialize, de::DeserializeOwned};
// Do not forget to patch zerocopy to make it support str
// by using the `trivial_bounds` feature
pub trait EPAsBytes {
fn as_bytes(&self) -> Cow<[u8]>;
}
pub trait EPFromBytes {
type Output: ToOwned + ?Sized;
fn from_bytes(bytes: &[u8]) -> Option<Cow<Self::Output>>;
}
impl<T: AsBytes + ?Sized> EPAsBytes for T {
fn as_bytes(&self) -> Cow<[u8]> {
Cow::Borrowed(<T as AsBytes>::as_bytes(self))
}
}
#[derive(Clone)]
pub struct Serde<T>(T);
impl<T: Serialize> EPAsBytes for Serde<T> {
fn as_bytes(&self) -> Cow<[u8]> {
bincode::serialize(&self.0).map(Cow::Owned).unwrap()
}
}
impl<T: DeserializeOwned + Clone> EPFromBytes for Serde<T> {
type Output = Serde<T>;
fn from_bytes(bytes: &[u8]) -> Option<Cow<Self::Output>> {
bincode::deserialize(bytes).map(Serde).map(Cow::Owned).ok()
}
}
pub struct Type<T>(marker::PhantomData<T>);
impl<T: FromBytes + Copy> EPFromBytes for Type<T> {
type Output = T;
fn from_bytes(bytes: &[u8]) -> Option<Cow<Self::Output>> {
LayoutVerified::new(bytes).map(LayoutVerified::into_ref).map(Cow::Borrowed)
}
}
pub struct Slice<T>(marker::PhantomData<T>);
impl<T: FromBytes + Copy> EPFromBytes for Slice<T> {
type Output = [T];
fn from_bytes(bytes: &[u8]) -> Option<Cow<Self::Output>> {
LayoutVerified::new_slice(bytes).map(LayoutVerified::into_slice).map(Cow::Borrowed)
}
}
pub struct Str;
impl EPFromBytes for Str {
type Output = str;
fn from_bytes(bytes: &[u8]) -> Option<Cow<Self::Output>> {
str::from_utf8(bytes).map(Cow::Borrowed).ok()
}
}
pub struct Ignore;
impl EPFromBytes for Ignore {
type Output = ();
fn from_bytes(bytes: &[u8]) -> Option<Cow<Self::Output>> {
Some(Cow::Owned(()))
}
}
pub struct Database<KC, DC> {
marker: marker::PhantomData<(KC, DC)>,
}
impl<KC, DC> Database<KC, DC> {
fn new() -> Database<KC, DC> {
Database { marker: marker::PhantomData }
}
}
const STATIC_BYTES: [u8; 21] = [0; 21];
impl<KC, DC> Database<KC, DC>
where
KC: EPFromBytes,
KC::Output: EPAsBytes,
DC: EPFromBytes,
DC::Output: EPAsBytes,
{
pub fn get(&self, key: &KC::Output) -> io::Result<Option<Cow<DC::Output>>> {
let key_bytes: Cow<[u8]> = key.as_bytes();
Ok(DC::from_bytes(&STATIC_BYTES))
}
pub fn put(&self, key: &KC::Output, data: &DC::Output) -> io::Result<Option<Cow<DC::Output>>> {
let key_bytes: Cow<[u8]> = key.as_bytes();
let data_bytes: Cow<[u8]> = data.as_bytes();
Ok(DC::from_bytes(&STATIC_BYTES))
}
}
fn call() {
// you can specify that a database will support some typed key/data
//
// like here we specify that the key will be an array of two i32
// and the data will be an unsized array of u64
let db: Database<Type<[i32; 2]>, Slice<u64>> = Database::new();
let ret: Cow<[u64]> = db.put(&[2, 3], &[21, 22, 33][..]).unwrap().unwrap();
let ret: Cow<[u64]> = db.get(&[2, 3]).unwrap().unwrap();
// even str are supported,
// here the key will be an str and the data will be an array of two i32
let db: Database<Str, Type<[i32; 2]>> = Database::new();
let ret: Cow<[i32; 2]> = db.put("hello", &[2, 3]).unwrap().unwrap();
let ret: Cow<[i32; 2]> = db.get("hello").unwrap().unwrap();
// serde types are also supported but this could be improved a little bit...
#[derive(Clone, Serialize, Deserialize)]
struct Hello { string: String }
let db: Database<Str, Serde<Hello>> = Database::new();
let hello = Hello { string: String::from("hi") };
let ret: Cow<Serde<Hello>> = db.put("hello", &Serde(hello)).unwrap().unwrap();
let ret: Cow<Serde<Hello>> = db.get("hello").unwrap().unwrap();
// you can also ignore the key or the data
let db: Database<Str, Ignore> = Database::new();
let ret: Cow<()> = db.put("hello", &()).unwrap().unwrap();
let ret: Cow<()> = db.get("hello").unwrap().unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment