module Bytes:sig
..end
位元組序列操作。
位元組序列是一個可變的資料結構,包含固定長度的位元組序列。每個位元組都可以透過索引在常數時間內進行讀取或寫入。
給定一個長度為 l
的位元組序列 s
,我們可以透過其在序列中的索引存取 s
的每個 l
個位元組。索引從 0
開始,如果索引落在範圍 [0...l-1]
(包含)內,我們將稱之為 s
中的有效索引。位置是兩個位元組之間或序列的開頭或結尾的點。如果位置落在範圍 [0...l]
(包含)內,我們稱之為 s
中的有效位置。請注意,索引 n
處的位元組位於位置 n
和 n+1
之間。
如果 len >= 0
且 start
和 start+len
是 s
中的有效位置,則稱兩個參數 start
和 len
指定 s
的有效範圍。
位元組序列可以就地修改,例如透過下面描述的 set
和 blit
函數。另請參閱字串(模組 String
),它們幾乎是相同的資料結構,但無法就地修改。
位元組由 OCaml 類型 char
表示。
此模組的標籤版本可以使用 StdLabels
模組中所述的方式使用。
val length : bytes -> int
返回參數的長度(位元組數)。
val get : bytes -> int -> char
get s n
返回參數 s
中索引 n
處的位元組。
Invalid_argument
如果 n
不是 s
中的有效索引。val set : bytes -> int -> char -> unit
set s n c
就地修改 s
,將索引 n
處的位元組替換為 c
。
Invalid_argument
如果 n
不是 s
中的有效索引。val create : int -> bytes
create n
返回一個長度為 n
的新位元組序列。該序列未初始化,包含任意位元組。
Invalid_argument
如果 n < 0
或 n >
Sys.max_string_length
。val make : int -> char -> bytes
make n c
返回一個長度為 n
的新位元組序列,並以位元組 c
填充。
Invalid_argument
如果 n < 0
或 n >
Sys.max_string_length
。val init : int -> (int -> char) -> bytes
init n f
返回一個長度為 n
的新位元組序列,其中字元 i
初始化為 f i
的結果(以遞增的索引順序)。
Invalid_argument
如果 n < 0
或 n >
Sys.max_string_length
。val empty : bytes
大小為 0 的位元組序列。
val copy : bytes -> bytes
返回一個新的位元組序列,其中包含與參數相同的位元組。
val of_string : string -> bytes
返回一個新的位元組序列,其中包含與給定字串相同的位元組。
val to_string : bytes -> string
返回一個新的字串,其中包含與給定位元組序列相同的位元組。
val sub : bytes -> int -> int -> bytes
sub s pos len
返回一個長度為 len
的新位元組序列,其中包含 s
的子序列,該子序列從位置 pos
開始且長度為 len
。
Invalid_argument
如果 pos
和 len
沒有指定 s
的有效範圍。val sub_string : bytes -> int -> int -> string
與 Bytes.sub
相同,但返回字串而不是位元組序列。
val extend : bytes -> int -> int -> bytes
extend s left right
返回一個新的位元組序列,其中包含 s
的位元組,其中附加了 left
個未初始化的位元組,並在末尾附加了 right
個未初始化的位元組。如果 left
或 right
為負數,則會從 s
的相應側移除(而不是附加)位元組。
Invalid_argument
如果結果長度為負數或長度大於 Sys.max_string_length
個位元組。val fill : bytes -> int -> int -> char -> unit
fill s pos len c
就地修改 s
,從 pos
開始,將 len
個字元替換為 c
。
Invalid_argument
如果 pos
和 len
沒有指定 s
的有效範圍。val blit : bytes -> int -> bytes -> int -> int -> unit
blit src src_pos dst dst_pos len
將 len
個位元組從位元組序列 src
複製到位元組序列 dst
,從索引 src_pos
開始,到索引 dst_pos
開始。即使 src
和 dst
是相同的位元組序列,並且來源和目標間隔重疊,它也能正確運作。
Invalid_argument
如果 src_pos
和 len
沒有指定 src
的有效範圍,或者如果 dst_pos
和 len
沒有指定 dst
的有效範圍。val blit_string : string -> int -> bytes -> int -> int -> unit
blit_string src src_pos dst dst_pos len
將 len
個位元組從字串 src
複製到位元組序列 dst
,從索引 src_pos
開始,到索引 dst_pos
開始。
Invalid_argument
如果 src_pos
和 len
沒有指定 src
的有效範圍,或者如果 dst_pos
和 len
沒有指定 dst
的有效範圍。val concat : bytes -> bytes list -> bytes
concat sep sl
將位元組序列列表 sl
連接起來,在每個位元組序列之間插入分隔符位元組序列 sep
,並將結果作為新的位元組序列返回。
Invalid_argument
如果結果長度大於 Sys.max_string_length
個位元組。val cat : bytes -> bytes -> bytes
cat s1 s2
將 s1
和 s2
連接起來,並將結果作為新的位元組序列返回。
Invalid_argument
如果結果長度大於 Sys.max_string_length
個位元組。val iter : (char -> unit) -> bytes -> unit
iter f s
依次將函數 f
應用於 s
的所有位元組。它等效於 f (get s 0); f (get s 1); ...; f (get s
。
(length s - 1)); ()
val iteri : (int -> char -> unit) -> bytes -> unit
與 Bytes.iter
相同,但是該函數以位元組的索引作為第一個參數,以位元組本身作為第二個參數應用。
val map : (char -> char) -> bytes -> bytes
map f s
依次將函數 f
應用於 s
的所有位元組(以遞增的索引順序),並將結果位元組儲存在一個新的序列中,該序列作為結果返回。
val mapi : (int -> char -> char) -> bytes -> bytes
mapi f s
使用 s
的每個字元及其索引(以遞增的索引順序)呼叫 f
,並將結果位元組儲存在一個新的序列中,該序列作為結果返回。
val fold_left : ('acc -> char -> 'acc) -> 'acc -> bytes -> 'acc
fold_left f x s
計算 f (... (f (f x (get s 0)) (get s 1)) ...) (get s (n-1))
,其中 n
是 s
的長度。
val fold_right : (char -> 'acc -> 'acc) -> bytes -> 'acc -> 'acc
fold_right f s x
計算 f (get s 0) (f (get s 1) ( ... (f (get s (n-1)) x) ...))
,其中 n
是 s
的長度。
val for_all : (char -> bool) -> bytes -> bool
for_all p s
檢查 s
中的所有字元是否滿足謂詞 p
。
val exists : (char -> bool) -> bytes -> bool
exists p s
檢查 s
中是否至少有一個字元滿足謂詞 p
。
val trim : bytes -> bytes
返回參數的副本,不包含開頭和結尾的空白。被視為空白的位元組是 ASCII 字元 ' '
、 '\012'
、'\n'
、'\r'
和 '\t'
。
val escaped : bytes -> bytes
返回參數的副本,其中特殊字元由逸出序列表示,遵循 OCaml 的詞彙慣例。所有 ASCII 可列印範圍 (32..126) 之外的字元以及反斜線和雙引號都會逸出。
Invalid_argument
如果結果長度大於 Sys.max_string_length
個位元組。val index : bytes -> char -> int
index s c
返回 s
中位元組 c
的第一次出現的索引。
Not_found
如果 c
沒有出現在 s
中。val index_opt : bytes -> char -> int option
index_opt s c
返回 s
中位元組 c
的第一次出現的索引,或者如果 c
沒有出現在 s
中,則返回 None
。
val rindex : bytes -> char -> int
rindex s c
返回 s
中位元組 c
的最後一次出現的索引。
Not_found
如果 c
沒有出現在 s
中。val rindex_opt : bytes -> char -> int option
rindex_opt s c
返回 s
中位元組 c
的最後一次出現的索引,或者如果 c
沒有出現在 s
中,則返回 None
。
val index_from : bytes -> int -> char -> int
index_from s i c
返回位置 i
之後的 s
中位元組 c
的第一次出現的索引。index s c
等效於 index_from s 0 c
。
i
不是 s
中的有效位置,則引發 Invalid_argument
。c
沒有出現在位置 i
之後的 s
中,則引發 Not_found
。val index_from_opt : bytes -> int -> char -> int option
index_from_opt s i c
返回位置 i
之後的 s
中位元組 c
的第一次出現的索引,或者如果 c
沒有出現在位置 i
之後的 s
中,則返回 None
。index_opt s c
等效於 index_from_opt s 0 c
。
i
不是 s
中的有效位置,則會引發 Invalid_argument
異常。val rindex_from : bytes -> int -> char -> int
rindex_from s i c
會傳回位元組 c
在 s
中,位置 i+1
之前的最後一次出現的索引。rindex s c
等同於 rindex_from s (length s - 1) c
。
i+1
不是 s
中的有效位置,則會引發 Invalid_argument
異常。c
在位置 i+1
之前未出現在 s
中,則會引發 Not_found
異常。val rindex_from_opt : bytes -> int -> char -> int option
rindex_from_opt s i c
會傳回位元組 c
在 s
中,位置 i+1
之前的最後一次出現的索引。如果位元組 c
在位置 i+1
之前未出現在 s
中,則會傳回 None
。rindex_opt s c
等同於 rindex_from s (length s - 1) c
。
i+1
不是 s
中的有效位置,則會引發 Invalid_argument
異常。val contains : bytes -> char -> bool
contains s c
會測試位元組 c
是否出現在 s
中。
val contains_from : bytes -> int -> char -> bool
contains_from s start c
會測試位元組 c
是否出現在 s
中,位置 start
之後。contains s c
等同於 contains_from
。
s 0 c
start
不是 s
中的有效位置,則會引發 Invalid_argument
異常。val rcontains_from : bytes -> int -> char -> bool
rcontains_from s stop c
會測試位元組 c
是否出現在 s
中,位置 stop+1
之前。
stop < 0
或 stop+1
不是 s
中的有效位置,則會引發 Invalid_argument
異常。val uppercase_ascii : bytes -> bytes
傳回引數的副本,其中所有小寫字母都使用 US-ASCII 字元集轉換為大寫字母。
val lowercase_ascii : bytes -> bytes
傳回引數的副本,其中所有大寫字母都使用 US-ASCII 字元集轉換為小寫字母。
val capitalize_ascii : bytes -> bytes
傳回引數的副本,其中第一個字元使用 US-ASCII 字元集設定為大寫字母。
val uncapitalize_ascii : bytes -> bytes
傳回引數的副本,其中第一個字元使用 US-ASCII 字元集設定為小寫字母。
typet =
bytes
位元組序列類型的別名。
val compare : t -> t -> int
val equal : t -> t -> bool
位元組序列的相等函式。
val starts_with : prefix:bytes -> bytes -> bool
當且僅當 s
以 prefix
開頭時,starts_with
~prefix s
為 true
。
val ends_with : suffix:bytes -> bytes -> bool
當且僅當 s
以 suffix
結尾時,ends_with
~suffix s
為 true
。
本節描述 bytes
和 string
之間不安全、低階的轉換函式。它們不會複製內部資料;如果使用不當,可能會破壞 -safe-string
選項提供的字串的不可變性不變量。它們適用於專業的程式庫作者,但對於大多數用途,您應該改用始終正確的 Bytes.to_string
和 Bytes.of_string
。
val unsafe_to_string : bytes -> string
不安全地將位元組序列轉換為字串。
為了推論 unsafe_to_string
的使用,考慮「所有權」規則會很方便。操作某些資料的一段程式碼「擁有」該資料;有幾種不相交的所有權模式,包括
獨有所有權是線性的:將資料傳遞給另一段程式碼意味著放棄所有權(我們無法再次寫入資料)。獨有擁有者可以決定將資料設為共用(放棄對資料的變更權),但共用資料可能無法再次成為獨有資料。
只有在呼叫者擁有位元組序列 s
時,才能使用 unsafe_to_string s
-- 要麼是獨有,要麼是作為共用的不可變資料。呼叫者會放棄 s
的所有權,並取得傳回字串的所有權。
有兩種有效的使用案例符合此所有權規則
1. 透過初始化和變更永遠不會在執行初始化後變更的位元組序列來建立字串。
let string_init len f : string =
let s = Bytes.create len in
for i = 0 to len - 1 do Bytes.set s i (f i) done;
Bytes.unsafe_to_string s
此函式是安全的,因為在呼叫 unsafe_to_string
後,永遠不會存取或變更位元組序列 s
。string_init
程式碼會放棄 s
的所有權,並將產生的字串的所有權傳回給其呼叫者。
請注意,如果將 s
作為額外參數傳遞給函式 f
,則會不安全,因為它可能會以這種方式跳脫並在未來被變更 -- string_init
會放棄 s
的所有權,以將其傳遞給 f
,並且無法安全地呼叫 unsafe_to_string
。
我們提供了 String.init
、String.map
和 String.mapi
函式,以涵蓋建立新字串的大多數情況。您應盡可能優先選擇這些函式,而不是 to_string
或 unsafe_to_string
。
2. 暫時將位元組序列的所有權提供給預期獨有字串並傳回所有權的函式,以便我們可以在呼叫結束後再次變更序列。
let bytes_length (s : bytes) =
String.length (Bytes.unsafe_to_string s)
在此使用案例中,我們不保證在呼叫 bytes_length s
後,s
永遠不會被變更。String.length
函式會暫時借用位元組序列的獨有所有權(並將其視為 string
),但會將此所有權傳回給呼叫者,呼叫者可以假設在呼叫後 s
仍然是有效的位元組序列。請注意,這僅在我們知道 String.length
不會擷取其引數時才是正確的 -- 它可能會透過副作用通道(例如記憶體化組合器)跳脫。
在借用字串時,呼叫者不得變更 s
(它已暫時放棄所有權)。這會影響並行程式,也會影響高階函式:如果 String.length
傳回稍後要呼叫的閉包,則在完全套用此閉包並傳回所有權之前,不應變更 s
。
val unsafe_of_string : string -> bytes
不安全地將共用字串轉換為不應變更的位元組序列。
適用於 unsafe_to_string
的相同所有權規則也適用於 unsafe_of_string
:如果您是 string
值的所有者,則可以使用它,並且您將以相同的模式擁有傳回的 bytes
。
實際上,很難正確推論字串值的獨有所有權。您應始終假設字串是共用的,而不是獨有的。
例如,字串文字會由編譯器隱式共用,因此您永遠不會獨有擁有它們。
let incorrect = Bytes.unsafe_of_string "hello"
let s = Bytes.of_string "hello"
第一個宣告是不正確的,因為字串文字 "hello"
可能會由編譯器與程式的其他部分共用,而變更 incorrect
是一個錯誤。您必須始終使用第二個版本,此版本會執行複製,因此是正確的。
假設不是字串文字,而是(部分)由字串文字建立的字串的獨有所有權,也是不正確的。例如,變更 unsafe_of_string ("foo" ^ s)
可能會變更共用字串 "foo"
-- 假設字串的表示法類似繩索。更一般來說,對字串進行操作的函式會假設共用所有權,它們不會保留獨有所有權。因此,假設 unsafe_of_string
結果的獨有所有權是不正確的。
我們合理確信是安全的唯一情況是,產生的 bytes
是共用的 -- 用作不可變的位元組序列。這可能對於低階程式的遞增遷移非常有用,這些程式會操作不可變的位元組序列(例如 Marshal.from_bytes
)並先前將 string
類型用於此目的。
val split_on_char : char -> bytes -> bytes list
split_on_char sep s
會傳回由 sep
字元分隔的 s
的所有(可能為空)子序列的清單。如果 s
為空,則結果為單例清單 [empty]
。
函式的輸出由以下不變量指定
sep
作為分隔符號串連其元素會傳回等於輸入的位元組序列(Bytes.concat (Bytes.make 1 sep)
(Bytes.split_on_char sep s) = s
)。sep
字元。val to_seq : t -> char Seq.t
以遞增索引順序反覆運算字串。在反覆運算期間修改字串將會反映在序列中。
val to_seqi : t -> (int * char) Seq.t
以遞增順序反覆運算字串,並產生沿著字元的索引
val of_seq : char Seq.t -> t
從產生器建立字串
val get_utf_8_uchar : t -> int -> Uchar.utf_decode
get_utf_8_uchar b i
會解碼 b
中索引 i
處的 UTF-8 字元。
val set_utf_8_uchar : t -> int -> Uchar.t -> int
set_utf_8_uchar b i u
會在 b
中索引 i
處的 UTF-8 編碼 u
,並傳回從 i
開始寫入的位元組數 n
。如果 n
為 0
,則沒有足夠的空間在 i
處編碼 u
,並且 b
保持不變。否則,可以在 i + n
處編碼新字元。
val is_valid_utf_8 : t -> bool
當且僅當 b
包含有效的 UTF-8 資料時,is_valid_utf_8 b
為 true
。
val get_utf_16be_uchar : t -> int -> Uchar.utf_decode
get_utf_16be_uchar b i
會解碼 b
中索引 i
處的 UTF-16BE 字元。
val set_utf_16be_uchar : t -> int -> Uchar.t -> int
set_utf_16be_uchar b i u
以 UTF-16BE 編碼方式將 u
編碼至 b
中索引值為 i
的位置,並回傳從 i
開始寫入的位元組數量 n
。如果 n
為 0
,表示在 i
的位置沒有足夠的空間來編碼 u
,且 b
將保持不變。否則,可以在 i + n
的位置編碼新的字元。
val is_valid_utf_16be : t -> bool
當且僅當 b
包含有效的 UTF-16BE 資料時,is_valid_utf_16be b
的值為 true
。
val get_utf_16le_uchar : t -> int -> Uchar.utf_decode
get_utf_16le_uchar b i
會解碼 b
中索引值為 i
的 UTF-16LE 字元。
val set_utf_16le_uchar : t -> int -> Uchar.t -> int
set_utf_16le_uchar b i u
以 UTF-16LE 編碼方式將 u
編碼至 b
中索引值為 i
的位置,並回傳從 i
開始寫入的位元組數量 n
。如果 n
為 0
,表示在 i
的位置沒有足夠的空間來編碼 u
,且 b
將保持不變。否則,可以在 i + n
的位置編碼新的字元。
val is_valid_utf_16le : t -> bool
當且僅當 b
包含有效的 UTF-16LE 資料時,is_valid_utf_16le b
的值為 true
。
本節中的函式會將整數二進位編碼和解碼為位元組序列,以及從位元組序列解碼和編碼為整數。
如果索引值為 i
的位置沒有足夠的空間來解碼或編碼整數,則所有以下函式都會拋出 Invalid_argument
例外。
小端(little-endian,反之為大端 big-endian)編碼表示最低有效位元組(反之為最高有效位元組)會先儲存。大端編碼也稱為網路位元組順序。原生端編碼則根據 Sys.big_endian
決定為小端或大端。
32 位元和 64 位元整數以 int32
和 int64
類型表示,可以將它們解釋為帶正負號或無正負號的數字。
8 位元和 16 位元整數以 int
類型表示,該類型具有比二進位編碼更多的位元。這些額外的位元會按照以下方式處理
int
值表示時會將結果進行符號擴展(反之為零擴展)。int
值表示的 8 位元或 16 位元整數的函式會將其輸入截斷至其最低有效位元組。val get_uint8 : bytes -> int -> int
get_uint8 b i
會取得 b
中從位元組索引值 i
開始的無正負號 8 位元整數。
val get_int8 : bytes -> int -> int
get_int8 b i
會取得 b
中從位元組索引值 i
開始的帶正負號 8 位元整數。
val get_uint16_ne : bytes -> int -> int
get_uint16_ne b i
會取得 b
中從位元組索引值 i
開始的原生端無正負號 16 位元整數。
val get_uint16_be : bytes -> int -> int
get_uint16_be b i
會取得 b
中從位元組索引值 i
開始的大端無正負號 16 位元整數。
val get_uint16_le : bytes -> int -> int
get_uint16_le b i
會取得 b
中從位元組索引值 i
開始的小端無正負號 16 位元整數。
val get_int16_ne : bytes -> int -> int
get_int16_ne b i
會取得 b
中從位元組索引值 i
開始的原生端帶正負號 16 位元整數。
val get_int16_be : bytes -> int -> int
get_int16_be b i
會取得 b
中從位元組索引值 i
開始的大端帶正負號 16 位元整數。
val get_int16_le : bytes -> int -> int
get_int16_le b i
會取得 b
中從位元組索引值 i
開始的小端帶正負號 16 位元整數。
val get_int32_ne : bytes -> int -> int32
get_int32_ne b i
會取得 b
中從位元組索引值 i
開始的原生端 32 位元整數。
val get_int32_be : bytes -> int -> int32
get_int32_be b i
會取得 b
中從位元組索引值 i
開始的大端 32 位元整數。
val get_int32_le : bytes -> int -> int32
get_int32_le b i
會取得 b
中從位元組索引值 i
開始的小端 32 位元整數。
val get_int64_ne : bytes -> int -> int64
get_int64_ne b i
會取得 b
中從位元組索引值 i
開始的原生端 64 位元整數。
val get_int64_be : bytes -> int -> int64
get_int64_be b i
會取得 b
中從位元組索引值 i
開始的大端 64 位元整數。
val get_int64_le : bytes -> int -> int64
get_int64_le b i
會取得 b
中從位元組索引值 i
開始的小端 64 位元整數。
val set_uint8 : bytes -> int -> int -> unit
set_uint8 b i v
將 b
中從位元組索引值 i
開始的無正負號 8 位元整數設定為 v
。
val set_int8 : bytes -> int -> int -> unit
set_int8 b i v
將 b
中從位元組索引值 i
開始的帶正負號 8 位元整數設定為 v
。
val set_uint16_ne : bytes -> int -> int -> unit
set_uint16_ne b i v
將 b
中從位元組索引值 i
開始的原生端無正負號 16 位元整數設定為 v
。
val set_uint16_be : bytes -> int -> int -> unit
set_uint16_be b i v
將 b
中從位元組索引值 i
開始的大端無正負號 16 位元整數設定為 v
。
val set_uint16_le : bytes -> int -> int -> unit
set_uint16_le b i v
將 b
中從位元組索引值 i
開始的小端無正負號 16 位元整數設定為 v
。
val set_int16_ne : bytes -> int -> int -> unit
set_int16_ne b i v
將 b
中從位元組索引值 i
開始的原生端帶正負號 16 位元整數設定為 v
。
val set_int16_be : bytes -> int -> int -> unit
set_int16_be b i v
將 b
中從位元組索引值 i
開始的大端帶正負號 16 位元整數設定為 v
。
val set_int16_le : bytes -> int -> int -> unit
set_int16_le b i v
將 b
中從位元組索引值 i
開始的小端帶正負號 16 位元整數設定為 v
。
val set_int32_ne : bytes -> int -> int32 -> unit
set_int32_ne b i v
將 b
中從位元組索引值 i
開始的原生端 32 位元整數設定為 v
。
val set_int32_be : bytes -> int -> int32 -> unit
set_int32_be b i v
將 b
中從位元組索引值 i
開始的大端 32 位元整數設定為 v
。
val set_int32_le : bytes -> int -> int32 -> unit
set_int32_le b i v
將 b
中從位元組索引值 i
開始的小端 32 位元整數設定為 v
。
val set_int64_ne : bytes -> int -> int64 -> unit
set_int64_ne b i v
將 b
中從位元組索引值 i
開始的原生端 64 位元整數設定為 v
。
val set_int64_be : bytes -> int -> int64 -> unit
set_int64_be b i v
將 b
中從位元組索引值 i
開始的大端 64 位元整數設定為 v
。
val set_int64_le : bytes -> int -> int64 -> unit
set_int64_le b i v
將 b
中從位元組索引值 i
開始的小端 64 位元整數設定為 v
。
從多個網域同時存取位元組序列時,必須小心:存取位元組序列永遠不會使程式崩潰,但不同步的存取可能會產生令人驚訝的(非循序一致的)結果。
每個存取多個位元組的位元組序列操作都不是原子性的。這包括迭代和掃描。
例如,請考慮以下程式
let size = 100_000_000
let b = Bytes.make size ' '
let update b f () =
Bytes.iteri (fun i x -> Bytes.set b i (Char.chr (f (Char.code x)))) b
let d1 = Domain.spawn (update b (fun x -> x + 1))
let d2 = Domain.spawn (update b (fun x -> 2 * x + 1))
let () = Domain.join d1; Domain.join d2
位元組序列 b
可能會包含 '!'
、'A'
、'B'
和 'C'
值的非確定性混合。
在執行此程式碼後,序列 b
的每個位元組不是 '!'
,就是 'A'
、'B'
或 'C'
。如果需要原子性,則使用者必須實作自己的同步處理(例如,使用 Mutex.t
)。
如果兩個網域僅存取位元組序列的不相交部分,則觀察到的行為等同於來自兩個網域的操作的某些循序交錯。
當兩個網域在沒有同步的情況下存取同一個位元組,且至少其中一個存取是寫入時,就會發生資料競爭。在沒有資料競爭的情況下,觀察到的行為等同於來自不同網域的操作的某些循序交錯。
應盡可能透過使用同步來調解對序列元素的存取,以避免資料競爭。
事實上,在出現資料競爭的情況下,程式不會崩潰,但觀察到的行為可能不等同於來自不同網域的操作的任何循序交錯。儘管如此,即使存在資料競爭,讀取操作也會傳回先前對該位置的寫入值。
另一個微妙的重點是,如果資料競爭涉及對同一位置的混合大小寫入和讀取,則網域觀察到這些寫入和讀取的順序是未指定的。例如,以下程式碼會依序將 32 位元整數和 char
寫入至相同的索引
let b = Bytes.make 10 '\000'
let d1 = Domain.spawn (fun () -> Bytes.set_int32_ne b 0 100; b.[0] <- 'd' )
在這種情況下,觀察到 'd' 寫入至 b.0
的網域,並不能保證也會觀察到對索引值 1
、2
或 3
的寫入。