|
@@ -56,16 +56,21 @@ cdef class CompressorBase:
|
|
|
also handles compression format auto detection and
|
|
|
adding/stripping the ID header (which enable auto detection).
|
|
|
"""
|
|
|
- ID = b'\xFF\xFF' # reserved and not used
|
|
|
- # overwrite with a unique 2-bytes bytestring in child classes
|
|
|
+ ID = b'\xFF' # reserved and not used
|
|
|
+ # overwrite with a unique 1-byte bytestring in child classes
|
|
|
name = 'baseclass'
|
|
|
|
|
|
@classmethod
|
|
|
def detect(cls, data):
|
|
|
return data.startswith(cls.ID)
|
|
|
|
|
|
- def __init__(self, **kwargs):
|
|
|
- pass
|
|
|
+ def __init__(self, level=255, **kwargs):
|
|
|
+ assert 0 <= level <= 255
|
|
|
+ if self.ID is not None:
|
|
|
+ self.id_level = self.ID + bytes((level, )) # level 255 means "unknown level"
|
|
|
+ assert len(self.id_level) == 2
|
|
|
+ else:
|
|
|
+ self.id_level = None
|
|
|
|
|
|
def decide(self, data):
|
|
|
"""
|
|
@@ -85,8 +90,8 @@ cdef class CompressorBase:
|
|
|
Compress *data* (bytes) and return bytes result. Prepend the ID bytes of this compressor,
|
|
|
which is needed so that the correct decompressor can be used for decompression.
|
|
|
"""
|
|
|
- # add ID bytes
|
|
|
- return self.ID + data
|
|
|
+ # add id_level bytes
|
|
|
+ return self.id_level + data
|
|
|
|
|
|
def decompress(self, data):
|
|
|
"""
|
|
@@ -96,7 +101,7 @@ cdef class CompressorBase:
|
|
|
Only handles input generated by _this_ Compressor - for a general purpose
|
|
|
decompression method see *Compressor.decompress*.
|
|
|
"""
|
|
|
- # strip ID bytes
|
|
|
+ # strip id_level bytes
|
|
|
return data[2:]
|
|
|
|
|
|
cdef class DecidingCompressor(CompressorBase):
|
|
@@ -106,8 +111,8 @@ cdef class DecidingCompressor(CompressorBase):
|
|
|
"""
|
|
|
name = 'decidebaseclass'
|
|
|
|
|
|
- def __init__(self, **kwargs):
|
|
|
- super().__init__(**kwargs)
|
|
|
+ def __init__(self, level=255, **kwargs):
|
|
|
+ super().__init__(level=level, **kwargs)
|
|
|
|
|
|
def _decide(self, data):
|
|
|
"""
|
|
@@ -148,9 +153,12 @@ class CNONE(CompressorBase):
|
|
|
"""
|
|
|
none - no compression, just pass through data
|
|
|
"""
|
|
|
- ID = b'\x00\x00'
|
|
|
+ ID = b'\x00'
|
|
|
name = 'none'
|
|
|
|
|
|
+ def __init__(self, level=255, **kwargs):
|
|
|
+ super().__init__(level=level, **kwargs) # no defined levels for CNONE, so just say "unknown"
|
|
|
+
|
|
|
def compress(self, data):
|
|
|
return super().compress(data)
|
|
|
|
|
@@ -170,11 +178,11 @@ class LZ4(DecidingCompressor):
|
|
|
- wrapper releases CPython's GIL to support multithreaded code
|
|
|
- uses safe lz4 methods that never go beyond the end of the output buffer
|
|
|
"""
|
|
|
- ID = b'\x01\x00'
|
|
|
+ ID = b'\x01'
|
|
|
name = 'lz4'
|
|
|
|
|
|
- def __init__(self, **kwargs):
|
|
|
- pass
|
|
|
+ def __init__(self, level=255, **kwargs):
|
|
|
+ super().__init__(level=level, **kwargs) # no defined levels for LZ4, so just say "unknown"
|
|
|
|
|
|
def _decide(self, idata):
|
|
|
"""
|
|
@@ -235,11 +243,11 @@ class LZMA(DecidingCompressor):
|
|
|
"""
|
|
|
lzma compression / decompression
|
|
|
"""
|
|
|
- ID = b'\x02\x00'
|
|
|
+ ID = b'\x02'
|
|
|
name = 'lzma'
|
|
|
|
|
|
def __init__(self, level=6, **kwargs):
|
|
|
- super().__init__(**kwargs)
|
|
|
+ super().__init__(level=level, **kwargs)
|
|
|
self.level = level
|
|
|
if lzma is None:
|
|
|
raise ValueError('No lzma support found.')
|
|
@@ -270,11 +278,11 @@ class ZSTD(DecidingCompressor):
|
|
|
# This is a NOT THREAD SAFE implementation.
|
|
|
# Only ONE python context must be created at a time.
|
|
|
# It should work flawlessly as long as borg will call ONLY ONE compression job at time.
|
|
|
- ID = b'\x03\x00'
|
|
|
+ ID = b'\x03'
|
|
|
name = 'zstd'
|
|
|
|
|
|
def __init__(self, level=3, **kwargs):
|
|
|
- super().__init__(**kwargs)
|
|
|
+ super().__init__(level=level, **kwargs)
|
|
|
self.level = level
|
|
|
|
|
|
def _decide(self, idata):
|
|
@@ -335,11 +343,11 @@ class ZLIB(DecidingCompressor):
|
|
|
"""
|
|
|
zlib compression / decompression (python stdlib)
|
|
|
"""
|
|
|
- ID = b'\x05\x00'
|
|
|
+ ID = b'\x05'
|
|
|
name = 'zlib'
|
|
|
|
|
|
def __init__(self, level=6, **kwargs):
|
|
|
- super().__init__(**kwargs)
|
|
|
+ super().__init__(level=level, **kwargs)
|
|
|
self.level = level
|
|
|
|
|
|
def _decide(self, data):
|
|
@@ -373,8 +381,8 @@ class ZLIB_legacy(CompressorBase):
|
|
|
Newer borg uses the ZLIB class that has separate ID bytes (as all the other
|
|
|
compressors) and does not need this hack.
|
|
|
"""
|
|
|
- ID = b'\x08\x00' # not used here, see detect()
|
|
|
- # avoid all 0x.8.. IDs elsewhere!
|
|
|
+ ID = b'\x08' # not used here, see detect()
|
|
|
+ # avoid all 0x.8 IDs elsewhere!
|
|
|
name = 'zlib_legacy'
|
|
|
|
|
|
@classmethod
|
|
@@ -386,7 +394,7 @@ class ZLIB_legacy(CompressorBase):
|
|
|
return check_ok and is_deflate
|
|
|
|
|
|
def __init__(self, level=6, **kwargs):
|
|
|
- super().__init__(**kwargs)
|
|
|
+ super().__init__(level=level, **kwargs)
|
|
|
self.level = level
|
|
|
|
|
|
def compress(self, data):
|
|
@@ -478,14 +486,14 @@ class ObfuscateSize(CompressorBase):
|
|
|
"""
|
|
|
Meta-Compressor that obfuscates the compressed data size.
|
|
|
"""
|
|
|
- ID = b'\x04\x00'
|
|
|
+ ID = b'\x04'
|
|
|
name = 'obfuscate'
|
|
|
|
|
|
header_fmt = Struct('>I')
|
|
|
header_len = len(header_fmt.pack(0))
|
|
|
|
|
|
def __init__(self, level=None, compressor=None):
|
|
|
- super().__init__()
|
|
|
+ super().__init__(level=level) # data will be encrypted, so we can tell the level
|
|
|
self.compressor = compressor
|
|
|
if level is None:
|
|
|
pass # decompression
|