[Bf-blender-cvs] [fa2f7c6] master: mathutils: Implement __hash__() functions

Campbell Barton noreply at git.blender.org
Sun Feb 15 04:06:15 CET 2015


Commit: fa2f7c69acc55fd1fe3db0610ad319d08d77d79e
Author: Campbell Barton
Date:   Sun Feb 15 10:46:14 2015 +1100
Branches: master
https://developer.blender.org/rBfa2f7c69acc55fd1fe3db0610ad319d08d77d79e

mathutils: Implement __hash__() functions

- all mathutils types
- only works on frozen data (so vectors can be used in sets/dict keys)
- uses same method as CPython, (matches hashing a tuple)

D1104 by @juicyfruit with own modifications

===================================================================

M	source/blender/python/mathutils/mathutils.c
M	source/blender/python/mathutils/mathutils.h
M	source/blender/python/mathutils/mathutils_Color.c
M	source/blender/python/mathutils/mathutils_Euler.c
M	source/blender/python/mathutils/mathutils_Matrix.c
M	source/blender/python/mathutils/mathutils_Quaternion.c
M	source/blender/python/mathutils/mathutils_Vector.c

===================================================================

diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 28b2d26d..7b51b08 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -79,6 +79,37 @@ static int mathutils_array_parse_fast(float *array,
 	return size;
 }
 
+/**
+ * helper function that returns a Python ``__hash__``.
+ *
+ * \note consistent with the equivalent tuple of floats (CPython's 'tuplehash')
+ */
+Py_hash_t mathutils_array_hash(const float *array, size_t array_len)
+{
+	int i;
+	Py_uhash_t x;  /* Unsigned for defined overflow behavior. */
+	Py_hash_t y;
+	Py_uhash_t mult;
+	Py_ssize_t len;
+
+	mult = _PyHASH_MULTIPLIER;
+	len = array_len;
+	x = 0x345678UL;
+	i = 0;
+	while (--len >= 0) {
+		y = _Py_HashDouble((double)(array[i++]));
+		if (y == -1)
+			return -1;
+		x = (x ^ y) * mult;
+		/* the cast might truncate len; that doesn't change hash stability */
+		mult += (Py_hash_t)(82520UL + len + len);
+	}
+	x += 97531UL;
+	if (x == (Py_uhash_t)-1)
+		x = -2;
+	return x;
+}
+
 /* helper functionm returns length of the 'value', -1 on error */
 int mathutils_array_parse(float *array, int array_min, int array_max, PyObject *value, const char *error_prefix)
 {
@@ -459,6 +490,13 @@ void _BaseMathObject_RaiseFrozenExc(const BaseMathObject *self)
 	             Py_TYPE(self)->tp_name);
 }
 
+void _BaseMathObject_RaiseNotFrozenExc(const BaseMathObject *self)
+{
+	PyErr_Format(PyExc_TypeError,
+	             "%s is not frozen (mutable), call freeze first",
+	             Py_TYPE(self)->tp_name);
+}
+
 /* BaseMathObject generic functions for all mathutils types */
 char BaseMathObject_owner_doc[] = "The item this is wrapping or None  (read-only).";
 PyObject *BaseMathObject_owner_get(BaseMathObject *self, void *UNUSED(closure))
diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h
index 03ce9af..e653b45 100644
--- a/source/blender/python/mathutils/mathutils.h
+++ b/source/blender/python/mathutils/mathutils.h
@@ -109,6 +109,7 @@ int _BaseMathObject_ReadIndexCallback(BaseMathObject *self, int index);
 int _BaseMathObject_WriteIndexCallback(BaseMathObject *self, int index);
 
 void _BaseMathObject_RaiseFrozenExc(const BaseMathObject *self);
+void _BaseMathObject_RaiseNotFrozenExc(const BaseMathObject *self);
 
 /* since this is called so often avoid where possible */
 #define BaseMath_ReadCallback(_self) \
@@ -133,12 +134,18 @@ void _BaseMathObject_RaiseFrozenExc(const BaseMathObject *self);
 	(UNLIKELY((_self)->flag & BASE_MATH_FLAG_IS_FROZEN) ? \
 	(_BaseMathObject_RaiseFrozenExc((BaseMathObject *)_self), -1) : 0)
 
