~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/pack.py

  • Committer: John Arbash Meinel
  • Date: 2005-07-10 16:36:24 UTC
  • mto: (0.5.85) (1185.82.1 bzr-w-changeset)
  • mto: This revision was merged to the branch mainline in revision 1738.
  • Revision ID: john@arbash-meinel.com-20050710163624-ea630748160dc9f7
Removing an unused function.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2009, 2010 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Container format for Bazaar data.
18
 
 
19
 
"Containers" and "records" are described in
20
 
doc/developers/container-format.txt.
21
 
"""
22
 
 
23
 
from __future__ import absolute_import
24
 
 
25
 
from cStringIO import StringIO
26
 
import re
27
 
 
28
 
from bzrlib import errors
29
 
 
30
 
 
31
 
FORMAT_ONE = "Bazaar pack format 1 (introduced in 0.18)"
32
 
 
33
 
 
34
 
_whitespace_re = re.compile('[\t\n\x0b\x0c\r ]')
35
 
 
36
 
 
37
 
def _check_name(name):
38
 
    """Do some basic checking of 'name'.
39
 
 
40
 
    At the moment, this just checks that there are no whitespace characters in a
41
 
    name.
42
 
 
43
 
    :raises InvalidRecordError: if name is not valid.
44
 
    :seealso: _check_name_encoding
45
 
    """
46
 
    if _whitespace_re.search(name) is not None:
47
 
        raise errors.InvalidRecordError("%r is not a valid name." % (name,))
48
 
 
49
 
 
50
 
def _check_name_encoding(name):
51
 
    """Check that 'name' is valid UTF-8.
52
 
 
53
 
    This is separate from _check_name because UTF-8 decoding is relatively
54
 
    expensive, and we usually want to avoid it.
55
 
 
56
 
    :raises InvalidRecordError: if name is not valid UTF-8.
57
 
    """
58
 
    try:
59
 
        name.decode('utf-8')
60
 
    except UnicodeDecodeError, e:
61
 
        raise errors.InvalidRecordError(str(e))
62
 
 
63
 
 
64
 
class ContainerSerialiser(object):
65
 
    """A helper class for serialising containers.
66
 
 
67
 
    It simply returns bytes from method calls to 'begin', 'end' and
68
 
    'bytes_record'.  You may find ContainerWriter to be a more convenient
69
 
    interface.
70
 
    """
71
 
 
72
 
    def begin(self):
73
 
        """Return the bytes to begin a container."""
74
 
        return FORMAT_ONE + "\n"
75
 
 
76
 
    def end(self):
77
 
        """Return the bytes to finish a container."""
78
 
        return "E"
79
 
 
80
 
    def bytes_header(self, length, names):
81
 
        """Return the header for a Bytes record."""
82
 
        # Kind marker
83
 
        byte_sections = ["B"]
84
 
        # Length
85
 
        byte_sections.append(str(length) + "\n")
86
 
        # Names
87
 
        for name_tuple in names:
88
 
            # Make sure we're writing valid names.  Note that we will leave a
89
 
            # half-written record if a name is bad!
90
 
            for name in name_tuple:
91
 
                _check_name(name)
92
 
            byte_sections.append('\x00'.join(name_tuple) + "\n")
93
 
        # End of headers
94
 
        byte_sections.append("\n")
95
 
        return ''.join(byte_sections)
96
 
 
97
 
    def bytes_record(self, bytes, names):
98
 
        """Return the bytes for a Bytes record with the given name and
99
 
        contents.
100
 
 
101
 
        If the content may be large, construct the header separately and then
102
 
        stream out the contents.
103
 
        """
104
 
        return self.bytes_header(len(bytes), names) + bytes
105
 
 
106
 
 
107
 
class ContainerWriter(object):
108
 
    """A class for writing containers to a file.
109
 
 
110
 
    :attribute records_written: The number of user records added to the
111
 
        container. This does not count the prelude or suffix of the container
112
 
        introduced by the begin() and end() methods.
113
 
    """
114
 
 
115
 
    # Join up headers with the body if writing fewer than this many bytes:
116
 
    # trades off memory usage and copying to do less IO ops.
117
 
    _JOIN_WRITES_THRESHOLD = 100000
118
 
 
119
 
    def __init__(self, write_func):
120
 
        """Constructor.
