~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: 2011-05-04 12:10:51 UTC
  • mfrom: (5819.1.4 777007-developer-doc)
  • Revision ID: pqm@pqm.ubuntu.com-20110504121051-aovlsmqiivjmc4fc
(jelmer) Small fixes to developer documentation. (Jonathan Riddell)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Compiled extensions for doing compression."""
 
18
 
 
19
#python2.4 support
 
20
cdef extern from "python-compat.h":
 
21
    pass
 
22
 
 
23
 
 
24
cdef extern from "Python.h":
 
25
    ctypedef struct PyObject:
 
26
        pass
 
27
    ctypedef int Py_ssize_t # Required for older pyrex versions
 
28
    int PyString_CheckExact(object)
 
29
    char * PyString_AS_STRING(object)
 
30
    Py_ssize_t PyString_GET_SIZE(object)
 
31
    object PyString_FromStringAndSize(char *, Py_ssize_t)
 
32
 
 
33
 
 
34
cdef extern from *:
 
35
    ctypedef unsigned long size_t
 
36
    void * malloc(size_t) nogil
 
37
    void * realloc(void *, size_t) nogil
 
38
    void free(void *) nogil
 
39
    void memcpy(void *, void *, size_t) nogil
 
40
 
 
41
 
 
42
cdef extern from "delta.h":
 
43
    struct source_info:
 
44
        void *buf
 
45
        unsigned long size
 
46
        unsigned long agg_offset
 
47
    struct delta_index:
 
48
        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) nogil
 
60
    delta_result create_delta_index_from_delta(source_info *delta,
 
61
                                               delta_index *old,
 
62
                                               delta_index **fresh) nogil
 
63
    void free_delta_index(delta_index *index) nogil
 
64
    delta_result create_delta(delta_index *indexes,
 
65
                              void *buf, unsigned long bufsize,
 
66
                              unsigned long *delta_size,
 
67
                              unsigned long max_delta_size,
 
68
                              void **delta_data) nogil
 
69
    unsigned long get_delta_hdr_size(unsigned char **datap,
 
70
                                     unsigned char *top) nogil
 
71
    unsigned long sizeof_delta_index(delta_index *index)
 
72
    Py_ssize_t DELTA_SIZE_MIN
 
73
 
 
74
 
 
75
cdef void *safe_malloc(size_t count) except NULL:
 
76
    cdef void *result
 
77
    result = malloc(count)
 
78
    if result == NULL:
 
79
        raise MemoryError('Failed to allocate %d bytes of memory' % (count,))
 
80
    return result
 
81
 
 
82
 
 
83
cdef void *safe_realloc(void * old, size_t count) except NULL:
 
84
    cdef void *result
 
85
    result = realloc(old, count)
 
86
    if result == NULL:
 
87
        raise MemoryError('Failed to reallocate to %d bytes of memory'
 
88
                          % (count,))
 
89
    return result
 
90
 
 
91
 
 
92
cdef int safe_free(void **val) except -1:
 
93
    assert val != NULL
 
94
    if val[0] != NULL:
 
95
        free(val[0])
 
96
        val[0] = NULL
 
97
 
 
98
def make_delta_index(source):
 
99
    return DeltaIndex(source)
 
100
 
 
101
 
 
102
cdef object _translate_delta_failure(delta_result result):
 
103
    if result == DELTA_OUT_OF_MEMORY:
 
104
        return MemoryError("Delta function failed to allocate memory")
 
105
    elif result == DELTA_INDEX_NEEDED:
 
106
        return ValueError("Delta function requires delta_index param")
 
107
    elif result == DELTA_SOURCE_EMPTY:
 
108
        return ValueError("Delta function given empty source_info param")
 
109
    elif result == DELTA_SOURCE_BAD:
 
110
        return RuntimeError("Delta function given invalid source_info param")
 
111
    elif result == DELTA_BUFFER_EMPTY:
 
112
        return ValueError("Delta function given empty buffer params")
 
113
    return AssertionError("Unrecognised delta result code: %d" % result)
 
114
 
 
115
 
 
116
cdef class DeltaIndex:
 
117
 
 
118
    # We need Pyrex 0.9.8+ to understand a 'list' definition, and this object
 
119
    # isn't performance critical
 
120
    # cdef readonly list _sources
 
121
    cdef readonly object _sources
 
122
    cdef source_info *_source_infos
 
123
    cdef delta_index *_index
 
124
    cdef public unsigned long _source_offset
 
125
    cdef readonly unsigned int _max_num_sources
 
126
 
 
127
    def __init__(self, source=None):
 
128
        self._sources = []
 
129
        self._index = NULL
 
130
        self._max_num_sources = 65000
 
131
        self._source_infos = <source_info *>safe_malloc(sizeof(source_info)
 
132
                                                        * self._max_num_sources)
 
133
        self._source_offset = 0
 
134
 
 
135
        if source is not None:
 
136
            self.add_source(source, 0)
 
137
 
 
138
    def __sizeof__(self):
 
139
        # We want to track the _source_infos allocations, but the referenced
 
140
        # void* are actually tracked in _sources itself.
 
141
        # XXX: Cython is capable of doing sizeof(class) and returning the size
 
142
        #      of the underlying struct. Pyrex (<= 0.9.9) refuses, so we need
 
143
        #      to do it manually. *sigh* Note that we might get it wrong
 
144
        #      because of alignment issues.
 
145
        cdef Py_ssize_t size
 
146
        # PyObject start, vtable *, 3 object pointers, 2 C ints
 
147
        size = ((sizeof(PyObject) + sizeof(void*) + 3*sizeof(PyObject*)
 
148
                 + sizeof(unsigned long)
 
149
                 + sizeof(unsigned int))
 
150
                + (sizeof(source_info) * self._max_num_sources)
 
151
                + sizeof_delta_index(self._index))
 
152
        return size
 
153
 
 
154
    def __repr__(self):
 
155
        return '%s(%d, %d)' % (self.__class__.__name__,
 
156
            len(self._sources), self._source_offset)
 
157
 
 
158
    def __dealloc__(self):
 
159
        if self._index != NULL:
 
160
            free_delta_index(self._index)
 
161
            self._index = NULL
 
162
        safe_free(<void **>&self._source_infos)
 
163
 
 
164
    def _has_index(self):
 
165
        return (self._index != NULL)
 
166
 
 
167
    def add_delta_source(self, delta, unadded_bytes):
 
168
        """Add a new delta to the source texts.
 
