/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/_patiencediff_c.c

  • Committer: Jelmer Vernooij
  • Date: 2017-08-07 11:49:46 UTC
  • mto: (6747.3.4 avoid-set-revid-3)
  • mto: This revision was merged to the branch mainline in revision 6750.
  • Revision ID: jelmer@jelmer.uk-20170807114946-luclmxuawyzhpiot
Avoid setting revision_ids.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 Copyright (C) 2007, 2010 Canonical Ltd
 
3
 
 
4
 This program is free software; you can redistribute it and/or modify
 
5
 it under the terms of the GNU General Public License as published by
 
6
 the Free Software Foundation; either version 2 of the License, or
 
7
 (at your option) any later version.
 
8
 
 
9
 This program is distributed in the hope that it will be useful,
 
10
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 GNU General Public License for more details.
 
13
 
 
14
 You should have received a copy of the GNU General Public License
 
15
 along with this program; if not, write to the Free Software
 
16
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
 
 
18
 Function equate_lines based on bdiff.c from Mercurial.
 
19
   Copyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>
 
20
 
 
21
 Functions unique_lcs/recurse_matches based on _patiencediff_py.py.
 
22
   Copyright (C) 2005 Bram Cohen, Copyright (C) 2005, 2006 Canonical Ltd
 
23
*/
 
24
 
 
25
 
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include <Python.h>
 
29
 
 
30
#include "python-compat.h"
 
31
 
 
32
 
 
33
#if defined(__GNUC__)
 
34
#   define inline __inline__
 
35
#elif defined(_MSC_VER)
 
36
#   define inline __inline
 
37
#else
 
38
#   define inline
 
39
#endif
 
40
 
 
41
 
 
42
#define MIN(a, b) (((a) > (b)) ? (b) : (a))
 
43
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
 
44
 
 
45
 
 
46
#define SENTINEL -1
 
47
 
 
48
 
 
49
/* malloc returns NULL on some platforms if you try to allocate nothing,
 
50
 * causing <https://bugs.launchpad.net/bzr/+bug/511267> and
 
51
 * <https://bugs.launchpad.net/bzr/+bug/331095>.  On glibc it passes, but
 
52
 * let's make it fail to aid testing. */
 
53
#define guarded_malloc(x) ( (x) ? malloc(x) : NULL )
 
54
 
 
55
enum {
 
56
    OP_EQUAL = 0,
 
57
    OP_INSERT,
 
58
    OP_DELETE,
 
59
    OP_REPLACE
 
60
};
 
61
 
 
62
 
 
63
/* values from this array need to correspont to the order of the enum above */
 
64
static char *opcode_names[] = {
 
65
    "equal",
 
66
    "insert",
 
67
    "delete",
 
68
    "replace",
 
69
};
 
70
 
 
71
 
 
72
struct line {
 
73
    long hash;         /* hash code of the string/object */
 
74
    Py_ssize_t next;   /* next line from the same equivalence class */
 
75
    Py_ssize_t equiv;  /* equivalence class */
 
76
    PyObject *data;
 
77
};
 
78
 
 
79
 
 
80
struct bucket {
 
81
    Py_ssize_t a_head;  /* first item in `a` from this equivalence class */
 
82
    Py_ssize_t a_count;
 
83
    Py_ssize_t b_head;  /* first item in `b` from this equivalence class */
 
84
    Py_ssize_t b_count;
 
85
    Py_ssize_t a_pos;
 
86
    Py_ssize_t b_pos;
 
87
};
 
88
 
 
89
 
 
90
struct hashtable {
 
91
    Py_ssize_t last_a_pos;
 
92
    Py_ssize_t last_b_pos;
 
93
    Py_ssize_t size;
 
94
    struct bucket *table;
 
95
};
 
96
 
 
97
struct matching_line {
 
98
    Py_ssize_t a;     /* index of the line in `a` */
 
99
    Py_ssize_t b;     /* index of the line in `b` */
 
100
};
 
101
 
 
102
 
 
103
struct matching_block {
 
104
    Py_ssize_t a;     /* index of the first line in `a` */
 
105
    Py_ssize_t b;     /* index of the first line in `b` */
 
106
    Py_ssize_t len;   /* length of the block */
 
107
};
 
108
 
 
109
 
 
110
struct matching_blocks {
 
111
    struct matching_block *matches;
 
112
    Py_ssize_t count;
 
113
};
 
114
 
 
115
 
 
116
struct opcode {
 
117
    int tag;
 
118
    Py_ssize_t i1;
 
119
    Py_ssize_t i2;
 
120
    Py_ssize_t j1;
 
121
    Py_ssize_t j2;
 
122
};
 
123
 
 
124
 
 
125
typedef struct {
 
126
    PyObject_HEAD
 
127
    Py_ssize_t asize;
 
128
    Py_ssize_t bsize;
 
129
    struct line *a;
 
130
    struct line *b;
 
131
    struct hashtable hashtable;
 
132
    Py_ssize_t *backpointers;
 
133
} PatienceSequenceMatcher;
 
134
 
 
135
 
 
136
static inline Py_ssize_t
 
137
bisect_left(Py_ssize_t *list, Py_ssize_t item, Py_ssize_t lo, Py_ssize_t hi)
 
138
{
 
139
    while (lo < hi) {
 
140
        Py_ssize_t mid = lo / 2 + hi / 2 + (lo % 2 + hi % 2) / 2;
 
141
        if (list[mid] < item)
 
142
            lo = mid + 1;
 
143
        else
 
144
            hi = mid;
 
145
    }
 
146
    return lo;
 
147
}
 
148
 
 
149
 
 
150
static inline int
 
151
compare_lines(struct line *a, struct line *b)
 
152
{
 
153
    return ((a->hash != b->hash)
 
154
            || PyObject_Compare(a->data, b->data));
 
155
}
 
156
 
 
157
 
 
158
static inline int
 
159
find_equivalence_class(struct bucket *hashtable, Py_ssize_t hsize,
 
160
                       struct line *lines, struct line *ref_lines,
 
161
                       Py_ssize_t i)
 
162
{
 
163
    Py_ssize_t j;
 
164
    for (j = lines[i].hash & hsize; hashtable[j].b_head != SENTINEL; j = (j + 1) & hsize) {
 
165
        if (!compare_lines(lines + i, ref_lines + hashtable[j].b_head)) {
 
166
            break;
 
167
        }
 
168
    }
 
169
    return j;
 
170
}
 
171
 
 
172
 
 
173
static int
 
174
equate_lines(struct hashtable *result,
 
175
             struct line *lines_a, struct line *lines_b,
 
176
             Py_ssize_t asize, Py_ssize_t bsize)
 
