~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/compiled/dirstate_helpers.pyx

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 16:43:12 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730164312-b025fd3ff0cee59e
rename  gpl.txt => COPYING.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 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.dirstate import DirState
23
 
 
24
 
 
25
 
cdef extern from *:
26
 
    ctypedef int size_t
27
 
 
28
 
cdef extern from "Python.h":
29
 
    # GetItem returns a borrowed reference
30
 
    void *PyDict_GetItem(object p, object key)
31
 
    int PyDict_SetItem(object p, object key, object val) except -1
32
 
 
33
 
    int PyList_Append(object lst, object item) except -1
34
 
    void *PyList_GetItem_object_void "PyList_GET_ITEM" (object lst, int index)
35
 
    object PyList_GET_ITEM (object lst, int index)
36
 
    int PyList_CheckExact(object)
37
 
 
38
 
    int PyTuple_CheckExact(object)
39
 
    void *PyTuple_GetItem_void_void "PyTuple_GET_ITEM" (void* tpl, int index)
40
 
    object PyTuple_New(int)
41
 
    int PyTuple_SetItem(object tpl, int offset, object val)
42
 
    void PyTuple_SET_ITEM(object tpl, int offset, object val)
43
 
    object PyTuple_Pack(int n, ...)
44
 
 
45
 
    char *PyString_AsString(object p)
46
 
    char *PyString_AS_STRING_void "PyString_AS_STRING" (void *p)
47
 
    int PyString_Size(object p)
48
 
    int PyString_GET_SIZE_void "PyString_GET_SIZE" (void *p)
49
 
    int PyString_CheckExact(object p)
50
 
 
51
 
    void Py_INCREF(object)
52
 
    void Py_DECREF(object)
53
 
 
54
 
 
55
 
cdef extern from "stdlib.h":
56
 
    unsigned long int strtoul(char *nptr, char **endptr, int base)
57
 
 
58
 
cdef extern from "string.h":
59
 
    char *strchr(char *s1, char c)
60
 
 
61
 
 
62
 
cdef object _split_from_path(object cache, object path):
63
 
    """get the dirblock tuple for a given path.
64
 
 
65
 
    :param cache: A Dictionary mapping string paths to tuples
66
 
    :param path: The path we care about.
67
 
    :return: A borrowed reference to a tuple stored in cache.
68
 
        You do not need to Py_DECREF() when you are done, unless you plan on
69
 
        using it for a while.
70
 
    """
71
 
    cdef void* value_ptr
72
 
    cdef object value
73
 
 
74
 
    value_ptr = PyDict_GetItem(cache, path)
75
 
    if value_ptr == NULL:
76
 
        value = path.split('/')
77
 
        cache[path] = value
78
 
    else:
79
 
        value = <object>value_ptr
80
 
 
81
 
    return value
82
 
 
83
 
 
84
 
cdef int _cmp_dirblock_strings(char *path1, int size1, char *path2, int size2):
85
 
    cdef char *cur1
86
 
    cdef char *cur2
87
 
    cdef char *end1
88
 
    cdef char *end2
89
 
    cdef int *cur_int1
90
 
    cdef int *cur_int2
91
 
    cdef int *end_int1
92
 
    cdef int *end_int2
93
 
 
94
 
    cur_int1 = <int*>path1
95
 
    cur_int2 = <int*>path2
96
 
    end_int1 = <int*>(path1 + size1 - (size1%4))
97
 
    end_int2 = <int*>(path2 + size2 - (size2%4))
98
 
    end1 = path1+size1
99
 
    end2 = path2+size2
100
 
 
101
 
    # Use 32-bit comparisons for the matching portion of the string.
102
 
    # Almost all CPU's are faster at loading and comparing 32-bit integers,
103
 
    # than they are at 8-bit integers.
104
 
    while cur_int1 < end_int1 and cur_int2 < end_int2:
105
 
        if cur_int1[0] != cur_int2[0]:
106
 
            break
107
 
        cur_int1 = cur_int1 + 1
108
 
        cur_int2 = cur_int2 + 1
109
 
 
110
 
    cur1 = <char*>cur_int1
111
 
    cur2 = <char*>cur_int2
112
 
 
113
 
    while cur1 < end1 and cur2 < end2:
114
 
        if cur1[0] == cur2[0]:
115
 
            # This character matches, just go to the next one
116
 
            cur1 = cur1 + 1
117
 
            cur2 = cur2 + 1
118
 
            continue
119
 
        # The current characters do not match
120
 
        if cur1[0] == c'/':
121
 
            return -1 # Reached the end of path1 segment first
122
 
        elif cur2[0] == c'/':
123
 
            return 1 # Reached the end of path2 segment first
124
 
        elif cur1[0] < cur2[0]:
125
 
            return -1
126
 
        else:
127
 
            return 1
128
 
 
129
 
    # We reached the end of at least one of the strings
130
 
    if cur1 < end1:
