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

  • Committer: John Arbash Meinel
  • Date: 2009-06-17 19:08:25 UTC
  • mto: This revision was merged to the branch mainline in revision 4460.
  • Revision ID: john@arbash-meinel.com-20090617190825-ktfk82li57rf2im6
It seems that fetch() no longer returns the number of revisions fetched.
It still does for *some* InterRepository fetch paths, but the generic one does not.
It is also not easy to get it to, since the Source and Sink are the ones
that would know how many keys were transmitted, and they are potentially 'remote'
objects.

This was also only tested to occur as a by-product in a random 'test_commit' test.
I assume if we really wanted the assurance, we would have a per_repo or interrepo
test for it.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
38
38
    Py_ssize_t PyString_Size(object p)
39
39
    Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *)
40
40
    char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *)
41
 
    char * PyString_AS_STRING(object)
42
 
    Py_ssize_t PyString_GET_SIZE(object)
43
41
    int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len)
44
42
    void PyString_InternInPlace(PyObject **)
45
43
    int PyTuple_CheckExact(object t)
46
 
    object PyTuple_New(Py_ssize_t n_entries)
47
 
    void PyTuple_SET_ITEM(object, Py_ssize_t offset, object) # steals the ref
48
44
    Py_ssize_t PyTuple_GET_SIZE(object t)
49
45
    PyObject *PyTuple_GET_ITEM_ptr_object "PyTuple_GET_ITEM" (object tpl, int index)
50
 
    void Py_INCREF(object)
51
46
    void Py_DECREF_ptr "Py_DECREF" (PyObject *)
52
47
 
53
48
cdef extern from "string.h":
57
52
    # void *memrchr(void *s, int c, size_t n)
58
53
    int strncmp(char *s1, char *s2, size_t n)
59
54
 
60
 
# It seems we need to import the definitions so that the pyrex compiler has
61
 
# local names to access them.
62
 
from _static_tuple_c cimport StaticTuple, \
63
 
    import_static_tuple_c, StaticTuple_New, \
64
 
    StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact
65
 
 
66
55
 
67
56
# TODO: Find some way to import this from _dirstate_helpers
68
 
cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
 
57
cdef void* _my_memrchr(void *s, int c, size_t n):
69
58
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
70
59
    # It is not present in any win32 standard library
71
60
    cdef char *pos
79
68
        pos = pos - 1
80
69
    return NULL
81
70
 
82
 
 
83
71
# TODO: Import this from _dirstate_helpers when it is merged
84
72
cdef object safe_string_from_size(char *s, Py_ssize_t size):
85
73
    if size < 0:
103
91
    Py_DECREF_ptr(py_str)
104
92
    return result
105
93
 
106
 
from bzrlib import _static_tuple_c
107
 
# This sets up the StaticTuple C_API functionality
108
 
import_static_tuple_c()
109
 
 
110
94
 
111
95
cdef class BTreeLeafParser:
112
96
    """Parse the leaf nodes of a BTree index.
146
130
        self._cur_str = NULL
147
131
        self._end_str = NULL
148
132
        self._header_found = 0
149
 
        # keys are tuples
150
133
 
151
134
    cdef extract_key(self, char * last):
152
135
        """Extract a key.
