~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_dirstate_helpers_c.pyx

MergeĀ inĀ upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Helper functions for DirState.
18
 
 
19
 
This is the python implementation for DirState functions.
20
 
"""
21
 
 
22
 
from bzrlib import errors
23
 
from bzrlib.dirstate import DirState
24
 
 
25
 
 
26
 
# Give Pyrex some function definitions for it to understand.
27
 
# All of these are just hints to Pyrex, so that it can try to convert python
28
 
# objects into similar C objects. (such as PyInt => int).
29
 
# In anything defined 'cdef extern from XXX' the real C header will be
30
 
# imported, and the real definition will be used from there. So these are just
31
 
# hints, and do not need to match exactly to the C definitions.
32
 
 
33
 
cdef extern from *:
34
 
    ctypedef unsigned long size_t
35
 
 
36
 
cdef extern from "_dirstate_helpers_c.h":
37
 
    ctypedef int intptr_t
38
 
 
39
 
 
40
 
cdef extern from "stdlib.h":
41
 
    unsigned long int strtoul(char *nptr, char **endptr, int base)
42
 
 
43
 
 
44
 
# These functions allow us access to a bit of the 'bare metal' of python
45
 
# objects, rather than going through the object abstraction. (For example,
46
 
# PyList_Append, rather than getting the 'append' attribute of the object, and
47
 
# creating a tuple, and then using PyCallObject).
48
 
# Functions that return (or take) a void* are meant to grab a C PyObject*. This
49
 
# differs from the Pyrex 'object'. If you declare a variable as 'object' Pyrex
50
 
# will automatically Py_INCREF and Py_DECREF when appropriate. But for some
51
 
# inner loops, we don't need to do that at all, as the reference only lasts for
52
 
# a very short time.
53
 
cdef extern from "Python.h":
54
 
    ctypedef int Py_ssize_t
55
 
    int PyList_Append(object lst, object item) except -1
56
 
    void *PyList_GetItem_object_void "PyList_GET_ITEM" (object lst, int index)
57
 
    int PyList_CheckExact(object)
58
 
 
59
 
    void *PyTuple_GetItem_void_void "PyTuple_GET_ITEM" (void* tpl, int index)
60
 
 
61
 
    char *PyString_AsString(object p)
62
 
    char *PyString_AS_STRING_void "PyString_AS_STRING" (void *p)
63
 
    object PyString_FromString(char *)
64
 
    object PyString_FromStringAndSize(char *, Py_ssize_t)
65
 
    int PyString_Size(object p)
66
 
    int PyString_GET_SIZE_void "PyString_GET_SIZE" (void *p)
67
 
    int PyString_CheckExact(object p)
68
 
 
69
 
 
70
 
cdef extern from "string.h":
71
 
    int strncmp(char *s1, char *s2, int len)
72
 
    void *memchr(void *s, int c, size_t len)
73
 
    int memcmp(void *b1, void *b2, size_t len)
74
 
    # ??? memrchr is a GNU extension :(
75
 
    # void *memrchr(void *s, int c, size_t len)
76
 
 
77
 
 
78
 
cdef void* _my_memrchr(void *s, int c, size_t n):
79
 
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
80
 
    cdef char *pos
81
 
    cdef char *start
82
 
 
83
 
    start = <char*>s
84
 
    pos = start + n - 1
85
 
    while pos >= start:
86
 
        if pos[0] == c:
87
 
            return <void*>pos
88
 
        pos = pos - 1
89
 
    return NULL
90
 
 
91
 
 
92
 
def _py_memrchr(s, c):
93
 
    """Just to expose _my_memrchr for testing.
94
 
 
95
 
    :param s: The Python string to search
96
 
    :param c: The character to search for
97
 
    :return: The offset to the last instance of 'c' in s
