Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions betree/include/betree.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef betree_h
#define betree_h

/* Generated with cbindgen:0.24.3 */
/* Generated with cbindgen:0.29.0 */

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

Expand Down Expand Up @@ -68,6 +68,11 @@ typedef struct err_t err_t;
*/
typedef struct name_iter_t name_iter_t;

/**
* Opaque struct for an iterator over objects.
*/
typedef struct obj_iter_t obj_iter_t;

/**
* The object store wrapper type
*/
Expand Down Expand Up @@ -144,6 +149,20 @@ typedef struct byte_slice_t {
const struct byte_slice_rc_t *arc;
} byte_slice_t;

/**
* Holds status information for an object.
*/
typedef struct betree_object_info_t {
/**
* Size of the object in bytes.
*/
uint64_t size;
/**
* Last modification time in microseconds since the UNIX epoch.
*/
uint64_t mtime_us;
} betree_object_info_t;

#define STORAGE_PREF_FASTEST (storage_pref_t){ ._0 = StoragePreference_FASTEST }

#define STORAGE_PREF_NONE (storage_pref_t){ ._0 = StoragePreference_NONE }
Expand Down Expand Up @@ -375,6 +394,16 @@ void betree_free_err(struct err_t *err);
*/
void betree_free_name_iter(struct name_iter_t *name_iter);

/**
* Free the object iterator.
*/
void betree_free_obj_iter(struct obj_iter_t *iter);

/**
* Frees the memory allocated for betree_object_info_t.
*/
void betree_free_object_info(struct betree_object_info_t *info);

/**
* Free a range iterator.
*/
Expand Down Expand Up @@ -437,6 +466,31 @@ struct obj_t *betree_object_create(struct obj_store_t *os,
*/
int betree_object_delete(struct obj_t *obj, struct err_t **err);

/**
* Retrieve status information about an object.
*
* On success, returns a pointer to a `betree_object_info_t` struct, which
* must be freed with `betree_free_object_info`.
* On error, returns null. If `err` is not null, it will be populated.
*/
struct betree_object_info_t *betree_object_info(struct obj_t *obj, struct err_t **err);

/**
* Create an iterator over all objects in the object store.
* On success, returns a pointer to the iterator which must be freed with `betree_free_obj_iter`.
*/
struct obj_iter_t *betree_object_iter_all(struct obj_store_t *os, struct err_t **err);

/**
* Advance the iterator and get the next object's key.
* On success (an item was found), returns 0 and populates `key`. `key` must be freed with `betree_free_byte_slice`.
* When the iterator is exhausted, returns -1.
* On error, returns -1 and sets `err`.
*/
int betree_object_iter_next(struct obj_iter_t *iter,
struct byte_slice_t *key,
struct err_t **err);

/**
* Open an existing object.
*/
Expand Down Expand Up @@ -467,6 +521,15 @@ int betree_object_read_at(struct obj_t *obj,
unsigned long *n_read,
struct err_t **err);

/**
* Create an iterator over objects with a given prefix in the object store.
* On success, returns a pointer to the iterator which must be freed with `betree_free_obj_iter`.
*/
struct obj_iter_t *betree_object_store_list_prefix(struct obj_store_t *os,
const char *prefix,
unsigned int prefix_len,
struct err_t **err);

/**
* Try to write `buf_len` bytes from `buf` into `obj`, starting at `offset` bytes into the objects
* data.
Expand Down Expand Up @@ -575,4 +638,4 @@ struct range_iter_t *betree_snapshot_range(const struct ss_t *ss,
*/
int betree_sync_db(struct db_t *db, struct err_t **err);

#endif /* betree_h */
#endif /* betree_h */
162 changes: 148 additions & 14 deletions betree/src/c_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
env::SplitPaths,
ffi::{CStr, OsStr},
io::{stderr, BufReader, Write},
ops::RangeFull,
os::{
raw::{c_char, c_int, c_uint, c_ulong},
unix::prelude::OsStrExt,
Expand All @@ -25,6 +26,9 @@ use crate::{
DatabaseConfiguration, StoragePreference,
};

use crate::object::ObjectInfo;
use std::time::UNIX_EPOCH;

/// The type for a database configuration
pub struct cfg_t(DatabaseConfiguration);
/// The general error type
Expand Down Expand Up @@ -164,6 +168,16 @@ impl HandleResult for Box<dyn Iterator<Item = Result<(CowBytes, SlicedCowBytes),
}
}

impl HandleResult for Box<dyn Iterator<Item = Result<(CowBytes, ObjectInfo), Error>>> {
type Result = *mut obj_iter_t;
fn success(self) -> *mut obj_iter_t {
b(obj_iter_t(self))
}
fn fail() -> *mut obj_iter_t {
null_mut()
}
}

