Lindenii Project Forge
Login

hare-lmdb

Hare bindings for LMDB
Commit info
ID
d84487307153b9b2b69bb9b9213fc85f0855c9ad
Author
Runxi Yu <me@runxiyu.org>
Author date
Sat, 15 Mar 2025 21:15:43 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Sat, 15 Mar 2025 22:29:19 +0800
Actions
Add basic env, txn, and dbi functions and modify examples to use them
use fmt;
use io;
use lmdb_ffi = lmdb::ffi;
use lmdb;
use strings;
use types;
use types::c;
use os;

export fn main() void = {
	if (len(os::args) != 3) {
		abort("need two arguments (db dir, key)");
	};

	const key = lmdb_ffi::val{
	const key = lmdb::val{
		mv_size = len(strings::toutf8(os::args[2])),
		mv_data = c::unterminatedstr(os::args[2]): *opaque,
	};
	let val: lmdb_ffi::val = lmdb_ffi::val{
		mv_size = 0,
		mv_data = null,
	};

	let env: nullable *lmdb_ffi::env = null;
	let rc: int = lmdb_ffi::env_create(&env);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to create env");
	};
	let env: *lmdb_ffi::env = env as *lmdb_ffi::env;
	let env = lmdb::env_create()!;

	let path = c::fromstr(os::args[1]);
	defer free(path);
	rc = lmdb_ffi::env_open(env, path, 0, 0o644);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to open env");
	};

	let txn: nullable *lmdb_ffi::txn = null;
	rc = lmdb_ffi::txn_begin(env, null, 0, &txn);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to create txn");
	};
	let txn: *lmdb_ffi::txn = txn as *lmdb_ffi::txn;
	lmdb::env_open(env, os::args[1], 0, 0o644)!;

	let dbi: lmdb_ffi::dbi = 0;
	rc = lmdb_ffi::dbi_open(txn, null, lmdb_ffi::CREATE, &dbi);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to create dbi");
	};
	let txn = lmdb::txn_begin(env, 0)!;

	rc = lmdb_ffi::get(txn, dbi, &key, &val);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to get");
	};
	let dbi = lmdb::dbi_open(txn, null, lmdb_ffi::CREATE)!;

	rc = lmdb_ffi::txn_abort(txn);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to abort");
	};
	let val = lmdb::get(dbi, &key)!;

	let data: []u8 = *(&(types::slice {
		data = val.mv_data,
		length = val.mv_size,
		capacity = val.mv_size,
	}): *[]u8);
	let data = lmdb::val_u8s(&val);

	io::write(os::stdout, data)!;

	lmdb::txn_abort(txn)!;
};
use fmt;
use io;
use lmdb;
use lmdb_ffi = lmdb::ffi;
use strings;
use types;
use types::c;
use os;

export fn main() void = {
	if (len(os::args) != 3) {
		abort("need two arguments (db dir, key)");
	};

	const key = lmdb_ffi::val{
	const key = lmdb::val {
		mv_size = len(strings::toutf8(os::args[2])),
		mv_data = c::unterminatedstr(os::args[2]): *opaque,
	};
	
	let v = io::drain(os::stdin)!;
	defer free(v);

	const val = lmdb_ffi::val{
		mv_size = len(v),
		mv_data = (*(&v: *types::slice)).data: *opaque,
	};
	const val = lmdb::u8s_val(v);

	let env: nullable *lmdb_ffi::env = null;
	let rc: int = lmdb_ffi::env_create(&env);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to create env");
	};
	let env: *lmdb_ffi::env = env as *lmdb_ffi::env;
	let env = lmdb::env_create()!;

	let path = c::fromstr(os::args[1]);
	defer free(path);
	rc = lmdb_ffi::env_open(env, path, 0, 0o644);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to open env");
	};
	lmdb::env_open(env, os::args[1], 0, 0o644)!;

	let txn: nullable *lmdb_ffi::txn = null;
	rc = lmdb_ffi::txn_begin(env, null, 0, &txn);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to create txn");
	};
	let txn: *lmdb_ffi::txn = txn as *lmdb_ffi::txn;
	let txn = lmdb::txn_begin(env, 0)!;

	let dbi: lmdb_ffi::dbi = 0;
	rc = lmdb_ffi::dbi_open(txn, null, lmdb_ffi::CREATE, &dbi);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to create dbi");
	};
	let dbi = lmdb::dbi_open(txn, null, lmdb_ffi::CREATE)!;

	rc = lmdb_ffi::put(txn, dbi, &key, &val, 0);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to put");
	};
	let val = lmdb::put(dbi, &key, &val, 0)!;

	rc = lmdb_ffi::txn_commit(txn);
	if (rc != 0) {
		fmt::printf("error: {}\n", rc)!;
		abort("failed to commit");
	};
	lmdb::txn_commit(txn)!;
};
use lmdb::ffi;
use types::c;

