#pragma once #include "routing/routing_exceptions.hpp" #include "coding/bit_streams.hpp" #include "coding/elias_coder.hpp" #include "base/assert.hpp" #include "base/bits.hpp" #include "base/checked_cast.hpp" #include "base/macros.hpp" #include #include namespace routing { template T ReadDelta(BitReader & reader) { static_assert(std::is_unsigned::value, "T should be an unsigned type"); uint64_t const decoded = coding::DeltaCoder::Decode(reader); if (decoded > std::numeric_limits::max()) { MYTHROW(CorruptedDataException, ("Decoded value", decoded, "out of limit", std::numeric_limits::max())); } return static_cast(decoded); } template void WriteDelta(BitWriter & writer, T value) { static_assert(std::is_unsigned::value, "T should be an unsigned type"); ASSERT_GREATER(value, 0, ()); bool const success = coding::DeltaCoder::Encode(writer, static_cast(value)); ASSERT(success, ()); UNUSED_VALUE(success); } template T ReadGamma(BitReader & reader) { static_assert(std::is_unsigned::value, "T should be an unsigned type"); uint64_t const decoded = coding::GammaCoder::Decode(reader); if (decoded > std::numeric_limits::max()) { MYTHROW(CorruptedDataException, ("Decoded value", decoded, "out of limit", std::numeric_limits::max())); } return static_cast(decoded); } template void WriteGamma(BitWriter & writer, T value) { static_assert(std::is_unsigned::value, "T should be an unsigned type"); ASSERT_GREATER(value, 0, ()); bool const success = coding::GammaCoder::Encode(writer, static_cast(value)); ASSERT(success, ()); UNUSED_VALUE(success); } // C++ standard says: // if the value can't be represented in the destination unsigned type, // the result is implementation-defined. // // ModularCast makes unambiguous conversion from unsigned value to signed. // The resulting value is the least signed integer congruent to the source integer // (modulo 2^n where n is the number of bits used to represent the unsigned type) template > Signed ModularCast(Unsigned value) { static_assert(std::is_unsigned::value, "T should be an unsigned type"); if (value <= static_cast(std::numeric_limits::max())) return static_cast(value); auto constexpr minSignedT = std::numeric_limits::min(); return static_cast(value - static_cast(minSignedT)) + minSignedT; } // Encodes current as delta compared with prev. template > UnsignedT EncodeZigZagDelta(T prev, T current) { static_assert(std::is_integral::value, "T should be an integral type"); auto const unsignedPrev = static_cast(prev); auto const unsignedCurrent = static_cast(current); auto originalDelta = ModularCast(static_cast(unsignedCurrent - unsignedPrev)); static_assert(std::is_same>::value, "It's expected that ModuleCast returns SignedT"); auto encodedDelta = bits::ZigZagEncode(originalDelta); static_assert(std::is_same::value, "It's expected that bits::ZigZagEncode returns UnsignedT"); return encodedDelta; } // Reverse function for EncodeZigZagDelta. template > T DecodeZigZagDelta(T prev, UnsignedT delta) { static_assert(std::is_integral::value, "T should be an integral type"); auto decoded = bits::ZigZagDecode(delta); static_assert(std::is_same>::value, "It's expected that bits::ZigZagDecode returns SignedT"); return prev + static_cast(decoded); } } // namespace routing