From d84487307153b9b2b69bb9b9213fc85f0855c9ad Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Sat, 15 Mar 2025 21:15:43 +0800 Subject: [PATCH] Add basic env, txn, and dbi functions and modify examples to use them --- example/get.ha | 58 +++++++++-------------------------------------------- example/put.ha | 51 +++++++++------------------------------------------ lmdb/dbi.ha | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lmdb/env.ha | 38 ++++++++++++++++++++++++++++++++++++++ lmdb/error.ha | 2 ++ lmdb/txn.ha | 36 ++++++++++++++++++++++++++++++++++++ lmdb/types.ha | 12 ------------ lmdb/val.ha | 10 ++++++++++ diff --git a/example/get.ha b/example/get.ha index c901eb3cd7a4fbb99a2ddc94699942384b8ab48b..e3ba91d6f8c3b42caf74c5215e47a8a9f99a18cd 100644 --- a/example/get.ha +++ b/example/get.ha @@ -1,6 +1,7 @@ use fmt; use io; use lmdb_ffi = lmdb::ffi; +use lmdb; use strings; use types; use types::c; @@ -11,63 +12,24 @@ 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)!; }; diff --git a/example/put.ha b/example/put.ha index c432a2e0b85170cfbd6bdb818953267174b92607..6eeef1acd4d4ac17a63b76a64db73c16af788eaa 100644 --- a/example/put.ha +++ b/example/put.ha @@ -1,5 +1,6 @@ use fmt; use io; +use lmdb; use lmdb_ffi = lmdb::ffi; use strings; use types; @@ -11,7 +12,7 @@ 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, }; @@ -19,51 +20,17 @@ 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)!; }; diff --git a/lmdb/dbi.ha b/lmdb/dbi.ha new file mode 100644 index 0000000000000000000000000000000000000000..fccca7e7b7d78f5db0baca7cb36e1635dd85a7e6 --- /dev/null +++ b/lmdb/dbi.ha @@ -0,0 +1,59 @@ +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; + }; +}; diff --git a/lmdb/env.ha b/lmdb/env.ha new file mode 100644 index 0000000000000000000000000000000000000000..122c1eb932b56d0dbbd239678a27639afe6ad8d1 --- /dev/null +++ b/lmdb/env.ha @@ -0,0 +1,38 @@ +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; + }; +}; diff --git a/lmdb/error.ha b/lmdb/error.ha new file mode 100644 index 0000000000000000000000000000000000000000..0709465d138c94fd556c3001c42daa793635c191 --- /dev/null +++ b/lmdb/error.ha @@ -0,0 +1,2 @@ +// Any error returned by LMDB. +export type error = !int; diff --git a/lmdb/txn.ha b/lmdb/txn.ha new file mode 100644 index 0000000000000000000000000000000000000000..5fded9157c5fb5402182faeaf760eb250f929bd1 --- /dev/null +++ b/lmdb/txn.ha @@ -0,0 +1,36 @@ +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; + }; +}; diff --git a/lmdb/types.ha b/lmdb/types.ha index 0ff1ddd1686851f70b8355e140a4c8be30ed2066..023e00772690e517f335e532b0ea24e838c97098 100644 --- a/lmdb/types.ha +++ b/lmdb/types.ha @@ -1,16 +1,4 @@ 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; diff --git a/lmdb/val.ha b/lmdb/val.ha index 182ae34a0dd00f5498efa6073412f4d57ae47ad4..e267cf7d8f875c3b19dcec3acf9c50f78da5c3be 100644 --- a/lmdb/val.ha +++ b/lmdb/val.ha @@ -1,3 +1,4 @@ +use lmdb::ffi; use types; // Generic structure used for passing keys and data in and out @@ -12,3 +13,12 @@ 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); +}; -- 2.48.1