編組/封送(marshalling)
/ MAR-shul-ing /
假設你想寄一組複雜的樂高模型給朋友。你沒辦法把組好的模型整個塞進信箱;你得把它拆解成一排扁平的積木,連同說明書裝進盒子,再把盒子寄出去。你的朋友打開盒子、重新組起模型。編組就是這個「攤平」動作的計算版本:它是把一份記憶體中的資料——一個數字、一個字串、一個串列、一整個物件——轉換成一串扁平的位元組,好讓它能在網路上傳輸、或被寫進檔案。在另一端把它重建回來,叫做解編組(unmarshalling,或反序列化)。
為什麼需要攤平?在一支執行中的程式裡,資料以一種豐富的形狀住在記憶體中:一個物件可能含有指向其他四散物件的指標(記憶體位址)。那些位址在另一台機器上毫無意義——你電腦上的位址 0x7ffe1234,在我的電腦上什麼都不指。所以編組會走過這份資料,把它真正的內容以一種雙方約定、自我完備的格式寫出來:數值 42 變成特定的位元組、一個字串變成它的長度後面接著它的字元、一個串列變成它的元素個數後面依序接著每個被編組的元素。接收端讀取那串位元組,在自己的記憶體裡重建出等價的資料。雙方都必須對格式取得共識,也必須對位元組順序(一個多位元組數字的哪一端先出現)這類細節取得共識。
為什麼重要:編組是每一次遠端程序呼叫、每一個網頁 API、每一個遊戲存檔底下那個默默工作的主力。每當資料跨越一個邊界——機器之間、程式與磁碟之間、兩種語言之間——它幾乎一定會被編組成位元組、再被解編組回來。誠實要說的是它有真實的成本與陷阱:它要花 CPU 時間、兩端可能對格式各說各話(這是經典的臭蟲來源),而且並非所有東西都能乾淨地攤平——一個開啟中的檔案代號、或一條活著的網路連線,就無法被簡單地裝進盒子、再到別處重建。
要把串列 [1, "hi", true] 送過網路,編組可能產生出代表這些意思的位元組:「一個含 3 個項目的串列;第 1 項是整數 1;第 2 項是 2 個字元的字串 h、i;第 3 項是布林值 true」。接收端讀進的正是這些,並在自己的記憶體裡重建一個等價的串列。JSON、Protocol Buffers、XML 這些格式,就是用來做這件事的不同約定語言。
把結構化資料攤平成位元組以便傳送;在另一端再重建它。兩端都必須對格式取得共識。
編組與序列化通常當成同義詞使用。要小心:有些東西無法被有意義地編組(一條活著的 socket、一個作業系統代號),而傳送端與接收端的格式版本不相符,是一種常見又難察覺的臭蟲。