/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_map_pyx.pyx

  • 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) 2009 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
 
 
18
#python2.4 support
 
19
cdef extern from "python-compat.h":
 
20
    pass
 
21
 
 
22
cdef extern from *:
 
23
    ctypedef unsigned int size_t
 
24
    int memcmp(void *, void*, size_t)
 
25
    void memcpy(void *, void*, size_t)
 
26
    void *memchr(void *s, int c, size_t len)
 
27
    long strtol(char *, char **, int)
 
28
    void sprintf(char *, char *, ...)
 
29
 
 
30
cdef extern from "Python.h":
 
31
    ctypedef int Py_ssize_t # Required for older pyrex versions
 
32
    struct _PyObject:
 
33
        pass
 
34
    ctypedef _PyObject PyObject
 
35
    int PyTuple_CheckExact(object p)
 
36
    Py_ssize_t PyTuple_GET_SIZE(object t)
 
37
    int PyString_CheckExact(object)
 
38
    char *PyString_AS_STRING(object s)
 
39
    Py_ssize_t PyString_GET_SIZE(object)
 
40
 
 
41
    int PyDict_SetItem(object d, object k, object v) except -1
 
42
 
 
43
    object PyTuple_New(Py_ssize_t count)
 
44
    void PyTuple_SET_ITEM(object t, Py_ssize_t offset, object)
 
45
 
 
46
    void Py_INCREF(object)
 
47
 
 
48
    PyObject * PyTuple_GET_ITEM_ptr "PyTuple_GET_ITEM" (object t,
 
49
                                                        Py_ssize_t offset)
 
50
    int PyString_CheckExact_ptr "PyString_CheckExact" (PyObject *p)
 
51
    Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *s)
 
52
    char *PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *s)
 
53
    object PyString_FromStringAndSize(char*, Py_ssize_t)
 
54
 
 
55
cdef extern from "zlib.h":
 
56
    ctypedef unsigned long uLong
 
57
    ctypedef unsigned int uInt
 
58
    ctypedef unsigned char Bytef
 
59
 
 
60
    uLong crc32(uLong crc, Bytef *buf, uInt len)
 
61
 
 
62
 
 
63
_LeafNode = None
 
64
_InternalNode = None
 
65
_unknown = None
 
66
 
 
67
# We shouldn't just copy this from _dirstate_helpers_c
 
68
cdef void* _my_memrchr(void *s, int c, size_t n):
 
69
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
 
70
    cdef char *pos
 
71
    cdef char *start
 
72
 
 
73
    start = <char*>s
 
74
    pos = start + n - 1
 
75
    while pos >= start:
 
76
        if pos[0] == c:
 
77
            return <void*>pos
 
78
        pos = pos - 1
 
79
    return NULL
 
80
 
 
81
 
 
82
def _search_key_16(key):
 
83
    """See chk_map._search_key_16."""
 
84
    cdef Py_ssize_t num_bits
 
85
    cdef Py_ssize_t i, j
 
86
    cdef Py_ssize_t num_out_bytes
 
87
    cdef Bytef *c_bit
 
88
    cdef uLong c_len
 
89
    cdef uInt crc_val
 
90
    cdef Py_ssize_t out_off
 
91
    cdef char *c_out
 
92
    cdef PyObject *bit
 
93
 
 
94
    if not PyTuple_CheckExact(key):
 
95
        raise TypeError('key %r is not a tuple' % (key,))
 
96
    num_bits = PyTuple_GET_SIZE(key)
 
97
    # 4 bytes per crc32, and another 1 byte between bits
 
98
    num_out_bytes = (9 * num_bits) - 1
 
99
    out = PyString_FromStringAndSize(NULL, num_out_bytes)
 
100
    c_out = PyString_AS_STRING(out)
 
101
    for i from 0 <= i < num_bits:
 
102
        if i > 0:
 
103
            c_out[0] = c'\x00'
 
104
            c_out = c_out + 1
 
105
        # We use the _ptr variant, because GET_ITEM returns a borrowed
 
106
        # reference, and Pyrex assumes that returned 'object' are a new
 
107
        # reference
 
108
        bit = PyTuple_GET_ITEM_ptr(key, i)
 
109
        if not PyString_CheckExact_ptr(bit):
 
110
            raise TypeError('Bit %d of %r is not a string' % (i, key))
 
111
        c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)
 
112
        c_len = PyString_GET_SIZE_ptr(bit)
 
113
        crc_val = crc32(0, c_bit, c_len)
 
114
        # Hex(val) order
 
115
        sprintf(c_out, '%08X', crc_val)
 
116
        c_out = c_out + 8
 
117
    return out
 
118
 
 
119
 
 
120
def _search_key_255(key):
 
121
    """See chk_map._search_key_255."""
 
