/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 bzrlib/_static_tuple_c.c

  • Committer: John Arbash Meinel
  • Date: 2009-10-17 04:43:14 UTC
  • mto: This revision was merged to the branch mainline in revision 4756.
  • Revision ID: john@arbash-meinel.com-20091017044314-nlvrrqnz0f2wzcp4
change the GroupcompressBlock code a bit.
If the first decompress request is big enough, just decompress everything.
And when we do that, let go of the decompressobj.

After digging through the zlib code, it looks like 1 zlib stream object
contains a 5kB internal state, and another 4*64kB buffers. (about 260kB
of total state.)
That turns out to be quite a lot if you think about it.


In the case of branching a copy of 'bzr.dev' locally, this turned out
to be 383MB w/ bzr.dev and 345MB w/ only this patch. (So ~11% of peak).

Also, this was 'unreferenced' memory, because it is hidden in the
zlib internal state in working buffers. So it wasn't memory that Meliae
could find. \o/.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2009 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
 
 
18
/* Must be defined before importing _static_tuple_c.h so that we get the right
 
19
 * linkage.
 
20
 */
 
21
#define STATIC_TUPLE_MODULE
 
22
 
 
23
#include <Python.h>
 
24
#include "python-compat.h"
 
25
 
 
26
#include "_static_tuple_c.h"
 
27
#include "_export_c_api.h"
 
28
 
 
29
/* Pyrex 0.9.6.4 exports _simple_set_pyx_api as
 
30
 * import__simple_set_pyx(), while Pyrex 0.9.8.5 and Cython 0.11.3 export them
 
31
 * as import_bzrlib___simple_set_pyx(). As such, we just #define one to be
 
32
 * equivalent to the other in our internal code.
 
33
 */
 
34
#define import__simple_set_pyx import_bzrlib___simple_set_pyx
 
35
#include "_simple_set_pyx_api.h"
 
36
 
 
37
#if defined(__GNUC__)
 
38
#   define inline __inline__
 
39
#elif defined(_MSC_VER)
 
40
#   define inline __inline
 
41
#else
 
42
#   define inline
 
43
#endif
 
44
 
 
45
 
 
46
/* The one and only StaticTuple with no values */
 
47
static StaticTuple *_empty_tuple = NULL;
 
48
static PyObject *_interned_tuples = NULL;
 
49
 
 
50
 
 
51
static inline int
 
52
_StaticTuple_is_interned(StaticTuple *self)
 
53
{
 
54
    return self->flags & STATIC_TUPLE_INTERNED_FLAG;
 
55
}
 
56
 
 
57
 
 
58
 
 
59
static PyObject *
 
60
StaticTuple_as_tuple(StaticTuple *self)
 
61
{
 
62
    PyObject *tpl = NULL, *obj = NULL;
 
63
    int i, len;
 
64
 
 
65
    len = self->size;
 
66
    tpl = PyTuple_New(len);
 
67
    if (!tpl) {
 
68
        /* Malloc failure */
 
69
        return NULL;
 
70
    }
 
71
    for (i = 0; i < len; ++i) {
 
72
        obj = (PyObject *)self->items[i];
 
73
        Py_INCREF(obj);
 
74
        PyTuple_SET_ITEM(tpl, i, obj);
 
75
    }
 
76
    return tpl;
 
77
}
 
78
 
 
79
 
 
80
static char StaticTuple_as_tuple_doc[] = "as_tuple() => tuple";
 
81
 
 
82
static StaticTuple *
 
83
StaticTuple_Intern(StaticTuple *self)
 
84
{
 
85
    PyObject *canonical_tuple = NULL;
 
86
 
 
87
    if (_interned_tuples == NULL || _StaticTuple_is_interned(self)) {
 
88
        Py_INCREF(self);
 
89
        return self;
 
90
    }
 
91
    /* SimpleSet_Add returns whatever object is present at self
 
92
     * or the new object if it needs to add it.
 
93
     */
 
94
    canonical_tuple = SimpleSet_Add(_interned_tuples, (PyObject *)self);
 
95
    if (!canonical_tuple) {
 
96
        // Some sort of exception, propogate it.
 
97
        return NULL;
 
98
    }
 
99
    if (canonical_tuple != (PyObject *)self) {
 
100
        // There was already a tuple with that value
 
101
        return (StaticTuple *)canonical_tuple;
 
102
    }
 
103
    self->flags |= STATIC_TUPLE_INTERNED_FLAG;
 
104
    // The two references in the dict do not count, so that the StaticTuple
 
105
    // object does not become immortal just because it was interned.
 
106
    Py_REFCNT(self) -= 1;
 
107
    return self;
 
108
}
 
