/*
 * Copyright (c) 2012 Lawrence Livermore National Laboratory
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *
 * Author: Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
 */

#ifndef HASH_H
#define HASH_H

#include "assert.h"
#include "hash-fnv.h"
#include "hash-function.h"
#include "hash-murmur3.h"
#include "ptr.h"

#include <string>

/**
 * @file
 * @ingroup hash
 * @brief ns3::Hasher, ns3::Hash32() and ns3::Hash64() function declarations.
 */

namespace ns3
{

/**
 * @ingroup core
 * @defgroup hash Hash Functions
 *
 * @brief Generic Hash function interface.
 *
 * See \ref Hasher for main entry point.
 * See \ref hash-example.cc for example usage.
 */
/**
 *  @ingroup hash
 *
 *  @brief Generic Hash function interface.
 *
 *  This class provides a generic interface for computing hashes
 *  of buffers.  Various getters return hashes of different lengths.
 *
 *  Call clear() between calls to the getter to reset the
 *  internal state and hash each buffer separately.
 *
 *  If you don't call clear() between calls to the getter
 *  you can hash successive buffers.  The final return value
 *  will be the cumulative hash across all calls.
 *
 *  The choice of hash function can be made at construction by
 *    @code
 *    Hasher hasher = Hasher ( Create<Hash::Function::Fnv1a> () );
 *    uint32_t hash = Hasher.GetHash32 (data);
 *    @endcode
 *
 *  The available implementations are documented in \ref hash.
 *  The default implementation is Murmur3.  FNV1a is also available.
 *
 *  In addition to this class interface, global functions are
 *  defined which use the default hash implementation.
 *
 * @internal
 *
 *  Would be nice to offer longer hashes.  \c uint128_t looks doable,
 *  except that our fallback \c int64x64_t implementation doesn't
 *  offer \c unsigned.
 *
 *  Longer hashes require returning a byte buffer of some sort,
 *  but our \ref Buffer class seems a bit overkill for this case.
 *
 */
class Hasher
{
  public:
    /**
     * Constructor using the default implementation.
     */
    Hasher();
    /**
     * Constructor using the supplied implementation.
     *
     * @param [in] hp Ptr<Hash::Implementation> to the desired implementation.
     */
    Hasher(Ptr<Hash::Implementation> hp);
    /**
     * Compute 32-bit hash of a byte buffer.
     *
     * Call clear () between calls to GetHash32() to reset the
     * internal state and hash each buffer separately.
     *
     * If you don't call clear() between calls to GetHash32,
     * you can hash successive buffers.  The final return value
     * will be the cumulative hash across all calls.
     *
     * @param [in] buffer Pointer to the beginning of the buffer.
     * @param [in] size Length of the buffer, in bytes.
     * @return 32-bit hash of the buffer..
     */
    uint32_t GetHash32(const char* buffer, const std::size_t size);
    /**
     * Compute 64-bit hash of a byte buffer.
     *
     * Call clear () between calls to GetHash64() to reset the
     * internal state and hash each buffer separately.
     *
     * If you don't call clear() between calls to GetHash64,
     * you can hash successive buffers.  The final return value
     * will be the cumulative hash across all calls.
     *
     * @param [in] buffer Pointer to the beginning of the buffer.
     * @param [in] size Length of the buffer, in bytes.
     * @return 64-bit hash of the buffer.
     */
    uint64_t GetHash64(const char* buffer, const std::size_t size);

    /**
     * Compute 32-bit hash of a string.
     *
     * Call clear () between calls to GetHash32() to reset the
     * internal state and hash each string separately.
     *
     * If you don't call clear() between calls to GetHash32,
     * you can hash successive strings.  The final return value
     * will be the cumulative hash across all calls.
     *
     * @param [in] s String to hash.
     * @return 32-bit hash of the string.
     */
    uint32_t GetHash32(const std::string s);
    /**
     * Compute 64-bit hash of a string.
     *
     * Call clear () between calls to GetHash64() to reset the
     * internal state and hash each string separately.
     *
     * If you don't call clear() between calls to GetHash64,
     * you can hash successive strings.  The final return value
     * will be the cumulative hash across all calls.
     *
     * @param [in] s String to hash.
     * @return 64-bit hash of the string.
     */
    uint64_t GetHash64(const std::string s);
    /**
     * Restore initial state.
     *
     * Returning this Hasher allows code like this:
     *
     * @code
     *   Hasher h;
     *   h.GetHash32 (...);
     *   ...
     *   h.clear ().GetHash64 (...);
     * @endcode
     *
     * @return This hasher.
     */
    Hasher& clear();

  private:
    Ptr<Hash::Implementation> m_impl; /**< Hash implementation. */

    // end of class Hasher
};

/*************************************************
 **  Global functions declarations
 ************************************************/

/**
 * @ingroup hash
 *
 * Compute 32-bit hash of a byte buffer, using the default hash function.
 *
 * @param [in] buffer Pointer to the beginning of the buffer.
 * @param [in] size Length of the buffer, in bytes.
 * @return 32-bit hash of the buffer.
 */
uint32_t Hash32(const char* buffer, const std::size_t size);
/**
 * @ingroup hash
 *
 * Compute 64-bit hash of a byte buffer, using the default hash function.
 *
 * @param [in] buffer Pointer to the beginning of the buffer.
 * @param [in] size Length of the buffer, in bytes.
 * @return 64-bit hash of the buffer.
 */
uint64_t Hash64(const char* buffer, const std::size_t size);

/**
 * @ingroup hash
 *
 * Compute 32-bit hash of a string, using the default hash function.
 *
 * @param [in] s String to hash.
 * @return 32-bit hash of the string.
 */
uint32_t Hash32(const std::string s);
/**
 * @ingroup hash
 *
 * Compute 64-bit hash of a string, using the default hash function.
 *
 * @param [in] s String to hash.
 * @return 64-bit hash of the string.
 */
uint64_t Hash64(const std::string s);

} // namespace ns3

/*************************************************
 **  Inline implementations for rvo
 ************************************************/

namespace ns3
{

/*************************************************
 class Hasher implementation, inlined for rvo
*/

inline uint32_t
Hasher::GetHash32(const char* buffer, const std::size_t size)
{
    NS_ASSERT(m_impl);
    return m_impl->GetHash32(buffer, size);
}

inline uint64_t
Hasher::GetHash64(const char* buffer, const std::size_t size)
{
    NS_ASSERT(m_impl);
    return m_impl->GetHash64(buffer, size);
}

inline uint32_t
Hasher::GetHash32(const std::string s)
{
    NS_ASSERT(m_impl);
    return m_impl->GetHash32(s.c_str(), s.size());
}

inline uint64_t
Hasher::GetHash64(const std::string s)
{
    NS_ASSERT(m_impl);
    return m_impl->GetHash64(s.c_str(), s.size());
}

/*************************************************
  Global hash functions, inlined for rvo
*/

/**
 * @brief Get a reference to the static global hasher at g_hasher
 * @return Reference to the static Hasher instance.
 */
Hasher& GetStaticHash();

inline uint32_t
Hash32(const char* buffer, const std::size_t size)
{
    return GetStaticHash().GetHash32(buffer, size);
}

inline uint64_t
Hash64(const char* buffer, const std::size_t size)
{
    return GetStaticHash().GetHash64(buffer, size);
}

inline uint32_t
Hash32(const std::string s)
{
    return GetStaticHash().GetHash32(s);
}

inline uint64_t
Hash64(const std::string s)
{
    return GetStaticHash().GetHash64(s);
}

} // namespace ns3

#endif /* HASH_H */