122
    cdef Py_ssize_t num_bits
 
123
    cdef Py_ssize_t i, j
 
124
    cdef Py_ssize_t num_out_bytes
 
125
    cdef Bytef *c_bit
 
126
    cdef uLong c_len
 
127
    cdef uInt crc_val
 
128
    cdef Py_ssize_t out_off
 
129
    cdef char *c_out
 
130
    cdef PyObject *bit
 
131
 
 
132
    if not PyTuple_CheckExact(key):
 
133
        raise TypeError('key %r is not a tuple' % (key,))
 
134
    num_bits = PyTuple_GET_SIZE(key)
 
135
    # 4 bytes per crc32, and another 1 byte between bits
 
136
    num_out_bytes = (5 * num_bits) - 1
 
137
    out = PyString_FromStringAndSize(NULL, num_out_bytes)
 
138
    c_out = PyString_AS_STRING(out)
 
139
    for i from 0 <= i < num_bits:
 
140
        if i > 0:
 
141
            c_out[0] = c'\x00'
 
142
            c_out = c_out + 1
 
143
        bit = PyTuple_GET_ITEM_ptr(key, i)
 
144
        if not PyString_CheckExact_ptr(bit):
 
145
            raise TypeError('Bit %d of %r is not a string: %r' % (i, key,
 
146
            <object>bit))
 
147
        c_bit = <Bytef *>PyString_AS_STRING_ptr(bit)
 
148
        c_len = PyString_GET_SIZE_ptr(bit)
 
149
        crc_val = crc32(0, c_bit, c_len)
 
150
        # MSB order
 
151
        c_out[0] = (crc_val >> 24) & 0xFF
 
152
        c_out[1] = (crc_val >> 16) & 0xFF
 
153
        c_out[2] = (crc_val >> 8) & 0xFF
 
154
        c_out[3] = (crc_val >> 0) & 0xFF
 
155
        for j from 0 <= j < 4:
 
156
            if c_out[j] == c'\n':
 
157
                c_out[j] = c'_'
 
158
        c_out = c_out + 4
 
159
    return out
 
160
 
 
161
 
 
162
cdef int _get_int_from_line(char **cur, char *end, char *message) except -1:
 
163
    """Read a positive integer from the data stream.
 
164
 
 
165
    :param cur: The start of the data, this will be moved to after the
 
166
        trailing newline when done.
 
167
    :param end: Do not parse any data past this byte.
 
168
    :return: The integer stored in those bytes
 
169
    """
 
170
    cdef int value
 
171
    cdef char *next_line, *next
 
172
 
 
173
    next_line = <char *>memchr(cur[0], c'\n', end - cur[0])
 
174
    if next_line == NULL:
 
175
        raise ValueError("Missing %s line\n" % message)
 
176
 
 
177
    value = strtol(cur[0], &next, 10)
 
178
    if next != next_line:
 
179
        raise ValueError("%s line not a proper int\n" % message)
 
180
    cur[0] = next_line + 1
 
181
    return value
 
182
 
 
183
 
 
184
def _deserialise_leaf_node(bytes, key, search_key_func=None):
 
185
    """Deserialise bytes, with key key, into a LeafNode.
 
186
 
 
187
    :param bytes: The bytes of the node.
 
188
    :param key: The key that the serialised node has.
 
189
    """
 
190
    cdef char *c_bytes, *cur, *next, *end
 
191
    cdef char *next_line
 
192
    cdef Py_ssize_t c_bytes_len, prefix_length, items_length
 
193
    cdef int maximum_size, width, length, i, prefix_tail_len
 
194
    cdef int num_value_lines, num_prefix_bits
 
195
    cdef char *prefix, *value_start, *prefix_tail
 
196
    cdef char *next_null, *last_null, *line_start
 
197
    cdef char *c_entry, *entry_start
 
198
 
 
199
    if _LeafNode is None:
 
200
        from bzrlib import chk_map
 
201
        _LeafNode = chk_map.LeafNode
 
202
        _InternalNode = chk_map.InternalNode
 
203
        _unknown = chk_map._unknown
 
204
 
 
205
    result = _LeafNode(search_key_func=search_key_func)
 
206
    # Splitlines can split on '\r' so don't use it, split('\n') adds an
 
207
    # extra '' if the bytes ends in a final newline.
 
208
    if not PyString_CheckExact(bytes):
 
209
        raise TypeError('bytes must be a plain string not %s' % (type(bytes),))
 
210
 
 
211
    c_bytes = PyString_AS_STRING(bytes)
 
212
    c_bytes_len = PyString_GET_SIZE(bytes)
 
213
 
 
214
    if c_bytes_len < 9 or memcmp(c_bytes, "chkleaf:\n", 9) != 0:
 
215
        raise ValueError("not a serialised leaf node: %r" % bytes)
 
