/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: Robert Collins
  • Date: 2007-07-15 15:40:37 UTC
  • mto: (2592.3.33 repository)
  • mto: This revision was merged to the branch mainline in revision 2624.
  • Revision ID: robertc@robertcollins.net-20070715154037-3ar8g89decddc9su
Make GraphIndex accept nodes as key, value, references, so that the method
signature is closer to what a simple key->value index delivers. Also
change the behaviour when the reference list count is zero to accept
key, value as nodes, and emit key, value to make it identical in that case
to a simple key->value index. This may not be a good idea, but for now it
seems ok.

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