121
 
 
122
 
        :param write_func: a callable that will be called when this
123
 
            ContainerWriter needs to write some bytes.
124
 
        """
125
 
        self._write_func = write_func
126
 
        self.current_offset = 0
127
 
        self.records_written = 0
128
 
        self._serialiser = ContainerSerialiser()
129
 
 
130
 
    def begin(self):
131
 
        """Begin writing a container."""
132
 
        self.write_func(self._serialiser.begin())
133
 
 
134
 
    def write_func(self, bytes):
135
 
        self._write_func(bytes)
136
 
        self.current_offset += len(bytes)
137
 
 
138
 
    def end(self):
139
 
        """Finish writing a container."""
140
 
        self.write_func(self._serialiser.end())
141
 
 
142
 
    def add_bytes_record(self, bytes, names):
143
 
        """Add a Bytes record with the given names.
144
 
 
145
 
        :param bytes: The bytes to insert.
146
 
        :param names: The names to give the inserted bytes. Each name is
147
 
            a tuple of bytestrings. The bytestrings may not contain
148
 
            whitespace.
149
 
        :return: An offset, length tuple. The offset is the offset
150
 
            of the record within the container, and the length is the
151
 
            length of data that will need to be read to reconstitute the
152
 
            record. These offset and length can only be used with the pack
153
 
            interface - they might be offset by headers or other such details
154
 
            and thus are only suitable for use by a ContainerReader.
155
 
        """
156
 
        current_offset = self.current_offset
157
 
        length = len(bytes)
158
 
        if length < self._JOIN_WRITES_THRESHOLD:
159
 
            self.write_func(self._serialiser.bytes_header(length, names)
160
 
                + bytes)
161
 
        else:
162
 
            self.write_func(self._serialiser.bytes_header(length, names))
163
 
            self.write_func(bytes)
164
 
        self.records_written += 1
165
 
        # return a memo of where we wrote data to allow random access.
166
 
        return current_offset, self.current_offset - current_offset
167
 
 
168
 
 
169
 
class ReadVFile(object):
170
 
    """Adapt a readv result iterator to a file like protocol.
171
 
    
172
 
    The readv result must support the iterator protocol returning (offset,
173
 
    data_bytes) pairs.
174
 
    """
175
 
 
176
 
    # XXX: This could be a generic transport class, as other code may want to
177
 
    # gradually consume the readv result.
178
 
 
179
 
    def __init__(self, readv_result):
180
 
        """Construct a new ReadVFile wrapper.
181
 
 
182
 
        :seealso: make_readv_reader
183
 
 
184
 
        :param readv_result: the most recent readv result - list or generator
185
 
        """
186
 
        # readv can return a sequence or an iterator, but we require an
187
 
        # iterator to know how much has been consumed.
188
 
        readv_result = iter(readv_result)
189
 
        self.readv_result = readv_result
190
 
        self._string = None
191
 
 
192
 
    def _next(self):
193
 
        if (self._string is None or
194
 
            self._string.tell() == self._string_length):
195
 
            offset, data = self.readv_result.next()
196
 
            self._string_length = len(data)
197
 
            self._string = StringIO(data)
198
 
 
199
 
    def read(self, length):
200
 
        self._next()
201
 
        result = self._string.read(length)
202
 
        if len(result) < length:
203
 
            raise errors.BzrError('wanted %d bytes but next '
204
 
                'hunk only contains %d: %r...' %
205
 
                (length, len(result), result[:20]))
206
 
        return result
207
 
 
208
 
    def readline(self):
209
 
        """Note that readline will not cross readv segments."""
210
 
        self._next()
211
 
        result = self._string.readline()
212
 
        if self._string.tell() == self._string_length and result[-1] != '\n':
213
 
            raise errors.BzrError('short readline in the readvfile hunk: %r'
214
 
                % (result, ))
215
 
        return result
216
 
 
217
 
 
218
 
def make_readv_reader(transport, filename, requested_records):
219
 
    """Create a ContainerReader that will read selected records only.
220
 
 
221
 
    :param transport: The transport the pack file is located on.
