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