Marshal 格式

Marshal 格式用于序列化 Ruby 对象。该格式可以通过三个用户定义的扩展机制存储任意对象。

有关使用 Marshal 序列化和反序列化对象的文档,请参阅 Marshal 模块。

本文档将一组已序列化的对象称为流。Ruby 实现可以从 StringIO 或实现 getc 方法的对象加载一组对象。

流格式

流的前两个字节包含主版本和次版本,每个字节编码一个数字。Ruby 中实现的版本是 4.8(存储为“x04x08”),ruby 1.8.0 及更高版本支持该版本。

不同主版本的 Marshal 格式不兼容,其他主版本无法理解它们。较低次版本的格式可以被较高次版本理解。4.8 实现可以加载格式 4.7,但 4.7 实现无法加载格式 4.8。

在版本字节后面是一个描述序列化对象的流。该流包含嵌套对象(与 Ruby 对象相同),但流中的对象不一定与 Ruby 对象模型直接映射。

流中的每个对象都由一个字节描述其类型,后跟一个或多个字节描述该对象。当下面提到“对象”时,它表示下面定义 Ruby 对象的任何类型。

true、false、nil

这些对象每个长一个字节。“T”表示 true,“F”表示 false,“0”表示 nil

Fixnum 和 long

“i” 表示使用压缩格式的带符号 32 位值。类型后跟一到五个字节。加载的值始终为 Fixnum。在 32 位平台(Fixnum 的精度低于 32 位)上,加载大值会导致 CRuby 溢出。

fixnum 类型用于表示 ruby Fixnum 对象和编组数组、哈希、实例变量和其他类型的大小。在以下部分中,“long” 将表示下面描述的格式,该格式支持完整的 32 位精度。

第一个字节具有以下特殊值

“x00”

整数的值为 0。后面没有字节。

“x01”

整数的总大小为两个字节。后面的字节为 0 到 255 范围内的正整数。为了节省字节,只有 123 到 255 之间的值应该这样表示。

“xff”

整数的总大小为两个字节。后面的字节为 -1 到 -256 范围内的负整数。

“x02”

整数的总大小为三个字节。后面的两个字节为正小端整数。

“xfe”

整数的总大小为三个字节。后面的两个字节为负小端整数。

“x03”

整数的总大小为四个字节。后面的三个字节为正小端整数。

“xfd”

整数的总大小为四个字节。后面的三个字节为负小端整数。

“x04”

整数的总大小为五个字节。后面的四个字节为正小端整数。为了与 32 位 ruby 兼容,只有小于 1073741824 的 Fixnum 应该这样表示。对于流对象的大小,可以使用完全精度。

“xfc”

整数的总大小为五个字节。后面的四个字节为负小端整数。为了与 32 位 ruby 兼容,只有大于 -10737341824 的 Fixnum 应该这样表示。对于流对象的大小,可以使用完全精度。

否则,第一个字节是带偏移量的符号扩展八位值。如果值为正,则通过从值中减去 5 来确定值。如果值为负,则通过向值中添加 5 来确定值。

许多值有多个表示形式。CRuby 始终输出最短的可能表示形式。

符号和字节序列

“:” 表示一个真正的符号。真正的符号包含为流的其余部分定义符号所需的数据,因为流中的未来出现将改为对此符号的引用(符号链接)。引用是一个从零开始的 32 位值(因此:hello 的第一次出现为 0)。

类型字节之后是字节序列,其中包含一个指示序列中字节数的长整数,后跟该数量的数据字节。字节序列没有编码。

例如,以下流包含 Symbol :hello

"\x04\x08:\x0ahello"

“;”表示 Symbol 链接,它引用先前定义的 Symbol。类型字节之后是长整数,其中包含查找表中链接(引用)的 Symbol 的索引。

例如,以下流包含 [:hello, :hello]

"\x04\b[\a:\nhello;\x00"

当下面引用“符号”时,它可以是实际符号或符号链接。

Object 引用

与符号引用类似但不同,流仅包含每个对象的一个副本(由 object_id 确定),但 true、false、nil、Fixnum 和 Symbol(如上所述单独存储)除外,当再次遇到对象时,将存储和重用一个从 1 开始的 32 位值。(第一个对象的索引为 1)。

“@”表示对象链接。类型字节之后是长整数,给出对象的索引。

例如,以下流包含一个 Array,其中包含相同的 "hello" 对象两次

"\004\b[\a\"\nhello@\006"

实例变量

“I”表示实例变量紧随下一个对象之后。对象紧随类型字节之后。对象之后是长度,指示对象中实例变量的数量。长度之后是一组名称-值对。名称是符号,而值是对象。符号必须是实例变量名称(:@name)。

