comparison src/org/tmatesoft/hg/repo/Revlog.java @ 695:053bb4397bf9

Refactoring: nice Revlog.indexWalk() implementation
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 05 Aug 2013 18:45:16 +0200
parents 7efabe0cddcf
children
comparison
equal deleted inserted replaced
694:7efabe0cddcf 695:053bb4397bf9
29 import org.tmatesoft.hg.internal.DataAccess; 29 import org.tmatesoft.hg.internal.DataAccess;
30 import org.tmatesoft.hg.internal.Experimental; 30 import org.tmatesoft.hg.internal.Experimental;
31 import org.tmatesoft.hg.internal.IntMap; 31 import org.tmatesoft.hg.internal.IntMap;
32 import org.tmatesoft.hg.internal.Preview; 32 import org.tmatesoft.hg.internal.Preview;
33 import org.tmatesoft.hg.internal.RevisionLookup; 33 import org.tmatesoft.hg.internal.RevisionLookup;
34 import org.tmatesoft.hg.internal.RevlogDelegate;
34 import org.tmatesoft.hg.internal.RevlogStream; 35 import org.tmatesoft.hg.internal.RevlogStream;
35 import org.tmatesoft.hg.util.Adaptable; 36 import org.tmatesoft.hg.util.Adaptable;
36 import org.tmatesoft.hg.util.ByteChannel; 37 import org.tmatesoft.hg.util.ByteChannel;
37 import org.tmatesoft.hg.util.CancelSupport; 38 import org.tmatesoft.hg.util.CancelSupport;
38 import org.tmatesoft.hg.util.CancelledException; 39 import org.tmatesoft.hg.util.CancelledException;
350 end = lastRev; 351 end = lastRev;
351 } 352 }
352 final RevisionInspector revisionInsp = Adaptable.Factory.getAdapter(inspector, RevisionInspector.class, null); 353 final RevisionInspector revisionInsp = Adaptable.Factory.getAdapter(inspector, RevisionInspector.class, null);
353 final ParentInspector parentInsp = Adaptable.Factory.getAdapter(inspector, ParentInspector.class, null); 354 final ParentInspector parentInsp = Adaptable.Factory.getAdapter(inspector, ParentInspector.class, null);
354 final LinkRevisionInspector linkRevInspector = Adaptable.Factory.getAdapter(inspector, LinkRevisionInspector.class, null); 355 final LinkRevisionInspector linkRevInspector = Adaptable.Factory.getAdapter(inspector, LinkRevisionInspector.class, null);
355 final Nodeid[] allRevisions = parentInsp == null ? null : new Nodeid[end - _start + 1]; 356
356 // next are to build set of parent indexes that are not part of the range iteration 357 // instantiate implementors in reverse order
357 // i.e. those parents we need to read separately. See Issue 31 for details. 358 RevlogDelegate head = parentInsp == null ? null : new ParentDelegate(parentInsp, null, _start, end);
358 final int[] firstParentIndexes = parentInsp == null || _start == 0 ? null : new int[allRevisions.length]; 359 if (revisionInsp != null) {
359 final int[] secondParentIndexes = parentInsp == null || _start == 0 ? null : new int[allRevisions.length]; 360 head = new RevisionDelegate(revisionInsp, head);
360 final IntMap<Nodeid> missingParents = parentInsp == null || _start == 0 ? null : new IntMap<Nodeid>(16); 361 }
361 362 if (linkRevInspector != null) {
362 content.iterate(_start, end, false, new RevlogStream.Inspector() { 363 head = new LinkRevDelegate(linkRevInspector, head);
363 private int i = 0; 364 }
364 365 // first to get notified is created last
365 public void next(int revisionIndex, int actualLen, int baseRevIndex, int linkRevIndex, int parent1RevIndex, int parent2RevIndex, byte[] nodeid, DataAccess data) throws HgRuntimeException { 366
366 // FIXME refactor either as chain of RevlogStream.Inspector or an external AnyOf() runner not to keep everything 367 assert head != null; // we know all subclasses of Revlog.Inspector
367 // in a single method 368 head.walk(getRepo(), content, _start, end);
368 if (linkRevInspector != null) { 369 }
369 linkRevInspector.next(revisionIndex, linkRevIndex); 370
370 if (parentInsp == null && revisionInsp == null) {
371 return;
372 }
373 }
374 Nodeid nid = Nodeid.fromBinary(nodeid, 0);
375 if (revisionInsp != null) {
376 revisionInsp.next(revisionIndex, nid, linkRevIndex);
377 }
378 if (parentInsp != null) {
379 allRevisions[i] = nid;
380 if (_start > 0) {
381 // there are chances we don't know parents here,
382 // postpone parent dispatching for later, now just collect what's missing
383 firstParentIndexes[i] = parent1RevIndex;
384 secondParentIndexes[i] = parent2RevIndex;
385 if (parent1RevIndex < _start && parent1RevIndex >= 0) {
386 missingParents.put(parent1RevIndex, null);
387 }
388 if (parent2RevIndex < _start && parent2RevIndex >= 0) {
389 missingParents.put(parent2RevIndex, null);
390 }
391 } else {
392 // we iterate from the very beginning, got every index we'll need
393 Nodeid p1 = parent1RevIndex == -1 ? Nodeid.NULL : allRevisions[parent1RevIndex];
394 Nodeid p2 = parent2RevIndex == -1 ? Nodeid.NULL : allRevisions[parent2RevIndex];
395 parentInsp.next(revisionIndex, allRevisions[i], parent1RevIndex, parent2RevIndex, p1, p2);
396 }
397 i++;
398 }
399 }
400 });
401 if (parentInsp != null && _start > 0 ) {
402 if (missingParents.size() > 0) {
403 // it's possible to get empty missingParents when _start > 0 e.g. when n-th file revision
404 // is a copy of another file and hence got -1,-1 parents in this revlog, and we indexWalk(n,n)
405 for (int k = missingParents.firstKey(), l = missingParents.lastKey(); k <= l; k++) {
406 // TODO [post-1.1] int[] IntMap#keys() or even sort of iterator that can modify values
407 if (missingParents.containsKey(k)) {
408 Nodeid nid = getRepo().getChangelog().getRevision(k);
409 missingParents.put(k, nid);
410 }
411 }
412 }
413
414 for (int i = 0, revNum = _start; i < allRevisions.length; i++, revNum++) {
415 int riP1 = firstParentIndexes[i];
416 int riP2 = secondParentIndexes[i];
417 Nodeid p1, p2;
418 p1 = p2 = Nodeid.NULL;
419 if (riP1 >= _start) {
420 // p1 of revNum's revision is out of iterated range
421 // (don't check for riP1<end as I assume parents come prior to children in the changelog)
422 p1 = allRevisions[riP1 - start];
423 } else if (riP1 != -1) {
424 assert riP1 >=0 && riP1 < _start;
425 p1 = missingParents.get(riP1);
426 assert p1 != null;
427 }
428 // same for Pp2
429 if (riP2 >= _start) {
430 p2 = allRevisions[riP2 - start];
431 } else if (riP2 != -1) {
432 assert riP2 >= 0 && riP2 < _start;
433 p2 = missingParents.get(riP2);
434 assert p2 != null;
435 }
436 parentInsp.next(revNum, allRevisions[i], riP1, riP2, p1, p2);
437 }
438 }
439 }
440
441 /** 371 /**
442 * MARKER 372 * MARKER
443 */ 373 */
444 @Experimental 374 @Experimental
445 public interface Inspector { 375 public interface Inspector {
575 } catch (CancelledException ex) { 505 } catch (CancelledException ex) {
576 recordFailure(ex); 506 recordFailure(ex);
577 } 507 }
578 } 508 }
579 } 509 }
510
511
512 private static final class LinkRevDelegate extends RevlogDelegate {
513 private final LinkRevisionInspector insp;
514
515 public LinkRevDelegate(LinkRevisionInspector inspector, RevlogDelegate next) {
516 super(next);
517 assert inspector != null;
518 insp = inspector;
519 }
520 @Override
521 public void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgRuntimeException {
522 insp.next(revisionIndex, linkRevision);
523 super.next(revisionIndex, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeid, data);
524 }
525 }
526
527 private static final class RevisionDelegate extends RevlogDelegate {
528 private final RevisionInspector insp;
529 public RevisionDelegate(RevisionInspector inspector, RevlogDelegate next) {
530 super(next);
531 assert inspector != null;
532 insp = inspector;
533 }
534 @Override
535 public void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgRuntimeException {
536 Nodeid nid = getRevision(nodeid);
537 insp.next(revisionIndex, nid, linkRevision);
538 super.next(revisionIndex, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeid, data);
539 }
540 }
541
542 private static class ParentDelegate extends RevlogDelegate {
543 private final ParentInspector insp;
544 private int i = 0;
545 private final Nodeid[] allRevisions;
546 // next are to build set of parent indexes that are not part of the range iteration
547 // i.e. those parents we need to read separately. See Issue 31 for details.
548 private final int[] firstParentIndexes;
549 private final int[] secondParentIndexes;
550 private final IntMap<Nodeid> missingParents;
551 private final int start;
552
553 public ParentDelegate(ParentInspector inspector, RevlogDelegate next, int _start, int end) {
554 super(next);
555 insp = inspector;
556 start = _start;
557 allRevisions = new Nodeid[end - _start + 1];
558 firstParentIndexes = _start == 0 ? null : new int[allRevisions.length];
559 secondParentIndexes = _start == 0 ? null : new int[allRevisions.length];
560 missingParents = _start == 0 ? null : new IntMap<Nodeid>(16);
561 }
562 @Override
563 public void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1RevIndex, int parent2RevIndex, byte[] nodeid, DataAccess data) throws HgRuntimeException {
564 allRevisions[i] = getRevision(nodeid);
565 if (start > 0) {
566 // there are chances we don't know parents here,
567 // postpone parent dispatching for later, now just collect what's missing
568 firstParentIndexes[i] = parent1RevIndex;
569 secondParentIndexes[i] = parent2RevIndex;
570 if (parent1RevIndex < start && parent1RevIndex >= 0) {
571 missingParents.put(parent1RevIndex, null);
572 }
573 if (parent2RevIndex < start && parent2RevIndex >= 0) {
574 missingParents.put(parent2RevIndex, null);
575 }
576 } else {
577 // we iterate from the very beginning, got every index we'll need
578 Nodeid p1 = parent1RevIndex == -1 ? Nodeid.NULL : allRevisions[parent1RevIndex];
579 Nodeid p2 = parent2RevIndex == -1 ? Nodeid.NULL : allRevisions[parent2RevIndex];
580 insp.next(revisionIndex, allRevisions[i], parent1RevIndex, parent2RevIndex, p1, p2);
581 }
582 i++;
583 super.next(revisionIndex, actualLen, baseRevision, linkRevision, parent1RevIndex, parent2RevIndex, nodeid, data);
584 }
585
586 @Override
587 protected void postWalk(HgRepository hgRepo) {
588 if (start > 0) {
589 complete(hgRepo);
590 }
591 super.postWalk(hgRepo);
592 }
593
594 private void complete(HgRepository repo) {
595 if (missingParents.size() > 0) {
596 // it's possible to get empty missingParents when _start > 0 e.g. when n-th file revision
597 // is a copy of another file and hence got -1,-1 parents in this revlog, and we indexWalk(n,n)
598 for (int k = missingParents.firstKey(), l = missingParents.lastKey(); k <= l; k++) {
599 // TODO [post-1.1] int[] IntMap#keys() or even sort of iterator that can modify values
600 if (missingParents.containsKey(k)) {
601 Nodeid nid = repo.getChangelog().getRevision(k);
602 missingParents.put(k, nid);
603 }
604 }
605 }
606
607 for (int i = 0, revNum = start; i < allRevisions.length; i++, revNum++) {
608 int riP1 = firstParentIndexes[i];
609 int riP2 = secondParentIndexes[i];
610 Nodeid p1, p2;
611 p1 = p2 = Nodeid.NULL;
612 if (riP1 >= start) {
613 // p1 of revNum's revision is out of iterated range
614 // (don't check for riP1<end as I assume parents come prior to children in the changelog)
615 p1 = allRevisions[riP1 - start];
616 } else if (riP1 != -1) {
617 assert riP1 >=0 && riP1 < start;
618 p1 = missingParents.get(riP1);
619 assert p1 != null;
620 }
621 // same for Pp2
622 if (riP2 >= start) {
623 p2 = allRevisions[riP2 - start];
624 } else if (riP2 != -1) {
625 assert riP2 >= 0 && riP2 < start;
626 p2 = missingParents.get(riP2);
627 assert p2 != null;
628 }
629 insp.next(revNum, allRevisions[i], riP1, riP2, p1, p2);
630 }
631 }
632 }
580 } 633 }