[log] Create BekSorter.

This commit is contained in:
Erokhin Stanislav
2014-05-22 19:32:46 +04:00
parent e3177e86f4
commit 9774870c9b
7 changed files with 513 additions and 3 deletions
@@ -22,6 +22,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.graph.*;
import com.intellij.vcs.log.graph.api.LinearGraph;
import com.intellij.vcs.log.graph.api.permanent.PermanentGraphInfo;
import com.intellij.vcs.log.graph.impl.facade.bek.*;
import com.intellij.vcs.log.graph.impl.permanent.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -45,7 +46,7 @@ public class PermanentGraphImpl<CommitId> implements PermanentGraph<CommitId>, P
public int compare(@NotNull Integer nodeIndex1, @NotNull Integer nodeIndex2) {
CommitId commitId1 = commitIdPermanentCommitsInfo.getCommitId(nodeIndex1);
CommitId commitId2 = commitIdPermanentCommitsInfo.getCommitId(nodeIndex2);
return graphColorManager.compareHeads(commitId1, commitId2);
return graphColorManager.compareHeads(commitId2, commitId1);
}
});
@@ -69,6 +70,8 @@ public class PermanentGraphImpl<CommitId> implements PermanentGraph<CommitId>, P
private final Map<CommitId, GraphCommit<CommitId>> myCommitsWithNotLoadParent;
@NotNull
private final ContainingBranchesGetter myBranchesGetter;
@NotNull
private final PermanentGraphInfo<CommitId> myBekGraphInfo;
public PermanentGraphImpl(@NotNull PermanentLinearGraphImpl permanentLinearGraph,
@NotNull GraphLayoutImpl permanentGraphLayout,
@@ -84,6 +87,7 @@ public class PermanentGraphImpl<CommitId> implements PermanentGraph<CommitId>, P
myCommitsWithNotLoadParent = commitsWithNotLoadParent;
myBranchNodeIndexes = permanentCommitsInfo.convertToCommitIndexes(branchesCommitId);
myBranchesGetter = new ContainingBranchesGetter(permanentLinearGraph, myBranchNodeIndexes);
myBekGraphInfo = createBekSort();
}
@NotNull
@@ -92,12 +96,29 @@ public class PermanentGraphImpl<CommitId> implements PermanentGraph<CommitId>, P
@Nullable Set<CommitId> headsOfVisibleBranches,
@Nullable Condition<CommitId> filter) {
if (filter == null) {
return CollapsedVisibleGraph.newInstance(this, headsOfVisibleBranches);
return CollapsedVisibleGraph.newInstance(getBekPermanentGraphInfo(sortType), headsOfVisibleBranches);
} else {
return FilterVisibleGraph.newInstance(this, headsOfVisibleBranches, filter);
return FilterVisibleGraph.newInstance(getBekPermanentGraphInfo(sortType), headsOfVisibleBranches, filter);
}
}
@NotNull
private PermanentGraphInfo<CommitId> getBekPermanentGraphInfo(@NotNull SortType sortType) {
if (sortType == SortType.Normal)
return this;
else
return myBekGraphInfo;
}
@NotNull
private PermanentGraphInfo<CommitId> createBekSort() {
BekIntMap bekMap = BekSorter.createBekMap(myPermanentLinearGraph, myPermanentGraphLayout, myPermanentCommitsInfo.getTimestampGetter());
DelegatedPermanentGraphInfo<CommitId> graphInfo = new DelegatedPermanentGraphInfo<CommitId>(this, bekMap);
assert BekChecker.checkLinearGraph(graphInfo.getPermanentLinearGraph());
return graphInfo;
}
@NotNull
@Override
public List<GraphCommit<CommitId>> getAllCommits() {
@@ -0,0 +1,101 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.graph.impl.facade.bek;
import com.intellij.vcs.log.graph.api.LinearGraph;
import com.intellij.vcs.log.graph.utils.TimestampGetter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
class BekBranch {
private static final int MAX_BLOCK_SIZE = 20;
private static final int MAX_DELTA_TIME = 60 * 60 * 24 * 3 * 1000;
private static final int SMALL_DELTA_TIME = 60 * 60 * 4 * 1000;
@NotNull
private final LinearGraph myPermanentGraph;
@NotNull
private final List<Integer> myNodeIndexes;
private int myNoInsertSize;
@Nullable
private List<Integer> myPrepareForInsertPart = null;
public BekBranch(@NotNull LinearGraph permanentGraph, @NotNull List<Integer> nodeIndexes) {
myPermanentGraph = permanentGraph;
myNodeIndexes = nodeIndexes;
myNoInsertSize = myNodeIndexes.size();
}
public void updatePrepareForInsertPart(@NotNull TimestampGetter timestampGetter, @NotNull BekEdgeRestrictions edgeRestrictions) {
assert myPrepareForInsertPart == null;
int currentNode = myNodeIndexes.get(myNoInsertSize - 1);
if (edgeRestrictions.hasRestriction(currentNode))
return;
int prevIndex;
for (prevIndex = myNoInsertSize - 1; prevIndex > 0; prevIndex--) {
int upNode = myNodeIndexes.get(prevIndex - 1);
int downNode = myNodeIndexes.get(prevIndex);
// for correct topological order
if (edgeRestrictions.hasRestriction(upNode))
break;
// upNode is mergeCommit
List<Integer> downNodes = myPermanentGraph.getDownNodes(upNode);
if (downNodes.size() > 1 && downNodes.contains(downNode))
continue;
// division
if (!downNodes.contains(downNode))
break;
long delta = Math.abs(timestampGetter.getTimestamp(upNode) - timestampGetter.getTimestamp(downNode));
// long time between commits
if (delta > MAX_DELTA_TIME)
break;
// if block so long
if (prevIndex < myNoInsertSize - MAX_BLOCK_SIZE && delta > SMALL_DELTA_TIME)
break;
}
myPrepareForInsertPart = myNodeIndexes.subList(prevIndex, myNoInsertSize);
}
@Nullable
public List<Integer> getPrepareForInsertPart() {
return myPrepareForInsertPart;
}
public void doneInsertPreparedPart() {
assert myPrepareForInsertPart != null;
myNoInsertSize -= myPrepareForInsertPart.size();
myPrepareForInsertPart = null;
}
public boolean isDone() {
return myNoInsertSize == 0;
}
}
@@ -0,0 +1,107 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.graph.impl.facade.bek;
import com.intellij.openapi.util.Pair;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.graph.api.LinearGraph;
import com.intellij.vcs.log.graph.impl.permanent.GraphLayoutImpl;
import com.intellij.vcs.log.graph.utils.DfsUtil;
import com.intellij.vcs.log.graph.utils.Flags;
import com.intellij.vcs.log.graph.utils.impl.BitSetFlags;
import org.jetbrains.annotations.NotNull;
import java.util.List;
class BekBranchCreator {
@NotNull
private final LinearGraph myPermanentGraph;
@NotNull
private final GraphLayoutImpl myGraphLayout;
@NotNull
private final Flags myDoneNodes;
@NotNull
private final DfsUtil myDfsUtil = new DfsUtil();
@NotNull
private final BekEdgeRestrictions myEdgeRestrictions = new BekEdgeRestrictions();
public BekBranchCreator(@NotNull LinearGraph permanentGraph, @NotNull GraphLayoutImpl graphLayout) {
myPermanentGraph = permanentGraph;
myGraphLayout = graphLayout;
myDoneNodes = new BitSetFlags(permanentGraph.nodesCount(), false);
}
@NotNull
public Pair<List<BekBranch>, BekEdgeRestrictions> getResult() {
List<BekBranch> bekBranches = ContainerUtil.newArrayList();
for (int headNode : myGraphLayout.getHeadNodeIndex()) {
List<Integer> nextBranch = createNextBranch(headNode);
bekBranches.add(new BekBranch(myPermanentGraph, nextBranch));
}
return Pair.create(bekBranches, myEdgeRestrictions);
}
public List<Integer> createNextBranch(int headNode) {
final List<Integer> nodeIndexes = ContainerUtil.newArrayList();
assert !myDoneNodes.get(headNode);
myDoneNodes.set(headNode, true);
nodeIndexes.add(headNode);
final int startLayout = myGraphLayout.getLayoutIndex(headNode);
myDfsUtil.nodeDfsIterator(headNode, new DfsUtil.NextNode() {
@Override
public int fun(int currentNode) {
int currentLayout = myGraphLayout.getLayoutIndex(currentNode);
List<Integer> downNodes = myPermanentGraph.getDownNodes(currentNode);
for (int i = downNodes.size() - 1; i >= 0; i--) {
int downNode = downNodes.get(i);
if (downNode == LinearGraph.NOT_LOAD_COMMIT)
continue;
if (myDoneNodes.get(downNode)) {
if (myGraphLayout.getLayoutIndex(downNode) < startLayout)
myEdgeRestrictions.addRestriction(currentNode, downNode);
}
else if (currentLayout <= myGraphLayout.getLayoutIndex(downNode)) {
// almost ok node, except (may be) up nodes
boolean hasUndoneUpNodes = false;
for (int upNode : myPermanentGraph.getUpNodes(downNode)) {
if (!myDoneNodes.get(upNode) && myGraphLayout.getLayoutIndex(upNode) <= currentLayout) {
hasUndoneUpNodes = true;
break;
}
}
if (!hasUndoneUpNodes) {
myDoneNodes.set(downNode, true);
nodeIndexes.add(downNode);
return downNode;
}
}
}
return NODE_NOT_FOUND;
}
});
return nodeIndexes;
}
}
@@ -0,0 +1,92 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.graph.impl.facade.bek;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.graph.utils.TimestampGetter;
import org.jetbrains.annotations.NotNull;
import java.util.List;
class BekBranchMerger {
@NotNull
private final List<BekBranch> myBekBranches;
@NotNull
private final BekEdgeRestrictions myEdgeRestrictions;
@NotNull
private final TimestampGetter myTimestampGetter;
@NotNull
private final List<Integer> myInverseResultList = ContainerUtil.newArrayList();
public BekBranchMerger(@NotNull List<BekBranch> bekBranches,
@NotNull BekEdgeRestrictions edgeRestrictions,
@NotNull TimestampGetter timestampGetter) {
myBekBranches = bekBranches;
myEdgeRestrictions = edgeRestrictions;
myTimestampGetter = timestampGetter;
}
// return true, if exist some undone branch
private boolean prepareLastPartsForBranches() {
boolean hasUndoneBranches = false;
for (BekBranch bekBranch : myBekBranches) {
if (!bekBranch.isDone()){
hasUndoneBranches = true;
if (bekBranch.getPrepareForInsertPart() == null) {
bekBranch.updatePrepareForInsertPart(myTimestampGetter, myEdgeRestrictions);
}
}
}
return hasUndoneBranches;
}
private long getBranchLastPartTimestamp(BekBranch bekBranch) {
List<Integer> prepareForInsertPart = bekBranch.getPrepareForInsertPart();
if (prepareForInsertPart == null)
return Long.MAX_VALUE;
assert !prepareForInsertPart.isEmpty();
int nodeIndex = prepareForInsertPart.get(0);
return myTimestampGetter.getTimestamp(nodeIndex);
}
private void step() {
BekBranch selectBranch = myBekBranches.get(0);
for (BekBranch bekBranch : myBekBranches) {
if (getBranchLastPartTimestamp(selectBranch) > getBranchLastPartTimestamp(bekBranch)) {
selectBranch = bekBranch;
}
}
List<Integer> prepareForInsertPart = selectBranch.getPrepareForInsertPart();
assert prepareForInsertPart != null;
for (int insertedNode: prepareForInsertPart)
myEdgeRestrictions.removeRestriction(insertedNode);
myInverseResultList.addAll(ContainerUtil.reverse(prepareForInsertPart));
selectBranch.doneInsertPreparedPart();
}
@NotNull
public List<Integer> getResult() {
while (prepareLastPartsForBranches()) {
step();
}
return ContainerUtil.reverse(myInverseResultList);
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.graph.impl.facade.bek;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.vcs.log.graph.api.LinearGraph;
import org.jetbrains.annotations.NotNull;
public class BekChecker {
private final static Logger LOG = Logger.getInstance("#com.intellij.vcs.log.graph.impl.facade.bek.BekChecker");
public static boolean checkLinearGraph(@NotNull LinearGraph linearGraph) {
for (int i = 0; i < linearGraph.nodesCount(); i++) {
for (int downNode : linearGraph.getDownNodes(i)) {
if (downNode <= i)
LOG.error("Illegal node: " + i + ", with downNode: " + downNode);
}
for (int upNode : linearGraph.getUpNodes(i)) {
if (upNode >= i)
LOG.error("Illegal node: " + i + ", with upNode: " + upNode);
}
}
return true;
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.graph.impl.facade.bek;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
class BekEdgeRestrictions {
@NotNull
private final MultiMap<Integer, Integer> myUpToEdge = new MultiMap<Integer, Integer>();
@NotNull
private final MultiMap<Integer, Integer> myDownToEdge = new MultiMap<Integer, Integer>();
void addRestriction(int upNode, int downNode) {
myUpToEdge.putValue(upNode, downNode);
myDownToEdge.putValue(downNode, upNode);
}
void removeRestriction(int downNode) {
for (int upNode: myDownToEdge.get(downNode)) {
myUpToEdge.remove(upNode, downNode);
}
}
boolean hasRestriction(int upNode) {
return myUpToEdge.containsKey(upNode);
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.graph.impl.facade.bek;
import com.intellij.openapi.util.Pair;
import com.intellij.vcs.log.graph.api.LinearGraph;
import com.intellij.vcs.log.graph.impl.permanent.GraphLayoutImpl;
import com.intellij.vcs.log.graph.utils.IntList;
import com.intellij.vcs.log.graph.utils.TimestampGetter;
import com.intellij.vcs.log.graph.utils.impl.CompressedIntList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class BekSorter {
@NotNull
public static BekIntMap createBekMap(@NotNull LinearGraph permanentGraph,
@NotNull GraphLayoutImpl graphLayout,
@NotNull TimestampGetter timestampGetter) {
BekSorter bekSorter = new BekSorter(permanentGraph, graphLayout, timestampGetter);
List<Integer> result = bekSorter.getResult();
assert result.size() == permanentGraph.nodesCount();
return createBekIntMap(result);
}
private static BekIntMap createBekIntMap(final List<Integer> result) {
final int[] reverseMap = new int[result.size()];
for (int i = 0; i < result.size(); i++) {
reverseMap[result.get(i)] = i;
}
final IntList compressedBekMap = CompressedIntList.newInstance(new IntList() {
@Override
public int size() {
return result.size();
}
@Override
public int get(int index) {
return result.get(index);
}
}, CompressedIntList.DEFAULT_BLOCK_SIZE);
final IntList compressedReverseMap = CompressedIntList.newInstance(reverseMap);
return new BekIntMap() {
@Override
public int size() {
return compressedBekMap.size();
}
@Override
public int getBekIndex(int usualIndex) {
if (usualIndex == LinearGraph.NOT_LOAD_COMMIT)
return LinearGraph.NOT_LOAD_COMMIT;
return compressedReverseMap.get(usualIndex);
}
@Override
public int getUsualIndex(int bekIndex) {
if (bekIndex == LinearGraph.NOT_LOAD_COMMIT)
return LinearGraph.NOT_LOAD_COMMIT;
return compressedBekMap.get(bekIndex);
}
};
}
@NotNull
private final LinearGraph myPermanentGraph;
@NotNull
private final GraphLayoutImpl myGraphLayout;
@NotNull
private final TimestampGetter myTimestampGetter;
private BekSorter(@NotNull LinearGraph permanentGraph, @NotNull GraphLayoutImpl graphLayout, @NotNull TimestampGetter timestampGetter) {
myPermanentGraph = permanentGraph;
myGraphLayout = graphLayout;
myTimestampGetter = timestampGetter;
}
public List<Integer> getResult() {
BekBranchCreator bekBranchCreator = new BekBranchCreator(myPermanentGraph, myGraphLayout);
Pair<List<BekBranch>, BekEdgeRestrictions> branches = bekBranchCreator.getResult();
BekBranchMerger bekBranchMerger = new BekBranchMerger(branches.first, branches.second, myTimestampGetter);
return bekBranchMerger.getResult();
}
}