/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/groupcompress.py

  • Committer: Vincent Ladeuil
  • Date: 2009-09-07 08:46:00 UTC
  • mto: (4677.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4678.
  • Revision ID: v.ladeuil+lp@free.fr-20090907084600-g2gheboken7azq5y
FreeBSD and OSX create temp dirs with the 'wheel' group.

* bzrlib/tests/test_permissions.py:
(TestPermissions.test_new_files_group_sticky_bit): Same cause,
same fix.

* bzrlib/tests/per_branch/test_permissions.py:
(TestPermissions.test_new_branch_group_sticky_bit): Same cause,
same fix.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
31
31
    knit,
32
32
    osutils,
33
33
    pack,
34
 
    static_tuple,
35
34
    trace,
36
35
    )
37
36
from bzrlib.btree_index import BTreeBuilder
120
119
        :param num_bytes: Ensure that we have extracted at least num_bytes of
121
120
            content. If None, consume everything
122
121
        """
123
 
        if self._content_length is None:
124
 
            raise AssertionError('self._content_length should never be None')
 
122
        # TODO: If we re-use the same content block at different times during
 
123
        #       get_record_stream(), it is possible that the first pass will
 
124
        #       get inserted, triggering an extract/_ensure_content() which
 
125
        #       will get rid of _z_content. And then the next use of the block
 
126
        #       will try to access _z_content (to send it over the wire), and
 
127
        #       fail because it is already extracted. Consider never releasing
 
128
        #       _z_content because of this.
125
129
        if num_bytes is None:
126
130
            num_bytes = self._content_length
127
131
        elif (self._content_length is not None
144
148
                self._content = pylzma.decompress(self._z_content)
145
149
            elif self._compressor_name == 'zlib':
146
150
                # Start a zlib decompressor
147
 
                if num_bytes * 4 > self._content_length * 3:
148
 
                    # If we are requesting more that 3/4ths of the content,
149
 
                    # just extract the whole thing in a single pass
150
 
                    num_bytes = self._content_length
 
151
                if num_bytes is None:
151
152
                    self._content = zlib.decompress(self._z_content)
152
153
                else:
153
154
                    self._z_content_decompressor = zlib.decompressobj()
155
156
                    # that the rest of the code is simplified
156
157
                    self._content = self._z_content_decompressor.decompress(
157
158
                        self._z_content, num_bytes + _ZLIB_DECOMP_WINDOW)
158
 
                    if not self._z_content_decompressor.unconsumed_tail:
159
 
                        self._z_content_decompressor = None
160
159
            else:
161
160
                raise AssertionError('Unknown compressor: %r'
162
161
                                     % self._compressor_name)
164
163
        # 'unconsumed_tail'
165
164
 
166
165
        # Do we have enough bytes already?
167
 
        if len(self._content) >= num_bytes:
 
166
        if num_bytes is not None and len(self._content) >= num_bytes:
 
167
            return
 
168
        if num_bytes is None and self._z_content_decompressor is None:
 
169
            # We must have already decompressed everything
168
170
            return
169
171
        # If we got this far, and don't have a decompressor, something is wrong
170
172
        if self._z_content_decompressor is None:
171
173
            raise AssertionError(
172
174
                'No decompressor to decompress %d bytes' % num_bytes)
173
175
        remaining_decomp = self._z_content_decompressor.unconsumed_tail
174
 
        if not remaining_decomp:
175
 
            raise AssertionError('Nothing left to decompress')
176
 
        needed_bytes = num_bytes - len(self._content)
177
 
        # We always set max_size to 32kB over the minimum needed, so that
178
 
        # zlib will give us as much as we really want.
179
 
        # TODO: If this isn't good enough, we could make a loop here,
180
 
        #       that keeps expanding the request until we get enough
181
 
        self._content += self._z_content_decompressor.decompress(
182
 
            remaining_decomp, needed_bytes + _ZLIB_DECOMP_WINDOW)
183
 
        if len(self._content) < num_bytes:
184
 
            raise AssertionError('%d bytes wanted, only %d available'
185
 
                                 % (num_bytes, len(self._content)))
186
 
        if not self._z_content_decompressor.unconsumed_tail:
187
 
            # The stream is finished
188
 
            self._z_content_decompressor = None
 
176
        if num_bytes is None:
 
177
            if remaining_decomp:
 
178
                # We don't know how much is left, but we'll decompress it all
 
179
                self._content += self._z_content_decompressor.decompress(
 
180
                    remaining_decomp)
 
181
                # Note: There's what I consider a bug in zlib.decompressobj
 
182
                #       If you pass back in the entire unconsumed_tail, only
 
183
                #       this time you don't pass a max-size, it doesn't
 
184
                #       change the unconsumed_tail back to None/''.
 
185
                #       However, we know we are done with the whole stream
 
186
                self._z_content_decompressor = None
 
187
            # XXX: Why is this the only place in this routine we set this?
 
188
            self._content_length = len(self._content)
 
189
        else:
 
190
            if not remaining_decomp:
 
191
                raise AssertionError('Nothing left to decompress')
 
192
            needed_bytes = num_bytes - len(self._content)
 
193
            # We always set max_size to 32kB over the minimum needed, so that
 
194
            # zlib will give us as much as we really want.
 
195
            # TODO: If this isn't good enough, we could make a loop here,
 
196
            #       that keeps expanding the request until we get enough
 
197
            self._content += self._z_content_decompressor.decompress(
 
198
                remaining_decomp, needed_bytes + _ZLIB_DECOMP_WINDOW)
 
199
            if len(self._content) < num_bytes:
 
200
                raise AssertionError('%d bytes wanted, only %d available'
 
201
                                     % (num_bytes, len(self._content)))
 
202
            if not self._z_content_decompressor.unconsumed_tail:
 
203
                # The stream is finished
 
204
                self._z_content_decompressor = None
189
205
 
190
206
    def _parse_bytes(self, bytes, pos):
191
207
        """Read the various lengths from the header.
