Skip to content

Commit

Permalink
split out database functionality from fugue-core
Browse files Browse the repository at this point in the history
  • Loading branch information
xorpse committed Jun 2, 2024
1 parent e5b8ef6 commit 525950b
Show file tree
Hide file tree
Showing 12 changed files with 956 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"fugue-bytes",
"fugue-bv",
"fugue-core",
"fugue-db",
"fugue-fp",
"fugue-ir",
"fugue-state",
Expand Down
35 changes: 29 additions & 6 deletions fugue-core/src/util/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ where
let environment = unsafe {
EnvOpenOptions::new()
.max_dbs(16)
.map_size(4 * 1024 * 1024 * 1024)
.map_size(4 * 4 * 1024 * 1024 * 1024)
.open(backing.as_ref())
.map_err(MmapTableError::database)?
};
Expand Down Expand Up @@ -201,30 +201,30 @@ impl<'a, K, T> MmapTypedTableWriter<'a, K, T>
where
T: Archive + Serialize<AllocSerializer<1024>>,
{
pub fn get<KE>(&self, key: impl AsRef<KE>) -> Result<Option<&T::Archived>, MmapTableError>
pub fn get<KE>(&self, key: &KE) -> Result<Option<&T::Archived>, MmapTableError>
where
K: for<'b> BytesEncode<'b, EItem = KE>,
KE: ?Sized + 'static,
{
let val = self
.table
.database
.get(&self.txn, key.as_ref())
.get(&self.txn, key)
.map_err(MmapTableError::database)?;

Ok(val.map(|val| unsafe { rkyv::archived_root::<T>(val) }))
}

pub fn set<KE>(&mut self, key: impl AsRef<KE>, val: impl AsRef<T>) -> Result<(), MmapTableError>
pub fn set<KE>(&mut self, key: &KE, val: &T) -> Result<(), MmapTableError>
where
K: for<'b> BytesEncode<'b, EItem = KE>,
KE: ?Sized + 'static,
{
let val = rkyv::to_bytes::<_, 1024>(val.as_ref()).map_err(MmapTableError::database)?;
let val = rkyv::to_bytes::<_, 1024>(val).map_err(MmapTableError::database)?;

self.table
.database
.put(&mut self.txn, key.as_ref(), val.as_ref())
.put(&mut self.txn, key, &val)
.map_err(MmapTableError::database)?;

Ok(())
Expand Down Expand Up @@ -323,7 +323,9 @@ impl<'a, K> MmapTableWriter<'a, K> {

#[cfg(test)]
mod test {
use fugue_bytes::LE;
use heed::types::Str;
use heed::types::U64;

use super::*;

Expand Down Expand Up @@ -351,4 +353,25 @@ mod test {

Ok(())
}

#[test]
fn test_project_insns() -> Result<(), Box<dyn std::error::Error>> {
let mut pt = MmapTable::<U64<LE>>::temporary("project")?;

{
let mut writer = pt.typed_writer::<(String, String, Vec<u8>)>()?;

for k in 0..10_000_000u64 {
writer.set(&k, &(
format!("{k:x}_one"),
format!("{k:x}_two"),
vec![0u8; 16],
))?;
}

writer.commit()?;
}

Ok(())
}
}
16 changes: 16 additions & 0 deletions fugue-db/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "fugue-db"
version = "0.3.0"
edition = "2021"

[dependencies]
anyhow = "1"
arrayvec = "0.7"
fugue-bytes = { path = "../fugue-bytes", version = "0.3" }
fugue-ir = { path = "../fugue-ir", version = "0.3" }
heed = { version = "0.20", features = ["read-txn-no-tls"] }
rkyv = "0.7"
rkyv-with = "0.1"
tempfile = "3"
thiserror = "1"
uuid = "1"
26 changes: 26 additions & 0 deletions fugue-db/src/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use fugue_ir::Address;
use rkyv::{Archive, Deserialize, Serialize};

use crate::project::{ProjectDBError, ProjectDBReader, ProjectDBStorable, ProjectDBWriter};
use crate::types::key::Key;

#[derive(Archive, Deserialize, Serialize)]
pub struct CodeBlock {
#[with(crate::types::address::Address)]
addr: Address,
size: u32,
}

impl ProjectDBStorable for CodeBlock {
fn key(&self) -> Key {
Key::basic_block(self.addr)
}

fn fetch<'a>(_db: &ProjectDBReader<'a>) -> Result<&'a ArchivedCodeBlock, ProjectDBError> {
todo!()
}

fn store<'a>(&self, db: &mut ProjectDBWriter<'a>) -> Result<(), ProjectDBError> {
db.write::<_, 0>(&self.key(), self)
}
}
26 changes: 26 additions & 0 deletions fugue-db/src/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use fugue_ir::Address;
use rkyv::{Archive, Deserialize, Serialize};

use crate::project::{ProjectDBError, ProjectDBReader, ProjectDBStorable, ProjectDBWriter};
use crate::types::key::Key;

#[derive(Archive, Deserialize, Serialize)]
pub struct Function {
#[with(crate::types::address::Address)]
addr: Address,
name: String, // NOTE: we could use SmolStr
}

