/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: Jelmer Vernooij
  • Date: 2018-05-19 13:16:11 UTC
  • mto: (6968.4.3 git-archive)
  • mto: This revision was merged to the branch mainline in revision 6972.
  • Revision ID: jelmer@jelmer.uk-20180519131611-l9h9ud41j7qg1m03
Move tar/zip to breezy.archive.

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
static long
 
324
StaticTuple_hash(StaticTuple *self)
 
325
{
 
326
    /* adapted from tuplehash(), is the specific hash value considered
 
327
     * 'stable'?
 
328
     */
 
329
    register long x, y;
 
330
    Py_ssize_t len = self->size;
 
331
    PyObject **p;
 
332
    long mult = 1000003L;
 
333
 
 
334
#if STATIC_TUPLE_HAS_HASH
 
335
    if (self->hash != -1) {
 
336
        return self->hash;
 
337
    }
 
338
#endif
 
339
    x = 0x345678L;
 
340
    p = self->items;
 
341
    // TODO: We could set specific flags if we know that, for example, all the
 
342
    //       items are strings. I haven't seen a real-world benefit to that
 
343
    //       yet, though.
 
344
    while (--len >= 0) {
 
345
        y = PyObject_Hash(*p++);
 
346
        if (y == -1) /* failure */
 
347
            return -1;
 
348
        x = (x ^ y) * mult;
 
349
        /* the cast might truncate len; that doesn't change hash stability */
 
350
        mult += (long)(82520L + len + len);
 
351
    }
 
352
    x += 97531L;
 
353
    if (x == -1)
 
354
        x = -2;
 
355
#if STATIC_TUPLE_HAS_HASH
 
356
    self->hash = x;
 
357
#endif
 
358
    return x;
 
359
}
 
360
 
 
361
static PyObject *
 
362
StaticTuple_richcompare_to_tuple(StaticTuple *v, PyObject *wt, int op)
 
363
{
 
364
    PyObject *vt;
 
365
    PyObject *result = NULL;
 
366
 
 
367
    vt = StaticTuple_as_tuple((StaticTuple *)v);
 
368
    if (vt == NULL) {
 
369
        goto done;
 
370
    }
 
371
    if (!PyTuple_Check(wt)) {
 
372
        PyErr_BadInternalCall();
 
373
        goto done;
 
374
    }
 
375
    /* Now we have 2 tuples to compare, do it */
 
376
    result = PyTuple_Type.tp_richcompare(vt, wt, op);
 
377
done:
 
378
    Py_XDECREF(vt);
 
379
    return result;
 
380
}
 
381
 
 
382
/** Compare two objects to determine if they are equivalent.
 
383
 * The basic flow is as follows
 
384
 *  1) First make sure that both objects are StaticTuple instances. If they
 
385
 *     aren't then cast self to a tuple, and have the tuple do the comparison.
 
386
 *  2) Special case comparison to Py_None, because it happens to occur fairly
 
387
 *     often in the test suite.
 
388
 *  3) Special case when v and w are the same pointer. As we know the answer to
 
389
 *     all queries without walking individual items.
 
390
 *  4) For all operations, we then walk the items to find the first paired
 
391
 *     items that are not equal.
 
392
 *  5) If all items found are equal, we then check the length of self and
 
393
 *     other to determine equality.
 
394
 *  6) If an item differs, then we apply "op" to those last two items. (eg.
 
395
 *     StaticTuple(A, B) > StaticTuple(A, C) iff B > C)
 
396
 */
 
397
 
 
398
static PyObject *
 
399
StaticTuple_richcompare(PyObject *v, PyObject *w, int op)
 
