/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-19 15:39:25 UTC
  • mto: This revision was merged to the branch mainline in revision 4758.
  • Revision ID: john@arbash-meinel.com-20091019153925-pkvnaoho6a2aawj7
Start exposing an GraphIndex.clear_cache() member.
This is exposed on GraphIndex, CombinedGraphIndex and BTreeGraphIndex.

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 StaticTuple *
 
178
StaticTuple_FromSequence(PyObject *sequence)
 
179
{
 
180
    StaticTuple *new;
 
181
    PyObject *item;
 
182
    Py_ssize_t i, size;
 
183
 
 
184
    if (StaticTuple_CheckExact(sequence)) {
 
185
        Py_INCREF(sequence);
 
186
        return (StaticTuple *)sequence;
 
187
    }
 
188
    if (!PySequence_Check(sequence)) {
 
189
        PyErr_Format(PyExc_TypeError, "Type %s is not a sequence type",
 
190
                     Py_TYPE(sequence)->tp_name);
 
191
        return NULL;
 
192
    }
 
193
    size = PySequence_Size(sequence);
 
194
    if (size == -1)
 
195
        return NULL;
 
196
    new = StaticTuple_New(size);
 
197
    if (new == NULL) {
 
198
        return NULL;
 
199
    }
 
200
    for (i = 0; i < size; ++i) {
 
201
        // This returns a new reference, which we then 'steal' with 
 
202
        // StaticTuple_SET_ITEM
 
203
        item = PySequence_GetItem(sequence, i);
 
204
        if (item == NULL) {
 
205
            Py_DECREF(new);
 
206
            return NULL;
 
207
        }
 
208
        StaticTuple_SET_ITEM(new, i, item);
 
209
    }
 
210
    return (StaticTuple *)new;
 
211
}
 
212
 
 
213
static StaticTuple *
 
214
StaticTuple_from_sequence(PyObject *self, PyObject *args, PyObject *kwargs)
 
215
{
 
216
    PyObject *sequence;
 
217
    if (!PyArg_ParseTuple(args, "O", &sequence))
 
218
        return NULL;
 
219
    return StaticTuple_FromSequence(sequence);
 
220
}
 
221
 
 
222
 
 
223
static PyObject *
 
