/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/chk_serializer.py

  • Committer: Robert Collins
  • Date: 2010-05-11 08:36:16 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100511083616-b8fjb19zomwupid0
Make all lock methods return Result objects, rather than lock_read returning self, as per John's review.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Serializer object for CHK based inventory storage."""
18
18
 
19
 
from __future__ import absolute_import
20
 
 
21
 
from .. import lazy_import
22
 
lazy_import.lazy_import(globals(),
23
 
                        """
24
 
from breezy.bzr import (
25
 
    serializer,
26
 
    xml_serializer,
27
 
    )
28
 
""")
29
 
from .. import (
 
19
from bzrlib import (
30
20
    bencode,
31
21
    cache_utf8,
32
 
    errors,
 
22
    inventory,
33
23
    revision as _mod_revision,
34
 
    )
35
 
from . import (
36
 
    serializer,
37
 
    )
38
 
from ..sixish import (
39
 
    BytesIO,
 
24
    xml6,
 
25
    xml7,
40
26
    )
41
27
 
42
28
 
43
29
def _validate_properties(props, _decode=cache_utf8._utf8_decode):
44
30
    # TODO: we really want an 'isascii' check for key
45
31
    # Cast the utf8 properties into Unicode 'in place'
46
 
    return {_decode(key)[0]: _decode(value, 'surrogateescape')[0] for key, value in props.items()}
 
32
    for key, value in props.iteritems():
 
33
        props[key] = _decode(value)[0]
 
34
    return props
47
35
 
48
36
 
49
37
def _is_format_10(value):
65
53
    # the type.
66
54
    # TODO: add a 'validate_utf8' for things like revision_id and file_id
67
55
    #       and a validator for parent-ids
68
 
    _schema = {b'format': (None, int, _is_format_10),
69
 
               b'committer': ('committer', bytes, cache_utf8.decode),
70
 
               b'timezone': ('timezone', int, None),
71
 
               b'timestamp': ('timestamp', bytes, float),
72
 
               b'revision-id': ('revision_id', bytes, None),
73
 
               b'parent-ids': ('parent_ids', list, None),
74
 
               b'inventory-sha1': ('inventory_sha1', bytes, None),
75
 
               b'message': ('message', bytes, cache_utf8.decode),
76
 
               b'properties': ('properties', dict, _validate_properties),
77
 
               }
 
56
    _schema = {'format': (None, int, _is_format_10),
 
57
               'committer': ('committer', str, cache_utf8.decode),
 
58
               'timezone': ('timezone', int, None),
 
59
               'timestamp': ('timestamp', str, float),
 
60
               'revision-id': ('revision_id', str, None),
 
61
               'parent-ids': ('parent_ids', list, None),
 
62
               'inventory-sha1': ('inventory_sha1', str, None),
 
63
               'message': ('message', str, cache_utf8.decode),
 
64
               'properties': ('properties', dict, _validate_properties),
 
65
    }
78
66
 
79
67
    def write_revision_to_string(self, rev):
80
68
        encode_utf8 = cache_utf8._utf8_encode
82
70
        # This lets us control the ordering, so that we are able to create
83
71
        # smaller deltas
84
72
        ret = [
85
 
            (b"format", 10),
86
 
            (b"committer", encode_utf8(rev.committer)[0]),
 
73
            ("format", 10),
 
74
            ("committer", encode_utf8(rev.committer)[0]),
87
75
        ]
88
76
        if rev.timezone is not None:
89
 
            ret.append((b"timezone", rev.timezone))
 
77
            ret.append(("timezone", rev.timezone))
90
78
        # For bzr revisions, the most common property is just 'branch-nick'
91
79
        # which changes infrequently.
92
80
        revprops = {}
93
 
        for key, value in rev.properties.items():
94
 
            revprops[encode_utf8(key)[0]] = encode_utf8(value, 'surrogateescape')[0]
95
 
        ret.append((b'properties', revprops))
 
81
        for key, value in rev.properties.iteritems():
 
82
            revprops[key] = encode_utf8(value)[0]
 
83
        ret.append(('properties', revprops))
96
84
        ret.extend([
97
 
            (b"timestamp", b"%.3f" % rev.timestamp),
98
 
            (b"revision-id", rev.revision_id),
99
 
            (b"parent-ids", rev.parent_ids),
100
 
            (b"inventory-sha1", rev.inventory_sha1),
101
 
            (b"message", encode_utf8(rev.message)[0]),
 
85
            ("timestamp", "%.3f" % rev.timestamp),
 
86
            ("revision-id", rev.revision_id),
 
87
            ("parent-ids", rev.parent_ids),
 
88
            ("inventory-sha1", rev.inventory_sha1),
 
89
            ("message", encode_utf8(rev.message)[0]),
102
90
        ])
103
91
        return bencode.bencode(ret)
104
92
 
105
 
    def write_revision_to_lines(self, rev):
106
 
        return self.write_revision_to_string(rev).splitlines(True)
 
93
    def write_revision(self, rev, f):
 
94
        f.write(self.write_revision_to_string(rev))
107
95
 
108
96
    def read_revision_from_string(self, text):
109
97
        # TODO: consider writing a Revision decoder, rather than using the
111
99
        #       However, to decode all 25k revisions of bzr takes approx 1.3s
112
100
        #       If we remove all extra validation that goes down to about 1.2s.
113
101
        #       Of that time, probably 0.6s is spend in bencode.bdecode().
114
 
        #       Regardless 'time brz log' of everything is 7+s, so 1.3s to
 
102
        #       Regardless 'time bzr log' of everything is 7+s, so 1.3s to
115
103
        #       extract revision texts isn't a majority of time.
116
104
        ret = bencode.bdecode(text)
117
105
        if not isinstance(ret, list):
131
119
                value = validator(value)
132
120
            bits[var_name] = value
133
121
        if len(bits) != len(schema):
134
 
            missing = [key for key, (var_name, _, _) in schema.items()
 
122
            missing = [key for key, (var_name, _, _) in schema.iteritems()
135
123
                       if var_name not in bits]
136
124
            raise ValueError('Revision text was missing expected keys %s.'
137
125
                             ' text %r' % (missing, text))
143
131
        return self.read_revision_from_string(f.read())
144
132
 
145
133
 
146
 
class CHKSerializer(serializer.Serializer):
 
134
class CHKSerializerSubtree(BEncodeRevisionSerializer1, xml7.Serializer_v7):
 
135
    """A CHKInventory based serializer that supports tree references"""
 
136
 
 
137
    supported_kinds = set(['file', 'directory', 'symlink', 'tree-reference'])
 
138
    format_num = '9'
 
139
    revision_format_num = None
 
140
    support_altered_by_hack = False
 
141
 
 
142
    def _unpack_entry(self, elt, entry_cache=None, return_from_cache=False):
 
143
        kind = elt.tag
 
144
        if not kind in self.supported_kinds:
 
145
            raise AssertionError('unsupported entry kind %s' % kind)
 
146
        if kind == 'tree-reference':
 
147
            file_id = elt.attrib['file_id']
 
148
            name = elt.attrib['name']
 
149
            parent_id = elt.attrib['parent_id']
 
150
            revision = elt.get('revision')
 
151
            reference_revision = elt.get('reference_revision')
 
152
            return inventory.TreeReference(file_id, name, parent_id, revision,
 
153
                                           reference_revision)
 
154
        else:
 
155
            return xml7.Serializer_v7._unpack_entry(self, elt,
 
156
                entry_cache=entry_cache, return_from_cache=return_from_cache)
 
157
 
 
158
    def __init__(self, node_size, search_key_name):
 
159
        self.maximum_size = node_size
 
160
        self.search_key_name = search_key_name
 
161
 
 
162
 
 
163
class CHKSerializer(xml6.Serializer_v6):
147
164
    """A CHKInventory based serializer with 'plain' behaviour."""
148
165
 
149
 
    format_num = b'9'
 
166
    format_num = '9'
150
167
    revision_format_num = None
151
168
    support_altered_by_hack = False
152
 
    supported_kinds = {'file', 'directory', 'symlink', 'tree-reference'}
153
169
 
154
170
    def __init__(self, node_size, search_key_name):
155
171
        self.maximum_size = node_size
156
172
        self.search_key_name = search_key_name
157
173
 
158
 
    def _unpack_inventory(self, elt, revision_id=None, entry_cache=None,
159
 
                          return_from_cache=False):
160
 
        """Construct from XML Element"""
161
 
        inv = xml_serializer.unpack_inventory_flat(elt, self.format_num,
162
 
                                                   xml_serializer.unpack_inventory_entry, entry_cache,
163
 
                                                   return_from_cache)
164
 
        return inv
165
 
 
166
 
    def read_inventory_from_lines(self, xml_lines, revision_id=None,
167
 
                                  entry_cache=None, return_from_cache=False):
168
 
        """Read xml_string into an inventory object.
169
 
 
170
 
        :param xml_string: The xml to read.
171
 
        :param revision_id: If not-None, the expected revision id of the
172
 
            inventory.
173
 
        :param entry_cache: An optional cache of InventoryEntry objects. If
174
 
            supplied we will look up entries via (file_id, revision_id) which
175
 
            should map to a valid InventoryEntry (File/Directory/etc) object.
176
 
        :param return_from_cache: Return entries directly from the cache,
177
 
            rather than copying them first. This is only safe if the caller
178
 
            promises not to mutate the returned inventory entries, but it can
179
 
            make some operations significantly faster.
180
 
        """
181
 
        try:
182
 
            return self._unpack_inventory(
183
 
                xml_serializer.fromstringlist(xml_lines), revision_id,
184
 
                entry_cache=entry_cache,
185
 
                return_from_cache=return_from_cache)
186
 
        except xml_serializer.ParseError as e:
187
 
            raise serializer.UnexpectedInventoryFormat(e)
188
 
 
189
 
    def read_inventory(self, f, revision_id=None):
190
 
        """Read an inventory from a file-like object."""
191
 
        try:
192
 
            try:
193
 
                return self._unpack_inventory(self._read_element(f),
194
 
                                              revision_id=None)
195
 
            finally:
196
 
                f.close()
197
 
        except xml_serializer.ParseError as e:
198
 
            raise serializer.UnexpectedInventoryFormat(e)
199
 
 
200
 
    def write_inventory_to_lines(self, inv):
201
 
        """Return a list of lines with the encoded inventory."""
202
 
        return self.write_inventory(inv, None)
203
 
 
204
 
    def write_inventory_to_chunks(self, inv):
205
 
        """Return a list of lines with the encoded inventory."""
206
 
        return self.write_inventory(inv, None)
207
 
 
208
 
    def write_inventory(self, inv, f, working=False):
209
 
        """Write inventory to a file.
210
 
 
211
 
        :param inv: the inventory to write.
212
 
        :param f: the file to write. (May be None if the lines are the desired
213
 
            output).
214
 
        :param working: If True skip history data - text_sha1, text_size,
215
 
            reference_revision, symlink_target.
216
 
        :return: The inventory as a list of lines.
217
 
        """
218
 
        output = []
219
 
        append = output.append
220
 
        if inv.revision_id is not None:
221
 
            revid = b''.join(
222
 
                [b' revision_id="',
223
 
                 xml_serializer.encode_and_escape(inv.revision_id), b'"'])
224
 
        else:
225
 
            revid = b""
226
 
        append(b'<inventory format="%s"%s>\n' % (
227
 
            self.format_num, revid))
228
 
        append(b'<directory file_id="%s" name="%s" revision="%s" />\n' % (
229
 
            xml_serializer.encode_and_escape(inv.root.file_id),
230
 
            xml_serializer.encode_and_escape(inv.root.name),
231
 
            xml_serializer.encode_and_escape(inv.root.revision)))
232
 
        xml_serializer.serialize_inventory_flat(inv,
233
 
                                                append,
234
 
                                                root_id=None, supported_kinds=self.supported_kinds,
235
 
                                                working=working)
236
 
        if f is not None:
237
 
            f.writelines(output)
238
 
        return output
239
 
 
240
 
 
241
 
chk_serializer_255_bigpage = CHKSerializer(65536, b'hash-255-way')
 
174
 
 
175
chk_serializer_255_bigpage = CHKSerializer(65536, 'hash-255-way')
242
176
 
243
177
 
244
178
class CHKBEncodeSerializer(BEncodeRevisionSerializer1, CHKSerializer):
245
179
    """A CHKInventory and BEncode based serializer with 'plain' behaviour."""
246
180
 
247
 
    format_num = b'10'
248
 
 
249
 
 
250
 
chk_bencode_serializer = CHKBEncodeSerializer(65536, b'hash-255-way')
 
181
    format_num = '10'
 
182
 
 
183
 
 
184
chk_bencode_serializer = CHKBEncodeSerializer(65536, 'hash-255-way')