バイナリデータの扱いは、少し厄介に感じることがあります。そこでGleamには、バイナリデータを構築したりパターンマッチしたりするためのBitArray型と、それを支える構文が用意されています。
ビット配列のリテラルは、<<>>という構文で定義します。ビット配列のリテラルを定義するときは、セグメントに分けて書きます。各セグメントは値と注釈からなり、この2つは:で区切ります。注釈は、その値をエンコードするのに何ビット使うかを指定するもので、丸ごと省略することもできます。省略した場合は、既定で8ビットの整数値になります。
// This defines a bit array with three segments of a single bit each
<<0:1, 1:1, 0:1>>
// This defines a bit array with three segments of 8 bits each
<<0, 1, 0>>
型を:1と指定するのは、:size(1)と書く省略記法です。ビットサイズが変数から得られる場合は、長い方の構文を使う必要があります。
let segment_size = 1
<<0:size(segment_size), 1:size(segment_size), 0:size(segment_size)>>
2進数の整数リテラルを書くときは、リテラルの前に0bを付けると、2進数表記で直接書くことができます。ただし、テストや自分のプログラムの中で出力されるときには、10進数として表示されることに注意してください。
<<0b1011:4>> == <<11:4>>
// -> True
セグメントの値がそのセグメントの型の容量を超えると、左側から切り捨てられます。
<<0b1011:3>> == <<0b0011:3>>
// -> True
ビット配列の構文を使うと、既存のビット配列の先頭にも末尾にも追加できます。既存のビット配列には、:bits注釈を使う必要があります。
let value = <<0b110:3, 0b001:3>>
let new_value = <<0b011:3, value:bits, 0b000:3>>
// -> <<120, 8:size(4)>>
この構文を使えば、変数に入ったビット配列同士を連結できます。サイズが変数のビット配列を2つ連結するときは、:bits注釈を使う必要があります。
let first = <<0b110:3>>
let second = <<0b001:3>>
let concatenated = <<first:bits, second:bits>>
// -> <<49:size(6)>>
パターンマッチを使って、ビット配列から値を取り出すこともできます。取り出したい断片ごとに、そのビット数をあらかじめ知っておく必要がありますが、例外が1つあります。:bits注釈を使えば、サイズがわからないビット配列をパターンマッチできますが、これが使えるのは最後の断片だけです。
let assert <<value:4, rest:bits>> = <<0b01101001:8>>
value == 0b0110
// -> true
ビット配列は、作成するときに使った形式とは違う形式で表示されることがあります。これはビット配列を学ぶときに、よく混乱の原因になります。
既定では、ビット配列は8ビット(1バイト)の断片として表示されます。作成するときに別のサイズの断片を使っていても変わりません。
<<2011:11>>
// -> <<251, 3:size(3)>>
表示可能なUTF-8エンコード文字列を表すビット配列を作成した場合、Erlangをターゲットにしているときはecho keywordを使うと、文字列として表示されることがあります。これは、Gleamが内部的に文字列を表現する方法の実装上の都合によるものです。
<<>>
// -> ""
<<65, 66, 67>>
// -> "ABC"