17
17
"""Definition of a class that is similar to Set with some small changes."""
19
from __future__ import absolute_import
21
from cpython.object cimport (
31
from cpython.mem cimport (
35
from cpython.ref cimport (
39
from libc.string cimport memset
19
cdef extern from "python-compat.h":
22
cdef extern from "Python.h":
23
ctypedef unsigned long size_t
24
ctypedef long (*hashfunc)(PyObject*) except -1
25
ctypedef object (*richcmpfunc)(PyObject *, PyObject *, int)
26
ctypedef int (*visitproc)(PyObject *, void *)
27
ctypedef int (*traverseproc)(PyObject *, visitproc, void *)
29
void Py_INCREF(PyObject *)
30
void Py_DECREF(PyObject *)
31
ctypedef struct PyTypeObject:
33
richcmpfunc tp_richcompare
34
traverseproc tp_traverse
36
PyTypeObject *Py_TYPE(PyObject *)
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
41
void *PyMem_Malloc(size_t nbytes)
42
void PyMem_Free(void *)
43
void memset(void *, int, size_t)
42
46
# Dummy is an object used to mark nodes that have been deleted. Since
55
59
_NotImplemented = NotImplemented
58
cdef int _is_equal(object this, long this_hash, object other) except -1:
62
cdef int _is_equal(PyObject *this, long this_hash, PyObject *other) except -1:
59
63
cdef long other_hash
61
67
other_hash = PyObject_Hash(other)
62
68
if other_hash != this_hash:
109
115
raise MemoryError()
110
116
memset(self._table, 0, n_bytes)
112
def __sizeof__(self):
113
# Note: Pyrex doesn't allow sizeof(class) so we re-implement it here.
119
# Note that we might get alignment, etc, wrong, but at least this is
120
# better than no estimate at all
121
# return sizeof(SimpleSet) + (self._mask + 1) * (sizeof(PyObject*))
122
return (sizeof(PyObject) + sizeof(void*)
123
+ 3*sizeof(Py_ssize_t) + sizeof(PyObject**)
124
+ (self._mask + 1) * sizeof(PyObject*))
126
118
def __dealloc__(self):
127
119
if self._table != NULL:
128
120
PyMem_Free(self._table)
197
189
cdef size_t i, n_lookup
198
190
cdef long the_hash
199
cdef PyObject **table
191
cdef PyObject **table, **slot
201
192
cdef Py_ssize_t mask
203
194
mask = self._mask
204
195
table = self._table
206
the_hash = PyObject_Hash(<object>key)
197
the_hash = PyObject_Hash(key)
208
199
for n_lookup from 0 <= n_lookup <= <size_t>mask: # Don't loop forever
209
200
slot = &table[i & mask]
229
220
:return: The new size of the internal table
231
222
cdef Py_ssize_t new_size, n_bytes, remaining
232
cdef PyObject **new_table
233
cdef PyObject **old_table
223
cdef PyObject **new_table, **old_table, **slot
236
225
new_size = DEFAULT_SIZE
237
226
while new_size <= min_used and new_size > 0:
274
263
PyMem_Free(old_table)
277
cpdef object add(self, key):
278
267
"""Similar to set.add(), start tracking this key.
280
269
There is one small difference, which is that we return the object that
281
270
is stored at the given location. (which is closer to the
282
271
dict.setdefault() functionality.)
287
if (Py_TYPE(key).tp_richcompare == NULL
288
or Py_TYPE(key).tp_hash == NULL):
273
return self._add(key)
275
cdef object _add(self, key):
276
cdef PyObject **slot, *py_key
279
py_key = <PyObject *>key
280
if (Py_TYPE(py_key).tp_richcompare == NULL
281
or Py_TYPE(py_key).tp_hash == NULL):
289
282
raise TypeError('Types added to SimpleSet must implement'
290
283
' both tp_richcompare and tp_hash')
293
286
assert self._used < self._mask
294
287
slot = _lookup(self, key)
295
288
if (slot[0] == NULL):
297
290
self._fill = self._fill + 1
298
291
self._used = self._used + 1
299
slot[0] = <PyObject *>key
301
294
elif (slot[0] == _dummy):
303
296
self._used = self._used + 1
304
slot[0] = <PyObject *>key
306
299
# No else: clause. If _lookup returns a pointer to
307
300
# a live object, then we already have a value at this location.
315
308
# so we can still return it
318
cpdef bint discard(self, key) except -1:
311
def discard(self, key):
319
312
"""Remove key from the set, whether it exists or not.
321
314
:return: False if the item did not exist, True if it did
316
if self._discard(key):
320
cdef int _discard(self, key) except -1:
321
cdef PyObject **slot, *py_key
325
323
slot = _lookup(self, key)
326
324
if slot[0] == NULL or slot[0] == _dummy:
328
326
self._used = self._used - 1
329
Py_DECREF(<object>slot[0])
331
329
# PySet uses the heuristic: If more than 1/5 are dummies, then resize
446
445
cdef size_t i, n_lookup
447
446
cdef Py_ssize_t mask
448
447
cdef long key_hash
449
cdef PyObject **table
452
cdef PyObject **free_slot
448
cdef PyObject **table, **slot, *cur, **free_slot, *py_key
454
key_hash = PyObject_Hash(key)
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)
455
454
i = <size_t>key_hash
456
455
mask = self._mask
457
456
table = self._table
469
if cur == <PyObject *>key:
470
469
# Found an exact pointer to the key
472
471
if cur == _dummy:
473
472
if free_slot == NULL:
475
elif _is_equal(key, key_hash, <object>cur):
474
elif _is_equal(py_key, key_hash, cur):
476
475
# Both py_key and cur belong in this slot, return it
478
477
i = i + 1 + n_lookup
504
503
This may be the same object, or it may be an equivalent object.
505
504
(consider dict.setdefault(key, key))
507
return _check_self(self).add(key)
506
return _check_self(self)._add(key)
510
509
cdef api int SimpleSet_Contains(object self, object key) except -1:
511
510
"""Is key present in self?"""
520
519
:return: 1 if there was an object present, 0 if there was not, and -1 on
523
return _check_self(self).discard(key)
522
return _check_self(self)._discard(key)
526
525
cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL: