llvm.org GIT mirror llvm / 4b665d2
Allow VarStreamArray to use stateful extractors. Previously extractors tried to be stateless with any additional context information needed in order to parse items being passed in via the extraction method. This led to quite cumbersome implementation challenges and awkwardness of use. This patch brings back support for stateful extractors, making the implementation and usage simpler. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@305093 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 2 years ago
15 changed file(s) with 144 addition(s) and 218 deletion(s). Raw diff Collapse all Expand all
6161
6262 template
6363 struct VarStreamArrayExtractor> {
64 typedef void ContextType;
65
66 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
67 codeview::CVRecord &Item) {
64 Error operator()(BinaryStreamRef Stream, uint32_t &Len,
65 codeview::CVRecord &Item) {
6866 using namespace codeview;
6967 const RecordPrefix *Prefix = nullptr;
7068 BinaryStreamReader Reader(Stream);
3535 public:
3636 typedef void ContextType;
3737
38 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
39 codeview::FileChecksumEntry &Item);
38 Error operator()(BinaryStreamRef Stream, uint32_t &Len,
39 codeview::FileChecksumEntry &Item);
4040 };
4141 }
4242
3030 public:
3131 typedef void ContextType;
3232
33 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
34 codeview::CrossModuleImportItem &Item);
33 Error operator()(BinaryStreamRef Stream, uint32_t &Len,
34 codeview::CrossModuleImportItem &Item);
3535 };
3636 }
3737
4242 }
4343
4444 template <> struct VarStreamArrayExtractor {
45 typedef bool ContextType;
46
47 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
48 codeview::InlineeSourceLine &Item, bool HasExtraFiles);
45 Error operator()(BinaryStreamRef Stream, uint32_t &Len,
46 codeview::InlineeSourceLine &Item);
47 bool HasExtraFiles = false;
4948 };
5049
5150 namespace codeview {
6363
6464 class LineColumnExtractor {
6565 public:
66 typedef const LineFragmentHeader *ContextType;
66 Error operator()(BinaryStreamRef Stream, uint32_t &Len,
67 LineColumnEntry &Item);
6768
68 static Error extract(BinaryStreamRef Stream, uint32_t &Len,
69 LineColumnEntry &Item, const LineFragmentHeader *Ctx);
69 const LineFragmentHeader *Header = nullptr;
7070 };
7171
7272 class DebugLinesSubsectionRef final : public DebugSubsectionRef {
6161 } // namespace codeview
6262
6363 template <> struct VarStreamArrayExtractor {
64 typedef void ContextType;
65
66 static Error extract(BinaryStreamRef Stream, uint32_t &Length,
67 codeview::DebugSubsectionRecord &Info) {
64 Error operator()(BinaryStreamRef Stream, uint32_t &Length,
65 codeview::DebugSubsectionRecord &Info) {
6866 if (auto EC = codeview::DebugSubsectionRecord::initialize(
6967 Stream, Info, codeview::CodeViewContainer::Pdb))
7068 return EC;
5555 } // end namespace pdb
5656
5757 template <> struct VarStreamArrayExtractor {
58 typedef void ContextType;
59 static Error extract(BinaryStreamRef Stream, uint32_t &Length,
60 pdb::DbiModuleDescriptor &Info) {
58 Error operator()(BinaryStreamRef Stream, uint32_t &Length,
59 pdb::DbiModuleDescriptor &Info) {
6160 if (auto EC = pdb::DbiModuleDescriptor::initialize(Stream, Info))
6261 return EC;
6362 Length = Info.getRecordLength();
4141 /// having to specify a second template argument to VarStreamArray (documented
4242 /// below).
4343 template struct VarStreamArrayExtractor {
44 struct ContextType {};
45
4644 // Method intentionally deleted. You must provide an explicit specialization
47 // with one of the following two methods implemented.
48 static Error extract(BinaryStreamRef Stream, uint32_t &Len, T &Item) = delete;
49
50 static Error extract(BinaryStreamRef Stream, uint32_t &Len, T &Item,
51 const ContextType &Ctx) = delete;
52 };
53
54 template
55 typename WrappedCtx>
45 // with the following method implemented.
46 Error operator()(BinaryStreamRef Stream, uint32_t &Len,
47 T &Item) const = delete;
48 };
49
50 /// VarStreamArray represents an array of variable length records backed by a
51 /// stream. This could be a contiguous sequence of bytes in memory, it could
52 /// be a file on disk, or it could be a PDB stream where bytes are stored as
53 /// discontiguous blocks in a file. Usually it is desirable to treat arrays
54 /// as contiguous blocks of memory, but doing so with large PDB files, for
55 /// example, could mean allocating huge amounts of memory just to allow
56 /// re-ordering of stream data to be contiguous before iterating over it. By
57 /// abstracting this out, we need not duplicate this memory, and we can
58 /// iterate over arrays in arbitrarily formatted streams. Elements are parsed
59 /// lazily on iteration, so there is no upfront cost associated with building
60 /// or copying a VarStreamArray, no matter how large it may be.
61 ///
62 /// You create a VarStreamArray by specifying a ValueType and an Extractor type.
63 /// If you do not specify an Extractor type, you are expected to specialize
64 /// VarStreamArrayExtractor for your ValueType.
65 ///
66 /// By default an Extractor is default constructed in the class, but in some
67 /// cases you might find it useful for an Extractor to maintain state across
68 /// extractions. In this case you can provide your own Extractor through a
69 /// secondary constructor. The following examples show various ways of
70 /// creating a VarStreamArray.
71 ///
72 /// // Will use VarStreamArrayExtractor as the extractor.
73 /// VarStreamArray MyTypeArray;
74 ///
75 /// // Will use a default-constructed MyExtractor as the extractor.
76 /// VarStreamArray MyTypeArray2;
77 ///
78 /// // Will use the specific instance of MyExtractor provided.
79 /// // MyExtractor need not be default-constructible in this case.
80 /// MyExtractor E(SomeContext);
81 /// VarStreamArray MyTypeArray3(E);
82 ///
83
84 template class VarStreamArrayIterator;
85
86 template
87 typename Extractor = VarStreamArrayExtractor>
88 class VarStreamArray {
89 friend class VarStreamArrayIterator;
90
91 public:
92 typedef VarStreamArrayIterator Iterator;
93
94 VarStreamArray() = default;
95
96 explicit VarStreamArray(const Extractor &E) : E(E) {}
97
98 explicit VarStreamArray(BinaryStreamRef Stream) : Stream(Stream) {}
99
100 VarStreamArray(BinaryStreamRef Stream, const Extractor &E)
101 : Stream(Stream), E(E) {}
102
103 Iterator begin(bool *HadError = nullptr) const {
104 return Iterator(*this, E, HadError);
105 }
106
107 bool valid() const { return Stream.valid(); }
108
109 Iterator end() const { return Iterator(E); }
110
111 bool empty() const { return Stream.getLength() == 0; }
112
113 /// \brief given an offset into the array's underlying stream, return an
114 /// iterator to the record at that offset. This is considered unsafe
115 /// since the behavior is undefined if \p Offset does not refer to the
116 /// beginning of a valid record.
117 Iterator at(uint32_t Offset) const {
118 return Iterator(*this, E, Offset, nullptr);
119 }
120
121 const Extractor &getExtractor() const { return E; }
122 Extractor &getExtractor() { return E; }
123
124 BinaryStreamRef getUnderlyingStream() const { return Stream; }
125 void setUnderlyingStream(BinaryStreamRef S) { Stream = S; }
126
127 private:
128 BinaryStreamRef Stream;
129 Extractor E;
130 };
131
132 template
56133 class VarStreamArrayIterator
57 : public iterator_facade_base<
58 VarStreamArrayIterator,
59 std::forward_iterator_tag, Value> {
60 typedef VarStreamArrayIterator
61 IterType;
134 : public iterator_facade_base,
135 std::forward_iterator_tag, ValueType> {
136 typedef VarStreamArrayIterator IterType;
137 typedef VarStreamArray ArrayType;
62138
63139 public:
64 VarStreamArrayIterator() = default;
65 VarStreamArrayIterator(const ArrayType &Array, const WrappedCtx &Ctx,
66 BinaryStreamRef Stream, bool *HadError = nullptr,
67 uint32_t Offset = 0)
68 : IterRef(Stream), Ctx(&Ctx), Array(&Array), AbsOffset(Offset),
69 HadError(HadError) {
140 VarStreamArrayIterator(const ArrayType &Array, const Extractor &E,
141 bool *HadError)
142 : VarStreamArrayIterator(Array, E, 0, HadError) {}
143
144 VarStreamArrayIterator(const ArrayType &Array, const Extractor &E,
145 uint32_t Offset, bool *HadError)
146 : IterRef(Array.Stream.drop_front(Offset)), Array(&Array),
147 AbsOffset(Offset), HadError(HadError), Extract(E) {
70148 if (IterRef.getLength() == 0)
71149 moveToEnd();
72150 else {
73 auto EC = Ctx.template invoke(IterRef, ThisLen, ThisValue);
151 auto EC = Extract(IterRef, ThisLen, ThisValue);
74152 if (EC) {
75153 consumeError(std::move(EC));
76154 markError();
78156 }
79157 }
80158
81 VarStreamArrayIterator(const ArrayType &Array, const WrappedCtx &Ctx,
82 bool *HadError = nullptr)
83 : VarStreamArrayIterator(Array, Ctx, Array.Stream, HadError) {}
84
85 VarStreamArrayIterator(const WrappedCtx &Ctx) : Ctx(&Ctx) {}
86 VarStreamArrayIterator(const VarStreamArrayIterator &Other) = default;
87
159 VarStreamArrayIterator() = default;
160 explicit VarStreamArrayIterator(const Extractor &E) : Extract(E) {}
88161 ~VarStreamArrayIterator() = default;
89162
90163 bool operator==(const IterType &R) const {
102175 return false;
103176 }
104177
105 const Value &operator*() const {
178 const ValueType &operator*() const {
106179 assert(Array && !HasError);
107180 return ThisValue;
108181 }
109182
110 Value &operator*() {
183 ValueType &operator*() {
111184 assert(Array && !HasError);
112185 return ThisValue;
113186 }
124197 moveToEnd();
125198 } else {
126199 // There is some data after the current record.
127 auto EC = Ctx->template invoke(IterRef, ThisLen, ThisValue);
200 auto EC = Extract(IterRef, ThisLen, ThisValue);
128201 if (EC) {
129202 consumeError(std::move(EC));
130203 markError();
152225 *HadError = true;
153226 }
154227
155 Value ThisValue;
228 ValueType ThisValue;
156229 BinaryStreamRef IterRef;
157 const WrappedCtx *Ctx{nullptr};
230 Extractor Extract;
158231 const ArrayType *Array{nullptr};
159232 uint32_t ThisLen{0};
160233 uint32_t AbsOffset{0};
161234 bool HasError{false};
162235 bool *HadError{nullptr};
163236 };
164
165 template struct ContextWrapper {
166 ContextWrapper() = default;
167
168 explicit ContextWrapper(Context &&Ctx) : Ctx(Ctx) {}
169
170 template
171 Error invoke(BinaryStreamRef Stream, uint32_t &Len, T &Item) const {
172 return Extractor::extract(Stream, Len, Item, Ctx);
173 }
174
175 Context Ctx;
176 };
177
178 template struct ContextWrapper {
179 ContextWrapper() = default;
180
181 template
182 Error invoke(BinaryStreamRef Stream, uint32_t &Len, T &Item) const {
183 return Extractor::extract(Stream, Len, Item);
184 }
185 };
186
187 /// VarStreamArray represents an array of variable length records backed by a
188 /// stream. This could be a contiguous sequence of bytes in memory, it could
189 /// be a file on disk, or it could be a PDB stream where bytes are stored as
190 /// discontiguous blocks in a file. Usually it is desirable to treat arrays
191 /// as contiguous blocks of memory, but doing so with large PDB files, for
192 /// example, could mean allocating huge amounts of memory just to allow
193 /// re-ordering of stream data to be contiguous before iterating over it. By
194 /// abstracting this out, we need not duplicate this memory, and we can
195 /// iterate over arrays in arbitrarily formatted streams. Elements are parsed
196 /// lazily on iteration, so there is no upfront cost associated with building
197 /// or copying a VarStreamArray, no matter how large it may be.
198 ///
199 /// You create a VarStreamArray by specifying a ValueType and an Extractor type.
200 /// If you do not specify an Extractor type, you are expected to specialize
201 /// VarStreamArrayExtractor for your ValueType.
202 ///
203 /// The default extractor type is stateless, but by specializing
204 /// VarStreamArrayExtractor or defining your own custom extractor type and
205 /// adding the appropriate ContextType typedef to the class, you can pass a
206 /// context field during construction of the VarStreamArray that will be
207 /// passed to each call to extract.
208 ///
209 template
210 class VarStreamArrayBase {
211 typedef VarStreamArrayBase MyType;
212
213 public:
214 typedef VarStreamArrayIterator Iterator;
215 friend Iterator;
216
217 VarStreamArrayBase() = default;
218
219 VarStreamArrayBase(BinaryStreamRef Stream, const WrappedCtx &Ctx)
220 : Stream(Stream), Ctx(Ctx) {}
221
222 VarStreamArrayBase(const MyType &Other)
223 : Stream(Other.Stream), Ctx(Other.Ctx) {}
224
225 Iterator begin(bool *HadError = nullptr) const {
226 if (empty())
227 return end();
228
229 return Iterator(*this, Ctx, Stream, HadError);
230 }
231
232 bool valid() const { return Stream.valid(); }
233
234 Iterator end() const { return Iterator(Ctx); }
235
236 bool empty() const { return Stream.getLength() == 0; }
237
238 /// \brief given an offset into the array's underlying stream, return an
239 /// iterator to the record at that offset. This is considered unsafe
240 /// since the behavior is undefined if \p Offset does not refer to the
241 /// beginning of a valid record.
242 Iterator at(uint32_t Offset) const {
243 return Iterator(*this, Ctx, Stream.drop_front(Offset), nullptr, Offset);
244 }
245
246 BinaryStreamRef getUnderlyingStream() const { return Stream; }
247
248 private:
249 BinaryStreamRef Stream;
250 WrappedCtx Ctx;
251 };
252
253 template
254 class VarStreamArrayImpl
255 : public VarStreamArrayBase
256 ContextWrapper> {
257 typedef ContextWrapper WrappedContext;
258 typedef VarStreamArrayImpl MyType;
259 typedef VarStreamArrayBase BaseType;
260
261 public:
262 typedef Context ContextType;
263
264 VarStreamArrayImpl() = default;
265 VarStreamArrayImpl(BinaryStreamRef Stream, Context &&Ctx)
266 : BaseType(Stream, WrappedContext(std::forward(Ctx))) {}
267 };
268
269 template
270 class VarStreamArrayImpl
271 : public VarStreamArrayBase> {
272 typedef ContextWrapper WrappedContext;
273 typedef VarStreamArrayImpl MyType;
274 typedef VarStreamArrayBase BaseType;
275
276 public:
277 VarStreamArrayImpl() = default;
278 VarStreamArrayImpl(BinaryStreamRef Stream)
279 : BaseType(Stream, WrappedContext()) {}
280 };
281
282 template >
283 using VarStreamArray =
284 VarStreamArrayImpl;
285237
286238 template class FixedStreamArrayIterator;
287239
197197 BinaryStreamRef S;
198198 if (auto EC = readStreamRef(S, Size))
199199 return EC;
200 Array = VarStreamArray(S);
201 return Error::success();
202 }
203
204 /// Read a VarStreamArray of size \p Size bytes and store the result into
205 /// \p Array. Updates the stream's offset to point after the newly read
206 /// array. Never causes a copy (although iterating the elements of the
207 /// VarStreamArray may, depending upon the implementation of the underlying
208 /// stream).
209 ///
210 /// \returns a success error code if the data was successfully read, otherwise
211 /// returns an appropriate error code.
212 template
213 Error readArray(VarStreamArray &Array, uint32_t Size,
214 ContextType &&Context) {
215 BinaryStreamRef S;
216 if (auto EC = readStreamRef(S, Size))
217 return EC;
218 Array = VarStreamArray(S, std::move(Context));
200 Array.setUnderlyingStream(S);
219201 return Error::success();
220202 }
221203
2424 // Checksum bytes follow.
2525 };
2626
27 Error llvm::VarStreamArrayExtractor::extract(
28 BinaryStreamRef Stream, uint32_t &Len, FileChecksumEntry &Item) {
27 Error llvm::VarStreamArrayExtractor::
28 operator()(BinaryStreamRef Stream, uint32_t &Len, FileChecksumEntry &Item) {
2929 BinaryStreamReader Reader(Stream);
3030
3131 const FileChecksumEntryHeader *Header;
1515 using namespace llvm::codeview;
1616
1717 namespace llvm {
18 Error VarStreamArrayExtractor::extract(
19 BinaryStreamRef Stream, uint32_t &Len,
20 codeview::CrossModuleImportItem &Item) {
18 Error VarStreamArrayExtractor::
19 operator()(BinaryStreamRef Stream, uint32_t &Len,
20 codeview::CrossModuleImportItem &Item) {
2121 BinaryStreamReader Reader(Stream);
2222 if (Reader.bytesRemaining() < sizeof(CrossModuleImport))
2323 return make_error(
1616 using namespace llvm;
1717 using namespace llvm::codeview;
1818
19 Error VarStreamArrayExtractor::extract(
20 BinaryStreamRef Stream, uint32_t &Len, InlineeSourceLine &Item,
21 bool HasExtraFiles) {
19 Error VarStreamArrayExtractor::
20 operator()(BinaryStreamRef Stream, uint32_t &Len, InlineeSourceLine &Item) {
2221 BinaryStreamReader Reader(Stream);
2322
2423 if (auto EC = Reader.readObject(Item.Header))
4342 if (auto EC = Reader.readEnum(Signature))
4443 return EC;
4544
46 if (auto EC =
47 Reader.readArray(Lines, Reader.bytesRemaining(), hasExtraFiles()))
45 Lines.getExtractor().HasExtraFiles = hasExtraFiles();
46 if (auto EC = Reader.readArray(Lines, Reader.bytesRemaining()))
4847 return EC;
4948
5049 assert(Reader.bytesRemaining() == 0);
1616 using namespace llvm;
1717 using namespace llvm::codeview;
1818
19 Error LineColumnExtractor::extract(BinaryStreamRef Stream, uint32_t &Len,
20 LineColumnEntry &Item,
21 const LineFragmentHeader *Header) {
19 Error LineColumnExtractor::operator()(BinaryStreamRef Stream, uint32_t &Len,
20 LineColumnEntry &Item) {
2221 using namespace codeview;
2322 const LineBlockFragmentHeader *BlockHeader;
2423 BinaryStreamReader Reader(Stream);
5554 if (auto EC = Reader.readObject(Header))
5655 return EC;
5756
58 if (auto EC =
59 Reader.readArray(LinesAndColumns, Reader.bytesRemaining(), Header))
57 LinesAndColumns.getExtractor().Header = Header;
58 if (auto EC = Reader.readArray(LinesAndColumns, Reader.bytesRemaining()))
6059 return EC;
6160
6261 return Error::success();
5050
5151 CVType Type;
5252 uint32_t Len;
53 error(VarStreamArrayExtractor::extract(Bytes, Len, Type));
53 VarStreamArrayExtractor Extract;
54 error(Extract(Bytes, Len, Type));
5455
5556 TypeDatabaseVisitor DBV(Database);
5657 error(codeview::visitTypeRecord(Type, Index, DBV));
415415
416416 struct StringExtractor {
417417 public:
418 typedef uint32_t &ContextType;
419 static Error extract(BinaryStreamRef Stream, uint32_t &Len, StringRef &Item,
420 uint32_t &Index) {
418 Error operator()(BinaryStreamRef Stream, uint32_t &Len, StringRef &Item) {
421419 if (Index == 0)
422420 Len = strlen("1. Test");
423421 else if (Index == 1)
434432 ++Index;
435433 return Error::success();
436434 }
435
436 uint32_t Index = 0;
437437 };
438438
439439 for (auto &Stream : Streams) {
440 uint32_t Context = 0;
441 VarStreamArray Array(*Stream.Input, Context);
440 VarStreamArray Array(*Stream.Input);
442441 auto Iter = Array.begin();
443442 ASSERT_EQ("1. Test", *Iter++);
444443 ASSERT_EQ("2. Longer Test", *Iter++);