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

  • Committer: John Arbash Meinel
  • Date: 2006-04-25 15:05:42 UTC
  • mfrom: (1185.85.85 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060425150542-c7b518dca9928691
[merge] the old bzr-encoding changes, reparenting them on bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2009, 2010 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
 
#include "_simple_set_pyx_api.h"
30
 
 
31
 
#if defined(__GNUC__)
32
 
#   define inline __inline__
33
 
#elif defined(_MSC_VER)
34
 
#   define inline __inline
35
 
#else
36
 
#   define inline
37
 
#endif
38
 
 
39
 
 
40
 
/* The one and only StaticTuple with no values */
41
 
static StaticTuple *_empty_tuple = NULL;
42
 
static PyObject *_interned_tuples = NULL;
43
 
 
44
 
 
45
 
static inline int
46
 
_StaticTuple_is_interned(StaticTuple *self)
47
 
{
48
 
    return self->flags & STATIC_TUPLE_INTERNED_FLAG;
49
 
}
50
 
 
51
 
 
52
 
 
53
 
static PyObject *
54
 
StaticTuple_as_tuple(StaticTuple *self)
55
 
{
56
 
    PyObject *tpl = NULL, *obj = NULL;
57
 
    int i, len;
58
 
 
59
 
    len = self->size;
60
 
    tpl = PyTuple_New(len);
61
 
    if (!tpl) {
62
 
        /* Malloc failure */
63
 
        return NULL;
64
 
    }
65
 
    for (i = 0; i < len; ++i) {
66
 
        obj = (PyObject *)self->items[i];
67
 
        Py_INCREF(obj);
68
 
        PyTuple_SET_ITEM(tpl, i, obj);
69
 
    }
70
 
    return tpl;
71
 
}
72
 
 
73
 
 
74
 
static char StaticTuple_as_tuple_doc[] = "as_tuple() => tuple";
75
 
 
76
 
static StaticTuple *
77
 
StaticTuple_Intern(StaticTuple *self)
78
 
{
79
 
    PyObject *canonical_tuple = NULL;
80
 
 
81
 
    if (_interned_tuples == NULL || _StaticTuple_is_interned(self)) {
82
 
        Py_INCREF(self);
83
 
        return self;
84
 
    }
85
 
    /* SimpleSet_Add returns whatever object is present at self
86
 
     * or the new object if it needs to add it.
87
 
     */
88
 
    canonical_tuple = SimpleSet_Add(_interned_tuples, (PyObject *)self);
89
 
    if (!canonical_tuple) {
90
 
        // Some sort of exception, propogate it.
91
 
        return NULL;
92
 
    }
93
 
    if (canonical_tuple != (PyObject *)self) {
94
 
        // There was already a tuple with that value
95
 
        return (StaticTuple *)canonical_tuple;
96
 
    }
97
 
    self->flags |= STATIC_TUPLE_INTERNED_FLAG;
98
 
    // The two references in the dict do not count, so that the StaticTuple
99
 
    // object does not become immortal just because it was interned.
100
 
    Py_REFCNT(self) -= 1;
101
 
    return self;
102
 
}
103
 
 
104
 
static char StaticTuple_Intern_doc[] = "intern() => unique StaticTuple\n"
105
 
    "Return a 'canonical' StaticTuple object.\n"
106
 
    "Similar to intern() for strings, this makes sure there\n"
107
 
    "is only one StaticTuple object for a given value\n."
108
 
    "Common usage is:\n"
109
 
    "  key = StaticTuple('foo', 'bar').intern()\n";
110
 
 
111
 
 
112
 
static void
113
 
StaticTuple_dealloc(StaticTuple *self)
114
 
{
115
 
    int i, len;
116
 
 
117
 
    if (_StaticTuple_is_interned(self)) {
118
 
        /* revive dead object temporarily for Discard */
119
 
        Py_REFCNT(self) = 2;
120
 
        if (SimpleSet_Discard(_interned_tuples, (PyObject*)self) != 1)
121
 
            Py_FatalError("deletion of interned StaticTuple failed");
122
 
        self->flags &= ~STATIC_TUPLE_INTERNED_FLAG;
123
 
    }
124
 
    len = self->size;
125
 
    for (i = 0; i < len; ++i) {
126
 
        Py_XDECREF(self->items[i]);
127
 
    }
128
 
    Py_TYPE(self)->tp_free((PyObject *)self);
129
 
}
130
 
 
131
 
 
132
 
/* Similar to PyTuple_New() */
133
 
static StaticTuple *
134
 
StaticTuple_New(Py_ssize_t size)
135
 
{
136
 
    StaticTuple *stuple;
137
 
 
138
 
    if (size < 0 || size > 255) {
139
 
        /* Too big or too small */
140
 
        PyErr_SetString(PyExc_ValueError, "StaticTuple(...)"
141
 
            " takes from 0 to 255 items");
142
 
        return NULL;
143
 
    }
144
 
    if (size == 0 && _empty_tuple != NULL) {
145
 
        Py_INCREF(_empty_tuple);
146
 
        return _empty_tuple;
147
 
    }
148
 
    /* Note that we use PyObject_NewVar because we want to allocate a variable
149
 
     * width entry. However we *aren't* truly a PyVarObject because we don't
150
 
     * use a long for ob_size. Instead we use a plain 'size' that is an int,
151
 
     * and will be overloaded with flags in the future.
152
 
     * As such we do the alloc, and then have to clean up anything it does
153
 
     * incorrectly.
154
 
     */
155
 
    stuple = PyObject_NewVar(StaticTuple, &StaticTuple_Type, size);
156
 
    if (stuple == NULL) {
157
 
        return NULL;
158
 
    }
159
 
    stuple->size = size;
160
 
    stuple->flags = 0;
161
 
    stuple->_unused0 = 0;
162
 
    stuple->_unused1 = 0;
163
 
    if (size > 0) {
164
 
        memset(stuple->items, 0, sizeof(PyObject *) * size);
165
 
    }
166
 
#if STATIC_TUPLE_HAS_HASH
167
 
    stuple->hash = -1;
168
 
#endif
169
 
    return stuple;
170
 
}
171
 
 
172
 
 
173
 
static StaticTuple *
174
 
StaticTuple_FromSequence(PyObject *sequence)
175
 
{
176
 
    StaticTuple *new = NULL;
177
 
    PyObject *as_tuple = NULL;
178
 
    PyObject *item;
179
 
    Py_ssize_t i, size;
180
 
 
181
 
    if (StaticTuple_CheckExact(sequence)) {
182
 
        Py_INCREF(sequence);
183
 
        return (StaticTuple *)sequence;
184
 
    }
185
 
    if (!PySequence_Check(sequence)) {
186
 
        as_tuple = PySequence_Tuple(sequence);
187
 
        if (as_tuple == NULL)
188
 
            goto done;
189
 
        sequence = as_tuple;
190
 
    }
191
 
    size = PySequence_Size(sequence);
192
 
    if (size == -1) {
193
 
        goto done;
194
 
    }
195
 
    new = StaticTuple_New(size);
196
 
    if (new == NULL) {
197
 
        goto done;
198
 
    }
199
 
    for (i = 0; i < size; ++i) {
200
 
        // This returns a new reference, which we then 'steal' with 
201
 
        // StaticTuple_SET_ITEM
202
 
        item = PySequence_GetItem(sequence, i);
203
 
        if (item == NULL) {
204
 
            Py_DECREF(new);
205
 
            new = NULL;
206
 
            goto done;
207
 
        }
208
 
        StaticTuple_SET_ITEM(new, i, item);
209
 
    }
210
 
done:
211
 
    Py_XDECREF(as_tuple);
212
 
    return (StaticTuple *)new;
213
 
}
214
 
 
215
 
static StaticTuple *
216
 
StaticTuple_from_sequence(PyObject *self, PyObject *args, PyObject *kwargs)
217
 
{
218
 
    PyObject *sequence;
219
 
    if (!PyArg_ParseTuple(args, "O", &sequence))
220
 
        return NULL;
221
 
    return StaticTuple_FromSequence(sequence);
222
 
}
223
 
 
224
 
 
225
 
/* Check that all items we point to are 'valid' */
226
 
static int
227
 
StaticTuple_check_items(StaticTuple *self)
228
 
{
229
 
    int i;
230
 
    PyObject *obj;
231
 
 
232
 
    for (i = 0; i < self->size; ++i) {
233
 
        obj = self->items[i];
234
 
        if (obj == NULL) {
235
 
            PyErr_SetString(PyExc_RuntimeError, "StaticTuple(...)"
236
 
                " should not have a NULL entry.");
237
 
            return 0;
238
 
        }
239
 
        if (PyBytes_CheckExact(obj)
240
 
            || StaticTuple_CheckExact(obj)
241
 
            || obj == Py_None
242
 
            || PyBool_Check(obj)
243
 
#if PY_MAJOR_VERSION >= 3
244
 
#else
245
 
            || PyInt_CheckExact(obj)
246
 
#endif
247
 
            || PyLong_CheckExact(obj)
248
 
            || PyFloat_CheckExact(obj)
249
 
            || PyUnicode_CheckExact(obj)
250
 
            ) continue;
251
 
        PyErr_Format(PyExc_TypeError, "StaticTuple(...)"
252
 
            " requires that all items are one of"
253
 
            " str, StaticTuple, None, bool, int, long, float, or unicode"
254
 
            " not %s.", Py_TYPE(obj)->tp_name);
255
 
        return 0;
256
 
    }
257
 
    return 1;
258
 
}
259
 
 
260
 
static PyObject *
261
 
StaticTuple_new_constructor(PyTypeObject *type, PyObject *args, PyObject *kwds)
262
 
{
263
 
    StaticTuple *self;
264
 
    PyObject *obj = NULL;
265
 
    Py_ssize_t i, len = 0;
266
 
 
267
 
    if (type != &StaticTuple_Type) {
268
 
        PyErr_SetString(PyExc_TypeError, "we only support creating StaticTuple");
269
 
        return NULL;
270
 
    }
271
 
    if (!PyTuple_CheckExact(args)) {
272
 
        PyErr_SetString(PyExc_TypeError, "args must be a tuple");
273
 
        return NULL;
274
 
    }
275
 
    len = PyTuple_GET_SIZE(args);
276
 
    if (len < 0 || len > 255) {
277
 
        /* Check the length here so we can raise a TypeError instead of
278
 
         * StaticTuple_New's ValueError.
279
 
         */
280
 
        PyErr_SetString(PyExc_TypeError, "StaticTuple(...)"
281
 
            " takes from 0 to 255 items");
282
 
        return NULL;
283
 
    }
284
 
    self = (StaticTuple *)StaticTuple_New(len);
285
 
    if (self == NULL) {
286
 
        return NULL;
287
 
    }
288
 
    for (i = 0; i < len; ++i) {
289
 
        obj = PyTuple_GET_ITEM(args, i);
290
 
        Py_INCREF(obj);
291
 
        self->items[i] = obj;
292
 
    }
293
 
    if (!StaticTuple_check_items(self)) {
294
 
        type->tp_dealloc((PyObject *)self);
295
 
        return NULL;
296
 
    }
297
 
    return (PyObject *)self;
298
 
}
299
 
 
300
 
static PyObject *
301
 
StaticTuple_repr(StaticTuple *self)
302
 
{
303
 
    PyObject *as_tuple, *tuple_repr, *result;
304
 
 
305
 
    as_tuple = StaticTuple_as_tuple(self);
306
 
    if (as_tuple == NULL) {
307
 
        return NULL;
308
 
    }
309
 
    tuple_repr = PyObject_Repr(as_tuple);
310
 
    Py_DECREF(as_tuple);
311
 
    if (tuple_repr == NULL) {
312
 
        return NULL;
313
 
    }
314
 
#if PY_MAJOR_VERSION >= 3
315
 
    result = PyUnicode_FromFormat("StaticTuple%U", tuple_repr);
316
 
#else
317
 
    result = PyString_FromFormat("StaticTuple%s",
318
 
                                 PyString_AsString(tuple_repr));
319
 
#endif
320
 
    return result;
321
 
}
322
 
 
323
 
/* adapted from tuplehash(), is the specific hash value considered
324
 
 * 'stable'?
325
 
 */
326
 
 
327
 
#if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8)
328
 
/* Hash for tuples. This is a slightly simplified version of the xxHash
329
 
   non-cryptographic hash:
330
 
   - we do not use any parallellism, there is only 1 accumulator.
331
 
   - we drop the final mixing since this is just a permutation of the
332
 
     output space: it does not help against collisions.
333
 
   - at the end, we mangle the length with a single constant.
334
 
   For the xxHash specification, see
335
 
   https://github.com/Cyan4973/xxHash/blob/master/doc/xxhash_spec.md
336
 
 
337
 
   Below are the official constants from the xxHash specification. Optimizing
338
 
   compilers should emit a single "rotate" instruction for the
339
 
   _PyHASH_XXROTATE() expansion. If that doesn't happen for some important
340
 
   platform, the macro could be changed to expand to a platform-specific rotate
341
 
   spelling instead.
342
 
*/
343
 
#if SIZEOF_PY_UHASH_T > 4
344
 
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL)
345
 
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL)
346
 
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL)
347
 
