作者
Richard A. O'Keefe <ok(at)cs(dot)otago(dot)ac(dot)nz>
狀態
草案
類型
標準追蹤
建立日期
2008-08-27
Erlang 版本
OTP_R12B-4

EEP 22:二進位範圍檢查 #

摘要 #

模組可以請求對位元欄位進行範圍檢查。

規格 #

新增了一個新的指示詞。

-bit_range_check(Wanted).

其中 Wanted 為 ‘false’ 或 ‘true’。

回想一下,位元字串(或二進位)的區段具有以下形式

Value [':' Size] ['/' Type_Specifier_List]

其中 Type_Specifier_List 包括 ‘integer’、‘signed’ 和 ‘unsigned’ 等。目前的文件指出

"Signedness ... Only matters for matching and when the type
 is integer.  The default is unsigned."

SizeUnit 結合會得到 Size_In_Bits。線上 Erlang 手冊在 6.16 節中沒有說明,在建構位元字串時,整數的最低 Size_In_Bits 位元會被使用,其餘的則會被靜默忽略,但實際上是如此。

指示詞 -bit_range_check(false) 明確表達了程式設計師的意圖,即應該發生這種類似 C 語言的截斷。

指示詞 -bit_range_check(true) 表示,如果在 Value 不在 0 <= Value < 2**Size 的範圍內,則在

Value:Size/unsigned-integer-unit:1

或在其他等效的建構中,會發生經過檢查的執行階段錯誤;如果在 Value 不在 -(2**(Size-1)) <= Value < 2**(Size-1) 的範圍內,則在

Value:Size/signed-integer-unit:1

或在其他等效的建構中,會發生經過檢查的執行階段錯誤。

