~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2009, 2010 Canonical Ltd
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
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
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
17
"""Definition of a class that is similar to Set with some small changes."""
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
18
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
19
cdef extern from "python-compat.h":
20
    pass
21
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
22
cdef extern from "Python.h":
23
    ctypedef unsigned long size_t
4744.1.1 by John Arbash Meinel
Add a test case for the bug w/ NotImplemented.
24
    ctypedef long (*hashfunc)(PyObject*) except -1
25
    ctypedef object (*richcmpfunc)(PyObject *, PyObject *, int)
4679.3.84 by John Arbash Meinel
Remove 2 more mallocs per line by using a fixed-width StaticTuple for reflists.
26
    ctypedef int (*visitproc)(PyObject *, void *)
27
    ctypedef int (*traverseproc)(PyObject *, visitproc, void *)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
28
    int Py_EQ
29
    void Py_INCREF(PyObject *)
30
    void Py_DECREF(PyObject *)
31
    ctypedef struct PyTypeObject:
32
        hashfunc tp_hash
33
        richcmpfunc tp_richcompare
4679.3.84 by John Arbash Meinel
Remove 2 more mallocs per line by using a fixed-width StaticTuple for reflists.
34
        traverseproc tp_traverse
4679.3.62 by John Arbash Meinel
Move some of the information into the pxd header file.
35
36
    PyTypeObject *Py_TYPE(PyObject *)
4739.2.1 by John Arbash Meinel
Stop using hash() because of bugs wrt pyrex 0.9.8.5
37
    # Note: *Don't* use hash(), Pyrex 0.9.8.5 thinks it returns an 'int', and
38
    #       thus silently truncates to 32-bits on 64-bit machines.
39
    long PyObject_Hash(PyObject *) except -1
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
40
        
41
    void *PyMem_Malloc(size_t nbytes)
42
    void PyMem_Free(void *)
43
    void memset(void *, int, size_t)
44
4679.3.86 by John Arbash Meinel
some whitespace and expose the rest of the C api in the .pxd file.
45
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
46
# Dummy is an object used to mark nodes that have been deleted. Since
47
# collisions require us to move a node to an alternative location, if we just
48
# set an entry to NULL on delete, we won't find any relocated nodes.
49
# We have to use _dummy_obj because we need to keep a refcount to it, but we
50
# also use _dummy as a pointer, because it avoids having to put <PyObject*> all
51
# over the code base.
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
52
cdef object _dummy_obj
53
cdef PyObject *_dummy
54
_dummy_obj = object()
55
_dummy = <PyObject *>_dummy_obj
56
4679.3.74 by John Arbash Meinel
Revert back to before I started trying to move to pyrex/cython code.
57
4744.1.1 by John Arbash Meinel
Add a test case for the bug w/ NotImplemented.
58
cdef object _NotImplemented
59
_NotImplemented = NotImplemented
60
61
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
62
cdef int _is_equal(PyObject *this, long this_hash, PyObject *other) except -1:
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
63
    cdef long other_hash
64
65
    if this == other:
66
        return 1
4739.2.1 by John Arbash Meinel
Stop using hash() because of bugs wrt pyrex 0.9.8.5
67
    other_hash = PyObject_Hash(other)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
68
    if other_hash != this_hash:
69
        return 0
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
70
71
    # This implements a subset of the PyObject_RichCompareBool functionality.
72
    # Namely it:
73
    #   1) Doesn't try to do anything with old-style classes
74
    #   2) Assumes that both objects have a tp_richcompare implementation, and
75
    #      that if that is not enough to compare equal, then they are not
76
    #      equal. (It doesn't try to cast them both to some intermediate form
77
    #      that would compare equal.)
4679.3.62 by John Arbash Meinel
Move some of the information into the pxd header file.
78
    res = Py_TYPE(this).tp_richcompare(this, other, Py_EQ)
4744.1.1 by John Arbash Meinel
Add a test case for the bug w/ NotImplemented.
79
    if res is _NotImplemented:
4679.3.62 by John Arbash Meinel
Move some of the information into the pxd header file.
80
        res = Py_TYPE(other).tp_richcompare(other, this, Py_EQ)
4744.1.1 by John Arbash Meinel
Add a test case for the bug w/ NotImplemented.
81
        if res is _NotImplemented:
