1
# Copyright (C) 2007 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Container format for Bazaar data.
19
"Containers" and "records" are described in doc/developers/container-format.txt.
24
from bzrlib import errors
27
FORMAT_ONE = "Bazaar pack format 1"
30
_whitespace_re = re.compile('[\t\n\x0b\x0c\r ]')
33
def _check_name(name):
34
"""Do some basic checking of 'name'.
36
At the moment, this just checks that there are no whitespace characters in a
39
:raises InvalidRecordError: if name is not valid.
41
# XXX: consider checking that name is a str of valid UTF-8 too?
42
if _whitespace_re.search(name) is not None:
43
raise errors.InvalidRecordError("%r is not a valid name." % (name,))
46
class ContainerWriter(object):
47
"""A class for writing containers."""
49
def __init__(self, write_func):
52
:param write_func: a callable that will be called when this
53
ContainerWriter needs to write some bytes.
55
self.write_func = write_func
58
"""Begin writing a container."""
59
self.write_func(FORMAT_ONE + "\n")
62
"""Finish writing a container."""
65
def add_bytes_record(self, bytes, names):
66
"""Add a Bytes record with the given names."""
70
self.write_func(str(len(bytes)) + "\n")
73
# Make sure we're writing valid names. Note that we will leave a
74
# half-written record if a name is bad!
76
self.write_func(name + "\n")
79
# Finally, the contents.
80
self.write_func(bytes)
83
class BaseReader(object):
85
def __init__(self, reader_func):
88
:param reader_func: a callable that takes one optional argument,
89
``size``, and returns at most that many bytes. When the callable
90
returns less than the requested number of bytes, then the end of the
91
file/stream has been reached.
93
self.reader_func = reader_func
96
"""Read a line from the input stream.
98
This is a simple but inefficient implementation that just reads one byte
99
at a time. Lines should not be very long, so this is probably
102
:returns: a line, without the trailing newline
104
# XXX: Have a maximum line length, to prevent malicious input from
105
# consuming an unreasonable amount of resources?
106
# -- Andrew Bennetts, 2007-05-07.
108
while not line.endswith('\n'):
109
byte = self.reader_func(1)
111
raise errors.UnexpectedEndOfContainerError()
116
class ContainerReader(BaseReader):
117
"""A class for reading Bazaar's container format."""
119
def iter_records(self):
120
"""Iterate over the container, yielding each record as it is read.
122
Each yielded record will be a 2-tuple of (names, bytes), where names is
123
a ``list`` and bytes is a ``str``.
125
:raises UnknownContainerFormatError: if the format of the container is
128
format = self._read_line()
129
if format != FORMAT_ONE:
130
raise errors.UnknownContainerFormatError(format)
131
return self._iter_records()
133
def _iter_records(self):
135
record_kind = self.reader_func(1)
136
if record_kind == 'B':
138
reader = BytesRecordReader(self.reader_func)
140
elif record_kind == 'E':
141
# End marker. There are no more records.
143
elif record_kind == '':
144
# End of stream encountered, but no End Marker record seen, so
145
# this container is incomplete.
146
raise errors.UnexpectedEndOfContainerError()
148
# Unknown record type.
149
raise errors.UnknownRecordTypeError(record_kind)
152
class BytesRecordReader(BaseReader):
155
# Read the content length.
156
length_line = self._read_line()
158
length = int(length_line)
160
raise errors.InvalidRecordError(
161
"%r is not a valid length." % (length_line,))
163
# Read the list of names.
166
name = self._read_line()
171
bytes = self.reader_func(length)
172
if len(bytes) != length:
173
raise errors.UnexpectedEndOfContainerError()