Skip to content

Commit

Permalink
* Added hash template class for 256 & 512 bit hashes
Browse files Browse the repository at this point in the history
* Added optional picosha2 dependency for calculating hashes
  • Loading branch information
Cooolrik committed Feb 10, 2024
1 parent 213df16 commit a1d28be
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 9 deletions.
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,17 @@ if(CTLE_BUILD_TESTS)
GIT_TAG 6fdeff4d67f3db493d47c44da20aa1efaa6574ef # (2020 Aug 06)
)

# picosha2 - used by hash.h to calculate sha256 hashes
FetchContent_Declare(
picosha2
GIT_REPOSITORY https://github.com/okdshin/PicoSHA2.git
GIT_TAG 27fcf6979298949e8a462e16d09a0351c18fcaf2 # (2022 Aug 08)
)

FetchContent_MakeAvailable(
googletest
glm
picosha2
)

# lots of warnings and all warnings as errors
Expand Down Expand Up @@ -71,6 +79,7 @@ if(CTLE_BUILD_TESTS)
./ctle/thread_safe_map.h
./ctle/types.h
./ctle/uuid.h
./ctle/hash.h
./ctle/util.h
./ctle/_macros.inl
./ctle/_undef_macros.inl
Expand All @@ -94,6 +103,7 @@ if(CTLE_BUILD_TESTS)
./unit_tests/test_types.cpp
./unit_tests/test_util.cpp
./unit_tests/test_uuid.cpp
./unit_tests/test_hash.cpp
./unit_tests/test_macros.cpp

./ctle.natvis
Expand All @@ -102,6 +112,7 @@ if(CTLE_BUILD_TESTS)
target_include_directories(
unit_tests
PUBLIC ${glm_SOURCE_DIR}
PUBLIC ${picosha2_SOURCE_DIR}
)

