Last active
October 4, 2019 08:13
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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