RawDataIo.java

package com.github.junrar.io;

import javax.crypto.Cipher;
import java.io.IOException;
import java.util.LinkedList;

public class RawDataIo implements SeekableReadOnlyByteChannel {
    private Cipher cipher = null;
    private final SeekableReadOnlyByteChannel underlyingByteChannel;
    private boolean isEncrypted = false;
    private final LinkedList<Byte> dataPool = new LinkedList<>();
    private final byte[] reused = new byte[1];

    public RawDataIo(SeekableReadOnlyByteChannel channel) {
        this.underlyingByteChannel = channel;
    }

    public Cipher getCipher() {
        return cipher;
    }

    public void setCipher(Cipher cipher) {
        this.cipher = cipher;
        isEncrypted = true;
    }

    @Override
    public long getPosition() throws IOException {
        return underlyingByteChannel.getPosition();
    }

    @Override
    public void setPosition(long pos) throws IOException {
        underlyingByteChannel.setPosition(pos);
    }

    @Override
    public int read() throws IOException {
        read(reused, 0, 1);
        return reused[0];
    }

    @Override
    public int read(byte[] buffer, int off, int count) throws IOException {
        byte[] tmp = new byte[count];
        int size = readFully(tmp, count);
        System.arraycopy(tmp, 0, buffer, off, count);
        return size;
    }

    @Override
    public int readFully(byte[] buffer, int count) throws IOException {
        if (isEncrypted) {
            int remainingSize = dataPool.size();
            int toRead = count - remainingSize;
            int realRead = toRead + ((~toRead + 1) & 0xF);
            byte[] tmp = new byte[realRead];

            if (realRead > 0) {
                underlyingByteChannel.readFully(tmp, realRead);
                byte[] decrypted = cipher.update(tmp);
                for (int i = 0; i < decrypted.length; i++) {
                    dataPool.add(decrypted[i]);
                }
            }

            int realReadSize = 0;
            for (int i = 0; i < count && !dataPool.isEmpty(); i++) {
                buffer[i] = dataPool.poll();
                realReadSize++;
            }
            return realReadSize;

        } else {
            return underlyingByteChannel.readFully(buffer, count);
        }
    }

    @Override
    public void close() throws IOException {
        this.underlyingByteChannel.close();
    }

}