/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/pack.py

  • Committer: Martin Pool
  • Date: 2007-09-14 06:31:28 UTC
  • mfrom: (2822 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2823.
  • Revision ID: mbp@sourcefrog.net-20070914063128-0p7mh6zfb4pzdg9p
merge trunk

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
from cStringIO import StringIO
22
23
import re
23
24
 
24
25
from bzrlib import errors
58
59
 
59
60
 
60
61
class ContainerWriter(object):
61
 
    """A class for writing containers."""
 
62
    """A class for writing containers.
 
63
 
 
64
    :attribute records_written: The number of user records added to the
 
65
        container. This does not count the prelude or suffix of the container
 
66
        introduced by the begin() and end() methods.
 
67
    """
62
68
 
63
69
    def __init__(self, write_func):
64
70
        """Constructor.
66
72
        :param write_func: a callable that will be called when this
67
73
            ContainerWriter needs to write some bytes.
68
74
        """
69
 
        self.write_func = write_func
 
75
        self._write_func = write_func
 
76
        self.current_offset = 0
 
77
        self.records_written = 0
70
78
 
71
79
    def begin(self):
72
80
        """Begin writing a container."""
73
81
        self.write_func(FORMAT_ONE + "\n")
74
82
 
 
83
    def write_func(self, bytes):
 
84
        self._write_func(bytes)
 
85
        self.current_offset += len(bytes)
 
86
 
75
87
    def end(self):
76
88
        """Finish writing a container."""
77
89
        self.write_func("E")
78
90
 
79
91
    def add_bytes_record(self, bytes, names):
80
 
        """Add a Bytes record with the given names."""
 
92
        """Add a Bytes record with the given names.
 
93
        
 
94
        :param bytes: The bytes to insert.
 
95
        :param names: The names to give the inserted bytes. Each name is
 
96
            a tuple of bytestrings. The bytestrings may not contain
 
97
            whitespace.
 
98
        :return: An offset, length tuple. The offset is the offset
 
99
            of the record within the container, and the length is the
 
100
            length of data that will need to be read to reconstitute the
 
101
            record. These offset and length can only be used with the pack
 
102
            interface - they might be offset by headers or other such details
 
103
            and thus are only suitable for use by a ContainerReader.
 
104
        """
 
105
        current_offset = self.current_offset
81
106
        # Kind marker
82
 
        self.write_func("B")
 
107
        byte_sections = ["B"]
83
108
        # Length
84
 
        self.write_func(str(len(bytes)) + "\n")
 
109
        byte_sections.append(str(len(bytes)) + "\n")
85
110
        # Names
86
 
        for name in names:
 
111
        for name_tuple in names:
87
112
            # Make sure we're writing valid names.  Note that we will leave a
88
113
            # half-written record if a name is bad!
89
 
            _check_name(name)
90
 
            self.write_func(name + "\n")
 
114
            for name in name_tuple:
 
115
                _check_name(name)
 
116
            byte_sections.append('\x00'.join(name_tuple) + "\n")
91
117
        # End of headers
92
 
        self.write_func("\n")
 
118
        byte_sections.append("\n")
93
119
        # Finally, the contents.
94
 
        self.write_func(bytes)
 
120
        byte_sections.append(bytes)
 
121
        # XXX: This causes a memory copy of bytes in size, but is usually
 
122
        # faster than two write calls (12 vs 13 seconds to output a gig of
 
123
        # 1k records.) - results may differ on significantly larger records
 
124
        # like .iso's but as they should be rare in any case and thus not
 
125
        # likely to be the common case. The biggest issue is causing extreme
 
126
        # memory pressure in that case. One possibly improvement here is to
 
127
        # check the size of the content before deciding to join here vs call
 
128
        # write twice.
 
129
        self.write_func(''.join(byte_sections))
 
130
        self.records_written += 1
 
131
        # return a memo of where we wrote data to allow random access.
 
132
        return current_offset, self.current_offset - current_offset
 
133
 
 
134
 
 
135
class ReadVFile(object):
 
136
    """Adapt a readv result iterator to a file like protocol."""
 
137
 
 
138
    def __init__(self, readv_result):
 
139
        self.readv_result = readv_result
 
140
        # the most recent readv result block
 
141
        self._string = None
 
142
 
 
143
    def _next(self):
 
144
        if (self._string is None or
 
145
            self._string.tell() == self._string_length):
 
146
            length, data = self.readv_result.next()
 
147
            self._string_length = len(data)
 
148
            self._string = StringIO(data)
 
149
 
 
150
    def read(self, length):
 
151
        self._next()
 
152
        result = self._string.read(length)
 
153
        if len(result) < length:
 
154
            raise errors.BzrError('request for too much data from a readv hunk.')
 
155
        return result
 
156
 
 
157
    def readline(self):
 
158
        """Note that readline will not cross readv segments."""
 
159
        self._next()
 
160
        result = self._string.readline()
 
161
        if self._string.tell() == self._string_length and result[-1] != '\n':
 
162
            raise errors.BzrError('short readline in the readvfile hunk.')
 
163
        return result
 
164
 
 
165
 
 
166
def make_readv_reader(transport, filename, requested_records):
 
167
    """Create a ContainerReader that will read selected records only.
 
168
 
 
169
    :param transport: The transport the pack file is located on.
 
170
    :param filename: The filename of the pack file.
 
171
    :param requested_records: The record offset, length tuples as returned
 
172
        by add_bytes_record for the desired records.
 
173
    """
 
174
    readv_blocks = [(0, len(FORMAT_ONE)+1)]
 
175
    readv_blocks.extend(requested_records)
 
176
    result = ContainerReader(ReadVFile(
 
177
        transport.readv(filename, readv_blocks)))
 
178
    return result
95
179
 
96
180
 
97
181
class BaseReader(object):
197
281
        all_names = set()
198
282
        for record_names, read_bytes in self.iter_records():
199
283
            read_bytes(None)
200
 
            for name in record_names:
201
 
                _check_name_encoding(name)
 
284
            for name_tuple in record_names:
 
285
                for name in name_tuple:
 
286
                    _check_name_encoding(name)
202
287
                # Check that the name is unique.  Note that Python will refuse
203
288
                # to decode non-shortest forms of UTF-8 encoding, so there is no
204
289
                # risk that the same unicode string has been encoded two
205
290
                # different ways.
206
 
                if name in all_names:
207
 
                    raise errors.DuplicateRecordNameError(name)
208
 
                all_names.add(name)
 
291
                if name_tuple in all_names:
 
292
                    raise errors.DuplicateRecordNameError(name_tuple)
 
293
                all_names.add(name_tuple)
209
294
        excess_bytes = self.reader_func(1)
210
295
        if excess_bytes != '':
211
296
            raise errors.ContainerHasExcessDataError(excess_bytes)
235
320
        # Read the list of names.
236
321
        names = []
237
322
        while True:
238
 
            name = self._read_line()
239
 
            if name == '':
 
323
            name_line = self._read_line()
 
324
            if name_line == '':
240
325
                break
241
 
            _check_name(name)
242
 
            names.append(name)
 
326
            name_tuple = tuple(name_line.split('\x00'))
 
327
            for name in name_tuple:
 
328
                _check_name(name)
 
329
            names.append(name_tuple)
243
330
 
244
331
        self._remaining_length = length
245
332
        return names, self._content_reader
263
350
        :raises ContainerError: if this record is invalid.
264
351
        """
265
352
        names, read_bytes = self.read()
266
 
        for name in names:
267
 
            _check_name_encoding(name)
 
353
        for name_tuple in names:
 
354
            for name in name_tuple:
 
355
                _check_name_encoding(name)
268
356
        read_bytes(None)
269
357