177
{
 
178
    Py_ssize_t i, j, hsize;
 
179
    struct bucket *hashtable;
 
180
 
 
181
    /* check for overflow, we need the table to be at least bsize+1 */
 
182
    if (bsize == PY_SSIZE_T_MAX) {
 
183
        PyErr_SetNone(PyExc_OverflowError);
 
184
        return 0;
 
185
    }
 
186
 
 
187
    /* build a hash table of the next highest power of 2 */
 
188
    hsize = 1;
 
189
    while (hsize < bsize + 1)
 
190
        hsize *= 2;
 
191
 
 
192
    /* can't be 0 */
 
193
    hashtable = (struct bucket *) guarded_malloc(sizeof(struct bucket) * hsize);
 
194
    if (hashtable == NULL) {
 
195
        PyErr_NoMemory();
 
196
        return 0;
 
197
    }
 
198
 
 
199
    /* initialise the hashtable */
 
200
    for (i = 0; i < hsize; i++) {
 
201
        hashtable[i].a_count = 0;
 
202
        hashtable[i].b_count = 0;
 
203
        hashtable[i].a_head = SENTINEL;
 
204
        hashtable[i].b_head = SENTINEL;
 
205
    }
 
206
    hsize--;
 
207
 
 
208
    /* add lines from lines_b to the hash table chains. iterating
 
209
       backwards so the matching lines are sorted to the linked list
 
210
       by the line number (because we are adding new lines to the
 
211
       head of the list) */
 
212
    for (i = bsize - 1; i >= 0; i--) {
 
213
        /* find the first hashtable entry, which is either empty or contains
 
214
           the same line as lines_b[i] */
 
215
        j = find_equivalence_class(hashtable, hsize, lines_b, lines_b, i);
 
216
 
 
217
        /* set the equivalence class */
 
218
        lines_b[i].equiv = j;
 
219
 
 
220
        /* add to the head of the equivalence class */
 
221
        lines_b[i].next = hashtable[j].b_head;
 
222
        hashtable[j].b_head = i;
 
223
        hashtable[j].b_count++;
 
224
    }
 
225
 
 
226
    /* match items from lines_a to their equivalence class in lines_b.
 
227
       again, iterating backwards for the right order of the linked lists */
 
228
    for (i = asize - 1; i >= 0; i--) {
 
229
        /* find the first hash entry, which is either empty or contains
 
230
           the same line as lines_a[i] */
 
231
        j = find_equivalence_class(hashtable, hsize, lines_a, lines_b, i);
 
232
 
 
233
        /* set the equivalence class, even if we are not interested in this
 
234
           line, because the values are not pre-filled */
 
235
        lines_a[i].equiv = j;
 
236
 
 
237
        /* we are not interested in lines which are not also in lines_b */
 
238
        if (hashtable[j].b_head == SENTINEL)
 
239
            continue;
 
240
 
 
241
        /* add to the head of the equivalence class */
 
242
        lines_a[i].next = hashtable[j].a_head;
 
243
        hashtable[j].a_head = i;
 
244
        hashtable[j].a_count++;
 
245
    }
 
246
 
 
247
    result->last_a_pos = -1;
 
248
    result->last_b_pos = -1;
 
249
    result->size = hsize + 1;
 
250
    result->table = hashtable;
 
251
 
 
252
    return 1;
 
253
}
 
254
 
 
255
 
 
256
 
 
257
/* Finds longest common subsequence of unique lines in a[alo:ahi] and
 
258
   b[blo:bhi].
 
259
   Parameter backpointers must have allocated memory for at least
 
260
   4 * (bhi - blo) ints. */
 
261
Py_ssize_t
 
262
unique_lcs(struct matching_line *answer,
 
263
           struct hashtable *hashtable, Py_ssize_t *backpointers,
 
264
           struct line *lines_a, struct line *lines_b,
 
265
           Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi)
 
266
{
 
267
    Py_ssize_t i, k, equiv, apos, bpos, norm_apos, norm_bpos, bsize, stacksize;
 
268
    Py_ssize_t *stacks, *lasts, *btoa;
 
269
    struct bucket *h;
 
270
 
 
271
    k = 0;
 
272
    stacksize = 0;
 
273
    bsize = bhi - blo;
 
274
    h = hashtable->table;
 
275
 
 
276
    /* "unpack" the allocated memory */
 
277
    stacks = backpointers + bsize;
 
278
    lasts = stacks + bsize;
 
279
    btoa = lasts + bsize;
 
280
 
 
281
    /* initialise the backpointers */
 
282
    for (i = 0; i < bsize; i++)
 
283
        backpointers[i] = SENTINEL;
 
284
 
 
285
    if (hashtable->last_a_pos == -1 || hashtable->last_a_pos > alo)
 
286
        for (i = 0; i < hashtable->size; i++)
 
287
            h[i].a_pos = h[i].a_head;
 
288
    hashtable->last_a_pos = alo;
 
289
 
 
290
    if (hashtable->last_b_pos == -1 || hashtable->last_b_pos > blo)
 
291
        for (i = 0; i < hashtable->size; i++)
 
292
            h[i].b_pos = h[i].b_head;
 
293
    hashtable->last_b_pos = blo;
 
294
 
 
295
    for (bpos = blo; bpos < bhi; bpos++) {
 
296
        equiv = lines_b[bpos].equiv;
 
297
 
 
298
        /* no lines in a or b  */
 
299
        if (h[equiv].a_count == 0 || h[equiv].b_count == 0)
 
300
            continue;
 
301
 
 
302
        /* find an unique line in lines_a that matches lines_b[bpos]
 
303
           if we find more than one line within the range alo:ahi,
 
304
           jump to the next line from lines_b immediately */
 
305
        apos = SENTINEL;
 
306
        /* loop through all lines in the linked list */
 
307
        for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) {
 
308
            /* the index is lower than alo, continue to the next line */
 
309
            if (i < alo) {
 
310
                h[equiv].a_pos = i;
 
311
                continue;
 
312
            }
 
313
            /* the index is higher than ahi, stop searching */
 
314
            if (i >= ahi)
 
315
                break;
 
316
            /* if the line is within our range, check if it's a duplicate */
 
317
            if (apos != SENTINEL)
 
318
                goto nextb;
 
319
            /* save index to the line */
 
320
            apos = i;
 
321
        }
 
322
        /* this line has no equivalent in lines_a[alo:ahi] */
 
323
        if (apos == SENTINEL)
 
324
            goto nextb;
 
325
 
 
326
        /* check for duplicates of this line in lines_b[blo:bhi] */
 
327
        /* loop through all lines in the linked list */
 
328
        for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) {
 
329
            /* the index is lower than blo, continue to the next line */
 
330
            if (i < blo) {
 
331
                h[equiv].b_pos = i;
 
332
                continue;
 
333
            }
 
334
            /* the index is higher than bhi, stop searching */
 
335
            if (i >= bhi)
 
336
                break;
 
337
            /* if this isn't the line with started with and it's within
 
338
               our range, it's a duplicate */
 
339
            if (i != bpos)
 
340
                goto nextb;
 
341
        }
 
