~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_chk_map_pyx.pyx

Turn completion assertions into separate methods.

Many common assertions used to be expressed as arguments to the complete
method.  This makes the checks more explicit, and the code easier to read.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 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
 
    ctypedef struct PyObject:
33
 
        pass
34
 
    int PyTuple_CheckExact(object p)
35
 
    Py_ssize_t PyTuple_GET_SIZE(object t)
36
 
    int PyString_CheckExact(object)
37
 
    char *PyString_AS_STRING(object s)
38
 
    PyObject *PyString_FromStringAndSize_ptr "PyString_FromStringAndSize" (char *, Py_ssize_t)
39
 
    Py_ssize_t PyString_GET_SIZE(object)
40
 
    void PyString_InternInPlace(PyObject **)
41
 
    long PyInt_AS_LONG(object)
42
 
 
43
 
    int PyDict_SetItem(object d, object k, object v) except -1
44
 
 
45
 
    void Py_INCREF(object)
46
 
    void Py_DECREF_ptr "Py_DECREF" (PyObject *)
47
 
 
48
 
    object PyString_FromStringAndSize(char*, Py_ssize_t)
49
 
 
50
 
# cimport all of the definitions we will need to access
51
 
from _static_tuple_c cimport StaticTuple,\
52
 
    import_static_tuple_c, StaticTuple_New, \
53
 
    StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact, \
54
 
    StaticTuple_GET_SIZE
55
 
 
56
 
cdef object crc32
57
 
from zlib import crc32
58
 
 
59
 
 
60
 
# Set up the StaticTuple C_API functionality
61
 
import_static_tuple_c()
62
 
 
63
 
cdef object _LeafNode
64
 
_LeafNode = None
65
 
cdef object _InternalNode
66
 
_InternalNode = None
67
 
cdef object _unknown
68
 
_unknown = None
69
 
 
70
 
# We shouldn't just copy this from _dirstate_helpers_pyx
71
 
cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
72
 
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
73
 
    cdef char *pos
74
 
    cdef char *start
75
 
 
76
 
    start = <char*>s
77
 
    pos = start + n - 1
78
 
    while pos >= start:
79
 
        if pos[0] == c:
80
 
            return <void*>pos
81
 
        pos = pos - 1
82
 
    return NULL
83
 
 
84
 
 
85
 
cdef object safe_interned_string_from_size(char *s, Py_ssize_t size):
86
 
    cdef PyObject *py_str
87
 
    if size < 0:
88
 
        raise AssertionError(
89
 
            'tried to create a string with an invalid size: %d @0x%x'
90
 
            % (size, <int>s))
91
 
    py_str = PyString_FromStringAndSize_ptr(s, size)
92
 
    PyString_InternInPlace(&py_str)
93
 
    result = <object>py_str
94
 
    # Casting a PyObject* to an <object> triggers an INCREF from Pyrex, so we
95
 
    # DECREF it to avoid geting immortal strings
96
 
    Py_DECREF_ptr(py_str)
97
 
    return result
98
 
 
99
 
 
100
 
def _search_key_16(key):
101
 
    """See chk_map._search_key_16."""
102
 
    cdef Py_ssize_t num_bits
103
 
    cdef Py_ssize_t i, j
104
 
    cdef Py_ssize_t num_out_bytes
105
 
    cdef unsigned long crc_val
106
 
    cdef Py_ssize_t out_off
107
 
    cdef char *c_out
108
 
 
109
 
    num_bits = len(key)
110
 
    # 4 bytes per crc32, and another 1 byte between bits
111
 
    num_out_bytes = (9 * num_bits) - 1
112
 
    out = PyString_FromStringAndSize(NULL, num_out_bytes)
113
 
    c_out = PyString_AS_STRING(out)
114
 
    for i from 0 <= i < num_bits:
115
 
        if i > 0:
116
 
            c_out[0] = c'\x00'
117
 
            c_out = c_out + 1
118
 
        crc_val = PyInt_AS_LONG(crc32(key[i]))
119
 
        # Hex(val) order
120
 
        sprintf(c_out, '%08X', crc_val)
121
 
        c_out = c_out + 8
122
 
    return out