400
{
 
401
    StaticTuple *v_st, *w_st;
 
402
    Py_ssize_t vlen, wlen, min_len, i;
 
403
    PyObject *v_obj, *w_obj;
 
404
    richcmpfunc string_richcompare;
 
405
 
 
406
    if (!StaticTuple_CheckExact(v)) {
 
407
        /* This has never triggered, according to python-dev it seems this
 
408
         * might trigger if '__op__' is defined but '__rop__' is not, sort of
 
409
         * case. Such as "None == StaticTuple()"
 
410
         */
 
411
        fprintf(stderr, "self is not StaticTuple\n");
 
412
        Py_INCREF(Py_NotImplemented);
 
413
        return Py_NotImplemented;
 
414
    }
 
415
    v_st = (StaticTuple *)v;
 
416
    if (StaticTuple_CheckExact(w)) {
 
417
        /* The most common case */
 
418
        w_st = (StaticTuple*)w;
 
419
    } else if (PyTuple_Check(w)) {
 
420
        /* One of v or w is a tuple, so we go the 'slow' route and cast up to
 
421
         * tuples to compare.
 
422
         */
 
423
        /* TODO: This seems to be triggering more than I thought it would...
 
424
         *       We probably want to optimize comparing self to other when
 
425
         *       other is a tuple.
 
426
         */
 
427
        return StaticTuple_richcompare_to_tuple(v_st, w, op);
 
428
    } else if (w == Py_None) {
 
429
        // None is always less than the object
 
430
        switch (op) {
 
431
        case Py_NE:
 
432
#if PY_MAJOR_VERSION >= 3
 
433
#else
 
434
        case Py_GT:case Py_GE:
 
435
#endif
 
436
            Py_INCREF(Py_True);
 
437
            return Py_True;
 
438
        case Py_EQ:
 
439
#if PY_MAJOR_VERSION >= 3
 
440
#else
 
441
        case Py_LT:case Py_LE:
 
442
#endif
 
443
            Py_INCREF(Py_False);
 
444
            return Py_False;
 
445
        default: // Should only happen on Python 3
 
446
            return Py_NotImplemented;
 
447
        }
 
448
    } else {
 
449
        /* We don't special case this comparison, we just let python handle
 
450
         * it.
 
451
         */
 
452
         Py_INCREF(Py_NotImplemented);
 
453
         return Py_NotImplemented;
 
454
    }
 
455
    /* Now we know that we have 2 StaticTuple objects, so let's compare them.
 
456
     * This code is inspired from tuplerichcompare, except we know our
 
457
     * objects are limited in scope, so we can inline some comparisons.
 
458
     */
 
459
    if (v == w) {
 
460
        /* Identical pointers, we can shortcut this easily. */
 
461
        switch (op) {
 
462
        case Py_EQ:case Py_LE:case Py_GE:
 
463
            Py_INCREF(Py_True);
 
464
            return Py_True;
 
465
        case Py_NE:case Py_LT:case Py_GT:
 
466
            Py_INCREF(Py_False);
 
467
            return Py_False;
 
468
        }
 
469
    }
 
470
    if (op == Py_EQ
 
471
        && _StaticTuple_is_interned(v_st)
 
472
        && _StaticTuple_is_interned(w_st))
 
473
    {
 
474
        /* If both objects are interned, we know they are different if the
 
475
         * pointer is not the same, which would have been handled by the
 
476
         * previous if. No need to compare the entries.
 
477
         */
 
478
        Py_INCREF(Py_False);
 
479
        return Py_False;
 
480
    }
 
481
 
 
482
    /* The only time we are likely to compare items of different lengths is in
 
483
     * something like the interned_keys set. However, the hash is good enough
 
484
     * that it is rare. Note that 'tuple_richcompare' also does not compare
 
485
     * lengths here.
 
486
     */
 
487
    vlen = v_st->size;
 
488
    wlen = w_st->size;
 
489
    min_len = (vlen < wlen) ? vlen : wlen;
 
490
    string_richcompare = PyBytes_Type.tp_richcompare;
 
491
    for (i = 0; i < min_len; i++) {
 
492
        PyObject *result = NULL;
 
493
        v_obj = StaticTuple_GET_ITEM(v_st, i);
 
494
        w_obj = StaticTuple_GET_ITEM(w_st, i);
 
495
        if (v_obj == w_obj) {
 
496
            /* Shortcut case, these must be identical */
 
497
            continue;
 
498
        }
 
499
        if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj)) {
 
500
            result = string_richcompare(v_obj, w_obj, Py_EQ);
 
501
        } else if (StaticTuple_CheckExact(v_obj) &&
 
502
                   StaticTuple_CheckExact(w_obj))
 