#define _PyHASH_XXROTATE(x) ((x << 31) | (x >> 33))  /* Rotate left 31 bits */
348
 
#else
349
 
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)2654435761UL)
350
 
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)2246822519UL)
351
 
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)374761393UL)
352
 
#define _PyHASH_XXROTATE(x) ((x << 13) | (x >> 19))  /* Rotate left 13 bits */
353
 
#endif
354
 
 
355
 
/* Tests have shown that it's not worth to cache the hash value, see
356
 
   https://bugs.python.org/issue9685 */
357
 
static Py_hash_t
358
 
StaticTuple_hash(StaticTuple *self)
359
 
{
360
 
    Py_ssize_t i, len = self->size;
361
 
    PyObject **item = self->items;
362
 
 
363
 
#if STATIC_TUPLE_HAS_HASH
364
 
    if (self->hash != -1) {
365
 
        return self->hash;
366
 
    }
367
 
#endif
368
 
 
369
 
    Py_uhash_t acc = _PyHASH_XXPRIME_5;
370
 
    for (i = 0; i < len; i++) {
371
 
        Py_uhash_t lane = PyObject_Hash(item[i]);
372
 
        if (lane == (Py_uhash_t)-1) {
373
 
            return -1;
374
 
        }
375
 
        acc += lane * _PyHASH_XXPRIME_2;
376
 
        acc = _PyHASH_XXROTATE(acc);
377
 
        acc *= _PyHASH_XXPRIME_1;
378
 
    }
379
 
 
380
 
    /* Add input length, mangled to keep the historical value of hash(()). */
381
 
    acc += len ^ (_PyHASH_XXPRIME_5 ^ 3527539UL);
382
 
 
383
 
    if (acc == (Py_uhash_t)-1) {
384
 
        acc = 1546275796;
385
 
    }
386
 
 
387
 
#if STATIC_TUPLE_HAS_HASH
388
 
    self->hash = acc;
389
 
#endif
390
 
    return acc;
391
 
}
392
 
 
393
 
 
394
 
