Skip to content

Commit

Permalink
Trac #14254: Add a signed_id() function returning an "id" as Py_ssize_t
Browse files Browse the repository at this point in the history
  • Loading branch information
jdemeyer committed Mar 11, 2013
1 parent 8ec5028 commit e2afec1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 20 deletions.
4 changes: 2 additions & 2 deletions src/sage/categories/homset.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
60 changes: 42 additions & 18 deletions src/sage/structure/coerce_dict.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,47 @@ 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 <Py_ssize_t><void *>(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
# an index into our bucket list by taking the hash modulo the number of buckets.
# 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
# (<size_t) h)% modulus
# (<size_t> h) % modulus
# to ensure we're doing an unsigned modulus.

############################################
Expand Down Expand Up @@ -528,7 +552,7 @@ cdef class MonoDict:
sage: 15 in L
False
"""
cdef Py_ssize_t h = <Py_ssize_t><void *>k
cdef Py_ssize_t h = signed_id(k)
cdef Py_ssize_t i
cdef list all_buckets = self.buckets
cdef list bucket = <object>PyList_GET_ITEM(all_buckets, (<size_t>h)% PyList_GET_SIZE(all_buckets))
Expand Down Expand Up @@ -571,7 +595,7 @@ cdef class MonoDict:
return self.get(k)

cdef get(self, object k):
cdef Py_ssize_t h =<Py_ssize_t><void *>k
cdef Py_ssize_t h = signed_id(k)
cdef Py_ssize_t i
cdef list all_buckets = self.buckets
cdef list bucket = <object>PyList_GET_ITEM(all_buckets, (<size_t>h) % PyList_GET_SIZE(all_buckets))
Expand Down Expand Up @@ -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 = <Py_ssize_t><void *>k
cdef Py_ssize_t h = signed_id(k)
cdef Py_ssize_t i
cdef list bucket = <object>PyList_GET_ITEM(self.buckets,(<size_t> h) % PyList_GET_SIZE(self.buckets))
cdef object r
Expand Down Expand Up @@ -679,7 +703,7 @@ cdef class MonoDict:
sage: a in L
False
"""
cdef Py_ssize_t h = <Py_ssize_t><void *>k
cdef Py_ssize_t h = signed_id(k)
cdef object r
cdef Py_ssize_t i
cdef object tmp
Expand Down Expand Up @@ -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 = <Py_ssize_t><void *>k1
cdef Py_ssize_t h2 = <Py_ssize_t><void *>k2
cdef Py_ssize_t h3 = <Py_ssize_t><void *>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
Expand Down Expand Up @@ -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 = <Py_ssize_t><void *>k1
cdef Py_ssize_t h2 = <Py_ssize_t><void *>k2
cdef Py_ssize_t h3 = <Py_ssize_t><void *>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
Expand Down Expand Up @@ -1234,9 +1258,9 @@ cdef class TripleDict:
k1, k2, k3 = k
except (TypeError,ValueError):
raise KeyError, k
cdef Py_ssize_t h1 = <Py_ssize_t><void *>k1
cdef Py_ssize_t h2 = <Py_ssize_t><void *>k2
cdef Py_ssize_t h3 = <Py_ssize_t><void *>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
Expand Down

0 comments on commit e2afec1

Please sign in to comment.