RandomAccessFileByteWriter.java 10.8 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.adobe.internal.io;

import com.adobe.internal.io.ByteWriter;
import java.io.IOException;
import java.io.RandomAccessFile;

public final class RandomAccessFileByteWriter
implements ByteWriter {
    private boolean closed = false;
    private long fileLength = -1;
    private static final int DEFAULT_BUFFERSIZE = 4096;
    private static final int DEFAULT_NUMBERBUFFERS = 4;
    private int numberOfBuffers;
    private int bufferSize;
    private RandomAccessFile file;
    private Buffer[] buffers;
    private long counter;
    private Buffer mru;

    public RandomAccessFileByteWriter(RandomAccessFile file, int numberOfBuffers, int bufferSize) {
        if (numberOfBuffers < 1 || bufferSize < 1) {
            throw new IllegalArgumentException("Invalid buffer size or number of buffers.");
        }
        this.numberOfBuffers = numberOfBuffers;
        this.bufferSize = bufferSize;
        this.file = file;
        this.buffers = new Buffer[this.numberOfBuffers];
        for (int i = 0; i < this.numberOfBuffers; ++i) {
            this.buffers[i] = new Buffer();
        }
        this.mru = this.buffers[0];
    }

    public RandomAccessFileByteWriter(RandomAccessFile file) {
        this(file, 4, 4096);
    }

    @Override
    public void write(long position, int b) throws IOException {
        if (this.closed) {
            throw new IOException("ByteReader was closed");
        }
        boolean bufFound = false;
        if (this.fileLength == -1) {
            this.fileLength = this.file.length();
        }
        if (position < 0) {
            throw new IOException("Position is less than zero.");
        }
        if (position >= this.mru.base && position < this.mru.base + (long)this.bufferSize) {
            bufFound = true;
        } else {
            for (int bufferIndex = 0; bufferIndex < this.numberOfBuffers; ++bufferIndex) {
                Buffer currentBuffer = this.buffers[bufferIndex];
                if (position < currentBuffer.base || position >= currentBuffer.base + (long)this.bufferSize) continue;
                this.mru = currentBuffer;
                bufFound = true;
                break;
            }
        }
        if (!bufFound) {
            this.mru = this.loadLRU(position);
            bufFound = true;
        }
        this.mru.references = ++this.counter;
        Buffer.access$600((Buffer)this.mru)[(int)(position - Buffer.access$400((Buffer)this.mru))] = (byte)(b & 255);
        this.mru.bytesUsed = (int)Math.max((long)this.mru.bytesUsed, position - this.mru.base + 1);
        this.mru.isDirty = true;
        this.fileLength = Math.max(this.fileLength, position + 1);
    }

    @Override
    public void write(long position, byte[] b, int offset, int length) throws IOException {
        if (this.closed) {
            throw new IOException("ByteReader was closed");
        }
        boolean bufFound = false;
        if (this.fileLength == -1) {
            this.fileLength = this.file.length();
        }
        if (position < 0) {
            throw new IOException("Position is less than zero.");
        }
        if (position >= this.mru.base && position + (long)length <= this.mru.base + (long)this.bufferSize) {
            bufFound = true;
        } else {
            for (int bufferIndex = 0; bufferIndex < this.numberOfBuffers; ++bufferIndex) {
                Buffer currentBuffer = this.buffers[bufferIndex];
                if (position < currentBuffer.base || position + (long)length > currentBuffer.base + (long)this.bufferSize) continue;
                this.mru = currentBuffer;
                bufFound = true;
                break;
            }
        }
        if (!bufFound) {
            if (this.mru.buffersRequiredForRequest(position, length) != 1) {
                this.flush();
                this.file.seek(position);
                this.file.write(b, offset, length);
                this.fileLength = this.file.length();
                return;
            }
            this.mru = this.loadLRU(position);
            bufFound = true;
        }
        this.mru.references = ++this.counter;
        System.arraycopy(b, offset, this.mru.data, (int)(position - this.mru.base), length);
        this.mru.bytesUsed = (int)Math.max((long)this.mru.bytesUsed, position - this.mru.base + (long)length);
        this.mru.isDirty = true;
        this.fileLength = Math.max(this.fileLength, position + (long)length);
    }

    @Override
    public long length() throws IOException {
        if (this.closed) {
            throw new IOException("ByteReader was closed");
        }
        if (this.fileLength == -1) {
            this.fileLength = this.file.length();
        }
        return this.fileLength;
    }

    @Override
    public void flush() throws IOException {
        if (this.closed) {
            throw new IOException("ByteReader was closed");
        }
        for (int bufferIndex = 0; bufferIndex < this.numberOfBuffers; ++bufferIndex) {
            if (this.buffers[bufferIndex].isDirty) {
                this.buffers[bufferIndex].flushBuffer();
            }
            this.buffers[bufferIndex].resetBuffer();
        }
    }

    @Override
    public void close() throws IOException {
        if (this.closed) {
            return;
        }
        this.flush();
        this.closed = true;
        this.file.close();
    }

    @Override
    public int read(long position) throws IOException {
        if (this.closed) {
            throw new IOException("ByteReader was closed");
        }
        boolean bufFound = false;
        if (this.fileLength == -1) {
            this.fileLength = this.file.length();
        }
        if (position < 0 || position >= this.fileLength) {
            return -1;
        }
        if (position >= this.mru.base && position < this.mru.base + (long)this.bufferSize) {
            bufFound = true;
        } else {
            for (int bufferIndex = 0; bufferIndex < this.numberOfBuffers; ++bufferIndex) {
                Buffer currentBuffer = this.buffers[bufferIndex];
                if (position < currentBuffer.base || position >= currentBuffer.base + (long)this.bufferSize) continue;
                this.mru = currentBuffer;
                bufFound = true;
                break;
            }
        }
        if (!bufFound) {
            this.mru = this.loadLRU(position);
            bufFound = true;
        }
        this.mru.references = ++this.counter;
        return this.mru.data[(int)(position - this.mru.base)] & 255;
    }

    @Override
    public int read(long position, byte[] b, int offset, int length) throws IOException {
        if (this.closed) {
            throw new IOException("ByteReader was closed");
        }
        boolean bufFound = false;
        if (this.fileLength == -1) {
            this.fileLength = this.file.length();
        }
        if (position < 0 || position >= this.fileLength) {
            return -1;
        }
        length = (int)Math.min((long)length, this.fileLength - position);
        if (position >= this.mru.base && position + (long)length <= this.mru.base + (long)this.bufferSize) {
            bufFound = true;
        } else {
            for (int bufferIndex = 0; bufferIndex < this.numberOfBuffers; ++bufferIndex) {
                Buffer currentBuffer = this.buffers[bufferIndex];
                if (position < currentBuffer.base || position + (long)length > currentBuffer.base + (long)this.bufferSize) continue;
                this.mru = currentBuffer;
                bufFound = true;
                break;
            }
        }
        if (!bufFound) {
            if (this.mru.buffersRequiredForRequest(position, length) != 1) {
                this.flush();
                this.file.seek(position);
                return this.file.read(b, offset, length);
            }
            this.mru = this.loadLRU(position);
            bufFound = true;
        }
        this.mru.references = ++this.counter;
        System.arraycopy(this.mru.data, (int)(position - this.mru.base), b, offset, length);
        return length;
    }

    private Buffer loadLRU(long position) throws IOException {
        Buffer lru = null;
        long minCounter = Long.MAX_VALUE;
        for (int bufferIndex = 0; bufferIndex < this.numberOfBuffers; ++bufferIndex) {
            if (this.buffers[bufferIndex].references >= minCounter) continue;
            lru = this.buffers[bufferIndex];
            minCounter = lru.references;
        }
        if (lru.isDirty) {
            lru.flushBuffer();
        }
        lru.loadBuffer(position);
        return lru;
    }

    public String toString() {
        return this.file.toString();
    }

    private class Buffer {
        private long base;
        private long references;
        private boolean isDirty;
        private int bytesUsed;
        private byte[] data;

        Buffer() {
            this.base = Long.MAX_VALUE;
            this.isDirty = false;
            this.data = new byte[RandomAccessFileByteWriter.this.bufferSize];
        }

        void loadBuffer(long position) throws IOException {
            this.base = this.calculateBufferBase(position);
            this.references = ++RandomAccessFileByteWriter.this.counter;
            this.bytesUsed = (int)Math.min((long)RandomAccessFileByteWriter.this.bufferSize, Math.max(RandomAccessFileByteWriter.this.fileLength - this.base, 0));
            if (this.base >= RandomAccessFileByteWriter.this.fileLength) {
                return;
            }
            RandomAccessFileByteWriter.this.file.seek(this.base);
            long bytesRead = RandomAccessFileByteWriter.this.file.read(this.data, 0, this.bytesUsed);
            if (bytesRead != (long)this.bytesUsed && (bytesRead != -1 || this.bytesUsed != 0)) {
                throw new IOException("Didn't read enough bytes from the file.  Expected = " + this.bytesUsed + ", Actual = " + bytesRead);
            }
        }

        void flushBuffer() throws IOException {
            RandomAccessFileByteWriter.this.file.seek(this.base);
            RandomAccessFileByteWriter.this.file.write(this.data, 0, this.bytesUsed);
            this.isDirty = false;
        }

        void resetBuffer() {
            this.base = Long.MAX_VALUE;
        }

        int buffersRequiredForRequest(long position, int length) {
            long base = this.calculateBufferBase(position);
            long initialBufferSpace = (long)RandomAccessFileByteWriter.this.bufferSize - (position - base);
            long overage = Math.max((long)length - initialBufferSpace, 0);
            int extraBuffers = (int)Math.ceil((float)overage / (float)RandomAccessFileByteWriter.this.bufferSize);
            return (length != 0 ? 1 : 0) + extraBuffers;
        }

        private long calculateBufferBase(long position) {
            return position / (long)RandomAccessFileByteWriter.this.bufferSize * (long)RandomAccessFileByteWriter.this.bufferSize;
        }
    }

}