222
 
    :param filename: The filename of the pack file.
223
 
    :param requested_records: The record offset, length tuples as returned
224
 
        by add_bytes_record for the desired records.
225
 
    """
226
 
    readv_blocks = [(0, len(FORMAT_ONE)+1)]
227
 
    readv_blocks.extend(requested_records)
228
 
    result = ContainerReader(ReadVFile(
229
 
        transport.readv(filename, readv_blocks)))
230
 
    return result
231
 
 
232
 
 
233
 
class BaseReader(object):
234
 
 
235
 
    def __init__(self, source_file):
236
 
        """Constructor.
237
 
 
238
 
        :param source_file: a file-like object with `read` and `readline`
239
 
            methods.
240
 
        """
241
 
        self._source = source_file
242
 
 
243
 
    def reader_func(self, length=None):
244
 
        return self._source.read(length)
245
 
 
246
 
    def _read_line(self):
247
 
        line = self._source.readline()
248
 
        if not line.endswith('\n'):
249
 
            raise errors.UnexpectedEndOfContainerError()
250
 
        return line.rstrip('\n')
251
 
 
252
 
 
253
 
class ContainerReader(BaseReader):
254
 
    """A class for reading Bazaar's container format."""
255
 
 
256
 
    def iter_records(self):
257
 
        """Iterate over the container, yielding each record as it is read.
258
 
 
259
 
        Each yielded record will be a 2-tuple of (names, callable), where names
260
 
        is a ``list`` and bytes is a function that takes one argument,
261
 
        ``max_length``.
262
 
 
263
 
        You **must not** call the callable after advancing the iterator to the
264
 
        next record.  That is, this code is invalid::
265
 
 
266
 
            record_iter = container.iter_records()
267
 
            names1, callable1 = record_iter.next()
268
 
            names2, callable2 = record_iter.next()
269
 
            bytes1 = callable1(None)
270
 
 
271
 
        As it will give incorrect results and invalidate the state of the
272
 
        ContainerReader.
273
 
 
274
 
        :raises ContainerError: if any sort of container corruption is
275
 
            detected, e.g. UnknownContainerFormatError is the format of the
276
 
            container is unrecognised.
277
 
        :seealso: ContainerReader.read
278
 
        """
279
 
        self._read_format()
280
 
        return self._iter_records()
281
 
 
282
 
    def iter_record_objects(self):
283
 
        """Iterate over the container, yielding each record as it is read.
284
 
 
285
 
        Each yielded record will be an object with ``read`` and ``validate``
286
 
        methods.  Like with iter_records, it is not safe to use a record object
287
 
        after advancing the iterator to yield next record.
288
 
 
289
 
        :raises ContainerError: if any sort of container corruption is
290
 
            detected, e.g. UnknownContainerFormatError is the format of the
291
 
            container is unrecognised.
292
 
        :seealso: iter_records
293
 
        """
294
 
        self._read_format()
295
 
        return self._iter_record_objects()
296
 
 
297
 
    def _iter_records(self):
298
 
        for record in self._iter_record_objects():
299
 
            yield record.read()
300
 
 
301
 
    def _iter_record_objects(self):
302
 
        while True:
303
 
            record_kind = self.reader_func(1)
304
 
            if record_kind == 'B':
305
 
                # Bytes record.
306
 
                reader = BytesRecordReader(self._source)
307
 
                yield reader
308
 
            elif record_kind == 'E':
309
 
                # End marker.  There are no more records.
310
 
                return
311
 
            elif record_kind == '':
312
 
                # End of stream encountered, but no End Marker record seen, so
313
 
                # this container is incomplete.
314
 
                raise errors.UnexpectedEndOfContainerError()
315
 
            else:
316
 
                # Unknown record type.
317
 
                raise errors.UnknownRecordTypeError(record_kind)
318
 
 
319
 
    def _read_format(self):
320
 
        format = self._read_line()
321
 
        if format != FORMAT_ONE:
322
 
            raise errors.UnknownContainerFormatError(format)
323
 
 
324
 
    def validate(self):
