Bit Expressions com Erlang – P1
Aos poucos venho estudando uma nova linguagem que vem me encantando: Erlang. Ainda estou aprendendo o seu básico, mas já consigo enxergar algumas funcionalidades interessantes, apesar de ainda estar me acostumando com o paradigma funcional.
Erlang proporciona um mecanismo interessante de representação em formato binário, o que se justifica se lembrarmos que a linguagem foi desenvolvida para se trabalhar com telecomunicações (a linguagem foi desenvolvida pela Ericsson. Nas fontes que consultei existem divergências sobre a origem do nome Erlang. Há alguns lugares que dizem ser a contração de ERicsson LANGuage. Outras fontes afirmam ser uma referência ao matemático Agner Krarup Erlang).
Esse mecanismo de representação binária facilitaria a transmissão e manipuação, em nível de bit, dos dados. Por Exemplo, para se representar uma sequência de bytes em Erlang, envolvemos a em << … >>:
<<1, 77, 32, 88 >>.
Isso representa justamente quatro bytes, contendo os valores decimais: 1, 77, 32 e 88. Se quisermos entrar com os bytes em, por exemplo, hexadecimal, devemos explicitamente indicar a base:
<< 16#1, 16#4d, 16#20, 16#58 >>.
Okay, o leitor diria, até agora não vi nada de diferente em relação à uma lista. Por exemplo, eu poderia simplesmente fazer
[ 16#1, 16#4d, 16#20, 16#58 ].
e tudo daria no mesmo.
A primeira diferença em se usar Binary Sintax em Erlang é que este trunca os valores em 255 (ou 8 bits, FFh, como você preferir enxergar), ou seja, qualquer valor que for colocado acima de 255 ocasionará um overflow, justamente como um sistema difgital puro se comporta. Por exemplo:
<< 257 >>.
retorna:
<<1>>
Isso faz muito sentido, pois estamos trabalhando com bytes diretamente.
Bem, essa foi só uma rápida introdução para que possamos chegar onde eu desejo, que é a notação Binary Sintax do Erlang. Essa notação nos permite trabalhar com conjunto de dados estipulando arbitrariamente o tamanho de cada elemento em bits (isso mesmo, bits, não bytes). Imagine por exemplo que eu quero salvar um conjunto de dados que tem três elementos, cada qual pode ocupar uma faixa bem definida de valores. Com base nos valores máximo eu posso estipular a quantidade específica de bits a serem utilizados em sua representação.
Vamos ver isso de uma maneira mais prática:
Proposta – Representar em uma stream de bits o valor de um pixel que contém as três cores (Red, Green e Blue) em níveis que varias:
0~31 para R
0~63 para G
0~31 para B
Aqui usamos um bit a mais para representar a cor verde, pois o olho humano tem uma maior sensibilidade à essa cor. Perceba que estamos falando em quantidade de bits necessários para representar cada cor, e não quantidade de bytes. Se pretendêssemos usar, por exemplo, uma struct C para representar esse conjunto fariamos mais ou menos assim:
struct RGB{
char r;
char g;
char b;
};
Assim eu aloquei 8 bits para cada cor. O que é completamente desnecessário, pois a se olharmos para a faixa que vada valor deve representar, e cpontarmos quantos bits realmente são necessários, teremos:
Cor Faixa Bits
R 0~31 5
G 0~63 6
B 0~31 5
Total 16
Notamos aqui que não são necessários mais que 16 bits para representar as cores RGB no nosso sistema, porém estamos trabalhando com uma struct de três chars, o que consome 24 bits. Uma alternativa é você representar as três cores em um array de char de tamanho 2, porém caberia a você toda a manipulação dos bits, o que daria um pouco de trabalho (mas nada que um programador competente não resolva em uma tarde).
A grande sacada de se usar Bit Syntax em Erlang é que você pode especificar quantos bits cada elemento deve ocupar, e quando você passa um valor que não cabe naquele elemento, ele se comporta da mesma maneira descrita anteriormente: ocorre-se um overflow. Vamos, utilizando bit syntax, passar os valores de RGB e recupera-los na forma de um stream de 16 bits:
R = 15.
G = 52.
B = 19.
<< R:5, G:6, B:5 >>.
note que a saída foi <<126,147>>, um bit stream de exatamente 16 bits. Podemos agora encapsular isso, e cuidar para que ninguém coloque valores fora da faixa especificada:
-module(rgb).
-export([marshall/3, unmarshall/1]).
marshall(R,G,B) ->
if
R > 31 ->
throw({overlimit, R});
G > 63 ->
throw({overlimit, G});
B > 31 ->
throw({overlimit, B});
true -> true
end,
<< R:5, G:6, B:5 >>.
unmarshall(Binary) ->
<< R:5, G:6, B:5 >> = Binary,
{R,G,B}.
Salvando esse código em um arquivo de mesmo nome de seu módulo (rgb.erl), vamos entrar no console Erlang e compila-lo:
> c(rgb).
{ok,rgb}
Pronto, agora estamos aptos a usar tanto as funções marshall e unmarshall que empacotam nossos valores RGB em stream de bits e desempacotam esse stream, devolvendo uma tupla com esses valores.
Exemplo:
rgb:unmarshall(rgb:marshall(8,19,30)).
Esse é um exemplo simplório da utilização de bit syntax em erlang, mas já dá pra ter uma pequena noção do seu poder.A idéia é explorar um pouco mais com coisas mais complexas, que provavelmente nos daria muito trabalho implementando em linguagens como C.
3 comentários