xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/gzip.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Functions that read and write gzipped files.
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerThe user of the file doesn't have to worry about the compression,
4*cda5da8dSAndroid Build Coastguard Workerbut random access is not allowed."""
5*cda5da8dSAndroid Build Coastguard Worker
6*cda5da8dSAndroid Build Coastguard Worker# based on Andrew Kuchling's minigzip.py distributed with the zlib module
7*cda5da8dSAndroid Build Coastguard Worker
8*cda5da8dSAndroid Build Coastguard Workerimport struct, sys, time, os
9*cda5da8dSAndroid Build Coastguard Workerimport zlib
10*cda5da8dSAndroid Build Coastguard Workerimport builtins
11*cda5da8dSAndroid Build Coastguard Workerimport io
12*cda5da8dSAndroid Build Coastguard Workerimport _compression
13*cda5da8dSAndroid Build Coastguard Worker
14*cda5da8dSAndroid Build Coastguard Worker__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]
15*cda5da8dSAndroid Build Coastguard Worker
16*cda5da8dSAndroid Build Coastguard WorkerFTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16
17*cda5da8dSAndroid Build Coastguard Worker
18*cda5da8dSAndroid Build Coastguard WorkerREAD, WRITE = 1, 2
19*cda5da8dSAndroid Build Coastguard Worker
20*cda5da8dSAndroid Build Coastguard Worker_COMPRESS_LEVEL_FAST = 1
21*cda5da8dSAndroid Build Coastguard Worker_COMPRESS_LEVEL_TRADEOFF = 6
22*cda5da8dSAndroid Build Coastguard Worker_COMPRESS_LEVEL_BEST = 9
23*cda5da8dSAndroid Build Coastguard Worker
24*cda5da8dSAndroid Build Coastguard Worker
25*cda5da8dSAndroid Build Coastguard Workerdef open(filename, mode="rb", compresslevel=_COMPRESS_LEVEL_BEST,
26*cda5da8dSAndroid Build Coastguard Worker         encoding=None, errors=None, newline=None):
27*cda5da8dSAndroid Build Coastguard Worker    """Open a gzip-compressed file in binary or text mode.
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Worker    The filename argument can be an actual filename (a str or bytes object), or
30*cda5da8dSAndroid Build Coastguard Worker    an existing file object to read from or write to.
31*cda5da8dSAndroid Build Coastguard Worker
32*cda5da8dSAndroid Build Coastguard Worker    The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or "ab" for
33*cda5da8dSAndroid Build Coastguard Worker    binary mode, or "rt", "wt", "xt" or "at" for text mode. The default mode is
34*cda5da8dSAndroid Build Coastguard Worker    "rb", and the default compresslevel is 9.
35*cda5da8dSAndroid Build Coastguard Worker
36*cda5da8dSAndroid Build Coastguard Worker    For binary mode, this function is equivalent to the GzipFile constructor:
37*cda5da8dSAndroid Build Coastguard Worker    GzipFile(filename, mode, compresslevel). In this case, the encoding, errors
38*cda5da8dSAndroid Build Coastguard Worker    and newline arguments must not be provided.
39*cda5da8dSAndroid Build Coastguard Worker
40*cda5da8dSAndroid Build Coastguard Worker    For text mode, a GzipFile object is created, and wrapped in an
41*cda5da8dSAndroid Build Coastguard Worker    io.TextIOWrapper instance with the specified encoding, error handling
42*cda5da8dSAndroid Build Coastguard Worker    behavior, and line ending(s).
43*cda5da8dSAndroid Build Coastguard Worker
44*cda5da8dSAndroid Build Coastguard Worker    """
45*cda5da8dSAndroid Build Coastguard Worker    if "t" in mode:
46*cda5da8dSAndroid Build Coastguard Worker        if "b" in mode:
47*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Invalid mode: %r" % (mode,))
48*cda5da8dSAndroid Build Coastguard Worker    else:
49*cda5da8dSAndroid Build Coastguard Worker        if encoding is not None:
50*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Argument 'encoding' not supported in binary mode")
51*cda5da8dSAndroid Build Coastguard Worker        if errors is not None:
52*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Argument 'errors' not supported in binary mode")
53*cda5da8dSAndroid Build Coastguard Worker        if newline is not None:
54*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Argument 'newline' not supported in binary mode")
55*cda5da8dSAndroid Build Coastguard Worker
56*cda5da8dSAndroid Build Coastguard Worker    gz_mode = mode.replace("t", "")
57*cda5da8dSAndroid Build Coastguard Worker    if isinstance(filename, (str, bytes, os.PathLike)):
58*cda5da8dSAndroid Build Coastguard Worker        binary_file = GzipFile(filename, gz_mode, compresslevel)
59*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(filename, "read") or hasattr(filename, "write"):
60*cda5da8dSAndroid Build Coastguard Worker        binary_file = GzipFile(None, gz_mode, compresslevel, filename)
61*cda5da8dSAndroid Build Coastguard Worker    else:
62*cda5da8dSAndroid Build Coastguard Worker        raise TypeError("filename must be a str or bytes object, or a file")
63*cda5da8dSAndroid Build Coastguard Worker
64*cda5da8dSAndroid Build Coastguard Worker    if "t" in mode:
65*cda5da8dSAndroid Build Coastguard Worker        encoding = io.text_encoding(encoding)
66*cda5da8dSAndroid Build Coastguard Worker        return io.TextIOWrapper(binary_file, encoding, errors, newline)
67*cda5da8dSAndroid Build Coastguard Worker    else:
68*cda5da8dSAndroid Build Coastguard Worker        return binary_file
69*cda5da8dSAndroid Build Coastguard Worker
70*cda5da8dSAndroid Build Coastguard Workerdef write32u(output, value):
71*cda5da8dSAndroid Build Coastguard Worker    # The L format writes the bit pattern correctly whether signed
72*cda5da8dSAndroid Build Coastguard Worker    # or unsigned.
73*cda5da8dSAndroid Build Coastguard Worker    output.write(struct.pack("<L", value))
74*cda5da8dSAndroid Build Coastguard Worker
75*cda5da8dSAndroid Build Coastguard Workerclass _PaddedFile:
76*cda5da8dSAndroid Build Coastguard Worker    """Minimal read-only file object that prepends a string to the contents
77*cda5da8dSAndroid Build Coastguard Worker    of an actual file. Shouldn't be used outside of gzip.py, as it lacks
78*cda5da8dSAndroid Build Coastguard Worker    essential functionality."""
79*cda5da8dSAndroid Build Coastguard Worker
80*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, f, prepend=b''):
81*cda5da8dSAndroid Build Coastguard Worker        self._buffer = prepend
82*cda5da8dSAndroid Build Coastguard Worker        self._length = len(prepend)
83*cda5da8dSAndroid Build Coastguard Worker        self.file = f
84*cda5da8dSAndroid Build Coastguard Worker        self._read = 0
85*cda5da8dSAndroid Build Coastguard Worker
86*cda5da8dSAndroid Build Coastguard Worker    def read(self, size):
87*cda5da8dSAndroid Build Coastguard Worker        if self._read is None:
88*cda5da8dSAndroid Build Coastguard Worker            return self.file.read(size)
89*cda5da8dSAndroid Build Coastguard Worker        if self._read + size <= self._length:
90*cda5da8dSAndroid Build Coastguard Worker            read = self._read
91*cda5da8dSAndroid Build Coastguard Worker            self._read += size
92*cda5da8dSAndroid Build Coastguard Worker            return self._buffer[read:self._read]
93*cda5da8dSAndroid Build Coastguard Worker        else:
94*cda5da8dSAndroid Build Coastguard Worker            read = self._read
95*cda5da8dSAndroid Build Coastguard Worker            self._read = None
96*cda5da8dSAndroid Build Coastguard Worker            return self._buffer[read:] + \
97*cda5da8dSAndroid Build Coastguard Worker                   self.file.read(size-self._length+read)
98*cda5da8dSAndroid Build Coastguard Worker
99*cda5da8dSAndroid Build Coastguard Worker    def prepend(self, prepend=b''):
100*cda5da8dSAndroid Build Coastguard Worker        if self._read is None:
101*cda5da8dSAndroid Build Coastguard Worker            self._buffer = prepend
102*cda5da8dSAndroid Build Coastguard Worker        else:  # Assume data was read since the last prepend() call
103*cda5da8dSAndroid Build Coastguard Worker            self._read -= len(prepend)
104*cda5da8dSAndroid Build Coastguard Worker            return
105*cda5da8dSAndroid Build Coastguard Worker        self._length = len(self._buffer)
106*cda5da8dSAndroid Build Coastguard Worker        self._read = 0
107*cda5da8dSAndroid Build Coastguard Worker
108*cda5da8dSAndroid Build Coastguard Worker    def seek(self, off):
109*cda5da8dSAndroid Build Coastguard Worker        self._read = None
110*cda5da8dSAndroid Build Coastguard Worker        self._buffer = None
111*cda5da8dSAndroid Build Coastguard Worker        return self.file.seek(off)
112*cda5da8dSAndroid Build Coastguard Worker
113*cda5da8dSAndroid Build Coastguard Worker    def seekable(self):
114*cda5da8dSAndroid Build Coastguard Worker        return True  # Allows fast-forwarding even in unseekable streams
115*cda5da8dSAndroid Build Coastguard Worker
116*cda5da8dSAndroid Build Coastguard Worker
117*cda5da8dSAndroid Build Coastguard Workerclass BadGzipFile(OSError):
118*cda5da8dSAndroid Build Coastguard Worker    """Exception raised in some cases for invalid gzip files."""
119*cda5da8dSAndroid Build Coastguard Worker
120*cda5da8dSAndroid Build Coastguard Worker
121*cda5da8dSAndroid Build Coastguard Workerclass GzipFile(_compression.BaseStream):
122*cda5da8dSAndroid Build Coastguard Worker    """The GzipFile class simulates most of the methods of a file object with
123*cda5da8dSAndroid Build Coastguard Worker    the exception of the truncate() method.
124*cda5da8dSAndroid Build Coastguard Worker
125*cda5da8dSAndroid Build Coastguard Worker    This class only supports opening files in binary mode. If you need to open a
126*cda5da8dSAndroid Build Coastguard Worker    compressed file in text mode, use the gzip.open() function.
127*cda5da8dSAndroid Build Coastguard Worker
128*cda5da8dSAndroid Build Coastguard Worker    """
129*cda5da8dSAndroid Build Coastguard Worker
130*cda5da8dSAndroid Build Coastguard Worker    # Overridden with internal file object to be closed, if only a filename
131*cda5da8dSAndroid Build Coastguard Worker    # is passed in
132*cda5da8dSAndroid Build Coastguard Worker    myfileobj = None
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, filename=None, mode=None,
135*cda5da8dSAndroid Build Coastguard Worker                 compresslevel=_COMPRESS_LEVEL_BEST, fileobj=None, mtime=None):
136*cda5da8dSAndroid Build Coastguard Worker        """Constructor for the GzipFile class.
137*cda5da8dSAndroid Build Coastguard Worker
138*cda5da8dSAndroid Build Coastguard Worker        At least one of fileobj and filename must be given a
139*cda5da8dSAndroid Build Coastguard Worker        non-trivial value.
140*cda5da8dSAndroid Build Coastguard Worker
141*cda5da8dSAndroid Build Coastguard Worker        The new class instance is based on fileobj, which can be a regular
142*cda5da8dSAndroid Build Coastguard Worker        file, an io.BytesIO object, or any other object which simulates a file.
143*cda5da8dSAndroid Build Coastguard Worker        It defaults to None, in which case filename is opened to provide
144*cda5da8dSAndroid Build Coastguard Worker        a file object.
145*cda5da8dSAndroid Build Coastguard Worker
146*cda5da8dSAndroid Build Coastguard Worker        When fileobj is not None, the filename argument is only used to be
147*cda5da8dSAndroid Build Coastguard Worker        included in the gzip file header, which may include the original
148*cda5da8dSAndroid Build Coastguard Worker        filename of the uncompressed file.  It defaults to the filename of
149*cda5da8dSAndroid Build Coastguard Worker        fileobj, if discernible; otherwise, it defaults to the empty string,
150*cda5da8dSAndroid Build Coastguard Worker        and in this case the original filename is not included in the header.
151*cda5da8dSAndroid Build Coastguard Worker
152*cda5da8dSAndroid Build Coastguard Worker        The mode argument can be any of 'r', 'rb', 'a', 'ab', 'w', 'wb', 'x', or
153*cda5da8dSAndroid Build Coastguard Worker        'xb' depending on whether the file will be read or written.  The default
154*cda5da8dSAndroid Build Coastguard Worker        is the mode of fileobj if discernible; otherwise, the default is 'rb'.
155*cda5da8dSAndroid Build Coastguard Worker        A mode of 'r' is equivalent to one of 'rb', and similarly for 'w' and
156*cda5da8dSAndroid Build Coastguard Worker        'wb', 'a' and 'ab', and 'x' and 'xb'.
157*cda5da8dSAndroid Build Coastguard Worker
158*cda5da8dSAndroid Build Coastguard Worker        The compresslevel argument is an integer from 0 to 9 controlling the
159*cda5da8dSAndroid Build Coastguard Worker        level of compression; 1 is fastest and produces the least compression,
160*cda5da8dSAndroid Build Coastguard Worker        and 9 is slowest and produces the most compression. 0 is no compression
161*cda5da8dSAndroid Build Coastguard Worker        at all. The default is 9.
162*cda5da8dSAndroid Build Coastguard Worker
163*cda5da8dSAndroid Build Coastguard Worker        The mtime argument is an optional numeric timestamp to be written
164*cda5da8dSAndroid Build Coastguard Worker        to the last modification time field in the stream when compressing.
165*cda5da8dSAndroid Build Coastguard Worker        If omitted or None, the current time is used.
166*cda5da8dSAndroid Build Coastguard Worker
167*cda5da8dSAndroid Build Coastguard Worker        """
168*cda5da8dSAndroid Build Coastguard Worker
169*cda5da8dSAndroid Build Coastguard Worker        if mode and ('t' in mode or 'U' in mode):
170*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Invalid mode: {!r}".format(mode))
171*cda5da8dSAndroid Build Coastguard Worker        if mode and 'b' not in mode:
172*cda5da8dSAndroid Build Coastguard Worker            mode += 'b'
173*cda5da8dSAndroid Build Coastguard Worker        if fileobj is None:
174*cda5da8dSAndroid Build Coastguard Worker            fileobj = self.myfileobj = builtins.open(filename, mode or 'rb')
175*cda5da8dSAndroid Build Coastguard Worker        if filename is None:
176*cda5da8dSAndroid Build Coastguard Worker            filename = getattr(fileobj, 'name', '')
177*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(filename, (str, bytes)):
178*cda5da8dSAndroid Build Coastguard Worker                filename = ''
179*cda5da8dSAndroid Build Coastguard Worker        else:
180*cda5da8dSAndroid Build Coastguard Worker            filename = os.fspath(filename)
181*cda5da8dSAndroid Build Coastguard Worker        origmode = mode
182*cda5da8dSAndroid Build Coastguard Worker        if mode is None:
183*cda5da8dSAndroid Build Coastguard Worker            mode = getattr(fileobj, 'mode', 'rb')
184*cda5da8dSAndroid Build Coastguard Worker
185*cda5da8dSAndroid Build Coastguard Worker        if mode.startswith('r'):
186*cda5da8dSAndroid Build Coastguard Worker            self.mode = READ
187*cda5da8dSAndroid Build Coastguard Worker            raw = _GzipReader(fileobj)
188*cda5da8dSAndroid Build Coastguard Worker            self._buffer = io.BufferedReader(raw)
189*cda5da8dSAndroid Build Coastguard Worker            self.name = filename
190*cda5da8dSAndroid Build Coastguard Worker
191*cda5da8dSAndroid Build Coastguard Worker        elif mode.startswith(('w', 'a', 'x')):
192*cda5da8dSAndroid Build Coastguard Worker            if origmode is None:
193*cda5da8dSAndroid Build Coastguard Worker                import warnings
194*cda5da8dSAndroid Build Coastguard Worker                warnings.warn(
195*cda5da8dSAndroid Build Coastguard Worker                    "GzipFile was opened for writing, but this will "
196*cda5da8dSAndroid Build Coastguard Worker                    "change in future Python releases.  "
197*cda5da8dSAndroid Build Coastguard Worker                    "Specify the mode argument for opening it for writing.",
198*cda5da8dSAndroid Build Coastguard Worker                    FutureWarning, 2)
199*cda5da8dSAndroid Build Coastguard Worker            self.mode = WRITE
200*cda5da8dSAndroid Build Coastguard Worker            self._init_write(filename)
201*cda5da8dSAndroid Build Coastguard Worker            self.compress = zlib.compressobj(compresslevel,
202*cda5da8dSAndroid Build Coastguard Worker                                             zlib.DEFLATED,
203*cda5da8dSAndroid Build Coastguard Worker                                             -zlib.MAX_WBITS,
204*cda5da8dSAndroid Build Coastguard Worker                                             zlib.DEF_MEM_LEVEL,
205*cda5da8dSAndroid Build Coastguard Worker                                             0)
206*cda5da8dSAndroid Build Coastguard Worker            self._write_mtime = mtime
207*cda5da8dSAndroid Build Coastguard Worker        else:
208*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Invalid mode: {!r}".format(mode))
209*cda5da8dSAndroid Build Coastguard Worker
210*cda5da8dSAndroid Build Coastguard Worker        self.fileobj = fileobj
211*cda5da8dSAndroid Build Coastguard Worker
212*cda5da8dSAndroid Build Coastguard Worker        if self.mode == WRITE:
213*cda5da8dSAndroid Build Coastguard Worker            self._write_gzip_header(compresslevel)
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Worker    @property
216*cda5da8dSAndroid Build Coastguard Worker    def filename(self):
217*cda5da8dSAndroid Build Coastguard Worker        import warnings
218*cda5da8dSAndroid Build Coastguard Worker        warnings.warn("use the name attribute", DeprecationWarning, 2)
219*cda5da8dSAndroid Build Coastguard Worker        if self.mode == WRITE and self.name[-3:] != ".gz":
220*cda5da8dSAndroid Build Coastguard Worker            return self.name + ".gz"
221*cda5da8dSAndroid Build Coastguard Worker        return self.name
222*cda5da8dSAndroid Build Coastguard Worker
223*cda5da8dSAndroid Build Coastguard Worker    @property
224*cda5da8dSAndroid Build Coastguard Worker    def mtime(self):
225*cda5da8dSAndroid Build Coastguard Worker        """Last modification time read from stream, or None"""
226*cda5da8dSAndroid Build Coastguard Worker        return self._buffer.raw._last_mtime
227*cda5da8dSAndroid Build Coastguard Worker
228*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
229*cda5da8dSAndroid Build Coastguard Worker        s = repr(self.fileobj)
230*cda5da8dSAndroid Build Coastguard Worker        return '<gzip ' + s[1:-1] + ' ' + hex(id(self)) + '>'
231*cda5da8dSAndroid Build Coastguard Worker
232*cda5da8dSAndroid Build Coastguard Worker    def _init_write(self, filename):
233*cda5da8dSAndroid Build Coastguard Worker        self.name = filename
234*cda5da8dSAndroid Build Coastguard Worker        self.crc = zlib.crc32(b"")
235*cda5da8dSAndroid Build Coastguard Worker        self.size = 0
236*cda5da8dSAndroid Build Coastguard Worker        self.writebuf = []
237*cda5da8dSAndroid Build Coastguard Worker        self.bufsize = 0
238*cda5da8dSAndroid Build Coastguard Worker        self.offset = 0  # Current file offset for seek(), tell(), etc
239*cda5da8dSAndroid Build Coastguard Worker
240*cda5da8dSAndroid Build Coastguard Worker    def _write_gzip_header(self, compresslevel):
241*cda5da8dSAndroid Build Coastguard Worker        self.fileobj.write(b'\037\213')             # magic header
242*cda5da8dSAndroid Build Coastguard Worker        self.fileobj.write(b'\010')                 # compression method
243*cda5da8dSAndroid Build Coastguard Worker        try:
244*cda5da8dSAndroid Build Coastguard Worker            # RFC 1952 requires the FNAME field to be Latin-1. Do not
245*cda5da8dSAndroid Build Coastguard Worker            # include filenames that cannot be represented that way.
246*cda5da8dSAndroid Build Coastguard Worker            fname = os.path.basename(self.name)
247*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(fname, bytes):
248*cda5da8dSAndroid Build Coastguard Worker                fname = fname.encode('latin-1')
249*cda5da8dSAndroid Build Coastguard Worker            if fname.endswith(b'.gz'):
250*cda5da8dSAndroid Build Coastguard Worker                fname = fname[:-3]
251*cda5da8dSAndroid Build Coastguard Worker        except UnicodeEncodeError:
252*cda5da8dSAndroid Build Coastguard Worker            fname = b''
253*cda5da8dSAndroid Build Coastguard Worker        flags = 0
254*cda5da8dSAndroid Build Coastguard Worker        if fname:
255*cda5da8dSAndroid Build Coastguard Worker            flags = FNAME
256*cda5da8dSAndroid Build Coastguard Worker        self.fileobj.write(chr(flags).encode('latin-1'))
257*cda5da8dSAndroid Build Coastguard Worker        mtime = self._write_mtime
258*cda5da8dSAndroid Build Coastguard Worker        if mtime is None:
259*cda5da8dSAndroid Build Coastguard Worker            mtime = time.time()
260*cda5da8dSAndroid Build Coastguard Worker        write32u(self.fileobj, int(mtime))
261*cda5da8dSAndroid Build Coastguard Worker        if compresslevel == _COMPRESS_LEVEL_BEST:
262*cda5da8dSAndroid Build Coastguard Worker            xfl = b'\002'
263*cda5da8dSAndroid Build Coastguard Worker        elif compresslevel == _COMPRESS_LEVEL_FAST:
264*cda5da8dSAndroid Build Coastguard Worker            xfl = b'\004'
265*cda5da8dSAndroid Build Coastguard Worker        else:
266*cda5da8dSAndroid Build Coastguard Worker            xfl = b'\000'
267*cda5da8dSAndroid Build Coastguard Worker        self.fileobj.write(xfl)
268*cda5da8dSAndroid Build Coastguard Worker        self.fileobj.write(b'\377')
269*cda5da8dSAndroid Build Coastguard Worker        if fname:
270*cda5da8dSAndroid Build Coastguard Worker            self.fileobj.write(fname + b'\000')
271*cda5da8dSAndroid Build Coastguard Worker
272*cda5da8dSAndroid Build Coastguard Worker    def write(self,data):
273*cda5da8dSAndroid Build Coastguard Worker        self._check_not_closed()
274*cda5da8dSAndroid Build Coastguard Worker        if self.mode != WRITE:
275*cda5da8dSAndroid Build Coastguard Worker            import errno
276*cda5da8dSAndroid Build Coastguard Worker            raise OSError(errno.EBADF, "write() on read-only GzipFile object")
277*cda5da8dSAndroid Build Coastguard Worker
278*cda5da8dSAndroid Build Coastguard Worker        if self.fileobj is None:
279*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("write() on closed GzipFile object")
280*cda5da8dSAndroid Build Coastguard Worker
281*cda5da8dSAndroid Build Coastguard Worker        if isinstance(data, (bytes, bytearray)):
282*cda5da8dSAndroid Build Coastguard Worker            length = len(data)
283*cda5da8dSAndroid Build Coastguard Worker        else:
284*cda5da8dSAndroid Build Coastguard Worker            # accept any data that supports the buffer protocol
285*cda5da8dSAndroid Build Coastguard Worker            data = memoryview(data)
286*cda5da8dSAndroid Build Coastguard Worker            length = data.nbytes
287*cda5da8dSAndroid Build Coastguard Worker
288*cda5da8dSAndroid Build Coastguard Worker        if length > 0:
289*cda5da8dSAndroid Build Coastguard Worker            self.fileobj.write(self.compress.compress(data))
290*cda5da8dSAndroid Build Coastguard Worker            self.size += length
291*cda5da8dSAndroid Build Coastguard Worker            self.crc = zlib.crc32(data, self.crc)
292*cda5da8dSAndroid Build Coastguard Worker            self.offset += length
293*cda5da8dSAndroid Build Coastguard Worker
294*cda5da8dSAndroid Build Coastguard Worker        return length
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Worker    def read(self, size=-1):
297*cda5da8dSAndroid Build Coastguard Worker        self._check_not_closed()
298*cda5da8dSAndroid Build Coastguard Worker        if self.mode != READ:
299*cda5da8dSAndroid Build Coastguard Worker            import errno
300*cda5da8dSAndroid Build Coastguard Worker            raise OSError(errno.EBADF, "read() on write-only GzipFile object")
301*cda5da8dSAndroid Build Coastguard Worker        return self._buffer.read(size)
302*cda5da8dSAndroid Build Coastguard Worker
303*cda5da8dSAndroid Build Coastguard Worker    def read1(self, size=-1):
304*cda5da8dSAndroid Build Coastguard Worker        """Implements BufferedIOBase.read1()
305*cda5da8dSAndroid Build Coastguard Worker
306*cda5da8dSAndroid Build Coastguard Worker        Reads up to a buffer's worth of data if size is negative."""
307*cda5da8dSAndroid Build Coastguard Worker        self._check_not_closed()
308*cda5da8dSAndroid Build Coastguard Worker        if self.mode != READ:
309*cda5da8dSAndroid Build Coastguard Worker            import errno
310*cda5da8dSAndroid Build Coastguard Worker            raise OSError(errno.EBADF, "read1() on write-only GzipFile object")
311*cda5da8dSAndroid Build Coastguard Worker
312*cda5da8dSAndroid Build Coastguard Worker        if size < 0:
313*cda5da8dSAndroid Build Coastguard Worker            size = io.DEFAULT_BUFFER_SIZE
314*cda5da8dSAndroid Build Coastguard Worker        return self._buffer.read1(size)
315*cda5da8dSAndroid Build Coastguard Worker
316*cda5da8dSAndroid Build Coastguard Worker    def peek(self, n):
317*cda5da8dSAndroid Build Coastguard Worker        self._check_not_closed()
318*cda5da8dSAndroid Build Coastguard Worker        if self.mode != READ:
319*cda5da8dSAndroid Build Coastguard Worker            import errno
320*cda5da8dSAndroid Build Coastguard Worker            raise OSError(errno.EBADF, "peek() on write-only GzipFile object")
321*cda5da8dSAndroid Build Coastguard Worker        return self._buffer.peek(n)
322*cda5da8dSAndroid Build Coastguard Worker
323*cda5da8dSAndroid Build Coastguard Worker    @property
324*cda5da8dSAndroid Build Coastguard Worker    def closed(self):
325*cda5da8dSAndroid Build Coastguard Worker        return self.fileobj is None
326*cda5da8dSAndroid Build Coastguard Worker
327*cda5da8dSAndroid Build Coastguard Worker    def close(self):
328*cda5da8dSAndroid Build Coastguard Worker        fileobj = self.fileobj
329*cda5da8dSAndroid Build Coastguard Worker        if fileobj is None:
330*cda5da8dSAndroid Build Coastguard Worker            return
331*cda5da8dSAndroid Build Coastguard Worker        self.fileobj = None
332*cda5da8dSAndroid Build Coastguard Worker        try:
333*cda5da8dSAndroid Build Coastguard Worker            if self.mode == WRITE:
334*cda5da8dSAndroid Build Coastguard Worker                fileobj.write(self.compress.flush())
335*cda5da8dSAndroid Build Coastguard Worker                write32u(fileobj, self.crc)
336*cda5da8dSAndroid Build Coastguard Worker                # self.size may exceed 2 GiB, or even 4 GiB
337*cda5da8dSAndroid Build Coastguard Worker                write32u(fileobj, self.size & 0xffffffff)
338*cda5da8dSAndroid Build Coastguard Worker            elif self.mode == READ:
339*cda5da8dSAndroid Build Coastguard Worker                self._buffer.close()
340*cda5da8dSAndroid Build Coastguard Worker        finally:
341*cda5da8dSAndroid Build Coastguard Worker            myfileobj = self.myfileobj
342*cda5da8dSAndroid Build Coastguard Worker            if myfileobj:
343*cda5da8dSAndroid Build Coastguard Worker                self.myfileobj = None
344*cda5da8dSAndroid Build Coastguard Worker                myfileobj.close()
345*cda5da8dSAndroid Build Coastguard Worker
346*cda5da8dSAndroid Build Coastguard Worker    def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
347*cda5da8dSAndroid Build Coastguard Worker        self._check_not_closed()
348*cda5da8dSAndroid Build Coastguard Worker        if self.mode == WRITE:
349*cda5da8dSAndroid Build Coastguard Worker            # Ensure the compressor's buffer is flushed
350*cda5da8dSAndroid Build Coastguard Worker            self.fileobj.write(self.compress.flush(zlib_mode))
351*cda5da8dSAndroid Build Coastguard Worker            self.fileobj.flush()
352*cda5da8dSAndroid Build Coastguard Worker
353*cda5da8dSAndroid Build Coastguard Worker    def fileno(self):
354*cda5da8dSAndroid Build Coastguard Worker        """Invoke the underlying file object's fileno() method.
355*cda5da8dSAndroid Build Coastguard Worker
356*cda5da8dSAndroid Build Coastguard Worker        This will raise AttributeError if the underlying file object
357*cda5da8dSAndroid Build Coastguard Worker        doesn't support fileno().
358*cda5da8dSAndroid Build Coastguard Worker        """
359*cda5da8dSAndroid Build Coastguard Worker        return self.fileobj.fileno()
360*cda5da8dSAndroid Build Coastguard Worker
361*cda5da8dSAndroid Build Coastguard Worker    def rewind(self):
362*cda5da8dSAndroid Build Coastguard Worker        '''Return the uncompressed stream file position indicator to the
363*cda5da8dSAndroid Build Coastguard Worker        beginning of the file'''
364*cda5da8dSAndroid Build Coastguard Worker        if self.mode != READ:
365*cda5da8dSAndroid Build Coastguard Worker            raise OSError("Can't rewind in write mode")
366*cda5da8dSAndroid Build Coastguard Worker        self._buffer.seek(0)
367*cda5da8dSAndroid Build Coastguard Worker
368*cda5da8dSAndroid Build Coastguard Worker    def readable(self):
369*cda5da8dSAndroid Build Coastguard Worker        return self.mode == READ
370*cda5da8dSAndroid Build Coastguard Worker
371*cda5da8dSAndroid Build Coastguard Worker    def writable(self):
372*cda5da8dSAndroid Build Coastguard Worker        return self.mode == WRITE
373*cda5da8dSAndroid Build Coastguard Worker
374*cda5da8dSAndroid Build Coastguard Worker    def seekable(self):
375*cda5da8dSAndroid Build Coastguard Worker        return True
376*cda5da8dSAndroid Build Coastguard Worker
377*cda5da8dSAndroid Build Coastguard Worker    def seek(self, offset, whence=io.SEEK_SET):
378*cda5da8dSAndroid Build Coastguard Worker        if self.mode == WRITE:
379*cda5da8dSAndroid Build Coastguard Worker            if whence != io.SEEK_SET:
380*cda5da8dSAndroid Build Coastguard Worker                if whence == io.SEEK_CUR:
381*cda5da8dSAndroid Build Coastguard Worker                    offset = self.offset + offset
382*cda5da8dSAndroid Build Coastguard Worker                else:
383*cda5da8dSAndroid Build Coastguard Worker                    raise ValueError('Seek from end not supported')
384*cda5da8dSAndroid Build Coastguard Worker            if offset < self.offset:
385*cda5da8dSAndroid Build Coastguard Worker                raise OSError('Negative seek in write mode')
386*cda5da8dSAndroid Build Coastguard Worker            count = offset - self.offset
387*cda5da8dSAndroid Build Coastguard Worker            chunk = b'\0' * 1024
388*cda5da8dSAndroid Build Coastguard Worker            for i in range(count // 1024):
389*cda5da8dSAndroid Build Coastguard Worker                self.write(chunk)
390*cda5da8dSAndroid Build Coastguard Worker            self.write(b'\0' * (count % 1024))
391*cda5da8dSAndroid Build Coastguard Worker        elif self.mode == READ:
392*cda5da8dSAndroid Build Coastguard Worker            self._check_not_closed()
393*cda5da8dSAndroid Build Coastguard Worker            return self._buffer.seek(offset, whence)
394*cda5da8dSAndroid Build Coastguard Worker
395*cda5da8dSAndroid Build Coastguard Worker        return self.offset
396*cda5da8dSAndroid Build Coastguard Worker
397*cda5da8dSAndroid Build Coastguard Worker    def readline(self, size=-1):
398*cda5da8dSAndroid Build Coastguard Worker        self._check_not_closed()
399*cda5da8dSAndroid Build Coastguard Worker        return self._buffer.readline(size)
400*cda5da8dSAndroid Build Coastguard Worker
401*cda5da8dSAndroid Build Coastguard Worker
402*cda5da8dSAndroid Build Coastguard Workerdef _read_exact(fp, n):
403*cda5da8dSAndroid Build Coastguard Worker    '''Read exactly *n* bytes from `fp`
404*cda5da8dSAndroid Build Coastguard Worker
405*cda5da8dSAndroid Build Coastguard Worker    This method is required because fp may be unbuffered,
406*cda5da8dSAndroid Build Coastguard Worker    i.e. return short reads.
407*cda5da8dSAndroid Build Coastguard Worker    '''
408*cda5da8dSAndroid Build Coastguard Worker    data = fp.read(n)
409*cda5da8dSAndroid Build Coastguard Worker    while len(data) < n:
410*cda5da8dSAndroid Build Coastguard Worker        b = fp.read(n - len(data))
411*cda5da8dSAndroid Build Coastguard Worker        if not b:
412*cda5da8dSAndroid Build Coastguard Worker            raise EOFError("Compressed file ended before the "
413*cda5da8dSAndroid Build Coastguard Worker                           "end-of-stream marker was reached")
414*cda5da8dSAndroid Build Coastguard Worker        data += b
415*cda5da8dSAndroid Build Coastguard Worker    return data
416*cda5da8dSAndroid Build Coastguard Worker
417*cda5da8dSAndroid Build Coastguard Worker
418*cda5da8dSAndroid Build Coastguard Workerdef _read_gzip_header(fp):
419*cda5da8dSAndroid Build Coastguard Worker    '''Read a gzip header from `fp` and progress to the end of the header.
420*cda5da8dSAndroid Build Coastguard Worker
421*cda5da8dSAndroid Build Coastguard Worker    Returns last mtime if header was present or None otherwise.
422*cda5da8dSAndroid Build Coastguard Worker    '''
423*cda5da8dSAndroid Build Coastguard Worker    magic = fp.read(2)
424*cda5da8dSAndroid Build Coastguard Worker    if magic == b'':
425*cda5da8dSAndroid Build Coastguard Worker        return None
426*cda5da8dSAndroid Build Coastguard Worker
427*cda5da8dSAndroid Build Coastguard Worker    if magic != b'\037\213':
428*cda5da8dSAndroid Build Coastguard Worker        raise BadGzipFile('Not a gzipped file (%r)' % magic)
429*cda5da8dSAndroid Build Coastguard Worker
430*cda5da8dSAndroid Build Coastguard Worker    (method, flag, last_mtime) = struct.unpack("<BBIxx", _read_exact(fp, 8))
431*cda5da8dSAndroid Build Coastguard Worker    if method != 8:
432*cda5da8dSAndroid Build Coastguard Worker        raise BadGzipFile('Unknown compression method')
433*cda5da8dSAndroid Build Coastguard Worker
434*cda5da8dSAndroid Build Coastguard Worker    if flag & FEXTRA:
435*cda5da8dSAndroid Build Coastguard Worker        # Read & discard the extra field, if present
436*cda5da8dSAndroid Build Coastguard Worker        extra_len, = struct.unpack("<H", _read_exact(fp, 2))
437*cda5da8dSAndroid Build Coastguard Worker        _read_exact(fp, extra_len)
438*cda5da8dSAndroid Build Coastguard Worker    if flag & FNAME:
439*cda5da8dSAndroid Build Coastguard Worker        # Read and discard a null-terminated string containing the filename
440*cda5da8dSAndroid Build Coastguard Worker        while True:
441*cda5da8dSAndroid Build Coastguard Worker            s = fp.read(1)
442*cda5da8dSAndroid Build Coastguard Worker            if not s or s==b'\000':
443*cda5da8dSAndroid Build Coastguard Worker                break
444*cda5da8dSAndroid Build Coastguard Worker    if flag & FCOMMENT:
445*cda5da8dSAndroid Build Coastguard Worker        # Read and discard a null-terminated string containing a comment
446*cda5da8dSAndroid Build Coastguard Worker        while True:
447*cda5da8dSAndroid Build Coastguard Worker            s = fp.read(1)
448*cda5da8dSAndroid Build Coastguard Worker            if not s or s==b'\000':
449*cda5da8dSAndroid Build Coastguard Worker                break
450*cda5da8dSAndroid Build Coastguard Worker    if flag & FHCRC:
451*cda5da8dSAndroid Build Coastguard Worker        _read_exact(fp, 2)     # Read & discard the 16-bit header CRC
452*cda5da8dSAndroid Build Coastguard Worker    return last_mtime
453*cda5da8dSAndroid Build Coastguard Worker
454*cda5da8dSAndroid Build Coastguard Worker
455*cda5da8dSAndroid Build Coastguard Workerclass _GzipReader(_compression.DecompressReader):
456*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, fp):
457*cda5da8dSAndroid Build Coastguard Worker        super().__init__(_PaddedFile(fp), zlib.decompressobj,
458*cda5da8dSAndroid Build Coastguard Worker                         wbits=-zlib.MAX_WBITS)
459*cda5da8dSAndroid Build Coastguard Worker        # Set flag indicating start of a new member
460*cda5da8dSAndroid Build Coastguard Worker        self._new_member = True
461*cda5da8dSAndroid Build Coastguard Worker        self._last_mtime = None
462*cda5da8dSAndroid Build Coastguard Worker
463*cda5da8dSAndroid Build Coastguard Worker    def _init_read(self):
464*cda5da8dSAndroid Build Coastguard Worker        self._crc = zlib.crc32(b"")
465*cda5da8dSAndroid Build Coastguard Worker        self._stream_size = 0  # Decompressed size of unconcatenated stream
466*cda5da8dSAndroid Build Coastguard Worker
467*cda5da8dSAndroid Build Coastguard Worker    def _read_gzip_header(self):
468*cda5da8dSAndroid Build Coastguard Worker        last_mtime = _read_gzip_header(self._fp)
469*cda5da8dSAndroid Build Coastguard Worker        if last_mtime is None:
470*cda5da8dSAndroid Build Coastguard Worker            return False
471*cda5da8dSAndroid Build Coastguard Worker        self._last_mtime = last_mtime
472*cda5da8dSAndroid Build Coastguard Worker        return True
473*cda5da8dSAndroid Build Coastguard Worker
474*cda5da8dSAndroid Build Coastguard Worker    def read(self, size=-1):
475*cda5da8dSAndroid Build Coastguard Worker        if size < 0:
476*cda5da8dSAndroid Build Coastguard Worker            return self.readall()
477*cda5da8dSAndroid Build Coastguard Worker        # size=0 is special because decompress(max_length=0) is not supported
478*cda5da8dSAndroid Build Coastguard Worker        if not size:
479*cda5da8dSAndroid Build Coastguard Worker            return b""
480*cda5da8dSAndroid Build Coastguard Worker
481*cda5da8dSAndroid Build Coastguard Worker        # For certain input data, a single
482*cda5da8dSAndroid Build Coastguard Worker        # call to decompress() may not return
483*cda5da8dSAndroid Build Coastguard Worker        # any data. In this case, retry until we get some data or reach EOF.
484*cda5da8dSAndroid Build Coastguard Worker        while True:
485*cda5da8dSAndroid Build Coastguard Worker            if self._decompressor.eof:
486*cda5da8dSAndroid Build Coastguard Worker                # Ending case: we've come to the end of a member in the file,
487*cda5da8dSAndroid Build Coastguard Worker                # so finish up this member, and read a new gzip header.
488*cda5da8dSAndroid Build Coastguard Worker                # Check the CRC and file size, and set the flag so we read
489*cda5da8dSAndroid Build Coastguard Worker                # a new member
490*cda5da8dSAndroid Build Coastguard Worker                self._read_eof()
491*cda5da8dSAndroid Build Coastguard Worker                self._new_member = True
492*cda5da8dSAndroid Build Coastguard Worker                self._decompressor = self._decomp_factory(
493*cda5da8dSAndroid Build Coastguard Worker                    **self._decomp_args)
494*cda5da8dSAndroid Build Coastguard Worker
495*cda5da8dSAndroid Build Coastguard Worker            if self._new_member:
496*cda5da8dSAndroid Build Coastguard Worker                # If the _new_member flag is set, we have to
497*cda5da8dSAndroid Build Coastguard Worker                # jump to the next member, if there is one.
498*cda5da8dSAndroid Build Coastguard Worker                self._init_read()
499*cda5da8dSAndroid Build Coastguard Worker                if not self._read_gzip_header():
500*cda5da8dSAndroid Build Coastguard Worker                    self._size = self._pos
501*cda5da8dSAndroid Build Coastguard Worker                    return b""
502*cda5da8dSAndroid Build Coastguard Worker                self._new_member = False
503*cda5da8dSAndroid Build Coastguard Worker
504*cda5da8dSAndroid Build Coastguard Worker            # Read a chunk of data from the file
505*cda5da8dSAndroid Build Coastguard Worker            buf = self._fp.read(io.DEFAULT_BUFFER_SIZE)
506*cda5da8dSAndroid Build Coastguard Worker
507*cda5da8dSAndroid Build Coastguard Worker            uncompress = self._decompressor.decompress(buf, size)
508*cda5da8dSAndroid Build Coastguard Worker            if self._decompressor.unconsumed_tail != b"":
509*cda5da8dSAndroid Build Coastguard Worker                self._fp.prepend(self._decompressor.unconsumed_tail)
510*cda5da8dSAndroid Build Coastguard Worker            elif self._decompressor.unused_data != b"":
511*cda5da8dSAndroid Build Coastguard Worker                # Prepend the already read bytes to the fileobj so they can
512*cda5da8dSAndroid Build Coastguard Worker                # be seen by _read_eof() and _read_gzip_header()
513*cda5da8dSAndroid Build Coastguard Worker                self._fp.prepend(self._decompressor.unused_data)
514*cda5da8dSAndroid Build Coastguard Worker
515*cda5da8dSAndroid Build Coastguard Worker            if uncompress != b"":
516*cda5da8dSAndroid Build Coastguard Worker                break
517*cda5da8dSAndroid Build Coastguard Worker            if buf == b"":
518*cda5da8dSAndroid Build Coastguard Worker                raise EOFError("Compressed file ended before the "
519*cda5da8dSAndroid Build Coastguard Worker                               "end-of-stream marker was reached")
520*cda5da8dSAndroid Build Coastguard Worker
521*cda5da8dSAndroid Build Coastguard Worker        self._add_read_data( uncompress )
522*cda5da8dSAndroid Build Coastguard Worker        self._pos += len(uncompress)
523*cda5da8dSAndroid Build Coastguard Worker        return uncompress
524*cda5da8dSAndroid Build Coastguard Worker
525*cda5da8dSAndroid Build Coastguard Worker    def _add_read_data(self, data):
526*cda5da8dSAndroid Build Coastguard Worker        self._crc = zlib.crc32(data, self._crc)
527*cda5da8dSAndroid Build Coastguard Worker        self._stream_size = self._stream_size + len(data)
528*cda5da8dSAndroid Build Coastguard Worker
529*cda5da8dSAndroid Build Coastguard Worker    def _read_eof(self):
530*cda5da8dSAndroid Build Coastguard Worker        # We've read to the end of the file
531*cda5da8dSAndroid Build Coastguard Worker        # We check that the computed CRC and size of the
532*cda5da8dSAndroid Build Coastguard Worker        # uncompressed data matches the stored values.  Note that the size
533*cda5da8dSAndroid Build Coastguard Worker        # stored is the true file size mod 2**32.
534*cda5da8dSAndroid Build Coastguard Worker        crc32, isize = struct.unpack("<II", _read_exact(self._fp, 8))
535*cda5da8dSAndroid Build Coastguard Worker        if crc32 != self._crc:
536*cda5da8dSAndroid Build Coastguard Worker            raise BadGzipFile("CRC check failed %s != %s" % (hex(crc32),
537*cda5da8dSAndroid Build Coastguard Worker                                                             hex(self._crc)))
538*cda5da8dSAndroid Build Coastguard Worker        elif isize != (self._stream_size & 0xffffffff):
539*cda5da8dSAndroid Build Coastguard Worker            raise BadGzipFile("Incorrect length of data produced")
540*cda5da8dSAndroid Build Coastguard Worker
541*cda5da8dSAndroid Build Coastguard Worker        # Gzip files can be padded with zeroes and still have archives.
542*cda5da8dSAndroid Build Coastguard Worker        # Consume all zero bytes and set the file position to the first
543*cda5da8dSAndroid Build Coastguard Worker        # non-zero byte. See http://www.gzip.org/#faq8
544*cda5da8dSAndroid Build Coastguard Worker        c = b"\x00"
545*cda5da8dSAndroid Build Coastguard Worker        while c == b"\x00":
546*cda5da8dSAndroid Build Coastguard Worker            c = self._fp.read(1)
547*cda5da8dSAndroid Build Coastguard Worker        if c:
548*cda5da8dSAndroid Build Coastguard Worker            self._fp.prepend(c)
549*cda5da8dSAndroid Build Coastguard Worker
550*cda5da8dSAndroid Build Coastguard Worker    def _rewind(self):
551*cda5da8dSAndroid Build Coastguard Worker        super()._rewind()
552*cda5da8dSAndroid Build Coastguard Worker        self._new_member = True
553*cda5da8dSAndroid Build Coastguard Worker
554*cda5da8dSAndroid Build Coastguard Worker
555*cda5da8dSAndroid Build Coastguard Workerdef _create_simple_gzip_header(compresslevel: int,
556*cda5da8dSAndroid Build Coastguard Worker                               mtime = None) -> bytes:
557*cda5da8dSAndroid Build Coastguard Worker    """
558*cda5da8dSAndroid Build Coastguard Worker    Write a simple gzip header with no extra fields.
559*cda5da8dSAndroid Build Coastguard Worker    :param compresslevel: Compresslevel used to determine the xfl bytes.
560*cda5da8dSAndroid Build Coastguard Worker    :param mtime: The mtime (must support conversion to a 32-bit integer).
561*cda5da8dSAndroid Build Coastguard Worker    :return: A bytes object representing the gzip header.
562*cda5da8dSAndroid Build Coastguard Worker    """
563*cda5da8dSAndroid Build Coastguard Worker    if mtime is None:
564*cda5da8dSAndroid Build Coastguard Worker        mtime = time.time()
565*cda5da8dSAndroid Build Coastguard Worker    if compresslevel == _COMPRESS_LEVEL_BEST:
566*cda5da8dSAndroid Build Coastguard Worker        xfl = 2
567*cda5da8dSAndroid Build Coastguard Worker    elif compresslevel == _COMPRESS_LEVEL_FAST:
568*cda5da8dSAndroid Build Coastguard Worker        xfl = 4
569*cda5da8dSAndroid Build Coastguard Worker    else:
570*cda5da8dSAndroid Build Coastguard Worker        xfl = 0
571*cda5da8dSAndroid Build Coastguard Worker    # Pack ID1 and ID2 magic bytes, method (8=deflate), header flags (no extra
572*cda5da8dSAndroid Build Coastguard Worker    # fields added to header), mtime, xfl and os (255 for unknown OS).
573*cda5da8dSAndroid Build Coastguard Worker    return struct.pack("<BBBBLBB", 0x1f, 0x8b, 8, 0, int(mtime), xfl, 255)
574*cda5da8dSAndroid Build Coastguard Worker
575*cda5da8dSAndroid Build Coastguard Worker
576*cda5da8dSAndroid Build Coastguard Workerdef compress(data, compresslevel=_COMPRESS_LEVEL_BEST, *, mtime=None):
577*cda5da8dSAndroid Build Coastguard Worker    """Compress data in one shot and return the compressed string.
578*cda5da8dSAndroid Build Coastguard Worker
579*cda5da8dSAndroid Build Coastguard Worker    compresslevel sets the compression level in range of 0-9.
580*cda5da8dSAndroid Build Coastguard Worker    mtime can be used to set the modification time. The modification time is
581*cda5da8dSAndroid Build Coastguard Worker    set to the current time by default.
582*cda5da8dSAndroid Build Coastguard Worker    """
583*cda5da8dSAndroid Build Coastguard Worker    if mtime == 0:
584*cda5da8dSAndroid Build Coastguard Worker        # Use zlib as it creates the header with 0 mtime by default.
585*cda5da8dSAndroid Build Coastguard Worker        # This is faster and with less overhead.
586*cda5da8dSAndroid Build Coastguard Worker        return zlib.compress(data, level=compresslevel, wbits=31)
587*cda5da8dSAndroid Build Coastguard Worker    header = _create_simple_gzip_header(compresslevel, mtime)
588*cda5da8dSAndroid Build Coastguard Worker    trailer = struct.pack("<LL", zlib.crc32(data), (len(data) & 0xffffffff))
589*cda5da8dSAndroid Build Coastguard Worker    # Wbits=-15 creates a raw deflate block.
590*cda5da8dSAndroid Build Coastguard Worker    return (header + zlib.compress(data, level=compresslevel, wbits=-15) +
591*cda5da8dSAndroid Build Coastguard Worker            trailer)
592*cda5da8dSAndroid Build Coastguard Worker
593*cda5da8dSAndroid Build Coastguard Worker
594*cda5da8dSAndroid Build Coastguard Workerdef decompress(data):
595*cda5da8dSAndroid Build Coastguard Worker    """Decompress a gzip compressed string in one shot.
596*cda5da8dSAndroid Build Coastguard Worker    Return the decompressed string.
597*cda5da8dSAndroid Build Coastguard Worker    """
598*cda5da8dSAndroid Build Coastguard Worker    decompressed_members = []
599*cda5da8dSAndroid Build Coastguard Worker    while True:
600*cda5da8dSAndroid Build Coastguard Worker        fp = io.BytesIO(data)
601*cda5da8dSAndroid Build Coastguard Worker        if _read_gzip_header(fp) is None:
602*cda5da8dSAndroid Build Coastguard Worker            return b"".join(decompressed_members)
603*cda5da8dSAndroid Build Coastguard Worker        # Use a zlib raw deflate compressor
604*cda5da8dSAndroid Build Coastguard Worker        do = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
605*cda5da8dSAndroid Build Coastguard Worker        # Read all the data except the header
606*cda5da8dSAndroid Build Coastguard Worker        decompressed = do.decompress(data[fp.tell():])
607*cda5da8dSAndroid Build Coastguard Worker        if not do.eof or len(do.unused_data) < 8:
608*cda5da8dSAndroid Build Coastguard Worker            raise EOFError("Compressed file ended before the end-of-stream "
609*cda5da8dSAndroid Build Coastguard Worker                           "marker was reached")
610*cda5da8dSAndroid Build Coastguard Worker        crc, length = struct.unpack("<II", do.unused_data[:8])
611*cda5da8dSAndroid Build Coastguard Worker        if crc != zlib.crc32(decompressed):
612*cda5da8dSAndroid Build Coastguard Worker            raise BadGzipFile("CRC check failed")
613*cda5da8dSAndroid Build Coastguard Worker        if length != (len(decompressed) & 0xffffffff):
614*cda5da8dSAndroid Build Coastguard Worker            raise BadGzipFile("Incorrect length of data produced")
615*cda5da8dSAndroid Build Coastguard Worker        decompressed_members.append(decompressed)
616*cda5da8dSAndroid Build Coastguard Worker        data = do.unused_data[8:].lstrip(b"\x00")
617*cda5da8dSAndroid Build Coastguard Worker
618*cda5da8dSAndroid Build Coastguard Worker
619*cda5da8dSAndroid Build Coastguard Workerdef main():
620*cda5da8dSAndroid Build Coastguard Worker    from argparse import ArgumentParser
621*cda5da8dSAndroid Build Coastguard Worker    parser = ArgumentParser(description=
622*cda5da8dSAndroid Build Coastguard Worker        "A simple command line interface for the gzip module: act like gzip, "
623*cda5da8dSAndroid Build Coastguard Worker        "but do not delete the input file.")
624*cda5da8dSAndroid Build Coastguard Worker    group = parser.add_mutually_exclusive_group()
625*cda5da8dSAndroid Build Coastguard Worker    group.add_argument('--fast', action='store_true', help='compress faster')
626*cda5da8dSAndroid Build Coastguard Worker    group.add_argument('--best', action='store_true', help='compress better')
627*cda5da8dSAndroid Build Coastguard Worker    group.add_argument("-d", "--decompress", action="store_true",
628*cda5da8dSAndroid Build Coastguard Worker                        help="act like gunzip instead of gzip")
629*cda5da8dSAndroid Build Coastguard Worker
630*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument("args", nargs="*", default=["-"], metavar='file')
631*cda5da8dSAndroid Build Coastguard Worker    args = parser.parse_args()
632*cda5da8dSAndroid Build Coastguard Worker
633*cda5da8dSAndroid Build Coastguard Worker    compresslevel = _COMPRESS_LEVEL_TRADEOFF
634*cda5da8dSAndroid Build Coastguard Worker    if args.fast:
635*cda5da8dSAndroid Build Coastguard Worker        compresslevel = _COMPRESS_LEVEL_FAST
636*cda5da8dSAndroid Build Coastguard Worker    elif args.best:
637*cda5da8dSAndroid Build Coastguard Worker        compresslevel = _COMPRESS_LEVEL_BEST
638*cda5da8dSAndroid Build Coastguard Worker
639*cda5da8dSAndroid Build Coastguard Worker    for arg in args.args:
640*cda5da8dSAndroid Build Coastguard Worker        if args.decompress:
641*cda5da8dSAndroid Build Coastguard Worker            if arg == "-":
642*cda5da8dSAndroid Build Coastguard Worker                f = GzipFile(filename="", mode="rb", fileobj=sys.stdin.buffer)
643*cda5da8dSAndroid Build Coastguard Worker                g = sys.stdout.buffer
644*cda5da8dSAndroid Build Coastguard Worker            else:
645*cda5da8dSAndroid Build Coastguard Worker                if arg[-3:] != ".gz":
646*cda5da8dSAndroid Build Coastguard Worker                    sys.exit(f"filename doesn't end in .gz: {arg!r}")
647*cda5da8dSAndroid Build Coastguard Worker                f = open(arg, "rb")
648*cda5da8dSAndroid Build Coastguard Worker                g = builtins.open(arg[:-3], "wb")
649*cda5da8dSAndroid Build Coastguard Worker        else:
650*cda5da8dSAndroid Build Coastguard Worker            if arg == "-":
651*cda5da8dSAndroid Build Coastguard Worker                f = sys.stdin.buffer
652*cda5da8dSAndroid Build Coastguard Worker                g = GzipFile(filename="", mode="wb", fileobj=sys.stdout.buffer,
653*cda5da8dSAndroid Build Coastguard Worker                             compresslevel=compresslevel)
654*cda5da8dSAndroid Build Coastguard Worker            else:
655*cda5da8dSAndroid Build Coastguard Worker                f = builtins.open(arg, "rb")
656*cda5da8dSAndroid Build Coastguard Worker                g = open(arg + ".gz", "wb")
657*cda5da8dSAndroid Build Coastguard Worker        while True:
658*cda5da8dSAndroid Build Coastguard Worker            chunk = f.read(io.DEFAULT_BUFFER_SIZE)
659*cda5da8dSAndroid Build Coastguard Worker            if not chunk:
660*cda5da8dSAndroid Build Coastguard Worker                break
661*cda5da8dSAndroid Build Coastguard Worker            g.write(chunk)
662*cda5da8dSAndroid Build Coastguard Worker        if g is not sys.stdout.buffer:
663*cda5da8dSAndroid Build Coastguard Worker            g.close()
664*cda5da8dSAndroid Build Coastguard Worker        if f is not sys.stdin.buffer:
665*cda5da8dSAndroid Build Coastguard Worker            f.close()
666*cda5da8dSAndroid Build Coastguard Worker
667*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__':
668*cda5da8dSAndroid Build Coastguard Worker    main()
669