216
    if c_bytes[c_bytes_len - 1] != c'\n':
 
217
        raise ValueError("bytes does not end in a newline")
 
218
 
 
219
    end = c_bytes + c_bytes_len
 
220
    cur = c_bytes + 9
 
221
    maximum_size = _get_int_from_line(&cur, end, "maximum_size")
 
222
    width = _get_int_from_line(&cur, end, "width")
 
223
    length = _get_int_from_line(&cur, end, "length")
 
224
 
 
225
    next_line = <char *>memchr(cur, c'\n', end - cur)
 
226
    if next_line == NULL:
 
227
        raise ValueError('Missing the prefix line\n')
 
228
    prefix = cur
 
229
    prefix_length = next_line - cur
 
230
    cur = next_line + 1
 
231
 
 
232
    prefix_bits = []
 
233
    prefix_tail = prefix
 
234
    num_prefix_bits = 0
 
235
    next_null = <char *>memchr(prefix, c'\0', prefix_length)
 
236
    while next_null != NULL:
 
237
        num_prefix_bits = num_prefix_bits + 1
 
238
        prefix_bits.append(
 
239
            PyString_FromStringAndSize(prefix_tail, next_null - prefix_tail))
 
240
        prefix_tail = next_null + 1
 
241
        next_null = <char *>memchr(prefix_tail, c'\0', next_line - prefix_tail)
 
242
    prefix_tail_len = next_line - prefix_tail
 
243
 
 
244
    if num_prefix_bits >= width:
 
245
        raise ValueError('Prefix has too many nulls versus width')
 
246
 
 
247
    items_length = end - cur
 
248
    items = {}
 
249
    while cur < end:
 
250
        line_start = cur
 
251
        next_line = <char *>memchr(cur, c'\n', end - cur)
 
252
        if next_line == NULL:
 
253
            raise ValueError('null line\n')
 
254
        last_null = <char *>_my_memrchr(cur, c'\0', next_line - cur)
 
255
        if last_null == NULL:
 
256
            raise ValueError('fail to find the num value lines null')
 
257
        next_null = last_null + 1 # move past NULL
 
258
        num_value_lines = _get_int_from_line(&next_null, next_line + 1,
 
259
                                             "num value lines")
 
260
        cur = next_line + 1
 
261
        value_start = cur
 
262
        # Walk num_value_lines forward
 
263
        for i from 0 <= i < num_value_lines:
 
264
            next_line = <char *>memchr(cur, c'\n', end - cur)
 
265
            if next_line == NULL:
 
266
                raise ValueError('missing trailing newline')
 
267
            cur = next_line + 1
 
268
        entry_bits = PyTuple_New(width)
 
269
        for i from 0 <= i < num_prefix_bits:
 
270
            entry = prefix_bits[i]
 
271
            # SET_ITEM 'steals' a reference
 
272
            Py_INCREF(entry)
 
273
            PyTuple_SET_ITEM(entry_bits, i, entry)
 
274
        value = PyString_FromStringAndSize(value_start, next_line - value_start)
 
275
        # The next entry bit needs the 'tail' from the prefix, and first part
 
276
        # of the line
 
277
        entry_start = line_start
 
278
        next_null = <char *>memchr(entry_start, c'\0',
 
279
                                   last_null - entry_start + 1)
 
280
        if next_null == NULL:
 
281
            raise ValueError('bad no null, bad')
 
282
        entry = PyString_FromStringAndSize(NULL,
 
283
                    prefix_tail_len + next_null - line_start)
 
284
        c_entry = PyString_AS_STRING(entry)
 
285
        if prefix_tail_len > 0:
 
286
            memcpy(c_entry, prefix_tail, prefix_tail_len)
 
287
        if next_null - line_start > 0:
 
288
            memcpy(c_entry + prefix_tail_len, line_start, next_null - line_start)
 
289
        Py_INCREF(entry)
 
290
        i = num_prefix_bits
 
291
        PyTuple_SET_ITEM(entry_bits, i, entry)
 
292
        while next_null != last_null: # We have remaining bits
 
293
            i = i + 1
 
294
            if i > width:
 
295
                raise ValueError("Too many bits for entry")
 
296
            entry_start = next_null + 1
 
297
            next_null = <char *>memchr(entry_start, c'\0',
 
298
                                       last_null - entry_start + 1)
 
299
            if next_null == NULL:
 
300
                raise ValueError('bad no null')
 
301
            entry = PyString_FromStringAndSize(entry_start,
 
302
                                               next_null - entry_start)
 
303
            Py_INCREF(entry)
 
304
            PyTuple_SET_ITEM(entry_bits, i, entry)
 
305
        if len(entry_bits) != width:
 
