/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: Vincent Ladeuil
  • Date: 2009-10-23 16:31:03 UTC
  • mto: This revision was merged to the branch mainline in revision 4767.
  • Revision ID: v.ladeuil+lp@free.fr-20091023163103-x1h0sbrz61r0bqzx
Revert incomplete fix for bug #353370

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