模块 Marshal

module Marshal: sig .. end

数据结构的序列化。

此模块提供函数,用于将任意数据结构编码为字节序列,然后可以将其写入文件或通过管道或网络连接发送。这些字节随后可以在以后读取,可能在另一个进程中,并解码回数据结构。字节序列的格式对于给定版本的 OCaml 在所有机器上都是兼容的。

警告:序列化目前不是类型安全的。序列化数据的类型不会与数据的值一起传输,因此无法检查读取回的数据是否具有上下文期望的类型。特别是,Marshal.from_* 函数的结果类型被给出为 'a,但这具有误导性:返回的 OCaml 值并非对所有 'a 都有类型 'a;它具有一个唯一的类型,该类型在编译时无法确定。程序员应该使用以下语法显式给出返回值的预期类型

  • (Marshal.from_channel chan : type)。如果文件中的对象不属于给定类型,则在运行时可能会发生任何事情。

可扩展变体类型的值,例如由反序列化程序返回的异常(可扩展类型 exn),不应通过 match ... withtry ... with 进行模式匹配,因为反序列化不会保留匹配其构造函数所需的信息。与其他可扩展变体值的结构相等也不起作用。大多数其他用途,例如 Printexc.to_string,仍将按预期工作。

序列化值的表示形式不是人类可读的,并且使用不可打印字符的字节。因此,与 Marshal.to_channelMarshal.from_channel 结合使用的输入和输出通道必须以二进制模式打开,例如使用 open_out_binopen_in_bin;在文本模式下打开的通道将在文本通道的行为与二进制通道不同的平台(例如 Windows)上导致反序列化错误。


type extern_flags = 
| No_sharing (*

不保留共享

*)
| Closures (*

发送函数闭包

*)
| Compat_32 (*

确保 32 位兼容性

*)

以下 Marshal.to_* 函数的标志。

val to_channel : out_channel -> 'a -> extern_flags list -> unit

Marshal.to_channel chan v flagsv 的表示形式写入通道 chanflags 参数是一个可能为空的标志列表,用于控制在共享、函数值以及 32 位和 64 位平台之间的兼容性方面进行序列化的行为。

如果 flags 不包含 Marshal.No_sharing,则会检测并保留值 v 内部循环和共享在生成的字节序列中。特别是,这保证了序列化始终终止。连续调用 Marshal.to_channel 序列化的值之间的共享既不会被检测到也不会被保留。如果 flags 包含 Marshal.No_sharing,则会忽略共享。如果 v 不包含共享的子结构,这将导致更快的序列化,但如果 v 实际上包含共享,或者甚至在 v 包含循环时,可能会导致更慢的序列化和更大的字节表示,甚至导致不终止。

如果 flags 不包含 Marshal.Closures,则当序列化遇到 v 内部的函数值时会失败:只有“纯”数据结构(不包含函数或对象)才能安全地在不同的程序之间传输。如果 flags 包含 Marshal.Closures,则函数值将被序列化为程序代码中的位置以及与闭包中捕获的自由变量相对应的值。在这种情况下,序列化的输出只能在运行完全相同的程序、具有完全相同的编译代码的进程中读取回。(这在反序列化时使用代码位置一起传输的代码的 MD5 摘要进行检查。)

闭包中捕获哪些自由变量的确切定义未指定,并且可能因字节码和本机代码(以及优化标志)而异。特别是,访问全局引用的函数值可能包含也可能不包含其闭包中的引用。如果包含,则反序列化相应的闭包将创建一个新的引用,与全局引用不同。

如果 flags 包含 Marshal.Compat_32,则当序列化遇到在 32 位平台上可表示的整数范围 -230230-1 之外的整数值时会失败。这确保了在 64 位平台上生成的数据可以在 32 位平台上安全地读回。如果 flags 不包含 Marshal.Compat_32,则会序列化范围 -230230-1 之外的整数值,并且可以在 64 位平台上读回,但在 32 位平台上读回时会导致反序列化时出错。Mashal.Compat_32 标志仅在 64 位平台上执行序列化时才重要;如果在 32 位平台上执行序列化,则它没有效果。

val to_bytes : 'a -> extern_flags list -> bytes

Marshal.to_bytes v flags 返回包含 v 表示形式的字节序列。 flags 参数与 Marshal.to_channel 的含义相同。

val to_string : 'a -> extern_flags list -> string

to_bytes 相同,但将结果作为字符串而不是字节序列返回。

val to_buffer : bytes -> int -> int -> 'a -> extern_flags list -> int

Marshal.to_buffer buff ofs len v flags 序列化值 v,将其字节表示存储在序列 buff 中,从索引 ofs 开始,最多写入 len 个字节。它返回实际写入序列的字节数。如果 v 的字节表示形式不适合 len 个字符,则会引发异常 Failure

val from_channel : in_channel -> 'a

Marshal.from_channel chan 从通道 chan 读取结构化值的字节表示形式,如 Marshal.to_* 函数之一生成的,并重建并返回相应的值。

val from_bytes : bytes -> int -> 'a

Marshal.from_bytes buff ofs 反序列化结构化值,类似于 Marshal.from_channel 所做的那样,只是字节表示形式不是从通道读取的,而是从字节序列 buff 中获取的,从位置 ofs 开始。字节序列不会发生变异。

val from_string : string -> int -> 'a

from_bytes 相同,但将字符串作为参数而不是字节序列。

val header_size : int

表示序列化值的字节由一个固定大小的头部和一个可变大小的数据部分组成,其大小可以从头部确定。Marshal.header_size 是头部的字节大小。Marshal.data_size buff ofs 是数据部分的字节大小,假设有效的头部存储在从位置 ofs 开始的 buff 中。最后,Marshal.total_size buff ofs 是序列化值的总字节大小。Marshal.data_sizeMarshal.total_size 都会在 buffofs 不包含有效头部时引发 Failure

要将序列化值的字节表示形式读入字节序列,程序需要先将 Marshal.header_size 个字节读入序列,然后使用 Marshal.data_size 确定表示形式其余部分的长度,确保序列足够大以容纳剩余的数据,然后读取它,最后调用 Marshal.from_bytes 来反序列化该值。

val data_size : bytes -> int -> int
val total_size : bytes -> int -> int

Marshal 和域安全性

在序列化可能被不同域修改的可变值时必须小心。修改正在序列化的值(即,转换为字节序列)是一个编程错误,并且由于撕裂,可能会导致令人惊讶的值(在反序列化时),因为序列化涉及逐字节复制。