155
156
# assert key == value
159
cdef int _insert_clean(self, PyObject *key) except -1:
160
"""Insert a key into self.table.
162
This is only meant to be used during times like '_resize',
163
as it makes a lot of assuptions about keys not already being present,
164
and there being no dummy entries.
166
cdef size_t i, perturb, mask
168
cdef PyObject **table, **entry
173
the_hash = Py_TYPE(key).tp_hash(key)
177
# Because we know that we made all items unique before, we can just
178
# iterate as long as the target location is not empty, we don't have to
179
# do any comparison, etc.
180
while entry[0] != NULL:
181
i = (i << 2) + i + perturb + 1
182
entry = &table[i & mask]
183
perturb >>= PERTURB_SHIFT
188
cpdef Py_ssize_t _resize(self, Py_ssize_t min_used) except -1:
189
"""Resize the internal table.
191
The final table will be big enough to hold at least min_used entries.
192
We will copy the data from the existing table over, leaving out dummy
195
:return: The new size of the internal table
197
cdef Py_ssize_t new_size, n_bytes, remaining
198
cdef PyObject **new_table, **old_table, **entry
200
new_size = DEFAULT_SIZE
201
while new_size <= min_used and new_size > 0:
202
new_size = new_size << 1
203
# We rolled over our signed size field
206
if new_size == (self.mask + 1):
210
# if new_size < self.used:
211
# raise RuntimeError('cannot shrink StaticTupleInterner to something'
212
# ' smaller than the number of used slots.')
213
n_bytes = sizeof(PyObject*) * new_size;
214
new_table = <PyObject **>PyMem_Malloc(n_bytes)
215
if new_table == NULL:
218
old_table = self.table
219
self.table = new_table
220
memset(self.table, 0, n_bytes)
221
self.mask = new_size - 1
223
remaining = self.fill
226
# Moving everything to the other table is refcount neutral, so we don't
230
if entry[0] == NULL: # unused slot
232
elif entry[0] == _dummy: # dummy slot
236
self._insert_clean(entry[0])
238
PyMem_Free(old_table)
158
241
cpdef object add(self, key):
159
242
"""Similar to set.add(), start tracking this key.
163
246
dict.setdefault() functionality.)
165
248
cdef PyObject **slot, *py_key
251
# We need at least one empty slot
252
assert self.used < self.mask
167
253
slot = _lookup(self, key)
168
254
py_key = <PyObject *>key
169
255
if (slot[0] == NULL):
174
261
elif (slot[0] == _dummy):
175
262
Py_INCREF(py_key)
178
266
# No else: clause. If _lookup returns a pointer to
179
267
# a live object, then we already have a value at this location.
180
return <object>(slot[0])
268
retval = <object>(slot[0])
269
# PySet and PyDict use a 2-3rds full algorithm, we'll follow suit
270
if added and (self.fill * 3) >= ((self.mask + 1) * 2):
271
# However, we always work for a load factor of 2:1
272
self._resize(self.used * 2)
273
# Even if we resized and ended up moving retval into a different slot,
274
# it is still the value that is held at the slot equivalent to 'key',
275
# so we can still return it
182
278
cpdef int discard(self, key) except -1:
183
279
"""Remove key from the dict, whether it exists or not.