/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package com.bc.ceres.binio.util; import com.bc.ceres.binio.DataContext; import com.bc.ceres.binio.IOHandler; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class MappedFileChannelIOHandler implements IOHandler { private final FileChannel channel; private MappedByteBuffer mappedBuffer; private long mappedPos; private long mappedUpperBound; private long streamPos; public MappedFileChannelIOHandler(FileChannel channel) throws IOException { if (channel == null) { throw new IllegalArgumentException("channel == null"); } if (!channel.isOpen()) { throw new IllegalArgumentException("channel.isOpen() == false"); } this.channel = channel; long channelPosition = channel.position(); streamPos = channelPosition; long fullSize = channel.size() - channelPosition; long mappedSize = Math.min(fullSize, 2147483647L); mappedPos = 0L; mappedUpperBound = mappedPos + mappedSize; mappedBuffer = channel.map(FileChannel.MapMode.READ_ONLY, channelPosition, mappedSize); } @Override public void read(DataContext context, byte[] data, long position) throws IOException { synchronized (this) { seek(position); read(data); } } @Override public void write(DataContext context, byte[] data, long position) throws IOException { throw new RuntimeException("not implemented"); } @Override public long getMaxPosition() throws IOException { synchronized (this) { return channel.size(); } } private void seek(long pos) throws IOException { streamPos = pos; if (pos >= mappedPos && pos < mappedUpperBound) { mappedBuffer.position((int) (pos - mappedPos)); } else { int len = (int) Math.min(channel.size() - pos, 2147483647L); mappedBuffer = getMappedBuffer(len); } } private int read(byte b[]) throws IOException { if (b.length == 0) { return 0; } final int len = b.length; ByteBuffer byteBuffer = getMappedBuffer(len); byteBuffer.get(b, 0, len); streamPos += len; return len; } private MappedByteBuffer getMappedBuffer(int len) throws IOException { if (streamPos < mappedPos || streamPos + (long) len >= mappedUpperBound) { mappedPos = streamPos; long mappedSize = Math.min(channel.size() - mappedPos, 2147483647L); mappedUpperBound = mappedPos + mappedSize; mappedBuffer = channel.map( FileChannel.MapMode.READ_ONLY, mappedPos, mappedSize); } return mappedBuffer; } }