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 物件和封送陣列、雜湊、執行個體變數和其他類型的尺寸。在以下各節中,「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 和 Symbols 除外(如上所述,這些物件會另外儲存),當再次遇到物件時,會儲存和重複使用一個從 1 開始編號的 32 位元值。(第一個物件的索引為 1)。

「@」代表物件連結。在類型位元組之後,會有一個長整數提供物件的索引。

例如,以下串流包含一個 Array,其中包含同一個 "hello" 物件兩次

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

執行個體變數

「I」表示執行個體變數會在下一物件之後。一個物件會在類型位元組之後。在物件之後,會有一個長度,表示物件的執行個體變數數量。在長度之後,會有一組名稱-值對。名稱是符號,而值是物件。符號必須是執行個體變數名稱(:@name)。

一個 Object(「o」類型,如下所述)會使用與這裡所述相同的格式,作為其執行個體變數。

對於 StringRegexp(如下所述),會使用一個特殊的執行個體變數 :E 來表示 Encoding

延伸

「e」表示下一個物件會由模組延伸。一個物件會在類型位元組之後。在物件之後,會有一個符號,其中包含物件延伸的模組名稱。

Array

「[」代表一個 Array。在類型位元組之後,會有一個長整數,表示陣列中的物件數量。指定數量的物件會在長度之後。

Bignum

「l」代表一個 Bignum,它由三部分組成

符號

一個單一位元組,包含正值的「+」或負值的「-」。

長度

一個長度表示 Bignum 資料的位元組數,除以二。將長度乘以二,以確定後續資料的位元組數。

資料

表示數字的 Bignum 資料位元組。

下列 Ruby 程式碼將從位元組陣列重建 Bignum 值

result = 0

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

類別模組

“c” 表示 類別 物件,“m” 表示 模組,“M” 表示類別或模組(這是為了相容性而使用的舊式格式)。不包含類別或模組內容,此類型只是一個參考。在類型位元組之後,會有一個位元組序列,分別用於查詢現有的類別或模組。

類別或模組不允許有實例變數。

如果不存在類別或模組,則應引發例外狀況。

對於“c”和“m”類型,載入的物件必須分別是類別或模組。

資料

“d” 表示 資料 物件。(資料 物件是 Ruby 延伸模組的封裝指標。)在類型位元組之後,會有一個符號表示 資料 物件的類別,以及一個包含 資料 物件狀態的物件。

若要傾印 資料 物件,Ruby 會呼叫 _dump_data。若要載入 資料 物件,Ruby 會在一個新配置的實例上呼叫 _load_data,並傳入物件的狀態。

浮點數

“f” 表示 浮點數 物件。在類型位元組之後,會有一個位元組序列,其中包含浮點數值。下列值是特殊的

“inf”

正無限大

“-inf”

負無限大

“nan”

非數字

否則,位元組序列包含一個 C double(可由 strtod(3) 載入)。較舊的 Marshal 次要版本也儲存了額外的尾數位元,以確保跨平台的可攜性,但 4.8 不包含這些位元。請參閱

ruby-talk:69518

以取得一些說明。

雜湊 和具有預設值的 雜湊

“{” 表示 雜湊 物件,而 “}” 表示具有預設值設定的 雜湊Hash.new 0)。在類型位元組之後,會有一個長度表示 雜湊 中的鍵值對數量,即大小。物件的給定數量兩倍會出現在大小之後。

對於具有預設值的 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,並使用資料。