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