~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_groupcompress_pyx.pyx

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-10-06 20:45:48 UTC
  • mfrom: (4728.1.2 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20091006204548-bjnc3z4k256ppimz
MutableTree.has_changes() does not require a tree parameter anymore

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Compiled extensions for doing compression."""
18
18
 
 
19
#python2.4 support
 
20
cdef extern from "python-compat.h":
 
21
    pass
 
22
 
 
23
 
 
24
cdef extern from "Python.h":
 
25
    ctypedef int Py_ssize_t # Required for older pyrex versions
 
26
    int PyString_CheckExact(object)
 
27
    char * PyString_AS_STRING(object)
 
28
    Py_ssize_t PyString_GET_SIZE(object)
 
29
    object PyString_FromStringAndSize(char *, Py_ssize_t)
 
30
 
 
31
 
19
32
cdef extern from *:
20
33
    ctypedef unsigned long size_t
21
34
    void * malloc(size_t)
23
36
    void free(void *)
24
37
    void memcpy(void *, void *, size_t)
25
38
 
 
39
 
26
40
cdef extern from "delta.h":
27
41
    struct source_info:
28
42
        void *buf
44
58
                      void *delta_buf, unsigned long delta_size,
45
59
                      unsigned long *dst_size)
46
60
 
47
 
cdef extern from "Python.h":
48
 
    int PyString_CheckExact(object)
49
 
    char * PyString_AS_STRING(object)
50
 
    Py_ssize_t PyString_GET_SIZE(object)
51
 
    object PyString_FromStringAndSize(char *, Py_ssize_t)
52
 
 
53
61
 
54
62
cdef void *safe_malloc(size_t count) except NULL:
55
63
    cdef void *result
110
118
            self._index = NULL
111
119
        safe_free(<void **>&self._source_infos)
112
120
 
 
121
    def _has_index(self):
 
122
        return (self._index != NULL)
 
123
 
113
124
    def add_delta_source(self, delta, unadded_bytes):
114
125
        """Add a new delta to the source texts.
115
126
 
163
174
        source_location = len(self._sources)
164
175
        if source_location >= self._max_num_sources:
165
176
            self._expand_sources()
 
177
        if source_location != 0 and self._index == NULL:
 
178
            # We were lazy about populating the index, create it now
 
179
            self._populate_first_index()
166
180
        self._sources.append(source)
167
181
        c_source = PyString_AS_STRING(source)
168
182
        c_source_size = PyString_GET_SIZE(source)
171
185
        src.size = c_source_size
172
186
 
173
187
        src.agg_offset = self._source_offset + unadded_bytes
174
 
        index = create_delta_index(src, self._index)
175
188
        self._source_offset = src.agg_offset + src.size
176
 
        if index != NULL:
177
 
            free_delta_index(self._index)
178
 
            self._index = index
 
189
        # We delay creating the index on the first insert
 
190
        if source_location != 0:
 
191
            index = create_delta_index(src, self._index)
 
192
            if index != NULL:
 
193
                free_delta_index(self._index)
 
194
                self._index = index
 
195
 
 
196
    cdef _populate_first_index(self):
 
197
        cdef delta_index *index
 
198
        if len(self._sources) != 1 or self._index != NULL:
 
199
            raise AssertionError('_populate_first_index should only be'
 
200
                ' called when we have a single source and no index yet')
 
201
 
 
202
        # We know that self._index is already NULL, so whatever
 
203
        # create_delta_index returns is fine
 
204
        self._index = create_delta_index(&self._source_infos[0], NULL)
 
205
        assert self._index != NULL
179
206
 
180
207
    cdef _expand_sources(self):
181
208
        raise RuntimeError('if we move self._source_infos, then we need to'
193
220
        cdef unsigned long delta_size
194
221
 
195
222
        if self._index == NULL:
196
 
            return None
 
223
            if len(self._sources) == 0:
 
224
                return None
 
225
            # We were just lazy about generating the index
 
226
            self._populate_first_index()
197
227
 
198
228
        if not PyString_CheckExact(target_bytes):
199
229
            raise TypeError('target is not a str')
245
275
    return _apply_delta(source, source_size, delta, delta_size)
246
276
 
247
277
 
 
278
cdef unsigned char *_decode_copy_instruction(unsigned char *bytes,
 
279
    unsigned char cmd, unsigned int *offset, unsigned int *length):
 
280
    """Decode a copy instruction from the next few bytes.
 
281
 
 
282
    A copy instruction is a variable number of bytes, so we will parse the
 
283
    bytes we care about, and return the new position, as well as the offset and
 
284
    length referred to in the bytes.
 
285
 
 
286
    :param bytes: Pointer to the start of bytes after cmd
 
287
    :param cmd: The command code
 
288
    :return: Pointer to the bytes just after the last decode byte
 
289
    """
 
290
    cdef unsigned int off, size, count
 
291
    off = 0
 
292
    size = 0
 
293
    count = 0
 
294
    if (cmd & 0x01):
 
295
        off = bytes[count]
 
296
        count = count + 1
 
297
    if (cmd & 0x02):
 
298
        off = off | (bytes[count] << 8)
 
299
        count = count + 1
 
300
    if (cmd & 0x04):
 
301
        off = off | (bytes[count] << 16)
 
302
        count = count + 1
 
303
    if (cmd & 0x08):
 
304
        off = off | (bytes[count] << 24)
 
305
        count = count + 1
 
306
    if (cmd & 0x10):
 
307
        size = bytes[count]
 
308
        count = count + 1
 
309
    if (cmd & 0x20):
 
310
        size = size | (bytes[count] << 8)
 
311
        count = count + 1
 
312
    if (cmd & 0x40):
 
313
        size = size | (bytes[count] << 16)
 
314
        count = count + 1
 
315
    if (size == 0):
 
316
        size = 0x10000
 
317
    offset[0] = off
 
318
    length[0] = size
 
319
    return bytes + count
 
320
 
 
321
 
248
322
cdef object _apply_delta(char *source, Py_ssize_t source_size,
249
323
                         char *delta, Py_ssize_t delta_size):
250
324
    """common functionality between apply_delta and apply_delta_to_source."""
251
325
    cdef unsigned char *data, *top
252
326
    cdef unsigned char *dst_buf, *out, cmd
253
327
    cdef Py_ssize_t size
254
 
    cdef unsigned long cp_off, cp_size
 
328
    cdef unsigned int cp_off, cp_size
255
329
 
256
330
    data = <unsigned char *>delta
257
331
    top = data + delta_size
260
334
    size = get_delta_hdr_size(&data, top)
261
335
    result = PyString_FromStringAndSize(NULL, size)
262
336
    dst_buf = <unsigned char*>PyString_AS_STRING(result)
263
 
    # XXX: The original code added a trailing null here, but this shouldn't be
264
 
    #      necessary when using PyString_FromStringAndSize
265
 
    # dst_buf[size] = 0
266
337
 
267
338
    out = dst_buf
268
339
    while (data < top):
269
340
        cmd = data[0]
270
341
        data = data + 1
271
342
        if (cmd & 0x80):
272
 
            cp_off = cp_size = 0
273
 
            if (cmd & 0x01):
274
 
                cp_off = data[0]
275
 
                data = data + 1
276
 
            if (cmd & 0x02):
277
 
                cp_off = cp_off | (data[0] << 8)
278
 
                data = data + 1
279
 
            if (cmd & 0x04):
280
 
                cp_off = cp_off | (data[0] << 16)
281
 
                data = data + 1
282
 
            if (cmd & 0x08):
283
 
                cp_off = cp_off | (data[0] << 24)
284
 
                data = data + 1
285
 
            if (cmd & 0x10):
286
 
                cp_size = data[0]
287
 
                data = data + 1
288
 
            if (cmd & 0x20):
289
 
                cp_size = cp_size | (data[0] << 8)
290
 
                data = data + 1
291
 
            if (cmd & 0x40):
292
 
                cp_size = cp_size | (data[0] << 16)
293
 
                data = data + 1
294
 
            if (cp_size == 0):
295
 
                cp_size = 0x10000
 
343
            # Copy instruction
 
344
            data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
296
345
            if (cp_off + cp_size < cp_size or
297
346
                cp_off + cp_size > source_size or
298
347
                cp_size > size):
303
352
            memcpy(out, source + cp_off, cp_size)
304
353
            out = out + cp_size
305
354
            size = size - cp_size
306
 
        elif (cmd):
 
355
        else:
 
356
            # Insert instruction
 
357
            if cmd == 0:
 
358
                # cmd == 0 is reserved for future encoding
 
359
                # extensions. In the mean time we must fail when
 
360
                # encountering them (might be data corruption).
 
361
                raise RuntimeError('Got delta opcode: 0, not supported')
307
362
            if (cmd > size):
308
363
                raise RuntimeError('Insert instruction longer than remaining'
309
364
                    ' bytes: %d > %d' % (cmd, size))
311
366
            out = out + cmd
312
367
            data = data + cmd
313
368
            size = size - cmd
314
 
        else:
315
 
            # /*
316
 
            #  * cmd == 0 is reserved for future encoding
317
 
            #  * extensions. In the mean time we must fail when
318
 
            #  * encountering them (might be data corruption).
319
 
            #  */
320
 
            ## /* XXX: error("unexpected delta opcode 0"); */
321
 
            raise RuntimeError('Got delta opcode: 0, not supported')
322
369
 
323
 
    # /* sanity check */
 
370
    # sanity check
324
371
    if (data != top or size != 0):
325
 
        ## /* XXX: error("delta replay has gone wild"); */
326
372
        raise RuntimeError('Did not extract the number of bytes we expected'
327
373
            ' we were left with %d bytes in "size", and top - data = %d'
328
374
            % (size, <int>(top - data)))
329
375
        return None
330
376
 
331
377
    # *dst_size = out - dst_buf;
332
 
    assert (out - dst_buf) == PyString_GET_SIZE(result)
 
378
    if (out - dst_buf) != PyString_GET_SIZE(result):
 
379
        raise RuntimeError('Number of bytes extracted did not match the'
 
380
            ' size encoded in the delta header.')
333
381
    return result
334
382
 
335
383
 
399
447
    # We take off 1, because we have to be able to decode the non-expanded byte
400
448
    num_low_bytes = PyString_GET_SIZE(bytes) - 1
401
449
    while (c_bytes[offset] & 0x80) and offset < num_low_bytes:
402
 
        val |= (c_bytes[offset] & 0x7F) << shift
 
450
        val = val | ((c_bytes[offset] & 0x7F) << shift)
403
451
        shift = shift + 7
404
452
        offset = offset + 1
405
453
    if c_bytes[offset] & 0x80:
406
454
        raise ValueError('Data not properly formatted, we ran out of'
407
455
                         ' bytes before 0x80 stopped being set.')
408
 
    val |= c_bytes[offset] << shift
 
456
    val = val | (c_bytes[offset] << shift)
409
457
    offset = offset + 1
410
458
    if val < 0:
411
459
        uval = <unsigned int> val