131
 
        return 1 # Not at the end of cur1, must be at the end of cur2
132
 
    if cur2 < end2:
133
 
        return -1 # At the end of cur1, but not at cur2
134
 
    # We reached the end of both strings
135
 
    return 0
136
 
 
137
 
 
138
 
def cmp_dirblock_strings(path1, path2):
139
 
    """Compare to python strings in dirblock fashion."""
140
 
    return _cmp_dirblock_strings(PyString_AsString(path1),
141
 
                                 PyString_Size(path1),
142
 
                                 PyString_AsString(path2),
143
 
                                 PyString_Size(path2))
144
 
 
145
 
 
146
 
def c_bisect_dirblock(dirblocks, dirname, lo=0, hi=None, cache=None):
147
 
    """Return the index where to insert dirname into the dirblocks.
148
 
 
149
 
    The return value idx is such that all directories blocks in dirblock[:idx]
150
 
    have names < dirname, and all blocks in dirblock[idx:] have names >=
151
 
    dirname.
152
 
 
153
 
    Optional args lo (default 0) and hi (default len(dirblocks)) bound the
154
 
    slice of a to be searched.
155
 
    """
156
 
    cdef int _lo
157
 
    cdef int _hi
158
 
    cdef int _mid
159
 
    cdef char *dirname_str
160
 
    cdef int dirname_size
161
 
    cdef char *cur_str
162
 
    cdef int cur_size
163
 
    cdef void *cur
164
 
 
165
 
    if hi is None:
166
 
        _hi = len(dirblocks)
167
 
    else:
168
 
        _hi = hi
169
 
 
170
 
    if not PyList_CheckExact(dirblocks):
171
 
        raise TypeError('you must pass a python list for dirblocks')
172
 
    _lo = lo
173
 
    if not PyString_CheckExact(dirname):
174
 
        raise TypeError('you must pass a string for dirname')
175
 
    dirname_str = PyString_AsString(dirname)
176
 
    dirname_size = PyString_Size(dirname)
177
 
 
178
 
    while _lo < _hi:
179
 
        _mid = (_lo+_hi)/2
180
 
        # Grab the dirname for the current dirblock
181
 
        # cur = dirblocks[_mid][0]
182
 
        cur = PyTuple_GetItem_void_void(
183
 
                PyList_GetItem_object_void(dirblocks, _mid), 0)
184
 
        cur_str = PyString_AS_STRING_void(cur)
185
 
        cur_size = PyString_GET_SIZE_void(cur)
186
 
        if _cmp_dirblock_strings(cur_str, cur_size,
187
 
                                 dirname_str, dirname_size) < 0:
188
 
            _lo = _mid+1
189
 
        else:
190
 
            _hi = _mid
191
 
    return _lo
192
 
 
193
 
 
194
 
cdef object _List_GetItem_Incref(object lst, int offset):
195
 
    """Get an item, and increment a reference to it.
196
 
 
197
 
    The caller must have checked that the object really is a list.
198
 
    """
199
 
    cdef object cur
200
 
    cur = PyList_GET_ITEM(lst, offset)
201
 
    Py_INCREF(cur)
202
 
    return cur
203
 
 
204
 
 
205
 
cdef object _fields_to_entry_0_parents(object fields):
206
 
    cdef object path_name_file_id_key
207
 
    cdef char *size_str
208
 
    cdef unsigned long int size
209
 
    cdef char* executable_str
210
 
    cdef object is_executable
211
 
    if not PyList_CheckExact(fields):
212
 
        raise TypeError('fields must be a list')
213
 
    path_name_file_id_key = (_List_GetItem_Incref(fields, 0),
214
 
                             _List_GetItem_Incref(fields, 1),
215
 
                             _List_GetItem_Incref(fields, 2),
216
 
                            )
217
 
 
218
 
    size_str = PyString_AS_STRING_void(
219
 
                PyList_GetItem_object_void(fields, 5))
220
 
    size = strtoul(size_str, NULL, 10)
221
 
    executable_str = PyString_AS_STRING_void(
222
 
                        PyList_GetItem_object_void(fields, 6))
223
 
    if executable_str[0] == c'y':
224
 
        is_executable = True
225
 
    else:
226
 
        is_executable = False
227
 
    return (path_name_file_id_key, [
228
 
        ( # Current tree
229
 
            _List_GetItem_Incref(fields, 3),# minikind
230
 
            _List_GetItem_Incref(fields, 4),# fingerprint
231
 
            size,                           # size
232
 
            is_executable,                  # executable
233
 
            _List_GetItem_Incref(fields, 7),# packed_stat or revision_id
234
 
        )])
235
 
 
236
 
 
237
 
def _c_read_dirblocks(state):
238
 
    """Read in the dirblocks for the given DirState object.
239
 
 
240
 
    This is tightly bound to the DirState internal representation. It should be
241
 
    thought of as a member function, which is only separated out so that we can
242
 
    re-write it in pyrex.
243
 
 
244
 
    :param state: A DirState object.
245
 
    :return: None
246
 
    """
