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
# XXX: probably rename this to pack.py
24
from bzrlib import errors
27
FORMAT_ONE = "bzr pack format 1"
30
class ContainerReader(object):
31
"""A class for reading Bazaar's container format."""
33
def __init__(self, reader_func):
36
:param reader_func: a callable that takes one optional argument,
37
``size``, and returns at most that many bytes. When the callable
38
returns an empty string, then at most that many bytes are read.
40
self.reader_func = reader_func
42
def iter_records(self):
43
"""Iterate over the container, yielding each record as it is read.
45
Each yielded record will be a 2-tuple of (names, bytes), where names is
46
a ``list`` and bytes is a ``str`.
48
format = self._read_line()
49
if format != FORMAT_ONE:
50
raise errors.UnknownContainerFormatError(format)
51
return self._iter_records()
53
def _iter_records(self):
55
record_kind = self.reader_func(1)
56
if record_kind == 'B':
58
yield self._read_bytes_record()
59
elif record_kind == 'E':
60
# End marker. There are no more records.
62
elif record_kind == '':
63
# End of stream encountered, but no End Marker record seen, so
64
# this container is incomplete.
65
raise errors.UnexpectedEndOfContainerError()
67
# Unknown record type.
68
raise errors.UnknownRecordTypeError(record_kind)
70
def _read_bytes_record(self):
71
length = int(self._read_line())
74
name = self._read_line()
78
bytes = self.reader_func(length)
79
# XXX: deal with case where len(bytes) != length
83
"""Read a line from the input stream.
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
89
:returns: a line, without the trailing newline
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.
95
while not line.endswith('\n'):
96
line += self.reader_func(1)
100
class ContainerWriter(object):
101
"""A class for writing containers."""
103
def __init__(self, write_func):
106
:param write_func: a callable that will be called when this
107
ContainerWriter needs to write some bytes.
109
self.write_func = write_func
112
"""Begin writing a container."""
113
self.write_func(FORMAT_ONE + "\n")
116
"""Finish writing a container."""
119
def add_bytes_record(self, bytes, names):
120
"""Add a Bytes record with the given names."""
124
self.write_func(str(len(bytes)) + "\n")
127
self.write_func(name + "\n")
129
self.write_func("\n")
130
# Finally, the contents.
131
self.write_func(bytes)