// 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 #include #include 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 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 inline typename MakeSizedUnsignedInteger::type ByteSwap( typename MakeSizedUnsignedInteger::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 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 inline std::enable_if_t::value, Integer> ByteSwap( Integer x) { return internal::ByteSwap(x); } // Read a POD integer from |src| in big-endian byte order, returning the integer // in native byte order. template inline Integer ReadBigEndian(const void* src) { Integer result; memcpy(&result, src, sizeof(result)); if (!IsBigEndianArchitecture()) { result = ByteSwap::type>(result); } return result; } // Write a POD integer |val| to |dest| in big-endian byte order. template inline void WriteBigEndian(Integer val, void* dest) { if (!IsBigEndianArchitecture()) { val = ByteSwap::type>(val); } memcpy(dest, &val, sizeof(val)); } template 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 { public: BigEndianReader(const uint8_t* buffer, size_t length); template bool Read(T* out) { const uint8_t* read_position = current(); if (Skip(sizeof(T))) { *out = ReadBigEndian(read_position); return true; } return false; } bool Read(size_t length, void* out); }; class BigEndianWriter : public BigEndianBuffer { public: BigEndianWriter(uint8_t* buffer, size_t length); template bool Write(T value) { uint8_t* write_position = current(); if (Skip(sizeof(T))) { WriteBigEndian(value, write_position); return true; } return false; } bool Write(const void* buffer, size_t length); }; } // namespace openscreen #endif // UTIL_BIG_ENDIAN_H_