~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_groupcompress_pyx.pyx

[merge] robert's knit-performance work

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,
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
64
 
    void free_delta_index(delta_index *index) 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
70
 
    unsigned long get_delta_hdr_size(unsigned char **datap,
71
 
                                     unsigned char *top) nogil
72
 
    unsigned long sizeof_delta_index(delta_index *index)
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)
78
 
 
79
 
 
80
 
cdef void *safe_malloc(size_t count) except NULL:
81
 
    cdef void *result
82
 
    result = malloc(count)
83
 
    if result == NULL:
84
 
        raise MemoryError('Failed to allocate %d bytes of memory' % (count,))
85
 
    return result
86
 
 
87
 
 
88
 
cdef void *safe_realloc(void * old, size_t count) except NULL:
89
 
    cdef void *result
90
 
    result = realloc(old, count)
91
 
    if result == NULL:
92
 
        raise MemoryError('Failed to reallocate to %d bytes of memory'
93
 
                          % (count,))
94
 
    return result
95
 
 
96
 
 
97
 
cdef int safe_free(void **val) except -1:
98
 
    assert val != NULL
99
 
    if val[0] != NULL:
100
 
        free(val[0])
101
 
        val[0] = NULL
102
 
 
103
 
def make_delta_index(source):
104
 
    return DeltaIndex(source)
105
 
 
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
 
 
130
 
cdef class DeltaIndex:
131
 
 
132
 
    # We need Pyrex 0.9.8+ to understand a 'list' definition, and this object
133
 
    # isn't performance critical
134
 
    # cdef readonly list _sources
135
 
    cdef readonly object _sources
136
 
    cdef source_info *_source_infos
137
 
    cdef delta_index *_index
138
 
    cdef public unsigned long _source_offset
139
 
    cdef readonly unsigned int _max_num_sources
140
 
    cdef public int _max_bytes_to_index
141
 
 
142
 
    def __init__(self, source=None, max_bytes_to_index=None):
143
 
        self._sources = []
144
 
        self._index = NULL
145
 
        self._max_num_sources = 65000
146
 
        self._source_infos = <source_info *>safe_malloc(sizeof(source_info)
147
 
                                                        * self._max_num_sources)
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
152
 
 
153
 
        if source is not None:
154
 
            self.add_source(source, 0)
155
 
 
156
 
    def __sizeof__(self):
157
 
        # We want to track the _source_infos allocations, but the referenced
158
 
        # void* are actually tracked in _sources itself.
159
 
        # XXX: Cython is capable of doing sizeof(class) and returning the size
160
 
        #      of the underlying struct. Pyrex (<= 0.9.9) refuses, so we need
161
 
        #      to do it manually. *sigh* Note that we might get it wrong
162
 
        #      because of alignment issues.
163
 
        cdef Py_ssize_t size
164
 
        # PyObject start, vtable *, 3 object pointers, 2 C ints
165
 
        size = ((sizeof(PyObject) + sizeof(void*) + 3*sizeof(PyObject*)
166
 
                 + sizeof(unsigned long)
167
 
                 + sizeof(unsigned int))
168
 
                + (sizeof(source_info) * self._max_num_sources)
169
 
                + sizeof_delta_index(self._index))
170
 
        return size
171
 
 
172
 
    def __repr__(self):
173
 
        return '%s(%d, %d)' % (self.__class__.__name__,
174
 
            len(self._sources), self._source_offset)
175
 
 
176
 
    def __dealloc__(self):
177
 
        if self._index != NULL:
178
 
            free_delta_index(self._index)
179
 
            self._index = NULL
180
 
        safe_free(<void **>&self._source_infos)
181
 
 
182
 
    def _has_index(self):
183
 
        return (self._index != NULL)
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
 
 
223
 
    def add_delta_source(self, delta, unadded_bytes):
224
 
        """Add a new delta to the source texts.
225
 
 
226
 
        :param delta: The text of the delta, this must be a byte string.
227
 
        :param unadded_bytes: Number of bytes that were added to the source
228
 
            that were not indexed.
229
 
        """
