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