package org.opencloudb.mpp.tmp;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel.MapMode;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
/**
* 基于内存映射的自动增长byteArray<br/>
* 非HotSpotVM需要重写release函<br/>
* 数释放文件句柄 <br/>
*
* @author Czp
*
*/
public class MemMapBytesArray implements Iterable<byte[]> {
private int size;
private int curIndex;
private int bufsSize;
private int bufsIndex;
private ThisItor itor;
private File swapFilePath;
private LinkedList<File> files;
private MemMapLongArray offsets;
private MappedByteBuffer curBuf;
private volatile boolean isClose;
private ArrayList<MappedByteBuffer> bufs;
private LinkedList<RandomAccessFile> foss;
private static final int INIT_SIZE = 8 * 50 * 1024 * 1024;
public MemMapBytesArray(String swapPath) {
itor = new ThisItor();
files = new LinkedList<File>();
bufs = new ArrayList<MappedByteBuffer>();
foss = new LinkedList<RandomAccessFile>();
createOffsetAndSwapPath(swapPath);
createBuffer();
}
public synchronized byte[] get(int index) {
MemMapUtil.checkRangeState(index, size, isClose);
long curDataOffset = offsets.get(index);
int[] pageAndOffset = pageAndLenDecode(curDataOffset);
int pageIndex = pageAndOffset[0];
int dataPosOffset = pageAndOffset[1];
MappedByteBuffer buf = bufs.get(pageIndex);
int curPos = buf.position();
buf.position(dataPosOffset);
char dataLen = buf.getChar();
byte[] data = new byte[dataLen];
buf.get(data);
buf.position(curPos);
return data;
}
/**
* 交换数据,主要用于排序,实际上只是改变数据的指针
*
* @param k
* @param j
*/
public synchronized void swap(int k, int j) {
if (k != j) {
MemMapUtil.checkRangeState(k, j, size, isClose);
long kPos = offsets.get(k);
long jPos = offsets.get(j);
offsets.set(k, jPos);
offsets.set(j, kPos);
}
}
/**
* 添加数据,数据长度必须小于65535
*
* @param data
* @return
*/
public synchronized boolean add(byte[] data) {
MemMapUtil.checkStateAndLen(isClose, data.length);
byte[] tmp = data;
int len = tmp.length;
int pos = curBuf.position();
int curBufRemainSize = curBuf.capacity() - pos;
if (curBufRemainSize >= len + 2) {
offsets.add(pageAndLenEncode(bufsSize - 1, pos));
curBuf.putChar((char) len);
for (int i = 0; i < len; i++) {
curBuf.put(tmp[i]);
}
} else {
createBuffer();
offsets.add(pageAndLenEncode(bufsSize - 1, 0));
curBuf.putChar((char) len);
for (int i = 0; i < len; i++) {
curBuf.put(tmp[i]);
}
}
size++;
return true;
}
/**
* 编码长度和位置
*
* @param page
* @param len
* @return
*/
private final static long pageAndLenEncode(int page, int len) {
long codeLen = len;
codeLen <<= 32;
codeLen |= page;
return codeLen;
}
/**
* 解码长度和位置
*
* @param len
* @return
*/
private final static int[] pageAndLenDecode(long len) {
int page = (int) len;
int realLen = (int) (len >> 32);
return new int[] { page, realLen };
}
/**
* 获取数据的总数
*
* @return
*/
public int size() {
return size;
}
/**
* 使用完后必须调用此方法释放句柄
*/
public synchronized void release() {
isClose = true;
for (RandomAccessFile fos : foss) {
MemMapUtil.close(fos);
}
for (MappedByteBuffer mbuf : bufs) {
MemMapUtil.releaseMemory(mbuf);
}
for (File f : files) {
if (!f.delete())
System.out.println("release mapbuf fail");
}
offsets.release();
foss.clear();
bufs.clear();
files.clear();
}
// @Override
public synchronized Iterator<byte[]> iterator() {
curIndex = 0;
curBuf = bufs.get(bufsIndex++);
curBuf.flip();
return itor;
}
private void createOffsetAndSwapPath(String swapPath) {
swapFilePath = new File(swapPath);
if (!swapFilePath.exists())
swapFilePath.mkdirs();
offsets = new MemMapLongArray(swapPath);
}
private void createBuffer() {
File file = null;
RandomAccessFile mapFos = null;
try {
file = File.createTempFile("mapb-", ".dat", swapFilePath);
mapFos = new RandomAccessFile(file, "rw");
curBuf = mapFos.getChannel().map(MapMode.READ_WRITE, 0, INIT_SIZE);
foss.add(mapFos);
bufs.add(curBuf);
files.add(file);
bufsSize++;
} catch (Exception e) {
MemMapUtil.close(mapFos);
if (offsets != null)
offsets.release();
if (file != null)
file.delete();
throw new RuntimeException(e);
}
}
private class ThisItor implements Iterator<byte[]> {
// @Override
public synchronized boolean hasNext() {
return curIndex < size;
}
// @Override
public synchronized byte[] next() {
MemMapUtil.checkState(isClose);
return get(curIndex++);
}
// @Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}