98
 
    """
99
 
    cdef void *_s
100
 
    cdef void *found
101
 
    cdef int length
102
 
    cdef char *_c
103
 
 
104
 
    _s = PyString_AsString(s)
105
 
    length = PyString_Size(s)
106
 
 
107
 
    _c = PyString_AsString(c)
108
 
    assert PyString_Size(c) == 1,\
109
 
        'Must be a single character string, not %s' % (c,)
110
 
    found = _my_memrchr(_s, _c[0], length)
111
 
    if found == NULL:
112
 
        return None
113
 
    return <char*>found - <char*>_s
114
 
 
115
 
cdef object safe_string_from_size(char *s, Py_ssize_t size):
116
 
    if size < 0:
117
 
        raise AssertionError(
118
 
            'tried to create a string with an invalid size: %d @0x%x'
119
 
            % (size, <int>s))
120
 
    return PyString_FromStringAndSize(s, size)
121
 
 
122
 
 
123
 
cdef int _is_aligned(void *ptr):
124
 
    """Is this pointer aligned to an integer size offset?
125
 
 
126
 
    :return: 1 if this pointer is aligned, 0 otherwise.
127
 
    """
128
 
    return ((<intptr_t>ptr) & ((sizeof(int))-1)) == 0
129
 
 
130
 
 
131
 
cdef int _cmp_by_dirs(char *path1, int size1, char *path2, int size2):
132
 
    cdef unsigned char *cur1
133
 
    cdef unsigned char *cur2
134
 
    cdef unsigned char *end1
135
 
    cdef unsigned char *end2
136
 
    cdef int *cur_int1
137
 
    cdef int *cur_int2
138
 
    cdef int *end_int1
139
 
    cdef int *end_int2
140
 
 
141
 
    if path1 == path2 and size1 == size2:
142
 
        return 0
143
 
 
144
 
    end1 = <unsigned char*>path1+size1
145
 
    end2 = <unsigned char*>path2+size2
146
 
 
147
 
    # Use 32-bit comparisons for the matching portion of the string.
148
 
    # Almost all CPU's are faster at loading and comparing 32-bit integers,
149
 
    # than they are at 8-bit integers.
150
 
    # 99% of the time, these will be aligned, but in case they aren't just skip
151
 
    # this loop
152
 
    if _is_aligned(path1) and _is_aligned(path2):
153
 
        cur_int1 = <int*>path1
154
 
        cur_int2 = <int*>path2
155
 
        end_int1 = <int*>(path1 + size1 - (size1 % sizeof(int)))
156
 
        end_int2 = <int*>(path2 + size2 - (size2 % sizeof(int)))
157
 
 
158
 
        while cur_int1 < end_int1 and cur_int2 < end_int2:
159
 
            if cur_int1[0] != cur_int2[0]:
160
 
                break
161
 
            cur_int1 = cur_int1 + 1
162
 
            cur_int2 = cur_int2 + 1
163
 
 
164
 
        cur1 = <unsigned char*>cur_int1
165
 
        cur2 = <unsigned char*>cur_int2
166
 
    else:
167
 
        cur1 = <unsigned char*>path1
168
 
        cur2 = <unsigned char*>path2
169
 
 
170
 
    while cur1 < end1 and cur2 < end2:
171
 
        if cur1[0] == cur2[0]:
172
 
            # This character matches, just go to the next one
173
 
            cur1 = cur1 + 1
174
 
            cur2 = cur2 + 1
175
 
            continue
176
 
        # The current characters do not match
177
 
        if cur1[0] == c'/':
178
 
            return -1 # Reached the end of path1 segment first
179
 
        elif cur2[0] == c'/':
180
 
            return 1 # Reached the end of path2 segment first
181
 
        elif cur1[0] < cur2[0]:
182
 
            return -1
183
 
        else:
184
 
            return 1
185
 
 
186
 
    # We reached the end of at least one of the strings
187
 
    if cur1 < end1:
188
 
        return 1 # Not at the end of cur1, must be at the end of cur2
189
 
    if cur2 < end2:
190
 
        return -1 # At the end of cur1, but not at cur2
191
 
    # We reached the end of both strings
192
 
    return 0
193
 
 
194
 
 
195
 
def cmp_by_dirs_c(path1, path2):
196
 
    """Compare two paths directory by directory.