224
StaticTuple_new_constructor(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
225
{
 
226
    StaticTuple *self;
 
227
    PyObject *obj = NULL;
 
228
    Py_ssize_t i, len = 0;
 
229
 
 
230
    if (type != &StaticTuple_Type) {
 
231
        PyErr_SetString(PyExc_TypeError, "we only support creating StaticTuple");
 
232
        return NULL;
 
233
    }
 
234
    if (!PyTuple_CheckExact(args)) {
 
235
        PyErr_SetString(PyExc_TypeError, "args must be a tuple");
 
236
        return NULL;
 
237
    }
 
238
    len = PyTuple_GET_SIZE(args);
 
239
    if (len < 0 || len > 255) {
 
240
        /* Too big or too small */
 
241
        PyErr_SetString(PyExc_ValueError, "StaticTuple.__init__(...)"
 
242
            " takes from 0 to 255 items");
 
243
        return NULL;
 
244
    }
 
245
    self = (StaticTuple *)StaticTuple_New(len);
 
246
    if (self == NULL) {
 
247
        return NULL;
 
248
    }
 
249
    for (i = 0; i < len; ++i) {
 
250
        obj = PyTuple_GET_ITEM(args, i);
 
251
        if (!PyString_CheckExact(obj)) {
 
252
            if (!StaticTuple_CheckExact(obj)) {
 
253
                PyErr_SetString(PyExc_TypeError, "StaticTuple.__init__(...)"
 
254
                    " requires that all items are strings or StaticTuple.");
 
255
                type->tp_dealloc((PyObject *)self);
 
256
                return NULL;
 
257
            }
 
258
        }
 
259
        Py_INCREF(obj);
 
260
        self->items[i] = obj;
 
261
    }
 
262
    return (PyObject *)self;
 
263
}
 
264
 
 
265
static PyObject *
 
266
StaticTuple_repr(StaticTuple *self)
 
267
{
 
268
    PyObject *as_tuple, *tuple_repr, *result;
 
269
 
 
270
    as_tuple = StaticTuple_as_tuple(self);
 
271
    if (as_tuple == NULL) {
 
272
        return NULL;
 
273
    }
 
274
    tuple_repr = PyObject_Repr(as_tuple);
 
275
    Py_DECREF(as_tuple);
 
276
    if (tuple_repr == NULL) {
 
277
        return NULL;
 
278
    }
 
279
    result = PyString_FromFormat("%s%s", Py_TYPE(self)->tp_name,
 
280
                                         PyString_AsString(tuple_repr));
 
281
    return result;
 
282
}
 
283
 
 
284
static long
 
285
StaticTuple_hash(StaticTuple *self)
 
286
{
 
287
    /* adapted from tuplehash(), is the specific hash value considered
 
288
     * 'stable'?
 
289
     */
 
290
    register long x, y;
 
291
    Py_ssize_t len = self->size;
 
292
    PyObject **p;
 
293
    long mult = 1000003L;
 
294
 
 
295
#if STATIC_TUPLE_HAS_HASH
 
296
    if (self->hash != -1) {
 
297
        return self->hash;
 
298
    }
 
299
#endif
 
300
    x = 0x345678L;
 
301
    p = self->items;
 
302
    // TODO: We could set specific flags if we know that, for example, all the
 
303
    //       items are strings. I haven't seen a real-world benefit to that
 
304
    //       yet, though.
 
305
    while (--len >= 0) {
 
306
        y = PyObject_Hash(*p++);
 
307
        if (y == -1) /* failure */
 
308
            return -1;
 
309
        x = (x ^ y) * mult;
 
310
        /* the cast might truncate len; that doesn't change hash stability */
 
311
        mult += (long)(82520L + len + len);
 
312
    }
 
313
    x += 97531L;
 
314
    if (x == -1)
 
315
        x = -2;
 
316
#if STATIC_TUPLE_HAS_HASH
 
317
    self->hash = x;
 
318
#endif
 
319
    return x;
 
320
}
 
321
 
 
322
static PyObject *
 
323
StaticTuple_richcompare_to_tuple(StaticTuple *v, PyObject *wt, int op)
 
324
{
 
325
    PyObject *vt;
 
326
    PyObject *result = NULL;
 
327
    
 
328
    vt = StaticTuple_as_tuple((StaticTuple *)v);
 
329
    if (vt == NULL) {
 
330
        goto done;
 
331
    }
 
332
    if (!PyTuple_Check(wt)) {
 
333
        PyErr_BadInternalCall();
 
334
        goto done;
 
335
    }
 
336
    /* Now we have 2 tuples to compare, do it */
 
337
    result = PyTuple_Type.tp_richcompare(vt, wt, op);
 
338
done:
 
339
    Py_XDECREF(vt);
 
340
    return result;
 
341
}
 
342
 
 
343
/** Compare two objects to determine if they are equivalent.
 
344
 * The basic flow is as follows
 
345
 *  1) First make sure that both objects are StaticTuple instances. If they
 
346
 *     aren't then cast self to a tuple, and have the tuple do the comparison.
 
347
 *  2) Special case comparison to Py_None, because it happens to occur fairly
 
348
 *     often in the test suite.
 
349
 *  3) Special case when v and w are the same pointer. As we know the answer to
 
350
 *     all queries without walking individual items.
 
351
 *  4) For all operations, we then walk the items to find the first paired
 
352
 *     items that are not equal.
 
353
 *  5) If all items found are equal, we then check the length of self and
 
354
 *     other to determine equality.
 
355
 *  6) If an item differs, then we apply "op" to those last two items. (eg.
 
356
 *     StaticTuple(A, B) > StaticTuple(A, C) iff B > C)
 
357
 */
 
358
 
 
359
static PyObject *
 
360
StaticTuple_richcompare(PyObject *v, PyObject *w, int op)
 
361
{
 
362
    StaticTuple *v_st, *w_st;
 
363
    Py_ssize_t vlen, wlen, min_len, i;
 
364
    PyObject *v_obj, *w_obj;
 
365
    richcmpfunc string_richcompare;
 
366
 
 
367
    if (!StaticTuple_CheckExact(v)) {
 
368
        /* This has never triggered, according to python-dev it seems this
 
369
         * might trigger if '__op__' is defined but '__rop__' is not, sort of
 
370
         * case. Such as "None == StaticTuple()"
 
371
         */
 
372
        fprintf(stderr, "self is not StaticTuple\n");
 
373
        Py_INCREF(Py_NotImplemented);
 
374
        return Py_NotImplemented;
 
375
    }
 
376
    v_st = (StaticTuple *)v;
 
377
    if (StaticTuple_CheckExact(w)) {
 
378
        /* The most common case */
 
379
        w_st = (StaticTuple*)w;
 
380
    } else if (PyTuple_Check(w)) {
 
381
        /* One of v or w is a tuple, so we go the 'slow' route and cast up to
 
382
         * tuples to compare.
 
383
         */
 
384
        /* TODO: This seems to be triggering more than I thought it would...
 
385
         *       We probably want to optimize comparing self to other when
 
386
         *       other is a tuple.
 
387
         */
 
388
        return StaticTuple_richcompare_to_tuple(v_st, w, op);
 
389
    } else if (w == Py_None) {
 
390
        // None is always less than the object
 
391
        switch (op) {
 
392
        case Py_NE:case Py_GT:case Py_GE:
 
393
            Py_INCREF(Py_True);
 
394
            return Py_True;
 
395
        case Py_EQ:case Py_LT:case Py_LE:
 
396
            Py_INCREF(Py_False);
 
397
            return Py_False;
 
398
    default: // Should never happen
 
399
        return Py_NotImplemented;
 
400
        }
 
401
    } else {
 
402
        /* We don't special case this comparison, we just let python handle
 
403
         * it.
 
404
         */
 
405
         Py_INCREF(Py_NotImplemented);
 
406
         return Py_NotImplemented;
 
407
    }
 
408
    /* Now we know that we have 2 StaticTuple objects, so let's compare them.
 
409
     * This code is inspired from tuplerichcompare, except we know our
 
410
     * objects are limited in scope, so we can inline some comparisons.
 
411
     */
 
412
    if (v == w) {
 
413
        /* Identical pointers, we can shortcut this easily. */
 
414
        switch (op) {
 
415
        case Py_EQ:case Py_LE:case Py_GE:
 
416
            Py_INCREF(Py_True);
 
417
            return Py_True;
 
418
        case Py_NE:case Py_LT:case Py_GT:
 
419
            Py_INCREF(Py_False);
 
420
            return Py_False;
 
421
        }
 
422
    }
 
423
    if (op == Py_EQ
 
424
        && _StaticTuple_is_interned(v_st)
 
425
        && _StaticTuple_is_interned(w_st))
 
426
    {
 
427
        /* If both objects are interned, we know they are different if the
 
428
         * pointer is not the same, which would have been handled by the
 
429
         * previous if. No need to compare the entries.
 
430
         */
 
431
        Py_INCREF(Py_False);
 
432
        return Py_False;
 
433
    }
 
434
 
 
435
    /* The only time we are likely to compare items of different lengths is in
 
436
     * something like the interned_keys set. However, the hash is good enough
 
437
     * that it is rare. Note that 'tuple_richcompare' also does not compare
 
438
     * lengths here.
 
439
     */
 
440
    vlen = v_st->size;
 
441
    wlen = w_st->size;
 
442
    min_len = (vlen < wlen) ? vlen : wlen;
 
443
    string_richcompare = PyString_Type.tp_richcompare;
 
444
    for (i = 0; i < min_len; i++) {
 
445
        PyObject *result = NULL;
 
446
        v_obj = StaticTuple_GET_ITEM(v_st, i);
 
447
        w_obj = StaticTuple_GET_ITEM(w_st, i);
 
448
        if (v_obj == w_obj) {
 
449
            /* Shortcut case, these must be identical */
 
450
            continue;
 
451
        }
 
452
        if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) {
 
453
            result = string_richcompare(v_obj, w_obj, Py_EQ);
 
454
        } else if (StaticTuple_CheckExact(v_obj) &&
 
455
                   StaticTuple_CheckExact(w_obj))
 
456
        {
 
457
            /* Both are StaticTuple types, so recurse */
 
458
            result = StaticTuple_richcompare(v_obj, w_obj, Py_EQ);
 
459
        } else {
 
460
            /* Not the same type, obviously they won't compare equal */
 
461
            break;
 
462
        }
 
463
        if (result == NULL) {
 
464
            return NULL; /* There seems to be an error */
 
465
        }
 
466
        if (result == Py_NotImplemented) {
 
467
            Py_DECREF(result);
 
468
            /* One side must have had a string and the other a StaticTuple.
 
469
             * This clearly means that they are not equal.
 
470
             */
 
471
            if (op == Py_EQ) {
 
472
                Py_INCREF(Py_False);
 
473
                return Py_False;
 
474
            }
 
475
            result = PyObject_RichCompare(v_obj, w_obj, Py_EQ);
 
476
        }
 
477
        if (result == Py_False) {
 
478
            /* This entry is not identical
 
479
             * Shortcut for Py_EQ
 
480
             */
 
481
            if (op == Py_EQ) {
 
482
                return result;
 
483
            }
 
484
            Py_DECREF(result);
 
485
            break;
 
486
        }
 
487
        if (result != Py_True) {
 
488
            /* We don't know *what* richcompare is returning, but it
 
489
             * isn't something we recognize
 
490
             */
 
491
            PyErr_BadInternalCall();
 
492
            Py_DECREF(result);
 
493
            return NULL;
 
494
        }
 
495
        Py_DECREF(result);
 
496
    }
 
497
    if (i >= min_len) {
 
498
        /* We walked off one of the lists, but everything compared equal so
 
499
         * far. Just compare the size.
 
500
         */
 
501
        int cmp;
 
502
        PyObject *res;
 
503
        switch (op) {
 
504
        case Py_LT: cmp = vlen <  wlen; break;
 
505
        case Py_LE: cmp = vlen <= wlen; break;
 
506
        case Py_EQ: cmp = vlen == wlen; break;
 
507
        case Py_NE: cmp = vlen != wlen; break;
 
508
        case Py_GT: cmp = vlen >  wlen; break;
 
509
        case Py_GE: cmp = vlen >= wlen; break;
 
510
        default: return NULL; /* cannot happen */
 
511
        }
 
512
        if (cmp)
 
513
            res = Py_True;
 
514
        else
 
515
            res = Py_False;
 
516
        Py_INCREF(res);
 
517
        return res;
 
518
    }
 
519
    /* The last item differs, shortcut the Py_NE case */
 
520
    if (op == Py_NE) {
 
521
        Py_INCREF(Py_True);
 
522
        return Py_True;
 
523
    }
 
524
    /* It is some other comparison, go ahead and do the real check. */
 
525
    if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj))
 
