~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tuned_gzip.py

  • Committer: John Arbash Meinel
  • Date: 2007-04-12 20:27:42 UTC
  • mto: (2399.1.15 doc-cleanup)
  • mto: This revision was merged to the branch mainline in revision 2431.
  • Revision ID: john@arbash-meinel.com-20070412202742-4cr2qmchdfe9mg7n
Cherrypick just the epydoc builder changes.
This is just the piece of change that makes 'make api-docs' work,
without any actual documentation changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 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
13
13
#
14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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
 
 
22
20
from cStringIO import StringIO
23
21
 
24
22
# make GzipFile faster:
25
23
import gzip
26
 
from gzip import FEXTRA, FCOMMENT, FNAME, FHCRC
 
24
from gzip import U32, LOWU32, FEXTRA, FCOMMENT, FNAME, FHCRC
27
25
import sys
28
26
import struct
29
27
import zlib
30
28
 
31
29
# we want a \n preserved, break on \n only splitlines.
32
 
from bzrlib import symbol_versioning
33
 
 
34
 
__all__ = ["GzipFile", "bytes_to_gzip"]
35
 
 
36
 
 
37
 
def U32(i):
38
 
    """Return i as an unsigned integer, assuming it fits in 32 bits.
39
 
 
40
 
    If it's >= 2GB when viewed as a 32-bit unsigned int, return a long.
41
 
    """
42
 
    if i < 0:
43
 
        i += 1L << 32
44
 
    return i
45
 
 
46
 
 
47
 
def LOWU32(i):
48
 
    """Return the low-order 32 bits of an int, as a non-negative int."""
49
 
    return i & 0xFFFFFFFFL
50
 
 
51
 
 
52
 
def bytes_to_gzip(bytes, factory=zlib.compressobj,
53
 
    level=zlib.Z_DEFAULT_COMPRESSION, method=zlib.DEFLATED,
54
 
    width=-zlib.MAX_WBITS, mem=zlib.DEF_MEM_LEVEL,
55
 
    crc32=zlib.crc32):
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
 
    """
69
 
    result = [
70
 
        '\037\213'  # self.fileobj.write('\037\213')  # magic header
71
 
        '\010'      # self.fileobj.write('\010')      # compression method
72
 
                    # fname = self.filename[:-3]
73
 
                    # flags = 0
74
 
                    # if fname:
75
 
                    #     flags = FNAME
76
 
        '\x00'      # self.fileobj.write(chr(flags))
77
 
        '\0\0\0\0'  # write32u(self.fileobj, long(time.time()))
78
 
        '\002'      # self.fileobj.write('\002')
79
 
        '\377'      # self.fileobj.write('\377')
80
 
                    # if fname:
81
 
        ''          #     self.fileobj.write(fname + '\000')
82
 
        ]
83
 
    # using a compressobj avoids a small header and trailer that the compress()
84
 
    # utility function adds.
85
 
    compress = factory(level, method, width, mem, 0)
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)
94
 
    result.append(compress.flush())
95
 
    # size may exceed 2GB, or even 4GB
96
 
    result.append(struct.pack("<LL", LOWU32(crc), LOWU32(total_len)))
97
 
    return ''.join(result)
 
30
import bzrlib
 
31
 
 
32
__all__ = ["GzipFile"]
98
33
 
99
34
 
100
35
class GzipFile(gzip.GzipFile):
120
55
    Yes, its only 1.6 seconds, but they add up.
121
56
    """
122
57
 
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
58
    def _add_read_data(self, data):
131
59
        # 4169 calls in 183
132
60
        # temp var for len(data) and switch to +='s.
141
69
        """A tuned version of gzip._write_gzip_header
142
70
 
143
71
        We have some extra constrains that plain Gzip does not.
144
 
        1) We want to write the whole blob at once. rather than multiple
 
72
        1) We want to write the whole blob at once. rather than multiple 
145
73
           calls to fileobj.write().
146
74
        2) We never have a filename
147
75
        3) We don't care about the time
163
91
 
164
92
    def _read(self, size=1024):
165
93
        # various optimisations:
166
 
        # reduces lsprof count from 2500 to
 
94
        # reduces lsprof count from 2500 to 
167
95
        # 8337 calls in 1272, 365 internal
168
96
        if self.fileobj is None:
169
97
            raise EOFError, "Reached EOF"
191
119
 
192
120
        if buf == "":
193
121
            self._add_read_data(self.decompress.flush())
194
 
            if len(self.decompress.unused_data) < 8:
195
 
                raise AssertionError("what does flush do?")
 
122
            assert len(self.decompress.unused_data) >= 8, "what does flush do?"
196
123
            self._gzip_tail = self.decompress.unused_data[0:8]
197
124
            self._read_eof()
198
125
            # tell the driving read() call we have stuffed all the data
218
145
                self._gzip_tail = self.decompress.unused_data[0:8]
219
146
            elif seek_length < 0:
220
147
                # we haven't read enough to check the checksum.
221
 
                if not (-8 < seek_length):
222
 
                    raise AssertionError("too great a seek")
 
148
                assert -8 < seek_length, "too great a seek."
223
149
                buf = self.fileobj.read(-seek_length)
224
150
                self._gzip_tail = self.decompress.unused_data + buf
225
151
            else:
234
160
        """tuned to reduce function calls and eliminate file seeking:
