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