Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/Revlog.java @ 243:0e01f9182e16
External cache Nodeid<->int added, Revlog.RevisionMap
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 23 Jun 2011 16:58:38 +0200 |
parents | 047b1dec7a04 |
children | 9fb50c04f03c |
comparison
equal
deleted
inserted
replaced
242:ad6a046943be | 243:0e01f9182e16 |
---|---|
14 * the terms of a license other than GNU General Public License | 14 * the terms of a license other than GNU General Public License |
15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
16 */ | 16 */ |
17 package org.tmatesoft.hg.repo; | 17 package org.tmatesoft.hg.repo; |
18 | 18 |
19 import static org.tmatesoft.hg.core.Nodeid.NULL; | |
19 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; | 20 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; |
20 import static org.tmatesoft.hg.repo.HgRepository.TIP; | 21 import static org.tmatesoft.hg.repo.HgRepository.TIP; |
21 | 22 |
22 import java.io.IOException; | 23 import java.io.IOException; |
23 import java.nio.ByteBuffer; | 24 import java.nio.ByteBuffer; |
85 public final Nodeid getRevision(int revision) { | 86 public final Nodeid getRevision(int revision) { |
86 // XXX cache nodeids? | 87 // XXX cache nodeids? |
87 return Nodeid.fromBinary(content.nodeid(revision), 0); | 88 return Nodeid.fromBinary(content.nodeid(revision), 0); |
88 } | 89 } |
89 | 90 |
91 /** | |
92 * Get local revision number (index) of the specified revision. | |
93 * | |
94 * For occasional queries, this method works with decent performance, despite its O(n/2) approach. | |
95 * Alternatively, if you need to perform multiple queries (e.g. at least 15-20), {@link RevisionMap} may come handy. | |
96 * | |
97 * @param nid revision to look up | |
98 * @return revision index, or {@link HgRepository#BAD_REVISION} if specified revision not found. | |
99 */ | |
90 public final int getLocalRevision(Nodeid nid) { | 100 public final int getLocalRevision(Nodeid nid) { |
91 int revision = content.findLocalRevisionNumber(nid); | 101 int revision = content.findLocalRevisionNumber(nid); |
92 if (revision == BAD_REVISION) { | 102 if (revision == BAD_REVISION) { |
93 throw new IllegalArgumentException(String.format("%s doesn't represent a revision of %s", nid.toString(), this /*XXX HgDataFile.getPath might be more suitable here*/)); | 103 throw new IllegalArgumentException(String.format("%s doesn't represent a revision of %s", nid.toString(), this /*XXX HgDataFile.getPath might be more suitable here*/)); |
94 } | 104 } |
227 | 237 |
228 int ix = 0; | 238 int ix = 0; |
229 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { | 239 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { |
230 if (ix != revisionNumber) { | 240 if (ix != revisionNumber) { |
231 // XXX temp code, just to make sure I understand what's going on here | 241 // XXX temp code, just to make sure I understand what's going on here |
242 // FIXME check against cpython or another tool-mangled repository | |
232 throw new IllegalStateException(); | 243 throw new IllegalStateException(); |
233 } | 244 } |
234 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { | 245 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { |
235 throw new IllegalStateException(); // sanity, revisions are sequential | 246 throw new IllegalStateException(); // sanity, revisions are sequential |
236 } | 247 } |
368 } | 379 } |
369 return false; | 380 return false; |
370 } | 381 } |
371 } | 382 } |
372 | 383 |
384 /** | |
385 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of | |
386 * multiple {@link Revlog#getLocalRevision(Nodeid)} calls. | |
387 * | |
388 * getLocalRevision(Nodeid) with straightforward lookup approach performs O(n/2) | |
389 * #localRevision() is log(n), plus initialization is O(n) | |
390 */ | |
391 public final class RevisionMap { | |
392 /* | |
393 * in fact, initialization is much slower as it instantiates Nodeids, while #getLocalRevision | |
394 * compares directly against byte buffer. Measuring cpython with 70k+ gives 3 times difference (47 vs 171) | |
395 * for complete changelog iteration. | |
396 */ | |
397 | |
398 /* | |
399 * XXX 3 * (x * 4) bytes. Can I do better? | |
400 */ | |
401 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering | |
402 private Nodeid[] sorted; // for binary search | |
403 private int[] sorted2natural; | |
404 | |
405 public RevisionMap() { | |
406 } | |
407 | |
408 public HgRepository getRepo() { | |
409 return Revlog.this.getRepo(); | |
410 } | |
411 | |
412 /** | |
413 * @return <code>this</code> for convenience. | |
414 */ | |
415 public RevisionMap init(/*XXX Pool<Nodeid> to reuse nodeids, if possible. */) { | |
416 // XXX HgRepository.register((RepoChangeListener) this); // listen to changes in repo, re-init if needed? | |
417 final int revisionCount = Revlog.this.content.revisionCount(); | |
418 sequential = new Nodeid[revisionCount]; | |
419 sorted = new Nodeid[revisionCount]; | |
420 sorted2natural = new int[revisionCount]; | |
421 RevlogStream.Inspector insp = new RevlogStream.Inspector() { | |
422 | |
423 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | |
424 final Nodeid nid = new Nodeid(nodeid, true); | |
425 sequential[revisionNumber] = sorted[revisionNumber] = nid; | |
426 } | |
427 }; | |
428 Revlog.this.content.iterate(0, TIP, false, insp); | |
429 Arrays.sort(sorted); | |
430 for (int i = 0; i < revisionCount; i++) { | |
431 Nodeid n = sequential[i]; | |
432 int x = Arrays.binarySearch(sorted, n); | |
433 sorted2natural[x] = i; | |
434 } | |
435 return this; | |
436 } | |
437 | |
438 public Nodeid revision(int localRevision) { | |
439 return sequential[localRevision]; | |
440 } | |
441 public int localRevision(Nodeid revision) { | |
442 if (revision == null || NULL.equals(revision)) { | |
443 return BAD_REVISION; | |
444 } | |
445 int x = Arrays.binarySearch(sorted, revision); | |
446 if (x < 0) { | |
447 return BAD_REVISION; | |
448 } | |
449 return sorted2natural[x]; | |
450 } | |
451 } | |
452 | |
453 | |
373 protected static class ContentPipe implements RevlogStream.Inspector, CancelSupport { | 454 protected static class ContentPipe implements RevlogStream.Inspector, CancelSupport { |
374 private final ByteChannel sink; | 455 private final ByteChannel sink; |
375 private final CancelSupport cancelSupport; | 456 private final CancelSupport cancelSupport; |
376 private Exception failure; | 457 private Exception failure; |
377 private final int offset; | 458 private final int offset; |