#else
395
 
static long
396
 
StaticTuple_hash(StaticTuple *self)
397
 
{
398
 
    /* adapted from tuplehash(), is the specific hash value considered
399
 
     * 'stable'?
400
 
     */
401
 
    register long x, y;
402
 
    Py_ssize_t len = self->size;
403
 
    PyObject **p;
404
 
    long mult = 1000003L;
405
 
 
406
 
#if STATIC_TUPLE_HAS_HASH
407
 
    if (self->hash != -1) {
408
 
        return self->hash;
409
 
    }
410
 
#endif
411
 
    x = 0x345678L;
412
 
    p = self->items;
413
 
    // TODO: We could set specific flags if we know that, for example, all the
414
 
    //       items are strings. I haven't seen a real-world benefit to that
415
 
    //       yet, though.
416
 
    while (--len >= 0) {
417
 
        y = PyObject_Hash(*p++);
418
 
        if (y == -1) /* failure */
419
 
            return -1;
420
 
        x = (x ^ y) * mult;
421
 
        /* the cast might truncate len; that doesn't change hash stability */
422
 
        mult += (long)(82520L + len + len);
423
 
    }
424
 
    x += 97531L;
425
 
    if (x == -1)
426
 
        x = -2;
427
 
#if STATIC_TUPLE_HAS_HASH
428
 
    self->hash = x;
429
 
#endif
430
 
    return x;
431
 
}
432
 
