• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef UTIL_BIG_ENDIAN_H_
6 #define UTIL_BIG_ENDIAN_H_
7 
8 #include <stdint.h>
9 
10 #include <cstring>
11 #include <type_traits>
12 
13 namespace openscreen {
14 
15 ////////////////////////////////////////////////////////////////////////////////
16 // Note: All of the functions here are defined inline, as any half-decent
17 // compiler will optimize them to a single integer constant or single
18 // instruction on most architectures.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 // Returns true if this code is running on a big-endian architecture.
IsBigEndianArchitecture()22 inline bool IsBigEndianArchitecture() {
23   const uint16_t kTestWord = 0x0100;
24   uint8_t bytes[sizeof(kTestWord)];
25   memcpy(bytes, &kTestWord, sizeof(bytes));
26   return !!bytes[0];
27 }
28 
29 namespace internal {
30 
31 template <int size>
32 struct MakeSizedUnsignedInteger;
33 
34 template <>
35 struct MakeSizedUnsignedInteger<1> {
36   using type = uint8_t;
37 };
38 
39 template <>
40 struct MakeSizedUnsignedInteger<2> {
41   using type = uint16_t;
42 };
43 
44 template <>
45 struct MakeSizedUnsignedInteger<4> {
46   using type = uint32_t;
47 };
48 
49 template <>
50 struct MakeSizedUnsignedInteger<8> {
51   using type = uint64_t;
52 };
53 
54 template <int size>
55 inline typename MakeSizedUnsignedInteger<size>::type ByteSwap(
56     typename MakeSizedUnsignedInteger<size>::type x) {
57   static_assert(size <= 8,
58                 "ByteSwap() specialization missing in " __FILE__
59                 ". "
60                 "Are you trying to use an integer larger than 64 bits?");
61 }
62 
63 template <>
64 inline uint8_t ByteSwap<1>(uint8_t x) {
65   return x;
66 }
67 
68 #if defined(__clang__) || defined(__GNUC__)
69 
70 template <>
71 inline uint64_t ByteSwap<8>(uint64_t x) {
72   return __builtin_bswap64(x);
73 }
74 template <>
75 inline uint32_t ByteSwap<4>(uint32_t x) {
76   return __builtin_bswap32(x);
77 }
78 template <>
79 inline uint16_t ByteSwap<2>(uint16_t x) {
80   return __builtin_bswap16(x);
81 }
82 
83 #elif defined(_MSC_VER)
84 
85 template <>
86 inline uint64_t ByteSwap<8>(uint64_t x) {
87   return _byteswap_uint64(x);
88 }
89 template <>
90 inline uint32_t ByteSwap<4>(uint32_t x) {
91   return _byteswap_ulong(x);
92 }
93 template <>
94 inline uint16_t ByteSwap<2>(uint16_t x) {
95   return _byteswap_ushort(x);
96 }
97 
98 #else
99 
100 #include <byteswap.h>
101 
102 template <>
103 inline uint64_t ByteSwap<8>(uint64_t x) {
104   return bswap_64(x);
105 }
106 template <>
107 inline uint32_t ByteSwap<4>(uint32_t x) {
108   return bswap_32(x);
109 }
110 template <>
111 inline uint16_t ByteSwap<2>(uint16_t x) {
112   return bswap_16(x);
113 }
114 
115 #endif
116 
117 }  // namespace internal
118 
119 // Returns the bytes of |x| in reverse order. This is only defined for 16-, 32-,
120 // and 64-bit unsigned integers.
121 template <typename Integer>
122 inline std::enable_if_t<std::is_unsigned<Integer>::value, Integer> ByteSwap(
123     Integer x) {
124   return internal::ByteSwap<sizeof(Integer)>(x);
125 }
126 
127 // Read a POD integer from |src| in big-endian byte order, returning the integer
128 // in native byte order.
129 template <typename Integer>
130 inline Integer ReadBigEndian(const void* src) {
131   Integer result;
132   memcpy(&result, src, sizeof(result));
133   if (!IsBigEndianArchitecture()) {
134     result = ByteSwap<typename std::make_unsigned<Integer>::type>(result);
135   }
136   return result;
137 }
138 
139 // Write a POD integer |val| to |dest| in big-endian byte order.
140 template <typename Integer>
141 inline void WriteBigEndian(Integer val, void* dest) {
142   if (!IsBigEndianArchitecture()) {
143     val = ByteSwap<typename std::make_unsigned<Integer>::type>(val);
144   }
145   memcpy(dest, &val, sizeof(val));
146 }
147 
148 template <class T>
149 class BigEndianBuffer {
150  public:
151   class Cursor {
152    public:
153     explicit Cursor(BigEndianBuffer* buffer)
154         : buffer_(buffer), origin_(buffer_->current_) {}
155     Cursor(const Cursor& other) = delete;
156     Cursor(Cursor&& other) = delete;
157     ~Cursor() { buffer_->current_ = origin_; }
158 
159     Cursor& operator=(const Cursor& other) = delete;
160     Cursor& operator=(Cursor&& other) = delete;
161 
162     void Commit() { origin_ = buffer_->current_; }
163 
164     T* origin() { return origin_; }
165     size_t delta() { return buffer_->current_ - origin_; }
166 
167    private:
168     BigEndianBuffer* buffer_;
169     T* origin_;
170   };
171 
172   bool Skip(size_t length) {
173     if (current_ + length > end_) {
174       return false;
175     }
176     current_ += length;
177     return true;
178   }
179 
180   T* begin() const { return begin_; }
181   T* current() const { return current_; }
182   T* end() const { return end_; }
183   size_t length() const { return end_ - begin_; }
184   size_t remaining() const { return end_ - current_; }
185   size_t offset() const { return current_ - begin_; }
186 
187   BigEndianBuffer(T* buffer, size_t length)
188       : begin_(buffer), current_(buffer), end_(buffer + length) {}
189   BigEndianBuffer(const BigEndianBuffer&) = delete;
190   BigEndianBuffer& operator=(const BigEndianBuffer&) = delete;
191 
192  private:
193   T* begin_;
194   T* current_;
195   T* end_;
196 };
197 
198 class BigEndianReader : public BigEndianBuffer<const uint8_t> {
199  public:
200   BigEndianReader(const uint8_t* buffer, size_t length);
201 
202   template <typename T>
203   bool Read(T* out) {
204     const uint8_t* read_position = current();
205     if (Skip(sizeof(T))) {
206       *out = ReadBigEndian<T>(read_position);
207       return true;
208     }
209     return false;
210   }
211 
212   bool Read(size_t length, void* out);
213 };
214 
215 class BigEndianWriter : public BigEndianBuffer<uint8_t> {
216  public:
217   BigEndianWriter(uint8_t* buffer, size_t length);
218 
219   template <typename T>
220   bool Write(T value) {
221     uint8_t* write_position = current();
222     if (Skip(sizeof(T))) {
223       WriteBigEndian<T>(value, write_position);
224       return true;
225     }
226     return false;
227   }
228 
229   bool Write(const void* buffer, size_t length);
230 };
231 
232 }  // namespace openscreen
233 
234 #endif  // UTIL_BIG_ENDIAN_H_
235