Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/structure/coerce_dict.pyx
8814 views
1
#*****************************************************************************
2
# Copyright (C) 2007 Robert Bradshaw <[email protected]>
3
# 2012 Simon King <[email protected]>
4
# 2013 Nils Bruin <[email protected]>
5
#
6
# Distributed under the terms of the GNU General Public License (GPL)
7
#
8
# http://www.gnu.org/licenses/
9
#*****************************************************************************
10
"""
11
Containers for storing coercion data
12
13
This module provides :class:`TripleDict` and :class:`MonoDict`. These are
14
structures similar to :class:`~weakref.WeakKeyDictionary` in Python's weakref
15
module, and are optimized for lookup speed. The keys for :class:`TripleDict`
16
consist of triples (k1,k2,k3) and are looked up by identity rather than
17
equality. The keys are stored by weakrefs if possible. If any one of the
18
components k1, k2, k3 gets garbage collected, then the entry is removed from
19
the :class:`TripleDict`.
20
21
Key components that do not allow for weakrefs are stored via a normal
22
refcounted reference. That means that any entry stored using a triple
23
(k1,k2,k3) so that none of the k1,k2,k3 allows a weak reference behaves
24
as an entry in a normal dictionary: Its existence in :class:`TripleDict`
25
prevents it from being garbage collected.
26
27
That container currently is used to store coercion and conversion maps between
28
two parents (:trac:`715`) and to store homsets of pairs of objects of a
29
category (:trac:`11521`). In both cases, it is essential that the parent
30
structures remain garbage collectable, it is essential that the data access is
31
faster than with a usual :class:`~weakref.WeakKeyDictionary`, and we enforce
32
the "unique parent condition" in Sage (parent structures should be identical
33
if they are equal).
34
35
:class:`MonoDict` behaves similarly, but it takes a single item as a key. It
36
is used for caching the parents which allow a coercion map into a fixed other
37
parent (:trac:`12313`).
38
39
By :trac:`14159`, :class:`MonoDict` and :class:`TripleDict` can be optionally
40
used with weak references on the values.
41
42
"""
43
from cpython.list cimport *
44
from cpython.mem cimport *
45
from cpython.string cimport PyString_FromString
46
from cpython cimport Py_XINCREF, Py_XDECREF
47
from libc.string cimport memset
48
from weakref import KeyedRef, ref
49
#to avoid having to go and look in the module dictionary every time, we put
50
#a pointer to this class in here. No monkeypatching! or if you want to, perhaps
51
#cdef public this so that it can still be accessed.
52
#furthermore, by declaring this as "type", cython will compile
53
#isinstance(O,fixed_KeyedRef) to PyObject_CheckType(O, fixed_KeyedRef)
54
#which is considerable faster than PyObject_IsInstance(O, fixed_KeyedRef)
55
cdef type fixed_KeyedRef = KeyedRef
56
cdef type fixed_ref = ref
57
58
#we use a capsule to wrap our void* in the callback parameter.
59
#no assumptions on whether a void* fits in a Py_ssize_t anymore!
60
from cpython.pycapsule cimport PyCapsule_New, PyCapsule_GetPointer
61
62
cdef extern from "Python.h":
63
PyObject* PyWeakref_GetObject(object r)
64
PyObject* Py_None
65
int PyList_SetItem(object list, Py_ssize_t index,PyObject * item) except -1
66
67
ctypedef int (*visitproc)(PyObject* ob, void* arg)
68
ctypedef struct PyTypeObject:
69
void * tp_traverse
70
void * tp_clear
71
72
#this serves no purpose here anymore. Perhaps elsewhere?
73
cpdef inline Py_ssize_t signed_id(x):
74
"""
75
A function like Python's :func:`id` returning *signed* integers,
76
which are guaranteed to fit in a ``Py_ssize_t``.
77
78
Theoretically, there is no guarantee that two different Python
79
objects have different ``signed_id()`` values. However, under the
80
mild assumption that a C pointer fits in a ``Py_ssize_t``, this
81
is guaranteed.
82
83
TESTS::
84
85
sage: a = 1.23e45 # some object
86
sage: from sage.structure.coerce_dict import signed_id
87
sage: s = signed_id(a)
88
sage: id(a) == s or id(a) == s + 2**32 or id(a) == s + 2**64
89
True
90
sage: signed_id(a) <= sys.maxsize
91
True
92
"""
93
return <Py_ssize_t><void *>(x)
94
95
#it's important that this is not an interned string: this object
96
#must be a unique sentinel. We could reuse the "dummy" sentinel
97
#that is defined in python's dictobject.c
98
99
cdef object dummy_object = PyString_FromString("dummy")
100
cdef PyObject* dummy = <PyObject*><void *>dummy_object
101
102
cdef struct mono_cell:
103
void* key_id
104
PyObject* key_weakref
105
PyObject* value
106
107
cdef object extract_mono_cell(mono_cell *cell):
108
#takes the refcounted components from a mono_cell
109
#and puts them in a list and returns it.
110
#The mono_cell itself is marked as "freed".
111
#The refcounts originally accounting for the
112
#presence in the mono_cell now account for the presence
113
#in the returned list.
114
#
115
#the returned result is only used to throw away:
116
#an advantage is that the containing list participates
117
#in CPython's trashcan, which prevents stack overflow
118
#on large dereffing cascades.
119
#
120
#a slight disadvantage is that this routine needs to
121
#allocate a list (mainly just to be thrown away)
122
if cell.key_id != NULL and cell.key_id != dummy :
123
L=PyList_New(2)
124
PyList_SetItem(L,0,cell.key_weakref)
125
PyList_SetItem(L,1,cell.value)
126
cell.key_id=dummy
127
cell.key_weakref=NULL
128
cell.value=NULL
129
return L
130
else:
131
raise RuntimeError("unused mono_cell")
132
133
cdef struct triple_cell:
134
void* key_id1
135
void* key_id2
136
void* key_id3
137
PyObject* key_weakref1
138
PyObject* key_weakref2
139
PyObject* key_weakref3
140
PyObject* value
141
142
cdef object extract_triple_cell(triple_cell *cell):
143
#see extract_mono_cell for the rationale
144
#behind this routine.
145
if cell.key_id1 != NULL and cell.key_id1 != dummy :
146
L=PyList_New(4)
147
PyList_SetItem(L,0,cell.key_weakref1)
148
PyList_SetItem(L,1,cell.key_weakref2)
149
PyList_SetItem(L,2,cell.key_weakref3)
150
PyList_SetItem(L,3,cell.value)
151
cell.key_id1=dummy
152
cell.key_id2=NULL
153
cell.key_id3=NULL
154
cell.key_weakref1=NULL
155
cell.key_weakref2=NULL
156
cell.key_weakref3=NULL
157
cell.value=NULL
158
return L
159
else:
160
raise RuntimeError("unused triple_cell")
161
162
cdef class MonoDictEraser:
163
"""
164
Erase items from a :class:`MonoDict` when a weak reference becomes
165
invalid.
166
167
This is of internal use only. Instances of this class will be passed as a
168
callback function when creating a weak reference.
169
170
EXAMPLES::
171
172
sage: from sage.structure.coerce_dict import MonoDict
173
sage: class A: pass
174
sage: a = A()
175
sage: M = MonoDict()
176
sage: M[a] = 1
177
sage: len(M)
178
1
179
sage: del a
180
sage: import gc
181
sage: n = gc.collect()
182
sage: len(M) # indirect doctest
183
0
184
185
AUTHOR:
186
187
- Simon King (2012-01)
188
- Nils Bruin (2013-11)
189
"""
190
cdef D
191
def __init__(self, D):
192
"""
193
INPUT:
194
195
A :class:`MonoDict`.
196
197
EXAMPLES::
198
199
sage: k = set([1])
200
sage: D = sage.structure.coerce_dict.MonoDict([(k,1)])
201
sage: len(D)
202
1
203
sage: del k
204
sage: len(D) # indirect doctest
205
0
206
"""
207
self.D = fixed_ref(D)
208
209
def __call__(self, r):
210
"""
211
INPUT:
212
213
A weak reference with key.
214
215
For internal use only.
216
217
EXAMPLES::
218
219
sage: k = set([1])
220
sage: D = sage.structure.coerce_dict.MonoDict([(k,1)])
221
sage: len(D)
222
1
223
sage: del k
224
sage: len(D) # indirect doctest
225
0
226
"""
227
cdef MonoDict md = <object> PyWeakref_GetObject(self.D)
228
if md is None:
229
return
230
if md.table == NULL:
231
return
232
cdef mono_cell* cursor = md.lookup(<PyObject *>PyCapsule_GetPointer(r.key,NULL))
233
if (cursor.key_id != NULL and cursor.key_id != dummy):
234
if (cursor.key_weakref == <PyObject*><void*>r or cursor.value == <PyObject*><void*>r):
235
L=extract_mono_cell(cursor)
236
md.used -= 1
237
else:
238
#this should probably never happen
239
raise RuntimeError("eraser: key match but no weakref match")
240
241
cdef class MonoDict:
242
"""
243
This is a hashtable specifically designed for (read) speed in
244
the coercion model.
245
246
It differs from a python WeakKeyDictionary in the following important ways:
247
248
- Comparison is done using the 'is' rather than '==' operator.
249
- Only weak references to the keys are stored if at all possible.
250
Keys that do not allow for weak references are stored with a normal
251
refcounted reference.
252
- The callback of the weak references is safe against recursion, see below.
253
254
There are special cdef set/get methods for faster access.
255
It is bare-bones in the sense that not all dictionary methods are
256
implemented.
257
258
IMPLEMENTATION:
259
260
It is implemented as a hash table with open addressing, similar to python's
261
dict.
262
263
If ki supports weak references then ri is a weak reference to ki with a
264
callback to remove the entry from the dictionary if ki gets garbage
265
collected. If ki is does not support weak references then ri is identical to ki.
266
In the latter case the presence of the key in the dictionary prevents it from
267
being garbage collected.
268
269
INPUT:
270
271
- ``size`` -- unused parameter, present for backward compatibility.
272
- ``data`` -- optional iterable defining initial data.
273
- ``threshold`` -- unused parameter, present for backward compatibility.
274
- ``weak_values`` -- optional bool (default False). If it is true, weak references
275
to the values in this dictionary will be used, when possible.
276
277
EXAMPLES::
278
279
sage: from sage.structure.coerce_dict import MonoDict
280
sage: L = MonoDict()
281
sage: a = 'a'; b = 'ab'; c = -15
282
sage: L[a] = 1
283
sage: L[b] = 2
284
sage: L[c] = 3
285
286
The key is expected to be a unique object. Hence, the item stored for ``c``
287
can not be obtained by providing another equal number::
288
289
sage: L[a]
290
1
291
sage: L[b]
292
2
293
sage: L[c]
294
3
295
sage: L[-15]
296
Traceback (most recent call last):
297
...
298
KeyError: -15
299
300
Not all features of Python dictionaries are available, but iteration over
301
the dictionary items is possible::
302
303
sage: # for some reason the following failed in "make ptest"
304
sage: # on some installations, see #12313 for details
305
sage: sorted(L.iteritems()) # random layout
306
[(-15, 3), ('a', 1), ('ab', 2)]
307
sage: # the following seems to be more consistent
308
sage: set(L.iteritems())
309
set([('a', 1), ('ab', 2), (-15, 3)])
310
sage: del L[c]
311
sage: sorted(L.iteritems())
312
[('a', 1), ('ab', 2)]
313
sage: len(L)
314
2
315
sage: for i in range(1000):
316
... L[i] = i
317
sage: len(L)
318
1002
319
sage: L['a']
320
1
321
sage: L['c']
322
Traceback (most recent call last):
323
...
324
KeyError: 'c'
325
326
Note that this kind of dictionary is also used for caching actions
327
and coerce maps. In previous versions of Sage, the cache was by
328
strong references and resulted in a memory leak in the following
329
example. However, this leak was fixed by :trac:`715`, using
330
weak references::
331
332
sage: K = GF(1<<55,'t')
333
sage: for i in range(50):
334
... a = K.random_element()
335
... E = EllipticCurve(j=a)
336
... P = E.random_point()
337
... Q = 2*P
338
sage: import gc
339
sage: n = gc.collect()
340
sage: from sage.schemes.elliptic_curves.ell_finite_field import EllipticCurve_finite_field
341
sage: LE = [x for x in gc.get_objects() if isinstance(x, EllipticCurve_finite_field)]
342
sage: len(LE) # indirect doctest
343
1
344
345
TESTS:
346
347
Here, we demonstrate the use of weak values.
348
::
349
350
sage: M = MonoDict(13)
351
sage: MW = MonoDict(13, weak_values=True)
352
sage: class Foo: pass
353
sage: a = Foo()
354
sage: b = Foo()
355
sage: k = 1
356
sage: M[k] = a
357
sage: MW[k] = b
358
sage: M[k] is a
359
True
360
sage: MW[k] is b
361
True
362
sage: k in M
363
True
364
sage: k in MW
365
True
366
367
While ``M`` uses a strong reference to ``a``, ``MW`` uses a *weak*
368
reference to ``b``, and after deleting ``b``, the corresponding item of
369
``MW`` will be removed during the next garbage collection::
370
371
sage: import gc
372
sage: del a,b
373
sage: _ = gc.collect()
374
sage: k in M
375
True
376
sage: k in MW
377
False
378
sage: len(MW)
379
0
380
sage: len(M)
381
1
382
383
Note that ``MW`` also accepts values that do not allow for weak references::
384
385
sage: MW[k] = int(5)
386
sage: MW[k]
387
5
388
389
The following demonstrates that :class:`MonoDict` is safer than
390
:class:`~weakref.WeakKeyDictionary` against recursions created by nested
391
callbacks; compare :trac:`15069` (the mechanism used now is different, though)::
392
393
sage: M = MonoDict(11)
394
sage: class A: pass
395
sage: a = A()
396
sage: prev = a
397
sage: for i in range(1000):
398
....: newA = A()
399
....: M[prev] = newA
400
....: prev = newA
401
sage: len(M)
402
1000
403
sage: del a
404
sage: len(M)
405
0
406
407
The corresponding example with a Python :class:`weakref.WeakKeyDictionary`
408
would result in a too deep recursion during deletion of the dictionary
409
items::
410
411
sage: import weakref
412
sage: M = weakref.WeakKeyDictionary()
413
sage: a = A()
414
sage: prev = a
415
sage: for i in range(1000):
416
....: newA = A()
417
....: M[prev] = newA
418
....: prev = newA
419
sage: len(M)
420
1000
421
sage: del a
422
Exception RuntimeError: 'maximum recursion depth exceeded while calling a Python object' in <function remove at ...> ignored
423
sage: len(M)>0
424
True
425
426
Check that also in the presence of circular references, :class:`MonoDict`
427
gets properly collected::
428
429
sage: import gc
430
sage: def count_type(T):
431
....: return len([c for c in gc.get_objects() if isinstance(c,T)])
432
sage: _=gc.collect()
433
sage: N=count_type(MonoDict)
434
sage: for i in range(100):
435
....: V = [ MonoDict(11,{"id":j+100*i}) for j in range(100)]
436
....: n= len(V)
437
....: for i in range(n): V[i][V[(i+1)%n]]=(i+1)%n
438
....: del V
439
....: _=gc.collect()
440
....: assert count_type(MonoDict) == N
441
sage: count_type(MonoDict) == N
442
True
443
444
AUTHORS:
445
446
- Simon King (2012-01)
447
- Nils Bruin (2012-08)
448
- Simon King (2013-02)
449
- Nils Bruin (2013-11)
450
"""
451
cdef mono_cell* lookup(self, PyObject* key):
452
"""
453
This routine is used for all cases where a (potential) spot for
454
a key is looked up. The returned value is a pointer into the dictionary
455
store that either contains an entry with the requested key or a free spot
456
where an entry for that key should go.
457
"""
458
cdef size_t perturb
459
cdef size_t mask = self.mask
460
cdef mono_cell* table = self.table
461
cdef mono_cell* cursor
462
cdef mono_cell* first_freed = NULL
463
464
#We seed our starting probe using the higher bits of the key as well.
465
#Our hash is a memory location, so the bottom bits are likely 0.
466
467
cdef size_t i = ((<size_t>key)>>8+(<size_t>key))
468
if key == NULL or key == dummy:
469
print("Request to look up invalid key")
470
cursor = &(table[i & mask])
471
# if the cell was never used, the entry wasn't there
472
perturb = (<size_t>key)>>3
473
474
#the probing algorithm is heavily inspired by python's own dict.
475
#there is always at least one NULL entry in the store, and the probe
476
#sequence eventually covers the entire store (see python's dictobject.c),
477
#so the loop below does terminate. Since this loop executes only
478
#straightforward C, we know the table will not change.
479
480
while (cursor.key_id != key):
481
if cursor.key_id == NULL:
482
return first_freed if (first_freed != NULL) else cursor
483
if first_freed == NULL and cursor.key_id == dummy:
484
first_freed = cursor
485
i = 5*i + perturb +1
486
cursor = &(table[i & mask])
487
perturb = perturb >> 5
488
return cursor
489
490
cdef int resize(self) except -1:
491
"""
492
Resize dictionary. That can also mean shrink! Size is always a power of 2.
493
"""
494
cdef mono_cell* old_table=self.table
495
cdef size_t old_mask = self.mask
496
cdef size_t newsize = 8
497
cdef size_t minsize = 2*self.used
498
cdef size_t i
499
cdef mono_cell* cursor
500
cdef mono_cell* entry
501
while newsize < minsize:
502
newsize = newsize<<1
503
cdef mono_cell* table = <mono_cell*> PyMem_Malloc((newsize)*sizeof(mono_cell))
504
if table == NULL:
505
raise MemoryError()
506
memset(table,0,(newsize)*sizeof(mono_cell))
507
508
#we're done with memory activity. We can move the new (empty) table into place:
509
510
self.table = table
511
self.mask = newsize-1
512
self.used = 0
513
self.fill = 0
514
515
#and move all entries over. We're not changing any refcounts here, so this is a very
516
#tight loop that doesn't need to worry about tables changing.
517
518
for i in range(old_mask+1):
519
entry = &(old_table[i])
520
if entry.key_id != NULL and entry.key_id != dummy:
521
cursor=self.lookup(<PyObject*>(entry.key_id))
522
assert cursor.key_id == NULL
523
cursor[0]=entry[0]
524
self.used +=1
525
self.fill +=1
526
PyMem_Free(old_table)
527
return 0
528
529
def __init__(self, size=11, data=None, threshold=0.7, weak_values=False):
530
"""
531
Create a special dict using singletons for keys.
532
533
EXAMPLES::
534
535
sage: from sage.structure.coerce_dict import MonoDict
536
sage: L = MonoDict(31)
537
sage: a = 'a'
538
sage: L[a] = 1
539
sage: L[a]
540
1
541
sage: L = MonoDict({a: 1})
542
sage: L[a]
543
1
544
"""
545
#if only one argument is supplied and it's iterable, use it for data rather than
546
#for size. This way we're compatible with the old calling sequence (an integer is
547
#just ignored) and we can also use the more usual construction.
548
if data is None:
549
try:
550
data=size.iteritems()
551
except AttributeError:
552
try:
553
data=iter(size)
554
except TypeError:
555
pass
556
else:
557
try:
558
data=data.iteritems()
559
except AttributeError:
560
pass
561
if self.table != NULL:
562
raise RuntimeError("table already initialized. Called __init__ second time?")
563
cdef minsize = 8
564
cdef size_t newsize = 1<<3
565
while newsize < minsize:
566
newsize = newsize <<1
567
self.mask = newsize - 1
568
cdef mono_cell* table = <mono_cell*> PyMem_Malloc(newsize*sizeof(mono_cell))
569
if table == NULL:
570
raise MemoryError()
571
memset(table,0,newsize*sizeof(mono_cell))
572
self.table = table
573
self.used = 0
574
self.fill = 0
575
self.eraser = MonoDictEraser(self)
576
self.weak_values = weak_values
577
if data:
578
for k,v in data:
579
self.set(k,v)
580
581
def __dealloc__(self):
582
"""
583
Ensure that the memory allocated by a MonoDict is properly freed.
584
"""
585
#is this required or does this get called anyway if we set tp_clear properly?
586
#at least it's safe, since MonoDict_clear checks if it has already run.
587
MonoDict_clear(self)
588
589
def __len__(self):
590
"""
591
The number of items in self.
592
EXAMPLES::
593
594
sage: from sage.structure.coerce_dict import MonoDict
595
sage: L = MonoDict(37)
596
sage: a = 'a'; b = 'b'; c = 'c'
597
sage: L[a] = 1
598
sage: L[a] = -1 # re-assign
599
sage: L[b] = 1
600
sage: L[c] = None
601
sage: len(L)
602
3
603
"""
604
return self.used
605
606
def __contains__(self, k):
607
"""
608
Test if the dictionary contains a given key.
609
610
EXAMPLES::
611
612
sage: from sage.structure.coerce_dict import MonoDict
613
sage: L = MonoDict(31)
614
sage: a = 'a'; b = 'ab'; c = 15
615
sage: L[a] = 1
616
sage: L[b] = 2
617
sage: L[c] = 3
618
sage: c in L # indirect doctest
619
True
620
621
The keys are compared by identity, not by equality. Hence, we have::
622
623
sage: c == 15
624
True
625
sage: 15 in L
626
False
627
"""
628
cdef mono_cell* cursor = self.lookup(<PyObject*><void*>k)
629
if cursor.key_id == NULL or cursor.key_id == dummy:
630
return False
631
r = <object>cursor.key_weakref
632
if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None:
633
return False
634
elif not(self.weak_values):
635
return True
636
else:
637
value = <object>cursor.value
638
return (not isinstance(value,fixed_KeyedRef)) or PyWeakref_GetObject(value) != Py_None
639
640
def __getitem__(self, k):
641
"""
642
Get the value corresponding to a key.
643
644
EXAMPLES::
645
646
sage: from sage.structure.coerce_dict import MonoDict
647
sage: L = MonoDict(31)
648
sage: a = 'a'; b = 'b'; c = 15
649
sage: L[a] = 1
650
sage: L[b] = 2
651
sage: L[c] = 3
652
sage: L[c] # indirect doctest
653
3
654
655
Note that the keys are supposed to be unique::
656
657
sage: c==15
658
True
659
sage: c is 15
660
False
661
sage: L[15]
662
Traceback (most recent call last):
663
...
664
KeyError: 15
665
"""
666
return self.get(k)
667
668
cdef get(self, object k):
669
cdef mono_cell* cursor = self.lookup(<PyObject*><void *>k)
670
if cursor.key_id == NULL or cursor.key_id == dummy:
671
raise KeyError, k
672
r = <object>cursor.key_weakref
673
if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None:
674
raise KeyError, k
675
value = <object>cursor.value
676
if self.weak_values and isinstance(value,fixed_KeyedRef):
677
value = <object>PyWeakref_GetObject(value)
678
if value is None:
679
raise KeyError, k
680
return value
681
682
def __setitem__(self, k, value):
683
"""
684
Set the value corresponding to a key.
685
686
EXAMPLES::
687
688
sage: from sage.structure.coerce_dict import MonoDict
689
sage: L = MonoDict(31)
690
sage: a = 'a'
691
sage: L[a] = -1 # indirect doctest
692
sage: L[a]
693
-1
694
sage: L[a] = 1
695
sage: L[a]
696
1
697
sage: len(L)
698
1
699
"""
700
self.set(k,value)
701
702
cdef set(self,object k, object value):
703
cdef mono_cell entry
704
cdef PyObject* old_value = NULL
705
cdef bint maybe_resize = False
706
entry.key_id = <void*>k
707
if self.weak_values:
708
cap_k=PyCapsule_New(<void *>(k),NULL,NULL)
709
try:
710
value_store = fixed_KeyedRef(value,self.eraser,cap_k)
711
entry.value = <PyObject*><void*>value_store
712
except TypeError:
713
entry.value = <PyObject*><void*>value
714
else:
715
entry.value = <PyObject*><void*>value
716
Py_XINCREF(entry.value)
717
cursor = self.lookup(<PyObject*><void*>k)
718
if cursor.key_id == NULL or cursor.key_id == dummy:
719
self.used += 1
720
if cursor.key_id == NULL:
721
self.fill += 1
722
maybe_resize = True
723
if not(self.weak_values):
724
cap_k=PyCapsule_New(<void *>(k),NULL,NULL)
725
try:
726
key_store=fixed_KeyedRef(k,self.eraser,cap_k)
727
entry.key_weakref = <PyObject*><void*>key_store
728
except TypeError:
729
entry.key_weakref = <PyObject*><void*>k
730
Py_XINCREF(entry.key_weakref)
731
732
#we're taking a bit of a gamble here: we're assuming the dictionary has
733
#not been resized (otherwise cursor might not be a valid location
734
#anymore). The only way in which that could happen is if the allocation
735
#activity above forced a GC that triggered code that ADDS entries to this
736
#dictionary: the dictionary can only get reshaped if self.fill increases.
737
#(as happens below). Note that we're holding a strong ref to the dict
738
#itself, so that's not liable to disappear.
739
#for the truly paranoid: we could detect a change by checking if self.table
740
#has changed value
741
cursor[0] = entry
742
743
#this is the one place where resize gets called:
744
if maybe_resize and 3*self.fill > 2*self.mask: self.resize()
745
else:
746
old_value = cursor.value
747
cursor.value = entry.value
748
Py_XDECREF(old_value)
749
750
def __delitem__(self, k):
751
"""
752
Delete the value corresponding to a key.
753
754
EXAMPLES::
755
756
sage: from sage.structure.coerce_dict import MonoDict
757
sage: L = MonoDict(31)
758
sage: a = 15
759
sage: L[a] = -1
760
sage: len(L)
761
1
762
763
Note that the keys are unique, hence using a key that is equal but not
764
identical to a results in an error::
765
766
sage: del L[15]
767
Traceback (most recent call last):
768
...
769
KeyError: 15
770
sage: a in L
771
True
772
sage: del L[a]
773
sage: len(L)
774
0
775
sage: a in L
776
False
777
"""
778
cdef mono_cell* cursor = self.lookup(<PyObject *><void *>k)
779
if cursor.key_id == NULL or cursor.key_id==dummy:
780
raise KeyError, k
781
L=extract_mono_cell(cursor)
782
self.used -= 1
783
784
def iteritems(self):
785
"""
786
EXAMPLES::
787
788
sage: from sage.structure.coerce_dict import MonoDict
789
sage: L = MonoDict(31)
790
sage: L[1] = None
791
sage: L[2] = True
792
sage: list(sorted(L.iteritems()))
793
[(1, None), (2, True)]
794
"""
795
#iteration is tricky because the table could change from under us.
796
#the following iterates properly if the dictionary does not
797
#get "resize"d, which is guaranteed if no NEW entries in the
798
#dictionary are introduced. At least we make sure to get our data fresh
799
#from "self" every iteration, so that at least we're not reading random memory
800
#(if the dictionary changes, it's not guaranteed you get to see any particular entry)
801
cdef size_t i = 0
802
while i <= self.mask:
803
cursor = &(self.table[i])
804
i += 1
805
if cursor.key_id != NULL and cursor.key_id != dummy:
806
key = <object>(cursor.key_weakref)
807
value = <object>(cursor.value)
808
if isinstance(key,fixed_KeyedRef):
809
key = <object>PyWeakref_GetObject(key)
810
if key is None:
811
print "found defunct key"
812
continue
813
if self.weak_values and isinstance(value,fixed_KeyedRef):
814
value = <object>PyWeakref_GetObject(value)
815
if value is None:
816
print "found defunct value"
817
continue
818
yield (key, value)
819
820
def __reduce__(self):
821
"""
822
Note that we don't expect equality as this class concerns itself with
823
object identity rather than object equality.
824
825
EXAMPLES::
826
827
sage: from sage.structure.coerce_dict import MonoDict
828
sage: L = MonoDict(31)
829
sage: L[1] = True
830
sage: loads(dumps(L)) == L
831
False
832
sage: list(loads(dumps(L)).iteritems())
833
[(1, True)]
834
"""
835
return MonoDict, (11, dict(self.iteritems()), 0.7)
836
837
#the cython supplied tp_traverse and tp_clear do not take the dynamically
838
#allocated table into account, so we have to supply our own.
839
#the only additional link to follow (that cython does pick up and we have
840
#to replicate here) is the "eraser" which in its closure stores a reference
841
#back to the dictionary itself (meaning that MonoDicts only disappear
842
#on cyclic GC)
843
844
cdef int MonoDict_traverse(MonoDict op, visitproc visit, void *arg):
845
cdef int r
846
if op.table == NULL:
847
return 0
848
table=op.table
849
cdef size_t i = 0
850
if (<void*>(op.eraser)):
851
r=visit(<PyObject*><void*>(op.eraser),arg)
852
if r: return r
853
for i in range(op.mask+1):
854
cursor = &table[i]
855
if cursor.key_id != NULL and cursor.key_id != dummy:
856
r=visit(cursor.key_weakref,arg)
857
if r: return r
858
r=visit(cursor.value,arg)
859
if r: return r
860
return 0
861
862
863
#we clear a monodict by taking first taking away the table before dereffing
864
#its contents. That shortcuts callbacks, so we deref the entries straight here.
865
#that means this code does not participate in Python's trashcan the way that
866
#deletion code based on extract_mono_cell does, so there is probably a way
867
#this code can be used to overflow the C stack. It would have to be a pretty
868
#devious example, though.
869
cdef int MonoDict_clear(MonoDict op):
870
if op.table == NULL:
871
return 0
872
873
tmp=op.eraser
874
cdef mono_cell* table=op.table
875
cdef size_t mask=op.mask
876
op.table=NULL
877
878
op.eraser=None
879
op.mask=0
880
op.used=0
881
op.fill=0
882
883
#this deletion method incurs an extra refcount +-, but it seems very difficult in cython to do it
884
#any other way and still be sure that the reference op.eraser is gone by the time the object gets
885
#deallocated.
886
del tmp
887
for i in range(mask+1):
888
cursor = &(table[i])
889
if cursor.key_id != NULL and cursor.key_id != dummy:
890
cursor.key_id = dummy
891
Py_XDECREF(cursor.key_weakref)
892
Py_XDECREF(cursor.value)
893
PyMem_Free(table)
894
return 0
895
896
(<PyTypeObject *><void *>MonoDict).tp_traverse = &MonoDict_traverse
897
(<PyTypeObject *><void *>MonoDict).tp_clear = &MonoDict_clear
898
899
cdef class TripleDictEraser:
900
"""
901
Erases items from a :class:`TripleDict` when a weak reference becomes
902
invalid.
903
904
This is of internal use only. Instances of this class will be passed as a
905
callback function when creating a weak reference.
906
907
EXAMPLES::
908
909
sage: from sage.structure.coerce_dict import TripleDict
910
sage: class A: pass
911
sage: a = A()
912
sage: T = TripleDict()
913
sage: T[a,ZZ,None] = 1
914
sage: T[ZZ,a,1] = 2
915
sage: T[a,a,ZZ] = 3
916
sage: len(T)
917
3
918
sage: del a
919
sage: import gc
920
sage: n = gc.collect()
921
sage: len(T) # indirect doctest
922
0
923
924
AUTHOR:
925
926
- Simon King (2012-01)
927
- Nils Bruin (2013-11)
928
"""
929
cdef D
930
def __init__(self, D):
931
"""
932
INPUT:
933
934
A :class:`TripleDict`. For internal use only.
935
936
EXAMPLES::
937
938
sage: D = sage.structure.coerce_dict.TripleDict()
939
sage: k = set([1])
940
sage: D[k,1,1] = 1
941
sage: len(D)
942
1
943
sage: del k
944
sage: len(D) # indirect doctest
945
0
946
947
"""
948
self.D = fixed_ref(D)
949
950
def __call__(self, r):
951
"""
952
INPUT:
953
954
A weak reference with key.
955
956
For internal use only.
957
958
EXAMPLES::
959
960
sage: from sage.structure.coerce_dict import TripleDict
961
sage: class A: pass
962
sage: a = A()
963
sage: T = TripleDict()
964
sage: T[a,ZZ,None] = 1
965
sage: T[ZZ,a,1] = 2
966
sage: T[a,a,ZZ] = 3
967
sage: len(T)
968
3
969
sage: del a
970
sage: import gc
971
sage: n = gc.collect()
972
sage: len(T) # indirect doctest
973
0
974
"""
975
cdef TripleDict td = <object>PyWeakref_GetObject(self.D)
976
if td is None:
977
return
978
if td.table == NULL:
979
return
980
981
k1,k2,k3 = r.key
982
cdef triple_cell* cursor = td.lookup(<PyObject*>PyCapsule_GetPointer(k1,NULL),
983
<PyObject*>PyCapsule_GetPointer(k2,NULL),
984
<PyObject*>PyCapsule_GetPointer(k3,NULL))
985
if (cursor.key_id1 != NULL and cursor.key_id1 != dummy):
986
if (cursor.key_weakref1 == <PyObject*><void*>r or
987
cursor.key_weakref2 == <PyObject*><void*>r or
988
cursor.key_weakref3 == <PyObject*><void*>r or
989
cursor.value == <PyObject*><void*>r):
990
L=extract_triple_cell(cursor)
991
td.used -= 1
992
else:
993
raise RuntimeError("eraser: key match but no weakref match")
994
995
cdef class TripleDict:
996
"""
997
This is a hashtable specifically designed for (read) speed in
998
the coercion model.
999
1000
It differs from a python dict in the following important ways:
1001
1002
- All keys must be sequence of exactly three elements. All sequence
1003
types (tuple, list, etc.) map to the same item.
1004
- Comparison is done using the 'is' rather than '==' operator.
1005
1006
There are special cdef set/get methods for faster access.
1007
It is bare-bones in the sense that not all dictionary methods are
1008
implemented.
1009
1010
It is implemented as a list of lists (hereafter called buckets). The bucket is
1011
chosen according to a very simple hash based on the object pointer, and each
1012
bucket is of the form [id(k1), id(k2), id(k3), r1, r2, r3, value, id(k1),
1013
id(k2), id(k3), r1, r2, r3, value, ...], on which a linear search is performed.
1014
If a key component ki supports weak references then ri is a weak reference to
1015
ki; otherwise ri is identical to ki.
1016
1017
INPUT:
1018
1019
- ``size`` -- an integer, the initial number of buckets. To spread objects
1020
evenly, the size should ideally be a prime, and certainly not divisible
1021
by 2.
1022
- ``data`` -- optional iterable defining initial data.
1023
- ``threshold`` -- optional number, default `0.7`. It determines how frequently
1024
the dictionary will be resized (large threshold implies rare resizing).
1025
- ``weak_values`` -- optional bool (default False). If it is true, weak references
1026
to the values in this dictionary will be used, when possible.
1027
1028
If any of the key components k1,k2,k3 (this can happen for a key component
1029
that supports weak references) gets garbage collected then the entire
1030
entry disappears. In that sense this structure behaves like a nested
1031
:class:`~weakref.WeakKeyDictionary`.
1032
1033
EXAMPLES::
1034
1035
sage: from sage.structure.coerce_dict import TripleDict
1036
sage: L = TripleDict()
1037
sage: a = 'a'; b = 'b'; c = 'c'
1038
sage: L[a,b,c] = 1
1039
sage: L[a,b,c]
1040
1
1041
sage: L[c,b,a] = -1
1042
sage: list(L.iteritems()) # random order of output.
1043
[(('c', 'b', 'a'), -1), (('a', 'b', 'c'), 1)]
1044
sage: del L[a,b,c]
1045
sage: list(L.iteritems())
1046
[(('c', 'b', 'a'), -1)]
1047
sage: len(L)
1048
1
1049
sage: for i in range(1000):
1050
... L[i,i,i] = i
1051
sage: len(L)
1052
1001
1053
sage: L = TripleDict(L)
1054
sage: L[c,b,a]
1055
-1
1056
sage: L[a,b,c]
1057
Traceback (most recent call last):
1058
...
1059
KeyError: ('a', 'b', 'c')
1060
sage: L[a]
1061
Traceback (most recent call last):
1062
...
1063
KeyError: 'a'
1064
sage: L[a] = 1
1065
Traceback (most recent call last):
1066
...
1067
KeyError: 'a'
1068
1069
Note that this kind of dictionary is also used for caching actions
1070
and coerce maps. In previous versions of Sage, the cache was by
1071
strong references and resulted in a memory leak in the following
1072
example. However, this leak was fixed by :trac:`715`, using weak
1073
references::
1074
1075
sage: K = GF(1<<55,'t')
1076
sage: for i in range(50):
1077
... a = K.random_element()
1078
... E = EllipticCurve(j=a)
1079
... P = E.random_point()
1080
... Q = 2*P
1081
sage: import gc
1082
sage: n = gc.collect()
1083
sage: from sage.schemes.elliptic_curves.ell_finite_field import EllipticCurve_finite_field
1084
sage: LE = [x for x in gc.get_objects() if isinstance(x, EllipticCurve_finite_field)]
1085
sage: len(LE) # indirect doctest
1086
1
1087
1088
TESTS:
1089
1090
Here, we demonstrate the use of weak values.
1091
::
1092
1093
sage: class Foo: pass
1094
sage: T = TripleDict(13)
1095
sage: TW = TripleDict(13, weak_values=True)
1096
sage: a = Foo()
1097
sage: b = Foo()
1098
sage: k = 1
1099
sage: T[a,k,k]=1
1100
sage: T[k,a,k]=2
1101
sage: T[k,k,a]=3
1102
sage: T[k,k,k]=a
1103
sage: TW[b,k,k]=1
1104
sage: TW[k,b,k]=2
1105
sage: TW[k,k,b]=3
1106
sage: TW[k,k,k]=b
1107
sage: len(T)
1108
4
1109
sage: len(TW)
1110
4
1111
sage: (k,k,k) in T
1112
True
1113
sage: (k,k,k) in TW
1114
True
1115
sage: T[k,k,k] is a
1116
True
1117
sage: TW[k,k,k] is b
1118
True
1119
1120
Now, ``T`` holds a strong reference to ``a``, namely in ``T[k,k,k]``. Hence,
1121
when we delete ``a``, *all* items of ``T`` survive::
1122
1123
sage: del a
1124
sage: _ = gc.collect()
1125
sage: len(T)
1126
4
1127
1128
Only when we remove the strong reference, the items become collectable::
1129
1130
sage: del T[k,k,k]
1131
sage: _ = gc.collect()
1132
sage: len(T)
1133
0
1134
1135
The situation is different for ``TW``, since it only holds *weak*
1136
references to ``a``. Therefore, all items become collectable after
1137
deleting ``a``::
1138
1139
sage: del b
1140
sage: _ = gc.collect()
1141
sage: len(TW)
1142
0
1143
1144
.. NOTE::
1145
1146
The index `h` corresponding to the key [k1, k2, k3] is computed as a
1147
value of unsigned type size_t as follows:
1148
1149
.. MATH::
1150
1151
h = id(k1) + 13*id(k2) xor 503 id(k3)
1152
1153
The natural type for this quantity is Py_ssize_t, which is a signed
1154
quantity with the same length as size_t. Storing it in a signed way gives the most
1155
efficient storage into PyInt, while preserving sign information.
1156
1157
In previous situations there were some problems with ending up with negative
1158
indices, which required casting to an unsigned type, i.e.,
1159
(<size_t> h)% N
1160
since C has a sign-preserving % operation This caused problems on 32 bits systems,
1161
see :trac:`715` for details. This is irrelevant for the current implementation.
1162
1163
AUTHORS:
1164
1165
- Robert Bradshaw, 2007-08
1166
1167
- Simon King, 2012-01
1168
1169
- Nils Bruin, 2012-08
1170
1171
- Simon King, 2013-02
1172
1173
- Nils Bruin, 2013-11
1174
"""
1175
cdef triple_cell* lookup(self, PyObject* key1, PyObject* key2, PyObject* key3):
1176
#returns a pointer to where key should be stored in this dictionary.
1177
cdef size_t perturb
1178
cdef size_t mask = self.mask
1179
cdef triple_cell* table = self.table
1180
cdef triple_cell* cursor
1181
cdef triple_cell* first_freed = NULL
1182
cdef int j
1183
global summed_expected, samples
1184
#we device some hash that reasonably depends on bits of all keys
1185
#making sure it's not commutative and also involves some of the higher order
1186
#bits at an early stage.
1187
cdef size_t key = <size_t>key1 + 13*(<size_t>key2) ^ 503*(<size_t>key3)
1188
cdef size_t i = key>>8 + key
1189
cursor = &(table[i & mask])
1190
perturb = (key)>>3
1191
j=1
1192
while (cursor.key_id1 != key1 or cursor.key_id2 != key2 or cursor.key_id3 != key3):
1193
if cursor.key_id1 == NULL:
1194
return first_freed if (first_freed != NULL) else cursor
1195
if first_freed == NULL and cursor.key_id1 == dummy:
1196
first_freed = cursor
1197
i = 5*i + perturb +1
1198
cursor = &(table[i & mask])
1199
perturb = perturb >> 5
1200
return cursor
1201
1202
cdef int resize(self) except -1:
1203
cdef triple_cell* old_table=self.table
1204
cdef size_t old_mask = self.mask
1205
cdef size_t newsize = 8
1206
cdef size_t minsize = 2*self.used
1207
cdef size_t i
1208
cdef triple_cell* cursor
1209
cdef triple_cell* entry
1210
while newsize < minsize:
1211
newsize = newsize<<1
1212
cdef triple_cell* table = <triple_cell*> PyMem_Malloc((newsize)*sizeof(triple_cell))
1213
if table == NULL:
1214
raise MemoryError()
1215
memset(table,0,(newsize)*sizeof(triple_cell))
1216
self.table = table
1217
self.mask = newsize-1
1218
self.used = 0
1219
self.fill = 0
1220
for i in range(old_mask+1):
1221
entry = &(old_table[i])
1222
if entry.key_id1 != NULL and entry.key_id1 != dummy:
1223
cursor=self.lookup(<PyObject*>(entry.key_id1),<PyObject*>(entry.key_id2),<PyObject*>(entry.key_id3))
1224
assert cursor.key_id1 == NULL
1225
cursor[0]=entry[0]
1226
self.used +=1
1227
self.fill +=1
1228
PyMem_Free(old_table)
1229
return 0
1230
1231
def __init__(self, size=11, data=None, threshold=0.7, weak_values=False):
1232
"""
1233
Create a special dict using triples for keys.
1234
1235
EXAMPLES::
1236
1237
sage: from sage.structure.coerce_dict import TripleDict
1238
sage: L = TripleDict(31)
1239
sage: a = 'a'; b = 'b'; c = 'c'
1240
sage: L[a,b,c] = 1
1241
sage: L[a,b,c]
1242
1
1243
"""
1244
#if only one argument is supplied and it's iterable, use it for data rather than
1245
#for size
1246
if data is None:
1247
try:
1248
data=size.iteritems()
1249
except AttributeError:
1250
try:
1251
data=iter(size)
1252
except TypeError:
1253
pass
1254
else:
1255
try:
1256
data=data.iteritems()
1257
except AttributeError:
1258
pass
1259
if self.table != NULL:
1260
raise RuntimeError("table already initialized. Called __init__ second time?")
1261
cdef minsize = 8
1262
cdef size_t newsize = 1<<3
1263
while newsize < minsize:
1264
newsize = newsize <<1
1265
self.mask = newsize - 1
1266
cdef triple_cell* table = <triple_cell*> PyMem_Malloc(newsize*sizeof(triple_cell))
1267
if table == NULL:
1268
raise MemoryError()
1269
memset(table,0,newsize*sizeof(triple_cell))
1270
self.table = table
1271
self.used = 0
1272
self.fill = 0
1273
self.eraser = TripleDictEraser(self)
1274
self.weak_values = weak_values
1275
if data:
1276
for k,v in data:
1277
self[k]=v
1278
1279
def __dealloc__(self):
1280
#is this required? (TripleDict_clear is safe to call multiple times)
1281
TripleDict_clear(self)
1282
1283
def __len__(self):
1284
"""
1285
The number of items in self.
1286
1287
EXAMPLES::
1288
1289
sage: from sage.structure.coerce_dict import TripleDict
1290
sage: L = TripleDict(37)
1291
sage: a = 'a'; b = 'b'; c = 'c'
1292
sage: L[a,b,c] = 1
1293
sage: L[a,b,c] = -1 # re-assign
1294
sage: L[a,c,b] = 1
1295
sage: L[a,a,None] = None
1296
sage: len(L)
1297
3
1298
"""
1299
return self.used
1300
1301
def __contains__(self, k):
1302
"""
1303
Test if the dictionary contains a given key.
1304
1305
EXAMPLES::
1306
1307
sage: from sage.structure.coerce_dict import TripleDict
1308
sage: L = TripleDict(31)
1309
sage: a = 'a'; b = 'ab'; c = 15
1310
sage: L[a,b,c] = 123
1311
sage: (a,b,c) in L # indirect doctest
1312
True
1313
1314
The keys are compared by identity, not by equality. Hence, we have::
1315
1316
sage: c == 15
1317
True
1318
sage: (a,b,15) in L
1319
False
1320
"""
1321
cdef object k1,k2,k3
1322
try:
1323
k1, k2, k3 = k
1324
except (TypeError,ValueError):
1325
raise KeyError, k
1326
cdef triple_cell* cursor = self.lookup(<PyObject*><void*>k1,<PyObject*><void*>k2,<PyObject*><void*>k3)
1327
if cursor.key_id1 == NULL or cursor.key_id1 == dummy:
1328
return False
1329
r = <object>cursor.key_weakref1
1330
if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None:
1331
return False
1332
r = <object>cursor.key_weakref2
1333
if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None:
1334
return False
1335
r = <object>cursor.key_weakref3
1336
if isinstance(r, fixed_KeyedRef) and PyWeakref_GetObject(r) == Py_None:
1337
return False
1338
if not(self.weak_values):
1339
return True
1340
value = <object>cursor.value
1341
return (not isinstance(value,fixed_KeyedRef)) or PyWeakref_GetObject(value) != Py_None
1342
1343
def __getitem__(self, k):
1344
"""
1345
Get the value corresponding to a key.
1346
1347
EXAMPLES::
1348
1349
sage: from sage.structure.coerce_dict import TripleDict
1350
sage: L = TripleDict(31)
1351
sage: a = 'a'; b = 'b'; c = 'c'
1352
sage: L[a,b,c] = 1
1353
sage: L[a,b,c]
1354
1
1355
"""
1356
cdef object k1,k2,k3
1357
try:
1358
k1, k2, k3 = k
1359
except (TypeError,ValueError):
1360
raise KeyError, k
1361
return self.get(k1, k2, k3)
1362
1363
cdef get(self, object k1, object k2, object k3):
1364
cdef triple_cell* cursor = self.lookup(<PyObject*><void *>k1,<PyObject*><void *>k2,<PyObject*><void *>k3)
1365
if cursor.key_id1 == NULL or cursor.key_id1 == dummy:
1366
raise KeyError, (k1, k2, k3)
1367
r1 = <object>cursor.key_weakref1
1368
r2 = <object>cursor.key_weakref2
1369
r3 = <object>cursor.key_weakref3
1370
if (isinstance(r1, fixed_KeyedRef) and PyWeakref_GetObject(r1) == Py_None) or \
1371
(isinstance(r2, fixed_KeyedRef) and PyWeakref_GetObject(r2) == Py_None) or \
1372
(isinstance(r3, fixed_KeyedRef) and PyWeakref_GetObject(r3) == Py_None):
1373
raise KeyError, (k1, k2, k3)
1374
value = <object>cursor.value
1375
if self.weak_values and isinstance(value,fixed_KeyedRef):
1376
value = <object>PyWeakref_GetObject(value)
1377
if value is None:
1378
raise KeyError, (k1, k2, k3)
1379
return value
1380
1381
def __setitem__(self, k, value):
1382
"""
1383
Set the value corresponding to a key.
1384
1385
EXAMPLES::
1386
1387
sage: from sage.structure.coerce_dict import TripleDict
1388
sage: L = TripleDict(31)
1389
sage: a = 'a'; b = 'b'; c = 'c'
1390
sage: L[a,b,c] = -1
1391
sage: L[a,b,c]
1392
-1
1393
"""
1394
cdef object k1,k2,k3
1395
try:
1396
k1, k2, k3 = k
1397
except (TypeError,ValueError):
1398
raise KeyError, k
1399
self.set(k1, k2, k3, value)
1400
1401
cdef set(self, object k1, object k2, object k3, value):
1402
cdef triple_cell entry
1403
cdef PyObject* old_value = NULL
1404
cdef bint maybe_resize = False
1405
entry.key_id1 = <void*>k1
1406
entry.key_id2 = <void*>k2
1407
entry.key_id3 = <void*>k3
1408
if self.weak_values:
1409
k = (PyCapsule_New(<void *>(k1),NULL,NULL),
1410
PyCapsule_New(<void *>(k2),NULL,NULL),
1411
PyCapsule_New(<void *>(k3),NULL,NULL))
1412
try:
1413
value_store = fixed_KeyedRef(value,self.eraser,k)
1414
entry.value = <PyObject*><void*>value_store
1415
except TypeError:
1416
entry.value = <PyObject*><void*>value
1417
else:
1418
entry.value = <PyObject*><void*>value
1419
Py_XINCREF(entry.value)
1420
cursor = self.lookup(<PyObject*><void*>k1,<PyObject*><void*>k2,<PyObject*><void*>k3)
1421
if cursor.key_id1 == NULL or cursor.key_id1 == dummy:
1422
self.used += 1
1423
if cursor.key_id1 == NULL:
1424
self.fill += 1
1425
maybe_resize = True
1426
if not(self.weak_values):
1427
k = (PyCapsule_New(<void *>(k1),NULL,NULL),
1428
PyCapsule_New(<void *>(k2),NULL,NULL),
1429
PyCapsule_New(<void *>(k3),NULL,NULL))
1430
try:
1431
key_store=fixed_KeyedRef(k1,self.eraser,k)
1432
entry.key_weakref1 = <PyObject*><void*>key_store
1433
except TypeError:
1434
entry.key_weakref1 = <PyObject*><void*>k1
1435
Py_XINCREF(entry.key_weakref1)
1436
try:
1437
key_store=fixed_KeyedRef(k2,self.eraser,k)
1438
entry.key_weakref2 = <PyObject*><void*>key_store
1439
except TypeError:
1440
entry.key_weakref2 = <PyObject*><void*>k2
1441
Py_XINCREF(entry.key_weakref2)
1442
try:
1443
key_store=fixed_KeyedRef(k3,self.eraser,k)
1444
entry.key_weakref3 = <PyObject*><void*>key_store
1445
except TypeError:
1446
entry.key_weakref3 = <PyObject*><void*>k3
1447
Py_XINCREF(entry.key_weakref3)
1448
1449
#we're taking a bit of a gamble here: we're assuming the dictionary has
1450
#not been resized (otherwise cursor might not be a valid location
1451
#anymore). The only way in which that could happen is if the allocation
1452
#activity above forced a GC that triggered code that ADDS entries to this
1453
#dictionary: the dictionary can only get reshaped if self.fill increases.
1454
#(as happens below). Note that we're holding a strong ref to the dict
1455
#itself, so that's not liable to disappear.
1456
#for the truly paranoid: we could detect a change by checking if self.table
1457
#has changed value
1458
cursor[0] = entry
1459
1460
#this is the only place where resize gets called:
1461
if maybe_resize and 3*self.fill > 2*self.mask: self.resize()
1462
else:
1463
old_value = cursor.value
1464
cursor.value = entry.value
1465
Py_XDECREF(old_value)
1466
1467
def __delitem__(self, k):
1468
"""
1469
Delete the value corresponding to a key.
1470
1471
EXAMPLES::
1472
1473
sage: from sage.structure.coerce_dict import TripleDict
1474
sage: L = TripleDict(31)
1475
sage: a = 'a'; b = 'b'; c = 'c'
1476
sage: L[a,b,c] = -1
1477
sage: (a,b,c) in L
1478
True
1479
sage: del L[a,b,c]
1480
sage: len(L)
1481
0
1482
sage: (a,b,c) in L
1483
False
1484
"""
1485
cdef object k1,k2,k3
1486
try:
1487
k1, k2, k3 = k
1488
except (TypeError,ValueError):
1489
raise KeyError, k
1490
cdef triple_cell* cursor = self.lookup(<PyObject *><void *>k1,<PyObject *><void *>k2,<PyObject *><void *>k3)
1491
if cursor.key_id1 == NULL or cursor.key_id1==dummy:
1492
raise KeyError, k
1493
L=extract_triple_cell(cursor)
1494
self.used -= 1
1495
1496
def iteritems(self):
1497
"""
1498
EXAMPLES::
1499
1500
sage: from sage.structure.coerce_dict import TripleDict
1501
sage: L = TripleDict(31)
1502
sage: L[1,2,3] = None
1503
sage: list(L.iteritems())
1504
[((1, 2, 3), None)]
1505
"""
1506
1507
cdef size_t i = 0
1508
while i <= self.mask:
1509
cursor = &(self.table[i])
1510
i += 1
1511
if cursor.key_id1 != NULL and cursor.key_id1 != dummy:
1512
key1 = <object>(cursor.key_weakref1)
1513
key2 = <object>(cursor.key_weakref2)
1514
key3 = <object>(cursor.key_weakref3)
1515
value = <object>(cursor.value)
1516
if isinstance(key1,fixed_KeyedRef):
1517
key1 = <object>PyWeakref_GetObject(key1)
1518
if key1 is None:
1519
print "found defunct key1"
1520
continue
1521
if isinstance(key2,fixed_KeyedRef):
1522
key2 = <object>PyWeakref_GetObject(key2)
1523
if key2 is None:
1524
print "found defunct key2"
1525
continue
1526
if isinstance(key3,fixed_KeyedRef):
1527
key3 = <object>PyWeakref_GetObject(key3)
1528
if key3 is None:
1529
print "found defunct key3"
1530
continue
1531
if self.weak_values and isinstance(value,fixed_KeyedRef):
1532
value = <object>PyWeakref_GetObject(value)
1533
if value is None:
1534
print "found defunct value"
1535
continue
1536
yield ((key1,key2,key3), value)
1537
1538
def __reduce__(self):
1539
"""
1540
Note that we don't expect equality as this class concerns itself with
1541
object identity rather than object equality.
1542
1543
EXAMPLES::
1544
1545
sage: from sage.structure.coerce_dict import TripleDict
1546
sage: L = TripleDict(31)
1547
sage: L[1,2,3] = True
1548
sage: loads(dumps(L)) == L
1549
False
1550
sage: list(loads(dumps(L)).iteritems())
1551
[((1, 2, 3), True)]
1552
"""
1553
return TripleDict, (11, dict(self.iteritems()), 0.7)
1554
1555
#the cython supplied tp_traverse and tp_clear do not take the dynamically
1556
#allocated table into account, so we have to supply our own.
1557
#the only additional link to follow (that cython does pick up and we have
1558
#to replicate here) is the "eraser" which in its closure stores a reference
1559
#back to the dictionary itself (meaning that MonoDicts only disappear
1560
#on cyclic GC)
1561
1562
cdef int TripleDict_traverse(TripleDict op, visitproc visit, void *arg):
1563
cdef int r
1564
if op.table == NULL:
1565
return 0
1566
table=op.table
1567
cdef size_t i = 0
1568
if (<void*>(op.eraser)):
1569
r=visit(<PyObject*><void*>(op.eraser),arg)
1570
if r: return r
1571
for i in range(op.mask+1):
1572
cursor = &table[i]
1573
if cursor.key_id1 != NULL and cursor.key_id1 != dummy:
1574
r=visit(cursor.key_weakref1,arg)
1575
if r: return r
1576
r=visit(cursor.key_weakref2,arg)
1577
if r: return r
1578
r=visit(cursor.key_weakref3,arg)
1579
if r: return r
1580
r=visit(cursor.value,arg)
1581
if r: return r
1582
return 0
1583
1584
cdef int TripleDict_clear(TripleDict op):
1585
if op.table == NULL:
1586
return 0
1587
1588
tmp=op.eraser
1589
cdef triple_cell* table=op.table
1590
cdef size_t mask=op.mask
1591
op.table=NULL
1592
op.eraser=None
1593
op.mask=0
1594
op.used=0
1595
op.fill=0
1596
1597
#refcount dance to ensure op.eraser==None when the actual object gets deallocated
1598
del tmp
1599
for i in range(mask+1):
1600
cursor = &(table[i])
1601
if cursor.key_id1 != NULL and cursor.key_id1 != dummy:
1602
cursor.key_id1 = dummy
1603
Py_XDECREF(cursor.key_weakref1)
1604
Py_XDECREF(cursor.key_weakref2)
1605
Py_XDECREF(cursor.key_weakref3)
1606
Py_XDECREF(cursor.value)
1607
PyMem_Free(table)
1608
return 0
1609
1610
(<PyTypeObject *><void *>TripleDict).tp_traverse = &TripleDict_traverse
1611
(<PyTypeObject *><void *>TripleDict).tp_clear = &TripleDict_clear
1612
1613