ToBase64ByteWriter.java 11.9 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.adobe.internal.util.base64;

import com.adobe.internal.io.ByteWriter;
import com.adobe.internal.io.FilterByteWriter;
import com.adobe.internal.util.base64.Base64Engine;
import java.io.IOException;

public class ToBase64ByteWriter
extends FilterByteWriter {
    protected int dataBytesPerBlock = 3;
    protected int base64BytesPerBlock = 4;
    protected int blocksPerLine;
    protected static final int linesPerBuffer = 10;
    protected byte[] buffer;
    protected int dataBytesPerBuffer;
    protected int base64BytesPerBuffer;
    protected byte[] partialConversionBuffer = new byte[6];
    protected int dataLineLength;
    protected int base64DataLineLength;
    protected int base64TotalLineLength;
    protected int fillerBytes;
    protected Base64Engine base64Engine;

    public ToBase64ByteWriter(ByteWriter byteWriter) throws IOException {
        this(byteWriter, 19, Base64Engine.LineEnding.CR);
    }

    public ToBase64ByteWriter(ByteWriter byteWriter, int blocksPerLine, Base64Engine.LineEnding lineEnding) throws IOException {
        super(byteWriter);
        this.base64Engine = new Base64Engine(blocksPerLine * this.base64BytesPerBlock, lineEnding, true);
        this.blocksPerLine = blocksPerLine;
        this.dataLineLength = this.blocksPerLine * this.dataBytesPerBlock;
        this.base64DataLineLength = this.blocksPerLine * this.base64BytesPerBlock;
        this.base64TotalLineLength = this.base64DataLineLength + lineEnding.length();
        this.buffer = new byte[this.base64TotalLineLength * 10];
        this.dataBytesPerBuffer = this.dataLineLength * 10;
        this.base64BytesPerBuffer = this.base64DataLineLength * 10;
        this.fillerBytes = this.countFillerBytes();
    }

    private int countFillerBytes() throws IOException {
        int count = 0;
        long position = super.length() - 1;
        while (super.read(position) == 61) {
            ++count;
            --position;
        }
        return count;
    }

    @Override
    public long length() throws IOException {
        long originalLength = super.length();
        long lines = originalLength / (long)this.base64TotalLineLength;
        long extra = originalLength % (long)this.base64TotalLineLength;
        long length = lines * (long)this.dataLineLength + extra / (long)this.base64BytesPerBlock * (long)this.dataBytesPerBlock;
        return length - (long)this.fillerBytes;
    }

    @Override
    public int read(long position, byte[] b, int offset, int length) throws IOException {
        int base64BytesThisRead;
        int totalDataBytesRead;
        long base64Position = this.dataPositionToBase64BlockPosition(position);
        int dataBytesRead = 0;
        int initialDataBytesToSkip = (int)(position % (long)this.dataBytesPerBlock);
        for (totalDataBytesRead = 0; totalDataBytesRead < length && (base64BytesThisRead = super.read(base64Position, this.buffer, 0, this.buffer.length)) != -1; totalDataBytesRead += dataBytesRead) {
            dataBytesRead = this.base64Engine.decode(this.buffer, 0, base64BytesThisRead, b, totalDataBytesRead, Math.min(length - totalDataBytesRead, b.length - totalDataBytesRead), initialDataBytesToSkip);
            if (dataBytesRead == 0) break;
            base64Position = this.dataPositionToBase64BlockPosition(position + (long)totalDataBytesRead);
            initialDataBytesToSkip = (int)((position + (long)totalDataBytesRead) % (long)this.dataBytesPerBlock);
        }
        return totalDataBytesRead;
    }

    private long dataPositionToDataBlockPosition(long dataPosition) {
        return dataPosition / (long)this.dataBytesPerBlock * (long)this.dataBytesPerBlock;
    }

    private long dataPositionToBase64BlockPosition(long dataPosition) {
        long lines = dataPosition / (long)this.dataLineLength;
        long lineOffset = dataPosition % (long)this.dataLineLength / (long)this.dataBytesPerBlock * (long)this.base64BytesPerBlock;
        return lines * (long)this.base64TotalLineLength + lineOffset;
    }

    private void fillEmptySpace(long position, long length) throws IOException {
        int base64BytesToWrite;
        byte[] zeroByteLine = new byte[this.dataLineLength];
        long base64Position = this.dataPositionToBase64BlockPosition(position);
        int initialDataBytesToSkip = (int)(position % (long)this.dataBytesPerBlock);
        int initialDataBytesToWrite = (int)Math.min(length, (long)((this.dataBytesPerBlock - initialDataBytesToSkip) % this.dataBytesPerBlock));
        int initialLineFullDataBytes = (int)((length - (long)initialDataBytesToWrite) / (long)this.dataBytesPerBlock * (long)this.dataBytesPerBlock % (long)this.dataLineLength);
        long linesToWrite = (length - (long)initialDataBytesToWrite - (long)initialLineFullDataBytes) / (long)this.dataLineLength;
        int finalLineFullDataBytes = (int)((length - (long)initialDataBytesToWrite - (long)initialLineFullDataBytes) % (long)this.dataLineLength);
        if (initialDataBytesToWrite > 0) {
            super.read(base64Position, this.partialConversionBuffer, 0, this.base64BytesPerBlock);
            this.base64Engine.decode(this.partialConversionBuffer, 0, this.base64BytesPerBlock, this.buffer, 0);
            switch (initialDataBytesToSkip) {
                case 1: {
                    this.buffer[1] = 0;
                }
                case 2: {
                    this.buffer[2] = 0;
                }
            }
            int bytesConverted = this.base64Engine.encode(this.buffer, 0, initialDataBytesToSkip + initialDataBytesToWrite, this.partialConversionBuffer, 0, (int)(base64Position % (long)this.base64TotalLineLength));
            super.write(base64Position, this.partialConversionBuffer, 0, bytesConverted);
            base64Position += (long)bytesConverted;
        }
        if (initialLineFullDataBytes > 0) {
            base64BytesToWrite = this.base64Engine.encode(zeroByteLine, 0, initialLineFullDataBytes, this.buffer, 0, (int)(base64Position % (long)this.base64TotalLineLength));
            super.write(base64Position, this.buffer, 0, base64BytesToWrite);
            base64Position += (long)base64BytesToWrite;
        }
        if (linesToWrite > 0) {
            base64BytesToWrite = this.base64Engine.encode(zeroByteLine, 0, this.dataLineLength, this.buffer, 0, 0);
            while (linesToWrite > 0) {
                super.write(base64Position, this.buffer, 0, base64BytesToWrite);
                base64Position += (long)base64BytesToWrite;
                --linesToWrite;
            }
        }
        if (finalLineFullDataBytes > 0) {
            base64BytesToWrite = this.base64Engine.encode(zeroByteLine, 0, finalLineFullDataBytes, this.buffer, 0, (int)(base64Position % (long)this.base64TotalLineLength));
            super.write(base64Position, this.buffer, 0, base64BytesToWrite);
            base64Position += (long)base64BytesToWrite;
        }
    }

    @Override
    public int read(long position) throws IOException {
        long base64Position = this.dataPositionToBase64BlockPosition(position);
        int byteInBlock = (int)(position % (long)this.dataBytesPerBlock);
        int base64BytesThisRead = super.read(base64Position, this.buffer, 0, this.base64BytesPerBlock);
        this.base64Engine.decode(this.buffer, 0, base64BytesThisRead, this.partialConversionBuffer, 0);
        return this.partialConversionBuffer[byteInBlock] & 255;
    }

    @Override
    public void write(long position, byte[] b, int offset, int length) throws IOException {
        int bytesConverted;
        byte[] block;
        long base64Position = this.dataPositionToBase64BlockPosition(position);
        int dataBytesWritten = 0;
        int initialDataBytesToSkip = (int)(position % (long)this.dataBytesPerBlock);
        int initialDataBytesToWrite = Math.min((this.dataBytesPerBlock - initialDataBytesToSkip) % this.dataBytesPerBlock, length);
        int finalDataBytesToWrite = Math.max(length - initialDataBytesToWrite, 0) % this.dataBytesPerBlock;
        int bulkDataBytesToWrite = length - initialDataBytesToWrite - finalDataBytesToWrite;
        long fileLength = this.length();
        this.fillerBytes = finalDataBytesToWrite == 0 && bulkDataBytesToWrite == 0 ? this.dataBytesPerBlock - initialDataBytesToSkip - initialDataBytesToWrite : (this.dataBytesPerBlock - finalDataBytesToWrite) % this.dataBytesPerBlock;
        if (position > fileLength) {
            this.fillEmptySpace(fileLength, position - fileLength);
        }
        if (position >= fileLength) {
            bulkDataBytesToWrite += finalDataBytesToWrite;
            finalDataBytesToWrite = 0;
        }
        if (initialDataBytesToWrite > 0) {
            super.read(base64Position, this.partialConversionBuffer, 0, this.base64BytesPerBlock);
            block = new byte[this.base64BytesPerBlock];
            this.base64Engine.decode(this.partialConversionBuffer, 0, this.base64BytesPerBlock, block, 0);
            System.arraycopy(b, 0, block, initialDataBytesToSkip, Math.min(initialDataBytesToWrite, b.length));
            bytesConverted = this.base64Engine.encode(block, 0, this.dataBytesPerBlock, this.partialConversionBuffer, 0, (int)(base64Position % (long)this.base64TotalLineLength));
            super.write(base64Position, this.partialConversionBuffer, 0, bytesConverted);
            base64Position += (long)bytesConverted;
            dataBytesWritten += initialDataBytesToWrite;
        }
        while (bulkDataBytesToWrite > 0) {
            int dataBytesThisWrite = Math.min(this.dataBytesPerBuffer, bulkDataBytesToWrite);
            int base64BytesToWrite = this.base64Engine.encode(b, dataBytesWritten, dataBytesThisWrite, this.buffer, 0, (int)(base64Position % (long)this.base64TotalLineLength));
            super.write(base64Position, this.buffer, 0, base64BytesToWrite);
            bulkDataBytesToWrite -= dataBytesThisWrite;
            dataBytesWritten += dataBytesThisWrite;
            base64Position += (long)base64BytesToWrite;
        }
        if (finalDataBytesToWrite > 0) {
            super.read(base64Position, this.partialConversionBuffer, 0, this.base64BytesPerBlock);
            block = new byte[this.base64BytesPerBlock];
            this.base64Engine.decode(this.partialConversionBuffer, 0, this.base64BytesPerBlock, block, 0);
            System.arraycopy(b, dataBytesWritten, block, 0, finalDataBytesToWrite);
            bytesConverted = this.base64Engine.encode(block, 0, this.dataBytesPerBlock, this.buffer, 0, (int)(base64Position % (long)this.base64TotalLineLength));
            super.write(base64Position, this.buffer, 0, bytesConverted);
            base64Position += (long)bytesConverted;
            dataBytesWritten += initialDataBytesToWrite;
        }
    }

    @Override
    public void write(long position, int b) throws IOException {
        long base64Position = this.dataPositionToBase64BlockPosition(position);
        int byteInBlock = (int)(position % (long)this.dataBytesPerBlock);
        int bytesAtEndOfBlock = this.dataBytesPerBlock - (byteInBlock + 1);
        long fileLength = this.length();
        if (position > fileLength) {
            this.fillEmptySpace(fileLength, position - fileLength);
        }
        if (position >= fileLength) {
            this.fillerBytes = bytesAtEndOfBlock;
        }
        int base64BytesRead = super.read(base64Position, this.partialConversionBuffer, 0, this.base64BytesPerBlock);
        base64BytesRead = Math.max(base64BytesRead, 0);
        byte[] block = new byte[this.base64BytesPerBlock];
        this.base64Engine.decode(this.partialConversionBuffer, 0, base64BytesRead, block, 0);
        block[byteInBlock] = (byte)(255 & b);
        int bytesToConvert = Math.max(byteInBlock + 1, (int)Math.min((long)this.dataBytesPerBlock, fileLength - this.dataPositionToDataBlockPosition(position)));
        int bytesConverted = this.base64Engine.encode(block, 0, bytesToConvert, this.partialConversionBuffer, 0, (int)(base64Position % (long)this.base64TotalLineLength));
        super.write(base64Position, this.partialConversionBuffer, 0, bytesConverted);
    }
}