109
 
 
110
static char StaticTuple_Intern_doc[] = "intern() => unique StaticTuple\n"
 
111
    "Return a 'canonical' StaticTuple object.\n"
 
112
    "Similar to intern() for strings, this makes sure there\n"
 
113
    "is only one StaticTuple object for a given value\n."
 
114
    "Common usage is:\n"
 
115
    "  key = StaticTuple('foo', 'bar').intern()\n";
 
116
 
 
117
 
 
118
static void
 
119
StaticTuple_dealloc(StaticTuple *self)
 
120
{
 
121
    int i, len;
 
122
 
 
123
    if (_StaticTuple_is_interned(self)) {
 
124
        /* revive dead object temporarily for Discard */
 
125
        Py_REFCNT(self) = 2;
 
126
        if (SimpleSet_Discard(_interned_tuples, (PyObject*)self) != 1)
 
127
            Py_FatalError("deletion of interned StaticTuple failed");
 
128
        self->flags &= ~STATIC_TUPLE_INTERNED_FLAG;
 
129
    }
 
130
    len = self->size;
 
131
    for (i = 0; i < len; ++i) {
 
132
        Py_XDECREF(self->items[i]);
 
133
    }
 
134
    Py_TYPE(self)->tp_free((PyObject *)self);
 
135
}
 
136
 
 
137
 
 
138
/* Similar to PyTuple_New() */
 
139
static StaticTuple *
 
140
StaticTuple_New(Py_ssize_t size)
 
141
{
 
142
    StaticTuple *stuple;
 
143
    if (size < 0) {
 
144
        PyErr_BadInternalCall();
 
145
        return NULL;
 
146
    }
 
147
 
 
148
    if (size == 0 && _empty_tuple != NULL) {
 
149
        Py_INCREF(_empty_tuple);
 
150
        return _empty_tuple;
 
151
    }
 
152
    /* Note that we use PyObject_NewVar because we want to allocate a variable
 
153
     * width entry. However we *aren't* truly a PyVarObject because we don't
 
154
     * use a long for ob_size. Instead we use a plain 'size' that is an int,
 
155
     * and will be overloaded with flags in the future.
 
156
     * As such we do the alloc, and then have to clean up anything it does
 
157
     * incorrectly.
 
158
     */
 
159
    stuple = PyObject_NewVar(StaticTuple, &StaticTuple_Type, size);
 
160
    if (stuple == NULL) {
 
161
        return NULL;
 
162
    }
 
163
    stuple->size = size;
 
164
    stuple->flags = 0;
 
165
    stuple->_unused0 = 0;
 
166
    stuple->_unused1 = 0;
 
167
    if (size > 0) {
 
168
        memset(stuple->items, 0, sizeof(PyObject *) * size);
 
169
    }
 
170
#if STATIC_TUPLE_HAS_HASH
 
171
    stuple->hash = -1;
 
172
#endif
 
173
    return stuple;
 
174
}
 
175
 
 
176
 
 
177
static PyObject *
 
