diff options
author | Hein-Pieter van Braam <hp@tmm.cx> | 2017-02-15 14:41:16 +0100 |
---|---|---|
committer | Hein-Pieter van Braam <hp@tmm.cx> | 2017-02-16 18:44:29 +0100 |
commit | b696beea65bbffd31edac169ccf9708f46ab9652 (patch) | |
tree | 27bd4f630dda96d028aa0efd7752000844ac996b /core/variant.cpp | |
parent | 903a3aa5f0e128abb1fb752c10b343b34af8f799 (diff) | |
download | redot-engine-b696beea65bbffd31edac169ccf9708f46ab9652.tar.gz |
Correct hash behavior for floating point numbers
This fixes HashMap where a key or part of a key is a floating point
number. To fix this the following has been done:
* HashMap now takes an extra template argument Comparator. This class
gets used to compare keys. The default Comperator now works correctly
for common types and floating point numbets.
* Variant implements ::hash_compare() now. This function implements
nan-safe comparison for all types with components that contain floating
point numbers.
* Variant now has a VariantComparator which uses Variant::hash_compare()
safely compare floating point components of variant's types.
* The hash functions for floating point numbers will now normalize NaN
values so that all floating point numbers that are NaN hash to the same
value.
C++ module writers that want to use HashMap internally in their modules
can now also safeguard against this crash by defining their on
Comperator class that safely compares their types.
GDScript users, or writers of modules that don't use HashMap internally
in their modules don't need to do anything.
This fixes #7354 and fixes #6947.
Diffstat (limited to 'core/variant.cpp')
-rw-r--r-- | core/variant.cpp | 187 |
1 files changed, 182 insertions, 5 deletions
diff --git a/core/variant.cpp b/core/variant.cpp index 103c8f6746..132d7a1c88 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -28,6 +28,7 @@ /*************************************************************************/ #include "variant.h" +#include "math_funcs.h" #include "resource.h" #include "print_string.h" #include "scene/main/node.h" @@ -2674,14 +2675,10 @@ uint32_t Variant::hash() const { case INT: { return _data._int; - } break; case REAL: { - MarshallFloat mf; - mf.f=_data._real; - return mf.i; - + return hash_djb2_one_float(_data._real); } break; case STRING: { @@ -2921,6 +2918,186 @@ uint32_t Variant::hash() const { } +#define hash_compare_scalar(p_lhs, p_rhs)\ + ((p_lhs) == (p_rhs)) || (Math::is_nan(p_lhs) == Math::is_nan(p_rhs)) + +#define hash_compare_vector2(p_lhs, p_rhs)\ + (hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \ + (hash_compare_scalar((p_lhs).y, (p_rhs).y)) + +#define hash_compare_vector3(p_lhs, p_rhs)\ + (hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \ + (hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \ + (hash_compare_scalar((p_lhs).z, (p_rhs).z)) + +#define hash_compare_quat(p_lhs, p_rhs)\ + (hash_compare_scalar((p_lhs).x, (p_rhs).x)) && \ + (hash_compare_scalar((p_lhs).y, (p_rhs).y)) && \ + (hash_compare_scalar((p_lhs).z, (p_rhs).z)) && \ + (hash_compare_scalar((p_lhs).w, (p_rhs).w)) + +#define hash_compare_color(p_lhs, p_rhs)\ + (hash_compare_scalar((p_lhs).r, (p_rhs).r)) && \ + (hash_compare_scalar((p_lhs).g, (p_rhs).g)) && \ + (hash_compare_scalar((p_lhs).b, (p_rhs).b)) && \ + (hash_compare_scalar((p_lhs).a, (p_rhs).a)) + +#define hash_compare_pool_array(p_lhs, p_rhs, p_type, p_compare_func)\ + const PoolVector<p_type>& l = *reinterpret_cast<const PoolVector<p_type>*>(p_lhs);\ + const PoolVector<p_type>& r = *reinterpret_cast<const PoolVector<p_type>*>(p_rhs);\ + \ + if(l.size() != r.size()) \ + return false; \ + \ + PoolVector<p_type>::Read lr = l.read(); \ + PoolVector<p_type>::Read rr = r.read(); \ + \ + for(int i = 0; i < l.size(); ++i) { \ + if(! p_compare_func((lr[0]), (rr[0]))) \ + return false; \ + }\ + \ + return true + +bool Variant::hash_compare(const Variant& p_variant) const { + if (type != p_variant.type) + return false; + + switch( type ) { + case REAL: { + return hash_compare_scalar(_data._real, p_variant._data._real); + } break; + + case VECTOR2: { + const Vector2* l = reinterpret_cast<const Vector2*>(_data._mem); + const Vector2* r = reinterpret_cast<const Vector2*>(p_variant._data._mem); + + return hash_compare_vector2(*l, *r); + } break; + + case RECT2: { + const Rect2* l = reinterpret_cast<const Rect2*>(_data._mem); + const Rect2* r = reinterpret_cast<const Rect2*>(p_variant._data._mem); + + return (hash_compare_vector2(l->pos, r->pos)) && + (hash_compare_vector2(l->size, r->size)); + } break; + + case TRANSFORM2D: { + Transform2D* l = _data._transform2d; + Transform2D* r = p_variant._data._transform2d; + + for(int i=0;i<3;i++) { + if (! (hash_compare_vector2(l->elements[i], r->elements[i]))) + return false; + } + + return true; + } break; + + case VECTOR3: { + const Vector3* l = reinterpret_cast<const Vector3*>(_data._mem); + const Vector3* r = reinterpret_cast<const Vector3*>(p_variant._data._mem); + + return hash_compare_vector3(*l, *r); + } break; + + case PLANE: { + const Plane* l = reinterpret_cast<const Plane*>(_data._mem); + const Plane* r = reinterpret_cast<const Plane*>(p_variant._data._mem); + + return (hash_compare_vector3(l->normal, r->normal)) && + (hash_compare_scalar(l->d, r->d)); + } break; + + case RECT3: { + const Rect3* l = _data._rect3; + const Rect3* r = p_variant._data._rect3; + + return (hash_compare_vector3(l->pos, r->pos) && + (hash_compare_vector3(l->size, r->size))); + + } break; + + case QUAT: { + const Quat* l = reinterpret_cast<const Quat*>(_data._mem); + const Quat* r = reinterpret_cast<const Quat*>(p_variant._data._mem); + + return hash_compare_quat(*l, *r); + } break; + + case BASIS: { + const Basis* l = _data._basis; + const Basis* r = p_variant._data._basis; + + for(int i=0;i<3;i++) { + if (! (hash_compare_vector3(l->elements[i], r->elements[i]))) + return false; + } + + return true; + } break; + + case TRANSFORM: { + const Transform* l = _data._transform; + const Transform* r = p_variant._data._transform; + + for(int i=0;i<3;i++) { + if (! (hash_compare_vector3(l->basis.elements[i], r->basis.elements[i]))) + return false; + } + + return hash_compare_vector3(l->origin, r->origin); + } break; + + case COLOR: { + const Color* l = reinterpret_cast<const Color*>(_data._mem); + const Color* r = reinterpret_cast<const Color*>(p_variant._data._mem); + + return hash_compare_color(*l, *r); + } break; + + case ARRAY: { + const Array& l = *(reinterpret_cast<const Array*>(_data._mem)); + const Array& r = *(reinterpret_cast<const Array*>(p_variant._data._mem)); + + if(l.size() != r.size()) + return false; + + for(int i = 0; i < l.size(); ++i) { + if(! l[0].hash_compare(r[0])) + return false; + } + + return true; + } break; + + case POOL_REAL_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, real_t, hash_compare_scalar); + } break; + + case POOL_VECTOR2_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector2, hash_compare_vector2); + } break; + + case POOL_VECTOR3_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Vector3, hash_compare_vector3); + } break; + + case POOL_COLOR_ARRAY: { + hash_compare_pool_array(_data._mem, p_variant._data._mem, Color, hash_compare_color); + } break; + + default: + bool v; + Variant r; + evaluate(OP_EQUAL,*this,p_variant,r,v); + return r; + } + + return false; +} + bool Variant::is_ref() const { |