模組 Marshal

module Marshal: sig .. end

資料結構的封送處理。

此模組提供將任意資料結構編碼為位元組序列的函式,這些位元組序列可以寫入檔案或透過管道或網路連線傳送。這些位元組稍後可以讀回(可能在另一個程序中),並解碼回資料結構。對於給定的 OCaml 版本,位元組序列的格式在所有機器上都是相容的。

警告:封送處理目前不是型別安全的。封送資料的型別不會隨著資料的值一起傳輸,因此無法檢查讀回的資料是否具有上下文預期的型別。特別是,Marshal.from_* 函式的結果型別被給定為 'a,但這是誤導性的:返回的 OCaml 值並非對於所有 'a 都具有型別 'a;它具有一個獨特的型別,該型別無法在編譯時確定。程式設計師應使用以下語法明確指定返回值的預期型別:

  • (Marshal.from_channel chan : type)。如果檔案中的物件不屬於給定的型別,則在執行時可能會發生任何事情。

可擴展變體型別的值,例如例外(屬於可擴展型別 exn),由解封送器返回的值不應透過 match ... withtry ... with 進行模式比對,因為解封送處理不會保留比對其建構子所需之資訊。與其他可擴展變體值的結構相等性也不起作用。大多數其他用法(例如 Printexc.to_string)仍會如預期般運作。

封送值的表示形式不是人類可讀的,並且使用非列印字元的位元組。因此,與 Marshal.to_channelMarshal.from_channel 結合使用的輸入和輸出通道必須以二進位模式開啟,例如使用 open_out_binopen_in_bin;以文字模式開啟的通道會在文字通道行為與二進位通道不同的平台上(例如 Windows)導致解封送錯誤。


type extern_flags = 
| No_sharing (*

不保留共享

*)
| Closures (*

傳送函式閉包

*)
| Compat_32 (*

確保 32 位元相容性

*)

以下是 Marshal.to_* 函式的旗標。

val to_channel : out_channel -> 'a -> extern_flags list -> unit

Marshal.to_channel chan v flagsv 的表示形式寫入通道 chanflags 引數是一個可能為空的旗標清單,它控制關於共享、函式值以及 32 位元和 64 位元平台之間相容性的封送行為。

如果 flags 不包含 Marshal.No_sharing,則會偵測並保留 v 值內的循環和共享,並將其保留在產生的位元組序列中。特別是,這保證封送處理始終終止。但是,不會偵測或保留對 Marshal.to_channel 的連續呼叫所封送的值之間的共享。如果 flags 包含 Marshal.No_sharing,則會忽略共享。如果 v 不包含共享的子結構,則這會導致更快的封送處理,但如果 v 實際上包含共享,則可能會導致更慢的封送處理和更大的位元組表示形式,甚至如果 v 包含循環則可能不會終止。

如果 flags 不包含 Marshal.Closures,則當在 v 內部遇到函式值時,封送處理會失敗:只有「純」資料結構(不包含函式或物件)才能在不同的程式之間安全地傳輸。如果 flags 包含 Marshal.Closures,則函式值會被封送為程式碼中函式的位置,以及與閉包中捕獲的自由變數相對應的值。在這種情況下,封送的輸出只能在執行完全相同的程式,且具有完全相同的編譯程式碼的程序中讀回。(這會在解封送時檢查,使用隨程式碼位置一起傳輸的程式碼的 MD5 摘要。)

閉包中捕獲的自由變數的確切定義未指定,並且在位元組碼和機器碼之間(以及根據最佳化旗標)可能會有所不同。特別是,存取全域參考的函式值可能會或可能不會在其閉包中包含該參考。如果包含,則解封送對應的閉包將會建立一個新的參考,與全域參考不同。

如果 flags 包含 Marshal.Compat_32,則當遇到超出 32 位元平台上可表示的整數範圍 -230230-1 的整數值時,封送處理會失敗。這確保在 64 位元平台上產生的封送資料可以在 32 位元平台上安全地讀回。如果 flags 不包含 Marshal.Compat_32,則會封送超出範圍 -230230-1 的整數值,並且可以在 64 位元平台上讀回,但是在 32 位元平台上讀回時會在解封送時導致錯誤。Mashal.Compat_32 旗標僅在 64 位元平台上執行封送處理時才重要;如果是在 32 位元平台上執行封送處理,則沒有任何效果。

val to_bytes : 'a -> extern_flags list -> bytes

Marshal.to_bytes v flags 傳回包含 v 表示形式的位元組序列。flags 引數的含義與 Marshal.to_channel 相同。

val to_string : 'a -> extern_flags list -> string

to_bytes 相同,但將結果作為字串而不是位元組序列傳回。

val to_buffer : bytes -> int -> int -> 'a -> extern_flags list -> int

Marshal.to_buffer buff ofs len v flags 封送值 v,將其位元組表示形式儲存在序列 buff 中,從索引 ofs 開始,最多寫入 len 個位元組。它會傳回實際寫入序列的位元組數。如果 v 的位元組表示形式不符合 len 個字元,則會引發 Failure 例外狀況。

val from_channel : in_channel -> 'a

Marshal.from_channel chan 從通道 chan 讀取結構化值的位元組表示形式,如 Marshal.to_* 函式之一所產生,並重建並傳回對應的值。

val from_bytes : bytes -> int -> 'a

Marshal.from_bytes buff ofs 解封送結構化值,如 Marshal.from_channel 所做,只是位元組表示形式不是從通道讀取,而是取自位元組序列 buff,從位置 ofs 開始。位元組序列不會被變動。

val from_string : string -> int -> 'a

from_bytes 相同,但採用字串作為引數,而不是位元組序列。

val header_size : int

表示封送值的位元組由固定大小的標頭和可變大小的資料部分組成,其大小可以從標頭確定。Marshal.header_size 是標頭的大小(以位元組為單位)。Marshal.data_size buff ofs 是資料部分的大小(以位元組為單位),假設有效的標頭儲存在 buff 中,從位置 ofs 開始。最後,Marshal.total_size buff ofs 是封送值的總大小(以位元組為單位)。如果 buffofs 不包含有效的標頭,則 Marshal.data_sizeMarshal.total_size 都會引發 Failure

若要將封送值的位元組表示形式讀取到位元組序列中,程式需要先將 Marshal.header_size 個位元組讀取到序列中,然後使用 Marshal.data_size 確定表示形式其餘部分的長度,確保序列足夠大以容納剩餘的資料,然後讀取它,最後呼叫 Marshal.from_bytes 來解封送該值。

val data_size : bytes -> int -> int

請參閱 Marshal.header_size

val total_size : bytes -> int -> int

請參閱 Marshal.header_size

Marshal 與網域安全

當封送可能由不同網域修改的可變值時,必須小心。變動正在封送(即轉換為位元組序列)的值是一種程式設計錯誤,並且由於封送處理涉及逐位元組複製,因此可能會導致(在解封送時)產生令人驚訝的值。