Home | History | Annotate | Line # | Download | only in Support
      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_BINARYBYTESTREAM_H
    274