#pragma once
// little.hpp - integers of known endianness
//
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
// 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/>.
#include "fwd.hpp"
#include <endian.h>
#include <cstdint>
namespace tmwa
{
// We implement our own actual swapping, because glibc emits assembly
// instead of letting the *compiler* do what it does best.
#if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
# error "broken endians"
#endif
namespace ints
{
// gcc doesn't always provide a builtin for this,
// but it *does* optimize hand-written ones
constexpr
uint16_t bswap16(uint16_t v)
{
return v >> 8 | v << 8;
}
// TODO hoist this to byte.hpp and also implement big.hpp
struct Byte
{
uint8_t value;
};
struct Little16
{
uint8_t data[2];
};
struct Little32
{
uint8_t data[4];
};
struct Little64
{
uint8_t data[8];
};
inline __attribute__((warn_unused_result))
bool native_to_network(Byte *net, uint8_t nat)
{
net->value = nat;
return true;
}
inline __attribute__((warn_unused_result))
bool native_to_network(Little16 *net, uint16_t nat)
{
if (__BYTE_ORDER == __BIG_ENDIAN)
nat = bswap16(nat);
__builtin_memcpy(net, &nat, 2);
return true;
}
inline __attribute__((warn_unused_result))
bool native_to_network(Little32 *net, uint32_t nat)
{
if (__BYTE_ORDER == __BIG_ENDIAN)
nat = __builtin_bswap32(nat);
__builtin_memcpy(net, &nat, 4);
return true;
}
inline __attribute__((warn_unused_result))
bool native_to_network(Little64 *net, uint64_t nat)
{
if (__BYTE_ORDER == __BIG_ENDIAN)
nat = __builtin_bswap64(nat);
__builtin_memcpy(net, &nat, 8);
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(uint8_t *nat, Byte net)
{
*nat = net.value;
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(uint16_t *nat, Little16 net)
{
uint16_t tmp;
__builtin_memcpy(&tmp, &net, 2);
if (__BYTE_ORDER == __BIG_ENDIAN)
tmp = bswap16(tmp);
*nat = tmp;
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(uint32_t *nat, Little32 net)
{
uint32_t tmp;
__builtin_memcpy(&tmp, &net, 4);
if (__BYTE_ORDER == __BIG_ENDIAN)
tmp = __builtin_bswap32(tmp);
*nat = tmp;
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(uint64_t *nat, Little64 net)
{
uint64_t tmp;
__builtin_memcpy(&tmp, &net, 8);
if (__BYTE_ORDER == __BIG_ENDIAN)
tmp = __builtin_bswap64(tmp);
*nat = tmp;
return true;
}
inline __attribute__((warn_unused_result))
bool native_to_network(Byte *net, int8_t nat)
{
net->value = nat;
return true;
}
inline __attribute__((warn_unused_result))
bool native_to_network(Little16 *net, int16_t nat)
{
if (__BYTE_ORDER == __BIG_ENDIAN)
nat = bswap16(nat);
__builtin_memcpy(net, &nat, 2);
return true;
}
inline __attribute__((warn_unused_result))
bool native_to_network(Little32 *net, int32_t nat)
{
if (__BYTE_ORDER == __BIG_ENDIAN)
nat = __builtin_bswap32(nat);
__builtin_memcpy(net, &nat, 4);
return true;
}
inline __attribute__((warn_unused_result))
bool native_to_network(Little64 *net, int64_t nat)
{
if (__BYTE_ORDER == __BIG_ENDIAN)
nat = __builtin_bswap64(nat);
__builtin_memcpy(net, &nat, 8);
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(int8_t *nat, Byte net)
{
*nat = net.value;
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(int16_t *nat, Little16 net)
{
int16_t tmp;
__builtin_memcpy(&tmp, &net, 2);
if (__BYTE_ORDER == __BIG_ENDIAN)
tmp = bswap16(tmp);
*nat = tmp;
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(int32_t *nat, Little32 net)
{
int32_t tmp;
__builtin_memcpy(&tmp, &net, 4);
if (__BYTE_ORDER == __BIG_ENDIAN)
tmp = __builtin_bswap32(tmp);
*nat = tmp;
return true;
}
inline __attribute__((warn_unused_result))
bool network_to_native(int64_t *nat, Little64 net)
{
int64_t tmp;
__builtin_memcpy(&tmp, &net, 8);
if (__BYTE_ORDER == __BIG_ENDIAN)
tmp = __builtin_bswap64(tmp);
*nat = tmp;
return true;
}
} // namespace ints
using ints::Byte;
using ints::Little16;
using ints::Little32;
using ints::Little64;
} // namespace tmwa