526
    {
 
527
        return string_richcompare(v_obj, w_obj, op);
 
528
    } else if (StaticTuple_CheckExact(v_obj) &&
 
529
               StaticTuple_CheckExact(w_obj))
 
530
    {
 
531
        /* Both are StaticTuple types, so recurse */
 
532
        return StaticTuple_richcompare(v_obj, w_obj, op);
 
533
    } else {
 
534
        Py_INCREF(Py_NotImplemented);
 
535
        return Py_NotImplemented;
 
536
    }
 
537
}
 
538
 
 
539
 
 
540
static Py_ssize_t
 
541
StaticTuple_length(StaticTuple *self)
 
542
{
 
543
    return self->size;
 
544
}
 
545
 
 
546
 
 
547
static PyObject *
 
548
StaticTuple__is_interned(StaticTuple *self)
 
549
{
 
550
    if (_StaticTuple_is_interned(self)) {
 
551
        Py_INCREF(Py_True);
 
552
        return Py_True;
 
553
    }
 
554
    Py_INCREF(Py_False);
 
555
    return Py_False;
 
556
}
 
557
 
 
558
static char StaticTuple__is_interned_doc[] = "_is_interned() => True/False\n"
 
559
    "Check to see if this tuple has been interned.\n";
 
560
 
 
561
 
 
562
static PyObject *
 
