5ad4c03566df7f62269e892394114a209b42bc13
[controller.git] / third-party / atomix / storage / src / main / java / io / atomix / storage / buffer / MappedBytes.java
1 /*
2  * Copyright 2015-present Open Networking Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package io.atomix.storage.buffer;
17
18 import io.atomix.utils.AtomixIOException;
19 import io.atomix.utils.memory.BufferCleaner;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.RandomAccessFile;
26 import java.nio.ByteBuffer;
27 import java.nio.MappedByteBuffer;
28 import java.nio.channels.FileChannel;
29 import java.nio.file.Files;
30
31 /**
32  * {@link ByteBuffer} based mapped bytes.
33  */
34 public class MappedBytes extends ByteBufferBytes {
35
36   private static final Logger LOGGER = LoggerFactory.getLogger(MappedBytes.class);
37
38   /**
39    * Allocates a mapped buffer in {@link FileChannel.MapMode#READ_WRITE} mode.
40    * <p>
41    * Memory will be mapped by opening and expanding the given {@link File} to the desired {@code count} and mapping the
42    * file contents into memory via {@link FileChannel#map(FileChannel.MapMode, long, long)}.
43    *
44    * @param file The file to map into memory. If the file doesn't exist it will be automatically created.
45    * @param size The count of the buffer to allocate (in bytes).
46    * @return The mapped buffer.
47    * @throws NullPointerException     If {@code file} is {@code null}
48    * @throws IllegalArgumentException If {@code count} is greater than {@link MappedBytes#MAX_SIZE}
49    * @see #allocate(File, FileChannel.MapMode, int)
50    */
51   public static MappedBytes allocate(File file, int size) {
52     return allocate(file, FileChannel.MapMode.READ_WRITE, size);
53   }
54
55   /**
56    * Allocates a mapped buffer.
57    * <p>
58    * Memory will be mapped by opening and expanding the given {@link File} to the desired {@code count} and mapping the
59    * file contents into memory via {@link FileChannel#map(FileChannel.MapMode, long, long)}.
60    *
61    * @param file The file to map into memory. If the file doesn't exist it will be automatically created.
62    * @param mode The mode with which to map the file.
63    * @param size The count of the buffer to allocate (in bytes).
64    * @return The mapped buffer.
65    * @throws NullPointerException     If {@code file} is {@code null}
66    * @throws IllegalArgumentException If {@code count} is greater than {@link Integer#MAX_VALUE}
67    * @see #allocate(File, int)
68    */
69   public static MappedBytes allocate(File file, FileChannel.MapMode mode, int size) {
70     return FileBytes.allocate(file, size).map(0, size, mode);
71   }
72
73   private final File file;
74   private final RandomAccessFile randomAccessFile;
75   private final FileChannel.MapMode mode;
76
77   protected MappedBytes(File file, RandomAccessFile randomAccessFile, MappedByteBuffer buffer, FileChannel.MapMode mode) {
78     super(buffer);
79     this.file = file;
80     this.randomAccessFile = randomAccessFile;
81     this.mode = mode;
82   }
83
84   @Override
85   protected ByteBuffer newByteBuffer(int size) {
86     try {
87       return randomAccessFile.getChannel().map(mode, 0, size);
88     } catch (IOException e) {
89       throw new AtomixIOException(e);
90     }
91   }
92
93   @Override
94   public boolean isDirect() {
95     return true;
96   }
97
98   @Override
99   public Bytes flush() {
100     ((MappedByteBuffer) buffer).force();
101     return this;
102   }
103
104   @Override
105   public void close() {
106     try {
107       BufferCleaner.freeBuffer(buffer);
108     } catch (Exception e) {
109       if (LOGGER.isDebugEnabled()) {
110         LOGGER.debug("Failed to unmap direct buffer", e);
111       }
112     }
113     try {
114       randomAccessFile.close();
115     } catch (IOException e) {
116       throw new RuntimeException(e);
117     }
118     super.close();
119   }
120
121   /**
122    * Deletes the underlying file.
123    */
124   public void delete() {
125     try {
126       close();
127       Files.delete(file.toPath());
128     } catch (IOException e) {
129       throw new RuntimeException(e);
130     }
131   }
132
133   private static String parseMode(FileChannel.MapMode mode) {
134     if (mode == FileChannel.MapMode.READ_ONLY) {
135       return "r";
136     } else if (mode == FileChannel.MapMode.READ_WRITE) {
137       return "rw";
138     }
139     throw new IllegalArgumentException("unsupported map mode");
140   }
141
142 }