#### SpookyHash
#### Copyright (c) 2023-2024, Remilia Scarlet
#### Copyright (c) 2015, Guillaume Voirin
#### BSD 3-Clause License
####
#### All rights reserved.
####
#### Redistribution and use in source and binary forms, with or without
#### modification, are permitted provided that the following conditions are met:
####
####     Redistributions of source code must retain the above copyright notice,
####     this list of conditions and the following disclaimer.
####
####     Redistributions in binary form must reproduce the above copyright
####     notice, this list of conditions and the following disclaimer in the
####     documentation and/or other materials provided with the distribution.
####
####     Neither the name of the copyright holder nor the names of its
####     contributors may be used to endorse or promote products derived from
####     this software without specific prior written permission.
####
#### THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#### AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#### IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#### ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
#### LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#### CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#### SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#### INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#### CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#### ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#### POSSIBILITY OF SUCH DAMAGE.

####
#### SpookyHash Global Variables and Utility Macros/Methods
####

class RemiLib::Digest::SpookyHash
  # The version of the algorithm that's supported.
  VERSION = "1.0.7"

  # :nodoc:
  SPOOKYHASH_VARIABLES = 12

  # :nodoc:
  SPOOKYHASH_BLOCK_SIZE = SPOOKYHASH_VARIABLES * 8

  # :nodoc:
  SPOOKYHASH_BUFFER_SIZE = 2 * SPOOKYHASH_BLOCK_SIZE

  # :nodoc:
  SPOOKYHASH_CONSTANT = 0xDEADBEEFDEADBEEF_u64

  # :nodoc:
  alias PUInt8 = Pointer(UInt8)

  # :nodoc:
  alias PUInt64 = Pointer(UInt64)

  # Ensures `val` is little-endian.
  private macro little(val)
    # At time of writing (6 May 2023), Crystal only seems to be on little-endian
    # architectures, so this is rather a moot point.  But it's here for future
    # use as-needed.
    {% if flag?(:aarch64) || flag?(:arm) || flag?(:i386) || flag?(:x86_64) %}
      {{val}}
    {% else %}
      {% raise "Don't know the endianness of this architecture" %}
    {% end %}
  end

  @[AlwaysInline]
  protected def self.rotate(val : UInt64, by : Int) : UInt64
    val.unsafe_shl(by) | (val.unsafe_shr(64 &- by))
  end

  protected def self.splitU128(num) : Tuple(UInt64, UInt64)
    {
      num.to_u64!,
      ((num & 0xFFFFFFFFFFFFFFFF0000000000000000_u128) >> 64).to_u64!
    }
  end

  # :nodoc:
  macro makeU128(low, high)
    ({{high}}.to_u128!.unsafe_shl(64) | {{low}})
  end
end
