+2
lib/bucket.ml
+2
lib/bucket.ml
+19
-8
lib/bucket.mli
+19
-8
lib/bucket.mli
···
3
3
(** Bucket handle parameterized by transaction mode *)
4
4
type 'mode t
5
5
6
-
(** Get a value by key *)
7
-
val get : 'mode t -> string -> (string option, Error.t) result
6
+
(** Get a value by key.
8
7
9
-
(** Put a key-value pair (only in read-write mode) *)
10
-
val put : Types.rw t -> string -> string -> (unit, Error.t) result
8
+
The returned value is a zero-copy slice into the database and is only valid during
9
+
the transaction lifetime. Copy with {!Types.bytes_of_value} if you need the value
10
+
to outlive the transaction. *)
11
+
val get : 'mode t -> Types.key -> (Types.value option, Error.t) result
12
+
13
+
(** Put a key-value pair (only in read-write mode).
14
+
15
+
The value will be copied into the database. *)
16
+
val put : Types.rw t -> Types.key -> Types.value -> (unit, Error.t) result
17
+
18
+
(** Put a key-value pair from bytes (convenience function) *)
19
+
val put_bytes : Types.rw t -> Types.key -> bytes -> (unit, Error.t) result
11
20
12
21
(** Delete a key (only in read-write mode) *)
13
-
val delete : Types.rw t -> string -> (unit, Error.t) result
22
+
val delete : Types.rw t -> Types.key -> (unit, Error.t) result
23
+
24
+
(** Create or open a nested bucket (only in read-write mode).
14
25
15
-
(** Create or open a nested bucket (only in read-write mode) *)
16
-
val create_bucket : Types.rw t -> string -> (Types.rw t, Error.t) result
26
+
Bucket names are stored as keys. *)
27
+
val create_bucket : Types.rw t -> Types.key -> (Types.rw t, Error.t) result
17
28
18
29
(** Open a nested bucket *)
19
-
val bucket : 'mode t -> string -> ('mode t option, Error.t) result
30
+
val bucket : 'mode t -> Types.key -> ('mode t option, Error.t) result
20
31
21
32
(** Create a cursor for iteration *)
22
33
val cursor : 'mode t -> 'mode Cursor.t
+16
-11
lib/cursor.mli
+16
-11
lib/cursor.mli
···
3
3
(** Cursor handle parameterized by transaction mode *)
4
4
type 'mode t
5
5
6
-
(** Convert cursor to a sequence of key-value pairs *)
7
-
val to_seq : 'mode t -> (string * string) Seq.t
6
+
(** Convert cursor to a sequence of key-value pairs.
7
+
8
+
The keys and values in the sequence are zero-copy slices and
9
+
are only valid during the transaction lifetime.
10
+
11
+
Copy them if you need them to outlive the transaction. *)
12
+
val to_seq : 'mode t -> (Types.key * Types.value) Seq.t
8
13
9
14
(** Seek to a specific key *)
10
-
val seek : 'mode t -> string -> unit
15
+
val seek : 'mode t -> Types.key -> unit
11
16
12
-
(** Move to first key *)
13
-
val first : 'mode t -> (string * string) option
17
+
(** Move to first key. Returns a zero-copy view into the database. *)
18
+
val first : 'mode t -> (Types.key * Types.value) option
14
19
15
-
(** Move to last key *)
16
-
val last : 'mode t -> (string * string) option
20
+
(** Move to last key. Returns a zero-copy view into the database. *)
21
+
val last : 'mode t -> (Types.key * Types.value) option
17
22
18
-
(** Move to next key *)
19
-
val next : 'mode t -> (string * string) option
23
+
(** Move to next key. Returns a zero-copy view into the database. *)
24
+
val next : 'mode t -> (Types.key * Types.value) option
20
25
21
-
(** Move to previous key *)
22
-
val prev : 'mode t -> (string * string) option
26
+
(** Move to previous key. Returns a zero-copy view into the database. *)
27
+
val prev : 'mode t -> (Types.key * Types.value) option
+30
lib/types.ml
+30
lib/types.ml
···
16
16
| Active
17
17
| Committed
18
18
| Rolled_back
19
+
20
+
type key = bytes
21
+
22
+
type value = (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
23
+
24
+
let key_of_string s = Bytes.of_string s
25
+
26
+
let string_of_key k = Bytes.to_string k
27
+
28
+
let value_of_bytes b =
29
+
let len = Bytes.length b in
30
+
let arr = Bigarray.Array1.create Bigarray.char Bigarray.c_layout len in
31
+
for i = 0 to len - 1 do
32
+
Bigarray.Array1.set arr i (Bytes.get b i)
33
+
done;
34
+
arr
35
+
;;
36
+
37
+
let bytes_of_value v =
38
+
let len = Bigarray.Array1.dim v in
39
+
let b = Bytes.create len in
40
+
for i = 0 to len - 1 do
41
+
Bytes.set b i (Bigarray.Array1.get v i)
42
+
done;
43
+
b
44
+
;;
45
+
46
+
let value_of_string s = value_of_bytes (Bytes.of_string s)
47
+
48
+
let string_of_value v = Bytes.to_string (bytes_of_value v)
+23
-5
lib/types.mli
+23
-5
lib/types.mli
···
8
8
9
9
type page_id = int64
10
10
11
-
type metadata = {
12
-
version : int;
13
-
page_size : int;
14
-
root_bucket : page_id option;
15
-
}
11
+
type metadata =
12
+
{ version : int
13
+
; page_size : int
14
+
; root_bucket : page_id option
15
+
}
16
16
17
17
type txn_state =
18
18
| Active
19
19
| Committed
20
20
| Rolled_back
21
+
22
+
(** Key type - represented as bytes for efficient binary key handling *)
23
+
type key = bytes
24
+
25
+
(** Value type - opaque Bigarray slice for zero-copy access to mmap'd data.
26
+
27
+
Values are only valid during the lifetime of the transaction that created them.
28
+
If you need a value to outlive the transaction, copy it with {!bytes_of_value}. *)
29
+
type value = (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
30
+
31
+
val key_of_string : string -> key
32
+
val string_of_key : key -> string
33
+
34
+
val value_of_string : string -> value
35
+
val string_of_value : value -> string
36
+
37
+
val value_of_bytes : bytes -> value
38
+
val bytes_of_value : value -> bytes
+5
-1
test/test_lithos.ml
+5
-1
test/test_lithos.ml
+66
test/test_types.ml
+66
test/test_types.ml
···
1
+
(** Tests for Types module conversions *)
2
+
3
+
let test_key_string_roundtrip () =
4
+
let open Lithos.Types in
5
+
let original = "hello_world" in
6
+
let key = key_of_string original in
7
+
let result = string_of_key key in
8
+
Alcotest.(check string) "key roundtrip" original result
9
+
;;
10
+
11
+
let test_value_string_roundtrip () =
12
+
let open Lithos.Types in
13
+
let original = "test_value_123" in
14
+
let value = value_of_string original in
15
+
let result = string_of_value value in
16
+
Alcotest.(check string) "value string roundtrip" original result
17
+
;;
18
+
19
+
let test_value_bytes_roundtrip () =
20
+
let open Lithos.Types in
21
+
let original = Bytes.of_string "binary_data" in
22
+
let value = value_of_bytes original in
23
+
let result = bytes_of_value value in
24
+
Alcotest.(check bool) "value bytes roundtrip" true (Bytes.equal original result)
25
+
;;
26
+
27
+
let test_empty_key () =
28
+
let open Lithos.Types in
29
+
let key = key_of_string "" in
30
+
let result = string_of_key key in
31
+
Alcotest.(check string) "empty key" "" result
32
+
;;
33
+
34
+
let test_empty_value () =
35
+
let open Lithos.Types in
36
+
let value = value_of_string "" in
37
+
let result = string_of_value value in
38
+
Alcotest.(check string) "empty value" "" result
39
+
;;
40
+
41
+
let test_binary_data () =
42
+
let open Lithos.Types in
43
+
let original = Bytes.of_string "\x00\x01\x02\xff\xfe\xfd" in
44
+
let value = value_of_bytes original in
45
+
let result = bytes_of_value value in
46
+
Alcotest.(check bool) "binary data preservation" true (Bytes.equal original result)
47
+
;;
48
+
49
+
let test_value_length () =
50
+
let open Lithos.Types in
51
+
let original = "test" in
52
+
let value = value_of_string original in
53
+
let len = Bigarray.Array1.dim value in
54
+
Alcotest.(check int) "value length" (String.length original) len
55
+
;;
56
+
57
+
let suite =
58
+
[ "key_string_roundtrip", `Quick, test_key_string_roundtrip
59
+
; "value_string_roundtrip", `Quick, test_value_string_roundtrip
60
+
; "value_bytes_roundtrip", `Quick, test_value_bytes_roundtrip
61
+
; "empty_key", `Quick, test_empty_key
62
+
; "empty_value", `Quick, test_empty_value
63
+
; "binary_data", `Quick, test_binary_data
64
+
; "value_length", `Quick, test_value_length
65
+
]
66
+
;;