diff --git a/lib/cstruct.ml b/lib/cstruct.ml index 6ebb1c8..092643a 100644 --- a/lib/cstruct.ml +++ b/lib/cstruct.ml @@ -384,22 +384,19 @@ let fillv ~src ~dst = ) in aux dst 0 src - -let to_bytes t = - let sz = len t in - let b = Bytes.create sz in - unsafe_blit_bigstring_to_bytes t.buffer t.off b 0 sz; - b - -let to_string t = +let to_string ?(off=0) ?len:sz t = + let len = match sz with None -> len t - off | Some l -> l in (* The following call is safe, since this is the only reference to the freshly-created value built by [to_bytes t]. *) - Bytes.unsafe_to_string (to_bytes t) + copy t off len -let of_data_abstract blitfun lenfun ?allocator ?(off=0) ?len buf = +let to_bytes ?off ?len t = + Bytes.unsafe_of_string (to_string ?off ?len t) + +let [@inline always] of_data_abstract blitfun lenfun ?allocator ?(off=0) ?len buf = let buflen = match len with - | None -> lenfun buf + | None -> lenfun buf - off | Some len -> len in match allocator with | None -> @@ -417,7 +414,11 @@ let of_string ?allocator ?off ?len buf = let of_bytes ?allocator ?off ?len buf = of_data_abstract blit_from_bytes Bytes.length ?allocator ?off ?len buf -let of_hex str = +let of_hex ?(off=0) ?len str = + let str = + let l = match len with None -> String.length str - off | Some l -> l in + String.sub str off l + in let string_fold ~f ~z str = let st = ref z in ( String.iter (fun c -> st := f !st c) str ; !st ) diff --git a/lib/cstruct.mli b/lib/cstruct.mli index 75633fd..22ba4f0 100644 --- a/lib/cstruct.mli +++ b/lib/cstruct.mli @@ -177,7 +177,8 @@ val empty : t val of_bigarray: ?off:int -> ?len:int -> buffer -> t (** [of_bigarray ~off ~len b] is the cstruct contained in [b] starting - at [off], of length [len]. *) + at offset [off] (default [0]) of length [len] + (default [Bigarray.Array1.dim b - off]). *) val to_bigarray: t -> buffer (** [to_bigarray t] converts a {!t} into a {!buffer} Bigarray, using @@ -202,24 +203,34 @@ val create_unsafe : int -> t val of_string: ?allocator:(int -> t) -> ?off:int -> ?len:int -> string -> t (** [of_string ~allocator ~off ~len str] is the cstruct representation of [str] - slice located at [off] offset and of [len] length, + slice located at offset [off] (default [0]) and of length [len] (default + [String.length str - off]), with the underlying buffer allocated by [alloc]. If [allocator] is not - provided, [create] is used. *) + provided, [create] is used. + + @raise Invalid_argument if [off] or [len] is negative, or + [String.length str - off] < [len]. +*) val of_bytes: ?allocator:(int -> t) -> ?off:int -> ?len:int -> bytes -> t (** [of_bytes ~allocator byt] is the cstruct representation of [byt] - slice located at [off] offset and of [len] length, + slice located at offset [off] (default [0]) and of length [len] (default + [Bytes.length byt - off]), with the underlying buffer allocated by [alloc]. If [allocator] is not - provided, [create] is used. *) + provided, [create] is used. + + @raise Invalid_argument if [off] or [len] is negative, or + [Bytes.length str - off] < [len]. *) -val of_hex: string -> t -(** [of_hex str] is the cstruct [cs]. Every pair of hex-encoded characters in - [str] are converted to one byte in [cs]. Whitespaces (space, newline, tab, - carriage return) in [str] are skipped. The resulting cstruct is exactly - half the size of the non-skipped characters of [str]. +val of_hex: ?off:int -> ?len:int -> string -> t +(** [of_hex ~off ~len str] is the cstruct [cs]. Every pair of hex-encoded + characters in [str] starting at offset [off] (default [0]) of length [len] + (default [String.length str - off]) are converted to one byte in [cs]. + Whitespaces (space, newline, tab, carriage return) in [str] are skipped. @raise Invalid_argument if the input string contains invalid characters or - has an odd numbers of non-whitespace characters. *) + has an odd numbers of non-whitespace characters, or if [off] or [len] are + negative, or [String.length str - off] < [len]. *) (** {2 Comparison } *) @@ -351,13 +362,21 @@ val split: ?start:int -> t -> int -> t * t @raise Invalid_argument if [start] exceeds the cstruct length, or if there is a bounds violation of the cstruct via [len+start]. *) -val to_string: t -> string -(** [to_string t] will allocate a fresh OCaml [string] and copy the - contents of the cstruct into it, and return that string copy. *) +val to_string: ?off:int -> ?len:int -> t -> string +(** [to_string ~off ~len t] will allocate a fresh OCaml [string] and copy the + contents of the cstruct starting at offset [off] (default [0]) of length + [len] (default [Cstruct.len t - off]) into it, and return that string. + + @raise Invalid_argument if [off] or [len] is negative, or + [Cstruct.len str - off] < [len]. *) + +val to_bytes: ?off:int -> ?len:int -> t -> bytes +(** [to_bytes ~off ~len t] will allocate a fresh OCaml [bytes] and copy the + contents of the cstruct starting at offset [off] (default [0]) of length + [len] (default [Cstruct.len t - off]) into it, and return that bytes. -val to_bytes: t -> bytes -(** [to_bytes t] will allocate a fresh OCaml [bytes] and copy the - contents of the cstruct into it, and return that byte copy. *) + @raise Invalid_argument if [off] or [len] is negative, or + [Cstruct.len str - off] < [len]. *) (** {2 Debugging } *) diff --git a/lib/cstruct_cap.ml b/lib/cstruct_cap.ml index 6108d35..21d840d 100644 --- a/lib/cstruct_cap.ml +++ b/lib/cstruct_cap.ml @@ -29,24 +29,8 @@ type wo = < wr: unit; > external ro : 'a rd t -> ro t = "%identity" external wo : 'a wr t -> wo t = "%identity" -let of_string ?off ?len x = - Cstruct.of_string ?off ?len x -let of_bytes ?off ?len x = - Cstruct.of_bytes ?off ?len x - -let to_string ?(off= 0) ?len t = - let len = match len with - | Some len -> len - | None -> Cstruct.length t - off in - Cstruct.copy t off len - -let to_bytes ?(off= 0) ?len t = - let len = match len with - | Some len -> len - | None -> Cstruct.length t - off in - (* XXX(dinosaure): this is safe when [copy] allocates itself [bytes] - and uses [Bytes.unsafe_to_string]. *) - Bytes.unsafe_of_string (Cstruct.copy t off len) +let of_string = Cstruct.of_string ?allocator:None +let of_bytes = Cstruct.of_bytes ?allocator:None let pp ppf t = Cstruct.hexdump_pp ppf t @@ -72,6 +56,8 @@ let sub t ~off ~len = Cstruct.sub t off len [@@inline] +let unsafe_to_bigarray = Cstruct.to_bigarray + let concat vss = let res = create_unsafe (Cstruct.sum_lengths ~caller:"Cstruct.Cap.concat" vss) in let go off v = diff --git a/lib/cstruct_cap.mli b/lib/cstruct_cap.mli index fa02b47..d74453f 100644 --- a/lib/cstruct_cap.mli +++ b/lib/cstruct_cap.mli @@ -15,6 +15,35 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) +(** Raw memory buffers with capabilities + + [Cstruct_cap] wraps OCaml Stdlib's + {{:http://caml.inria.fr/pub/docs/manual-ocaml/libref/Bigarray.html}Bigarray} + module. Each [t] consists of a proxy (consisting of offset, length, and the + actual {!Bigarray.t} buffer). The goal of this module is two-fold: enable + zero-copy - the underlying buffer is shared by most of the functions - and + static checking of read and write capabilities to the underlying buffer + (using phantom types). + + Each ['a t] is parameterized by the available capabilities: read ([rd]) and + write ([wr]): to access the contents of the buffer the [read] capability is + necessary, for modifying the content of the buffer the [write] capability is + necessary. Capabilities can only be dropped, never gained, to a buffer. If + code only has read capability, this does not mean that there is no other code + fragment with write capability to the underlying buffer. + + The functions that retrieve bytes ({!get_uint8} etc.) require a [read] + capability, functions mutating the underlying buffer ({!set_uint8} etc.) + require a [write] capability. Allocation of a buffer (via {!create}, ...) + returns a [t] with read and write capabilities. {!ro} drops the write + capability, {!wo} drops the read capability. The only exception is + {!unsafe_to_bigarray} that returns the underlying [Bigarray.t]. + + Accessors and mutators for fixed size integers (8, 16, 32, 64 bit) are + provided for big-endian and little-endian encodings. *) + +(** {2 Types} *) + type 'a rd = < rd: unit; .. > as 'a (** Type of read capability. *) @@ -27,14 +56,14 @@ type 'a t type buffer = (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t (** Type of buffer. A {!t} is composed of an underlying buffer. *) -type rdwr = < rd: unit; wr: unit; > -(** Type of read-and-capability. *) +type rdwr = < rd: unit; wr: unit; > +(** Type of both read and write capability. *) type ro = < rd: unit; > -(** Type of read-only capability. *) +(** Type of only read capability. *) type wo = < wr: unit; > -(** Type of write-only capability. *) +(** Type of only write capability. *) type uint8 = int (** 8-bit unsigned integer. *) @@ -48,15 +77,59 @@ type uint32 = int32 type uint64 = int64 (** 64-bit unsigned integer. *) +(** {2 Capabilities} *) + +val ro : 'a rd t -> ro t +(** [ro t] is [t'] with only read capability. *) + +val wo : 'a wr t -> wo t +(** [wo t] is [t'] with only write capability. *) + +(** {2 Basic operations} *) + +val equal : 'a rd t -> 'b rd t -> bool +(** [equal a b] is [true] iff [a] and [b] correspond to the same sequence of + bytes (it uses [memcmp] internally). Both [a] and [b] need at least read + capability {!rd}. *) + +val compare : 'a rd t -> 'b rd t -> int +(** [compare a b] gives an unspecified total ordering over {!t}. Both [a] and + [b] need at least read capability {!rd}. *) + +val pp : Format.formatter -> 'a rd t -> unit +(** [pp ppf t] pretty-prints [t] on [ppf]. [t] needs read capability {!rd}. *) + +val length : 'a t -> int +(** [length t] return length of [t]. Note that this length is potentially + smaller than the actual size of the underlying buffer, as functions such as + {!sub}, {!shift}, and {!split} can construct a smaller view. *) + +val check_alignment : 'a t -> int -> bool +(** [check_alignment t alignment] is [true] if the first byte stored + in the underlying buffer of [t] is at a memory address where + [address mod alignment = 0], [false] otherwise. The [mod] used has the + C/OCaml semantic (which differs from Python). + Typical uses are to check a buffer is aligned to a page or disk sector + boundary. + + @raise Invalid_argument if [alignment] is not a positive integer. *) + +val lenv : 'a t list -> int +(** [lenv vs] is the combined length of all {!t} in [vs]. + + @raise Invalid_argument if computing the sum overflows. *) + +(** {2 Constructors} *) + val create : int -> rdwr t -(** [create len] is a fresh read-and-write {!t} of size [len]. with an offset of - 0, filled with zero bytes. *) +(** [create len] allocates a buffer and proxy with both read and write + capabilities of size [len]. It is filled with zero bytes. *) val create_unsafe : int -> rdwr t -(** [create_unsafe len] is a fresh read-and-write {!t} of size [len] with an - offset of 0. +(** [create_unsafe len] allocates a buffer and proxy with both read and + write capabilities of size [len]. - Note that the returned cstruct will contain arbitrary data, likely including + Note that the returned [t] will contain arbitrary data, likely including the contents of previously-deallocated cstructs. Beware! @@ -64,172 +137,225 @@ val create_unsafe : int -> rdwr t Forgetting to replace this data could cause your application to leak sensitive information. *) -val ro : 'a rd t -> ro t -(** [ro t] has [t] to be read-only then. *) +(** {2 Subviews} *) -val wo : 'a wr t -> wo t -(** [wo t] has [t] to be write-only then. *) +val sub : 'a t -> off:int -> len:int -> 'a t +(** [sub t ~off ~len] returns a proxy which shares the underlying buffer of [t]. + It is sliced at offset [off] and of length [len]. The returned value has the + same capabilities as [t]. -val of_string : ?off:int -> ?len:int -> string -> rdwr t -(** [of_string ~off ~len s] is a fresh read-and-write {!t} of [s] sliced on - [off] (default is [0]) and of [len] (default is [String.length s]) length. *) + @raise Invalid_argument if the offset exceeds [t] length. *) -val of_bytes : ?off:int -> ?len:int -> bytes -> rdwr t val of_hex : string -> rdwr t -(** [of_bytes ~off ~len x] is a fresh read-and-write {!t} of [x] sliced on - [off] (default is [0]) and of [len] (default is [Bytes.length x]) length. *) +val shift : 'a t -> int -> 'a t +(** [shift t len] returns a proxy which shares the underlying buffer of [t]. The + returned value starts [len] bytes later than the given [t]. The returned + value has the same capabilities as [t]. -val to_bigarray : 'a t -> buffer -(** [to_bigarray t] converts {!t} into a {!buffer} Bigarray, using the Bigarray - slicing to allocate a fresh {i proxy} Bigarray that preserves sharing of the - underlying buffer. + @raise Invalid_argument if the offset exceeds [t] length. *) - In other words: +val split : ?start:int -> 'a t -> int -> 'a t * 'a t +(** [split ~start t len] returns two proxies extracted from [t]. The first + starts at offset [start] (default [0]), and is of length [len]. The second + is the remainder of [t]. The underlying buffer is shared, the capabilities + are preserved. - {[let t = Cstruct_cap.create 10 in - let b = Cstruct_cap.to_bigarray t in - Bigarray.Array1.set b 0 '\x42' ; - assert (Cstruct_cap.get_char t 0 = '\x42')]} *) + @raise Invalid_argument if [start] exceeds the length of [t], + or if there is a bounds violation of [t] via [len + start]. *) -val equal : 'a rd t -> 'b rd t -> bool -(** [equal a b] is [true] iff [a] and [b] correspond to the same sequence of - bytes (it uses [memcmp] internally). Both need at least read capability - {!rd}. *) +(** {2 Construction from existing t} *) -val compare : 'a rd t -> 'b rd t -> int -(** [compare a b] gives an unspecified total ordering over {!t}. Both need at - least read capability {!rd}. *) +val append : 'a rd t -> 'b rd t -> rdwr t +(** [append a b] allocates a buffer [r] of size [length a + length b]. Then the + content of [a] is copied at the start of the buffer [r], and [b] is copied + behind [a]'s end in [r]. [a] and [b] need at least read capability {!rd}, + the returned value has both read and write capabilities. *) -val check_alignment : 'a rd t -> int -> bool -(** [check_alignment t alignment] is [true] if the first byte stored - within [t] is at a memory address where [address mod alignment = 0], - [false] otherwise. The [mod] used has the C/OCaml semantic (which differs - from Python). - Typical uses are to check a buffer is aligned to a page or disk sector - boundary. [t] needs at least read capability {!rd}. +val concat : 'a rd t list -> rdwr t +(** [concat vss] allocates a buffer [r] of size [lenv vss]. Each [v] of [vss] + is copied into the buffer [r]. Each [v] of [vss] need at least read + capability {!rd}, the returned value has both read and write capabilities. +*) - @raise Invalid_argument if [alignment] is not a positive integer. *) +val fillv : src:'a rd t list -> dst:'b wr t -> int * 'a rd t list +(** [fillv ~src ~dst] copies from [src] to [dst] until [src] is exhausted or + [dst] is full. It returns the number of bytes copied and the remaining data + from [src], if any. This is useful if you want to {i bufferize} data into + fixed-sized chunks. Each {!t} of [src] need at least read capability {!rd}. + [dst] needs at least write capability {!wr}. *) -val get_char : 'a rd t -> int -> char -(** [get_char t off] returns the character contained in [t] at offset [off]. - [t] needs at least read capability {!rd}. +val rev : 'a rd t -> rdwr t +(** [rev t] allocates a buffer [r] of size [length t], and fills it with the + bytes of [t] in reverse order. The given [t] needs at least read capability + {!rd}, the returned value has both read and write capabilities. *) - @raise Invalid_argument if the offset exceeds [t] length (which can differ - from underlying buffer length). *) +(** {2 Mutation of the underlying buffer} *) -val get_uint8 : 'a rd t -> int -> uint8 -(** [get_uint8 t off] returns the byte contained in [t] at offset [off]. - [t] needs at least read capability {!rd}. +val memset : 'a wr t -> int -> unit +(** [memset t x] sets all bytes of [t] to [x land 0xFF]. [t] needs at least + write capability {!wr}. *) - @raise Invalid_argument if the offset exceeds [t] length (which can differ - from underlying buffer length). *) +val blit : 'a rd t -> src_off:int -> 'b wr t -> dst_off:int -> len:int -> unit +(** [blit src ~src_off dst ~dst_off ~len] copies [len] bytes from [src] starting + at index [src_off] to [dst] starting at index [dst_off]. It works correctly + even if [src] and [dst] refer to the same underlying buffer, and the [src] + and [dst] intervals overlap. This function uses [memmove] internally. -val set_char : 'a wr t -> int -> char -> unit -(** [set_char t off c] sets the character contained in [t] at offset [off] - to character [c]. [t] needs at least write capability {!wr}. + [src] needs at least read capability {!rd}. [dst] needs at least + write capability {!wr}. + + @raise Invalid_argument if [src_off] and [len] do not designate a valid + segment of [src], or if [dst_off] and [len] do not designate a valid segment + of [dst]. *) - @raise Invalid_argument if the offset exceeds [t] length (which can differ - from underlying buffer length). *) +val blit_from_string : string -> src_off:int -> 'a wr t -> dst_off:int -> + len:int -> unit +(** [blit_from_string src ~src_off dst ~dst_off ~len] copies [len] byres from + [src] starting at index [src_off] to [dst] starting at index [dst_off]. This + function uses [memcpy] internally. -val set_uint8 : 'a wr t -> int -> uint8 -> unit -(** [set_uint8 t off x] sets the byte contained in [t] at offset [off] - to byte [x]. [t] needs at least write capability {!wr}. + [dst] needs at least write capability {!wr}. + + @raise Invalid_argument if [src_off] and [len] do not designate a valid + sub-string of [src], or if [dst_off] and [len] do not designate a valid + segment of [dst]. *) - @raise Invalid_argument if the offset exceeds [t] length (which can differ - from underlying buffer length). *) +val blit_from_bytes : bytes -> src_off:int -> 'a wr t -> dst_off:int -> len:int + -> unit +(** [blit_from_bytes src ~src_off dst ~dst_off ~len] copies [len] bytes from + [src] starting at index [src_off] to [dst] starting at index [dst_off]. This + uses [memcpy] internally. -val sub : 'a rd t -> off:int -> len:int -> 'a rd t -(** [sub t ~off ~len] returns a fresh {!t} with the shared underlying buffer of - [t] sliced on [off] and of [len] length. New {!t} shares same capabilities - than [t]. + [dst] needs at least write capability {!wr}. - @raise Invalid_argument if the offset exceeds [t] length. *) + @raise Invalid_argument if [src_off] and [len] do not designate a valid + sub-sequence of [src], or if [dst_off] and [len] do no designate a valid + segment of [dst]. *) -val shift : 'a rd t -> int -> 'a rd t -(** [shift t len] returns a fresh {!t} with the shared underlying buffer of [t] - shifted to [len] bytes. New {!t} shares same capabilities than [t]. +(** {2 Converters: string, bytes, bigarray} *) - @raise Invalid_argument if the offset exceeds [t] length. *) +val of_string : ?off:int -> ?len:int -> string -> rdwr t +(** [of_string ~off ~len s] allocates a buffer and copies the contents of [s] + into it starting at offset [off] (default [0]) and of length [len] (default + [String.length s - off]). The returned value has both read and write + capabilities. + + @raise Invalid_argument if [off] and [len] does not designate a valid + segment of [s]. *) val to_string : ?off:int -> ?len:int -> 'a rd t -> string (** [to_string ~off ~len t] is the string representation of the segment of [t] - starting at [off] (default is [0]) of size [len] (default is [length t]). - [t] needs at least read-capability {!rd}. + starting at [off] (default [0]) of size [len] (default [length t - off]). + [t] needs at least read capability {!rd}. @raise Invalid_argument if [off] and [len] does not designate a valid segment of [t]. *) +val of_hex : ?off:int -> ?len:int -> string -> rdwr t +(** [of_hex ~off ~len s] allocates a buffer and copies the content of [s] + starting at offset [off] (default [0]) of length [len] (default + [String.length s - off]), decoding the hex-encoded characters. + Whitespaces in the string are ignored, every pair of hex-encoded characters + in [s] are converted to one byte in the returned {!t}, which is exactly + half the size of the non-whitespace characters of [s] from [off] of length + [len]. + + @raise Invalid_argument is the input string contains invalid characters or + an off number of non-whitespace characters. *) + +val copyv : 'a rd t list -> string +(** [copy vs] is the string representation of the concatenation of all {!t} in + [vs]. Each {!t} need at least read capability {!rd}. + + @raise Invalid_argument if the length of the result would exceed + {!Sys.max_string_length}. *) + +val of_bytes : ?off:int -> ?len:int -> bytes -> rdwr t +(** [of_bytes ~off ~len b] allocates a buffer and copies the contents of [b] + into it starting at offset [off] (default [0]) and of length [len] (default + [Bytes.length b - off]). The returned value has both read and write + capabilities. + + @raise Invalid_argument if [off] and [len] does not designate a valid + segment of [s]. *) + val to_bytes : ?off:int -> ?len:int -> 'a rd t -> bytes (** [to_bytes ~off ~len t] is the bytes representation of the segment of [t] - starting at [off] (default is [0]) of size [len] (default is [length t]). - [t] needs at least read-capability {!rd}. + starting at [off] (default [0]) of size [len] (default [length t - off]). + [t] needs at least read capability {!rd}. @raise Invalid_argument if [off] and [len] do not designate a valid segment of [t]. *) -val blit : 'a rd t -> src_off:int -> 'b wr t -> dst_off:int -> len:int -> unit -(** [blit src ~src_off dst ~dst_off ~len] copies [len] characters from [src], - starting at index [src_off], to [dst], starting at index [dst_off]. It works - correctly even if [src] and [dst] have the same underlying {!buffer}, and the - [src] and [dst] intervals overlap. This function uses [memmove] internally. +val blit_to_bytes : 'a rd t -> src_off:int -> bytes -> dst_off:int -> len:int + -> unit +(** [blit_to_bytes src ~src_off dst ~dst_off ~len] copies length [len] bytes + from [src], starting at index [src_off], to sequences [dst], starting at + index [dst_off]. [blit_to_bytes] uses [memcpy] internally. - [src] needs at least read-capability {!rd}. [dst] needs at least - write-capability {!wr}. Both don't share capabilities. + [src] needs at least read capability {!rd}. - @raise Invalid_argument if [src_off] and [len] do not designate a valid segment of [src], - or if [dst_off] and [len] do not designate a valid segment of [dst]. *) + @raise Invalid_argument if [src_off] and [len] do not designate a valid + segment of [src], or if [dst_off] and [len] do not designate a valid + sub-seuqnce of [dst]. *) -val blit_from_string : string -> src_off:int -> 'a wr t -> dst_off:int -> len:int -> unit -(** [blit_from_string src ~src_off dst ~dst_off ~len] copies [len] characters from [src], - starting at index [src_off], to [dst], starting at index [dst_off]. This function - uses [memcpy] internally. +val of_bigarray: ?off:int -> ?len:int -> buffer -> rdwr t +(** [of_bigarray ~off ~len b] is a proxy that contains [b] with offset [off] + (default [0]) of length [len] (default [Bigarray.Array1.dim b - off]). The + returned value has both read and write capabilties. - [dst] needs at least write-capability {!wr}. + @raise Invalid_argument if [off] and [len] do not designate a valid + segment of [b]. *) - @raise Invalid_argument if [src_off] and [len] do not designate a valid - sub-string of [src], or if [dst_off] and [len] do not designate a valid - segment of [dst]. *) +val unsafe_to_bigarray : 'a t -> buffer +(** [unsafe_to_bigarray t] converts [t] into a {!buffer} Bigarray, using the + Bigarray slicing to allocate a fresh {i proxy} Bigarray that preserves + sharing of the underlying buffer. + + In other words: -val blit_from_bytes : bytes -> src_off:int -> 'a wr t -> dst_off:int -> len:int -> unit -(** [blit_from_bytes src ~src_off dst ~dst_off ~len] copies [len] characters from [src], - starting at index [src_off], to [dst], starting at index [dst_off]. This uses - [memcpy] internally. + {[let t = Cstruct_cap.create 10 in + let b = Cstruct_cap.unsafe_to_bigarray t in + Bigarray.Array1.set b 0 '\x42' ; + assert (Cstruct_cap.get_char t 0 = '\x42')]} *) - [dst] needs at least write-capability {!wr}. +(** {2 Higher order functions} *) - @raise Invalid_argument if [src_off] and [len] do not designate a - valid sub-sequence of [src], or if [dst_off] and [len] do no designate - a valid segment of [dst]. *) +type 'a iter = unit -> 'a option +(** Type of iterator. *) -val blit_to_bytes : 'a rd t -> src_off:int -> bytes -> dst_off:int -> len:int -> unit -(** [blit_to_bytes src ~src_off dst ~dst_off ~len] copies [len] characters - from [src], starting at index [src_off], to sequences [dst], starting at index [dst_off]. - [blit_to_bytes] uses [memcpy] internally. +val iter : ('a rd t -> int option) -> ('a rd t -> 'v) -> 'a rd t -> 'v iter +(** [iter lenf of_cstruct t] is an iterator over [t] that returns elements of + size [lenf t] and type [of_cstruct t]. [t] needs at least read capability + {!rd} and [iter] keeps capabilities of [t] on [of_cstruct]. *) - [src] needs at least read-capability {!rd}. +val fold : ('acc -> 'x -> 'acc) -> 'x iter -> 'acc -> 'acc +(** [fold f iter acc] is [(f iterN accN ... (f iter acc)...)]. *) - @raise Invalid_argument if [src_off] and [len] do not designate a - valid segment of [src], or if [dst_off] and [len] do not designate - a valid sub-seuqnce of [dst]. *) +(** {2 Accessors and mutators} *) -val memset : 'a wr t -> int -> unit -(** [memset t x] sets all bytes of [t] to [x land 0xff]. [t] needs at least - write-capability {!wr}. *) +val get_char : 'a rd t -> int -> char +(** [get_char t off] returns the character contained in [t] at offset [off]. + [t] needs at least read capability {!rd}. -val length : 'a rd t -> int -(** [length t] return length of [t]. Note that this length is potentially smaller than - the actual size of the underlying buffer, as the {!sub} function can construct - a smaller view. [t] needs at least read-capability {!rd}. *) + @raise Invalid_argument if the offset exceeds [t] length. *) -val split : ?start:int -> 'a t -> int -> 'a t * 'a t -(** [split ~start t len] is a tuple containing {!t}s extracted from [t] at - offset [start] (default is [0]) of length [len] as first element, and the - rest of [t] as second element. +val set_char : 'a wr t -> int -> char -> unit +(** [set_char t off c] sets the character contained in [t] at offset [off] + to character [c]. [t] needs at least write capability {!wr}. - @raise Invalid_argument if [sart] exceeds the [t] length, - or if there is a bounds violation of [t] via [len + start]. *) + @raise Invalid_argument if the offset exceeds [t] length. *) -val pp : Format.formatter -> 'a rd t -> unit -(** Pretty-printer of {!t}. {!t} needs at least read capability {!rd}. *) +val get_uint8 : 'a rd t -> int -> uint8 +(** [get_uint8 t off] returns the byte contained in [t] at offset [off]. + [t] needs at least read capability {!rd}. + + @raise Invalid_argument if the offset exceeds [t] length. *) + +val set_uint8 : 'a wr t -> int -> uint8 -> unit +(** [set_uint8 t off x] sets the byte contained in [t] at offset [off] + to byte [x]. [t] needs at least write capability {!wr}. + + @raise Invalid_argument if the offset exceeds [t] length. *) module BE : sig (** {3 Big-endian Byte Order} @@ -244,40 +370,40 @@ module BE : sig dealing with raw frames, for example, in a userland networking stack. *) val get_uint16 : 'a rd t -> int -> uint16 - (** [get_uint16 t i] returns the two bytes in [t] starting at offset [i], - interpreted as an {!uint16}. Sign extension is not interpreted. + (** [get_uint16 t off] returns the two bytes in [t] starting at offset [off], + interpreted as an {!uint16}. [t] needs at least read capability {!rd}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 2]. *) val get_uint32 : 'a rd t -> int -> uint32 - (** [get_uint32 t i] returns the four bytes in [t] starting at offset [i]. - [t] needs at least read-capability {!rd}. + (** [get_uint32 t off] returns the four bytes in [t] starting at offset [off]. + [t] needs at least read capability {!rd}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 4]. *) val get_uint64 : 'a rd t -> int -> uint64 - (** [get_uint64 t i] returns the eight bytes in [t] starting at offset [i]. - [t] needs at least read-capability {!rd}. + (** [get_uint64 t off] returns the eight bytes in [t] starting at offset + [off]. [t] needs at least read capability {!rd}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 8]. *) val set_uint16 : 'a wr t -> int -> uint16 -> unit - (** [set_uint16 t i v] sets the two bytes in [t] starting at offset [i] to - the value [v]. [t] needs at least write-capability {!wr}. + (** [set_uint16 t off v] sets the two bytes in [t] starting at offset [off] to + the value [v]. [t] needs at least write capability {!wr}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 2]. *) val set_uint32 : 'a wr t -> int -> uint32 -> unit - (** [set_uint32 t i v] sets the four bytes in [t] starting at offset [i] to - the value [v]. [t] needs at least write-capability {!wr}. + (** [set_uint32 t off v] sets the four bytes in [t] starting at offset [off] + to the value [v]. [t] needs at least write capability {!wr}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 4]. *) val set_uint64 : 'a wr t -> int -> uint64 -> unit - (** [set_uint64 t i v] sets the eight bytes in [t] starting at offset [i] to - the value [v]. [t] needs at least write-capability {!wr}. + (** [set_uint64 t off v] sets the eight bytes in [t] starting at offset [off] + to the value [v]. [t] needs at least write capability {!wr}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 8]. *) end module LE : sig @@ -293,88 +419,42 @@ module LE : sig not, these operations will not do any byte reordering. *) val get_uint16 : 'a rd t -> int -> uint16 - (** [get_uint16 t i] returns the two bytes in [t] starting at offset [i], - interpreted as an {!uint16}. Sign extension is not interpreted. + (** [get_uint16 t off] returns the two bytes in [t] starting at offset [off], + interpreted as an {!uint16}. [t] needs at least read capability {!rd}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 2]. *) val get_uint32 : 'a rd t -> int -> uint32 - (** [get_uint32 t i] returns the four bytes in [t] starting at offset [i]. - [t] needs at least read-capability {!rd}. + (** [get_uint32 t off] returns the four bytes in [t] starting at offset [off]. + [t] needs at least read capability {!rd}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 4]. *) val get_uint64 : 'a rd t -> int -> uint64 - (** [get_uint64 t i] returns the eight bytes in [t] starting at offset [i]. - [t] needs at least read-capability {!rd}. + (** [get_uint64 t off] returns the eight bytes in [t] starting at offset + [off]. [t] needs at least read capability {!rd}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 8]. *) val set_uint16 : 'a wr t -> int -> uint16 -> unit - (** [set_uint16 t i v] sets the two bytes in [t] starting at offset [i] to - the value [v]. [t] needs at least write-capability {!wr}. + (** [set_uint16 t off v] sets the two bytes in [t] starting at offset [off] to + the value [v]. [t] needs at least write capability {!wr}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 2]. *) val set_uint32 : 'a wr t -> int -> uint32 -> unit - (** [set_uint32 t i v] sets the four bytes in [t] starting at offset [i] to - the value [v]. [t] needs at least write-capability {!wr}. + (** [set_uint32 t off v] sets the four bytes in [t] starting at offset [off] + to the value [v]. [t] needs at least write capability {!wr}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 4]. *) val set_uint64 : 'a wr t -> int -> uint64 -> unit - (** [set_uint64 t i v] sets the eight bytes in [t] starting at offset [i] to - the value [v]. [t] needs at least write-capability {!wr}. + (** [set_uint64 t off v] sets the eight bytes in [t] starting at offset [off] + to the value [v]. [t] needs at least write capability {!wr}. - @raise Invalid_argument if [t] is too small. *) + @raise Invalid_argument if offset [off] exceeds [length t - 8]. *) end -val lenv : 'a rd t list -> int -(** [lenv vs] is the combined length of all {!t} in [vs]. - Each {!t} need at least read-capability {!rd}. - - @raise Invalid_argument if computing the sum overflows. *) - -val copyv : 'a rd t list -> string -(** [copy vs] is the string representation of the concatenation of all {!t} in - [vss]. Each {!t} need at least read-capability {!rd}. - - @raise Invalid_argument if the length of the result would exceed - {!Sys.max_string_length}. *) - -val fillv : src:'a rd t list -> dst:'b wr t -> int * 'a rd t list -(** [fillv ~src ~dst] copies from [src] to [dst] until [src] is exhausted or - [dst] is full. It returns the number of bytes copied and the remaining data - from [src], if any. This is useful if you want to {i bufferize} data into - fixed-sized chunks. Each {!t} of [src] need at least read-capability {!rd}. - [dst] needs at least write-capability {!wr}. Each {!t} of [src] and dst don't - share capabilities. *) - -type 'a iter = unit -> 'a option -(** Type of iterator. *) - -val iter : ('a rd t -> int option) -> ('a rd t -> 'v) -> 'a rd t -> 'v iter -(** [iter lenf of_cstruct t] is an iterator over [t] that returns elements of - size [lenf t] and type [of_cstruct t]. [t] needs at least read-capability {!rd} and - [iter] keeps capabilities of [t] on [of_cstruct]. *) - -val fold : ('acc -> 'x -> 'acc) -> 'x iter -> 'acc -> 'acc -(** [fold f iter acc] is [(f iterN accN ... (f iter acc)...)]. *) - -val append : 'a rd t -> 'b rd t -> rdwr t -(** [append a b] create a fresh {!t} which is the concatenation of [a] and [b]. - [a] and [b] need at least read-capability {!rd}. Resulted {!t} has - read-and-write capability. *) - -val concat : 'a rd t list -> rdwr t -(** [concat vss] is the concatenation of all {!t} in [vss]. Each {!t} of [vss] need - at least read-capability {!rd}. - [concat] always creates a fresh {!t}. *) - -val rev : 'a rd t -> rdwr t -(** [rev t] is [t] in reverse order. The return value is a freshly allocated - {!t}, and [t] is not modified according {!rd} capability. *) - (** {2 Helpers to parse with capabilities.} As [Cstruct], capabilities interface provides helpers functions to help