1//===- BinaryStreamReader.h - Reads objects from a binary stream *- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_SUPPORT_BINARYSTREAMREADER_H
10#define LLVM_SUPPORT_BINARYSTREAMREADER_H
11
12#include "llvm/ADT/ArrayRef.h"
13#include "llvm/ADT/StringRef.h"
14#include "llvm/Support/Alignment.h"
15#include "llvm/Support/BinaryStreamArray.h"
16#include "llvm/Support/BinaryStreamRef.h"
17#include "llvm/Support/ConvertUTF.h"
18#include "llvm/Support/Endian.h"
19#include "llvm/Support/Error.h"
20#include <type_traits>
21
22namespace llvm {
23
24/// Provides read only access to a subclass of `BinaryStream`. Provides
25/// bounds checking and helpers for writing certain common data types such as
26/// null-terminated strings, integers in various flavors of endianness, etc.
27/// Can be subclassed to provide reading of custom datatypes, although no
28/// are overridable.
29class BinaryStreamReader {
30public:
31 BinaryStreamReader() = default;
32 explicit BinaryStreamReader(BinaryStreamRef Ref);
33 explicit BinaryStreamReader(BinaryStream &Stream);
34 explicit BinaryStreamReader(ArrayRef<uint8_t> Data, llvm::endianness Endian);
35 explicit BinaryStreamReader(StringRef Data, llvm::endianness Endian);
36
37 BinaryStreamReader(const BinaryStreamReader &Other) = default;
38
39 BinaryStreamReader &operator=(const BinaryStreamReader &Other) = default;
40
41 virtual ~BinaryStreamReader() = default;
42
43 /// Read as much as possible from the underlying string at the current offset
44 /// without invoking a copy, and set \p Buffer to the resulting data slice.
45 /// Updates the stream's offset to point after the newly read data.
46 ///
47 /// \returns a success error code if the data was successfully read, otherwise
48 /// returns an appropriate error code.
49 Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer);
50
51 /// Read \p Size bytes from the underlying stream at the current offset and
52 /// and set \p Buffer to the resulting data slice. Whether a copy occurs
53 /// depends on the implementation of the underlying stream. Updates the
54 /// stream's offset to point after the newly read data.
55 ///
56 /// \returns a success error code if the data was successfully read, otherwise
57 /// returns an appropriate error code.
58 Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size);
59
60 /// Read an integer of the specified endianness into \p Dest and update the
61 /// stream's offset. The data is always copied from the stream's underlying
62 /// buffer into \p Dest. Updates the stream's offset to point after the newly
63 /// read data.
64 ///
65 /// \returns a success error code if the data was successfully read, otherwise
66 /// returns an appropriate error code.
67 template <typename T> Error readInteger(T &Dest) {
68 static_assert(std::is_integral_v<T>,
69 "Cannot call readInteger with non-integral value!");
70
71 ArrayRef<uint8_t> Bytes;
72 if (auto EC = readBytes(Buffer&: Bytes, Size: sizeof(T)))
73 return EC;
74
75 Dest = llvm::support::endian::read<T>(Bytes.data(), Stream.getEndian());
76 return Error::success();
77 }
78
79 /// Similar to readInteger.
80 template <typename T> Error readEnum(T &Dest) {
81 static_assert(std::is_enum<T>::value,
82 "Cannot call readEnum with non-enum value!");
83 std::underlying_type_t<T> N;
84 if (auto EC = readInteger(N))
85 return EC;
86 Dest = static_cast<T>(N);
87 return Error::success();
88 }
89
90 /// Read an unsigned LEB128 encoded value.
91 ///
92 /// \returns a success error code if the data was successfully read, otherwise
93 /// returns an appropriate error code.
94 Error readULEB128(uint64_t &Dest);
95
96 /// Read a signed LEB128 encoded value.
97 ///
98 /// \returns a success error code if the data was successfully read, otherwise
99 /// returns an appropriate error code.
100 Error readSLEB128(int64_t &Dest);
101
102 /// Read a null terminated string from \p Dest. Whether a copy occurs depends
103 /// on the implementation of the underlying stream. Updates the stream's
104 /// offset to point after the newly read data.
105 ///
106 /// \returns a success error code if the data was successfully read, otherwise
107 /// returns an appropriate error code.
108 Error readCString(StringRef &Dest);
109
110 /// Similar to readCString, however read a null-terminated UTF16 string
111 /// instead.
112 ///
113 /// \returns a success error code if the data was successfully read, otherwise
114 /// returns an appropriate error code.
115 Error readWideString(ArrayRef<UTF16> &Dest);
116
117 /// Read a \p Length byte string into \p Dest. Whether a copy occurs depends
118 /// on the implementation of the underlying stream. Updates the stream's
119 /// offset to point after the newly read data.
120 ///
121 /// \returns a success error code if the data was successfully read, otherwise
122 /// returns an appropriate error code.
123 Error readFixedString(StringRef &Dest, uint32_t Length);
124
125 /// Read the entire remainder of the underlying stream into \p Ref. This is
126 /// equivalent to calling getUnderlyingStream().slice(Offset). Updates the
127 /// stream's offset to point to the end of the stream. Never causes a copy.
128 ///
129 /// \returns a success error code if the data was successfully read, otherwise
130 /// returns an appropriate error code.
131 Error readStreamRef(BinaryStreamRef &Ref);
132
133 /// Read \p Length bytes from the underlying stream into \p Ref. This is
134 /// equivalent to calling getUnderlyingStream().slice(Offset, Length).
135 /// Updates the stream's offset to point after the newly read object. Never
136 /// causes a copy.
137 ///
138 /// \returns a success error code if the data was successfully read, otherwise
139 /// returns an appropriate error code.
140 Error readStreamRef(BinaryStreamRef &Ref, uint32_t Length);
141
142 /// Read \p Length bytes from the underlying stream into \p Ref. This is
143 /// equivalent to calling getUnderlyingStream().slice(Offset, Length).
144 /// Updates the stream's offset to point after the newly read object. Never
145 /// causes a copy.
146 ///
147 /// \returns a success error code if the data was successfully read, otherwise
148 /// returns an appropriate error code.
149 Error readSubstream(BinarySubstreamRef &Ref, uint32_t Length);
150
151 /// Get a pointer to an object of type T from the underlying stream, as if by
152 /// memcpy, and store the result into \p Dest. It is up to the caller to
153 /// ensure that objects of type T can be safely treated in this manner.
154 /// Updates the stream's offset to point after the newly read object. Whether
155 /// a copy occurs depends upon the implementation of the underlying
156 /// stream.
157 ///
158 /// \returns a success error code if the data was successfully read, otherwise
159 /// returns an appropriate error code.
160 template <typename T> Error readObject(const T *&Dest) {
161 ArrayRef<uint8_t> Buffer;
162 if (auto EC = readBytes(Buffer, Size: sizeof(T)))
163 return EC;
164 Dest = reinterpret_cast<const T *>(Buffer.data());
165 return Error::success();
166 }
167
168 /// Get a reference to a \p NumElements element array of objects of type T
169 /// from the underlying stream as if by memcpy, and store the resulting array
170 /// slice into \p array. It is up to the caller to ensure that objects of
171 /// type T can be safely treated in this manner. Updates the stream's offset
172 /// to point after the newly read object. Whether a copy occurs depends upon
173 /// the implementation of the underlying stream.
174 ///
175 /// \returns a success error code if the data was successfully read, otherwise
176 /// returns an appropriate error code.
177 template <typename T>
178 Error readArray(ArrayRef<T> &Array, uint32_t NumElements) {
179 ArrayRef<uint8_t> Bytes;
180 if (NumElements == 0) {
181 Array = ArrayRef<T>();
182 return Error::success();
183 }
184
185 if (NumElements > UINT32_MAX / sizeof(T))
186 return make_error<BinaryStreamError>(
187 Args: stream_error_code::invalid_array_size);
188
189 if (auto EC = readBytes(Buffer&: Bytes, Size: NumElements * sizeof(T)))
190 return EC;
191
192 assert(isAddrAligned(Align::Of<T>(), Bytes.data()) &&
193 "Reading at invalid alignment!");
194
195 Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements);
196 return Error::success();
197 }
198
199 /// Read a VarStreamArray of size \p Size bytes and store the result into
200 /// \p Array. Updates the stream's offset to point after the newly read
201 /// array. Never causes a copy (although iterating the elements of the
202 /// VarStreamArray may, depending upon the implementation of the underlying
203 /// stream).
204 ///
205 /// \returns a success error code if the data was successfully read, otherwise
206 /// returns an appropriate error code.
207 template <typename T, typename U>
208 Error readArray(VarStreamArray<T, U> &Array, uint32_t Size,
209 uint32_t Skew = 0) {
210 BinaryStreamRef S;
211 if (auto EC = readStreamRef(Ref&: S, Length: Size))
212 return EC;
213 Array.setUnderlyingStream(S, Skew);
214 return Error::success();
215 }
216
217 /// Read a FixedStreamArray of \p NumItems elements and store the result into
218 /// \p Array. Updates the stream's offset to point after the newly read
219 /// array. Never causes a copy (although iterating the elements of the
220 /// FixedStreamArray may, depending upon the implementation of the underlying
221 /// stream).
222 ///
223 /// \returns a success error code if the data was successfully read, otherwise
224 /// returns an appropriate error code.
225 template <typename T>
226 Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) {
227 if (NumItems == 0) {
228 Array = FixedStreamArray<T>();
229 return Error::success();
230 }
231
232 if (NumItems > UINT32_MAX / sizeof(T))
233 return make_error<BinaryStreamError>(
234 Args: stream_error_code::invalid_array_size);
235
236 BinaryStreamRef View;
237 if (auto EC = readStreamRef(Ref&: View, Length: NumItems * sizeof(T)))
238 return EC;
239
240 Array = FixedStreamArray<T>(View);
241 return Error::success();
242 }
243
244 bool empty() const { return bytesRemaining() == 0; }
245 void setOffset(uint64_t Off) { Offset = Off; }
246 uint64_t getOffset() const { return Offset; }
247 uint64_t getLength() const { return Stream.getLength(); }
248 uint64_t bytesRemaining() const { return getLength() - getOffset(); }
249
250 /// Advance the stream's offset by \p Amount bytes.
251 ///
252 /// \returns a success error code if at least \p Amount bytes remain in the
253 /// stream, otherwise returns an appropriate error code.
254 Error skip(uint64_t Amount);
255
256 /// Examine the next byte of the underlying stream without advancing the
257 /// stream's offset. If the stream is empty the behavior is undefined.
258 ///
259 /// \returns the next byte in the stream.
260 uint8_t peek() const;
261
262 Error padToAlignment(uint32_t Align);
263
264 std::pair<BinaryStreamReader, BinaryStreamReader>
265 split(uint64_t Offset) const;
266
267private:
268 BinaryStreamRef Stream;
269 uint64_t Offset = 0;
270};
271} // namespace llvm
272
273#endif // LLVM_SUPPORT_BINARYSTREAMREADER_H
274

source code of llvm/include/llvm/Support/BinaryStreamReader.h