342
 
 
343
        /* use normalised indexes ([0,ahi-alo) instead of [alo,ahi))
 
344
           for the patience sorting algorithm */
 
345
        norm_bpos = bpos - blo;
 
346
        norm_apos = apos - alo;
 
347
        btoa[norm_bpos] = norm_apos;
 
348
 
 
349
        /*
 
350
        Ok, how does this work...
 
351
 
 
352
        We have a list of matching lines from two lists, a and b. These
 
353
        matches are stored in variable `btoa`. As we are iterating over this
 
354
        table by bpos, the lines from b already form an increasing sequence.
 
355
        We need to "sort" also the lines from a using the patience sorting
 
356
        algorithm, ignoring the lines which would need to be swapped.
 
357
 
 
358
          http://en.wikipedia.org/wiki/Patience_sorting
 
359
 
 
360
        For each pair of lines, we need to place the line from a on either
 
361
        an existing pile that has higher value on the top or create a new
 
362
        pile. Variable `stacks` represents the tops of these piles and in
 
363
        variable `lasts` we store the lines from b, that correspond to the
 
364
        lines from a in `stacks`.
 
365
 
 
366
        Whenever we place a new line on top of a pile, we store a
 
367
        backpointer to the line (b) from top of the previous pile. This means
 
368
        that after the loop, variable `backpointers` will contain an index
 
369
        to the previous matching lines that forms an increasing sequence
 
370
        (over both indexes a and b) with the current matching lines. If
 
371
        either index a or b of the previous matching lines would be higher
 
372
        than indexes of the current one or if the indexes of the current
 
373
        one are 0, it will contain SENTINEL.
 
374
 
 
375
        To construct the LCS, we will just need to follow these backpointers
 
376
        from the top of the last pile and stop when we reach SENTINEL.
 
377
        */
 
378
 
 
379
        /* as an optimization, check if the next line comes at the end,
 
380
           because it usually does */
 
381
        if (stacksize && stacks[stacksize - 1] < norm_apos)
 
382
            k = stacksize;
 
383
        /* as an optimization, check if the next line comes right after
 
384
           the previous line, because usually it does */
 
385
        else if (stacksize && (stacks[k] < norm_apos) &&
 
386
                 (k == stacksize - 1 || stacks[k + 1] > norm_apos))
 
387
            k += 1;
 
388
        else
 
389
            k = bisect_left(stacks, norm_apos, 0, stacksize);
 
390
 
 
391
        if (k > 0)
 
392
            backpointers[norm_bpos] = lasts[k - 1];
 
393
 
 
394
        if (k < stacksize) {
 
395
            stacks[k] = norm_apos;
 
396
            lasts[k] = norm_bpos;
 
397
        }
 
398
        else {
 
399
            stacks[stacksize] = norm_apos;
 
400
            lasts[stacksize] = norm_bpos;
 
401
            stacksize += 1;
 
402
        }
 
403
 
 
404
 
 
405
nextb:
 
406
        ;
 
407
    }
 
408
 
 
409
    if (stacksize == 0)
 
410
        return 0;
 
411
 
 
412
    /* backtrace the structures to find the LCS */
 
413
    i = 0;
 
414
    k = lasts[stacksize - 1];
 
415
    while (k != SENTINEL) {
 
416
        answer[i].a = btoa[k];
 
417
        answer[i].b = k;
 
418
        k = backpointers[k];
 
419
        i++;
 
420
    }
 
421
 
 
422
    return i;
 
423
}
 
424
 
 
425
/* Adds a new line to the list of matching blocks, either extending the
 
426
   current block or adding a new one. */
 
427
static inline void
 
428
add_matching_line(struct matching_blocks *answer, Py_ssize_t a, Py_ssize_t b)
 
429
{
 
430
    Py_ssize_t last_index = answer->count - 1;
 
431
    if ((last_index >= 0) &&
 
432
        (a == answer->matches[last_index].a +
 
433
              answer->matches[last_index].len) &&
 
434
        (b == answer->matches[last_index].b +
 
435
              answer->matches[last_index].len)) {
 
436
        /* enlarge the last block */
 
437
        answer->matches[last_index].len++;
 
438
    }
 
439
    else {
 
440
        /* create a new block */
 
441
        last_index++;
 
442
        answer->matches[last_index].a = a;
 
443
        answer->matches[last_index].b = b;
 
444
        answer->matches[last_index].len = 1;
 
445
        answer->count++;
 
446
    }
 
447
}
 
448
 
 
449
 
 
450
static int
 
451
recurse_matches(struct matching_blocks *answer, struct hashtable *hashtable,
 
452
                Py_ssize_t *backpointers, struct line *a, struct line *b,
 
453
                Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi,
 
454
                int maxrecursion)
 
