module Bytes: Bytesval length : bytes -> int返回参数的长度(字节数)。
val get : bytes -> int -> charget s n 返回参数 s 中索引为 n 的字节。
Invalid_argument 如果 n 不是 s 中的有效索引。val set : bytes -> int -> char -> unitset s n c 在原地修改 s,将索引为 n 的字节替换为 c。
Invalid_argument 如果 n 不是 s 中的有效索引。val create : int -> bytescreate n 返回一个长度为 n 的新字节序列。该序列未初始化,包含任意字节。
Invalid_argument 如果 n < 0 或 n > Sys.max_string_length。val make : int -> char -> bytesmake n c 返回一个长度为 n 的新字节序列,填充了字节 c。
Invalid_argument 如果 n < 0 或 n > Sys.max_string_length。val init : int -> (int -> char) -> bytesinit n f 返回一个长度为 n 的新字节序列,字符 i 初始化为 f i 的结果(按递增索引顺序)。
Invalid_argument 如果 n < 0 或 n > Sys.max_string_length。val empty : bytes一个大小为 0 的字节序列。
val copy : bytes -> bytes返回一个包含与参数相同字节的新字节序列。
val of_string : string -> bytes返回一个包含与给定字符串相同字节的新字节序列。
val to_string : bytes -> string返回一个包含与给定字节序列相同字节的新字符串。
val sub : bytes -> int -> int -> bytessub s pos len 返回一个长度为 len 的新字节序列,包含 s 中从位置 pos 开始且长度为 len 的子序列。
Invalid_argument 如果 pos 和 len 未指定 s 的有效范围。val sub_string : bytes -> int -> int -> string与 Bytes.sub 相同,但返回字符串而不是字节序列。
val extend : bytes -> int -> int -> bytesextend s left right 返回一个包含 s 字节的新字节序列,在其前面添加了 left 个未初始化的字节,在其后面添加了 right 个未初始化的字节。如果 left 或 right 为负数,则从 s 的相应侧移除字节(而不是追加)。
Invalid_argument 如果结果长度为负数或超过 Sys.max_string_length 字节。val fill : bytes -> int -> int -> char -> unitfill s pos len c 在原地修改 s,将 len 个字符替换为 c,从 pos 开始。
Invalid_argument 如果 pos 和 len 未指定 s 的有效范围。val blit : bytes -> int -> bytes -> int -> int -> unitblit src src_pos dst dst_pos len 将字节序列 src 中从索引 src_pos 开始的 len 个字节复制到字节序列 dst 中,从索引 dst_pos 开始。即使 src 和 dst 是同一个字节序列,并且源和目标区间重叠,它也能正常工作。
Invalid_argument 如果 src_pos 和 len 未指定 src 的有效范围,或者如果 dst_pos 和 len 未指定 dst 的有效范围。val blit_string : string -> int -> bytes -> int -> int -> unitblit_string src src_pos dst dst_pos len 将字符串 src 中从索引 src_pos 开始的 len 个字节复制到字节序列 dst 中,从索引 dst_pos 开始。
Invalid_argument 如果 src_pos 和 len 未指定 src 的有效范围,或者如果 dst_pos 和 len 未指定 dst 的有效范围。val concat : bytes -> bytes list -> bytesconcat sep sl 将字节序列列表 sl 连接起来,在每个字节序列之间插入分隔符字节序列 sep,并将结果作为新字节序列返回。
Invalid_argument 如果结果超过 Sys.max_string_length 字节。val cat : bytes -> bytes -> bytescat s1 s2 连接 s1 和 s2,并将结果作为新字节序列返回。
Invalid_argument 如果结果超过 Sys.max_string_length 字节。val iter : (char -> unit) -> bytes -> unititer f s 依次将函数 f 应用于 s 的所有字节。它等效于 f (get s 0); f (get s 1); ...; f (get s。
(length s - 1)); ()
val iteri : (int -> char -> unit) -> bytes -> unit与 Bytes.iter 相同,但函数应用于字节的索引作为第一个参数,字节本身作为第二个参数。
val map : (char -> char) -> bytes -> bytesmap f s 依次将函数 f 应用于 s 的所有字节(按递增索引顺序),并将结果字节存储在一个新的序列中,该序列作为结果返回。
val mapi : (int -> char -> char) -> bytes -> bytesmapi f s 使用 s 的每个字符及其索引(按递增索引顺序)调用 f,并将结果字节存储在一个新的序列中,该序列作为结果返回。
val fold_left : ('acc -> char -> 'acc) -> 'acc -> bytes -> 'accfold_left f x s 计算 f (... (f (f x (get s 0)) (get s 1)) ...) (get s (n-1)),其中 n 是 s 的长度。
val fold_right : (char -> 'acc -> 'acc) -> bytes -> 'acc -> 'accfold_right f s x 计算 f (get s 0) (f (get s 1) ( ... (f (get s (n-1)) x) ...)),其中 n 是 s 的长度。
val for_all : (char -> bool) -> bytes -> boolfor_all p s 检查 s 中的所有字符是否满足谓词 p。
val exists : (char -> bool) -> bytes -> boolexists p s 检查 s 中至少一个字符是否满足谓词 p。
val trim : bytes -> bytes返回参数的副本,去除前导和尾随空白。被视为空白的字节是 ASCII 字符 ' '、'\012'、'\n'、'\r' 和 '\t'。
val escaped : bytes -> bytes返回参数的副本,其中特殊字符用转义序列表示,遵循 OCaml 的词法约定。所有不在 ASCII 可打印范围(32..126)内的字符都已转义,以及反斜杠和双引号。
Invalid_argument 如果结果超过 Sys.max_string_length 字节。val index : bytes -> char -> intindex s c 返回字节 c 在 s 中首次出现的位置。
Not_found 如果 c 不出现在 s 中。val index_opt : bytes -> char -> int optionindex_opt s c 返回字节 c 在 s 中首次出现的位置,如果 c 不出现在 s 中,则返回 None。
val rindex : bytes -> char -> intrindex s c 返回字节 c 在 s 中最后出现的位置。
Not_found 如果 c 不出现在 s 中。val rindex_opt : bytes -> char -> int optionrindex_opt s c 返回字节 c 在 s 中最后出现的位置,如果 c 不出现在 s 中,则返回 None。
val index_from : bytes -> int -> char -> intindex_from s i c 返回字节 c 在位置 i 之后首次出现在 s 中的位置。 index s c 等效于 index_from s 0 c。
Invalid_argument 如果 i 不是 s 中的有效位置。Not_found 如果 c 不在位置 i 之后出现在 s 中。val index_from_opt : bytes -> int -> char -> int optionindex_from_opt s i c 返回字节 c 在位置 i 之后首次出现在 s 中的位置,如果 c 不在位置 i 之后出现在 s 中,则返回 None。 index_opt s c 等效于 index_from_opt s 0 c。
Invalid_argument 如果 i 不是 s 中的有效位置。val rindex_from : bytes -> int -> char -> intrindex_from s i c 返回字节 c 在位置 i+1 之前最后出现在 s 中的位置。 rindex s c 等效于 rindex_from s (length s - 1) c。
Invalid_argument 如果 i+1 不是 s 中的有效位置。Not_found 如果 c 不在位置 i+1 之前出现在 s 中。val rindex_from_opt : bytes -> int -> char -> int optionrindex_from_opt s i c 返回字节 c 在位置 i+1 之前最后出现在 s 中的位置,如果 c 不在位置 i+1 之前出现在 s 中,则返回 None。 rindex_opt s c 等效于 rindex_from s (length s - 1) c。
Invalid_argument 如果 i+1 不是 s 中的有效位置。val contains : bytes -> char -> boolcontains s c 测试字节 c 是否出现在 s 中。
val contains_from : bytes -> int -> char -> boolcontains_from s start c 测试字节 c 是否在位置 start 之后出现在 s 中。 contains s c 等效于 contains_from。
s 0 c
Invalid_argument 如果 start 不是 s 中的有效位置。val rcontains_from : bytes -> int -> char -> boolrcontains_from s stop c 测试字节 c 是否在位置 stop+1 之前出现在 s 中。
Invalid_argument 如果 stop < 0 或 stop+1 不是 s 中的有效位置。val uppercase_ascii : bytes -> bytes返回参数的副本,其中所有小写字母使用 US-ASCII 字符集转换为大写字母。
val lowercase_ascii : bytes -> bytes返回参数的副本,其中所有大写字母使用 US-ASCII 字符集转换为小写字母。
val capitalize_ascii : bytes -> bytes返回参数的副本,其中第一个字符设置为大写,使用 US-ASCII 字符集。
val uncapitalize_ascii : bytes -> bytes返回参数的副本,其中第一个字符设置为小写,使用 US-ASCII 字符集。
typet =bytes
字节序列类型的别名。
val compare : t -> t -> int
val equal : t -> t -> bool字节序列的相等函数。
val starts_with : prefix:bytes -> bytes -> boolstarts_with ~prefix s 当且仅当 s 以 prefix 开头时为 true。
val ends_with : suffix:bytes -> bytes -> boolends_with ~suffix s 当且仅当 s 以 suffix 结尾时为 true。
本节介绍 bytes 和 string 之间的非安全、低级转换函数。它们不复制内部数据;如果使用不当,可能会破坏 -safe-string 选项提供的字符串不变性。它们可供专家库作者使用,但在大多数情况下,您应该使用始终正确的 Bytes.to_string 和 Bytes.of_string 代替。
val unsafe_to_string : bytes -> string将字节序列非安全地转换为字符串。
为了论证 unsafe_to_string 的使用,考虑“所有权”原则。操纵某些数据的代码“拥有”它;有几种不相交的所有权模式,包括
独占所有权是线性的:将数据传递给另一段代码意味着放弃所有权(我们不能再次写入数据)。独占所有者可以决定使数据共享(放弃对其的修改权限),但共享数据不能再次变为独占所有权。
unsafe_to_string s 只能在调用者拥有字节序列 s 时使用,无论是独占所有权还是共享不可变数据。调用者放弃对 s 的所有权,并获得返回字符串的所有权。
有两个有效的用例符合此所有权原则
1. 通过初始化和修改一个字节序列来创建字符串,该序列在初始化完成之后不会被改变。
let string_init len f : string =
let s = Bytes.create len in
for i = 0 to len - 1 do Bytes.set s i (f i) done;
Bytes.unsafe_to_string s
此函数是安全的,因为字节序列 s 在调用 unsafe_to_string 之后将不会被访问或修改。 string_init 代码放弃对 s 的所有权,并将所得字符串的所有权返回给其调用者。
请注意,如果 s 作为附加参数传递给函数 f,则它将是不安全的,因为它可能通过这种方式逃逸并在将来被修改 - string_init 将放弃对 s 的所有权以将其传递给 f,并且不能安全地调用 unsafe_to_string。
我们已经提供了 String.init、String.map 和 String.mapi 函数来涵盖大多数构建新字符串的情况。只要适用,您应该优先选择这些函数而不是 to_string 或 unsafe_to_string。
2. 将字节序列的所有权临时赋予一个函数,该函数期望一个独占所有权的字符串并将所有权返回,以便我们可以在调用结束后再次修改该序列。
let bytes_length (s : bytes) =
String.length (Bytes.unsafe_to_string s)
在此用例中,我们不承诺 s 在调用 bytes_length s 之后永远不会被修改。 String.length 函数临时借用字节序列的独占所有权(并将其视为 string),但会将此所有权返回给调用者,调用者可以假设 s 在调用之后仍然是一个有效的字节序列。请注意,这只有在我们知道 String.length 没有捕获其参数时才是正确的 - 它可以通过诸如记忆组合器之类的旁路逃逸。
在借用字符串期间,调用者可能不会修改 s(它已经临时放弃了所有权)。这会影响并发程序,还会影响高阶函数:如果 String.length 返回一个稍后调用的闭包,则 s 应该在该闭包完全应用并返回所有权之前不要修改。
val unsafe_of_string : string -> bytes将共享字符串非安全地转换为不应修改的字节序列。
与使 unsafe_to_string 正确的相同所有权原则适用于 unsafe_of_string:如果您是 string 值的所有者,您可以使用它,并且您将以相同的模式拥有返回的 bytes。
在实践中,字符串值的独占所有权非常难以正确推理。您应该始终假设字符串是共享的,而不是独占拥有的。
例如,字符串文字由编译器隐式共享,因此您永远不会独占拥有它们。
let incorrect = Bytes.unsafe_of_string "hello"
let s = Bytes.of_string "hello"
第一个声明是错误的,因为字符串文字 "hello" 可能由编译器与程序的其他部分共享,修改 incorrect 是一个错误。您必须始终使用第二个版本,它会执行复制,因此是正确的。
假设不是字符串文字,而是(部分)由字符串文字构建的字符串的独占所有权也是错误的。例如,修改 unsafe_of_string ("foo" ^ s) 可能会修改共享字符串 "foo" - 假设字符串的绳索式表示。更一般地说,操作字符串的函数将假设共享所有权,它们不会保留独占所有权。因此,假设 unsafe_of_string 结果的独占所有权是错误的。
我们唯一有理由相信是安全的案例是如果生成的 bytes 是共享的 - 用作不可变的字节序列。这对于操作不可变字节序列(例如 Marshal.from_bytes)且以前为此目的使用 string 类型的低级程序的增量迁移可能很有用。
val split_on_char : char -> bytes -> bytes listsplit_on_char sep s 返回 s 的所有(可能是空的)子序列的列表,这些子序列由 sep 字符分隔。如果 s 为空,则结果是单例列表 [empty]。
函数的输出由以下不变式指定
sep 作为分隔符连接其元素将返回一个字节序列,该序列等于输入 (Bytes.concat (Bytes.make 1 sep)
(Bytes.split_on_char sep s) = s)。sep 字符。val to_seq : t -> char Seq.t在字符串上迭代,以递增索引顺序。迭代期间对字符串的修改将反映在序列中。
val to_seqi : t -> (int * char) Seq.t在字符串上迭代,以递增顺序,在字符之间产生索引
val of_seq : char Seq.t -> t从生成器创建字符串
val get_utf_8_uchar : t -> int -> Uchar.utf_decodeget_utf_8_uchar b i 解码 b 中索引 i 处的 UTF-8 字符。
val set_utf_8_uchar : t -> int -> Uchar.t -> intset_utf_8_uchar b i u 在 b 中索引 i 处对 u 进行 UTF-8 编码,并返回从 i 开始写入的字节数 n。如果 n 为 0,则没有足够的空间在 i 处编码 u,并且 b 保持不变。否则,可以在 i + n 处编码新字符。
val is_valid_utf_8 : t -> boolis_valid_utf_8 b 当且仅当 b 包含有效的 UTF-8 数据时为 true。
val get_utf_16be_uchar : t -> int -> Uchar.utf_decodeget_utf_16be_uchar b i 解码 b 中索引 i 处的 UTF-16BE 字符。
val set_utf_16be_uchar : t -> int -> Uchar.t -> intset_utf_16be_uchar b i u 在 b 中索引 i 处对 u 进行 UTF-16BE 编码,并返回从 i 开始写入的字节数 n。如果 n 为 0,则没有足够的空间在 i 处编码 u,并且 b 保持不变。否则,可以在 i + n 处编码新字符。
val is_valid_utf_16be : t -> boolis_valid_utf_16be b 当且仅当 b 包含有效的 UTF-16BE 数据时为 true。
val get_utf_16le_uchar : t -> int -> Uchar.utf_decodeget_utf_16le_uchar b i 解码 b 中索引 i 处的 UTF-16LE 字符。
val set_utf_16le_uchar : t -> int -> Uchar.t -> intset_utf_16le_uchar b i u 在 b 中索引 i 处对 u 进行 UTF-16LE 编码,并返回从 i 开始写入的字节数 n。如果 n 为 0,则没有足够的空间在 i 处编码 u,并且 b 保持不变。否则,可以在 i + n 处编码新字符。
val is_valid_utf_16le : t -> boolis_valid_utf_16le b 当且仅当 b 包含有效的 UTF-16LE 数据时为 true。
本节中的函数将整数二进制编码和解码到字节序列,反之亦然。
如果索引 i 处解码或编码整数所需的可用空间不足,则以下所有函数都会引发 Invalid_argument。
小端(resp. 大端)编码表示最低(resp. 最高)有效字节存储在最前面。大端也被称为网络字节序。本地端编码可以是小端或大端,具体取决于 Sys.big_endian。
32 位和 64 位整数由 int32 和 int64 类型表示,它们可以解释为有符号或无符号数。
8 位和 16 位整数由 int 类型表示,它比二进制编码有更多的位。这些额外的位按如下方式处理。
int 值表示的有符号(resp. 无符号)8 位或 16 位整数的函数将对其结果进行符号扩展(resp. 零扩展)。int 值表示的 8 位或 16 位整数的函数会将其输入截断到其最低有效字节。val get_uint8 : bytes -> int -> intget_uint8 b i 是 b 从字节索引 i 开始的无符号 8 位整数。
val get_int8 : bytes -> int -> intget_int8 b i 是 b 从字节索引 i 开始的有符号 8 位整数。
val get_uint16_ne : bytes -> int -> intget_uint16_ne b i 是 b 从字节索引 i 开始的本地端无符号 16 位整数。
val get_uint16_be : bytes -> int -> intget_uint16_be b i 是 b 从字节索引 i 开始的大端无符号 16 位整数。
val get_uint16_le : bytes -> int -> intget_uint16_le b i 是 b 从字节索引 i 开始的小端无符号 16 位整数。
val get_int16_ne : bytes -> int -> intget_int16_ne b i 是 b 从字节索引 i 开始的本地端有符号 16 位整数。
val get_int16_be : bytes -> int -> intget_int16_be b i 是 b 从字节索引 i 开始的大端有符号 16 位整数。
val get_int16_le : bytes -> int -> intget_int16_le b i 是 b 从字节索引 i 开始的小端有符号 16 位整数。
val get_int32_ne : bytes -> int -> int32get_int32_ne b i 是 b 从字节索引 i 开始的本地端 32 位整数。
val get_int32_be : bytes -> int -> int32get_int32_be b i 是 b 从字节索引 i 开始的大端 32 位整数。
val get_int32_le : bytes -> int -> int32get_int32_le b i 是 b 从字节索引 i 开始的小端 32 位整数。
val get_int64_ne : bytes -> int -> int64get_int64_ne b i 是 b 从字节索引 i 开始的本地端 64 位整数。
val get_int64_be : bytes -> int -> int64get_int64_be b i 是 b 从字节索引 i 开始的大端 64 位整数。
val get_int64_le : bytes -> int -> int64get_int64_le b i 是 b 从字节索引 i 开始的小端 64 位整数。
val set_uint8 : bytes -> int -> int -> unitset_uint8 b i v 将 b 从字节索引 i 开始的无符号 8 位整数设置为 v。
val set_int8 : bytes -> int -> int -> unitset_int8 b i v 将 b 从字节索引 i 开始的有符号 8 位整数设置为 v。
val set_uint16_ne : bytes -> int -> int -> unitset_uint16_ne b i v 将 b 从字节索引 i 开始的本地端无符号 16 位整数设置为 v。
val set_uint16_be : bytes -> int -> int -> unitset_uint16_be b i v 将 b 从字节索引 i 开始的大端无符号 16 位整数设置为 v。
val set_uint16_le : bytes -> int -> int -> unitset_uint16_le b i v 将 b 从字节索引 i 开始的小端无符号 16 位整数设置为 v。
val set_int16_ne : bytes -> int -> int -> unitset_int16_ne b i v 将 b 从字节索引 i 开始的本地端有符号 16 位整数设置为 v。
val set_int16_be : bytes -> int -> int -> unitset_int16_be b i v 将 b 从字节索引 i 开始的大端有符号 16 位整数设置为 v。
val set_int16_le : bytes -> int -> int -> unitset_int16_le b i v 将 b 从字节索引 i 开始的小端有符号 16 位整数设置为 v。
val set_int32_ne : bytes -> int -> int32 -> unitset_int32_ne b i v 将 b 从字节索引 i 开始的本地端 32 位整数设置为 v。
val set_int32_be : bytes -> int -> int32 -> unitset_int32_be b i v 将 b 从字节索引 i 开始的大端 32 位整数设置为 v。
val set_int32_le : bytes -> int -> int32 -> unitset_int32_le b i v 将 b 从字节索引 i 开始的小端 32 位整数设置为 v。
val set_int64_ne : bytes -> int -> int64 -> unitset_int64_ne b i v 将 b 从字节索引 i 开始的本地端 64 位整数设置为 v。
val set_int64_be : bytes -> int -> int64 -> unitset_int64_be b i v 将 b 从字节索引 i 开始的大端 64 位整数设置为 v。
val set_int64_le : bytes -> int -> int64 -> unitset_int64_le b i v 将 b 从字节索引 i 开始的小端 64 位整数设置为 v。
在从多个域并发访问字节序列时,必须谨慎:访问字节序列永远不会使程序崩溃,但不同步的访问可能会产生令人惊讶的(非顺序一致的)结果。
每个访问多个字节的字节序列操作都不是原子的。这包括迭代和扫描。
例如,考虑以下程序。
let size = 100_000_000
let b = Bytes.make size ' '
let update b f () =
Bytes.iteri (fun i x -> Bytes.set b i (Char.chr (f (Char.code x)))) b
let d1 = Domain.spawn (update b (fun x -> x + 1))
let d2 = Domain.spawn (update b (fun x -> 2 * x + 1))
let () = Domain.join d1; Domain.join d2
字节序列 b 可能包含 '!'、'A'、'B' 和 'C' 值的非确定性混合。
执行此代码后,序列 b 的每个字节都是 '!'、'A'、'B' 或 'C'。如果需要原子性,则用户必须实现自己的同步(例如,使用 Mutex.t)。
如果两个域只访问字节序列的不同部分,则观察到的行为等效于来自两个域的操作的一些顺序交织。
当两个域访问相同的字节而没有同步,并且至少其中一个访问是写入时,就会发生数据竞争。在没有数据竞争的情况下,观察到的行为等效于来自不同域的操作的一些顺序交织。
只要可能,应通过使用同步来调节对序列元素的访问来避免数据竞争。
事实上,在存在数据竞争的情况下,程序不会崩溃,但观察到的行为可能不等于来自不同域的操作的任何顺序交织。然而,即使在存在数据竞争的情况下,读取操作也会返回对该位置的某个先前写入的值。
另一个微妙之处是,如果数据竞争涉及对同一位置的混合大小的写入和读取,则这些写入和读取被域观察到的顺序没有指定。例如,以下代码按顺序将 32 位整数和 char 写入同一索引。
let b = Bytes.make 10 '\000'
let d1 = Domain.spawn (fun () -> Bytes.set_int32_ne b 0 100; b.[0] <- 'd' )
在这种情况下,观察到将 'd' 写入 b.0 的域不能保证也会观察到对索引 1、2 或 3 的写入。