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