+#define BaseMathObject_Prepare_ForHash(_self) \
+	(UNLIKELY(((_self)->flag & BASE_MATH_FLAG_IS_FROZEN) == 0) ? \
+	 (_BaseMathObject_RaiseNotFrozenExc((BaseMathObject *)_self), -1) : 0)
+
 /* utility func */
 int mathutils_array_parse(float *array, int array_min, int array_max, PyObject *value, const char *error_prefix);
 int mathutils_array_parse_alloc(float **array, int array_min, PyObject *value, const char *error_prefix);
 int mathutils_array_parse_alloc_v(float **array, int array_dim, PyObject *value, const char *error_prefix);
 int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix);
 
+Py_hash_t mathutils_array_hash(const float *float_array, size_t array_len);
+
 /* zero remaining unused elements of the array */
 #define MU_ARRAY_ZERO      (1 << 30)
 /* ignore larger py sequences than requested (just use first elements),
diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c
index 7fea0e5..add8c245 100644
--- a/source/blender/python/mathutils/mathutils_Color.c
+++ b/source/blender/python/mathutils/mathutils_Color.c
@@ -192,6 +192,17 @@ static PyObject *Color_richcmpr(PyObject *a, PyObject *b, int op)
 	return Py_INCREF_RET(res);
 }
 
+static Py_hash_t Color_hash(ColorObject *self)
+{
+	if (BaseMath_ReadCallback(self) == -1)
+		return -1;
+
+	if (BaseMathObject_Prepare_ForHash(self) == -1)
+		return -1;
+
+	return mathutils_array_hash(self->col, COLOR_SIZE);
+}
+
 /* ---------------------SEQUENCE PROTOCOLS------------------------ */
 /* ----------------------------len(object)------------------------ */
 /* sequence length */
@@ -843,7 +854,7 @@ PyTypeObject color_Type = {
 	&Color_NumMethods,              /* tp_as_number */
 	&Color_SeqMethods,              /* tp_as_sequence */
 	&Color_AsMapping,               /* tp_as_mapping */
-	NULL,                           /* tp_hash */
+	(hashfunc)Color_hash,           /* tp_hash */
 	NULL,                           /* tp_call */
 #ifndef MATH_STANDALONE
 	(reprfunc) Color_str,           /* tp_str */
diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c
index ad761ba..78532a1 100644
--- a/source/blender/python/mathutils/mathutils_Euler.c
+++ b/source/blender/python/mathutils/mathutils_Euler.c
@@ -389,6 +389,17 @@ static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op)
 	return Py_INCREF_RET(res);
 }
 
+static Py_hash_t Euler_hash(EulerObject *self)
+{
+	if (BaseMath_ReadCallback(self) == -1)
+		return -1;
+
+	if (BaseMathObject_Prepare_ForHash(self) == -1)
+		return -1;
+
+	return mathutils_array_hash(self->eul, EULER_SIZE);
+}
+
 /* ---------------------SEQUENCE PROTOCOLS------------------------ */
 /* ----------------------------len(object)------------------------ */
 /* sequence length */
@@ -696,7 +707,7 @@ PyTypeObject euler_Type = {
 	NULL,                           /* tp_as_number */
 	&Euler_SeqMethods,              /* tp_as_sequence */
 	&Euler_AsMapping,               /* tp_as_mapping */
-	NULL,                           /* tp_hash */
+	(hashfunc)Euler_hash,           /* tp_hash */
 	NULL,                           /* tp_call */
 #ifndef MATH_STANDALONE
 	(reprfunc) Euler_str,           /* tp_str */
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 7b72ae5..ce90709 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -895,6 +895,19 @@ static void matrix_copy(MatrixObject *mat_dst, const MatrixObject *mat_src)
 	memcpy(mat_dst->matrix, mat_src->matrix, sizeof(float) * (mat_dst->num_col * mat_dst->num_row));
 }
 
