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