#endif
433
 
 
434
 
static PyObject *
435
 
StaticTuple_richcompare_to_tuple(StaticTuple *v, PyObject *wt, int op)
436
 
{
437
 
    PyObject *vt;
438
 
    PyObject *result = NULL;
439
 
 
440
 
    vt = StaticTuple_as_tuple((StaticTuple *)v);
441
 
    if (vt == NULL) {
442
 
        goto done;
443
 
    }
444
 
    if (!PyTuple_Check(wt)) {
445
 
        PyErr_BadInternalCall();
446
 
        goto done;
447
 
    }
448
 
    /* Now we have 2 tuples to compare, do it */
449
 
    result = PyTuple_Type.tp_richcompare(vt, wt, op);
450
 
done:
451
 
    Py_XDECREF(vt);
452
 
    return result;
453
 
}
454
 
 
455
 
/** Compare two objects to determine if they are equivalent.
456
 
 * The basic flow is as follows
457
 
 *  1) First make sure that both objects are StaticTuple instances. If they
458
 
 *     aren't then cast self to a tuple, and have the tuple do the comparison.
459
 
 *  2) Special case comparison to Py_None, because it happens to occur fairly
460
 
 *     often in the test suite.
461
 
 *  3) Special case when v and w are the same pointer. As we know the answer to
462
 
 *     all queries without walking individual items.
463
 
 *  4) For all operations, we then walk the items to find the first paired
464
 
 *     items that are not equal.
465
 
 *  5) If all items found are equal, we then check the length of self and
466
 
 *     other to determine equality.
467
 
 *  6) If an item differs, then we apply "op" to those last two items. (eg.
468
 
 *     StaticTuple(A, B) > StaticTuple(A, C) iff B > C)
469
 
 */
470
 
 
471
 
static PyObject *
472
 
StaticTuple_richcompare(PyObject *v, PyObject *w, int op)
473
 
{
474
 
    StaticTuple *v_st, *w_st;
475
 
    Py_ssize_t vlen, wlen, min_len, i;
476
 
    PyObject *v_obj, *w_obj;
477
 
    richcmpfunc string_richcompare;
478
 
 
479
 
    if (!StaticTuple_CheckExact(v)) {
480
 
        /* This has never triggered, according to python-dev it seems this
481
 
         * might trigger if '__op__' is defined but '__rop__' is not, sort of
482
 
         * case. Such as "None == StaticTuple()"
483
 
         */
484
 
        fprintf(stderr, "self is not StaticTuple\n");
485
 
        Py_INCREF(Py_NotImplemented);
486
 
        return Py_NotImplemented;
487
 
    }
488
 
    v_st = (StaticTuple *)v;
489
 
    if (StaticTuple_CheckExact(w)) {
490
 
        /* The most common case */
491
 
        w_st = (StaticTuple*)w;
492
 
    } else if (PyTuple_Check(w)) {
493
 
        /* One of v or w is a tuple, so we go the 'slow' route and cast up to
494
 
         * tuples to compare.
495
 
         */
496
 
        /* TODO: This seems to be triggering more than I thought it would...
497
 
         *       We probably want to optimize comparing self to other when
498
 
         *       other is a tuple.
499
 
         */
500
 
        return StaticTuple_richcompare_to_tuple(v_st, w, op);
501
 
    } else if (w == Py_None) {
502
 
        // None is always less than the object
503
 
        switch (op) {
504
 
        case Py_NE:
505
 
#if PY_MAJOR_VERSION >= 3
506
 
#else
507
 
        case Py_GT:case Py_GE:
508
 
#endif
509
 
            Py_INCREF(Py_True);
510
 
            return Py_True;
511
 
        case Py_EQ:
512
 
#if PY_MAJOR_VERSION >= 3
513
 
#else
514
 
        case Py_LT:case Py_LE:
515
 
#endif
516
 
            Py_INCREF(Py_False);
517
 
            return Py_False;
518
 
        default: // Should only happen on Python 3
519
 
            return Py_NotImplemented;
520
 
        }
521
 
    } else {
522
 
        /* We don't special case this comparison, we just let python handle
523
 
         * it.
524
 
         */
525
 
         Py_INCREF(Py_NotImplemented);
526
 
         return Py_NotImplemented;
527
 
    }
528
 
    /* Now we know that we have 2 StaticTuple objects, so let's compare them.
529
 
     * This code is inspired from tuplerichcompare, except we know our
530
 
     * objects are limited in scope, so we can inline some comparisons.
531
 
     */
532
 
    if (v == w) {
533
 
        /* Identical pointers, we can shortcut this easily. */
534
 
        switch (op) {
535
 
        case Py_EQ:case Py_LE:case Py_GE:
536
 
            Py_INCREF(Py_True);
537
 
            return Py_True;
538
 
        case Py_NE:case Py_LT:case Py_GT:
539
 
            Py_INCREF(Py_False);
540
 
            return Py_False;
541
 
        }
542
 
    }
543
 
    if (op == Py_EQ
544
 
        && _StaticTuple_is_interned(v_st)
545
 
        && _StaticTuple_is_interned(w_st))
546
 
    {
547
 
        /* If both objects are interned, we know they are different if the
548
 
         * pointer is not the same, which would have been handled by the
549
 
         * previous if. No need to compare the entries.
550
 
         */
551
 
        Py_INCREF(Py_False);
552
 
        return Py_False;
553
 
    }
554
 
 
555
 
    /* The only time we are likely to compare items of different lengths is in
556
 
     * something like the interned_keys set. However, the hash is good enough
557
 
     * that it is rare. Note that 'tuple_richcompare' also does not compare
558
 
     * lengths here.
559
 
     */
560
 
    vlen = v_st->size;
561
 
    wlen = w_st->size;
562
 
    min_len = (vlen < wlen) ? vlen : wlen;
563
 
    string_richcompare = PyBytes_Type.tp_richcompare;
564
 
    for (i = 0; i < min_len; i++) {
565
 
        PyObject *result = NULL;
566
 
        v_obj = StaticTuple_GET_ITEM(v_st, i);
567
 
        w_obj = StaticTuple_GET_ITEM(w_st, i);
568
 
        if (v_obj == w_obj) {
569
 
            /* Shortcut case, these must be identical */
570
 
            continue;
571
 
        }
572
 
        if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj)) {
573
 
            result = string_richcompare(v_obj, w_obj, Py_EQ);
574
 
        } else if (StaticTuple_CheckExact(v_obj) &&
575
 
                   StaticTuple_CheckExact(w_obj))
