~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_groupcompress_pyx.pyx

  • Committer: John Arbash Meinel
  • Date: 2010-02-10 17:52:08 UTC
  • mfrom: (5021 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5023.
  • Revision ID: john@arbash-meinel.com-20100210175208-bubuwav4uqigu291
Merge bzr.dev 5021 to resolve NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2009 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
22
22
 
23
23
 
24
24
cdef extern from "Python.h":
25
 
    ctypedef struct PyObject:
26
 
        pass
27
25
    ctypedef int Py_ssize_t # Required for older pyrex versions
28
26
    int PyString_CheckExact(object)
29
27
    char * PyString_AS_STRING(object)
46
44
        unsigned long agg_offset
47
45
    struct delta_index:
48
46
        pass
49
 
    ctypedef enum delta_result:
50
 
        DELTA_OK
51
 
        DELTA_OUT_OF_MEMORY
52
 
        DELTA_INDEX_NEEDED
53
 
        DELTA_SOURCE_EMPTY
54
 
        DELTA_SOURCE_BAD
55
 
        DELTA_BUFFER_EMPTY
56
 
        DELTA_SIZE_TOO_BIG
57
 
    delta_result create_delta_index(source_info *src,
58
 
                                    delta_index *old,
59
 
                                    delta_index **fresh,
60
 
                                    int max_entries) nogil
61
 
    delta_result create_delta_index_from_delta(source_info *delta,
62
 
                                               delta_index *old,
63
 
                                               delta_index **fresh) nogil
 
47
    delta_index * create_delta_index(source_info *src, delta_index *old) nogil
 
48
    delta_index * create_delta_index_from_delta(source_info *delta,
 
49
                                                delta_index *old) nogil
64
50
    void free_delta_index(delta_index *index) nogil
65
 
    delta_result create_delta(delta_index *indexes,
66
 
                              void *buf, unsigned long bufsize,
67
 
                              unsigned long *delta_size,
68
 
                              unsigned long max_delta_size,
69
 
                              void **delta_data) nogil
 
51
    void *create_delta(delta_index *indexes,
 
52
             void *buf, unsigned long bufsize,
 
53
             unsigned long *delta_size, unsigned long max_delta_size) nogil
70
54
    unsigned long get_delta_hdr_size(unsigned char **datap,
71
55
                                     unsigned char *top) nogil
72
 
    unsigned long sizeof_delta_index(delta_index *index)
73
56
    Py_ssize_t DELTA_SIZE_MIN
74
 
    int get_hash_offset(delta_index *index, int pos, unsigned int *hash_offset)
75
 
    int get_entry_summary(delta_index *index, int pos,
76
 
                          unsigned int *global_offset, unsigned int *hash_val)
77
 
    unsigned int rabin_hash (unsigned char *data)
78
57
 
79
58
 
80
59
cdef void *safe_malloc(size_t count) except NULL:
104
83
    return DeltaIndex(source)
105
84
 
106
85
 
107
 
cdef object _translate_delta_failure(delta_result result):
108
 
    if result == DELTA_OUT_OF_MEMORY:
109
 
        return MemoryError("Delta function failed to allocate memory")
110
 
    elif result == DELTA_INDEX_NEEDED:
111
 
        return ValueError("Delta function requires delta_index param")
112
 
    elif result == DELTA_SOURCE_EMPTY:
113
 
        return ValueError("Delta function given empty source_info param")
114
 
    elif result == DELTA_SOURCE_BAD:
115
 
        return RuntimeError("Delta function given invalid source_info param")
116
 
    elif result == DELTA_BUFFER_EMPTY:
117
 
        return ValueError("Delta function given empty buffer params")
118
 
    return AssertionError("Unrecognised delta result code: %d" % result)
119
 
 
120
 
 
121
 
def _rabin_hash(content):
122
 
    if not PyString_CheckExact(content):
123
 
        raise ValueError('content must be a string')
124
 
    if len(content) < 16:
125
 
        raise ValueError('content must be at least 16 bytes long')
126
 
    # Try to cast it to an int, if it can fit
127
 
    return int(rabin_hash(<unsigned char*>(PyString_AS_STRING(content))))
128
 
 
129
 
 
130
86
cdef class DeltaIndex:
131
87
 
132
88
    # We need Pyrex 0.9.8+ to understand a 'list' definition, and this object
135
91
    cdef readonly object _sources
136
92
    cdef source_info *_source_infos
137
93
    cdef delta_index *_index
 
94
    cdef readonly unsigned int _max_num_sources
138
95
    cdef public unsigned long _source_offset
139
 
    cdef readonly unsigned int _max_num_sources
140
 
    cdef public int _max_bytes_to_index
141
96
 
142
 
    def __init__(self, source=None, max_bytes_to_index=None):
 
97
    def __init__(self, source=None):
143
98
        self._sources = []
144
99
        self._index = NULL
145
100
        self._max_num_sources = 65000
146
101
        self._source_infos = <source_info *>safe_malloc(sizeof(source_info)
147
102
                                                        * self._max_num_sources)
148
103
        self._source_offset = 0
149
 
        self._max_bytes_to_index = 0
150
 
        if max_bytes_to_index is not None:
151
 
            self._max_bytes_to_index = max_bytes_to_index
152
104
 
153
105
        if source is not None:
154
106
            self.add_source(source, 0)
155
107
 
156
 
    def __sizeof__(self):
157
 
        # We want to track the _source_infos allocations, but the referenced
158
 
        # void* are actually tracked in _sources itself.
159
 
        # XXX: Cython is capable of doing sizeof(class) and returning the size
160
 
        #      of the underlying struct. Pyrex (<= 0.9.9) refuses, so we need
161
 
        #      to do it manually. *sigh* Note that we might get it wrong
162
 
        #      because of alignment issues.
163
 
        cdef Py_ssize_t size
164
 
        # PyObject start, vtable *, 3 object pointers, 2 C ints
165
 
        size = ((sizeof(PyObject) + sizeof(void*) + 3*sizeof(PyObject*)
166
 
                 + sizeof(unsigned long)
167
 
                 + sizeof(unsigned int))
168
 
                + (sizeof(source_info) * self._max_num_sources)
169
 
                + sizeof_delta_index(self._index))
170
 
        return size
171
 
 
172
108
    def __repr__(self):
173
109
        return '%s(%d, %d)' % (self.__class__.__name__,
174
110
            len(self._sources), self._source_offset)
182
118
    def _has_index(self):
183
119
        return (self._index != NULL)
184
120
 
185
 
    def _dump_index(self):
186
 
        """Dump the pointers in the index.
187
 
 
188
 
        This is an arbitrary layout, used for testing. It is not meant to be
189
 
        used in production code.
190
 
 
191
 
        :return: (hash_list, entry_list)
192
 
            hash_list   A list of offsets, so hash[i] points to the 'hash
193
 
                        bucket' starting at the given offset and going until
194
 
                        hash[i+1]
195
 
            entry_list  A list of (text_offset, hash_val). text_offset is the
196
 
                        offset in the "source" texts, and hash_val is the RABIN
197
 
                        hash for that offset.
198
 
                        Note that the entry should be in the hash bucket
199
 
                        defined by
200
 
                        hash[(hash_val & mask)] && hash[(hash_val & mask) + 1]
201
 
        """
202
 
        cdef int pos
203
 
        cdef unsigned int text_offset
204
 
        cdef unsigned int hash_val
205
 
        cdef unsigned int hash_offset
206
 
        if self._index == NULL:
207
 
            return None
208
 
        hash_list = []
209
 
        pos = 0
210
 
        while get_hash_offset(self._index, pos, &hash_offset):
211
 
            hash_list.append(int(hash_offset))
212
 
            pos += 1
213
 
        entry_list = []
214
 
        pos = 0
215
 
        while get_entry_summary(self._index, pos, &text_offset, &hash_val):
216
 
            # Map back using 'int' so that we don't get Long everywhere, when
217
 
            # almost everything is <2**31.
218
 
            val = tuple(map(int, [text_offset, hash_val]))
219
 
            entry_list.append(val)
220
 
            pos += 1
221
 
        return hash_list, entry_list
222
 
 
223
121
    def add_delta_source(self, delta, unadded_bytes):
224
122
        """Add a new delta to the source texts.
225
123
 
230
128
        cdef char *c_delta
231
129
        cdef Py_ssize_t c_delta_size
232
130
        cdef delta_index *index
233
 
        cdef delta_result res
234
131
        cdef unsigned int source_location
235
132
        cdef source_info *src
236
133
        cdef unsigned int num_indexes
249
146
        src.size = c_delta_size
250
147
        src.agg_offset = self._source_offset + unadded_bytes
251
148
        with nogil:
252
 
            res = create_delta_index_from_delta(src, self._index, &index)
253
 
        if res != DELTA_OK:
254
 
            raise _translate_delta_failure(res)
 
149
            index = create_delta_index_from_delta(src, self._index)
255
150
        self._source_offset = src.agg_offset + src.size
256
 
        if index != self._index:
 
151
        if index != NULL:
257
152
            free_delta_index(self._index)
258
153
            self._index = index
259
154
 
263
158
        :param source: The text in question, this must be a byte string
264
159
        :param unadded_bytes: Assume there are this many bytes that didn't get
265
160
            added between this source and the end of the previous source.
266
 
        :param max_pointers: Add no more than this many entries to the index.
267
 
            By default, we sample every 16 bytes, if that would require more
268
 
            than max_entries, we will reduce the sampling rate.
269
 
            A value of 0 means unlimited, None means use the default limit.
270
161
        """
271
162
        cdef char *c_source
272
163
        cdef Py_ssize_t c_source_size
273
164
        cdef delta_index *index
274
 
        cdef delta_result res
275
165
        cdef unsigned int source_location
276
166
        cdef source_info *src
277
167
        cdef unsigned int num_indexes
278
 
        cdef int max_num_entries
279
168
 
280
169
        if not PyString_CheckExact(source):
281
170
            raise TypeError('source is not a str')
298
187
        # We delay creating the index on the first insert
299
188
        if source_location != 0:
300
189
            with nogil:
301
 
                res = create_delta_index(src, self._index, &index,
302
 
                                         self._max_bytes_to_index)
303
 
            if res != DELTA_OK:
304
 
                raise _translate_delta_failure(res)
305
 
            if index != self._index:
 
190
                index = create_delta_index(src, self._index)
 
191
            if index != NULL:
306
192
                free_delta_index(self._index)
307
193
                self._index = index
308
194
 
309
195
    cdef _populate_first_index(self):
310
196
        cdef delta_index *index
311
 
        cdef delta_result res
312
197
        if len(self._sources) != 1 or self._index != NULL:
313
198
            raise AssertionError('_populate_first_index should only be'
314
199
                ' called when we have a single source and no index yet')
315
200
 
316
 
        # We know that self._index is already NULL, so create_delta_index
317
 
        # will always create a new index unless there's a malloc failure
 
201
        # We know that self._index is already NULL, so whatever
 
202
        # create_delta_index returns is fine
318
203
        with nogil:
319
 
            res = create_delta_index(&self._source_infos[0], NULL, &index,
320
 
                                     self._max_bytes_to_index)
321
 
        if res != DELTA_OK:
322
 
            raise _translate_delta_failure(res)
323
 
        self._index = index
 
204
            self._index = create_delta_index(&self._source_infos[0], NULL)
 
205
        assert self._index != NULL
324
206
 
325
207
    cdef _expand_sources(self):
326
208
        raise RuntimeError('if we move self._source_infos, then we need to'
337
219
        cdef void * delta
338
220
        cdef unsigned long delta_size
339
221
        cdef unsigned long c_max_delta_size
340
 
        cdef delta_result res
341
222
 
342
223
        if self._index == NULL:
343
224
            if len(self._sources) == 0:
356
237
        #       allocate the bytes into the final string
357
238
        c_max_delta_size = max_delta_size
358
239
        with nogil:
359
 
            res = create_delta(self._index, target, target_size,
360
 
                               &delta_size, c_max_delta_size, &delta)
 
240
            delta = create_delta(self._index,
 
241
                                 target, target_size,
 
242
                                 &delta_size, c_max_delta_size)
361
243
        result = None
362
 
        if res == DELTA_OK:
 
244
        if delta:
363
245
            result = PyString_FromStringAndSize(<char *>delta, delta_size)
364
246
            free(delta)
365
 
        elif res != DELTA_SIZE_TOO_BIG:
366
 
            raise _translate_delta_failure(res)
367
247
        return result
368
248
 
369
249
 
470
350
                # Copy instruction
471
351
                data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
472
352
                if (cp_off + cp_size < cp_size or
473
 
                    cp_off + cp_size > <unsigned int>source_size or
474
 
                    cp_size > <unsigned int>size):
 
353
                    cp_off + cp_size > source_size or
 
354
                    cp_size > size):
475
355
                    failed = 1
476
356
                    break
477
357
                memcpy(out, source + cp_off, cp_size)