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::support::endianness Endian) 34 : Endian(Endian), Data(Data) {} 35 BinaryByteStream(StringRef Data, llvm::support::endianness Endian) 36 : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {} 37 38 llvm::support::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::support::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::support::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::support::endianness Endian) 93 : Data(Data), ImmutableStream(Data, Endian) {} 94 95 llvm::support::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::support::endianness Endian = llvm::support::little; 137 138 public: 139 AppendingBinaryByteStream() = default; 140 AppendingBinaryByteStream(llvm::support::endianness Endian) 141 : Endian(Endian) {} 142 143 void clear() { Data.clear(); } 144 145 llvm::support::endianness getEndian() const override { return Endian; } 146 147 Error readBytes(uint64_t Offset, uint64_t Size, 148 ArrayRef<uint8_t> &Buffer) override { 149 if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) 150 return EC; 151 152 Buffer = makeArrayRef(Data).slice(Offset, Size); 153 return Error::success(); 154 } 155 156 void insert(uint64_t Offset, ArrayRef<uint8_t> Bytes) { 157 Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end()); 158 } 159 160 Error readLongestContiguousChunk(uint64_t Offset, 161 ArrayRef<uint8_t> &Buffer) override { 162 if (auto EC = checkOffsetForWrite(Offset, 1)) 163 return EC; 164 165 Buffer = makeArrayRef(Data).slice(Offset); 166 return Error::success(); 167 } 168 169 uint64_t getLength() override { return Data.size(); } 170 171 Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Buffer) override { 172 if (Buffer.empty()) 173 return Error::success(); 174 175 // This is well-defined for any case except where offset is strictly 176 // greater than the current length. If offset is equal to the current 177 // length, we can still grow. If offset is beyond the current length, we 178 // would have to decide how to deal with the intermediate uninitialized 179 // bytes. So we punt on that case for simplicity and just say it's an 180 // error. 181 if (Offset > getLength()) 182 return make_error<BinaryStreamError>(stream_error_code::invalid_offset); 183 184 uint64_t RequiredSize = Offset + Buffer.size(); 185 if (RequiredSize > Data.size()) 186 Data.resize(RequiredSize); 187 188 ::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size()); 189 return Error::success(); 190 } 191 192 Error commit() override { return Error::success(); } 193 194 /// Return the properties of this stream. 195 BinaryStreamFlags getFlags() const override { return BSF_Write | BSF_Append; } 196 197 MutableArrayRef<uint8_t> data() { return Data; } 198 }; 199 200 /// An implementation of WritableBinaryStream backed by an llvm 201 /// FileOutputBuffer. 202 class FileBufferByteStream : public WritableBinaryStream { 203 private: 204 class StreamImpl : public MutableBinaryByteStream { 205 public: 206 StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer, 207 llvm::support::endianness Endian) 208 : MutableBinaryByteStream( 209 MutableArrayRef<uint8_t>(Buffer->getBufferStart(), 210 Buffer->getBufferEnd()), 211 Endian), 212 FileBuffer(std::move(Buffer)) {} 213 214 Error commit() override { 215 if (FileBuffer->commit()) 216 return make_error<BinaryStreamError>( 217 stream_error_code::filesystem_error); 218 return Error::success(); 219 } 220 221 /// Returns a pointer to the start of the buffer. 222 uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); } 223 224 /// Returns a pointer to the end of the buffer. 225 uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); } 226 227 private: 228 std::unique_ptr<FileOutputBuffer> FileBuffer; 229 }; 230 231 public: 232 FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer, 233 llvm::support::endianness Endian) 234 : Impl(std::move(Buffer), Endian) {} 235 236 llvm::support::endianness getEndian() const override { 237 return Impl.getEndian(); 238 } 239 240 Error readBytes(uint64_t Offset, uint64_t Size, 241 ArrayRef<uint8_t> &Buffer) override { 242 return Impl.readBytes(Offset, Size, Buffer); 243 } 244 245 Error readLongestContiguousChunk(uint64_t Offset, 246 ArrayRef<uint8_t> &Buffer) override { 247 return Impl.readLongestContiguousChunk(Offset, Buffer); 248 } 249 250 uint64_t getLength() override { return Impl.getLength(); } 251 252 Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) override { 253 return Impl.writeBytes(Offset, Data); 254 } 255 256 Error commit() override { return Impl.commit(); } 257 258 /// Returns a pointer to the start of the buffer. 259 uint8_t *getBufferStart() const { return Impl.getBufferStart(); } 260 261 /// Returns a pointer to the end of the buffer. 262 uint8_t *getBufferEnd() const { return Impl.getBufferEnd(); } 263 264 private: 265 StreamImpl Impl; 266 }; 267 268 } // end namespace llvm 269 270 #endif // LLVM_SUPPORT_BINARYBYTESTREAM_H 271