A non-moving (in the gc sense) contiguous range of bytes, useful for I/O operations.
An iobuf consists of:
All iobuf operations are restricted to operate within the limits. Initially, the
window of an iobuf is identical to the limits. A phantom type, "seek" permission,
controls whether or not code is allowed to change the limits and window. With seek
permission, the limits can be
narrowed, but can never be widened, and the window can
be set to an arbitrary subrange of the limits.
A phantom type controls whether code can read and write bytes in the bigstring (within the limits) or can only read them.
To present a restricted view of an iobuf to a client, one can create a sub-iobuf or add a type constraint.
The first type parameter controls whether the iobuf can be written to. The second type parameter controls whether the window and limits can be changed.
read_only access, a function's type uses
_ rather than
read_only as the type argument to
t. Analogously, to allow
access, a function's type uses
_ rather than
no_seek as the type argument to
_ allows the function to be directly applied to either permission. Using a
specific permission would require code to use coercion
create ~len creates a new iobuf, backed by a bigstring of length
with the limits and window set to the entire bigstring.
of_bigstring bigstring ~pos ~len returns an iobuf backed by
bigstring, with the
window and limits specified starting at
pos and of length
set_bounds_and_buffer ~src ~dst copies bounds (ie limits + window) and shallowly
copies the buffer from
read_write access is required on
because the caller might have
read_write access to
dst, and would after the call
then effectively have
read_write access to
set_bounds_and_buffer_sub ?pos ?len ~src ~dst () is a more efficient version of:
set_bounds_and_buffer ~src:(Iobuf.sub ?pos ?len src) ~dst.
set_bounds_and_buffer ~src ~dst is not the same as
set_bounds_and_buffer_sub ~dst ~src (), because the limits are narrowed in the
One may wonder why you'd want to call
no_seek, given that the casts
are already possible, e.g.
t : (read_write, _) t :> (read_only, _) t. It turns out
that if you want to define some
f : (_, seek) t -> unit of your own, which can be
conveniently applied to
read_write iobufs without the user having to
read_write up, you need this
capacity t returns the size of
t's limits subrange. The capacity of an iobuf can
be reduced via
One can call
Lo_bound.window t to get a snapshot of the lower bound of the
window, and then later restore that snapshot with
Lo_bound.restore. This is
useful for speculatively parsing, and then rewinding when there isn't enough data to
Using a snapshot with a different iobuf, even a sub iobuf of the snapshotted one, has unspecified results. An exception may be raised, or a silent error may occur. However, the safety guarantees of the iobuf will not be violated, i.e., the attempt will not enlarge the limits of the subject iobuf.
flip_lo t sets the window to range from the lower limit to the lower bound of the
old window. This is typically called after a series of
Fills, to reposition the
window in preparation to
Consume the newly written data.
The bounded version narrows the effective limit. This can preserve some data near the
limit, such as an hypothetical packet header, in the case of
unfilled suffix of a buffer, in
compact t copies data from the window to the lower limit of the iobuf and sets the
window to range from the end of the copied data to the upper limit. This is typically
called after a series of
Consumes to save unread data and prepare for the next
flip_hi t sets the window to range from the the upper bound of the current window to
the upper limit. This operation is dual to
flip_lo and is typically called when the
data in the current (narrowed) window has been processed and the window needs to be
positioned over the remaining data in the buffer. For example:
(* ... determine initial_data_len ... *) Iobuf.resize buf ~len:initial_data_len; (* ... and process initial data ... *) Iobuf.flip_hi buf;
Now the window of
buf ranges over the remainder of the data.
"consume" and "fill" functions access data at the lower bound of the window and advance lower bound of the window. "peek" and "poke" functions access data but do not advance the window.
to_string t returns the bytes in
t as a string. It does not alter the window.
to_string_hum t produces a readable, multi-line representation of an iobuf.
bounds defaults to
`Limits and determines how much of the contents are shown.
Consume.string t ~len reads
len characters (all, by default) from
t into a new
string and advances the lower bound of the window accordingly.
Consume.bin_prot X.bin_read_t t returns the initial
t, advancing past the
To_string.blito ~src ~dst ~dst_pos ~src_len () reads
src_len bytes from
src's window accordingly, and writes them into
dst starting at
dst_pos. By default
dst_pos = 0 and
src_len = length src. It is an error if
src_len don't specify a valid region of
src_len > length
Poke functions access a value at
pos from the lower bound of the window
and do not advance.
Peek.bin_prot X.bin_read_t t returns the initial
t without advancing.
bin_prot protocol, the representation of
X.bin_size_t x bytes
Fill.bin_prot do not add any size prefix or
other framing to the
Poke.bin_prot X.bin_write_t t x writes
x to the beginning of
t in binary form
without advancing. You can use
X.bin_size_t to tell how long it was.
X.bin_write_t is only allowed to write that portion of the buffer to which you have
Unsafe has submodules that are like their corresponding module, except with no range
checks. Hence, mistaken uses can cause segfaults. Be careful!
fill_bin_prot writes a bin-prot value to the lower bound of the window, prefixed by
its length, and advances by the amount written.
fill_bin_prot returns an error if
the window is too small to write the value.
consume_bin_prot t reader reads a bin-prot value from the lower bound of the window,
which should have been written using
fill_bin_prot, and advances the window by the
consume_bin_prot returns an error if there is not a complete message
in the window and in that case the window is left unchanged.
Don't use these without a good reason, as they are incompatible with similar functions
Writer. They use a 4-byte length rather than an 8-byte length.
When there are no packets awaiting reception,
returns <0 (actually, -
EAGAIN), rather than raising
EWOULDBLOCK. This saves the allocation of an exception (and backtrace) in
common cases, at the cost of callers having to handle but the return code and possible