230
 
        cdef char *c_delta
231
 
        cdef Py_ssize_t c_delta_size
232
 
        cdef delta_index *index
233
 
        cdef delta_result res
234
 
        cdef unsigned int source_location
235
 
        cdef source_info *src
236
 
        cdef unsigned int num_indexes
237
 
 
238
 
        if not PyString_CheckExact(delta):
239
 
            raise TypeError('delta is not a str')
240
 
 
241
 
        source_location = len(self._sources)
242
 
        if source_location >= self._max_num_sources:
243
 
            self._expand_sources()
244
 
        self._sources.append(delta)
245
 
        c_delta = PyString_AS_STRING(delta)
246
 
        c_delta_size = PyString_GET_SIZE(delta)
247
 
        src = self._source_infos + source_location
248
 
        src.buf = c_delta
249
 
        src.size = c_delta_size
250
 
        src.agg_offset = self._source_offset + unadded_bytes
251
 
        with nogil:
252
 
            res = create_delta_index_from_delta(src, self._index, &index)
253
 
        if res != DELTA_OK:
254
 
            raise _translate_delta_failure(res)
255
 
        self._source_offset = src.agg_offset + src.size
256
 
        if index != self._index:
257
 
            free_delta_index(self._index)
258
 
            self._index = index
259
 
 
260
 
    def add_source(self, source, unadded_bytes):
261
 
        """Add a new bit of source text to the delta indexes.
262
 
 
263
 
        :param source: The text in question, this must be a byte string
264
 
        :param unadded_bytes: Assume there are this many bytes that didn't get
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.
270
 
        """
271
 
        cdef char *c_source
272
 
        cdef Py_ssize_t c_source_size
273
 
        cdef delta_index *index
274
 
        cdef delta_result res
275
 
        cdef unsigned int source_location
276
 
        cdef source_info *src
277
 
        cdef unsigned int num_indexes
278
 
        cdef int max_num_entries
279
 
 
280
 
        if not PyString_CheckExact(source):
281
 
            raise TypeError('source is not a str')
282
 
 
283
 
        source_location = len(self._sources)
284
 
        if source_location >= self._max_num_sources:
285
 
            self._expand_sources()
286
 
        if source_location != 0 and self._index == NULL:
287
 
            # We were lazy about populating the index, create it now
288
 
            self._populate_first_index()
289
 
        self._sources.append(source)
290
 
        c_source = PyString_AS_STRING(source)
291
 
        c_source_size = PyString_GET_SIZE(source)
292
 
        src = self._source_infos + source_location
293
 
        src.buf = c_source
294
 
        src.size = c_source_size
295
 
 
296
 
        src.agg_offset = self._source_offset + unadded_bytes
297
 
        self._source_offset = src.agg_offset + src.size
298
 
        # We delay creating the index on the first insert
299
 
        if source_location != 0:
300
 
            with nogil:
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:
306
 
                free_delta_index(self._index)
307
 
                self._index = index
308
 
 
309
 
    cdef _populate_first_index(self):
310
 
        cdef delta_index *index
311
 
        cdef delta_result res
312
 
        if len(self._sources) != 1 or self._index != NULL:
313
 
            raise AssertionError('_populate_first_index should only be'
314
 
                ' called when we have a single source and no index yet')
315
 
 
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
318
 
        with nogil:
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
324
 
 
325
 
    cdef _expand_sources(self):
326
 
        raise RuntimeError('if we move self._source_infos, then we need to'
327
 
                           ' change all of the index pointers as well.')
328
 
        self._max_num_sources = self._max_num_sources * 2
329
 
        self._source_infos = <source_info *>safe_realloc(self._source_infos,
330
 
                                                sizeof(source_info)
331
 
                                                * self._max_num_sources)
332
 
 
333
 
    def make_delta(self, target_bytes, max_delta_size=0):
334
 
        """Create a delta from the current source to the target bytes."""
335
 
        cdef char *target