197
 
 
198
 
    This is equivalent to doing::
199
 
 
200
 
       cmp(path1.split('/'), path2.split('/'))
201
 
 
202
 
    The idea is that you should compare path components separately. This
203
 
    differs from plain ``cmp(path1, path2)`` for paths like ``'a-b'`` and
204
 
    ``a/b``. "a-b" comes after "a" but would come before "a/b" lexically.
205
 
 
206
 
    :param path1: first path
207
 
    :param path2: second path
208
 
    :return: negative number if ``path1`` comes first,
209
 
        0 if paths are equal,
210
 
        and positive number if ``path2`` sorts first
211
 
    """
212
 
    if not PyString_CheckExact(path1):
213
 
        raise TypeError("'path1' must be a plain string, not %s: %r"
214
 
                        % (type(path1), path1))
215
 
    if not PyString_CheckExact(path2):
216
 
        raise TypeError("'path2' must be a plain string, not %s: %r"
217
 
                        % (type(path2), path2))
218
 
    return _cmp_by_dirs(PyString_AsString(path1),
219
 
                        PyString_Size(path1),
220
 
                        PyString_AsString(path2),
221
 
                        PyString_Size(path2))
222
 
 
223
 
 
224
 
def _cmp_path_by_dirblock_c(path1, path2):
225
 
    """Compare two paths based on what directory they are in.
226
 
 
227
 
    This generates a sort order, such that all children of a directory are
228
 
    sorted together, and grandchildren are in the same order as the
229
 
    children appear. But all grandchildren come after all children.
230
 
 
231
 
    In other words, all entries in a directory are sorted together, and
232
 
    directorys are sorted in cmp_by_dirs order.
233
 
 
234
 
    :param path1: first path
235
 
    :param path2: the second path
236
 
    :return: negative number if ``path1`` comes first,
237
 
        0 if paths are equal
238
 
        and a positive number if ``path2`` sorts first
239
 
    """
240
 
    if not PyString_CheckExact(path1):
241
 
        raise TypeError("'path1' must be a plain string, not %s: %r"
242
 
                        % (type(path1), path1))
243
 
    if not PyString_CheckExact(path2):
244
 
        raise TypeError("'path2' must be a plain string, not %s: %r"
245
 
                        % (type(path2), path2))
246
 
    return _cmp_path_by_dirblock(PyString_AsString(path1),
247
 
                                 PyString_Size(path1),
248
 
                                 PyString_AsString(path2),
249
 
                                 PyString_Size(path2))
250
 
 
251
 
 
252
 
cdef int _cmp_path_by_dirblock(char *path1, int path1_len,
253
 
                               char *path2, int path2_len):
254
 
    """Compare two paths by what directory they are in.
255
 
 
256
 
    see ``_cmp_path_by_dirblock_c`` for details.
