1
# Copyright (C) 2007 Canonical Ltd
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.
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.
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
17
"""Helper functions for DirState.
19
This is the python implementation for DirState functions.
22
from bzrlib.dirstate import DirState
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
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)
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, ...)
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)
51
void Py_INCREF(object)
52
void Py_DECREF(object)
55
cdef extern from "stdlib.h":
56
unsigned long int strtoul(char *nptr, char **endptr, int base)
58
cdef extern from "string.h":
59
char *strchr(char *s1, char c)
62
cdef object _split_from_path(object cache, object path):
63
"""get the dirblock tuple for a given path.
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
74
value_ptr = PyDict_GetItem(cache, path)
76
value = path.split('/')
79
value = <object>value_ptr
84
cdef int _cmp_dirblock_strings(char *path1, int size1, char *path2, int size2):
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))
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]:
107
cur_int1 = cur_int1 + 1
108
cur_int2 = cur_int2 + 1
110
cur1 = <char*>cur_int1
111
cur2 = <char*>cur_int2
113
while cur1 < end1 and cur2 < end2:
114
if cur1[0] == cur2[0]:
115
# This character matches, just go to the next one
119
# The current characters do not match
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]:
129
# We reached the end of at least one of the strings
131
return 1 # Not at the end of cur1, must be at the end of cur2
133
return -1 # At the end of cur1, but not at cur2
134
# We reached the end of both strings
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))
146
def c_bisect_dirblock(dirblocks, dirname, lo=0, hi=None, cache=None):
147
"""Return the index where to insert dirname into the dirblocks.
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 >=
153
Optional args lo (default 0) and hi (default len(dirblocks)) bound the
154
slice of a to be searched.
159
cdef char *dirname_str
160
cdef int dirname_size
170
if not PyList_CheckExact(dirblocks):
171
raise TypeError('you must pass a python list for dirblocks')
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)
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:
194
cdef object _List_GetItem_Incref(object lst, int offset):
195
"""Get an item, and increment a reference to it.
197
The caller must have checked that the object really is a list.
200
cur = PyList_GET_ITEM(lst, offset)
205
cdef object _fields_to_entry_0_parents(object fields):
206
cdef object path_name_file_id_key
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),
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':
226
is_executable = False
227
return (path_name_file_id_key, [
229
_List_GetItem_Incref(fields, 3),# minikind
230
_List_GetItem_Incref(fields, 4),# fingerprint
232
is_executable, # executable
233
_List_GetItem_Incref(fields, 7),# packed_stat or revision_id
237
def _c_read_dirblocks(state):
238
"""Read in the dirblocks for the given DirState object.
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.
244
:param state: A DirState object.
251
cdef int num_present_parents
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)
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.
263
# skip the first field which is the trailing null from the header.
265
# Each line now has an extra '\n' field which is not used
266
# so we just skip over it
268
# 3 fields for the key
269
# + number of fields per tree_data (5) * tree count
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)
283
if num_present_parents == 1:
284
# Bind external functions to local names
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):
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]
299
append_entry = current_block.append
300
for count in xrange(state._num_entries):
304
if dirname != current_dirname:
305
# new block - different dirname
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),
315
next(), # fingerprint
317
next() == 'y', # executable
318
next(), # packed_stat or revision_id
322
next(), # fingerprint
324
next() == 'y', # executable
325
next(), # packed_stat or revision_id
329
assert trailing == '\n'
330
# append the entry to the current block
332
state._split_root_dirblock_into_contents()
333
elif num_present_parents == 0:
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)
342
fields_to_entry = state._get_fields_to_entry()
344
entries_append = entries.append
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