La mayoría de los lenguajes admiten la manipulación de datos como números, átomos, tuplas, listas, registros y/o estructuras, etc. La mayoría de ellos también solo tienen funciones muy básicas para manipular datos binarios. Erlang hace todo lo posible para proporcionar abstracciones útiles cuando se trata de valores binarios con coincidencia de patrones llevada al siguiente nivel. Hace que tratar con datos binarios sin procesar sea divertido y fácil, lo cual era necesario para las aplicaciones de telecomunicaciones para las que fue creado. La manipulación de bits tiene una sintaxis y modismos únicos que pueden parecer un poco extraños al principio, pero si sabes cómo funcionan generalmente los bits y los bytes, esto debería tener sentido.
La sintaxis de bits encierra datos binarios entre << y >>, los divide en segmentos legibles y cada segmento está separado por una coma. Un segmento es una secuencia de bits de un binario (no necesariamente en un límite de bytes, aunque este es el comportamiento predeterminado). Digamos que queremos almacenar un píxel naranja (24 bits). Si alguna vez comprobó los colores en Photoshop o en una hoja de estilos CSS para la web, sabrá que la notación hexadecimal tiene el formato #RRGGBB. Un tinte naranja es #F09A29 en esa notación, que podría ampliarse en Erlang a:
1> Color = 16#F09A29.
15768105
2> Pixel = <<Color:24>>.
<<240,154,41>>
Básicamente dice "Coloque los valores binarios de #F09A29 en 24 bits (rojo en 8 bits, verde en 8 bits y azul también en 8 bits) en la variable Píxel". Posteriormente se puede tomar el valor para escribirlo en un archivo. Esto no parece mucho, pero una vez escrito en un archivo, lo que obtendría al abrirlo en un editor de texto sería un montón de caracteres ilegibles. Cuando vuelva a leer el archivo, Erlang interpretará el binario en el bonito formato <<240,151,41>> nuevamente.
Lo que es más interesante es la capacidad de hacer coincidir patrones con archivos binarios para descomprimir contenido:
3> Pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
4> <<Pix1,Pix2,Pix3,Pix4>> = Pixels.
** exception error: no match of right hand side value <<213,45,132,64,76,32,76,
0,0,234,32,15>>
5> <<Pix1:24, Pix2:24, Pix3:24, Pix4:24>> = Pixels.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
Lo que hicimos en el comando 3 fue declarar lo que serían exactamente 4 píxeles de colores RGB en binario.
En la expresión 4, intentamos descomprimir 4 valores del contenido binario. Lanza una excepción, porque tenemos más de 4 segmentos, ¡de hecho tenemos 12! Entonces, lo que hacemos es decirle a Erlang que cada variable del lado izquierdo contendrá 24 bits de datos. Eso es lo que significa Var:24. Luego podemos tomar el primer píxel y descomprimirlo en valores de un solo color:
6> <<R:8, G:8, B:8>> = <<Pix1:24>>.
<<213,45,132>>
7> R.
213
"Sí, eso es genial. ¿Y si solo quisiera el primer color desde el principio? ¿Tendré que descomprimir todos estos valores todo el tiempo?" ¡Ja! ¡No lo dudes! Erlang introduce más azúcar sintáctico y coincidencia de patrones para ayudarte con:
8> <<R:8, Rest/binary>> = Pixels.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
9> R.
213
Bonito, ¿eh? Esto se debe a que Erlang acepta más de una forma de describir un segmento binario. Todos estos son válidos:
Value
Value:Size
Value/TypeSpecifierList
Value:Size/TypeSpecifierList
donde Tamaño representará bits o bytes (según el Tipo y Unidad a continuación) y TypeSpecifierList representa uno o más de los siguientes:
integer | float | binary | bytes | bitstring | bits | utf8 | utf16 | utf32
Esto representa el tipo de datos binarios utilizados. Tenga en cuenta que "bytes" es la abreviatura de "binario" y "bits" es la abreviatura de "cadena de bits". Cuando no se especifica ningún tipo, Erlang asume un tipo "entero".
Firma
Valores posibles: signed | unsigned
Solo importa para la coincidencia cuando el tipo es un número entero. El valor predeterminado es "sin firmar".
Endianidad
Valores posibles: big | little | native
La endianidad solo importa cuando el tipo es entero, utf16, utf32 o flotante. Esto tiene que ver con cómo el sistema lee los datos binarios. Como ejemplo, el formato de encabezado de imagen BMP mantiene el tamaño de su archivo como un número entero almacenado en 4 bytes. Para un archivo que tiene un tamaño de 72 bytes, un sistema little-endian lo representaría como <<72,0,0,0>> y uno big-endian como <<0,0,0,72>>. Uno se leerá como '72' mientras que el otro se leerá como '1207959552', así que asegúrese de utilizar el endianismo correcto. También existe la opción de usar 'nativo', que elegirá en tiempo de ejecución si la CPU usa little endianness o big endianness de forma nativa. De forma predeterminada, la endianidad está establecida en "grande".
Unidad
unidad unit:Integer
Este es el tamaño de cada segmento, en bits. El rango permitido es 1..256 y está establecido de forma predeterminada en 1 para números enteros, flotantes y cadenas de bits y en 8 para binarios. Los tipos utf8, utf16 y utf32 no requieren que se defina ninguna unidad. La multiplicación de Tamaño por Unidad es igual a la cantidad de bits que tomará el segmento y debe ser divisible por 8. El tamaño de la unidad generalmente se usa para garantizar la alineación de bytes.
TypeSpecifierList se construye separando los atributos por un '-'.
Algunos ejemplos pueden ayudar a digerir las definiciones:
10> <<X1/unsigned>> = <<-44>>.
<<"Ô">>
11> X1.
212
12> <<X2/signed>> = <<-44>>.
<<"Ô">>
13> X2.
-44
14> <<X2/integer-signed-little>> = <<-44>>.
<<"Ô">>
15> X2.
-44
16> <<N:8/unit:1>> = <<72>>.
<<"H">>
17> N.
72
18> <<N/integer>> = <<72>>.
<<"H">>
19> <<Y:4/little-unit:8>> = <<72,0,0,0>>.
<<72,0,0,0>>
20> Y.
72
Puede ver que hay más de una forma de leer, almacenar e interpretar datos binarios. Esto es un poco confuso, pero aún así es mucho más sencillo que utilizar las herramientas habituales que ofrecen la mayoría de los lenguajes.
Las operaciones binarias estándar (desplazamiento de bits a izquierda y derecha, 'y' binario, 'o', 'xor' o 'no') también existen en Erlang. Simplemente use las funciones bsl (Bit Shift Left), bsr (Bit Shift Right), band, bor, bxor y bnot.
2#00100 = 2#00010 bsl 1.
2#00001 = 2#00010 bsr 1.
2#10101 = 2#10001 bor 2#00101.