Legend:
Library
Module
Module type
Parameter
Class
Class type
A non-moving (in the GC sense) contiguous range of bytes, useful for I/O operations.
An iobuf consists of:
bigstring
limits -- a subrange of the bigstring
window -- a subrange of the limits
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.
Functions operate on the window unless the documentation or naming indicates otherwise.
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.
See the Perms module for information on how the first type parameter is used.
To allow no_seek or seek access, a function's type uses _ rather than no_seek as the type argument to t. Using _ allows the function to be directly applied to either permission. Using a specific permission would require code to use coercion :>.
There is no t_of_sexp. One should use Iobuf.Hexdump.t_of_sexp or sexp_opaque as desired.
val invariant :
'aBase__.Invariant_intf.inv->'bBase__.Invariant_intf.inv->('a, 'b)tBase__.Invariant_intf.inv
of_string s returns a new iobuf whose contents are s.
val sub_shared : ?pos:int ->?len:int ->('d, _)t->('d, _)t
sub_shared t ~pos ~len returns a new iobuf with limits and window set to the subrange of t's window specified by pos and len. sub_shared preserves data permissions, but allows arbitrary seek permissions on the resulting iobuf.
set_bounds_and_buffer ~src ~dst copies bounds metadata (i.e., limits and window) and shallowly copies the buffer (data pointer) from src to dst. It does not access data, but does allow access through dst. This makes dst an alias of src.
Because set_bounds_and_buffer creates an alias, we disallow immutable src and dst using [> write]. Otherwise, one of src or dst could be read_write :>
read and the other immutable :> read, which would allow to write the immutable alias's data through the read_write alias.
set_bounds_and_buffer is typically used with a frame iobuf that need be allocated only once. This frame can be updated repeatedly and handed to users, without further allocation. Allocation-sensitive applications need this.
val set_bounds_and_buffer_sub :
pos:int ->len:int ->src:([> Core_kernel.Perms.Write.t ]as 'data, _)t->dst:('data, seek)t->
unit
set_bounds_and_buffer_sub ~pos ~len ~src ~dst is a more efficient version of: set_bounds_and_buffer ~src:(Iobuf.sub_shared ~pos ~len src) ~dst.
set_bounds_and_buffer ~src ~dst is not the same as set_bounds_and_buffer_sub ~dst
~src ~len:(Iobuf.length src) because the limits are narrowed in the latter case.
~len and ~pos are mandatory for performance reasons, in concert with @@inline. If they were optional, allocation would be necessary when passing a non-default, non-constant value, which is an important use case.
One may wonder why you'd want to call no_seek, given that a cast is already possible, e.g. t : (_, seek) t :> (_, no_seek) t. It turns out that if you want to define some f : (_, _) t -> unit of your own, which can be conveniently applied to seek iobufs without the user having to cast seek up, you need this no_seek function.
read_only is more of an historical convenience now that read_write is a polymorphic variant, as one can now explicitly specify the general type for an argument with something like t : (_ perms, _) t :> (read, _) t.
narrow_hi t sets t's upper limit to the end of the current window.
Changing the window
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 finish.
Similarly for Hi_bound.window and Lo_bound.restore.
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 bounded_flip_lo or unfilled suffix of a buffer, in bounded_flip_hi.
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 series of Fills and flip_lo.
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.
val protect_window_and_bounds :
('rw, no_seek)t->f:(('rw, seek)t->'a)->'a
protect_window_and_bounds t ~f applies f to t and restores t's bounds and buffer afterward.
Getting and setting 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.
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 access.
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 amount read. 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 in Reader and Writer. They use a 4-byte length rather than an 8-byte length.
recvmmsg_assume_fd_is_nonblocking fd context returns the number of context iobufs read into (or errno). fd must not block. THREAD_IO_CUTOFF is ignored.
EINVAL is returned if an Iobuf passed to Recvmmsg_context.create has its buf or limits changed.