1152
1168
class GroupCompressVersionedFiles(VersionedFiles):
1153
1169
    """A group-compress based VersionedFiles implementation."""
1154
1170
 
1155
 
    def __init__(self, index, access, delta=True, _unadded_refs=None):
 
1171
    def __init__(self, index, access, delta=True):
1156
1172
        """Create a GroupCompressVersionedFiles object.
1157
1173
 
1158
1174
        :param index: The index object storing access and graph data.
1159
1175
        :param access: The access object storing raw data.
1160
1176
        :param delta: Whether to delta compress or just entropy compress.
1161
 
        :param _unadded_refs: private parameter, don't use.
1162
1177
        """
1163
1178
        self._index = index
1164
1179
        self._access = access
1165
1180
        self._delta = delta
1166
 
        if _unadded_refs is None:
1167
 
            _unadded_refs = {}
1168
 
        self._unadded_refs = _unadded_refs
 
1181
        self._unadded_refs = {}
1169
1182
        self._group_cache = LRUSizeCache(max_size=50*1024*1024)
1170
1183
        self._fallback_vfs = []
1171
1184
 
1172
 
    def without_fallbacks(self):
1173
 
        """Return a clone of this object without any fallbacks configured."""
1174
 
        return GroupCompressVersionedFiles(self._index, self._access,
1175
 
            self._delta, _unadded_refs=dict(self._unadded_refs))
1176
 
 
1177
1185
    def add_lines(self, key, parents, lines, parent_texts=None,
1178
1186
        left_matching_blocks=None, nostore_sha=None, random_id=False,
1179
1187
        check_content=True):
1266
1274
        else:
1267
1275
            return self.get_record_stream(keys, 'unordered', True)
1268
1276
 
1269
 
    def clear_cache(self):
1270
 
        """See VersionedFiles.clear_cache()"""
1271
 
        self._group_cache.clear()
1272
 
        self._index._graph_index.clear_cache()
1273
 
        self._index._int_cache.clear()
1274
 
 
1275
1277
    def _check_add(self, key, lines, random_id, check_content):
1276
1278
        """check that version_id and lines are safe to add."""
1277
1279
        version_id = key[-1]
1631
1633
        keys_to_add = []
1632
1634
        def flush():
1633
1635
            bytes = self._compressor.flush().to_bytes()
1634
 
            self._compressor = GroupCompressor()
1635
1636
            index, start, length = self._access.add_raw_records(
1636
1637
                [(None, len(bytes))], bytes)[0]
1637
1638
            nodes = []