306
            raise AssertionError(
 
307
                'Incorrect number of elements (%d vs %d)'
 
308
                % (len(entry_bits)+1, width + 1))
 
309
        PyDict_SetItem(items, entry_bits, value)
 
310
    if len(items) != length:
 
311
        raise ValueError("item count (%d) mismatch for key %s,"
 
312
                         " bytes %r" % (length, entry_bits, bytes))
 
313
    result._items = items
 
314
    result._len = length
 
315
    result._maximum_size = maximum_size
 
316
    result._key = key
 
317
    result._key_width = width
 
318
    result._raw_size = items_length + length * prefix_length
 
319
    if length == 0:
 
320
        result._search_prefix = None
 
321
        result._common_serialised_prefix = None
 
322
    else:
 
323
        result._search_prefix = _unknown
 
324
        result._common_serialised_prefix = PyString_FromStringAndSize(prefix,
 
325
                                                prefix_length)
 
326
    if c_bytes_len != result._current_size():
 
327
        raise AssertionError('_current_size computed incorrectly %d != %d',
 
328
            c_bytes_len, result._current_size())
 
329
    return result
 
330
 
 
331
 
 
332
def _deserialise_internal_node(bytes, key, search_key_func=None):
 
333
    cdef char *c_bytes, *cur, *next, *end
 
334
    cdef char *next_line
 
335
    cdef Py_ssize_t c_bytes_len, prefix_length
 
336
    cdef int maximum_size, width, length, i, prefix_tail_len
 
337
    cdef char *prefix, *line_prefix, *next_null, *c_item_prefix
 
338
 
 
339
    if _InternalNode is None:
 
340
        from bzrlib import chk_map
 
341
        _LeafNode = chk_map.LeafNode
 
342
        _InternalNode = chk_map.InternalNode
 
343
        _unknown = chk_map._unknown
 
344
    result = _InternalNode(search_key_func=search_key_func)
 
345
 
 
346
    if not PyString_CheckExact(bytes):
 
347
        raise TypeError('bytes must be a plain string not %s' % (type(bytes),))
 
348
 
 
349
    c_bytes = PyString_AS_STRING(bytes)
 
350
    c_bytes_len = PyString_GET_SIZE(bytes)
 
351
 
 
352
    if c_bytes_len < 9 or memcmp(c_bytes, "chknode:\n", 9) != 0:
 
353
        raise ValueError("not a serialised internal node: %r" % bytes)
 
354
    if c_bytes[c_bytes_len - 1] != c'\n':
 
355
        raise ValueError("bytes does not end in a newline")
 
356
 
 
357
    items = {}
 
358
    cur = c_bytes + 9
 
359
    end = c_bytes + c_bytes_len
 
360
    maximum_size = _get_int_from_line(&cur, end, "maximum_size")
 
361
    width = _get_int_from_line(&cur, end, "width")
 
362
    length = _get_int_from_line(&cur, end, "length")
 
363
 
 
364
    next_line = <char *>memchr(cur, c'\n', end - cur)
 
365
    if next_line == NULL:
 
366
        raise ValueError('Missing the prefix line\n')
 
367
    prefix = cur
 
368
    prefix_length = next_line - cur
 
369
    cur = next_line + 1
 
370
 
 
371
    while cur < end:
 
372
        # Find the null separator
 
373
        next_line = <char *>memchr(cur, c'\n', end - cur)
 
374
        if next_line == NULL:
 
375
            raise ValueError('missing trailing newline')
 
376
        next_null = <char *>_my_memrchr(cur, c'\0', next_line - cur)
 
377
        if next_null == NULL:
 
378
            raise ValueError('bad no null')
 
379
        item_prefix = PyString_FromStringAndSize(NULL,
 
380
            prefix_length + next_null - cur)
 
381
        c_item_prefix = PyString_AS_STRING(item_prefix)
 
382
        if prefix_length:
 
383
            memcpy(c_item_prefix, prefix, prefix_length)
 
384
        memcpy(c_item_prefix + prefix_length, cur, next_null - cur)
 
385
        flat_key = PyString_FromStringAndSize(next_null + 1,
 
386
                                              next_line - next_null - 1)
 
387
        PyDict_SetItem(items, item_prefix, (flat_key,))
 
388
        cur = next_line + 1
 
389
    assert len(items) > 0
 
390
    result._items = items
 
391
    result._len = length
 
392
    result._maximum_size = maximum_size
 
393
    result._key = key
 
394
    result._key_width = width
 
395
    # XXX: InternalNodes don't really care about their size, and this will
 
396
    #      change if we add prefix compression
 
397
    result._raw_size = None # len(bytes)
 
398
    result._node_width = len(item_prefix)
 
399
    result._search_prefix = PyString_FromStringAndSize(prefix, prefix_length)
 
400
    return result
 
401