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.
22
from bzrlib import errors
25
FORMAT_ONE = "bzr pack format 1"
28
class ContainerWriter(object):
29
"""A class for writing containers."""
31
def __init__(self, write_func):
34
:param write_func: a callable that will be called when this
35
ContainerWriter needs to write some bytes.
37
self.write_func = write_func
40
"""Begin writing a container."""
41
self.write_func(FORMAT_ONE + "\n")
44
"""Finish writing a container."""
47
def add_bytes_record(self, bytes, names):
48
"""Add a Bytes record with the given names."""
52
self.write_func(str(len(bytes)) + "\n")
55
self.write_func(name + "\n")
58
# Finally, the contents.
59
self.write_func(bytes)
62
class BaseReader(object):
64
def __init__(self, reader_func):
67
:param reader_func: a callable that takes one optional argument,
68
``size``, and returns at most that many bytes. When the callable
69
returns less than the requested number of bytes, then the end of the
70
file/stream has been reached.
72
self.reader_func = reader_func
75
"""Read a line from the input stream.
77
This is a simple but inefficient implementation that just reads one byte
78
at a time. Lines should not be very long, so this is probably
81
:returns: a line, without the trailing newline
83
# XXX: Have a maximum line length, to prevent malicious input from
84
# consuming an unreasonable amount of resources?
85
# -- Andrew Bennetts, 2007-05-07.
87
while not line.endswith('\n'):
88
byte = self.reader_func(1)
90
raise errors.UnexpectedEndOfContainerError()
95
class ContainerReader(BaseReader):
96
"""A class for reading Bazaar's container format."""
98
def iter_records(self):
99
"""Iterate over the container, yielding each record as it is read.
101
Each yielded record will be a 2-tuple of (names, bytes), where names is
102
a ``list`` and bytes is a ``str`.
104
:raises UnknownContainerFormatError: if the format of the container is
107
format = self._read_line()
108
if format != FORMAT_ONE:
109
raise errors.UnknownContainerFormatError(format)
110
return self._iter_records()
112
def _iter_records(self):
114
record_kind = self.reader_func(1)
115
if record_kind == 'B':
117
reader = BytesRecordReader(self.reader_func)
119
elif record_kind == 'E':
120
# End marker. There are no more records.
122
elif record_kind == '':
123
# End of stream encountered, but no End Marker record seen, so
124
# this container is incomplete.
125
raise errors.UnexpectedEndOfContainerError()
127
# Unknown record type.
128
raise errors.UnknownRecordTypeError(record_kind)
131
class ContainerWriter(object):
132
"""A class for writing containers."""
134
def __init__(self, write_func):
137
:param write_func: a callable that will be called when this
138
ContainerWriter needs to write some bytes.
140
self.write_func = write_func
143
"""Begin writing a container."""
144
self.write_func(FORMAT_ONE + "\n")
147
"""Finish writing a container."""
150
def add_bytes_record(self, bytes, names):
151
"""Add a Bytes record with the given names."""
155
self.write_func(str(len(bytes)) + "\n")
158
self.write_func(name + "\n")
160
self.write_func("\n")
161
# Finally, the contents.
162
self.write_func(bytes)
165
class BytesRecordReader(BaseReader):
168
# Read the content length.
169
length_line = self._read_line()
171
length = int(length_line)
173
raise errors.InvalidRecordError(
174
"%r is not a valid length." % (length_line,))
176
# Read the list of names.
179
name = self._read_line()
183
bytes = self.reader_func(length)
184
if len(bytes) != length:
185
raise errors.UnexpectedEndOfContainerError()