1 | //===- BinaryStreamRef.h - A copyable reference to a 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_BINARYSTREAMREF_H |
10 | #define LLVM_SUPPORT_BINARYSTREAMREF_H |
11 | |
12 | #include "llvm/ADT/ArrayRef.h" |
13 | #include "llvm/Support/BinaryStream.h" |
14 | #include "llvm/Support/BinaryStreamError.h" |
15 | #include "llvm/Support/Error.h" |
16 | #include <cstdint> |
17 | #include <memory> |
18 | #include <optional> |
19 | |
20 | namespace llvm { |
21 | |
22 | /// Common stuff for mutable and immutable StreamRefs. |
23 | template <class RefType, class StreamType> class BinaryStreamRefBase { |
24 | protected: |
25 | BinaryStreamRefBase() = default; |
26 | explicit BinaryStreamRefBase(StreamType &BorrowedImpl) |
27 | : BorrowedImpl(&BorrowedImpl), ViewOffset(0) { |
28 | if (!(BorrowedImpl.getFlags() & BSF_Append)) |
29 | Length = BorrowedImpl.getLength(); |
30 | } |
31 | |
32 | BinaryStreamRefBase(std::shared_ptr<StreamType> SharedImpl, uint64_t Offset, |
33 | std::optional<uint64_t> Length) |
34 | : SharedImpl(SharedImpl), BorrowedImpl(SharedImpl.get()), |
35 | ViewOffset(Offset), Length(Length) {} |
36 | BinaryStreamRefBase(StreamType &BorrowedImpl, uint64_t Offset, |
37 | std::optional<uint64_t> Length) |
38 | : BorrowedImpl(&BorrowedImpl), ViewOffset(Offset), Length(Length) {} |
39 | BinaryStreamRefBase(const BinaryStreamRefBase &Other) = default; |
40 | BinaryStreamRefBase &operator=(const BinaryStreamRefBase &Other) = default; |
41 | |
42 | BinaryStreamRefBase &operator=(BinaryStreamRefBase &&Other) = default; |
43 | BinaryStreamRefBase(BinaryStreamRefBase &&Other) = default; |
44 | |
45 | public: |
46 | llvm::endianness getEndian() const { return BorrowedImpl->getEndian(); } |
47 | |
48 | uint64_t getLength() const { |
49 | if (Length) |
50 | return *Length; |
51 | |
52 | return BorrowedImpl ? (BorrowedImpl->getLength() - ViewOffset) : 0; |
53 | } |
54 | |
55 | /// Return a new BinaryStreamRef with the first \p N elements removed. If |
56 | /// this BinaryStreamRef is length-tracking, then the resulting one will be |
57 | /// too. |
58 | RefType drop_front(uint64_t N) const { |
59 | if (!BorrowedImpl) |
60 | return RefType(); |
61 | |
62 | N = std::min(N, getLength()); |
63 | RefType Result(static_cast<const RefType &>(*this)); |
64 | if (N == 0) |
65 | return Result; |
66 | |
67 | Result.ViewOffset += N; |
68 | if (Result.Length) |
69 | *Result.Length -= N; |
70 | return Result; |
71 | } |
72 | |
73 | /// Return a new BinaryStreamRef with the last \p N elements removed. If |
74 | /// this BinaryStreamRef is length-tracking and \p N is greater than 0, then |
75 | /// this BinaryStreamRef will no longer length-track. |
76 | RefType drop_back(uint64_t N) const { |
77 | if (!BorrowedImpl) |
78 | return RefType(); |
79 | |
80 | RefType Result(static_cast<const RefType &>(*this)); |
81 | N = std::min(N, getLength()); |
82 | |
83 | if (N == 0) |
84 | return Result; |
85 | |
86 | // Since we're dropping non-zero bytes from the end, stop length-tracking |
87 | // by setting the length of the resulting StreamRef to an explicit value. |
88 | if (!Result.Length) |
89 | Result.Length = getLength(); |
90 | |
91 | *Result.Length -= N; |
92 | return Result; |
93 | } |
94 | |
95 | /// Return a new BinaryStreamRef with only the first \p N elements remaining. |
96 | RefType keep_front(uint64_t N) const { |
97 | assert(N <= getLength()); |
98 | return drop_back(N: getLength() - N); |
99 | } |
100 | |
101 | /// Return a new BinaryStreamRef with only the last \p N elements remaining. |
102 | RefType keep_back(uint64_t N) const { |
103 | assert(N <= getLength()); |
104 | return drop_front(N: getLength() - N); |
105 | } |
106 | |
107 | /// Return a new BinaryStreamRef with the first and last \p N elements |
108 | /// removed. |
109 | RefType drop_symmetric(uint64_t N) const { |
110 | return drop_front(N).drop_back(N); |
111 | } |
112 | |
113 | /// Return a new BinaryStreamRef with the first \p Offset elements removed, |
114 | /// and retaining exactly \p Len elements. |
115 | RefType slice(uint64_t Offset, uint64_t Len) const { |
116 | return drop_front(N: Offset).keep_front(Len); |
117 | } |
118 | |
119 | bool valid() const { return BorrowedImpl != nullptr; } |
120 | |
121 | friend bool operator==(const RefType &LHS, const RefType &RHS) { |
122 | if (LHS.BorrowedImpl != RHS.BorrowedImpl) |
123 | return false; |
124 | if (LHS.ViewOffset != RHS.ViewOffset) |
125 | return false; |
126 | if (LHS.Length != RHS.Length) |
127 | return false; |
128 | return true; |
129 | } |
130 | |
131 | protected: |
132 | Error checkOffsetForRead(uint64_t Offset, uint64_t DataSize) const { |
133 | if (Offset > getLength()) |
134 | return make_error<BinaryStreamError>(Args: stream_error_code::invalid_offset); |
135 | if (getLength() < DataSize + Offset) |
136 | return make_error<BinaryStreamError>(Args: stream_error_code::stream_too_short); |
137 | return Error::success(); |
138 | } |
139 | |
140 | std::shared_ptr<StreamType> SharedImpl; |
141 | StreamType *BorrowedImpl = nullptr; |
142 | uint64_t ViewOffset = 0; |
143 | std::optional<uint64_t> Length; |
144 | }; |
145 | |
146 | /// BinaryStreamRef is to BinaryStream what ArrayRef is to an Array. It |
147 | /// provides copy-semantics and read only access to a "window" of the underlying |
148 | /// BinaryStream. Note that BinaryStreamRef is *not* a BinaryStream. That is to |
149 | /// say, it does not inherit and override the methods of BinaryStream. In |
150 | /// general, you should not pass around pointers or references to BinaryStreams |
151 | /// and use inheritance to achieve polymorphism. Instead, you should pass |
152 | /// around BinaryStreamRefs by value and achieve polymorphism that way. |
153 | class BinaryStreamRef |
154 | : public BinaryStreamRefBase<BinaryStreamRef, BinaryStream> { |
155 | friend BinaryStreamRefBase<BinaryStreamRef, BinaryStream>; |
156 | friend class WritableBinaryStreamRef; |
157 | BinaryStreamRef(std::shared_ptr<BinaryStream> Impl, uint64_t ViewOffset, |
158 | std::optional<uint64_t> Length) |
159 | : BinaryStreamRefBase(Impl, ViewOffset, Length) {} |
160 | |
161 | public: |
162 | BinaryStreamRef() = default; |
163 | BinaryStreamRef(BinaryStream &Stream); |
164 | BinaryStreamRef(BinaryStream &Stream, uint64_t Offset, |
165 | std::optional<uint64_t> Length); |
166 | explicit BinaryStreamRef(ArrayRef<uint8_t> Data, llvm::endianness Endian); |
167 | explicit BinaryStreamRef(StringRef Data, llvm::endianness Endian); |
168 | |
169 | BinaryStreamRef(const BinaryStreamRef &Other) = default; |
170 | BinaryStreamRef &operator=(const BinaryStreamRef &Other) = default; |
171 | BinaryStreamRef(BinaryStreamRef &&Other) = default; |
172 | BinaryStreamRef &operator=(BinaryStreamRef &&Other) = default; |
173 | |
174 | // Use BinaryStreamRef.slice() instead. |
175 | BinaryStreamRef(BinaryStreamRef &S, uint64_t Offset, |
176 | uint64_t Length) = delete; |
177 | |
178 | /// Given an Offset into this StreamRef and a Size, return a reference to a |
179 | /// buffer owned by the stream. |
180 | /// |
181 | /// \returns a success error code if the entire range of data is within the |
182 | /// bounds of this BinaryStreamRef's view and the implementation could read |
183 | /// the data, and an appropriate error code otherwise. |
184 | Error readBytes(uint64_t Offset, uint64_t Size, |
185 | ArrayRef<uint8_t> &Buffer) const; |
186 | |
187 | /// Given an Offset into this BinaryStreamRef, return a reference to the |
188 | /// largest buffer the stream could support without necessitating a copy. |
189 | /// |
190 | /// \returns a success error code if implementation could read the data, |
191 | /// and an appropriate error code otherwise. |
192 | Error readLongestContiguousChunk(uint64_t Offset, |
193 | ArrayRef<uint8_t> &Buffer) const; |
194 | }; |
195 | |
196 | struct BinarySubstreamRef { |
197 | uint64_t Offset = 0; // Offset in the parent stream |
198 | BinaryStreamRef StreamData; // Stream Data |
199 | |
200 | BinarySubstreamRef slice(uint64_t Off, uint64_t Size) const { |
201 | BinaryStreamRef SubSub = StreamData.slice(Offset: Off, Len: Size); |
202 | return {.Offset: Off + Offset, .StreamData: SubSub}; |
203 | } |
204 | BinarySubstreamRef drop_front(uint64_t N) const { |
205 | return slice(Off: N, Size: size() - N); |
206 | } |
207 | BinarySubstreamRef keep_front(uint64_t N) const { return slice(Off: 0, Size: N); } |
208 | |
209 | std::pair<BinarySubstreamRef, BinarySubstreamRef> split(uint64_t Off) const { |
210 | return std::make_pair(x: keep_front(N: Off), y: drop_front(N: Off)); |
211 | } |
212 | |
213 | uint64_t size() const { return StreamData.getLength(); } |
214 | bool empty() const { return size() == 0; } |
215 | }; |
216 | |
217 | class WritableBinaryStreamRef |
218 | : public BinaryStreamRefBase<WritableBinaryStreamRef, |
219 | WritableBinaryStream> { |
220 | friend BinaryStreamRefBase<WritableBinaryStreamRef, WritableBinaryStream>; |
221 | WritableBinaryStreamRef(std::shared_ptr<WritableBinaryStream> Impl, |
222 | uint64_t ViewOffset, std::optional<uint64_t> Length) |
223 | : BinaryStreamRefBase(Impl, ViewOffset, Length) {} |
224 | |
225 | Error checkOffsetForWrite(uint64_t Offset, uint64_t DataSize) const { |
226 | if (!(BorrowedImpl->getFlags() & BSF_Append)) |
227 | return checkOffsetForRead(Offset, DataSize); |
228 | |
229 | if (Offset > getLength()) |
230 | return make_error<BinaryStreamError>(Args: stream_error_code::invalid_offset); |
231 | return Error::success(); |
232 | } |
233 | |
234 | public: |
235 | WritableBinaryStreamRef() = default; |
236 | WritableBinaryStreamRef(WritableBinaryStream &Stream); |
237 | WritableBinaryStreamRef(WritableBinaryStream &Stream, uint64_t Offset, |
238 | std::optional<uint64_t> Length); |
239 | explicit WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data, |
240 | llvm::endianness Endian); |
241 | WritableBinaryStreamRef(const WritableBinaryStreamRef &Other) = default; |
242 | WritableBinaryStreamRef & |
243 | operator=(const WritableBinaryStreamRef &Other) = default; |
244 | |
245 | WritableBinaryStreamRef(WritableBinaryStreamRef &&Other) = default; |
246 | WritableBinaryStreamRef &operator=(WritableBinaryStreamRef &&Other) = default; |
247 | |
248 | // Use WritableBinaryStreamRef.slice() instead. |
249 | WritableBinaryStreamRef(WritableBinaryStreamRef &S, uint64_t Offset, |
250 | uint64_t Length) = delete; |
251 | |
252 | /// Given an Offset into this WritableBinaryStreamRef and some input data, |
253 | /// writes the data to the underlying stream. |
254 | /// |
255 | /// \returns a success error code if the data could fit within the underlying |
256 | /// stream at the specified location and the implementation could write the |
257 | /// data, and an appropriate error code otherwise. |
258 | Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) const; |
259 | |
260 | /// Conver this WritableBinaryStreamRef to a read-only BinaryStreamRef. |
261 | operator BinaryStreamRef() const; |
262 | |
263 | /// For buffered streams, commits changes to the backing store. |
264 | Error commit(); |
265 | }; |
266 | |
267 | } // end namespace llvm |
268 | |
269 | #endif // LLVM_SUPPORT_BINARYSTREAMREF_H |
270 | |