~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/pack.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-08-28 08:23:40 UTC
  • mfrom: (2757.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070828082340-2byilw23kzl3cjx4
(Daniel Watkins) Better explanation of -r in uncommit help

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2007 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Container format for Bazaar data.
18
18
 
19
 
"Containers" and "records" are described in
20
 
doc/developers/container-format.txt.
 
19
"Containers" and "records" are described in doc/developers/container-format.txt.
21
20
"""
22
21
 
23
22
from cStringIO import StringIO
34
33
 
35
34
def _check_name(name):
36
35
    """Do some basic checking of 'name'.
37
 
 
 
36
    
38
37
    At the moment, this just checks that there are no whitespace characters in a
39
38
    name.
40
39
 
47
46
 
48
47
def _check_name_encoding(name):
49
48
    """Check that 'name' is valid UTF-8.
50
 
 
 
49
    
51
50
    This is separate from _check_name because UTF-8 decoding is relatively
52
51
    expensive, and we usually want to avoid it.
53
52
 
59
58
        raise errors.InvalidRecordError(str(e))
60
59
 
61
60
 
62
 
class ContainerSerialiser(object):
63
 
    """A helper class for serialising containers.
64
 
 
65
 
    It simply returns bytes from method calls to 'begin', 'end' and
66
 
    'bytes_record'.  You may find ContainerWriter to be a more convenient
67
 
    interface.
68
 
    """
69
 
 
70
 
    def begin(self):
71
 
        """Return the bytes to begin a container."""
72
 
        return FORMAT_ONE + "\n"
73
 
 
74
 
    def end(self):
75
 
        """Return the bytes to finish a container."""
76
 
        return "E"
77
 
 
78
 
    def bytes_record(self, bytes, names):
79
 
        """Return the bytes for a Bytes record with the given name and
80
 
        contents.
81
 
        """
82
 
        # Kind marker
83
 
        byte_sections = ["B"]
84
 
        # Length
85
 
        byte_sections.append(str(len(bytes)) + "\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
 
        # Finally, the contents.
96
 
        byte_sections.append(bytes)
97
 
        # XXX: This causes a memory copy of bytes in size, but is usually
98
 
        # faster than two write calls (12 vs 13 seconds to output a gig of
99
 
        # 1k records.) - results may differ on significantly larger records
100
 
        # like .iso's but as they should be rare in any case and thus not
101
 
        # likely to be the common case. The biggest issue is causing extreme
102
 
        # memory pressure in that case. One possibly improvement here is to
103
 
        # check the size of the content before deciding to join here vs call
104
 
        # write twice.
105
 
        return ''.join(byte_sections)
106
 
 
107
 
 
108
61
class ContainerWriter(object):
109
 
    """A class for writing containers to a file.
 
62
    """A class for writing containers.
110
63
 
111
64
    :attribute records_written: The number of user records added to the
112
65
        container. This does not count the prelude or suffix of the container
122
75
        self._write_func = write_func
123
76
        self.current_offset = 0
124
77
        self.records_written = 0
125
 
        self._serialiser = ContainerSerialiser()
126
78
 
127
79
    def begin(self):
128
80
        """Begin writing a container."""
129
 
        self.write_func(self._serialiser.begin())
 
81
        self.write_func(FORMAT_ONE + "\n")
130
82
 
131
83
    def write_func(self, bytes):
132
84
        self._write_func(bytes)
134
86
 
135
87
    def end(self):
136
88
        """Finish writing a container."""
137
 
        self.write_func(self._serialiser.end())
 
89
        self.write_func("E")
138
90
 
139
91
    def add_bytes_record(self, bytes, names):
140
92
        """Add a Bytes record with the given names.
141
 
 
 
93
        
142
94
        :param bytes: The bytes to insert.
143
95
        :param names: The names to give the inserted bytes. Each name is
144
96
            a tuple of bytestrings. The bytestrings may not contain
151
103
            and thus are only suitable for use by a ContainerReader.
152
104
        """
153
105
        current_offset = self.current_offset
154
 
        serialised_record = self._serialiser.bytes_record(bytes, names)
155
 
        self.write_func(serialised_record)
 
106
        # Kind marker
 
107
        self.write_func("B")
 
108
        # Length
 
109
        self.write_func(str(len(bytes)) + "\n")
 
110
        # Names
 
111
        for name_tuple in names:
 
112
            # Make sure we're writing valid names.  Note that we will leave a
 
113
            # half-written record if a name is bad!
 
114
            for name in name_tuple:
 
115
                _check_name(name)
 
116
            self.write_func('\x00'.join(name_tuple) + "\n")
 
117
        # End of headers
 
118
        self.write_func("\n")
 
119
        # Finally, the contents.
 
120
        self.write_func(bytes)
156
121
        self.records_written += 1
157
122
        # return a memo of where we wrote data to allow random access.
158
123
        return current_offset, self.current_offset - current_offset
159
124
 
160
125
 
161
126
class ReadVFile(object):
162
 
    """Adapt a readv result iterator to a file like protocol.
163
 
    
164
 
    The readv result must support the iterator protocol returning (offset,
165
 
    data_bytes) pairs.
166
 
    """
167
 
 
168
 
    # XXX: This could be a generic transport class, as other code may want to
169
 
    # gradually consume the readv result.
 
127
    """Adapt a readv result iterator to a file like protocol."""
170
128
 
171
129
    def __init__(self, readv_result):
172
 
        """Construct a new ReadVFile wrapper.
173
 
 
174
 
        :seealso: make_readv_reader
175
 
 
176
 
        :param readv_result: the most recent readv result - list or generator
177
 
        """
178
 
        # readv can return a sequence or an iterator, but we require an
179
 
        # iterator to know how much has been consumed.
180
 
        readv_result = iter(readv_result)
181
130
        self.readv_result = readv_result
 
131
        # the most recent readv result block
182
132
        self._string = None
183
133
 
184
134
    def _next(self):
185
135
        if (self._string is None or
186
136
            self._string.tell() == self._string_length):
187
 
            offset, data = self.readv_result.next()
 
137
            length, data = self.readv_result.next()
188
138
            self._string_length = len(data)
189
139
            self._string = StringIO(data)
190
140
 
192
142
        self._next()
193
143
        result = self._string.read(length)
194
144
        if len(result) < length:
195
 
            raise errors.BzrError('wanted %d bytes but next '
196
 
                'hunk only contains %d: %r...' %
197
 
                (length, len(result), result[:20]))
 
145
            raise errors.BzrError('request for too much data from a readv hunk.')
198
146
        return result
199
147
 
200
148
    def readline(self):
202
150
        self._next()
203
151
        result = self._string.readline()
204
152
        if self._string.tell() == self._string_length and result[-1] != '\n':
205
 
            raise errors.BzrError('short readline in the readvfile hunk: %r'
206
 
                % (result, ))
 
153
            raise errors.BzrError('short readline in the readvfile hunk.')
207
154
        return result
208
155
 
209
156
 
252
199
        is a ``list`` and bytes is a function that takes one argument,
253
200
        ``max_length``.
254
201
 
255
 
        You **must not** call the callable after advancing the iterator to the
 
202
        You **must not** call the callable after advancing the interator to the
256
203
        next record.  That is, this code is invalid::
257
204
 
258
205
            record_iter = container.iter_records()
259
206
            names1, callable1 = record_iter.next()
260
207
            names2, callable2 = record_iter.next()
261
208
            bytes1 = callable1(None)
262
 
 
 
209
        
263
210
        As it will give incorrect results and invalidate the state of the
264
211
        ContainerReader.
265
212
 
266
 
        :raises ContainerError: if any sort of container corruption is
 
213
        :raises ContainerError: if any sort of containter corruption is
267
214
            detected, e.g. UnknownContainerFormatError is the format of the
268
215
            container is unrecognised.
269
216
        :seealso: ContainerReader.read
270
217
        """
271
218
        self._read_format()
272
219
        return self._iter_records()
273
 
 
 
220
    
274
221
    def iter_record_objects(self):
275
222
        """Iterate over the container, yielding each record as it is read.
276
223
 
278
225
        methods.  Like with iter_records, it is not safe to use a record object
279
226
        after advancing the iterator to yield next record.
280
227
 
281
 
        :raises ContainerError: if any sort of container corruption is
 
228
        :raises ContainerError: if any sort of containter corruption is
282
229
            detected, e.g. UnknownContainerFormatError is the format of the
283
230
            container is unrecognised.
284
231
        :seealso: iter_records
285
232
        """
286
233
        self._read_format()
287
234
        return self._iter_record_objects()
288
 
 
 
235
    
289
236
    def _iter_records(self):
290
237
        for record in self._iter_record_objects():
291
238
            yield record.read()
360
307
        except ValueError:
361
308
            raise errors.InvalidRecordError(
362
309
                "%r is not a valid length." % (length_line,))
363
 
 
 
310
        
364
311
        # Read the list of names.
365
312
        names = []
366
313
        while True:
399
346
                _check_name_encoding(name)
400
347
        read_bytes(None)
401
348
 
402
 
 
403
 
class ContainerPushParser(object):
404
 
    """A "push" parser for container format 1.
405
 
 
406
 
    It accepts bytes via the ``accept_bytes`` method, and parses them into
407
 
    records which can be retrieved via the ``read_pending_records`` method.
408
 
    """
409
 
 
410
 
    def __init__(self):
411
 
        self._buffer = ''
412
 
        self._state_handler = self._state_expecting_format_line
413
 
        self._parsed_records = []
414
 
        self._reset_current_record()
415
 
        self.finished = False
416
 
 
417
 
    def _reset_current_record(self):
418
 
        self._current_record_length = None
419
 
        self._current_record_names = []
420
 
 
421
 
    def accept_bytes(self, bytes):
422
 
        self._buffer += bytes
423
 
        # Keep iterating the state machine until it stops consuming bytes from
424
 
        # the buffer.
425
 
        last_buffer_length = None
426
 
        cur_buffer_length = len(self._buffer)
427
 
        last_state_handler = None
428
 
        while (cur_buffer_length != last_buffer_length
429
 
               or last_state_handler != self._state_handler):
430
 
            last_buffer_length = cur_buffer_length
431
 
            last_state_handler = self._state_handler
432
 
            self._state_handler()
433
 
            cur_buffer_length = len(self._buffer)
434
 
 
435
 
    def read_pending_records(self, max=None):
436
 
        if max:
437
 
            records = self._parsed_records[:max]
438
 
            del self._parsed_records[:max]
439
 
            return records
440
 
        else:
441
 
            records = self._parsed_records
442
 
            self._parsed_records = []
443
 
            return records
444
 
 
445
 
    def _consume_line(self):
446
 
        """Take a line out of the buffer, and return the line.
447
 
 
448
 
        If a newline byte is not found in the buffer, the buffer is
449
 
        unchanged and this returns None instead.
450
 
        """
451
 
        newline_pos = self._buffer.find('\n')
452
 
        if newline_pos != -1:
453
 
            line = self._buffer[:newline_pos]
454
 
            self._buffer = self._buffer[newline_pos+1:]
455
 
            return line
456
 
        else:
457
 
            return None
458
 
 
459
 
    def _state_expecting_format_line(self):
460
 
        line = self._consume_line()
461
 
        if line is not None:
462
 
            if line != FORMAT_ONE:
463
 
                raise errors.UnknownContainerFormatError(line)
464
 
            self._state_handler = self._state_expecting_record_type
465
 
 
466
 
    def _state_expecting_record_type(self):
467
 
        if len(self._buffer) >= 1:
468
 
            record_type = self._buffer[0]
469
 
            self._buffer = self._buffer[1:]
470
 
            if record_type == 'B':
471
 
                self._state_handler = self._state_expecting_length
472
 
            elif record_type == 'E':
473
 
                self.finished = True
474
 
                self._state_handler = self._state_expecting_nothing
475
 
            else:
476
 
                raise errors.UnknownRecordTypeError(record_type)
477
 
 
478
 
    def _state_expecting_length(self):
479
 
        line = self._consume_line()
480
 
        if line is not None:
481
 
            try:
482
 
                self._current_record_length = int(line)
483
 
            except ValueError:
484
 
                raise errors.InvalidRecordError(
485
 
                    "%r is not a valid length." % (line,))
486
 
            self._state_handler = self._state_expecting_name
487
 
 
488
 
    def _state_expecting_name(self):
489
 
        encoded_name_parts = self._consume_line()
490
 
        if encoded_name_parts == '':
491
 
            self._state_handler = self._state_expecting_body
492
 
        elif encoded_name_parts:
493
 
            name_parts = tuple(encoded_name_parts.split('\x00'))
494
 
            for name_part in name_parts:
495
 
                _check_name(name_part)
496
 
            self._current_record_names.append(name_parts)
497
 
 
498
 
    def _state_expecting_body(self):
499
 
        if len(self._buffer) >= self._current_record_length:
500
 
            body_bytes = self._buffer[:self._current_record_length]
501
 
            self._buffer = self._buffer[self._current_record_length:]
502
 
            record = (self._current_record_names, body_bytes)
503
 
            self._parsed_records.append(record)
504
 
            self._reset_current_record()
505
 
            self._state_handler = self._state_expecting_record_type
506
 
 
507
 
    def _state_expecting_nothing(self):
508
 
        pass
509
 
 
510
 
    def read_size_hint(self):
511
 
        hint = 16384
512
 
        if self._state_handler == self._state_expecting_body:
513
 
            remaining = self._current_record_length - len(self._buffer)
514
 
            if remaining < 0:
515
 
                remaining = 0
516
 
            return max(hint, remaining)
517
 
        return hint
518
 
 
519
 
 
520
 
def iter_records_from_file(source_file):
521
 
    parser = ContainerPushParser()
522
 
    while True:
523
 
        bytes = source_file.read(parser.read_size_hint())
524
 
        parser.accept_bytes(bytes)
525
 
        for record in parser.read_pending_records():
526
 
            yield record
527
 
        if parser.finished:
528
 
            break
529