Home | History | Annotate | Download | only in Support
      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