~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_static_tuple_c.c

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-10-12 19:51:59 UTC
  • mfrom: (4679.5.11 2.1-static-tuple-no-use)
  • Revision ID: pqm@pqm.ubuntu.com-20091012195159-n0de3utv1q92agvg
(jam) Implement a StaticTuple() class,
        with lighter memory and not in Python's GC.

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