178
StaticTuple_new_constructor(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
179
{
 
180
    StaticTuple *self;
 
181
    PyObject *obj = NULL;
 
182
    Py_ssize_t i, len = 0;
 
183
 
 
184
    if (type != &StaticTuple_Type) {
 
185
        PyErr_SetString(PyExc_TypeError, "we only support creating StaticTuple");
 
186
        return NULL;
 
187
    }
 
188
    if (!PyTuple_CheckExact(args)) {
 
189
        PyErr_SetString(PyExc_TypeError, "args must be a tuple");
 
190
        return NULL;
 
191
    }
 
192
    len = PyTuple_GET_SIZE(args);
 
193
    if (len < 0 || len > 255) {
 
194
        /* Too big or too small */
 
195
        PyErr_SetString(PyExc_ValueError, "StaticTuple.__init__(...)"
 
196
            " takes from 0 to 255 items");
 
197
        return NULL;
 
198
    }
 
199
    self = (StaticTuple *)StaticTuple_New(len);
 
200
    if (self == NULL) {
 
201
        return NULL;
 
202
    }
 
203
    for (i = 0; i < len; ++i) {
 
204
        obj = PyTuple_GET_ITEM(args, i);
 
205
        if (!PyString_CheckExact(obj)) {
 
206
            if (!StaticTuple_CheckExact(obj)) {
 
207
                PyErr_SetString(PyExc_TypeError, "StaticTuple.__init__(...)"
 
208
                    " requires that all items are strings or StaticTuple.");
 
209
                type->tp_dealloc((PyObject *)self);
 
210
                return NULL;
 
211
            }
 
212
        }
 
213
        Py_INCREF(obj);
 
214
        self->items[i] = obj;
 
215
    }
 
216
    return (PyObject *)self;
 
217
}
 
218
 
 
219
static PyObject *
 
220
StaticTuple_repr(StaticTuple *self)
 
221
{
 
222
    PyObject *as_tuple, *tuple_repr, *result;
 
223
 
 
224
    as_tuple = StaticTuple_as_tuple(self);
 
225
    if (as_tuple == NULL) {
 
226
        return NULL;
 
227
    }
 
228
    tuple_repr = PyObject_Repr(as_tuple);
 
229
    Py_DECREF(as_tuple);
 
230
    if (tuple_repr == NULL) {
 
231
        return NULL;
 
232
    }
 
233
    result = PyString_FromFormat("%s%s", Py_TYPE(self)->tp_name,
 
234
                                         PyString_AsString(tuple_repr));
 
235
    return result;
 
236
}
 
237
 
 
238
static long
 
239
StaticTuple_hash(StaticTuple *self)
 
240
{
 
241
    /* adapted from tuplehash(), is the specific hash value considered
 
242
     * 'stable'?
 
243
     */
 
244
    register long x, y;
 
245
    Py_ssize_t len = self->size;
 
246
    PyObject **p;
 
247
    long mult = 1000003L;
 
248
 
 
249
#if STATIC_TUPLE_HAS_HASH
 
250
    if (self->hash != -1) {
 
251
        return self->hash;
 
252
    }
 
253
#endif
 
254
    x = 0x345678L;
 
255
    p = self->items;
 
256
    // TODO: We could set specific flags if we know that, for example, all the
 
257
    //       items are strings. I haven't seen a real-world benefit to that
 
258
    //       yet, though.
 
259
    while (--len >= 0) {
 
260
        y = PyObject_Hash(*p++);
 
261
        if (y == -1) /* failure */
 
262
            return -1;
 
263
        x = (x ^ y) * mult;
 
264
        /* the cast might truncate len; that doesn't change hash stability */
 
265
        mult += (long)(82520L + len + len);
 
266
    }
 
267
    x += 97531L;
 
268
    if (x == -1)
 
269
        x = -2;
 
270
#if STATIC_TUPLE_HAS_HASH
 
271
    self->hash = x;
 
272
#endif
 
273
    return x;
 
274
}
 
275
 
 
276
static PyObject *
 
277
StaticTuple_richcompare_to_tuple(StaticTuple *v, PyObject *wt, int op)
 
278
{
 
279
    PyObject *vt;
 
280
    PyObject *result = NULL;
 
281
    
 
282
    vt = StaticTuple_as_tuple((StaticTuple *)v);
 
283
    if (vt == NULL) {
 
284
        goto done;
 
285
    }
 
286
    if (!PyTuple_Check(wt)) {
 
287
        PyErr_BadInternalCall();
 
288
        goto done;
 
289
    }
 
290
    /* Now we have 2 tuples to compare, do it */
 
291
    result = PyTuple_Type.tp_richcompare(vt, wt, op);
 
292
done:
 
293
    Py_XDECREF(vt);
 
294
    return result;
 
295
}
 
296
 
 
297
/** Compare two objects to determine if they are equivalent.
 
298
 * The basic flow is as follows
 
299
 *  1) First make sure that both objects are StaticTuple instances. If they
 
300
 *     aren't then cast self to a tuple, and have the tuple do the comparison.
 
301
 *  2) Special case comparison to Py_None, because it happens to occur fairly
 
302
 *     often in the test suite.
 
303
 *  3) Special case when v and w are the same pointer. As we know the answer to
 
304
 *     all queries without walking individual items.
 
305
 *  4) For all operations, we then walk the items to find the first paired
 
306
 *     items that are not equal.
 
307
 *  5) If all items found are equal, we then check the length of self and
 
308
 *     other to determine equality.
 
309
 *  6) If an item differs, then we apply "op" to those last two items. (eg.
 
310
 *     StaticTuple(A, B) > StaticTuple(A, C) iff B > C)
 
311
 */
 
312
 
 
313
static PyObject *
 
314
StaticTuple_richcompare(PyObject *v, PyObject *w, int op)
 
315
{
 
316
    StaticTuple *v_st, *w_st;
 
317
    Py_ssize_t vlen, wlen, min_len, i;
 
318
    PyObject *v_obj, *w_obj;
 
319
    richcmpfunc string_richcompare;
 
320
 
 
321
    if (!StaticTuple_CheckExact(v)) {
 
322
        /* This has never triggered, according to python-dev it seems this
 
323
         * might trigger if '__op__' is defined but '__rop__' is not, sort of
 
324
         * case. Such as "None == StaticTuple()"
 
325
         */
 
326
        fprintf(stderr, "self is not StaticTuple\n");
 
327
        Py_INCREF(Py_NotImplemented);
 
328
        return Py_NotImplemented;
 
329
    }
 
330
    v_st = (StaticTuple *)v;
 
331
    if (StaticTuple_CheckExact(w)) {
 
332
        /* The most common case */
 
333
        w_st = (StaticTuple*)w;
 
334
    } else if (PyTuple_Check(w)) {
 
335
        /* One of v or w is a tuple, so we go the 'slow' route and cast up to
 
336
         * tuples to compare.
 
337
         */
 
338
        /* TODO: This seems to be triggering more than I thought it would...
 
339
         *       We probably want to optimize comparing self to other when
 
340
         *       other is a tuple.
 
341
         */
 
342
        return StaticTuple_richcompare_to_tuple(v_st, w, op);
 
343
    } else if (w == Py_None) {
 
344
        // None is always less than the object
 
345
        switch (op) {
 
346
        case Py_NE:case Py_GT:case Py_GE:
 
347
            Py_INCREF(Py_True);
 
348
            return Py_True;
 
349
        case Py_EQ:case Py_LT:case Py_LE:
 
350
            Py_INCREF(Py_False);
 
351
            return Py_False;
 
352
    default: // Should never happen
 
353
        return Py_NotImplemented;
 
354
        }
 
355
    } else {
 
356
        /* We don't special case this comparison, we just let python handle
 
357
         * it.
 
358
         */
 
359
         Py_INCREF(Py_NotImplemented);
 
360
         return Py_NotImplemented;
 
361
    }
 
362
    /* Now we know that we have 2 StaticTuple objects, so let's compare them.
 
363
     * This code is inspired from tuplerichcompare, except we know our
 
364
     * objects are limited in scope, so we can inline some comparisons.
 
365
     */
 
366
    if (v == w) {
 
367
        /* Identical pointers, we can shortcut this easily. */
 
368
        switch (op) {
 
369
        case Py_EQ:case Py_LE:case Py_GE:
 
370
            Py_INCREF(Py_True);
 
371
            return Py_True;
 
372
        case Py_NE:case Py_LT:case Py_GT:
 
373
            Py_INCREF(Py_False);
 
374
            return Py_False;
 
375
        }
 
376
    }
 
377
    if (op == Py_EQ
 
378
        && _StaticTuple_is_interned(v_st)
 
379
        && _StaticTuple_is_interned(w_st))
 
380
    {
 
381
        /* If both objects are interned, we know they are different if the
 
382
         * pointer is not the same, which would have been handled by the
 
383
         * previous if. No need to compare the entries.
 
384
         */
 
385
        Py_INCREF(Py_False);
 
386
        return Py_False;
 
387
    }
 
388
 
 
389
    /* The only time we are likely to compare items of different lengths is in
 
390
     * something like the interned_keys set. However, the hash is good enough
 
391
     * that it is rare. Note that 'tuple_richcompare' also does not compare
 
392
     * lengths here.
 
393
     */
 
394
    vlen = v_st->size;
 
395
    wlen = w_st->size;
 
396
    min_len = (vlen < wlen) ? vlen : wlen;
 
397
    string_richcompare = PyString_Type.tp_richcompare;
 
398
    for (i = 0; i < min_len; i++) {
 
399
        PyObject *result = NULL;
 
400
        v_obj = StaticTuple_GET_ITEM(v_st, i);
 
401
        w_obj = StaticTuple_GET_ITEM(w_st, i);
 
402
        if (v_obj == w_obj) {
 
403
            /* Shortcut case, these must be identical */
 
404
            continue;
 
405
        }
 
406
        if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) {
 
407
            result = string_richcompare(v_obj, w_obj, Py_EQ);
 
408
        } else if (StaticTuple_CheckExact(v_obj) &&
 
409
                   StaticTuple_CheckExact(w_obj))
 
410
        {
 
411
            /* Both are StaticTuple types, so recurse */
 
412
            result = StaticTuple_richcompare(v_obj, w_obj, Py_EQ);
 
413
        } else {
 
414
            /* Not the same type, obviously they won't compare equal */
 
415
            break;
 
416
        }
 
417
        if (result == NULL) {
 
418
            return NULL; /* There seems to be an error */
 
419
        }
 
420
        if (result == Py_NotImplemented) {
 
421
            Py_DECREF(result);
 
422
            /* One side must have had a string and the other a StaticTuple.
 
423
             * This clearly means that they are not equal.
 
424
             */
 
425
            if (op == Py_EQ) {
 
426
                Py_INCREF(Py_False);
 
427
                return Py_False;
 
428
            }
 
429
            result = PyObject_RichCompare(v_obj, w_obj, Py_EQ);
 
430
        }
 
431
        if (result == Py_False) {
 
432
            /* This entry is not identical
 
433
             * Shortcut for Py_EQ
 
434
             */
 
435
            if (op == Py_EQ) {
 
436
                return result;
 
437
            }
 
438
            Py_DECREF(result);
 
439
            break;
 
440
        }
 
441
        if (result != Py_True) {
 
442
            /* We don't know *what* richcompare is returning, but it
 
443
             * isn't something we recognize
 
444
             */
 
445
            PyErr_BadInternalCall();
 
446
            Py_DECREF(result);
 
447
            return NULL;
 
448
        }
 
449
        Py_DECREF(result);
 
450
    }
 
451
    if (i >= min_len) {
 
452
        /* We walked off one of the lists, but everything compared equal so
 
453
         * far. Just compare the size.
 
454
         */
 
455
        int cmp;
 
456
        PyObject *res;
 
457
        switch (op) {
 
458
        case Py_LT: cmp = vlen <  wlen; break;
 
459
        case Py_LE: cmp = vlen <= wlen; break;
 
460
        case Py_EQ: cmp = vlen == wlen; break;
 
461
        case Py_NE: cmp = vlen != wlen; break;
 
462
        case Py_GT: cmp = vlen >  wlen; break;
 
463
        case Py_GE: cmp = vlen >= wlen; break;
 
464
        default: return NULL; /* cannot happen */
 
465
        }
 
466
        if (cmp)
 
467
            res = Py_True;
 
468
        else
 
469
            res = Py_False;
 
470
        Py_INCREF(res);
 
471
        return res;
 
472
    }
 
473
    /* The last item differs, shortcut the Py_NE case */
 
474
    if (op == Py_NE) {
 
475
        Py_INCREF(Py_True);
 
476
        return Py_True;
 
477
    }
 
478
    /* It is some other comparison, go ahead and do the real check. */
 
479
    if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj))
 
