800
801
new_version_ids.add(version)
801
802
return new_version_ids
805
class KeyMapper(object):
806
"""KeyMappers map between keys and underlying paritioned storage."""
809
"""Map key to an underlying storage identifier.
811
:param key: A key tuple e.g. ('file-id', 'revision-id').
812
:return: An underlying storage identifier, specific to the partitioning
816
def unmap(self, partition_id):
817
"""Map a partitioned storage id back to a key prefix.
819
:param partition_id: The underlying partition id.
820
:return: As much of a key (or prefix) as is derivable from the parition
825
class ConstantMapper(KeyMapper):
826
"""A key mapper that maps to a constant result."""
828
def __init__(self, result):
829
"""Create a ConstantMapper which will return result for all maps."""
830
self._result = result
833
"""See KeyMapper.map()."""
837
class PrefixMapper(KeyMapper):
838
"""A key mapper that extracts the first component of a key."""
841
"""See KeyMapper.map()."""
844
def unmap(self, partition_id):
845
"""See KeyMapper.unmap()."""
846
return (partition_id,)
849
class HashPrefixMapper(KeyMapper):
850
"""A key mapper that combines the first component of a key with a hash."""
853
"""See KeyMapper.map()."""
854
prefix = self._escape(key[0])
855
return "%02x/%s" % (adler32(prefix) & 0xff, prefix)
857
def _escape(self, prefix):
858
"""No escaping needed here."""
861
def unmap(self, partition_id):
862
"""See KeyMapper.unmap()."""
863
return (self._unescape(osutils.basename(partition_id)),)
865
def _unescape(self, basename):
866
"""No unescaping needed for HashPrefixMapper."""
870
class HashEscapedPrefixMapper(HashPrefixMapper):
871
"""Combines the escaped first component of a key with a hash."""
873
_safe = "abcdefghijklmnopqrstuvwxyz0123456789-_@,."
875
def _escape(self, prefix):
876
"""Turn a key element into a filesystem safe string.
878
This is similar to a plain urllib.quote, except
879
it uses specific safe characters, so that it doesn't
880
have to translate a lot of valid file ids.
882
# @ does not get escaped. This is because it is a valid
883
# filesystem character we use all the time, and it looks
884
# a lot better than seeing %40 all the time.
885
r = [((c in self._safe) and c or ('%%%02x' % ord(c)))
889
def _unescape(self, basename):
890
"""Escaped names are unescaped by urlutils."""
891
return urllib.unquote(basename)