+/* transposes memory layout, rol/col's don't have to match */
+static void matrix_transpose_internal(float mat_dst_fl[], const MatrixObject *mat_src)
+{
+	unsigned short col, row;
+	unsigned int i = 0;
+
+	for (row = 0; row < mat_src->num_row; row++) {
+		for (col = 0; col < mat_src->num_col; col++) {
+			mat_dst_fl[i++] = MATRIX_ITEM(mat_src, row, col);
+		}
+	}
+}
+
 /* assumes rowsize == colsize is checked and the read callback has run */
 static float matrix_determinant_internal(const MatrixObject *self)
 {
@@ -2040,6 +2053,21 @@ static PyObject *Matrix_richcmpr(PyObject *a, PyObject *b, int op)
 	return Py_INCREF_RET(res);
 }
 
+static Py_hash_t Matrix_hash(MatrixObject *self)
+{
+	float mat[SQUARE(MATRIX_MAX_DIM)];
+
+	if (BaseMath_ReadCallback(self) == -1)
+		return -1;
+
+	if (BaseMathObject_Prepare_ForHash(self) == -1)
+		return -1;
+
+	matrix_transpose_internal(mat, self);
+
+	return mathutils_array_hash(mat, self->num_row * self->num_col);
+}
+
 /*---------------------SEQUENCE PROTOCOLS------------------------
  * ----------------------------len(object)------------------------
  * sequence length */
@@ -2745,7 +2773,7 @@ PyTypeObject matrix_Type = {
 	&Matrix_NumMethods,                 /*tp_as_number*/
 	&Matrix_SeqMethods,                 /*tp_as_sequence*/
 	&Matrix_AsMapping,                  /*tp_as_mapping*/
-	NULL,                               /*tp_hash*/
+	(hashfunc)Matrix_hash,              /*tp_hash*/
 	NULL,                               /*tp_call*/
 #ifndef MATH_STANDALONE
 	(reprfunc) Matrix_str,              /*tp_str*/
diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c
index 8734d19..e13255b 100644
--- a/source/blender/python/mathutils/mathutils_Quaternion.c
+++ b/source/blender/python/mathutils/mathutils_Quaternion.c
@@ -575,6 +575,17 @@ static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op)
 	return Py_INCREF_RET(res);
 }
 
+static Py_hash_t Quaternion_hash(QuaternionObject *self)
+{
+	if (BaseMath_ReadCallback(self) == -1)
+		return -1;
+
+	if (BaseMathObject_Prepare_ForHash(self) == -1)
+		return -1;
+
+	return mathutils_array_hash(self->quat, QUAT_SIZE);
+}
+
 /* ---------------------SEQUENCE PROTOCOLS------------------------ */
 /* ----------------------------len(object)------------------------ */
 /* sequence length */
@@ -1275,7 +1286,7 @@ PyTypeObject quaternion_Type = {
 	&Quaternion_NumMethods,             /* tp_as_number */
 	&Quaternion_SeqMethods,             /* tp_as_sequence */
 	&Quaternion_AsMapping,              /* tp_as_mapping */
-	NULL,                               /* tp_hash */
+	(hashfunc)Quaternion_hash,          /* tp_hash */
 	NULL,                               /* tp_call */
 #ifndef MATH_STANDALONE
 	(reprfunc) Quaternion_str,          /* tp_str */
diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c
index 91090e5..06d9a5c 100644
--- a/source/blender/python/mathutils/mathutils_Vector.c
+++ b/source/blender/python/mathutils/mathutils_Vector.c
@@ -2051,6 +2051,17 @@ static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int compa
 	}
 }
 
+static Py_hash_t Vector_hash(VectorObject *self)
+{
+	if (BaseMath_ReadCallback(self) == -1)
+		return -1;
+
+	if (BaseMathObject_Prepare_ForHash(self) == -1)
+		return -1;
+
+	return mathutils_array_hash(self->vec, self->size);
+}
+
 /*-----------------PROTCOL DECLARATIONS--------------------------*/
 static PySequenceMethods Vector_SeqMethods = {
 	(lenfunc) Vector_len,               /* sq_length */
@@ -2927,7 +2938,7 @@ PyTypeObject vector_Type = {
 
 	/* More standard operations (here for binary compatibility) */
 
-	NULL,                       /* hashfunc tp_hash; */
+	(hashfunc)Vector_hash,   

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list