package io.atomix.catalyst.buffer;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
/**
* {@link ByteBuffer} based mapped bytes.
*/
public class MappedBytes extends ByteBufferBytes {
/**
* Allocates a mapped buffer in {@link java.nio.channels.FileChannel.MapMode#READ_WRITE} mode.
* <p>
* Memory will be mapped by opening and expanding the given {@link java.io.File} to the desired {@code count} and mapping the
* file contents into memory via {@link java.nio.channels.FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
*
* @param file The file to map into memory. If the file doesn't exist it will be automatically created.
* @param size The count of the buffer to allocate (in bytes).
* @return The mapped buffer.
* @throws NullPointerException If {@code file} is {@code null}
* @throws IllegalArgumentException If {@code count} is greater than {@link MappedMemory#MAX_SIZE}
*
* @see #allocate(java.io.File, java.nio.channels.FileChannel.MapMode, long)
*/
public static MappedBytes allocate(File file, long size) {
return allocate(file, FileChannel.MapMode.READ_WRITE, size);
}
/**
* Allocates a mapped buffer.
* <p>
* Memory will be mapped by opening and expanding the given {@link java.io.File} to the desired {@code count} and mapping the
* file contents into memory via {@link java.nio.channels.FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
*
* @param file The file to map into memory. If the file doesn't exist it will be automatically created.
* @param mode The mode with which to map the file.
* @param size The count of the buffer to allocate (in bytes).
* @return The mapped buffer.
* @throws NullPointerException If {@code file} is {@code null}
* @throws IllegalArgumentException If {@code count} is greater than {@link Integer#MAX_VALUE}
*
* @see #allocate(java.io.File, long)
*/
public static MappedBytes allocate(File file, FileChannel.MapMode mode, long size) {
if (file == null)
throw new NullPointerException("file cannot be null");
if (mode == null)
mode = FileChannel.MapMode.READ_WRITE;
if (size > Integer.MAX_VALUE)
throw new IllegalArgumentException("size for MappedBytes cannot be greater than " + Integer.MAX_VALUE);
RandomAccessFile randomAccessFile = createFile(file, mode);
try {
MappedByteBuffer buffer = randomAccessFile.getChannel().map(mode, 0, size);
return new MappedBytes(file, randomAccessFile, buffer, mode);
} catch (IOException e) {
throw new CatalystIOException(e);
}
}
private final File file;
private final RandomAccessFile randomAccessFile;
private final FileChannel.MapMode mode;
protected MappedBytes(File file, RandomAccessFile randomAccessFile, MappedByteBuffer buffer, FileChannel.MapMode mode) {
super(buffer);
this.file = file;
this.randomAccessFile = randomAccessFile;
this.mode = mode;
}
@Override
protected ByteBuffer newByteBuffer(long size) {
try {
return randomAccessFile.getChannel().map(mode, 0, size);
} catch (IOException e) {
throw new CatalystIOException(e);
}
}
@Override
public boolean isDirect() {
return true;
}
@Override
public Bytes flush() {
((MappedByteBuffer) buffer).force();
return this;
}
@Override
public void close() {
try {
randomAccessFile.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
super.close();
}
/**
* Deletes the underlying file.
*/
public void delete() {
try {
close();
Files.delete(file.toPath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static RandomAccessFile createFile(File file, FileChannel.MapMode mode) {
if (file == null)
throw new NullPointerException("file cannot be null");
if (mode == null)
mode = FileChannel.MapMode.READ_WRITE;
try {
return new RandomAccessFile(file, parseMode(mode));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static String parseMode(FileChannel.MapMode mode) {
if (mode == FileChannel.MapMode.READ_ONLY) {
return "r";
} else if (mode == FileChannel.MapMode.READ_WRITE) {
return "rw";
}
throw new IllegalArgumentException("unsupported map mode");
}
}