LittleEndian X BigEndian – Formato de ordenação dos bytes(endianness)

Os formatos de ordenação dos bytes(endianess), estão relacionados em como é feita a escrita e a leitura dos bytes nos registradores da CPU.

Basicamente no formato “Little Endian” os bytes de menor ordem são armazenados na memória nos menores endereços, ou seja, escritos da esquerda para à direita.

Por exemplo, o número 1 no formato Little Endian ficaria assim na memória:

endereço de memória 0x01  =  1

endereço de memória 0x02  =  0

endereço de memória 0x03  =  0

endereço de memória 0x04  =  0

E o número 105001593 no formato Little Endian ficaria assim na memória:

endereço de memória 0x01  =  6

endereço de memória 0x02  =  66

endereço de memória 0x03  =  50

endereço de memória 0x04  =  121

Já no formato “Big Endian” onde os bytes de menor ordem são armazenados na memória nos maiores endereços, os bytes são escritos da direita para à esquerda.

O número 1 no formato BigEndian ficaria assim na memória.

endereço de memória 0x01  =  0

endereço de memória 0x02  =  0

endereço de memória 0x03  =  0

endereço de memória 0x04  =  1

E  o número 105001593 no formato BigEndian ficaria assim na memória:

endereço de memória 0x01  =  121

endereço de memória 0x02  =  50

endereço de memória 0x03  =  66

endereço de memória 0x04  =  6

É dizer “Seis” ou “Meia dúzia”. Representa a mesma informação, mas de formas diferentes. O Java por exemplo, trabalha em Big Endian e o .NET em Litlle Endian.

Isso influenciará no seu desenvolvimento para os formatos short, int, long, mas nunca pra byte(óbvio) e também array de bytes.

Segue abaixo um código para tentar exemplificar a teoria, que é um pouco baixo nível(não confundir com baixo calão, ahahaah).

  1. Primeiro inicializei a classe ByteBuffer com o seu tamanho máximo, como um array de bytes(byte[]);
  2. Configurei o ByteOrder para BigEndian, o que é desnecessário aqui pois esse é o padrão;
  3. Depois fui criando blocos de código onde fui reiniciando um ByteBuffer e adicionando cada tipo de variável(int, short, long), e a cada bloco fui exibindo como estava ficando o array. Fui criando vários ByteBuffer, para alocá-los com o tamanho exato, para que o print do array ficasse mais claro(bonitinho);
  4. Isto posto, configurei um novo ByteBuffer para LittleEndian e repeti os mesmos passos para os mesmos valores.
public class BigLittleEndianTest {
	
	public static void main(String[] args) {
		BufferLog log = new BufferLog();
		
		ByteBuffer bb = ByteBuffer.allocate(4);
		bb.order(ByteOrder.BIG_ENDIAN);
		System.out.println("Escrevendo no formato BigEndian(Java)");
		System.out.println("");
		System.out.println("Escrevendo um int " + 100);
		bb.putInt(100);
		log.printBuffer(bb.array());		
		
		System.out.println(""); 
		System.out.println("Adicionando um short " + 10);
		
		bb = ByteBuffer.allocate(2);
		bb.putShort((short)10);
		log.printBuffer(bb.array());
		
		System.out.println("");
		System.out.println("Adicionando um long " + 10000000);
		
		bb = ByteBuffer.allocate(8);
		bb.putLong(10000000l);
		log.printBuffer(bb.array());
		
		System.out.println("");
		
		bb = ByteBuffer.allocate(4);
		bb.order(ByteOrder.LITTLE_ENDIAN);
		System.out.println("Escrevendo no formato LittleEndian(.Net)");
		System.out.println("");
		System.out.println("Escrevendo um int " + 100);		
		bb.putInt(100);
		log.printBuffer(bb.array());
		
		System.out.println("");
		
		bb = ByteBuffer.allocate(2);
		bb.order(ByteOrder.LITTLE_ENDIAN);
		System.out.println("Adicionando um short " + 10);
		bb.putShort((short)10);
		log.printBuffer(bb.array());
		
		System.out.println("");
		
		bb = ByteBuffer.allocate(8);
		bb.order(ByteOrder.LITTLE_ENDIAN);
		System.out.println("Adicionando um long " + 10000000);
		bb.putLong(10000000l);
		log.printBuffer(bb.array());

	}
}

Você também precisará desta classe, a BufferLog, onde coloquei o código responsável pela exibição dos arrays na tela de forma bem didática, bem para mim pareceu didático. Precisei de um método leftStr, para fazer alinhamento à direita de um texto, e do método printBuffer, para exibir o array no formato decimal unsigned, e em hexadecimal.

public class BufferLog {	
	