123
 
 
124
 
 
125
 
def _search_key_255(key):
126
 
    """See chk_map._search_key_255."""
127
 
    cdef Py_ssize_t num_bits
128
 
    cdef Py_ssize_t i, j
129
 
    cdef Py_ssize_t num_out_bytes
130
 
    cdef unsigned long crc_val
131
 
    cdef Py_ssize_t out_off
132
 
    cdef char *c_out
133
 
 
134
 
    num_bits = len(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
 
        crc_val = PyInt_AS_LONG(crc32(key[i]))
144
 
        # MSB order
145
 
        c_out[0] = (crc_val >> 24) & 0xFF
146
 
        c_out[1] = (crc_val >> 16) & 0xFF
147
 
        c_out[2] = (crc_val >> 8) & 0xFF
148
 
        c_out[3] = (crc_val >> 0) & 0xFF
149
 
        for j from 0 <= j < 4:
150
 
            if c_out[j] == c'\n':
151
 
                c_out[j] = c'_'
152
 
        c_out = c_out + 4
153
 
    return out
154
 
 
155
 
 
156
 
cdef int _get_int_from_line(char **cur, char *end, char *message) except -1:
157
 
    """Read a positive integer from the data stream.
158
 
 
159
 
    :param cur: The start of the data, this will be moved to after the
160
 
        trailing newline when done.
161
 
    :param end: Do not parse any data past this byte.
162
 
    :return: The integer stored in those bytes
163
 
    """
164
 
    cdef int value
165
 
    cdef char *next_line, *next
166
 
 
167
 
    next_line = <char *>memchr(cur[0], c'\n', end - cur[0])
168
 
    if next_line == NULL:
169
 
        raise ValueError("Missing %s line\n" % message)
170
 
 
171
 
    value = strtol(cur[0], &next, 10)
172
 
    if next != next_line:
173
 
        raise ValueError("%s line not a proper int\n" % message)
174
 
    cur[0] = next_line + 1
175
 
    return value
176
 
 
177
 
 
178
 
cdef _import_globals():
179
 
    """Set the global attributes. Done lazy to avoid recursive import loops."""
180
 
    global _LeafNode, _InternalNode, _unknown
181
 
 
182
 
    from bzrlib import chk_map
183
 
    _LeafNode = chk_map.LeafNode
184
 
    _InternalNode = chk_map.InternalNode
185
 
    _unknown = chk_map._unknown
186
 
 
187
 
 
188
 
def _deserialise_leaf_node(bytes, key, search_key_func=None):
189
 
    """Deserialise bytes, with key key, into a LeafNode.
190
 
 
191
 
    :param bytes: The bytes of the node.
192
 
    :param key: The key that the serialised node has.
193
 
    """
194
 
    cdef char *c_bytes, *cur, *next, *end
195
 
    cdef char *next_line
196
 
    cdef Py_ssize_t c_bytes_len, prefix_length, items_length
197
 
    cdef int maximum_size, width, length, i, prefix_tail_len
198
 
    cdef int num_value_lines, num_prefix_bits
199
 
    cdef char *prefix, *value_start, *prefix_tail
200
 
    cdef char *next_null, *last_null, *line_start
201
 
    cdef char *c_entry, *entry_start
202
 
    cdef StaticTuple entry_bits
203
 
 
204
 
    if _LeafNode is None:
205
 
        _import_globals()
206
 
 
207
 
    result = _LeafNode(search_key_func=search_key_func)
208
 
    # Splitlines can split on '\r' so don't use it, split('\n') adds an
209
 
    # extra '' if the bytes ends in a final newline.
210
 
    if not PyString_CheckExact(bytes):
211
 
        raise TypeError('bytes must be a plain string not %s' % (type(bytes),))
212
 
 
213
 
    c_bytes = PyString_AS_STRING(bytes)
214
 
    c_bytes_len = PyString_GET_SIZE(bytes)
215
 
 
216
 
    if c_bytes_len < 9 or memcmp(c_bytes, "chkleaf:\n", 9) != 0:
217
 
        raise ValueError("not a serialised leaf node: %r" % bytes)
218
 
    if c_bytes[c_bytes_len - 1] != c'\n':
219
 
        raise ValueError("bytes does not end in a newline")
220
 
 
221
 
    end = c_bytes + c_bytes_len
222
 
    cur = c_bytes + 9
223
 
    maximum_size = _get_int_from_line(&cur, end, "maximum_size")
224
 
    width = _get_int_from_line(&cur, end, "width")
225
 
    length = _get_int_from_line(&cur, end, "length")
226
 
 
227
 
    next_line = <char *>memchr(cur, c'\n', end - cur)
228
 
    if next_line == NULL:
229
 
        raise ValueError('Missing the prefix line\n')
230
 
    prefix = cur
231
 
    prefix_length = next_line - cur
232
 
    cur = next_line + 1
233
 
 
234
 
    prefix_bits = []
235
 
    prefix_tail = prefix
236
 
    num_prefix_bits = 0
237
 
    next_null = <char *>memchr(prefix, c'\0', prefix_length)
238
 
    while next_null != NULL:
239
 
        num_prefix_bits = num_prefix_bits + 1
240
 
        prefix_bits.append(
241
 
            PyString_FromStringAndSize(prefix_tail, next_null - prefix_tail))
242
 
        prefix_tail = next_null + 1
243
 
        next_null = <char *>memchr(prefix_tail, c'\0', next_line - prefix_tail)
244
 
    prefix_tail_len = next_line - prefix_tail
245
 
 
246
 
    if num_prefix_bits >= width:
247
 
        raise ValueError('Prefix has too many nulls versus width')
248
 
 
249
 
    items_length = end - cur
250
 
    items = {}
251
 
    while cur < end:
252
 
        line_start = cur
253
 
        next_line = <char *>memchr(cur, c'\n', end - cur)
254
 
        if next_line == NULL:
255
 
            raise ValueError('null line\n')
256
 
        last_null = <char *>_my_memrchr(cur, c'\0', next_line - cur)
257
 
        if last_null == NULL:
258
 
            raise ValueError('fail to find the num value lines null')
259
 
        next_null = last_null + 1 # move past NULL
260
 
        num_value_lines = _get_int_from_line(&next_null, next_line + 1,
261
 
                                             "num value lines")
262
 
        cur = next_line + 1
263
 
        value_start = cur
264
 
        # Walk num_value_lines forward
265
 
        for i from 0 <= i < num_value_lines:
266
 
            next_line = <char *>memchr(cur, c'\n', end - cur)
267
 
            if next_line == NULL:
268
 
                raise ValueError('missing trailing newline')
269
 
            cur = next_line + 1
270
 
        entry_bits = StaticTuple_New(width)
271
 
        for i from 0 <= i < num_prefix_bits:
272
 
            # TODO: Use PyList_GetItem, or turn prefix_bits into a
273
 
            #       tuple/StaticTuple
274
 
            entry = prefix_bits[i]
275
 
            # SET_ITEM 'steals' a reference
276
 
            Py_INCREF(entry)
277
 
            StaticTuple_SET_ITEM(entry_bits, i, entry)
278
 
        value = PyString_FromStringAndSize(value_start, next_line - value_start)
279
 
        # The next entry bit needs the 'tail' from the prefix, and first part
280
 
        # of the line
281
 
        entry_start = line_start
282
 
        next_null = <char *>memchr(entry_start, c'\0',
283
 
                                   last_null - entry_start + 1)
284
 
        if next_null == NULL:
285
 
            raise ValueError('bad no null, bad')
286
 
        entry = PyString_FromStringAndSize(NULL,
287
 
                    prefix_tail_len + next_null - line_start)
288
 
        c_entry = PyString_AS_STRING(entry)
289
 
        if prefix_tail_len > 0:
290
 
            memcpy(c_entry, prefix_tail, prefix_tail_len)
291
 
        if next_null - line_start > 0:
292
 
            memcpy(c_entry + prefix_tail_len, line_start, next_null - line_start)
293
 
        Py_INCREF(entry)
294
 
        i = num_prefix_bits
295
 
        StaticTuple_SET_ITEM(entry_bits, i, entry)
296
 
        while next_null != last_null: # We have remaining bits
297
 
            i = i + 1
298
 
            if i > width:
299
 
                raise ValueError("Too many bits for entry")
300
 
            entry_start = next_null + 1
301
 
            next_null = <char *>memchr(entry_start, c'\0',
302
 
                                       last_null - entry_start + 1)
303
 
            if next_null == NULL:
304
 
                raise ValueError('bad no null')
305
 
            entry = PyString_FromStringAndSize(entry_start,
306
 
                                               next_null - entry_start)
307
 
            Py_INCREF(entry)
308
 
            StaticTuple_SET_ITEM(entry_bits, i, entry)
309
 
        if StaticTuple_GET_SIZE(entry_bits) != width:
310
 
            raise AssertionError(
311
 
                'Incorrect number of elements (%d vs %d)'
312
 
                % (len(entry_bits)+1, width + 1))
313
 
        entry_bits = StaticTuple_Intern(entry_bits)
314
 
        PyDict_SetItem(items, entry_bits, value)
315
 
    if len(items) != length:
316
 
        raise ValueError("item count (%d) mismatch for key %s,"
317
 
                         " bytes %r" % (length, entry_bits, bytes))
318
 
    result._items = items
319
 
    result._len = length
320
 
    result._maximum_size = maximum_size
321
 
    result._key = key
322
 
    result._key_width = width
323
 
    result._raw_size = items_length + length * prefix_length
324
 
    if length == 0:
325
 
        result._search_prefix = None
326
 
        result._common_serialised_prefix = None
327
 
    else:
328
 
        result._search_prefix = _unknown
329
 
        result._common_serialised_prefix = PyString_FromStringAndSize(prefix,
330
 
                                                prefix_length)
331
 
    if c_bytes_len != result._current_size():
332
 
        raise AssertionError('_current_size computed incorrectly %d != %d',
333
 
            c_bytes_len, result._current_size())
334
 
    return result
335
 
 
336
 
 
337
 
def _deserialise_internal_node(bytes, key, search_key_func=None):
338
 
    cdef char *c_bytes, *cur, *next, *end
339
 
    cdef char *next_line
340
 
    cdef Py_ssize_t c_bytes_len, prefix_length
341
 
    cdef int maximum_size, width, length, i, prefix_tail_len
342
 
    cdef char *prefix, *line_prefix, *next_null, *c_item_prefix
343
 
 
344
 
    if _InternalNode is None:
345
 
        _import_globals()
346
 
    result = _InternalNode(search_key_func=search_key_func)
347
 
 
348
 
    if not StaticTuple_CheckExact(key):
349
 
        raise TypeError('key %r is not a StaticTuple' % (key,))
350
 
    if not PyString_CheckExact(bytes):
351
 
        raise TypeError('bytes must be a plain string not %s' % (type(bytes),))
352
 
 
353
 
    c_bytes = PyString_AS_STRING(bytes)
354
 
    c_bytes_len = PyString_GET_SIZE(bytes)
355
 
 
356
 
    if c_bytes_len < 9 or memcmp(c_bytes, "chknode:\n", 9) != 0:
357
 
        raise ValueError("not a serialised internal node: %r" % bytes)
358
 
    if c_bytes[c_bytes_len - 1] != c'\n':
359
 
        raise ValueError("bytes does not end in a newline")
360
 
 
361
 
    items = {}
362
 
    cur = c_bytes + 9
363
 
    end = c_bytes + c_bytes_len
364
 
    maximum_size = _get_int_from_line(&cur, end, "maximum_size")
365
 
    width = _get_int_from_line(&cur, end, "width")
366
 
    length = _get_int_from_line(&cur, end, "length")
367
 
 
368
 
    next_line = <char *>memchr(cur, c'\n', end - cur)
369
 
    if next_line == NULL:
370
 
        raise ValueError('Missing the prefix line\n')
371
 
    prefix = cur
372
 
    prefix_length = next_line - cur
373
 
    cur = next_line + 1
374
 
 
375
 
    while cur < end:
376
 
        # Find the null separator
377
 
        next_line = <char *>memchr(cur, c'\n', end - cur)
378
 
        if next_line == NULL:
379
 
            raise ValueError('missing trailing newline')
380
 
        next_null = <char *>_my_memrchr(cur, c'\0', next_line - cur)
381
 
        if next_null == NULL:
382
 
            raise ValueError('bad no null')
383
 
        item_prefix = PyString_FromStringAndSize(NULL,
384
 
            prefix_length + next_null - cur)
385
 
        c_item_prefix = PyString_AS_STRING(item_prefix)
386
 
        if prefix_length:
387
 
            memcpy(c_item_prefix, prefix, prefix_length)
388
 
        memcpy(c_item_prefix + prefix_length, cur, next_null - cur)
389
 
        flat_key = PyString_FromStringAndSize(next_null + 1,
390
 
                                              next_line - next_null - 1)
391
 
        flat_key = StaticTuple(flat_key).intern()
392
 
        PyDict_SetItem(items, item_prefix, flat_key)
393
 
        cur = next_line + 1
394
 
    assert len(items) > 0
395
 
    result._items = items
396
 
    result._len = length
397
 
    result._maximum_size = maximum_size
398
 
    result._key = key
399
 
    result._key_width = width
400
 
    # XXX: InternalNodes don't really care about their size, and this will
401
 
    #      change if we add prefix compression
402
 
    result._raw_size = None # len(bytes)
403
 
    result._node_width = len(item_prefix)
404
 
    result._search_prefix = PyString_FromStringAndSize(prefix, prefix_length)
405
 
    return result
406
 
 
407
 
 
408
 
def _bytes_to_text_key(bytes):
409
 
    """Take a CHKInventory value string and return a (file_id, rev_id) tuple"""
410
 
    cdef StaticTuple key
411
 
    cdef char *byte_str, *cur_end, *file_id_str, *byte_end
412
 
    cdef char *revision_str
413
 
    cdef Py_ssize_t byte_size, pos, file_id_len
414
 
 
415
 
    if not PyString_CheckExact(bytes):
416
 
        raise TypeError('bytes must be a string, got %r' % (type(bytes),))
417
 
    byte_str = PyString_AS_STRING(bytes)
418
 
    byte_size = PyString_GET_SIZE(bytes)
419
 
    byte_end = byte_str + byte_size
420
 
    cur_end = <char*>memchr(byte_str, c':', byte_size)
421
 
    if cur_end == NULL:
422
 
        raise ValueError('No kind section found.')
423
 
    if cur_end[1] != c' ':
424
 
        raise ValueError(
425
 
            'Kind section should end with ": ", got %r' % str(cur_end[:2],))
426
 
    file_id_str = cur_end + 2
427
 
    # file_id is now the data up until the next newline
428
 
    cur_end = <char*>memchr(file_id_str, c'\n', byte_end - file_id_str)
429
 
    if cur_end == NULL:
430
 
        raise ValueError('no newline after file-id')
431
 
    file_id = safe_interned_string_from_size(file_id_str,
432
 
                                             cur_end - file_id_str)
433
 
    # this is the end of the parent_str
434
 
    cur_end = <char*>memchr(cur_end + 1, c'\n', byte_end - cur_end - 1)
435
 
    if cur_end == NULL:
436
 
        raise ValueError('no newline after parent_str')
437
 
    # end of the name str
438
 
    cur_end = <char*>memchr(cur_end + 1, c'\n', byte_end - cur_end - 1)
439
 
    if cur_end == NULL:
440
 
        raise ValueError('no newline after name str')
441
 
    # the next section is the revision info
442
 
    revision_str = cur_end + 1
443
 
    cur_end = <char*>memchr(cur_end + 1, c'\n', byte_end - cur_end - 1)
444
 
    if cur_end == NULL:
445
 
        # This is probably a dir: entry, which has revision as the last item
446
 
        cur_end = byte_end
447
 
    revision = safe_interned_string_from_size(revision_str,
448
 
        cur_end - revision_str)
449
 
    key = StaticTuple_New(2)
450
 
    Py_INCREF(file_id)
451
 
    StaticTuple_SET_ITEM(key, 0, file_id) 
452
 
    Py_INCREF(revision)
453
 
    StaticTuple_SET_ITEM(key, 1, revision) 
454
 
    return StaticTuple_Intern(key)