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