~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/pack.py

  • Committer: Andrew Bennetts
  • Date: 2007-06-14 11:23:38 UTC
  • mto: (2506.2.7 container-format)
  • mto: This revision was merged to the branch mainline in revision 2572.
  • Revision ID: andrew.bennetts@canonical.com-20070614112338-6u3900u6nkag66u8
Return a callable instead of a str from read, and add more validation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
    name.
38
38
 
39
39
    :raises InvalidRecordError: if name is not valid.
 
40
    :seealso: _check_name_encoding
40
41
    """
41
 
    # XXX: consider checking that name is a str of valid UTF-8 too?
42
42
    if _whitespace_re.search(name) is not None:
43
43
        raise errors.InvalidRecordError("%r is not a valid name." % (name,))
44
44
 
45
45
 
 
46
def _check_name_encoding(name):
 
47
    """Check that 'name' is valid UTF-8.
 
48
    
 
49
    This is separate from _check_name because UTF-8 decoding is relatively
 
50
    expensive, and we usually want to avoid it.
 
51
 
 
52
    :raises InvalidRecordError: if name is not valid UTF-8.
 
53
    """
 
54
    try:
 
55
        name.decode('utf-8')
 
56
    except UnicodeDecodeError, e:
 
57
        raise errors.InvalidRecordError(str(e))
 
58
 
 
59
 
46
60
class ContainerWriter(object):
47
61
    """A class for writing containers."""
48
62
 
122
136
        Each yielded record will be a 2-tuple of (names, bytes), where names is
123
137
        a ``list`` and bytes is a ``str``.
124
138
 
125
 
        :raises UnknownContainerFormatError: if the format of the container is
126
 
            unrecognised.
 
139
        :raises ContainerError: if any sort of containter corruption is
 
140
            detected, e.g. UnknownContainerFormatError is the format of the
 
141
            container is unrecognised.
127
142
        """
128
 
        format = self._read_line()
129
 
        if format != FORMAT_ONE:
130
 
            raise errors.UnknownContainerFormatError(format)
 
143
        self._read_format()
131
144
        return self._iter_records()
132
145
    
 
146
    def iter_record_objects(self):
 
147
        """Iterate over the container, yielding each record as it is read.
 
148
 
 
149
        Each yielded record will be an object with ``read`` and ``validate``
 
150
        methods.
 
151
 
 
152
        :raises ContainerError: if any sort of containter corruption is
 
153
            detected, e.g. UnknownContainerFormatError is the format of the
 
154
            container is unrecognised.
 
155
        """
 
156
        self._read_format()
 
157
        return self._iter_record_objects()
 
158
    
133
159
    def _iter_records(self):
 
160
        for record in self._iter_record_objects():
 
161
            yield record.read()
 
162
 
 
163
    def _iter_record_objects(self):
134
164
        while True:
135
165
            record_kind = self.reader_func(1)
136
166
            if record_kind == 'B':
137
167
                # Bytes record.
138
168
                reader = BytesRecordReader(self.reader_func)
139
 
                yield reader.read()
 
169
                yield reader
140
170
            elif record_kind == 'E':
141
171
                # End marker.  There are no more records.
142
172
                return
148
178
                # Unknown record type.
149
179
                raise errors.UnknownRecordTypeError(record_kind)
150
180
 
 
181
    def _read_format(self):
 
182
        format = self._read_line()
 
183
        if format != FORMAT_ONE:
 
184
            raise errors.UnknownContainerFormatError(format)
 
185
 
151
186
    def validate(self):
152
187
        """Validate this container and its records.
153
188
 
155
190
 
156
191
        :raises ContainerError: if something is invalid.
157
192
        """
158
 
        for names, bytes in self.iter_records():
159
 
            # XXX: bytes is str, should be callable to get bytes.
160
 
            # callable()
161
 
            pass
 
193
        all_names = set()
 
194
        for record_names, read_bytes in self.iter_records():
 
195
            read_bytes(None)
 
196
            for name in record_names:
 
197
                _check_name_encoding(name)
 
198
                # Check that the name is unique.  Note that Python will refuse
 
199
                # to decode non-shortest forms of UTF-8 encoding, so there is no
 
200
                # risk that the same unicode string has been encoded two
 
201
                # different ways.
 
202
                if name in all_names:
 
203
                    raise errors.DuplicateRecordNameError(name)
 
204
                all_names.add(name)
162
205
        excess_bytes = self.reader_func(1)
163
206
        if excess_bytes != '':
164
207
            raise errors.ContainerHasExcessDataError(excess_bytes)
171
214
 
172
215
        You can either validate or read, you can't do both.
173
216
 
174
 
        :returns: (names, bytes)
 
217
        :returns: A tuple of (names, callable).  The callable can be called
 
218
            repeatedly to obtain the bytes for the record, with a max_length
 
219
            argument.  If max_length is None, returns all the bytes.  Because
 
220
            records can be arbitrarily large, using None is not recommended
 
221
            unless you have reason to believe the content will fit in memory.
175
222
        """
176
223
        # Read the content length.
177
224
        length_line = self._read_line()
189
236
                break
190
237
            _check_name(name)
191
238
            names.append(name)
192
 
        bytes = self.reader_func(length)
193
 
        if len(bytes) != length:
 
239
 
 
240
        self._remaining_length = length
 
241
        return names, self._content_reader
 
242
 
 
243
    def _content_reader(self, max_length):
 
244
        if max_length is None:
 
245
            length_to_read = self._remaining_length
 
246
        else:
 
247
            length_to_read = min(max_length, self._remaining_length)
 
248
        self._remaining_length -= length_to_read
 
249
        bytes = self.reader_func(length_to_read)
 
250
        if len(bytes) != length_to_read:
194
251
            raise errors.UnexpectedEndOfContainerError()
195
 
        return names, bytes
 
252
        return bytes
196
253
 
197
254
    def validate(self):
198
255
        """Validate this record.
201
258
 
202
259
        :raises ContainerError: if this record is invalid.
203
260
        """
204
 
        self.read()
 
261
        names, read_bytes = self.read()
 
262
        for name in names:
 
263
            _check_name_encoding(name)
 
264
        read_bytes(None)
 
265