455
{
 
456
    int res;
 
457
    Py_ssize_t new, last_a_pos, last_b_pos, lcs_size, nahi, nbhi, i, apos, bpos;
 
458
    struct matching_line *lcs;
 
459
 
 
460
    if (maxrecursion < 0)
 
461
        return 1;
 
462
 
 
463
    if (alo == ahi || blo == bhi)
 
464
        return 1;
 
465
 
 
466
    new = 0;
 
467
    last_a_pos = alo - 1;
 
468
    last_b_pos = blo - 1;
 
469
 
 
470
    lcs = (struct matching_line *)guarded_malloc(sizeof(struct matching_line) * (bhi - blo));
 
471
    if (lcs == NULL)
 
472
        return 0;
 
473
 
 
474
    lcs_size = unique_lcs(lcs, hashtable, backpointers, a, b, alo, blo, ahi, bhi);
 
475
 
 
476
    /* recurse between lines which are unique in each file and match */
 
477
    for (i = lcs_size - 1; i >= 0; i--) {
 
478
        apos = alo + lcs[i].a;
 
479
        bpos = blo + lcs[i].b;
 
480
        if (last_a_pos + 1 != apos || last_b_pos + 1 != bpos) {
 
481
            res = recurse_matches(answer, hashtable,
 
482
                                  backpointers, a, b,
 
483
                                  last_a_pos + 1, last_b_pos + 1,
 
484
                                  apos, bpos, maxrecursion - 1);
 
485
            if (!res)
 
486
                goto error;
 
487
        }
 
488
        last_a_pos = apos;
 
489
        last_b_pos = bpos;
 
490
        add_matching_line(answer, apos, bpos);
 
491
        new = 1;
 
492
    }
 
493
 
 
494
    free(lcs);
 
495
    lcs = NULL;
 
496
 
 
497
    /* find matches between the last match and the end */
 
498
    if (new > 0) {
 
499
        res = recurse_matches(answer, hashtable,
 
500
                              backpointers, a, b,
 
501
                              last_a_pos + 1, last_b_pos + 1,
 
502
                              ahi, bhi, maxrecursion - 1);
 
503
        if (!res)
 
504
            goto error;
 
505
    }
 
506
 
 
507
 
 
508
    /* find matching lines at the very beginning */
 
509
    else if (a[alo].equiv == b[blo].equiv) {
 
510
        while (alo < ahi && blo < bhi && a[alo].equiv == b[blo].equiv)
 
511
            add_matching_line(answer, alo++, blo++);
 
512
        res = recurse_matches(answer, hashtable,
 
513
                              backpointers, a, b,
 
514
                              alo, blo, ahi, bhi, maxrecursion - 1);
 
515
        if (!res)
 
516
            goto error;
 
517
    }
 
518
 
 
519
    /* find matching lines at the very end */
 
520
    else if (a[ahi - 1].equiv == b[bhi - 1].equiv) {
 
521
        nahi = ahi - 1;
 
522
        nbhi = bhi - 1;
 
523
        while (nahi > alo && nbhi > blo && a[nahi - 1].equiv == b[nbhi - 1].equiv) {
 
524
            nahi--;
 
525
            nbhi--;
 
526
        }
 
527
        res = recurse_matches(answer, hashtable,
 
528
                              backpointers, a, b,
 
529
                              last_a_pos + 1, last_b_pos + 1,
 
530
                              nahi, nbhi, maxrecursion - 1);
 
531
        if (!res)
 
532
            goto error;
 
533
        for (i = 0; i < ahi - nahi; i++)
 
534
            add_matching_line(answer, nahi + i, nbhi + i);
 
535
    }
 
536
 
 
537
    return 1;
 
538
 
 
539
error:
 
540
    free(lcs);
 
541
    return 0;
 
542
}
 
543
 
 
544
 
 
545
static void
 
546
delete_lines(struct line *lines, Py_ssize_t size)
 
547
{
 
548
    struct line *line = lines;
 
549
    while (size-- > 0) {
 
550
        Py_XDECREF(line->data);
 
551
        line++;
 
552
    }
 
553
    free(lines);
 
554
}
 
555
 
 
556
 
 
557
static Py_ssize_t
 
558
load_lines(PyObject *orig, struct line **lines)
 
559
{
 
560
    Py_ssize_t size, i;
 
561
    struct line *line;
 
562
    PyObject *seq, *item;
 
563
 
 
564
    seq = PySequence_Fast(orig, "sequence expected");
 
565
    if (seq == NULL) {
 
566
        return -1;
 
567
    }
 
568
 
 
569
    size = PySequence_Fast_GET_SIZE(seq);
 
570
    if (size == 0) {
 
571
        Py_DECREF(seq);
 
572
        return 0;
 
573
    }
 
574
 
 
575
    /* Allocate a memory block for line data, initialized to 0 */
 
576
    line = *lines = (struct line *)calloc(size, sizeof(struct line));
 
577
    if (line == NULL) {
 
578
        PyErr_NoMemory();
 
579
        Py_DECREF(seq);
 
580
        return -1;
 
581
    }
 
582
 
 
583
    for (i = 0; i < size; i++) {
 
584
        item = PySequence_Fast_GET_ITEM(seq, i);
 
585
        Py_INCREF(item);
 
586
        line->data = item;
 
587
        line->hash = PyObject_Hash(item);
 
588
        if (line->hash == (-1)) {
 
589
            /* Propogate the hash exception */
 
590
            size = -1;
 
591
            goto cleanup;
 
592
        }
 
593
        line->next = SENTINEL;
 
594
        line++;
 
595
    }
 
596
 
 
597
    cleanup:
 
598
    Py_DECREF(seq);
 
599
    if (size == -1) {
 
600
        /* Error -- cleanup unused object references */
 
601
        delete_lines(*lines, i);
 
602
        *lines = NULL;
 
603
    }
 
604
    return size;
 
605
}
 
606
 
 
607
 
 
608
static PyObject *
 
609
py_unique_lcs(PyObject *self, PyObject *args)
 
610
{
 
611
    PyObject *aseq, *bseq, *res, *item;
 
612
    Py_ssize_t asize, bsize, i, nmatches, *backpointers = NULL;
 
613
    struct line *a = NULL, *b = NULL;
 
614
    struct matching_line *matches = NULL;
 
615
    struct hashtable hashtable;
 
616
 
 
617
    if (!PyArg_ParseTuple(args, "OO", &aseq, &bseq))
 
618
        return NULL;
 
619
 
 
620
    hashtable.table = NULL;
 
621
 
 
622
    asize = load_lines(aseq, &a);
 
623
    bsize = load_lines(bseq, &b);
 
624
    if (asize == -1 || bsize == -1)
 
625
        goto error;
 
626
 
 
627
    if (!equate_lines(&hashtable, a, b, asize, bsize))
 
628
        goto error;
 
629
 
 
630
    if (bsize > 0) {
 
631
        matches = (struct matching_line *)guarded_malloc(sizeof(struct matching_line) * bsize);
 
632
        if (matches == NULL)
 
633
            goto error;
 
634
 
 
635
        backpointers = (Py_ssize_t *)guarded_malloc(sizeof(Py_ssize_t) * bsize * 4);
 
636
        if (backpointers == NULL)
 
637
            goto error;
 
638
    }
 
639
 
 
640
    nmatches = unique_lcs(matches, &hashtable, backpointers, a, b, 0, 0, asize, bsize);
 
641
 
 
642
    res = PyList_New(nmatches);
 
643
    for (i = 0; i < nmatches; i++) {
 
644
        item = Py_BuildValue("nn", matches[nmatches - i - 1].a, matches[nmatches - i - 1].b);
 
645
        if (item == NULL)
 
646
            goto error;
 
647
        if (PyList_SetItem(res, i, item) != 0)
 
648
            goto error;
 
649
    }
 
650
 
 
651
    free(backpointers);
 
652
    free(matches);
 
653
    free(hashtable.table);
 
654
    delete_lines(b, bsize);
 
655
    delete_lines(a, asize);
 
656
    return res;
 
657
 
 
658
error:
 
659
    free(backpointers);
 
660
    free(matches);
 
661
    free(hashtable.table);
 
662
    delete_lines(b, bsize);
 
663
    delete_lines(a, asize);
 
664
    return NULL;
 
665
}
 
