comparison src/org/tmatesoft/hg/internal/RevlogStream.java @ 608:e1b29756f901

Clean, organize and resolve some TODOs and FIXMEs: minor refactorings and comments
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 07 May 2013 21:27:51 +0200
parents 66f1cc23b906
children 65c01508f002
comparison
equal deleted inserted replaced
607:66f1cc23b906 608:e1b29756f901
48 * @author Artem Tikhomirov 48 * @author Artem Tikhomirov
49 * @author TMate Software Ltd. 49 * @author TMate Software Ltd.
50 */ 50 */
51 public class RevlogStream { 51 public class RevlogStream {
52 52
53 static final int INLINEDATA = 1 << 16;
54
53 /* 55 /*
54 * makes sense for index with inline data only - actual offset of the record in the .i file (record entry + revision * record size)) 56 * makes sense for index with inline data only - actual offset of the record in the .i file (record entry + revision * record size))
55 * 57 *
56 * long[] in fact (there are 8-bytes field in the revlog) 58 * long[] in fact (there are 8-bytes field in the revlog)
57 * However, (a) DataAccess currently doesn't operate with long seek/length 59 * However, (a) DataAccess currently doesn't operate with long seek/length
61 private int[] indexRecordOffset; 63 private int[] indexRecordOffset;
62 private int[] baseRevisions; 64 private int[] baseRevisions;
63 private boolean inline = false; 65 private boolean inline = false;
64 private final File indexFile; 66 private final File indexFile;
65 private File dataFile; 67 private File dataFile;
66 private final DataAccessProvider dataAccess; 68 private final Internals repo;
67 // keeps last complete revision we've read. Note, this cached revision doesn't help 69 // keeps last complete revision we've read. Note, this cached revision doesn't help
68 // for subsequent #iterate() calls with the same revision (Inspector needs more data than 70 // for subsequent #iterate() calls with the same revision (Inspector needs more data than
69 // we currently cache here, perhaps, we shall cache everything it wants to cover same 71 // we currently cache here, perhaps, we shall cache everything it wants to cover same
70 // revision case as well). Now this helps when second #iterate() call is for a revision greater 72 // revision case as well). Now this helps when second #iterate() call is for a revision greater
71 // than one from the first call, and both revisions got same base rev. It's often the case when 73 // than one from the first call, and both revisions got same base rev. It's often the case when
75 // 77 //
76 private final RevlogChangeMonitor changeTracker; 78 private final RevlogChangeMonitor changeTracker;
77 private List<Observer> observers; 79 private List<Observer> observers;
78 private boolean shallDropDerivedCaches = false; 80 private boolean shallDropDerivedCaches = false;
79 81
80 // if we need anything else from HgRepo, might replace DAP parameter with HgRepo and query it for DAP. 82 public RevlogStream(Internals hgRepo, File indexFile) {
81 public RevlogStream(DataAccessProvider dap, File indexFile) { 83 repo = hgRepo;
82 this.dataAccess = dap;
83 this.indexFile = indexFile; 84 this.indexFile = indexFile;
84 // TODO in fact, shall ask Internals for an instance (there we'll decide whether to use 85 changeTracker = repo.getRevlogTracker(indexFile);
85 // one monitor per multiple files or an instance per file; and let SessionContext pass
86 // alternative implementation)
87 changeTracker = new RevlogChangeMonitor(indexFile);
88 } 86 }
89 87
90 /** 88 /**
91 * @param shortRead pass <code>true</code> to indicate intention to read few revisions only (as opposed to reading most of/complete revlog) 89 * @param shortRead pass <code>true</code> to indicate intention to read few revisions only (as opposed to reading most of/complete revlog)
92 * @return never <code>null</code>, empty {@link DataAccess} if no stream is available 90 * @return never <code>null</code>, empty {@link DataAccess} if no stream is available
93 */ 91 */
94 /*package*/ DataAccess getIndexStream(boolean shortRead) { 92 /*package*/ DataAccess getIndexStream(boolean shortRead) {
95 // shortRead hint helps to avoid mmap files when only 93 // shortRead hint helps to avoid mmap files when only
96 // few bytes are to be read (i.e. #dataLength()) 94 // few bytes are to be read (i.e. #dataLength())
95 DataAccessProvider dataAccess = repo.getDataAccess();
97 return dataAccess.createReader(indexFile, shortRead); 96 return dataAccess.createReader(indexFile, shortRead);
98 } 97 }
99 98
100 /*package*/ DataAccess getDataStream() { 99 /*package*/ DataAccess getDataStream() {
100 DataAccessProvider dataAccess = repo.getDataAccess();
101 return dataAccess.createReader(getDataFile(), false); 101 return dataAccess.createReader(getDataFile(), false);
102 } 102 }
103 103
104 /*package*/ DataSerializer getIndexStreamWriter() { 104 /*package*/ DataSerializer getIndexStreamWriter() {
105 DataAccessProvider dataAccess = repo.getDataAccess();
105 return dataAccess.createWriter(indexFile, true); 106 return dataAccess.createWriter(indexFile, true);
106 } 107 }
107 108
108 /*package*/ DataSerializer getDataStreamWriter() { 109 /*package*/ DataSerializer getDataStreamWriter() {
110 DataAccessProvider dataAccess = repo.getDataAccess();
109 return dataAccess.createWriter(getDataFile(), true); 111 return dataAccess.createWriter(getDataFile(), true);
110 } 112 }
111 113
112 /** 114 /**
113 * Constructs file object that corresponds to .d revlog counterpart. 115 * Constructs file object that corresponds to .d revlog counterpart.
299 start = indexSize - 1; 301 start = indexSize - 1;
300 } 302 }
301 HgInternals.checkRevlogRange(start, end, indexSize-1); 303 HgInternals.checkRevlogRange(start, end, indexSize-1);
302 // XXX may cache [start .. end] from index with a single read (pre-read) 304 // XXX may cache [start .. end] from index with a single read (pre-read)
303 305
304 ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); 306 ReaderN1 r = new ReaderN1(needData, inspector, repo.shallMergePatches());
305 try { 307 try {
306 r.start(end - start + 1, getLastRevisionRead()); 308 r.start(end - start + 1, getLastRevisionRead());
307 r.range(start, end); 309 r.range(start, end);
308 } catch (IOException ex) { 310 } catch (IOException ex) {
309 throw new HgInvalidControlFileException(String.format("Failed reading [%d..%d]", start, end), ex, indexFile); 311 throw new HgInvalidControlFileException(String.format("Failed reading [%d..%d]", start, end), ex, indexFile);
310 } catch (HgInvalidControlFileException ex) {
311 throw ex;
312 } finally { 312 } finally {
313 CachedRevision cr = r.finish(); 313 CachedRevision cr = r.finish();
314 setLastRevisionRead(cr); 314 setLastRevisionRead(cr);
315 } 315 }
316 } 316 }
332 } 332 }
333 if (sortedRevisions[sortedRevisions.length - 1] > indexSize) { 333 if (sortedRevisions[sortedRevisions.length - 1] > indexSize) {
334 throw new HgInvalidRevisionException(String.format("Can't iterate [%d, %d] in range [0..%d]", sortedRevisions[0], sortedRevisions[sortedRevisions.length - 1], indexSize), null, sortedRevisions[sortedRevisions.length - 1]); 334 throw new HgInvalidRevisionException(String.format("Can't iterate [%d, %d] in range [0..%d]", sortedRevisions[0], sortedRevisions[sortedRevisions.length - 1], indexSize), null, sortedRevisions[sortedRevisions.length - 1]);
335 } 335 }
336 336
337 ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); 337 ReaderN1 r = new ReaderN1(needData, inspector, repo.shallMergePatches());
338 try { 338 try {
339 r.start(sortedRevisions.length, getLastRevisionRead()); 339 r.start(sortedRevisions.length, getLastRevisionRead());
340 for (int i = 0; i < sortedRevisions.length; ) { 340 for (int i = 0; i < sortedRevisions.length; ) {
341 int x = i; 341 int x = i;
342 i++; 342 i++;
352 return; 352 return;
353 } 353 }
354 } 354 }
355 } catch (IOException ex) { 355 } catch (IOException ex) {
356 final int c = sortedRevisions.length; 356 final int c = sortedRevisions.length;
357 throw new HgInvalidControlFileException(String.format("Failed reading %d revisions in [%d; %d]",c, sortedRevisions[0], sortedRevisions[c-1]), ex, indexFile); 357 throw new HgInvalidControlFileException(String.format("Failed reading %d revisions in [%d; %d]", c, sortedRevisions[0], sortedRevisions[c-1]), ex, indexFile);
358 } catch (HgInvalidControlFileException ex) {
359 // TODO post-1.0 fill HgRuntimeException with appropriate file (either index or data, depending on error source)
360 throw ex;
361 } finally { 358 } finally {
362 CachedRevision cr = r.finish(); 359 CachedRevision cr = r.finish();
363 setLastRevisionRead(cr); 360 setLastRevisionRead(cr);
364 } 361 }
365 } 362 }
486 inline = true; 483 inline = true;
487 return; 484 return;
488 } 485 }
489 int versionField = da.readInt(); 486 int versionField = da.readInt();
490 da.readInt(); // just to skip next 4 bytes of offset + flags 487 da.readInt(); // just to skip next 4 bytes of offset + flags
491 final int INLINEDATA = 1 << 16;
492 inline = (versionField & INLINEDATA) != 0; 488 inline = (versionField & INLINEDATA) != 0;
493 IntVector resBases, resOffsets = null; 489 IntVector resBases, resOffsets = null;
494 int entryCountGuess = Internals.ltoi(da.longLength() / REVLOGV1_RECORD_SIZE); 490 int entryCountGuess = Internals.ltoi(da.longLength() / REVLOGV1_RECORD_SIZE);
495 if (inline) { 491 if (inline) {
496 entryCountGuess >>>= 2; // pure guess, assume useful data takes 3/4 of total space 492 entryCountGuess >>>= 2; // pure guess, assume useful data takes 3/4 of total space
590 private DataAccess lastUserData; 586 private DataAccess lastUserData;
591 // 587 //
592 // next are transient values, for range() use only 588 // next are transient values, for range() use only
593 private final Inflater inflater = new Inflater(); 589 private final Inflater inflater = new Inflater();
594 // can share buffer between instances of InflaterDataAccess as I never read any two of them in parallel 590 // can share buffer between instances of InflaterDataAccess as I never read any two of them in parallel
595 private final byte[] inflaterBuffer = new byte[10 * 1024]; // TODO consider using DAP.DEFAULT_FILE_BUFFER 591 private final byte[] inflaterBuffer = new byte[10 * 1024]; // TODO [post-1.1] consider using DAP.DEFAULT_FILE_BUFFER
596 private final byte[] nodeidBuf = new byte[20]; 592 private final byte[] nodeidBuf = new byte[20];
597 // revlog record fields 593 // revlog record fields
598 private long offset; 594 private long offset;
599 @SuppressWarnings("unused") 595 @SuppressWarnings("unused")
600 private int flags; 596 private int flags;
827 } 823 }
828 } 824 }
829 825
830 826
831 public interface Inspector { 827 public interface Inspector {
832 // XXX boolean retVal to indicate whether to continue? 828 /**
833 // TODO specify nodeid and data length, and reuse policy (i.e. if revlog stream doesn't reuse nodeid[] for each call) 829 * XXX boolean retVal to indicate whether to continue?
834 // implementers shall not invoke DataAccess.done(), it's accomplished by #iterate at appropraite moment 830 *
831 * Implementers shall not invoke DataAccess.done(), it's accomplished by #iterate at appropriate moment
832 *
833 * @param revisionIndex absolute index of revision in revlog being iterated
834 * @param actualLen length of the user data at this revision
835 * @param baseRevision last revision known to hold complete revision (other hold patches).
836 * if baseRevision != revisionIndex, data for this revision is a result of a sequence of patches
837 * @param linkRevision index of corresponding changeset revision
838 * @param parent1Revision index of first parent revision in this revlog, or {@link HgRepository#NO_REVISION}
839 * @param parent2Revision index of second parent revision in this revlog, or {@link HgRepository#NO_REVISION}
840 * @param nodeid 20-byte buffer, shared between invocations
841 * @param data access to revision content of actualLen size, or <code>null</code> if no data has been requested with
842 * {@link RevlogStream#iterate(int[], boolean, Inspector)}
843 */
835 void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data); 844 void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data);
836 } 845 }
837 846
838 public interface Observer { 847 public interface Observer {
839 // notify observer of invalidate/reload event in the stream 848 // notify observer of invalidate/reload event in the stream