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