336
 
        cdef Py_ssize_t target_size
337
 
        cdef void * delta
338
 
        cdef unsigned long delta_size
339
 
        cdef unsigned long c_max_delta_size
340
 
        cdef delta_result res
341
 
 
342
 
        if self._index == NULL:
343
 
            if len(self._sources) == 0:
344
 
                return None
345
 
            # We were just lazy about generating the index
346
 
            self._populate_first_index()
347
 
 
348
 
        if not PyString_CheckExact(target_bytes):
349
 
            raise TypeError('target is not a str')
350
 
 
351
 
        target = PyString_AS_STRING(target_bytes)
352
 
        target_size = PyString_GET_SIZE(target_bytes)
353
 
 
354
 
        # TODO: inline some of create_delta so we at least don't have to double
355
 
        #       malloc, and can instead use PyString_FromStringAndSize, to
356
 
        #       allocate the bytes into the final string
357
 
        c_max_delta_size = max_delta_size
358
 
        with nogil:
359
 
            res = create_delta(self._index, target, target_size,
360
 
                               &delta_size, c_max_delta_size, &delta)
361
 
        result = None
362
 
        if res == DELTA_OK:
363
 
            result = PyString_FromStringAndSize(<char *>delta, delta_size)
364
 
            free(delta)
365
 
        elif res != DELTA_SIZE_TOO_BIG:
366
 
            raise _translate_delta_failure(res)
367
 
        return result
368
 
 
369
 
 
370
 
def make_delta(source_bytes, target_bytes):
371
 
    """Create a delta, this is a wrapper around DeltaIndex.make_delta."""
372
 
    di = DeltaIndex(source_bytes)
373
 
    return di.make_delta(target_bytes)
374
 
 
375
 
 
376
 
def apply_delta(source_bytes, delta_bytes):
377
 
    """Apply a delta generated by make_delta to source_bytes."""
378
 
    cdef char *source
379
 
    cdef Py_ssize_t source_size
380
 
    cdef char *delta
381
 
    cdef Py_ssize_t delta_size
382
 
 
383
 
    if not PyString_CheckExact(source_bytes):
384
 
        raise TypeError('source is not a str')
385
 
    if not PyString_CheckExact(delta_bytes):
386
 
        raise TypeError('delta is not a str')
387
 
    source = PyString_AS_STRING(source_bytes)
388
 
    source_size = PyString_GET_SIZE(source_bytes)
389
 
    delta = PyString_AS_STRING(delta_bytes)
390
 
    delta_size = PyString_GET_SIZE(delta_bytes)
391
 
    # Code taken from patch-delta.c, only brought here to give better error
392
 
    # handling, and to avoid double allocating memory
393
 
    if (delta_size < DELTA_SIZE_MIN):
394
 
        # XXX: Invalid delta block
395
 
        raise RuntimeError('delta_size %d smaller than min delta size %d'
396
 
                           % (delta_size, DELTA_SIZE_MIN))
397
 
 
398
 
    return _apply_delta(source, source_size, delta, delta_size)
399
 
 
400
 
 
401
 
cdef unsigned char *_decode_copy_instruction(unsigned char *bytes,
402
 
    unsigned char cmd, unsigned int *offset,
403
 
    unsigned int *length) nogil: # cannot_raise
404
 
    """Decode a copy instruction from the next few bytes.
405
 
 
406
 
    A copy instruction is a variable number of bytes, so we will parse the
407
 
    bytes we care about, and return the new position, as well as the offset and
408
 
    length referred to in the bytes.
409
 
 
410
 
    :param bytes: Pointer to the start of bytes after cmd
411
 
    :param cmd: The command code
412
 
    :return: Pointer to the bytes just after the last decode byte
413
 
    """
414
 
    cdef unsigned int off, size, count
415
 
    off = 0
416
 
    size = 0
417
 
    count = 0
418
 
    if (cmd & 0x01):
419
 
        off = bytes[count]
420
 
        count = count + 1
421
 
    if (cmd & 0x02):