	public void printBuffer(byte[] buffer){
		StringBuilder sbDecimal = new StringBuilder();
		StringBuilder sbHexa    = new StringBuilder();
		
		sbDecimal.append("[");
		sbHexa.   append("[");
		
		for(int i = 0; i < buffer.length; i++){
			if(buffer[i] < 0){
				sbDecimal.append(leftStr(""+ (buffer[i] + 256), 3));
				sbHexa.   append(leftStr(Integer.toHexString(buffer[i] + 256).toUpperCase(), 3));
			}
			else{
				sbDecimal.append(leftStr(""+ buffer[i], 3));
				sbHexa.   append(leftStr(Integer.toHexString(buffer[i]).toUpperCase(), 3));
			}
			
			if(i == (buffer.length - 1)){
				sbDecimal.append("]");
				sbHexa.	  append("]");
			}
			else{
				sbDecimal.append(", ");
				sbHexa.append(", ");
			}
		}
		
		System.out.println("Buffer em Decimal.....: " + sbDecimal.toString());
		System.out.println("Buffer em Hexadecimal.: " + sbHexa.   toString());
	}
	
	public String leftStr(String s, int size){
		return leftStr(s, size, ' ');
	}
	
	public String leftStr(String s, int size, char x){
		StringBuilder sb = new StringBuilder();
		
		for(int i = 0; i < size - s.length(); i++){
			 sb.append(x);
		}
		
		sb.append(s);
		
		return sb.toString();
	}	

}

Este código deve produzir este resultado:


Escrevendo no formato BigEndian(Java)

Escrevendo um int 100
Buffer em Decimal.....: [ 0, 0, 0, 100]
Buffer em Hexadecimal.: [ 0, 0, 0, 64]

Adicionando um short 10
Buffer em Decimal.....: [ 0, 10]
Buffer em Hexadecimal.: [ 0, A]

Adicionando um long 10000000
Buffer em Decimal.....: [ 0, 0, 0, 0, 0, 152, 150, 128]
Buffer em Hexadecimal.: [ 0, 0, 0, 0, 0, 98, 96, 80]

Escrevendo no formato LittleEndian(.Net)

Escrevendo um int 100
Buffer em Decimal.....: [100, 0, 0, 0]
Buffer em Hexadecimal.: [ 64, 0, 0, 0]

Adicionando um short 10
Buffer em Decimal.....: [ 10, 0]
Buffer em Hexadecimal.: [ A, 0]

Adicionando um long 10000000
Buffer em Decimal.....: [128, 150, 152, 0, 0, 0, 0, 0]
Buffer em Hexadecimal.: [ 80, 96, 98, 0, 0, 0, 0, 0]

“Mas professor, pra que eu preciso saber isso? O sistema operacional não trata isso automaticamente?”

“Calma lá, Joaozinho”.

Os Sistemas Operacionais trabalham no formato específico do hardware, as Máquinas Virtuais do Java ou do .NET, colocam uma camada onde convertem para o seu formato preferido. O Java trabalha em Big Endian e o .NET em Litlle Endian.

“Então professor, você me deu razão!”

“Calma lá, de novo, cara-pálida!”

Em um sistema básico, onde só trocamos informação com nosso próprio sistema na mesma linguagem e etc, vai numa boa. O grande problema é quando temos que integrar com outros sistemas, de outras empresa, de outras linguagens….

Nessas horas começam ocorrer os erros “malucos”. O programador do outro sistema diz que enviou um valor para seu Serviço Web, ou Socket puro, e você vê que está recebendo algo “totalmente diferente”.

“Mas professor, então quem poderá nos defender?”

“Eu!!!!”

Neste caso você deverá converter as variáveis do tipo short, int, long, para trabalharem no mesmo formato “endian type”.

Criei duas wrappers class(Classe encapsuladora?!?!), para trabalhar com o ByteBuffer. Aconselho que você até utilize a classe ByteBuffer diretamente. Mas as minhas classes são uma simplificação da ByteBuffer, que inclusive esconde muitas de suas funcionalidades, e também tem forma de exibir e capturar melhor os buffers. Bem,  você pode se inspirar nesses códigos, ou utilizá-los diretamente.

Esses códigos possuem nenhum direito reservado, ou seja “de grátis”(hahahahaha).

Wrapper para Escrita:

public class ByteBufferWritter {
	
	private ByteBuffer byteBuffer;
	
	public ByteBufferWritter(ByteOrder byteOrder, int bufferSize){
		byteBuffer = ByteBuffer.allocate(bufferSize);
		byteBuffer.order(byteOrder);
	}
	
	public void writeInt(int value){
		byteBuffer.putInt(value);
	}
	
	public void writeLong(long value){
		byteBuffer.putLong(value);
	}
	
	public void writeShort(short value){
		byteBuffer.putShort(value);
	}
	
	public void writeByteArray(byte[] value){
		byteBuffer.put(value);
	}
	
