mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-19 02:25:42 +00:00
Add hash function
Based on the FNV-1a algorithm. Creates 32-bit hashes.
This commit is contained in:
parent
b1e6c1e34b
commit
fc7b90aca6
@ -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
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
|
||||
|
87
vtkm/testing/UnitTestHash.cxx
Normal file
87
vtkm/testing/UnitTestHash.cxx
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user