/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: Aaron Bentley
  • Date: 2007-08-14 23:35:48 UTC
  • mfrom: (2698 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2699.
  • Revision ID: aaron.bentley@utoronto.ca-20070814233548-ctlr8sb1lcufb3ny
Merge bzr.dev

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
66
67
        :param write_func: a callable that will be called when this
67
68
            ContainerWriter needs to write some bytes.
68
69
        """
69
 
        self.write_func = write_func
 
70
        self._write_func = write_func
 
71
        self.current_offset = 0
70
72
 
71
73
    def begin(self):
72
74
        """Begin writing a container."""
73
75
        self.write_func(FORMAT_ONE + "\n")
74
76
 
 
77
    def write_func(self, bytes):
 
78
        self._write_func(bytes)
 
79
        self.current_offset += len(bytes)
 
80
 
75
81
    def end(self):
76
82
        """Finish writing a container."""
77
83
        self.write_func("E")
78
84
 
79
85
    def add_bytes_record(self, bytes, names):
80
 
        """Add a Bytes record with the given names."""
 
86
        """Add a Bytes record with the given names.
 
87
        
 
88
        :param bytes: The bytes to insert.
 
89
        :param names: The names to give the inserted bytes. Each name is
 
90
            a tuple of bytestrings. The bytestrings may not contain
 
91
            whitespace.
 
92
        :return: An offset, length tuple. The offset is the offset
 
93
            of the record within the container, and the length is the
 
94
            length of data that will need to be read to reconstitute the
 
95
            record. These offset and length can only be used with the pack
 
96
            interface - they might be offset by headers or other such details
 
97
            and thus are only suitable for use by a ContainerReader.
 
98
        """
 
99
        current_offset = self.current_offset
81
100
        # Kind marker
82
101
        self.write_func("B")
83
102
        # Length
84
103
        self.write_func(str(len(bytes)) + "\n")
85
104
        # Names
86
 
        for name in names:
 
105
        for name_tuple in names:
87
106
            # Make sure we're writing valid names.  Note that we will leave a
88
107
            # half-written record if a name is bad!
89
 
            _check_name(name)
90
 
            self.write_func(name + "\n")
 
108
            for name in name_tuple:
 
109
                _check_name(name)
 
110
            self.write_func('\x00'.join(name_tuple) + "\n")
91
111
        # End of headers
92
112
        self.write_func("\n")
93
113
        # Finally, the contents.
94
114
        self.write_func(bytes)
 
115
        # return a memo of where we wrote data to allow random access.
 
116
        return current_offset, self.current_offset - current_offset
 
117
 
 
118
 
 
119
class ReadVFile(object):
 
120
    """Adapt a readv result iterator to a file like protocol."""
 
121
 
 
122
    def __init__(self, readv_result):
 
123
        self.readv_result = readv_result
 
124
        # the most recent readv result block
 
125
        self._string = None
 
126
 
 
127
    def _next(self):
 
128
        if (self._string is None or
 
129
            self._string.tell() == self._string_length):
 
130
            length, data = self.readv_result.next()
 
131
            self._string_length = len(data)
 
132
            self._string = StringIO(data)
 
133
 
 
134
    def read(self, length):
 
135
        self._next()
 
136
        result = self._string.read(length)
 
137
        if len(result) < length:
 
138
            raise errors.BzrError('request for too much data from a readv hunk.')
 
139
        return result
 
140
 
 
141
    def readline(self):
 
142
        """Note that readline will not cross readv segments."""
 
143
        self._next()
 
144
        result = self._string.readline()
 
145
        if self._string.tell() == self._string_length and result[-1] != '\n':
 
146
            raise errors.BzrError('short readline in the readvfile hunk.')
 
147
        return result
 
148
 
 
149
 
 
150
def make_readv_reader(transport, filename, requested_records):
 
151
    """Create a ContainerReader that will read selected records only.
 
152
 
 
153
    :param transport: The transport the pack file is located on.
 
154
    :param filename: The filename of the pack file.
 
155
    :param requested_records: The record offset, length tuples as returned
 
156
        by add_bytes_record for the desired records.
 
157
    """
 
158
    readv_blocks = [(0, len(FORMAT_ONE)+1)]
 
159
    readv_blocks.extend(requested_records)
 
160
    result = ContainerReader(ReadVFile(
 
161
        transport.readv(filename, readv_blocks)))
 
162
    return result
95
163
 
96
164
 
97
165
class BaseReader(object):
197
265
        all_names = set()
198
266
        for record_names, read_bytes in self.iter_records():
199
267
            read_bytes(None)
200
 
            for name in record_names:
201
 
                _check_name_encoding(name)
 
268
            for name_tuple in record_names:
 
269
                for name in name_tuple:
 
270
                    _check_name_encoding(name)
202
271
                # Check that the name is unique.  Note that Python will refuse
203
272
                # to decode non-shortest forms of UTF-8 encoding, so there is no
204
273
                # risk that the same unicode string has been encoded two
205
274
                # different ways.
206
 
                if name in all_names:
207
 
                    raise errors.DuplicateRecordNameError(name)
208
 
                all_names.add(name)
 
275
                if name_tuple in all_names:
 
276
                    raise errors.DuplicateRecordNameError(name_tuple)
 
277
                all_names.add(name_tuple)
209
278
        excess_bytes = self.reader_func(1)
210
279
        if excess_bytes != '':
211
280
            raise errors.ContainerHasExcessDataError(excess_bytes)
235
304
        # Read the list of names.
236
305
        names = []
237
306
        while True:
238
 
            name = self._read_line()
239
 
            if name == '':
 
307
            name_line = self._read_line()
 
308
            if name_line == '':
240
309
                break
241
 
            _check_name(name)
242
 
            names.append(name)
 
310
            name_tuple = tuple(name_line.split('\x00'))
 
311
            for name in name_tuple:
 
312
                _check_name(name)
 
313
            names.append(name_tuple)
243
314
 
244
315
        self._remaining_length = length
245
316
        return names, self._content_reader
263
334
        :raises ContainerError: if this record is invalid.
264
335
        """
265
336
        names, read_bytes = self.read()
266
 
        for name in names:
267
 
            _check_name_encoding(name)
 
337
        for name_tuple in names:
 
338
            for name in name_tuple:
 
339
                _check_name_encoding(name)
268
340
        read_bytes(None)
269
341