~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tuned_gzip.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
# Written by Robert Collins <robert.collins@canonical.com>
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
17
17
 
18
18
"""Bzrlib specific gzip tunings. We plan to feed these to the upstream gzip."""
19
19
 
 
20
from __future__ import absolute_import
 
21
 
20
22
from cStringIO import StringIO
21
23
 
22
24
# make GzipFile faster:
27
29
import zlib
28
30
 
29
31
# we want a \n preserved, break on \n only splitlines.
30
 
import bzrlib
 
32
from bzrlib import symbol_versioning
31
33
 
32
34
__all__ = ["GzipFile", "bytes_to_gzip"]
33
35
 
52
54
    width=-zlib.MAX_WBITS, mem=zlib.DEF_MEM_LEVEL,
53
55
    crc32=zlib.crc32):
54
56
    """Create a gzip file containing bytes and return its content."""
 
57
    return chunks_to_gzip([bytes])
 
58
 
 
59
 
 
60
def chunks_to_gzip(chunks, factory=zlib.compressobj,
 
61
    level=zlib.Z_DEFAULT_COMPRESSION, method=zlib.DEFLATED,
 
62
    width=-zlib.MAX_WBITS, mem=zlib.DEF_MEM_LEVEL,
 
63
    crc32=zlib.crc32):
 
64
    """Create a gzip file containing chunks and return its content.
 
65
 
 
66
    :param chunks: An iterable of strings. Each string can have arbitrary
 
67
        layout.
 
68
    """
55
69
    result = [
56
70
        '\037\213'  # self.fileobj.write('\037\213')  # magic header
57
71
        '\010'      # self.fileobj.write('\010')      # compression method
69
83
    # using a compressobj avoids a small header and trailer that the compress()
70
84
    # utility function adds.
71
85
    compress = factory(level, method, width, mem, 0)
72
 
    result.append(compress.compress(bytes))
 
86
    crc = 0
 
87
    total_len = 0
 
88
    for chunk in chunks:
 
89
        crc = crc32(chunk, crc)
 
90
        total_len += len(chunk)
 
91
        zbytes = compress.compress(chunk)
 
92
        if zbytes:
 
93
            result.append(zbytes)
73
94
    result.append(compress.flush())
74
 
    result.append(struct.pack("<L", LOWU32(crc32(bytes))))
75
95
    # size may exceed 2GB, or even 4GB
76
 
    result.append(struct.pack("<L", LOWU32(len(bytes))))
 
96
    result.append(struct.pack("<LL", LOWU32(crc), LOWU32(total_len)))
77
97
    return ''.join(result)
78
98
 
79
99
 
100
120
    Yes, its only 1.6 seconds, but they add up.
101
121
    """
102
122
 
103
 
    def _add_read_data(self, data):
104
 
        # 4169 calls in 183
105
 
        # temp var for len(data) and switch to +='s.
106
 
        # 4169 in 139
107
 
        len_data = len(data)
108
 
        self.crc = zlib.crc32(data, self.crc)
109
 
        self.extrabuf += data
110
 
        self.extrasize += len_data
111
 
        self.size += len_data
 
123
    def __init__(self, *args, **kwargs):
 
124
        symbol_versioning.warn(
 
125
            symbol_versioning.deprecated_in((2, 3, 0))
 
126
            % 'bzrlib.tuned_gzip.GzipFile',
 
127
            DeprecationWarning, stacklevel=2)
 
128
        gzip.GzipFile.__init__(self, *args, **kwargs)
 
129
 
 
130
    if sys.version_info >= (2, 7, 4):
 
131
        def _add_read_data(self, data):
 
132
            # 4169 calls in 183
 
133
            # temp var for len(data) and switch to +='s.
 
134
            # 4169 in 139
 
135
            len_data = len(data)
 
136
            self.crc = zlib.crc32(data, self.crc) & 0xffffffffL
 
137
            offset = self.offset - self.extrastart
 
138
            self.extrabuf = self.extrabuf[offset:] + data
 
139
            self.extrasize = self.extrasize + len_data
 
140
            self.extrastart = self.offset
 
141
            self.size = self.size + len_data
 
142
    else:
 
143
        def _add_read_data(self, data):
 
144
            # 4169 calls in 183
 
145
            # temp var for len(data) and switch to +='s.
 
146
            # 4169 in 139
 
147
            len_data = len(data)
 
148
            self.crc = zlib.crc32(data, self.crc)
 
149
            self.extrabuf += data
 
150
            self.extrasize += len_data
 
151
            self.size += len_data
112
152
 
113
153
    def _write_gzip_header(self):
114
154
        """A tuned version of gzip._write_gzip_header
134
174
            ''          #     self.fileobj.write(fname + '\000')
135
175
            )