82
            return 0
83
    if res:
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
84
        return 1
85
    return 0
86
87
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
88
cdef public api class SimpleSet [object SimpleSetObject, type SimpleSet_Type]:
89
    """This class can be used to track canonical forms for objects.
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
90
91
    It is similar in function to the interned dictionary that is used by
92
    strings. However:
93
94
      1) It assumes that hash(obj) is cheap, so does not need to inline a copy
95
         of it
96
      2) It only stores one reference to the object, rather than 2 (key vs
97
         key:value)
98
99
    As such, it uses 1/3rd the amount of memory to store a pointer to the
100
    interned object.
101
    """
4679.3.62 by John Arbash Meinel
Move some of the information into the pxd header file.
102
    # Attributes are defined in the .pxd file
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
103
    DEF DEFAULT_SIZE=1024
104
105
    def __init__(self):
106
        cdef Py_ssize_t size, n_bytes
107
108
        size = DEFAULT_SIZE
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
109
        self._mask = size - 1
110
        self._used = 0
111
        self._fill = 0
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
112
        n_bytes = sizeof(PyObject*) * size;
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
113
        self._table = <PyObject **>PyMem_Malloc(n_bytes)
114
        if self._table == NULL:
4679.3.63 by John Arbash Meinel
Implement resizing.
115
            raise MemoryError()
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
116
        memset(self._table, 0, n_bytes)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
117
118
    def __dealloc__(self):
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
119
        if self._table != NULL:
120
            PyMem_Free(self._table)
121
            self._table = NULL
122
123
    property used:
124
        def __get__(self):
125
            return self._used
126
127
    property fill:
128
        def __get__(self):
129
            return self._fill
130
131
    property mask:
132
        def __get__(self):
133
            return self._mask
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
134
4679.3.84 by John Arbash Meinel
Remove 2 more mallocs per line by using a fixed-width StaticTuple for reflists.
135
    def _memory_size(self):
136
        """Return the number of bytes of memory consumed by this class."""
137
        return sizeof(self) + (sizeof(PyObject*)*(self._mask + 1))
138
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
139
    def __len__(self):
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
140
        return self._used
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
141
142
    def _test_lookup(self, key):
143
        cdef PyObject **slot
144
4679.3.61 by John Arbash Meinel
Invert the logic.
145
        slot = _lookup(self, key)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
146
        if slot[0] == NULL:
147
            res = '<null>'
148
        elif slot[0] == _dummy:
149
            res = '<dummy>'
150
        else:
151
            res = <object>slot[0]
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
152
        return <int>(slot - self._table), res
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
153
154
    def __contains__(self, key):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
155
        """Is key present in this SimpleSet."""
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
156
        cdef PyObject **slot
157
4679.3.61 by John Arbash Meinel
Invert the logic.
158
        slot = _lookup(self, key)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
159
        if slot[0] == NULL or slot[0] == _dummy:
160
            return False
161
        return True
162
4679.3.62 by John Arbash Meinel
Move some of the information into the pxd header file.
163
    cdef PyObject *_get(self, object key) except? NULL:
4679.3.61 by John Arbash Meinel
Invert the logic.
164
        """Return the object (or nothing) define at the given location."""
165
        cdef PyObject **slot
166
167
        slot = _lookup(self, key)
168
        if slot[0] == NULL or slot[0] == _dummy:
169
            return NULL
170
        return slot[0]
171
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
172
    def __getitem__(self, key):
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
173
        """Return a stored item that is equivalent to key."""
4679.3.61 by John Arbash Meinel
Invert the logic.
174
        cdef PyObject *py_val
175
176
        py_val = self._get(key)
177
        if py_val == NULL:
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
178
            raise KeyError("Key %s is not present" % key)
4679.3.61 by John Arbash Meinel
Invert the logic.
179
        val = <object>(py_val)
4679.3.58 by John Arbash Meinel
Adding a StaticTupleInterner class.
180
        return val
181
4679.3.63 by John Arbash Meinel
Implement resizing.
182
    cdef int _insert_clean(self, PyObject *key) except -1:
183
        """Insert a key into self.table.
184
185
        This is only meant to be used during times like '_resize',
186
        as it makes a lot of assuptions about keys not already being present,
187
        and there being no dummy entries.
188
        """
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
189
        cdef size_t i, n_lookup