257
 
    """
258
 
    cdef char *dirname1
259
 
    cdef int dirname1_len
260
 
    cdef char *dirname2
261
 
    cdef int dirname2_len
262
 
    cdef char *basename1
263
 
    cdef int basename1_len
264
 
    cdef char *basename2
265
 
    cdef int basename2_len
266
 
    cdef int cur_len
267
 
    cdef int cmp_val
268
 
 
269
 
    if path1_len == 0 and path2_len == 0:
270
 
        return 0
271
 
 
272
 
    if path1 == path2 and path1_len == path2_len:
273
 
        return 0
274
 
 
275
 
    if path1_len == 0:
276
 
        return -1
277
 
 
278
 
    if path2_len == 0:
279
 
        return 1
280
 
 
281
 
    basename1 = <char*>_my_memrchr(path1, c'/', path1_len)
282
 
 
283
 
    if basename1 == NULL:
284
 
        basename1 = path1
285
 
        basename1_len = path1_len
286
 
        dirname1 = ''
287
 
        dirname1_len = 0
288
 
    else:
289
 
        dirname1 = path1
290
 
        dirname1_len = basename1 - path1
291
 
        basename1 = basename1 + 1
292
 
        basename1_len = path1_len - dirname1_len - 1
293
 
 
294
 
    basename2 = <char*>_my_memrchr(path2, c'/', path2_len)
295
 
 
296
 
    if basename2 == NULL:
297
 
        basename2 = path2
298
 
        basename2_len = path2_len
299
 
        dirname2 = ''
300
 
        dirname2_len = 0
301
 
    else:
302
 
        dirname2 = path2
303
 
        dirname2_len = basename2 - path2
304
 
        basename2 = basename2 + 1
305
 
        basename2_len = path2_len - dirname2_len - 1
306
 
 
307
 
    cmp_val = _cmp_by_dirs(dirname1, dirname1_len,
308
 
                           dirname2, dirname2_len)
309
 
    if cmp_val != 0:
310
 
        return cmp_val
311
 
 
312
 
    cur_len = basename1_len
313
 
    if basename2_len < basename1_len:
314
 
        cur_len = basename2_len
315
 
 
316
 
    cmp_val = memcmp(basename1, basename2, cur_len)
317
 
    if cmp_val != 0:
318
 
        return cmp_val
319
 
    if basename1_len == basename2_len:
320
 
        return 0
321
 
    if basename1_len < basename2_len:
322
 
        return -1
323
 
    return 1
324
 
 
325
 
 
326
 
def _bisect_path_left_c(paths, path):
327
 
    """Return the index where to insert path into paths.
328
 
 
329
 
    This uses a path-wise comparison so we get::
330
 
        a
331
 
        a-b
332
 
        a=b
333
 
        a/b
334
 
    Rather than::
335
 
        a
336
 
        a-b
337
 
        a/b
338
 
        a=b
339
 
    :param paths: A list of paths to search through
340
 
    :param path: A single path to insert
341
 
    :return: An offset where 'path' can be inserted.
342
 
    :seealso: bisect.bisect_left
343
 
    """
344
 
    cdef int _lo
345
 
    cdef int _hi
346
 
    cdef int _mid
347
 
    cdef char *path_cstr
348
 
    cdef int path_size
349
 
    cdef char *cur_cstr
350
 
    cdef int cur_size
351
 
    cdef void *cur
352
 
 
353
 
    if not PyList_CheckExact(paths):
354
 
        raise TypeError("you must pass a python list for 'paths' not: %s %r"
355
 
                        % (type(paths), paths))
356
 
    if not PyString_CheckExact(path):
357
 
        raise TypeError("you must pass a string for 'path' not: %s %r"
358
 
                        % (type(path), path))
359
 
 
360
 
    _hi = len(paths)
361
 
    _lo = 0
362
 
 
363
 
    path_cstr = PyString_AsString(path)
364
 
    path_size = PyString_Size(path)
365
 
 
366
 
    while _lo < _hi:
367
 
        _mid = (_lo + _hi) / 2
368
 
        cur = PyList_GetItem_object_void(paths, _mid)
369
 
        cur_cstr = PyString_AS_STRING_void(cur)
370
 
        cur_size = PyString_GET_SIZE_void(cur)
371
 
        if _cmp_path_by_dirblock(cur_cstr, cur_size, path_cstr, path_size) < 0:
372
 
            _lo = _mid + 1
373
 
        else:
374
 
            _hi = _mid
375
 
    return _lo
376
 
 
377
 
 
378
 
def _bisect_path_right_c(paths, path):
379
 
    """Return the index where to insert path into paths.
