From e2afec1596eb9282bca29e3b56634f0727ee0579 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 11 Mar 2013 15:49:56 +0100 Subject: [PATCH] Trac #14254: Add a signed_id() function returning an "id" as Py_ssize_t --- src/sage/categories/homset.py | 4 +- src/sage/structure/coerce_dict.pyx | 60 +++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index 8c9cc2d21ad..fa39d32a539 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -76,7 +76,7 @@ # introduced in trac ticket #715 from weakref import KeyedRef -from sage.structure.coerce_dict import TripleDict +from sage.structure.coerce_dict import signed_id, TripleDict _cache = TripleDict(53) def Hom(X, Y, category=None): @@ -263,7 +263,7 @@ def Hom(X, Y, category=None): H = category.hom_category().parent_class(X, Y, category = category) ##_cache[key] = weakref.ref(H) - _cache[key] = KeyedRef(H, _cache.eraser, (id(X),id(Y),id(category))) + _cache[key] = KeyedRef(H, _cache.eraser, (signed_id(X),signed_id(Y),signed_id(category))) return H def hom(X, Y, f): diff --git a/src/sage/structure/coerce_dict.pyx b/src/sage/structure/coerce_dict.pyx index 5dea4e745ae..50f5e65e3fd 100644 --- a/src/sage/structure/coerce_dict.pyx +++ b/src/sage/structure/coerce_dict.pyx @@ -44,15 +44,39 @@ cdef extern from "Python.h": PyObject* PyWeakref_GetObject(object ref) PyObject* Py_None +cpdef inline Py_ssize_t signed_id(x): + """ + A function like Python's :func:`id` returning *signed* integers, + which are guaranteed to fit in a ``Py_ssize_t``. + + Theoretically, there is no guarantee that two different Python + objects have different ``signed_id()`` values. However, under the + mild assumption that a C pointer fits in a ``Py_ssize_t``, this + is guaranteed. + + TESTS:: + + sage: a = 1.23e45 # some object + sage: from sage.structure.coerce_dict import signed_id + sage: s = signed_id(a) + sage: id(a) == s or id(a) == s + 2**32 or id(a) == s + 2**64 + True + sage: signed_id(a) <= sys.maxsize + True + """ + return (x) + import gc ############################################ # A note about how to store "id" keys in python structures: # -# In python a "pointer length integer" (size_t) normally, is encoded -# as a *signed* integer, of type Py_ssize_t. This has an advantage in that -# if the value gets encoded as a *python integer* it can do so in a sign-preserving -# way and still make use of all the bits that python offers to store (small) integers. +# We use the type Py_ssize_t to store "id"s generated by the signed_id +# function defined above. Assuming that Py_ssize_t is the same as a C +# long (which is true on most Unix-like systems), this also has the +# advantage that these Py_ssize_t values are stored as a Python "int" +# (as opposed to "long"), which allow for fast conversion to/from C +# types. # # There is one place where we have to be careful about signs: # Our hash values most naturally live in Py_ssize_t. We convert those into @@ -60,7 +84,7 @@ import gc # However, the modulo operator in C preserves the sign of the number we take the # modulus of, which is not what we want. # The solution is to always do -# ( h) % modulus # to ensure we're doing an unsigned modulus. ############################################ @@ -528,7 +552,7 @@ cdef class MonoDict: sage: 15 in L False """ - cdef Py_ssize_t h = k + cdef Py_ssize_t h = signed_id(k) cdef Py_ssize_t i cdef list all_buckets = self.buckets cdef list bucket = PyList_GET_ITEM(all_buckets, (h)% PyList_GET_SIZE(all_buckets)) @@ -571,7 +595,7 @@ cdef class MonoDict: return self.get(k) cdef get(self, object k): - cdef Py_ssize_t h =k + cdef Py_ssize_t h = signed_id(k) cdef Py_ssize_t i cdef list all_buckets = self.buckets cdef list bucket = PyList_GET_ITEM(all_buckets, (h) % PyList_GET_SIZE(all_buckets)) @@ -608,7 +632,7 @@ cdef class MonoDict: cdef set(self,object k, value): if self.threshold and self._size > len(self.buckets) * self.threshold: self.resize() - cdef Py_ssize_t h = k + cdef Py_ssize_t h = signed_id(k) cdef Py_ssize_t i cdef list bucket = PyList_GET_ITEM(self.buckets,( h) % PyList_GET_SIZE(self.buckets)) cdef object r @@ -679,7 +703,7 @@ cdef class MonoDict: sage: a in L False """ - cdef Py_ssize_t h = k + cdef Py_ssize_t h = signed_id(k) cdef object r cdef Py_ssize_t i cdef object tmp @@ -1101,9 +1125,9 @@ cdef class TripleDict: return self.get(k1, k2, k3) cdef get(self, object k1, object k2, object k3): - cdef Py_ssize_t h1 = k1 - cdef Py_ssize_t h2 = k2 - cdef Py_ssize_t h3 = k3 + cdef Py_ssize_t h1 = signed_id(k1) + cdef Py_ssize_t h2 = signed_id(k2) + cdef Py_ssize_t h3 = signed_id(k3) cdef Py_ssize_t h = (h1 + 13*h2 ^ 503*h3) cdef object r1,r2,r3 @@ -1149,9 +1173,9 @@ cdef class TripleDict: cdef set(self, object k1, object k2, object k3, value): if self.threshold and self._size > len(self.buckets) * self.threshold: self.resize() - cdef Py_ssize_t h1 = k1 - cdef Py_ssize_t h2 = k2 - cdef Py_ssize_t h3 = k3 + cdef Py_ssize_t h1 = signed_id(k1) + cdef Py_ssize_t h2 = signed_id(k2) + cdef Py_ssize_t h3 = signed_id(k3) cdef Py_ssize_t h = (h1 + 13*h2 ^ 503*h3) cdef object r1,r2,r3 @@ -1234,9 +1258,9 @@ cdef class TripleDict: k1, k2, k3 = k except (TypeError,ValueError): raise KeyError, k - cdef Py_ssize_t h1 = k1 - cdef Py_ssize_t h2 = k2 - cdef Py_ssize_t h3 = k3 + cdef Py_ssize_t h1 = signed_id(k1) + cdef Py_ssize_t h2 = signed_id(k2) + cdef Py_ssize_t h3 = signed_id(k3) cdef Py_ssize_t h = (h1 + 13*h2 ^ 503*h3) cdef Py_ssize_t i