/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 breezy/bzr/pack.py

  • Committer: Jelmer Vernooij
  • Date: 2018-11-21 03:39:28 UTC
  • mto: This revision was merged to the branch mainline in revision 7206.
  • Revision ID: jelmer@jelmer.uk-20181121033928-ck4sb5zfdwosw35b
Fix test.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
doc/developers/container-format.txt.
21
21
"""
22
22
 
23
 
from cStringIO import StringIO
 
23
from __future__ import absolute_import
 
24
 
24
25
import re
25
26
 
26
 
from bzrlib import errors
27
 
 
28
 
 
29
 
FORMAT_ONE = "Bazaar pack format 1 (introduced in 0.18)"
30
 
 
31
 
 
32
 
_whitespace_re = re.compile('[\t\n\x0b\x0c\r ]')
 
27
from .. import errors
 
28
from ..sixish import (
 
29
    BytesIO,
 
30
    )
 
31
 
 
32
 
 
33
FORMAT_ONE = b"Bazaar pack format 1 (introduced in 0.18)"
 
34
 
 
35
 
 
36
_whitespace_re = re.compile(b'[\t\n\x0b\x0c\r ]')
33
37
 
34
38
 
35
39
def _check_name(name):
55
59
    """
56
60
    try:
57
61
        name.decode('utf-8')
58
 
    except UnicodeDecodeError, e:
 
62
    except UnicodeDecodeError as e:
59
63
        raise errors.InvalidRecordError(str(e))
60
64
 
61
65
 
69
73
 
70
74
    def begin(self):
71
75
        """Return the bytes to begin a container."""
72
 
        return FORMAT_ONE + "\n"
 
76
        return FORMAT_ONE + b"\n"
73
77
 
74
78
    def end(self):
75
79
        """Return the bytes to finish a container."""
76
 
        return "E"
 
80
        return b"E"
77
81
 
78
 
    def bytes_record(self, bytes, names):
79
 
        """Return the bytes for a Bytes record with the given name and
80
 
        contents.
81
 
        """
 
82
    def bytes_header(self, length, names):
 
83
        """Return the header for a Bytes record."""
82
84
        # Kind marker
83
 
        byte_sections = ["B"]
 
85
        byte_sections = [b"B"]
84
86
        # Length
85
 
        byte_sections.append(str(len(bytes)) + "\n")
 
87
        byte_sections.append(b"%d\n" % (length,))
86
88
        # Names
87
89
        for name_tuple in names:
88
90
            # Make sure we're writing valid names.  Note that we will leave a
89
91
            # half-written record if a name is bad!
90
92
            for name in name_tuple:
91
93
                _check_name(name)
92
 
            byte_sections.append('\x00'.join(name_tuple) + "\n")
 
94
            byte_sections.append(b'\x00'.join(name_tuple) + b"\n")
93
95
        # 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)
 
96
        byte_sections.append(b"\n")
 
97
        return b''.join(byte_sections)
 
98
 
 
99
    def bytes_record(self, bytes, names):
 
100
        """Return the bytes for a Bytes record with the given name and
 
101
        contents.
 
102
 
 
103
        If the content may be large, construct the header separately and then
 
104
        stream out the contents.
 
105
        """
 
106
        return self.bytes_header(len(bytes), names) + bytes
106
107
 
107
108
 
108
109
class ContainerWriter(object):
113
114
        introduced by the begin() and end() methods.
114
115
    """
115
116
 
 
117
    # Join up headers with the body if writing fewer than this many bytes:
 
118
    # trades off memory usage and copying to do less IO ops.
 
119
    _JOIN_WRITES_THRESHOLD = 100000
 
120
 
116
121
    def __init__(self, write_func):
117
122
        """Constructor.
118
123
 
151
156
            and thus are only suitable for use by a ContainerReader.
152
157
        """
153
158
        current_offset = self.current_offset
154
 
        serialised_record = self._serialiser.bytes_record(bytes, names)
155
 
        self.write_func(serialised_record)
 
159
        length = len(bytes)
 
160
        if length < self._JOIN_WRITES_THRESHOLD:
 
161
            self.write_func(self._serialiser.bytes_header(length, names)
 
162
                            + bytes)
 
163
        else:
 
164
            self.write_func(self._serialiser.bytes_header(length, names))
 
165
            self.write_func(bytes)
156
166
        self.records_written += 1
157
167
        # return a memo of where we wrote data to allow random access.
158
168
        return current_offset, self.current_offset - current_offset
160
170
 
161
171
class ReadVFile(object):
162
172
    """Adapt a readv result iterator to a file like protocol.
163
 
    
 
173
 
164
174
    The readv result must support the iterator protocol returning (offset,
165
175
    data_bytes) pairs.
