From 42c4f1c1732fad212e0961e6d45716720d30d296 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 9 Feb 2026 21:24:59 -0500 Subject: [PATCH] add tests for leb128 library (#486) add tests for containers.leb128 also fix a bug in fix zigzag decoding --- src/leb128/containers_leb128.ml | 2 +- tests/leb128/dune | 11 ++ tests/leb128/t_leb128.ml | 230 ++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 tests/leb128/dune create mode 100644 tests/leb128/t_leb128.ml diff --git a/src/leb128/containers_leb128.ml b/src/leb128/containers_leb128.ml index f250a5eb..d43f3121 100644 --- a/src/leb128/containers_leb128.ml +++ b/src/leb128/containers_leb128.ml @@ -60,7 +60,7 @@ module Decode = struct Int64.to_int v, n_consumed let[@inline] decode_zigzag (v : int64) : int64 = - Int64.(logxor (shift_right v 1) (neg (logand v Int64.one))) + Int64.(logxor (shift_right_logical v 1) (sub 0L (logand v 1L))) let[@inline] i64 sl off : int64 * int = let v, n_consumed = u64 sl off in diff --git a/tests/leb128/dune b/tests/leb128/dune new file mode 100644 index 00000000..014fbac1 --- /dev/null +++ b/tests/leb128/dune @@ -0,0 +1,11 @@ +(executable + (name t_leb128) + (modules t_leb128) + (libraries containers containers.leb128 containers_testlib)) + +(rule + (alias runtest) + (deps t_leb128.exe) + (package containers) + (action + (run ./t_leb128.exe))) diff --git a/tests/leb128/t_leb128.ml b/tests/leb128/t_leb128.ml new file mode 100644 index 00000000..a937000f --- /dev/null +++ b/tests/leb128/t_leb128.ml @@ -0,0 +1,230 @@ +include (val Containers_testlib.make ~__FILE__ ()) +module Leb128 = Containers_leb128 +module Buf = CCByte_buffer +module Slice = CCByte_slice + +let encode_decode_u64 (i : int64) : bool = + let buf = Buf.create () in + Leb128.Encode.u64 buf i; + let slice = Buf.to_slice buf in + let i', n = Leb128.Decode.u64 slice 0 in + Int64.equal i i' && n = Slice.len slice + +let encode_decode_i64 (i : int64) : bool = + let buf = Buf.create () in + Leb128.Encode.i64 buf i; + let slice = Buf.to_slice buf in + let i', n = Leb128.Decode.i64 slice 0 in + Int64.equal i i' && n = Slice.len slice + +let encode_decode_uint (i : int) : bool = + i >= 0 + && + let buf = Buf.create () in + Leb128.Encode.uint buf i; + let slice = Buf.to_slice buf in + let i', n = Leb128.Decode.uint_truncate slice 0 in + Int.equal i i' && n = Slice.len slice + +let encode_decode_int (i : int) : bool = + let buf = Buf.create () in + Leb128.Encode.int buf i; + let slice = Buf.to_slice buf in + let i', n = Leb128.Decode.int_truncate slice 0 in + Int.equal i i' && n = Slice.len slice + +let zigzag_roundtrip (i : int64) : bool = + let encoded = Leb128.Encode.encode_zigzag i in + let decoded = Leb128.Decode.decode_zigzag encoded in + Int64.equal i decoded +;; + +q ~count:10_000 ~long_factor:20 Q.int64 @@ fun i -> +if not (encode_decode_u64 (Int64.abs i)) then + Q.Test.fail_reportf "u64 roundtrip failed for %Ld" i; +true +;; + +q ~count:10_000 ~long_factor:20 Q.int64 @@ fun i -> +if not (encode_decode_i64 i) then + Q.Test.fail_reportf "i64 roundtrip failed for %Ld" i; +true +;; + +q ~count:10_000 ~long_factor:20 Q.int @@ fun i -> +(* make sure [i] is non negative *) +let i = max 0 (abs i) in +if not (encode_decode_uint i) then + Q.Test.fail_reportf "uint roundtrip failed for %d" i; +true +;; + +q ~count:10_000 ~long_factor:20 Q.int @@ fun i -> +if not (encode_decode_int i) then + Q.Test.fail_reportf "int roundtrip failed for %d" i; +true +;; + +q ~count:10_000 ~long_factor:20 Q.int64 @@ fun i -> +if not (zigzag_roundtrip i) then + Q.Test.fail_reportf "zigzag roundtrip failed for %Ld" i; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.u64 buf 0L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.u64 slice 0 in +assert_equal ~printer:Int64.to_string 0L v; +assert_equal ~printer:string_of_int 1 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.u64 buf 127L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.u64 slice 0 in +assert_equal ~printer:Int64.to_string 127L v; +assert_equal ~printer:string_of_int 1 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.u64 buf 128L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.u64 slice 0 in +assert_equal ~printer:Int64.to_string 128L v; +assert_equal ~printer:string_of_int 2 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.u64 buf 16383L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.u64 slice 0 in +assert_equal ~printer:Int64.to_string 16383L v; +assert_equal ~printer:string_of_int 2 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.u64 buf 16384L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.u64 slice 0 in +assert_equal ~printer:Int64.to_string 16384L v; +assert_equal ~printer:string_of_int 3 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.i64 buf 0L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string 0L v; +assert_equal ~printer:string_of_int 1 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.u64 buf 127L; +Leb128.Encode.u64 buf 16384L; +Leb128.Encode.u64 buf 300L; +let slice = Buf.to_slice buf in +let n1 = Leb128.Decode.skip slice 0 in +let n2 = Leb128.Decode.skip slice n1 in +let n3 = Leb128.Decode.skip slice (n1 + n2) in +assert_equal ~printer:string_of_int 1 n1; +assert_equal ~printer:string_of_int 3 n2; +assert_equal ~printer:string_of_int 2 n3; +assert_equal ~printer:string_of_int 6 (n1 + n2 + n3); +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.i64 buf (-1L); +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string (-1L) v; +assert_equal ~printer:string_of_int 1 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.i64 buf 63L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string 63L v; +assert_equal ~printer:string_of_int 1 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.i64 buf (-64L); +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string (-64L) v; +assert_equal ~printer:string_of_int 1 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.i64 buf 64L; +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string 64L v; +assert_equal ~printer:string_of_int 2 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.i64 buf (-65L); +let slice = Buf.to_slice buf in +let v, n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string (-65L) v; +assert_equal ~printer:string_of_int 2 n; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.i64 buf Int64.min_int; +let slice = Buf.to_slice buf in +let v, _n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string Int64.min_int v; +true +;; + +let buf = Buf.create () in +Leb128.Encode.i64 buf Int64.max_int; +let slice = Buf.to_slice buf in +let v, _n = Leb128.Decode.i64 slice 0 in +assert_equal ~printer:Int64.to_string Int64.max_int v; +true +;; + +t @@ fun () -> +let buf = Buf.create () in +Leb128.Encode.u64 buf 300L; +Leb128.Encode.u64 buf 500L; +let slice = Buf.to_slice buf in +let v1, n1 = Leb128.Decode.u64 slice 0 in +let v2, n2 = Leb128.Decode.u64 slice n1 in +assert_equal ~printer:Int64.to_string 300L v1; +assert_equal ~printer:Int64.to_string 500L v2; +assert_equal ~printer:string_of_int 2 n1; +assert_equal ~printer:string_of_int 2 n2; +true + +let () = Containers_testlib.run_all ~descr:"test leb128" [ get () ]