~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/pack.py

  • Committer: Andrew Bennetts
  • Date: 2007-06-08 06:33:59 UTC
  • mto: (2506.2.2 container-format)
  • mto: This revision was merged to the branch mainline in revision 2572.
  • Revision ID: andrew.bennetts@canonical.com-20070608063359-s5ps81a8i85w7by0
More progress:

 * Rename container.py to pack.py
 * Refactor bytes record reading into a separate class for ease of unit testing.
 * Start handling error conditions such as invalid content lengths in byte
   records.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
"Containers" and "records" are described in doc/developers/container-format.txt.
20
20
"""
21
21
 
22
 
# XXX: probably rename this to pack.py
23
 
 
24
22
from bzrlib import errors
25
23
 
26
24
 
27
25
FORMAT_ONE = "bzr pack format 1"
28
26
 
29
27
 
30
 
class ContainerReader(object):
31
 
    """A class for reading Bazaar's container format."""
 
28
class ContainerWriter(object):
 
29
    """A class for writing containers."""
 
30
 
 
31
    def __init__(self, write_func):
 
32
        """Constructor.
 
33
 
 
34
        :param write_func: a callable that will be called when this
 
35
            ContainerWriter needs to write some bytes.
 
36
        """
 
37
        self.write_func = write_func
 
38
 
 
39
    def begin(self):
 
40
        """Begin writing a container."""
 
41
        self.write_func(FORMAT_ONE + "\n")
 
42
 
 
43
    def end(self):
 
44
        """Finish writing a container."""
 
45
        self.write_func("E")
 
46
 
 
47
    def add_bytes_record(self, bytes, names):
 
48
        """Add a Bytes record with the given names."""
 
49
        # Kind marker
 
50
        self.write_func("B")
 
51
        # Length
 
52
        self.write_func(str(len(bytes)) + "\n")
 
53
        # Names
 
54
        for name in names:
 
55
            self.write_func(name + "\n")
 
56
        # End of headers
 
57
        self.write_func("\n")
 
58
        # Finally, the contents.
 
59
        self.write_func(bytes)
 
60
 
 
61
 
 
62
class BaseReader(object):
32
63
 
33
64
    def __init__(self, reader_func):
34
65
        """Constructor.
39
70
        """
40
71
        self.reader_func = reader_func
41
72
 
 
73
    def _read_line(self):
 
74
        """Read a line from the input stream.
 
75
 
 
76
        This is a simple but inefficient implementation that just reads one byte
 
77
        at a time.  Lines should not be very long, so this is probably
 
78
        tolerable.
 
79
 
 
80
        :returns: a line, without the trailing newline
 
81
        """
 
82
        # XXX: Have a maximum line length, to prevent malicious input from
 
83
        # consuming an unreasonable amount of resources?
 
84
        #   -- Andrew Bennetts, 2007-05-07.
 
85
        line = ''
 
86
        while not line.endswith('\n'):
 
87
            line += self.reader_func(1)
 
88
        return line[:-1]
 
89
 
 
90
 
 
91
class ContainerReader(BaseReader):
 
92
    """A class for reading Bazaar's container format."""
 
93
 
42
94
    def iter_records(self):
43
95
        """Iterate over the container, yielding each record as it is read.
44
96
 
45
97
        Each yielded record will be a 2-tuple of (names, bytes), where names is
46
98
        a ``list`` and bytes is a ``str`.
 
99
 
 
100
        :raises UnknownContainerFormatError: if the format of the container is
 
101
            unrecognised.
47
102
        """
48
103
        format = self._read_line()
49
104
        if format != FORMAT_ONE:
55
110
            record_kind = self.reader_func(1)
56
111
            if record_kind == 'B':
57
112
                # Bytes record.
58
 
                yield self._read_bytes_record()
 
113
                reader = BytesRecordReader(self.reader_func)
 
114
                yield reader.read()
59
115
            elif record_kind == 'E':