4679.3.63 by John Arbash Meinel
Implement resizing.
190
        cdef long the_hash
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
191
        cdef PyObject **table, **slot
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
192
        cdef Py_ssize_t mask
4679.3.63 by John Arbash Meinel
Implement resizing.
193
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
194
        mask = self._mask
195
        table = self._table
4679.3.63 by John Arbash Meinel
Implement resizing.
196
4739.2.1 by John Arbash Meinel
Stop using hash() because of bugs wrt pyrex 0.9.8.5
197
        the_hash = PyObject_Hash(key)
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
198
        i = the_hash
199
        for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
200
            slot = &table[i & mask]
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
201
            if slot[0] == NULL:
202
                slot[0] = key
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
203
                self._fill = self._fill + 1
204
                self._used = self._used + 1
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
205
                return 1
206
            i = i + 1 + n_lookup
207
        raise RuntimeError('ran out of slots.')
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
208
209
    def _py_resize(self, min_used):
210
        """Do not use this directly, it is only exposed for testing."""
211
        return self._resize(min_used)
212
213
    cdef Py_ssize_t _resize(self, Py_ssize_t min_used) except -1:
4679.3.63 by John Arbash Meinel
Implement resizing.
214
        """Resize the internal table.
215
216
        The final table will be big enough to hold at least min_used entries.
217
        We will copy the data from the existing table over, leaving out dummy
218
        entries.
219
220
        :return: The new size of the internal table
221
        """
222
        cdef Py_ssize_t new_size, n_bytes, remaining
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
223
        cdef PyObject **new_table, **old_table, **slot
4679.3.63 by John Arbash Meinel
Implement resizing.
224
225
        new_size = DEFAULT_SIZE
226
        while new_size <= min_used and new_size > 0:
227
            new_size = new_size << 1
228
        # We rolled over our signed size field
229
        if new_size <= 0:
230
            raise MemoryError()
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
231
        # Even if min_used == self._mask + 1, and we aren't changing the actual
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
232
        # size, we will still run the algorithm so that dummy entries are
233
        # removed
4679.3.63 by John Arbash Meinel
Implement resizing.
234
        # TODO: Test this
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
235
        # if new_size < self._used:
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
236
        #     raise RuntimeError('cannot shrink SimpleSet to something'
4679.3.63 by John Arbash Meinel
Implement resizing.
237
        #                        ' smaller than the number of used slots.')
238
        n_bytes = sizeof(PyObject*) * new_size;
239
        new_table = <PyObject **>PyMem_Malloc(n_bytes)
240
        if new_table == NULL:
241
            raise MemoryError()
242
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
243
        old_table = self._table
244
        self._table = new_table
245
        memset(self._table, 0, n_bytes)
246
        self._mask = new_size - 1
247
        self._used = 0
248
        remaining = self._fill
249
        self._fill = 0
4679.3.63 by John Arbash Meinel
Implement resizing.
250
251
        # Moving everything to the other table is refcount neutral, so we don't
252
        # worry about it.
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
253
        slot = old_table
4679.3.63 by John Arbash Meinel
Implement resizing.
254
        while remaining > 0:
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
255
            if slot[0] == NULL: # unused slot
4679.3.63 by John Arbash Meinel
Implement resizing.
256
                pass 
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
257
            elif slot[0] == _dummy: # dummy slot
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
258
                remaining = remaining - 1
4679.3.63 by John Arbash Meinel
Implement resizing.
259
            else: # active slot
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
260
                remaining = remaining - 1
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
261
                self._insert_clean(slot[0])
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
262
            slot = slot + 1
4679.3.63 by John Arbash Meinel
Implement resizing.
263
        PyMem_Free(old_table)
264
        return new_size
265
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
266
    def add(self, key):
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
267
        """Similar to set.add(), start tracking this key.
268
        
269
        There is one small difference, which is that we return the object that
270
        is stored at the given location. (which is closer to the
271
        dict.setdefault() functionality.)
272
        """
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
273
        return self._add(key)
274
275
    cdef object _add(self, key):
4679.3.61 by John Arbash Meinel
Invert the logic.
276
        cdef PyObject **slot, *py_key
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
277
        cdef int added
