Import atomix/{storage,utils}
[controller.git] / third-party / atomix / storage / src / main / java / io / atomix / storage / buffer / FileBuffer.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.memory.Memory;
19
20 import java.io.File;
21 import java.nio.channels.FileChannel;
22
23 import static com.google.common.base.Preconditions.checkArgument;
24
25 /**
26  * File buffer.
27  * <p>
28  * File buffers wrap a simple {@link java.io.RandomAccessFile} instance to provide random access to a file on local disk. All
29  * operations are delegated directly to the {@link java.io.RandomAccessFile} interface, and limitations are dependent on the
30  * semantics of the underlying file.
31  *
32  * @author <a href="http://github.com/kuujo">Jordan Halterman</a>
33  */
34 public class FileBuffer extends AbstractBuffer {
35
36   /**
37    * Allocates a file buffer of unlimited capacity.
38    * <p>
39    * The buffer will initially be allocated with {@code 4096} bytes. As bytes are written to the resulting buffer and
40    * the original capacity is reached, the buffer's capacity will double.
41    *
42    * @param file The file to allocate.
43    * @return The allocated buffer.
44    * @see FileBuffer#allocate(File, int)
45    * @see FileBuffer#allocate(File, int, int)
46    * @see FileBuffer#allocate(File, String, int, int)
47    */
48   public static FileBuffer allocate(File file) {
49     return allocate(file, FileBytes.DEFAULT_MODE, DEFAULT_INITIAL_CAPACITY, Integer.MAX_VALUE);
50   }
51
52   /**
53    * Allocates a file buffer with the given initial capacity.
54    * <p>
55    * If the underlying file is empty, the file count will expand dynamically as bytes are written to the file.
56    * The underlying {@link FileBytes} will be initialized to the nearest power of {@code 2}.
57    *
58    * @param file            The file to allocate.
59    * @param initialCapacity The initial capacity of the bytes to allocate.
60    * @return The allocated buffer.
61    * @see FileBuffer#allocate(File)
62    * @see FileBuffer#allocate(File, int, int)
63    * @see FileBuffer#allocate(File, String, int, int)
64    */
65   public static FileBuffer allocate(File file, int initialCapacity) {
66     return allocate(file, FileBytes.DEFAULT_MODE, initialCapacity, Integer.MAX_VALUE);
67   }
68
69   /**
70    * Allocates a file buffer.
71    * <p>
72    * The underlying {@link java.io.RandomAccessFile} will be created in {@code rw} mode by default.
73    * The resulting buffer will be initialized with a capacity of {@code initialCapacity}. The underlying {@link FileBytes}
74    * will be initialized to the nearest power of {@code 2}. As bytes are written to the file the buffer's capacity will
75    * double up to {@code maxCapacity}.
76    *
77    * @param file            The file to allocate.
78    * @param initialCapacity The initial capacity of the buffer.
79    * @param maxCapacity     The maximum allowed capacity of the buffer.
80    * @return The allocated buffer.
81    * @see FileBuffer#allocate(File)
82    * @see FileBuffer#allocate(File, int)
83    * @see FileBuffer#allocate(File, String, int, int)
84    */
85   public static FileBuffer allocate(File file, int initialCapacity, int maxCapacity) {
86     return allocate(file, FileBytes.DEFAULT_MODE, initialCapacity, maxCapacity);
87   }
88
89   /**
90    * Allocates a file buffer.
91    * <p>
92    * The resulting buffer will be initialized with a capacity of {@code initialCapacity}. The underlying {@link FileBytes}
93    * will be initialized to the nearest power of {@code 2}. As bytes are written to the file the buffer's capacity will
94    * double up to {@code maxCapacity}.
95    *
96    * @param file            The file to allocate.
97    * @param mode            The mode in which to open the underlying {@link java.io.RandomAccessFile}.
98    * @param initialCapacity The initial capacity of the buffer.
99    * @param maxCapacity     The maximum allowed capacity of the buffer.
100    * @return The allocated buffer.
101    * @see FileBuffer#allocate(File)
102    * @see FileBuffer#allocate(File, int)
103    * @see FileBuffer#allocate(File, int, int)
104    */
105   public static FileBuffer allocate(File file, String mode, int initialCapacity, int maxCapacity) {
106     checkArgument(initialCapacity <= maxCapacity, "initial capacity cannot be greater than maximum capacity");
107     return new FileBuffer(new FileBytes(file, mode, (int) Math.min(Memory.Util.toPow2(initialCapacity), maxCapacity)), 0, initialCapacity, maxCapacity);
108   }
109
110   private final FileBytes bytes;
111
112   private FileBuffer(FileBytes bytes, int offset, int initialCapacity, int maxCapacity) {
113     super(bytes, offset, initialCapacity, maxCapacity, null);
114     this.bytes = bytes;
115   }
116
117   /**
118    * Returns the underlying file object.
119    *
120    * @return The underlying file.
121    */
122   public File file() {
123     return ((FileBytes) bytes).file();
124   }
125
126   /**
127    * Maps a portion of the underlying file into memory in {@link FileChannel.MapMode#READ_WRITE} mode
128    * starting at the current position up to the given {@code count}.
129    *
130    * @param size The count of the bytes to map into memory.
131    * @return The mapped buffer.
132    * @throws IllegalArgumentException If {@code count} is greater than the maximum allowed
133    *                                  {@link java.nio.MappedByteBuffer} count: {@link Integer#MAX_VALUE}
134    */
135   public MappedBuffer map(int size) {
136     return map(position(), size, FileChannel.MapMode.READ_WRITE);
137   }
138
139   /**
140    * Maps a portion of the underlying file into memory starting at the current position up to the given {@code count}.
141    *
142    * @param size The count of the bytes to map into memory.
143    * @param mode The mode in which to map the bytes into memory.
144    * @return The mapped buffer.
145    * @throws IllegalArgumentException If {@code count} is greater than the maximum allowed
146    *                                  {@link java.nio.MappedByteBuffer} count: {@link Integer#MAX_VALUE}
147    */
148   public MappedBuffer map(int size, FileChannel.MapMode mode) {
149     return map(position(), size, mode);
150   }
151
152   /**
153    * Maps a portion of the underlying file into memory in {@link FileChannel.MapMode#READ_WRITE} mode
154    * starting at the given {@code offset} up to the given {@code count}.
155    *
156    * @param offset The offset from which to map bytes into memory.
157    * @param size   The count of the bytes to map into memory.
158    * @return The mapped buffer.
159    * @throws IllegalArgumentException If {@code count} is greater than the maximum allowed
160    *                                  {@link java.nio.MappedByteBuffer} count: {@link Integer#MAX_VALUE}
161    */
162   public MappedBuffer map(int offset, int size) {
163     return map(offset, size, FileChannel.MapMode.READ_WRITE);
164   }
165
166   /**
167    * Maps a portion of the underlying file into memory starting at the given {@code offset} up to the given {@code count}.
168    *
169    * @param offset The offset from which to map bytes into memory.
170    * @param size   The count of the bytes to map into memory.
171    * @param mode   The mode in which to map the bytes into memory.
172    * @return The mapped buffer.
173    * @throws IllegalArgumentException If {@code count} is greater than the maximum allowed
174    *                                  {@link java.nio.MappedByteBuffer} count: {@link Integer#MAX_VALUE}
175    */
176   public MappedBuffer map(int offset, int size, FileChannel.MapMode mode) {
177     return new MappedBuffer(((FileBytes) bytes).map(offset, size, mode), 0, size, size);
178   }
179
180   @Override
181   protected void compact(int from, int to, int length) {
182     byte[] bytes = new byte[1024];
183     int position = from;
184     while (position < from + length) {
185       int size = Math.min((from + length) - position, 1024);
186       this.bytes.read(position, bytes, 0, size);
187       this.bytes.write(0, bytes, 0, size);
188       position += size;
189     }
190   }
191
192   @Override
193   public FileBuffer duplicate() {
194     return new FileBuffer(new FileBytes(bytes.file(), bytes.mode(), bytes.size()), offset(), capacity(), maxCapacity());
195   }
196
197   /**
198    * Duplicates the buffer using the given mode.
199    *
200    * @return The mode with which to open the duplicate buffer.
201    */
202   public FileBuffer duplicate(String mode) {
203     return new FileBuffer(new FileBytes(bytes.file(), mode, bytes.size()), offset(), capacity(), maxCapacity());
204   }
205
206   /**
207    * Deletes the underlying file.
208    */
209   public void delete() {
210     bytes.delete();
211   }
212
213 }