422
 
        off = off | (bytes[count] << 8)
423
 
        count = count + 1
424
 
    if (cmd & 0x04):
425
 
        off = off | (bytes[count] << 16)
426
 
        count = count + 1
427
 
    if (cmd & 0x08):
428
 
        off = off | (bytes[count] << 24)
429
 
        count = count + 1
430
 
    if (cmd & 0x10):
431
 
        size = bytes[count]
432
 
        count = count + 1
433
 
    if (cmd & 0x20):
434
 
        size = size | (bytes[count] << 8)
435
 
        count = count + 1
436
 
    if (cmd & 0x40):
437
 
        size = size | (bytes[count] << 16)
438
 
        count = count + 1
439
 
    if (size == 0):
440
 
        size = 0x10000
441
 
    offset[0] = off
442
 
    length[0] = size
443
 
    return bytes + count
444
 
 
445
 
 
446
 
cdef object _apply_delta(char *source, Py_ssize_t source_size,
447
 
                         char *delta, Py_ssize_t delta_size):
448
 
    """common functionality between apply_delta and apply_delta_to_source."""
449
 
    cdef unsigned char *data, *top
450
 
    cdef unsigned char *dst_buf, *out, cmd
451
 
    cdef Py_ssize_t size
452
 
    cdef unsigned int cp_off, cp_size
453
 
    cdef int failed
454
 
 
455
 
    data = <unsigned char *>delta
456
 
    top = data + delta_size
457
 
 
458
 
    # now the result size
459
 
    size = get_delta_hdr_size(&data, top)
460
 
    result = PyString_FromStringAndSize(NULL, size)
461
 
    dst_buf = <unsigned char*>PyString_AS_STRING(result)
462
 
 
463
 
    failed = 0
464
 
    with nogil:
465
 
        out = dst_buf
466
 
        while (data < top):
467
 
            cmd = data[0]
468
 
            data = data + 1
469
 
            if (cmd & 0x80):
470
 
                # Copy instruction
471
 
                data = _decode_copy_instruction(data, cmd, &cp_off, &cp_size)
472
 
                if (cp_off + cp_size < cp_size or
473
 
                    cp_off + cp_size > <unsigned int>source_size or
474
 
                    cp_size > <unsigned int>size):
475
 
                    failed = 1
476
 
                    break
477
 
                memcpy(out, source + cp_off, cp_size)
478
 
                out = out + cp_size
479
 
                size = size - cp_size
480
 
            else:
481
 
                # Insert instruction
482
 
                if cmd == 0:
483
 
                    # cmd == 0 is reserved for future encoding
484
 
                    # extensions. In the mean time we must fail when
485
 
                    # encountering them (might be data corruption).
486
 
                    failed = 2
487
 
                    break
488
 
                if cmd > size:
489
 
                    failed = 3
490
 
                    break
491
 
                memcpy(out, data, cmd)
492
 
                out = out + cmd
493
 
                data = data + cmd
494
 
                size = size - cmd
495
 
    if failed:
496
 
        if failed == 1:
497
 
            raise ValueError('Something wrong with:'
498
 
                ' cp_off = %s, cp_size = %s'
499
 
                ' source_size = %s, size = %s'
500
 
                % (cp_off, cp_size, source_size, size))
501
 
        elif failed == 2:
502
 
            raise ValueError('Got delta opcode: 0, not supported')
503
 
        elif failed == 3:
504
 
            raise ValueError('Insert instruction longer than remaining'
505
 
                ' bytes: %d > %d' % (cmd, size))
506
 
 
507
 
    # sanity check
508
 
    if (data != top or size != 0):
509
 
        raise RuntimeError('Did not extract the number of bytes we expected'
510
 
            ' we were left with %d bytes in "size", and top - data = %d'
511
 
            % (size, <int>(top - data)))
512
 
        return None
513
 
 
514
 
    # *dst_size = out - dst_buf;
515
 
    if (out - dst_buf) != PyString_GET_SIZE(result):
