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()) {