/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: John Arbash Meinel
  • Date: 2009-06-18 18:18:36 UTC
  • mto: This revision was merged to the branch mainline in revision 4461.
  • Revision ID: john@arbash-meinel.com-20090618181836-biodfkat9a8eyzjz
The new add_inventory_by_delta is returning a CHKInventory when mapping from NULL
Which is completely valid, but 'broke' one of the tests.
So to fix it, changed the test to use CHKInventories on both sides, and add an __eq__
member. The nice thing is that CHKInventory.__eq__ is fairly cheap, since it only
has to check the root keys.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Serializer object for CHK based inventory storage."""
 
18
 
 
19
from cStringIO import (
 
20
    StringIO,
 
21
    )
 
22
 
 
23
from bzrlib import (
 
24
    bencode,
 
25
    cache_utf8,
 
26
    inventory,
 
27
    osutils,
 
28
    revision as _mod_revision,
 
29
    xml5,
 
30
    xml6,
 
31
    )
 
32
 
 
33
 
 
34
def _validate_properties(props, _decode=cache_utf8._utf8_decode):
 
35
    # TODO: we really want an 'isascii' check for key
 
36
    # Cast the utf8 properties into Unicode 'in place'
 
37
    for key, value in props.iteritems():
 
38
        props[key] = _decode(value)[0]
 
39
    return props
 
40
 
 
41
 
 
42
def _is_format_10(value):
 
43
    if value != 10:
 
44
        raise ValueError('Format number was not recognized, expected 10 got %d'
 
45
                         % (value,))
 
46
    return 10
 
47
 
 
48
 
 
49
class BEncodeRevisionSerializer1(object):
 
50
    """Simple revision serializer based around bencode.
 