4679.3.61 by John Arbash Meinel
Invert the logic.
278
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
279
        py_key = <PyObject *>key
280
        if (Py_TYPE(py_key).tp_richcompare == NULL
281
            or Py_TYPE(py_key).tp_hash == NULL):
282
            raise TypeError('Types added to SimpleSet must implement'
283
                            ' both tp_richcompare and tp_hash')
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
284
        added = 0
4679.3.63 by John Arbash Meinel
Implement resizing.
285
        # We need at least one empty slot
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
286
        assert self._used < self._mask
4679.3.61 by John Arbash Meinel
Invert the logic.
287
        slot = _lookup(self, key)
288
        if (slot[0] == NULL):
289
            Py_INCREF(py_key)
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
290
            self._fill = self._fill + 1
291
            self._used = self._used + 1
4679.3.61 by John Arbash Meinel
Invert the logic.
292
            slot[0] = py_key
4679.3.63 by John Arbash Meinel
Implement resizing.
293
            added = 1
4679.3.61 by John Arbash Meinel
Invert the logic.
294
        elif (slot[0] == _dummy):
295
            Py_INCREF(py_key)
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
296
            self._used = self._used + 1
4679.3.61 by John Arbash Meinel
Invert the logic.
297
            slot[0] = py_key
4679.3.63 by John Arbash Meinel
Implement resizing.
298
            added = 1
4679.3.61 by John Arbash Meinel
Invert the logic.
299
        # No else: clause. If _lookup returns a pointer to
300
        # a live object, then we already have a value at this location.
4679.3.63 by John Arbash Meinel
Implement resizing.
301
        retval = <object>(slot[0])
302
        # PySet and PyDict use a 2-3rds full algorithm, we'll follow suit
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
303
        if added and (self._fill * 3) >= ((self._mask + 1) * 2):
4679.3.63 by John Arbash Meinel
Implement resizing.
304
            # However, we always work for a load factor of 2:1
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
305
            self._resize(self._used * 2)
4679.3.63 by John Arbash Meinel
Implement resizing.
306
        # Even if we resized and ended up moving retval into a different slot,
307
        # it is still the value that is held at the slot equivalent to 'key',
308
        # so we can still return it
309
        return retval
4679.3.61 by John Arbash Meinel
Invert the logic.
310
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
311
    def discard(self, key):
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
312
        """Remove key from the set, whether it exists or not.
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
313
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
314
        :return: False if the item did not exist, True if it did
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
315
        """
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
316
        if self._discard(key):
317
            return True
318
        return False
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
319
320
    cdef int _discard(self, key) except -1:
4679.3.61 by John Arbash Meinel
Invert the logic.
321
        cdef PyObject **slot, *py_key
322
323
        slot = _lookup(self, key)
324
        if slot[0] == NULL or slot[0] == _dummy:
325
            return 0
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
326
        self._used = self._used - 1
4679.3.61 by John Arbash Meinel
Invert the logic.
327
        Py_DECREF(slot[0])
328
        slot[0] = _dummy
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
329
        # PySet uses the heuristic: If more than 1/5 are dummies, then resize
330
        #                           them away
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
331
        #   if ((so->_fill - so->_used) * 5 < so->mask)
4679.3.64 by John Arbash Meinel
Add functionality for shrinking the table.
332
        # However, we are planning on using this as an interning structure, in
333
        # which we will be putting a lot of objects. And we expect that large
334
        # groups of them are going to have the same lifetime.
335
        # Dummy entries hurt a little bit because they cause the lookup to keep
336
        # searching, but resizing is also rather expensive
337
        # For now, we'll just use their algorithm, but we may want to revisit
338
        # it
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
339
        if ((self._fill - self._used) * 5 > self._mask):
340
            self._resize(self._used * 2)
4679.3.61 by John Arbash Meinel
Invert the logic.
341
        return 1
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
342
4679.3.65 by John Arbash Meinel
Add __iter__ support.
343
    def __iter__(self):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
344
        return _SimpleSet_iterator(self)
345
346
347
cdef class _SimpleSet_iterator:
348
    """Iterator over the SimpleSet structure."""