576
 
        {
577
 
            /* Both are StaticTuple types, so recurse */
578
 
            result = StaticTuple_richcompare(v_obj, w_obj, Py_EQ);
579
 
        } else {
580
 
            /* Fall back to generic richcompare */
581
 
            result = PyObject_RichCompare(v_obj, w_obj, Py_EQ);
582
 
        }
583
 
        if (result == NULL) {
584
 
            return NULL; /* There seems to be an error */
585
 
        }
586
 
        if (result == Py_False) {
587
 
            // This entry is not identical, Shortcut for Py_EQ
588
 
            if (op == Py_EQ) {
589
 
                return result;
590
 
            }
591
 
            Py_DECREF(result);
592
 
            break;
593
 
        }
594
 
        if (result != Py_True) {
595
 
            /* We don't know *what* richcompare is returning, but it
596
 
             * isn't something we recognize
597
 
             */
598
 
            PyErr_BadInternalCall();
599
 
            Py_DECREF(result);
600
 
            return NULL;
601
 
        }
602
 
        Py_DECREF(result);
603
 
    }
604
 
    if (i >= min_len) {
605
 
        /* We walked off one of the lists, but everything compared equal so
606
 
         * far. Just compare the size.
607
 
         */
608
 
        int cmp;
609
 
        PyObject *res;
610
 
        switch (op) {
611
 
        case Py_LT: cmp = vlen <  wlen; break;
612
 
        case Py_LE: cmp = vlen <= wlen; break;
613
 
        case Py_EQ: cmp = vlen == wlen; break;
614
 
        case Py_NE: cmp = vlen != wlen; break;
615
 
        case Py_GT: cmp = vlen >  wlen; break;
616
 
        case Py_GE: cmp = vlen >= wlen; break;
617
 
        default: return NULL; /* cannot happen */
618
 
        }
619
 
        if (cmp)
620
 
            res = Py_True;
621
 
        else
622
 
            res = Py_False;
623
 
        Py_INCREF(res);
624
 
        return res;
625
 
    }
626
 
    /* The last item differs, shortcut the Py_NE case */
627
 
    if (op == Py_NE) {
628
 
        Py_INCREF(Py_True);
629
 
        return Py_True;
630
 
    }
631
 
    /* It is some other comparison, go ahead and do the real check. */
632
 
    if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj))
633
 
    {
634
 
        return string_richcompare(v_obj, w_obj, op);
635
 
    } else if (StaticTuple_CheckExact(v_obj) &&
636
 
               StaticTuple_CheckExact(w_obj))
637
 
    {
638
 
        /* Both are StaticTuple types, so recurse */
639
 
        return StaticTuple_richcompare(v_obj, w_obj, op);
640
 
    } else {
641
 
        return PyObject_RichCompare(v_obj, w_obj, op);
642
 
    }
643
 
}
644
 
 
645
 
 
646
 
static Py_ssize_t
647
 
StaticTuple_length(StaticTuple *self)
648
 
{
649
 
    return self->size;
650
 
}
651
 
 
652
 
 
653
 
static PyObject *
654
 
StaticTuple__is_interned(StaticTuple *self)
655
 
{
656
 
    if (_StaticTuple_is_interned(self)) {
657
 
        Py_INCREF(Py_True);
658
 
        return Py_True;
659
 
    }
660
 
    Py_INCREF(Py_False);
661
 
    return Py_False;
662
 
}
663
 
 
664
 
static char StaticTuple__is_interned_doc[] = "_is_interned() => True/False\n"
665
 
    "Check to see if this tuple has been interned.\n";
666
 
 
667
 
 
668
 
static PyObject *
669
 
StaticTuple_reduce(StaticTuple *self)
670
 
