From 7f16e105d75c919d1e78a87898f39729cbfd9bc1 Mon Sep 17 00:00:00 2001 From: Valery Yundin Date: Wed, 21 May 2014 17:41:07 +0200 Subject: [PATCH] Make hash values deterministic. Hash seed will depend only on (mangled) class name, not on random memory address of that name. Makes canonical ordering more deterministic. Different runs with the same program/compiler combination will produce identical output. --- ginac/basic.cpp | 3 ++- ginac/expairseq.cpp | 3 ++- ginac/function.pl | 3 ++- ginac/hash_seed.h | 18 ++---------------- ginac/idx.cpp | 3 ++- ginac/relational.cpp | 4 +++- ginac/symbol.cpp | 2 +- ginac/symmetry.cpp | 3 ++- ginac/utils.h | 13 +++++++++++++ ginac/wildcard.cpp | 2 +- 10 files changed, 30 insertions(+), 24 deletions(-) diff --git a/ginac/basic.cpp b/ginac/basic.cpp index 4287f26..9f982b5 100644 --- a/ginac/basic.cpp +++ b/ginac/basic.cpp @@ -781,7 +781,8 @@ return_type_t basic::return_type_tinfo() const * would all end up with the same hashvalue. */ unsigned basic::calchash() const { - unsigned v = make_hash_seed(typeid(*this)); + static const unsigned seed = make_hash_seed(typeid(*this)); + unsigned v = seed; for (size_t i=0; iop(i).gethash(); diff --git a/ginac/expairseq.cpp b/ginac/expairseq.cpp index 7c6e522..9b735b6 100644 --- a/ginac/expairseq.cpp +++ b/ginac/expairseq.cpp @@ -599,7 +599,8 @@ unsigned expairseq::return_type() const unsigned expairseq::calchash() const { - unsigned v = make_hash_seed(typeid(*this)); + static const unsigned seed = make_hash_seed(typeid(*this)); + unsigned v = seed; epvector::const_iterator i = seq.begin(); const epvector::const_iterator end = seq.end(); while (i != end) { diff --git a/ginac/function.pl b/ginac/function.pl index d4a03ce..08f50cc 100644 --- a/ginac/function.pl +++ b/ginac/function.pl @@ -1066,7 +1066,8 @@ ex function::eval_ncmul(const exvector & v) const unsigned function::calchash() const { - unsigned v = golden_ratio_hash(make_hash_seed(typeid(*this)) ^ serial); + static const unsigned seed = make_hash_seed(typeid(*this)); + unsigned v = golden_ratio_hash(seed ^ serial); for (size_t i=0; iop(i).gethash(); diff --git a/ginac/hash_seed.h b/ginac/hash_seed.h index 32b057f..82f5fed 100644 --- a/ginac/hash_seed.h +++ b/ginac/hash_seed.h @@ -18,27 +18,13 @@ #include #include "crc32.h" #include "utils.h" -#ifdef _WIN32 -#define GINAC_HASH_USE_MANGLED_NAME 1 -#endif namespace GiNaC { -#ifndef GINAC_HASH_USE_MANGLED_NAME static inline unsigned make_hash_seed(const std::type_info& tinfo) { - // this pointer is the same for all objects of the same type. - // Hence we can use that pointer - const void* mangled_name_ptr = (const void*)tinfo.name(); - unsigned v = golden_ratio_hash((p_int)mangled_name_ptr); - return v; + // type name is the same for all objects of the same type + return string_fnv_hash(tinfo.name()); } -#else -static unsigned make_hash_seed(const std::type_info& tinfo) -{ - const char* mangled_name = tinfo.name(); - return crc32(mangled_name, std::strlen(mangled_name), 0); -} -#endif } // namespace GiNaC #endif /* GINAC_HASH_SEED_H */ diff --git a/ginac/idx.cpp b/ginac/idx.cpp index a3f0b84..9a44c2d 100644 --- a/ginac/idx.cpp +++ b/ginac/idx.cpp @@ -351,7 +351,8 @@ unsigned idx::calchash() const // hash keys. That is, the hash values must not depend on the index // dimensions or other attributes (variance etc.). // The compare_same_type() methods will take care of the rest. - unsigned v = make_hash_seed(typeid(*this)); + static const unsigned seed = make_hash_seed(typeid(*this)); + unsigned v = seed; v = rotate_left(v); v ^= value.gethash(); diff --git a/ginac/relational.cpp b/ginac/relational.cpp index 4ee87be..303e386 100644 --- a/ginac/relational.cpp +++ b/ginac/relational.cpp @@ -260,7 +260,9 @@ return_type_t relational::return_type_tinfo() const unsigned relational::calchash() const { - unsigned v = make_hash_seed(typeid(*this)); + static const unsigned seed = make_hash_seed(typeid(*this)); + unsigned v = seed; + unsigned lhash = lh.gethash(); unsigned rhash = rh.gethash(); diff --git a/ginac/symbol.cpp b/ginac/symbol.cpp index 4d73c9e..fa66a6a 100644 --- a/ginac/symbol.cpp +++ b/ginac/symbol.cpp @@ -272,7 +272,7 @@ bool symbol::is_equal_same_type(const basic & other) const unsigned symbol::calchash() const { - unsigned seed = make_hash_seed(typeid(*this)); + static const unsigned seed = make_hash_seed(typeid(*this)); hashvalue = golden_ratio_hash(seed ^ serial); setflag(status_flags::hash_calculated); return hashvalue; diff --git a/ginac/symmetry.cpp b/ginac/symmetry.cpp index a44cfcc..0386fef 100644 --- a/ginac/symmetry.cpp +++ b/ginac/symmetry.cpp @@ -187,7 +187,8 @@ int symmetry::compare_same_type(const basic & other) const unsigned symmetry::calchash() const { - unsigned v = make_hash_seed(typeid(*this)); + static const unsigned seed = make_hash_seed(typeid(*this)); + unsigned v = seed; if (type == none) { v = rotate_left(v); diff --git a/ginac/utils.h b/ginac/utils.h index 102f09d..d6dc40f 100644 --- a/ginac/utils.h +++ b/ginac/utils.h @@ -75,6 +75,19 @@ typedef uintptr_t p_int; typedef unsigned long p_int; #endif +/** 32 bit FNV-1a hash of a string. */ +inline unsigned string_fnv_hash(const char* str) +{ + const unsigned char* s = reinterpret_cast(str); + unsigned hash = 2166136261; + while (*s) { + hash ^= static_cast(*s++); + hash *= 16777619; + } + std::cout << "FNV " << str << " " << hash << std::endl; + return hash; +} + /** Truncated multiplication with golden ratio, for computing hash values. */ inline unsigned golden_ratio_hash(p_int n) { diff --git a/ginac/wildcard.cpp b/ginac/wildcard.cpp index 6140fa5..b2d1db6 100644 --- a/ginac/wildcard.cpp +++ b/ginac/wildcard.cpp @@ -107,7 +107,7 @@ unsigned wildcard::calchash() const // this is where the schoolbook method // (golden_ratio_hash(typeid(*this).name()) ^ label) // is not good enough yet... - unsigned seed = make_hash_seed(typeid(*this)); + static const unsigned seed = make_hash_seed(typeid(*this)); hashvalue = golden_ratio_hash(seed ^ label); setflag(status_flags::hash_calculated); return hashvalue; -- 1.8.4.5