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;