{
671
 
    PyObject *result = NULL, *as_tuple = NULL;
672
 
 
673
 
    result = PyTuple_New(2);
674
 
    if (!result) {
675
 
        return NULL;
676
 
    }
677
 
    as_tuple = StaticTuple_as_tuple(self);
678
 
    if (as_tuple == NULL) {
679
 
        Py_DECREF(result);
680
 
        return NULL;
681
 
    }
682
 
    Py_INCREF(&StaticTuple_Type);
683
 
    PyTuple_SET_ITEM(result, 0, (PyObject *)&StaticTuple_Type);
684
 
    PyTuple_SET_ITEM(result, 1, as_tuple);
685
 
    return result;
686
 
}
687
 
 
688
 
static char StaticTuple_reduce_doc[] = "__reduce__() => tuple\n";
689
 
 
690
 
 
691
 
static PyObject *
692
 
StaticTuple_add(PyObject *v, PyObject *w)
693
 
{
694
 
    Py_ssize_t i, len_v, len_w;
695
 
    PyObject *item;
696
 
    StaticTuple *result;
697
 
     /* StaticTuples and plain tuples may be added (concatenated) to
698
 
      * StaticTuples.
699
 
      */
700
 
    if (StaticTuple_CheckExact(v)) {
701
 
        len_v = ((StaticTuple*)v)->size;
702
 
    } else if (PyTuple_Check(v)) {
703
 
        len_v = PyTuple_GET_SIZE(v);
704
 
    } else {
705
 
        Py_INCREF(Py_NotImplemented);
706
 
        return Py_NotImplemented;
707
 
    }
708
 
    if (StaticTuple_CheckExact(w)) {
709
 
        len_w = ((StaticTuple*)w)->size;
710
 
    } else if (PyTuple_Check(w)) {
711
 
        len_w = PyTuple_GET_SIZE(w);
712
 
    } else {
713
 
        Py_INCREF(Py_NotImplemented);
714
 
        return Py_NotImplemented;
715
 
    }
716
 
    result = StaticTuple_New(len_v + len_w);
717
 
    if (result == NULL)
718
 
        return NULL;
719
 
    for (i = 0; i < len_v; ++i) {
720
 
        // This returns a new reference, which we then 'steal' with 
721
 
        // StaticTuple_SET_ITEM
722
 
        item = PySequence_GetItem(v, i);
723
 
        if (item == NULL) {
724
 
            Py_DECREF(result);
725
 
            return NULL;
726
 
        }
727
 
        StaticTuple_SET_ITEM(result, i, item);
728
 
    }
729
 
    for (i = 0; i < len_w; ++i) {
730
 
        item = PySequence_GetItem(w, i);
731
 
        if (item == NULL) {
732
 
            Py_DECREF(result);
733
 
            return NULL;
734
 
        }
735
 
        StaticTuple_SET_ITEM(result, i+len_v, item);
736
 
    }
737
 
    if (!StaticTuple_check_items(result)) {
738
 
        Py_DECREF(result);
739
 
        return NULL;
740
 
    }
741
 
    return (PyObject *)result;
742
 
}
743
 
 
744
 
static PyObject *
745
 
StaticTuple_item(StaticTuple *self, Py_ssize_t offset)
746
 
{
747
 
    PyObject *obj;
748
 
    /* We cast to (int) to avoid worrying about whether Py_ssize_t is a
749
 
     * long long, etc. offsets should never be >2**31 anyway.
750
 
     */
751
 
    if (offset < 0) {
752
 
        PyErr_Format(PyExc_IndexError, "StaticTuple_item does not support"
753
 
            " negative indices: %d\n", (int)offset);
754
 
    } else if (offset >= self->size) {
755
 
        PyErr_Format(PyExc_IndexError, "StaticTuple index out of range"
756
 
            " %d >= %d", (int)offset, (int)self->size);
757
 
        return NULL;
758
 
    }
759
 
    obj = (PyObject *)self->items[offset];
760
 
    Py_INCREF(obj);
761
 
    return obj;
762
 
}
763
 
 
764
 
#if PY_MAJOR_VERSION >= 3
765
 
#else
766
 
static PyObject *
767
 
StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
768
 
{
769
 
    PyObject *as_tuple, *result;
770
 
 
771
 
    as_tuple = StaticTuple_as_tuple(self);
772
 
    if (as_tuple == NULL) {
773
 
        return NULL;
774
 
    }
775
 
    result = PyTuple_Type.tp_as_sequence->sq_slice(as_tuple, ilow, ihigh);
776
 
    Py_DECREF(as_tuple);
777
 
    return result;
778
 
}
779
 
#endif
780
 
 
781
 
static PyObject *
782
 
StaticTuple_subscript(StaticTuple *self, PyObject *key)
783
 
{
784
 
    PyObject *as_tuple, *result;
785
 
 
786
 
    as_tuple = StaticTuple_as_tuple(self);
787
 
    if (as_tuple == NULL) {
788
 
        return NULL;
789
 
    }
790
 
    result = PyTuple_Type.tp_as_mapping->mp_subscript(as_tuple, key);
791
 
    Py_DECREF(as_tuple);
792
 
    return result;
793
 
}
794
 
 
795
 
static int
796
 
StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
797
 
{
798
 
    Py_ssize_t i;
799
 
    for (i = self->size; --i >= 0;) {
800
 
        Py_VISIT(self->items[i]);
801
 
    }
802
 
    return 0;
803
 
}
804
 
 
805
 
 
806
 
