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(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(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(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>(stream_error_code::invalid_offset);
135     if (getLength() < DataSize + Offset)
136       return make_error<BinaryStreamError>(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(Off, Size);
202     return {Off + Offset, SubSub};
203   }
204   BinarySubstreamRef drop_front(uint64_t N) const {
205     return slice(N, size() - N);
206   }
207   BinarySubstreamRef keep_front(uint64_t N) const { return slice(0, N); }
208 
209   std::pair<BinarySubstreamRef, BinarySubstreamRef> split(uint64_t Off) const {
210     return std::make_pair(keep_front(Off), drop_front(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>(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