target_link_libraries(
Expand Down
1 change: 1 addition & 0 deletions ctle/ctle.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
#include "../ctle/types.h"
#include "../ctle/util.h"
#include "../ctle/uuid.h"
#include "../ctle/hash.h"
200 changes: 200 additions & 0 deletions ctle/hash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// ctle Copyright (c) 2021 Ulrik Lindahl
// Licensed under the MIT license https://github.com/Cooolrik/ctle/blob/main/LICENSE
#pragma once

#include <cstdint>
#include <functional>
#include <iosfwd>

#include "status.h"

namespace ctle
{

// define hash for message digests, either 256 or 512 bits in size (32 or 64 bytes)
template<size_t _Size>
struct hash
{
static_assert( _Size == 256 || _Size == 512 , "Hash size must be 256 or 512");
static constexpr const size_t hash_size = _Size;

union
{
uint64_t _data_q[_Size/64] = {};
uint8_t data[_Size/8];
};

// compare operators
bool operator<( const hash &other ) const noexcept;
bool operator==( const hash &other ) const noexcept;
bool operator!=( const hash &other ) const noexcept;
};

template<size_t _Size>
inline bool hash<_Size>::operator<( const hash &right ) const noexcept
{
const uint8_t *u1 = this->data;
const uint8_t *u2 = right.data;

// hash values are stored big-endian, so MSB is first byte (index 0), LSB is last byte (index 31 or 63)
size_t n = _Size/8;
do
{
if( *u1 != *u2 ) // not equal, early exit, check if more or less than
{
if( *u1 < *u2 )
return true; // less than
else
return false; // more than
}
++u1;
++u2;
--n;
} while( n>0 );

return false; // equal, so not less
};

template<size_t _Size>
inline bool hash<_Size>::operator==( const hash &right ) const noexcept
{
const uint64_t *u1 = this->_data_q;
const uint64_t *u2 = right._data_q;

// hash values are stored big-endian, so MSB is first byte (index 0), LSB is last byte (index 31 or 63)
size_t n = _Size/64;
do
{
if( *u1 != *u2 ) // not equal, return false
{
return false;
}
++u1;
++u2;
--n;
} while( n>0 );

return true; // equal
};

template<size_t _Size>
inline bool hash<_Size>::operator!=( const hash &right ) const noexcept
{
return !this->operator==( right );
};

template<size_t _Size>
inline size_t calculate_size_hash( const hash<_Size> &value )
{
static_assert( sizeof( std::size_t ) == sizeof( std::uint64_t ), "The hashing code only works for 64bit size_t" );
size_t hval = value._data_q[0];
for( size_t inx=1; inx<(_Size/64); ++inx )
{
hval ^= value._data_q[inx];
}
return hval;
}

status calculate_sha256_hash( uint8_t destDigest[32], const uint8_t *srcData, size_t srcDataLength );
status calculate_sha256_hash( hash<256> &destHash, const uint8_t *srcData, size_t srcDataLength );

}
//namespace ctle

template <>
struct std::hash<ctle::hash<256>>
{
std::size_t operator()( const ctle::hash<256> &val ) const noexcept
{
return ctle::calculate_size_hash<256>( val );
}
};

template <>
struct std::hash<ctle::hash<512>>
{
std::size_t operator()( const ctle::hash<512> &val ) const noexcept
{
return ctle::calculate_size_hash<512>( val );
}
};

std::ostream &operator<<( std::ostream &os, const ctle::hash<256> &_hash );
std::ostream &operator<<( std::ostream &os, const ctle::hash<512> &_hash );

#ifdef CTLE_IMPLEMENTATION

#include <random>
#include <algorithm>
#include <memory>
#include <array>

#include "string_funcs.h"

namespace ctle
{

template <> std::string value_to_hex_string<hash<256>>( const hash<256> &value )
{
static_assert( sizeof( value ) == 32, "Error: hash<256> is assumed to be of size 32." );
return bytes_to_hex_string( &value, 32 );
}

template <> std::string value_to_hex_string<hash<512>>( const hash<512> &value )
{
static_assert( sizeof( value ) == 64, "Error: hash<512> is assumed to be of size 64." );
return bytes_to_hex_string( &value, 64 );
}

template <> hash<256> hex_string_to_value<hash<256>>( const char *hex_string )
{
hash<256> value;
static_assert( sizeof( value ) == 32, "Error: hash<256> is assumed to be of size 32." );
hex_string_to_bytes( &value, hex_string, 32 );
return value;
}

template <> hash<512> hex_string_to_value<hash<512>>( const char *hex_string )
{
hash<512> value;
static_assert( sizeof( value ) == 64, "Error: hash<512> is assumed to be of size 64." );
hex_string_to_bytes( &value, hex_string, 64 );
return value;
}

// if picosha-2 is included, implement the hash generation function for hash<256>
#ifdef PICOSHA2_H

status calculate_sha256_hash( uint8_t destDigest[32], const uint8_t *srcData, size_t srcDataLength )
{
picosha2::hash256_one_by_one hasher;

hasher.process( srcData, srcData + srcDataLength );
hasher.finish();
hasher.get_hash_bytes( destDigest, destDigest + 32 );

return status::ok;
}

status calculate_sha256_hash( hash<256> &destHash, const uint8_t *srcData, size_t srcDataLength )
{
return calculate_sha256_hash( destHash.data, srcData, srcDataLength );
}
#endif

}
//namespace ctle

std::ostream &operator<<( std::ostream &os, const ctle::hash<256> &_hash )
{
os << ctle::value_to_hex_string( _hash );
return os;
}

std::ostream &operator<<( std::ostream &os, const ctle::hash<512> &_hash )
{
os << ctle::value_to_hex_string( _hash );
return os;
}

#endif
12 changes: 3 additions & 9 deletions ctle/uuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,8 @@ inline bool uuid::operator!=( const ctle::uuid &right ) const noexcept
}
//namespace ctle

namespace std
{
template <>
struct hash<ctle::uuid>
struct std::hash<ctle::uuid>
{
std::size_t operator()( ctle::uuid const &val ) const noexcept
{
Expand All @@ -84,7 +82,7 @@ struct hash<ctle::uuid>
};

std::ostream &operator<<( std::ostream &os, const ctle::uuid &_uuid );
}

//namespace std

#ifdef CTLE_IMPLEMENTATION
Expand All @@ -100,7 +98,7 @@ namespace ctle
{
const uuid uuid::nil;

template <> inline std::string value_to_hex_string( const uuid &value )
template <> std::string value_to_hex_string( const uuid &value )
{
std::string ret;

Expand Down Expand Up @@ -182,14 +180,10 @@ uuid uuid::generate()
}
//namespace ctle

namespace std
{
std::ostream &operator<<( std::ostream &os, const ctle::uuid &_uuid )
{
os << ctle::value_to_hex_string( _uuid );
return os;
}
}
//namespace std

#endif
Loading

0 comments on commit a1d28be

Please sign in to comment.