impl ProjectDBStorable for Function {
fn key(&self) -> Key {
Key::function(self.addr)
}

fn fetch<'a>(_db: &ProjectDBReader<'a>) -> Result<&'a ArchivedFunction, ProjectDBError> {
todo!()
}

fn store<'a>(&self, db: &mut ProjectDBWriter<'a>) -> Result<(), ProjectDBError> {
db.write::<_, 0>(&self.key(), self)
}
}
5 changes: 5 additions & 0 deletions fugue-db/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod block;
pub mod function;
pub mod project;
pub mod table;
pub mod types;
180 changes: 180 additions & 0 deletions fugue-db/src/project.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
use std::path::Path;
use std::path::PathBuf;

use heed::types::Bytes;
use heed::{Database, Env, EnvOpenOptions, RoTxn, RwTxn};
use rkyv::ser::serializers::AllocSerializer;
use rkyv::{Archive, Archived, Serialize};
use tempfile::TempDir;
use thiserror::Error;

use crate::types::key::Key;

pub struct ProjectDB {
environment: Env,
database: Database<Bytes, Bytes>,
mappings: Database<Bytes, Bytes>,
location: ProjectDBLocation,
}

#[derive(Debug, Error)]
pub enum ProjectDBError {
#[error(transparent)]
Database(anyhow::Error),
#[error(transparent)]
IO(anyhow::Error),
}

impl ProjectDBError {
pub fn database<E>(e: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Database(e.into())
}

pub fn database_with<M>(m: M) -> Self
where
M: std::fmt::Debug + std::fmt::Display + Send + Sync + 'static,
{
Self::Database(anyhow::Error::msg(m))
}

pub fn io<E>(e: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::IO(e.into())
}
}

pub enum ProjectDBLocation {
Temporary(TempDir),
Path(PathBuf),
}

impl ProjectDBLocation {
pub fn temporary() -> Result<Self, ProjectDBError> {
Ok(Self::Temporary(TempDir::new().map_err(ProjectDBError::io)?))
}

pub fn fixed(path: impl AsRef<Path>) -> Self {
Self::Path(path.as_ref().to_path_buf())
}

pub fn path(&self) -> &Path {
match self {
Self::Temporary(d) => d.path(),
Self::Path(d) => d,
}
}
}

impl<T> From<T> for ProjectDBLocation
where
T: AsRef<Path>,
{
fn from(value: T) -> Self {
Self::fixed(value)
}
}

pub struct ProjectDBReader<'a> {
txn: RoTxn<'a>,
database: &'a Database<Bytes, Bytes>,
mappings: &'a Database<Bytes, Bytes>,
}

pub struct ProjectDBWriter<'a> {
txn: RwTxn<'a>,
database: &'a Database<Bytes, Bytes>,
mappings: &'a Database<Bytes, Bytes>,
}

impl<'a> ProjectDBWriter<'a> {
pub fn write<T, const N: usize>(&mut self, key: &Key, val: &T) -> Result<(), ProjectDBError>
where
T: Serialize<AllocSerializer<N>>,
{
self.database
.put(
&mut self.txn,
key.as_ref(),
rkyv::to_bytes::<_, N>(val)
.map_err(ProjectDBError::database)?
.as_ref(),
)
.map_err(ProjectDBError::database)?;

Ok(())
}
}

pub trait ProjectDBStorable: Archive {
fn key(&self) -> Key;

fn fetch<'a>(db: &ProjectDBReader<'a>) -> Result<&'a Archived<Self>, ProjectDBError>;
fn store<'a>(&self, db: &mut ProjectDBWriter<'a>) -> Result<(), ProjectDBError>;
}

impl ProjectDB {
pub fn new() -> Result<Self, ProjectDBError> {
Self::new_with(ProjectDBLocation::temporary()?)
}

pub fn new_with(location: impl Into<ProjectDBLocation>) -> Result<Self, ProjectDBError> {
let location = location.into();
let environment = unsafe {
EnvOpenOptions::new()
.max_dbs(2)
.map_size(4 * 4 * 1024 * 1024 * 1024)
.open(location.path())
.map_err(ProjectDBError::database)?
};

let (database, mappings) = {
let mut txn = environment.write_txn().map_err(ProjectDBError::database)?;

let database = environment
.create_database(&mut txn, Some("root"))
.map_err(ProjectDBError::database)?;

let mappings = environment
.create_database(&mut txn, Some("maps"))
.map_err(ProjectDBError::database)?;

txn.commit().map_err(ProjectDBError::database)?;

(database, mappings)
};

Ok(Self {
environment,
database,
mappings,
location,
})
}

pub fn reader(&self) -> Result<ProjectDBReader, ProjectDBError> {
Ok(ProjectDBReader {
txn: self
.environment
.read_txn()
.map_err(ProjectDBError::database)?,
database: &self.database,
mappings: &self.mappings,
})
}

pub fn writer(&mut self) -> Result<ProjectDBWriter, ProjectDBError> {
Ok(ProjectDBWriter {
txn: self
.environment
.write_txn()
.map_err(ProjectDBError::database)?,
database: &self.database,
mappings: &self.mappings,
})
}
}
Loading

0 comments on commit 525950b

Please sign in to comment.