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 对象以及编组数组、哈希、实例变量和其他类型的大小。在以下部分中,“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 和 Symbol 除外(它们单独存储,如上所述))仅包含每个对象的一个副本(由 object_id 确定),当再次遇到该对象时,将存储和重用一个从一开始的 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 数据的字节数,除以二。将长度乘以二以确定后面数据的字节数。
- 数据
-
表示数字的 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
。