Module Core.Iobuf
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 its limits. A phantom type, the "seek" permission, controls whether or not code is allowed to change the limits and window. With seek permission, the limits can be narrow
ed, 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.
type nonrec seek
= Iobuf_intf.seek
val sexp_of_seek : seek -> Ppx_sexp_conv_lib.Sexp.t
type nonrec no_seek
= Iobuf_intf.no_seek
val sexp_of_no_seek : no_seek -> Ppx_sexp_conv_lib.Sexp.t
type t_repr
This type is a compiler witness that 'rw and 'seek do not affect layout; it enables wider use of unboxed GADTs.
type (-'data_perm_read_write, +'seek_permission) t
= private t_repr
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
orseek
access, a function's type uses_
rather thanno_seek
as the type argument tot
. 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 useIobuf.Hexdump.t_of_sexp
or@sexp.opaque
as desired.
include Core__.Import.Invariant.S2 with type ('rw, 'seek) t := ('rw, 'seek) t
val invariant : 'a Base__.Invariant_intf.inv -> 'b Base__.Invariant_intf.inv -> ('a, 'b) t Base__.Invariant_intf.inv
module Window : Core__.Import.Hexdump.S2 with type ('rw, 'seek) t := ('rw, 'seek) t
Provides a
Window.Hexdump
submodule that renders the contents oft
's window.
module Limits : Core__.Import.Hexdump.S2 with type ('rw, 'seek) t := ('rw, 'seek) t
Provides a
Limits.Hexdump
submodule that renders the contents oft
's limits.
Provides a Hexdump
submodule that renders the contents of t
's window and limits using indices relative to the limits.
include Iobuf_intf.Compound_hexdump with type ('rw, 'seek) t := ('rw, 'seek) t
module Hexdump : sig ... end
module Debug : Iobuf_intf.Compound_hexdump with type ('rw, 'seek) t := ('rw, 'seek) t
Provides a
Debug.Hexdump
submodule that renders the contents oft
's window, limits, and underlying bigstring using indices relative to the bigstring.
Creation
val create : len:int -> (_, _) t
create ~len
creates a new iobuf, backed by a bigstring of lengthlen
, with the limits and window set to the entire bigstring.
val of_bigstring : ?pos:int -> ?len:int -> Core__.Import.Bigstring.t -> ([< Core__.Import.read_write ], _) t
of_bigstring bigstring ~pos ~len
returns an iobuf backed bybigstring
, with the window and limits specified starting atpos
and of lengthlen
.forbid
immutable
to prevent aliasing
val of_string : string -> (_, _) t
of_string s
returns a new iobuf whose contents ares
.
sub_shared t ~pos ~len
returns a new iobuf with limits and window set to the subrange oft
's window specified bypos
andlen
.sub_shared
preserves data permissions, but allows arbitrary seek permissions on the resulting iobuf.
val set_bounds_and_buffer : src:([> Core__.Import.write ] as 'data, _) t -> dst:('data, seek) t -> unit
set_bounds_and_buffer ~src ~dst
copies bounds metadata (i.e., limits and window) and shallowly copies the buffer (data pointer) fromsrc
todst
. It does not access data, but does allow access throughdst
. This makesdst
an alias ofsrc
.Because
set_bounds_and_buffer
creates an alias, we disallow immutablesrc
anddst
using[> write]
. Otherwise, one ofsrc
ordst
could beread_write :> read
and the otherimmutable :> read
, which would allow you to write theimmutable
alias's data through theread_write
alias.set_bounds_and_buffer
is typically used with a frame iobuf that need only be allocated 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__.Import.write ] as 'data, _) t -> dst:('data, seek) t -> unit
set_bounds_and_buffer_sub ~pos ~len ~src ~dst
is a more efficient version ofset_bounds_and_buffer ~src:(Iobuf.sub_shared ~pos ~len src) ~dst
.set_bounds_and_buffer ~src ~dst
is not the same asset_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.
val read_only : ([> Core__.Import.read ], 's) t -> (Core__.Import.read, 's) t
Generalization
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 somef : (_, _) t -> unit
of your own that can be conveniently applied toseek
iobufs without the user having to castseek
up, you need thisno_seek
function.read_only
is more of a historical convenience now thatread_write
is a polymorphic variant, as one can now explicitly specify the general type for an argument with something liket : (_ perms, _) t :> (read, _) t
.
Accessors
val capacity : (_, _) t -> int
capacity t
returns the size oft
's limits subrange. The capacity of an iobuf can be reduced vianarrow
.
val length : (_, _) t -> int
length t
returns the size oft
's window.
val length_lo : (_, _) t -> int
length_lo t
returns the length thatt
's window would have after callingflip_lo
, without actually changing the window. This is the number of bytes between the lower limit and the start of the window.When you're writing to the window, you can think of this as the number of bytes already written. When reading from the window, this can mean the number of bytes already consumed.
This is equivalent to:
Iobuf.Expert.(lo t - lo_min t)
.
val length_hi : (_, _) t -> int
length_hi t
returns the length thatt
's window would have after callingflip_hi
, without actually changing the window. This is the number of bytes between the end of the window and the upper limit of the buffer.This is equivalent to:
Iobuf.Expert.(hi_max t - hi t)
.
val is_empty : (_, _) t -> bool
is_empty t
islength t = 0
.
Changing the limits
Comparison
Changing the window
module type Bound = Iobuf_intf.Bound with type ('d, 'w) iobuf := ('d, 'w) t
val advance : (_, seek) t -> int -> unit
advance t amount
advances the lower bound of the window byamount
. It is an error to advance past the upper bound of the window or the lower limit.
val unsafe_advance : (_, seek) t -> int -> unit
unsafe_advance
is likeadvance
but with no bounds checking, so incorrect usage can easily cause segfaults.
val resize : (_, seek) t -> len:int -> unit
resize t
sets the length oft
's window, provided it does not exceed limits.
val unsafe_resize : (_, seek) t -> len:int -> unit
unsafe_resize
is likeresize
but with no bounds checking, so incorrect usage can easily cause segfaults.
val flip_lo : (_, seek) t -> unit
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 ofFill
s, to reposition the window in preparation toConsume
the newly written data.The bounded version narrows the effective limit. This can preserve some data near the limit, such as a hypothetical packet header (in the case of
bounded_flip_lo
) or unfilled suffix of a buffer (inbounded_flip_hi
).
val bounded_flip_lo : (_, seek) t -> Lo_bound.t -> unit
val compact : (Core__.Import.read_write, seek) t -> unit
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 ofConsume
s to save unread data and prepare for the next series ofFill
s andflip_lo
.
val bounded_compact : (Core__.Import.read_write, seek) t -> Lo_bound.t -> Hi_bound.t -> unit
val flip_hi : (_, seek) t -> unit
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 toflip_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.
Getting and setting data
"consume" and "fill" functions access data at the lower bound of the window and advance the lower bound of the window. "peek" and "poke" functions access data but do not advance the window.
val to_string : ?len:int -> ([> Core__.Import.read ], _) t -> string
to_string t
returns the bytes int
as a string. It does not alter the window.
val to_string_hum : ?max_lines:int -> (_, _) t -> string
Equivalent to
Hexdump.to_string_hum
. Renderst
's windows and limits.
val to_bytes : ?len:int -> (_, _) t -> Core__.Import.Bytes.t
to_bytes t
returns the bytes int
as a bytes. It does not alter the window.
val of_bytes : Core__.Import.Bytes.t -> (_, _) t
of_bytes b
returns a new iobuf whose contents isb
.
module Consume : sig ... end
Consume.string t ~len
readslen
characters (all, by default) fromt
into a new string and advances the lower bound of the window accordingly.
module Fill : sig ... end
Fill.bin_prot X.bin_write_t t x
writesx
tot
in bin-prot form, advancing past the bytes written.
module Peek : sig ... end
Peek
andPoke
functions access a value atpos
from the lower bound of the window and do not advance.
module Poke : sig ... end
Poke.bin_prot X.bin_write_t t x
writesx
to the beginning oft
in binary form without advancing. You can useX.bin_size_t
to tell how long it was.X.bin_write_t
is only allowed to write that portion of the buffer you have access to.
module Unsafe : sig ... end
Unsafe
has submodules that are like their corresponding module, except with no range checks. Hence, mistaken uses can cause segfaults. Be careful!
val bin_prot_length_prefix_bytes : int
The number of bytes in the length prefix of
consume_bin_prot
andfill_bin_prot
.
val fill_bin_prot : ([> Core__.Import.write ], seek) t -> 'a Bin_prot.Type_class.writer -> 'a -> unit Core__.Import.Or_error.t
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 usingfill_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
andWriter
. They use a 4-byte length rather than an 8-byte length.
val consume_bin_prot : ([> Core__.Import.read ], seek) t -> 'a Bin_prot.Type_class.reader -> 'a Core__.Import.Or_error.t
module Blit : sig ... end
Blit
copies between iobufs and advances neithersrc
nordst
.
module Blit_consume : sig ... end
Blit_consume
copies between iobufs and advancessrc
but does not advancedst
.
module Blit_fill : sig ... end
Blit_fill
copies between iobufs and advancesdst
but does not advancesrc
.
module Blit_consume_and_fill : sig ... end
Blit_consume_and_fill
copies between iobufs and advances bothsrc
anddst
.
I/O
val compare_ok_or_eof : ok_or_eof -> ok_or_eof -> int
val sexp_of_ok_or_eof : ok_or_eof -> Ppx_sexp_conv_lib.Sexp.t
val input : ([> Core__.Import.write ], seek) t -> Core__.Import.In_channel.t -> ok_or_eof
Iobuf
has analogs of variousBigstring
functions. These analogs advance by the amount written/read.
val read : ([> Core__.Import.write ], seek) t -> Iobuf_intf.Unix.File_descr.t -> ok_or_eof
val read_assume_fd_is_nonblocking : ([> Core__.Import.write ], seek) t -> Iobuf_intf.Unix.File_descr.t -> Core__.Syscall_result.Unit.t
val pread_assume_fd_is_nonblocking : ([> Core__.Import.write ], seek) t -> Iobuf_intf.Unix.File_descr.t -> offset:int -> unit
val recvfrom_assume_fd_is_nonblocking : ([> Core__.Import.write ], seek) t -> Iobuf_intf.Unix.File_descr.t -> Iobuf_intf.Unix.sockaddr
module Recvmmsg_context : sig ... end with type ('rw, 'seek) iobuf := ('rw, 'seek) t
recvmmsg
's context comprises data needed by the system call. Setup can be expensive, particularly for many buffers.
val recvmmsg_assume_fd_is_nonblocking : (Iobuf_intf.Unix.File_descr.t -> Recvmmsg_context.t -> Iobuf_intf.Unix.Syscall_result.Int.t) Core__.Import.Or_error.t
recvmmsg_assume_fd_is_nonblocking fd context
returns the number ofcontext
iobufs read into (orerrno
).fd
must not block.THREAD_IO_CUTOFF
is ignored.EINVAL
is returned if anIobuf
passed toRecvmmsg_context.create
has itsbuf
or limits changed.
val send_nonblocking_no_sigpipe : unit -> (([> Core__.Import.read ], seek) t -> Iobuf_intf.Unix.File_descr.t -> Iobuf_intf.Unix.Syscall_result.Unit.t) Core__.Import.Or_error.t
val sendto_nonblocking_no_sigpipe : unit -> (([> Core__.Import.read ], seek) t -> Iobuf_intf.Unix.File_descr.t -> Iobuf_intf.Unix.sockaddr -> Iobuf_intf.Unix.Syscall_result.Unit.t) Core__.Import.Or_error.t
val output : ([> Core__.Import.read ], seek) t -> Core__.Import.Out_channel.t -> unit
val write : ([> Core__.Import.read ], seek) t -> Iobuf_intf.Unix.File_descr.t -> unit
val write_assume_fd_is_nonblocking : ([> Core__.Import.read ], seek) t -> Iobuf_intf.Unix.File_descr.t -> unit
val pwrite_assume_fd_is_nonblocking : ([> Core__.Import.read ], seek) t -> Iobuf_intf.Unix.File_descr.t -> offset:int -> unit
Expert
module Expert : sig ... end
The
Expert
module is for building efficient out-of-moduleIobuf
abstractions.