580 lines
16 KiB
Fortran
580 lines
16 KiB
Fortran
!!******************************************************************************
|
|
!!
|
|
!! This file is part of the AMUN source code, a program to perform
|
|
!! Newtonian or relativistic magnetohydrodynamical simulations on uniform or
|
|
!! adaptive mesh.
|
|
!!
|
|
!! Copyright (C) 2012-2020 Yann Collet
|
|
!! Copyright (C) 2020-2021 Grzegorz Kowal <grzegorz@amuncode.org>
|
|
!!
|
|
!! This program is free software: you can redistribute it and/or modify
|
|
!! it under the terms of the GNU General Public License as published by
|
|
!! the Free Software Foundation, either version 3 of the License, or
|
|
!! (at your option) any later version.
|
|
!!
|
|
!! This program is distributed in the hope that it will be useful,
|
|
!! but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
!! GNU General Public License for more details.
|
|
!!
|
|
!! You should have received a copy of the GNU General Public License
|
|
!! along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
!!
|
|
!!******************************************************************************
|
|
!!
|
|
!! module: HASH
|
|
!!
|
|
!! This module is an interface to the XXH functions by Yann Collet provided
|
|
!! by the library libxxhash. If this library is not available, an internal
|
|
!! Fortran implementation of the 64-bit version of the xxHash64 is used.
|
|
!! The Fortran implementation is based on the XXH64 specification published at
|
|
!! https://github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md
|
|
!!
|
|
!! For additional info, see
|
|
!! http://www.xxhash.com or https://github.com/Cyan4973/xxHash
|
|
!!
|
|
!!******************************************************************************
|
|
!
|
|
module hash
|
|
|
|
implicit none
|
|
|
|
#ifdef XXHASH
|
|
! interfaces to functions XXH64() and XXH3_64bits() provided by
|
|
! the library libxxhash
|
|
!
|
|
interface
|
|
integer(c_int64_t) function xxh64_lib(input, length, seed) &
|
|
bind(C, name="XXH64")
|
|
use iso_c_binding, only: c_ptr, c_size_t, c_int64_t
|
|
implicit none
|
|
type(c_ptr) , value :: input
|
|
integer(kind=c_size_t), value :: length
|
|
integer(c_int64_t) , value :: seed
|
|
end function xxh64_lib
|
|
|
|
integer(c_int64_t) function xxh3_lib(input, length) &
|
|
bind(C, name="XXH3_64bits")
|
|
use iso_c_binding, only: c_ptr, c_size_t, c_int64_t
|
|
implicit none
|
|
type(c_ptr) , value :: input
|
|
integer(kind=c_size_t), value :: length
|
|
end function xxh3_lib
|
|
end interface
|
|
#else /* XXHASH */
|
|
! hash parameters
|
|
!
|
|
integer(kind=8), parameter :: prime1 = -7046029288634856825_8, &
|
|
prime2 = -4417276706812531889_8, &
|
|
prime3 = 1609587929392839161_8, &
|
|
prime4 = -8796714831421723037_8, &
|
|
prime5 = 2870177450012600261_8, &
|
|
prime6 = 6983438078262162902_8
|
|
#endif /* XXHASH */
|
|
|
|
! supported hash types
|
|
!
|
|
enum, bind(c)
|
|
enumerator hash_none
|
|
enumerator hash_xxh64
|
|
#ifdef XXHASH
|
|
enumerator hash_xxh3
|
|
#endif /* XXHASH */
|
|
end enum
|
|
|
|
private
|
|
public :: hash_info, hash_name, digest, check_digest
|
|
public :: digest_string, digest_integer
|
|
|
|
!- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
!
|
|
contains
|
|
!
|
|
!===============================================================================
|
|
!!
|
|
!!*** PUBLIC SUBROUTINES *****************************************************
|
|
!!
|
|
!===============================================================================
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! subroutine HASH_INFO:
|
|
! --------------------
|
|
!
|
|
! Subroutine returns the hash ID and the length in bytes (characters)
|
|
! by the provided hash name.
|
|
!
|
|
!===============================================================================
|
|
!
|
|
subroutine hash_info(hash_name, hash_id, hash_length)
|
|
|
|
use helpers, only : print_message
|
|
|
|
implicit none
|
|
|
|
character(len=*), intent(in) :: hash_name
|
|
integer , intent(out) :: hash_id, hash_length
|
|
|
|
character(len=*), parameter :: loc = "HASH::hash_info()"
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
select case(trim(hash_name))
|
|
case("xxh64", "XXH64")
|
|
hash_id = hash_xxh64
|
|
hash_length = 16
|
|
#ifdef XXHASH
|
|
case("xxh3", "XXH3")
|
|
hash_id = hash_xxh3
|
|
hash_length = 16
|
|
#endif /* XXHASH */
|
|
case("none")
|
|
hash_id = hash_none
|
|
hash_length = 0
|
|
case default
|
|
call print_message(loc, &
|
|
"Hash '" // trim(hash_name) // "' is not supported!")
|
|
end select
|
|
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end subroutine hash_info
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! function HASH_NAME:
|
|
! ------------------
|
|
!
|
|
! Function returns the hash name by the provided hash ID.
|
|
!
|
|
!===============================================================================
|
|
!
|
|
character(len=8) function hash_name(hash_type)
|
|
|
|
implicit none
|
|
|
|
integer, intent(in) :: hash_type
|
|
|
|
integer(kind(hash_none)) :: htype
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
htype = hash_type
|
|
select case(htype)
|
|
case(hash_xxh64)
|
|
hash_name = "xxh64"
|
|
#ifdef XXHASH
|
|
case(hash_xxh3)
|
|
hash_name = "xxh3"
|
|
#endif /* XXHASH */
|
|
case default
|
|
hash_name = "none"
|
|
end select
|
|
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end function hash_name
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! function DIGEST:
|
|
! ---------------
|
|
!
|
|
! Function calculates the digest for a given sequence of bytes.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! input - the input sequence of bytes;
|
|
! hash_type - the number corresponding to the hash type;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
integer(kind=8) function digest(input, hash_type) result(hash)
|
|
|
|
use iso_c_binding, only: c_loc
|
|
|
|
implicit none
|
|
|
|
integer(kind=1), dimension(:), target, intent(in) :: input
|
|
integer , intent(in) :: hash_type
|
|
|
|
integer(kind=8) :: length
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
select case(hash_type)
|
|
case(hash_xxh64)
|
|
#ifndef XXHASH
|
|
length = size(input, kind=8)
|
|
hash = xxh64(c_loc(input), length)
|
|
#else /* XXHASH */
|
|
length = size(input, kind=8)
|
|
hash = xxh64_lib(c_loc(input), length, 0_8)
|
|
case(hash_xxh3)
|
|
length = size(input, kind=8)
|
|
hash = xxh3_lib(c_loc(input), length)
|
|
#endif /* XXHASH */
|
|
case(hash_none)
|
|
hash = 0
|
|
end select
|
|
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end function digest
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! subroutine CHECK_DIGEST:
|
|
! -----------------------
|
|
!
|
|
! Subroutine checks if the provided digest matches the digest of
|
|
! the input data.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! loc - the location of check;
|
|
! fname - the file name;
|
|
! input - the input sequence of bytes;
|
|
! idigest - the data digest to check;
|
|
! hash_id - the hash ID;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
subroutine check_digest(loc, fname, input, idigest, hash_id)
|
|
|
|
use helpers, only : print_message
|
|
|
|
implicit none
|
|
|
|
character(len=*) , intent(in) :: loc, fname
|
|
integer(kind=1), dimension(:), intent(in) :: input
|
|
integer(kind=8) , intent(in) :: idigest
|
|
integer , intent(in) :: hash_id
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
if (hash_id == hash_none) return
|
|
|
|
if (idigest /= digest(input, hash_id)) &
|
|
call print_message(loc, trim(fname) // " seems to be corrupted!")
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end subroutine check_digest
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! subroutine DIGEST_STRING:
|
|
! ------------------------
|
|
!
|
|
! Subroutine converts the integer digest to string.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! idigest - the digest as integer;
|
|
! sdigest - the digest as string;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
subroutine digest_string(idigest, sdigest)
|
|
|
|
use helpers, only : print_message
|
|
|
|
implicit none
|
|
|
|
integer(kind=8) , intent(in) :: idigest
|
|
character(len=*), intent(inout) :: sdigest
|
|
|
|
character(len=*), parameter :: loc = "HASH::digest_string()"
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
if (len(sdigest) >= 16) then
|
|
write(sdigest,"(1z16.16)") idigest
|
|
else
|
|
call print_message(loc, &
|
|
"The string is too short to contain the whole digest!")
|
|
end if
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end subroutine digest_string
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! subroutine DIGEST_INTEGER:
|
|
! -------------------------
|
|
!
|
|
! Subroutine converts the string digest to its integer representation.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! sdigest - the digest as string;
|
|
! idigest - the digest as integer;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
subroutine digest_integer(sdigest, idigest)
|
|
|
|
implicit none
|
|
|
|
character(len=*), intent(in) :: sdigest
|
|
integer(kind=8) , intent(out) :: idigest
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
read(sdigest, fmt="(1z16)") idigest
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end subroutine digest_integer
|
|
!
|
|
!===============================================================================
|
|
!!
|
|
!!*** PRIVATE SUBROUTINES ****************************************************
|
|
!!
|
|
!===============================================================================
|
|
!
|
|
#ifndef XXHASH
|
|
!===============================================================================
|
|
!
|
|
! function XXH64:
|
|
! --------------
|
|
!
|
|
! Function calculates XXH64 hash for a given sequence of bytes.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! buffer - the buffer pointer;
|
|
! length - the buffer length;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
integer(kind=8) function xxh64(buffer, length) result(hash)
|
|
|
|
use iso_c_binding, only: c_ptr, c_f_pointer
|
|
|
|
implicit none
|
|
|
|
type(c_ptr) , intent(in) :: buffer
|
|
integer(kind=8), intent(in) :: length
|
|
|
|
integer(kind=8) :: remaining, offset
|
|
|
|
integer(kind=8), dimension(4) :: lane, chunk
|
|
|
|
integer(kind=1), dimension(:), pointer :: input
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
call c_f_pointer(buffer, input, [ length ])
|
|
|
|
hash = 0_8
|
|
offset = 1_8
|
|
remaining = length
|
|
|
|
if (remaining >= 32_8) then
|
|
lane(1) = prime6
|
|
lane(2) = prime2
|
|
lane(3) = 0_8
|
|
lane(4) = - prime1
|
|
|
|
do while (remaining >= 32_8)
|
|
chunk(1:4) = transfer(input(offset:offset+31), 1_8, 4)
|
|
|
|
lane(1) = xxh64_round(lane(1), chunk(1))
|
|
lane(2) = xxh64_round(lane(2), chunk(2))
|
|
lane(3) = xxh64_round(lane(3), chunk(3))
|
|
lane(4) = xxh64_round(lane(4), chunk(4))
|
|
|
|
offset = offset + 32_8
|
|
remaining = remaining - 32_8
|
|
end do
|
|
|
|
hash = xxh64_rotl(lane(1), 1) + xxh64_rotl(lane(2), 7) + &
|
|
xxh64_rotl(lane(3), 12) + xxh64_rotl(lane(4), 18)
|
|
|
|
hash = xxh64_merge(hash, lane(1))
|
|
hash = xxh64_merge(hash, lane(2))
|
|
hash = xxh64_merge(hash, lane(3))
|
|
hash = xxh64_merge(hash, lane(4))
|
|
|
|
else
|
|
hash = prime5
|
|
end if
|
|
|
|
hash = hash + length
|
|
|
|
do while (remaining >= 8_8)
|
|
chunk(1) = transfer(input(offset:offset+7), 1_8)
|
|
hash = ieor(hash, xxh64_round(0_8, chunk(1)))
|
|
hash = xxh64_rotl(hash, 27)
|
|
hash = hash * prime1 + prime4
|
|
|
|
offset = offset + 8_8
|
|
remaining = remaining - 8_8
|
|
end do
|
|
|
|
if (remaining >= 4_8) then
|
|
chunk(1) = transfer((/ input(offset:offset+3), 0_1, 0_1, 0_1, 0_1 /), 1_8)
|
|
hash = ieor(hash, chunk(1) * prime1)
|
|
hash = xxh64_rotl(hash, 23)
|
|
hash = hash * prime2 + prime3
|
|
|
|
offset = offset + 4_8
|
|
remaining = remaining - 4_8
|
|
end if
|
|
|
|
do while (remaining > 0_8)
|
|
chunk(1) = transfer((/ input(offset), 0_1, 0_1, 0_1, &
|
|
0_1, 0_1, 0_1, 0_1 /), 1_8)
|
|
hash = ieor(hash, chunk(1) * prime5)
|
|
hash = xxh64_rotl(hash, 11)
|
|
hash = hash * prime1
|
|
|
|
offset = offset + 1_8
|
|
remaining = remaining - 1_8
|
|
end do
|
|
|
|
hash = xxh64_aval(hash)
|
|
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end function xxh64
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! function XXH64_ROUND:
|
|
! --------------------
|
|
!
|
|
! Function processes one stripe of the input data updating
|
|
! the correponding lane.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! lane - the lane;
|
|
! input - the 8-byte data to process;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
integer(kind=8) function xxh64_round(lane, input)
|
|
|
|
implicit none
|
|
|
|
integer(kind=8), intent(in) :: lane, input
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
xxh64_round = lane + (input * prime2)
|
|
xxh64_round = xxh64_rotl(xxh64_round, 31)
|
|
xxh64_round = xxh64_round * prime1
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end function xxh64_round
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! function XXH64_MERGE:
|
|
! --------------------
|
|
!
|
|
! Function performs merging of the given lane in to the hash.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! hash - the hash to merge to;
|
|
! lane - the lane being merged;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
integer(kind=8) function xxh64_merge(hash, lane)
|
|
|
|
implicit none
|
|
|
|
integer(kind=8), intent(in) :: hash, lane
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
xxh64_merge = ieor(hash, xxh64_round(0_8, lane))
|
|
xxh64_merge = xxh64_merge * prime1 + prime4
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end function xxh64_merge
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! function XXH64_AVAL:
|
|
! -------------------
|
|
!
|
|
! Function calculates the final mix of the hash.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! hash - the hash to mix;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
integer(kind=8) function xxh64_aval(hash)
|
|
|
|
implicit none
|
|
|
|
integer(kind=8), intent(in) :: hash
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
xxh64_aval = hash
|
|
xxh64_aval = ieor(xxh64_aval, ishft(xxh64_aval, -33)) * prime2
|
|
xxh64_aval = ieor(xxh64_aval, ishft(xxh64_aval, -29)) * prime3
|
|
xxh64_aval = ieor(xxh64_aval, ishft(xxh64_aval, -32))
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end function xxh64_aval
|
|
!
|
|
!===============================================================================
|
|
!
|
|
! function XXH64_ROTL:
|
|
! -------------------
|
|
!
|
|
! Function calculates the rotation of the input 8-byte word by a given amount.
|
|
!
|
|
! Arguments:
|
|
!
|
|
! byte - the byte to be rotates;
|
|
! amount - the amount by which rotate the input byte;
|
|
!
|
|
!===============================================================================
|
|
!
|
|
integer(kind=8) function xxh64_rotl(byte, amount)
|
|
|
|
implicit none
|
|
|
|
integer(kind=8), intent(in) :: byte
|
|
integer(kind=4), intent(in) :: amount
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
xxh64_rotl = ior(ishft(byte, amount), ishft(byte, amount - 64))
|
|
return
|
|
|
|
!-------------------------------------------------------------------------------
|
|
!
|
|
end function xxh64_rotl
|
|
#endif /* ~XXHASH */
|
|
|
|
!===============================================================================
|
|
!
|
|
end module hash
|