503
        {
 
504
            /* Both are StaticTuple types, so recurse */
 
505
            result = StaticTuple_richcompare(v_obj, w_obj, Py_EQ);
 
506
        } else {
 
507
            /* Fall back to generic richcompare */
 
508
            result = PyObject_RichCompare(v_obj, w_obj, Py_EQ);
 
509
        }
 
510
        if (result == NULL) {
 
511
            return NULL; /* There seems to be an error */
 
512
        }
 
513
        if (result == Py_False) {
 
514
            // This entry is not identical, Shortcut for Py_EQ
 
515
            if (op == Py_EQ) {
 
516
                return result;
 
517
            }
 
518
            Py_DECREF(result);
 
519
            break;
 
520
        }
 
521
        if (result != Py_True) {
 
522
            /* We don't know *what* richcompare is returning, but it
 
523
             * isn't something we recognize
 
524
             */
 
525
            PyErr_BadInternalCall();
 
526
            Py_DECREF(result);
 
527
            return NULL;
 
528
        }
 
529
        Py_DECREF(result);
 
530
    }
 
531
    if (i >= min_len) {
 
532
        /* We walked off one of the lists, but everything compared equal so
 
533
         * far. Just compare the size.
 
534
         */
 
535
        int cmp;
 
536
        PyObject *res;
 
537
        switch (op) {
 
538
        case Py_LT: cmp = vlen <  wlen; break;
 
539
        case Py_LE: cmp = vlen <= wlen; break;
 
540
        case Py_EQ: cmp = vlen == wlen; break;
 
541
        case Py_NE: cmp = vlen != wlen; break;
 
542
        case Py_GT: cmp = vlen >  wlen; break;
 
543
        case Py_GE: cmp = vlen >= wlen; break;
 
544
        default: return NULL; /* cannot happen */
 
545
        }
 
546
        if (cmp)
 
547
            res = Py_True;
 
548
        else
 
549
            res = Py_False;
 
550
        Py_INCREF(res);
 
551
        return res;
 
552
    }
 
553
    /* The last item differs, shortcut the Py_NE case */
 
554
    if (op == Py_NE) {
 
555
        Py_INCREF(Py_True);
 
556
        return Py_True;
 
557
    }
 
558
    /* It is some other comparison, go ahead and do the real check. */
 
559
    if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj))
 
560
    {
 
561
        return string_richcompare(v_obj, w_obj, op);
 
562
    } else if (StaticTuple_CheckExact(v_obj) &&
 
563
               StaticTuple_CheckExact(w_obj))
 
564
    {
 
565
        /* Both are StaticTuple types, so recurse */
 
566
        return StaticTuple_richcompare(v_obj, w_obj, op);
 
567
    } else {
 
568
        return PyObject_RichCompare(v_obj, w_obj, op);
 
569
    }
 
570
}
 
571
 
 
572
 
 
573
static Py_ssize_t
 
574
StaticTuple_length(StaticTuple *self)
 
575
{
 
576
    return self->size;
 
577
}
 
578
 
 
579
 
 
580
static PyObject *
 
581
StaticTuple__is_interned(StaticTuple *self)
 
582
{
 
583
    if (_StaticTuple_is_interned(self)) {
 
584
        Py_INCREF(Py_True);
 
585
        return Py_True;
 
586
    }
 
587
    Py_INCREF(Py_False);
 
588
    return Py_False;
 
589
}
 
590
 
 
591
static char StaticTuple__is_interned_doc[] = "_is_interned() => True/False\n"
 
592
    "Check to see if this tuple has been interned.\n";
 
593
 
 
594
 
 
595
static PyObject *
 
596
StaticTuple_reduce(StaticTuple *self)
 