380
 
 
381
 
    This uses a path-wise comparison so we get::
382
 
        a
383
 
        a-b
384
 
        a=b
385
 
        a/b
386
 
    Rather than::
387
 
        a
388
 
        a-b
389
 
        a/b
390
 
        a=b
391
 
    :param paths: A list of paths to search through
392
 
    :param path: A single path to insert
393
 
    :return: An offset where 'path' can be inserted.
394
 
    :seealso: bisect.bisect_right
395
 
    """
396
 
    cdef int _lo
397
 
    cdef int _hi
398
 
    cdef int _mid
399
 
    cdef char *path_cstr
400
 
    cdef int path_size
401
 
    cdef char *cur_cstr
402
 
    cdef int cur_size
403
 
    cdef void *cur
404
 
 
405
 
    if not PyList_CheckExact(paths):
406
 
        raise TypeError("you must pass a python list for 'paths' not: %s %r"
407
 
                        % (type(paths), paths))
408
 
    if not PyString_CheckExact(path):
409
 
        raise TypeError("you must pass a string for 'path' not: %s %r"
410
 
                        % (type(path), path))
411
 
 
412
 
    _hi = len(paths)
413
 
    _lo = 0
414
 
 
415
 
    path_cstr = PyString_AsString(path)
416
 
    path_size = PyString_Size(path)
417
 
 
418
 
    while _lo < _hi:
419
 
        _mid = (_lo + _hi) / 2
420
 
        cur = PyList_GetItem_object_void(paths, _mid)
421
 
        cur_cstr = PyString_AS_STRING_void(cur)
422
 
        cur_size = PyString_GET_SIZE_void(cur)
423
 
        if _cmp_path_by_dirblock(path_cstr, path_size, cur_cstr, cur_size) < 0:
424
 
            _hi = _mid
425
 
        else:
426
 
            _lo = _mid + 1
427
 
    return _lo
428
 
 
429
 
 
430
 
def bisect_dirblock_c(dirblocks, dirname, lo=0, hi=None, cache=None):
431
 
    """Return the index where to insert dirname into the dirblocks.
432
 
 
433
 
    The return value idx is such that all directories blocks in dirblock[:idx]
434
 
    have names < dirname, and all blocks in dirblock[idx:] have names >=
435
 
    dirname.
436
 
 
437
 
    Optional args lo (default 0) and hi (default len(dirblocks)) bound the
438
 
    slice of a to be searched.
