~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_groupcompress_pyx.pyx

Merge bzr.dev, update to use new hooks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
46
46
        unsigned long agg_offset
47
47
    struct delta_index:
48
48
        pass
49
 
    delta_index * create_delta_index(source_info *src, delta_index *old) nogil
50
 
    delta_index * create_delta_index_from_delta(source_info *delta,
51
 
                                                delta_index *old) nogil
 
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
52
64
    void free_delta_index(delta_index *index) nogil
53
 
    void *create_delta(delta_index *indexes,
54
 
             void *buf, unsigned long bufsize,
55
 
             unsigned long *delta_size, unsigned long max_delta_size) 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
56
70
    unsigned long get_delta_hdr_size(unsigned char **datap,
57
71
                                     unsigned char *top) nogil
58
72
    unsigned long sizeof_delta_index(delta_index *index)
59
73
    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)
60
78
 
61
79
 
62
80
cdef void *safe_malloc(size_t count) except NULL:
86
104
    return DeltaIndex(source)
87
105
 
88
106
 
 
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
 
89
130
cdef class DeltaIndex:
90
131
 
91
132
    # We need Pyrex 0.9.8+ to understand a 'list' definition, and this object
96
137
    cdef delta_index *_index
97
138
    cdef public unsigned long _source_offset
98
139
    cdef readonly unsigned int _max_num_sources
 
140
    cdef public int _max_bytes_to_index
99
141
 
100
 
    def __init__(self, source=None):
 
142
    def __init__(self, source=None, max_bytes_to_index=None):
101
143
        self._sources = []
102
144
        self._index = NULL
103
145
        self._max_num_sources = 65000
104
146
        self._source_infos = <source_info *>safe_malloc(sizeof(source_info)
105
147
                                                        * self._max_num_sources)
106
148
        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
107
152
 
108
153
        if source is not None:
109
154
            self.add_source(source, 0)
137
182
    def _has_index(self):
138
183
        return (self._index != NULL)
139
184
 
 
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
 
140
223
    def add_delta_source(self, delta, unadded_bytes):
141
224
        """Add a new delta to the source texts.
142
225
 
147
230
        cdef char *c_delta
148
231
        cdef Py_ssize_t c_delta_size
149
232
        cdef delta_index *index
 
233
        cdef delta_result res
150
234
        cdef unsigned int source_location
151
235
        cdef source_info *src
152
236
        cdef unsigned int num_indexes
165
249
        src.size = c_delta_size
166
250
        src.agg_offset = self._source_offset + unadded_bytes
167
251
        with nogil:
168
 
            index = create_delta_index_from_delta(src, self._index)
 
252
            res = create_delta_index_from_delta(src, self._index, &index)
 
253
        if res != DELTA_OK:
 
254
            raise _translate_delta_failure(res)
169
255
        self._source_offset = src.agg_offset + src.size
170
 
        if index != NULL:
 
256
        if index != self._index:
171
257
            free_delta_index(self._index)
172
258
            self._index = index
173
259
 
177
263
        :param source: The text in question, this must be a byte string
178
264
        :param unadded_bytes: Assume there are this many bytes that didn't get
179
265
            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.
180
270
        """
181
271
        cdef char *c_source
182
272
        cdef Py_ssize_t c_source_size
183
273
        cdef delta_index *index
 
274
        cdef delta_result res
184
275
        cdef unsigned int source_location
185
276
        cdef source_info *src
186
277
        cdef unsigned int num_indexes
 
278
        cdef int max_num_entries
187
279
 
188
280
        if not PyString_CheckExact(source):
189
281
            raise TypeError('source is not a str')
206
298
        # We delay creating the index on the first insert
207
299
        if source_location != 0:
208
300
            with nogil:
209
 
                index = create_delta_index(src, self._index)
210
 
            if index != NULL:
 
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:
211
306
                free_delta_index(self._index)
212
307
                self._index = index
213
308
 
214
309
    cdef _populate_first_index(self):
215
310
        cdef delta_index *index
 
311
        cdef delta_result res
216
312
        if len(self._sources) != 1 or self._index != NULL:
217
313
            raise AssertionError('_populate_first_index should only be'
218
314
                ' called when we have a single source and no index yet')
219
315
 
220
 
        # We know that self._index is already NULL, so whatever
221
 
        # create_delta_index returns is fine
 
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
222
318
        with nogil:
223
 
            self._index = create_delta_index(&self._source_infos[0], NULL)
224
 
        assert self._index != NULL
 
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
225
324
 
226
325
    cdef _expand_sources(self):
227
326
        raise RuntimeError('if we move self._source_infos, then we need to'
238
337
        cdef void * delta
239
338
        cdef unsigned long delta_size
240
339
        cdef unsigned long c_max_delta_size
 
340
        cdef delta_result res
241
341
 
242
342
        if self._index == NULL:
243
343
            if len(self._sources) == 0:
256
356
        #       allocate the bytes into the final string
257
357
        c_max_delta_size = max_delta_size
258
358
        with nogil:
259
 
            delta = create_delta(self._index,
260
 
                                 target, target_size,
261
 
                                 &delta_size, c_max_delta_size)
 
359
            res = create_delta(self._index, target, target_size,
 
360
                               &delta_size, c_max_delta_size, &delta)
262
361
        result = None
263
 
        if delta:
 
362
        if res == DELTA_OK:
264
363
            result = PyString_FromStringAndSize(<char *>delta, delta_size)
265
364
            free(delta)
 
365
        elif res != DELTA_SIZE_TOO_BIG:
 
366
            raise _translate_delta_failure(res)
266
367
        return result
267
368
 
268
369
 
369
470
                # Copy instruction
370
471
                data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
371
472
                if (cp_off + cp_size < cp_size or
372
 
                    cp_off + cp_size > source_size or
373
 
                    cp_size > size):
 
473
                    cp_off + cp_size > <unsigned int>source_size or
 
474
                    cp_size > <unsigned int>size):
374
475
                    failed = 1
375
476
                    break
376
477
                memcpy(out, source + cp_off, cp_size)