static PyObject *
807
 
StaticTuple_sizeof(StaticTuple *self)
808
 
{
809
 
    Py_ssize_t res;
810
 
 
811
 
    res = _PyObject_SIZE(&StaticTuple_Type) + (int)self->size * sizeof(void*);
812
 
    return PyInt_FromSsize_t(res);
813
 
}
814
 
 
815
 
 
816
 
 
817
 
static char StaticTuple_doc[] =
818
 
    "C implementation of a StaticTuple structure."
819
 
    "\n This is used as StaticTuple(item1, item2, item3)"
820
 
    "\n This is similar to tuple, less flexible in what it"
821
 
    "\n supports, but also lighter memory consumption."
822
 
    "\n Note that the constructor mimics the () form of tuples"
823
 
    "\n Rather than the 'tuple()' constructor."
824
 
    "\n  eg. StaticTuple(a, b) == (a, b) == tuple((a, b))";
825
 
 
826
 
static PyMethodDef StaticTuple_methods[] = {
827
 
    {"as_tuple", (PyCFunction)StaticTuple_as_tuple, METH_NOARGS, StaticTuple_as_tuple_doc},
828
 
    {"intern", (PyCFunction)StaticTuple_Intern, METH_NOARGS, StaticTuple_Intern_doc},
829
 
    {"_is_interned", (PyCFunction)StaticTuple__is_interned, METH_NOARGS,
830
 
     StaticTuple__is_interned_doc},
831
 
    {"from_sequence", (PyCFunction)StaticTuple_from_sequence,
832
 
     METH_STATIC | METH_VARARGS,
833
 
     "Create a StaticTuple from a given sequence. This functions"
834
 
     " the same as the tuple() constructor."},
835
 
    {"__reduce__", (PyCFunction)StaticTuple_reduce, METH_NOARGS, StaticTuple_reduce_doc},
836
 
    {"__sizeof__",  (PyCFunction)StaticTuple_sizeof, METH_NOARGS}, 
837
 
    {NULL, NULL} /* sentinel */
838
 
};
839
 
 
840
 
 
841
 
static PyNumberMethods StaticTuple_as_number = {
842
 
    (binaryfunc) StaticTuple_add,   /* nb_add */
843
 
    0,                              /* nb_subtract */
844
 
    0,                              /* nb_multiply */
845
 
    0,                              /* nb_divide */
846
 
    0,                              /* nb_remainder */
847
 
    0,                              /* nb_divmod */
848
 
    0,                              /* nb_power */
849
 
    0,                              /* nb_negative */
850
 
    0,                              /* nb_positive */
851
 
    0,                              /* nb_absolute */
852
 
    0,                              /* nb_nonzero */
853
 
    0,                              /* nb_invert */
854
 
    0,                              /* nb_lshift */
855
 
    0,                              /* nb_rshift */
856
 
    0,                              /* nb_and */
857
 
    0,                              /* nb_xor */
858
 
    0,                              /* nb_or */
859
 
    0,                              /* nb_coerce */
860
 
};
861
 
 
862
 
 
863
 
static PySequenceMethods StaticTuple_as_sequence = {
864
 
    (lenfunc)StaticTuple_length,            /* sq_length */
865
 
    0,                              /* sq_concat */
866
 
    0,                              /* sq_repeat */
867
 
    (ssizeargfunc)StaticTuple_item,         /* sq_item */
868
 
#if PY_MAJOR_VERSION >= 3
869
 
#else
870
 
    (ssizessizeargfunc)StaticTuple_slice,   /* sq_slice */
871
 
#endif
872
 
    0,                              /* sq_ass_item */
873
 
    0,                              /* sq_ass_slice */
874
 
    0,                              /* sq_contains */
875
 
#if PY_MAJOR_VERSION >= 3
876
 
    0,                              /* sq_inplace_concat */
877
 
    0,                              /* sq_inplace_repeat */
878
 
#endif
879
 
};
880
 
 
881
 
 
882
 
static PyMappingMethods StaticTuple_as_mapping = {
883
 
    (lenfunc)StaticTuple_length,            /* mp_length */
884
 
    (binaryfunc)StaticTuple_subscript,      /* mp_subscript */
885
 
    0,                                      /* mp_ass_subscript */
886
 
};
887
 
 
888
 
 
889
 
