Nota: Em C isso é relativamente fácil de fazer por causa de sua vocação original baseada na manipulação de memória, por isso em C tem a função memcpy designada para isso é extremamente versátil.
Normalmente para se fazer isso o programador já converteu números decimais para inteiros: short, int, long ou BigDecimal com uma operação de multiplicação.
Alerta: A multiplicação convencional com operador "*" utilizado para ponto flutuante pode resultar em perda de precisão não só em Java, mas em qualquer linguagem sobre a plataforma Intel por constatação ao deparar com este mesmo problema em C ANSI, C++ e depois em Java sempre na plataforma Intel. Logo mais falarei de como resolver o problema num outro artigo. É importante deixar claro que a divisão com o operador "/" funciona perfeitamente.
A classe a seguir é uma síntese prática do memcpy do C, porém com algumas melhorias porque ela oferece:
- tamanhos de cópias pré-estabelecidas conforme as práticas mais comuns no dia-a-dia do programador;
- ainda para cada função já possui assinaturas capazes de lidar com os tipos inteiros mais comuns e;
- permite a reversão de bytes para casos específicos onde o destino I/O possui um ordem binária reversa a sua.
Vamos para a prática.
A classe MemCopy
import java.math.BigInteger;
import java.util.Arrays;
public class MemCopy {
public static byte copy8(byte[] value) {
byte result = 0;
if (value != null && value.length > 0) {
result = value[0];
}
return result;
}
public static byte copy8(long value) {
return (byte) (value & 0x00000000000000FF);
}
public static byte copy8(int value) {
return (byte) (value & 0x000000FF);
}
public static byte copy8(short value) {
return (byte) (value & 0x00FF);
}
public static byte[] copy16(byte[] value, boolean reverseBytes) {
byte [] result = new byte[2];
if (value == null || value.length < 2) {
return null;
}
if (reverseBytes) {
result[0] = value[0];
result[1] = value[1];
} else {
result[0] = value[1];
result[1] = value[0];
}
return result;
}
public static byte[] copy16(long value, boolean reverseBytes) {
return copy16((short) (value & 0x000000000000FFFFL), reverseBytes);
}
public static byte[] copy16(int value, boolean reverseBytes) {
return copy16((short) (value & 0x0000FFFF), reverseBytes);
}
public static byte[] copy16(short value, boolean reverseBytes) {
byte [] result = new byte[2];
byte [] aux = new byte[2];
aux[0] = (byte) (value & 0x00FF);
aux[1] = (byte) ((value >> 8) & 0x00FF) ;
if (reverseBytes) {
result[1] = aux[0];
result[0] = aux[1];
} else {
result = aux;
}
return result;
}
public static byte[] copy24(byte[] value, boolean reverseBytes) {
byte [] result = new byte[3];
if (value == null || value.length < 3) {
return null;
}
if (reverseBytes) {
result[0] = value[2];
result[1] = value[1];
result[2] = value[0];
} else {
result[0] = value[0];
result[1] = value[1];
result[2] = value[2];
}
return result;
}
public static byte[] copy24(long value, boolean reverseBytes) {
byte [] result = new byte[3];
byte [] aux = new byte[3];
aux[0] = (byte) (value & 0x000000FF);
aux[1] = (byte) ((value >> 8) & 0x000000FF) ;
aux[2] = (byte) ((value >> 16) & 0x000000FF) ;
if (reverseBytes) {
result[1] = aux[0];
result[0] = aux[1];
} else {
result = aux;
}
return result;
}
public static byte[] copy32(byte[] value, boolean reverseBytes) {
byte [] result = new byte[4];
if (value == null || value.length < 4) {
return null;
}
if (reverseBytes) {
result[0] = value[3];
result[1] = value[2];
result[2] = value[1];
result[3] = value[0];
} else {
result[0] = value[0];
result[1] = value[1];
result[2] = value[2];
result[3] = value[3];
}
return result;
}
public static byte[] copy32(long value, boolean reverseBytes) {
return copy32((int) (value & 0xFFFFFFFF), reverseBytes);
}
public static byte[] copy32(int value, boolean reverseBytes) {
byte [] result = new byte[4];
byte [] aux = new byte[4];
aux[0] = (byte) (value & 0x000000FF);
aux[1] = (byte) ((value >> 8) & 0x000000FF) ;
aux[2] = (byte) ((value >> 16) & 0x000000FF);
aux[3] = (byte) ((value >> 24) & 0x000000FF);
if (reverseBytes) {
result[3] = aux[0];
result[2] = aux[1];
result[1] = aux[2];
result[0] = aux[3];
} else {
result = aux;
}
return result;
}
public static byte[] copy64(byte[] value, boolean reverseBytes) {
byte [] result = new byte[8];
if (value == null || value.length < 8) {
return null;
}
if (reverseBytes) {
result[0] = value[7];
result[1] = value[6];
result[2] = value[5];
result[3] = value[4];
result[4] = value[3];
result[5] = value[2];
result[6] = value[1];
result[7] = value[0];
} else {
result[0] = value[0];
result[1] = value[1];
result[2] = value[2];
result[3] = value[3];
result[4] = value[4];
result[5] = value[5];
result[6] = value[6];
result[7] = value[7];
}
return result;
}
public static byte[] copy64(long value, boolean reverseBytes) {
byte [] result = new byte[8];
byte [] aux = new byte[8];
aux[0] = (byte) (value & 0x00000000000000FFL);
aux[1] = (byte) ((value >> 8) & 0x00000000000000FFL);
aux[2] = (byte) ((value >> 16) & 0x00000000000000FFL);
aux[3] = (byte) ((value >> 24) & 0x00000000000000FFL);
aux[4] = (byte) ((value >> 32) & 0x00000000000000FFL);
aux[5] = (byte) ((value >> 40) & 0x00000000000000FFL);
aux[6] = (byte) ((value >> 48) & 0x00000000000000FFL);
aux[7] = (byte) ((value >> 56) & 0x00000000000000FFL);
if (reverseBytes) {
result[7] = aux[0];
result[6] = aux[1];
result[5] = aux[2];
result[4] = aux[3];
result[3] = aux[4];
result[2] = aux[5];
result[1] = aux[6];
result[0] = aux[7];
} else {
result = aux;
}
return result;
}
public static byte[] copy64(BigInteger value, boolean reverseBytes) {
if (value == null)
return null;
byte [] aux = value.toByteArray();
if (aux == null || aux.length == 0)
return null;
byte [] result = new byte[8];
Arrays.fill(result, (byte) 0);
if (aux.length < 8) {
System.arraycopy(aux, 0, result, result.length-aux.length, aux.length);
} else {
System.arraycopy(aux, 0, result, 0, result.length);
}
aux = new byte[8];
System.arraycopy(result, 0, aux, 0, aux.length);
if (!reverseBytes) {
result[7] = aux[0];
result[6] = aux[1];
result[5] = aux[2];
result[4] = aux[3];
result[3] = aux[4];
result[2] = aux[5];
result[1] = aux[6];
result[0] = aux[7];
} else {
result = aux;
}
return result;
}
}
Teste da classe
O teste da classe requer as funções de conversão de array de bytes para BigInteger, porque é o maior tipo entre os testes e isso confere a capacidade de realizar os testes rápidos com todos os tamanhos de cópias, e ainda requer a função para converter array de bytes para hexadecimal para comprovar os testes.
import java.math.BigInteger;
public class TestMemCopy {
public static BigInteger byteArrayToBigInteger(byte[] b, boolean reverseBytes) {
if (b == null || b.length == 0) {
return null;
}
byte aux[] = new byte[b.length];
if (!reverseBytes) {
for (int i = 0; i < b.length; i++) {
aux[aux.length-1-i] = b[i];
}
} else {
System.arraycopy(b, 0, aux, 0, b.length);
}
return new BigInteger(aux);
}
public static String byteArrayToHex(byte[] bytes, boolean useSpace) {
StringBuffer retorno = new StringBuffer();;
if (bytes==null || bytes.length==0) {
return retorno.toString();
}
// Com espaço
if (useSpace) {
for (int i=0; i<bytes.length; i++) {
byte valor = bytes[i];
int d1 = valor & 0xF;
d1 += (d1 < 10) ? 48 : 55;
int d2 = (valor & 0xF0) >> 4;
d2 += (d2 < 10) ? 48 : 55;
retorno.append((char) d2);
retorno.append((char) d1);
retorno.append(" ");
}
// Sem espaço
} else {
for (int i=0; i<bytes.length; i++) {
byte valor = bytes[i];
int d1 = valor & 0xF;
d1 += (d1 < 10) ? 48 : 55;
int d2 = (valor & 0xF0) >> 4;
d2 += (d2 < 10) ? 48 : 55;
retorno.append((char) d2);
retorno.append((char) d1);
}
}
return retorno.toString();
}
public static void main(String[] args) {
/*******************************
* Copiando 1 byte de short
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = {0};
short decoded = 0;
// Teste short
short input = (short) 102;
encoded[0] = MemCopy.copy8(input);
decoded = (short) (encoded[0] & 0xFF);
System.out.println("Copiando 1 byte de short");
System.out.println(String.format("Original (short): %d, Encoding (Hex): %s, Decoding (short): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
/*******************************
* Copiando 2 bytes de short - Sem reversão de bytes
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = null;
BigInteger decoded = null;
// Teste short
short input = (short) 32767;
encoded = MemCopy.copy16(input, false);
decoded = byteArrayToBigInteger(encoded, false);
System.out.println("Copiando 2 bytes de short - Sem reversão de bytes");
System.out.println(String.format("Original (short): %d, Encoding (Hex): %s, Decoding (BigInteger): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
/*******************************
* Copiando 2 bytes de short - Com reversão de bytes
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = null;
BigInteger decoded = null;
// Teste short
short input = (short) 23761;
encoded = MemCopy.copy16(input, true);
decoded = byteArrayToBigInteger(encoded, true);
System.out.println("Copiando 2 bytes de short - Com reversão de bytes");
System.out.println(String.format("Original (short): %d, Encoding (Hex): %s, Decoding (BigInteger): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
/*******************************
* Copiando 4 bytes de int - Sem reversão de bytes
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = {0, 0};
BigInteger decoded = null;
// Teste short
int input = 2147483647;
encoded = MemCopy.copy32(input, false);
decoded = byteArrayToBigInteger(encoded, false);
System.out.println("Copiando 4 bytes de int - Sem reversão de bytes");
System.out.println(String.format("Original (int): %d, Encoding (Hex): %s, Decoding (BigInteger): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
/*******************************
* Copiando 4 bytes de int - Com reversão de bytes
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = null;
BigInteger decoded = null;
// Teste short
int input = 1157483800;
encoded = MemCopy.copy32(input, true);
decoded = byteArrayToBigInteger(encoded, true);
System.out.println("Copiando 4 bytes de int - Com reversão de bytes");
System.out.println(String.format("Original (int): %d, Encoding (Hex): %s, Decoding (BigInteger): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
/*******************************
* Copiando 8 bytes de long - Sem reversão de bytes
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = {0, 0};
BigInteger decoded = null;
// Teste short
long input = 922337203685478L;
encoded = MemCopy.copy64(input, false);
decoded = byteArrayToBigInteger(encoded, false);
System.out.println("Copiando 8 bytes de long - Sem reversão de bytes");
System.out.println(String.format("Original (long): %d, Encoding (Hex): %s, Decoding (BigInteger): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
/*******************************
* Copiando 8 bytes de long - Com reversão de bytes
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = null;
BigInteger decoded = null;
// Teste short
long input = 722337203685478L;
encoded = MemCopy.copy64(input, true);
decoded = byteArrayToBigInteger(encoded, true);
System.out.println("Copiando 8 bytes de long - Com reversão de bytes");
System.out.println(String.format("Original (long): %d, Encoding (Hex): %s, Decoding (BigInteger): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
/*******************************
* Copiando 8 bytes de long - Com reversão de bytes
******************************/
{
// Variáveis para realizar os testes
byte encoded[] = null;
BigInteger decoded = null;
// Teste short
long input = 722337203685478L;
encoded = MemCopy.copy64(input, true);
decoded = byteArrayToBigInteger(encoded, true);
System.out.println("Copiando 8 bytes de long - Com reversão de bytes");
System.out.println(String.format("Original (long): %d, Encoding (Hex): %s, Decoding (BigInteger): %d\n", input, byteArrayToHex(encoded, true) ,decoded));
}
}
}
Resultado
Copiando 1 byte de short
Original (short): 102, Encoding (Hex): 66 , Decoding (short): 102
Copiando 2 bytes de short - Sem reversão de bytes
Original (short): 32767, Encoding (Hex): FF 7F , Decoding (BigInteger): 32767
Copiando 2 bytes de short - Com reversão de bytes
Original (short): 23761, Encoding (Hex): 5C D1 , Decoding (BigInteger): 23761
Copiando 4 bytes de int - Sem reversão de bytes
Original (int): 2147483647, Encoding (Hex): FF FF FF 7F , Decoding (BigInteger): 2147483647
Copiando 4 bytes de int - Com reversão de bytes
Original (int): 1157483800, Encoding (Hex): 44 FD CD 18 , Decoding (BigInteger): 1157483800
Copiando 8 bytes de long - Sem reversão de bytes
Original (long): 922337203685478, Encoding (Hex): 66 88 63 5D DC 46 03 00 , Decoding (BigInteger): 922337203685478
Copiando 8 bytes de long - Com reversão de bytes
Original (long): 722337203685478, Encoding (Hex): 00 02 90 F6 3C 6F 08 66 , Decoding (BigInteger): 722337203685478
Conforme descrito no alerta, no próximo artigo vamos discutir a respeito da multiplicação de flutuantes e como evitar de vez a perda de precisão nesta operação.
Até mais.
Nenhum comentário:
Postar um comentário