package com.neocoretechs.bigsack.io;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
/*
* Copyright (c) 2003, NeoCoreTechs
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* Neither the name of NeoCoreTechs nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* Memory mapped file I/O.
* We can only map 2 gig at a time due to mmap, so we keep track of ranges
* currently mapped and remap when necessary.
* For pool, there are one of these per tablespace and pointers use the
* first 3 bits for tablespace so our theoretical max per tablespace is
* 2,305,843,009,213,693,952 bytes * 8 tablespaces
* @see IoInterface
* @author Groff
*/
final class LinkedMappedByteBuffer {
private FileChannel FC;
private MappedByteBuffer bb;
private static int rangeSize = Integer.MAX_VALUE;
private long rangeMin = 0L;
private long rangeMax = (long) rangeSize;
/**
* @param tFC The FileChannel to map
* @param tiSize The initial size to map (can be > than file size to extend)
* and must be less than Integer.MAX_SIZE
*/
LinkedMappedByteBuffer(FileChannel tFC, long tiSize)
throws IOException {
FC = tFC;
setRange(0L, tiSize);
}
/**
* @param tFC The FileChannel to map
* @param tiSize The initial size to map (can be > than file size to extend)
* and must be less than Integer.MAX_SIZE
* @param rPos The position to set to
*/
LinkedMappedByteBuffer(FileChannel tFC, long tiSize, long rPos)
throws IOException {
FC = tFC;
setRange(rPos, tiSize);
}
MappedByteBuffer force() {
return bb.force();
}
boolean isLoaded() {
return bb.isLoaded();
}
MappedByteBuffer load() {
return bb.load();
}
long position() throws IOException {
return rangeMin + bb.position();
}
void position(long offset) throws IOException {
checkRange(offset);
}
long capacity() throws IOException {
return FC.size();
}
/**
* Compute the range we are determined to map<br>
* It will only ever be maxxed at FileChannel size, so
* you must extend it via external mechanisim using Filechannel, it
* is not automatically extended!<br>
* Only chunks up to rangeSize are mapped
* @param rangeTarg The target that our range must contain
* @exception IOException if filechannel position or size ops fail
*/
private void checkRange(long rangeTarg) throws IOException {
if (rangeTarg >= rangeMin && rangeTarg <= rangeMax) {
// within range but exceeding capacity of buffer
// we must have extended the file so remap if necessary
int rPos = (int) (rangeTarg - rangeMin);
if (rPos < bb.capacity()) {
bb.position(rPos);
return;
}
}
long r1 = rangeTarg / rangeSize;
rangeMin = r1 * rangeSize;
rangeMax = (rangeMin + rangeSize) - 1L;
// remap new range
bb.force();
bb = null;
System.gc();
long iSize = FC.size();
if (iSize > rangeSize)
iSize = rangeSize;
bb = FC.map(FileChannel.MapMode.READ_WRITE, rangeMin, iSize);
bb.position((int) (rangeTarg - rangeMin));
}
/**
* Set the range we are determined to map<br>
* This method, called from c'tor can extend the file
* Only chunks up to rangesize are mapped and this method is used
* to set initial position other than default on startup
* @param rangeTarg The target that our range must contain
* @param iSize The desired size to map
* @exception IOException if FileChannel.map fails, or position fails
*/
private void setRange(long rangeTarg, long iSize) throws IOException {
long r1 = rangeTarg / rangeSize;
rangeMin = r1 * rangeSize;
rangeMax = (rangeMin + rangeSize) - 1L;
// map new range
if (iSize > rangeSize)
iSize = rangeSize;
bb = FC.map(FileChannel.MapMode.READ_WRITE, rangeMin, iSize);
bb.position((int) (rangeTarg - rangeMin));
}
// writing..
void put(byte[] buf) throws IOException {
try {
put(buf, 0, buf.length);
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
void put(byte[] buf, int ioffs, int numbyte) throws IOException {
try {
int i = ioffs, runcount = numbyte, blkbytes;
// assume our position is set and we have space
if (bb.position() == (rangeSize - 1))
checkRange(bb.position() + 1);
//
for (;;) {
blkbytes = (rangeSize - 1) - bb.position();
if (runcount > blkbytes) {
runcount -= blkbytes;
bb.put(buf, i, blkbytes);
i += blkbytes;
checkRange(bb.position() + 1);
} else {
bb.put(buf, i, runcount);
return;
}
}
//bb.put(obuf, 0, osiz);
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
void putInt(int obuf) throws IOException {
try {
//ByteArrayOutputStream baos = new ByteArrayOutputStream();
//DataOutputStream daos = new DataOutputStream(baos);
//daos.writeInt(obuf);
//daos.flush();
//daos.close();
//put(baos.toByteArray());
ByteBuffer tbb = ByteBuffer.allocate(4);
tbb.putInt(obuf);
put(tbb.array());
//bb.putInt(obuf);
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
void putLong(long obuf) throws IOException {
try {
//ByteArrayOutputStream baos = new ByteArrayOutputStream();
//DataOutputStream daos = new DataOutputStream(baos);
//daos.writeLong(obuf);
//daos.flush();
//daos.close();
//put(baos.toByteArray());
ByteBuffer tbb = ByteBuffer.allocate(8);
tbb.putLong(obuf);
put(tbb.array());
//bb.putLong(obuf);
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
void putShort(short obuf) throws IOException {
try {
//ByteArrayOutputStream baos = new ByteArrayOutputStream();
//DataOutputStream daos = new DataOutputStream(baos);
//daos.writeShort(obuf);
//daos.flush();
//daos.close();
//put(baos.toByteArray());
ByteBuffer tbb = ByteBuffer.allocate(2);
tbb.putShort(obuf);
put(tbb.array());
//bb.putShort(obuf);
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
// reading...
int get(byte[] buf, int ioffs, int numbyte) throws IOException {
try {
int i = ioffs, runcount = numbyte, blkbytes;
// assume our position is set and we have space
if (bb.position() == (rangeSize - 1L))
checkRange(bb.position() + 1);
//
for (;;) {
blkbytes = (rangeSize - 1) - bb.position();
if (runcount > blkbytes) {
runcount -= blkbytes;
bb.get(buf, i, blkbytes);
i += blkbytes;
checkRange(bb.position() + 1);
} else {
bb.get(buf, i, runcount);
i += runcount;
break;
}
}
//bb.get(b, 0, osiz);
return i;
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
int get(byte[] b) throws IOException {
try {
//bb.get(b);
return get(b, 0, b.length);
//return b.length;
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
int getInt() throws IOException {
try {
byte[] b = new byte[4];
get(b);
//ByteArrayInputStream bais = new ByteArrayInputStream(b);
//DataInputStream dais = new DataInputStream(bais);
//return dais.readInt();
ByteBuffer tbb = ByteBuffer.wrap(b);
return tbb.getInt();
//return bb.getInt();
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
long getLong() throws IOException {
try {
byte[] b = new byte[8];
get(b);
//ByteArrayInputStream bais = new ByteArrayInputStream(b);
//DataInputStream dais = new DataInputStream(bais);
//return dais.readLong();
ByteBuffer tbb = ByteBuffer.wrap(b);
return tbb.getLong();
//return bb.getLong();
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
short getShort() throws IOException {
try {
byte[] b = new byte[2];
get(b);
//ByteArrayInputStream bais = new ByteArrayInputStream(b);
//DataInputStream dais = new DataInputStream(bais);
//return dais.readShort();
ByteBuffer tbb = ByteBuffer.wrap(b);
return tbb.getShort();
//return bb.getShort();
} catch (Exception bue) {
throw new IOException(bue.toString());
}
}
}