480
    {
 
481
        return string_richcompare(v_obj, w_obj, op);
 
482
    } else if (StaticTuple_CheckExact(v_obj) &&
 
483
               StaticTuple_CheckExact(w_obj))
 
484
    {
 
485
        /* Both are StaticTuple types, so recurse */
 
486
        return StaticTuple_richcompare(v_obj, w_obj, op);
 
487
    } else {
 
488
        Py_INCREF(Py_NotImplemented);
 
489
        return Py_NotImplemented;
 
490
    }
 
491
}
 
492
 
 
493
 
 
494
static Py_ssize_t
 
495
StaticTuple_length(StaticTuple *self)
 
496
{
 
497
    return self->size;
 
498
}
 
499
 
 
500
 
 
501
static PyObject *
 
502
StaticTuple__is_interned(StaticTuple *self)
 
503
{
 
504
    if (_StaticTuple_is_interned(self)) {
 
505
        Py_INCREF(Py_True);
 
506
        return Py_True;
 
507
    }
 
508
    Py_INCREF(Py_False);
 
509
    return Py_False;
 
510
}
 
511
 
 
512
static char StaticTuple__is_interned_doc[] = "_is_interned() => True/False\n"
 
513
    "Check to see if this tuple has been interned.\n";
 
514
 
 
515
 
 
516
static PyObject *
 