597
{
 
598
    PyObject *result = NULL, *as_tuple = NULL;
 
599
 
 
600
    result = PyTuple_New(2);
 
601
    if (!result) {
 
602
        return NULL;
 
603
    }
 
604
    as_tuple = StaticTuple_as_tuple(self);
 
605
    if (as_tuple == NULL) {
 
606
        Py_DECREF(result);
 
607
        return NULL;
 
608
    }
 
609
    Py_INCREF(&StaticTuple_Type);
 
610
    PyTuple_SET_ITEM(result, 0, (PyObject *)&StaticTuple_Type);
 
611
    PyTuple_SET_ITEM(result, 1, as_tuple);
 
612
    return result;
 
613
}
 
614
 
 
615
static char StaticTuple_reduce_doc[] = "__reduce__() => tuple\n";
 
616
 
 
617
 
 
618
static PyObject *
 
619
StaticTuple_add(PyObject *v, PyObject *w)
 
620
{
 
621
    Py_ssize_t i, len_v, len_w;
 
622
    PyObject *item;
 
623
    StaticTuple *result;
 
624
     /* StaticTuples and plain tuples may be added (concatenated) to
 
625
      * StaticTuples.
 
626
      */
 
627
    if (StaticTuple_CheckExact(v)) {
 
628
        len_v = ((StaticTuple*)v)->size;
 
629
    } else if (PyTuple_Check(v)) {
 
630
        len_v = PyTuple_GET_SIZE(v);
 
631
    } else {
 
632
        Py_INCREF(Py_NotImplemented);
 
633
        return Py_NotImplemented;
 
634
    }
 
635
    if (StaticTuple_CheckExact(w)) {
 
636
        len_w = ((StaticTuple*)w)->size;
 
637
    } else if (PyTuple_Check(w)) {
 
638
        len_w = PyTuple_GET_SIZE(w);
 
639
    } else {
 
640
        Py_INCREF(Py_NotImplemented);
 
641
        return Py_NotImplemented;
 
642
    }
 
643
    result = StaticTuple_New(len_v + len_w);
 
644
    if (result == NULL)
 
645
        return NULL;
 
646
    for (i = 0; i < len_v; ++i) {
 
647
        // This returns a new reference, which we then 'steal' with 
 
648
        // StaticTuple_SET_ITEM
 
649
        item = PySequence_GetItem(v, i);
 
650
        if (item == NULL) {
 
651
            Py_DECREF(result);
 
652
            return NULL;
 
653
        }
 
654
        StaticTuple_SET_ITEM(result, i, item);
 
655
    }
 
656
    for (i = 0; i < len_w; ++i) {
 
657
        item = PySequence_GetItem(w, i);
 
658
        if (item == NULL) {
 
659
            Py_DECREF(result);
 
660
            return NULL;
 
661
        }
 
662
        StaticTuple_SET_ITEM(result, i+len_v, item);
 
663
    }
 
664
    if (!StaticTuple_check_items(result)) {
 
665
        Py_DECREF(result);
 
666
        return NULL;
 
667
    }
 
668
    return (PyObject *)result;
 
669
}
 
670
 
 
671
static PyObject *
 
672
StaticTuple_item(StaticTuple *self, Py_ssize_t offset)
 
673
{
 
674
    PyObject *obj;
 
675
    /* We cast to (int) to avoid worrying about whether Py_ssize_t is a
 
676
     * long long, etc. offsets should never be >2**31 anyway.
 
677
     */
 
678
    if (offset < 0) {
 
679
        PyErr_Format(PyExc_IndexError, "StaticTuple_item does not support"
 
680
            " negative indices: %d\n", (int)offset);
 
681
    } else if (offset >= self->size) {
 
682
        PyErr_Format(PyExc_IndexError, "StaticTuple index out of range"
 
683
            " %d >= %d", (int)offset, (int)self->size);
 
684
        return NULL;
 
685
    }
 
686
    obj = (PyObject *)self->items[offset];
 
687
    Py_INCREF(obj);
 
688
    return obj;
 
689
}
 
690
 
 
691
#if PY_MAJOR_VERSION >= 3
 
692
#else
 
693
static PyObject *
 
694
StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
 