666
 
 
667
 
 
668
static PyObject *
 
669
py_recurse_matches(PyObject *self, PyObject *args)
 
670
{
 
671
    PyObject *aseq, *bseq, *item, *answer;
 
672
    int maxrecursion, res;
 
673
    Py_ssize_t i, j, asize, bsize, alo, blo, ahi, bhi;
 
674
    Py_ssize_t *backpointers = NULL;
 
675
    struct line *a = NULL, *b = NULL;
 
676
    struct hashtable hashtable;
 
677
    struct matching_blocks matches;
 
678
 
 
679
    if (!PyArg_ParseTuple(args, "OOnnnnOi", &aseq, &bseq, &alo, &blo,
 
680
                          &ahi, &bhi, &answer, &maxrecursion))
 
681
        return NULL;
 
682
 
 
683
    hashtable.table = NULL;
 
684
    matches.matches = NULL;
 
685
 
 
686
    asize = load_lines(aseq, &a);
 
687
    bsize = load_lines(bseq, &b);
 
688
    if (asize == -1 || bsize == -1)
 
689
        goto error;
 
690
 
 
691
    if (!equate_lines(&hashtable, a, b, asize, bsize))
 
692
        goto error;
 
693
 
 
694
    matches.count = 0;
 
695
 
 
696
    if (bsize > 0) {
 
697
        matches.matches = (struct matching_block *)guarded_malloc(sizeof(struct matching_block) * bsize);
 
698
        if (matches.matches == NULL)
 
699
            goto error;
 
700
 
 
701
        backpointers = (Py_ssize_t *)guarded_malloc(sizeof(Py_ssize_t) * bsize * 4);
 
702
        if (backpointers == NULL)
 
703
            goto error;
 
704
    } else {
 
705
        matches.matches = NULL;
 
706
        backpointers = NULL;
 
707
    }
 
708
 
 
709
    res = recurse_matches(&matches, &hashtable, backpointers,
 
710
                          a, b, alo, blo, ahi, bhi, maxrecursion);
 
711
    if (!res)
 
712
        goto error;
 
713
 
 
714
    for (i = 0; i < matches.count; i++) {
 
715
        for (j = 0; j < matches.matches[i].len; j++) {
 
716
            item = Py_BuildValue("nn", matches.matches[i].a + j,
 
717
                                 matches.matches[i].b + j);
 
718
            if (item == NULL)
 
719
                goto error;
 
720
            if (PyList_Append(answer, item) != 0)
 
721
                goto error;
 
722
        }
 
723
    }
 
724
 
 
725
    free(backpointers);
 
726
    free(matches.matches);
 
727
    free(hashtable.table);
 
728
    delete_lines(b, bsize);
 
729
    delete_lines(a, asize);
 
730
    Py_RETURN_NONE;
 
731
 
 
732
error:
 
733
    free(backpointers);
 
734
    free(matches.matches);
 
735
    free(hashtable.table);
 
736
    delete_lines(b, bsize);
 
737
    delete_lines(a, asize);
 
738
    return NULL;
 
739
}
 
740
 
 
741
 
 
742
static PyObject *
 
743
PatienceSequenceMatcher_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
744
{
 
745
    PyObject *junk, *a, *b;
 
746
    PatienceSequenceMatcher *self;
 
747
 
 
748
    self = (PatienceSequenceMatcher *)type->tp_alloc(type, 0);
 
749
    if (self != NULL) {
 
750
 
 
751
        if (!PyArg_ParseTuple(args, "OOO", &junk, &a, &b)) {
 
752
            Py_DECREF(self);
 
753
            return NULL;
 
754
        }
 
755
 
 
756
        self->asize = load_lines(a, &(self->a));
 
757
        self->bsize = load_lines(b, &(self->b));
 
758
 
 
759
        if (self->asize == -1 || self->bsize == -1) {
 
760
            Py_DECREF(self);
 
761
            return NULL;
 
762
        }
 
763
 
 
764
        if (!equate_lines(&self->hashtable, self->a, self->b, self->asize, self->bsize)) {
 
765
            Py_DECREF(self);
 
766
            return NULL;
 
767
        }
 
768
 
 
769
        if (self->bsize > 0) {
 
770
            self->backpointers = (Py_ssize_t *)guarded_malloc(sizeof(Py_ssize_t) * self->bsize * 4);
 
771
            if (self->backpointers == NULL) {
 
772
                Py_DECREF(self);
 
773
                PyErr_NoMemory();
 
774
                return NULL;
 
775
            }
 
776
        } else {
 
777
            self->backpointers = NULL;
 
778
        }
 
779
 
 
780
    }
 
781
 
 
782
    return (PyObject *)self;
 
783
}
 
784
 
 
785
 
 
786
static void
 
787
PatienceSequenceMatcher_dealloc(PatienceSequenceMatcher* self)
 
788
{
 
789
    free(self->backpointers);
 
790
    free(self->hashtable.table);
 
791
    delete_lines(self->b, self->bsize);
 
792
    delete_lines(self->a, self->asize);
 
793
    self->ob_type->tp_free((PyObject *)self);
 
794
}
 
795
 
 
796
 
 
797
static char PatienceSequenceMatcher_get_matching_blocks_doc[] =
 
798
    "Return list of triples describing matching subsequences.\n"
 
799
    "\n"
 
800
    "Each triple is of the form (i, j, n), and means that\n"
 
801
    "a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in\n"
 
802
    "i and in j.\n"
 
803
    "\n"
 
804
    "The last triple is a dummy, (len(a), len(b), 0), and is the only\n"
 
805
    "triple with n==0.\n"
 
806
    "\n"
 
807
    ">>> s = PatienceSequenceMatcher(None, \"abxcd\", \"abcd\")\n"
 
808
    ">>> s.get_matching_blocks()\n"
 
809
    "[(0, 0, 2), (3, 2, 2), (5, 4, 0)]\n";
 
810
 
 
811
static PyObject *
 
812
PatienceSequenceMatcher_get_matching_blocks(PatienceSequenceMatcher* self)
 
