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; BinaryByteStream(ArrayRef<uint8_t> Data,llvm::support::endianness Endian)34 BinaryByteStream(ArrayRef<uint8_t> Data, llvm::support::endianness Endian) 35 : Endian(Endian), Data(Data) {} BinaryByteStream(StringRef Data,llvm::support::endianness Endian)36 BinaryByteStream(StringRef Data, llvm::support::endianness Endian) 37 : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {} 38 getEndian()39 llvm::support::endianness getEndian() const override { return Endian; } 40 readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)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 readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)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 getLength()57 uint32_t getLength() override { return Data.size(); } 58 data()59 ArrayRef<uint8_t> data() const { return Data; } 60 str()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: MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer,llvm::support::endianness Endian)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; MutableBinaryByteStream(MutableArrayRef<uint8_t> Data,llvm::support::endianness Endian)92 MutableBinaryByteStream(MutableArrayRef<uint8_t> Data, 93 llvm::support::endianness Endian) 94 : Data(Data), ImmutableStream(Data, Endian) {} 95 getEndian()96 llvm::support::endianness getEndian() const override { 97 return ImmutableStream.getEndian(); 98 } 99 readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)100 Error readBytes(uint32_t Offset, uint32_t Size, 101 ArrayRef<uint8_t> &Buffer) override { 102 return ImmutableStream.readBytes(Offset, Size, Buffer); 103 } 104 readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)105 Error readLongestContiguousChunk(uint32_t Offset, 106 ArrayRef<uint8_t> &Buffer) override { 107 return ImmutableStream.readLongestContiguousChunk(Offset, Buffer); 108 } 109 getLength()110 uint32_t getLength() override { return ImmutableStream.getLength(); } 111 writeBytes(uint32_t Offset,ArrayRef<uint8_t> Buffer)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 commit()124 Error commit() override { return Error::success(); } 125 data()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; AppendingBinaryByteStream(llvm::support::endianness Endian)141 AppendingBinaryByteStream(llvm::support::endianness Endian) 142 : Endian(Endian) {} 143 clear()144 void clear() { Data.clear(); } 145 getEndian()146 llvm::support::endianness getEndian() const override { return Endian; } 147 readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)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 insert(uint32_t Offset,ArrayRef<uint8_t> Bytes)157 void insert(uint32_t Offset, ArrayRef<uint8_t> Bytes) { 158 Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end()); 159 } 160 readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)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 getLength()170 uint32_t getLength() override { return Data.size(); } 171 writeBytes(uint32_t Offset,ArrayRef<uint8_t> Buffer)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 commit()193 Error commit() override { return Error::success(); } 194 195 /// Return the properties of this stream. getFlags()196 virtual BinaryStreamFlags getFlags() const override { 197 return BSF_Write | BSF_Append; 198 } 199 data()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: StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer,llvm::support::endianness Endian)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 commit()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. getBufferStart()225 uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); } 226 227 /// Returns a pointer to the end of the buffer. getBufferEnd()228 uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); } 229 230 private: 231 std::unique_ptr<FileOutputBuffer> FileBuffer; 232 }; 233 234 public: FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer,llvm::support::endianness Endian)235 FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer, 236 llvm::support::endianness Endian) 237 : Impl(std::move(Buffer), Endian) {} 238 getEndian()239 llvm::support::endianness getEndian() const override { 240 return Impl.getEndian(); 241 } 242 readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)243 Error readBytes(uint32_t Offset, uint32_t Size, 244 ArrayRef<uint8_t> &Buffer) override { 245 return Impl.readBytes(Offset, Size, Buffer); 246 } 247 readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)248 Error readLongestContiguousChunk(uint32_t Offset, 249 ArrayRef<uint8_t> &Buffer) override { 250 return Impl.readLongestContiguousChunk(Offset, Buffer); 251 } 252 getLength()253 uint32_t getLength() override { return Impl.getLength(); } 254 writeBytes(uint32_t Offset,ArrayRef<uint8_t> Data)255 Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) override { 256 return Impl.writeBytes(Offset, Data); 257 } 258 commit()259 Error commit() override { return Impl.commit(); } 260 261 /// Returns a pointer to the start of the buffer. getBufferStart()262 uint8_t *getBufferStart() const { return Impl.getBufferStart(); } 263 264 /// Returns a pointer to the end of the buffer. getBufferEnd()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