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