86
86
cdef Py_ssize_t size, n_bytes
88
88
size = DEFAULT_SIZE
92
92
n_bytes = sizeof(PyObject*) * size;
93
self.table = <PyObject **>PyMem_Malloc(n_bytes)
94
if self.table == NULL:
93
self._table = <PyObject **>PyMem_Malloc(n_bytes)
94
if self._table == NULL:
95
95
raise MemoryError()
96
memset(self.table, 0, n_bytes)
96
memset(self._table, 0, n_bytes)
98
98
def __dealloc__(self):
99
if self.table != NULL:
100
PyMem_Free(self.table)
99
if self._table != NULL:
100
PyMem_Free(self._table)
103
115
def __len__(self):
106
118
def _test_lookup(self, key):
107
119
cdef PyObject **slot
169
181
entry = &table[i & mask]
170
182
perturb >>= PERTURB_SHIFT
175
cpdef Py_ssize_t _resize(self, Py_ssize_t min_used) except -1:
187
def _py_resize(self, min_used):
188
"""Do not use this directly, it is only exposed for testing."""
189
return self._resize(min_used)
191
cdef Py_ssize_t _resize(self, Py_ssize_t min_used) except -1:
176
192
"""Resize the internal table.
178
194
The final table will be big enough to hold at least min_used entries.
190
206
# We rolled over our signed size field
191
207
if new_size <= 0:
192
208
raise MemoryError()
193
# Even if min_used == self.mask + 1, and we aren't changing the actual
209
# Even if min_used == self._mask + 1, and we aren't changing the actual
194
210
# size, we will still run the algorithm so that dummy entries are
196
212
# TODO: Test this
197
# if new_size < self.used:
213
# if new_size < self._used:
198
214
# raise RuntimeError('cannot shrink SimpleSet to something'
199
215
# ' smaller than the number of used slots.')
200
216
n_bytes = sizeof(PyObject*) * new_size;
202
218
if new_table == NULL:
203
219
raise MemoryError()
205
old_table = self.table
206
self.table = new_table
207
memset(self.table, 0, n_bytes)
208
self.mask = new_size - 1
210
remaining = self.fill
221
old_table = self._table
222
self._table = new_table
223
memset(self._table, 0, n_bytes)
224
self._mask = new_size - 1
226
remaining = self._fill
213
229
# Moving everything to the other table is refcount neutral, so we don't
214
230
# worry about it.
225
241
PyMem_Free(old_table)
228
cpdef object add(self, key):
229
245
"""Similar to set.add(), start tracking this key.
231
247
There is one small difference, which is that we return the object that
232
248
is stored at the given location. (which is closer to the
233
249
dict.setdefault() functionality.)
251
return self._add(key)
253
cdef object _add(self, key):
235
254
cdef PyObject **slot, *py_key
238
258
# We need at least one empty slot
239
assert self.used < self.mask
259
assert self._used < self._mask
240
260
slot = _lookup(self, key)
241
261
py_key = <PyObject *>key
242
262
if (slot[0] == NULL):
243
263
Py_INCREF(py_key)
248
268
elif (slot[0] == _dummy):
249
269
Py_INCREF(py_key)
253
273
# No else: clause. If _lookup returns a pointer to
254
274
# a live object, then we already have a value at this location.
255
275
retval = <object>(slot[0])
256
276
# PySet and PyDict use a 2-3rds full algorithm, we'll follow suit
257
if added and (self.fill * 3) >= ((self.mask + 1) * 2):
277
if added and (self._fill * 3) >= ((self._mask + 1) * 2):
258
278
# However, we always work for a load factor of 2:1
259
self._resize(self.used * 2)
279
self._resize(self._used * 2)
260
280
# Even if we resized and ended up moving retval into a different slot,
261
281
# it is still the value that is held at the slot equivalent to 'key',
262
282
# so we can still return it
265
cpdef int discard(self, key) except -1:
285
def discard(self, key):
266
286
"""Remove key from the dict, whether it exists or not.
268
288
:return: 0 if the item did not exist, 1 if it did
290
return self._discard(key)
292
cdef int _discard(self, key) except -1:
270
293
cdef PyObject **slot, *py_key
272
295
slot = _lookup(self, key)
273
296
if slot[0] == NULL or slot[0] == _dummy:
276
299
Py_DECREF(slot[0])
278
301
# PySet uses the heuristic: If more than 1/5 are dummies, then resize
280
# if ((so->fill - so->used) * 5 < so->mask)
303
# if ((so->_fill - so->_used) * 5 < so->mask)
281
304
# However, we are planning on using this as an interning structure, in
282
305
# which we will be putting a lot of objects. And we expect that large
283
306
# groups of them are going to have the same lifetime.
285
308
# searching, but resizing is also rather expensive
286
309
# For now, we'll just use their algorithm, but we may want to revisit
288
if ((self.fill - self.used) * 5 > self.mask):
289
self._resize(self.used * 2)
311
if ((self._fill - self._used) * 5 > self._mask):
312
self._resize(self._used * 2)
292
315
def __delitem__(self, key):
309
332
cdef Py_ssize_t pos
310
333
cdef SimpleSet set
311
cdef Py_ssize_t used # track if things have been mutated while iterating
334
cdef Py_ssize_t _used # track if things have been mutated while iterating
312
335
cdef Py_ssize_t len # number of entries left
314
337
def __init__(self, obj):
317
self.used = self.set.used
318
self.len = self.set.used
340
self._used = self.set._used
341
self.len = self.set._used
320
343
def __iter__(self):
366
389
python functions, but not for C functions.
367
390
So this is just a helper for all the apis that need to do the check.
392
cdef SimpleSet true_self
370
394
raise TypeError('self must not be None')
373
cdef inline PyObject **_lookup(SimpleSet self,
374
object key) except NULL:
399
cdef PyObject **_lookup(SimpleSet self, object key) except NULL:
375
400
"""Find the slot where 'key' would fit.
377
402
This is the same as a dicts 'lookup' function.
437
462
:param key: An object we are looking up
438
463
:param hash: The hash for key
439
:return: The location in self.table where key should be put
464
:return: The location in self._table where key should be put
440
465
should never be NULL, but may reference a NULL (PyObject*)
442
_check_self_not_none(self)
443
return _lookup(self, key)
467
return _lookup(_check_self(self), key)
446
470
cdef api object SimpleSet_Add(object self, object key):
453
477
This may be the same object, or it may be an equivalent object.
454
478
(consider dict.setdefault(key, key))
456
cdef SimpleSet true_self
457
_check_self_not_none(self)
459
return true_self.add(key)
480
return _check_self(self)._add(key)
462
cdef api bint SimpleSet_Contains(object self, object key) except -1:
483
cdef api int SimpleSet_Contains(object self, object key) except -1:
463
484
"""Is key present in self?"""
464
cdef SimpleSet true_self
465
_check_self_not_none(self)
467
return key in true_self
485
return (key in _check_self(self))
470
488
cdef api int SimpleSet_Discard(object self, object key) except -1:
475
493
:return: 1 if there was an object present, 0 if there was not, and -1 on
478
cdef SimpleSet true_self
479
_check_self_not_none(self)
481
return true_self.discard(key)
484
cdef api PyObject *SimpleSet_Get(SimpleSet self,
485
object key) except? NULL:
496
return _check_self(self)._discard(key)
499
cdef api PyObject *SimpleSet_Get(SimpleSet self, object key) except? NULL:
486
500
"""Get a pointer to the object present at location 'key'.
488
502
This returns an object which is equal to key which was previously added to
492
506
:param key: The value we are looking for
493
507
:return: The object present at that location
495
cdef SimpleSet true_self
496
_check_self_not_none(self)
498
return true_self._get(key)
509
return _check_self(self)._get(key)
501
512
cdef api Py_ssize_t SimpleSet_Size(object self) except -1:
502
513
"""Get the number of active entries in 'self'"""
503
cdef SimpleSet true_self = self
504
_check_self_not_none(self)
505
return true_self.used
514
return _check_self(self)._used
508
517
# TODO: this should probably have direct tests, since it isn't used by __iter__