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

scouter.io.BufferedRandomAccessX Maven / Gradle / Ivy

/*
 *  Copyright 2015 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. 
 */

package scouter.io;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import scouter.util.FileUtil;
import scouter.util.IClose;
import scouter.util.LongKeyLinkedMap;


public class BufferedRandomAccessX implements IClose {

	final private RandomAccessFile file;
	final private LongKeyLinkedMap buffer;
	final private int blockSize;
	private long fileLength = 0;

	public long readAccessCount;
	
	private FileChannel channel;

	public BufferedRandomAccessX(RandomAccessFile file) throws IOException {
		this(file, 8192, 1, false);
	}

	public BufferedRandomAccessX(RandomAccessFile file, boolean useNio) throws IOException {
		this(file, 8192, 1, useNio);
	}

	public BufferedRandomAccessX(RandomAccessFile file, int blockSize, int numberOfBlock) throws IOException {
		this(file, blockSize, numberOfBlock, false);
	}

	public BufferedRandomAccessX(RandomAccessFile file, int blockSize, int numberOfBlock, boolean useNio)
			throws IOException {
		this.file = file;
		this.buffer = new LongKeyLinkedMap().setMax(numberOfBlock);
		this.blockSize = blockSize;
		this.fileLength = file.length();
		if (useNio) {
			this.channel = this.file.getChannel();
		}

	}

	private long offset=0;;
	public synchronized byte[] read(long pos, int length) throws IOException {
	    if (pos >= this.fileLength)
		 return null;

	    this.offset = pos + length;

	  	DataOutputX out = new DataOutputX();
		int posOnBlock = (int) (pos % blockSize);
		if (posOnBlock > 0) {
			//long blockNum = (pos / blockSize);
			byte[] blockBytes = getReadBlock(pos / blockSize);
			if(blockBytes==null){
				return null;
			}
			if (blockBytes.length < blockSize) {
				int readLen = Math.min(blockBytes.length - posOnBlock, length);
				return DataInputX.get(blockBytes, posOnBlock, readLen);
			}

			int readLen = Math.min(blockSize - posOnBlock, length);
			out.write(DataInputX.get(blockBytes, posOnBlock, readLen));

			length -= readLen;
			pos += readLen;
		}

		int blockCount = length / blockSize;

		for (int i = 0; i < blockCount; i++) {
			//long blockNum = (int) (pos / blockSize);
			byte[] blockBytes = getReadBlock(pos / blockSize);
			if (blockBytes == null) {
				return out.toByteArray();
			}
			out.write(blockBytes);

			pos += blockSize;
			length -= blockSize;
		}

		if (length == 0) {
			return out.toByteArray();
		}

		int remainder = length;

	//	long blockNum = (int) (pos / blockSize);
		byte[] block = getReadBlock(pos / blockSize);
		if (block != null) {
			remainder = Math.min(block.length, remainder);
			out.write(DataInputX.get(block, 0, remainder));
		}

		return out.toByteArray();
	}

	public long getLength() throws IOException {
		if (this.channel == null) {
			return this.file.length();
		} else {
			return this.channel.size();
		}
	}

	public void append(byte[] data) throws IOException {
		this.write(this.fileLength, data);
	}

	public synchronized void write(long pos, byte[] data) throws IOException {
		if (data == null || data.length == 0)
			return;
		if (pos >= this.fileLength) {
			this.offset = this.fileLength + data.length;
			this.seek(this.fileLength);
			writeReal(data);
			return;
		}
		
		this.offset = pos + data.length;
		
		seek(pos);
		writeReal(data);
		// /////////////////

		int length = data.length;
		int offset = 0;

		int posOnBlock = (int) (pos % blockSize);
		if (posOnBlock > 0) {
			long blockNumber = (pos / blockSize);
			byte[] blockBytes = this.buffer.get(blockNumber);
			int writeLen = Math.min(blockSize - posOnBlock, length);
			if (blockBytes != null) {
				if (blockSize == blockBytes.length) {
					System.arraycopy(data, offset, blockBytes, posOnBlock, writeLen);
				} else {
					this.buffer.remove(blockNumber);
				}
			}
			length -= writeLen;
			pos += writeLen;
			offset += writeLen;
		}
		if (length == 0)
			return;
		int blockCount = length / blockSize;
		for (int i = 0; i < blockCount; i++) {
			long blockNumber = pos / blockSize;
			byte[] blockBytes = this.buffer.get(blockNumber);
			if (blockBytes != null) {
				if (blockSize == blockBytes.length) {
					System.arraycopy(data, offset, blockBytes, 0, blockSize);
				} else {
					this.buffer.remove(blockNumber);
				}
			}
			length -= blockSize;
			pos += blockSize;
			offset += blockSize;
		}
		int remainder = length;
		if (remainder == 0)
			return;
		if (remainder < 0) {
			throw new IOException("Write fail remainder=" + remainder + " pos=" + pos);
		}
		long blockNumber = pos / blockSize;
		byte[] blockBytes = this.buffer.get(blockNumber);
		if (blockBytes == null)
			return;
		if (blockSize == blockBytes.length) {
			System.arraycopy(data, offset, blockBytes, 0, remainder);
		} else {
			this.buffer.remove(blockNumber);
		}

	}

	private void writeReal(byte[] data) throws IOException {
		if (this.channel == null) {
			this.file.write(data);
		} else {
			ByteBuffer dst = ByteBuffer.wrap(data);
			channel.write(dst);
		}
		this.fileLength = this.getLength();
	}

	private byte[] getReadBlock(long blockNumber) throws IOException {

		byte[] blockBytes = this.buffer.get(blockNumber);
		if (blockBytes != null) {
			if (blockBytes.length == blockSize)
				return blockBytes;
		}
		long pos = blockNumber * blockSize;
		if(pos >=this.fileLength)
			return null;
		
		this.readAccessCount++;
		
		this.seek(pos);
		long len = Math.min((this.fileLength - pos), blockSize);
	
		blockBytes = new byte[(int) len];
		readReal(blockBytes);
		this.buffer.put(blockNumber, blockBytes);
		return blockBytes;
	}

	private void seek(long pos) throws IOException {
		if (this.channel == null) {
			this.file.seek(pos);
		} else {
			this.channel.position(pos);
		}
	}

	private void readReal(byte[] block) throws IOException {
		if (this.channel == null) {
			this.file.readFully(block);
		} else {
			ByteBuffer dst = ByteBuffer.wrap(block);
			channel.read(dst);
		}

	}

	public void close() {
		if (this.channel != null) {
			FileUtil.close(this.channel);
		}
		FileUtil.close(this.file);
	}

	public int readInt(long pos) throws IOException {
		byte[] buf = read(pos, 4);
		return DataInputX.toInt(buf, 0);
	}

	public void writeInt(long pos, int i) throws IOException {
		this.write(pos, DataOutputX.toBytes(i));
	}

	public byte readByte(long pos) throws IOException {
		byte[] buf = read(pos, 1);
		return buf[0];
	}
	public long getOffset(){
		return this.offset;
	}

	public short readShort(long pos) throws IOException {
		byte[] buf = read(pos, 2);
		return DataInputX.toShort(buf, 0);
	}



}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy