1# SPDX-License-Identifier: GPL-2.0+
2# Copyright 2022 Google LLC
3#
4"""Bintool implementation for lz4
5
6lz4 allows compression and decompression of files.
7
8Documentation is available via::
9
10   man lz4
11
12Here is the help:
13
14*** LZ4 command line interface 64-bits v1.9.3, by Yann Collet ***
15Usage :
16      lz4 [arg] [input] [output]
17
18input   : a filename
19          with no FILE, or when FILE is - or stdin, read standard input
20Arguments :
21 -1     : Fast compression (default)
22 -9     : High compression
23 -d     : decompression (default for .lz4 extension)
24 -z     : force compression
25 -D FILE: use FILE as dictionary
26 -f     : overwrite output without prompting
27 -k     : preserve source files(s)  (default)
28--rm    : remove source file(s) after successful de/compression
29 -h/-H  : display help/long help and exit
30
31Advanced arguments :
32 -V     : display Version number and exit
33 -v     : verbose mode
34 -q     : suppress warnings; specify twice to suppress errors too
35 -c     : force write to standard output, even if it is the console
36 -t     : test compressed file integrity
37 -m     : multiple input files (implies automatic output filenames)
38 -r     : operate recursively on directories (sets also -m)
39 -l     : compress using Legacy format (Linux kernel compression)
40 -B#    : cut file into blocks of size # bytes [32+]
41                     or predefined block size [4-7] (default: 7)
42 -BI    : Block Independence (default)
43 -BD    : Block dependency (improves compression ratio)
44 -BX    : enable block checksum (default:disabled)
45--no-frame-crc : disable stream checksum (default:enabled)
46--content-size : compressed frame includes original size (default:not present)
47--list FILE : lists information about .lz4 files (useful for files compressed
48    with --content-size flag)
49--[no-]sparse  : sparse mode (default:enabled on file, disabled on stdout)
50--favor-decSpeed: compressed files decompress faster, but are less compressed
51--fast[=#]: switch to ultra fast compression level (default: 1)
52--best  : same as -12
53Benchmark arguments :
54 -b#    : benchmark file(s), using # compression level (default : 1)
55 -e#    : test all compression levels from -bX to # (default : 1)
56 -i#    : minimum evaluation time in seconds (default : 3s)
57"""
58
59import re
60import tempfile
61
62from binman import bintool
63from u_boot_pylib import tools
64
65# pylint: disable=C0103
66class Bintoollz4(bintool.Bintool):
67    """Compression/decompression using the LZ4 algorithm
68
69    This bintool supports running `lz4` to compress and decompress data, as
70    used by binman.
71
72    It is also possible to fetch the tool, which uses `apt` to install it.
73
74    Documentation is available via::
75
76        man lz4
77    """
78    def __init__(self, name):
79        super().__init__(name, 'lz4 compression', r'.* (v[0-9.]*),.*')
80
81    def compress(self, indata):
82        """Compress data with lz4
83
84        Args:
85            indata (bytes): Data to compress
86
87        Returns:
88            bytes: Compressed data
89        """
90        with tempfile.NamedTemporaryFile(prefix='comp.tmp',
91                                         dir=tools.get_output_dir()) as tmp:
92            tools.write_file(tmp.name, indata)
93            args = ['--no-frame-crc', '-B4', '-5', '-c', tmp.name]
94            return self.run_cmd(*args, binary=True)
95
96    def decompress(self, indata):
97        """Decompress data with lz4
98
99        Args:
100            indata (bytes): Data to decompress
101
102        Returns:
103            bytes: Decompressed data
104        """
105        with tempfile.NamedTemporaryFile(prefix='decomp.tmp',
106                                         dir=tools.get_output_dir()) as inf:
107            tools.write_file(inf.name, indata)
108            args = ['-cd', inf.name]
109            return self.run_cmd(*args, binary=True)
110
111    def fetch(self, method):
112        """Fetch handler for lz4
113
114        This installs the lz4 package using the apt utility.
115
116        Args:
117            method (FETCH_...): Method to use
118
119        Returns:
120            True if the file was fetched and now installed, None if a method
121            other than FETCH_BIN was requested
122
123        Raises:
124            Valuerror: Fetching could not be completed
125        """
126        if method != bintool.FETCH_BIN:
127            return None
128        return self.apt_install('lz4')
129