563
StaticTuple_item(StaticTuple *self, Py_ssize_t offset)
 
564
{
 
565
    PyObject *obj;
 
566
    /* We cast to (int) to avoid worrying about whether Py_ssize_t is a
 
567
     * long long, etc. offsets should never be >2**31 anyway.
 
568
     */
 
569
    if (offset < 0) {
 
570
        PyErr_Format(PyExc_IndexError, "StaticTuple_item does not support"
 
571
            " negative indices: %d\n", (int)offset);
 
572
    } else if (offset >= self->size) {
 
573
        PyErr_Format(PyExc_IndexError, "StaticTuple index out of range"
 
574
            " %d >= %d", (int)offset, (int)self->size);
 
575
        return NULL;
 
576
    }
 
577
    obj = (PyObject *)self->items[offset];
 
578
    Py_INCREF(obj);
 
579
    return obj;
 
580
}
 
581
 
 
582
static PyObject *
 
583
StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
 
584
{
 
585
    PyObject *as_tuple, *result;
 
586
 
 
587
    as_tuple = StaticTuple_as_tuple(self);
 
588
    if (as_tuple == NULL) {
 
589
        return NULL;
 
590
    }
 
591
    result = PyTuple_Type.tp_as_sequence->sq_slice(as_tuple, ilow, ihigh);
 
592
    Py_DECREF(as_tuple);
 
593
    return result;
 
594
}
 