169
 
 
170
        :param delta: The text of the delta, this must be a byte string.
 
171
        :param unadded_bytes: Number of bytes that were added to the source
 
172
            that were not indexed.
 
173
        """
 
174
        cdef char *c_delta
 
175
        cdef Py_ssize_t c_delta_size
 
176
        cdef delta_index *index
 
177
        cdef delta_result res
 
178
        cdef unsigned int source_location
 
179
        cdef source_info *src
 
180
        cdef unsigned int num_indexes
 
181
 
 
182
        if not PyString_CheckExact(delta):
 
183
            raise TypeError('delta is not a str')
 
184
 
 
185
        source_location = len(self._sources)
 
186
        if source_location >= self._max_num_sources:
 
187
            self._expand_sources()
 
188
        self._sources.append(delta)
 
189
        c_delta = PyString_AS_STRING(delta)
 
190
        c_delta_size = PyString_GET_SIZE(delta)
 
191
        src = self._source_infos + source_location
 
192
        src.buf = c_delta
 
193
        src.size = c_delta_size
 
194
        src.agg_offset = self._source_offset + unadded_bytes
 
195
        with nogil:
 
196
            res = create_delta_index_from_delta(src, self._index, &index)
 
197
        if res != DELTA_OK:
 
198
            raise _translate_delta_failure(res)
 
199
        self._source_offset = src.agg_offset + src.size
 
200
        if index != self._index:
 
201
            free_delta_index(self._index)
 
202
            self._index = index
 
203
 
 
204
    def add_source(self, source, unadded_bytes):
 
205
        """Add a new bit of source text to the delta indexes.
 
206
 
 
207
        :param source: The text in question, this must be a byte string
 
208
        :param unadded_bytes: Assume there are this many bytes that didn't get
 
209
            added between this source and the end of the previous source.
 
