1//===- StreamArray.h - Array backed by an arbitrary stream ----------------===//
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_DEBUGINFO_CODEVIEW_STREAMARRAY_H
11#define LLVM_DEBUGINFO_CODEVIEW_STREAMARRAY_H
12
13#include "llvm/DebugInfo/CodeView/StreamRef.h"
14#include "llvm/Support/Error.h"
15
16#include <functional>
17#include <type_traits>
18
19namespace llvm {
20namespace codeview {
21
22/// VarStreamArrayExtractor is intended to be specialized to provide customized
23/// extraction logic.  On input it receives a StreamRef pointing to the
24/// beginning of the next record, but where the length of the record is not yet
25/// known.  Upon completion, it should return an appropriate Error instance if
26/// a record could not be extracted, or if one could be extracted it should
27/// return success and set Len to the number of bytes this record occupied in
28/// the underlying stream, and it should fill out the fields of the value type
29/// Item appropriately to represent the current record.
30///
31/// You can specialize this template for your own custom value types to avoid
32/// having to specify a second template argument to VarStreamArray (documented
33/// below).
34template <typename T> struct VarStreamArrayExtractor {
35  // Method intentionally deleted.  You must provide an explicit specialization
36  // with the following method implemented.
37  Error operator()(StreamRef Stream, uint32_t &Len, T &Item) const = delete;
38};
39
40/// VarStreamArray represents an array of variable length records backed by a
41/// stream.  This could be a contiguous sequence of bytes in memory, it could
42/// be a file on disk, or it could be a PDB stream where bytes are stored as
43/// discontiguous blocks in a file.  Usually it is desirable to treat arrays
44/// as contiguous blocks of memory, but doing so with large PDB files, for
45/// example, could mean allocating huge amounts of memory just to allow
46/// re-ordering of stream data to be contiguous before iterating over it.  By
47/// abstracting this out, we need not duplicate this memory, and we can
48/// iterate over arrays in arbitrarily formatted streams.  Elements are parsed
49/// lazily on iteration, so there is no upfront cost associated with building
50/// a VarStreamArray, no matter how large it may be.
51///
52/// You create a VarStreamArray by specifying a ValueType and an Extractor type.
53/// If you do not specify an Extractor type, it expects you to specialize
54/// VarStreamArrayExtractor<T> for your ValueType.
55///
56/// By default an Extractor is default constructed in the class, but in some
57/// cases you might find it useful for an Extractor to maintain state across
58/// extractions.  In this case you can provide your own Extractor through a
59/// secondary constructor.  The following examples show various ways of
60/// creating a VarStreamArray.
61///
62///       // Will use VarStreamArrayExtractor<MyType> as the extractor.
63///       VarStreamArray<MyType> MyTypeArray;
64///
65///       // Will use a default-constructed MyExtractor as the extractor.
66///       VarStreamArray<MyType, MyExtractor> MyTypeArray2;
67///
68///       // Will use the specific instance of MyExtractor provided.
69///       // MyExtractor need not be default-constructible in this case.
70///       MyExtractor E(SomeContext);
71///       VarStreamArray<MyType, MyExtractor> MyTypeArray3(E);
72///
73template <typename ValueType, typename Extractor> class VarStreamArrayIterator;
74
75template <typename ValueType,
76          typename Extractor = VarStreamArrayExtractor<ValueType>>
77class VarStreamArray {
78  friend class VarStreamArrayIterator<ValueType, Extractor>;
79
80public:
81  typedef VarStreamArrayIterator<ValueType, Extractor> Iterator;
82
83  VarStreamArray() {}
84  explicit VarStreamArray(const Extractor &E) : E(E) {}
85
86  explicit VarStreamArray(StreamRef Stream) : Stream(Stream) {}
87  VarStreamArray(StreamRef Stream, const Extractor &E) : Stream(Stream), E(E) {}
88
89  VarStreamArray(const VarStreamArray<ValueType, Extractor> &Other)
90      : Stream(Other.Stream), E(Other.E) {}
91
92  Iterator begin(bool *HadError = nullptr) const {
93    return Iterator(*this, E, HadError);
94  }
95
96  Iterator end() const { return Iterator(E); }
97
98  const Extractor &getExtractor() const { return E; }
99
100  StreamRef getUnderlyingStream() const { return Stream; }
101
102private:
103  StreamRef Stream;
104  Extractor E;
105};
106
107template <typename ValueType, typename Extractor> class VarStreamArrayIterator {
108  typedef VarStreamArrayIterator<ValueType, Extractor> IterType;
109  typedef VarStreamArray<ValueType, Extractor> ArrayType;
110
111public:
112  VarStreamArrayIterator(const ArrayType &Array, const Extractor &E,
113                         bool *HadError = nullptr)
114      : IterRef(Array.Stream), Array(&Array), HadError(HadError), Extract(E) {
115    auto EC = Extract(IterRef, ThisLen, ThisValue);
116    if (EC) {
117      consumeError(std::move(EC));
118      markError();
119    }
120  }
121  VarStreamArrayIterator() {}
122  explicit VarStreamArrayIterator(const Extractor &E) : Extract(E) {}
123  ~VarStreamArrayIterator() {}
124
125  bool operator==(const IterType &R) const {
126    if (Array && R.Array) {
127      // Both have a valid array, make sure they're same.
128      assert(Array == R.Array);
129      return IterRef == R.IterRef;
130    }
131
132    // Both iterators are at the end.
133    if (!Array && !R.Array)
134      return true;
135
136    // One is not at the end and one is.
137    return false;
138  }
139
140  bool operator!=(const IterType &R) { return !(*this == R); }
141
142  const ValueType &operator*() const {
143    assert(Array && !HasError);
144    return ThisValue;
145  }
146
147  IterType &operator++() {
148    // We are done with the current record, discard it so that we are
149    // positioned at the next record.
150    IterRef = IterRef.drop_front(ThisLen);
151    if (IterRef.getLength() == 0) {
152      // There is nothing after the current record, we must make this an end
153      // iterator.
154      moveToEnd();
155    } else {
156      // There is some data after the current record.
157      auto EC = Extract(IterRef, ThisLen, ThisValue);
158      if (EC) {
159        consumeError(std::move(EC));
160        markError();
161      } else if (ThisLen == 0) {
162        // An empty record? Make this an end iterator.
163        moveToEnd();
164      }
165    }
166    return *this;
167  }
168
169  IterType operator++(int) {
170    IterType Original = *this;
171    ++*this;
172    return Original;
173  }
174
175private:
176  void moveToEnd() {
177    Array = nullptr;
178    ThisLen = 0;
179  }
180  void markError() {
181    moveToEnd();
182    HasError = true;
183    if (HadError != nullptr)
184      *HadError = true;
185  }
186
187  ValueType ThisValue;
188  StreamRef IterRef;
189  const ArrayType *Array{nullptr};
190  uint32_t ThisLen{0};
191  bool HasError{false};
192  bool *HadError{nullptr};
193  Extractor Extract;
194};
195
196template <typename T> class FixedStreamArrayIterator;
197
198template <typename T> class FixedStreamArray {
199  friend class FixedStreamArrayIterator<T>;
200
201public:
202  FixedStreamArray() : Stream() {}
203  FixedStreamArray(StreamRef Stream) : Stream(Stream) {
204    assert(Stream.getLength() % sizeof(T) == 0);
205  }
206
207  const T &operator[](uint32_t Index) const {
208    assert(Index < size());
209    uint32_t Off = Index * sizeof(T);
210    ArrayRef<uint8_t> Data;
211    if (auto EC = Stream.readBytes(Off, sizeof(T), Data)) {
212      assert(false && "Unexpected failure reading from stream");
213      // This should never happen since we asserted that the stream length was
214      // an exact multiple of the element size.
215      consumeError(std::move(EC));
216    }
217    return *reinterpret_cast<const T *>(Data.data());
218  }
219
220  uint32_t size() const { return Stream.getLength() / sizeof(T); }
221
222  FixedStreamArrayIterator<T> begin() const {
223    return FixedStreamArrayIterator<T>(*this, 0);
224  }
225  FixedStreamArrayIterator<T> end() const {
226    return FixedStreamArrayIterator<T>(*this, size());
227  }
228
229  StreamRef getUnderlyingStream() const { return Stream; }
230
231private:
232  StreamRef Stream;
233};
234
235template <typename T> class FixedStreamArrayIterator {
236public:
237  FixedStreamArrayIterator(const FixedStreamArray<T> &Array, uint32_t Index)
238      : Array(Array), Index(Index) {}
239
240  bool operator==(const FixedStreamArrayIterator<T> &R) {
241    assert(&Array == &R.Array);
242    return Index == R.Index;
243  }
244
245  bool operator!=(const FixedStreamArrayIterator<T> &R) {
246    return !(*this == R);
247  }
248
249  const T &operator*() const { return Array[Index]; }
250
251  FixedStreamArrayIterator<T> &operator++() {
252    assert(Index < Array.size());
253    ++Index;
254    return *this;
255  }
256
257  FixedStreamArrayIterator<T> operator++(int) {
258    FixedStreamArrayIterator<T> Original = *this;
259    ++*this;
260    return Original;
261  }
262
263private:
264  const FixedStreamArray<T> &Array;
265  uint32_t Index;
266};
267
268} // namespace codeview
269} // namespace llvm
270
271#endif // LLVM_DEBUGINFO_CODEVIEW_STREAMARRAY_H
272