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