	public byte[] getAllBuffer(){
		int oldPosition = byteBuffer.position();
		byteBuffer.rewind();		
		
		byte[] bufferTemp = new byte[oldPosition];
		for(int i = 0; i < bufferTemp.length; i ++){
			bufferTemp[i] = byteBuffer.get();
		}					
		byteBuffer.position(oldPosition);
		return bufferTemp;
	}
	
	public static void main(String[] args) {
		BufferLog log = new BufferLog();

		ByteBufferWritter writer = new ByteBufferWritter(ByteOrder.BIG_ENDIAN, 20);
		writer.writeInt(1);
		log.printBuffer(writer.getAllBuffer());
		writer.writeShort((short) 255);
		log.printBuffer(writer.getAllBuffer());
		writer.writeLong(9999999999l);
		log.printBuffer(writer.getAllBuffer());
		writer.writeByteArray("AAAAAA".getBytes());
		log.printBuffer(writer.getAllBuffer());
	}
}

A execução do main deve produzir isso:

Buffer em Decimal.....: [  0,   0,   0,   1]
Buffer em Hexadecimal.: [  0,   0,   0,   1]
Buffer em Decimal.....: [  0,   0,   0,   1,   0, 255]
Buffer em Hexadecimal.: [  0,   0,   0,   1,   0,  FF]
Buffer em Decimal.....: [  0,   0,   0,   1,   0, 255,   0,   0,   0,   2,  84,  11, 227, 255]
Buffer em Hexadecimal.: [  0,   0,   0,   1,   0,  FF,   0,   0,   0,   2,  54,   B,  E3,  FF]
Buffer em Decimal.....: [  0,   0,   0,   1,   0, 255,   0,   0,   0,   2,  84,  11, 227, 255,  65,  65,  65,  65,  65,  65]
Buffer em Hexadecimal.: [  0,   0,   0,   1,   0,  FF,   0,   0,   0,   2,  54,   B,  E3,  FF,  41,  41,  41,  41,  41,  41]

Wrapper para Leitura:

public class ByteBufferReader {
	
	private ByteBuffer byteBuffer;
	
	public ByteBufferReader(ByteOrder byteOrder, byte[] buffer){
		byteBuffer = ByteBuffer.allocate(buffer.length);
		byteBuffer.order(byteOrder);
		byteBuffer.put(buffer);
		byteBuffer.rewind();
	}
	
	public byte[] getIntBuffer(){
		ByteBuffer bufferTemp = ByteBuffer.allocate(4);
		bufferTemp.order(byteBuffer.order());
		bufferTemp.putInt(byteBuffer.getInt());
		return bufferTemp.array();
	}
	
	public byte[] getShortBuffer(){
		ByteBuffer bufferTemp = ByteBuffer.allocate(2);
		bufferTemp.order(byteBuffer.order());
		bufferTemp.putShort(byteBuffer.getShort());
		return bufferTemp.array();
	}
	
	public byte[] getLongBuffer(){
		ByteBuffer bufferTemp = ByteBuffer.allocate(8);
		bufferTemp.order(byteBuffer.order());
		bufferTemp.putLong(byteBuffer.getLong());
		return bufferTemp.array();
	}
	
	public byte[] getBuffer(int size){
		byte[] bufferTemp = new byte[size];
		byteBuffer.get(bufferTemp);
		return bufferTemp;
	}
	
	public byte[] getAllBuffer(){
		byteBuffer.rewind();
		return byteBuffer.array();
	}
	
	public static void main(String[] args) {
		BufferLog log = new BufferLog();
		
		ByteBuffer bb = ByteBuffer.allocate(20);
		bb.putInt(1);
		bb.putShort((short)255);
		bb.putLong(9999999999l);
		bb.put("AAAAAA".getBytes());
		
		ByteBufferReader reader = new ByteBufferReader(ByteOrder.BIG_ENDIAN, bb.array());
		log.printBuffer(reader.getIntBuffer());
		log.printBuffer(reader.getShortBuffer());
		log.printBuffer(reader.getLongBuffer());
		System.out.println(new String(reader.getBuffer(6)));
		
	}

E sua provável saída:

Buffer em Decimal.....: [  0,   0,   0,   1]
Buffer em Hexadecimal.: [  0,   0,   0,   1]
Buffer em Decimal.....: [  0, 255]
Buffer em Hexadecimal.: [  0,  FF]
Buffer em Decimal.....: [  0,   0,   0,   2,  84,  11, 227, 255]
Buffer em Hexadecimal.: [  0,   0,   0,   2,  54,   B,  E3,  FF]
AAAAAA

Esse pode ser um assunto que para os novatos pode parecer complicado, mas não é. O que faz parecer mais complicado é porque a maioria dos programadores e nunca precisam utilizar dados em baixo nível. Mas é tudo questão de prática. Espero poder ajudar alguém com isso, senão pelo menos a mim eu sei que ajudou!

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s