605 lines
25 KiB
OCaml
605 lines
25 KiB
OCaml
(** A small library for manipulating bigstrings.
|
|
|
|
A bigstring is a mutable data structure that contains a fixed-length
|
|
sequence of bytes. Each byte can be indexed in constant time for reading and
|
|
writing.
|
|
|
|
Given a byte sequence [bstr] of length [len], we can access each of the
|
|
[len] bytes of [bstr] via its index in the sequence. Indexes start at [0],
|
|
and will call an index valid in [bstr] if it falls within the range
|
|
[[0...len-1]] (inclusive). A position is the point between two bytes or at
|
|
the beginning or end of the sequence. We call a position valid in [bstr] if
|
|
it falls within the range [[0...len]] (inclusive). Note that byte at index
|
|
[n] is between positions [n] and [n+1].
|
|
|
|
Two parameters [off] and [len] are said to designate a valid range of [bstr]
|
|
if [len >= 0] and [off] and [off+len] are valid positions in [bstr].
|
|
|
|
Byte sequences can be modified in place, for instance via the {!val:set} and
|
|
{!val:blit} functions described below.
|
|
|
|
{1:bigarray Bigstrings & Bigarrays.}
|
|
|
|
Bigstring is a specialised version of {!module:Bigarray} that not only
|
|
handles bytes in the form of {!module:Char}acter but also imposes a "C-like"
|
|
(see {!val:Bigarray.c_layout}) view as described above and allows common
|
|
functions such as [memcpy(3)] or [memmove(3)] to be offered.
|
|
|
|
For more details about Bigstrings and Bigarrays, we invite you to read the
|
|
{!module:Bigarray} documentation, which offers more general functions that
|
|
can be applied to Bigstrings.
|
|
|
|
{1:bytes Bigstrings & Bytes.}
|
|
|
|
Like bytes, a bigstring is a mutable data structure that contains a
|
|
fixed-length sequence of bytes. However, a bigstring has a few special
|
|
features that can make it more interesting to use than bytes.
|
|
|
|
{2:location Bigstrings and the Garbage Collector.}
|
|
|
|
A bigstring is not allocated in the same way as a standard OCaml value. In
|
|
fact, the byte sequence that the bigstring refers to is found in the
|
|
{i C heap} (rather than the OCaml heap). This means that the byte sequence
|
|
can come from a [malloc(3)] or a function requesting a particular memory
|
|
area from the system such as [Unix.map_file].
|
|
|
|
This particularity has an implication with the GC: the byte sequence is
|
|
{b not relocatable}. That is to say that during the cycle of the Garbage
|
|
Collector, this byte sequence does not move — in contrast, a [bytes] can be
|
|
moved by the GC (typically, from the minor heap to the major heap).
|
|
|
|
Thus, bigstrings have advantages and disadvantages compared to bytes due to
|
|
this particularity:
|
|
- Creating a bigstring can be expensive. Whether it is with
|
|
[malloc(3)]/{!val:create} or [Unix.map_file], creating a bigstring will
|
|
always be more expensive than creating bytes with OCaml. For small byte
|
|
sequences, it is therefore preferable to use bytes.
|
|
- Since a bigstring cannot be moved, its position can be shared by [Thread]s
|
|
and/or [Domain]s without interacting with the GC. An example is being able
|
|
to perform a complex computation in parallel from the bytes of this
|
|
sequence without {i blocking} the Garbage Collector during this
|
|
computation.
|
|
|
|
Depending on these characteristics, it may be more advantageous to use a
|
|
bigstring rather than [bytes]. This basically depends on your usage, and the
|
|
special features of bigstrings can unlock opportunities to outperform byte
|
|
calculations or analysis.
|
|
|
|
{2:sub Bigstring and slice.}
|
|
|
|
Another advantage of bigstrings is that copying is avoided when extracting
|
|
part of a larger bigstring. This is because the {!val:sub} function returns
|
|
a "proxy" of the original bigstring.
|
|
|
|
In this respect, and to be very precise, {!val:sub} avoids copying but the
|
|
creation of this "proxy" {b remains} costly. In addition, this library is
|
|
distributed with a new {!module:Slice_bstr} module. The latter offers a new
|
|
type whose {!val:Slice_bstr.sub} function is much less costly than
|
|
{!val:sub}.
|
|
|
|
{1 Bigstrings.} *)
|
|
|
|
type t = (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
|
|
|
|
(** {2 Constructors.} *)
|
|
|
|
val empty : t
|
|
(** [empty] is an empty bigstring. *)
|
|
|
|
val create : int -> t
|
|
(** [create len] returns a new byte sequence of length [len]. The sequence
|
|
{b is unitialized} and contains arbitrary bytes. *)
|
|
|
|
val make : int -> char -> t
|
|
(** [make len chr] is {!type:t} of length [len] with each index holding the
|
|
character [chr]. *)
|
|
|
|
val copy : t -> t
|
|
(** [copy t] returns a new byte sequence that contains the same bytes as the
|
|
argument. *)
|
|
|
|
val init : int -> (int -> char) -> t
|
|
(** [init len fn] returns a fresh byte sequence of length [len], with character
|
|
[idx] initialized to the result of [fn idx] (in increasing index order). *)
|
|
|
|
(** {2 Memory-safe Operations.} *)
|
|
|
|
val of_string : string -> t
|
|
(** [of_string str] returns a new {!type:t} that contains the contents of the
|
|
given string [str]. *)
|
|
|
|
val string : ?off:int -> ?len:int -> string -> t
|
|
(** [string ~off ~len str] is the sub-buffer of [str] that starts at position
|
|
[off] (defaults to [0]) and stops at position [off + len] (defaults to
|
|
[String.length str]). [str] is fully-replaced by a fresh allocated
|
|
{!type:t}.
|
|
|
|
@raise Invalid_argument
|
|
if [off] and [len] do not designate a valid range of [str]. *)
|
|
|
|
val sub_string : t -> off:int -> len:int -> string
|
|
(** [sub_string bstr ~off ~len] returns a string of length [len] containing the
|
|
bytes of [bstr] starting at [off].
|
|
|
|
@raise Invalid_argument
|
|
if [off] and [len] do not designate a valid range of [t]. *)
|
|
|
|
val to_string : t -> string
|
|
(** [to_string bstr] is equivalent to
|
|
[sub_string bstr ~off:0 ~len:(length bstr)]. *)
|
|
|
|
val length : t -> int
|
|
(** [length bstr] is the number of bytes in [bstr]. *)
|
|
|
|
val get : t -> int -> char
|
|
(** [get bstr i] is the byte of [bstr]' at index [i]. This is equivalent to the
|
|
[bstr.{i}] notation.
|
|
|
|
@raise Invalid_argument if [i] is not an index of [bstr]. *)
|
|
|
|
val set : t -> int -> char -> unit
|
|
(** [set t i chr] modifies [t] in place, replacing the byte at index [i] with
|
|
[chr].
|
|
|
|
@raise Invalid_argument if [i] is not a valid index in [t]. *)
|
|
|
|
val unsafe_get : t -> int -> char
|
|
(** [unsafe_get t idx] is like {!val:get} except no bounds checking is
|
|
performed. *)
|
|
|
|
val unsafe_set : t -> int -> char -> unit
|
|
(** [unsafe_set t idx chr] is like {!val:set} except no bounds checking is
|
|
performed. *)
|
|
|
|
val chop : ?rev:bool -> t -> char option
|
|
(** [chop bstr] returns the first element of [bstr] or the last element if
|
|
[rev = true]. If [bstr] is empty, it returns [None]. *)
|
|
|
|
val concat : string -> t list -> t
|
|
(** [concat sep ts] concatenates the list of bigstrings [ts], inserting the
|
|
separator string [sep] between each. *)
|
|
|
|
val extend : t -> int -> int -> t
|
|
(** [extend bstr left right] returns a new bigstring that contains the bytes of
|
|
[bstr], with [left] zero bytes prepended and [right] zero byte appended to
|
|
it. If [left] or [right] is negative, then bytes are removed (instead of
|
|
appended) from the corresponding side of [bstr].
|
|
|
|
@raise Invalid_argument if the result length is negative *)
|
|
|
|
(** {2 Copy operation from one byte sequence to another.} *)
|
|
|
|
val blit : t -> src_off:int -> t -> dst_off:int -> len:int -> unit
|
|
(** [blit src ~src_off dst ~dst_off ~len] copies [len] bytes from byte sequence
|
|
[src], starting at index [src_off], to byte sequence [dst], starting at
|
|
index [dst_off]. It works correctly even if [src] and [dst] are (physically)
|
|
the same byte sequence, and the source and destination intervals overlap.
|
|
|
|
@raise Invalid_argument
|
|
if [src_off] and [len] do not designate a valid range of [src], or if
|
|
[dst_off] and [len] do not designate a valid range of [dst]. *)
|
|
|
|
val blit_from_string :
|
|
string -> src_off:int -> t -> dst_off:int -> len:int -> unit
|
|
(** Just like {!val:blit}, but with a string as source one.
|
|
|
|
{b Note}: since it is impossible for [src] to overlap [dst], {!val:memcpy}
|
|
is used to do the copy.
|
|
|
|
@raise Invalid_argument
|
|
if [src_pos] and [len] do not designate a valid range of [src], or if
|
|
[dst_off] and [len] do not designate a valid range of [dst]. *)
|
|
|
|
val blit_from_bytes :
|
|
bytes -> src_off:int -> t -> dst_off:int -> len:int -> unit
|
|
(** Just like {!val:blit}, but with a bytes as source one.
|
|
|
|
{b Note}: since it is impossible for [src] to overlap [dst], {!val:memcpy}
|
|
is used to do the copy.
|
|
|
|
@raise Invalid_argument
|
|
if [src_pos] and [len] do not designate a valid range of [src], or if
|
|
[dst_off] and [len] do not designate a valid range of [dst]. *)
|
|
|
|
val blit_to_bytes : t -> src_off:int -> bytes -> dst_off:int -> len:int -> unit
|
|
(** [blit_to_bytes src ~src_off dst ~dst_off ~len] copies [len] bytes from
|
|
[src], starting at index [src_off], to byte sequence [dst], starting at
|
|
index [dst_off].
|
|
|
|
{b Note}: since it is impossible for [src] to overlap [dst], {!val:memcpy}
|
|
is used to do the copy.
|
|
|
|
@raise Invalid_argument
|
|
if [src_off] and [len] do not designate a valid range of [src], or if
|
|
[dst_off] and [len] do not designate a valid range of [dst]. *)
|
|
|
|
val memcpy : t -> src_off:int -> t -> dst_off:int -> len:int -> unit
|
|
(** [memcpy src ~src_off dst ~dst_off ~len] copies [len] bytes from [src] to
|
|
[dst]. [src] {b must not} overlap [dst]. Use {!val:memmove} if [src] & [dst]
|
|
do overlap.
|
|
|
|
You can check whether two buffers overlap using {!val:overlap}. If this
|
|
returns [None], the two values do not refer to a common memory area — and it
|
|
is safe to use memcpy.
|
|
|
|
@raise Invalid_argument
|
|
if [src_off] and [len] do not designate a valid range of [src], or if
|
|
[dst_off] and [len] do not designate a valid range of [dst]. *)
|
|
|
|
val memcpy_mmaped : t -> src_off:int -> t -> dst_off:int -> len:int -> unit
|
|
(** [memcpy_mmaped] is like {!val:memcpy} but [src] and [dst] can be a
|
|
{i mmaped} bigarray (from [Unix.map_file]). In this specific case, copying
|
|
from one to the other can take some time because it involves reading/writing
|
|
to disk. The operation can take longer than if the two bigarrays were
|
|
allocated via [malloc()]/{!val:Bigarray.Array1.create}.
|
|
|
|
It may therefore be worthwhile to release the GC lock so that this specific
|
|
operation can be carried out in parallel (in a [Thread]) without
|
|
interruption by the GC.
|
|
|
|
Note that the bigarrays do not necessarily need to be {i mmaped}. This
|
|
function also applies to "normal" bigarrays. It may also be worthwhile to
|
|
use this function if you know that you are copying a large area and would
|
|
like to do it in parallel (in a [Thread]). *)
|
|
|
|
val memmove : t -> src_off:int -> t -> dst_off:int -> len:int -> unit
|
|
(** [memmove src ~src_off dst ~dst_off ~len] copies [len] bytes from [src] to
|
|
[dst]. [src] and [dst] may overlap: copying takes place as though the bytes
|
|
in [src] are first copied into a temporary array that does not overlap [src]
|
|
or [dst], and the bytes are then copied from the temporary array to [dst].
|
|
|
|
@raise Invalid_argument
|
|
if [src_off] and [len] do not designate a valid range of [src], or if
|
|
[dst_off] and [len] do not designate a valid range of [dst]. *)
|
|
|
|
val memmove_mmaped : t -> src_off:int -> t -> dst_off:int -> len:int -> unit
|
|
(** [memmove_mmaped] is like {!val:memmove} but [src] and [dst] can be a
|
|
{i mmaped} bigarray (from [Unix.map_file]). In this specific case, copying
|
|
from one to the other can take some time because it involves reading/writing
|
|
to disk. The operation can take longer than if the two bigarrays were
|
|
allocated via [malloc()]/{!val:Bigarray.Array1.create}.
|
|
|
|
It may therefore be worthwhile to release the GC lock so that this specific
|
|
operation can be carried out in parallel (in a [Thread]) without
|
|
interruption by the GC.
|
|
|
|
Note that the bigarrays do not necessarily need to be {i mmaped}. This
|
|
function also applies to "normal" bigarrays. It may also be worthwhile to
|
|
use this function if you know that you are copying a large area and would
|
|
like to do it in parallel (in a [Thread]). *)
|
|
|
|
val memcmp : t -> src_off:int -> t -> dst_off:int -> len:int -> int
|
|
(** [memcmp s1 ~src_off s2 ~dst_off ~len] compares the first [len] bytes of the
|
|
memory areas [s1] (starting at [src_off]) and [s2] (starting at [dst_off]).
|
|
|
|
[memcmp] returns [0] is [s1] and [s2] don't match.
|
|
|
|
@raise Invalid_argument
|
|
if [src_off] and [len] do not designate a valid range of [src], or if
|
|
[dst_off] and [len] do not designate a valid range of [dst]. *)
|
|
|
|
val memset : t -> off:int -> len:int -> char -> unit
|
|
(** [memset t ~off ~len chr] fills [len] bytes (starting at [off]) into [t] with
|
|
the constant byte [chr].
|
|
|
|
@raise Invalid_argument
|
|
if [off] and [len] do not designate a valid range of [t]. *)
|
|
|
|
val fill : t -> ?off:int -> ?len:int -> char -> unit
|
|
(** [fill t off len chr] modifies [t] in place, replacing [len] characters with
|
|
[chr], starting at [off].
|
|
|
|
@raise Invalid_argument
|
|
if [off] and [len] do not designate a valid range of [t]. *)
|
|
|
|
(** {2 Decode integers from a byte sequence.} *)
|
|
|
|
val get_int8 : t -> int -> int
|
|
(** [get_int8 bstr i] is [bstr]'s signed 8-bit integer starting at byte index
|
|
[i]. *)
|
|
|
|
val get_uint8 : t -> int -> int
|
|
(** [get_uint8 bstr i] is [bstr]'s unsigned 8-bit integer starting at byte index
|
|
[i]. *)
|
|
|
|
val get_uint16_ne : t -> int -> int
|
|
(** [get_int16_ne bstr i] is [bstr]'s native-endian unsigned 16-bit integer
|
|
starting at byte index [i]. *)
|
|
|
|
val get_uint16_le : t -> int -> int
|
|
(** [get_int16_le bstr i] is [bstr]'s little-endian unsigned 16-bit integer
|
|
starting at byte index [i]. *)
|
|
|
|
val get_uint16_be : t -> int -> int
|
|
(** [get_int16_be bstr i] is [bstr]'s big-endian unsigned 16-bit integer
|
|
starting at byte index [i]. *)
|
|
|
|
val get_int16_ne : t -> int -> int
|
|
(** [get_int16_ne bstr i] is [bstr]'s native-endian signed 16-bit integer
|
|
starting at byte index [i]. *)
|
|
|
|
val get_int16_le : t -> int -> int
|
|
(** [get_int16_le bstr i] is [bstr]'s little-endian signed 16-bit integer
|
|
starting at byte index [i]. *)
|
|
|
|
val get_int16_be : t -> int -> int
|
|
(** [get_int16_be bstr i] is [bstr]'s big-endian signed 16-bit integer starting
|
|
at byte index [i]. *)
|
|
|
|
val get_int32_ne : t -> int -> int32
|
|
(** [get_int32_ne bstr i] is [bstr]'s native-endian 32-bit integer starting at
|
|
byte index [i]. *)
|
|
|
|
val get_int32_le : t -> int -> int32
|
|
(** [get_int32_le bstr i] is [bstr]'s little-endian 32-bit integer starting at
|
|
byte index [i]. *)
|
|
|
|
val get_int32_be : t -> int -> int32
|
|
(** [get_int32_be bstr i] is [bstr]'s big-endian 32-bit integer starting at byte
|
|
index [i]. *)
|
|
|
|
val get_int64_ne : t -> int -> int64
|
|
(** [get_int64_ne bstr i] is [bstr]'s native-endian 64-bit integer starting at
|
|
byte index [i]. *)
|
|
|
|
val get_int64_le : t -> int -> int64
|
|
(** [get_int64_le bstr i] is [bstr]'s little-endian 64-bit integer starting at
|
|
byte index [i]. *)
|
|
|
|
val get_int64_be : t -> int -> int64
|
|
(** [get_int64_be bstr i] is [bstr]'s big-endian 64-bit integer starting at byte
|
|
index [i]. *)
|
|
|
|
val set_int8 : t -> int -> int -> unit
|
|
(** [set_int8 t i v] sets [t]'s signed 8-bit integer starting at byte index [i]
|
|
to [v]. *)
|
|
|
|
val set_uint8 : t -> int -> int -> unit
|
|
(** [set_uint8 t i v] sets [t]'s unsigned 8-bit integer starting at byte index
|
|
[i] to [v]. *)
|
|
|
|
val set_uint16_ne : t -> int -> int -> unit
|
|
(** [set_uint16_ne t i v] sets [t]'s native-endian unsigned 16-bit integer
|
|
starting at byte index [i] to [v]. *)
|
|
|
|
val set_uint16_le : t -> int -> int -> unit
|
|
(** [set_uint16_le t i v] sets [t]'s little-endian unsigned 16-bit integer
|
|
starting at byte index [i] to [v]. *)
|
|
|
|
val set_uint16_be : t -> int -> int -> unit
|
|
(** [set_uint16_le t i v] sets [t]'s big-endian unsigned 16-bit integer starting
|
|
at byte index [i] to [v]. *)
|
|
|
|
val set_int16_ne : t -> int -> int -> unit
|
|
(** [set_uint16_ne t i v] sets [t]'s native-endian signed 16-bit integer
|
|
starting at byte index [i] to [v]. *)
|
|
|
|
val set_int16_le : t -> int -> int -> unit
|
|
(** [set_uint16_le t i v] sets [t]'s little-endian signed 16-bit integer
|
|
starting at byte index [i] to [v]. *)
|
|
|
|
val set_int16_be : t -> int -> int -> unit
|
|
(** [set_uint16_le t i v] sets [t]'s big-endian signed 16-bit integer starting
|
|
at byte index [i] to [v]. *)
|
|
|
|
val set_int32_ne : t -> int -> int32 -> unit
|
|
(** [set_int32_ne t i v] sets [t]'s native-endian 32-bit integer starting at
|
|
byte index [i] to [v]. *)
|
|
|
|
val set_int32_le : t -> int -> int32 -> unit
|
|
(** [set_int32_ne t i v] sets [t]'s little-endian 32-bit integer starting at
|
|
byte index [i] to [v]. *)
|
|
|
|
val set_int32_be : t -> int -> int32 -> unit
|
|
(** [set_int32_ne t i v] sets [t]'s big-endian 32-bit integer starting at byte
|
|
index [i] to [v]. *)
|
|
|
|
val set_int64_ne : t -> int -> int64 -> unit
|
|
(** [set_int32_ne t i v] sets [t]'s native-endian 64-bit integer starting at
|
|
byte index [i] to [v]. *)
|
|
|
|
val set_int64_le : t -> int -> int64 -> unit
|
|
(** [set_int32_ne t i v] sets [t]'s little-endian 64-bit integer starting at
|
|
byte index [i] to [v]. *)
|
|
|
|
val set_int64_be : t -> int -> int64 -> unit
|
|
(** [set_int32_ne t i v] sets [t]'s big-endian 64-bit integer starting at byte
|
|
index [i] to [v]. *)
|
|
|
|
val sub : t -> off:int -> len:int -> t
|
|
(** [sub bstr ~off ~len] does not allocate a bigstring, but instead returns a
|
|
new view into [bstr] starting at [off], and with length [len].
|
|
|
|
{b Note} [sub] does not allocate a new buffer, but instead shares the memory
|
|
area of [bstr] with the newly-returned bigstring. This means that the
|
|
changes ([set{,_*}] functions) made to the returned bigstring will also be
|
|
reflected in the [bstr] bigstring given.
|
|
|
|
{b Note} [sub] is more expensive than a [Slice.sub] (about 8 times slower).
|
|
If you want to focus on performance while avoiding copying, it's best to use
|
|
a [Slice]. *)
|
|
|
|
val shift : t -> int -> t
|
|
(** [shift bstr n] is [sub bstr n (length bstr - n)] (see {!val:sub} for more
|
|
details). *)
|
|
|
|
val overlap : t -> t -> (int * int * int) option
|
|
(** [overlap x y] returns the size (in bytes) of what is physically common
|
|
between [x] and [y], as well as the position of [y] in [x] and the position
|
|
of [x] in [y]. *)
|
|
|
|
(** {2 Predicates and comparaisons.} *)
|
|
|
|
val is_empty : t -> bool
|
|
(** [is_empty bstr] is [length bstr = 0]. *)
|
|
|
|
val is_prefix : affix:string -> t -> bool
|
|
(** [is_prefix ~affix bstr] is [true] iff [affix.[idx] = bstr.{idx}] for all
|
|
indices [idx] of [affix]. *)
|
|
|
|
val starts_with : prefix:t -> t -> bool
|
|
(** [starts_with ~prefix t] is like {!val:is_prefix} but the prefix is a
|
|
{!type:t} (instead of a [string]). *)
|
|
|
|
val is_infix : affix:string -> t -> bool
|
|
(** [is_infix ~affix bstr] is [true] iff there exists an index [j] in [bstr]
|
|
such that for all indices [i] of [affix] we have [affix.[i] = bstr.{j + i}].
|
|
*)
|
|
|
|
val is_suffix : affix:string -> t -> bool
|
|
(** [is_suffix ~affix bstr] is [true] iff [affix.[n - idx] = bstr.{m - idx}] for
|
|
all indices [idx] of [affix] with [n = String.length affix - 1] and
|
|
[m = length bstr - 1]. *)
|
|
|
|
val ends_with : suffix:t -> t -> bool
|
|
(** [ends_with ~suffix t] is like {!val:is_suffix} but the suffix is a {!type:t}
|
|
(instead of a [string]. *)
|
|
|
|
val for_all : (char -> bool) -> t -> bool
|
|
(** [for_all p bstr] is [true] iff for all indices [idx] of [bstr],
|
|
[p bstr.{idx} = true]. *)
|
|
|
|
val contains : t -> ?off:int -> ?len:int -> char -> bool
|
|
(** [contains bstr ?off ?len chr] is [true] if and only if [chr] appears in
|
|
[len] byte(s)'s [bstr] after position [off] (defaults to [0]). *)
|
|
|
|
val equal : t -> t -> bool
|
|
(** [equal a b] is [a = b]. *)
|
|
|
|
val constant_equal : t -> t -> bool
|
|
(** [constant_equal] gives the same result as {!val:equal} but the execution
|
|
time of the function, whether or not the two values are equivalent (as long
|
|
as they have the {b same} size) is the same.
|
|
|
|
Indeed, the {!val:equal} function ends as soon as a difference exists. This
|
|
function continues even if a difference exists. This function is useful when
|
|
comparing passwords — and avoiding an {i timing attack}. *)
|
|
|
|
val compare : t -> t -> int
|
|
(** [compare bstr0 bstr1] sorts [bstr0] and [bstr1] in lexicographical order. *)
|
|
|
|
val index : t -> ?off:int -> ?len:int -> char -> int option
|
|
(** [index bstr ?off ?len chr] is the index of the first occurrence of [chr] in
|
|
[len] byte(s)'s [bstr] after position [off] (defaults to [0]). If [chr] does
|
|
not occur in given range of [bstr], we return [None].
|
|
|
|
@raise Invalid_argument
|
|
if [off] and [len] do not designate a valid range of [bstr]. *)
|
|
|
|
val memchr : t -> off:int -> len:int -> char -> int
|
|
(** [memchr t ~off ~len chr] scans [len] bytes (starting at [off]) of [t] for
|
|
the first instance of [chr]. It returns the position in [t] where the first
|
|
occurrence of [chr] is found. Otherwise, it returns [-1].
|
|
|
|
@raise Invalid_argument
|
|
if [off] and [len] do not designate a valid range of [t]. *)
|
|
|
|
(** {2 Extracting substrings.} *)
|
|
|
|
val with_range : ?first:int -> ?len:int -> t -> t
|
|
(** [with_range ~first ~len bstr] are the consecutive bytes of [bstr] whose
|
|
indices exist in the range \[[first];[first + len - 1]\].
|
|
|
|
[first] defaults to [0] and [len] to [max_int]. Note that [first] can be any
|
|
integer and [len] any positive integer. *)
|
|
|
|
val with_index_range : ?first:int -> ?last:int -> t -> t
|
|
(** [with_index_range ~first ~last bstr] are the consecutive bytes of [bstr]
|
|
whose indices exists in the range \[[first];[last]\].
|
|
|
|
[first] defaults to [0] and [last] to [length bstr - 1].
|
|
|
|
Note that both [first] and [last] can be any integer. If [first > last] the
|
|
interval is empty and the empty bigstring is returned. *)
|
|
|
|
val trim : ?drop:(char -> bool) -> t -> t
|
|
(** [trim ~drop bstr] is [bstr] with prefix and suffix bytes satisfying [drop]
|
|
in [bstr] removed. [drop] defaults to [fun chr -> chr = ' ']. *)
|
|
|
|
val span :
|
|
?rev:bool -> ?min:int -> ?max:int -> ?sat:(char -> bool) -> t -> t * t
|
|
(** [span ~rev ~min ~max ~sat bstr] is [(l, r)] where:
|
|
- if [rev] is [false] (default), [l] is at least [min] and at most [max]
|
|
consecutive [sat] satisfying initial bytes of [bstr] or {!empty} if there
|
|
are no such bytes. [r] are the remaining bytes of [bstr].
|
|
- if [rev] is [true], [r] is at least [min] and at most [max] consecutive
|
|
[sat] satisfying final bytes of [bstr] or {!empty} if there are no such
|
|
bytes. [l] are the remaining bytes of [bstr].
|
|
|
|
If [max] is unspecified the span is unlimited. If [min] is unspecified it
|
|
defaults to [0]. If [min > max] the condition can't be satisfied and the
|
|
left or right span, depending on [rev], is always empty. [sat] defaults to
|
|
[Fun.const true].
|
|
|
|
@raise Invalid_argument if [max] or [min] is negative. *)
|
|
|
|
val take : ?rev:bool -> ?min:int -> ?max:int -> ?sat:(char -> bool) -> t -> t
|
|
(** [take ~rev ~min ~max ~sat bstr] is the matching span of {!span} without the
|
|
remaining one. In other words:
|
|
|
|
{[
|
|
(if rev then snd else fst) (span ~rev ~min ~max ~sat bstr)
|
|
]} *)
|
|
|
|
val drop : ?rev:bool -> ?min:int -> ?max:int -> ?sat:(char -> bool) -> t -> t
|
|
(** [drop ~rev ~min ~max ~sat bstr] is the remaining span of {!span} without the
|
|
matching span. In other words:
|
|
|
|
{[
|
|
(if rev then fst else snd) (span ~rev ~min ~max ~sat bstr)
|
|
]} *)
|
|
|
|
val cut : ?rev:bool -> sep:string -> t -> (t * t) option
|
|
(** [cut ~sep bstr] is either the pair [Some (l, r)] of the two (possibly empty)
|
|
sub-buffers of [bstr] that are delimited by the first match of the non empty
|
|
separator string [sep] or [None] if [sep] can't be matched in [bstr].
|
|
Matching starts from the beginning of [bstr] ([rev] is [false], default) or
|
|
the end ([rev] is [true]).
|
|
|
|
The invariant [l ^ sep ^ r = s] holds.
|
|
|
|
For instance, the {i ABNF} expression:
|
|
|
|
{v
|
|
field_name := *PRINT
|
|
field_value := *ASCII
|
|
field := field_name ":" field_value
|
|
v}
|
|
|
|
can be translated to:
|
|
|
|
{[
|
|
match Bstr.cut ~sep:":" value with
|
|
| Some (field_name, field_value) -> ...
|
|
| None -> invalid_arg "Invalid field"
|
|
]}
|
|
|
|
@raise Invalid_argument if [sep] is the empty buffer. *)
|
|
|
|
val split_on_char : char -> t -> t list
|
|
(** [split_on_char sep t] is the list of all (possibly empty)
|
|
{!val:sub}-bigstrings of [t] that are delimited by the character [sep]. If
|
|
[t] is empty, the result is the singleton list [[empty]].
|
|
|
|
The function's result is specified by the following invariant:
|
|
- the list is not empty.
|
|
- concatenating its elements using [sep] as a separator returns a bigstring
|
|
equal to the input.
|
|
- no bigstring in the result contains the [sep] character. *)
|
|
|
|
(** {2 Traversing strings.} *)
|
|
|
|
val iter : (char -> unit) -> t -> unit
|
|
(** [iter fn t] applies function [fn] in turn to all the characters of [t]. It
|
|
is equivalent to [fn t.{0}; fn t.{1}; ...; fn t.{length t - 1}; ()]. *)
|
|
|
|
val to_seq : t -> char Seq.t
|
|
(** Iterate on the bigstring, in increasing index order. Modifications of the
|
|
bigstring during iteration will be reflected in the sequence. *)
|
|
|
|
val to_seqi : t -> (int * char) Seq.t
|
|
(** Iterate on the bigstring, in increasing order, yielding indices along chars.
|
|
*)
|
|
|
|
val of_seq : char Seq.t -> t
|
|
(** Create a bigstring from the generator. *)
|