325
 
        """Validate this container and its records.
326
 
 
327
 
        Validating consumes the data stream just like iter_records and
328
 
        iter_record_objects, so you cannot call it after
329
 
        iter_records/iter_record_objects.
330
 
 
331
 
        :raises ContainerError: if something is invalid.
332
 
        """
333
 
        all_names = set()
334
 
        for record_names, read_bytes in self.iter_records():
335
 
            read_bytes(None)
336
 
            for name_tuple in record_names:
337
 
                for name in name_tuple:
338
 
                    _check_name_encoding(name)
339
 
                # Check that the name is unique.  Note that Python will refuse
340
 
                # to decode non-shortest forms of UTF-8 encoding, so there is no
341
 
                # risk that the same unicode string has been encoded two
342
 
                # different ways.
343
 
                if name_tuple in all_names:
344
 
                    raise errors.DuplicateRecordNameError(name_tuple[0])
345
 
                all_names.add(name_tuple)
346
 
        excess_bytes = self.reader_func(1)
347
 
        if excess_bytes != '':
348
 
            raise errors.ContainerHasExcessDataError(excess_bytes)
349
 
 
350
 
 
351
 
class BytesRecordReader(BaseReader):
352
 
 
353
 
    def read(self):
354
 
        """Read this record.
355
 
 
356
 
        You can either validate or read a record, you can't do both.
357
 
 
358
 
        :returns: A tuple of (names, callable).  The callable can be called
359
 
            repeatedly to obtain the bytes for the record, with a max_length
360
 
            argument.  If max_length is None, returns all the bytes.  Because
361
 
            records can be arbitrarily large, using None is not recommended
362
 
            unless you have reason to believe the content will fit in memory.
363
 
        """
364
 
        # Read the content length.
365
 
        length_line = self._read_line()
366
 
        try:
367
 
            length = int(length_line)
368
 
        except ValueError:
369
 
            raise errors.InvalidRecordError(
370
 
                "%r is not a valid length." % (length_line,))
371
 
 
372
 
        # Read the list of names.
373
 
        names = []
374
 
        while True:
375
 
            name_line = self._read_line()
376
 
            if name_line == '':
377
 
                break
378
 
            name_tuple = tuple(name_line.split('\x00'))
379
 
            for name in name_tuple:
380
 
                _check_name(name)
381
 
            names.append(name_tuple)
382
 
 
383
 
        self._remaining_length = length
384
 
        return names, self._content_reader
385
 
 
386
 
    def _content_reader(self, max_length):
387
 
        if max_length is None:
388
 
            length_to_read = self._remaining_length
389
 
        else:
390
 
            length_to_read = min(max_length, self._remaining_length)
391
 
        self._remaining_length -= length_to_read
392
 
        bytes = self.reader_func(length_to_read)
393
 
        if len(bytes) != length_to_read:
394
 
            raise errors.UnexpectedEndOfContainerError()
395
 
        return bytes
396
 
 
397
 
    def validate(self):
398
 
        """Validate this record.
399
 
 
400
 
        You can either validate or read, you can't do both.
401
 
 
402
 
        :raises ContainerError: if this record is invalid.
403
 
        """
404
 
        names, read_bytes = self.read()
405
 
        for name_tuple in names:
406
 
            for name in name_tuple:
407
 
                _check_name_encoding(name)
408
 
        read_bytes(None)
409
 
 
410
 
 
411
 
class ContainerPushParser(object):
412
 
    """A "push" parser for container format 1.
413
 
 
414
 
    It accepts bytes via the ``accept_bytes`` method, and parses them into
415
 
    records which can be retrieved via the ``read_pending_records`` method.
416
 
    """
417
 
 
418
 
    def __init__(self):
419
 
        self._buffer = ''
420
 
        self._state_handler = self._state_expecting_format_line
421
 
        self._parsed_records = []
422
 
        self._reset_current_record()
423
 
        self.finished = False
424
 
 
425
 
    def _reset_current_record(self):
426
 
        self._current_record_length = None
427
 
        self._current_record_names = []
428
 
 
429
 
    def accept_bytes(self, bytes):
430
 
        self._buffer += bytes
431
 
        # Keep iterating the state machine until it stops consuming bytes from
432
 
        # the buffer.
433
 
        last_buffer_length = None