4679.3.65 by John Arbash Meinel
Add __iter__ support.
349
350
    cdef Py_ssize_t pos
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
351
    cdef SimpleSet set
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
352
    cdef Py_ssize_t _used # track if things have been mutated while iterating
4679.3.65 by John Arbash Meinel
Add __iter__ support.
353
    cdef Py_ssize_t len # number of entries left
354
355
    def __init__(self, obj):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
356
        self.set = obj
4679.3.65 by John Arbash Meinel
Add __iter__ support.
357
        self.pos = 0
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
358
        self._used = self.set._used
359
        self.len = self.set._used
4679.3.65 by John Arbash Meinel
Add __iter__ support.
360
361
    def __iter__(self):
362
        return self
363
364
    def __next__(self):
365
        cdef Py_ssize_t mask, i
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
366
        cdef PyObject *key
4679.3.65 by John Arbash Meinel
Add __iter__ support.
367
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
368
        if self.set is None:
4679.3.65 by John Arbash Meinel
Add __iter__ support.
369
            raise StopIteration
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
370
        if self.set._used != self._used:
4679.3.65 by John Arbash Meinel
Add __iter__ support.
371
            # Force this exception to continue to be raised
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
372
            self._used = -1
4679.3.65 by John Arbash Meinel
Add __iter__ support.
373
            raise RuntimeError("Set size changed during iteration")
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
374
        if not SimpleSet_Next(self.set, &self.pos, &key):
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
375
            self.set = None
4679.3.65 by John Arbash Meinel
Add __iter__ support.
376
            raise StopIteration
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
377
        # we found something
378
        the_key = <object>key # INCREF
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
379
        self.len = self.len - 1
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
380
        return the_key
4679.3.65 by John Arbash Meinel
Add __iter__ support.
381
382
    def __length_hint__(self):
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
383
        if self.set is not None and self._used == self.set._used:
4679.3.65 by John Arbash Meinel
Add __iter__ support.
384
            return self.len
385
        return 0
386
    
387
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
388
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
389
cdef api SimpleSet SimpleSet_New():
390
    """Create a new SimpleSet object."""
391
    return SimpleSet()
4679.3.61 by John Arbash Meinel
Invert the logic.
392
393
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
394
cdef SimpleSet _check_self(object self):
4679.3.61 by John Arbash Meinel
Invert the logic.
395
    """Check that the parameter is not None.
396
397
    Pyrex/Cython will do type checking, but only to ensure that an object is
398
    either the right type or None. You can say "object foo not None" for pure
399
    python functions, but not for C functions.
400
    So this is just a helper for all the apis that need to do the check.
401
    """
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
402
    cdef SimpleSet true_self
4679.3.61 by John Arbash Meinel
Invert the logic.
403
    if self is None:
404
        raise TypeError('self must not be None')
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
405
    true_self = self
406
    return true_self
407
408
409
cdef PyObject **_lookup(SimpleSet self, object key) except NULL:
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
410
    """Find the slot where 'key' would fit.
411
412
    This is the same as a dicts 'lookup' function.
413
414
    :param key: An object we are looking up
415
    :param hash: The hash for key
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
416
    :return: The location in self.table where key should be put.
417
        location == NULL is an exception, but (*location) == NULL just
418
        indicates the slot is empty and can be used.
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
419
    """
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
420
    # This uses Quadratic Probing:
421
    #  http://en.wikipedia.org/wiki/Quadratic_probing
422
    # with c1 = c2 = 1/2
423
    # This leads to probe locations at:
424
    #  h0 = hash(k1)
425
    #  h1 = h0 + 1
426
    #  h2 = h0 + 3 = h1 + 1 + 1
427
    #  h3 = h0 + 6 = h2 + 1 + 2
428
    #  h4 = h0 + 10 = h2 + 1 + 3
429
    # Note that all of these are '& mask', but that is computed *after* the
430
    # offset.
431
    # This differs from the algorithm used by Set and Dict. Which, effectively,
432
    # use double-hashing, and a step size that starts large, but dwindles to
433
    # stepping one-by-one.
434
    # This gives more 'locality' in that if you have a collision at offset X,
435
    # the first fallback is X+1, which is fast to check. However, that means
436
    # that an object w/ hash X+1 will also check there, and then X+2 next.
437
    # However, for objects with differing hashes, their chains are different.