439
 
    """
440
 
    cdef int _lo
441
 
    cdef int _hi
442
 
    cdef int _mid
443
 
    cdef char *dirname_cstr
444
 
    cdef int dirname_size
445
 
    cdef char *cur_cstr
446
 
    cdef int cur_size
447
 
    cdef void *cur
448
 
 
449
 
    if not PyList_CheckExact(dirblocks):
450
 
        raise TypeError("you must pass a python list for 'dirblocks' not: %s %r"
451
 
                        % (type(dirblocks), dirblocks))
452
 
    if not PyString_CheckExact(dirname):
453
 
        raise TypeError("you must pass a string for dirname not: %s %r"
454
 
                        % (type(dirname), dirname))
455
 
    if hi is None:
456
 
        _hi = len(dirblocks)
457
 
    else:
458
 
        _hi = hi
459
 
 
460
 
    _lo = lo
461
 
    dirname_cstr = PyString_AsString(dirname)
462
 
    dirname_size = PyString_Size(dirname)
463
 
 
464
 
    while _lo < _hi:
465
 
        _mid = (_lo + _hi) / 2
466
 
        # Grab the dirname for the current dirblock
467
 
        # cur = dirblocks[_mid][0]
468
 
        cur = PyTuple_GetItem_void_void(
469
 
                PyList_GetItem_object_void(dirblocks, _mid), 0)
470
 
        cur_cstr = PyString_AS_STRING_void(cur)
471
 
        cur_size = PyString_GET_SIZE_void(cur)
472
 
        if _cmp_by_dirs(cur_cstr, cur_size, dirname_cstr, dirname_size) < 0:
473
 
            _lo = _mid + 1
474
 
        else:
475
 
            _hi = _mid
476
 
    return _lo
477
 
 
478
 
 
479
 
cdef class Reader:
480
 
    """Maintain the current location, and return fields as you parse them."""
481
 
 
482
 
    cdef object state # The DirState object
483
 
    cdef object text # The overall string object
484
 
    cdef char *text_cstr # Pointer to the beginning of text
485
 
    cdef int text_size # Length of text
486
 
 
487
 
    cdef char *end_cstr # End of text
488
 
    cdef char *cur_cstr # Pointer to the current record
489
 
    cdef char *next # Pointer to the end of this record
490
 
 
491
 
    def __init__(self, text, state):
492
 
        self.state = state
493
 
        self.text = text
494
 
        self.text_cstr = PyString_AsString(text)
495
 
        self.text_size = PyString_Size(text)
496
 
        self.end_cstr = self.text_cstr + self.text_size
497
 
        self.cur_cstr = self.text_cstr
498
 
 
499
 
    cdef char *get_next(self, int *size) except NULL:
500
 
        """Return a pointer to the start of the next field."""
501
 
        cdef char *next
502
 
        cdef Py_ssize_t extra_len
503
 
 
504
 
        if self.cur_cstr == NULL:
505
 
            raise AssertionError('get_next() called when cur_str is NULL')
506
 
        elif self.cur_cstr >= self.end_cstr:
507
 
            raise AssertionError('get_next() called when there are no chars'
508
 
                                 ' left')
509
 
        next = self.cur_cstr
510
 
        self.cur_cstr = <char*>memchr(next, c'\0', self.end_cstr - next)
511
 
        if self.cur_cstr == NULL:
512
 
            extra_len = self.end_cstr - next
513
 
            raise errors.DirstateCorrupt(self.state,
514
 
                'failed to find trailing NULL (\\0).'
515
 
                ' Trailing garbage: %r'
516
 
                % safe_string_from_size(next, extra_len))
517
 
        size[0] = self.cur_cstr - next
518
 
        self.cur_cstr = self.cur_cstr + 1
519
 
        return next
520
 
 
521
 
    cdef object get_next_str(self):
522
 
        """Get the next field as a Python string."""
523
 
        cdef int size
524
 
        cdef char *next
525
 
        next = self.get_next(&size)
526
 
        return safe_string_from_size(next, size)
527
 
 
528
 
    cdef int _init(self) except -1:
529
 
        """Get the pointer ready.
530
 
 
531
 
        This assumes that the dirstate header has already been read, and we
532
 
        already have the dirblock string loaded into memory.
533
 
        This just initializes our memory pointers, etc for parsing of the
534
 
        dirblock string.
535
 
        """
536
 
        cdef char *first
537
 
        cdef int size
538
 
        # The first field should be an empty string left over from the Header
539
 
        first = self.get_next(&size)
540
 
        if first[0] != c'\0' and size == 0:
541
 
            raise AssertionError('First character should be null not: %s'
542
 
                                 % (first,))
543
 
        return 0
544
 
 
545
 
    cdef object _get_entry(self, int num_trees, void **p_current_dirname,
546
 
                           int *new_block):
547
 
        """Extract the next entry.
548
 
 
549
 
        This parses the next entry based on the current location in
550
 
        ``self.cur_cstr``.
551
 
        Each entry can be considered a "row" in the total table. And each row
552
 
        has a fixed number of columns. It is generally broken up into "key"
553
 
        columns, then "current" columns, and then "parent" columns.
554
 
 
555
 
        :param num_trees: How many parent trees need to be parsed
556
 
        :param p_current_dirname: A pointer to the current PyString
