blob: 608ff7e9e295b9b1e8e9bb22c8646f013a99e50d (
plain) (
tree)
|
|
open Ctypes
type tree_handle = unit
module Key : sig
type t
val size : int
val of_string : string -> t option
val to_string : t -> string
end = struct
type t = string
let size = 32
let of_string value =
if String.length value = size then Some value else None
let to_string key = key
end
type t = { handle : tree_handle ptr; mutable closed : bool }
type error =
| Not_found
| Invalid_argument
| Out_of_memory
| Io_error
| Corrupt_file
| Closed
| Unknown of int
let tree_ptr = ptr void
let status_ok = 0
let error_of_status = function
| 1 -> Not_found
| 2 -> Invalid_argument
| 3 -> Out_of_memory
| 4 -> Io_error
| 5 -> Corrupt_file
| status -> Unknown status
let status_to_result = function
| status when status = status_ok -> Ok ()
| status -> Error (error_of_status status)
module Raw = struct
open Foreign
let load_library () =
let default_names =
if Sys.win32 then
[ "sakura_shabptree.dll" ]
else if Sys.os_type = "Unix" then
[ "libsakura_shabptree.so" ]
else
[ "libsakura_shabptree.dylib" ]
in
let candidates =
match Sys.getenv_opt "SAKURA_BPLUSTREE_DLL" with
| Some filename -> filename :: default_names
| None -> default_names
in
let rec try_load = function
| [] -> None
| filename :: rest ->
(try Some (Dl.dlopen ~filename ~flags:[ Dl.RTLD_NOW ]) with
| Dl.DL_error _ -> try_load rest)
in
try_load candidates
let library = lazy (load_library ())
let bind name signature =
match Lazy.force library with
| Some library -> foreign ~from:library name signature
| None -> foreign name signature
let create = bind "sakura_bptree_create" (void @-> returning tree_ptr)
let free = bind "sakura_bptree_free" (tree_ptr @-> returning void)
let len = bind "sakura_bptree_len" (tree_ptr @-> returning uint64_t)
let put =
bind "sakura_bptree_put"
(tree_ptr @-> ptr uint8_t @-> ptr void @-> uint64_t @-> returning int)
let get =
bind "sakura_bptree_get"
(tree_ptr @-> ptr uint8_t @-> ptr (ptr void) @-> ptr uint64_t @-> returning int)
let remove =
bind "sakura_bptree_remove" (tree_ptr @-> ptr uint8_t @-> returning int)
let save = bind "sakura_bptree_save" (tree_ptr @-> string @-> returning int)
let load = bind "sakura_bptree_load" (string @-> ptr tree_ptr @-> returning int)
let serialize =
bind "sakura_bptree_serialize"
(tree_ptr @-> ptr (ptr uint8_t) @-> ptr uint64_t @-> returning int)
let deserialize =
bind "sakura_bptree_deserialize"
(ptr uint8_t @-> uint64_t @-> ptr tree_ptr @-> returning int)
let free_buf = bind "free" (ptr uint8_t @-> returning void)
let status_string =
bind "sakura_bptree_status_string" (int @-> returning string)
end
let uint8_array_of_string value =
let len = String.length value in
let array = CArray.make uint8_t len in
for i = 0 to len - 1 do
CArray.set array i (Unsigned.UInt8.of_int (Char.code value.[i]))
done;
array
let with_key_ptr key f =
let c_key = uint8_array_of_string (Key.to_string key) in
f (CArray.start c_key)
let with_value_ptr value f =
let len = String.length value in
if len = 0 then f null 0
else
let c_value = uint8_array_of_string value in
f (to_voidp (CArray.start c_value)) len
let handle tree =
if tree.closed then Error Closed else Ok tree.handle
let free tree =
if not tree.closed then begin
Raw.free tree.handle;
tree.closed <- true
end
let close tree =
free tree;
Ok ()
let create () =
let handle = Raw.create () in
if is_null handle then Error Out_of_memory
else
let tree = { handle; closed = false } in
Gc.finalise free tree;
Ok tree
let length tree =
match handle tree with
| Error error -> Error error
| Ok handle -> Ok (Unsigned.UInt64.to_int (Raw.len handle))
let put tree key value =
match handle tree with
| Error error -> Error error
| Ok handle ->
with_key_ptr key @@ fun key_ptr ->
with_value_ptr value @@ fun value_ptr value_len ->
Raw.put handle key_ptr value_ptr (Unsigned.UInt64.of_int value_len)
|> status_to_result
let get tree key =
match handle tree with
| Error error -> Error error
| Ok handle ->
with_key_ptr key @@ fun key_ptr ->
let value_ptr = allocate (ptr void) null in
let len_ptr = allocate uint64_t Unsigned.UInt64.zero in
match Raw.get handle key_ptr value_ptr len_ptr |> status_to_result with
| Error error -> Error error
| Ok () ->
let len = Unsigned.UInt64.to_int !@len_ptr in
let value = from_voidp uint8_t !@value_ptr in
Ok (String.init len (fun i -> Char.chr (Unsigned.UInt8.to_int !@(value +@ i))))
let remove tree key =
match handle tree with
| Error error -> Error error
| Ok handle ->
with_key_ptr key @@ fun key_ptr -> Raw.remove handle key_ptr |> status_to_result
let save tree path =
match handle tree with
| Error error -> Error error
| Ok handle -> Raw.save handle path |> status_to_result
let load path =
let out = allocate tree_ptr null in
match Raw.load path out |> status_to_result with
| Ok () ->
let tree = { handle = !@out; closed = false } in
Gc.finalise free tree;
Ok tree
| Error error -> Error error
let serialize tree =
match handle tree with
| Error error -> Error error
| Ok handle ->
let out_ptr = allocate (ptr uint8_t) (from_voidp uint8_t null) in
let out_len = allocate uint64_t Unsigned.UInt64.zero in
(match Raw.serialize handle out_ptr out_len |> status_to_result with
| Error error -> Error error
| Ok () ->
let len = Unsigned.UInt64.to_int !@out_len in
let p = !@out_ptr in
let bytes = Bytes.init len (fun i -> Char.chr (Unsigned.UInt8.to_int !@(p +@ i))) in
Raw.free_buf p;
Ok (Bytes.to_string bytes))
let deserialize data =
let len = String.length data in
let c_data = uint8_array_of_string data in
let out = allocate tree_ptr null in
match
Raw.deserialize (CArray.start c_data) (Unsigned.UInt64.of_int len) out
|> status_to_result
with
| Error error -> Error error
| Ok () ->
let tree = { handle = !@out; closed = false } in
Gc.finalise free tree;
Ok tree
let error_message error =
match error with
| Closed -> "tree is closed"
| Unknown status -> Raw.status_string status
| _ ->
let status =
match error with
| Not_found -> 1
| Invalid_argument -> 2
| Out_of_memory -> 3
| Io_error -> 4
| Corrupt_file -> 5
| Closed -> 2
| Unknown status -> status
in
Raw.status_string status
|