Marshal
格式¶ ↑
Marshal
格式用于序列化 Ruby 对象。该格式可以通过三种用户定义的扩展机制存储任意对象。
有关使用 Marshal
序列化和反序列化对象的文档,请参阅 Marshal
模块。
本文档将一组序列化的对象称为流。Ruby 实现可以从 String
、IO
或实现 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”
-
整数的总大小为三个字节。后面的两个字节是正的 little-endian 整数。
- “xfe”
-
整数的总大小为三个字节。后面的两个字节是负的 little-endian 整数。
- “x03”
-
整数的总大小为四个字节。后面的三个字节是正的 little-endian 整数。
- “xfd”
-
整数的总大小为四个字节。后面的三个字节是负的 little-endian 整数。
- “x04”
-
整数的总大小为五个字节。后面的四个字节是正的 little-endian 整数。为了与 32 位 ruby 兼容,只有小于 1073741824 的 Fixnum 才应该以这种方式表示。对于流对象的大小,可以使用完整精度。
- “xfc”
-
整数的总大小为五个字节。后面的四个字节是负的 little-endian 整数。为了与 32 位 ruby 兼容,只有大于 -10737341824 的 Fixnum 才应该以这种方式表示。对于流对象的大小,可以使用完整精度。
否则,第一个字节是一个带偏移的符号扩展的八位值。如果该值是正数,则通过从该值减去 5 来确定该值。如果该值是负数,则通过将 5 添加到该值来确定该值。
许多值有多种表示形式。CRuby 始终输出最短的表示形式。
符号和字节序列¶ ↑
“:”表示一个真正的符号。真正的符号包含定义流中其余符号所需的数据,因为流中将来出现的符号将改为对此符号的引用(符号链接)。该引用是一个从零开始索引的 32 位值(因此第一个出现的 :hello
是 0)。
在类型字节之后是字节序列,该序列由一个 long 指示序列中的字节数,后跟相应数量的数据字节。字节序列没有编码。
例如,以下流包含 Symbol
:hello
"\x04\x08:\x0ahello"
“;”表示一个 Symbol
链接,该链接引用先前定义的 Symbol
。在类型字节之后是一个 long,其中包含链接的(引用的)Symbol
在查找表中的索引。
例如,以下流包含 [:hello, :hello]
"\x04\b[\a:\nhello;\x00"
当下面引用“符号”时,它可以是真正的符号或符号链接。
Object
引用¶ ↑
与符号引用分开但类似,该流对于所有对象(true、false、nil、Fixnum 和 Symbols 除外,它们如上所述单独存储)仅包含每个对象的一个副本(由 object_id 确定),当再次遇到该对象时,将存储和重用一个从 1 开始索引的 32 位值。(第一个对象的索引为 1)。
“@”表示对象链接。在类型字节之后是一个 long,给出对象的索引。
例如,以下流包含两次相同的 "hello"
对象的 Array
"\004\b[\a\"\nhello@\006"
实例变量¶ ↑
“I”表示实例变量在下一个对象之后。一个对象在类型字节之后。在该对象之后是一个长度,指示该对象的实例变量的数量。长度之后是一组名称-值对。名称是符号,而值是对象。符号必须是实例变量名称(:@name
)。
Object
(“o”类型,如下所述)为其实例变量使用与此处描述的相同的格式。
对于 String
和 Regexp
(如下所述),使用特殊的实例变量 :E
来指示 Encoding
。
扩展¶ ↑
“e”表示下一个对象由模块扩展。一个对象在类型字节之后。在该对象之后是一个符号,其中包含该对象所扩展的模块的名称。
Array
¶ ↑
“[”表示一个 Array
。在类型字节之后是一个 long,指示数组中对象的数量。给定数量的对象跟在长度后面。
Bignum¶ ↑
“l”表示一个 Bignum,它由三个部分组成
- 符号
-
一个字节,其中包含“+”表示正值或“-”表示负值。
- 长度
-
一个 long,指示 Bignum 数据字节数(除以 2)。将长度乘以 2 以确定后面数据字节数。
- 数据
-
表示数字的 Bignum 数据字节。
以下 ruby 代码将从字节数组中重建 Bignum 值
result = 0 bytes.each_with_index do |byte, exp| result += (byte * 2 ** (exp * 8)) end
Class
和 Module
¶ ↑
“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
对象,而 “}” 代表一个设置了默认值的 Hash
(Hash.new 0
)。在类型字节之后是一个长整数,指示 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” 代表 String
、Regexp
、Array
或 Hash
的子类。在类型字节之后是一个包含子类名称的符号。在名称之后是包装的对象。
用户定义¶ ↑
“u” 代表一个具有用户定义的序列化格式的对象,使用 _dump
实例方法和 _load
类方法。在类型字节之后是一个包含类名称的符号。在类名称之后是一个包含对象用户定义表示的字节序列。
使用从字节序列创建的字符串在类上调用类方法 _load
。
用户 Marshal
¶ ↑
“U” 代表一个具有用户定义的序列化格式的对象,使用 marshal_dump
和 marshal_load
实例方法。在类型字节之后是一个包含类名称的符号。在类名称之后是一个包含数据的对象。
加载时,必须分配一个新实例,并必须使用数据在实例上调用 marshal_load
。