156
139
        """
157
140
        cdef char *temp_ptr
158
141
        cdef int loop_counter
159
 
        cdef StaticTuple key
160
 
 
161
 
        key = StaticTuple_New(self.key_length)
162
 
        for loop_counter from 0 <= loop_counter < self.key_length:
 
142
        # keys are tuples
 
143
        loop_counter = 0
 
144
        key_segments = []
 
145
        while loop_counter < self.key_length:
 
146
            loop_counter = loop_counter + 1
163
147
            # grab a key segment
164
148
            temp_ptr = <char*>memchr(self._start, c'\0', last - self._start)
165
149
            if temp_ptr == NULL:
166
 
                if loop_counter + 1 == self.key_length:
 
150
                if loop_counter == self.key_length:
167
151
                    # capture to last
168
152
                    temp_ptr = last
169
153
                else:
173
157
                                                   last - self._start)))
174
158
                    raise AssertionError(failure_string)
175
159
            # capture the key string
176
 
            if (self.key_length == 1
177
 
                and (temp_ptr - self._start) == 45
178
 
                and strncmp(self._start, 'sha1:', 5) == 0):
179
 
                key_element = safe_string_from_size(self._start,
180
 
                                                    temp_ptr - self._start)
181
 
            else:
182
 
                key_element = safe_interned_string_from_size(self._start,
 
160
            # TODO: Consider using PyIntern_FromString, the only caveat is that
 
161
            # it assumes a NULL-terminated string, so we have to check if
 
162
            # temp_ptr[0] == c'\0' or some other char.
 
163
            key_element = safe_interned_string_from_size(self._start,
183
164
                                                         temp_ptr - self._start)
184
165
            # advance our pointer
185
166
            self._start = temp_ptr + 1
186
 
            Py_INCREF(key_element)
187
 
            StaticTuple_SET_ITEM(key, loop_counter, key_element)
188
 
        key = StaticTuple_Intern(key)
189
 
        return key
 
167
            PyList_Append(key_segments, key_element)
 
168
        return tuple(key_segments)
190
169
 
191
170
    cdef int process_line(self) except -1:
192
171
        """Process a line in the bytes."""
195
174
        cdef char *ref_ptr
196
175
        cdef char *next_start
197
176
        cdef int loop_counter
198
 
        cdef Py_ssize_t str_len
199
177
 
200
178
        self._start = self._cur_str
201
179
        # Find the next newline
208
186
            # And the next string is right after it
209
187
            self._cur_str = last + 1
210
188
            # The last character is right before the '\n'
 
189
            last = last
211
190
 
212
191
        if last == self._start:
213
192
            # parsed it all.
214
193
            return 0
215
194
        if last < self._start:
216
195
            # Unexpected error condition - fail
217
 
            raise AssertionError("last < self._start")
 
196
            return -1
218
197
        if 0 == self._header_found:
219
198
            # The first line in a leaf node is the header "type=leaf\n"
220
199
            if strncmp("type=leaf", self._start, last - self._start) == 0:
223
202
            else:
224
203
                raise AssertionError('Node did not start with "type=leaf": %r'
225
204
                    % (safe_string_from_size(self._start, last - self._start)))
 
205
                return -1
226
206
 
227
207
        key = self.extract_key(last)
228
208
        # find the value area
229
209
        temp_ptr = <char*>_my_memrchr(self._start, c'\0', last - self._start)
230
210
        if temp_ptr == NULL:
231
211
            # Invalid line
232
 
            raise AssertionError("Failed to find the value area")
 
212
            return -1
233
213
        else:
234
 
            # Because of how conversions were done, we ended up with *lots* of
235
 
            # values that are identical. These are all of the 0-length nodes
236
 
            # that are referred to by the TREE_ROOT (and likely some other
237
 
            # directory nodes.) For example, bzr has 25k references to
238
 
            # something like '12607215 328306 0 0', which ends up consuming 1MB
239
 
            # of memory, just for those strings.
240
 
            str_len = last - temp_ptr - 1
241
 
            if (str_len > 4
242
 
                and strncmp(" 0 0", last - 4, 4) == 0):
243
 
                # This drops peak mem for bzr.dev from 87.4MB => 86.2MB
244
 
                # For Launchpad 236MB => 232MB
245
 
                value = safe_interned_string_from_size(temp_ptr + 1, str_len)
246
 
            else:
247
 
                value = safe_string_from_size(temp_ptr + 1, str_len)
 
214
            # capture the value string
 
215
            value = safe_string_from_size(temp_ptr + 1, last - temp_ptr - 1)
248
216
            # shrink the references end point
249
217
            last = temp_ptr
250
 
 
251
218
        if self.ref_list_length:
252
 
            ref_lists = StaticTuple_New(self.ref_list_length)
 
219
            ref_lists = []
253
220
            loop_counter = 0
254
221
            while loop_counter < self.ref_list_length:
255
222
                ref_list = []
256
223
                # extract a reference list
257
224
                loop_counter = loop_counter + 1
258
225
                if last < self._start:
259
 
                    raise AssertionError("last < self._start")
 
226
                    return -1
260
227
                # find the next reference list end point:
261
228
                temp_ptr = <char*>memchr(self._start, c'\t', last - self._start)
262
229
                if temp_ptr == NULL:
263
230
                    # Only valid for the last list
264
231
                    if loop_counter != self.ref_list_length:
265
232
                        # Invalid line
266
 
                        raise AssertionError(
267
 
                            "invalid key, loop_counter != self.ref_list_length")
 
233
                        return -1
 
234
                        raise AssertionError("invalid key")
268
235
                    else:
269
236
                        # scan to the end of the ref list area
270
237
                        ref_ptr = last
281
248
                    if temp_ptr == NULL:
282
249
                        # key runs to the end
283
250
                        temp_ptr = ref_ptr
284
 
 
285
251
                    PyList_Append(ref_list, self.extract_key(temp_ptr))
286
 
                ref_list = StaticTuple_Intern(StaticTuple(*ref_list))
287
 
                Py_INCREF(ref_list)
288
 
                StaticTuple_SET_ITEM(ref_lists, loop_counter - 1, ref_list)
 
252
                PyList_Append(ref_lists, tuple(ref_list))
289
253
                # prepare for the next reference list
290
254
                self._start = next_start
291
 
            node_value = StaticTuple(value, ref_lists)
 
255
            ref_lists = tuple(ref_lists)
 
256
            node_value = (value, ref_lists)
292
257
        else:
293
258
            if last != self._start:
294
259
                # unexpected reference data present
295
 
                raise AssertionError("unexpected reference data present")
296
 
            node_value = StaticTuple(value, StaticTuple())
297
 
        PyList_Append(self.keys, StaticTuple(key, node_value))
 
260
                return -1
 
261
            node_value = (value, ())
 
262
        PyList_Append(self.keys, (key, node_value))
298
263
        return 0
299
264
 
300
265
    def parse(self):
329
294
    cdef Py_ssize_t flat_len
330
295
    cdef Py_ssize_t key_len
331
296
    cdef Py_ssize_t node_len
 
297
    cdef PyObject * val
332
298
    cdef char * value
333
299
    cdef Py_ssize_t value_len
334
300
    cdef char * out
337
303
    cdef int first_ref_list
338
304
    cdef int first_reference
339
305
    cdef int i
 
306
    cdef PyObject *ref_bit
340
307
    cdef Py_ssize_t ref_bit_len
341
308
 
342
 
    if not PyTuple_CheckExact(node) and not StaticTuple_CheckExact(node):
343
 
        raise TypeError('We expected a tuple() or StaticTuple() for node not: %s'
 
309
    if not PyTuple_CheckExact(node):
 
310
        raise TypeError('We expected a tuple() for node not: %s'
344
311
            % type(node))
345
 
    node_len = len(node)
 
312
    node_len = PyTuple_GET_SIZE(node)
346
313
    have_reference_lists = reference_lists
347
314
    if have_reference_lists:
348
315
        if node_len != 4:
351
318
    elif node_len < 3:
352
319
        raise ValueError('Without ref_lists, we need at least 3 entries not: %s'
353
320
            % len(node))
354
 
    # TODO: We can probably do better than string.join(), namely
355
 
    #       when key has only 1 item, we can just grab that string
356
 
    #       And when there are 2 items, we could do a single malloc + len() + 1
357
 
    #       also, doing .join() requires a PyObject_GetAttrString call, which
358
 
    #       we could also avoid.
359
 
    # TODO: Note that pyrex 0.9.6 generates fairly crummy code here, using the
360
 
    #       python object interface, versus 0.9.8+ which uses a helper that
361
 
    #       checks if this supports the sequence interface.
362
 
    #       We *could* do more work on our own, and grab the actual items
363
 
    #       lists. For now, just ask people to use a better compiler. :)
364
 
    string_key = '\0'.join(node[1])
 
321
    # I don't expect that we can do faster than string.join()
 
322
    string_key = '\0'.join(<object>PyTuple_GET_ITEM_ptr_object(node, 1))
365
323
 
366
324
    # TODO: instead of using string joins, precompute the final string length,
367
325
    #       and then malloc a single string and copy everything in.
378
336
    refs_len = 0
379
337
    if have_reference_lists:
380
338
        # Figure out how many bytes it will take to store the references
381
 
        ref_lists = node[3]
 
339
        ref_lists = <object>PyTuple_GET_ITEM_ptr_object(node, 3)
382
340
        next_len = len(ref_lists) # TODO: use a Py function
383
341
        if next_len > 0:
384
342
            # If there are no nodes, we don't need to do any work
392
350
                    # references
393
351
                    refs_len = refs_len + (next_len - 1)
394
352
                    for reference in ref_list:
395
 
                        if (not PyTuple_CheckExact(reference)
396
 
                            and not StaticTuple_CheckExact(reference)):
 
353
                        if not PyTuple_CheckExact(reference):
397
354
                            raise TypeError(
398
355
                                'We expect references to be tuples not: %s'
399
356
                                % type(reference))
400
 
                        next_len = len(reference)
 
357
                        next_len = PyTuple_GET_SIZE(reference)
401
358
                        if next_len > 0:
402
359
                            # We will need (len - 1) '\x00' characters to
403
360
                            # separate the reference key
404
361
                            refs_len = refs_len + (next_len - 1)
405
 
                            for ref_bit in reference:
406
 
                                if not PyString_CheckExact(ref_bit):
 
362
                            for i from 0 <= i < next_len:
 
363
                                ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)
 
364
                                if not PyString_CheckExact_ptr(ref_bit):
407
365
                                    raise TypeError('We expect reference bits'
408
366
                                        ' to be strings not: %s'
409
367
                                        % type(<object>ref_bit))
410
 
                                refs_len = refs_len + PyString_GET_SIZE(ref_bit)
 
368
                                refs_len = refs_len + PyString_GET_SIZE_ptr(ref_bit)
411
369
 
412
370
    # So we have the (key NULL refs NULL value LF)
413
371
    key_len = PyString_Size(string_key)
414
 
    val = node[2]
415
 
    if not PyString_CheckExact(val):
 
372
    val = PyTuple_GET_ITEM_ptr_object(node, 2)
 
373
    if not PyString_CheckExact_ptr(val):
416
374
        raise TypeError('Expected a plain str for value not: %s'
417
 
                        % type(val))
418
 
    value = PyString_AS_STRING(val)
419
 
    value_len = PyString_GET_SIZE(val)
 
375
                        % type(<object>val))
 
376
    value = PyString_AS_STRING_ptr(val)
 
377
    value_len = PyString_GET_SIZE_ptr(val)
420
378
    flat_len = (key_len + 1 + refs_len + 1 + value_len + 1)
421
379
    line = PyString_FromStringAndSize(NULL, flat_len)
422
380
    # Get a pointer to the new buffer
438
396
                    out[0] = c'\r'
439
397
                    out = out + 1
440
398
                first_reference = 0
441
 
                next_len = len(reference)
 
399
                next_len = PyTuple_GET_SIZE(reference)
442
400
                for i from 0 <= i < next_len:
443
401
                    if i != 0:
444
402
                        out[0] = c'\x00'
445
403
                        out = out + 1
446
 
                    ref_bit = reference[i]
447
 
                    ref_bit_len = PyString_GET_SIZE(ref_bit)
448
 
                    memcpy(out, PyString_AS_STRING(ref_bit), ref_bit_len)
 
404
                    ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i)
 
405
                    ref_bit_len = PyString_GET_SIZE_ptr(ref_bit)
 
406
                    memcpy(out, PyString_AS_STRING_ptr(ref_bit), ref_bit_len)
449
407
                    out = out + ref_bit_len
450
408
    out[0] = c'\0'
451
409
    out = out  + 1