1 //===- BinaryByteStream.h ---------------------------------------*- 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 // A BinaryStream which stores data in a single continguous memory buffer. 8 //===----------------------------------------------------------------------===// 9 10 #ifndef LLVM_SUPPORT_BINARYBYTESTREAM_H 11 #define LLVM_SUPPORT_BINARYBYTESTREAM_H 12 13 #include "llvm/ADT/ArrayRef.h" 14 #include "llvm/ADT/StringRef.h" 15 #include "llvm/Support/BinaryStream.h" 16 #include "llvm/Support/BinaryStreamError.h" 17 #include "llvm/Support/Error.h" 18 #include "llvm/Support/FileOutputBuffer.h" 19 #include "llvm/Support/MemoryBuffer.h" 20 #include <cstdint> 21 #include <cstring> 22 #include <memory> 23 24 namespace llvm { 25 26 /// An implementation of BinaryStream which holds its entire data set 27 /// in a single contiguous buffer. BinaryByteStream guarantees that no read 28 /// operation will ever incur a copy. Note that BinaryByteStream does not 29 /// own the underlying buffer. 30 class BinaryByteStream : public BinaryStream { 31 public: 32 BinaryByteStream() = default; 33 BinaryByteStream(ArrayRef<uint8_t> Data, llvm::endianness Endian) 34 : Endian(Endian), Data(Data) {} 35 BinaryByteStream(StringRef Data, llvm::endianness Endian) 36 : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {} 37 38 llvm::endianness getEndian() const override { return Endian; } 39 40 Error readBytes(uint64_t Offset, uint64_t Size, 41 ArrayRef<uint8_t> &Buffer) override { 42 if (auto EC = checkOffsetForRead(Offset, Size)) 43 return EC; 44 Buffer = Data.slice(Offset, Size); 45 return Error::success(); 46 } 47 48 Error readLongestContiguousChunk(uint64_t Offset, 49 ArrayRef<uint8_t> &Buffer) override { 50 if (auto EC = checkOffsetForRead(Offset, 1)) 51 return EC; 52 Buffer = Data.slice(Offset); 53 return Error::success(); 54 } 55 56 uint64_t getLength() override { return Data.size(); } 57 58 ArrayRef<uint8_t> data() const { return Data; } 59 60 StringRef str() const { 61 const char *CharData = reinterpret_cast<const char *>(Data.data()); 62 return StringRef(CharData, Data.size()); 63 } 64 65 protected: 66 llvm::endianness Endian; 67 ArrayRef<uint8_t> Data; 68 }; 69 70 /// An implementation of BinaryStream whose data is backed by an llvm 71 /// MemoryBuffer object. MemoryBufferByteStream owns the MemoryBuffer in 72 /// question. As with BinaryByteStream, reading from a MemoryBufferByteStream 73 /// will never cause a copy. 74 class MemoryBufferByteStream : public BinaryByteStream { 75 public: 76 MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer, 77 llvm::endianness Endian) 78 : BinaryByteStream(Buffer->getBuffer(), Endian), 79 MemBuffer(std::move(Buffer)) {} 80 81 std::unique_ptr<MemoryBuffer> MemBuffer; 82 }; 83 84 /// An implementation of BinaryStream which holds its entire data set 85 /// in a single contiguous buffer. As with BinaryByteStream, the mutable 86 /// version also guarantees that no read operation will ever incur a copy, 87 /// and similarly it does not own the underlying buffer. 88 class MutableBinaryByteStream : public WritableBinaryStream { 89 public: 90 MutableBinaryByteStream() = default; 91 MutableBinaryByteStream(MutableArrayRef<uint8_t> Data, 92 llvm::endianness Endian) 93 : Data(Data), ImmutableStream(Data, Endian) {} 94 95 llvm::endianness getEndian() const override { 96 return ImmutableStream.getEndian(); 97 } 98 99 Error readBytes(uint64_t Offset, uint64_t Size, 100 ArrayRef<uint8_t> &Buffer) override { 101 return ImmutableStream.readBytes(Offset, Size, Buffer); 102 } 103 104 Error readLongestContiguousChunk(uint64_t Offset, 105 ArrayRef<uint8_t> &Buffer) override { 106 return ImmutableStream.readLongestContiguousChunk(Offset, Buffer); 107 } 108 109 uint64_t getLength() override { return ImmutableStream.getLength(); } 110 111 Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Buffer) override { 112 if (Buffer.empty()) 113 return Error::success(); 114 115 if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) 116 return EC; 117 118 uint8_t *DataPtr = const_cast<uint8_t *>(Data.data()); 119 ::memcpy(DataPtr + Offset, Buffer.data(), Buffer.size()); 120 return Error::success(); 121 } 122 123 Error commit() override { return Error::success(); } 124 125 MutableArrayRef<uint8_t> data() const { return Data; } 126 127 private: 128 MutableArrayRef<uint8_t> Data; 129 BinaryByteStream ImmutableStream; 130 }; 131 132 /// An implementation of WritableBinaryStream which can write at its end 133 /// causing the underlying data to grow. This class owns the underlying data. 134 class AppendingBinaryByteStream : public WritableBinaryStream { 135 std::vector<uint8_t> Data; 136 llvm::endianness Endian = llvm::endianness::little; 137 138 public: 139 AppendingBinaryByteStream() = default; 140 AppendingBinaryByteStream(llvm::endianness Endian) : Endian(Endian) {} 141 142 void clear() { Data.clear(); } 143 144 llvm::endianness getEndian() const override { return Endian; } 145 146 Error readBytes(uint64_t Offset, uint64_t Size, 147 ArrayRef<uint8_t> &Buffer) override { 148 if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) 149 return EC; 150 151 Buffer = ArrayRef(Data).slice(Offset, Size); 152 return Error::success(); 153 } 154 155 void insert(uint64_t Offset, ArrayRef<uint8_t> Bytes) { 156 Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end()); 157 } 158 159 Error readLongestContiguousChunk(uint64_t Offset, 160 ArrayRef<uint8_t> &Buffer) override { 161 if (auto EC = checkOffsetForWrite(Offset, 1)) 162 return EC; 163 164 Buffer = ArrayRef(Data).slice(Offset); 165 return Error::success(); 166 } 167 168 uint64_t getLength() override { return Data.size(); } 169 170 Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Buffer) override { 171 if (Buffer.empty()) 172 return Error::success(); 173 174 // This is well-defined for any case except where offset is strictly 175 // greater than the current length. If offset is equal to the current 176 // length, we can still grow. If offset is beyond the current length, we 177 // would have to decide how to deal with the intermediate uninitialized 178 // bytes. So we punt on that case for simplicity and just say it's an 179 // error. 180 if (Offset > getLength()) 181 return make_error<BinaryStreamError>(stream_error_code::invalid_offset); 182 183 uint64_t RequiredSize = Offset + Buffer.size(); 184 if (RequiredSize > Data.size()) 185 Data.resize(RequiredSize); 186 187 ::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size()); 188 return Error::success(); 189 } 190 191 Error commit() override { return Error::success(); } 192 193 /// Return the properties of this stream. 194 BinaryStreamFlags getFlags() const override { return BSF_Write | BSF_Append; } 195 196 MutableArrayRef<uint8_t> data() { return Data; } 197 }; 198 199 /// An implementation of WritableBinaryStream backed by an llvm 200 /// FileOutputBuffer. 201 class FileBufferByteStream : public WritableBinaryStream { 202 private: 203 class StreamImpl : public MutableBinaryByteStream { 204 public: 205 StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer, 206 llvm::endianness Endian) 207 : MutableBinaryByteStream( 208 MutableArrayRef<uint8_t>(Buffer->getBufferStart(), 209 Buffer->getBufferEnd()), 210 Endian), 211 FileBuffer(std::move(Buffer)) {} 212 213 Error commit() override { 214 if (FileBuffer->commit()) 215 return make_error<BinaryStreamError>( 216 stream_error_code::filesystem_error); 217 return Error::success(); 218 } 219 220 /// Returns a pointer to the start of the buffer. 221 uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); } 222 223 /// Returns a pointer to the end of the buffer. 224 uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); } 225 226 private: 227 std::unique_ptr<FileOutputBuffer> FileBuffer; 228 }; 229 230 public: 231 FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer, 232 llvm::endianness Endian) 233 : Impl(std::move(Buffer), Endian) {} 234 235 llvm::endianness getEndian() const override { return Impl.getEndian(); } 236 237 Error readBytes(uint64_t Offset, uint64_t Size, 238 ArrayRef<uint8_t> &Buffer) override { 239 return Impl.readBytes(Offset, Size, Buffer); 240 } 241 242 Error readLongestContiguousChunk(uint64_t Offset, 243 ArrayRef<uint8_t> &Buffer) override { 244 return Impl.readLongestContiguousChunk(Offset, Buffer); 245 } 246 247 uint64_t getLength() override { return Impl.getLength(); } 248 249 Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) override { 250 return Impl.writeBytes(Offset, Data); 251 } 252 253 Error commit() override { return Impl.commit(); } 254 255 /// Returns a pointer to the start of the buffer. 256 uint8_t *getBufferStart() const { return Impl.getBufferStart(); } 257 258 /// Returns a pointer to the end of the buffer. 259 uint8_t *getBufferEnd() const { return Impl.getBufferEnd(); } 260 261 private: 262 StreamImpl Impl; 263 }; 264 265 } // end namespace llvm 266 267 #endif // LLVM_SUPPORT_BINARYBYTESTREAM_H 268