···6677let put _ _ _ = Ok ()
8899+let put_bytes _t _k b = put _t _k (Types.value_of_bytes b)
1010+911let delete _ _ = Ok ()
10121113let create_bucket _ _ = Ok ()
+19-8
lib/bucket.mli
···33(** Bucket handle parameterized by transaction mode *)
44type 'mode t
5566-(** Get a value by key *)
77-val get : 'mode t -> string -> (string option, Error.t) result
66+(** Get a value by key.
8799-(** Put a key-value pair (only in read-write mode) *)
1010-val put : Types.rw t -> string -> string -> (unit, Error.t) result
88+ The returned value is a zero-copy slice into the database and is only valid during
99+ the transaction lifetime. Copy with {!Types.bytes_of_value} if you need the value
1010+ to outlive the transaction. *)
1111+val get : 'mode t -> Types.key -> (Types.value option, Error.t) result
1212+1313+(** Put a key-value pair (only in read-write mode).
1414+1515+ The value will be copied into the database. *)
1616+val put : Types.rw t -> Types.key -> Types.value -> (unit, Error.t) result
1717+1818+(** Put a key-value pair from bytes (convenience function) *)
1919+val put_bytes : Types.rw t -> Types.key -> bytes -> (unit, Error.t) result
11201221(** Delete a key (only in read-write mode) *)
1313-val delete : Types.rw t -> string -> (unit, Error.t) result
2222+val delete : Types.rw t -> Types.key -> (unit, Error.t) result
2323+2424+(** Create or open a nested bucket (only in read-write mode).
14251515-(** Create or open a nested bucket (only in read-write mode) *)
1616-val create_bucket : Types.rw t -> string -> (Types.rw t, Error.t) result
2626+ Bucket names are stored as keys. *)
2727+val create_bucket : Types.rw t -> Types.key -> (Types.rw t, Error.t) result
17281829(** Open a nested bucket *)
1919-val bucket : 'mode t -> string -> ('mode t option, Error.t) result
3030+val bucket : 'mode t -> Types.key -> ('mode t option, Error.t) result
20312132(** Create a cursor for iteration *)
2233val cursor : 'mode t -> 'mode Cursor.t
+16-11
lib/cursor.mli
···33(** Cursor handle parameterized by transaction mode *)
44type 'mode t
5566-(** Convert cursor to a sequence of key-value pairs *)
77-val to_seq : 'mode t -> (string * string) Seq.t
66+(** Convert cursor to a sequence of key-value pairs.
77+88+ The keys and values in the sequence are zero-copy slices and
99+ are only valid during the transaction lifetime.
1010+1111+ Copy them if you need them to outlive the transaction. *)
1212+val to_seq : 'mode t -> (Types.key * Types.value) Seq.t
813914(** Seek to a specific key *)
1010-val seek : 'mode t -> string -> unit
1515+val seek : 'mode t -> Types.key -> unit
11161212-(** Move to first key *)
1313-val first : 'mode t -> (string * string) option
1717+(** Move to first key. Returns a zero-copy view into the database. *)
1818+val first : 'mode t -> (Types.key * Types.value) option
14191515-(** Move to last key *)
1616-val last : 'mode t -> (string * string) option
2020+(** Move to last key. Returns a zero-copy view into the database. *)
2121+val last : 'mode t -> (Types.key * Types.value) option
17221818-(** Move to next key *)
1919-val next : 'mode t -> (string * string) option
2323+(** Move to next key. Returns a zero-copy view into the database. *)
2424+val next : 'mode t -> (Types.key * Types.value) option
20252121-(** Move to previous key *)
2222-val prev : 'mode t -> (string * string) option
2626+(** Move to previous key. Returns a zero-copy view into the database. *)
2727+val prev : 'mode t -> (Types.key * Types.value) option
+30
lib/types.ml
···1616 | Active
1717 | Committed
1818 | Rolled_back
1919+2020+type key = bytes
2121+2222+type value = (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
2323+2424+let key_of_string s = Bytes.of_string s
2525+2626+let string_of_key k = Bytes.to_string k
2727+2828+let value_of_bytes b =
2929+ let len = Bytes.length b in
3030+ let arr = Bigarray.Array1.create Bigarray.char Bigarray.c_layout len in
3131+ for i = 0 to len - 1 do
3232+ Bigarray.Array1.set arr i (Bytes.get b i)
3333+ done;
3434+ arr
3535+;;
3636+3737+let bytes_of_value v =
3838+ let len = Bigarray.Array1.dim v in
3939+ let b = Bytes.create len in
4040+ for i = 0 to len - 1 do
4141+ Bytes.set b i (Bigarray.Array1.get v i)
4242+ done;
4343+ b
4444+;;
4545+4646+let value_of_string s = value_of_bytes (Bytes.of_string s)
4747+4848+let string_of_value v = Bytes.to_string (bytes_of_value v)
+23-5
lib/types.mli
···8899type page_id = int64
10101111-type metadata = {
1212- version : int;
1313- page_size : int;
1414- root_bucket : page_id option;
1515-}
1111+type metadata =
1212+ { version : int
1313+ ; page_size : int
1414+ ; root_bucket : page_id option
1515+ }
16161717type txn_state =
1818 | Active
1919 | Committed
2020 | Rolled_back
2121+2222+(** Key type - represented as bytes for efficient binary key handling *)
2323+type key = bytes
2424+2525+(** Value type - opaque Bigarray slice for zero-copy access to mmap'd data.
2626+2727+ Values are only valid during the lifetime of the transaction that created them.
2828+ If you need a value to outlive the transaction, copy it with {!bytes_of_value}. *)
2929+type value = (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
3030+3131+val key_of_string : string -> key
3232+val string_of_key : key -> string
3333+3434+val value_of_string : string -> value
3535+val string_of_value : value -> string
3636+3737+val value_of_bytes : bytes -> value
3838+val bytes_of_value : value -> bytes
···11+(** Tests for Types module conversions *)
22+33+let test_key_string_roundtrip () =
44+ let open Lithos.Types in
55+ let original = "hello_world" in
66+ let key = key_of_string original in
77+ let result = string_of_key key in
88+ Alcotest.(check string) "key roundtrip" original result
99+;;
1010+1111+let test_value_string_roundtrip () =
1212+ let open Lithos.Types in
1313+ let original = "test_value_123" in
1414+ let value = value_of_string original in
1515+ let result = string_of_value value in
1616+ Alcotest.(check string) "value string roundtrip" original result
1717+;;
1818+1919+let test_value_bytes_roundtrip () =
2020+ let open Lithos.Types in
2121+ let original = Bytes.of_string "binary_data" in
2222+ let value = value_of_bytes original in
2323+ let result = bytes_of_value value in
2424+ Alcotest.(check bool) "value bytes roundtrip" true (Bytes.equal original result)
2525+;;
2626+2727+let test_empty_key () =
2828+ let open Lithos.Types in
2929+ let key = key_of_string "" in
3030+ let result = string_of_key key in
3131+ Alcotest.(check string) "empty key" "" result
3232+;;
3333+3434+let test_empty_value () =
3535+ let open Lithos.Types in
3636+ let value = value_of_string "" in
3737+ let result = string_of_value value in
3838+ Alcotest.(check string) "empty value" "" result
3939+;;
4040+4141+let test_binary_data () =
4242+ let open Lithos.Types in
4343+ let original = Bytes.of_string "\x00\x01\x02\xff\xfe\xfd" in
4444+ let value = value_of_bytes original in
4545+ let result = bytes_of_value value in
4646+ Alcotest.(check bool) "binary data preservation" true (Bytes.equal original result)
4747+;;
4848+4949+let test_value_length () =
5050+ let open Lithos.Types in
5151+ let original = "test" in
5252+ let value = value_of_string original in
5353+ let len = Bigarray.Array1.dim value in
5454+ Alcotest.(check int) "value length" (String.length original) len
5555+;;
5656+5757+let suite =
5858+ [ "key_string_roundtrip", `Quick, test_key_string_roundtrip
5959+ ; "value_string_roundtrip", `Quick, test_value_string_roundtrip
6060+ ; "value_bytes_roundtrip", `Quick, test_value_bytes_roundtrip
6161+ ; "empty_key", `Quick, test_empty_key
6262+ ; "empty_value", `Quick, test_empty_value
6363+ ; "binary_data", `Quick, test_binary_data
6464+ ; "value_length", `Quick, test_value_length
6565+ ]
6666+;;