aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorEstevan Castilho <tevo@riajuu.net>2026-05-02 03:42:25 +0000
committerEstevan Castilho <tevo@riajuu.net>2026-05-02 03:42:25 +0000
commita78ee69166e6873eedfb20f2b2f84d0bbaabc794 (patch)
treed3d62699522553930a394542c1f637c8b64e3377 /lib
parentbd2e9fdbd2a58cdab3e3abd34972fec49c72a012 (diff)
downloadSakura-a78ee69166e6873eedfb20f2b2f84d0bbaabc794.tar.gz
Sakura-a78ee69166e6873eedfb20f2b2f84d0bbaabc794.zip
WIP LMDB backend
Diffstat (limited to 'lib')
-rw-r--r--lib/external/lmdb.ml84
-rw-r--r--lib/management/storage/lmdb.ml94
2 files changed, 162 insertions, 16 deletions
diff --git a/lib/external/lmdb.ml b/lib/external/lmdb.ml
index 9c1d646..a918ce8 100644
--- a/lib/external/lmdb.ml
+++ b/lib/external/lmdb.ml
@@ -16,28 +16,80 @@ let mdb_mode_t = mode_t
let mdb_result = view ~read:mdb_result_of_int ~write:int_of_mdb_result int
-type mdb_env = unit ptr
-let mdb_env = ptr Void
+type mdb_env
+let mdb_env : mdb_env structure typ = structure "mdb_env"
type mdb_dbi = Unsigned.uint
-let mdb_dbi = uint
+let mdb_dbi : mdb_dbi typ = uint
-type mdb_txn = unit ptr
-let mdb_txn = ptr Void
+type mdb_txn
+let mdb_txn : mdb_txn structure typ = structure "mdb_txn"
-let mdb_env_create = foreign "mdb_env_create" (ptr ptr mdb_env @-> returning mdb_result)
-let mdb_env_create' () =
+type mdb_val
+let mdb_val : mdb_val structure typ = structure "mdb_val"
+let mv_size = field mdb_val "mv_size" size_t
+let mv_data = field mdb_val "mv_data" (ptr void)
+let () = seal mdb_val
+
+let with_output_pointer output_type default body =
let open Utilities.Result in
- let env_ptr = allocate mdb_env null in
- let* () = mdb_env_create env_ptr in
- Ok (!@ env_ptr)
+ let output_ptr = allocate output_type default in
+ let* () = body output_ptr in
+ Ok (!@ output_ptr)
+
+let mdb_env_create = foreign "mdb_env_create" (ptr (ptr mdb_env) @-> returning mdb_result)
+let mdb_env_create' () = with_output_pointer
+ (ptr mdb_env)
+ (from_voidp mdb_env null)
+ mdb_env_create
let mdb_env_open = foreign "mdb_env_open" (ptr mdb_env @-> string @-> uint @-> mdb_mode_t @-> returning mdb_result)
-let mdb_env_close = foreign "mdb_env_close" (ptr mdb_env @-> returning Void)
+let mdb_env_close = foreign "mdb_env_close" (ptr mdb_env @-> returning void)
let mdb_dbi_open = foreign "mdb_dbi_open" (ptr mdb_txn @-> string @-> uint @-> ptr mdb_dbi @-> returning mdb_result)
-let mdb_dbi_open' txn name flags =
- let open Utilities.Result in
- let dbi_ptr = allocate mdb_dbi (Unsigned.UInt.of_int 0) in
- let* () = mdb_dbi_open txn name flags dbi_ptr in
- Ok (!@ dbi_ptr)
+let mdb_dbi_open' txn name flags = with_output_pointer
+ mdb_dbi
+ (Unsigned.UInt.of_int 0)
+ (mdb_dbi_open txn name flags)
+
+let mdb_txn_begin = foreign "mdb_txn_begin" (ptr mdb_env @-> ptr mdb_txn @-> uint @-> ptr (ptr mdb_txn) @-> returning mdb_result)
+let mdb_txn_begin' env parent flags = with_output_pointer
+ (ptr mdb_txn)
+ (from_voidp mdb_txn null)
+ (mdb_txn_begin env parent flags)
+
+let mdb_txn_commit = foreign "mdb_txn_commit" (ptr mdb_txn @-> returning mdb_result)
+let mdb_txn_abort = foreign "mdb_txn_abort" (ptr mdb_txn @-> returning mdb_result)
+
+let carray_of_bytes (b : bytes) =
+ let buffer = CArray.make char (Bytes.length b) in
+ Bytes.iteri (CArray.set buffer) b;
+ buffer
+
+let bytes_of_carray arr =
+ let buffer = Bytes.make (CArray.length arr) (Char.chr 0) in
+ CArray.iteri (Bytes.set buffer) arr;
+ buffer
+
+let mdb_val_ptr_of_bytes (b : bytes) =
+ let buf = carray_of_bytes b in
+ let s = make mdb_val in
+ setf s mv_size (Unsigned.Size_t.of_int (Bytes.length b));
+ setf s mv_data (to_voidp (CArray.start buf));
+ addr s
+
+let bytes_of_mdb_val (s : mdb_val structure) =
+ let buf = CArray.from_ptr
+ (from_voidp char (getf s mv_data))
+ (Unsigned.Size_t.to_int (getf s mv_size)) in
+ bytes_of_carray buf
+
+let mdb_get = foreign "mdb_get" (ptr mdb_txn @-> mdb_dbi @-> ptr mdb_val @-> ptr mdb_val @-> returning mdb_result)
+let mdb_get' txn dbi key = with_output_pointer
+ mdb_val
+ (make mdb_val)
+ (mdb_get txn dbi (mdb_val_ptr_of_bytes key))
+ |> Result.map bytes_of_mdb_val
+
+let mdb_put = foreign "mdb_put" (ptr mdb_txn @-> mdb_dbi @-> ptr mdb_val @-> ptr mdb_val @-> uint @-> returning mdb_result)
+let mdb_put' txn dbi key data flags = mdb_put txn dbi (mdb_val_ptr_of_bytes key) (mdb_val_ptr_of_bytes data) flags
diff --git a/lib/management/storage/lmdb.ml b/lib/management/storage/lmdb.ml
new file mode 100644
index 0000000..80153cd
--- /dev/null
+++ b/lib/management/storage/lmdb.ml
@@ -0,0 +1,94 @@
+let not_implemented = Obj.magic ()
+
+module Backend : Physical.BACKEND = struct
+ open External.Lmdb
+ open Ctypes
+
+ module MDB_Errors = struct
+ let mdb_notfound = -30798
+ end
+
+ type configuration = { path : string; name : string }
+ type connection = { env : mdb_env structure ptr; dbi : mdb_dbi }
+ type error = int
+
+ type _ Effect.t +=
+ | Current: mdb_txn structure ptr Effect.t
+ | Rollback: unit Effect.t
+
+ let parse (expr : Sexplib.Sexp.t) =
+ let open Utilities.Result in
+ let* keys = Configuration.map_of_sexp expr in
+ let* path = Utilities.StringMap.find_opt "path" keys
+ |> Option.to_result ~none:"Missing key: path"
+ |> fmap (Configuration.atom_of_sexp)
+ in
+ let* name = Utilities.StringMap.find_opt "name" keys
+ |> Option.to_result ~none:"Missing key: name"
+ |> fmap (Configuration.atom_of_sexp)
+ in
+ Ok { path; name }
+
+ let current_tx () = Effect.perform Current
+ let current_tx' () = try current_tx () with Effect.Unhandled Current -> (from_voidp mdb_txn null)
+
+ let with_transaction' env body =
+ let open Utilities.Result in
+ let success = ref false in
+ let* tx = mdb_txn_begin' env (current_tx' ()) (Unsigned.UInt.of_int 0) in
+ Fun.protect
+ (fun () ->
+ match body () with
+ | x ->
+ let* () = mdb_txn_commit tx in
+ success := true;
+ Ok (Some x)
+ | effect Current, k -> Effect.Deep.continue k tx
+ (* Should a transaction abort be an error value? *)
+ | effect Rollback, _ ->
+ let* () = mdb_txn_abort tx in
+ success := true;
+ Ok None)
+ ~finally:(fun () -> if not !success then mdb_txn_abort tx |> ignore)
+
+ let with_transaction { env; _ } body = with_transaction' env body
+
+ let connect { path; name } =
+ let open Utilities.Result in
+ let* env = mdb_env_create' () in
+ match mdb_env_open env path (Unsigned.UInt.of_int 0) PosixTypes.Mode.zero with
+ | Error x ->
+ mdb_env_close env;
+ Error x
+ | Ok () ->
+ Result.bind
+ (with_transaction' env
+ (fun () ->
+ let* dbi = mdb_dbi_open' (current_tx ()) name (Unsigned.UInt.of_int 0) in
+ Ok { env; dbi }))
+ Option.get
+
+ let disconnect { env; _ } =
+ mdb_env_close env
+
+ let begin_transaction _conn = not_implemented
+
+ let commit _conn = not_implemented
+
+ let rollback _conn = not_implemented
+
+ let get { dbi; _ } hash =
+ match mdb_get' (current_tx ()) dbi (Bytes.of_string hash) with
+ | Ok x -> Ok (Some x)
+ | Error e when e = MDB_Errors.mdb_notfound -> Ok (None)
+ | Error e -> Error e
+
+ let put _conn _hash _value = not_implemented
+
+ let exists _conn _hash = not_implemented
+
+ let get_many _conn _hashes = not_implemented
+
+ let put_many _conn _values = not_implemented
+end
+[@warning "-38-32"]