~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_groupcompress_pyx.pyx

  • Committer: Vincent Ladeuil
  • Date: 2010-10-26 08:08:23 UTC
  • mfrom: (5514.1.1 665100-content-type)
  • mto: This revision was merged to the branch mainline in revision 5516.
  • Revision ID: v.ladeuil+lp@free.fr-20101026080823-3wggo03b7cpn9908
Correctly set the Content-Type header when POSTing http requests

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