All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.shacocloud.mirage.loader.data.RandomAccessDataFile Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
package cc.shacocloud.mirage.loader.data;

import java.io.*;

/**
 * {@link RandomAccessData} 实现由 {@link RandomAccessFile} 支持
 */
public class RandomAccessDataFile implements RandomAccessData {
    
    private final FileAccess fileAccess;
    
    private final long offset;
    
    private final long length;
    
    /**
     * 创建一个由指定文件支持的新 {@link RandomAccessDataFile}
     *
     * @param file 基础文件
     * @throws IllegalArgumentException 如果文件为空或不存在
     */
    public RandomAccessDataFile(File file) {
        if (file == null) {
            throw new IllegalArgumentException("文件不得为空");
        }
        this.fileAccess = new FileAccess(file);
        this.offset = 0L;
        this.length = file.length();
    }
    
    /**
     * 用于创建 {@link #getSubsection(long, long)} 的私有构造函数
     */
    private RandomAccessDataFile(FileAccess fileAccess, long offset, long length) {
        this.fileAccess = fileAccess;
        this.offset = offset;
        this.length = length;
    }
    
    /**
     * 返回基础文件
     */
    public File getFile() {
        return this.fileAccess.file;
    }
    
    @Override
    public InputStream getInputStream() throws IOException {
        return new DataInputStream();
    }
    
    @Override
    public RandomAccessData getSubsection(long offset, long length) {
        if (offset < 0 || length < 0 || offset + length > this.length) {
            throw new IndexOutOfBoundsException();
        }
        return new RandomAccessDataFile(this.fileAccess, this.offset + offset, length);
    }
    
    @Override
    public byte[] read() throws IOException {
        return read(0, this.length);
    }
    
    @Override
    public byte[] read(long offset, long length) throws IOException {
        if (offset > this.length) {
            throw new IndexOutOfBoundsException();
        }
        if (offset + length > this.length) {
            throw new EOFException();
        }
        byte[] bytes = new byte[(int) length];
        read(bytes, offset, 0, bytes.length);
        return bytes;
    }
    
    private int readByte(long position) throws IOException {
        if (position >= this.length) {
            return -1;
        }
        return this.fileAccess.readByte(this.offset + position);
    }
    
    private int read(byte[] bytes, long position, int offset, int length) throws IOException {
        if (position > this.length) {
            return -1;
        }
        return this.fileAccess.read(bytes, this.offset + position, offset, length);
    }
    
    @Override
    public long getSize() {
        return this.length;
    }
    
    public void close() throws IOException {
        this.fileAccess.close();
    }
    
    /**
     * {@link InputStream} 实现 {@link RandomAccessDataFile}
     */
    private class DataInputStream extends InputStream {
        
        private int position;
        
        @Override
        public int read() throws IOException {
            int read = RandomAccessDataFile.this.readByte(this.position);
            if (read > -1) {
                moveOn(1);
            }
            return read;
        }
        
        @Override
        public int read(byte[] b) throws IOException {
            return read(b, 0, (b != null) ? b.length : 0);
        }
        
        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            if (b == null) {
                throw new NullPointerException("Bytes must not be null");
            }
            return doRead(b, off, len);
        }
        
        /**
         * 执行实际读取
         *
         * @param b   读取单个字节时要读取的字节数或 {@code null}
         * @param off 字节数组的偏移量
         * @param len 要读取的数据长度
         * @return 读入 {@code b} 的字节数或实际读取字节数(如果 {@code b} 为 {@code null}。到达流的末尾时返回 -1
         */
        int doRead(byte[] b, int off, int len) throws IOException {
            if (len == 0) {
                return 0;
            }
            int cappedLen = cap(len);
            if (cappedLen <= 0) {
                return -1;
            }
            return (int) moveOn(RandomAccessDataFile.this.read(b, this.position, off, cappedLen));
        }
        
        @Override
        public long skip(long n) throws IOException {
            return (n <= 0) ? 0 : moveOn(cap(n));
        }
        
        @Override
        public int available() throws IOException {
            return (int) RandomAccessDataFile.this.length - this.position;
        }
        
        /**
         * 限制指定值,使其不能超过剩余字节数
         */
        private int cap(long n) {
            return (int) Math.min(RandomAccessDataFile.this.length - this.position, n);
        }
        
        /**
         * 将流位置向前移动指定的量
         */
        private long moveOn(int amount) {
            this.position += amount;
            return amount;
        }
        
    }
    
    private static final class FileAccess {
        
        private final Object monitor = new Object();
        
        private final File file;
        
        private RandomAccessFile randomAccessFile;
        
        private FileAccess(File file) {
            this.file = file;
            openIfNecessary();
        }
        
        private int read(byte[] bytes, long position, int offset, int length) throws IOException {
            synchronized (this.monitor) {
                openIfNecessary();
                this.randomAccessFile.seek(position);
                return this.randomAccessFile.read(bytes, offset, length);
            }
        }
        
        private void openIfNecessary() {
            if (this.randomAccessFile == null) {
                try {
                    this.randomAccessFile = new RandomAccessFile(this.file, "r");
                } catch (FileNotFoundException ex) {
                    throw new IllegalArgumentException(String.format("文件 %s 必须存在", this.file.getAbsolutePath()));
                }
            }
        }
        
        private void close() throws IOException {
            synchronized (this.monitor) {
                if (this.randomAccessFile != null) {
                    this.randomAccessFile.close();
                    this.randomAccessFile = null;
                }
            }
        }
        
        private int readByte(long position) throws IOException {
            synchronized (this.monitor) {
                openIfNecessary();
                this.randomAccessFile.seek(position);
                return this.randomAccessFile.read();
            }
        }
        
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy