/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-12 18:10:24 UTC
  • mto: This revision was merged to the branch mainline in revision 4736.
  • Revision ID: john@arbash-meinel.com-20091012181024-q21zm9xpyf62ld7t
Add some tests that we *can* compare to strings, even if we don't care
what the result actually is.

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