136
176
 
137
 
    def _read(self, size=1024):
138
 
        # various optimisations:
139
 
        # reduces lsprof count from 2500 to
140
 
        # 8337 calls in 1272, 365 internal
141
 
        if self.fileobj is None:
142
 
            raise EOFError, "Reached EOF"
143
 
 
144
 
        if self._new_member:
145
 
            # If the _new_member flag is set, we have to
146
 
            # jump to the next member, if there is one.
147
 
            #
148
 
            # First, check if we're at the end of the file;
149
 
            # if so, it's time to stop; no more members to read.
150
 
            next_header_bytes = self.fileobj.read(10)
151
 
            if next_header_bytes == '':
 
177
    if sys.version_info < (2, 7, 4):
 
178
        def _read(self, size=1024):
 
179
            # various optimisations:
 
180
            # reduces lsprof count from 2500 to
 
181
            # 8337 calls in 1272, 365 internal
 
182
            if self.fileobj is None:
152
183
                raise EOFError, "Reached EOF"
153
184
 
154
 
            self._init_read()
155
 
            self._read_gzip_header(next_header_bytes)
156
 
            self.decompress = zlib.decompressobj(-zlib.MAX_WBITS)
157
 
            self._new_member = False
158
 
 
159
 
        # Read a chunk of data from the file
160
 
        buf = self.fileobj.read(size)
161
 
 
162
 
        # If the EOF has been reached, flush the decompression object
163
 
        # and mark this object as finished.
164
 
 
165
 
        if buf == "":
166
 
            self._add_read_data(self.decompress.flush())
167
 
            if len(self.decompress.unused_data) < 8:
168
 
                raise AssertionError("what does flush do?")
169
 
            self._gzip_tail = self.decompress.unused_data[0:8]
170
 
            self._read_eof()
171
 
            # tell the driving read() call we have stuffed all the data
172
 
            # in self.extrabuf
173
 
            raise EOFError, 'Reached EOF'
174
 
 
175
 
        self._add_read_data(self.decompress.decompress(buf))
176
 
 
177
 
        if self.decompress.unused_data != "":
178
 
            # Ending case: we've come to the end of a member in the file,
179
 
            # so seek back to the start of the data for the next member which
180
 
            # is the length of the decompress objects unused data - the first
181
 
            # 8 bytes for the end crc and size records.
182
 
            #
183
 
            # so seek back to the start of the unused data, finish up
184
 
            # this member, and read a new gzip header.
185
 
            # (The number of bytes to seek back is the length of the unused
186
 
            # data, minus 8 because those 8 bytes are part of this member.
187
 
            seek_length = len (self.decompress.unused_data) - 8
188
 
            if seek_length > 0:
189
 
                # we read too much data
190
 
                self.fileobj.seek(-seek_length, 1)
 
185
            if self._new_member:
 
186
                # If the _new_member flag is set, we have to
 
187
                # jump to the next member, if there is one.
 
188
                #
 
189
                # First, check if we're at the end of the file;
 
190
                # if so, it's time to stop; no more members to read.
 
191
                next_header_bytes = self.fileobj.read(10)
 
192
                if next_header_bytes == '':
 
193
                    raise EOFError, "Reached EOF"
 
194
 
 
195
                self._init_read()
 
196
                self._read_gzip_header(next_header_bytes)
 
197
                self.decompress = zlib.decompressobj(-zlib.MAX_WBITS)
 
198
                self._new_member = False
 
199
 
 
200
            # Read a chunk of data from the file
 
201
            buf = self.fileobj.read(size)
 
202
 
 
203
            # If the EOF has been reached, flush the decompression object
 
204
            # and mark this object as finished.
 
205
 
 
206
            if buf == "":
 
207
                self._add_read_data(self.decompress.flush())
 
208
                if len(self.decompress.unused_data) < 8:
 
209
                    raise AssertionError("what does flush do?")
191
210
                self._gzip_tail = self.decompress.unused_data[0:8]
192
 
            elif seek_length < 0:
193
 
                # we haven't read enough to check the checksum.
194
 
                if not (-8 < seek_length):
195
 
                    raise AssertionError("too great a seek")
196
 
                buf = self.fileobj.read(-seek_length)
197
 
                self._gzip_tail = self.decompress.unused_data + buf
198
 
            else:
199
 
                self._gzip_tail = self.decompress.unused_data
200
 
 
201
 
            # Check the CRC and file size, and set the flag so we read
202
 
            # a new member on the next call
203
 
            self._read_eof()
204
 
            self._new_member = True
205
 
 
206
 
    def _read_eof(self):