引發的錯誤類似於為 (1//0):Size/Type_Specifier_List 引發的錯誤,只是使用 ‘badrange’ 而不是 ‘badarith’。

在沒有 -bit_range_check 指示詞的情況下,整數位元語法區段的行為是實作定義的,並且可能會更改。

BEAM 系統擴展了一個或多個新指令,類似於現有的整數區段指令,但會檢查範圍。編譯器擴展以針對 <<...>> 表達式,在 -bit_range_check(true) 指示詞的範圍內產生這些指令。

-bit_range_check 指示詞不得出現在位元語法模式或表達式之後,也不得出現在另一個 -bit_range_check 指示詞之後。

動機 #

對於 Erlang 程式設計師來說,這種截斷的發生一直是一個不愉快的驚喜。資訊的靜默破壞在其他方面與 Erlang 格格不入:整數運算是不受限制的,而不是像某些(但不是全部)C 系統中那樣被包裹起來;element/2 不會將索引對元組大小取模,如果索引超出範圍,則會引發例外,依此類推。

在任何需要截斷的情況下,Erlang 程式設計師已經可以寫

(Value rem 256):unsigned-integer

並且 Erlang 編譯器可以注意到這一點並優化掉 ‘rem’ 操作,因此截斷不僅在 Erlang 中不常見,而且在這種特殊情況下也是出乎意料的。

它不僅出乎意料,而且還會減少發現錯誤的機會,因此似乎是不可取的。

Edwin Fine 問道:「在不嚴重影響 Erlang 執行階段執行的正確性的情況下,增加可選的執行階段檢查以檢測這種情況有多困難?」

Björn Gustavsson 回答說:「最好在編譯器中增加可選的支援以開啟檢查(無論是對整個模組還是對二進位的單獨區段)。如果有人撰寫 EEP,我們會考慮實作它。」

這就是那個 EEP。

原理 #

Erlang/OTP 團隊認為舊的行為是一個功能,並希望保留它。特別是,他們希望那些期望舊行為的模組(目前)能夠繼續工作而無需修改。

一種替代方案是新增新的語法,例如擁有新的 ‘checked’ 指定符,以便

Value/checked-unsigned-integer

需要一個在 0..255 範圍內的值。但是,許多 Erlang 程式設計師會希望將此用作正常情況,並且不喜歡安全的版本比不安全版本要付出更多的編寫努力。

似乎「需要截斷/不需要截斷」不是這個表達式或那個表達式的問題,而是這個程式設計師或那個程式設計師的問題,而且我們可以預期每個模組都會由只期望一種行為或只期望另一種行為的人編寫。

向模組新增

-bit_range_check(true).

指示詞比什麼都不做要付出更多的努力,但是想要這種行為的程式設計師應該能夠設置他們的編輯環境,以便在他們建立新 Erlang 模組的範本中包含這一行。

有幾個問題

  • 這應該適用於位元字串以及整數嗎?
  • 指示詞的名稱應該是什麼?
  • 指示詞的參數應該是什麼?
  • 是否允許在模組中多次使用該指示詞?

位元字串:假設 X = <<5:3/unsigned-integer-unit:1>>。目前,<<X:2/bits>> 會靜默截斷 X。這會從 X 的右側刪除位元,從而得到 <<2:2>>。如果這與整數的工作方式相同,您會期望 <<1:2>>。這當然很奇怪。由於我們在整數的左側獲得截斷,並在左側獲得填充,因此我們自然期望位元字串在右側進行填充,以配合右側的截斷。但是 <<X:4/bits>> 不是 <<10:4>>,而是一個執行階段例外。一切都非常奇怪。程式設計師可以輕鬆指示他們是否想要在左側或右側進行截斷,以及在左側或右側進行填充,這當然是可取的。也許是一個新的內建函數

set_bit_size(Bit_String, Desired_Width,
             Truncation, Padding, Fill)

Bit_String : a bit string
Desired_Width : a non-negative integer, the width wanted
Truncation: 'left' | 'right' | 'error';
    if bit_size(Bit_String) > Desired_Width
        truncate on the left/truncate on the right/
        report an error
Padding: 'left' | 'right' | 'error';
    if bit_size(Bit_String) < Desired_Width
        pad on the left/pad on the right/report an error
Fill: 0 | 1 | 'copy';
    pad with 0/pad with 1/pad with a copy of the
    last bit at the end where padding is done.

但是,這個想法只是部分成形,並且不屬於目前的提案。就目前情況而言,使用位元語法並依賴隱式截斷是提取位元字串的前導位元的最簡單方法。

只要指示詞的名稱能夠揭示意圖,它叫什麼名字就沒那麼重要。我建議使用 bit_range_check,因為它全部都是關於位元語法中的檢查、範圍,但是由於在本草案中,它不適用於位元字串區段,因此也許 bit_integer_range_check 會更好。

參數 false 和 true 看起來很清楚。替代方案可能是類似

-bit_integer_range(check).
-bit_integer_range(no_check).

這也很好。

古典的 Pascal 編譯器允許您執行以下操作

{$I-}   (* disable index checks *)
(* code with no index checks *)
{$I+}   (* re-enable index checks *)

允許在模組中使用多個 -bit_range_check 指示詞,可以讓您在其他方面使用新方法的模組內使用為舊方法編寫的程式碼。我不認為我們希望鼓勵這種事情:如果所有程式碼都遵循相同的規則,則在閱讀模組時會容易得多。

對於期望能夠以任何順序處理函數定義的 Erlang 編譯器來說,也更容易。編譯器可以在處理任何位元語法形式之前,檢查模組中是否已使用這些指示詞之一。但是,如果人們在第一次看到 <<...>> 建構時,已經看到了任何可能影響其含義的指示詞,則閱讀模組會更容易。

如果需要,可以隨時放寬對這些指示詞的數量和位置的限制。

向後相容性 #

所有現有的 Erlang 程式碼仍然可以接受,並且語義不變。

參考實作 #

無,因為我仍然找不到編譯器的工作方式。

版權 #

本文檔已放入公共領域。