813
{
 
814
    PyObject *answer, *item;
 
815
    int res;
 
816
    Py_ssize_t i;
 
817
    struct matching_blocks matches;
 
818
 
 
819
    matches.count = 0;
 
820
    if (self->bsize > 0) {
 
821
        matches.matches = (struct matching_block *)
 
822
            guarded_malloc(sizeof(struct matching_block) * self->bsize);
 
823
        if (matches.matches == NULL)
 
824
            return PyErr_NoMemory();
 
825
    } else
 
826
        matches.matches = NULL;
 
827
 
 
828
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
829
                          self->a, self->b, 0, 0,
 
830
                          self->asize, self->bsize, 10);
 
831
    if (!res) {
 
832
        free(matches.matches);
 
833
        return PyErr_NoMemory();
 
834
    }
 
835
 
 
836
    answer = PyList_New(matches.count + 1);
 
837
    if (answer == NULL) {
 
838
        free(matches.matches);
 
839
        return NULL;
 
840
    }
 
841
 
 
842
    for (i = 0; i < matches.count; i++) {
 
843
        item = Py_BuildValue("nnn", matches.matches[i].a,
 
844
                             matches.matches[i].b, matches.matches[i].len);
 
845
        if (item == NULL)
 
846
            goto error;
 
847
        if (PyList_SetItem(answer, i, item) != 0)
 
848
            goto error;
 
849
    }
 
850
    item = Py_BuildValue("nnn", self->asize, self->bsize, 0);
 
851
    if (item == NULL)
 
852
        goto error;
 
853
    if (PyList_SetItem(answer, i, item) != 0)
 
854
        goto error;
 
855
 
 
856
    free(matches.matches);
 
857
    return answer;
 
858
 
 
859
error:
 
860
    free(matches.matches);
 
861
    Py_DECREF(answer);
 
862
    return NULL;
 
863
}
 
864
 
 
865
 
 
866
static char PatienceSequenceMatcher_get_opcodes_doc[] =
 
867
    "Return list of 5-tuples describing how to turn a into b.\n"
 
868
    "\n"
 
869
    "Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple\n"
 
870
    "has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the\n"
 
871
    "tuple preceding it, and likewise for j1 == the previous j2.\n"
 
872
    "\n"
 
873
    "The tags are strings, with these meanings:\n"
 
874
    "\n"
 
875
    "'replace':  a[i1:i2] should be replaced by b[j1:j2]\n"
 
876
    "'delete':   a[i1:i2] should be deleted.\n"
 
877
    "               Note that j1==j2 in this case.\n"
 
878
    "'insert':   b[j1:j2] should be inserted at a[i1:i1].\n"
 
879
    "               Note that i1==i2 in this case.\n"
 
880
    "'equal':    a[i1:i2] == b[j1:j2]\n"
 
881
    "\n"
 
882
    ">>> a = \"qabxcd\"\n"
 
883
    ">>> b = \"abycdf\"\n"
 
884
    ">>> s = PatienceSequenceMatcher(None, a, b)\n"
 
885
    ">>> for tag, i1, i2, j1, j2 in s.get_opcodes():\n"
 
886
    "...    print (\"%7s a[%d:%d] (%s) b[%d:%d] (%s)\" %\n"
 
887
    "...           (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))\n"
 
888
    " delete a[0:1] (q) b[0:0] ()\n"
 
889
    "  equal a[1:3] (ab) b[0:2] (ab)\n"
 
890
    "replace a[3:4] (x) b[2:3] (y)\n"
 
891
    "  equal a[4:6] (cd) b[3:5] (cd)\n"
 
892
    " insert a[6:6] () b[5:6] (f)\n";
 
893
 
 
894
static PyObject *
 
895
PatienceSequenceMatcher_get_opcodes(PatienceSequenceMatcher* self)
 
896
{
 
897
    PyObject *answer, *item;
 
898
    Py_ssize_t i, j, k, ai, bj;
 
899
    int tag, res;
 
900
    struct matching_blocks matches;
 
901
 
 
902
    matches.count = 0;
 
903
    matches.matches = (struct matching_block *)guarded_malloc(sizeof(struct matching_block) * (self->bsize + 1));
 
904
    if (matches.matches == NULL)
 
905
        return PyErr_NoMemory();
 
906
 
 
907
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
908
                          self->a, self->b, 0, 0,
 
909
                          self->asize, self->bsize, 10);
 
910
    if (!res) {
 
911
        free(matches.matches);
 
912
        return PyErr_NoMemory();
 
913
    }
 
914
 
 
915
    matches.matches[matches.count].a = self->asize;
 
916
    matches.matches[matches.count].b = self->bsize;
 
917
    matches.matches[matches.count].len = 0;
 
918
    matches.count++;
 
919
 
 
920
    answer = PyList_New(0);
 
921
    if (answer == NULL) {
 
922
        free(matches.matches);
 
923
        return NULL;
 
924
    }
 
925
 
 
926
    i = j = 0;
 
927
    for (k = 0; k < matches.count; k++) {
 
928
        ai = matches.matches[k].a;
 
929
        bj = matches.matches[k].b;
 
930
 
 
931
        tag = -1;
 
932
        if (i < ai && j < bj)
 
933
            tag = OP_REPLACE;
 
934
        else if (i < ai)
 
935
            tag = OP_DELETE;
 
936
        else if (j < bj)
 
937
            tag = OP_INSERT;
 
938
 
 
939
        if (tag != -1) {
 
940
            item = Py_BuildValue("snnnn", opcode_names[tag], i, ai, j, bj);
 
941
            if (item == NULL)
 
942
                goto error;
 
943
            if (PyList_Append(answer, item) != 0)
 
944
                goto error;
 
945
        }
 
946
 
 
947
        i = ai + matches.matches[k].len;
 
948
        j = bj + matches.matches[k].len;
 
949
 
 
950
        if (matches.matches[k].len > 0) {
 
951
            item = Py_BuildValue("snnnn", opcode_names[OP_EQUAL], ai, i, bj, j);
 
952
            if (item == NULL)
 
953
                goto error;
 
954
            if (PyList_Append(answer, item) != 0)
 
955
                goto error;
 
956
        }
 
957
    }
 
958
 
 
959
    free(matches.matches);
 
960
    return answer;
 
961
 
 
962
error:
 
963
    free(matches.matches);
 
964
    Py_DECREF(answer);
 
965
    return NULL;
 
966
}
 
967
 
 
968
 
 
969
static char PatienceSequenceMatcher_get_grouped_opcodes_doc[] =
 
970
    "Isolate change clusters by eliminating ranges with no changes.\n"
 