207
 
        """tuned to reduce function calls and eliminate file seeking:
208
 
        pass 1:
209
 
        reduces lsprof count from 800 to 288
210
 
        4168 in 296
211
 
        avoid U32 call by using struct format L
212
 
        4168 in 200
213
 
        """
214
 
        # We've read to the end of the file, so we should have 8 bytes of
215
 
        # unused data in the decompressor. If we don't, there is a corrupt file.
216
 
        # We use these 8 bytes to calculate the CRC and the recorded file size.
217
 
        # We then check the that the computed CRC and size of the
218
 
        # uncompressed data matches the stored values.  Note that the size
219
 
        # stored is the true file size mod 2**32.
220
 
        if not (len(self._gzip_tail) == 8):
221
 
            raise AssertionError("gzip trailer is incorrect length.")
222
 
        crc32, isize = struct.unpack("<LL", self._gzip_tail)
223
 
        # note that isize is unsigned - it can exceed 2GB
224
 
        if crc32 != U32(self.crc):
225
 
            raise IOError, "CRC check failed %d %d" % (crc32, U32(self.crc))
226
 
        elif isize != LOWU32(self.size):
227
 
            raise IOError, "Incorrect length of data produced"
 
211
                self._read_eof()
 
212
                # tell the driving read() call we have stuffed all the data
 
213
                # in self.extrabuf
 
214
                raise EOFError, 'Reached EOF'
 
215
 
 
216
            self._add_read_data(self.decompress.decompress(buf))
 
217
 
 
218
            if self.decompress.unused_data != "":
 
219
                # Ending case: we've come to the end of a member in the file,
 
220
                # so seek back to the start of the data for the next member
 
221
                # which is the length of the decompress objects unused data -
 
222
                # the first 8 bytes for the end crc and size records.
 
223
                #
 
224
                # so seek back to the start of the unused data, finish up
 
225
                # this member, and read a new gzip header.
 
226
                # (The number of bytes to seek back is the length of the unused
 
227
                # data, minus 8 because those 8 bytes are part of this member.
 
228
                seek_length = len (self.decompress.unused_data) - 8
 
229
                if seek_length > 0:
 
230
                    # we read too much data
 
231
                    self.fileobj.seek(-seek_length, 1)
 
232
                    self._gzip_tail = self.decompress.unused_data[0:8]
 
233
                elif seek_length < 0:
 
234
                    # we haven't read enough to check the checksum.
 
235
                    if not (-8 < seek_length):
 
236
                        raise AssertionError("too great a seek")
 
237
                    buf = self.fileobj.read(-seek_length)
 
238
                    self._gzip_tail = self.decompress.unused_data + buf
 
239
                else:
 
240
                    self._gzip_tail = self.decompress.unused_data
 
241
 
 
242
                # Check the CRC and file size, and set the flag so we read
 
243
                # a new member on the next call
 
244
                self._read_eof()
 
245
                self._new_member = True
 
246
 
 
247
        def _read_eof(self):
 
248
            """tuned to reduce function calls and eliminate file seeking:
 
249
            pass 1:
 
250
            reduces lsprof count from 800 to 288
 
251
            4168 in 296
 
252
            avoid U32 call by using struct format L
 
253
            4168 in 200
 
254
            """
 
255
            # We've read to the end of the file, so we should have 8 bytes of
 
256
            # unused data in the decompressor. If we don't, there is a corrupt
 
257
            # file.  We use these 8 bytes to calculate the CRC and the recorded
 
258
            # file size.  We then check the that the computed CRC and size of
 
259
            # the uncompressed data matches the stored values.  Note that the
 
260
            # size stored is the true file size mod 2**32.
 
261
            if not (len(self._gzip_tail) == 8):
 
262
                raise AssertionError("gzip trailer is incorrect length.")
 
263
            crc32, isize = struct.unpack("<LL", self._gzip_tail)
 
264
            # note that isize is unsigned - it can exceed 2GB
 
265
            if crc32 != U32(self.crc):
 
266
                raise IOError, "CRC check failed %d %d" % (crc32, U32(self.crc))
 
267
            elif isize != LOWU32(self.size):
 
268
                raise IOError, "Incorrect length of data produced"
228
269
 
229
270
    def _read_gzip_header(self, bytes=None):
230
271
        """Supply bytes if the minimum header size is already read.
377
418
        # (4 seconds to 1 seconds for the sample upgrades I was testing).
378
419
        self.write(''.join(lines))
379
420
 
 
421
    if sys.version_info > (2, 7):
 
422
        # As of Python 2.7 the crc32 must be positive when close is called
 
423
        def close(self):
 
424
            if self.fileobj is None:
 
425
                return
 
426
            if self.mode == gzip.WRITE:
 
427
                self.crc &= 0xFFFFFFFFL
 
428
            gzip.GzipFile.close(self)
380
429