695
{
 
696
    PyObject *as_tuple, *result;
 
697
 
 
698
    as_tuple = StaticTuple_as_tuple(self);
 
699
    if (as_tuple == NULL) {
 
700
        return NULL;
 
701
    }
 
702
    result = PyTuple_Type.tp_as_sequence->sq_slice(as_tuple, ilow, ihigh);
 
703
    Py_DECREF(as_tuple);
 
704
    return result;
 
705
}
 
706
#endif
 
707
 
 
708
static PyObject *
 
709
StaticTuple_subscript(StaticTuple *self, PyObject *key)
 
710
{
 
711
    PyObject *as_tuple, *result;
 
712
 
 
713
    as_tuple = StaticTuple_as_tuple(self);
 
714
    if (as_tuple == NULL) {
 
715
        return NULL;
 
716
    }
 
717
    result = PyTuple_Type.tp_as_mapping->mp_subscript(as_tuple, key);
 
718
    Py_DECREF(as_tuple);
 
719
    return result;
 
720
}
 
721
 
 
722
static int
 
723
StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
 
724
{
 
725
    Py_ssize_t i;
 
726
    for (i = self->size; --i >= 0;) {
 
727
        Py_VISIT(self->items[i]);
 
728
    }
 
729
    return 0;
 
730
}
 
731
 
 
732
 
 
733
static PyObject *
 
734
StaticTuple_sizeof(StaticTuple *self)
 
735
{
 
736
    Py_ssize_t res;
 
737
 
 
738
    res = _PyObject_SIZE(&StaticTuple_Type) + (int)self->size * sizeof(void*);
 
739
    return PyInt_FromSsize_t(res);
 
740
}
 
741
 
 
742
 
 
743
 
 
744
static char StaticTuple_doc[] =
 
745
    "C implementation of a StaticTuple structure."
 
746
    "\n This is used as StaticTuple(item1, item2, item3)"
 
747
    "\n This is similar to tuple, less flexible in what it"
 
748
    "\n supports, but also lighter memory consumption."
 
749
    "\n Note that the constructor mimics the () form of tuples"
 
750
    "\n Rather than the 'tuple()' constructor."
 
751
    "\n  eg. StaticTuple(a, b) == (a, b) == tuple((a, b))";
 
752
 
 
753
static PyMethodDef StaticTuple_methods[] = {
 
754
    {"as_tuple", (PyCFunction)StaticTuple_as_tuple, METH_NOARGS, StaticTuple_as_tuple_doc},
 
755
    {"intern", (PyCFunction)StaticTuple_Intern, METH_NOARGS, StaticTuple_Intern_doc},
 
756
    {"_is_interned", (PyCFunction)StaticTuple__is_interned, METH_NOARGS,
 
757
     StaticTuple__is_interned_doc},
 
758
    {"from_sequence", (PyCFunction)StaticTuple_from_sequence,
 
759
     METH_STATIC | METH_VARARGS,
 
760
     "Create a StaticTuple from a given sequence. This functions"
 
761
     " the same as the tuple() constructor."},
 
762
    {"__reduce__", (PyCFunction)StaticTuple_reduce, METH_NOARGS, StaticTuple_reduce_doc},
 
763
    {"__sizeof__",  (PyCFunction)StaticTuple_sizeof, METH_NOARGS}, 
 
764
    {NULL, NULL} /* sentinel */
 
765
};
 
766
 
 
767
 
 
768
static PyNumberMethods StaticTuple_as_number = {
 
769
    (binaryfunc) StaticTuple_add,   /* nb_add */
 
770
    0,                              /* nb_subtract */
 
771
    0,                              /* nb_multiply */
 
772
    0,                              /* nb_divide */
 
773
    0,                              /* nb_remainder */
 
774
    0,                              /* nb_divmod */
 
775
    0,                              /* nb_power */
 
776
    0,                              /* nb_negative */
 
777
    0,                              /* nb_positive */
 
778
    0,                              /* nb_absolute */
 
779
    0,                              /* nb_nonzero */
 
780
    0,                              /* nb_invert */
 
781
    0,                              /* nb_lshift */
 
782
    0,                              /* nb_rshift */
 
783
    0,                              /* nb_and */
 
784
    0,                              /* nb_xor */
 
785
    0,                              /* nb_or */
 
786
    0,                              /* nb_coerce */
 
787
};
 