517
StaticTuple_item(StaticTuple *self, Py_ssize_t offset)
 
518
{
 
519
    PyObject *obj;
 
520
    /* We cast to (int) to avoid worrying about whether Py_ssize_t is a
 
521
     * long long, etc. offsets should never be >2**31 anyway.
 
522
     */
 
523
    if (offset < 0) {
 
524
        PyErr_Format(PyExc_IndexError, "StaticTuple_item does not support"
 
525
            " negative indices: %d\n", (int)offset);
 
526
    } else if (offset >= self->size) {
 
527
        PyErr_Format(PyExc_IndexError, "StaticTuple index out of range"
 
528
            " %d >= %d", (int)offset, (int)self->size);
 
529
        return NULL;
 
530
    }
 
531
    obj = (PyObject *)self->items[offset];
 
532
    Py_INCREF(obj);
 
533
    return obj;
 
534
}
 
535
 
 
536
static PyObject *
 
537
StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
 
538
{
 
539
    PyObject *as_tuple, *result;
 
540
 
 
541
    as_tuple = StaticTuple_as_tuple(self);
 
542
    if (as_tuple == NULL) {
 
543
        return NULL;
 
544
    }
 
545
    result = PyTuple_Type.tp_as_sequence->sq_slice(as_tuple, ilow, ihigh);
 
546
    Py_DECREF(as_tuple);
 
547
    return result;
 
548
}
 