1640
1641
            self._index.add_records(nodes, random_id=random_id)
1641
1642
            self._unadded_refs = {}
1642
1643
            del keys_to_add[:]
 
1644
            self._compressor = GroupCompressor()
1643
1645
 
1644
1646
        last_prefix = None
1645
1647
        max_fulltext_len = 0
1747
1749
                key = record.key
1748
1750
            self._unadded_refs[key] = record.parents
1749
1751
            yield found_sha1
1750
 
            as_st = static_tuple.StaticTuple.from_sequence
1751
 
            if record.parents is not None:
1752
 
                parents = as_st([as_st(p) for p in record.parents])
1753
 
            else:
1754
 
                parents = None
1755
 
            refs = static_tuple.StaticTuple(parents)
1756
 
            keys_to_add.append((key, '%d %d' % (start_point, end_point), refs))
 
1752
            keys_to_add.append((key, '%d %d' % (start_point, end_point),
 
1753
                (record.parents,)))
1757
1754
        if len(keys_to_add):
1758
1755
            flush()
1759
1756
        self._compressor = None
1839
1836
        self.has_graph = parents
1840
1837
        self._is_locked = is_locked
1841
1838
        self._inconsistency_fatal = inconsistency_fatal
1842
 
        # GroupCompress records tend to have the same 'group' start + offset
1843
 
        # repeated over and over, this creates a surplus of ints
1844
 
        self._int_cache = {}
1845
1839
        if track_external_parent_refs:
1846
1840
            self._key_dependencies = knit._KeyRefs(
1847
1841
                track_new_keys=track_new_keys)
1883
1877
        if not random_id:
1884
1878
            present_nodes = self._get_entries(keys)
1885
1879
            for (index, key, value, node_refs) in present_nodes:
1886
 
                # Sometimes these are passed as a list rather than a tuple
1887
 
                node_refs = static_tuple.as_tuples(node_refs)
1888
 
                passed = static_tuple.as_tuples(keys[key])
1889
 
                if node_refs != passed[1]:
1890
 
                    details = '%s %s %s' % (key, (value, node_refs), passed)
 
1880
                if node_refs != keys[key][1]:
 
1881
                    details = '%s %s %s' % (key, (value, node_refs), keys[key])
1891
1882
                    if self._inconsistency_fatal:
1892
1883
                        raise errors.KnitCorrupt(self, "inconsistent details"
1893
1884
                                                 " in add_records: %s" %
2026
2017
        """Convert an index value to position details."""
2027
2018
        bits = node[2].split(' ')
2028
2019
        # It would be nice not to read the entire gzip.
2029
 
        # start and stop are put into _int_cache because they are very common.
2030
 
        # They define the 'group' that an entry is in, and many groups can have
2031
 
        # thousands of objects.
2032
 
        # Branching Launchpad, for example, saves ~600k integers, at 12 bytes
2033
 
        # each, or about 7MB. Note that it might be even more when you consider
2034
 
        # how PyInt is allocated in separate slabs. And you can't return a slab
2035
 
        # to the OS if even 1 int on it is in use. Note though that Python uses
2036
 
        # a LIFO when re-using PyInt slots, which probably causes more
2037
 
        # fragmentation.
2038
2020
        start = int(bits[0])
2039
 
        start = self._int_cache.setdefault(start, start)
2040
2021
        stop = int(bits[1])
2041
 
        stop = self._int_cache.setdefault(stop, stop)
2042
2022
        basis_end = int(bits[2])
2043
2023
        delta_end = int(bits[3])
2044
 
        # We can't use StaticTuple here, because node[0] is a BTreeGraphIndex
2045
 
        # instance...
2046
 
        return (node[0], start, stop, basis_end, delta_end)
 
2024
        return node[0], start, stop, basis_end, delta_end
2047
2025
 
2048
2026
    def scan_unvalidated_index(self, graph_index):
2049
2027
        """Inform this _GCGraphIndex that there is an unvalidated index.
2080
2058
        decode_base128_int,
2081
2059
        )
2082
2060
    GroupCompressor = PyrexGroupCompressor
2083
 
except ImportError, e:
2084
 
    osutils.failed_to_load_extension(e)
 
2061
except ImportError:
2085
2062
    GroupCompressor = PythonGroupCompressor
2086
2063