Translate

quinta-feira, 27 de setembro de 2012

Java - Bufferizar dados

Após algumas vezes programar rotinas para capturar bytes vindos de conexão socket ou porta serial, armazenar estes bytes e em seguida processá-los para extrair a informação original, percebi logo a complexidade disso.

Qualquer um que já tenha programado um client ou server socket em Java, C++ e C ANSI sabe o que digo, pois utilizar um byte array para armazenar (bufferizar) dados recebidos antes de enquadrar uma informação significativa é uma maneira arcaica e dispensiosa.

Certo dia, resolvi simplificar esta situação com a criação de um mecanismo chamado WTByteBuffer bastante simples para prover apenas dois métodos, um método de escrita e outro de leitura a partir do ByteBuffer do Java NIO.

O método de escrita armazena os bytes lidos e o método de leitura consome os bytes. Desta forma, o programador Java será poupado de todo o trabalho discutido anteriormente para bufferizar e consumir dados recebidos.

WTByteBuffer - Classe para bufferizar


import java.nio.ByteBuffer;

public class WTByteBuffer {

    public static final int DEFAULT_MAX_SIZE = 1024 * 65;

    private volatile ByteBuffer buffer;
    private int size;
    private int bufferMaxSize;

    public int getSize() {
        return this.size;
    }

    public ByteBuffer getBuffer() {
        return this.buffer;
    }
   
   
    public WTByteBuffer() {
        this(WTByteBuffer.DEFAULT_MAX_SIZE);
    }

    public WTByteBuffer(int bufferMaxSize) {
        this.bufferMaxSize = bufferMaxSize;
        this.buffer = ByteBuffer.allocate(bufferMaxSize);
        buffer.position(0);
        this.size = 0;
       
    }

    public synchronized void write(byte[] bytes) {

        if (this.buffer == null)
            System.out.println("Buffer nulo : W");
       
        this.buffer.put(bytes);
        this.size = this.buffer.position();
       
    }

    public synchronized int read(byte[] bytes) {

        int target, position, newPosition, capacity;
        byte[] completeBuffer = null;
        byte[] remainingBuffer = null;

        if (bytes == null || bytes.length == 0) {
            return 0;
        }

        if (this.buffer == null)
            System.out.println("Buffer nulo : R");

        target = bytes.length;
        position = this.buffer.position();
        capacity = this.buffer.capacity();

        if (target > position) {
            target = position;
        }

        newPosition = position - target;
        completeBuffer = new byte[position];
        remainingBuffer = new byte[newPosition];
       
        this.buffer.flip();
        this.buffer.get(completeBuffer, 0, position);
        System.arraycopy(completeBuffer, 0, bytes, 0, target);

        this.buffer = null;
        this.buffer = ByteBuffer.allocate(capacity);
       
        if (newPosition > 0) {
            System.arraycopy(completeBuffer, target, remainingBuffer, 0, newPosition);
            this.buffer.put(remainingBuffer);
        }
   
        this.size = this.buffer.position();
   
        return target;

    }
   
    public synchronized void clear() {
       
        if (this.buffer != null) {
            this.buffer.clear();
            this.buffer = null;
        }
        this.buffer = ByteBuffer.allocate(bufferMaxSize);
        buffer.position(0);
        this.size = 0;

    }   
 

}

Testando um buffer

Este exemplo cria uma instância de WTByteBuffer com 100 bytes de capacidade, escreve a série de bytes de um texto qualquer e recupera de um em um byte. Depois repete o teste recuperando de dois em dois bytes do texto bufferizado.

        WTByteBuffer buffer = new WTByteBuffer(100);
        String text = "ABXYZ-0123456-abc";
       
        byte[] read = new byte[2];
        byte[] serialized = text.getBytes();
        int size = 0;

        System.out.println("-----------------------------------------");

        buffer.write(serialized);

        System.out.format("Texto guardado em buffer: %s\n", text);
       
        System.out.println("Recupera texto de 1 em 1 byte...");
       
        while (buffer.getSize() > 0) {

            read = new byte[1];

            size = buffer.read(read);

            System.out.format("1 byte lido: %s\n", new String(read));

        }

        System.out.format("Bytes guardados: %d\n", buffer.getSize());

        System.out.println("-----------------------------------------");

        buffer.write(serialized);

        System.out.format("Texto guardado em buffer: %s\n", text);
       
        System.out.println("Recupera texto de 2 em 2 byte...");
       
        while (buffer.getSize() > 0) {

            read = new byte[2];

            size = buffer.read(read);

            System.out.format("%d byte(s) lido(s): %s\n", size, new String(read));

        }
       
        buffer = null;



Numa situação real a capacidade deste buffer deverá ser maior do que 100. Caso desejar aumentar o tamanho, faça isso ao construir o WTByteBuffer ou então utilize o construtor padrão para iniciar com o tamanho padrão de reserva máxima.

A classe abordada ainda provê métodos adicionais para controle e acesso ao buffer central.

Boa sorte.