From 9b4677b266072e3813eb3f203a5f273a32207c85 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 10 Feb 2015 15:51:11 +0100 Subject: [PATCH] wip: ring buffer (currently an experiment called "bufferIO") -> make it polymorphic --- _oasis | 3 +- src/data/CCBufferIO.ml | 214 ++++++++++++++++++++++++++++++++++++++++ src/data/CCBufferIO.mli | 96 ++++++++++++++++++ 3 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/data/CCBufferIO.ml create mode 100644 src/data/CCBufferIO.mli diff --git a/_oasis b/_oasis index a9c62dc0..5338b481 100644 --- a/_oasis +++ b/_oasis @@ -69,7 +69,8 @@ Library "containers_sexp" Library "containers_data" Path: src/data Modules: CCMultiMap, CCMultiSet, CCTrie, CCFlatHashtbl, CCCache, - CCPersistentHashtbl, CCDeque, CCFQueue, CCBV, CCMixtbl + CCPersistentHashtbl, CCDeque, CCFQueue, CCBV, CCMixtbl, + CCBufferIO FindlibParent: containers FindlibName: data diff --git a/src/data/CCBufferIO.ml b/src/data/CCBufferIO.ml new file mode 100644 index 00000000..58264d4f --- /dev/null +++ b/src/data/CCBufferIO.ml @@ -0,0 +1,214 @@ +(* + * BatBufferIO - Circular byte buffer + * Copyright (C) 2014 Simon Cruanes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version, + * with the special exception on linking described in file LICENSE. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *) + +(** Circular Byte Buffer for IO *) + +type t = { + mutable start : int; + mutable stop : int; (* excluded *) + mutable buf : string; +} + +exception Empty + +let create size = + { start=0; + stop=0; + buf =String.make size ' '; + } + +let copy b = + { b with buf=String.copy b.buf; } + +let of_string s = + { start=0; + stop=String.length s; + buf=String.copy s; + } + +let capacity b = String.length b.buf + +let length b = + if b.stop >= b.start + then b.stop - b.start + else (String.length b.buf - b.start) + b.stop + +(* resize [b] so that inner capacity is [cap] *) +let resize b cap = + assert (cap >= String.length b.buf); + let buf' = String.make cap ' ' in + (* copy into buf' *) + let len = + if b.stop >= b.start + then begin + String.blit b.buf b.start buf' 0 (b.stop - b.start); + b.stop - b.start + end else begin + let len_end = String.length b.buf - b.start in + String.blit b.buf b.start buf' 0 len_end; + String.blit b.buf 0 buf' len_end b.stop; + len_end + b.stop + end + in + b.buf <- buf'; + b.start <- 0; + b.stop <- len; + () + +let blit_from b s o len = + let cap = capacity b - length b in + (* resize if needed, with a constant to amortize *) + if cap < len then resize b (String.length b.buf + len + 24); + assert (capacity b - length b >= len); + if b.stop >= b.start + then (* [_______ start xxxxxxxxx stop ______] *) + let len_end = String.length b.buf - b.stop in + if len_end >= len + then (String.blit s o b.buf b.stop len; + b.stop <- b.stop + len) + else (String.blit s o b.buf b.stop len_end; + String.blit s (o+len_end) b.buf 0 (len-len_end); + b.stop <- len-len_end) + else begin (* [xxxxx stop ____________ start xxxxxx] *) + let len_middle = b.start - b.stop in + assert (len_middle >= len); + String.blit s o b.buf b.stop len; + b.stop <- b.stop + len + end; + () + +let blit_into b s o len = + if o+len > String.length s + then raise (Invalid_argument "BufferIO.blit_into"); + if b.stop >= b.start + then + let n = min (b.stop - b.start) len in + let _ = String.blit b.buf b.start s o n in + n + else begin + let len_end = String.length b.buf - b.start in + String.blit b.buf b.start s o (min len_end len); + if len_end >= len + then len (* done *) + else begin + let n = min b.stop (len - len_end) in + String.blit b.buf 0 s (o+len_end) n; + n + len_end + end + end + +let add_string b s = blit_from b s 0 (String.length s) + +(*$Q + (Q.pair Q.printable_string Q.printable_string) (fun (s,s') -> \ + let b = create 24 in add_string b s; add_string b s'; \ + String.length s + String.length s' = length b) +*) + +let to_string b = + let s = String.make (length b) ' ' in + let n = blit_into b s 0 (String.length s) in + assert (n = String.length s); + s + +(*$Q + (Q.pair Q.printable_string Q.printable_string) (fun (s,s') -> \ + let b = create 24 in add_string b s; add_string b s'; \ + to_string b = s ^ s') +*) + +let clear b = + b.stop <- 0; + b.start <- 0; + () + +let reset b = + clear b; + if capacity b > 64 + then b.buf <- String.make 64 ' '; (* reset *) + () + +let is_empty b = b.start = b.stop + +let next b = + if b.start = b.stop then raise Empty; + b.buf.[b.start] + +let pop b = + if b.start = b.stop then raise Empty; + let c = b.buf.[b.start] in + if b.start + 1 = String.length b.buf + then b.start <- 0 + else b.start <- b.start + 1; + c + +let junk b = + if b.start = b.stop then raise Empty; + if b.start + 1 = String.length b.buf + then b.start <- 0 + else b.start <- b.start + 1 + +let skip b len = + if len > length b then raise (Invalid_argument "BufferIO.skip"); + if b.stop >= b.start + then b.start <- b.start + len + else + let len_end = String.length b.buf - b.start in + if len > len_end + then b.start <- len-len_end (* wrap to the beginning *) + else b.start <- b.start + len + +(*$Q + (Q.pair Q.printable_string Q.printable_string) (fun (s,s') -> \ + let b = create 24 in add_string b s; add_string b s'; \ + add_string b "hello world"; (* big enough *) \ + let l = length b in let l' = l/2 in skip b l'; \ + length b + l' = l) +*) + +let iteri b f = + if b.stop >= b.start + then for i = b.start to b.stop - 1 do f i b.buf.[i] done + else ( + for i = b.start to String.length b.buf -1 do f i b.buf.[i] done; + for i = 0 to b.stop - 1 do f i b.buf.[i] done; + ) + +(*$T + let s = "hello world" in \ + let b = of_string s in \ + try iteri b (fun i c -> if s.[i] <> c then raise Exit); true with Exit -> false +*) + +let get b i = + if b.stop >= b.start + then + if i >= b.stop - b.start + then raise (Invalid_argument "BufferIO.get") + else b.buf.[b.start + i] + else + let len_end = String.length b.buf - b.start in + if i < len_end + then b.buf.[b.start + i] + else if i - len_end > b.stop + then raise (Invalid_argument "BufferIO.get") + else b.buf.[i - len_end] + + diff --git a/src/data/CCBufferIO.mli b/src/data/CCBufferIO.mli new file mode 100644 index 00000000..c3c12fd4 --- /dev/null +++ b/src/data/CCBufferIO.mli @@ -0,0 +1,96 @@ +(* + * BatBufferIO - Circular byte buffer + * Copyright (C) 2014 Simon Cruanes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version, + * with the special exception on linking described in file LICENSE. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *) + +(** Circular Byte Buffer for IO *) + +type t = private { + mutable start : int; + mutable stop : int; (* excluded *) + mutable buf : string; +} + +exception Empty + +val create : int -> t +(** [create size] creates a new buffer with given size *) + +val copy : t -> t +(** fresh copy of the buffer *) + +val of_string : string -> t +(** build a buffer from an initial string. The string is copied. + Use {!String.blit_from} if you want more control. *) + +val capacity : t -> int +(** length of the inner string buffer *) + +val length : t -> int +(** number of bytes currently stored in the buffer *) + +val blit_from : t -> string -> int -> int -> unit +(** [blit_from buf s o len] copies the slice [o, ... o + len - 1] from + the string [s] to the end of the buffer. + @raise Invalid_argument if [o,len] is not a valid slice of [s] *) + +val blit_into : t -> string -> int -> int -> int +(** [blit_into buf s o len] copies at most [len] bytes from [buf] + into [s], starting at offset [o] in [s]. + @return the number of bytes actually copied ([min len (length buf)]). + @raise Invalid_argument if [o,len] is not a valid slice of [s] *) + +val add_string : t -> string -> unit +(** [add_string buf s] adds [s] at the end of [buf]. *) + +val to_string : t -> string +(** extract the current content into a string *) + +val clear : t -> unit +(** clear the content of the buffer. Doesn't actually destroy the content. *) + +val reset : t -> unit +(** clear the content of the buffer, and also resize it to a default size *) + +val is_empty : t -> bool +(** is the buffer empty (i.e. contains no byte)? *) + +val next : t -> char +(** obtain next char (the first one of the buffer) + @raise Empty if the buffer is empty *) + +val pop : t -> char +(** obtain and remove next char (the first one) + @raise Empty if the buffer is empty *) + +val junk : t -> unit +(** Drop next element. + @raise Empty if the buffer is already empty *) + +val skip : t -> int -> unit +(** [skip b len] removes [len] elements from [b]. + @raise Invalid_argument if [len > length b]. *) + +val iteri : t -> (int -> char -> unit) -> unit +(** [iteri b f] calls [f i c] for each char [c] in [buf], with [i] + being its relative index within [buf]. *) + +val get : t -> int -> char +(** [get buf i] returns the [i]-th character of [buf], ie the one that + is returned by [next buf] after [i-1] calls to [junk buf]. + @raise Invalid_argument if the index is invalid (> [length buf]) *)