//===- BinaryByteStream.h ---------------------------------------*- C++ -*-===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//===----------------------------------------------------------------------===//
 
// A BinaryStream which stores data in a single continguous memory buffer.
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_SUPPORT_BINARYBYTESTREAM_H
 
#define LLVM_SUPPORT_BINARYBYTESTREAM_H
 
 
 
#include "llvm/ADT/ArrayRef.h"
 
#include "llvm/ADT/StringRef.h"
 
#include "llvm/Support/BinaryStream.h"
 
#include "llvm/Support/BinaryStreamError.h"
 
#include "llvm/Support/Error.h"
 
#include "llvm/Support/FileOutputBuffer.h"
 
#include "llvm/Support/MemoryBuffer.h"
 
#include <cstdint>
 
#include <cstring>
 
#include <memory>
 
 
 
namespace llvm {
 
 
 
/// An implementation of BinaryStream which holds its entire data set
 
/// in a single contiguous buffer.  BinaryByteStream guarantees that no read
 
/// operation will ever incur a copy.  Note that BinaryByteStream does not
 
/// own the underlying buffer.
 
class BinaryByteStream : public BinaryStream {
 
public:
 
  BinaryByteStream() = default;
 
  BinaryByteStream(ArrayRef<uint8_t> Data, llvm::support::endianness Endian)
 
      : Endian(Endian), Data(Data) {}
 
  BinaryByteStream(StringRef Data, llvm::support::endianness Endian)
 
      : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {}
 
 
 
  llvm::support::endianness getEndian() const override { return Endian; }
 
 
 
  Error readBytes(uint64_t Offset, uint64_t Size,
 
                  ArrayRef<uint8_t> &Buffer) override {
 
    if (auto EC = checkOffsetForRead(Offset, Size))
 
      return EC;
 
    Buffer = Data.slice(Offset, Size);
 
    return Error::success();
 
  }
 
 
 
  Error readLongestContiguousChunk(uint64_t Offset,
 
                                   ArrayRef<uint8_t> &Buffer) override {
 
    if (auto EC = checkOffsetForRead(Offset, 1))
 
      return EC;
 
    Buffer = Data.slice(Offset);
 
    return Error::success();
 
  }
 
 
 
  uint64_t getLength() override { return Data.size(); }
 
 
 
  ArrayRef<uint8_t> data() const { return Data; }
 
 
 
  StringRef str() const {
 
    const char *CharData = reinterpret_cast<const char *>(Data.data());
 
    return StringRef(CharData, Data.size());
 
  }
 
 
 
protected:
 
  llvm::support::endianness Endian;
 
  ArrayRef<uint8_t> Data;
 
};
 
 
 
/// An implementation of BinaryStream whose data is backed by an llvm
 
/// MemoryBuffer object.  MemoryBufferByteStream owns the MemoryBuffer in
 
/// question.  As with BinaryByteStream, reading from a MemoryBufferByteStream
 
/// will never cause a copy.
 
class MemoryBufferByteStream : public BinaryByteStream {
 
public:
 
  MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer,
 
                         llvm::support::endianness Endian)
 
      : BinaryByteStream(Buffer->getBuffer(), Endian),
 
        MemBuffer(std::move(Buffer)) {}
 
 
 
  std::unique_ptr<MemoryBuffer> MemBuffer;
 
};
 
 
 
/// An implementation of BinaryStream which holds its entire data set
 
/// in a single contiguous buffer.  As with BinaryByteStream, the mutable
 
/// version also guarantees that no read operation will ever incur a copy,
 
/// and similarly it does not own the underlying buffer.
 
class MutableBinaryByteStream : public WritableBinaryStream {
 
public:
 
  MutableBinaryByteStream() = default;
 
  MutableBinaryByteStream(MutableArrayRef<uint8_t> Data,
 
                          llvm::support::endianness Endian)
 
      : Data(Data), ImmutableStream(Data, Endian) {}
 
 
 
  llvm::support::endianness getEndian() const override {
 
    return ImmutableStream.getEndian();
 
  }
 
 
 
  Error readBytes(uint64_t Offset, uint64_t Size,
 
                  ArrayRef<uint8_t> &Buffer) override {
 
    return ImmutableStream.readBytes(Offset, Size, Buffer);
 
  }
 
 
 
  Error readLongestContiguousChunk(uint64_t Offset,
 
                                   ArrayRef<uint8_t> &Buffer) override {
 
    return ImmutableStream.readLongestContiguousChunk(Offset, Buffer);
 
  }
 
 
 
  uint64_t getLength() override { return ImmutableStream.getLength(); }
 
 
 
  Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Buffer) override {
 
    if (Buffer.empty())
 
      return Error::success();
 
 
 
    if (auto EC = checkOffsetForWrite(Offset, Buffer.size()))
 
      return EC;
 
 
 
    uint8_t *DataPtr = const_cast<uint8_t *>(Data.data());
 
    ::memcpy(DataPtr + Offset, Buffer.data(), Buffer.size());
 
    return Error::success();
 
  }
 
 
 
  Error commit() override { return Error::success(); }
 
 
 
  MutableArrayRef<uint8_t> data() const { return Data; }
 
 
 