235
161
        pass 1:
236
162
        reduces lsprof count from 800 to 288
237
 
        4168 in 296
 
163
        4168 in 296 
238
164
        avoid U32 call by using struct format L
239
165
        4168 in 200
240
166
        """
241
 
        # We've read to the end of the file, so we should have 8 bytes of
 
167
        # We've read to the end of the file, so we should have 8 bytes of 
242
168
        # unused data in the decompressor. If we don't, there is a corrupt file.
243
169
        # We use these 8 bytes to calculate the CRC and the recorded file size.
244
170
        # We then check the that the computed CRC and size of the
245
171
        # uncompressed data matches the stored values.  Note that the size
246
172
        # stored is the true file size mod 2**32.
247
 
        if not (len(self._gzip_tail) == 8):
248
 
            raise AssertionError("gzip trailer is incorrect length.")
 
173
        assert len(self._gzip_tail) == 8, "gzip trailer is incorrect length."
249
174
        crc32, isize = struct.unpack("<LL", self._gzip_tail)
250
175
        # note that isize is unsigned - it can exceed 2GB
251
176
        if crc32 != U32(self.crc):
255
180
 
256
181
    def _read_gzip_header(self, bytes=None):
257
182
        """Supply bytes if the minimum header size is already read.
258
 
 
 
183
        
259
184
        :param bytes: 10 bytes of header data.
260
185
        """
261
186
        """starting cost: 300 in 3998
298
223
 
299
224
    def readline(self, size=-1):
300
225
        """Tuned to remove buffer length calls in _unread and...
301
 
 
 
226
        
302
227
        also removes multiple len(c) calls, inlines _unread,
303
228
        total savings - lsprof 5800 to 5300
304
229
        phase 2:
308
233
        leading to a drop to:
309
234
        4168 calls in 1977
310
235
        4168 call to read() in 1646
311
 
        - i.e. just reduced the function call overhead. May be worth
 
236
        - i.e. just reduced the function call overhead. May be worth 
312
237
          keeping.
313
238
        """
314
239
        if size < 0: size = sys.maxint
356
281
        # to :
357
282
        # 4168 calls in 417.
358
283
        # Negative numbers result in reading all the lines
359
 
 
 
284
        
360
285
        # python's gzip routine uses sizehint. This is a more efficient way
361
286
        # than python uses to honor it. But it is even more efficient to
362
287
        # just read the entire thing and use cStringIO to split into lines.
369
294
 
370
295
    def _unread(self, buf, len_buf=None):
371
296
        """tuned to remove unneeded len calls.
372
 
 
 
297
        
373
298
        because this is such an inner routine in readline, and readline is
374
299
        in many inner loops, this has been inlined into readline().
375
300
 
376
301
        The len_buf parameter combined with the reduction in len calls dropped
377
 
        the lsprof ms count for this routine on my test data from 800 to 200 -
 
302
        the lsprof ms count for this routine on my test data from 800 to 200 - 
378
303
        a 75% saving.
379
304
        """
380
305
        if len_buf is None:
398
323
            self.offset += data_len
399
324
 
400
325
    def writelines(self, lines):
401
 
        # profiling indicated a significant overhead
 
326
        # profiling indicated a significant overhead 
402
327
        # calling write for each line.
403
328
        # this batch call is a lot faster :).
404
329
        # (4 seconds to 1 seconds for the sample upgrades I was testing).
405
330
        self.write(''.join(lines))
406
331
 
407
 
    if sys.version_info > (2, 7):
408
 
        # As of Python 2.7 the crc32 must be positive when close is called
409
 
        def close(self):
410
 
            if self.fileobj is None:
411
 
                return
412
 
            if self.mode == gzip.WRITE:
413
 
                self.crc &= 0xFFFFFFFFL
414
 
            gzip.GzipFile.close(self)
415
332