595
 
 
596
static int
 
597
StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
 
598
{
 
599
    Py_ssize_t i;
 
600
    for (i = self->size; --i >= 0;) {
 
601
        Py_VISIT(self->items[i]);
 
602
    }
 
603
    return 0;
 
604
}
 
605
 
 
606
static char StaticTuple_doc[] =
 
607
    "C implementation of a StaticTuple structure."
 
608
    "\n This is used as StaticTuple(item1, item2, item3)"
 
609
    "\n This is similar to tuple, less flexible in what it"
 
610
    "\n supports, but also lighter memory consumption."
 
611
    "\n Note that the constructor mimics the () form of tuples"
 
612
    "\n Rather than the 'tuple()' constructor."
 
613
    "\n  eg. StaticTuple(a, b) == (a, b) == tuple((a, b))";
 
614
 
 
615
static PyMethodDef StaticTuple_methods[] = {
 
616
    {"as_tuple", (PyCFunction)StaticTuple_as_tuple, METH_NOARGS, StaticTuple_as_tuple_doc},
 
617
    {"intern", (PyCFunction)StaticTuple_Intern, METH_NOARGS, StaticTuple_Intern_doc},
 
618
    {"_is_interned", (PyCFunction)StaticTuple__is_interned, METH_NOARGS,
 
619
     StaticTuple__is_interned_doc},
 
620
    {"from_sequence", (PyCFunction)StaticTuple_from_sequence,
 
621
     METH_STATIC | METH_VARARGS,
 
622
     "Create a StaticTuple from a given sequence. This functions"
 
623
     " the same as the tuple() constructor."},
 
624
    {NULL, NULL} /* sentinel */
 
625
};
 
626
 
 
627
static PySequenceMethods StaticTuple_as_sequence = {
 
628
    (lenfunc)StaticTuple_length,            /* sq_length */
 
629
    0,                              /* sq_concat */
 
630
    0,                              /* sq_repeat */
 
631
    (ssizeargfunc)StaticTuple_item,         /* sq_item */
 
632
    (ssizessizeargfunc)StaticTuple_slice,   /* sq_slice */
 
633
    0,                              /* sq_ass_item */
 
634
    0,                              /* sq_ass_slice */
 
635
    0,                              /* sq_contains */
 
636
};
 
637
 
 
638
/* TODO: Implement StaticTuple_as_mapping.
 
639
 *       The only thing we really want to support from there is mp_subscript,
 
640
 *       so that we could support extended slicing (foo[::2]). Not worth it
 
641
 *       yet, though.
 
642
 */
 
