You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
5.5 KiB
235 lines
5.5 KiB
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef UTIL_BIG_ENDIAN_H_
|
|
#define UTIL_BIG_ENDIAN_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <cstring>
|
|
#include <type_traits>
|
|
|
|
namespace openscreen {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Note: All of the functions here are defined inline, as any half-decent
|
|
// compiler will optimize them to a single integer constant or single
|
|
// instruction on most architectures.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Returns true if this code is running on a big-endian architecture.
|
|
inline bool IsBigEndianArchitecture() {
|
|
const uint16_t kTestWord = 0x0100;
|
|
uint8_t bytes[sizeof(kTestWord)];
|
|
memcpy(bytes, &kTestWord, sizeof(bytes));
|
|
return !!bytes[0];
|
|
}
|
|
|
|
namespace internal {
|
|
|
|
template <int size>
|
|
struct MakeSizedUnsignedInteger;
|
|
|
|
template <>
|
|
struct MakeSizedUnsignedInteger<1> {
|
|
using type = uint8_t;
|
|
};
|
|
|
|
template <>
|
|
struct MakeSizedUnsignedInteger<2> {
|
|
using type = uint16_t;
|
|
};
|
|
|
|
template <>
|
|
struct MakeSizedUnsignedInteger<4> {
|
|
using type = uint32_t;
|
|
};
|
|
|
|
template <>
|
|
struct MakeSizedUnsignedInteger<8> {
|
|
using type = uint64_t;
|
|
};
|
|
|
|
template <int size>
|
|
inline typename MakeSizedUnsignedInteger<size>::type ByteSwap(
|
|
typename MakeSizedUnsignedInteger<size>::type x) {
|
|
static_assert(size <= 8,
|
|
"ByteSwap() specialization missing in " __FILE__
|
|
". "
|
|
"Are you trying to use an integer larger than 64 bits?");
|
|
}
|
|
|
|
template <>
|
|
inline uint8_t ByteSwap<1>(uint8_t x) {
|
|
return x;
|
|
}
|
|
|
|
#if defined(__clang__) || defined(__GNUC__)
|
|
|
|
template <>
|
|
inline uint64_t ByteSwap<8>(uint64_t x) {
|
|
return __builtin_bswap64(x);
|
|
}
|
|
template <>
|
|
inline uint32_t ByteSwap<4>(uint32_t x) {
|
|
return __builtin_bswap32(x);
|
|
}
|
|
template <>
|
|
inline uint16_t ByteSwap<2>(uint16_t x) {
|
|
return __builtin_bswap16(x);
|
|
}
|
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
template <>
|
|
inline uint64_t ByteSwap<8>(uint64_t x) {
|
|
return _byteswap_uint64(x);
|
|
}
|
|
template <>
|
|
inline uint32_t ByteSwap<4>(uint32_t x) {
|
|
return _byteswap_ulong(x);
|
|
}
|
|
template <>
|
|
inline uint16_t ByteSwap<2>(uint16_t x) {
|
|
return _byteswap_ushort(x);
|
|
}
|
|
|
|
#else
|
|
|
|
#include <byteswap.h>
|
|
|
|
template <>
|
|
inline uint64_t ByteSwap<8>(uint64_t x) {
|
|
return bswap_64(x);
|
|
}
|
|
template <>
|
|
inline uint32_t ByteSwap<4>(uint32_t x) {
|
|
return bswap_32(x);
|
|
}
|
|
template <>
|
|
inline uint16_t ByteSwap<2>(uint16_t x) {
|
|
return bswap_16(x);
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace internal
|
|
|
|
// Returns the bytes of |x| in reverse order. This is only defined for 16-, 32-,
|
|
// and 64-bit unsigned integers.
|
|
template <typename Integer>
|
|
inline std::enable_if_t<std::is_unsigned<Integer>::value, Integer> ByteSwap(
|
|
Integer x) {
|
|
return internal::ByteSwap<sizeof(Integer)>(x);
|
|
}
|
|
|
|
// Read a POD integer from |src| in big-endian byte order, returning the integer
|
|
// in native byte order.
|
|
template <typename Integer>
|
|
inline Integer ReadBigEndian(const void* src) {
|
|
Integer result;
|
|
memcpy(&result, src, sizeof(result));
|
|
if (!IsBigEndianArchitecture()) {
|
|
result = ByteSwap<typename std::make_unsigned<Integer>::type>(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Write a POD integer |val| to |dest| in big-endian byte order.
|
|
template <typename Integer>
|
|
inline void WriteBigEndian(Integer val, void* dest) {
|
|
if (!IsBigEndianArchitecture()) {
|
|
val = ByteSwap<typename std::make_unsigned<Integer>::type>(val);
|
|
}
|
|
memcpy(dest, &val, sizeof(val));
|
|
}
|
|
|
|
template <class T>
|
|
class BigEndianBuffer {
|
|
public:
|
|
class Cursor {
|
|
public:
|
|
explicit Cursor(BigEndianBuffer* buffer)
|
|
: buffer_(buffer), origin_(buffer_->current_) {}
|
|
Cursor(const Cursor& other) = delete;
|
|
Cursor(Cursor&& other) = delete;
|
|
~Cursor() { buffer_->current_ = origin_; }
|
|
|
|
Cursor& operator=(const Cursor& other) = delete;
|
|
Cursor& operator=(Cursor&& other) = delete;
|
|
|
|
void Commit() { origin_ = buffer_->current_; }
|
|
|
|
T* origin() { return origin_; }
|
|
size_t delta() { return buffer_->current_ - origin_; }
|
|
|
|
private:
|
|
BigEndianBuffer* buffer_;
|
|
T* origin_;
|
|
};
|
|
|
|
bool Skip(size_t length) {
|
|
if (current_ + length > end_) {
|
|
return false;
|
|
}
|
|
current_ += length;
|
|
return true;
|
|
}
|
|
|
|
T* begin() const { return begin_; }
|
|
T* current() const { return current_; }
|
|
T* end() const { return end_; }
|
|
size_t length() const { return end_ - begin_; }
|
|
size_t remaining() const { return end_ - current_; }
|
|
size_t offset() const { return current_ - begin_; }
|
|
|
|
BigEndianBuffer(T* buffer, size_t length)
|
|
: begin_(buffer), current_(buffer), end_(buffer + length) {}
|
|
BigEndianBuffer(const BigEndianBuffer&) = delete;
|
|
BigEndianBuffer& operator=(const BigEndianBuffer&) = delete;
|
|
|
|
private:
|
|
T* begin_;
|
|
T* current_;
|
|
T* end_;
|
|
};
|
|
|
|
class BigEndianReader : public BigEndianBuffer<const uint8_t> {
|
|
public:
|
|
BigEndianReader(const uint8_t* buffer, size_t length);
|
|
|
|
template <typename T>
|
|
bool Read(T* out) {
|
|
const uint8_t* read_position = current();
|
|
if (Skip(sizeof(T))) {
|
|
*out = ReadBigEndian<T>(read_position);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Read(size_t length, void* out);
|
|
};
|
|
|
|
class BigEndianWriter : public BigEndianBuffer<uint8_t> {
|
|
public:
|
|
BigEndianWriter(uint8_t* buffer, size_t length);
|
|
|
|
template <typename T>
|
|
bool Write(T value) {
|
|
uint8_t* write_position = current();
|
|
if (Skip(sizeof(T))) {
|
|
WriteBigEndian<T>(value, write_position);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Write(const void* buffer, size_t length);
|
|
};
|
|
|
|
} // namespace openscreen
|
|
|
|
#endif // UTIL_BIG_ENDIAN_H_
|