247
 
    cdef int cur
248
 
    cdef int pos
249
 
    cdef int entry_size
250
 
    cdef int field_count
251
 
    cdef int num_present_parents
252
 
 
253
 
    state._state_file.seek(state._end_of_header)
254
 
    text = state._state_file.read()
255
 
    # TODO: check the crc checksums. crc_measured = zlib.crc32(text)
256
 
 
257
 
    fields = text.split('\0')
258
 
    # Remove the last blank entry
259
 
    trailing = fields.pop()
260
 
    assert trailing == ''
261
 
    # consider turning fields into a tuple.
262
 
 
263
 
    # skip the first field which is the trailing null from the header.
264
 
    cur = 1
265
 
    # Each line now has an extra '\n' field which is not used
266
 
    # so we just skip over it
267
 
    # entry size:
268
 
    #  3 fields for the key
269
 
    #  + number of fields per tree_data (5) * tree count
270
 
    #  + newline
271
 
    num_present_parents = state._num_present_parents()
272
 
    tree_count = 1 + num_present_parents
273
 
    entry_size = state._fields_per_entry()
274
 
    expected_field_count = entry_size * state._num_entries
275
 
    field_count = len(fields)
276
 
    # this checks our adjustment, and also catches file too short.
277
 
    assert field_count - cur == expected_field_count, \
278
 
        'field count incorrect %s != %s, entry_size=%s, '\
279
 
        'num_entries=%s fields=%r' % (
280
 
            field_count - cur, expected_field_count, entry_size,
281
 
            state._num_entries, fields)
282
 
 
283
 
    if num_present_parents == 1:
284
 
        # Bind external functions to local names
285
 
        _int = int
286
 
        # We access all fields in order, so we can just iterate over
287
 
        # them. Grab an straight iterator over the fields. (We use an
288
 
        # iterator because we don't want to do a lot of additions, nor
289
 
        # do we want to do a lot of slicing)
290
 
        next = iter(fields).next
291
 
        # Move the iterator to the current position
292
 
        for x in xrange(cur):
293
 
            next()
294
 
        # The two blocks here are deliberate: the root block and the
295
 
        # contents-of-root block.
296
 
        state._dirblocks = [('', []), ('', [])]
297
 
        current_block = state._dirblocks[0][1]
298
 
        current_dirname = ''
299
 
        append_entry = current_block.append
300
 
        for count in xrange(state._num_entries):
301
 
            dirname = next()
302
 
            name = next()
303
 
            file_id = next()
304
 
            if dirname != current_dirname:
305
 
                # new block - different dirname
306
 
                current_block = []
307
 
                current_dirname = dirname
308
 
                state._dirblocks.append((current_dirname, current_block))
309
 
                append_entry = current_block.append
310
 
            # we know current_dirname == dirname, so re-use it to avoid
311
 
            # creating new strings
312
 
            entry = ((current_dirname, name, file_id),
313
 
                     [(# Current Tree
314
 
                         next(),                # minikind
315
 
                         next(),                # fingerprint
316
 
                         _int(next()),          # size
317
 
                         next() == 'y',         # executable
318
 
                         next(),                # packed_stat or revision_id
319
 
                     ),
320
 
                     ( # Parent 1
321
 
                         next(),                # minikind
322
 
                         next(),                # fingerprint
323
 
                         _int(next()),          # size
324
 
                         next() == 'y',         # executable
325
 
                         next(),                # packed_stat or revision_id
326
 
                     ),
327
 
                     ])
328
 
            trailing = next()
329
 
            assert trailing == '\n'
330
 
            # append the entry to the current block
331
 
            append_entry(entry)
332
 
        state._split_root_dirblock_into_contents()
333
 
    elif num_present_parents == 0:
334
 
        entries = []
335
 
        pos = cur
336
 
        while pos < field_count:
337
 
            PyList_Append(entries,
338
 
                _fields_to_entry_0_parents(fields[pos:pos+entry_size]))
339
 
            pos = pos + entry_size
340
 
        state._entries_to_current_state(entries)
341
 
    else:
342
 
        fields_to_entry = state._get_fields_to_entry()
343
 
        entries = []
344
 
        entries_append = entries.append
345
 
        pos = cur
346
 
        while pos < field_count:
347
 
            entries_append(fields_to_entry(fields[pos:pos+entry_size]))
348
 
            pos = pos + entry_size
349
 
        state._entries_to_current_state(entries)
350
 
    # To convert from format 2  => format 3
351
 
    # state._dirblocks = sorted(state._dirblocks,
352
 
    #                          key=lambda blk:blk[0].split('/'))
353
 
    # To convert from format 3 => format 2
354
 
    # state._dirblocks = sorted(state._dirblocks)
355
 
    state._dirblock_state = DirState.IN_MEMORY_UNMODIFIED