--- /dev/null
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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 io.atomix.utils.memory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Mapped memory allocator.
+ * <p>
+ * The mapped memory allocator provides direct memory access to memory mapped from a file on disk. The mapped allocator
+ * supports allocating memory in any {@link FileChannel.MapMode}. Once the file is mapped and the
+ * memory has been allocated, the mapped allocator provides the memory address of the underlying
+ * {@link java.nio.MappedByteBuffer} for access via {@link sun.misc.Unsafe}.
+ *
+ * @author <a href="http://github.com/kuujo">Jordan Halterman</a>
+ */
+public class MappedMemoryAllocator implements MemoryAllocator<MappedMemory> {
+ public static final FileChannel.MapMode DEFAULT_MAP_MODE = FileChannel.MapMode.READ_WRITE;
+
+ private final AtomicInteger referenceCount = new AtomicInteger();
+ private final RandomAccessFile file;
+ private final FileChannel channel;
+ private final FileChannel.MapMode mode;
+ private final long offset;
+
+ public MappedMemoryAllocator(File file) {
+ this(file, DEFAULT_MAP_MODE, 0);
+ }
+
+ public MappedMemoryAllocator(File file, FileChannel.MapMode mode) {
+ this(file, mode, 0);
+ }
+
+ public MappedMemoryAllocator(File file, FileChannel.MapMode mode, long offset) {
+ this(createFile(file, mode), mode, offset);
+ }
+
+ public MappedMemoryAllocator(RandomAccessFile file, FileChannel.MapMode mode, long offset) {
+ if (file == null) {
+ throw new NullPointerException("file cannot be null");
+ }
+ if (mode == null) {
+ throw new NullPointerException("mode cannot be null");
+ }
+ if (offset < 0) {
+ throw new IllegalArgumentException("offset cannot be negative");
+ }
+ this.file = file;
+ this.channel = this.file.getChannel();
+ this.mode = mode;
+ this.offset = offset;
+ }
+
+ private static RandomAccessFile createFile(File file, FileChannel.MapMode mode) {
+ if (file == null) {
+ throw new NullPointerException("file cannot be null");
+ }
+ if (mode == null) {
+ mode = DEFAULT_MAP_MODE;
+ }
+ 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");
+ }
+
+ @Override
+ public MappedMemory allocate(int size) {
+ try {
+ if (file.length() < size) {
+ file.setLength(size);
+ }
+ referenceCount.incrementAndGet();
+ return new MappedMemory(channel.map(mode, offset, size), this);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public MappedMemory reallocate(MappedMemory memory, int size) {
+ MappedMemory newMemory = allocate(size);
+ memory.free();
+ return newMemory;
+ }
+
+ public void close() {
+ try {
+ file.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Releases a reference from the allocator.
+ */
+ void release() {
+ if (referenceCount.decrementAndGet() == 0) {
+ close();
+ }
+ }
+
+}