643
 
 
644
 
 
645
PyTypeObject StaticTuple_Type = {
 
646
    PyObject_HEAD_INIT(NULL)
 
647
    0,                                           /* ob_size */
 
648
    "StaticTuple",                               /* tp_name */
 
649
    sizeof(StaticTuple),                         /* tp_basicsize */
 
650
    sizeof(PyObject *),                          /* tp_itemsize */
 
651
    (destructor)StaticTuple_dealloc,             /* tp_dealloc */
 
652
    0,                                           /* tp_print */
 
653
    0,                                           /* tp_getattr */
 
654
    0,                                           /* tp_setattr */
 
655
    0,                                           /* tp_compare */
 
656
    (reprfunc)StaticTuple_repr,                  /* tp_repr */
 
657
    0,                                           /* tp_as_number */
 
658
    &StaticTuple_as_sequence,                    /* tp_as_sequence */
 
659
    0,                                           /* tp_as_mapping */
 
660
    (hashfunc)StaticTuple_hash,                  /* tp_hash */
 
661
    0,                                           /* tp_call */
 
662
    0,                                           /* tp_str */
 
663
    0,                                           /* tp_getattro */
 
664
    0,                                           /* tp_setattro */
 
665
    0,                                           /* tp_as_buffer */
 
666
    Py_TPFLAGS_DEFAULT,                          /* tp_flags*/
 
667
    StaticTuple_doc,                             /* tp_doc */
 
668
    /* gc.get_referents checks the IS_GC flag before it calls tp_traverse
 
669
     * And we don't include this object in the garbage collector because we
 
670
     * know it doesn't create cycles. However, 'meliae' will follow
 
671
     * tp_traverse, even if the object isn't GC, and we want that.
 
672
     */
 
673
    (traverseproc)StaticTuple_traverse,          /* tp_traverse */
 
674
    0,                                           /* tp_clear */
 
675
    StaticTuple_richcompare,                     /* tp_richcompare */
 
676
    0,                                           /* tp_weaklistoffset */
 
677
    // without implementing tp_iter, Python will fall back to PySequence*
 
678
    // which seems to work ok, we may need something faster/lighter in the
 
679
    // future.
 
680
    0,                                           /* tp_iter */
 
681
    0,                                           /* tp_iternext */
 
682
    StaticTuple_methods,                         /* tp_methods */
 
683
    0,                                           /* tp_members */
 
684
    0,                                           /* tp_getset */
 
685
    0,                                           /* tp_base */
 
686
    0,                                           /* tp_dict */
 
687
    0,                                           /* tp_descr_get */
 
688
    0,                                           /* tp_descr_set */
 
689
    0,                                           /* tp_dictoffset */
 
690
    0,                                           /* tp_init */
 
691
    0,                                           /* tp_alloc */
 
692
    StaticTuple_new_constructor,                 /* tp_new */
 
693
};
 
694
 
 
695
 
 
696
static PyMethodDef static_tuple_c_methods[] = {
 
697
    {NULL, NULL}
 
698
};
 
699
 
 
700
 
 
701
static void
 
702
setup_interned_tuples(PyObject *m)
 
703
{
 
704
    _interned_tuples = (PyObject *)SimpleSet_New();
 
705
    if (_interned_tuples != NULL) {
 
706
        Py_INCREF(_interned_tuples);
 
707
        PyModule_AddObject(m, "_interned_tuples", _interned_tuples);
 
708
    }
 
709
}
 
710
 
 
711
 
 
712
static void
 
713
setup_empty_tuple(PyObject *m)
 
714
{
 
715
    StaticTuple *stuple;
 
716
    if (_interned_tuples == NULL) {
 
717
        fprintf(stderr, "You need to call setup_interned_tuples() before"
 
718
                " setup_empty_tuple, because we intern it.\n");
 
719
    }
 
720
    // We need to create the empty tuple
 
721
    stuple = (StaticTuple *)StaticTuple_New(0);
 
722
    _empty_tuple = StaticTuple_Intern(stuple);
 
723
    assert(_empty_tuple == stuple);
 
724
    // At this point, refcnt is 2: 1 from New(), and 1 from the return from
 
725
    // intern(). We will keep 1 for the _empty_tuple global, and use the other
 
726
    // for the module reference.
 
727
    PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple);
 
