Marshal 格式

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

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

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

流格式

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

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

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

流中的每个对象都由一个指示其类型的字节以及一个或多个描述对象的字节组成。下面的“对象”一词指的是下面定义了 Ruby 对象的任何类型。

true, false, nil

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

Fixnum 和 long

“i”表示一个使用打包格式的有符号 32 位值。一个到五个字节跟随类型。加载的值始终是 Fixnum。在 32 位平台上(其中 Fixnum 的精度小于 32 位),加载大值会导致 CRuby 溢出。

fixnum 类型用于表示 Ruby Fixnum 对象以及 marshaled 数组、哈希、实例变量和其他类型的长度。在以下各节中,“long”将指下面描述的格式,该格式支持完整的 32 位精度。

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

“x00”

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

“x01”

整数的总大小为两个字节。下一个字节是 0 到 255 范围内的正整数。为了节省字节,只有 123 到 255 之间的值应该用这种方式表示。

“xff”

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

“x02”

整数的总大小为三个字节。接下来的两个字节是一个小端序正整数。

“xfe”

整数的总大小为三个字节。接下来的两个字节是一个小端序负整数。

“x03”

整数的总大小为四个字节。接下来的三个字节是一个小端序正整数。

“xfd”

整数的总大小为四个字节。接下来的三个字节是一个小端序负整数。

“x04”

整数的总大小为五个字节。接下来的四个字节是一个小端序正整数。为了兼容 32 位 ruby,只有小于 1073741824 的 Fixnums 应该用这种方式表示。对于流对象的大小,可以使用全精度。

“xfc”

整数的总大小为五个字节。接下来的四个字节是一个小端序负整数。为了兼容 32 位 ruby,只有大于 -10737341824 的 Fixnums 应该用这种方式表示。对于流对象的大小,可以使用全精度。

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

许多值有多种表示方式。CRuby 总是输出尽可能最短的表示方式。

Symbols 和字节序列

“:”代表一个真实符号。一个真实符号包含定义该符号所需的数据,以便该流中后续出现的将是对此符号的引用(符号链接)。该引用是一个零索引的 32 位值(因此 :hello 的第一次出现是 0)。

类型字节后面是一个字节序列,该序列由一个指示序列中字节数的 long 和后面相应数量的数据字节组成。字节序列没有编码。

例如,以下流包含 Symbol :hello

"\x04\x08:\x0ahello"

“;”代表一个 Symbol 链接,它引用一个先前定义的 Symbol。类型字节后面是一个 long,其中包含链接(引用的)Symbol 的查找表中的索引。

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

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

当下面的“符号”被引用时,它可能是一个真实符号或一个符号链接。

Object 引用

与符号引用分开但类似,流中每个对象(根据 object_id 确定)只包含一个副本,除了 true、false、nil、Fixnums 和 Symbols(它们单独存储,如上所述),一个一索引的 32 位值将被存储并在再次遇到该对象时重用。(第一个对象的索引是 1)。

“@”代表一个对象链接。类型字节后面是一个 long,给出对象的索引。

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

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

实例变量

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

一个 Object(“o”类型,下面描述)使用与此处描述的实例变量相同的格式。

对于 StringRegexp(下面描述),一个特殊的实例变量 :E 用于指示 Encoding

扩展

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

Array

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

Bignum

“l”代表一个 Bignum,它由三部分组成

sign

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

length

一个 long,指示后面 Bignum 数据的字节数,除以二。将长度乘以二以确定后面数据的字节数。

数据

表示数字的 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 会调用 _load_data,并将对象的状态传递给新分配的实例。

Float

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

“inf”

正无穷

“-inf”

负无穷

“nan”

非数字

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

ruby-talk:69518

以获得一些解释。

Hash 和带默认值的 Hash

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

对于带默认值的 Hash,默认值跟随所有键值对。

Module 和旧 Module

Object

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

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

正则表达式

“/”代表一个正则表达式。类型字节之后是一个包含正则表达式源的字节序列。类型字节之后是一个字节,包含正则表达式选项(不区分大小写等)作为有符号 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。类型字节之后是一个符号,包含结构体的名称。名称之后是一个 long,指示结构体的成员数量。成员计数的两倍对象跟随。每个成员都是一个对,包含成员的符号和该成员值的对象。

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

如果当前运行的 ruby 中的结构体与 marshaled 结构体中的成员计数不匹配,则应引发异常。

用户类

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

用户定义

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

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

不推荐为新创建的类使用此类型,因为它有一些限制

用户 Marshal

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

加载时必须分配新实例,并在实例上调用 marshal_load,并将数据传递给它。