-
Notifications
You must be signed in to change notification settings - Fork 9
/
big_endian.h
255 lines (209 loc) · 8.62 KB
/
big_endian.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_BIG_ENDIAN_H_
#define BASE_BIG_ENDIAN_H_
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <optional>
#include <type_traits>
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/memory/raw_span.h"
#include "base/strings/string_piece.h"
#include "base/sys_byteorder.h"
#include "build/build_config.h"
namespace base {
namespace internal {
// ByteSwapIfLittleEndian performs ByteSwap if this platform is little-endian,
// otherwise it is a no-op.
#if defined(ARCH_CPU_LITTLE_ENDIAN)
template <typename T>
inline auto ByteSwapIfLittleEndian(T val) -> decltype(ByteSwap(val)) {
return ByteSwap(val);
}
#else
// The use of decltype ensures this is only enabled for types for which
// ByteSwap() is defined, so the same set of overloads will work on both
// little-endian and big-endian platforms.
template <typename T>
inline auto ByteSwapIfLittleEndian(T val) -> decltype(ByteSwap(val)) {
return val;
}
#endif
// We never need to byte-swap a single-byte value, but it's convenient to have
// this overload to avoid a special case.
inline uint8_t ByteSwapIfLittleEndian(uint8_t val) {
return val;
}
} // namespace internal
// Read an integer (signed or unsigned) from |buf| in Big Endian order.
// Note: this loop is unrolled with -O1 and above.
// NOTE(szym): glibc dns-canon.c use ntohs(*(uint16_t*)ptr) which is
// potentially unaligned.
// This would cause SIGBUS on ARMv5 or earlier and ARMv6-M.
//
// DEPRECATED: Use base::numerics::*FromBigEndian to convert big-endian byte
// encoding to primitives.
template <typename T>
inline void ReadBigEndian(span<const uint8_t, sizeof(T)> buffer, T* out) {
static_assert(std::is_integral_v<T>, "T has to be an integral type.");
// Make an unsigned version of the output type to make shift possible
// without UB.
std::make_unsigned_t<T> raw;
byte_span_from_ref(raw).copy_from(buffer);
*out = static_cast<T>(internal::ByteSwapIfLittleEndian(raw));
}
// TODO(crbug.com/40284755): Remove this function when there are no callers.
template <typename T>
inline void ReadBigEndian(const uint8_t buf[], T* out) {
ReadBigEndian(span<const uint8_t, sizeof(T)>(buf, sizeof(T)), out);
}
// Write an integer (signed or unsigned) `val` to `buffer` in Big Endian order.
// The `buffer` must be the same size (in bytes) as the integer `val`.
//
// DEPRECATED: Use base::numerics::*ToBigEndian to convert primitives to big-
// endian byte encoding.
template <typename T>
requires(std::is_integral_v<T>)
inline void WriteBigEndian(span<uint8_t, sizeof(T)> buffer, T val) {
const auto unsigned_val = static_cast<std::make_unsigned_t<T>>(val);
const auto raw = internal::ByteSwapIfLittleEndian(unsigned_val);
buffer.copy_from(byte_span_from_ref(raw));
}
// TODO(crbug.com/40284755): Remove this function when there are no callers.
template <typename T>
requires(std::is_integral_v<T>)
inline void WriteBigEndian(char buf[], T val) {
return WriteBigEndian(
as_writable_bytes(span<char, sizeof(T)>(buf, sizeof(T))), val);
}
// Allows reading integers in network order (big endian) while iterating over
// an underlying buffer. All the reading functions advance the internal pointer.
class BASE_EXPORT BigEndianReader {
public:
static BigEndianReader FromStringPiece(base::StringPiece string_piece);
explicit BigEndianReader(base::span<const uint8_t> buffer);
// TODO(crbug.com/40284755): Remove this overload.
UNSAFE_BUFFER_USAGE BigEndianReader(const uint8_t* buf, size_t len);
~BigEndianReader();
// Returns a span over all unread bytes.
span<const uint8_t> remaining_bytes() const { return buffer_; }
// TODO(crbug.com/40284755): Remove this method.
const uint8_t* ptr() const { return buffer_.data(); }
// TODO(crbug.com/40284755): Remove this method.
size_t remaining() const { return buffer_.size(); }
// Moves the internal state forward `len` bytes, or returns false if there is
// not enough bytes left to read from.
bool Skip(size_t len);
// Reads an 8-bit integer and advances past it. Returns false if there is not
// enough bytes to read from.
bool ReadU8(uint8_t* value);
// Reads a 16-bit integer and advances past it. Returns false if there is not
// enough bytes to read from.
bool ReadU16(uint16_t* value);
// Reads a 32-bit integer and advances past it. Returns false if there is not
// enough bytes to read from.
bool ReadU32(uint32_t* value);
// Reads a 64-bit integer and advances past it. Returns false if there is not
// enough bytes to read from.
bool ReadU64(uint64_t* value);
// An alias for `ReadU8` that works with a `char` pointer instead of
// `uint8_t`.
bool ReadChar(char* value) {
return ReadU8(reinterpret_cast<uint8_t*>(value));
}
// Creates a StringPiece in |out| that points to the underlying buffer.
bool ReadPiece(base::StringPiece* out, size_t len);
// Returns a span over `n` bytes from the buffer and moves the internal state
// past those bytes, or returns nullopt and if there are not `n` bytes
// remaining in the buffer.
std::optional<span<const uint8_t>> ReadSpan(base::StrictNumeric<size_t> n);
// Returns a span over `N` bytes from the buffer and moves the internal state
// past those bytes, or returns nullopt and if there are not `N` bytes
// remaining in the buffer.
template <size_t N>
std::optional<span<const uint8_t, N>> ReadSpan() {
if (remaining() < N) {
return std::nullopt;
}
auto [consume, remain] = buffer_.split_at<N>();
buffer_ = remain;
return {consume};
}
// Copies into a span (writing to the whole span) from the buffer and moves
// the internal state past the copied bytes, or returns false and if there are
// not enough bytes remaining in the buffer to fill the span and leaves the
// internal state unchanged.
bool ReadBytes(span<uint8_t> out);
// Copies into a span of `N` bytes from the buffer and moves the internal
// state past the copied bytes, or returns false and if there are not `N`
// bytes remaining in the buffer and leaves the internal state unchanged.
template <size_t N>
bool ReadBytes(span<uint8_t, N> out) {
std::optional<span<const uint8_t, N>> span = ReadSpan<N>();
if (!span.has_value()) {
return false;
}
out.copy_from(*span);
return true;
}
// Reads a length-prefixed region:
// 1. reads a big-endian length L from the buffer;
// 2. sets |*out| to a StringPiece over the next L many bytes
// of the buffer (beyond the end of the bytes encoding the length); and
// 3. skips the main reader past this L-byte substring.
//
// Fails if reading a U8 or U16 fails, or if the parsed length is greater
// than the number of bytes remaining in the stream.
//
// On failure, leaves the stream at the same position
// as before the call.
bool ReadU8LengthPrefixed(base::StringPiece* out);
bool ReadU16LengthPrefixed(base::StringPiece* out);
private:
raw_span<const uint8_t> buffer_;
};
// Allows writing integers in network order (big endian) while iterating over
// an underlying buffer. All the writing functions advance the internal pointer.
class BASE_EXPORT BigEndianWriter {
public:
// Constructs a BigEndianWriter that will write into the given buffer.
BigEndianWriter(span<uint8_t> buffer);
// TODO(crbug.com/40284755): Remove this overload.
UNSAFE_BUFFER_USAGE BigEndianWriter(char* buf, size_t len);
~BigEndianWriter();
char* ptr() const { return reinterpret_cast<char*>(buffer_.data()); }
size_t remaining() const { return buffer_.size(); }
// Returns a span over all unwritten bytes.
span<uint8_t> remaining_bytes() const { return buffer_; }
bool Skip(size_t len);
// TODO(crbug.com/40284755): WriteBytes() calls should be replaced with
// WriteSpan().
bool WriteBytes(const void* buf, size_t len);
bool WriteU8(uint8_t value);
bool WriteU16(uint16_t value);
bool WriteU32(uint32_t value);
bool WriteU64(uint64_t value);
// Writes the span of bytes to the backing buffer. If there is not enough
// room, it writes nothing and returns false.
bool WriteSpan(base::span<const uint8_t> bytes);
// Writes `N` bytes to the backing buffer. If there is not enough room, it
// writes nothing and returns false.
template <size_t N>
bool WriteFixedSpan(base::span<const uint8_t, N> bytes) {
if (remaining() < N) {
return false;
}
auto [write, remain] = buffer_.split_at<N>();
write.copy_from(bytes);
buffer_ = remain;
return true;
}
private:
raw_span<uint8_t> buffer_;
};
} // namespace base
#endif // BASE_BIG_ENDIAN_H_