788
 
 
789
 
 
790
static PySequenceMethods StaticTuple_as_sequence = {
 
791
    (lenfunc)StaticTuple_length,            /* sq_length */
 
792
    0,                              /* sq_concat */
 
793
    0,                              /* sq_repeat */
 
794
    (ssizeargfunc)StaticTuple_item,         /* sq_item */
 
795
#if PY_MAJOR_VERSION >= 3
 
796
#else
 
797
    (ssizessizeargfunc)StaticTuple_slice,   /* sq_slice */
 
798
#endif
 
799
    0,                              /* sq_ass_item */
 
800
    0,                              /* sq_ass_slice */
 
801
    0,                              /* sq_contains */
 
802
#if PY_MAJOR_VERSION >= 3
 
803
    0,                              /* sq_inplace_concat */
 
804
    0,                              /* sq_inplace_repeat */
 
805
#endif
 
806
};
 
807
 
 
808
 
 
809
static PyMappingMethods StaticTuple_as_mapping = {
 
810
    (lenfunc)StaticTuple_length,            /* mp_length */
 
811
    (binaryfunc)StaticTuple_subscript,      /* mp_subscript */
 
812
    0,                                      /* mp_ass_subscript */
 
813
};
 
814
 
 
815
 
 
816
PyTypeObject StaticTuple_Type = {
 
817
    PyVarObject_HEAD_INIT(NULL, 0)
 
818
    "breezy._static_tuple_c.StaticTuple",        /* tp_name */
 
819
    sizeof(StaticTuple),                         /* tp_basicsize */
 
820
    sizeof(PyObject *),                          /* tp_itemsize */
 
821
    (destructor)StaticTuple_dealloc,             /* tp_dealloc */
 
822
    0,                                           /* tp_print */
 
823
    0,                                           /* tp_getattr */
 
824
    0,                                           /* tp_setattr */
 
825
    0,                                           /* tp_compare */
 
826
    (reprfunc)StaticTuple_repr,                  /* tp_repr */
 
827
    &StaticTuple_as_number,                      /* tp_as_number */
 
828
    &StaticTuple_as_sequence,                    /* tp_as_sequence */
 
829
    &StaticTuple_as_mapping,                     /* tp_as_mapping */
 
830
    (hashfunc)StaticTuple_hash,                  /* tp_hash */
 
831
    0,                                           /* tp_call */
 
832
    0,                                           /* tp_str */
 
833
    0,                                           /* tp_getattro */
 
834
    0,                                           /* tp_setattro */
 
835
    0,                                           /* tp_as_buffer */
 
836
    /* Py_TPFLAGS_CHECKTYPES tells the number operations that they shouldn't
 
837
     * try to 'coerce' but instead stuff like 'add' will check it arguments.
 
838
     */
 
839
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,  /* tp_flags*/
 
840
    StaticTuple_doc,                             /* tp_doc */
 
841
    /* gc.get_referents checks the IS_GC flag before it calls tp_traverse
 
842
     * And we don't include this object in the garbage collector because we
 
843
     * know it doesn't create cycles. However, 'meliae' will follow
 
844
     * tp_traverse, even if the object isn't GC, and we want that.
 
845
     */
 
846
    (traverseproc)StaticTuple_traverse,          /* tp_traverse */
 
847
    0,                                           /* tp_clear */
 
848
    StaticTuple_richcompare,                     /* tp_richcompare */
 
849
    0,                                           /* tp_weaklistoffset */
 
850
    // without implementing tp_iter, Python will fall back to PySequence*
 
851
    // which seems to work ok, we may need something faster/lighter in the
 
852
    // future.
 
853
    0,                                           /* tp_iter */
 
854
    0,                                           /* tp_iternext */
 
855
    StaticTuple_methods,                         /* tp_methods */
 
856
    0,                                           /* tp_members */
 
857
    0,                                           /* tp_getset */
 
858
    0,                                           /* tp_base */
 
859
    0,                                           /* tp_dict */
 
860
    0,                                           /* tp_descr_get */
 
861
    0,                                           /* tp_descr_set */
 
862
    0,                                           /* tp_dictoffset */
 
863
    0,                                           /* tp_init */
 
864
    0,                                           /* tp_alloc */
 
865
    StaticTuple_new_constructor,                 /* tp_new */
 
866
};
 
