Switch jgit library to the EDL (3-clause BSD)
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / lib / WindowedFile.java
blob323b396df6922317d8730e2bf8f7cd359c7c970b
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.lib;
40 import java.io.EOFException;
41 import java.io.File;
42 import java.io.IOException;
43 import java.io.RandomAccessFile;
44 import java.nio.MappedByteBuffer;
45 import java.nio.channels.FileChannel.MapMode;
46 import java.util.zip.DataFormatException;
47 import java.util.zip.Inflater;
49 /**
50 * Read-only cached file access.
51 * <p>
52 * Supports reading data from large read-only files by loading and caching
53 * regions (windows) of the file in memory. Windows may be loaded using either
54 * traditional byte[] or by taking advantage of the operating system's virtual
55 * memory manager and mapping the file into the JVM's address space.
56 * </p>
57 * <p>
58 * Using no MapMode is the most portable way to access a file and does not run
59 * into problems with garbage collection, but will take longer to access as the
60 * entire window must be copied from the operating system into the Java byte[]
61 * before any single byte can be accessed.
62 * </p>
63 * <p>
64 * Using a specific MapMode will avoid the complete copy by mmaping in the
65 * operating system's file buffers, however this may cause problems if a large
66 * number of windows are being heavily accessed as the Java garbage collector
67 * may not be able to unmap old windows fast enough to permit new windows to be
68 * mapped.
69 * </p>
71 public class WindowedFile {
72 private final File fPath;
74 final int hash;
76 private RandomAccessFile fd;
78 private long length;
80 /** Total number of windows actively in the associated cache. */
81 int openCount;
83 /**
84 * Open a file for reading through window caching.
86 * @param file
87 * the file to open.
89 public WindowedFile(final File file) {
90 fPath = file;
91 hash = System.identityHashCode(this);
92 length = Long.MAX_VALUE;
95 /**
96 * Get the total number of bytes available in this file.
98 * @return the number of bytes contained within this file.
100 public long length() {
101 return length;
105 * Get the path name of this file.
107 * @return the absolute path name of the file.
109 public String getName() {
110 return fPath.getAbsolutePath();
114 * Read the bytes into a buffer until it is full.
115 * <p>
116 * This routine always reads until either the requested number of bytes has
117 * been copied or EOF has been reached. Consequently callers do not need to
118 * invoke it in a loop to make sure their buffer has been fully populated.
119 * </p>
121 * @param position
122 * the starting offset, as measured in bytes from the beginning
123 * of this file, to copy from.
124 * @param dstbuf
125 * buffer to copy the bytes into.
126 * @param curs
127 * current cursor for reading data from the file.
128 * @return total number of bytes read. Always <code>dstbuf.length</code>
129 * unless the requested range to copy is over the end of the file.
130 * @throws IOException
131 * a necessary window was not found in the window cache and
132 * trying to load it in from the operating system failed.
134 public int read(final long position, final byte[] dstbuf,
135 final WindowCursor curs) throws IOException {
136 return read(position, dstbuf, 0, dstbuf.length, curs);
140 * Read the requested number of bytes into a buffer.
141 * <p>
142 * This routine always reads until either the requested number of bytes has
143 * been copied or EOF has been reached. Consequently callers do not need to
144 * invoke it in a loop to make sure their buffer has been fully populated.
145 * </p>
147 * @param position
148 * the starting offset, as measured in bytes from the beginning
149 * of this file, to copy from.
150 * @param dstbuf
151 * buffer to copy the bytes into.
152 * @param dstoff
153 * offset within <code>dstbuf</code> to start copying into.
154 * @param cnt
155 * number of bytes to copy. Must not exceed
156 * <code>dstbuf.length - dstoff</code>.
157 * @param curs
158 * current cursor for reading data from the file.
159 * @return total number of bytes read. Always <code>length</code> unless
160 * the requested range to copy is over the end of the file.
161 * @throws IOException
162 * a necessary window was not found in the window cache and
163 * trying to load it in from the operating system failed.
165 public int read(long position, final byte[] dstbuf, int dstoff,
166 final int cnt, final WindowCursor curs) throws IOException {
167 return curs.copy(this, position, dstbuf, dstoff, cnt);
171 * Read the bytes into a buffer until it is full.
172 * <p>
173 * This routine always reads until either the requested number of bytes has
174 * been copied or EOF has been reached. Consequently callers do not need to
175 * invoke it in a loop to make sure their buffer has been fully populated.
176 * </p>
178 * @param position
179 * the starting offset, as measured in bytes from the beginning
180 * of this file, to copy from.
181 * @param dstbuf
182 * buffer to copy the bytes into.
183 * @param curs
184 * current cursor for reading data from the file.
185 * @throws IOException
186 * a necessary window was not found in the window cache and
187 * trying to load it in from the operating system failed.
188 * @throws EOFException
189 * the file ended before <code>dstbuf.length</code> bytes
190 * could be read.
192 public void readFully(final long position, final byte[] dstbuf,
193 final WindowCursor curs) throws IOException {
194 if (read(position, dstbuf, 0, dstbuf.length, curs) != dstbuf.length)
195 throw new EOFException();
198 void readCompressed(final long position, final byte[] dstbuf,
199 final WindowCursor curs) throws IOException, DataFormatException {
200 final Inflater inf = InflaterCache.get();
201 try {
202 if (curs.inflate(this, position, dstbuf, 0, inf) != dstbuf.length)
203 throw new EOFException("Short compressed stream at " + position);
204 } finally {
205 InflaterCache.release(inf);
210 * Overridable hook called after the file is opened.
211 * <p>
212 * This hook is invoked each time the file is opened for reading, but before
213 * the first window is mapped into the cache. Implementers are free to use
214 * any of the window access methods to obtain data, however doing so may
215 * pollute the window cache with otherwise unnecessary windows.
216 * </p>
218 * @throws IOException
219 * something is wrong with the file, for example the caller does
220 * not understand its version header information.
222 protected void onOpen() throws IOException {
223 // Do nothing by default.
226 /** Close this file and remove all open windows. */
227 public void close() {
228 WindowCache.purge(this);
231 void cacheOpen() throws IOException {
232 fd = new RandomAccessFile(fPath, "r");
233 length = fd.length();
234 try {
235 onOpen();
236 } catch (IOException ioe) {
237 cacheClose();
238 throw ioe;
239 } catch (RuntimeException re) {
240 cacheClose();
241 throw re;
242 } catch (Error re) {
243 cacheClose();
244 throw re;
248 void cacheClose() {
249 try {
250 fd.close();
251 } catch (IOException err) {
252 // Ignore a close event. We had it open only for reading.
253 // There should not be errors related to network buffers
254 // not flushed, etc.
256 fd = null;
259 void loadWindow(final WindowCursor curs, final int windowId,
260 final long pos, final int windowSize) throws IOException {
261 if (WindowCache.mmap) {
262 final MappedByteBuffer map = fd.getChannel().map(MapMode.READ_ONLY,
263 pos, windowSize);
264 if (map.hasArray()) {
265 final byte[] b = map.array();
266 curs.window = new ByteArrayWindow(this, pos, windowId, b);
267 curs.handle = b;
268 } else {
269 curs.window = new ByteBufferWindow(this, pos, windowId, map);
270 curs.handle = map;
272 return;
275 final byte[] b = new byte[windowSize];
276 synchronized (fd) {
277 fd.seek(pos);
278 fd.readFully(b);
280 curs.window = new ByteArrayWindow(this, pos, windowId, b);
281 curs.handle = b;