private:
 
  MutableArrayRef<uint8_t> Data;
 
  BinaryByteStream ImmutableStream;
 
};
 
 
 
/// An implementation of WritableBinaryStream which can write at its end
 
/// causing the underlying data to grow.  This class owns the underlying data.
 
class AppendingBinaryByteStream : public WritableBinaryStream {
 
  std::vector<uint8_t> Data;
 
  llvm::support::endianness Endian = llvm::support::little;
 
 
 
public:
 
  AppendingBinaryByteStream() = default;
 
  AppendingBinaryByteStream(llvm::support::endianness Endian)
 
      : Endian(Endian) {}
 
 
 
  void clear() { Data.clear(); }
 
 
 
  llvm::support::endianness getEndian() const override { return Endian; }
 
 
 
  Error readBytes(uint64_t Offset, uint64_t Size,
 
                  ArrayRef<uint8_t> &Buffer) override {
 
    if (auto EC = checkOffsetForWrite(Offset, Buffer.size()))
 
      return EC;
 
 
 
    Buffer = ArrayRef(Data).slice(Offset, Size);
 
    return Error::success();
 
  }
 
 
 
  void insert(uint64_t Offset, ArrayRef<uint8_t> Bytes) {
 
    Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end());
 
  }
 
 
 
  Error readLongestContiguousChunk(uint64_t Offset,
 
                                   ArrayRef<uint8_t> &Buffer) override {
 
    if (auto EC = checkOffsetForWrite(Offset, 1))
 
      return EC;
 
 
 
    Buffer = ArrayRef(Data).slice(Offset);
 
    return Error::success();
 
  }
 
 
 
  uint64_t getLength() override { return Data.size(); }
 
 
 
  Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Buffer) override {
 
    if (Buffer.empty())
 
      return Error::success();
 
 
 
    // This is well-defined for any case except where offset is strictly
 
    // greater than the current length.  If offset is equal to the current
 
    // length, we can still grow.  If offset is beyond the current length, we
 
    // would have to decide how to deal with the intermediate uninitialized
 
    // bytes.  So we punt on that case for simplicity and just say it's an
 
    // error.
 
    if (Offset > getLength())
 
      return make_error<BinaryStreamError>(stream_error_code::invalid_offset);
 
 
 
    uint64_t RequiredSize = Offset + Buffer.size();
 
    if (RequiredSize > Data.size())
 
      Data.resize(RequiredSize);
 
 
 
    ::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size());
 
    return Error::success();
 
  }
 
 
 
  Error commit() override { return Error::success(); }
 
 
 
  /// Return the properties of this stream.
 
  BinaryStreamFlags getFlags() const override { return BSF_Write | BSF_Append; }
 
 
 
  MutableArrayRef<uint8_t> data() { return Data; }
 
};
 
 
 
/// An implementation of WritableBinaryStream backed by an llvm
 
/// FileOutputBuffer.
 
class FileBufferByteStream : public WritableBinaryStream {
 
private:
 
  class StreamImpl : public MutableBinaryByteStream {
 
  public:
 
    StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer,
 
               llvm::support::endianness Endian)
 
        : MutableBinaryByteStream(
 
              MutableArrayRef<uint8_t>(Buffer->getBufferStart(),
 
                                       Buffer->getBufferEnd()),
 
              Endian),
 
          FileBuffer(std::move(Buffer)) {}
 
 
 
    Error commit() override {
 
      if (FileBuffer->commit())
 
        return make_error<BinaryStreamError>(
 
            stream_error_code::filesystem_error);
 
      return Error::success();
 
    }
 
 
 
    /// Returns a pointer to the start of the buffer.
 
    uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); }
 
 
 
    /// Returns a pointer to the end of the buffer.
 
    uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); }
 
 
 
  private:
 
    std::unique_ptr<FileOutputBuffer> FileBuffer;
 
  };
 
 
 
public:
 
  FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer,
 
                       llvm::support::endianness Endian)
 
      : Impl(std::move(Buffer), Endian) {}
 
 
 
  llvm::support::endianness getEndian() const override {
 
    return Impl.getEndian();
 
  }
 
 
 
  Error readBytes(uint64_t Offset, uint64_t Size,
 
                  ArrayRef<uint8_t> &Buffer) override {
 
    return Impl.readBytes(Offset, Size, Buffer);
 
  }
 
 
 
  Error readLongestContiguousChunk(uint64_t Offset,
 
                                   ArrayRef<uint8_t> &Buffer) override {
 
    return Impl.readLongestContiguousChunk(Offset, Buffer);
 
  }
 
 
 
  uint64_t getLength() override { return Impl.getLength(); }
 
 
 
  Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) override {
 
    return Impl.writeBytes(Offset, Data);
 
  }
 
 
 
  Error commit() override { return Impl.commit(); }
 
 
 
  /// Returns a pointer to the start of the buffer.
 
  uint8_t *getBufferStart() const { return Impl.getBufferStart(); }
 
 
 
  /// Returns a pointer to the end of the buffer.
 
  uint8_t *getBufferEnd() const { return Impl.getBufferEnd(); }
 
 
 
private:
 
  StreamImpl Impl;
 
};
 
 
 
} // end namespace llvm
 
 
 
#endif // LLVM_SUPPORT_BINARYBYTESTREAM_H