/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# Copyright (C) 2007 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""Container format for Bazaar data.

"Containers" and "records" are described in doc/developers/container-format.txt.
"""

from bzrlib import errors


FORMAT_ONE = "bzr pack format 1"


class ContainerWriter(object):
    """A class for writing containers."""

    def __init__(self, write_func):
        """Constructor.

        :param write_func: a callable that will be called when this
            ContainerWriter needs to write some bytes.
        """
        self.write_func = write_func

    def begin(self):
        """Begin writing a container."""
        self.write_func(FORMAT_ONE + "\n")

    def end(self):
        """Finish writing a container."""
        self.write_func("E")

    def add_bytes_record(self, bytes, names):
        """Add a Bytes record with the given names."""
        # Kind marker
        self.write_func("B")
        # Length
        self.write_func(str(len(bytes)) + "\n")
        # Names
        for name in names:
            self.write_func(name + "\n")
        # End of headers
        self.write_func("\n")
        # Finally, the contents.
        self.write_func(bytes)


class BaseReader(object):

    def __init__(self, reader_func):
        """Constructor.

        :param reader_func: a callable that takes one optional argument,
            ``size``, and returns at most that many bytes.  When the callable
            returns less than the requested number of bytes, then the end of the
            file/stream has been reached.
        """
        self.reader_func = reader_func

    def _read_line(self):
        """Read a line from the input stream.

        This is a simple but inefficient implementation that just reads one byte
        at a time.  Lines should not be very long, so this is probably
        tolerable.

        :returns: a line, without the trailing newline
        """
        # XXX: Have a maximum line length, to prevent malicious input from
        # consuming an unreasonable amount of resources?
        #   -- Andrew Bennetts, 2007-05-07.
        line = ''
        while not line.endswith('\n'):
            byte = self.reader_func(1)
            if byte == '':
                raise errors.UnexpectedEndOfContainerError()
            line += byte
        return line[:-1]


class ContainerReader(BaseReader):
    """A class for reading Bazaar's container format."""

    def iter_records(self):
        """Iterate over the container, yielding each record as it is read.

        Each yielded record will be a 2-tuple of (names, bytes), where names is
        a ``list`` and bytes is a ``str`.

        :raises UnknownContainerFormatError: if the format of the container is
            unrecognised.
        """
        format = self._read_line()
        if format != FORMAT_ONE:
            raise errors.UnknownContainerFormatError(format)
        return self._iter_records()
    
    def _iter_records(self):
        while True:
            record_kind = self.reader_func(1)
            if record_kind == 'B':
                # Bytes record.
                reader = BytesRecordReader(self.reader_func)
                yield reader.read()
            elif record_kind == 'E':
                # End marker.  There are no more records.
                return
            elif record_kind == '':
                # End of stream encountered, but no End Marker record seen, so
                # this container is incomplete.
                raise errors.UnexpectedEndOfContainerError()
            else:
                # Unknown record type.
                raise errors.UnknownRecordTypeError(record_kind)


class ContainerWriter(object):
    """A class for writing containers."""

    def __init__(self, write_func):
        """Constructor.

        :param write_func: a callable that will be called when this
            ContainerWriter needs to write some bytes.
        """
        self.write_func = write_func

    def begin(self):
        """Begin writing a container."""
        self.write_func(FORMAT_ONE + "\n")

    def end(self):
        """Finish writing a container."""
        self.write_func("E")

    def add_bytes_record(self, bytes, names):
        """Add a Bytes record with the given names."""
        # Kind marker
        self.write_func("B")
        # Length
        self.write_func(str(len(bytes)) + "\n")
        # Names
        for name in names:
            self.write_func(name + "\n")
        # End of headers
        self.write_func("\n")
        # Finally, the contents.
        self.write_func(bytes)


class BytesRecordReader(BaseReader):

    def read(self):
        # Read the content length.
        length_line = self._read_line()
        try:
            length = int(length_line)
        except ValueError:
            raise errors.InvalidRecordError(
                "%r is not a valid length." % (length_line,))
        
        # Read the list of names.
        names = []
        while True:
            name = self._read_line()
            if name == '':
                break
            names.append(name)
        bytes = self.reader_func(length)
        if len(bytes) != length:
            raise errors.UnexpectedEndOfContainerError()
        return names, bytes