/*
* Copyright 2011 Paula Gearon
*
* 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 org.mulgara.util.io;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import org.apache.log4j.Logger;
/**
* A memory mapped read-write version of LBufferedFile. This is not safe for transactional work.
* @author pag
*/
public class LMappedBufferedFileRW extends LMappedBufferedFile {
/** Logger. */
static final Logger logger = Logger.getLogger(LMappedBufferedFileRW.class);
/** The page size to use. */
public static final int PREFERRED_PAGE_SIZE;
/** The size of the page for this object. */
protected final int pageSize;
/** The size of a page to be mapped */
private static final int DEFAULT_PAGE_SIZE = 33554432; // 32 MB
static {
String pageSizeStr = System.getProperty(PAGE_SIZE_PROP);
int tmp = DEFAULT_PAGE_SIZE;
try {
if (pageSizeStr != null) tmp = Integer.parseInt(pageSizeStr);
} catch (NumberFormatException e) {
logger.warn("Property [" + PAGE_SIZE_PROP + "] is not a number [" + pageSizeStr + "]. Using default: " + tmp);
}
PREFERRED_PAGE_SIZE = tmp;
}
/**
* Create a new buffered file for Long seeks.
* @param file The file to buffer.
* @throws IOException There was an error setting up initial mapping of the file.
*/
public LMappedBufferedFileRW(RandomAccessFile file, int recordSize) throws IOException {
super(file);
fc = file.getChannel();
int recordsPerPage = PREFERRED_PAGE_SIZE / recordSize;
pageSize = recordsPerPage * recordSize;
mapFile();
if (logger.isDebugEnabled()) {
logger.debug("Mapping files for R/W with pages of " + pageSize + "bytes at "+ recordsPerPage + " records/page");
}
}
@Override
public int getPageSize() {
return pageSize;
}
@Override
public ByteBuffer read(long offset, int length) throws IOException {
// get the offset into the last buffer, this may be negative if before the last page
long lastPageOffset = (offset + (long)length) - (pageSize * (long)(buffers.length - 1));
// if the offset is larger than the final page, then remap
if (lastPageOffset > pageSize || lastPageOffset > buffers[buffers.length - 1].limit() || buffers.length == 0) {
mapFile();
}
int page = (int)(offset / pageSize);
int page_offset = (int)(offset % pageSize);
assert page_offset + length <= pageSize : "Access to block outside of record boundaries";
ByteBuffer bb = buffers[page];
bb.position(page_offset);
bb.limit(page_offset + length);
return bb.slice();
}
@Override
FileChannel.MapMode getMode() {
return FileChannel.MapMode.READ_WRITE;
}
byte[] dump() {
byte[] d = new byte[buffers[0].capacity()];
int p = buffers[0].position();
buffers[0].position(0);
buffers[0].get(d);
buffers[0].position(p);
return d;
}
}