Add hash function

Based on the FNV-1a algorithm. Creates 32-bit hashes.
This commit is contained in:
Kenneth Moreland 2017-08-24 10:02:18 -06:00
parent b1e6c1e34b
commit fc7b90aca6
4 changed files with 216 additions and 0 deletions

@ -34,6 +34,7 @@ set(headers
Bounds.h
CellShape.h
CellTraits.h
Hash.h
ListTag.h
Math.h
Matrix.h

127
vtkm/Hash.h Normal file

@ -0,0 +1,127 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//
// Copyright 2017 Sandia Corporation.
// Copyright 2017 UT-Battelle, LLC.
// Copyright 2017 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_Hash_h
#define vtk_m_Hash_h
#include <vtkm/TypeTraits.h>
#include <vtkm/Types.h>
#include <vtkm/VecTraits.h>
namespace vtkm
{
using HashType = vtkm::UInt32;
namespace detail
{
static const vtkm::HashType FNV1A_OFFSET = 2166136261;
static const vtkm::HashType FNV1A_PRIME = 16777619;
/// \brief Performs an FNV-1a hash on 32-bit integers returning a 32-bit hash
///
template <typename InVecType>
VTKM_EXEC_CONT inline vtkm::HashType HashFNV1a32(const InVecType& inVec)
{
using Traits = vtkm::VecTraits<InVecType>;
const vtkm::IdComponent numComponents = Traits::GetNumberOfComponents(inVec);
vtkm::HashType hash = FNV1A_OFFSET;
for (vtkm::IdComponent index = 0; index < numComponents; index++)
{
vtkm::HashType dataBits = static_cast<vtkm::HashType>(Traits::GetComponent(inVec, index));
hash = (hash * FNV1A_PRIME) ^ dataBits;
}
return hash;
}
/// \brief Performs an FNV-1a hash on 64-bit integers returning a 32-bit hash
///
template <typename InVecType>
VTKM_EXEC_CONT inline vtkm::HashType HashFNV1a64(const InVecType& inVec)
{
using Traits = vtkm::VecTraits<InVecType>;
const vtkm::IdComponent numComponents = Traits::GetNumberOfComponents(inVec);
vtkm::HashType hash = FNV1A_OFFSET;
for (vtkm::IdComponent index = 0; index < numComponents; index++)
{
vtkm::UInt64 allDataBits = static_cast<vtkm::UInt64>(Traits::GetComponent(inVec, index));
vtkm::HashType upperDataBits =
static_cast<vtkm::HashType>((allDataBits & 0xFFFFFFFF00000000L) >> 32);
hash = (hash * FNV1A_PRIME) ^ upperDataBits;
vtkm::HashType lowerDataBits = static_cast<vtkm::HashType>(allDataBits & 0x00000000FFFFFFFFL);
hash = (hash * FNV1A_PRIME) ^ lowerDataBits;
}
return hash;
}
// If you get a compile error saying that there is no implementation of the class HashChooser,
// then you have tried to make a hash from an invalid type (like a float).
template <typename NumericTag, vtkm::Id DataSize>
struct HashChooser;
template <>
struct HashChooser<vtkm::TypeTraitsIntegerTag, 4>
{
template <typename InVecType>
VTKM_EXEC_CONT static vtkm::HashType Hash(const InVecType& inVec)
{
return vtkm::detail::HashFNV1a32(inVec);
}
};
template <>
struct HashChooser<vtkm::TypeTraitsIntegerTag, 8>
{
template <typename InVecType>
VTKM_EXEC_CONT static vtkm::HashType Hash(const InVecType& inVec)
{
return vtkm::detail::HashFNV1a64(inVec);
}
};
} // namespace detail
/// \brief Returns a 32-bit hash on a group of integer-type values.
///
/// The input to the hash is expected to be a vtkm::Vec or a Vec-like object. The values can be
/// either 32-bit integers or 64-bit integers (signed or unsigned). Regardless, the resulting hash
/// is an unsigned 32-bit integer.
///
/// The hash is designed to minimize the probability of collisions, but collisions are always
/// possible. Thus, when using these hashes there should be a contingency for dealing with
/// collisions.
///
template <typename InVecType>
VTKM_EXEC_CONT inline vtkm::HashType Hash(const InVecType& inVec)
{
using VecTraits = vtkm::VecTraits<InVecType>;
using ComponentType = typename VecTraits::ComponentType;
using ComponentTraits = vtkm::TypeTraits<ComponentType>;
using Chooser = detail::HashChooser<typename ComponentTraits::NumericTag, sizeof(ComponentType)>;
return Chooser::Hash(inVec);
}
} // namespace vtkm
#endif //vtk_m_Hash_h

@ -33,6 +33,7 @@ set(unit_tests
UnitTestBounds.cxx
UnitTestCellShape.cxx
UnitTestExceptions.cxx
UnitTestHash.cxx
UnitTestListTag.cxx
UnitTestMath.cxx
UnitTestMatrix.cxx

@ -0,0 +1,87 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//
// Copyright 2017 Sandia Corporation.
// Copyright 2017 UT-Battelle, LLC.
// Copyright 2017 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#include <vtkm/Hash.h>
#include <vtkm/testing/Testing.h>
#include <algorithm>
#include <vector>
namespace
{
VTKM_CONT
static void CheckUnique(std::vector<vtkm::HashType> hashes)
{
std::sort(hashes.begin(), hashes.end());
for (std::size_t index = 1; index < hashes.size(); ++index)
{
VTKM_TEST_ASSERT(hashes[index - 1] != hashes[index], "Found duplicate hashes.");
}
}
template <typename VecType>
VTKM_CONT static void DoHashTest(VecType&&)
{
std::cout << "Test hash for " << vtkm::testing::TypeName<VecType>::Name() << std::endl;
const vtkm::Id NUM_HASHES = 100;
std::cout << " Make sure the first " << NUM_HASHES << " values are unique." << std::endl;
// There is a small probability that two values of these 100 could be the same. If this test
// fails we could just be unlucky (and have to use a different set of 100 hashes), but it is
// suspicious and you should double check the hashes.
std::vector<vtkm::HashType> hashes;
hashes.reserve(NUM_HASHES);
for (vtkm::Id index = 0; index < NUM_HASHES; ++index)
{
hashes.push_back(vtkm::Hash(TestValue(index, VecType())));
}
CheckUnique(hashes);
std::cout << " Try close values that should have different hashes." << std::endl;
hashes.clear();
VecType vec = TestValue(5, VecType());
hashes.push_back(vtkm::Hash(vec));
vec[0] = vec[0] + 1;
hashes.push_back(vtkm::Hash(vec));
vec[1] = vec[1] - 1;
hashes.push_back(vtkm::Hash(vec));
CheckUnique(hashes);
}
VTKM_CONT
static void TestHash()
{
DoHashTest(vtkm::Id2());
DoHashTest(vtkm::Id3());
DoHashTest(vtkm::Vec<vtkm::Id, 10>());
DoHashTest(vtkm::Vec<vtkm::IdComponent, 2>());
DoHashTest(vtkm::Vec<vtkm::IdComponent, 3>());
DoHashTest(vtkm::Vec<vtkm::IdComponent, 10>());
}
} // anonymous namespace
int UnitTestHash(int, char* [])
{
return vtkm::testing::Testing::Run(TestHash);
}