867
 
 
868
 
 
869
static PyMethodDef static_tuple_c_methods[] = {
 
870
    {NULL, NULL}
 
871
};
 
872
 
 
873
 
 
874
static void
 
875
setup_interned_tuples(PyObject *m)
 
876
{
 
877
    _interned_tuples = (PyObject *)SimpleSet_New();
 
878
    if (_interned_tuples != NULL) {
 
879
        Py_INCREF(_interned_tuples);
 
880
        PyModule_AddObject(m, "_interned_tuples", _interned_tuples);
 
881
    }
 
882
}
 
883
 
 
884
 
 
885
static void
 
886
setup_empty_tuple(PyObject *m)
 
887
{
 
888
    StaticTuple *stuple;
 
889
    if (_interned_tuples == NULL) {
 
890
        fprintf(stderr, "You need to call setup_interned_tuples() before"
 
891
                " setup_empty_tuple, because we intern it.\n");
 
892
    }
 
893
    // We need to create the empty tuple
 
894
    stuple = (StaticTuple *)StaticTuple_New(0);
 
895
    _empty_tuple = StaticTuple_Intern(stuple);
 
896
    assert(_empty_tuple == stuple);
 
897
    // At this point, refcnt is 2: 1 from New(), and 1 from the return from
 
898
    // intern(). We will keep 1 for the _empty_tuple global, and use the other
 
899
    // for the module reference.
 
900
    PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple);
 
901
}
 
902
 
 
903
static int
 
904
_StaticTuple_CheckExact(PyObject *obj)
 
905
{
 
906
    return StaticTuple_CheckExact(obj);
 
907
}
 
908
 
 
909
static void
 
910
setup_c_api(PyObject *m)
 
911
{
 
912
    _export_function(m, "StaticTuple_New", StaticTuple_New,
 
913
        "StaticTuple *(Py_ssize_t)");
 
914
    _export_function(m, "StaticTuple_Intern", StaticTuple_Intern,
 
915
        "StaticTuple *(StaticTuple *)");
 
916
    _export_function(m, "StaticTuple_FromSequence", StaticTuple_FromSequence,
 
917
        "StaticTuple *(PyObject *)");
 
918
    _export_function(m, "_StaticTuple_CheckExact", _StaticTuple_CheckExact,
 
919
        "int(PyObject *)");
 
920
}
 
921
 
 
922
 
 
923
PYMOD_INIT_FUNC(_static_tuple_c)
 
924
{
 
925
    PyObject* m;
 
926
 
 
927
    StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
 
928
    if (PyType_Ready(&StaticTuple_Type) < 0) {
 
929
        return PYMOD_ERROR;
 
930
    }
 
931
 
 
932
    PYMOD_CREATE(m, "_static_tuple_c",
 
933
                 "C implementation of a StaticTuple structure",
 
934
                 static_tuple_c_methods);
 
935
    if (m == NULL) {
 
936
      return PYMOD_ERROR;
 
937
    }
 
938
 
 
939
    Py_INCREF(&StaticTuple_Type);
 
940
    PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
 
941
    if (import_breezy___simple_set_pyx() == -1) {
 
942
        return PYMOD_ERROR;
 
943
    }
 
944
    setup_interned_tuples(m);
 
945
    setup_empty_tuple(m);
 
946
    setup_c_api(m);
 
947
 
 
948
    return PYMOD_SUCCESS(m);
 
949
}
 
950
 
 
951
// vim: tabstop=4 sw=4 expandtab