/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2019 Danny Robson */ #pragma once #include "table.hpp" #include "../bitwise.hpp" #include "../std.hpp" #include "../view.hpp" #include /////////////////////////////////////////////////////////////////////////////// namespace cruft::hash { /// Implements a rolling hash by using a cyclic polynomial, aka buzhash. /// see: https://en.wikipedia.org/wiki/Rolling_hash#Cyclic_polynomial /// /// \tparam StateT The hash state size, and the result type. /// \tparam WordT The data type for each term of the polynomial /// \tparam HashT The object used to hash the words prior to mixing. /// This needs to be very fast for most clients of the algorithm; /// although functionally it could be any functor that maps WordT /// onto StateT. template < typename StateT, typename WordT = u08, typename HashT = table > class buzhash { public: static_assert (sizeof (StateT) >= sizeof (WordT)); using digest_type = StateT; template buzhash ( std::size_t _width, cruft::view _init, Args &&...args ) : m_width (_width) , m_hash (std::forward (args)...) { // Zero width would make for a constant zero hash. CHECK_NEZ (_width); // Rotations greater than data type size are often undefined. CHECK_LT (_width, sizeof (StateT) * 8); if (_init.size () < m_width) throw std::out_of_range ("buzhash input too small"); // Prime the initial window auto cursor = _init.begin (); for (std::size_t i = 1; i <= m_width; i++, cursor++) { m_state ^= rotatel (m_hash (*cursor), m_width - i); } } buzhash (buzhash const&) = default; buzhash (buzhash &&) noexcept = default; buzhash& operator= (buzhash const&) = default; buzhash& operator= (buzhash &&) noexcept = default; /// Rotate the hash over a pointer to the buffer we've been operating /// on. /// /// The previous `width` bytes _must_ be dereferencable and identical /// to the previously observed values. StateT operator() (WordT const *cursor) { return (*this) (cursor[0], *(cursor - m_width)); } /// Rotate a word into the hash, and the corresponding word out of the /// hash. /// /// The `removal` value _must_ be the same as the value seen `width` /// values previously. StateT operator() (WordT const addition, WordT const removal) { // Shift the polynomial m_state = rotatel (m_state, 1) // Remove the data that's about to leave our window. ^ rotatel (m_hash (removal), m_width) // Mix in the new data ^ m_hash (addition); return m_state; } /// An observer for the hash state/value. /// /// Provided for symmetry with other hash objects. constexpr StateT digest (void) const noexcept { return m_state; } operator StateT () const { return digest (); } private: std::size_t m_width; HashT m_hash; StateT m_state = 0; }; }