1919
2020from abc import ABCMeta , abstractmethod
2121
22- from barman .compression import _try_import_snappy , get_internal_compressor
22+ from barman .compression import (
23+ _try_import_lz4 ,
24+ _try_import_snappy ,
25+ get_internal_compressor ,
26+ )
2327from barman .utils import with_metaclass
2428
2529
@@ -49,6 +53,19 @@ def decompress(self, data):
4953 :rtype: bytes
5054 """
5155
56+ def flush (self ):
57+ """
58+ Flushes any remaining compressed data and returns the final bytes.
59+
60+ This method should be called after all data has been compressed with
61+ add_chunk() to ensure any buffered data and end markers are written.
62+ The default implementation returns an empty bytes object.
63+
64+ :return: Any remaining compressed data
65+ :rtype: bytes
66+ """
67+ return b""
68+
5269
5370class SnappyCompressor (ChunkedCompressor ):
5471 """
@@ -82,20 +99,89 @@ def decompress(self, data):
8299 return self .decompressor .decompress (data )
83100
84101
102+ class Lz4Compressor (ChunkedCompressor ):
103+ """
104+ A ChunkedCompressor implementation based on lz4.
105+
106+ Uses lz4.frame for streaming compression and decompression. The compressor
107+ maintains state across add_chunk() calls and requires flush() to be called
108+ at the end to write the frame end marker.
109+ """
110+
111+ def __init__ (self ):
112+ lz4 = _try_import_lz4 ()
113+ self ._lz4_frame = lz4 .frame
114+ self ._compressor = None
115+ self ._decompressor = None
116+ self ._started = False
117+ self ._flushed = False
118+
119+ def add_chunk (self , data ):
120+ """
121+ Compresses the supplied data and returns the compressed bytes.
122+
123+ On the first call, this initializes the lz4 frame and writes the header.
124+ Subsequent calls compress additional data within the same frame.
125+
126+ :param bytes data: The chunk of data to be compressed
127+ :return: The compressed data
128+ :rtype: bytes
129+ """
130+ if self ._compressor is None :
131+ self ._compressor = self ._lz4_frame .LZ4FrameCompressor (auto_flush = True )
132+
133+ if not self ._started :
134+ self ._started = True
135+ return self ._compressor .begin () + self ._compressor .compress (data )
136+ return self ._compressor .compress (data )
137+
138+ def decompress (self , data ):
139+ """
140+ Decompresses the supplied chunk of data and returns the uncompressed data.
141+
142+ The LZ4FrameDecompressor handles streaming decompression and buffering
143+ of partial frames automatically.
144+
145+ :param bytes data: The chunk of data to be decompressed
146+ :return: The decompressed data
147+ :rtype: bytes
148+ """
149+ if self ._decompressor is None :
150+ self ._decompressor = self ._lz4_frame .LZ4FrameDecompressor ()
151+ return self ._decompressor .decompress (data )
152+
153+ def flush (self ):
154+ """
155+ Flushes any remaining data and returns the frame end marker.
156+
157+ This must be called after all data has been compressed to ensure the
158+ lz4 frame is properly terminated. Subsequent calls return empty bytes.
159+
160+ :return: The frame end marker bytes
161+ :rtype: bytes
162+ """
163+ if self ._compressor is not None and self ._started and not self ._flushed :
164+ self ._flushed = True
165+ return self ._compressor .flush ()
166+ return b""
167+
168+
85169def get_compressor (compression ):
86170 """
87171 Helper function which returns a ChunkedCompressor for the specified compression
88- algorithm. Currently only snappy is supported. The other compression algorithms
172+ algorithm. Snappy and lz4 are supported. The other compression algorithms
89173 supported by barman cloud use the decompression built into TarFile.
90174
91- :param str compression: The compression algorithm to use. Can be set to snappy
92- or any compression supported by the TarFile mode string.
175+ :param str compression: The compression algorithm to use. Can be set to snappy,
176+ lz4, or any compression supported by the TarFile mode string.
93177 :return: A ChunkedCompressor capable of compressing and decompressing using the
94178 specified compression.
95179 :rtype: ChunkedCompressor
96180 """
97181 if compression == "snappy" :
98182 return SnappyCompressor ()
183+ if compression == "lz4" :
184+ return Lz4Compressor ()
99185 return None
100186
101187
@@ -107,12 +193,13 @@ def get_streaming_tar_mode(mode, compression):
107193 ignored so that barman-cloud can apply them itself.
108194
109195 :param str mode: The file mode to use, either r or w.
110- :param str compression: The compression algorithm to use. Can be set to snappy
111- or any compression supported by the TarFile mode string.
196+ :param str compression: The compression algorithm to use. Can be set to snappy,
197+ lz4, or any compression supported by the TarFile mode string.
112198 :return: The full filemode for a streaming tar file
113199 :rtype: str
114200 """
115- if compression == "snappy" or compression is None :
201+ # Compression algorithms that require manual handling (not built into TarFile)
202+ if compression in ("snappy" , "lz4" ) or compression is None :
116203 return "%s|" % mode
117204 else :
118205 return "%s|%s" % (mode , compression )
0 commit comments