~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tuned_gzip.py

  • Committer: Martin Pool
  • Date: 2009-01-13 03:06:36 UTC
  • mfrom: (3932.2.3 1.11)
  • mto: This revision was merged to the branch mainline in revision 3937.
  • Revision ID: mbp@sourcefrog.net-20090113030636-dqx4t8yaaqgdvam5
MergeĀ 1.11rc1

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:
29
27
import zlib
30
28
 
31
29
# we want a \n preserved, break on \n only splitlines.
32
 
from bzrlib import symbol_versioning
 
30
import bzrlib
33
31
 
34
32
__all__ = ["GzipFile", "bytes_to_gzip"]
35
33
 
54
52
    width=-zlib.MAX_WBITS, mem=zlib.DEF_MEM_LEVEL,
55
53
    crc32=zlib.crc32):
56
54
    """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
55
    result = [
70
56
        '\037\213'  # self.fileobj.write('\037\213')  # magic header
71
57
        '\010'      # self.fileobj.write('\010')      # compression method
83
69
    # using a compressobj avoids a small header and trailer that the compress()
84
70
    # utility function adds.
85
71
    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)
 
72
    result.append(compress.compress(bytes))
94
73
    result.append(compress.flush())
 
74
    result.append(struct.pack("<L", LOWU32(crc32(bytes))))
95
75
    # size may exceed 2GB, or even 4GB
96
 
    result.append(struct.pack("<LL", LOWU32(crc), LOWU32(total_len)))
 
76
    result.append(struct.pack("<L", LOWU32(len(bytes))))
97
77
    return ''.join(result)
98
78
 
99
79
 
120
100
    Yes, its only 1.6 seconds, but they add up.
121
101
    """
122
102
 
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
103
    def _add_read_data(self, data):
131
104
        # 4169 calls in 183
132
105
        # temp var for len(data) and switch to +='s.
141
114
        """A tuned version of gzip._write_gzip_header
142
115
 
143
116
        We have some extra constrains that plain Gzip does not.
144
 
        1) We want to write the whole blob at once. rather than multiple
 
117
        1) We want to write the whole blob at once. rather than multiple 
145
118
           calls to fileobj.write().
146
119
        2) We never have a filename
147
120
        3) We don't care about the time
163
136
 
164
137
    def _read(self, size=1024):
165
138
        # various optimisations:
166
 
        # reduces lsprof count from 2500 to
 
139
        # reduces lsprof count from 2500 to 
167
140
        # 8337 calls in 1272, 365 internal
168
141
        if self.fileobj is None:
169
142
            raise EOFError, "Reached EOF"
234
207
        """tuned to reduce function calls and eliminate file seeking:
235
208
        pass 1:
236
209
        reduces lsprof count from 800 to 288
237
 
        4168 in 296
 
210
        4168 in 296 
238
211
        avoid U32 call by using struct format L
239
212
        4168 in 200
240
213
        """
241
 
        # We've read to the end of the file, so we should have 8 bytes of
 
214
        # We've read to the end of the file, so we should have 8 bytes of 
242
215
        # unused data in the decompressor. If we don't, there is a corrupt file.
243
216
        # We use these 8 bytes to calculate the CRC and the recorded file size.
244
217
        # We then check the that the computed CRC and size of the
255
228
 
256
229
    def _read_gzip_header(self, bytes=None):
257
230
        """Supply bytes if the minimum header size is already read.
258
 
 
 
231
        
259
232
        :param bytes: 10 bytes of header data.
260
233
        """
261
234
        """starting cost: 300 in 3998
298
271
 
299
272
    def readline(self, size=-1):
300
273
        """Tuned to remove buffer length calls in _unread and...
301
 
 
 
274
        
302
275
        also removes multiple len(c) calls, inlines _unread,
303
276
        total savings - lsprof 5800 to 5300
304
277
        phase 2:
308
281
        leading to a drop to:
309
282
        4168 calls in 1977
310
283
        4168 call to read() in 1646
311
 
        - i.e. just reduced the function call overhead. May be worth
 
284
        - i.e. just reduced the function call overhead. May be worth 
312
285
          keeping.
313
286
        """
314
287
        if size < 0: size = sys.maxint
356
329
        # to :
357
330
        # 4168 calls in 417.
358
331
        # Negative numbers result in reading all the lines
359
 
 
 
332
        
360
333
        # python's gzip routine uses sizehint. This is a more efficient way
361
334
        # than python uses to honor it. But it is even more efficient to
362
335
        # just read the entire thing and use cStringIO to split into lines.
369
342
 
370
343
    def _unread(self, buf, len_buf=None):
371
344
        """tuned to remove unneeded len calls.
372
 
 
 
345
        
373
346
        because this is such an inner routine in readline, and readline is
374
347
        in many inner loops, this has been inlined into readline().
375
348
 
376
349
        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 -
 
350
        the lsprof ms count for this routine on my test data from 800 to 200 - 
378
351
        a 75% saving.
379
352
        """
380
353
        if len_buf is None:
398
371
            self.offset += data_len
399
372
 
400
373
    def writelines(self, lines):
401
 
        # profiling indicated a significant overhead
 
374
        # profiling indicated a significant overhead 
402
375
        # calling write for each line.
403
376
        # this batch call is a lot faster :).
404
377
        # (4 seconds to 1 seconds for the sample upgrades I was testing).
405
378
        self.write(''.join(lines))
406
379
 
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
380