971
    "\n"
 
972
    "Return a list of groups with upto n lines of context.\n"
 
973
    "Each group is in the same format as returned by get_opcodes().\n"
 
974
    "\n"
 
975
    ">>> from pprint import pprint\n"
 
976
    ">>> a = map(str, range(1,40))\n"
 
977
    ">>> b = a[:]\n"
 
978
    ">>> b[8:8] = ['i']     # Make an insertion\n"
 
979
    ">>> b[20] += 'x'       # Make a replacement\n"
 
980
    ">>> b[23:28] = []      # Make a deletion\n"
 
981
    ">>> b[30] += 'y'       # Make another replacement\n"
 
982
    ">>> pprint(PatienceSequenceMatcher(None,a,b).get_grouped_opcodes())\n"
 
983
    "[[('equal', 5, 8, 5, 8), ('insert', 8, 8, 8, 9), ('equal', 8, 11, 9, 12)],\n"
 
984
    " [('equal', 16, 19, 17, 20),\n"
 
985
    "  ('replace', 19, 20, 20, 21),\n"
 
986
    "  ('equal', 20, 22, 21, 23),\n"
 
987
    "  ('delete', 22, 27, 23, 23),\n"
 
988
    "  ('equal', 27, 30, 23, 26)],\n"
 
989
    " [('equal', 31, 34, 27, 30),\n"
 
990
    "  ('replace', 34, 35, 30, 31),\n"
 
991
    "  ('equal', 35, 38, 31, 34)]]\n";
 
992
 
 
993
static PyObject *
 
994
PatienceSequenceMatcher_get_grouped_opcodes(PatienceSequenceMatcher* self,
 
995
                                            PyObject *args)
 
996
{
 
997
    PyObject *answer, *group, *item;
 
998
    Py_ssize_t i, j, k, ai, bj, size, ncodes, tag;
 
999
    Py_ssize_t i1, i2, j1, j2;
 
1000
    int n = 3, nn, res;
 
1001
    struct matching_blocks matches;
 
1002
    struct opcode *codes;
 
1003
 
 
1004
    if (!PyArg_ParseTuple(args, "|i", &n))
 
1005
        return NULL;
 
1006
 
 
1007
    matches.count = 0;
 
1008
    matches.matches = (struct matching_block *)guarded_malloc(sizeof(struct matching_block) * (self->bsize + 1));
 
1009
    if (matches.matches == NULL)
 
1010
        return PyErr_NoMemory();
 
1011
 
 
1012
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
1013
                          self->a, self->b, 0, 0,
 
1014
                          self->asize, self->bsize, 10);
 
1015
    if (!res) {
 
1016
        free(matches.matches);
 
1017
        return PyErr_NoMemory();
 
1018
    }
 
1019
 
 
1020
    matches.matches[matches.count].a = self->asize;
 
1021
    matches.matches[matches.count].b = self->bsize;
 
1022
    matches.matches[matches.count].len = 0;
 
1023
    matches.count++;
 
1024
 
 
1025
    ncodes = 0;
 
1026
    codes = (struct opcode *)guarded_malloc(sizeof(struct opcode) * matches.count * 2);
 
1027
    if (codes == NULL) {
 
1028
        free(matches.matches);
 
1029
        return PyErr_NoMemory();
 
1030
    }
 
1031
 
 
1032
    i = j = 0;
 
1033
    for (k = 0; k < matches.count; k++) {
 
1034
        ai = matches.matches[k].a;
 
1035
        bj = matches.matches[k].b;
 
1036
 
 
1037
        tag = -1;
 
1038
        if (i < ai && j < bj)
 
1039
            tag = OP_REPLACE;
 
1040
        else if (i < ai)
 
1041
            tag = OP_DELETE;
 
1042
        else if (j < bj)
 
1043
            tag = OP_INSERT;
 
1044
 
 
1045
        if (tag != -1) {
 
1046
            codes[ncodes].tag = tag;
 
1047
            codes[ncodes].i1 = i;
 
1048
            codes[ncodes].i2 = ai;
 
1049
            codes[ncodes].j1 = j;
 
1050
            codes[ncodes].j2 = bj;
 
1051
            ncodes++;
 
1052
        }
 
1053
 
 
1054
        i = ai + matches.matches[k].len;
 
1055
        j = bj + matches.matches[k].len;
 
1056
 
 
1057
        if (matches.matches[k].len > 0) {
 
1058
            codes[ncodes].tag = OP_EQUAL;
 
1059
            codes[ncodes].i1 = ai;
 
1060
            codes[ncodes].i2 = i;
 
1061
            codes[ncodes].j1 = bj;
 
1062
            codes[ncodes].j2 = j;
 
1063
            ncodes++;
 
1064
        }
 
1065
    }
 
1066
 
 
1067
    if (ncodes == 0) {
 
1068
        codes[ncodes].tag = OP_EQUAL;
 
1069
        codes[ncodes].i1 = 0;
 
1070
        codes[ncodes].i2 = 1;
 
1071
        codes[ncodes].j1 = 0;
 
1072
        codes[ncodes].j2 = 1;
 
1073
        ncodes++;
 
1074
    }
 
1075
 
 
1076
    /* fixup leading and trailing groups if they show no changes. */
 
1077
    if (codes[0].tag == OP_EQUAL) {
 
1078
        codes[0].i1 = MAX(codes[0].i1, codes[0].i2 - n);
 
1079
        codes[0].j1 = MAX(codes[0].j1, codes[0].j2 - n);
 
1080
    }
 
1081
    if (codes[ncodes - 1].tag == OP_EQUAL) {
 
1082
        codes[ncodes - 1].i2 = MIN(codes[ncodes - 1].i2,
 
1083
                                   codes[ncodes - 1].i1 + n);
 
1084
        codes[ncodes - 1].j2 = MIN(codes[ncodes - 1].j2,
 
1085
                                   codes[ncodes - 1].j1 + n);
 
1086
    }
 
1087
 
 
1088
    group = NULL;
 
1089
 
 
1090
    answer = PyList_New(0);
 
1091
    if (answer == NULL)
 
1092
        goto error;
 
1093
 
 
1094
    group = PyList_New(0);
 
1095
    if (group == NULL)
 
1096
        goto error;
 
1097
 
 
1098
    nn = n + n;
 
1099
    tag = -1;
 