166
176
    """
183
193
 
184
194
    def _next(self):
185
195
        if (self._string is None or
186
 
            self._string.tell() == self._string_length):
187
 
            offset, data = self.readv_result.next()
 
196
                self._string.tell() == self._string_length):
 
197
            offset, data = next(self.readv_result)
188
198
            self._string_length = len(data)
189
 
            self._string = StringIO(data)
 
199
            self._string = BytesIO(data)
190
200
 
191
201
    def read(self, length):
192
202
        self._next()
193
203
        result = self._string.read(length)
194
204
        if len(result) < length:
195
205
            raise errors.BzrError('wanted %d bytes but next '
196
 
                'hunk only contains %d: %r...' %
197
 
                (length, len(result), result[:20]))
 
206
                                  'hunk only contains %d: %r...' %
 
207
                                  (length, len(result), result[:20]))
198
208
        return result
199
209
 
200
210
    def readline(self):
201
211
        """Note that readline will not cross readv segments."""
202
212
        self._next()
203
213
        result = self._string.readline()
204
 
        if self._string.tell() == self._string_length and result[-1] != '\n':
 
214
        if self._string.tell() == self._string_length and result[-1:] != b'\n':
205
215
            raise errors.BzrError('short readline in the readvfile hunk: %r'
206
 
                % (result, ))
 
216
                                  % (result, ))
207
217
        return result
208
218
 
209
219
 
215
225
    :param requested_records: The record offset, length tuples as returned
216
226
        by add_bytes_record for the desired records.
217
227
    """
218
 
    readv_blocks = [(0, len(FORMAT_ONE)+1)]
 
228
    readv_blocks = [(0, len(FORMAT_ONE) + 1)]
219
229
    readv_blocks.extend(requested_records)
220
230
    result = ContainerReader(ReadVFile(
221
231
        transport.readv(filename, readv_blocks)))
237
247
 
238
248
    def _read_line(self):
239
249
        line = self._source.readline()
240
 
        if not line.endswith('\n'):
 
250
        if not line.endswith(b'\n'):
241
251
            raise errors.UnexpectedEndOfContainerError()
242
 
        return line.rstrip('\n')
 
252
        return line.rstrip(b'\n')
243
253
 
244
254
 
245
255
class ContainerReader(BaseReader):
292
302
 
293
303
    def _iter_record_objects(self):
294
304
        while True:
295
 
            record_kind = self.reader_func(1)
296
 
            if record_kind == 'B':
 
305
            try:
 
306
                record_kind = self.reader_func(1)
 
307
            except StopIteration:
 
308
                return
 
309
            if record_kind == b'B':
297
310
                # Bytes record.
298
311
                reader = BytesRecordReader(self._source)
299
312
                yield reader
300
 
            elif record_kind == 'E':
 
313
            elif record_kind == b'E':
301
314
                # End marker.  There are no more records.
302
315
                return
303
 
            elif record_kind == '':
 
316
            elif record_kind == b'':
304
317
                # End of stream encountered, but no End Marker record seen, so
305
318
                # this container is incomplete.
306
319
                raise errors.UnexpectedEndOfContainerError()
333
346
                # risk that the same unicode string has been encoded two
334
347
                # different ways.
335
348
                if name_tuple in all_names:
336
 
                    raise errors.DuplicateRecordNameError(name_tuple)
 
349
                    raise errors.DuplicateRecordNameError(name_tuple[0])
337
350
                all_names.add(name_tuple)
338
351
        excess_bytes = self.reader_func(1)
339
 
        if excess_bytes != '':
 
352
        if excess_bytes != b'':
340
353
            raise errors.ContainerHasExcessDataError(excess_bytes)
341
354
 
342
355
 
365
378
        names = []
366
379
        while True:
367
380
            name_line = self._read_line()
368
 
            if name_line == '':
 
381
            if name_line == b'':
369
382
                break
370
 
            name_tuple = tuple(name_line.split('\x00'))
 
383
            name_tuple = tuple(name_line.split(b'\x00'))
371
384
            for name in name_tuple:
372
385
                _check_name(name)
373
386
            names.append(name_tuple)
408
421
    """
409
422
 
410
423
    def __init__(self):
411
 
        self._buffer = ''
 
424
        self._buffer = b''
412
425
        self._state_handler = self._state_expecting_format_line
413
426
        self._parsed_records = []
414
427
        self._reset_current_record()
448
461
        If a newline byte is not found in the buffer, the buffer is
449
462
        unchanged and this returns None instead.
450
463
        """
451
 
        newline_pos = self._buffer.find('\n')
 
464
        newline_pos = self._buffer.find(b'\n')
452
465
        if newline_pos != -1:
453
466
            line = self._buffer[:newline_pos]
454
 
            self._buffer = self._buffer[newline_pos+1:]
 
467
            self._buffer = self._buffer[newline_pos + 1:]
455
468
            return line
456
469
        else:
457
470
            return None
465
478
 
466
479
    def _state_expecting_record_type(self):
467
480
        if len(self._buffer) >= 1:
468
 
            record_type = self._buffer[0]
 
481
            record_type = self._buffer[:1]
469
482
            self._buffer = self._buffer[1:]
470
 
            if record_type == 'B':
 
483
            if record_type == b'B':
471
484
                self._state_handler = self._state_expecting_length
472
 
            elif record_type == 'E':
 
485
            elif record_type == b'E':
473
486
                self.finished = True
474
487
                self._state_handler = self._state_expecting_nothing
475
488
            else:
487
500
 
488
501
    def _state_expecting_name(self):
489
502
        encoded_name_parts = self._consume_line()
490
 
        if encoded_name_parts == '':
 
503
        if encoded_name_parts == b'':
491
504
            self._state_handler = self._state_expecting_body
492
505
        elif encoded_name_parts:
493
 
            name_parts = tuple(encoded_name_parts.split('\x00'))
 
506
            name_parts = tuple(encoded_name_parts.split(b'\x00'))
494
507
            for name_part in name_parts:
495
508
                _check_name(name_part)
496
509
            self._current_record_names.append(name_parts)
526
539
            yield record
527
540
        if parser.finished:
528
541
            break
529