blob: 2f78bab9bbbf9e74cf244ca14d0711f53a67bc17 [file] [log] [blame]
Simon Glassad35ce52022-01-09 20:14:03 -07001# SPDX-License-Identifier: GPL-2.0+
2# Copyright 2022 Google LLC
3#
4"""Utilities to compress and decompress data"""
5
6import struct
7import tempfile
8
Simon Glass33ce3512022-01-09 20:14:06 -07009from binman import bintool
Simon Glassad35ce52022-01-09 20:14:03 -070010from patman import tools
11
Simon Glass33ce3512022-01-09 20:14:06 -070012LZ4 = bintool.Bintool.create('lz4')
13HAVE_LZ4 = LZ4.is_present()
14
Simon Glass359e4312022-01-09 20:14:08 -070015LZMA_ALONE = bintool.Bintool.create('lzma_alone')
16HAVE_LZMA_ALONE = LZMA_ALONE.is_present()
17
Simon Glass33ce3512022-01-09 20:14:06 -070018
Simon Glass0d1e95a2022-01-09 20:14:04 -070019def compress(indata, algo, with_header=True):
Simon Glassad35ce52022-01-09 20:14:03 -070020 """Compress some data using a given algorithm
21
22 Note that for lzma this uses an old version of the algorithm, not that
23 provided by xz.
24
25 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
26 directory to be previously set up, by calling PrepareOutputDir().
27
28 Care is taken to use unique temporary files so that this function can be
29 called from multiple threads.
30
31 Args:
Simon Glass0d1e95a2022-01-09 20:14:04 -070032 indata (bytes): Input data to compress
33 algo (str): Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
Simon Glassad35ce52022-01-09 20:14:03 -070034
35 Returns:
Simon Glass0d1e95a2022-01-09 20:14:04 -070036 bytes: Compressed data
Simon Glassad35ce52022-01-09 20:14:03 -070037 """
38 if algo == 'none':
39 return indata
40 fname = tempfile.NamedTemporaryFile(prefix='%s.comp.tmp' % algo,
41 dir=tools.GetOutputDir()).name
42 tools.WriteFile(fname, indata)
43 if algo == 'lz4':
Simon Glass33ce3512022-01-09 20:14:06 -070044 data = LZ4.compress(indata)
Simon Glassad35ce52022-01-09 20:14:03 -070045 # cbfstool uses a very old version of lzma
46 elif algo == 'lzma':
Simon Glass359e4312022-01-09 20:14:08 -070047 data = LZMA_ALONE.compress(indata)
Simon Glassad35ce52022-01-09 20:14:03 -070048 elif algo == 'gzip':
49 data = tools.Run('gzip', '-c', fname, binary=True)
50 else:
51 raise ValueError("Unknown algorithm '%s'" % algo)
52 if with_header:
53 hdr = struct.pack('<I', len(data))
54 data = hdr + data
55 return data
56
Simon Glass0d1e95a2022-01-09 20:14:04 -070057def decompress(indata, algo, with_header=True):
Simon Glassad35ce52022-01-09 20:14:03 -070058 """Decompress some data using a given algorithm
59
60 Note that for lzma this uses an old version of the algorithm, not that
61 provided by xz.
62
63 This requires 'lz4' and 'lzma_alone' tools. It also requires an output
64 directory to be previously set up, by calling PrepareOutputDir().
65
66 Args:
Simon Glass0d1e95a2022-01-09 20:14:04 -070067 indata (bytes): Input data to decompress
68 algo (str): Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
Simon Glassad35ce52022-01-09 20:14:03 -070069
70 Returns:
Simon Glass0d1e95a2022-01-09 20:14:04 -070071 (bytes) Compressed data
Simon Glassad35ce52022-01-09 20:14:03 -070072 """
73 if algo == 'none':
74 return indata
75 if with_header:
76 data_len = struct.unpack('<I', indata[:4])[0]
77 indata = indata[4:4 + data_len]
78 fname = tools.GetOutputFilename('%s.decomp.tmp' % algo)
Simon Glass0d1e95a2022-01-09 20:14:04 -070079 tools.WriteFile(fname, indata)
Simon Glassad35ce52022-01-09 20:14:03 -070080 if algo == 'lz4':
Simon Glass33ce3512022-01-09 20:14:06 -070081 data = LZ4.decompress(indata)
Simon Glassad35ce52022-01-09 20:14:03 -070082 elif algo == 'lzma':
Simon Glass359e4312022-01-09 20:14:08 -070083 data = LZMA_ALONE.decompress(indata)
Simon Glassad35ce52022-01-09 20:14:03 -070084 elif algo == 'gzip':
85 data = tools.Run('gzip', '-cd', fname, binary=True)
86 else:
87 raise ValueError("Unknown algorithm '%s'" % algo)
88 return data