60
116
                # End marker.  There are no more records.
61
117
                return
67
123
                # Unknown record type.
68
124
                raise errors.UnknownRecordTypeError(record_kind)
69
125
 
70
 
    def _read_bytes_record(self):
71
 
        length = int(self._read_line())
 
126
 
 
127
class ContainerWriter(object):
 
128
    """A class for writing containers."""
 
129
 
 
130
    def __init__(self, write_func):
 
131
        """Constructor.
 
132
 
 
133
        :param write_func: a callable that will be called when this
 
134
            ContainerWriter needs to write some bytes.
 
135
        """
 
136
        self.write_func = write_func
 
137
 
 
138
    def begin(self):
 
139
        """Begin writing a container."""
 
140
        self.write_func(FORMAT_ONE + "\n")
 
141
 
 
142
    def end(self):
 
143
        """Finish writing a container."""
 
144
        self.write_func("E")
 
145
 
 
146
    def add_bytes_record(self, bytes, names):
 
147
        """Add a Bytes record with the given names."""
 
148
        # Kind marker
 
149
        self.write_func("B")
 
150
        # Length
 
151
        self.write_func(str(len(bytes)) + "\n")
 
152
        # Names
 
153
        for name in names:
 
154
            self.write_func(name + "\n")
 
155
        # End of headers
 
156
        self.write_func("\n")
 
157
        # Finally, the contents.
 
158
        self.write_func(bytes)
 
159
 
 
160
 
 
161
class BytesRecordReader(BaseReader):
 
162
 
 
163
    def read(self):
 
164
        # Read the content length.
 
165
        length_line = self._read_line()
 
166
        try:
 
167
            length = int(length_line)
 
168
        except ValueError:
 
169
            raise errors.InvalidRecordError(
 
170
                "%r is not a valid length." % (length_line,))
 
171
        
 
172
        # Read the list of names.
72
173
        names = []
73
174
        while True:
74
175
            name = self._read_line()
79
180
        # XXX: deal with case where len(bytes) != length
80
181
        return names, bytes
81
182
 
82
 
    def _read_line(self):
83
 
        """Read a line from the input stream.
84
 
 
85
 
        This is a simple but inefficient implementation that just reads one byte
86
 
        at a time.  Lines should not be very long, so this is probably
87
 
        tolerable.
88
 
 
89
 
        :returns: a line, without the trailing newline
90
 
        """
91
 
        # XXX: Have a maximum line length, to prevent malicious input from
92
 
        # consuming an unreasonable amount of resources?
93
 
        #   -- Andrew Bennetts, 2007-05-07.
94
 
        line = ''
95
 
        while not line.endswith('\n'):
96
 
            line += self.reader_func(1)
97
 
        return line[:-1]
98
 
 
99
 
 
100
 
class ContainerWriter(object):
101
 
    """A class for writing containers."""
102
 
 
103
 
    def __init__(self, write_func):
104
 
        """Constructor.
105
 
 
106
 
        :param write_func: a callable that will be called when this
107
 
            ContainerWriter needs to write some bytes.
108
 
        """
109
 
        self.write_func = write_func
110
 
 
111
 
    def begin(self):
112
 
        """Begin writing a container."""
113
 
        self.write_func(FORMAT_ONE + "\n")
114
 
 
115
 
    def end(self):
116
 
        """Finish writing a container."""
117
 
        self.write_func("E")
118
 
 
119
 
    def add_bytes_record(self, bytes, names):
120
 
        """Add a Bytes record with the given names."""
121
 
        # Kind marker
122
 
        self.write_func("B")
123
 
        # Length
124
 
        self.write_func(str(len(bytes)) + "\n")
125
 
        # Names
126
 
        for name in names:
127
 
            self.write_func(name + "\n")
128
 
        # End of headers
129
 
        self.write_func("\n")
130
 
        # Finally, the contents.
131
 
        self.write_func(bytes)
132