~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/pack.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-08-02 06:33:21 UTC
  • mfrom: (2661.2.3 pack)
  • Revision ID: pqm@pqm.ubuntu.com-20070802063321-lpx3oazcxyac24oa
(robertc) Add support to the bzrlib.pack interface for arbitrary-record access via a readv adapter for transports. (Robert Collins)

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.
 
90
        :return: An offset, length tuple. The offset is the offset
 
91
            of the record within the container, and the length is the
 
92
            length of data that will need to be read to reconstitute the
 
93
            record. These offset and length can only be used with the pack
 
94
            interface - they might be offset by headers or other such details
 
95
            and thus are only suitable for use by a ContainerReader.
 
96
        """
 
97
        current_offset = self.current_offset
81
98
        # Kind marker
82
99
        self.write_func("B")
83
100
        # Length
92
109
        self.write_func("\n")
93
110
        # Finally, the contents.
94
111
        self.write_func(bytes)
 
112
        # return a memo of where we wrote data to allow random access.
 
113
        return current_offset, self.current_offset - current_offset
 
114
 
 
115
 
 
116
class ReadVFile(object):
 
117
    """Adapt a readv result iterator to a file like protocol."""
 
118
 
 
119
    def __init__(self, readv_result):
 
120
        self.readv_result = readv_result
 
121
        # the most recent readv result block
 
122
        self._string = None
 
123
 
 
124
    def _next(self):
 
125
        if (self._string is None or
 
126
            self._string.tell() == self._string_length):
 
127
            length, data = self.readv_result.next()
 
128
            self._string_length = len(data)
 
129
            self._string = StringIO(data)
 
130
 
 
131
    def read(self, length):
 
132
        self._next()
 
133
        result = self._string.read(length)
 
134
        if len(result) < length:
 
135
            raise errors.BzrError('request for too much data from a readv hunk.')
 
136
        return result
 
137
 
 
138
    def readline(self):
 
139
        """Note that readline will not cross readv segments."""
 
140
        self._next()
 
141
        result = self._string.readline()
 
142
        if self._string.tell() == self._string_length and result[-1] != '\n':
 
143
            raise errors.BzrError('short readline in the readvfile hunk.')
 
144
        return result
 
145
 
 
146
 
 
147
def make_readv_reader(transport, filename, requested_records):
 
148
    """Create a ContainerReader that will read selected records only.
 
149
 
 
150
    :param transport: The transport the pack file is located on.
 
151
    :param filename: The filename of the pack file.
 
152
    :param requested_records: The record offset, length tuples as returned
 
153
        by add_bytes_record for the desired records.
 
154
    """
 
155
    readv_blocks = [(0, len(FORMAT_ONE)+1)]
 
156
    readv_blocks.extend(requested_records)
 
157
    result = ContainerReader(ReadVFile(
 
158
        transport.readv(filename, readv_blocks)))
 
159
    return result
95
160
 
96
161
 
97
162
class BaseReader(object):