~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/pack.py

  • Committer: Andrew Bennetts
  • Date: 2007-10-29 08:49:20 UTC
  • mfrom: (2916.2.13 streamable-containers)
  • mto: This revision was merged to the branch mainline in revision 3174.
  • Revision ID: andrew.bennetts@canonical.com-20071029084920-fbybous3xzspcu4n
MergeĀ fromĀ streamable-containers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Container format for Bazaar data.
18
18
 
19
 
"Containers" and "records" are described in doc/developers/container-format.txt.
 
19
"Containers" and "records" are described in
 
20
doc/developers/container-format.txt.
20
21
"""
21
22
 
22
23
from cStringIO import StringIO
59
60
 
60
61
 
61
62
class ContainerSerialiser(object):
62
 
    """A class for serialising containers."""
 
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
    """
63
69
 
64
70
    def begin(self):
65
 
        """Begin a container."""
 
71
        """Return the bytes to begin a container."""
66
72
        return FORMAT_ONE + "\n"
67
73
 
68
74
    def end(self):
69
 
        """Finish a container."""
 
75
        """Return the bytes to finish a container."""
70
76
        return "E"
71
77
 
72
78
    def bytes_record(self, bytes, names):
 
79
        """Return the bytes for a Bytes record with the given name and
 
80
        contents.
 
81
        """
73
82
        # Kind marker
74
83
        byte_sections = ["B"]
75
84
        # Length
380
389
        self._state_handler = self._state_expecting_format_line
381
390
        self._parsed_records = []
382
391
        self._reset_current_record()
 
392
        self.finished = False
383
393
 
384
394
    def _reset_current_record(self):
385
395
        self._current_record_length = None
389
399
        self._buffer += bytes
390
400
        # Keep iterating the state machine until it stops consuming bytes from
391
401
        # the buffer.
392
 
        buffer_length = None
393
 
        while len(self._buffer) != buffer_length:
394
 
            buffer_length = len(self._buffer)
 
402
        last_buffer_length = None
 
403
        cur_buffer_length = len(self._buffer)
 
404
        while cur_buffer_length != last_buffer_length:
 
405
            last_buffer_length = cur_buffer_length
395
406
            self._state_handler()
 
407
            cur_buffer_length = len(self._buffer)
396
408
 
397
409
    def read_pending_records(self):
398
410
        records = self._parsed_records
399
411
        self._parsed_records = []
400
412
        return records
401
413
    
402
 
    def _consume_until_byte(self, byte):
403
 
        """Take all bytes up to the given out of the buffer, and return it.
 
414
    def _consume_line(self):
 
415
        """Take a line out of the buffer, and return the line.
404
416
 
405
 
        If the specified byte is not found in the buffer, the buffer is
 
417
        If a newline byte is not found in the buffer, the buffer is
406
418
        unchanged and this returns None instead.
407
419
        """
408
420
        newline_pos = self._buffer.find('\n')
413
425
        else:
414
426
            return None
415
427
 
416
 
    def _consume_line(self):
417
 
        return self._consume_until_byte('\n')
418
 
 
419
428
    def _state_expecting_format_line(self):
420
429
        line = self._consume_line()
421
430
        if line is not None:
427
436
        if len(self._buffer) >= 1:
428
437
            record_type = self._buffer[0]
429
438
            self._buffer = self._buffer[1:]
430
 
            if record_type != 'B':
431
 
                raise NotImplementedError('XXX')
432
 
            self._state_handler = self._state_expecting_length
 
439
            if record_type == 'B':
 
440
                self._state_handler = self._state_expecting_length
 
441
            elif record_type == 'E':
 
442
                self.finished = True
 
443
                self._state_handler = self._state_expecting_nothing
 
444
            else:
 
445
                raise errors.UnknownRecordTypeError(record_type)
433
446
 
434
447
    def _state_expecting_length(self):
435
448
        line = self._consume_line()
443
456
 
444
457
    def _state_expecting_name(self):
445
458
        encoded_name_parts = self._consume_line()
446
 
        if encoded_name_parts is not None:
447
 
            if encoded_name_parts == '':
448
 
                self._state_handler = self._state_expecting_body
449
 
            else:
450
 
                name_parts = tuple(encoded_name_parts.split('\x00'))
451
 
                for name_part in name_parts:
452
 
                    _check_name(name_part)
453
 
                self._current_record_names.append(name_parts)
 
459
        if encoded_name_parts == '':
 
460
            self._state_handler = self._state_expecting_body
 
461
        elif encoded_name_parts:
 
462
            name_parts = tuple(encoded_name_parts.split('\x00'))
 
463
            for name_part in name_parts:
 
464
                _check_name(name_part)
 
465
            self._current_record_names.append(name_parts)
454
466
            
455
467
    def _state_expecting_body(self):
456
468
        if len(self._buffer) >= self._current_record_length:
461
473
            self._reset_current_record()
462
474
            self._state_handler = self._state_expecting_record_type
463
475
 
 
476
    def _state_expecting_nothing(self):
 
477
        pass
 
478
 
 
479
    def read_size_hint(self):
 
480
        hint = 16384
 
481
        if self._state_handler == self._state_expecting_body:
 
482
            remaining = self._current_record_length - len(self._buffer)
 
483
            if remaining < 0:
 
484
                remaining = 0
 
485
            return max(hint, remaining)
 
486
        return hint
 
487
 
 
488
 
 
489
def iter_records_from_file(source_file):
 
490
    parser = ContainerPushParser()
 
491
    while True:
 
492
        bytes = source_file.read(parser.read_size_hint())
 
493
        parser.accept_bytes(bytes)
 
494
        for record in parser.read_pending_records():
 
495
            yield record
 
496
        if parser.finished:
 
497
            break
464
498