PyTypeObject StaticTuple_Type = {
890
 
    PyVarObject_HEAD_INIT(NULL, 0)
891
 
    "breezy._static_tuple_c.StaticTuple",        /* tp_name */
892
 
    sizeof(StaticTuple),                         /* tp_basicsize */
893
 
    sizeof(PyObject *),                          /* tp_itemsize */
894
 
    (destructor)StaticTuple_dealloc,             /* tp_dealloc */
895
 
    0,                                           /* tp_print */
896
 
    0,                                           /* tp_getattr */
897
 
    0,                                           /* tp_setattr */
898
 
    0,                                           /* tp_compare */
899
 
    (reprfunc)StaticTuple_repr,                  /* tp_repr */
900
 
    &StaticTuple_as_number,                      /* tp_as_number */
901
 
    &StaticTuple_as_sequence,                    /* tp_as_sequence */
902
 
    &StaticTuple_as_mapping,                     /* tp_as_mapping */
903
 
    (hashfunc)StaticTuple_hash,                  /* tp_hash */
904
 
    0,                                           /* tp_call */
905
 
    0,                                           /* tp_str */
906
 
    0,                                           /* tp_getattro */
907
 
    0,                                           /* tp_setattro */
908
 
    0,                                           /* tp_as_buffer */
909
 
    /* Py_TPFLAGS_CHECKTYPES tells the number operations that they shouldn't
910
 
     * try to 'coerce' but instead stuff like 'add' will check it arguments.
911
 
     */
912
 
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,  /* tp_flags*/
913
 
    StaticTuple_doc,                             /* tp_doc */
914
 
    /* gc.get_referents checks the IS_GC flag before it calls tp_traverse
915
 
     * And we don't include this object in the garbage collector because we
916
 
     * know it doesn't create cycles. However, 'meliae' will follow
917
 
     * tp_traverse, even if the object isn't GC, and we want that.
918
 
     */
919
 
    (traverseproc)StaticTuple_traverse,          /* tp_traverse */
920
 
    0,                                           /* tp_clear */
921
 
    StaticTuple_richcompare,                     /* tp_richcompare */
922
 
    0,                                           /* tp_weaklistoffset */
923
 
    // without implementing tp_iter, Python will fall back to PySequence*
924
 
    // which seems to work ok, we may need something faster/lighter in the
925
 
    // future.
926
 
    0,                                           /* tp_iter */
927
 
    0,                                           /* tp_iternext */
928
 
    StaticTuple_methods,                         /* tp_methods */
929
 
    0,                                           /* tp_members */
930
 
    0,                                           /* tp_getset */
931
 
    0,                                           /* tp_base */
932
 
    0,                                           /* tp_dict */
933
 
    0,                                           /* tp_descr_get */
934
 
    0,                                           /* tp_descr_set */
935
 
    0,                                           /* tp_dictoffset */
936
 
    0,                                           /* tp_init */
937
 
    0,                                           /* tp_alloc */
938
 
    StaticTuple_new_constructor,                 /* tp_new */
939
 
};
940
 
 
941
 
 
942
 
static PyMethodDef static_tuple_c_methods[] = {
943
 
    {NULL, NULL}
944
 
};
945
 
 
946
 
 
947
 
static void
948
 
setup_interned_tuples(PyObject *m)
949
 
{
950
 
    _interned_tuples = (PyObject *)SimpleSet_New();
951
 
    if (_interned_tuples != NULL) {
952
 
        Py_INCREF(_interned_tuples);
953
 
        PyModule_AddObject(m, "_interned_tuples", _interned_tuples);
954
 
    }
955
 
}
956
 
 
957
 
 
958
 
static void
959
 
setup_empty_tuple(PyObject *m)
960
 
{
961
 
    StaticTuple *stuple;
962
 
    if (_interned_tuples == NULL) {
963
 
        fprintf(stderr, "You need to call setup_interned_tuples() before"
964
 
                " setup_empty_tuple, because we intern it.\n");
965
 
    }
966
 
    // We need to create the empty tuple
967
 
    stuple = (StaticTuple *)StaticTuple_New(0);
968
 
    _empty_tuple = StaticTuple_Intern(stuple);
969
 
    assert(_empty_tuple == stuple);
970
 
    // At this point, refcnt is 2: 1 from New(), and 1 from the return from
971
 
    // intern(). We will keep 1 for the _empty_tuple global, and use the other
972
 
    // for the module reference.
973
 
    PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple);
974
 
}
975
 
 
976
 
static int
977
 
_StaticTuple_CheckExact(PyObject *obj)
978
 
{
979
 
    return StaticTuple_CheckExact(obj);
980
 
}
981
 
 
982
 
static void
983
 
setup_c_api(PyObject *m)
984
 
{
985
 
    _export_function(m, "StaticTuple_New", StaticTuple_New,
986
 
        "StaticTuple *(Py_ssize_t)");
987
 
    _export_function(m, "StaticTuple_Intern", StaticTuple_Intern,
988
 
        "StaticTuple *(StaticTuple *)");
989
 
    _export_function(m, "StaticTuple_FromSequence", StaticTuple_FromSequence,
990
 
        "StaticTuple *(PyObject *)");
991
 
    _export_function(m, "_StaticTuple_CheckExact", _StaticTuple_CheckExact,
992
 
        "int(PyObject *)");
993
 
}
994
 
 
995
 
 
996
 
PYMOD_INIT_FUNC(_static_tuple_c)
997
 
{
998
 
    PyObject* m;
999
 
 
1000
 
    StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
1001
 
    if (PyType_Ready(&StaticTuple_Type) < 0) {
1002
 
        return PYMOD_ERROR;
1003
 
    }
1004
 
 
1005
 
    PYMOD_CREATE(m, "_static_tuple_c",
1006
 
                 "C implementation of a StaticTuple structure",
1007
 
                 static_tuple_c_methods);
1008
 
    if (m == NULL) {
1009
 
      return PYMOD_ERROR;
1010
 
    }
1011
 
 
1012
 
    Py_INCREF(&StaticTuple_Type);
1013
 
    PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
1014
 
    if (import_breezy___simple_set_pyx() == -1) {
1015
 
        return PYMOD_ERROR;
1016
 
    }
1017
 
    setup_interned_tuples(m);
1018
 
    setup_empty_tuple(m);
1019
 
    setup_c_api(m);
1020
 
 
1021
 
    return PYMOD_SUCCESS(m);
1022
 
}
1023
 
 
1024
 
// vim: tabstop=4 sw=4 expandtab