LargeFileBuffer.java
package com.xenoamess.ipplus360;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* 大文件缓冲区,支持超过2GB的文件
* 完全无共享状态的设计,每个实例独立操作,绝对线程安全
*/
class LargeFileBuffer {
private static final int SEGMENT_SIZE = Integer.MAX_VALUE;
private final FileChannel fileChannel;
private final long fileSize;
private final MappedByteBuffer[] segments;
private final int segmentCount;
private long position;
private long limit;
private long capacity;
/**
* 私有构造方法,用于创建副本
*/
private LargeFileBuffer(FileChannel fileChannel, long fileSize, MappedByteBuffer[] segments, int segmentCount) {
this.fileChannel = fileChannel;
this.fileSize = fileSize;
this.segments = segments;
this.segmentCount = segmentCount;
this.capacity = fileSize;
this.limit = fileSize;
this.position = 0;
}
public LargeFileBuffer(FileChannel fileChannel, long fileSize) throws IOException {
this.fileChannel = fileChannel;
this.fileSize = fileSize;
this.segmentCount = (int) ((fileSize + SEGMENT_SIZE - 1) / SEGMENT_SIZE);
this.segments = new MappedByteBuffer[segmentCount];
this.capacity = fileSize;
this.limit = fileSize;
this.position = 0;
// 预创建所有Segment的内存映射,只做一次
for (int i = 0; i < segmentCount; i++) {
long segmentStart = (long) i * SEGMENT_SIZE;
long segmentSize = Math.min(SEGMENT_SIZE, fileSize - segmentStart);
segments[i] = fileChannel.map(FileChannel.MapMode.READ_ONLY, segmentStart, segmentSize);
}
}
/**
* 创建缓冲区副本,共享底层内存映射,独立维护position和limit
* 多线程场景下,每个线程应该获取独立的副本,避免锁竞争
*/
public LargeFileBuffer duplicate() {
LargeFileBuffer copy = new LargeFileBuffer(this.fileChannel, this.fileSize, this.segments, this.segmentCount);
copy.position = this.position;
copy.limit = this.limit;
return copy;
}
private MappedByteBuffer getSegment(long position) {
int segmentIndex = (int) (position / SEGMENT_SIZE);
return segments[segmentIndex];
}
private int getSegmentOffset(long position) {
return (int) (position % SEGMENT_SIZE);
}
public byte get() {
if (position >= limit) {
throw new java.nio.BufferUnderflowException();
}
MappedByteBuffer segment = getSegment(position);
int offset = getSegmentOffset(position);
position++;
return segment.get(offset);
}
public byte get(int index) {
if (index >= limit) {
throw new IndexOutOfBoundsException();
}
MappedByteBuffer segment = getSegment(index);
int offset = getSegmentOffset(index);
return segment.get(offset);
}
/**
* 从指定偏移位置读取数据,不修改当前buffer的position
*/
public void get(long offset, byte[] dst, int off, int len) {
if (offset < 0 || offset + len > capacity) {
throw new IndexOutOfBoundsException("Offset " + offset + " out of bounds");
}
int remaining = len;
int dstOffset = off;
long currentPosition = offset;
while (remaining > 0) {
MappedByteBuffer segment = getSegment(currentPosition);
int segmentOffset = getSegmentOffset(currentPosition);
int bytesToRead = Math.min(remaining, SEGMENT_SIZE - segmentOffset);
// 使用segment的副本进行读取,不修改共享的segment的position,完全线程安全
MappedByteBuffer segmentCopy = (MappedByteBuffer) segment.duplicate();
segmentCopy.position(segmentOffset);
segmentCopy.get(dst, dstOffset, bytesToRead);
currentPosition += bytesToRead;
dstOffset += bytesToRead;
remaining -= bytesToRead;
}
}
public void get(byte[] dst, int offset, int length) {
get(position, dst, offset, length);
position += length;
}
public void get(byte[] dst) {
get(dst, 0, dst.length);
}
public char getChar() {
byte[] bytes = new byte[2];
get(bytes);
return (char) ((bytes[0] << 8) | (bytes[1] & 0xFF));
}
public short getShort() {
byte[] bytes = new byte[2];
get(bytes);
return (short) ((bytes[0] << 8) | (bytes[1] & 0xFF));
}
public int getInt() {
byte[] bytes = new byte[4];
get(bytes);
return (bytes[0] << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
}
public long getLong() {
byte[] bytes = new byte[8];
get(bytes);
long result = 0;
for (int i = 0; i < 8; i++) {
result = (result << 8) | (bytes[i] & 0xFF);
}
return result;
}
public float getFloat() {
return Float.intBitsToFloat(getInt());
}
public double getDouble() {
return Double.longBitsToDouble(getLong());
}
public boolean hasRemaining() {
return position < limit;
}
public long remaining() {
return limit - position;
}
public long position() {
return position;
}
public void position(long newPosition) {
if (newPosition < 0 || newPosition > fileSize) {
throw new IllegalArgumentException("Position " + newPosition + " out of bounds [0, " + fileSize + "]");
}
this.position = newPosition;
}
public long limit() {
return limit;
}
public void limit(long newLimit) {
if (newLimit < 0 || newLimit > fileSize) {
throw new IllegalArgumentException("Limit " + newLimit + " out of bounds [0, " + fileSize + "]");
}
this.limit = newLimit;
}
public long capacity() {
return fileSize;
}
public void clear() {
position = 0;
limit = capacity;
}
public void flip() {
limit = position;
position = 0;
}
public void rewind() {
position = 0;
}
}