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