#ifndef MS_UTILS_HPP
#define MS_UTILS_HPP
#include "common.hpp"
#include "RTC/Consts.hpp"
#include <openssl/evp.h>
#include <cassert>
#include <cmath>
#include <cstring>
#include <limits>
#include <random>
#include <string>
#include <type_traits>
#ifdef _WIN32
#include <ws2ipdef.h>
#include <intrin.h>
#define __builtin_popcount __popcnt
#endif
namespace Utils
{
class IP
{
public:
static int GetFamily(const std::string& ip);
static void GetAddressInfo(const struct sockaddr* addr, int& family, std::string& ip, uint16_t& port);
static size_t GetAddressLen(const struct sockaddr* addr);
static bool CompareAddresses(const struct sockaddr* addr1, const struct sockaddr* addr2)
{
if (
addr1->sa_family != addr2->sa_family ||
(addr1->sa_family != AF_INET && addr1->sa_family != AF_INET6) ||
(addr2->sa_family != AF_INET && addr2->sa_family != AF_INET6))
{
return false;
}
if (
reinterpret_cast<const struct sockaddr_in*>(addr1)->sin_port !=
reinterpret_cast<const struct sockaddr_in*>(addr2)->sin_port)
{
return false;
}
switch (addr1->sa_family)
{
case AF_INET:
{
return (
reinterpret_cast<const struct sockaddr_in*>(addr1)->sin_addr.s_addr ==
reinterpret_cast<const struct sockaddr_in*>(addr2)->sin_addr.s_addr);
}
case AF_INET6:
{
return (
std::memcmp(
std::addressof(reinterpret_cast<const struct sockaddr_in6*>(addr1)->sin6_addr),
std::addressof(reinterpret_cast<const struct sockaddr_in6*>(addr2)->sin6_addr),
16) == 0);
}
default:
{
return false;
}
}
}
static struct sockaddr_storage CopyAddress(const struct sockaddr* addr)
{
struct sockaddr_storage copiedAddr{};
switch (addr->sa_family)
{
case AF_INET:
std::memcpy(std::addressof(copiedAddr), addr, sizeof(struct sockaddr_in));
break;
case AF_INET6:
std::memcpy(std::addressof(copiedAddr), addr, sizeof(struct sockaddr_in6));
break;
default:;
}
return copiedAddr;
}
static std::string NormalizeIp(std::string& ip);
};
class File
{
public:
static void CheckFile(const char* file);
};
class Byte
{
public:
static uint8_t Get1Byte(const uint8_t* data, size_t i)
{
return data[i];
}
static uint16_t Get2Bytes(const uint8_t* data, size_t i)
{
return uint16_t{ data[i + 1] } | uint16_t{ data[i] } << 8;
}
static uint32_t Get3Bytes(const uint8_t* data, size_t i)
{
return uint32_t{ data[i + 2] } | uint32_t{ data[i + 1] } << 8 | uint32_t{ data[i] } << 16;
}
static int32_t Get3BytesSigned(const uint8_t* data, size_t i)
{
auto byte2 = data[i]; auto byte1 = data[i + 1];
auto byte0 = data[i + 2];
const uint8_t extension = byte2 & 0b10000000 ? 0b11111111 : 0b00000000;
return int32_t{ byte0 } | (int32_t{ byte1 } << 8) | (int32_t{ byte2 } << 16) |
(int32_t{ extension } << 24);
}
static uint32_t Get4Bytes(const uint8_t* data, size_t i)
{
return uint32_t{ data[i + 3] } | uint32_t{ data[i + 2] } << 8 |
uint32_t{ data[i + 1] } << 16 | uint32_t{ data[i] } << 24;
}
static uint64_t Get8Bytes(const uint8_t* data, size_t i)
{
return uint64_t{ Byte::Get4Bytes(data, i) } << 32 | Byte::Get4Bytes(data, i + 4);
}
static void Set1Byte(uint8_t* data, size_t i, uint8_t value)
{
data[i] = value;
}
static void Set2Bytes(uint8_t* data, size_t i, uint16_t value)
{
data[i + 1] = static_cast<uint8_t>(value);
data[i] = static_cast<uint8_t>(value >> 8);
}
static void Set3Bytes(uint8_t* data, size_t i, uint32_t value)
{
data[i + 2] = static_cast<uint8_t>(value);
data[i + 1] = static_cast<uint8_t>(value >> 8);
data[i] = static_cast<uint8_t>(value >> 16);
}
static void Set3BytesSigned(uint8_t* data, size_t i, int32_t value)
{
data[i + 2] = static_cast<int8_t>(value);
data[i + 1] = static_cast<uint8_t>(value >> 8);
data[i] = static_cast<uint8_t>(value >> 16);
}
static void Set4Bytes(uint8_t* data, size_t i, uint32_t value)
{
data[i + 3] = static_cast<uint8_t>(value);
data[i + 2] = static_cast<uint8_t>(value >> 8);
data[i + 1] = static_cast<uint8_t>(value >> 16);
data[i] = static_cast<uint8_t>(value >> 24);
}
static void Set8Bytes(uint8_t* data, size_t i, uint64_t value)
{
data[i + 7] = static_cast<uint8_t>(value);
data[i + 6] = static_cast<uint8_t>(value >> 8);
data[i + 5] = static_cast<uint8_t>(value >> 16);
data[i + 4] = static_cast<uint8_t>(value >> 24);
data[i + 3] = static_cast<uint8_t>(value >> 32);
data[i + 2] = static_cast<uint8_t>(value >> 40);
data[i + 1] = static_cast<uint8_t>(value >> 48);
data[i] = static_cast<uint8_t>(value >> 56);
}
template<typename T>
typename std::enable_if<std::is_unsigned<T>::value, bool>::type static IsPaddedTo4Bytes(T size)
{
return (size & 0x03) == 0u;
}
template<typename T>
typename std::enable_if<std::is_unsigned<T>::value, bool>::type static IsPaddedTo8Bytes(T size)
{
return (size & 0x07) == 0u;
}
template<typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type static PadTo4Bytes(T size)
{
return (size + 3) & ~static_cast<T>(0x03);
}
template<typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type static PadTo8Bytes(T size)
{
return (size + 7) & ~static_cast<T>(0x07);
}
};
class Bits
{
public:
static size_t CountSetBits(const uint16_t mask)
{
return static_cast<size_t>(__builtin_popcount(mask));
}
};
class Crypto
{
public:
static void ClassInit();
static void ClassDestroy();
template<typename T>
static T GetRandomUInt(T min, T max)
{
static_assert(
std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t> ||
std::is_same_v<T, size_t>,
"T must be uint16_t, uint32_t, uint64_t, size_t");
std::uniform_int_distribution<T> dist(min, max);
return dist(Crypto::rng);
}
static std::string GetRandomString(size_t len);
static uint32_t GetCRC32(const uint8_t* data, size_t size);
static uint32_t GetCRC32c(const uint8_t* data, size_t size);
static const uint8_t* GetHmacSha1(const char* key, size_t keyLen, const uint8_t* data, size_t len);
static void WriteRandomBytes(uint8_t* buffer, size_t len);
private:
thread_local static std::mt19937_64 rng;
thread_local static EVP_MAC* mac;
thread_local static EVP_MAC_CTX* hmacSha1Ctx;
thread_local static uint8_t hmacSha1Buffer[];
static const uint32_t Crc32Table[256];
static const uint32_t Crc32cTable[256];
};
class String
{
public:
static void ToLowerCase(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
}
static std::string Base64Encode(const uint8_t* data, size_t len);
static std::string Base64Encode(const std::string& str);
static uint8_t* Base64Decode(const uint8_t* data, size_t len, size_t& outLen);
static uint8_t* Base64Decode(const std::string& str, size_t& outLen);
};
class Number
{
public:
template<typename T, uint8_t N = 0>
static bool IsEqualThan(T lhs, T rhs)
{
static_assert(
std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>,
"T must be uint8_t, uint16_t, uint32_t or uint64_t");
constexpr T MaxValue = (N == 0) ? std::numeric_limits<T>::max() : ((1 << N) - 1);
lhs &= MaxValue;
rhs &= MaxValue;
return (lhs == rhs);
}
template<typename T, uint8_t N = 0>
static bool IsHigherThan(T lhs, T rhs)
{
static_assert(
std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>,
"T must be uint8_t, uint16_t, uint32_t or uint64_t");
constexpr T MaxValue = (N == 0) ? std::numeric_limits<T>::max() : ((1 << N) - 1);
lhs &= MaxValue;
rhs &= MaxValue;
return ((lhs > rhs) && (lhs - rhs <= MaxValue / 2)) ||
((rhs > lhs) && (rhs - lhs > MaxValue / 2));
}
template<typename T, uint8_t N = 0>
static bool IsLowerThan(T lhs, T rhs)
{
static_assert(
std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>,
"T must be uint8_t, uint16_t, uint32_t or uint64_t");
constexpr T MaxValue = (N == 0) ? std::numeric_limits<T>::max() : ((1 << N) - 1);
lhs &= MaxValue;
rhs &= MaxValue;
return ((rhs > lhs) && (rhs - lhs <= MaxValue / 2)) ||
((lhs > rhs) && (lhs - rhs > MaxValue / 2));
}
template<typename T, uint8_t N = 0>
static bool IsHigherOrEqualThan(T lhs, T rhs)
{
static_assert(
std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>,
"T must be uint8_t, uint16_t, uint32_t or uint64_t");
constexpr T MaxValue = (N == 0) ? std::numeric_limits<T>::max() : ((1 << N) - 1);
lhs &= MaxValue;
rhs &= MaxValue;
return (lhs == rhs) || ((lhs > rhs) && (lhs - rhs <= MaxValue / 2)) ||
((rhs > lhs) && (rhs - lhs > MaxValue / 2));
}
template<typename T, uint8_t N = 0>
static bool IsLowerOrEqualThan(T lhs, T rhs)
{
static_assert(
std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> ||
std::is_same_v<T, uint64_t>,
"T must be uint8_t, uint16_t, uint32_t or uint64_t");
constexpr T MaxValue = (N == 0) ? std::numeric_limits<T>::max() : ((1 << N) - 1);
lhs &= MaxValue;
rhs &= MaxValue;
return (lhs == rhs) || ((rhs > lhs) && (rhs - lhs <= MaxValue / 2)) ||
((lhs > rhs) && (lhs - rhs > MaxValue / 2));
}
template<typename T, T M>
static T ForwardDiff(T a, T b) requires(M > 0)
{
static_assert(std::is_unsigned<T>::value, "type must be an unsigned integer");
assert(a < M);
assert(b < M);
return a <= b ? b - a : M - (a - b);
}
template<typename T, T M>
static T ForwardDiff(T a, T b) requires(M == 0)
{
static_assert(std::is_unsigned<T>::value, "type must be an unsigned integer");
return b - a;
}
template<typename T>
static T ForwardDiff(T a, T b)
{
return ForwardDiff<T, 0>(a, b);
}
template<typename T, T M>
static T ReverseDiff(T a, T b) requires(M > 0)
{
static_assert(std::is_unsigned<T>::value, "type must be an unsigned integer");
assert(a < M);
assert(b < M);
return b <= a ? a - b : M - (b - a);
}
template<typename T, T M>
static T ReverseDiff(T a, T b) requires(M == 0)
{
static_assert(std::is_unsigned<T>::value, "type must be an unsigned integer");
return a - b;
}
template<typename T>
static T ReverseDiff(T a, T b)
{
return ReverseDiff<T, 0>(a, b);
}
};
class Time
{
private:
static constexpr uint32_t UnixNtpOffset{ 0x83AA7E80 };
static constexpr uint64_t NtpFractionalUnit{ 1LL << 32 };
public:
struct Ntp
{
uint32_t seconds;
uint32_t fractions;
};
static Time::Ntp TimeMs2Ntp(uint64_t ms)
{
Time::Ntp ntp{};
ntp.seconds = ms / 1000;
ntp.fractions =
static_cast<uint32_t>((static_cast<double>(ms % 1000) / 1000) * NtpFractionalUnit);
return ntp;
}
static uint64_t Ntp2TimeMs(Time::Ntp ntp)
{
return (
(static_cast<uint64_t>(ntp.seconds) * 1000) +
static_cast<uint64_t>(
std::round((static_cast<double>(ntp.fractions) * 1000) / NtpFractionalUnit)));
}
static uint32_t TimeMsToAbsSendTime(uint64_t ms)
{
return static_cast<uint32_t>(((ms << 18) + 500) / 1000) & 0x00FFFFFF;
}
};
class BitStream
{
public:
BitStream(uint8_t* data, size_t len);
~BitStream() = default;
const uint8_t* GetData() const;
size_t GetLength() const;
uint32_t GetOffset() const;
void Reset();
uint8_t GetBit();
uint32_t GetBits(size_t count);
uint32_t GetLeftBits() const;
uint32_t GetNumBits(uint32_t n) const;
std::optional<uint32_t> ReadNs(uint32_t n);
void SkipBits(size_t count);
void Write(uint32_t offset, uint32_t n, uint32_t v);
void PutBit(uint8_t bit);
void PutBits(uint32_t count, uint32_t bits);
private:
void PutBit(uint32_t offset, uint8_t bit);
void PutBits(uint32_t offset, uint32_t count, uint32_t bits);
private:
uint8_t data[RTC::Consts::TwoBytesRtpExtensionMaxLength];
uint32_t len{ 0 };
uint32_t offset{ 0 };
};
}
#endif