1100
    for (i = 0; i < ncodes; i++) {
 
1101
        tag = codes[i].tag;
 
1102
        i1 = codes[i].i1;
 
1103
        i2 = codes[i].i2;
 
1104
        j1 = codes[i].j1;
 
1105
        j2 = codes[i].j2;
 
1106
        /* end the current group and start a new one whenever
 
1107
           there is a large range with no changes. */
 
1108
        if (tag == OP_EQUAL && i2 - i1 > nn) {
 
1109
            item = Py_BuildValue("snnnn", opcode_names[tag],
 
1110
                                  i1, MIN(i2, i1 + n), j1, MIN(j2, j1 + n));
 
1111
            if (item == NULL)
 
1112
                goto error;
 
1113
            if (PyList_Append(group, item) != 0)
 
1114
                goto error;
 
1115
            if (PyList_Append(answer, group) != 0)
 
1116
                goto error;
 
1117
            group = PyList_New(0);
 
1118
            if (group == NULL)
 
1119
                goto error;
 
1120
            i1 = MAX(i1, i2 - n);
 
1121
            j1 = MAX(j1, j2 - n);
 
1122
        }
 
1123
        item = Py_BuildValue("snnnn", opcode_names[tag], i1, i2, j1 ,j2);
 
1124
        if (item == NULL)
 
1125
            goto error;
 
1126
        if (PyList_Append(group, item) != 0)
 
1127
            goto error;
 
1128
    }
 
1129
    size = PyList_Size(group);
 
1130
    if (size > 0 && !(size == 1 && tag == OP_EQUAL)) {
 
1131
        if (PyList_Append(answer, group) != 0)
 
1132
            goto error;
 
1133
    }
 
1134
    else
 
1135
        Py_DECREF(group);
 
1136
 
 
1137
    free(codes);
 
1138
    free(matches.matches);
 
1139
    return answer;
 
1140
 
 
1141
error:
 
1142
    free(codes);
 
1143
    free(matches.matches);
 
1144
    Py_DECREF(group);
 
1145
    Py_DECREF(answer);
 
1146
    return NULL;
 
1147
}
 
1148
 
 
1149
 
 
1150
static PyMethodDef PatienceSequenceMatcher_methods[] = {
 
1151
    {"get_matching_blocks",
 
1152
     (PyCFunction)PatienceSequenceMatcher_get_matching_blocks,
 
1153
     METH_NOARGS,
 
1154
     PatienceSequenceMatcher_get_matching_blocks_doc},
 
1155
    {"get_opcodes",
 
1156
     (PyCFunction)PatienceSequenceMatcher_get_opcodes,
 
1157
     METH_NOARGS,
 
1158
     PatienceSequenceMatcher_get_opcodes_doc},
 
1159
    {"get_grouped_opcodes",
 
1160
     (PyCFunction)PatienceSequenceMatcher_get_grouped_opcodes,
 
1161
     METH_VARARGS,
 
1162
     PatienceSequenceMatcher_get_grouped_opcodes_doc},
 
1163
    {NULL}
 
1164
};
 
1165
 
 
1166
 
 
1167
static char PatienceSequenceMatcher_doc[] =
 
1168
    "C implementation of PatienceSequenceMatcher";
 
1169
 
 
1170
 
 
1171
static PyTypeObject PatienceSequenceMatcherType = {
 
1172
    PyObject_HEAD_INIT(NULL)
 
1173
    0,                                           /* ob_size */
 
1174
    "PatienceSequenceMatcher",                   /* tp_name */
 
1175
    sizeof(PatienceSequenceMatcher),             /* tp_basicsize */
 
1176
    0,                                           /* tp_itemsize */
 
1177
    (destructor)PatienceSequenceMatcher_dealloc, /* tp_dealloc */
 
1178
    0,                                           /* tp_print */
 
1179
    0,                                           /* tp_getattr */
 
1180
    0,                                           /* tp_setattr */
 
1181
    0,                                           /* tp_compare */
 
1182
    0,                                           /* tp_repr */
 
1183
    0,                                           /* tp_as_number */
 
1184
    0,                                           /* tp_as_sequence */
 
1185
    0,                                           /* tp_as_mapping */
 
1186
    0,                                           /* tp_hash */
 
1187
    0,                                           /* tp_call */
 
1188
    0,                                           /* tp_str */
 
1189
    0,                                           /* tp_getattro */
 
1190
    0,                                           /* tp_setattro */
 
1191
    0,                                           /* tp_as_buffer */
 
1192
    Py_TPFLAGS_DEFAULT,                          /* tp_flags*/
 
1193
    PatienceSequenceMatcher_doc,                 /* tp_doc */
 
1194
    0,                                           /* tp_traverse */
 
1195
    0,                                           /* tp_clear */
 
1196
    0,                                           /* tp_richcompare */
 
1197
    0,                                           /* tp_weaklistoffset */
 
1198
    0,                                           /* tp_iter */
 
1199
    0,                                           /* tp_iternext */
 
1200
    PatienceSequenceMatcher_methods,             /* tp_methods */
 
1201
    0,                                           /* tp_members */
 
1202
    0,                                           /* tp_getset */
 
1203
    0,                                           /* tp_base */
 
1204
    0,                                           /* tp_dict */
 
1205
    0,                                           /* tp_descr_get */
 
1206
    0,                                           /* tp_descr_set */
 
1207
    0,                                           /* tp_dictoffset */
 
1208
    0,                                           /* tp_init */
 
1209
    0,                                           /* tp_alloc */
 
1210
    PatienceSequenceMatcher_new,                 /* tp_new */
 
1211
};
 
1212
 
 
1213
 
 
1214
static PyMethodDef cpatiencediff_methods[] = {
 
1215
    {"unique_lcs_c", py_unique_lcs, METH_VARARGS},
 
1216
    {"recurse_matches_c", py_recurse_matches, METH_VARARGS},
 
1217
    {NULL, NULL}
 
1218
};
 
1219
 
 
1220
 
 
1221
PyMODINIT_FUNC
 
1222
init_patiencediff_c(void)
 
1223
{
 
1224
    PyObject* m;
 
1225
 
 
1226
    if (PyType_Ready(&PatienceSequenceMatcherType) < 0)
 
1227
        return;
 
1228
 
 
1229
    m = Py_InitModule3("_patiencediff_c", cpatiencediff_methods,
 
1230
                       "C implementation of PatienceSequenceMatcher");
 
1231
    if (m == NULL)
 
1232
      return;
 
1233
 
 
1234
    Py_INCREF(&PatienceSequenceMatcherType);
 
1235
    PyModule_AddObject(m, "PatienceSequenceMatcher_c",
 
1236
                       (PyObject *)&PatienceSequenceMatcherType);
 
1237
}
 
1238
 
 
1239
 
 
1240
/* vim: sw=4 et 
 
1241
 */