#include <Python.h>
+#include <boost/static_assert.hpp>
+
+#include <climits> // for CHAR_BIT
#include <stdexcept>
#include <string>
#if PY_MINOR_VERSION < 2
typedef long Py_hash_t;
#endif
+
+/// \brief Convert a hash value of arbitrary type to a Python hash value.
+///
+/// This templated function is a convenient wrapper to produce a valid hash
+/// value of type Py_hash_t, which is expected to be used as the return value
+/// of a PyTypeObject.tp_hash implementation. Py_hash_t is a signed integer
+/// the same size as Py_ssize_t.
+///
+/// The major concern is that -1 means an error in tp_hash and so we need to
+/// ensure that this value is never returned.
+///
+/// If the size of the original hash type is small enough, we convert
+/// the original hash value to a value of Py_hash_t, resetting all higher bits
+/// to 0. Due to the assumption on the sizes, the conversion to HashvalType
+/// is safe, and (after clearing the higher bits) results in a valid positive
+/// value.
+///
+/// If the size of the input and return types is the same, we clear the
+/// highest bit of the original hash so the resulting value will be in a valid
+/// positive range of Py_hash_t. If the original type is unsigned, there's
+/// probably no better conversion than this because otherwise the conversion
+/// to Py_hash_t could result in an undefined behavior. If the original type
+/// is signed, this conversion reduces the effective value range of the
+/// original hash. If this is not desired, the caller should convert it
+/// by itself (note that -1 should be still avoided).
+///
+/// This function does not support the case where the size of the original
+/// hash type is larger than that of Py_hash_t.
+template <typename HashvalType>
+Py_hash_t
+convertToPyHash(HashvalType val) {
+ BOOST_STATIC_ASSERT(sizeof(HashvalType) <= sizeof(Py_hash_t));
+
+ if (sizeof(HashvalType) < sizeof(Py_hash_t)) {
+ // The original hash type has small enough. Do trivial conversion.
+ const Py_hash_t mask = ~(static_cast<Py_hash_t>(-1) <<
+ (CHAR_BIT * sizeof(HashvalType)));
+ return (static_cast<Py_hash_t>(val) & mask);
+ } else {
+ // Clear the highest bit of the original hash so the conversion is
+ // safe and avoids -1.
+ const HashvalType mask = ~(static_cast<HashvalType>(1) <<
+ (CHAR_BIT * sizeof(HashvalType) - 1));
+ BOOST_STATIC_ASSERT(mask != static_cast<HashvalType>(-1));
+ return (val & mask);
+ }
+}
} // namespace python
} // namespace dns
} // namespace isc