123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244type'aiter=('a->unit)->unittypet={mutablebytes:bytes;mutablesz:int;}letcreate?(cap=0)():t=letbytes=ifcap=0thenBytes.unsafe_of_string""elseBytes.createcapin{sz=0;bytes}let[@inline]capacityself:int=Bytes.lengthself.byteslet[@inline]bytesself=self.byteslet[@inline]lengthself=self.szlet[@inline]is_emptyself=self.sz=0let[@inline]clearself=self.sz<-0(*$T
(let b = create() in is_empty b)
(let b = create ~cap:32 () in is_empty b)
(let b = create() in length b = 0)
(let b = create ~cap:32 () in length b = 0)
*)letgrow_cap_self=minSys.max_string_length(letn=capacityselfinn+nlsl1+5)letgrow_to_selfnewcap=ifnewcap=capacityselfthen(invalid_arg"byte_buf: cannot grow further";);letnewbytes=Bytes.createnewcapinBytes.blitself.bytes0newbytes0self.sz;self.bytes<-newbyteslet[@inlinenever]grow_self=letnewcap=grow_cap_selfingrow_to_selfnewcapletensure_capselfn=ifn>capacityselfthen(letnewcap=maxn(grow_cap_self)ingrow_to_selfnewcap)letshrink_toselfn=ifself.sz>nthenself.sz<-nletappend_buf(self:t)buf:unit=letn=Buffer.lengthbufinensure_capself(lengthself+n);Buffer.blitbuf0self.bytesself.szn;self.sz<-self.sz+nletappend_subbytesselfbofflen=ensure_capself(lengthself+len);Bytes.blitboffself.bytesself.szlen;self.sz<-self.sz+lenletappend_bytesselfb=append_subbytesselfb0(Bytes.lengthb)letappend_stringselfs=append_bytesself(Bytes.unsafe_of_strings)letappend_substringselfsofflen=append_subbytesself(Bytes.unsafe_of_strings)offlenlet[@inline]add_char_unsafe_selfc=Bytes.unsafe_setself.bytesself.szc;self.sz<-self.sz+1let[@inline]add_charselfc=ifself.sz=capacityselfthengrow_self;add_char_unsafe_selfclet[@inline]unsafe_getselfi=Bytes.unsafe_getself.bytesilet[@inline]unsafe_setselfic=Bytes.unsafe_setself.bytesiclet[@inline]getselfi=ifi<0||i>=self.sztheninvalid_arg"Byte_buf.get";unsafe_getselfilet[@inline]setselfic=ifi<0||i>=self.sztheninvalid_arg"Byte_buf.set";unsafe_setselficlet[@inline]contentsself=Bytes.sub_stringself.bytes0self.szlet[@inline]contents_bytesself=Bytes.subself.bytes0self.szlet[@inline]append_iterselfi=i(add_charself)let[@inline]append_seqselfseq=Seq.iter(add_charself)seqletfold_leftfaccself=let{bytes;sz}=selfin(* capture current content *)letacc=refaccinfori=0toszdoacc:=f!acc(Bytes.unsafe_getbytesi)done;!accletiterfself=let{bytes;sz}=selfin(* capture current content *)fori=0toszdof(Bytes.unsafe_getbytesi)doneletof_seqseq=letself=create~cap:32()inappend_seqselfseq;selfletof_iteriter=letself=create~cap:32()inappend_iterselfiter;selfletto_iterselfyield=iteryieldselfletto_seqself=let{bytes;sz}=selfinletrecsi()=ifi=szthenSeq.NilelseSeq.Cons(Bytes.unsafe_getbytesi,s(i+1))ins0(* TODO: unicode operators.*)(*$inject
let test_count = 2_500
open QCheck
type op =
| Add_char of char
| Add_string of string
| Get_contents
| Get of int
| Clear
| Shrink_to of int
| Set of int * char
let spf = Printf.sprintf
let str_op = function
| Add_char c -> spf "add_char %C" c
| Add_string s -> spf "add_string %S" s
| Get_contents -> "contents"
| Get i -> spf "get %d" i
| Clear -> "clear"
| Shrink_to n -> spf "shrink %d" n
| Set (i,c) -> spf "set %d %C" i c
let gen_op size : (_*_) Gen.t =
let open Gen in
let base = if size>0 then
[1, ((0--size) >|= fun x -> Get x, size);
1, ((0--size) >>= fun x -> printable >|= fun c -> Set (x,c), size);
1, ((0--size) >|= fun x -> Shrink_to x, x);
]
else []
in
frequency (base @ [
1, return (Get_contents, size);
1, return (Clear, 0);
3, (printable >|= fun c -> Add_char c, size+1);
1, (string_size (0 -- 100) ~gen:printable >|= fun s ->
Add_string s, size+String.length s);
])
let rec gen_l acc sz n =
let open Gen in
if n=0 then return (List.rev acc)
else (
gen_op sz >>= fun (op, sz) ->
gen_l (op::acc) sz (n-1)
)
let gen : op list Gen.t = Gen.sized (gen_l [] 0)
let is_valid ops =
let rec loop sz = function
| [] -> true
| Add_char _ :: tl -> loop (sz+1) tl
| Clear :: tl -> loop 0 tl
| Add_string s :: tl -> loop (sz+String.length s) tl
| (Get n | Set (n,_)) :: tl -> n < sz && loop sz tl
| Get_contents :: tl -> loop sz tl
| Shrink_to x :: tl -> x <= sz && loop x tl
in loop 0 ops
let shrink_op = Iter.(function
| Get_contents | Clear -> empty
| Get n -> Shrink.int n >|= fun n->Get n
| Add_char c -> Shrink.char c >|= fun c -> Add_char c
| Add_string s -> Shrink.string s >|= fun s -> Add_string s
| Shrink_to n -> Shrink.int n >|= fun n -> Shrink_to n
| Set (n,c) ->
(Shrink.int n >|= fun n-> Set(n,c)) <+>
(Shrink.char c >|= fun c-> Set(n,c))
)
let arb = make gen ~print:(Print.list str_op)
~shrink:Shrink.(filter is_valid @@ list ~shrink:shrink_op)
exception Nope of string
let prop_consistent ops =
let buf = ref "" in
let b = create ~cap:32 () in
let run_op op =
match op with
| Get i ->
assert (String.length !buf = length b);
let c1 = (!buf).[i] in
let c2 = get b i in
if c1<>c2 then raise (Nope (spf "c1=%C, c2=%C" c1 c2))
| Get_contents ->
let s1 = !buf in
let s2 = contents b in
if s1<>s2 then raise (Nope (spf "s1=%S, s2=%S" s1 s2))
| Add_char c -> buf := !buf ^ String.make 1 c; add_char b c
| Add_string s -> buf := !buf ^ s; append_string b s
| Clear -> buf := ""; clear b
| Shrink_to n -> buf := String.sub !buf 0 n; shrink_to b n
| Set (n,c) ->
(
let b' = Bytes.of_string !buf in
Bytes.set b' n c;
buf := Bytes.unsafe_to_string b';
);
set b n c
in
assume (is_valid ops);
try List.iter run_op ops; true
with Nope str ->
Test.fail_reportf "consistent ops failed:\n%s" str
*)(*$Q
arb (fun ops -> prop_consistent ops)
*)