// An individual LMDB database, i.e. one key-value store.
//
// They are only valid for the lifetime of their parent transaction.
export type dbi = struct {
	txn: *ffi::txn,
	dbi: ffi::dbi,
};

// Opens a database from a transaction.
export fn dbi_open(txn: *txn, name: nullable *str, flags: uint) (dbi | error) = {
	const n: nullable *c::char = match (name) {
	case *str => yield c::fromstr(*(name as *str));
	case null => yield null;
	};
	defer free(n);

	let d: ffi::dbi = 0;

	const rc = ffi::dbi_open(txn: *ffi::txn, n, flags, &d): error;

	switch (rc) {
	case 0 =>
		return dbi {
			txn = txn: *ffi::txn,
			dbi = d,
		};
	case =>
		return rc;
	};
};

// Get a value from the database. The returned value is only valid for the
// lifetime of the transaction associated with the dbi.
export fn get(dbi: dbi, key: *val) (val | error) = {
	let data = ffi::val {
		mv_size = 0,
		mv_data = null,
	};

	const rc = ffi::get(dbi.txn, dbi.dbi, key: *ffi::val, &data): error;

	switch (rc) {
	case 0 => return data: val;
	case => return rc;
	};
};

// Put a value into the database.
export fn put(dbi: dbi, key: *val, data: *val, flags: uint) (void | error) = {
	const rc = ffi::put(dbi.txn, dbi.dbi, key: *ffi::val, data: *ffi::val, flags): error;

	switch (rc) {
	case 0 => return void;
	case => return rc;
	};
};
use lmdb::ffi;
use types::c;

// Opaque structure for a database environment.
//
// A DB environment supports multiple databases, all residing in the same
// shared-memory map.
export type env = ffi::env;

// Creates an [[env]]. The caller must free it with [[env_close]].
export fn env_create() (*env | error) = {
	let e: nullable *ffi::env = null;

	const rc = ffi::env_create(&e): error;

	switch (rc) {
	case 0 => return e: *env;
	case => return rc;
	};
};

// Closes an [[env]].
export fn env_close(env: *env) void = {
	ffi::env_close(env: *ffi::env);
};

// Associates an [[env]] with a on-disk environment.
export fn env_open(env: *env, path: str, flag: uint, mode: u32) (void | error) = {
	const p = c::fromstr(path);
	defer free(p);

	const rc = ffi::env_open(env: *ffi::env, p, flag, mode): error;

	return switch (rc) {
	case 0 => yield;
	case => yield rc;
	};
};
// Any error returned by LMDB.
export type error = !int;
use lmdb::ffi;

// Opaque structure for a transaction handle.
export type txn = ffi::txn;

// Initiate a transaction.
export fn txn_begin(env: *env, flags: uint, parent: nullable *txn = null) (*txn | error) = {
	let t: nullable *ffi::txn = null;

	const rc = ffi::txn_begin(env: *ffi::env, parent: nullable *ffi::txn, flags, &t): error;

	switch (rc) {
	case 0 => return t: *txn;
	case => return rc;
	};
};

// Abort a transaction, discaring anything it does.
export fn txn_abort(txn: *txn) (void | error) = {
	const rc = ffi::txn_abort(txn: *ffi::txn): error;

	switch (rc) {
	case 0 => return;
	case => return rc;
	};
};

// Commiting a transaction, writing anything it does.
export fn txn_commit(txn: *txn) (void | error) = {
	const rc = ffi::txn_commit(txn: *ffi::txn): error;

	switch (rc) {
	case 0 => return;
	case => return rc;
	};
};
use lmdb::ffi;

// Opaque structure for a database environment.
//
// A DB environment supports multiple databases, all residing in the same
// shared-memory map.
export type env = ffi::env;

// A handle for an individual database in the DB environment.
export type dbi = ffi::dbi;

// Opaque structure for a transaction handle.
export type txn = ffi::txn;

// Opaque structure for navigating through a database.
export type cursor = ffi::cursor;
use lmdb::ffi;
use types;

// Generic structure used for passing keys and data in and out
// of the database.
export type val = ffi::val;

// Convert a []u8 to a [[val]].
export fn u8s_val(s: []u8) val = {
	let ss = *(&s: *types::slice);
	return val {
		mv_size = ss.length,
		mv_data = ss.data,
	};
};

// Convert a [[val]] to a []u8.
export fn val_u8s(s: *val) []u8 = {
	return *(&types::slice {
		data = s.mv_data,
		length = s.mv_size,
		capacity = s.mv_size,
	}: *[]u8);
};