51
    """
 
52
 
 
53
    squashes_xml_invalid_characters = False
 
54
 
 
55
    # Maps {key:(Revision attribute, bencode_type, validator)}
 
56
    # This tells us what kind we expect bdecode to create, what variable on
 
57
    # Revision we should be using, and a function to call to validate/transform
 
58
    # the type.
 
59
    # TODO: add a 'validate_utf8' for things like revision_id and file_id
 
60
    #       and a validator for parent-ids
 
61
    _schema = {'format': (None, int, _is_format_10),
 
62
               'committer': ('committer', str, cache_utf8.decode),
 
63
               'timezone': ('timezone', int, None),
 
64
               'timestamp': ('timestamp', str, float),
 
65
               'revision-id': ('revision_id', str, None),
 
66
               'parent-ids': ('parent_ids', list, None),
 
67
               'inventory-sha1': ('inventory_sha1', str, None),
 
68
               'message': ('message', str, cache_utf8.decode),
 
69
               'properties': ('properties', dict, _validate_properties),
 
70
    }
 
71
 
 
72
    def write_revision_to_string(self, rev):
 
73
        encode_utf8 = cache_utf8._utf8_encode
 
74
        # Use a list of tuples rather than a dict
 
75
        # This lets us control the ordering, so that we are able to create
 
76
        # smaller deltas
 
77
        ret = [
 
78
            ("format", 10),
 
79
            ("committer", encode_utf8(rev.committer)[0]),
 
80
        ]
 
81
        if rev.timezone is not None:
 
82
            ret.append(("timezone", rev.timezone))
 
83
        # For bzr revisions, the most common property is just 'branch-nick'
 
84
        # which changes infrequently.
 
85
        revprops = {}
 
86
        for key, value in rev.properties.iteritems():
 
87
            revprops[key] = encode_utf8(value)[0]
 
88
        ret.append(('properties', revprops))
 
89
        ret.extend([
 
90
            ("timestamp", "%.3f" % rev.timestamp),
 
91
            ("revision-id", rev.revision_id),
 
92
            ("parent-ids", rev.parent_ids),
 
93
            ("inventory-sha1", rev.inventory_sha1),
 
94
            ("message", encode_utf8(rev.message)[0]),
 
95
        ])
 
96
        return bencode.bencode(ret)
 
97
 
 
98
    def write_revision(self, rev, f):
 
99
        f.write(self.write_revision_to_string(rev))
 
100
 
 
101
    def read_revision_from_string(self, text):
 
102
        # TODO: consider writing a Revision decoder, rather than using the
 
103
        #       generic bencode decoder
 
104
        #       However, to decode all 25k revisions of bzr takes approx 1.3s
 
105
        #       If we remove all extra validation that goes down to about 1.2s.
 
106
        #       Of that time, probably 0.6s is spend in bencode.bdecode().
 
107
        #       Regardless 'time bzr log' of everything is 7+s, so 1.3s to
 
108
        #       extract revision texts isn't a majority of time.
 
109
        ret = bencode.bdecode(text)
 
110
        if not isinstance(ret, list):
 
111
            raise ValueError("invalid revision text")
 
112
        schema = self._schema
 
113
        # timezone is allowed to be missing, but should be set
 
114
        bits = {'timezone': None}
 
115
        for key, value in ret:
 
116
            # Will raise KeyError if not a valid part of the schema, or an
 
117
            # entry is given 2 times.
 
118
            var_name, expected_type, validator = schema[key]
 
119
            if value.__class__ is not expected_type:
 
120
                raise ValueError('key %s did not conform to the expected type'
 
121
                                 ' %s, but was %s'
 
122
                                 % (key, expected_type, type(value)))
 
123
            if validator is not None:
 
124
                value = validator(value)
 
125
            bits[var_name] = value
 
126
        if len(bits) != len(schema):
 
127
            missing = [key for key, (var_name, _, _) in schema.iteritems()
 
128
                       if var_name not in bits]
 
129
            raise ValueError('Revision text was missing expected keys %s.'
 
130
                             ' text %r' % (missing, text))
 
131
        del bits[None]  # Get rid of 'format' since it doesn't get mapped
 
132
        rev = _mod_revision.Revision(**bits)
 
133
        return rev
 
134
 
 
135
    def read_revision(self, f):
 
136
        return self.read_revision_from_string(f.read())
 
137
 
 
138
 
 
139
class CHKSerializerSubtree(BEncodeRevisionSerializer1, xml6.Serializer_v6):
 
140
    """A CHKInventory based serializer that supports tree references"""
 
141
 
 
142
    supported_kinds = set(['file', 'directory', 'symlink', 'tree-reference'])
 
143
    format_num = '9'
 
144
    revision_format_num = None
 
145
    support_altered_by_hack = False
 
146
 
 
147
    def _unpack_entry(self, elt):
 
148
        kind = elt.tag
 
149
        if not kind in self.supported_kinds:
 
150
            raise AssertionError('unsupported entry kind %s' % kind)
 
151
        if kind == 'tree-reference':
 
152
            file_id = elt.attrib['file_id']
 
153
            name = elt.attrib['name']
 
154
            parent_id = elt.attrib['parent_id']
 
155
            revision = elt.get('revision')
 
156
            reference_revision = elt.get('reference_revision')
 
157
            return inventory.TreeReference(file_id, name, parent_id, revision,
 
158
                                           reference_revision)
 
159
        else:
 
160
            return xml6.Serializer_v6._unpack_entry(self, elt)
 
161
 
 
162
    def __init__(self, node_size, search_key_name):
 
163
        self.maximum_size = node_size
 
164
        self.search_key_name = search_key_name
 
165
 
 
166
 
 
167
class CHKSerializer(xml5.Serializer_v5):
 
168
    """A CHKInventory based serializer with 'plain' behaviour."""
 
169
 
 
170
    format_num = '9'
 
171
    revision_format_num = None
 
172
    support_altered_by_hack = False
 
173
 
 
174
    def __init__(self, node_size, search_key_name):
 
175
        self.maximum_size = node_size
 
176
        self.search_key_name = search_key_name
 
177
 
 
178
 
 
179
chk_serializer_255_bigpage = CHKSerializer(65536, 'hash-255-way')
 
180
 
 
181
 
 
182
class CHKBEncodeSerializer(BEncodeRevisionSerializer1, CHKSerializer):
 
183
    """A CHKInventory and BEncode based serializer with 'plain' behaviour."""
 
184
 
 
185
    format_num = '10'
 
186
 
 
187
 
 
188
chk_bencode_serializer = CHKBEncodeSerializer(65536, 'hash-255-way')