549
 
 
550
static int
 
551
StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
 
552
{
 
553
    Py_ssize_t i;
 
554
    for (i = self->size; --i >= 0;) {
 
555
        Py_VISIT(self->items[i]);
 
556
    }
 
557
    return 0;
 
558
}
 
559
 
 
560
static char StaticTuple_doc[] =
 
561
    "C implementation of a StaticTuple structure."
 
562
    "\n This is used as StaticTuple(item1, item2, item3)"
 
563
    "\n This is similar to tuple, less flexible in what it"
 
564
    "\n supports, but also lighter memory consumption."
 
565
    "\n Note that the constructor mimics the () form of tuples"
 
566
    "\n Rather than the 'tuple()' constructor."
 
567
    "\n  eg. StaticTuple(a, b) == (a, b) == tuple((a, b))";
 
568
 
 
569
static PyMethodDef StaticTuple_methods[] = {
 
570
    {"as_tuple", (PyCFunction)StaticTuple_as_tuple, METH_NOARGS, StaticTuple_as_tuple_doc},
 
571
    {"intern", (PyCFunction)StaticTuple_Intern, METH_NOARGS, StaticTuple_Intern_doc},
 
572
    {"_is_interned", (PyCFunction)StaticTuple__is_interned, METH_NOARGS,
 
573
     StaticTuple__is_interned_doc},
 
574
    {NULL, NULL} /* sentinel */
 
575
};
 
576
 
 
577
static PySequenceMethods StaticTuple_as_sequence = {
 
578
    (lenfunc)StaticTuple_length,            /* sq_length */
 
579
    0,                              /* sq_concat */
 
580
    0,                              /* sq_repeat */
 
581
    (ssizeargfunc)StaticTuple_item,         /* sq_item */
 
582
    (ssizessizeargfunc)StaticTuple_slice,   /* sq_slice */
 
583
    0,                              /* sq_ass_item */
 
584
    0,                              /* sq_ass_slice */
 
585
    0,                              /* sq_contains */
 
586
};
 
