diff --git a/wpiutil/src/main/native/cpp/MemoryBuffer.cpp b/wpiutil/src/main/native/cpp/MemoryBuffer.cpp new file mode 100644 index 0000000000..ad1e7cf96e --- /dev/null +++ b/wpiutil/src/main/native/cpp/MemoryBuffer.cpp @@ -0,0 +1,522 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +//===--- MemoryBuffer.cpp - Memory Buffer implementation ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the MemoryBuffer interface. +// +//===----------------------------------------------------------------------===// + +#include "wpi/MemoryBuffer.h" + +#ifdef _MSC_VER +// no matching operator delete +#pragma warning(disable : 4291) +#endif + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include // NOLINT(build/include_order) + +#endif + +#include +#include + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#include "wpi/Errc.h" +#include "wpi/Errno.h" +#include "wpi/MappedFileRegion.h" +#include "wpi/SmallVector.h" +#include "wpi/SmallVectorMemoryBuffer.h" +#include "wpi/fs.h" + +#ifdef _WIN32 +#include "wpi/WindowsError.h" +#endif + +using namespace wpi; + +//===----------------------------------------------------------------------===// +// MemoryBuffer implementation itself. +//===----------------------------------------------------------------------===// + +MemoryBuffer::~MemoryBuffer() {} + +/// init - Initialize this MemoryBuffer as a reference to externally allocated +/// memory. +void MemoryBuffer::Init(const uint8_t* bufStart, const uint8_t* bufEnd) { + m_bufferStart = bufStart; + m_bufferEnd = bufEnd; +} + +//===----------------------------------------------------------------------===// +// MemoryBufferMem implementation. +//===----------------------------------------------------------------------===// + +/// CopyStringRef - Copies contents of a StringRef into a block of memory and +/// null-terminates it. +static void CopyStringView(uint8_t* memory, std::string_view data) { + if (!data.empty()) { + std::memcpy(memory, data.data(), data.size()); + } + memory[data.size()] = 0; // Null terminate string. +} + +namespace { +struct NamedBufferAlloc { + std::string_view name; + explicit NamedBufferAlloc(std::string_view name) : name(name) {} +}; +} // namespace + +void* operator new(size_t N, NamedBufferAlloc alloc) { + uint8_t* mem = static_cast(operator new(N + alloc.name.size() + 1)); + CopyStringView(mem + N, alloc.name); + return mem; +} + +namespace { +/// MemoryBufferMem - Named MemoryBuffer pointing to a block of memory. +template +class MemoryBufferMem : public MB { + public: + explicit MemoryBufferMem(span inputData) { + MemoryBuffer::Init(inputData.begin(), inputData.end()); + } + + /// Disable sized deallocation for MemoryBufferMem, because it has + /// tail-allocated data. + void operator delete(void* p) { ::operator delete(p); } // NOLINT + + std::string_view GetBufferIdentifier() const override { + // The name is stored after the class itself. + return std::string_view(reinterpret_cast(this + 1)); + } + + MemoryBuffer::BufferKind GetBufferKind() const override { + return MemoryBuffer::MemoryBuffer_Malloc; + } +}; +} // namespace + +template +static std::unique_ptr GetFileAux(std::string_view filename, + std::error_code& ec, int64_t fileSize, + uint64_t mapSize, uint64_t offset); + +std::unique_ptr MemoryBuffer::GetMemBuffer( + span inputData, std::string_view bufferName) { + auto* ret = new (NamedBufferAlloc(bufferName)) + MemoryBufferMem(inputData); + return std::unique_ptr(ret); +} + +std::unique_ptr MemoryBuffer::GetMemBuffer(MemoryBufferRef ref) { + return std::unique_ptr( + GetMemBuffer(ref.GetBuffer(), ref.GetBufferIdentifier())); +} + +static std::unique_ptr GetMemBufferCopyImpl( + span inputData, std::string_view bufferName, + std::error_code& ec) { + auto buf = + WritableMemoryBuffer::GetNewUninitMemBuffer(inputData.size(), bufferName); + if (!buf) { + ec = make_error_code(errc::not_enough_memory); + return nullptr; + } + std::memcpy(buf->begin(), inputData.data(), inputData.size()); + return buf; +} + +std::unique_ptr MemoryBuffer::GetMemBufferCopy( + span inputData, std::string_view bufferName) { + std::error_code ec; + return GetMemBufferCopyImpl(inputData, bufferName, ec); +} + +std::unique_ptr MemoryBuffer::GetFileSlice( + std::string_view filePath, std::error_code& ec, uint64_t mapSize, + uint64_t offset) { + return GetFileAux(filePath, ec, -1, mapSize, offset); +} + +//===----------------------------------------------------------------------===// +// MemoryBuffer::getFile implementation. +//===----------------------------------------------------------------------===// + +namespace { + +template +constexpr auto kMapMode = MappedFileRegion::kReadOnly; +template <> +constexpr auto kMapMode = MappedFileRegion::kReadOnly; +template <> +constexpr auto kMapMode = MappedFileRegion::kPriv; +template <> +constexpr auto kMapMode = + MappedFileRegion::kReadWrite; + +/// Memory maps a file descriptor using MappedFileRegion. +/// +/// This handles converting the offset into a legal offset on the platform. +template +class MemoryBufferMMapFile : public MB { + MappedFileRegion m_mfr; + + static uint64_t getLegalMapOffset(uint64_t offset) { + return offset & ~(MappedFileRegion::GetAlignment() - 1); + } + + static uint64_t getLegalMapSize(uint64_t len, uint64_t offset) { + return len + (offset - getLegalMapOffset(offset)); + } + + const uint8_t* getStart(uint64_t len, uint64_t offset) { + return m_mfr.const_data() + (offset - getLegalMapOffset(offset)); + } + + public: + MemoryBufferMMapFile(fs::file_t f, uint64_t len, uint64_t offset, + std::error_code& ec) + : m_mfr(f, getLegalMapSize(len, offset), getLegalMapOffset(offset), + kMapMode, ec) { + if (!ec) { + const uint8_t* Start = getStart(len, offset); + MemoryBuffer::Init(Start, Start + len); + } + } + + /// Disable sized deallocation for MemoryBufferMMapFile, because it has + /// tail-allocated data. + void operator delete(void* p) { ::operator delete(p); } // NOLINT + + std::string_view GetBufferIdentifier() const override { + // The name is stored after the class itself. + return std::string_view(reinterpret_cast(this + 1)); + } + + MemoryBuffer::BufferKind GetBufferKind() const override { + return MemoryBuffer::MemoryBuffer_MMap; + } +}; +} // namespace + +static std::unique_ptr GetMemoryBufferForStream( + fs::file_t f, std::string_view bufferName, std::error_code& ec) { + constexpr size_t ChunkSize = 4096 * 4; + SmallVector buffer; +#ifdef _WIN32 + DWORD readBytes; +#else + ssize_t readBytes; +#endif + // Read into Buffer until we hit EOF. + do { + buffer.reserve(buffer.size() + ChunkSize); +#ifdef _WIN32 + if (!ReadFile(f, buffer.end(), ChunkSize, &readBytes, nullptr)) { + ec = mapWindowsError(GetLastError()); + return nullptr; + } +#else + readBytes = sys::RetryAfterSignal(-1, ::read, f, buffer.end(), ChunkSize); + if (readBytes == -1) { + ec = std::error_code(errno, std::generic_category()); + return nullptr; + } +#endif + buffer.set_size(buffer.size() + readBytes); + } while (readBytes != 0); + + return GetMemBufferCopyImpl(buffer, bufferName, ec); +} + +std::unique_ptr MemoryBuffer::GetFile(std::string_view filename, + std::error_code& ec, + int64_t fileSize) { + return GetFileAux(filename, ec, fileSize, fileSize, 0); +} + +template +static std::unique_ptr GetOpenFileImpl(fs::file_t f, + std::string_view filename, + std::error_code& ec, + uint64_t fileSize, uint64_t mapSize, + int64_t offset); + +template +static std::unique_ptr GetFileAux(std::string_view filename, + std::error_code& ec, int64_t fileSize, + uint64_t mapSize, uint64_t offset) { + fs::file_t F = fs::OpenFileForRead(filename, ec, fs::OF_None); + if (ec) { + return nullptr; + } + + auto Ret = GetOpenFileImpl(F, filename, ec, fileSize, mapSize, offset); + fs::CloseFile(F); + return Ret; +} + +std::unique_ptr WritableMemoryBuffer::GetFile( + std::string_view filename, std::error_code& ec, int64_t fileSize) { + return GetFileAux(filename, ec, fileSize, fileSize, 0); +} + +std::unique_ptr WritableMemoryBuffer::GetFileSlice( + std::string_view filename, std::error_code& ec, uint64_t mapSize, + uint64_t offset) { + return GetFileAux(filename, ec, -1, mapSize, offset); +} + +std::unique_ptr +WritableMemoryBuffer::GetNewUninitMemBuffer(size_t size, + std::string_view bufferName) { + using MemBuffer = MemoryBufferMem; + // Allocate space for the MemoryBuffer, the data and the name. It is important + // that MemoryBuffer and data are aligned so PointerIntPair works with them. + // TODO: Is 16-byte alignment enough? We copy small object files with large + // alignment expectations into this buffer. + size_t alignedStringLen = + alignTo(sizeof(MemBuffer) + bufferName.size() + 1, 16); + size_t realLen = alignedStringLen + size + 1; + uint8_t* mem = static_cast(operator new(realLen, std::nothrow)); + if (!mem) { + return nullptr; + } + + // The name is stored after the class itself. + CopyStringView(mem + sizeof(MemBuffer), bufferName); + + // The buffer begins after the name and must be aligned. + uint8_t* buf = mem + alignedStringLen; + buf[size] = 0; // Null terminate buffer. + + auto* ret = new (mem) MemBuffer({buf, size}); + return std::unique_ptr(ret); +} + +std::unique_ptr WritableMemoryBuffer::GetNewMemBuffer( + size_t size, std::string_view bufferName) { + auto sb = WritableMemoryBuffer::GetNewUninitMemBuffer(size, bufferName); + if (!sb) { + return nullptr; + } + std::memset(sb->begin(), 0, size); + return sb; +} + +static std::unique_ptr GetReadWriteFile( + std::string_view filename, std::error_code& ec, uint64_t fileSize, + uint64_t mapSize, uint64_t offset) { + fs::file_t f = + fs::OpenFileForReadWrite(filename, ec, fs::CD_OpenExisting, fs::OF_None); + if (ec) { + return nullptr; + } + + // Default is to map the full file. + if (mapSize == uint64_t(-1)) { + // If we don't know the file size, use fstat to find out. fstat on an open + // file descriptor is cheaper than stat on a random path. + if (fileSize == uint64_t(-1)) { +#ifdef _WIN32 + // If this not a file or a block device (e.g. it's a named pipe + // or character device), we can't mmap it, so error out. + if (GetFileType(f) != FILE_TYPE_DISK) { + ec = std::error_code(errno, std::generic_category()); + return nullptr; + } + + LARGE_INTEGER fileSizeWin; + if (!GetFileSizeEx(f, &fileSizeWin)) { + ec = wpi::mapWindowsError(GetLastError()); + return nullptr; + } + fileSize = fileSizeWin.QuadPart; +#else + struct stat status; + if (fstat(f, &status) < 0) { + ec = std::error_code(errno, std::generic_category()); + return nullptr; + } + + // If this not a file or a block device (e.g. it's a named pipe + // or character device), we can't mmap it, so error out. + if (status.st_mode != S_IFREG && status.st_mode != S_IFBLK) { + ec = make_error_code(errc::invalid_argument); + return nullptr; + } + + fileSize = status.st_size; +#endif + } + mapSize = fileSize; + } + + std::unique_ptr result(new (NamedBufferAlloc( + filename)) MemoryBufferMMapFile(f, mapSize, + offset, ec)); + if (ec) { + return nullptr; + } + return result; +} + +std::unique_ptr WriteThroughMemoryBuffer::GetFile( + std::string_view filename, std::error_code& ec, int64_t fileSize) { + return GetReadWriteFile(filename, ec, fileSize, fileSize, 0); +} + +/// Map a subrange of the specified file as a WritableMemoryBuffer. +std::unique_ptr +WriteThroughMemoryBuffer::GetFileSlice(std::string_view filename, + std::error_code& ec, uint64_t mapSize, + uint64_t offset) { + return GetReadWriteFile(filename, ec, -1, mapSize, offset); +} + +template +static std::unique_ptr GetOpenFileImpl(fs::file_t f, + std::string_view filename, + std::error_code& ec, + uint64_t fileSize, uint64_t mapSize, + int64_t offset) { + // Default is to map the full file. + if (mapSize == uint64_t(-1)) { + // If we don't know the file size, use fstat to find out. fstat on an open + // file descriptor is cheaper than stat on a random path. + if (fileSize == uint64_t(-1)) { +#ifdef _WIN32 + // If this not a file or a block device (e.g. it's a named pipe + // or character device), we can't trust the size. Create the memory + // buffer by copying off the stream. + LARGE_INTEGER fileSizeWin; + if (GetFileType(f) != FILE_TYPE_DISK || !GetFileSizeEx(f, &fileSizeWin)) { + return GetMemoryBufferForStream(f, filename, ec); + } + fileSize = fileSizeWin.QuadPart; +#else + struct stat status; + if (fstat(f, &status) < 0) { + ec = std::error_code(errno, std::generic_category()); + return nullptr; + } + + // If this not a file or a block device (e.g. it's a named pipe + // or character device), we can't trust the size. Create the memory + // buffer by copying off the stream. + if (status.st_mode != S_IFREG && status.st_mode != S_IFBLK) { + return GetMemoryBufferForStream(f, filename, ec); + } + + fileSize = status.st_size; +#endif + } + mapSize = fileSize; + } + + // Don't use mmap for small files + if (mapSize >= 4 * 4096) { + std::unique_ptr result(new (NamedBufferAlloc( + filename)) MemoryBufferMMapFile(f, mapSize, offset, ec)); + if (!ec) { + return result; + } + } + + auto buf = WritableMemoryBuffer::GetNewUninitMemBuffer(mapSize, filename); + if (!buf) { + // Failed to create a buffer. The only way it can fail is if + // new(std::nothrow) returns 0. + ec = make_error_code(errc::not_enough_memory); + return nullptr; + } + + uint8_t* bufPtr = buf.get()->begin(); + + size_t bytesLeft = mapSize; + while (bytesLeft) { +#ifdef _WIN32 + LARGE_INTEGER offsetWin; + offsetWin.QuadPart = offset; + DWORD numRead; + if (!SetFilePointerEx(f, offsetWin, nullptr, FILE_BEGIN) || + !ReadFile(f, bufPtr, bytesLeft, &numRead, nullptr)) { + ec = mapWindowsError(GetLastError()); + return nullptr; + } +// TODO +#else + ssize_t numRead = sys::RetryAfterSignal(-1, ::pread, f, bufPtr, bytesLeft, + mapSize - bytesLeft + offset); + if (numRead == -1) { + // Error while reading. + ec = std::error_code(errno, std::generic_category()); + return nullptr; + } +#endif + if (numRead == 0) { + std::memset(bufPtr, 0, bytesLeft); // zero-initialize rest of the buffer. + break; + } + bytesLeft -= numRead; + bufPtr += numRead; + } + + return buf; +} + +std::unique_ptr MemoryBuffer::GetOpenFile( + fs::file_t f, std::string_view filename, std::error_code& ec, + uint64_t fileSize) { + return GetOpenFileImpl(f, filename, ec, fileSize, fileSize, 0); +} + +std::unique_ptr MemoryBuffer::GetOpenFileSlice( + fs::file_t f, std::string_view filename, std::error_code& ec, + uint64_t mapSize, int64_t offset) { + assert(mapSize != uint64_t(-1)); + return GetOpenFileImpl(f, filename, ec, -1, mapSize, offset); +} + +std::unique_ptr MemoryBuffer::GetFileAsStream( + std::string_view filename, std::error_code& ec) { + fs::file_t f = fs::OpenFileForRead(filename, ec, fs::OF_None); + if (ec) { + return nullptr; + } + std::unique_ptr ret = GetMemoryBufferForStream(f, filename, ec); + fs::CloseFile(f); + return ret; +} + +MemoryBufferRef MemoryBuffer::GetMemBufferRef() const { + return MemoryBufferRef(GetBuffer(), GetBufferIdentifier()); +} + +SmallVectorMemoryBuffer::~SmallVectorMemoryBuffer() {} diff --git a/wpiutil/src/main/native/include/wpi/MemoryBuffer.h b/wpiutil/src/main/native/include/wpi/MemoryBuffer.h new file mode 100644 index 0000000000..330d1b19b4 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/MemoryBuffer.h @@ -0,0 +1,240 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +//===--- MemoryBuffer.h - Memory Buffer Interface ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the MemoryBuffer interface. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include + +#include +#include +#include +#include + +#include "wpi/span.h" + +// Duplicated from fs.h to avoid a dependency +namespace fs { +#if defined(_WIN32) +// A Win32 HANDLE is a typedef of void* +using file_t = void*; +#else +using file_t = int; +#endif +} // namespace fs + +namespace wpi { + +class MemoryBufferRef; + +/// This interface provides simple read-only access to a block of memory, and +/// provides simple methods for reading files and standard input into a memory +/// buffer. +class MemoryBuffer { + const uint8_t* m_bufferStart; // Start of the buffer. + const uint8_t* m_bufferEnd; // End of the buffer. + + protected: + MemoryBuffer() = default; + + void Init(const uint8_t* bufStart, const uint8_t* bufEnd); + + public: + MemoryBuffer(const MemoryBuffer&) = delete; + MemoryBuffer& operator=(const MemoryBuffer&) = delete; + virtual ~MemoryBuffer(); + + const uint8_t* begin() const { return m_bufferStart; } + const uint8_t* end() const { return m_bufferEnd; } + size_t size() const { return m_bufferEnd - m_bufferStart; } + + span GetBuffer() const { return {begin(), end()}; } + + /// Return an identifier for this buffer, typically the filename it was read + /// from. + virtual std::string_view GetBufferIdentifier() const { + return "Unknown buffer"; + } + + /// Open the specified file as a MemoryBuffer, returning a new MemoryBuffer + /// if successful, otherwise returning null. If FileSize is specified, this + /// means that the client knows that the file exists and that it has the + /// specified size. + static std::unique_ptr GetFile(std::string_view filename, + std::error_code& ec, + int64_t fileSize = -1); + + /// Read all of the specified file into a MemoryBuffer as a stream + /// (i.e. until EOF reached). This is useful for special files that + /// look like a regular file but have 0 size (e.g. /proc/cpuinfo on Linux). + static std::unique_ptr GetFileAsStream( + std::string_view filename, std::error_code& ec); + + /// Given an already-open file descriptor, map some slice of it into a + /// MemoryBuffer. The slice is specified by an \p Offset and \p MapSize. + static std::unique_ptr GetOpenFileSlice( + fs::file_t f, std::string_view filename, std::error_code& ec, + uint64_t mapSize, int64_t offset); + + /// Given an already-open file descriptor, read the file and return a + /// MemoryBuffer. + static std::unique_ptr GetOpenFile(fs::file_t f, + std::string_view filename, + std::error_code& ec, + uint64_t fileSize); + + /// Open the specified memory range as a MemoryBuffer. + static std::unique_ptr GetMemBuffer( + span inputData, std::string_view bufferName = ""); + + static std::unique_ptr GetMemBuffer(MemoryBufferRef ref); + + /// Open the specified memory range as a MemoryBuffer, copying the contents + /// and taking ownership of it. + static std::unique_ptr GetMemBufferCopy( + span inputData, std::string_view bufferName = ""); + + /// Map a subrange of the specified file as a MemoryBuffer. + static std::unique_ptr GetFileSlice(std::string_view filename, + std::error_code& ec, + uint64_t mapSize, + uint64_t offset); + + //===--------------------------------------------------------------------===// + // Provided for performance analysis. + //===--------------------------------------------------------------------===// + + /// The kind of memory backing used to support the MemoryBuffer. + enum BufferKind { MemoryBuffer_Malloc, MemoryBuffer_MMap }; + + /// Return information on the memory mechanism used to support the + /// MemoryBuffer. + virtual BufferKind GetBufferKind() const = 0; + + MemoryBufferRef GetMemBufferRef() const; +}; + +/// This class is an extension of MemoryBuffer, which allows copy-on-write +/// access to the underlying contents. It only supports creation methods that +/// are guaranteed to produce a writable buffer. For example, mapping a file +/// read-only is not supported. +class WritableMemoryBuffer : public MemoryBuffer { + protected: + WritableMemoryBuffer() = default; + + public: + using MemoryBuffer::begin; + using MemoryBuffer::end; + using MemoryBuffer::GetBuffer; + using MemoryBuffer::size; + + // const_cast is well-defined here, because the underlying buffer is + // guaranteed to have been initialized with a mutable buffer. + uint8_t* begin() { return const_cast(MemoryBuffer::begin()); } + uint8_t* end() { return const_cast(MemoryBuffer::end()); } + span GetBuffer() { return {begin(), end()}; } + + static std::unique_ptr GetFile( + std::string_view filename, std::error_code& ec, int64_t fileSize = -1); + + /// Map a subrange of the specified file as a WritableMemoryBuffer. + static std::unique_ptr GetFileSlice( + std::string_view filename, std::error_code& ec, uint64_t mapSize, + uint64_t offset); + + /// Allocate a new MemoryBuffer of the specified size that is not initialized. + /// Note that the caller should initialize the memory allocated by this + /// method. The memory is owned by the MemoryBuffer object. + static std::unique_ptr GetNewUninitMemBuffer( + size_t size, std::string_view bufferName = ""); + + /// Allocate a new zero-initialized MemoryBuffer of the specified size. Note + /// that the caller need not initialize the memory allocated by this method. + /// The memory is owned by the MemoryBuffer object. + static std::unique_ptr GetNewMemBuffer( + size_t size, std::string_view bufferName = ""); + + private: + // Hide these base class factory function so one can't write + // WritableMemoryBuffer::getXXX() + // and be surprised that they got a read-only Buffer. + using MemoryBuffer::GetFileAsStream; + using MemoryBuffer::GetMemBuffer; + using MemoryBuffer::GetMemBufferCopy; + using MemoryBuffer::GetOpenFile; + using MemoryBuffer::GetOpenFileSlice; +}; + +/// This class is an extension of MemoryBuffer, which allows write access to +/// the underlying contents and committing those changes to the original source. +/// It only supports creation methods that are guaranteed to produce a writable +/// buffer. For example, mapping a file read-only is not supported. +class WriteThroughMemoryBuffer : public MemoryBuffer { + protected: + WriteThroughMemoryBuffer() = default; + + public: + using MemoryBuffer::begin; + using MemoryBuffer::end; + using MemoryBuffer::GetBuffer; + using MemoryBuffer::size; + + // const_cast is well-defined here, because the underlying buffer is + // guaranteed to have been initialized with a mutable buffer. + uint8_t* begin() { return const_cast(MemoryBuffer::begin()); } + uint8_t* end() { return const_cast(MemoryBuffer::end()); } + span GetBuffer() { return {begin(), end()}; } + + static std::unique_ptr GetFile( + std::string_view filename, std::error_code& ec, int64_t fileSize = -1); + + /// Map a subrange of the specified file as a ReadWriteMemoryBuffer. + static std::unique_ptr GetFileSlice( + std::string_view filename, std::error_code& ec, uint64_t mapSize, + uint64_t offset); + + private: + // Hide these base class factory function so one can't write + // WritableMemoryBuffer::getXXX() + // and be surprised that they got a read-only Buffer. + using MemoryBuffer::GetFileAsStream; + using MemoryBuffer::GetMemBuffer; + using MemoryBuffer::GetMemBufferCopy; + using MemoryBuffer::GetOpenFile; + using MemoryBuffer::GetOpenFileSlice; +}; + +class MemoryBufferRef { + span m_buffer; + std::string_view m_id; + + public: + MemoryBufferRef() = default; + MemoryBufferRef(MemoryBuffer& buffer) // NOLINT + : m_buffer(buffer.GetBuffer()), m_id(buffer.GetBufferIdentifier()) {} + MemoryBufferRef(span buffer, std::string_view id) + : m_buffer(buffer), m_id(id) {} + + span GetBuffer() const { return m_buffer; } + + std::string_view GetBufferIdentifier() const { return m_id; } + + const uint8_t* begin() const { return m_buffer.begin(); } + const uint8_t* end() const { return m_buffer.end(); } + size_t size() const { return m_buffer.size(); } +}; + +} // namespace wpi diff --git a/wpiutil/src/main/native/include/wpi/SmallVectorMemoryBuffer.h b/wpiutil/src/main/native/include/wpi/SmallVectorMemoryBuffer.h new file mode 100644 index 0000000000..61737142a4 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/SmallVectorMemoryBuffer.h @@ -0,0 +1,63 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +//===- SmallVectorMemoryBuffer.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares a wrapper class to hold the memory into which an +// object will be generated. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include +#include + +#include "wpi/MemoryBuffer.h" +#include "wpi/SmallVector.h" +#include "wpi/raw_ostream.h" + +namespace wpi { + +/// SmallVector-backed MemoryBuffer instance. +/// +/// This class enables efficient construction of MemoryBuffers from SmallVector +/// instances. +class SmallVectorMemoryBuffer : public MemoryBuffer { + public: + /// Construct an SmallVectorMemoryBuffer from the given SmallVector + /// r-value. + SmallVectorMemoryBuffer(SmallVectorImpl&& sv) // NOLINT + : m_sv(std::move(sv)), m_bufferName("") { + Init(this->m_sv.begin(), this->m_sv.end()); + } + + /// Construct a named SmallVectorMemoryBuffer from the given + /// SmallVector r-value and StringRef. + SmallVectorMemoryBuffer(SmallVectorImpl&& sv, std::string_view name) + : m_sv(std::move(sv)), m_bufferName(name) { + Init(this->m_sv.begin(), this->m_sv.end()); + } + + // Key function. + ~SmallVectorMemoryBuffer() override; + + std::string_view GetBufferIdentifier() const override { return m_bufferName; } + + BufferKind GetBufferKind() const override { return MemoryBuffer_Malloc; } + + private: + SmallVector m_sv; + std::string m_bufferName; +}; + +} // namespace wpi