516
 
        raise RuntimeError('Number of bytes extracted did not match the'
517
 
            ' size encoded in the delta header.')
518
 
    return result
519
 
 
520
 
 
521
 
def apply_delta_to_source(source, delta_start, delta_end):
522
 
    """Extract a delta from source bytes, and apply it."""
523
 
    cdef char *c_source
524
 
    cdef Py_ssize_t c_source_size
525
 
    cdef char *c_delta
526
 
    cdef Py_ssize_t c_delta_size
527
 
    cdef Py_ssize_t c_delta_start, c_delta_end
528
 
 
529
 
    if not PyString_CheckExact(source):
530
 
        raise TypeError('source is not a str')
531
 
    c_source_size = PyString_GET_SIZE(source)
532
 
    c_delta_start = delta_start
533
 
    c_delta_end = delta_end
534
 
    if c_delta_start >= c_source_size:
535
 
        raise ValueError('delta starts after source')
536
 
    if c_delta_end > c_source_size:
537
 
        raise ValueError('delta ends after source')
538
 
    if c_delta_start >= c_delta_end:
539
 
        raise ValueError('delta starts after it ends')
540
 
 
541
 
    c_delta_size = c_delta_end - c_delta_start
542
 
    c_source = PyString_AS_STRING(source)
543
 
    c_delta = c_source + c_delta_start
544
 
    # We don't use source_size, because we know the delta should not refer to
545
 
    # any bytes after it starts
546
 
    return _apply_delta(c_source, c_delta_start, c_delta, c_delta_size)
547
 
 
548
 
 
549
 
def encode_base128_int(val):
550
 
    """Convert an integer into a 7-bit lsb encoding."""
551
 
    cdef unsigned int c_val
552
 
    cdef Py_ssize_t count
553
 
    cdef unsigned int num_bytes
554
 
    cdef unsigned char c_bytes[8] # max size for 32-bit int is 5 bytes
555
 
 
556
 
    c_val = val
557
 
    count = 0
558
 
    while c_val >= 0x80 and count < 8:
559
 
        c_bytes[count] = <unsigned char>((c_val | 0x80) & 0xFF)
560
 
        c_val = c_val >> 7
561
 
        count = count + 1
562
 
    if count >= 8 or c_val >= 0x80:
563
 
        raise ValueError('encode_base128_int overflowed the buffer')
564
 
    c_bytes[count] = <unsigned char>(c_val & 0xFF)
565
 
    count = count + 1
566
 
    return PyString_FromStringAndSize(<char *>c_bytes, count)
567
 
 
568
 
 
569
 
def decode_base128_int(bytes):
570
 
    """Decode an integer from a 7-bit lsb encoding."""
571
 
    cdef int offset
572
 
    cdef int val
573
 
    cdef unsigned int uval
574
 
    cdef int shift
575
 
    cdef Py_ssize_t num_low_bytes
576
 
    cdef unsigned char *c_bytes
577
 
 
578
 
    offset = 0
579
 
    val = 0
580
 
    shift = 0
581
 
    if not PyString_CheckExact(bytes):
582
 
        raise TypeError('bytes is not a string')
583
 
    c_bytes = <unsigned char*>PyString_AS_STRING(bytes)
584
 
    # We take off 1, because we have to be able to decode the non-expanded byte
585
 
    num_low_bytes = PyString_GET_SIZE(bytes) - 1
586
 
    while (c_bytes[offset] & 0x80) and offset < num_low_bytes:
587
 
        val = val | ((c_bytes[offset] & 0x7F) << shift)
588
 
        shift = shift + 7
589
 
        offset = offset + 1
590
 
    if c_bytes[offset] & 0x80:
591
 
        raise ValueError('Data not properly formatted, we ran out of'
592
 
                         ' bytes before 0x80 stopped being set.')
593
 
    val = val | (c_bytes[offset] << shift)
594
 
    offset = offset + 1
595
 
    if val < 0:
596
 
        uval = <unsigned int> val
597
 
        return uval, offset
598
 
    return val, offset
599
 
 
600