|
@@ -19,7 +19,7 @@ from .logger import create_logger
|
|
|
logger = create_logger()
|
|
|
|
|
|
from . import xattr
|
|
|
-from .chunker import get_chunker, Chunk, cached_hash
|
|
|
+from .chunker import get_chunker, Chunk
|
|
|
from .cache import ChunkListEntry
|
|
|
from .crypto.key import key_factory
|
|
|
from .compress import Compressor, CompressionSpec
|
|
@@ -41,6 +41,7 @@ from .helpers import ellipsis_truncate, ProgressIndicatorPercent, log_multi
|
|
|
from .helpers import os_open, flags_normal, flags_dir
|
|
|
from .helpers import msgpack
|
|
|
from .helpers import sig_int
|
|
|
+from .lrucache import LRUCache
|
|
|
from .patterns import PathPrefixPattern, FnmatchPattern, IECommand
|
|
|
from .item import Item, ArchiveItem, ItemDiff
|
|
|
from .platform import acl_get, acl_set, set_flags, get_flags, swidth, hostname
|
|
@@ -1088,6 +1089,32 @@ class MetadataCollector:
|
|
|
return attrs
|
|
|
|
|
|
|
|
|
+# remember a few recently used all-zero chunk hashes in this mapping.
|
|
|
+# (hash_func, chunk_length) -> chunk_hash
|
|
|
+# we play safe and have the hash_func in the mapping key, in case we
|
|
|
+# have different hash_funcs within the same borg run.
|
|
|
+zero_chunk_ids = LRUCache(10, dispose=lambda _: None)
|
|
|
+
|
|
|
+
|
|
|
+def cached_hash(chunk, id_hash):
|
|
|
+ allocation = chunk.meta['allocation']
|
|
|
+ if allocation == CH_DATA:
|
|
|
+ data = chunk.data
|
|
|
+ chunk_id = id_hash(data)
|
|
|
+ elif allocation in (CH_HOLE, CH_ALLOC):
|
|
|
+ size = chunk.meta['size']
|
|
|
+ assert size <= len(zeros)
|
|
|
+ data = memoryview(zeros)[:size]
|
|
|
+ try:
|
|
|
+ chunk_id = zero_chunk_ids[(id_hash, size)]
|
|
|
+ except KeyError:
|
|
|
+ chunk_id = id_hash(data)
|
|
|
+ zero_chunk_ids[(id_hash, size)] = chunk_id
|
|
|
+ else:
|
|
|
+ raise ValueError('unexpected allocation type')
|
|
|
+ return chunk_id, data
|
|
|
+
|
|
|
+
|
|
|
class ChunksProcessor:
|
|
|
# Processes an iterator of chunks for an Item
|
|
|
|