Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/DataAccessProvider.java @ 420:6c22bdc0bdfd
Respect long offsets in revlogs
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 22 Mar 2012 22:56:01 +0100 |
parents | bb278ccf9866 |
children | 48f993aa2f41 |
comparison
equal
deleted
inserted
replaced
419:7f136a3fa671 | 420:6c22bdc0bdfd |
---|---|
21 import java.io.IOException; | 21 import java.io.IOException; |
22 import java.nio.ByteBuffer; | 22 import java.nio.ByteBuffer; |
23 import java.nio.MappedByteBuffer; | 23 import java.nio.MappedByteBuffer; |
24 import java.nio.channels.FileChannel; | 24 import java.nio.channels.FileChannel; |
25 | 25 |
26 import org.tmatesoft.hg.core.HgBadStateException; | |
27 import org.tmatesoft.hg.core.SessionContext; | 26 import org.tmatesoft.hg.core.SessionContext; |
28 | 27 |
29 /** | 28 /** |
30 * | 29 * |
31 * @author Artem Tikhomirov | 30 * @author Artem Tikhomirov |
67 if (!f.exists()) { | 66 if (!f.exists()) { |
68 return new DataAccess(); | 67 return new DataAccess(); |
69 } | 68 } |
70 try { | 69 try { |
71 FileChannel fc = new FileInputStream(f).getChannel(); | 70 FileChannel fc = new FileInputStream(f).getChannel(); |
72 int flen = (int) fc.size(); | 71 long flen = fc.size(); |
73 if (fc.size() - flen != 0) { | |
74 throw new HgBadStateException("Files greater than 2Gb are not yet supported"); | |
75 } | |
76 if (flen > mapioMagicBoundary) { | 72 if (flen > mapioMagicBoundary) { |
77 // TESTS: bufLen of 1024 was used to test MemMapFileAccess | 73 // TESTS: bufLen of 1024 was used to test MemMapFileAccess |
78 return new MemoryMapFileAccess(fc, flen, getConfigOption(context, CFG_PROPERTY_MAPIO_BUFFER_SIZE, 100*1024 /*same as default boundary*/)); | 74 return new MemoryMapFileAccess(fc, flen, getConfigOption(context, CFG_PROPERTY_MAPIO_BUFFER_SIZE, 100*1024 /*same as default boundary*/)); |
79 } else { | 75 } else { |
80 // XXX once implementation is more or less stable, | 76 // XXX once implementation is more or less stable, |
89 context.getLog().error(getClass(), ex, null); | 85 context.getLog().error(getClass(), ex, null); |
90 } | 86 } |
91 return new DataAccess(); // non-null, empty. | 87 return new DataAccess(); // non-null, empty. |
92 } | 88 } |
93 | 89 |
94 // DOESN'T WORK YET | |
95 private static class MemoryMapFileAccess extends DataAccess { | 90 private static class MemoryMapFileAccess extends DataAccess { |
96 private FileChannel fileChannel; | 91 private FileChannel fileChannel; |
97 private final int size; | 92 private final long size; |
98 private long position = 0; // always points to buffer's absolute position in the file | 93 private long position = 0; // always points to buffer's absolute position in the file |
99 private final int memBufferSize; | 94 private final int memBufferSize; |
100 private MappedByteBuffer buffer; | 95 private MappedByteBuffer buffer; |
101 | 96 |
102 public MemoryMapFileAccess(FileChannel fc, int channelSize, int bufferSize) { | 97 public MemoryMapFileAccess(FileChannel fc, long channelSize, int bufferSize) { |
103 fileChannel = fc; | 98 fileChannel = fc; |
104 size = channelSize; | 99 size = channelSize; |
105 memBufferSize = bufferSize > channelSize ? channelSize : bufferSize; // no reason to waste memory more than there's data | 100 memBufferSize = bufferSize > channelSize ? (int) channelSize : bufferSize; // no reason to waste memory more than there's data |
106 } | 101 } |
107 | 102 |
108 @Override | 103 @Override |
109 public boolean isEmpty() { | 104 public boolean isEmpty() { |
110 return position + (buffer == null ? 0 : buffer.position()) >= size; | 105 return position + (buffer == null ? 0 : buffer.position()) >= size; |
111 } | 106 } |
112 | 107 |
113 @Override | 108 @Override |
109 public DataAccess reset() throws IOException { | |
110 longSeek(0); | |
111 return this; | |
112 } | |
113 | |
114 @Override | |
114 public int length() { | 115 public int length() { |
116 return Internals.ltoi(longLength()); | |
117 } | |
118 | |
119 @Override | |
120 public long longLength() { | |
115 return size; | 121 return size; |
116 } | 122 } |
117 | 123 |
118 @Override | 124 @Override |
119 public DataAccess reset() throws IOException { | 125 public void longSeek(long offset) { |
120 seek(0); | |
121 return this; | |
122 } | |
123 | |
124 @Override | |
125 public void seek(int offset) { | |
126 assert offset >= 0; | 126 assert offset >= 0; |
127 // offset may not necessarily be further than current position in the file (e.g. rewind) | 127 // offset may not necessarily be further than current position in the file (e.g. rewind) |
128 if (buffer != null && /*offset is within buffer*/ offset >= position && (offset - position) < buffer.limit()) { | 128 if (buffer != null && /*offset is within buffer*/ offset >= position && (offset - position) < buffer.limit()) { |
129 buffer.position((int) (offset - position)); | 129 // cast is ok according to check above |
130 buffer.position(Internals.ltoi(offset - position)); | |
130 } else { | 131 } else { |
131 position = offset; | 132 position = offset; |
132 buffer = null; | 133 buffer = null; |
133 } | 134 } |
135 } | |
136 | |
137 @Override | |
138 public void seek(int offset) { | |
139 longSeek(offset); | |
134 } | 140 } |
135 | 141 |
136 @Override | 142 @Override |
137 public void skip(int bytes) throws IOException { | 143 public void skip(int bytes) throws IOException { |
138 assert bytes >= 0; | 144 assert bytes >= 0; |
204 } | 210 } |
205 | 211 |
206 // (almost) regular file access - FileChannel and buffers. | 212 // (almost) regular file access - FileChannel and buffers. |
207 private static class FileAccess extends DataAccess { | 213 private static class FileAccess extends DataAccess { |
208 private FileChannel fileChannel; | 214 private FileChannel fileChannel; |
209 private final int size; | 215 private final long size; |
210 private ByteBuffer buffer; | 216 private ByteBuffer buffer; |
211 private int bufferStartInFile = 0; // offset of this.buffer in the file. | 217 private long bufferStartInFile = 0; // offset of this.buffer in the file. |
212 | 218 |
213 public FileAccess(FileChannel fc, int channelSize, int bufferSizeHint, boolean useDirect) { | 219 public FileAccess(FileChannel fc, long channelSize, int bufferSizeHint, boolean useDirect) { |
214 fileChannel = fc; | 220 fileChannel = fc; |
215 size = channelSize; | 221 size = channelSize; |
216 final int capacity = size < bufferSizeHint ? size : bufferSizeHint; | 222 final int capacity = size < bufferSizeHint ? (int) size : bufferSizeHint; |
217 buffer = useDirect ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity); | 223 buffer = useDirect ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity); |
218 buffer.flip(); // or .limit(0) to indicate it's empty | 224 buffer.flip(); // or .limit(0) to indicate it's empty |
219 } | 225 } |
220 | 226 |
221 @Override | 227 @Override |
222 public boolean isEmpty() { | 228 public boolean isEmpty() { |
223 return bufferStartInFile + buffer.position() >= size; | 229 return bufferStartInFile + buffer.position() >= size; |
224 } | 230 } |
225 | 231 |
226 @Override | 232 @Override |
233 public DataAccess reset() throws IOException { | |
234 longSeek(0); | |
235 return this; | |
236 } | |
237 | |
238 @Override | |
227 public int length() { | 239 public int length() { |
240 return Internals.ltoi(longLength()); | |
241 } | |
242 | |
243 @Override | |
244 public long longLength() { | |
228 return size; | 245 return size; |
229 } | 246 } |
230 | 247 |
231 @Override | 248 @Override |
232 public DataAccess reset() throws IOException { | 249 public void longSeek(long offset) throws IOException { |
233 seek(0); | |
234 return this; | |
235 } | |
236 | |
237 @Override | |
238 public void seek(int offset) throws IOException { | |
239 if (offset > size) { | 250 if (offset > size) { |
240 throw new IllegalArgumentException(String.format("Can't seek to %d for the file of size %d (buffer start:%d)", offset, size, bufferStartInFile)); | 251 throw new IllegalArgumentException(String.format("Can't seek to %d for the file of size %d (buffer start:%d)", offset, size, bufferStartInFile)); |
241 } | 252 } |
242 if (offset < bufferStartInFile + buffer.limit() && offset >= bufferStartInFile) { | 253 if (offset < bufferStartInFile + buffer.limit() && offset >= bufferStartInFile) { |
243 buffer.position((int) (offset - bufferStartInFile)); | 254 // cast to int is safe, we've checked we fit into buffer |
255 buffer.position(Internals.ltoi(offset - bufferStartInFile)); | |
244 } else { | 256 } else { |
245 // out of current buffer, invalidate it (force re-read) | 257 // out of current buffer, invalidate it (force re-read) |
246 // XXX or ever re-read it right away? | 258 // XXX or ever re-read it right away? |
247 bufferStartInFile = offset; | 259 bufferStartInFile = offset; |
248 buffer.clear(); | 260 buffer.clear(); |
250 fileChannel.position(offset); | 262 fileChannel.position(offset); |
251 } | 263 } |
252 } | 264 } |
253 | 265 |
254 @Override | 266 @Override |
267 public void seek(int offset) throws IOException { | |
268 longSeek(offset); | |
269 } | |
270 | |
271 @Override | |
255 public void skip(int bytes) throws IOException { | 272 public void skip(int bytes) throws IOException { |
256 final int newPos = buffer.position() + bytes; | 273 final int newPos = buffer.position() + bytes; |
257 if (newPos >= 0 && newPos < buffer.limit()) { | 274 if (newPos >= 0 && newPos < buffer.limit()) { |
258 // no need to move file pointer, just rewind/seek buffer | 275 // no need to move file pointer, just rewind/seek buffer |
259 buffer.position(newPos); | 276 buffer.position(newPos); |
260 } else { | 277 } else { |
261 // | 278 // |
262 seek(bufferStartInFile + newPos); | 279 longSeek(bufferStartInFile + newPos); |
263 } | 280 } |
264 } | 281 } |
265 | 282 |
266 private boolean fill() throws IOException { | 283 private boolean fill() throws IOException { |
267 if (!buffer.hasRemaining()) { | 284 if (!buffer.hasRemaining()) { |