Object(“o”类型,如下所述)对其实例变量使用此处描述的相同格式。

对于 StringRegexp(如下所述),特殊实例变量 :E 用于指示 Encoding

扩展

“e”表示下一个对象由模块扩展。对象紧随类型字节之后。对象之后是符号,其中包含扩展对象的模块的名称。

Array

“[”表示 Array。类型字节之后是长整数,指示数组中的对象数量。给定的对象数量紧随长度之后。

Bignum

“l”表示 Bignum,它由三部分组成

sign

一个包含“+”表示正值或“-”表示负值的单字节。

length

一个长整数,表示 Bignum 数据的字节数,除以 2。将长度乘以 2 以确定后续数据的字节数。

data

表示数字的 Bignum 数据字节。

以下 ruby 代码将从字节数组中重建 Bignum 值

result = 0

bytes.each_with_index do |byte, exp|
 result += (byte * 2 ** (exp * 8))
end

ClassModule

“c”表示 Class 对象,“m”表示 Module,而“M”表示类或模块(这是为了兼容性而保留的旧式用法)。不包含类或模块内容,此类型仅为引用。在类型字节之后是一个字节序列,分别用于查找现有的类或模块。

类或模块上不允许有实例变量。

如果不存在类或模块,则应引发异常。

对于“c”和“m”类型,加载的对象必须分别是类或模块。

Data

“d”表示 Data 对象。(Data 对象是从 ruby 扩展中包装的指针。)在类型字节之后是一个符号,表示 Data 对象的类,以及一个包含 Data 对象状态的对象。

要转储 Data 对象,Ruby 会调用 _dump_data。要加载 Data 对象,Ruby 会在新建实例上使用对象的 state 调用 _load_data。

Float

“f”表示 Float 对象。在类型字节之后是一个包含浮点值的字节序列。以下值是特殊的

“inf”

正无穷大

“-inf”

负无穷大

“nan”

非数字

否则,字节序列包含一个 C double(可通过 strtod(3) 加载)。Marshal 的较旧次要版本还存储了额外的尾数位,以确保跨平台的可移植性,但 4.8 不包含这些位。请参见

ruby-talk:69518

以获得一些说明。

Hash 和带默认值的 Hash

“{” 表示 Hash 对象,而 “}” 表示设置了默认值(Hash.new 0)的 Hash。在类型字节之后是一个长整数,表示 Hash 中的键值对数量,即大小。给定的对象数量的两倍紧跟在大小之后。

对于带默认值的 Hash,默认值紧跟在所有对之后。

Module 和旧 Module

Object

“o” 表示没有任何其他特殊形式(例如用户定义或内置格式)的对象。在类型字节之后是一个符号,其中包含对象的类名。在类名之后是一个长整数,表示对象的实例变量名称和值的数量。给定的对象对数量的两倍紧跟在大小之后。

对中的键必须是包含实例变量名称的符号。

正则表达式

“/” 表示正则表达式。在类型字节之后是一个字节序列,其中包含正则表达式源。在类型字节之后是一个字节,其中包含正则表达式选项(不区分大小写等)作为有符号 8 位值。

正则表达式可以通过实例变量附加编码(见上文)。如果没有附加编码,则必须删除 ruby 1.8 中不存在的以下正则表达式特殊字符的转义符:g-m、o-q、u、y、E、F、H-L、N-V、X、Y。

String

‘“’ 表示 String。在类型字节之后是一个字节序列,其中包含字符串内容。从 ruby 1.9 倾倒时,应包含编码实例变量(:E,见上文),除非编码为二进制。

Struct

“S” 表示 Struct。在类型字节之后是一个符号,其中包含结构体的名称。在名称之后是一个长整数,表示结构体中的成员数量。成员数量的两倍紧跟在对象之后。每个成员都是一对,其中包含成员的符号和该成员值的对象。

如果结构体名称与正在运行的 ruby 中的 Struct 子类不匹配,则应引发异常。

如果当前运行的 Ruby 中的结构与已编组结构中的成员数量不匹配,则应引发异常。

用户 Class

“C”表示 StringRegexpArrayHash 的子类。类型字节后是一个包含子类名称的符号。名称后是包装对象。

用户定义

“u”表示使用 _dump 实例方法和 _load 类方法的用户定义序列化格式的对象。类型字节后是一个包含类名称的符号。类名称后是一个包含对象的自定义表示的字节序列。

类方法 _load 在类上调用,并使用从字节序列创建的字符串。

用户 Marshal

“U”表示使用 marshal_dumpmarshal_load 实例方法的用户定义序列化格式的对象。类型字节后是一个包含类名称的符号。类名称后是一个包含数据的对象。

加载时必须分配一个新实例,并且必须使用数据在实例上调用 marshal_load