mte/unikernel/duniverse/ke/fuzz/fuzz.ml

279 lines
8.1 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
type 'a action = Push of 'a * 'a action | Pop of 'a action | Empty
exception Invalid
external identity : 'a -> 'a = "%identity"
let valid a =
let rec go k = function
| Empty -> k 0
| Push (_, a) -> go (fun len -> k (succ len)) a
| Pop a -> go (function 0 -> raise Invalid | n -> k (pred n)) a
in
go identity a
let push x a = Push (x, a)
let empty = Empty
let pop a = Pop a
let () = assert (valid empty = 0)
let () = assert (valid (push 0 empty) = 1)
let () = assert (valid (push 1 (push 0 empty)) = 2)
let () = assert (valid (pop (push 0 empty)) = 0)
let () =
assert (match valid (pop empty) with _ -> false | exception Invalid -> true)
module Peano = struct type zero = Zero type 'a succ = Succ end
module Refl = struct type ('a, 'b) t = Refl : ('a, 'a) t end
module Value = struct
type 'a value =
| Zero : Peano.zero value
| Succ : 'a value -> 'a Peano.succ value
type t = V : 'a value -> t
let of_int n =
let rec go k = function
| 0 -> k (V Zero)
| n -> go (fun (V n) -> k (V (Succ n))) (pred n)
in
if n < 0 then Fmt.invalid_arg "Value.of_int" else go identity n
let to_int v =
let rec go : type a. (int -> 'r) -> a value -> 'r =
fun k -> function Zero -> k 0 | Succ x -> go (fun v -> k (succ v)) x
in
go identity v
let () = assert (to_int Zero = 0)
let () = assert (to_int (Succ Zero) = 1)
let () = assert (to_int (Succ (Succ Zero)) = 2)
let pp : t Fmt.t = fun ppf (V v) -> Fmt.int ppf (to_int v)
let is_zero : type a. a value -> (a, Peano.zero) Refl.t option = function
| Zero -> Some Refl.Refl
| Succ _ -> None
type 'a is_not_zero =
| Is_not_zero : ('a, _ Peano.succ) Refl.t -> 'a is_not_zero
let is_not_zero : type a. a value -> a is_not_zero option = function
| Zero -> None
| Succ _ -> Some (Is_not_zero Refl.Refl)
end
module Stack = struct
type ('a, 'l) action =
| Push : 'a * ('a, 'l) action -> ('a, 'l Peano.succ) action
| Pop : ('a, 'l Peano.succ) action -> ('a, 'l) action
| Empty : ('a, Peano.zero) action
type 'a t = V : ('a, 'l) action -> 'a t
let rec pp : type l. 'a Fmt.t -> ('a, l) action Fmt.t =
fun pp_elt ppf -> function
| Push (v, a) ->
Fmt.pf ppf "@[<1>(Push %a)]" Fmt.(Dump.pair pp_elt (pp pp_elt)) (v, a)
| Pop a -> Fmt.pf ppf "@[<1>(Pop %a)]" (pp pp_elt) a
| Empty -> Fmt.pf ppf "Empty"
let rec length : type a l. (a, l) action -> l Value.value = function
| Empty -> Value.Zero
| Pop a -> ( match length a with Value.Succ x -> x)
| Push (_, a) -> Value.Succ (length a)
let is_empty : type l. ('a, l) action -> (l, Peano.zero) Refl.t option =
function
| Empty -> Some Refl.Refl
| Push _ -> None
| Pop a -> (
match length a with
| Value.Succ Value.Zero -> Some Refl.Refl
| Value.Succ _ -> None)
type 'a is_not_empty =
| Is_not_empty : ('a, _ Peano.succ) Refl.t -> 'a is_not_empty
let is_not_empty : type l. ('a, l) action -> l is_not_empty option = function
| Empty -> None
| Push _ -> Some (Is_not_empty Refl.Refl)
| Pop a -> (
match length a with
| Value.Succ Value.Zero -> None
| Value.Succ (Value.Succ _) -> Some (Is_not_empty Refl.Refl))
end
open Crowbar
type tree = Tree of Value.t * tree list * bool
let rec list_of_tree (Tree (v, x, pop)) : [ `Push of Value.t | `Pop ] list =
if pop then [ `Push v ] @ List.concat (List.map list_of_tree x) @ [ `Pop ]
else [ `Push v ] @ List.concat (List.map list_of_tree x)
let generate : tree gen =
let value = map [ range 30 ] Value.of_int in
fix @@ fun m -> map [ value; list m; bool ] (fun v l pop -> Tree (v, l, pop))
let action_of_tree tree : Value.t Stack.t =
let lst = list_of_tree tree in
List.fold_left
(fun (Stack.V acc) -> function
| `Push v -> Stack.(V (Push (v, acc)))
| `Pop -> (
match Stack.is_not_empty acc with
| Some (Stack.Is_not_empty Refl.Refl) -> Stack.V (Stack.Pop acc)
| None -> bad_test ()))
Stack.(V Empty)
lst
(* XXX(dinosaure): [Stdlib.Queue] is oracle. *)
module Compare = struct
exception Not_equal
let fke q fke =
let q' = Queue.copy q in
try
Ke.Fke.iter
(fun x ->
let x' = Queue.pop q' in
if x <> x' then raise Not_equal)
fke;
true
with Not_equal | Queue.Empty -> false
let rke q rke =
let q' = Queue.copy q in
try
Ke.Rke.iter
(fun x ->
let x' = Queue.pop q' in
if x <> x' then raise Not_equal)
rke;
true
with Not_equal | Queue.Empty -> false
end
let iter iter pp_name pp_elt ppf v =
let is_first = ref true in
let pp_elt v =
if !is_first then is_first := false else Fmt.pf ppf "@ ";
Fmt.pf ppf "@[%a@]" pp_elt v
in
Fmt.pf ppf "@[<1>(%a@ " pp_name v;
iter pp_elt v;
Fmt.pf ppf ")@]"
let pp_fke pp_elt = iter Ke.Fke.iter (Fmt.any "fke") pp_elt
let pp_rke pp_elt = iter Ke.Rke.iter (Fmt.any "rke") pp_elt
let rke_of_action a =
let q =
Ke.Rke.create ~capacity:(Value.to_int (Stack.length a)) Bigarray.Int
in
let rec go : type l. (Value.t, l) Stack.action -> unit = function
| Stack.Empty -> ()
| Stack.Push (Value.V v, a) ->
go a;
Ke.Rke.push q (Value.to_int v)
| Stack.Pop a ->
go a;
ignore @@ Ke.Rke.pop_exn q
in
go a;
q
let queue_of_action a =
let q = Queue.create () in
let rec go : type l. (Value.t, l) Stack.action -> unit = function
| Stack.Empty -> ()
| Stack.Push (Value.V v, a) ->
go a;
Queue.push (Value.to_int v) q
| Stack.Pop a ->
go a;
ignore @@ Queue.pop q
in
go a;
q
let fke_of_action a =
let rec go : type l. (int Ke.Fke.t -> 'r) -> (Value.t, l) Stack.action -> 'r =
fun k -> function
| Stack.Empty -> k Ke.Fke.empty
| Stack.Push (Value.V v, a) ->
go
(fun q ->
let q = Ke.Fke.push q (Value.to_int v) in
k q)
a
| Stack.Pop a ->
go
(fun q ->
let _, q = Ke.Fke.pop_exn q in
k q)
a
in
go identity a
let () =
add_test ~name:"queue" [ map [ generate ] action_of_tree ]
@@ fun (Stack.V a) ->
let fke = fke_of_action a in
let rke = rke_of_action a in
let queue = queue_of_action a in
if not (Compare.fke queue fke) then
failf "%a <> %a" Fmt.(Dump.queue int) queue (pp_fke Fmt.int) fke;
if not (Compare.rke queue rke) then
failf "%a <> %a" Fmt.(Dump.queue int) queue (pp_rke Fmt.int) rke;
()
let ( >>= ) = dynamic_bind
let failf fmt = Fmt.kstr fail fmt
let blit src src_off dst dst_off len =
let a = Bigarray.Array1.sub src src_off len in
let b = Bigarray.Array1.sub dst dst_off len in
Bigarray.Array1.blit a b
let blit_from_string src src_off dst dst_off len =
Bigstringaf.blit_from_string src ~src_off dst ~dst_off ~len
let () =
add_test ~name:"compress-and-push"
[ range 0x100 >>= bytes_fixed; range 0x100 >>= bytes_fixed ]
@@ fun fill0 fill1 ->
let capacity = String.length fill0 + String.length fill1 in
let q, capacity = Ke.Rke.Weighted.create ~capacity Bigarray.Char in
match
Ke.Rke.Weighted.N.push q ~blit:blit_from_string ~length:String.length fill0
with
| Some [ fill0' ] -> (
let fill0' = Bigstringaf.create (Bigstringaf.length fill0') in
Ke.Rke.Weighted.compress q;
Ke.Rke.Weighted.N.keep_exn q ~blit ~length:Bigstringaf.length fill0';
match
Ke.Rke.Weighted.N.push q ~blit:blit_from_string ~length:String.length
fill1
with
| Some [ fill1' ] ->
let a =
Bigstringaf.memcmp_string fill0' 0 fill0 0 (String.length fill0)
in
let b =
Bigstringaf.memcmp_string fill1' 0 fill1 0 (String.length fill1)
in
if a <> 0 || b <> 0 then failf "Queue differs from inputs"
| Some _ -> failf "push returns multiple payloads"
| None ->
if String.length fill0 + String.length fill1 <= capacity then
failf "push fails for unknow reason"
else bad_test ())
| Some _ -> failf "push returns multiple payloads."
| None ->
if String.length fill0 <= capacity then
failf "push fails for unknow reason"
else bad_test ()