587
 
 
588
/* TODO: Implement StaticTuple_as_mapping.
 
589
 *       The only thing we really want to support from there is mp_subscript,
 
590
 *       so that we could support extended slicing (foo[::2]). Not worth it
 
591
 *       yet, though.
 
592
 */
 
593
 
 
594
 
 
595
PyTypeObject StaticTuple_Type = {
 
596
    PyObject_HEAD_INIT(NULL)
 
597
    0,                                           /* ob_size */
 
598
    "StaticTuple",                               /* tp_name */
 
599
    sizeof(StaticTuple),                         /* tp_basicsize */
 
600
    sizeof(PyObject *),                          /* tp_itemsize */
 
601
    (destructor)StaticTuple_dealloc,             /* tp_dealloc */
 
602
    0,                                           /* tp_print */
 
603
    0,                                           /* tp_getattr */
 
604
    0,                                           /* tp_setattr */
 
605
    0,                                           /* tp_compare */
 
606
    (reprfunc)StaticTuple_repr,                  /* tp_repr */
 
607
    0,                                           /* tp_as_number */
 
608
    &StaticTuple_as_sequence,                    /* tp_as_sequence */
 
609
    0,                                           /* tp_as_mapping */
 
610
    (hashfunc)StaticTuple_hash,                  /* tp_hash */
 
611
    0,                                           /* tp_call */
 
612
    0,                                           /* tp_str */
 
613
    0,                                           /* tp_getattro */
 
614
    0,                                           /* tp_setattro */
 
615
    0,                                           /* tp_as_buffer */
 
616
    Py_TPFLAGS_DEFAULT,                          /* tp_flags*/
 
617
    StaticTuple_doc,                             /* tp_doc */
 
618
    /* gc.get_referents checks the IS_GC flag before it calls tp_traverse
 
619
     * And we don't include this object in the garbage collector because we
 
620
     * know it doesn't create cycles. However, 'meliae' will follow
 
621
     * tp_traverse, even if the object isn't GC, and we want that.
 
622
     */
 
623
    (traverseproc)StaticTuple_traverse,          /* tp_traverse */
 
624
    0,                                           /* tp_clear */
 
625
    StaticTuple_richcompare,                     /* tp_richcompare */
 
626
    0,                                           /* tp_weaklistoffset */
 
627
    // without implementing tp_iter, Python will fall back to PySequence*
 
628
    // which seems to work ok, we may need something faster/lighter in the
 
629
    // future.
 
630
    0,                                           /* tp_iter */
 
631
    0,                                           /* tp_iternext */
 
632
    StaticTuple_methods,                         /* tp_methods */
 
633
    0,                                           /* tp_members */
 
634
    0,                                           /* tp_getset */
 
635
    0,                                           /* tp_base */
 
636
    0,                                           /* tp_dict */
 
637
    0,                                           /* tp_descr_get */
 
638
    0,                                           /* tp_descr_set */
 
639
    0,                                           /* tp_dictoffset */
 
640
    0,                                           /* tp_init */
 
641
    0,                                           /* tp_alloc */
 
642
    StaticTuple_new_constructor,                 /* tp_new */
 
643
};
 
644
 
 
645
 
 
646
static PyMethodDef static_tuple_c_methods[] = {
 
647
    {NULL, NULL}
 
648
};
 
649
 
 
650
 
 
651
static void
 
652
setup_interned_tuples(PyObject *m)
 
653
{
 
654
    _interned_tuples = (PyObject *)SimpleSet_New();
 
655
    if (_interned_tuples != NULL) {
 
656
        Py_INCREF(_interned_tuples);
 
657
        PyModule_AddObject(m, "_interned_tuples", _interned_tuples);
 
658
    }
 
659
}
 
660
 
 
661
 
 
662
static void
 
663
setup_empty_tuple(PyObject *m)
 
664
{
 
665
    StaticTuple *stuple;
 
666
    if (_interned_tuples == NULL) {
 
667
        fprintf(stderr, "You need to call setup_interned_tuples() before"
 
668
                " setup_empty_tuple, because we intern it.\n");
 
669
    }
 
670
    // We need to create the empty tuple
 
671
    stuple = (StaticTuple *)StaticTuple_New(0);
 
672
    _empty_tuple = StaticTuple_Intern(stuple);
 
673
    assert(_empty_tuple == stuple);
 
674
    // At this point, refcnt is 2: 1 from New(), and 1 from the return from
 
675
    // intern(). We will keep 1 for the _empty_tuple global, and use the other
 
676
    // for the module reference.
 
677
    PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple);
 