210
        """
 
211
        cdef char *c_source
 
212
        cdef Py_ssize_t c_source_size
 
213
        cdef delta_index *index
 
214
        cdef delta_result res
 
215
        cdef unsigned int source_location
 
216
        cdef source_info *src
 
217
        cdef unsigned int num_indexes
 
218
 
 
219
        if not PyString_CheckExact(source):
 
220
            raise TypeError('source is not a str')
 
221
 
 
222
        source_location = len(self._sources)
 
223
        if source_location >= self._max_num_sources:
 
224
            self._expand_sources()
 
225
        if source_location != 0 and self._index == NULL:
 
226
            # We were lazy about populating the index, create it now
 
227
            self._populate_first_index()
 
228
        self._sources.append(source)
 
229
        c_source = PyString_AS_STRING(source)
 
230
        c_source_size = PyString_GET_SIZE(source)
 
231
        src = self._source_infos + source_location
 
232
        src.buf = c_source
 
233
        src.size = c_source_size
 
234
 
 
235
        src.agg_offset = self._source_offset + unadded_bytes
 
236
        self._source_offset = src.agg_offset + src.size
 
237
        # We delay creating the index on the first insert
 
238
        if source_location != 0:
 
239
            with nogil:
 
240
                res = create_delta_index(src, self._index, &index)
 
241
            if res != DELTA_OK:
 
242
                raise _translate_delta_failure(res)
 
243
            if index != self._index:
 
244
                free_delta_index(self._index)
 
245
                self._index = index
 
246
 
 
247
    cdef _populate_first_index(self):
 
248
        cdef delta_index *index
 
249
        cdef delta_result res
 
250
        if len(self._sources) != 1 or self._index != NULL:
 
251
            raise AssertionError('_populate_first_index should only be'
 
252
                ' called when we have a single source and no index yet')
 
253
 
 
254
        # We know that self._index is already NULL, so create_delta_index
 
255
        # will always create a new index unless there's a malloc failure
 
256
        with nogil:
 
257
            res = create_delta_index(&self._source_infos[0], NULL, &index)
 
258
        if res != DELTA_OK:
 
259
            raise _translate_delta_failure(res)
 
260
        self._index = index
 
261
 
 
262
    cdef _expand_sources(self):
 
263
        raise RuntimeError('if we move self._source_infos, then we need to'
 
264
                           ' change all of the index pointers as well.')
 
265
        self._max_num_sources = self._max_num_sources * 2
 
266
        self._source_infos = <source_info *>safe_realloc(self._source_infos,
 
267
                                                sizeof(source_info)
 
268
                                                * self._max_num_sources)
 
269
 
 
270
    def make_delta(self, target_bytes, max_delta_size=0):
 
271
        """Create a delta from the current source to the target bytes."""
 
272
        cdef char *target
 
273
        cdef Py_ssize_t target_size
 
274
        cdef void * delta
 
275
        cdef unsigned long delta_size
 
276
        cdef unsigned long c_max_delta_size
 
277
        cdef delta_result res
 
278
 
 
279
        if self._index == NULL:
 
280
            if len(self._sources) == 0:
 
281
                return None
 
282
            # We were just lazy about generating the index
 
283
            self._populate_first_index()
 
284
 
 
285
        if not PyString_CheckExact(target_bytes):
 
286
            raise TypeError('target is not a str')
 
287
 
 
288
        target = PyString_AS_STRING(target_bytes)
 
289
        target_size = PyString_GET_SIZE(target_bytes)
 
290
 
 
291
        # TODO: inline some of create_delta so we at least don't have to double
 
292
        #       malloc, and can instead use PyString_FromStringAndSize, to
 
293
        #       allocate the bytes into the final string
 
294
        c_max_delta_size = max_delta_size
 
295
        with nogil:
 
296
            res = create_delta(self._index, target, target_size,
 
297
                               &delta_size, c_max_delta_size, &delta)
 
298
        result = None
 
299
        if res == DELTA_OK:
 
300
            result = PyString_FromStringAndSize(<char *>delta, delta_size)
 
301
            free(delta)
 
302
        elif res != DELTA_SIZE_TOO_BIG:
 
303
            raise _translate_delta_failure(res)
 
304
        return result
 
305
 
 
306
 
 
307
def make_delta(source_bytes, target_bytes):
 
308
    """Create a delta, this is a wrapper around DeltaIndex.make_delta."""
 
309
    di = DeltaIndex(source_bytes)
 
310
    return di.make_delta(target_bytes)
 
311
 
 
312
 
 
313
def apply_delta(source_bytes, delta_bytes):
 
314
    """Apply a delta generated by make_delta to source_bytes."""
 
315
    cdef char *source
 
316
    cdef Py_ssize_t source_size
 
317
    cdef char *delta
 
318
    cdef Py_ssize_t delta_size
 
319
 
 
320
    if not PyString_CheckExact(source_bytes):
 
321
        raise TypeError('source is not a str')
 
322
    if not PyString_CheckExact(delta_bytes):
 
323
        raise TypeError('delta is not a str')
 
324
    source = PyString_AS_STRING(source_bytes)
 
325
    source_size = PyString_GET_SIZE(source_bytes)
 
326
    delta = PyString_AS_STRING(delta_bytes)
 
327
    delta_size = PyString_GET_SIZE(delta_bytes)
 
328
    # Code taken from patch-delta.c, only brought here to give better error
 
329
    # handling, and to avoid double allocating memory
 
330
    if (delta_size < DELTA_SIZE_MIN):
 
331
        # XXX: Invalid delta block
 
332
        raise RuntimeError('delta_size %d smaller than min delta size %d'
 
333
                           % (delta_size, DELTA_SIZE_MIN))
 
334
 
 
335
    return _apply_delta(source, source_size, delta, delta_size)
 
336
 
 
337
 
 
338
cdef unsigned char *_decode_copy_instruction(unsigned char *bytes,
 
339
    unsigned char cmd, unsigned int *offset,
 
340
    unsigned int *length) nogil: # cannot_raise
 
341
    """Decode a copy instruction from the next few bytes.
 
