Marshal
格式¶ ↑
The 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 位值。类型后面跟着 1 到 5 个字节。加载的值始终为 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 的 Fixnums 应该以这种方式表示。对于流对象的尺寸,可以使用全精度。
- “xfc”
-
整数的总大小为五个字节。接下来的四个字节是一个负的小端整数。为了与 32 位 ruby 兼容,只有大于 -10737341824 的 Fixnums 应该以这种方式表示。对于流对象的尺寸,可以使用全精度。
否则,第一个字节是一个带偏移量的符号扩展的八位值。如果该值是正数,则该值通过从该值中减去 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、Fixnums 和 Symbols(如上所述,它们分别存储)之外的所有对象,将存储一个从 1 开始的 32 位值,并在再次遇到该对象时重复使用。(第一个对象的索引为 1)。
“@” 代表一个对象链接。类型字节后面是一个长整型,表示对象的索引。
例如,以下流包含一个 Array
,其中包含两次相同的 "hello"
对象
"\004\b[\a\"\nhello@\006"
实例变量¶ ↑
“I” 表示实例变量紧随下一个对象。一个对象紧随类型字节。对象后面是一个长度,表示该对象实例变量的数量。长度后面是一组名称-值对。名称是符号,而值是对象。符号必须是实例变量名称 (:@name
)。
一个 Object
(“o” 类型,下面描述)使用与这里描述的相同的格式来表示其实例变量。
对于 String
和 Regexp
(下面描述),一个特殊的实例变量 :E
用于表示 Encoding
。
扩展¶ ↑
“e” 表示下一个对象被一个模块扩展。一个对象紧随类型字节。对象后面是一个符号,包含扩展该对象的模块的名称。
Array
¶ ↑
“[” 代表一个 Array
。类型字节后面是一个长整型,表示数组中对象的个数。给定的对象数量紧随长度。
大整数¶ ↑
“l” 代表一个大整数,它由三个部分组成
- 符号
-
一个字节,包含正值用“+”表示,负值用“-”表示。
- 长度
-
一个长整型,表示后面跟随的大整数数据的字节数,除以二。将长度乘以二以确定后面跟随的数据字节数。
- 数据
-
表示该数字的大整数数据字节。
以下 Ruby 代码将从字节数组中重建大整数的值
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 双精度数(可通过 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 中不存在的 regexp 特殊字符的转义符: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
。