438
    # The former checks X, X+1, X+3, ... the latter checks X+1, X+2, X+4, ...
439
    # So different hashes diverge quickly.
440
    # A bigger problem is that we *only* ever use the lowest bits of the hash
441
    # So all integers (x + SIZE*N) will resolve into the same bucket, and all
442
    # use the same collision resolution. We may want to try to find a way to
443
    # incorporate the upper bits of the hash with quadratic probing. (For
444
    # example, X, X+1, X+3+some_upper_bits, X+6+more_upper_bits, etc.)
445
    cdef size_t i, n_lookup
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
446
    cdef Py_ssize_t mask
4679.3.61 by John Arbash Meinel
Invert the logic.
447
    cdef long key_hash
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
448
    cdef PyObject **table, **slot, *cur, **free_slot, *py_key
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
449
4739.2.1 by John Arbash Meinel
Stop using hash() because of bugs wrt pyrex 0.9.8.5
450
    py_key = <PyObject *>key
451
    # Note: avoid using hash(obj) because of a bug w/ pyrex 0.9.8.5 and 64-bit
452
    #       (it treats hash() as returning an 'int' rather than a 'long')
453
    key_hash = PyObject_Hash(py_key)
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
454
    i = <size_t>key_hash
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
455
    mask = self._mask
456
    table = self._table
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
457
    free_slot = NULL
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
458
    for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever
459
        slot = &table[i & mask]
460
        cur = slot[0]
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
461
        if cur == NULL:
462
            # Found a blank spot
463
            if free_slot != NULL:
464
                # Did we find an earlier _dummy entry?
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
465
                return free_slot
466
            else:
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
467
                return slot
468
        if cur == py_key:
469
            # Found an exact pointer to the key
470
            return slot
471
        if cur == _dummy:
472
            if free_slot == NULL:
473
                free_slot = slot
474
        elif _is_equal(py_key, key_hash, cur):
475
            # Both py_key and cur belong in this slot, return it
476
            return slot
4679.3.91 by John Arbash Meinel
Change the _lookup function to use Quadratic Probing.
477
        i = i + 1 + n_lookup
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
478
    raise AssertionError('should never get here')
479
480
4679.3.84 by John Arbash Meinel
Remove 2 more mallocs per line by using a fixed-width StaticTuple for reflists.
481
cdef api PyObject **_SimpleSet_Lookup(object self, object key) except NULL:
4679.3.61 by John Arbash Meinel
Invert the logic.
482
    """Find the slot where 'key' would fit.
483
484
    This is the same as a dicts 'lookup' function. This is a private
485
    api because mutating what you get without maintaing the other invariants
486
    is a 'bad thing'.
487
488
    :param key: An object we are looking up
489
    :param hash: The hash for key
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
490
    :return: The location in self._table where key should be put
4679.3.61 by John Arbash Meinel
Invert the logic.
491
        should never be NULL, but may reference a NULL (PyObject*)
492
    """
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
493
    return _lookup(_check_self(self), key)
4679.3.61 by John Arbash Meinel
Invert the logic.
494
495
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
496
cdef api object SimpleSet_Add(object self, object key):
497
    """Add a key to the SimpleSet (set).
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
498
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
499
    :param self: The SimpleSet to add the key to.
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
500
    :param key: The key to be added. If the key is already present,
501
        self will not be modified
502
    :return: The current key stored at the location defined by 'key'.
503
        This may be the same object, or it may be an equivalent object.
504
        (consider dict.setdefault(key, key))
505
    """
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
506
    return _check_self(self)._add(key)
4679.3.61 by John Arbash Meinel
Invert the logic.
507
    
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
508
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
509
cdef api int SimpleSet_Contains(object self, object key) except -1:
4679.3.61 by John Arbash Meinel
Invert the logic.
510
    """Is key present in self?"""
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
511
    return (key in _check_self(self))
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
512
513
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
514
cdef api int SimpleSet_Discard(object self, object key) except -1:
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
515
    """Remove the object referenced at location 'key'.
516
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
517
    :param self: The SimpleSet being modified
4679.3.60 by John Arbash Meinel
Start working on more of the C api for StaticTupleInterner.
518
    :param key: The key we are checking on
519
    :return: 1 if there was an object present, 0 if there was not, and -1 on
520
        error.
521
    """
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
522
    return _check_self(self)._discard(key)