728
}
 
729
 
 
730
static int
 
731
_StaticTuple_CheckExact(PyObject *obj)
 
732
{
 
733
    return StaticTuple_CheckExact(obj);
 
734
}
 
735
 
 
736
static void
 
737
setup_c_api(PyObject *m)
 
738
{
 
739
    _export_function(m, "StaticTuple_New", StaticTuple_New,
 
740
        "StaticTuple *(Py_ssize_t)");
 
741
    _export_function(m, "StaticTuple_Intern", StaticTuple_Intern,
 
742
        "StaticTuple *(StaticTuple *)");
 
743
    _export_function(m, "StaticTuple_FromSequence", StaticTuple_FromSequence,
 
744
        "StaticTuple *(PyObject *)");
 
745
    _export_function(m, "_StaticTuple_CheckExact", _StaticTuple_CheckExact,
 
746
        "int(PyObject *)");
 
747
}
 
748
 
 
749
 
 
750
static int
 
751
_workaround_pyrex_096(void)
 
752
{
 
753
    /* Work around an incompatibility in how pyrex 0.9.6 exports a module,
 
754
     * versus how pyrex 0.9.8 and cython 0.11 export it.
 
755
     * Namely 0.9.6 exports import__simple_set_pyx and tries to
 
756
     * "import _simple_set_pyx" but it is available only as
 
757
     * "import bzrlib._simple_set_pyx"
 
758
     * It is a shame to hack up sys.modules, but that is what we've got to do.
 
759
     */
 
760
    PyObject *sys_module = NULL, *modules = NULL, *set_module = NULL;
 
761
    int retval = -1;
 
762
 
 
763
    /* Clear out the current ImportError exception, and try again. */
 
764
    PyErr_Clear();
 
765
    /* Note that this only seems to work if somewhere else imports
 
766
     * bzrlib._simple_set_pyx before importing bzrlib._static_tuple_c
 
767
     */
 
768
    set_module = PyImport_ImportModule("bzrlib._simple_set_pyx");
 
769
    if (set_module == NULL) {
 
770
        // fprintf(stderr, "Failed to import bzrlib._simple_set_pyx\n");
 
771
        goto end;
 
772
    }
 
773
    /* Add the _simple_set_pyx into sys.modules at the appropriate location. */
 
774
    sys_module = PyImport_ImportModule("sys");
 
775
    if (sys_module == NULL) {
 
776
        // fprintf(stderr, "Failed to import sys\n");
 
777
        goto end;
 
778
    }
 
779
    modules = PyObject_GetAttrString(sys_module, "modules");
 
780
    if (modules == NULL || !PyDict_Check(modules)) {
 
781
        // fprintf(stderr, "Failed to find sys.modules\n");
 
782
        goto end;
 
783
    }
 
784
    PyDict_SetItemString(modules, "_simple_set_pyx", set_module);
 
785
    /* Now that we have hacked it in, try the import again. */
 
786
    retval = import_bzrlib___simple_set_pyx();
 
787
end:
 
788
    Py_XDECREF(set_module);
 
789
    Py_XDECREF(sys_module);
 
790
    Py_XDECREF(modules);
 
791
    return retval;
 
792
}
 
793
 
 
794
 
 
795
PyMODINIT_FUNC
 
796
init_static_tuple_c(void)
 
797
{
 
798
    PyObject* m;
 
799
 
 
800
    StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
 
801
    if (PyType_Ready(&StaticTuple_Type) < 0)
 
802
        return;
 
803
 
 
804
    m = Py_InitModule3("_static_tuple_c", static_tuple_c_methods,
 
805
                       "C implementation of a StaticTuple structure");
 
806
    if (m == NULL)
 
807
      return;
 
808
 
 
809
    Py_INCREF(&StaticTuple_Type);
 
810
    PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
 
811
    if (import_bzrlib___simple_set_pyx() == -1
 
812
        && _workaround_pyrex_096() == -1)
 
813
    {
 
814
        return;
 
815
    }
 
816
    setup_interned_tuples(m);
 
817
    setup_empty_tuple(m);
 
818
    setup_c_api(m);
 
819
}