557
 
            representing the directory name.
558
 
            We pass this in as a void * so that pyrex doesn't have to
559
 
            increment/decrement the PyObject reference counter for each
560
 
            _get_entry call.
561
 
            We use a pointer so that _get_entry can update it with the new
562
 
            value.
563
 
        :param new_block: This is to let the caller know that it needs to
564
 
            create a new directory block to store the next entry.
565
 
        """
566
 
        cdef object path_name_file_id_key
567
 
        cdef char *entry_size_cstr
568
 
        cdef unsigned long int entry_size
569
 
        cdef char* executable_cstr
570
 
        cdef int is_executable
571
 
        cdef char* dirname_cstr
572
 
        cdef char* trailing
573
 
        cdef int cur_size
574
 
        cdef int i
575
 
        cdef object minikind
576
 
        cdef object fingerprint
577
 
        cdef object info
578
 
 
579
 
        # Read the 'key' information (dirname, name, file_id)
580
 
        dirname_cstr = self.get_next(&cur_size)
581
 
        # Check to see if we have started a new directory block.
582
 
        # If so, then we need to create a new dirname PyString, so that it can
583
 
        # be used in all of the tuples. This saves time and memory, by re-using
584
 
        # the same object repeatedly.
585
 
 
586
 
        # Do the cheap 'length of string' check first. If the string is a
587
 
        # different length, then we *have* to be a different directory.
588
 
        if (cur_size != PyString_GET_SIZE_void(p_current_dirname[0])
589
 
            or strncmp(dirname_cstr,
590
 
                       # Extract the char* from our current dirname string.  We
591
 
                       # know it is a PyString, so we can use
592
 
                       # PyString_AS_STRING, we use the _void version because
593
 
                       # we are tricking Pyrex by using a void* rather than an
594
 
                       # <object>
595
 
                       PyString_AS_STRING_void(p_current_dirname[0]),
596
 
                       cur_size+1) != 0):
597
 
            dirname = safe_string_from_size(dirname_cstr, cur_size)
598
 
            p_current_dirname[0] = <void*>dirname
599
 
            new_block[0] = 1
600
 
        else:
601
 
            new_block[0] = 0
602
 
 
603
 
        # Build up the key that will be used.
604
 
        # By using <object>(void *) Pyrex will automatically handle the
605
 
        # Py_INCREF that we need.
606
 
        path_name_file_id_key = (<object>p_current_dirname[0],
607
 
                                 self.get_next_str(),
608
 
                                 self.get_next_str(),
609
 
                                )
610
 
 
611
 
        # Parse all of the per-tree information. current has the information in
612
 
        # the same location as parent trees. The only difference is that 'info'
613
 
        # is a 'packed_stat' for current, while it is a 'revision_id' for
614
 
        # parent trees.
615
 
        # minikind, fingerprint, and info will be returned as regular python
616
 
        # strings
617
 
        # entry_size and is_executable will be parsed into a python Long and
618
 
        # python Boolean, respectively.
619
 
        # TODO: jam 20070718 Consider changin the entry_size conversion to
620
 
        #       prefer python Int when possible. They are generally faster to
621
 
        #       work with, and it will be rare that we have a file >2GB.
622
 
        #       Especially since this code is pretty much fixed at a max of
623
 
        #       4GB.
624
 
        trees = []
625
 
        for i from 0 <= i < num_trees:
626
 
            minikind = self.get_next_str()
627
 
            fingerprint = self.get_next_str()
628
 
            entry_size_cstr = self.get_next(&cur_size)
629
 
            entry_size = strtoul(entry_size_cstr, NULL, 10)
630
 
            executable_cstr = self.get_next(&cur_size)
631
 
            is_executable = (executable_cstr[0] == c'y')
632
 
            info = self.get_next_str()
633
 
            PyList_Append(trees, (
634
 
                minikind,     # minikind
635
 
                fingerprint,  # fingerprint
636
 
                entry_size,   # size
637
 
                is_executable,# executable
638
 
                info,         # packed_stat or revision_id
639
 
            ))
640
 
 
641
 
        # The returned tuple is (key, [trees])
642
 
        ret = (path_name_file_id_key, trees)
643
 
        # Ignore the trailing newline, but assert that it does exist, this
644
 
        # ensures that we always finish parsing a line on an end-of-entry
645
 
        # marker.
646
 
        trailing = self.get_next(&cur_size)
647
 
        if cur_size != 1 or trailing[0] != c'\n':
648
 
            raise errors.DirstateCorrupt(self.state,
649
 
                'Bad parse, we expected to end on \\n, not: %d %s: %s'
650
 
                % (cur_size, safe_string_from_size(trailing, cur_size),
651
 
                   ret))
652
 
        return ret
653
 
 
654
 
    def _parse_dirblocks(self):
655
 
        """Parse all dirblocks in the state file."""
656
 
        cdef int num_trees
657
 
        cdef object current_block
658
 
        cdef object entry
659
 
        cdef void * current_dirname
660
 
        cdef int new_block
661
 
        cdef int expected_entry_count
662
 
        cdef int entry_count
663
 
 
664
 
        num_trees = self.state._num_present_parents() + 1
665
 
        expected_entry_count = self.state._num_entries
666
 
 
667
 
        # Ignore the first record
668
 
        self._init()
669
 
 
670
 
        current_block = []
671
 
        dirblocks = [('', current_block), ('', [])]
672
 
        self.state._dirblocks = dirblocks
673
 
        obj = ''
674
 
        current_dirname = <void*>obj
675
 
        new_block = 0
676
 
        entry_count = 0
677
 
 
678
 
        # TODO: jam 2007-05-07 Consider pre-allocating some space for the
679
 
        #       members, and then growing and shrinking from there. If most
680
 
        #       directories have close to 10 entries in them, it would save a
681
 
        #       few mallocs if we default our list size to something
682
 
        #       reasonable. Or we could malloc it to something large (100 or
683
 
        #       so), and then truncate. That would give us a malloc + realloc,
684
 
        #       rather than lots of reallocs.
685
 
        while self.cur_cstr < self.end_cstr:
686
 
            entry = self._get_entry(num_trees, &current_dirname, &new_block)
687
 
            if new_block:
688
 
                # new block - different dirname
689
 
                current_block = []
690
 
                PyList_Append(dirblocks,
691
 
                              (<object>current_dirname, current_block))
692
 
            PyList_Append(current_block, entry)
693
 
            entry_count = entry_count + 1
694
 
        if entry_count != expected_entry_count:
695
 
            raise errors.DirstateCorrupt(self.state,
696
 
                    'We read the wrong number of entries.'
697
 
                    ' We expected to read %s, but read %s'
698
 
                    % (expected_entry_count, entry_count))
699
 
        self.state._split_root_dirblock_into_contents()
700
 
 
701
 
 
702
 
def _read_dirblocks_c(state):
703
 
    """Read in the dirblocks for the given DirState object.
704
 
 
705
 
    This is tightly bound to the DirState internal representation. It should be
706
 
    thought of as a member function, which is only separated out so that we can
707
 
    re-write it in pyrex.
708
 
 
709
 
    :param state: A DirState object.
710
 
    :return: None
711
 
    :postcondition: The dirblocks will be loaded into the appropriate fields in
712
 
        the DirState object.
713
 
    """
714
 
    state._state_file.seek(state._end_of_header)
715
 
    text = state._state_file.read()
716
 
    # TODO: check the crc checksums. crc_measured = zlib.crc32(text)
717
 
 
718
 
    reader = Reader(text, state)
719
 
 
720
 
    reader._parse_dirblocks()
721
 
    state._dirblock_state = DirState.IN_MEMORY_UNMODIFIED