678
}
 
679
 
 
680
static int
 
681
_StaticTuple_CheckExact(PyObject *obj)
 
682
{
 
683
    return StaticTuple_CheckExact(obj);
 
684
}
 
685
 
 
686
static void
 
687
setup_c_api(PyObject *m)
 
688
{
 
689
    _export_function(m, "StaticTuple_New", StaticTuple_New,
 
690
        "StaticTuple *(Py_ssize_t)");
 
691
    _export_function(m, "StaticTuple_Intern", StaticTuple_Intern,
 
692
        "StaticTuple *(StaticTuple *)");
 
693
    _export_function(m, "_StaticTuple_CheckExact", _StaticTuple_CheckExact,
 
694
        "int(PyObject *)");
 
695
}
 
696
 
 
697
 
 
698
static int
 
699
_workaround_pyrex_096(void)
 
700
{
 
701
    /* Work around an incompatibility in how pyrex 0.9.6 exports a module,
 
702
     * versus how pyrex 0.9.8 and cython 0.11 export it.
 
703
     * Namely 0.9.6 exports import__simple_set_pyx and tries to
 
704
     * "import _simple_set_pyx" but it is available only as
 
705
     * "import bzrlib._simple_set_pyx"
 
706
     * It is a shame to hack up sys.modules, but that is what we've got to do.
 
707
     */
 
708
    PyObject *sys_module = NULL, *modules = NULL, *set_module = NULL;
 
709
    int retval = -1;
 
710
 
 
711
    /* Clear out the current ImportError exception, and try again. */
 
712
    PyErr_Clear();
 
713
    /* Note that this only seems to work if somewhere else imports
 
714
     * bzrlib._simple_set_pyx before importing bzrlib._static_tuple_c
 
715
     */
 
716
    set_module = PyImport_ImportModule("bzrlib._simple_set_pyx");
 
717
    if (set_module == NULL) {
 
718
        // fprintf(stderr, "Failed to import bzrlib._simple_set_pyx\n");
 
719
        goto end;
 
720
    }
 
721
    /* Add the _simple_set_pyx into sys.modules at the appropriate location. */
 
722
    sys_module = PyImport_ImportModule("sys");
 
723
    if (sys_module == NULL) {
 
724
        // fprintf(stderr, "Failed to import sys\n");
 
725
        goto end;
 
726
    }
 
727
    modules = PyObject_GetAttrString(sys_module, "modules");
 
728
    if (modules == NULL || !PyDict_Check(modules)) {
 
729
        // fprintf(stderr, "Failed to find sys.modules\n");
 
730
        goto end;
 
731
    }
 
732
    PyDict_SetItemString(modules, "_simple_set_pyx", set_module);
 
733
    /* Now that we have hacked it in, try the import again. */
 
734
    retval = import_bzrlib___simple_set_pyx();
 
735
end:
 
736
    Py_XDECREF(set_module);
 
737
    Py_XDECREF(sys_module);
 
738
    Py_XDECREF(modules);
 
739
    return retval;
 
740
}
 
741
 
 
742
 
 
743
PyMODINIT_FUNC
 
744
init_static_tuple_c(void)
 
745
{
 
746
    PyObject* m;
 
747
 
 
748
    StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
 
749
    if (PyType_Ready(&StaticTuple_Type) < 0)
 
750
        return;
 
751
 
 
752
    m = Py_InitModule3("_static_tuple_c", static_tuple_c_methods,
 
753
                       "C implementation of a StaticTuple structure");
 
754
    if (m == NULL)
 
755
      return;
 
756
 
 
757
    Py_INCREF(&StaticTuple_Type);
 
758
    PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
 
759
    if (import_bzrlib___simple_set_pyx() == -1
 
760
        && _workaround_pyrex_096() == -1)
 
761
    {
 
762
        return;
 
763
    }
 
764
    setup_interned_tuples(m);
 
765
    setup_empty_tuple(m);
 
766
    setup_c_api(m);
 
767
}