impl HandleResult for ObjectStore {
type Result = *mut obj_store_t;
fn success(self) -> *mut obj_store_t {
Expand Down Expand Up @@ -505,6 +519,7 @@ pub unsafe extern "C" fn betree_iter_datasets(
err: *mut *mut err_t,
) -> *mut name_iter_t {
let db = &mut (*db).0;
// Result<impl Iterator<Item = Result<SlicedCowBytes>>>
db.iter_datasets()
.map(|it| Box::new(it) as Box<dyn Iterator<Item = Result<SlicedCowBytes, Error>>>)
.handle_result(err)
Expand Down Expand Up @@ -961,23 +976,142 @@ pub unsafe extern "C" fn betree_object_write_at(
.handle_result(err)
}

/*
/// Return the objects size in bytes.
/// Holds status information for an object.
#[repr(C)]
pub struct betree_object_info_t {
/// Size of the object in bytes.
pub size: u64,
/// Last modification time in microseconds since the UNIX epoch.
pub mtime_us: u64,
}

/// Frees the memory allocated for betree_object_info_t.
#[no_mangle]
pub unsafe extern "C" fn betree_object_status(obj: *const obj_t, err: *mut *mut err_t) -> c_ulong {
let obj = &(*obj).0;
let info = obj.info();
obj.
obj.size()
pub unsafe extern "C" fn betree_free_object_info(info: *mut betree_object_info_t) {
let _ = Box::from_raw(info);
}

/// Returns the last modification timestamp in microseconds since the Unix epoch.
/// Retrieve status information about an object.
///
/// On success, returns a pointer to a `betree_object_info_t` struct, which
/// must be freed with `betree_free_object_info`.
/// On error, returns null. If `err` is not null, it will be populated.
#[no_mangle]
pub unsafe extern "C" fn betree_object_mtime_us(obj: *const obj_t) -> c_ulong {
pub unsafe extern "C" fn betree_object_info<'os>(
obj: *mut obj_t<'os>,
err: *mut *mut err_t,
) -> *mut betree_object_info_t {
let obj = &(*obj).0;
obj.modification_time()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_micros() as u64)
.unwrap_or(0)
match obj.info() {
Ok(Some(info)) => {
let mtime_us = info
.mtime
.duration_since(UNIX_EPOCH)
.map(|d| d.as_micros() as u64)
.unwrap_or(0);

let result_info = betree_object_info_t {
size: info.size,
mtime_us,
};
Box::into_raw(Box::new(result_info))
}
Ok(None) => {
// Object does not exist or has no metadata
handle_err(Error::DoesNotExist, err);
null_mut()
}
Err(e) => {
handle_err(e, err);
null_mut()
}
}
}

/// Opaque struct for an iterator over objects.
pub struct obj_iter_t(Box<dyn Iterator<Item = Result<(CowBytes, ObjectInfo), Error>>>);

/// Create an iterator over all objects in the object store.
/// On success, returns a pointer to the iterator which must be freed with `betree_free_obj_iter`.
#[no_mangle]
pub unsafe extern "C" fn betree_object_iter_all(
os: *mut obj_store_t,
err: *mut *mut err_t,
) -> *mut obj_iter_t {
let os = &mut (*os).0;
os.list_objects::<_, &[u8]>(..)
.map(|iter| {
let new_iter: Box<dyn Iterator<Item = Result<_, _>>> = Box::new(
iter.map(|(handle, info)| Ok((CowBytes::from(handle.object.key()), info))),
);
new_iter
})
.handle_result(err)
}

/// Create an iterator over objects with a given prefix in the object store.
/// On success, returns a pointer to the iterator which must be freed with `betree_free_obj_iter`.
#[no_mangle]
pub unsafe extern "C" fn betree_object_store_list_prefix(
os: *mut obj_store_t,
prefix: *const c_char,
prefix_len: c_uint,
err: *mut *mut err_t,
) -> *mut obj_iter_t {
let os = &mut (*os).0;
let prefix_slice = from_raw_parts(prefix as *const u8, prefix_len as usize);

let mut end_key = prefix_slice.to_vec();
// A simple way to create an end bound for a prefix search is to find the
// "next" key in byte-order. For this we find the last byte which is not
// 0xFF, increment it, and truncate the rest.
if let Some(pos) = end_key.iter().rposition(|&b| b != 0xFF) {
end_key[pos] += 1;
end_key.truncate(pos + 1);
} else {
// The prefix is all 0xFF, there's no byte-larger key with this prefix
// so we return an empty iterator.
return b(obj_iter_t(Box::new(std::iter::empty())));
}

os.list_objects(prefix_slice..&*end_key)
.map(|iter| {
let new_iter: Box<dyn Iterator<Item = Result<_, _>>> = Box::new(
iter.map(|(handle, info)| Ok((CowBytes::from(handle.object.key()), info))),
);
new_iter
})
.handle_result(err)
}

/// Advance the iterator and get the next object's key.
/// On success (an item was found), returns 0 and populates `key`. `key` must be freed with `betree_free_byte_slice`.
/// When the iterator is exhausted, returns -1.
/// On error, returns -1 and sets `err`.
#[no_mangle]
pub unsafe extern "C" fn betree_object_iter_next(
iter: *mut obj_iter_t,
key: *mut byte_slice_t,
err: *mut *mut err_t,
) -> c_int {
let iter = &mut (*iter).0;
match iter.next() {
None => -1, // End of iterator
Some(Ok((k, _info))) => {
write(key, k.into());
0
}
Some(Err(e)) => {
handle_err(e, err);
-1
}
}
}

/// Free the object iterator.
#[no_mangle]
pub unsafe extern "C" fn betree_free_obj_iter(iter: *mut obj_iter_t) {
if !iter.is_null() {
let _ = Box::from_raw(iter);
}
}
*/