Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/Revlog.java @ 432:1fc0da631200
Revlog.ParentWalker helper class got promoted as TLC, renamed to HgParentChildMap
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 30 Mar 2012 16:22:51 +0200 |
| parents | 12f668401613 |
| children | be697c3e951e |
comparison
equal
deleted
inserted
replaced
| 431:12f668401613 | 432:1fc0da631200 |
|---|---|
| 20 | 20 |
| 21 import java.io.IOException; | 21 import java.io.IOException; |
| 22 import java.nio.ByteBuffer; | 22 import java.nio.ByteBuffer; |
| 23 import java.util.ArrayList; | 23 import java.util.ArrayList; |
| 24 import java.util.Arrays; | 24 import java.util.Arrays; |
| 25 import java.util.Collection; | |
| 26 import java.util.HashSet; | |
| 27 import java.util.LinkedList; | |
| 28 import java.util.List; | 25 import java.util.List; |
| 29 | 26 |
| 30 import org.tmatesoft.hg.core.Nodeid; | 27 import org.tmatesoft.hg.core.Nodeid; |
| 31 import org.tmatesoft.hg.internal.ArrayHelper; | 28 import org.tmatesoft.hg.internal.ArrayHelper; |
| 32 import org.tmatesoft.hg.internal.DataAccess; | 29 import org.tmatesoft.hg.internal.DataAccess; |
| 335 public interface ParentInspector extends Inspector { | 332 public interface ParentInspector extends Inspector { |
| 336 // XXX document whether parentX is -1 or a constant (BAD_REVISION? or dedicated?) | 333 // XXX document whether parentX is -1 or a constant (BAD_REVISION? or dedicated?) |
| 337 void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2); | 334 void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2); |
| 338 } | 335 } |
| 339 | 336 |
| 340 /* | 337 protected HgParentChildMap<? extends Revlog> getParentWalker() { |
| 341 * FIXME think over if it's better to do either: | 338 HgParentChildMap<Revlog> pw = new HgParentChildMap<Revlog>(this); |
| 342 * pw = getChangelog().new ParentWalker(); pw.init() and pass pw instance around as needed | 339 pw.init(); |
| 343 * or | 340 return pw; |
| 344 * add Revlog#getParentWalker(), static class, make cons() and #init package-local, and keep SoftReference to allow walker reuse. | |
| 345 * | |
| 346 * and yes, walker is not a proper name | |
| 347 */ | |
| 348 public final class ParentWalker implements ParentInspector { | |
| 349 | |
| 350 | |
| 351 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering | |
| 352 private Nodeid[] sorted; // for binary search | |
| 353 private int[] sorted2natural; | |
| 354 private Nodeid[] firstParent; | |
| 355 private Nodeid[] secondParent; | |
| 356 | |
| 357 // Nodeid instances shall be shared between all arrays | |
| 358 | |
| 359 public ParentWalker() { | |
| 360 } | |
| 361 | |
| 362 public HgRepository getRepo() { | |
| 363 return Revlog.this.getRepo(); | |
| 364 } | |
| 365 | |
| 366 public void next(int revisionNumber, Nodeid revision, int parent1Revision, int parent2Revision, Nodeid nidParent1, Nodeid nidParent2) { | |
| 367 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { | |
| 368 throw new IllegalStateException(); // sanity, revisions are sequential | |
| 369 } | |
| 370 int ix = revisionNumber; | |
| 371 sequential[ix] = sorted[ix] = revision; | |
| 372 if (parent1Revision != -1) { | |
| 373 firstParent[ix] = sequential[parent1Revision]; | |
| 374 } | |
| 375 if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1 | |
| 376 secondParent[ix] = sequential[parent2Revision]; | |
| 377 } | |
| 378 } | |
| 379 | |
| 380 public void init() throws HgInvalidControlFileException { | |
| 381 final int revisionCount = Revlog.this.getRevisionCount(); | |
| 382 firstParent = new Nodeid[revisionCount]; | |
| 383 // TODO [post 1.0] Branches/merges are less frequent, and most of secondParent would be -1/null, hence | |
| 384 // IntMap might be better alternative here, but need to carefully analyze (test) whether this brings | |
| 385 // real improvement (IntMap has 2n capacity, and element lookup is log(n) instead of array's constant) | |
| 386 secondParent = new Nodeid[revisionCount]; | |
| 387 // | |
| 388 sequential = new Nodeid[revisionCount]; | |
| 389 sorted = new Nodeid[revisionCount]; | |
| 390 Revlog.this.indexWalk(0, TIP, this); | |
| 391 Arrays.sort(sorted); | |
| 392 sorted2natural = new int[revisionCount]; | |
| 393 for (int i = 0; i < revisionCount; i++) { | |
| 394 Nodeid n = sequential[i]; | |
| 395 int x = Arrays.binarySearch(sorted, n); | |
| 396 assertSortedIndex(x); | |
| 397 sorted2natural[x] = i; | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 private void assertSortedIndex(int x) { | |
| 402 if (x < 0) { | |
| 403 throw new HgInvalidStateException(String.format("Bad index", x)); | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 /** | |
| 408 * Tells whether supplied revision is from the walker's associated revlog. | |
| 409 * Note, {@link Nodeid#NULL}, although implicitly present as parent of a first revision, is not recognized as known. | |
| 410 * @param nid revision to check, not <code>null</code> | |
| 411 * @return <code>true</code> if revision matches any revision in this revlog | |
| 412 */ | |
| 413 public boolean knownNode(Nodeid nid) { | |
| 414 return Arrays.binarySearch(sorted, nid) >= 0; | |
| 415 } | |
| 416 | |
| 417 /** | |
| 418 * null if none. only known nodes (as per #knownNode) are accepted as arguments | |
| 419 */ | |
| 420 public Nodeid firstParent(Nodeid nid) { | |
| 421 int x = Arrays.binarySearch(sorted, nid); | |
| 422 assertSortedIndex(x); | |
| 423 int i = sorted2natural[x]; | |
| 424 return firstParent[i]; | |
| 425 } | |
| 426 | |
| 427 // never null, Nodeid.NULL if none known | |
| 428 public Nodeid safeFirstParent(Nodeid nid) { | |
| 429 Nodeid rv = firstParent(nid); | |
| 430 return rv == null ? Nodeid.NULL : rv; | |
| 431 } | |
| 432 | |
| 433 public Nodeid secondParent(Nodeid nid) { | |
| 434 int x = Arrays.binarySearch(sorted, nid); | |
| 435 assertSortedIndex(x); | |
| 436 int i = sorted2natural[x]; | |
| 437 return secondParent[i]; | |
| 438 } | |
| 439 | |
| 440 public Nodeid safeSecondParent(Nodeid nid) { | |
| 441 Nodeid rv = secondParent(nid); | |
| 442 return rv == null ? Nodeid.NULL : rv; | |
| 443 } | |
| 444 | |
| 445 public boolean appendParentsOf(Nodeid nid, Collection<Nodeid> c) { | |
| 446 int x = Arrays.binarySearch(sorted, nid); | |
| 447 assertSortedIndex(x); | |
| 448 int i = sorted2natural[x]; | |
| 449 Nodeid p1 = firstParent[i]; | |
| 450 boolean modified = false; | |
| 451 if (p1 != null) { | |
| 452 modified = c.add(p1); | |
| 453 } | |
| 454 Nodeid p2 = secondParent[i]; | |
| 455 if (p2 != null) { | |
| 456 modified = c.add(p2) || modified; | |
| 457 } | |
| 458 return modified; | |
| 459 } | |
| 460 | |
| 461 // XXX alternative (and perhaps more reliable) approach would be to make a copy of allNodes and remove | |
| 462 // nodes, their parents and so on. | |
| 463 | |
| 464 // @return ordered collection of all children rooted at supplied nodes. Nodes shall not be descendants of each other! | |
| 465 // Nodeids shall belong to this revlog | |
| 466 public List<Nodeid> childrenOf(List<Nodeid> roots) { | |
| 467 HashSet<Nodeid> parents = new HashSet<Nodeid>(); | |
| 468 LinkedList<Nodeid> result = new LinkedList<Nodeid>(); | |
| 469 int earliestRevision = Integer.MAX_VALUE; | |
| 470 assert sequential.length == firstParent.length && firstParent.length == secondParent.length; | |
| 471 // first, find earliest index of roots in question, as there's no sense | |
| 472 // to check children among nodes prior to branch's root node | |
| 473 for (Nodeid r : roots) { | |
| 474 int x = Arrays.binarySearch(sorted, r); | |
| 475 assertSortedIndex(x); | |
| 476 int i = sorted2natural[x]; | |
| 477 if (i < earliestRevision) { | |
| 478 earliestRevision = i; | |
| 479 } | |
| 480 parents.add(sequential[i]); // add canonical instance in hope equals() is bit faster when can do a == | |
| 481 } | |
| 482 for (int i = earliestRevision + 1; i < sequential.length; i++) { | |
| 483 if (parents.contains(firstParent[i]) || parents.contains(secondParent[i])) { | |
| 484 parents.add(sequential[i]); // to find next child | |
| 485 result.add(sequential[i]); | |
| 486 } | |
| 487 } | |
| 488 return result; | |
| 489 } | |
| 490 | |
| 491 /** | |
| 492 * @return revisions that have supplied revision as their immediate parent | |
| 493 */ | |
| 494 public List<Nodeid> directChildren(Nodeid nid) { | |
| 495 LinkedList<Nodeid> result = new LinkedList<Nodeid>(); | |
| 496 int x = Arrays.binarySearch(sorted, nid); | |
| 497 assertSortedIndex(x); | |
| 498 nid = sorted[x]; // canonical instance | |
| 499 int start = sorted2natural[x]; | |
| 500 for (int i = start + 1; i < sequential.length; i++) { | |
| 501 if (nid == firstParent[i] || nid == secondParent[i]) { | |
| 502 result.add(sequential[i]); | |
| 503 } | |
| 504 } | |
| 505 return result; | |
| 506 } | |
| 507 | |
| 508 /** | |
| 509 * @param nid possibly parent node, shall be {@link #knownNode(Nodeid) known} in this revlog. | |
| 510 * @return <code>true</code> if there's any node in this revlog that has specified node as one of its parents. | |
| 511 */ | |
| 512 public boolean hasChildren(Nodeid nid) { | |
| 513 int x = Arrays.binarySearch(sorted, nid); | |
| 514 assertSortedIndex(x); | |
| 515 int i = sorted2natural[x]; | |
| 516 assert firstParent.length == secondParent.length; // just in case later I implement sparse array for secondParent | |
| 517 assert firstParent.length == sequential.length; | |
| 518 // to use == instead of equals, take the same Nodeid instance we used to fill all the arrays. | |
| 519 final Nodeid canonicalNode = sequential[i]; | |
| 520 i++; // no need to check node itself. child nodes may appear in sequential only after revision in question | |
| 521 for (; i < sequential.length; i++) { | |
| 522 // TODO [post 1.0] likely, not very effective. | |
| 523 // May want to optimize it with another (Tree|Hash)Set, created on demand on first use, | |
| 524 // however, need to be careful with memory usage | |
| 525 if (firstParent[i] == canonicalNode || secondParent[i] == canonicalNode) { | |
| 526 return true; | |
| 527 } | |
| 528 } | |
| 529 return false; | |
| 530 } | |
| 531 } | 341 } |
| 532 | 342 |
| 533 /** | 343 /** |
| 534 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of | 344 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of |
| 535 * multiple {@link Revlog#getRevisionIndex(Nodeid)} calls. | 345 * multiple {@link Revlog#getRevisionIndex(Nodeid)} calls. |