434
 
        cur_buffer_length = len(self._buffer)
435
 
        last_state_handler = None
436
 
        while (cur_buffer_length != last_buffer_length
437
 
               or last_state_handler != self._state_handler):
438
 
            last_buffer_length = cur_buffer_length
439
 
            last_state_handler = self._state_handler
440
 
            self._state_handler()
441
 
            cur_buffer_length = len(self._buffer)
442
 
 
443
 
    def read_pending_records(self, max=None):
444
 
        if max:
445
 
            records = self._parsed_records[:max]
446
 
            del self._parsed_records[:max]
447
 
            return records
448
 
        else:
449
 
            records = self._parsed_records
450
 
            self._parsed_records = []
451
 
            return records
452
 
 
453
 
    def _consume_line(self):
454
 
        """Take a line out of the buffer, and return the line.
455
 
 
456
 
        If a newline byte is not found in the buffer, the buffer is
457
 
        unchanged and this returns None instead.
458
 
        """
459
 
        newline_pos = self._buffer.find('\n')
460
 
        if newline_pos != -1:
461
 
            line = self._buffer[:newline_pos]
462
 
            self._buffer = self._buffer[newline_pos+1:]
463
 
            return line
464
 
        else:
465
 
            return None
466
 
 
467
 
    def _state_expecting_format_line(self):
468
 
        line = self._consume_line()
469
 
        if line is not None:
470
 
            if line != FORMAT_ONE:
471
 
                raise errors.UnknownContainerFormatError(line)
472
 
            self._state_handler = self._state_expecting_record_type
473
 
 
474
 
    def _state_expecting_record_type(self):
475
 
        if len(self._buffer) >= 1:
476
 
            record_type = self._buffer[0]
477
 
            self._buffer = self._buffer[1:]
478
 
            if record_type == 'B':
479
 
                self._state_handler = self._state_expecting_length
480
 
            elif record_type == 'E':
481
 
                self.finished = True
482
 
                self._state_handler = self._state_expecting_nothing
483
 
            else:
484
 
                raise errors.UnknownRecordTypeError(record_type)
485
 
 
486
 
    def _state_expecting_length(self):
487
 
        line = self._consume_line()
488
 
        if line is not None:
489
 
            try:
490
 
                self._current_record_length = int(line)
491
 
            except ValueError:
492
 
                raise errors.InvalidRecordError(
493
 
                    "%r is not a valid length." % (line,))
494
 
            self._state_handler = self._state_expecting_name
495
 
 
496
 
    def _state_expecting_name(self):
497
 
        encoded_name_parts = self._consume_line()
498
 
        if encoded_name_parts == '':
499
 
            self._state_handler = self._state_expecting_body
500
 
        elif encoded_name_parts:
501
 
            name_parts = tuple(encoded_name_parts.split('\x00'))
502
 
            for name_part in name_parts:
503
 
                _check_name(name_part)
504
 
            self._current_record_names.append(name_parts)
505
 
 
506
 
    def _state_expecting_body(self):
507
 
        if len(self._buffer) >= self._current_record_length:
508
 
            body_bytes = self._buffer[:self._current_record_length]
509
 
            self._buffer = self._buffer[self._current_record_length:]
510
 
            record = (self._current_record_names, body_bytes)
511
 
            self._parsed_records.append(record)
512
 
            self._reset_current_record()
513
 
            self._state_handler = self._state_expecting_record_type
514
 
 
515
 
    def _state_expecting_nothing(self):
516
 
        pass
517
 
 
518
 
    def read_size_hint(self):
519
 
        hint = 16384
520
 
        if self._state_handler == self._state_expecting_body:
521
 
            remaining = self._current_record_length - len(self._buffer)
522
 
            if remaining < 0:
523
 
                remaining = 0
524
 
            return max(hint, remaining)
525
 
        return hint
526
 
 
527
 
 
528
 
def iter_records_from_file(source_file):
529
 
    parser = ContainerPushParser()
530
 
    while True:
531
 
        bytes = source_file.read(parser.read_size_hint())
532
 
        parser.accept_bytes(bytes)
533
 
        for record in parser.read_pending_records():
534
 
            yield record
535
 
        if parser.finished:
536
 
            break
537