Files
nostalgia/deps/ox/src/ox/std/byteswap.hpp

212 lines
5.2 KiB
C++

/*
* Copyright 2015 - 2022 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "defines.hpp"
#include "stddef.hpp"
#include "types.hpp"
#include "typetraits.hpp"
namespace ox {
template<typename T>
[[nodiscard]]
constexpr T byteSwap(typename enable_if<sizeof(T) == 1, T>::type i) noexcept {
return i;
}
template<typename T>
[[nodiscard]]
constexpr T byteSwap(typename enable_if<sizeof(T) == 2, T>::type i) noexcept {
return static_cast<T>(i << 8) | static_cast<T>(i >> 8);
}
template<typename T>
[[nodiscard]]
constexpr T byteSwap(typename enable_if<sizeof(T) == 4, T>::type i) noexcept {
return ((i >> 24) & 0x000000ff) |
((i >> 8) & 0x0000ff00) |
((i << 8) & 0x00ff0000) |
((i << 24) & 0xff000000);
}
template<typename T>
[[nodiscard]]
constexpr T byteSwap(typename enable_if<sizeof(T) == 8, T>::type i) noexcept {
return ((i >> 56) & 0x00000000000000ff) |
((i >> 40) & 0x000000000000ff00) |
((i >> 24) & 0x0000000000ff0000) |
((i >> 8) & 0x00000000ff000000) |
((i << 8) & 0x000000ff00000000) |
((i << 24) & 0x0000ff0000000000) |
((i << 40) & 0x00ff000000000000) |
((i << 56) & 0xff00000000000000);
}
/**
* Takes an int and byte swaps if the platform is the given condition is true.
*/
template<typename T, bool byteSwap>
[[nodiscard]]
constexpr T conditionalByteSwap(T i) noexcept {
if constexpr(byteSwap) {
return ox::byteSwap<T>(i);
} else {
return i;
}
}
template<typename T>
[[nodiscard]]
constexpr T fromLittleEndian(T i) noexcept {
return conditionalByteSwap<T, defines::BigEndian>(i);
}
template<typename T>
[[nodiscard]]
constexpr T fromBigEndian(T i) noexcept {
return conditionalByteSwap<T, defines::LittleEndian>(i);
}
template<typename T>
[[nodiscard]]
constexpr T toLittleEndian(T i) noexcept {
return conditionalByteSwap<T, defines::BigEndian>(i);
}
template<typename T>
[[nodiscard]]
constexpr T toBigEndian(T i) noexcept {
return conditionalByteSwap<T, defines::LittleEndian>(i);
}
template<typename T, bool byteSwap>
class OX_PACKED ByteSwapInteger {
private:
T m_value;
public:
constexpr ByteSwapInteger() noexcept = default;
constexpr ByteSwapInteger(const ByteSwapInteger &other) noexcept {
m_value = other.m_value;
}
constexpr ByteSwapInteger(T value) noexcept: m_value(conditionalByteSwap<T, byteSwap>(value)) {
}
constexpr ByteSwapInteger &operator=(const ByteSwapInteger &other) noexcept {
m_value = other.m_value;
return *this;
}
constexpr ByteSwapInteger &operator=(T value) noexcept {
m_value = conditionalByteSwap<T, byteSwap>(value);
return *this;
}
constexpr operator T() const noexcept {
return conditionalByteSwap<T, byteSwap>(m_value);
}
constexpr ByteSwapInteger operator+=(T other) noexcept {
const auto newVal = static_cast<T>(*this + other);
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return *this;
}
constexpr ByteSwapInteger operator-=(T other) noexcept {
const auto newVal = static_cast<T>(*this - other);
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return *this;
}
constexpr ByteSwapInteger operator*=(T other) noexcept {
const auto newVal = static_cast<T>(*this * other);
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return *this;
}
constexpr ByteSwapInteger operator/=(T other) noexcept {
const auto newVal = static_cast<T>(*this / other);
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return *this;
}
// Prefix increment
constexpr ByteSwapInteger operator++() noexcept {
return operator+=(1);
}
// Prefix decrement
constexpr ByteSwapInteger operator--() noexcept {
return operator-=(1);
}
template<typename I>
constexpr T operator&=(I other) noexcept {
auto newVal = *this & other;
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return newVal;
}
template<typename I>
constexpr T operator|=(I other) noexcept {
auto newVal = *this | other;
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return newVal;
}
template<typename I>
constexpr T operator^=(I other) noexcept {
auto newVal = *this ^ other;
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return newVal;
}
template<typename I>
constexpr T operator>>=(I other) noexcept {
auto newVal = *this >> other;
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return newVal;
}
template<typename I>
constexpr T operator<<=(I other) noexcept {
auto newVal = *this << other;
m_value = conditionalByteSwap<T, byteSwap>(newVal);
return newVal;
}
[[nodiscard]]
constexpr auto get() const noexcept -> T {
return static_cast<T>(*this);
}
/**
* Returns the integer as it is stored. If it is stored as little endian,
* a little endian integer is returned regardless of the endianness of
* the system.
*/
[[nodiscard]]
constexpr auto raw() const noexcept -> T {
return m_value;
}
};
template<typename T>
using LittleEndian = ByteSwapInteger<T, defines::BigEndian>;
template<typename T>
using BigEndian = ByteSwapInteger<T, defines::LittleEndian>;
}