blob: 0953ddf08a617a48b6b4130fcbf9014f6e06a6d7 [file] [log] [blame]
Yuri Wiitala486c7ae2019-04-15 15:04:57 -07001// 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
Jordan Baylesa26582d2019-07-10 14:44:58 -07005#ifndef UTIL_BIG_ENDIAN_H_
6#define UTIL_BIG_ENDIAN_H_
Yuri Wiitala486c7ae2019-04-15 15:04:57 -07007
8#include <stdint.h>
9
10#include <cstring>
11#include <type_traits>
12
13namespace openscreen {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070014
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.
22inline 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
btolschf14fa292020-02-19 14:00:10 -080029namespace internal {
30
31template <int size>
32struct MakeSizedUnsignedInteger;
33
34template <>
35struct MakeSizedUnsignedInteger<1> {
36 using type = uint8_t;
37};
38
39template <>
40struct MakeSizedUnsignedInteger<2> {
41 using type = uint16_t;
42};
43
44template <>
45struct MakeSizedUnsignedInteger<4> {
46 using type = uint32_t;
47};
48
49template <>
50struct MakeSizedUnsignedInteger<8> {
51 using type = uint64_t;
52};
53
54template <int size>
55inline typename MakeSizedUnsignedInteger<size>::type ByteSwap(
56 typename MakeSizedUnsignedInteger<size>::type x) {
57 static_assert(size <= 8,
Yuri Wiitalacb9bf582020-02-11 16:29:15 -080058 "ByteSwap() specialization missing in " __FILE__
59 ". "
btolschf14fa292020-02-19 14:00:10 -080060 "Are you trying to use an integer larger than 64 bits?");
Yuri Wiitalacb9bf582020-02-11 16:29:15 -080061}
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070062
Max Yakimakha852f70c2019-05-07 14:14:35 -070063template <>
btolschf14fa292020-02-19 14:00:10 -080064inline uint8_t ByteSwap<1>(uint8_t x) {
Max Yakimakha852f70c2019-05-07 14:14:35 -070065 return x;
66}
67
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070068#if defined(__clang__) || defined(__GNUC__)
69
70template <>
btolschf14fa292020-02-19 14:00:10 -080071inline uint64_t ByteSwap<8>(uint64_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070072 return __builtin_bswap64(x);
73}
74template <>
btolschf14fa292020-02-19 14:00:10 -080075inline uint32_t ByteSwap<4>(uint32_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070076 return __builtin_bswap32(x);
77}
78template <>
btolschf14fa292020-02-19 14:00:10 -080079inline uint16_t ByteSwap<2>(uint16_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070080 return __builtin_bswap16(x);
81}
82
83#elif defined(_MSC_VER)
84
85template <>
btolschf14fa292020-02-19 14:00:10 -080086inline uint64_t ByteSwap<8>(uint64_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070087 return _byteswap_uint64(x);
88}
89template <>
btolschf14fa292020-02-19 14:00:10 -080090inline uint32_t ByteSwap<4>(uint32_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070091 return _byteswap_ulong(x);
92}
93template <>
btolschf14fa292020-02-19 14:00:10 -080094inline uint16_t ByteSwap<2>(uint16_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -070095 return _byteswap_ushort(x);
96}
97
98#else
99
100#include <byteswap.h>
101
102template <>
btolschf14fa292020-02-19 14:00:10 -0800103inline uint64_t ByteSwap<8>(uint64_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -0700104 return bswap_64(x);
105}
106template <>
btolschf14fa292020-02-19 14:00:10 -0800107inline uint32_t ByteSwap<4>(uint32_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -0700108 return bswap_32(x);
109}
110template <>
btolschf14fa292020-02-19 14:00:10 -0800111inline uint16_t ByteSwap<2>(uint16_t x) {
Yuri Wiitala486c7ae2019-04-15 15:04:57 -0700112 return bswap_16(x);
113}
114
115#endif
116
btolschf14fa292020-02-19 14:00:10 -0800117} // 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.
121template <typename Integer>
122inline std::enable_if_t<std::is_unsigned<Integer>::value, Integer> ByteSwap(
123 Integer x) {
124 return internal::ByteSwap<sizeof(Integer)>(x);
125}
126
Yuri Wiitala486c7ae2019-04-15 15:04:57 -0700127// Read a POD integer from |src| in big-endian byte order, returning the integer
128// in native byte order.
129template <typename Integer>
130inline 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.
140template <typename Integer>
141inline 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
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700148template <class T>
149class BigEndianBuffer {
150 public:
151 class Cursor {
152 public:
mark a. foltzb4e53be2020-05-28 11:42:36 -0700153 explicit Cursor(BigEndianBuffer* buffer)
btolscha0f6adb2019-06-18 14:07:26 -0700154 : buffer_(buffer), origin_(buffer_->current_) {}
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700155 Cursor(const Cursor& other) = delete;
156 Cursor(Cursor&& other) = delete;
157 ~Cursor() { buffer_->current_ = origin_; }
btolscha0f6adb2019-06-18 14:07:26 -0700158
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700159 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
198class BigEndianReader : public BigEndianBuffer<const uint8_t> {
Max Yakimakhadad3c642019-05-24 17:25:30 -0700199 public:
200 BigEndianReader(const uint8_t* buffer, size_t length);
Max Yakimakhadad3c642019-05-24 17:25:30 -0700201
202 template <typename T>
203 bool Read(T* out) {
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700204 const uint8_t* read_position = current();
205 if (Skip(sizeof(T))) {
206 *out = ReadBigEndian<T>(read_position);
207 return true;
Max Yakimakhadad3c642019-05-24 17:25:30 -0700208 }
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700209 return false;
Max Yakimakhadad3c642019-05-24 17:25:30 -0700210 }
211
Max Yakimakha55bbe802019-06-19 15:04:28 -0700212 bool Read(size_t length, void* out);
Max Yakimakhadad3c642019-05-24 17:25:30 -0700213};
214
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700215class BigEndianWriter : public BigEndianBuffer<uint8_t> {
Max Yakimakhadad3c642019-05-24 17:25:30 -0700216 public:
217 BigEndianWriter(uint8_t* buffer, size_t length);
Max Yakimakhadad3c642019-05-24 17:25:30 -0700218
219 template <typename T>
220 bool Write(T value) {
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700221 uint8_t* write_position = current();
222 if (Skip(sizeof(T))) {
223 WriteBigEndian<T>(value, write_position);
224 return true;
Max Yakimakhadad3c642019-05-24 17:25:30 -0700225 }
Max Yakimakhab7da13f2019-06-11 16:59:06 -0700226 return false;
Max Yakimakhadad3c642019-05-24 17:25:30 -0700227 }
228
Max Yakimakha2b8a98c2019-06-19 14:39:22 -0700229 bool Write(const void* buffer, size_t length);
Max Yakimakhadad3c642019-05-24 17:25:30 -0700230};
231
Yuri Wiitala486c7ae2019-04-15 15:04:57 -0700232} // namespace openscreen
233
Jordan Baylesa26582d2019-07-10 14:44:58 -0700234#endif // UTIL_BIG_ENDIAN_H_