342
 
 
343
    A copy instruction is a variable number of bytes, so we will parse the
 
344
    bytes we care about, and return the new position, as well as the offset and
 
345
    length referred to in the bytes.
 
346
 
 
347
    :param bytes: Pointer to the start of bytes after cmd
 
348
    :param cmd: The command code
 
349
    :return: Pointer to the bytes just after the last decode byte
 
350
    """
 
351
    cdef unsigned int off, size, count
 
352
    off = 0
 
353
    size = 0
 
354
    count = 0
 
355
    if (cmd & 0x01):
 
356
        off = bytes[count]
 
357
        count = count + 1
 
358
    if (cmd & 0x02):
 
359
        off = off | (bytes[count] << 8)
 
360
        count = count + 1
 
361
    if (cmd & 0x04):
 
362
        off = off | (bytes[count] << 16)
 
363
        count = count + 1
 
364
    if (cmd & 0x08):
 
365
        off = off | (bytes[count] << 24)
 
366
        count = count + 1
 
367
    if (cmd & 0x10):
 
368
        size = bytes[count]
 
369
        count = count + 1
 
370
    if (cmd & 0x20):
 
371
        size = size | (bytes[count] << 8)
 
372
        count = count + 1
 
373
    if (cmd & 0x40):
 
374
        size = size | (bytes[count] << 16)
 
375
        count = count + 1
 
376
    if (size == 0):
 
377
        size = 0x10000
 
378
    offset[0] = off
 
379
    length[0] = size
 
380
    return bytes + count
 
381
 
 
382
 
 
383
cdef object _apply_delta(char *source, Py_ssize_t source_size,
 
384
                         char *delta, Py_ssize_t delta_size):
 
385
    """common functionality between apply_delta and apply_delta_to_source."""
 
386
    cdef unsigned char *data, *top
 
387
    cdef unsigned char *dst_buf, *out, cmd
 
388
    cdef Py_ssize_t size
 
389
    cdef unsigned int cp_off, cp_size
 
390
    cdef int failed
 
391
 
 
392
    data = <unsigned char *>delta
 
393
    top = data + delta_size
 
394
 
 
395
    # now the result size
 
396
    size = get_delta_hdr_size(&data, top)
 
397
    result = PyString_FromStringAndSize(NULL, size)
 
398
    dst_buf = <unsigned char*>PyString_AS_STRING(result)
 
399
 
 
400
    failed = 0
 
401
    with nogil:
 
402
        out = dst_buf
 
403
        while (data < top):
 
404
            cmd = data[0]
 
405
            data = data + 1
 
406
            if (cmd & 0x80):
 
407
                # Copy instruction
 
408
                data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
 
409
                if (cp_off + cp_size < cp_size or
 
410
                    cp_off + cp_size > <unsigned int>source_size or
 
411
                    cp_size > <unsigned int>size):
 
412
                    failed = 1
 
413
                    break
 
414
                memcpy(out, source + cp_off, cp_size)
 
415
                out = out + cp_size
 
416
                size = size - cp_size
 
417
            else:
 
418
                # Insert instruction
 
419
                if cmd == 0:
 
420
                    # cmd == 0 is reserved for future encoding
 
421
                    # extensions. In the mean time we must fail when
 
422
                    # encountering them (might be data corruption).
 
423
                    failed = 2
 
424
                    break
 
425
                if cmd > size:
 
426
                    failed = 3
 
427
                    break
 
428
                memcpy(out, data, cmd)
 
429
                out = out + cmd
 
430
                data = data + cmd
 
431
                size = size - cmd
 
432
    if failed:
 
433
        if failed == 1:
 
434
            raise ValueError('Something wrong with:'
 
435
                ' cp_off = %s, cp_size = %s'
 
436
                ' source_size = %s, size = %s'
 
437
                % (cp_off, cp_size, source_size, size))
 
438
        elif failed == 2:
 
439
            raise ValueError('Got delta opcode: 0, not supported')
 
440
        elif failed == 3:
 
441
            raise ValueError('Insert instruction longer than remaining'
 
442
                ' bytes: %d > %d' % (cmd, size))
 
443
 
 
444
    # sanity check
 
445
    if (data != top or size != 0):
 
446
        raise RuntimeError('Did not extract the number of bytes we expected'
 
447
            ' we were left with %d bytes in "size", and top - data = %d'
 
448
            % (size, <int>(top - data)))
 
449
        return None
 
450
 
 
451
    # *dst_size = out - dst_buf;
 
452
    if (out - dst_buf) != PyString_GET_SIZE(result):
 
453
        raise RuntimeError('Number of bytes extracted did not match the'
 
454
            ' size encoded in the delta header.')
 
455
    return result
 
456
 
 
457
 
 
458
def apply_delta_to_source(source, delta_start, delta_end):
 
459
    """Extract a delta from source bytes, and apply it."""
 
460
    cdef char *c_source
 
461
    cdef Py_ssize_t c_source_size
 
462
    cdef char *c_delta
 
463
    cdef Py_ssize_t c_delta_size
 
464
    cdef Py_ssize_t c_delta_start, c_delta_end
 
465
 
 
466
    if not PyString_CheckExact(source):
 
467
        raise TypeError('source is not a str')
 
468
    c_source_size = PyString_GET_SIZE(source)
 
469
    c_delta_start = delta_start
 
470
    c_delta_end = delta_end
 
471
    if c_delta_start >= c_source_size:
 
472
        raise ValueError('delta starts after source')
 
473
    if c_delta_end > c_source_size:
 
474
        raise ValueError('delta ends after source')
 
475
    if c_delta_start >= c_delta_end:
 
476
        raise ValueError('delta starts after it ends')
 
477
 
 
478
    c_delta_size = c_delta_end - c_delta_start
 
479
    c_source = PyString_AS_STRING(source)
 
480
    c_delta = c_source + c_delta_start
 
481
    # We don't use source_size, because we know the delta should not refer to
 
482
    # any bytes after it starts
 
483
    return _apply_delta(c_source, c_delta_start, c_delta, c_delta_size)
 
484
 
 
485
 
 
486
def encode_base128_int(val):
 
487
    """Convert an integer into a 7-bit lsb encoding."""
 
488
    cdef unsigned int c_val
 
489
    cdef Py_ssize_t count
 
490
    cdef unsigned int num_bytes
 
491
    cdef unsigned char c_bytes[8] # max size for 32-bit int is 5 bytes
 
492
 
 
493
    c_val = val
 
494
    count = 0
 
495
    while c_val >= 0x80 and count < 8:
 
496
        c_bytes[count] = <unsigned char>((c_val | 0x80) & 0xFF)
 
497
        c_val = c_val >> 7
 
498
        count = count + 1
 
499
    if count >= 8 or c_val >= 0x80:
 
500
        raise ValueError('encode_base128_int overflowed the buffer')
 
501
    c_bytes[count] = <unsigned char>(c_val & 0xFF)
 
502
    count = count + 1
 
503
    return PyString_FromStringAndSize(<char *>c_bytes, count)
 
504
 
 
505
 
 
506
def decode_base128_int(bytes):
 
507
    """Decode an integer from a 7-bit lsb encoding."""
 
508
    cdef int offset
 
509
    cdef int val
 
510
    cdef unsigned int uval
 
511
    cdef int shift
 
512
    cdef Py_ssize_t num_low_bytes
 
513
    cdef unsigned char *c_bytes
 
514
 
 
515
    offset = 0
 
516
    val = 0
 
517
    shift = 0
 
518
    if not PyString_CheckExact(bytes):
 
519
        raise TypeError('bytes is not a string')
 
520
    c_bytes = <unsigned char*>PyString_AS_STRING(bytes)
 
521
    # We take off 1, because we have to be able to decode the non-expanded byte
 
522
    num_low_bytes = PyString_GET_SIZE(bytes) - 1
 
523
    while (c_bytes[offset] & 0x80) and offset < num_low_bytes:
 
524
        val = val | ((c_bytes[offset] & 0x7F) << shift)
 
525
        shift = shift + 7
 
526
        offset = offset + 1
 
527
    if c_bytes[offset] & 0x80:
 
528
        raise ValueError('Data not properly formatted, we ran out of'
 
529
                         ' bytes before 0x80 stopped being set.')
 
530
    val = val | (c_bytes[offset] << shift)
 
531
    offset = offset + 1
 
532
    if val < 0:
 
533
        uval = <unsigned int> val
 
534
        return uval, offset
 
535
    return val, offset
 
536
 
 
537