Mercurial > jhg
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 |