30
30
int PyDict_SetItem(object d, object k, object v) except -1
31
31
void Py_INCREF(object)
35
36
from bzrlib import revision
38
heappush = heapq.heappush
39
heappop = heapq.heappop
38
42
cdef class _KnownGraphNode:
39
43
"""Represents a single object in the known graph."""
42
# Ideally, we could change this into fixed arrays, rather than get the
43
# extra allocations. Consider that 99% of all revisions will have <= 2
44
# parents, we may want to put in the effort
46
# 99% of all revisions have <= 2 parents, so we pre-allocate space for it,
47
# but allow the flexibility of having N parents
48
cdef _KnownGraphNode left_parent
49
cdef _KnownGraphNode right_parent
47
52
cdef _KnownGraphNode linear_dominator_node
51
56
cdef object ancestor_of
53
58
def __init__(self, key, parents):
59
cdef _KnownGraphNode _parent_node
56
self.parents = parents
58
if not isinstance(parents, list):
59
raise TypeError('parents must be a list')
60
for parent in parents:
61
if not isinstance(parent, _KnownGraphNode):
62
raise TypeError('parents must be a list of _KnownGraphNode')
63
self.parents = parents
63
self.left_parent = None
64
self.right_parent = None
65
self.set_parents(parents)
65
68
# oldest ancestor, such that no parents between here and there have >1
66
69
# child or >1 parent.
90
93
return self.linear_dominator_node.key
95
cdef set_parents(self, list parents):
97
self.parents = parents
100
num_parents = len(self.parents)
105
self.left_parent = self.parents[0]
107
self.right_parent = self.parents[1]
92
109
cdef clear_references(self):
93
110
self.parents = None
94
111
self.children = None
95
112
self.linear_dominator_node = None
113
self.left_parent = None
114
self.right_parent = None
97
116
def __repr__(self):
378
401
heads.append(<object>test_key)
379
402
return dom_lookup_key, PyFrozenSet_New(heads)
404
cdef int _process_parent(self, _KnownGraphNode node,
405
_KnownGraphNode parent_node,
406
dict candidate_nodes,
408
"""Process the parent of a node, seeing if we need to walk it.
410
:return: 0 No extra work needed
411
1 This was a candidate node, and now there is only 1 candidate
412
left, so break out of the loop
414
cdef PyObject *maybe_candidate
415
maybe_candidate = PyDict_GetItem(candidate_nodes, parent_node.key)
416
if maybe_candidate != NULL:
417
candidate_nodes.pop(parent_node.key)
418
if len(candidate_nodes) <= 1:
420
if parent_node.ancestor_of is None:
421
# This node hasn't been walked yet, so just project node's ancestor
422
# info directly to parent_node, and enqueue it for later processing
423
parent_node.ancestor_of = node.ancestor_of
424
heappush(queue, (-parent_node.gdfo, parent_node))
425
self._to_cleanup.append(parent_node)
426
elif parent_node.ancestor_of != node.ancestor_of:
427
# Combine to get the full set of parents
428
# Rewrite using PySet_* functions, unfortunately you have to use
429
# PySet_Add since there is no PySet_Update... :(
430
all_ancestors = set(parent_node.ancestor_of)
431
all_ancestors.update(node.ancestor_of)
432
parent_node.ancestor_of = tuple(sorted(all_ancestors))
381
435
cdef object _heads_from_candidate_nodes(self, dict candidate_nodes):
383
436
cdef _KnownGraphNode node
384
437
cdef _KnownGraphNode parent_node
385
438
cdef int num_candidates
389
443
for node in candidate_nodes.itervalues():
390
444
assert node.ancestor_of is None
391
445
node.ancestor_of = (node.key,)
392
446
queue.append((-node.gdfo, node))
393
to_cleanup.append(node)
447
self._to_cleanup.append(node)
394
448
heapq.heapify(queue)
395
449
# These are nodes that we determined are 'common' that we are no longer
423
477
# We are at the tip of a long linear region
424
478
# We know that there is nothing between here and the tail
425
479
# that is interesting, so skip to the end
426
parents = [node.linear_dominator_node]
480
if (self._process_parent(node, node.linear_dominator_node,
481
candidate_nodes, queue)):
428
parents = node.parents
429
for parent_node in node.parents:
430
if parent_node.key in candidate_nodes:
431
candidate_nodes.pop(parent_node.key)
432
if len(candidate_nodes) <= 1:
434
if parent_node.ancestor_of is None:
435
# This node hasn't been walked yet
436
parent_node.ancestor_of = node.ancestor_of
438
heappush(queue, (-parent_node.gdfo, parent_node))
439
to_cleanup.append(parent_node)
440
elif parent_node.ancestor_of != node.ancestor_of:
441
# Combine to get the full set of parents
442
all_ancestors = set(parent_node.ancestor_of)
443
all_ancestors.update(node.ancestor_of)
444
parent_node.ancestor_of = tuple(sorted(all_ancestors))
445
for node in to_cleanup:
484
num_parents = len(node.parents)
486
for parent_node in node.parents:
487
if (self._process_parent(node, parent_node,
488
candidate_nodes, queue)):
492
if (self._process_parent(node, node.left_parent,
493
candidate_nodes, queue)):
496
if (self._process_parent(node, node.right_parent,
497
candidate_nodes, queue)):
499
for node in self._to_cleanup:
446
500
node.ancestor_of = None
447
501
return PyFrozenSet_New(candidate_nodes)