~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_groupcompress_pyx.pyx

Add a max_entries_per_source to DeltaIndex

This changes the sampling rate in the create_delta_from_source.
This isn't exposed higher up yet, but it work so far.

Show diffs side-by-side

added added

removed removed

Lines of Context:
56
56
        DELTA_SIZE_TOO_BIG
57
57
    delta_result create_delta_index(source_info *src,
58
58
                                    delta_index *old,
59
 
                                    delta_index **fresh) nogil
 
59
                                    delta_index **fresh,
 
60
                                    int max_entries) nogil
60
61
    delta_result create_delta_index_from_delta(source_info *delta,
61
62
                                               delta_index *old,
62
63
                                               delta_index **fresh) nogil
70
71
                                     unsigned char *top) nogil
71
72
    unsigned long sizeof_delta_index(delta_index *index)
72
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 c_rabin_hash "rabin_hash" (char *data)
73
78
 
74
79
 
75
80
cdef void *safe_malloc(size_t count) except NULL:
113
118
    return AssertionError("Unrecognised delta result code: %d" % result)
114
119
 
115
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(c_rabin_hash(PyString_AS_STRING(content)))
 
128
 
 
129
 
116
130
cdef class DeltaIndex:
117
131
 
118
132
    # We need Pyrex 0.9.8+ to understand a 'list' definition, and this object
123
137
    cdef delta_index *_index
124
138
    cdef public unsigned long _source_offset
125
139
    cdef readonly unsigned int _max_num_sources
 
140
    cdef public int _max_entries_per_source
126
141
 
127
 
    def __init__(self, source=None):
 
142
    def __init__(self, source=None, max_entries_per_source=None):
128
143
        self._sources = []
129
144
        self._index = NULL
130
145
        self._max_num_sources = 65000
131
146
        self._source_infos = <source_info *>safe_malloc(sizeof(source_info)
132
147
                                                        * self._max_num_sources)
133
148
        self._source_offset = 0
 
149
        self._max_entries_per_source = 0
 
150
        if max_entries_per_source is not None:
 
151
            self._max_entries_per_source = max_entries_per_source
134
152
 
135
153
        if source is not None:
136
154
            self.add_source(source, 0)
164
182
    def _has_index(self):
165
183
        return (self._index != NULL)
166
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
 
167
223
    def add_delta_source(self, delta, unadded_bytes):
168
224
        """Add a new delta to the source texts.
169
225
 
207
263
        :param source: The text in question, this must be a byte string
208
264
        :param unadded_bytes: Assume there are this many bytes that didn't get
209
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.
210
270
        """
211
271
        cdef char *c_source
212
272
        cdef Py_ssize_t c_source_size
215
275
        cdef unsigned int source_location
216
276
        cdef source_info *src
217
277
        cdef unsigned int num_indexes
 
278
        cdef int max_num_entries
218
279
 
219
280
        if not PyString_CheckExact(source):
220
281
            raise TypeError('source is not a str')
237
298
        # We delay creating the index on the first insert
238
299
        if source_location != 0:
239
300
            with nogil:
240
 
                res = create_delta_index(src, self._index, &index)
 
301
                res = create_delta_index(src, self._index, &index,
 
302
                                         self._max_entries_per_source)
241
303
            if res != DELTA_OK:
242
304
                raise _translate_delta_failure(res)
243
305
            if index != self._index:
254
316
        # We know that self._index is already NULL, so create_delta_index
255
317
        # will always create a new index unless there's a malloc failure
256
318
        with nogil:
257
 
            res = create_delta_index(&self._source_infos[0], NULL, &index)
 
319
            res = create_delta_index(&self._source_infos[0], NULL, &index,
 
320
                                     self._max_entries_per_source)
258
321
        if res != DELTA_OK:
259
322
            raise _translate_delta_failure(res)
260
323
        self._index = index