From 15933b9c51e94573f9ad508f924ed52e26d85a9b Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 19 Aug 2015 22:03:20 +0100 Subject: [PATCH] Added Jenkins hash algorithm --- jenkins.h | 303 ++++++++++++++++++++++++++++++++++++++++++ test/test_jenkins.cpp | 219 ++++++++++++++++++++++++++++++ 2 files changed, 522 insertions(+) create mode 100644 jenkins.h create mode 100644 test/test_jenkins.cpp diff --git a/jenkins.h b/jenkins.h new file mode 100644 index 00000000..730096c5 --- /dev/null +++ b/jenkins.h @@ -0,0 +1,303 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_FNV_1__ +#define __ETL_FNV_1__ + +#include + +#include "static_assert.h" +#include "type_traits.h" +#include "endian.h" +#include "ihash.h" + +#include "error_handler.h" + +#if defined(COMPILER_KEIL) +#pragma diag_suppress 1300 +#endif + +///\defgroup jenkins Jenkins 32 & 64 bit hash calculations +///\ingroup maths + +namespace etl +{ + //*************************************************************************** + /// Calculates the jenkins_64 hash. + ///\tparam ENDIANNESS The endianness of the calculation for input types larger than uint8_t. Default = endian::little. + ///\ingroup jenkins_64 + //*************************************************************************** + template + class jenkins_64 : public etl::ihash + { + public: + + typedef uint64_t value_type; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + jenkins_64() + : ihash(etl::endian(ENDIANNESS)) + { + reset(); + } + + //************************************************************************* + /// Constructor from range. + /// \param begin Start of the range. + /// \param end End of the range. + //************************************************************************* + template + jenkins_64(TIterator begin, const TIterator end) + : ihash(etl::endian(ENDIANNESS)) + { + reset(); + add(begin, end); + } + + //************************************************************************* + /// Resets the CRC to the initial state. + //************************************************************************* + void reset() + { + hash = 0; + is_finalised = false; + } + + //************************************************************************* + /// Adds a range. + /// \param begin + /// \param end + //************************************************************************* + template + void add(TIterator begin, const TIterator end) + { + ihash::add(begin, end); + } + + //************************************************************************* + /// Adds a value. + /// \param value The value to add to the checksum. + //************************************************************************* + template + void add(TValue value) + { + ihash::add(value); + } + + //************************************************************************* + /// \param value The char to add to the jenkins_64. + //************************************************************************* + void add(uint8_t value) + { + if (is_finalised) + { + etl::raise_error(hash_finalised()); + } + else + { + hash += value; + hash += (hash << 10); + hash ^= (hash >> 6); + } + } + + //************************************************************************* + /// Gets the jenkins_64 value. + //************************************************************************* + value_type value() + { + finalise(); + return hash; + } + + //************************************************************************* + /// Conversion operator to value_type. + //************************************************************************* + operator value_type () + { + return value(); + } + + //************************************************************************* + /// Gets the generic digest value. + //************************************************************************* + generic_digest digest() + { + finalise(); + return ihash::get_digest(hash); + } + + private: + + void finalise() + { + if (!is_finalised) + { + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + is_finalised = true; + } + } + + value_type hash; + bool is_finalised; + }; + + //*************************************************************************** + /// Calculates the jenkins_32 hash. + ///\tparam ENDIANNESS The endianness of the calculation for input types larger than uint8_t. Default = endian::little. + ///\ingroup jenkins_32 + //*************************************************************************** + template + class jenkins_32 : public etl::ihash + { + public: + + typedef uint32_t value_type; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + jenkins_32() + : ihash(etl::endian(ENDIANNESS)) + { + reset(); + } + + //************************************************************************* + /// Constructor from range. + /// \param begin Start of the range. + /// \param end End of the range. + //************************************************************************* + template + jenkins_32(TIterator begin, const TIterator end) + : ihash(etl::endian(ENDIANNESS)) + { + reset(); + add(begin, end); + } + + //************************************************************************* + /// Resets the CRC to the initial state. + //************************************************************************* + void reset() + { + hash = 0; + is_finalised = false; + } + + //************************************************************************* + /// Adds a range. + /// \param begin + /// \param end + //************************************************************************* + template + void add(TIterator begin, const TIterator end) + { + ihash::add(begin, end); + } + + //************************************************************************* + /// Adds a value. + /// \param value The value to add to the checksum. + //************************************************************************* + template + void add(TValue value) + { + ihash::add(value); + } + + //************************************************************************* + /// \param value The char to add to the jenkins_32. + //************************************************************************* + void add(uint8_t value) + { + if (is_finalised) + { + etl::raise_error(hash_finalised()); + } + else + { + hash += value; + hash += (hash << 10); + hash ^= (hash >> 6); + } + } + + //************************************************************************* + /// Gets the jenkins_32 value. + //************************************************************************* + value_type value() + { + finalise(); + return hash; + } + + //************************************************************************* + /// Conversion operator to value_type. + //************************************************************************* + operator value_type () + { + return value(); + } + + //************************************************************************* + /// Gets the generic digest value. + //************************************************************************* + generic_digest digest() + { + finalise(); + return ihash::get_digest(hash); + } + + private: + + //************************************************************************* + /// Finalise the hash. + //************************************************************************* + void finalise() + { + if (!is_finalised) + { + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + is_finalised = true; + } + } + + bool is_finalised; + value_type hash; + }; +} + +#endif diff --git a/test/test_jenkins.cpp b/test/test_jenkins.cpp new file mode 100644 index 00000000..e076f687 --- /dev/null +++ b/test/test_jenkins.cpp @@ -0,0 +1,219 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#include "murmurhash3.h" // The 'C' reference implementation. + +#include +#include +#include +#include + +#include "../jenkins.h" +#include "../endian.h" + +template +uint32_t jenkins32(T *key, size_t length) +{ + uint32_t hash = 0; + + for (size_t i = 0; i < length; ++i) + { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +template +uint64_t jenkins64(T *key, size_t length) +{ + uint64_t hash = 0; + + for (size_t i = 0; i < length; ++i) + { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +namespace +{ + SUITE(test_jenkins) + { + //************************************************************************* + TEST(test_jenkins_32_constructor) + { + std::string data("123456789"); + + uint32_t hash = etl::jenkins_32<>(data.begin(), data.end()); + uint32_t compare = jenkins32(data.c_str(), data.size()); + + CHECK_EQUAL(compare, hash); + } + + //************************************************************************* + TEST(test_jenkins_32_add_values) + { + std::string data("123456789"); + + etl::jenkins_32<> jenkins_32_calculator; + + for (size_t i = 0; i < data.size(); ++i) + { + jenkins_32_calculator += data[i]; + } + + uint32_t hash = jenkins_32_calculator; + uint32_t compare = jenkins32(data.c_str(), data.size()); + + CHECK_EQUAL(compare, hash); + } + + //************************************************************************* + TEST(test_jenkins_32_add_range) + { + std::string data("123456789"); + + etl::jenkins_32<> jenkins_32_calculator; + + jenkins_32_calculator.add(data.begin(), data.end()); + + uint32_t hash = jenkins_32_calculator.value(); + + uint32_t compare = jenkins32(data.c_str(), data.size()); + + CHECK_EQUAL(compare, hash); + } + + //************************************************************************* + TEST(test_jenkins_32_add_range_endian) + { + std::vector data1 = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + std::vector data2 = { 0x04030201, 0x08070605 }; + std::vector data3 = { 0x01020304, 0x05060708 }; + + uint32_t hash1 = etl::jenkins_32(data1.begin(), data1.end()); + uint32_t hash2 = etl::jenkins_32(data2.begin(), data2.end()); + uint32_t hash3 = etl::jenkins_32(data3.begin(), data3.end()); + CHECK_EQUAL(hash1, hash2); + CHECK_EQUAL(hash1, hash3); + + uint32_t compare1 = jenkins32((uint8_t*)&data1[0], data1.size()); + CHECK_EQUAL(compare1, hash1); + + uint32_t compare2 = jenkins32((uint8_t*)&data2[0], data2.size() * sizeof(uint32_t)); + CHECK_EQUAL(compare2, hash2); + + uint32_t compare3 = jenkins32((uint8_t*)&data3[0], data3.size() * sizeof(uint32_t)); + CHECK_EQUAL(compare2, hash3); + } + + //************************************************************************* + TEST(test_jenkins_64_constructor) + { + std::string data("123456789"); + + uint64_t hash = etl::jenkins_64<>(data.begin(), data.end()); + uint64_t compare = jenkins64(data.c_str(), data.size()); + + CHECK_EQUAL(compare, hash); + } + + //************************************************************************* + TEST(test_jenkins_64_add_values) + { + std::string data("123456789"); + + etl::jenkins_64<> jenkins_64_calculator; + + for (size_t i = 0; i < data.size(); ++i) + { + jenkins_64_calculator += data[i]; + } + + uint64_t hash = jenkins_64_calculator; + uint64_t compare = jenkins64(data.c_str(), data.size()); + + CHECK_EQUAL(compare, hash); + } + + //************************************************************************* + TEST(test_jenkins_64_add_range) + { + std::string data("123456789"); + + etl::jenkins_64<> jenkins_64_calculator; + + jenkins_64_calculator.add(data.begin(), data.end()); + + uint64_t hash = jenkins_64_calculator.value(); + + uint64_t compare = jenkins64(data.c_str(), data.size()); + + CHECK_EQUAL(compare, hash); + } + + //************************************************************************* + TEST(test_jenkins_64_add_range_endian) + { + std::vector data1 = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + std::vector data2 = { 0x04030201, 0x08070605 }; + std::vector data3 = { 0x01020304, 0x05060708 }; + + uint64_t hash1 = etl::jenkins_64(data1.begin(), data1.end()); + uint64_t hash2 = etl::jenkins_64(data2.begin(), data2.end()); + uint64_t hash3 = etl::jenkins_64(data3.begin(), data3.end()); + CHECK_EQUAL(hash1, hash2); + CHECK_EQUAL(hash1, hash3); + + uint64_t compare1 = jenkins64((uint8_t*)&data1[0], data1.size()); + CHECK_EQUAL(compare1, hash1); + + uint64_t compare2 = jenkins64((uint8_t*)&data2[0], data2.size() * sizeof(uint32_t)); + CHECK_EQUAL(compare2, hash2); + + uint64_t compare3 = jenkins64((uint8_t*)&data3[0], data3.size() * sizeof(uint32_t)); + CHECK_EQUAL(compare2, hash3); + } + }; +} +