'''
import itertools
-import weakref
from p2pool.util import skiplist, variable
skiplist.SkipList.__init__(self)
self.tracker = tracker
- self_ref = weakref.ref(self, lambda _: tracker.removed.unwatch(watch_id))
- watch_id = self.tracker.removed.watch(lambda share: self_ref().forget_item(share.hash))
+ self.tracker.removed.watch_weakref(self, lambda self, item: self.forget_item(item.hash))
def previous(self, element):
- return self.tracker.delta_type.from_element(self.tracker.shares[element]).tail
+ return self.tracker._delta_type.from_element(self.tracker.items[element]).tail
class DistanceSkipList(TrackerSkipList):
return cls(element_id, element_id, **dict((k, 0) for k in attrs))
@classmethod
- def from_element(cls, share):
- return cls(share.hash, share.previous_hash, **dict((k, v(share)) for k, v in attrs.iteritems()))
+ def from_element(cls, item):
+ return cls(item.hash, item.previous_hash, **dict((k, v(item)) for k, v in attrs.iteritems()))
def __init__(self, head, tail, **kwargs):
self.head, self.tail = head, tail
height=lambda item: 1,
))
+class TrackerView(object):
+ def __init__(self, tracker, delta_type):
+ self._tracker = tracker
+ self._delta_type = delta_type
+
+ self._deltas = {} # item_hash -> delta, ref
+ self._reverse_deltas = {} # ref -> set of item_hashes
+
+ self._ref_generator = itertools.count()
+ self._delta_refs = {} # ref -> delta
+ self._reverse_delta_refs = {} # delta.tail -> ref
+
+ self._tracker.preremove_special.watch_weakref(self, lambda self, item: self._handle_preremove_special(item))
+ self._tracker.postremove_special.watch_weakref(self, lambda self, item: self._handle_postremove_special(item))
+ self._tracker.removed.watch_weakref(self, lambda self, item: self._handle_removed(item))
+
+ def _handle_preremove_special(self, item):
+ delta = self._delta_type.from_element(item)
+
+ # move delta refs referencing children down to this, so they can be moved up in one step
+ if delta.tail in self._reverse_delta_refs:
+ for x in list(self._reverse_deltas.get(self._reverse_delta_refs.get(delta.head, object()), set())):
+ self.get_last(x)
+ assert delta.head not in self._reverse_delta_refs, list(self._reverse_deltas.get(self._reverse_delta_refs.get(delta.head, None), set()))
+
+ def _handle_postremove_special(self, item):
+ delta = self._delta_type.from_element(item)
+
+ # move ref pointing to this up
+ if delta.tail in self._reverse_delta_refs:
+ assert delta.head not in self._reverse_delta_refs, list(self._reverse_deltas.get(self._reverse_delta_refs.get(delta.head, object()), set()))
+
+ ref = self._reverse_delta_refs[delta.tail]
+ cur_delta = self._delta_refs[ref]
+ assert cur_delta.tail == delta.tail
+ self._delta_refs[ref] = cur_delta - self._delta_type.from_element(item)
+ assert self._delta_refs[ref].tail == delta.head
+ del self._reverse_delta_refs[delta.tail]
+ self._reverse_delta_refs[delta.head] = ref
+
+ def _handle_removed(self, item):
+ delta = self._delta_type.from_element(item)
+
+ # delete delta entry and ref if it is empty
+ if delta.head in self._deltas:
+ delta1, ref = self._deltas.pop(delta.head)
+ self._reverse_deltas[ref].remove(delta.head)
+ if not self._reverse_deltas[ref]:
+ del self._reverse_deltas[ref]
+ delta2 = self._delta_refs.pop(ref)
+ del self._reverse_delta_refs[delta2.tail]
+
+
+ def get_height(self, item_hash):
+ return self.get_delta_to_last(item_hash).height
+
+ def get_work(self, item_hash):
+ return self.get_delta_to_last(item_hash).work
+
+ def get_last(self, item_hash):
+ return self.get_delta_to_last(item_hash).tail
+
+ def get_height_and_last(self, item_hash):
+ delta = self.get_delta_to_last(item_hash)
+ return delta.height, delta.tail
+
+ def _get_delta(self, item_hash):
+ if item_hash in self._deltas:
+ delta1, ref = self._deltas[item_hash]
+ delta2 = self._delta_refs[ref]
+ res = delta1 + delta2
+ else:
+ res = self._delta_type.from_element(self._tracker.items[item_hash])
+ assert res.head == item_hash
+ return res
+
+ def _set_delta(self, item_hash, delta):
+ other_item_hash = delta.tail
+ if other_item_hash not in self._reverse_delta_refs:
+ ref = self._ref_generator.next()
+ assert ref not in self._delta_refs
+ self._delta_refs[ref] = self._delta_type.get_none(other_item_hash)
+ self._reverse_delta_refs[other_item_hash] = ref
+ del ref
+
+ ref = self._reverse_delta_refs[other_item_hash]
+ ref_delta = self._delta_refs[ref]
+ assert ref_delta.tail == other_item_hash
+
+ if item_hash in self._deltas:
+ prev_ref = self._deltas[item_hash][1]
+ self._reverse_deltas[prev_ref].remove(item_hash)
+ if not self._reverse_deltas[prev_ref] and prev_ref != ref:
+ self._reverse_deltas.pop(prev_ref)
+ x = self._delta_refs.pop(prev_ref)
+ self._reverse_delta_refs.pop(x.tail)
+ self._deltas[item_hash] = delta - ref_delta, ref
+ self._reverse_deltas.setdefault(ref, set()).add(item_hash)
+
+ def get_delta_to_last(self, item_hash):
+ assert isinstance(item_hash, (int, long, type(None)))
+ delta = self._delta_type.get_none(item_hash)
+ updates = []
+ while delta.tail in self._tracker.items:
+ updates.append((delta.tail, delta))
+ this_delta = self._get_delta(delta.tail)
+ delta += this_delta
+ for update_hash, delta_then in updates:
+ self._set_delta(update_hash, delta - delta_then)
+ return delta
+
+ def get_delta(self, item, ancestor):
+ assert self._tracker.is_child_of(ancestor, item)
+ return self.get_delta_to_last(item) - self.get_delta_to_last(ancestor)
+
class Tracker(object):
- def __init__(self, shares=[], delta_type=AttributeDelta):
- self.shares = {} # hash -> share
- self.reverse_shares = {} # delta.tail -> set of share_hashes
+ def __init__(self, items=[], delta_type=AttributeDelta):
+ self.items = {} # hash -> item
+ self.reverse = {} # delta.tail -> set of item_hashes
self.heads = {} # head hash -> tail_hash
self.tails = {} # tail hash -> set of head hashes
- self.deltas = {} # share_hash -> delta, ref
- self.reverse_deltas = {} # ref -> set of share_hashes
-
- self.ref_generator = itertools.count()
- self.delta_refs = {} # ref -> delta
- self.reverse_delta_refs = {} # delta.tail -> ref
-
self.added = variable.Event()
+ self.preremove_special = variable.Event()
+ self.postremove_special = variable.Event()
self.removed = variable.Event()
self.get_nth_parent_hash = DistanceSkipList(self)
- self.delta_type = delta_type
+ self._delta_type = delta_type
+ self._default_view = TrackerView(self, delta_type)
- for share in shares:
- self.add(share)
+ for item in items:
+ self.add(item)
- def add(self, share):
- assert not isinstance(share, (int, long, type(None)))
- delta = self.delta_type.from_element(share)
+ def __getattr__(self, name):
+ attr = getattr(self._default_view, name)
+ setattr(self, name, attr)
+ return attr
+
+ def add(self, item):
+ assert not isinstance(item, (int, long, type(None)))
+ delta = self._delta_type.from_element(item)
- if delta.head in self.shares:
- raise ValueError('share already present')
+ if delta.head in self.items:
+ raise ValueError('item already present')
if delta.head in self.tails:
heads = self.tails.pop(delta.head)
else:
tail = self.get_last(delta.tail)
- self.shares[delta.head] = share
- self.reverse_shares.setdefault(delta.tail, set()).add(delta.head)
+ self.items[delta.head] = item
+ self.reverse.setdefault(delta.tail, set()).add(delta.head)
self.tails.setdefault(tail, set()).update(heads)
if delta.tail in self.tails[tail]:
for head in heads:
self.heads[head] = tail
- self.added.happened(share)
+ self.added.happened(item)
- def remove(self, share_hash):
- assert isinstance(share_hash, (int, long, type(None)))
- if share_hash not in self.shares:
+ def remove(self, item_hash):
+ assert isinstance(item_hash, (int, long, type(None)))
+ if item_hash not in self.items:
raise KeyError()
- share = self.shares[share_hash]
- del share_hash
+ item = self.items[item_hash]
+ del item_hash
- delta = self.delta_type.from_element(share)
+ delta = self._delta_type.from_element(item)
- children = self.reverse_shares.get(delta.head, set())
+ children = self.reverse.get(delta.head, set())
if delta.head in self.heads and delta.tail in self.tails:
tail = self.heads.pop(delta.head)
elif delta.head in self.heads:
tail = self.heads.pop(delta.head)
self.tails[tail].remove(delta.head)
- if self.reverse_shares[delta.tail] != set([delta.head]):
+ if self.reverse[delta.tail] != set([delta.head]):
pass # has sibling
else:
self.tails[tail].add(delta.tail)
self.heads[delta.tail] = tail
- elif delta.tail in self.tails and len(self.reverse_shares[delta.tail]) <= 1:
- # move delta refs referencing children down to this, so they can be moved up in one step
- if delta.tail in self.reverse_delta_refs:
- for x in list(self.reverse_deltas.get(self.reverse_delta_refs.get(delta.head, object()), set())):
- self.get_last(x)
- assert delta.head not in self.reverse_delta_refs, list(self.reverse_deltas.get(self.reverse_delta_refs.get(delta.head, None), set()))
+ elif delta.tail in self.tails and len(self.reverse[delta.tail]) <= 1:
+ self.preremove_special.happened(item)
heads = self.tails.pop(delta.tail)
for head in heads:
self.heads[head] = delta.head
self.tails[delta.head] = set(heads)
- # move ref pointing to this up
- if delta.tail in self.reverse_delta_refs:
- assert delta.head not in self.reverse_delta_refs, list(self.reverse_deltas.get(self.reverse_delta_refs.get(delta.head, object()), set()))
-
- ref = self.reverse_delta_refs[delta.tail]
- cur_delta = self.delta_refs[ref]
- assert cur_delta.tail == delta.tail
- self.delta_refs[ref] = cur_delta - self.delta_type.from_element(share)
- assert self.delta_refs[ref].tail == delta.head
- del self.reverse_delta_refs[delta.tail]
- self.reverse_delta_refs[delta.head] = ref
+ self.postremove_special.happened(item)
else:
raise NotImplementedError()
- # delete delta entry and ref if it is empty
- if delta.head in self.deltas:
- delta1, ref = self.deltas.pop(delta.head)
- self.reverse_deltas[ref].remove(delta.head)
- if not self.reverse_deltas[ref]:
- del self.reverse_deltas[ref]
- delta2 = self.delta_refs.pop(ref)
- del self.reverse_delta_refs[delta2.tail]
-
- self.shares.pop(delta.head)
- self.reverse_shares[delta.tail].remove(delta.head)
- if not self.reverse_shares[delta.tail]:
- self.reverse_shares.pop(delta.tail)
-
- self.removed.happened(share)
-
- def get_height(self, share_hash):
- return self.get_delta(share_hash).height
-
- def get_work(self, share_hash):
- return self.get_delta(share_hash).work
-
- def get_last(self, share_hash):
- return self.get_delta(share_hash).tail
-
- def get_height_and_last(self, share_hash):
- delta = self.get_delta(share_hash)
- return delta.height, delta.tail
-
- def get_height_work_and_last(self, share_hash):
- delta = self.get_delta(share_hash)
- return delta.height, delta.work, delta.tail
-
- def _get_delta(self, share_hash):
- if share_hash in self.deltas:
- delta1, ref = self.deltas[share_hash]
- delta2 = self.delta_refs[ref]
- res = delta1 + delta2
- else:
- res = self.delta_type.from_element(self.shares[share_hash])
- assert res.head == share_hash
- return res
-
- def _set_delta(self, share_hash, delta):
- other_share_hash = delta.tail
- if other_share_hash not in self.reverse_delta_refs:
- ref = self.ref_generator.next()
- assert ref not in self.delta_refs
- self.delta_refs[ref] = self.delta_type.get_none(other_share_hash)
- self.reverse_delta_refs[other_share_hash] = ref
- del ref
+ self.items.pop(delta.head)
+ self.reverse[delta.tail].remove(delta.head)
+ if not self.reverse[delta.tail]:
+ self.reverse.pop(delta.tail)
- ref = self.reverse_delta_refs[other_share_hash]
- ref_delta = self.delta_refs[ref]
- assert ref_delta.tail == other_share_hash
-
- if share_hash in self.deltas:
- prev_ref = self.deltas[share_hash][1]
- self.reverse_deltas[prev_ref].remove(share_hash)
- if not self.reverse_deltas[prev_ref] and prev_ref != ref:
- self.reverse_deltas.pop(prev_ref)
- x = self.delta_refs.pop(prev_ref)
- self.reverse_delta_refs.pop(x.tail)
- self.deltas[share_hash] = delta - ref_delta, ref
- self.reverse_deltas.setdefault(ref, set()).add(share_hash)
-
- def get_delta(self, share_hash):
- assert isinstance(share_hash, (int, long, type(None)))
- delta = self.delta_type.get_none(share_hash)
- updates = []
- while delta.tail in self.shares:
- updates.append((delta.tail, delta))
- this_delta = self._get_delta(delta.tail)
- delta += this_delta
- for update_hash, delta_then in updates:
- self._set_delta(update_hash, delta - delta_then)
- return delta
+ self.removed.happened(item)
def get_chain(self, start_hash, length):
assert length <= self.get_height(start_hash)
for i in xrange(length):
- yield self.shares[start_hash]
- start_hash = self.delta_type.from_element(self.shares[start_hash]).tail
+ yield self.items[start_hash]
+ start_hash = self._delta_type.from_element(self.items[start_hash]).tail
- def is_child_of(self, share_hash, possible_child_hash):
- height, last = self.get_height_and_last(share_hash)
+ def is_child_of(self, item_hash, possible_child_hash):
+ height, last = self.get_height_and_last(item_hash)
child_height, child_last = self.get_height_and_last(possible_child_hash)
if child_last != last:
return None # not connected, so can't be determined
height_up = child_height - height
- return height_up >= 0 and self.get_nth_parent_hash(possible_child_hash, height_up) == share_hash
+ return height_up >= 0 and self.get_nth_parent_hash(possible_child_hash, height_up) == item_hash
+
+class SubsetTracker(Tracker):
+ def __init__(self, subset_of, **kwargs):
+ Tracker.__init__(self, **kwargs)
+ self.get_nth_parent_hash = subset_of.get_nth_parent_hash # overwrites Tracker.__init__'s
+ self._subset_of = subset_of
+
+ def add(self, item):
+ delta = self._delta_type.from_element(item)
+ if self._subset_of is not None:
+ assert delta.head in self._subset_of.items
+ Tracker.add(self, item)
+
+ def remove(self, item_hash):
+ if self._subset_of is not None:
+ assert item_hash in self._subset_of.items
+ Tracker.remove(self, item_hash)