diff --git a/src/core/CCByte_buffer.ml b/src/core/CCByte_buffer.ml new file mode 100644 index 00000000..aa612af6 --- /dev/null +++ b/src/core/CCByte_buffer.ml @@ -0,0 +1,114 @@ +type 'a iter = ('a -> unit) -> unit + +type t = { + mutable bytes: bytes; + mutable sz: int; +} + +let create ?(size=0) () : t = + let bytes = + if size=0 then Bytes.unsafe_of_string "" else Bytes.create size in + { sz=size; bytes } + +let[@inline] capacity self : int = Bytes.length self.bytes +let[@inline] bytes self = self.bytes +let[@inline] length self = self.sz + +let[@inline] is_empty self = self.sz = 0 +let[@inline] clear self = self.sz <- 0 + +let grow_cap_ self = + min Sys.max_string_length + (let n = capacity self in n + n lsl 1 + 5) + +let grow_to_ self newcap = + if newcap = capacity self then ( + invalid_arg "byte_buf: cannot grow further"; + ); + let newbytes = Bytes.create newcap in + Bytes.blit self.bytes 0 newbytes 0 self.sz; + self.bytes <- newbytes + +let[@inline never] grow_ self = + let newcap = grow_cap_ self in + grow_to_ self newcap + +let ensure_cap self n = + if n>capacity self then ( + let newcap = max n (grow_cap_ self) in + grow_to_ self newcap + ) + +let shrink_to self n = + if self.sz > n then self.sz <- n + +let append_buf (self:t) buf : unit = + let n = Buffer.length buf in + ensure_cap self (length self + n); + Buffer.blit buf 0 self.bytes self.sz n; + self.sz <- self.sz + n + +let append_subbytes self b off len = + ensure_cap self (length self + len); + Bytes.blit b off self.bytes self.sz len; + self.sz <- self.sz + len + +let append_bytes self b = append_subbytes self b 0 (Bytes.length b) +let append_string self s = append_bytes self (Bytes.unsafe_of_string s) +let append_substring self s off len = append_subbytes self (Bytes.unsafe_of_string s) off len + +let[@inline] add_char_unsafe_ self c = + Bytes.unsafe_set self.bytes self.sz c; + self.sz <- self.sz + 1 + +let[@inline] add_char self c = + if self.sz = capacity self then grow_ self; + add_char_unsafe_ self c + +let[@inline] unsafe_get self i = Bytes.unsafe_get self.bytes i +let[@inline] unsafe_set self i c = Bytes.unsafe_set self.bytes i c + +let[@inline] get self i = + if i < 0 || i >= self.sz then invalid_arg "Byte_buf.get"; + unsafe_get self i + +let[@inline] set self i c = + if i < 0 || i >= self.sz then invalid_arg "Byte_buf.set"; + unsafe_set self i c + +let[@inline] append_iter self i = i (add_char self) +let[@inline] append_seq self seq = Seq.iter (add_char self) seq + +let fold_left f acc self = + let {bytes; sz} = self in (* capture current content *) + + let acc = ref acc in + for i=0 to sz do + acc := f !acc (Bytes.unsafe_get bytes i) + done; + !acc + +let iter f self = + let {bytes; sz} = self in (* capture current content *) + for i=0 to sz do + f (Bytes.unsafe_get bytes i) + done + +let of_seq seq = + let self = create ~size:32 () in + append_seq self seq; + self + +let of_iter iter = + let self = create ~size:32 () in + append_iter self iter; + self + +let to_iter self yield = iter yield self +let to_seq self = + let {bytes;sz} = self in + let rec s i () = + if i= sz then Seq.Nil + else Seq.Cons (Bytes.unsafe_get bytes i, s (i+1)) + in + s 0 diff --git a/src/core/CCByte_buffer.mli b/src/core/CCByte_buffer.mli new file mode 100644 index 00000000..9592094d --- /dev/null +++ b/src/core/CCByte_buffer.mli @@ -0,0 +1,66 @@ + +(** Byte buffer. + + A dynamic vector of bytes. + @since NEXT_RELEASE +*) + +type t + +type 'a iter = ('a -> unit) -> unit + +val create : ?size:int -> unit -> t + +val length : t -> int +(** Current length. *) + +val capacity : t -> int +(** Current capacity (size of the array returned by {!bytes}) *) + +val bytes : t -> bytes +(** Access the underlying byte buffer. This buffer can change after + operations that affect the capacity (e.g. {!add_char}). *) + +val clear : t -> unit + +val ensure_cap : t -> int -> unit +(** [ensure_cap self n] ensures that [capacity self >= n]. *) + +val shrink_to : t -> int -> unit +(** [shrink_to buf n] reduces [length buf] to at most [n]. + Does nothing if the length is already <= n. *) + +val add_char : t -> char -> unit +(** Push a character at the end. *) + +val append_bytes : t -> bytes -> unit + +val append_subbytes : t -> bytes -> int -> int -> unit + +val append_string : t -> string -> unit + +val append_substring : t -> string -> int -> int -> unit + +val append_buf : t -> Buffer.t -> unit + +val append_iter : t -> char iter -> unit + +val append_seq : t -> char Seq.t -> unit + +val get : t -> int -> char + +val unsafe_get : t -> int -> char + +val set : t -> int -> char -> unit + +val unsafe_set : t -> int -> char -> unit + +val iter : (char -> unit) -> t -> unit + +val fold_left : ('a -> char -> 'a) -> 'a -> t -> 'a + +val of_iter : char iter -> t +val of_seq : char Seq.t -> t + +val to_iter : t -> char iter +val to_seq : t -> char Seq.t diff --git a/src/core/containers.ml b/src/core/containers.ml index c6172f92..3d7951e2 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -5,6 +5,7 @@ module Array = CCArray module Bool = CCBool +module Byte_buf = CCByte_buf module Char = CCChar module Equal = CCEqual module Either = CCEither diff --git a/src/core/containersLabels.ml b/src/core/containersLabels.ml index 29b1e21a..a320761f 100644 --- a/src/core/containersLabels.ml +++ b/src/core/containersLabels.ml @@ -5,6 +5,7 @@ module Array = CCArrayLabels module Bool = CCBool +module Byte_buf = CCByte_buf module Char = CCChar module Equal = CCEqualLabels module Either = CCEither