523
524
525
cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL:
4679.3.61 by John Arbash Meinel
Invert the logic.
526
    """Get a pointer to the object present at location 'key'.
527
528
    This returns an object which is equal to key which was previously added to
529
    self. This returns a borrowed reference, as it may also return NULL if no
530
    value is present at that location.
531
532
    :param key: The value we are looking for
533
    :return: The object present at that location
534
    """
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
535
    return _check_self(self)._get(key)
4679.3.61 by John Arbash Meinel
Invert the logic.
536
537
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
538
cdef api Py_ssize_t SimpleSet_Size(object self) except -1:
4679.3.61 by John Arbash Meinel
Invert the logic.
539
    """Get the number of active entries in 'self'"""
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
540
    return _check_self(self)._used
4679.3.66 by John Arbash Meinel
Implement the C variant of __iter__
541
542
4932.1.2 by John Arbash Meinel
Clean up _simple_set.pyx, and handle 'nogil'.
543
cdef api int SimpleSet_Next(object self, Py_ssize_t *pos,
544
                            PyObject **key) except -1:
4679.3.76 by John Arbash Meinel
Rename StaticTupleInterner => SimpleSet.
545
    """Walk over items in a SimpleSet.
4679.3.66 by John Arbash Meinel
Implement the C variant of __iter__
546
547
    :param pos: should be initialized to 0 by the caller, and will be updated
548
        by this function
549
    :param key: Will return a borrowed reference to key
550
    :return: 0 if nothing left, 1 if we are returning a new value
551
    """
552
    cdef Py_ssize_t i, mask
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
553
    cdef SimpleSet true_self
4679.3.66 by John Arbash Meinel
Implement the C variant of __iter__
554
    cdef PyObject **table
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
555
    true_self = _check_self(self)
4679.3.66 by John Arbash Meinel
Implement the C variant of __iter__
556
    i = pos[0]
557
    if (i < 0):
558
        return 0
4679.3.81 by John Arbash Meinel
Fix up _simple_set_pyx.pyx to be compatible with pyrex again.
559
    mask = true_self._mask
560
    table= true_self._table
4679.3.66 by John Arbash Meinel
Implement the C variant of __iter__
561
    while (i <= mask and (table[i] == NULL or table[i] == _dummy)):
4679.3.94 by John Arbash Meinel
Python 2.5 and Pyrex 0.9.6.4 compatibility.
562
        i = i + 1
4679.3.66 by John Arbash Meinel
Implement the C variant of __iter__
563
    pos[0] = i + 1
564
    if (i > mask):
565
        return 0 # All done
566
    if (key != NULL):
567
        key[0] = table[i]
568
    return 1
569
4679.3.84 by John Arbash Meinel
Remove 2 more mallocs per line by using a fixed-width StaticTuple for reflists.
570
4932.1.2 by John Arbash Meinel
Clean up _simple_set.pyx, and handle 'nogil'.
571
cdef int SimpleSet_traverse(SimpleSet self, visitproc visit,
572
                            void *arg) except -1:
4679.3.84 by John Arbash Meinel
Remove 2 more mallocs per line by using a fixed-width StaticTuple for reflists.
573
    """This is an implementation of 'tp_traverse' that hits the whole table.
574
575
    Cython/Pyrex don't seem to let you define a tp_traverse, and they only
576
    define one for you if you have an 'object' attribute. Since they don't
577
    support C arrays of objects, we access the PyObject * directly.
578
    """
579
    cdef Py_ssize_t pos
580
    cdef PyObject *next_key
581
    cdef int ret
582
583
    pos = 0
584
    while SimpleSet_Next(self, &pos, &next_key):
585
        ret = visit(next_key, arg)
586
        if ret:
587
            return ret
4679.3.88 by John Arbash Meinel
Some review comments from Andrew.
588
    return 0
4679.3.84 by John Arbash Meinel
Remove 2 more mallocs per line by using a fixed-width StaticTuple for reflists.
589
590
# It is a little bit ugly to do this, but it works, and means that Meliae can
591
# dump the total memory consumed by all child objects.
592
(<PyTypeObject *>SimpleSet).tp_traverse = <traverseproc>SimpleSet_traverse