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 | |
22 | namespace 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. |
29 | class BinaryStreamReader { |
30 | public: |
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 | |
267 | private: |
268 | BinaryStreamRef Stream; |
269 | uint64_t Offset = 0; |
270 | }; |
271 | } // namespace llvm |
272 | |
273 | #endif // LLVM_SUPPORT_BINARYSTREAMREADER_H |
274 | |