[wpiutil] Separate third party libraries (#4190)

This commit is contained in:
PJ Reiniger
2022-06-18 11:08:31 -04:00
committed by GitHub
parent 6671f8d099
commit 787fe6e7a5
102 changed files with 165 additions and 124 deletions

View File

@@ -1,523 +0,0 @@
// 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 <windows.h> // NOLINT(build/include_order)
#endif
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _MSC_VER
#include <io.h>
#else
#include <unistd.h>
#endif
#include <cassert>
#include <cerrno>
#include <cstring>
#include <new>
#include <system_error>
#include "wpi/Errc.h"
#include "wpi/Errno.h"
#include "wpi/MappedFileRegion.h"
#include "wpi/MathExtras.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<uint8_t*>(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 <typename MB>
class MemoryBufferMem : public MB {
public:
explicit MemoryBufferMem(span<const uint8_t> 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<const char*>(this + 1));
}
MemoryBuffer::BufferKind GetBufferKind() const override {
return MemoryBuffer::MemoryBuffer_Malloc;
}
};
} // namespace
template <typename MB>
static std::unique_ptr<MB> GetFileAux(std::string_view filename,
std::error_code& ec, int64_t fileSize,
uint64_t mapSize, uint64_t offset);
std::unique_ptr<MemoryBuffer> MemoryBuffer::GetMemBuffer(
span<const uint8_t> inputData, std::string_view bufferName) {
auto* ret = new (NamedBufferAlloc(bufferName))
MemoryBufferMem<MemoryBuffer>(inputData);
return std::unique_ptr<MemoryBuffer>(ret);
}
std::unique_ptr<MemoryBuffer> MemoryBuffer::GetMemBuffer(MemoryBufferRef ref) {
return std::unique_ptr<MemoryBuffer>(
GetMemBuffer(ref.GetBuffer(), ref.GetBufferIdentifier()));
}
static std::unique_ptr<WritableMemoryBuffer> GetMemBufferCopyImpl(
span<const uint8_t> 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> MemoryBuffer::GetMemBufferCopy(
span<const uint8_t> inputData, std::string_view bufferName) {
std::error_code ec;
return GetMemBufferCopyImpl(inputData, bufferName, ec);
}
std::unique_ptr<MemoryBuffer> MemoryBuffer::GetFileSlice(
std::string_view filePath, std::error_code& ec, uint64_t mapSize,
uint64_t offset) {
return GetFileAux<MemoryBuffer>(filePath, ec, -1, mapSize, offset);
}
//===----------------------------------------------------------------------===//
// MemoryBuffer::getFile implementation.
//===----------------------------------------------------------------------===//
namespace {
template <typename MB>
constexpr auto kMapMode = MappedFileRegion::kReadOnly;
template <>
constexpr auto kMapMode<MemoryBuffer> = MappedFileRegion::kReadOnly;
template <>
constexpr auto kMapMode<WritableMemoryBuffer> = MappedFileRegion::kPriv;
template <>
constexpr auto kMapMode<WriteThroughMemoryBuffer> =
MappedFileRegion::kReadWrite;
/// Memory maps a file descriptor using MappedFileRegion.
///
/// This handles converting the offset into a legal offset on the platform.
template <typename MB>
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<MB>, 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<const char*>(this + 1));
}
MemoryBuffer::BufferKind GetBufferKind() const override {
return MemoryBuffer::MemoryBuffer_MMap;
}
};
} // namespace
static std::unique_ptr<WritableMemoryBuffer> GetMemoryBufferForStream(
fs::file_t f, std::string_view bufferName, std::error_code& ec) {
constexpr size_t ChunkSize = 4096 * 4;
SmallVector<uint8_t, ChunkSize> 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> MemoryBuffer::GetFile(std::string_view filename,
std::error_code& ec,
int64_t fileSize) {
return GetFileAux<MemoryBuffer>(filename, ec, fileSize, fileSize, 0);
}
template <typename MB>
static std::unique_ptr<MB> GetOpenFileImpl(fs::file_t f,
std::string_view filename,
std::error_code& ec,
uint64_t fileSize, uint64_t mapSize,
int64_t offset);
template <typename MB>
static std::unique_ptr<MB> 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<MB>(F, filename, ec, fileSize, mapSize, offset);
fs::CloseFile(F);
return Ret;
}
std::unique_ptr<WritableMemoryBuffer> WritableMemoryBuffer::GetFile(
std::string_view filename, std::error_code& ec, int64_t fileSize) {
return GetFileAux<WritableMemoryBuffer>(filename, ec, fileSize, fileSize, 0);
}
std::unique_ptr<WritableMemoryBuffer> WritableMemoryBuffer::GetFileSlice(
std::string_view filename, std::error_code& ec, uint64_t mapSize,
uint64_t offset) {
return GetFileAux<WritableMemoryBuffer>(filename, ec, -1, mapSize, offset);
}
std::unique_ptr<WritableMemoryBuffer>
WritableMemoryBuffer::GetNewUninitMemBuffer(size_t size,
std::string_view bufferName) {
using MemBuffer = MemoryBufferMem<WritableMemoryBuffer>;
// 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<uint8_t*>(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<WritableMemoryBuffer>(ret);
}
std::unique_ptr<WritableMemoryBuffer> 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<WriteThroughMemoryBuffer> 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<WriteThroughMemoryBuffer> result(new (NamedBufferAlloc(
filename)) MemoryBufferMMapFile<WriteThroughMemoryBuffer>(f, mapSize,
offset, ec));
if (ec) {
return nullptr;
}
return result;
}
std::unique_ptr<WriteThroughMemoryBuffer> 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>
WriteThroughMemoryBuffer::GetFileSlice(std::string_view filename,
std::error_code& ec, uint64_t mapSize,
uint64_t offset) {
return GetReadWriteFile(filename, ec, -1, mapSize, offset);
}
template <typename MB>
static std::unique_ptr<MB> 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<MB> result(new (NamedBufferAlloc(
filename)) MemoryBufferMMapFile<MB>(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> MemoryBuffer::GetOpenFile(
fs::file_t f, std::string_view filename, std::error_code& ec,
uint64_t fileSize) {
return GetOpenFileImpl<MemoryBuffer>(f, filename, ec, fileSize, fileSize, 0);
}
std::unique_ptr<MemoryBuffer> 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<MemoryBuffer>(f, filename, ec, -1, mapSize, offset);
}
std::unique_ptr<MemoryBuffer> 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<MemoryBuffer> ret = GetMemoryBufferForStream(f, filename, ec);
fs::CloseFile(f);
return ret;
}
MemoryBufferRef MemoryBuffer::GetMemBufferRef() const {
return MemoryBufferRef(GetBuffer(), GetBufferIdentifier());
}
SmallVectorMemoryBuffer::~SmallVectorMemoryBuffer() {}

View File

@@ -1,360 +0,0 @@
// 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.
//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
//
// 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 StringExtras.h header
//
//===----------------------------------------------------------------------===//
#include "wpi/StringExtras.h"
#include <algorithm>
#include <cstdlib>
#include <string_view>
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
// strncasecmp() is not available on non-POSIX systems, so define an
// alternative function here.
static int ascii_strncasecmp(const char* lhs, const char* rhs,
size_t length) noexcept {
for (size_t i = 0; i < length; ++i) {
unsigned char lhc = wpi::toLower(lhs[i]);
unsigned char rhc = wpi::toLower(rhs[i]);
if (lhc != rhc) {
return lhc < rhc ? -1 : 1;
}
}
return 0;
}
int wpi::compare_lower(std::string_view lhs, std::string_view rhs) noexcept {
if (int Res = ascii_strncasecmp(lhs.data(), rhs.data(),
(std::min)(lhs.size(), rhs.size()))) {
return Res;
}
if (lhs.size() == rhs.size()) {
return 0;
}
return lhs.size() < rhs.size() ? -1 : 1;
}
std::string_view::size_type wpi::find_lower(
std::string_view str, char ch, std::string_view::size_type from) noexcept {
char lch = toLower(ch);
auto s = drop_front(str, from);
while (!s.empty()) {
if (toLower(s.front()) == lch) {
return str.size() - s.size();
}
s.remove_prefix(1);
}
return std::string_view::npos;
}
std::string_view::size_type wpi::find_lower(
std::string_view str, std::string_view other,
std::string_view::size_type from) noexcept {
auto s = substr(str, from);
while (s.size() >= other.size()) {
if (starts_with_lower(s, other)) {
return from;
}
s.remove_prefix(1);
++from;
}
return std::string_view::npos;
}
std::string_view::size_type wpi::rfind_lower(
std::string_view str, char ch, std::string_view::size_type from) noexcept {
from = (std::min)(from, str.size());
auto data = str.data();
std::string_view::size_type i = from;
while (i != 0) {
--i;
if (toLower(data[i]) == toLower(ch)) {
return i;
}
}
return std::string_view::npos;
}
std::string_view::size_type wpi::rfind_lower(std::string_view str,
std::string_view other) noexcept {
std::string_view::size_type n = other.size();
if (n > str.size()) {
return std::string_view::npos;
}
for (size_t i = str.size() - n + 1, e = 0; i != e;) {
--i;
if (equals_lower(substr(str, i, n), other)) {
return i;
}
}
return std::string_view::npos;
}
bool wpi::starts_with_lower(std::string_view str,
std::string_view prefix) noexcept {
return str.size() >= prefix.size() &&
ascii_strncasecmp(str.data(), prefix.data(), prefix.size()) == 0;
}
bool wpi::ends_with_lower(std::string_view str,
std::string_view suffix) noexcept {
return str.size() >= suffix.size() &&
ascii_strncasecmp(str.data() + str.size() - suffix.size(),
suffix.data(), suffix.size()) == 0;
}
void wpi::split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
std::string_view separator, int maxSplit,
bool keepEmpty) noexcept {
std::string_view s = str;
// Count down from maxSplit. When maxSplit is -1, this will just split
// "forever". This doesn't support splitting more than 2^31 times
// intentionally; if we ever want that we can make maxSplit a 64-bit integer
// but that seems unlikely to be useful.
while (maxSplit-- != 0) {
auto idx = s.find(separator);
if (idx == std::string_view::npos) {
break;
}
// Push this split.
if (keepEmpty || idx > 0) {
arr.push_back(slice(s, 0, idx));
}
// Jump forward.
s = slice(s, idx + separator.size(), std::string_view::npos);
}
// Push the tail.
if (keepEmpty || !s.empty()) {
arr.push_back(s);
}
}
void wpi::split(std::string_view str, SmallVectorImpl<std::string_view>& arr,
char separator, int maxSplit, bool keepEmpty) noexcept {
std::string_view s = str;
// Count down from maxSplit. When maxSplit is -1, this will just split
// "forever". This doesn't support splitting more than 2^31 times
// intentionally; if we ever want that we can make maxSplit a 64-bit integer
// but that seems unlikely to be useful.
while (maxSplit-- != 0) {
size_t idx = s.find(separator);
if (idx == std::string_view::npos) {
break;
}
// Push this split.
if (keepEmpty || idx > 0) {
arr.push_back(slice(s, 0, idx));
}
// Jump forward.
s = slice(s, idx + 1, std::string_view::npos);
}
// Push the tail.
if (keepEmpty || !s.empty()) {
arr.push_back(s);
}
}
static unsigned GetAutoSenseRadix(std::string_view& str) noexcept {
if (str.empty()) {
return 10;
}
if (wpi::starts_with(str, "0x") || wpi::starts_with(str, "0X")) {
str.remove_prefix(2);
return 16;
}
if (wpi::starts_with(str, "0b") || wpi::starts_with(str, "0B")) {
str.remove_prefix(2);
return 2;
}
if (wpi::starts_with(str, "0o")) {
str.remove_prefix(2);
return 8;
}
if (str[0] == '0' && str.size() > 1 && wpi::isDigit(str[1])) {
str.remove_prefix(1);
return 8;
}
return 10;
}
bool wpi::detail::ConsumeUnsignedInteger(
std::string_view& str, unsigned radix,
unsigned long long& result) noexcept { // NOLINT(runtime/int)
// Autosense radix if not specified.
if (radix == 0) {
radix = GetAutoSenseRadix(str);
}
// Empty strings (after the radix autosense) are invalid.
if (str.empty()) {
return true;
}
// Parse all the bytes of the string given this radix. Watch for overflow.
std::string_view str2 = str;
result = 0;
while (!str2.empty()) {
unsigned charVal;
if (str2[0] >= '0' && str2[0] <= '9') {
charVal = str2[0] - '0';
} else if (str2[0] >= 'a' && str2[0] <= 'z') {
charVal = str2[0] - 'a' + 10;
} else if (str2[0] >= 'A' && str2[0] <= 'Z') {
charVal = str2[0] - 'A' + 10;
} else {
break;
}
// If the parsed value is larger than the integer radix, we cannot
// consume any more characters.
if (charVal >= radix) {
break;
}
// Add in this character.
unsigned long long prevResult = result; // NOLINT(runtime/int)
result = result * radix + charVal;
// Check for overflow by shifting back and seeing if bits were lost.
if (result / radix < prevResult) {
return true;
}
str2.remove_prefix(1);
}
// We consider the operation a failure if no characters were consumed
// successfully.
if (str.size() == str2.size()) {
return true;
}
str = str2;
return false;
}
bool wpi::detail::ConsumeSignedInteger(
std::string_view& str, unsigned radix,
long long& result) noexcept { // NOLINT(runtime/int)
unsigned long long ullVal; // NOLINT(runtime/int)
// Handle positive strings first.
if (str.empty() || str.front() != '-') {
if (wpi::detail::ConsumeUnsignedInteger(str, radix, ullVal) ||
// Check for value so large it overflows a signed value.
static_cast<long long>(ullVal) < 0) { // NOLINT(runtime/int)
return true;
}
result = ullVal;
return false;
}
// Get the positive part of the value.
std::string_view str2 = wpi::drop_front(str, 1);
if (wpi::detail::ConsumeUnsignedInteger(str2, radix, ullVal) ||
// Reject values so large they'd overflow as negative signed, but allow
// "-0". This negates the unsigned so that the negative isn't undefined
// on signed overflow.
static_cast<long long>(-ullVal) > 0) { // NOLINT(runtime/int)
return true;
}
str = str2;
result = -ullVal;
return false;
}
bool wpi::detail::GetAsUnsignedInteger(
std::string_view str, unsigned radix,
unsigned long long& result) noexcept { // NOLINT(runtime/int)
if (wpi::detail::ConsumeUnsignedInteger(str, radix, result)) {
return true;
}
// For getAsUnsignedInteger, we require the whole string to be consumed or
// else we consider it a failure.
return !str.empty();
}
bool wpi::detail::GetAsSignedInteger(
std::string_view str, unsigned radix,
long long& result) noexcept { // NOLINT(runtime/int)
if (wpi::detail::ConsumeSignedInteger(str, radix, result)) {
return true;
}
// For getAsSignedInteger, we require the whole string to be consumed or else
// we consider it a failure.
return !str.empty();
}
template <>
std::optional<float> wpi::parse_float<float>(std::string_view str) noexcept {
if (str.empty()) {
return std::nullopt;
}
wpi::SmallString<32> storage{str};
char* end;
float val = std::strtof(storage.c_str(), &end);
if (*end != '\0') {
return std::nullopt;
}
return val;
}
template <>
std::optional<double> wpi::parse_float<double>(std::string_view str) noexcept {
if (str.empty()) {
return std::nullopt;
}
wpi::SmallString<32> storage{str};
char* end;
double val = std::strtod(storage.c_str(), &end);
if (*end != '\0') {
return std::nullopt;
}
return val;
}
template <>
std::optional<long double> wpi::parse_float<long double>(
std::string_view str) noexcept {
if (str.empty()) {
return std::nullopt;
}
wpi::SmallString<32> storage{str};
char* end;
long double val = std::strtold(storage.c_str(), &end);
if (*end != '\0') {
return std::nullopt;
}
return val;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,540 +0,0 @@
/*----------------------------------------------------------------------------*/
/* Modifications Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
| | |__ | | | | | | version 3.1.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#define WPI_JSON_IMPLEMENTATION
#include "wpi/json.h"
#include <numeric> // accumulate
#include "fmt/format.h"
#include "wpi/SmallString.h"
#include "wpi/StringExtras.h"
namespace wpi {
std::string json_pointer::to_string() const noexcept
{
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + escape(b);
});
}
int json_pointer::array_index(std::string_view s)
{
SmallString<128> str{s};
std::size_t processed_chars = 0;
const int res = std::stoi(str.c_str(), &processed_chars);
// check if the string was completely read
if (JSON_UNLIKELY(processed_chars != str.size()))
{
JSON_THROW(detail::out_of_range::create(404, fmt::format("unresolved reference token '{}'", s)));
}
return res;
}
json& json_pointer::get_and_create(json& j) const
{
using size_type = typename json::size_type;
auto result = &j;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for (const auto& reference_token : reference_tokens)
{
switch (result->m_type)
{
case detail::value_t::null:
{
if (reference_token == "0")
{
// start a new array if reference token is 0
result = &result->operator[](0);
}
else
{
// start a new object otherwise
result = &result->operator[](reference_token);
}
break;
}
case detail::value_t::object:
{
// create an entry in the object
result = &result->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// create an entry in the array
JSON_TRY
{
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0, fmt::format("array index '{}' is not a number", reference_token)));
}
break;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
}
}
return *result;
}
json& json_pointer::get_unchecked(json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
if (ptr->m_type == detail::value_t::null)
{
// check if reference token is a number
const bool nums =
std::all_of(reference_token.begin(), reference_token.end(),
[](const char x)
{
return (x >= '0' and x <= '9');
});
// change value to array for numbers or "-" or to object otherwise
*ptr = (nums or reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
if (reference_token == "-")
{
// explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
{
// convert array index to number; unchecked access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0,
fmt::format("array index '{}' is not a number", reference_token)));
}
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404,
fmt::format("unresolved reference token '{}'", reference_token)));
}
}
return *ptr;
}
json& json_pointer::get_checked(json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0,
fmt::format("array index '{}' is not a number", reference_token)));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404,
fmt::format("unresolved reference token '{}'", reference_token)));
}
}
return *ptr;
}
const json& json_pointer::get_unchecked(const json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402,
fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
// use unchecked array access
JSON_TRY
{
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0,
fmt::format("array index '{}' is not a number", reference_token)));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404,
fmt::format("unresolved reference token '{}'", reference_token)));
}
}
return *ptr;
}
const json& json_pointer::get_checked(const json* ptr) const
{
using size_type = typename json::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->m_type)
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
fmt::format("array index '-' ({}) is out of range", ptr->m_value.array->size())));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
fmt::format("array index '{}' must not begin with '0'", reference_token)));
}
// note: at performs range check
JSON_TRY
{
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
}
JSON_CATCH(std::invalid_argument&)
{
JSON_THROW(detail::parse_error::create(109, 0,
fmt::format("array index '{}' is not a number", reference_token)));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404,
fmt::format("unresolved reference token '{}'", reference_token)));
}
}
return *ptr;
}
std::vector<std::string> json_pointer::split(std::string_view ref_str)
{
std::vector<std::string> result;
// special case: empty reference string -> no reference tokens
if (ref_str.empty())
{
return result;
}
// check if nonempty reference string begins with slash
if (JSON_UNLIKELY(ref_str[0] != '/'))
{
JSON_THROW(detail::parse_error::create(107, 1,
fmt::format("JSON pointer must be empty or begin with '/' - was: '{}'", ref_str)));
}
// extract the reference tokens:
// - slash: position of the last read slash (or end of string)
// - start: position after the previous slash
for (
// search for the first slash after the first character
std::size_t slash = ref_str.find_first_of('/', 1),
// set the beginning of the first reference token
start = 1;
// we can stop if start == string::npos+1 = 0
start != 0;
// set the beginning of the next reference token
// (will eventually be 0 if slash == std::string::npos)
start = slash + 1,
// find next slash
slash = ref_str.find_first_of('/', start))
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
auto reference_token = slice(ref_str, start, slash);
// check reference tokens are properly escaped
for (std::size_t pos = reference_token.find_first_of('~');
pos != std::string_view::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
assert(reference_token[pos] == '~');
// ~ must be followed by 0 or 1
if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
(reference_token[pos + 1] != '0' and
reference_token[pos + 1] != '1')))
{
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
}
}
// finally, store the reference token
std::string ref_tok{reference_token};
unescape(ref_tok);
result.emplace_back(std::move(ref_tok));
}
return result;
}
void json_pointer::replace_substring(std::string& s, const std::string& f,
const std::string& t)
{
assert(not f.empty());
for (auto pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t, and
pos = s.find(f, pos + t.size())) // find next occurrence of f
{}
}
std::string json_pointer::escape(std::string s)
{
replace_substring(s, "~", "~0");
replace_substring(s, "/", "~1");
return s;
}
/// unescape "~1" to tilde and "~0" to slash (order is important!)
void json_pointer::unescape(std::string& s)
{
replace_substring(s, "~1", "/");
replace_substring(s, "~0", "~");
}
void json_pointer::flatten(std::string_view reference_string,
const json& value,
json& result)
{
switch (value.m_type)
{
case detail::value_t::array:
{
if (value.m_value.array->empty())
{
// flatten empty array as null
result[reference_string] = nullptr;
}
else
{
// iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
{
flatten(fmt::format("{}/{}", reference_string, i),
value.m_value.array->operator[](i), result);
}
}
break;
}
case detail::value_t::object:
{
if (value.m_value.object->empty())
{
// flatten empty object as null
result[reference_string] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(fmt::format("{}/{}", reference_string, escape(std::string{element.first()})), element.second, result);
}
}
break;
}
default:
{
// add primitive value with its reference string
result[reference_string] = value;
break;
}
}
}
json
json_pointer::unflatten(const json& value)
{
if (JSON_UNLIKELY(not value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
}
// we need to iterate over the object values in sorted key order
SmallVector<StringMapConstIterator<json>, 64> sorted;
for (auto i = value.m_value.object->begin(),
end = value.m_value.object->end(); i != end; ++i)
{
if (!i->second.is_primitive())
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
}
sorted.push_back(i);
}
std::sort(sorted.begin(), sorted.end(),
[](const StringMapConstIterator<json>& a,
const StringMapConstIterator<json>& b) {
return a->getKey() < b->getKey();
});
json result;
// iterate the sorted JSON object values
for (const auto& element : sorted)
{
// assign value to reference pointed to by JSON pointer; Note
// that if the JSON pointer is "" (i.e., points to the whole
// value), function get_and_create returns a reference to
// result itself. An assignment will then create a primitive
// value.
json_pointer(element->first()).get_and_create(result) = element->second;
}
return result;
}
} // namespace wpi

File diff suppressed because it is too large Load Diff

View File

@@ -1,833 +0,0 @@
/*===--- ConvertUTF.c - Universal Character Names conversions ---------------===
*
* 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
*
*===------------------------------------------------------------------------=*/
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
June 2002: Tim Dodd added detection and handling of incomplete
source sequences, enhanced error detection, added casts
to eliminate compiler warnings.
July 2003: slight mods to back out aggressive FFFE detection.
Jan 2004: updated switches in from-UTF8 conversions.
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
See the header file "ConvertUTF.h" for complete documentation.
------------------------------------------------------------------------ */
#include "wpi/ConvertUTF.h"
#ifdef CVTUTF_DEBUG
#include <stdio.h>
#endif
#include <assert.h>
#ifdef _WIN32
#include "wpi/WindowsError.h"
#include "Windows/WindowsSupport.h"
#endif
/*
* This code extensively uses fall-through switches.
* Keep the compiler from warning about that.
*/
#if defined(__clang__) && defined(__has_warning)
# if __has_warning("-Wimplicit-fallthrough")
# define ConvertUTF_DISABLE_WARNINGS \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wimplicit-fallthrough\"")
# define ConvertUTF_RESTORE_WARNINGS \
_Pragma("clang diagnostic pop")
# endif
#elif defined(__GNUC__) && __GNUC__ > 6
# define ConvertUTF_DISABLE_WARNINGS \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
# define ConvertUTF_RESTORE_WARNINGS \
_Pragma("GCC diagnostic pop")
#endif
#ifndef ConvertUTF_DISABLE_WARNINGS
# define ConvertUTF_DISABLE_WARNINGS
#endif
#ifndef ConvertUTF_RESTORE_WARNINGS
# define ConvertUTF_RESTORE_WARNINGS
#endif
ConvertUTF_DISABLE_WARNINGS
namespace wpi {
static const int halfShift = 10; /* used for shifting by 10 bits */
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
/* --------------------------------------------------------------------- */
/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
* left as-is for anyone who may want to do such conversion, which was
* allowed in earlier algorithms.
*/
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
* for *legal* UTF-8 will be 4 or fewer bytes total.
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
/* --------------------------------------------------------------------- */
/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF16 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
if (target >= targetEnd) {
result = targetExhausted; break;
}
ch = *source++;
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_LEGAL_UTF32) {
if (flags == strictConversion) {
result = sourceIllegal;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
--source; /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF32 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF32* target = *targetStart;
UTF32 ch, ch2;
while (source < sourceEnd) {
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
if (target >= targetEnd) {
source = oldSource; /* Back up source pointer! */
result = targetExhausted; break;
}
*target++ = ch;
}
*sourceStart = source;
*targetStart = target;
#ifdef CVTUTF_DEBUG
if (result == sourceIllegal) {
fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
fflush(stderr);
}
#endif
return result;
}
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
UTF32 ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/* Figure out how many bytes the result will require */
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
}
target += bytesToWrite;
if (target > targetEnd) {
source = oldSource; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
ch = *source++;
if (flags == strictConversion ) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/*
* Figure out how many bytes the result will require. Turn any
* illegally large UTF32 things (> Plane 17) into replacement chars.
*/
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
result = sourceIllegal;
}
target += bytesToWrite;
if (target > targetEnd) {
--source; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/
static Boolean isLegalUTF8(const UTF8 *source, int length) {
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
/* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
switch (*source) {
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
}
if (*source > 0xF4) return false;
return true;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 sequence is legal or not.
* This is not used here; it's just exported.
*/
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
int length = trailingBytesForUTF8[*source]+1;
if (length > sourceEnd - source) {
return false;
}
return isLegalUTF8(source, length);
}
/* --------------------------------------------------------------------- */
static unsigned
findMaximalSubpartOfIllFormedUTF8Sequence(const UTF8 *source,
const UTF8 *sourceEnd) {
UTF8 b1, b2, b3;
assert(!isLegalUTF8Sequence(source, sourceEnd));
/*
* Unicode 6.3.0, D93b:
*
* Maximal subpart of an ill-formed subsequence: The longest code unit
* subsequence starting at an unconvertible offset that is either:
* a. the initial subsequence of a well-formed code unit sequence, or
* b. a subsequence of length one.
*/
if (source == sourceEnd)
return 0;
/*
* Perform case analysis. See Unicode 6.3.0, Table 3-7. Well-Formed UTF-8
* Byte Sequences.
*/
b1 = *source;
++source;
if (b1 >= 0xC2 && b1 <= 0xDF) {
/*
* First byte is valid, but we know that this code unit sequence is
* invalid, so the maximal subpart has to end after the first byte.
*/
return 1;
}
if (source == sourceEnd)
return 1;
b2 = *source;
++source;
if (b1 == 0xE0) {
return (b2 >= 0xA0 && b2 <= 0xBF) ? 2 : 1;
}
if (b1 >= 0xE1 && b1 <= 0xEC) {
return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
}
if (b1 == 0xED) {
return (b2 >= 0x80 && b2 <= 0x9F) ? 2 : 1;
}
if (b1 >= 0xEE && b1 <= 0xEF) {
return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
}
if (b1 == 0xF0) {
if (b2 >= 0x90 && b2 <= 0xBF) {
if (source == sourceEnd)
return 2;
b3 = *source;
return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
}
return 1;
}
if (b1 >= 0xF1 && b1 <= 0xF3) {
if (b2 >= 0x80 && b2 <= 0xBF) {
if (source == sourceEnd)
return 2;
b3 = *source;
return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
}
return 1;
}
if (b1 == 0xF4) {
if (b2 >= 0x80 && b2 <= 0x8F) {
if (source == sourceEnd)
return 2;
b3 = *source;
return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
}
return 1;
}
assert((b1 >= 0x80 && b1 <= 0xC1) || b1 >= 0xF5);
/*
* There are no valid sequences that start with these bytes. Maximal subpart
* is defined to have length 1 in these cases.
*/
return 1;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return the total number of bytes in a codepoint
* represented in UTF-8, given the value of the first byte.
*/
unsigned getNumBytesForUTF8(UTF8 first) {
return trailingBytesForUTF8[first] + 1;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 string is legal or not.
* This is not used here; it's just exported.
*/
Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd) {
while (*source != sourceEnd) {
int length = trailingBytesForUTF8[**source] + 1;
if (length > sourceEnd - *source || !isLegalUTF8(*source, length))
return false;
*source += length;
}
return true;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (extraBytesToRead >= sourceEnd - source) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (!isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_UTF16) {
if (flags == strictConversion) {
result = sourceIllegal;
source -= (extraBytesToRead+1); /* return to the start */
break; /* Bail out; shouldn't continue */
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
static ConversionResult ConvertUTF8toUTF32Impl(
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags,
Boolean InputIsPartial) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF32* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (extraBytesToRead >= sourceEnd - source) {
if (flags == strictConversion || InputIsPartial) {
result = sourceExhausted;
break;
} else {
result = sourceIllegal;
/*
* Replace the maximal subpart of ill-formed sequence with
* replacement character.
*/
source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
sourceEnd);
*target++ = UNI_REPLACEMENT_CHAR;
continue;
}
}
if (target >= targetEnd) {
result = targetExhausted; break;
}
/* Do this check whether lenient or strict */
if (!isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
if (flags == strictConversion) {
/* Abort conversion. */
break;
} else {
/*
* Replace the maximal subpart of ill-formed sequence with
* replacement character.
*/
source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
sourceEnd);
*target++ = UNI_REPLACEMENT_CHAR;
continue;
}
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6;
case 4: ch += *source++; ch <<= 6;
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (ch <= UNI_MAX_LEGAL_UTF32) {
/*
* UTF-16 surrogate values are illegal in UTF-32, and anything
* over Plane 17 (> 0x10FFFF) is illegal.
*/
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = ch;
}
} else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
result = sourceIllegal;
*target++ = UNI_REPLACEMENT_CHAR;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
ConversionResult ConvertUTF8toUTF32Partial(const UTF8 **sourceStart,
const UTF8 *sourceEnd,
UTF32 **targetStart,
UTF32 *targetEnd,
ConversionFlags flags) {
return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
flags, /*InputIsPartial=*/true);
}
ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
const UTF8 *sourceEnd, UTF32 **targetStart,
UTF32 *targetEnd, ConversionFlags flags) {
return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
flags, /*InputIsPartial=*/false);
}
/* ---------------------------------------------------------------------
Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.
--------------------------------------------------------------------- */
#ifdef _WIN32
namespace sys {
namespace windows {
std::error_code CodePageToUTF16(unsigned codepage,
std::string_view original,
wpi::SmallVectorImpl<wchar_t> &utf16) {
if (!original.empty()) {
int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(),
original.size(), utf16.begin(), 0);
if (len == 0) {
return mapWindowsError(::GetLastError());
}
utf16.reserve(len + 1);
utf16.set_size(len);
len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(),
original.size(), utf16.begin(), utf16.size());
if (len == 0) {
return mapWindowsError(::GetLastError());
}
}
// Make utf16 null terminated.
utf16.push_back(0);
utf16.pop_back();
return std::error_code();
}
std::error_code UTF8ToUTF16(std::string_view utf8,
wpi::SmallVectorImpl<wchar_t> &utf16) {
return CodePageToUTF16(CP_UTF8, utf8, utf16);
}
std::error_code CurCPToUTF16(std::string_view curcp,
wpi::SmallVectorImpl<wchar_t> &utf16) {
return CodePageToUTF16(CP_ACP, curcp, utf16);
}
static
std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16,
size_t utf16_len,
wpi::SmallVectorImpl<char> &converted) {
if (utf16_len) {
// Get length.
int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(),
0, NULL, NULL);
if (len == 0) {
return mapWindowsError(::GetLastError());
}
converted.reserve(len);
converted.set_size(len);
// Now do the actual conversion.
len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(),
converted.size(), NULL, NULL);
if (len == 0) {
return mapWindowsError(::GetLastError());
}
}
// Make the new string null terminated.
converted.push_back(0);
converted.pop_back();
return std::error_code();
}
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
wpi::SmallVectorImpl<char> &utf8) {
return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8);
}
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
wpi::SmallVectorImpl<char> &curcp) {
return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp);
}
} // end namespace windows
} // end namespace sys
#endif // _WIN32
} // namespace wpi
ConvertUTF_RESTORE_WARNINGS

View File

@@ -1,253 +0,0 @@
//===-- ConvertUTFWrapper.cpp - Wrap ConvertUTF.h with clang data types -----===
//
// 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
//
//===----------------------------------------------------------------------===//
#include "wpi/span.h"
#include "wpi/ConvertUTF.h"
#include "wpi/SmallVector.h"
#include "wpi/ErrorHandling.h"
#include "wpi/SwapByteOrder.h"
#include <string>
#include <string_view>
#include <vector>
namespace wpi {
bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source,
char *&ResultPtr, const UTF8 *&ErrorPtr) {
assert(WideCharWidth == 1 || WideCharWidth == 2 || WideCharWidth == 4);
ConversionResult result = conversionOK;
// Copy the character span over.
if (WideCharWidth == 1) {
const UTF8 *Pos = reinterpret_cast<const UTF8*>(Source.data());
if (!isLegalUTF8String(&Pos, reinterpret_cast<const UTF8*>(Source.data() + Source.size()))) {
result = sourceIllegal;
ErrorPtr = Pos;
} else {
memcpy(ResultPtr, Source.data(), Source.size());
ResultPtr += Source.size();
}
} else if (WideCharWidth == 2) {
const UTF8 *sourceStart = (const UTF8*)Source.data();
// FIXME: Make the type of the result buffer correct instead of
// using reinterpret_cast.
UTF16 *targetStart = reinterpret_cast<UTF16*>(ResultPtr);
ConversionFlags flags = strictConversion;
result = ConvertUTF8toUTF16(
&sourceStart, sourceStart + Source.size(),
&targetStart, targetStart + Source.size(), flags);
if (result == conversionOK)
ResultPtr = reinterpret_cast<char*>(targetStart);
else
ErrorPtr = sourceStart;
} else if (WideCharWidth == 4) {
const UTF8 *sourceStart = (const UTF8*)Source.data();
// FIXME: Make the type of the result buffer correct instead of
// using reinterpret_cast.
UTF32 *targetStart = reinterpret_cast<UTF32*>(ResultPtr);
ConversionFlags flags = strictConversion;
result = ConvertUTF8toUTF32(
&sourceStart, sourceStart + Source.size(),
&targetStart, targetStart + Source.size(), flags);
if (result == conversionOK)
ResultPtr = reinterpret_cast<char*>(targetStart);
else
ErrorPtr = sourceStart;
}
assert((result != targetExhausted)
&& "ConvertUTF8toUTFXX exhausted target buffer");
return result == conversionOK;
}
bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) {
const UTF32 *SourceStart = &Source;
const UTF32 *SourceEnd = SourceStart + 1;
UTF8 *TargetStart = reinterpret_cast<UTF8 *>(ResultPtr);
UTF8 *TargetEnd = TargetStart + 4;
ConversionResult CR = ConvertUTF32toUTF8(&SourceStart, SourceEnd,
&TargetStart, TargetEnd,
strictConversion);
if (CR != conversionOK)
return false;
ResultPtr = reinterpret_cast<char*>(TargetStart);
return true;
}
bool hasUTF16ByteOrderMark(span<const char> S) {
return (S.size() >= 2 &&
((S[0] == '\xff' && S[1] == '\xfe') ||
(S[0] == '\xfe' && S[1] == '\xff')));
}
bool convertUTF16ToUTF8String(span<const char> SrcBytes, SmallVectorImpl<char> &Out) {
assert(Out.empty());
// Error out on an uneven byte count.
if (SrcBytes.size() % 2)
return false;
// Avoid OOB by returning early on empty input.
if (SrcBytes.empty())
return true;
const UTF16 *Src = reinterpret_cast<const UTF16 *>(SrcBytes.begin());
const UTF16 *SrcEnd = reinterpret_cast<const UTF16 *>(SrcBytes.end());
assert((uintptr_t)Src % sizeof(UTF16) == 0);
// Byteswap if necessary.
std::vector<UTF16> ByteSwapped;
if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_SWAPPED) {
ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd);
for (unsigned I = 0, E = ByteSwapped.size(); I != E; ++I)
ByteSwapped[I] = wpi::ByteSwap_16(ByteSwapped[I]);
Src = &ByteSwapped[0];
SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1;
}
// Skip the BOM for conversion.
if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_NATIVE)
Src++;
// Just allocate enough space up front. We'll shrink it later. Allocate
// enough that we can fit a null terminator without reallocating.
Out.resize(SrcBytes.size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1);
UTF8 *Dst = reinterpret_cast<UTF8 *>(&Out[0]);
UTF8 *DstEnd = Dst + Out.size();
ConversionResult CR =
ConvertUTF16toUTF8(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
assert(CR != targetExhausted);
if (CR != conversionOK) {
Out.clear();
return false;
}
Out.resize(reinterpret_cast<char *>(Dst) - &Out[0]);
Out.push_back(0);
Out.pop_back();
return true;
}
bool convertUTF16ToUTF8String(span<const UTF16> Src, SmallVectorImpl<char> &Out)
{
return convertUTF16ToUTF8String(
span<const char>(reinterpret_cast<const char *>(Src.data()),
Src.size() * sizeof(UTF16)), Out);
}
bool convertUTF8ToUTF16String(std::string_view SrcUTF8,
SmallVectorImpl<UTF16> &DstUTF16) {
assert(DstUTF16.empty());
// Avoid OOB by returning early on empty input.
if (SrcUTF8.empty()) {
DstUTF16.push_back(0);
DstUTF16.pop_back();
return true;
}
const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.data());
const UTF8 *SrcEnd = reinterpret_cast<const UTF8 *>(SrcUTF8.data() + SrcUTF8.size());
// Allocate the same number of UTF-16 code units as UTF-8 code units. Encoding
// as UTF-16 should always require the same amount or less code units than the
// UTF-8 encoding. Allocate one extra byte for the null terminator though,
// so that someone calling DstUTF16.data() gets a null terminated string.
// We resize down later so we don't have to worry that this over allocates.
DstUTF16.resize(SrcUTF8.size()+1);
UTF16 *Dst = &DstUTF16[0];
UTF16 *DstEnd = Dst + DstUTF16.size();
ConversionResult CR =
ConvertUTF8toUTF16(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
assert(CR != targetExhausted);
if (CR != conversionOK) {
DstUTF16.clear();
return false;
}
DstUTF16.resize(Dst - &DstUTF16[0]);
DstUTF16.push_back(0);
DstUTF16.pop_back();
return true;
}
static_assert(sizeof(wchar_t) == 1 || sizeof(wchar_t) == 2 ||
sizeof(wchar_t) == 4,
"Expected wchar_t to be 1, 2, or 4 bytes");
template <typename TResult>
static inline bool ConvertUTF8toWideInternal(std::string_view Source,
TResult &Result) {
// Even in the case of UTF-16, the number of bytes in a UTF-8 string is
// at least as large as the number of elements in the resulting wide
// string, because surrogate pairs take at least 4 bytes in UTF-8.
Result.resize(Source.size() + 1);
char *ResultPtr = reinterpret_cast<char *>(&Result[0]);
const UTF8 *ErrorPtr;
if (!ConvertUTF8toWide(sizeof(wchar_t), Source, ResultPtr, ErrorPtr)) {
Result.clear();
return false;
}
Result.resize(reinterpret_cast<wchar_t *>(ResultPtr) - &Result[0]);
return true;
}
bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result) {
return ConvertUTF8toWideInternal(Source, Result);
}
bool ConvertUTF8toWide(const char *Source, std::wstring &Result) {
if (!Source) {
Result.clear();
return true;
}
return ConvertUTF8toWide(std::string_view(Source), Result);
}
bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl<char> &Result) {
if (sizeof(wchar_t) == 1) {
const UTF8 *Start = reinterpret_cast<const UTF8 *>(Source.data());
const UTF8 *End =
reinterpret_cast<const UTF8 *>(Source.data() + Source.size());
if (!isLegalUTF8String(&Start, End))
return false;
Result.resize(Source.size());
memcpy(&Result[0], Source.data(), Source.size());
return true;
} else if (sizeof(wchar_t) == 2) {
return convertUTF16ToUTF8String(
span<const UTF16>(reinterpret_cast<const UTF16 *>(Source.data()),
Source.size()),
Result);
} else if (sizeof(wchar_t) == 4) {
const UTF32 *Start = reinterpret_cast<const UTF32 *>(Source.data());
const UTF32 *End =
reinterpret_cast<const UTF32 *>(Source.data() + Source.size());
Result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * Source.size());
UTF8 *ResultPtr = reinterpret_cast<UTF8 *>(&Result[0]);
UTF8 *ResultEnd = reinterpret_cast<UTF8 *>(&Result[0] + Result.size());
if (ConvertUTF32toUTF8(&Start, End, &ResultPtr, ResultEnd,
strictConversion) == conversionOK) {
Result.resize(reinterpret_cast<char *>(ResultPtr) - &Result[0]);
return true;
} else {
Result.clear();
return false;
}
} else {
wpi_unreachable(
"Control should never reach this point; see static_assert further up");
}
}
} // end namespace wpi

View File

@@ -1,240 +0,0 @@
//===- lib/Support/ErrorHandling.cpp - Callbacks for errors ---------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file defines an API used to indicate fatal error conditions. Non-fatal
// errors (most of them) should be handled through LLVMContext.
//
//===----------------------------------------------------------------------===//
#include "wpi/ErrorHandling.h"
#include "wpi/SmallVector.h"
#include "wpi/Errc.h"
#include "wpi/WindowsError.h"
#include "fmt/format.h"
#include <cassert>
#include <cstdlib>
#include <mutex>
#include <new>
#ifndef _WIN32
#include <unistd.h>
#endif
#if defined(_MSC_VER)
#include <io.h>
#endif
using namespace wpi;
static fatal_error_handler_t ErrorHandler = nullptr;
static void *ErrorHandlerUserData = nullptr;
static fatal_error_handler_t BadAllocErrorHandler = nullptr;
static void *BadAllocErrorHandlerUserData = nullptr;
// Mutexes to synchronize installing error handlers and calling error handlers.
// Do not use ManagedStatic, or that may allocate memory while attempting to
// report an OOM.
//
// This usage of std::mutex has to be conditionalized behind ifdefs because
// of this script:
// compiler-rt/lib/sanitizer_common/symbolizer/scripts/build_symbolizer.sh
// That script attempts to statically link the LLVM symbolizer library with the
// STL and hide all of its symbols with 'opt -internalize'. To reduce size, it
// cuts out the threading portions of the hermetic copy of libc++ that it
// builds. We can remove these ifdefs if that script goes away.
static std::mutex ErrorHandlerMutex;
static std::mutex BadAllocErrorHandlerMutex;
void wpi::install_fatal_error_handler(fatal_error_handler_t handler,
void *user_data) {
std::scoped_lock Lock(ErrorHandlerMutex);
assert(!ErrorHandler && "Error handler already registered!\n");
ErrorHandler = handler;
ErrorHandlerUserData = user_data;
}
void wpi::remove_fatal_error_handler() {
std::scoped_lock Lock(ErrorHandlerMutex);
ErrorHandler = nullptr;
ErrorHandlerUserData = nullptr;
}
void wpi::report_fatal_error(const char *Reason, bool GenCrashDiag) {
report_fatal_error(std::string_view(Reason), GenCrashDiag);
}
void wpi::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
report_fatal_error(std::string_view(Reason), GenCrashDiag);
}
void wpi::report_fatal_error(std::string_view Reason, bool GenCrashDiag) {
wpi::fatal_error_handler_t handler = nullptr;
void* handlerData = nullptr;
{
// Only acquire the mutex while reading the handler, so as not to invoke a
// user-supplied callback under a lock.
std::scoped_lock Lock(ErrorHandlerMutex);
handler = ErrorHandler;
handlerData = ErrorHandlerUserData;
}
if (handler) {
handler(handlerData, std::string{Reason}, GenCrashDiag);
} else {
fmt::print(stderr, "LLVM ERROR: {}\n", Reason);
}
exit(1);
}
void wpi::install_bad_alloc_error_handler(fatal_error_handler_t handler,
void *user_data) {
std::scoped_lock Lock(BadAllocErrorHandlerMutex);
assert(!ErrorHandler && "Bad alloc error handler already registered!\n");
BadAllocErrorHandler = handler;
BadAllocErrorHandlerUserData = user_data;
}
void wpi::remove_bad_alloc_error_handler() {
std::scoped_lock Lock(BadAllocErrorHandlerMutex);
BadAllocErrorHandler = nullptr;
BadAllocErrorHandlerUserData = nullptr;
}
void wpi::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
fatal_error_handler_t Handler = nullptr;
void *HandlerData = nullptr;
{
// Only acquire the mutex while reading the handler, so as not to invoke a
// user-supplied callback under a lock.
std::scoped_lock Lock(BadAllocErrorHandlerMutex);
Handler = BadAllocErrorHandler;
HandlerData = BadAllocErrorHandlerUserData;
}
if (Handler) {
Handler(HandlerData, Reason, GenCrashDiag);
wpi_unreachable("bad alloc handler should not return");
}
// Don't call the normal error handler. It may allocate memory. Directly write
// an OOM to stderr and abort.
const char *OOMMessage = "LLVM ERROR: out of memory\n";
const char *Newline = "\n";
#ifdef _WIN32
(void)!::_write(2, OOMMessage, strlen(OOMMessage));
(void)!::_write(2, Reason, strlen(Reason));
(void)!::_write(2, Newline, strlen(Newline));
#else
(void)!::write(2, OOMMessage, strlen(OOMMessage));
(void)!::write(2, Reason, strlen(Reason));
(void)!::write(2, Newline, strlen(Newline));
#endif
abort();
}
// Causes crash on allocation failure. It is called prior to the handler set by
// 'install_bad_alloc_error_handler'.
static void out_of_memory_new_handler() {
wpi::report_bad_alloc_error("Allocation failed");
}
// Installs new handler that causes crash on allocation failure. It is called by
// InitLLVM.
void wpi::install_out_of_memory_new_handler() {
std::new_handler old = std::set_new_handler(out_of_memory_new_handler);
(void)old;
assert((old == nullptr || old == out_of_memory_new_handler) &&
"new-handler already installed");
}
void wpi::wpi_unreachable_internal(const char *msg, const char *file,
unsigned line) {
// This code intentionally doesn't call the ErrorHandler callback, because
// wpi_unreachable is intended to be used to indicate "impossible"
// situations, and not legitimate runtime errors.
if (msg)
fmt::print(stderr, "{}\n", msg);
std::fputs("UNREACHABLE executed", stderr);
if (file)
fmt::print(stderr, " at {}:{}", file, line);
fmt::print(stderr, "{}", "!\n");
abort();
#ifdef LLVM_BUILTIN_UNREACHABLE
// Windows systems and possibly others don't declare abort() to be noreturn,
// so use the unreachable builtin to avoid a Clang self-host warning.
LLVM_BUILTIN_UNREACHABLE;
#endif
}
#ifdef _WIN32
#include <winerror.h>
// I'd rather not double the line count of the following.
#define MAP_ERR_TO_COND(x, y) \
case x: \
return std::make_error_code(std::errc::y)
std::error_code wpi::mapWindowsError(unsigned EV) {
switch (EV) {
MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
MAP_ERR_TO_COND(ERROR_SEEK, io_error);
MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
MAP_ERR_TO_COND(WSAEACCES, permission_denied);
MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
MAP_ERR_TO_COND(WSAEFAULT, bad_address);
MAP_ERR_TO_COND(WSAEINTR, interrupted);
MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
default:
return std::error_code(EV, std::system_category());
}
}
#endif

View File

@@ -1,28 +0,0 @@
//===-------------- lib/Support/Hashing.cpp -------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file provides implementation bits for the LLVM common hashing
// infrastructure. Documentation and most of the other information is in the
// header file.
//
//===----------------------------------------------------------------------===//
#include "wpi/Hashing.h"
using namespace wpi;
// Provide a definition and static initializer for the fixed seed. This
// initializer should always be zero to ensure its value can never appear to be
// non-zero, even during dynamic initialization.
uint64_t wpi::hashing::detail::fixed_seed_override = 0;
// Implement the function for forced setting of the fixed seed.
// FIXME: Use atomic operations here so that there is no data race.
void wpi::set_fixed_execution_hash_seed(uint64_t fixed_value) {
hashing::detail::fixed_seed_override = fixed_value;
}

View File

@@ -1,77 +0,0 @@
//===-- ManagedStatic.cpp - Static Global wrapper -------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the ManagedStatic class and wpi_shutdown().
//
//===----------------------------------------------------------------------===//
#include "wpi/ManagedStatic.h"
#include "wpi/mutex.h"
#include <cassert>
#include <mutex>
using namespace wpi;
static const ManagedStaticBase *StaticList = nullptr;
static wpi::mutex *getManagedStaticMutex() {
static wpi::mutex m;
return &m;
}
void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
void (*Deleter)(void*)) const {
assert(Creator);
if (1) {
std::scoped_lock Lock(*getManagedStaticMutex());
if (!Ptr.load(std::memory_order_relaxed)) {
void *Tmp = Creator();
Ptr.store(Tmp, std::memory_order_release);
DeleterFn = Deleter;
// Add to list of managed statics.
Next = StaticList;
StaticList = this;
}
} else {
assert(!Ptr && !DeleterFn && !Next &&
"Partially initialized ManagedStatic!?");
Ptr = Creator();
DeleterFn = Deleter;
// Add to list of managed statics.
Next = StaticList;
StaticList = this;
}
}
void ManagedStaticBase::destroy() const {
assert(DeleterFn && "ManagedStatic not initialized correctly!");
assert(StaticList == this &&
"Not destroyed in reverse order of construction?");
// Unlink from list.
StaticList = Next;
Next = nullptr;
// Destroy memory.
DeleterFn(Ptr);
// Cleanup.
Ptr = nullptr;
DeleterFn = nullptr;
}
/// wpi_shutdown - Deallocate and destroy all ManagedStatic variables.
/// IMPORTANT: it's only safe to call wpi_shutdown() in single thread,
/// without any other threads executing LLVM APIs.
/// wpi_shutdown() should be the last use of LLVM APIs.
void wpi::wpi_shutdown() {
while (StaticList)
StaticList->destroy();
}

View File

@@ -1,34 +0,0 @@
//===- MemAlloc.cpp - Memory allocation functions -------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "wpi/MemAlloc.h"
// These are out of line to have __cpp_aligned_new not affect ABI.
LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void *
wpi::allocate_buffer(size_t Size, size_t Alignment) {
return ::operator new(Size
#ifdef __cpp_aligned_new
,
std::align_val_t(Alignment)
#endif
);
}
void wpi::deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) {
::operator delete(Ptr
#ifdef __cpp_sized_deallocation
,
Size
#endif
#ifdef __cpp_aligned_new
,
std::align_val_t(Alignment)
#endif
);
}

View File

@@ -1,271 +0,0 @@
//===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the SmallPtrSet class. See SmallPtrSet.h for an
// overview of the algorithm.
//
//===----------------------------------------------------------------------===//
#include "wpi/SmallPtrSet.h"
#include "wpi/DenseMapInfo.h"
#include "wpi/ErrorHandling.h"
#include "wpi/MathExtras.h"
#include "wpi/MemAlloc.h"
#include <algorithm>
#include <cassert>
#include <cstdlib>
using namespace wpi;
void SmallPtrSetImplBase::shrink_and_clear() {
assert(!isSmall() && "Can't shrink a small set!");
free(CurArray);
// Reduce the number of buckets.
unsigned Size = size();
CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
NumNonEmpty = NumTombstones = 0;
// Install the new array. Clear all the buckets to empty.
CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
memset(CurArray, -1, CurArraySize*sizeof(void*));
}
std::pair<const void *const *, bool>
SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
// If more than 3/4 of the array is full, grow.
Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
} else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
// If fewer of 1/8 of the array is empty (meaning that many are filled with
// tombstones), rehash.
Grow(CurArraySize);
}
// Okay, we know we have space. Find a hash bucket.
const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
if (*Bucket == Ptr)
return std::make_pair(Bucket, false); // Already inserted, good.
// Otherwise, insert it!
if (*Bucket == getTombstoneMarker())
--NumTombstones;
else
++NumNonEmpty; // Track density.
*Bucket = Ptr;
incrementEpoch();
return std::make_pair(Bucket, true);
}
const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
unsigned ArraySize = CurArraySize;
unsigned ProbeAmt = 1;
const void *const *Array = CurArray;
const void *const *Tombstone = nullptr;
while (true) {
// If we found an empty bucket, the pointer doesn't exist in the set.
// Return a tombstone if we've seen one so far, or the empty bucket if
// not.
if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
return Tombstone ? Tombstone : Array+Bucket;
// Found Ptr's bucket?
if (LLVM_LIKELY(Array[Bucket] == Ptr))
return Array+Bucket;
// If this is a tombstone, remember it. If Ptr ends up not in the set, we
// prefer to return it than something that would require more probing.
if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
Tombstone = Array+Bucket; // Remember the first tombstone found.
// It's a hash collision or a tombstone. Reprobe.
Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
}
}
/// Grow - Allocate a larger backing store for the buckets and move it over.
///
void SmallPtrSetImplBase::Grow(unsigned NewSize) {
const void **OldBuckets = CurArray;
const void **OldEnd = EndPointer();
bool WasSmall = isSmall();
// Install the new array. Clear all the buckets to empty.
const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
// Reset member only if memory was allocated successfully
CurArray = NewBuckets;
CurArraySize = NewSize;
memset(CurArray, -1, NewSize*sizeof(void*));
// Copy over all valid entries.
for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
// Copy over the element if it is valid.
const void *Elt = *BucketPtr;
if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
*const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
}
if (!WasSmall)
free(OldBuckets);
NumNonEmpty -= NumTombstones;
NumTombstones = 0;
}
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
const SmallPtrSetImplBase &that) {
SmallArray = SmallStorage;
// If we're becoming small, prepare to insert into our stack space
if (that.isSmall()) {
CurArray = SmallArray;
// Otherwise, allocate new heap space (unless we were the same size)
} else {
CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
}
// Copy over the that array.
CopyHelper(that);
}
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
unsigned SmallSize,
SmallPtrSetImplBase &&that) {
SmallArray = SmallStorage;
MoveHelper(SmallSize, std::move(that));
}
void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
assert(&RHS != this && "Self-copy should be handled by the caller.");
if (isSmall() && RHS.isSmall())
assert(CurArraySize == RHS.CurArraySize &&
"Cannot assign sets with different small sizes");
// If we're becoming small, prepare to insert into our stack space
if (RHS.isSmall()) {
if (!isSmall())
free(CurArray);
CurArray = SmallArray;
// Otherwise, allocate new heap space (unless we were the same size)
} else if (CurArraySize != RHS.CurArraySize) {
if (isSmall())
CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
else {
const void **T = (const void**)safe_realloc(CurArray,
sizeof(void*) * RHS.CurArraySize);
CurArray = T;
}
}
CopyHelper(RHS);
}
void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
// Copy over the new array size
CurArraySize = RHS.CurArraySize;
// Copy over the contents from the other set
std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
NumNonEmpty = RHS.NumNonEmpty;
NumTombstones = RHS.NumTombstones;
}
void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
SmallPtrSetImplBase &&RHS) {
if (!isSmall())
free(CurArray);
MoveHelper(SmallSize, std::move(RHS));
}
void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
SmallPtrSetImplBase &&RHS) {
assert(&RHS != this && "Self-move should be handled by the caller.");
if (RHS.isSmall()) {
// Copy a small RHS rather than moving.
CurArray = SmallArray;
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
} else {
CurArray = RHS.CurArray;
RHS.CurArray = RHS.SmallArray;
}
// Copy the rest of the trivial members.
CurArraySize = RHS.CurArraySize;
NumNonEmpty = RHS.NumNonEmpty;
NumTombstones = RHS.NumTombstones;
// Make the RHS small and empty.
RHS.CurArraySize = SmallSize;
assert(RHS.CurArray == RHS.SmallArray);
RHS.NumNonEmpty = 0;
RHS.NumTombstones = 0;
}
void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
if (this == &RHS) return;
// We can only avoid copying elements if neither set is small.
if (!this->isSmall() && !RHS.isSmall()) {
std::swap(this->CurArray, RHS.CurArray);
std::swap(this->CurArraySize, RHS.CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap(this->NumTombstones, RHS.NumTombstones);
return;
}
// FIXME: From here on we assume that both sets have the same small size.
// If only RHS is small, copy the small elements into LHS and move the pointer
// from LHS to RHS.
if (!this->isSmall() && RHS.isSmall()) {
assert(RHS.CurArray == RHS.SmallArray);
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
std::swap(RHS.CurArraySize, this->CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap(this->NumTombstones, RHS.NumTombstones);
RHS.CurArray = this->CurArray;
this->CurArray = this->SmallArray;
return;
}
// If only LHS is small, copy the small elements into RHS and move the pointer
// from RHS to LHS.
if (this->isSmall() && !RHS.isSmall()) {
assert(this->CurArray == this->SmallArray);
std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
RHS.SmallArray);
std::swap(RHS.CurArraySize, this->CurArraySize);
std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
std::swap(RHS.NumTombstones, this->NumTombstones);
this->CurArray = RHS.CurArray;
RHS.CurArray = RHS.SmallArray;
return;
}
// Both a small, just swap the small elements.
assert(this->isSmall() && RHS.isSmall());
unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
RHS.SmallArray);
if (this->NumNonEmpty > MinNonEmpty) {
std::copy(this->SmallArray + MinNonEmpty,
this->SmallArray + this->NumNonEmpty,
RHS.SmallArray + MinNonEmpty);
} else {
std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
this->SmallArray + MinNonEmpty);
}
assert(this->CurArraySize == RHS.CurArraySize);
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
std::swap(this->NumTombstones, RHS.NumTombstones);
}

View File

@@ -1,120 +0,0 @@
//===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the SmallVector class.
//
//===----------------------------------------------------------------------===//
#include "wpi/SmallVector.h"
#include <cstdint>
#ifdef LLVM_ENABLE_EXCEPTIONS
#include <stdexcept>
#endif
using namespace wpi;
// Check that no bytes are wasted and everything is well-aligned.
namespace {
struct Struct16B {
alignas(16) void *X;
};
struct Struct32B {
alignas(32) void *X;
};
}
static_assert(sizeof(SmallVector<void *, 0>) ==
sizeof(unsigned) * 2 + sizeof(void *),
"wasted space in SmallVector size 0");
static_assert(alignof(SmallVector<Struct16B, 0>) >= alignof(Struct16B),
"wrong alignment for 16-byte aligned T");
static_assert(alignof(SmallVector<Struct32B, 0>) >= alignof(Struct32B),
"wrong alignment for 32-byte aligned T");
static_assert(sizeof(SmallVector<Struct16B, 0>) >= alignof(Struct16B),
"missing padding for 16-byte aligned T");
static_assert(sizeof(SmallVector<Struct32B, 0>) >= alignof(Struct32B),
"missing padding for 32-byte aligned T");
static_assert(sizeof(SmallVector<void *, 1>) ==
sizeof(unsigned) * 2 + sizeof(void *) * 2,
"wasted space in SmallVector size 1");
/// Report that MinSize doesn't fit into this vector's size type. Throws
/// std::length_error or calls report_fatal_error.
LLVM_ATTRIBUTE_NORETURN
static void report_size_overflow(size_t MinSize, size_t MaxSize);
static void report_size_overflow(size_t MinSize, size_t MaxSize) {
std::string Reason = "SmallVector unable to grow. Requested capacity (" +
std::to_string(MinSize) +
") is larger than maximum value for size type (" +
std::to_string(MaxSize) + ")";
#ifdef LLVM_ENABLE_EXCEPTIONS
throw std::length_error(Reason);
#else
report_fatal_error(Reason);
#endif
}
/// Report that this vector is already at maximum capacity. Throws
/// std::length_error or calls report_fatal_error.
LLVM_ATTRIBUTE_NORETURN static void report_at_maximum_capacity(size_t MaxSize);
static void report_at_maximum_capacity(size_t MaxSize) {
std::string Reason =
"SmallVector capacity unable to grow. Already at maximum size " +
std::to_string(MaxSize);
#ifdef LLVM_ENABLE_EXCEPTIONS
throw std::length_error(Reason);
#else
report_fatal_error(Reason);
#endif
}
// Note: Moving this function into the header may cause performance regression.
static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
constexpr size_t MaxSize = std::numeric_limits<unsigned>::max();
// Ensure we can fit the new capacity.
// This is only going to be applicable when the capacity is 32 bit.
if (MinSize > MaxSize)
report_size_overflow(MinSize, MaxSize);
// Ensure we can meet the guarantee of space for at least one more element.
// The above check alone will not catch the case where grow is called with a
// default MinSize of 0, but the current capacity cannot be increased.
// This is only going to be applicable when the capacity is 32 bit.
if (OldCapacity == MaxSize)
report_at_maximum_capacity(MaxSize);
// In theory 2*capacity can overflow if the capacity is 64 bit, but the
// original capacity would never be large enough for this to be a problem.
size_t NewCapacity = 2 * OldCapacity + 1; // Always grow.
return (std::min)((std::max)(NewCapacity, MinSize), MaxSize);
}
// Note: Moving this function into the header may cause performance regression.
void *SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize,
size_t &NewCapacity) {
NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
return wpi::safe_malloc(NewCapacity * TSize);
}
// Note: Moving this function into the header may cause performance regression.
void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize,
size_t TSize) {
size_t NewCapacity = getNewCapacity(MinSize, TSize, this->capacity());
void *NewElts;
if (BeginX == FirstEl) {
NewElts = safe_malloc(NewCapacity * TSize);
// Copy the elements over. No need to run dtors on PODs.
memcpy(NewElts, this->BeginX, size() * TSize);
} else {
// If this wasn't grown from the inline copy, grow the allocated space.
NewElts = safe_realloc(this->BeginX, NewCapacity * TSize);
}
this->BeginX = NewElts;
this->Capacity = NewCapacity;
}

View File

@@ -1,261 +0,0 @@
//===--- StringMap.cpp - String Hash table map implementation -------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the StringMap class.
//
//===----------------------------------------------------------------------===//
#include "wpi/StringMap.h"
#include "wpi/StringExtras.h"
#include "wpi/DJB.h"
#include "wpi/MathExtras.h"
using namespace wpi;
/// Returns the number of buckets to allocate to ensure that the DenseMap can
/// accommodate \p NumEntries without need to grow().
static unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
// Ensure that "NumEntries * 4 < NumBuckets * 3"
if (NumEntries == 0)
return 0;
// +1 is required because of the strict equality.
// For example if NumEntries is 48, we need to return 401.
return NextPowerOf2(NumEntries * 4 / 3 + 1);
}
StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) {
ItemSize = itemSize;
// If a size is specified, initialize the table with that many buckets.
if (InitSize) {
// The table will grow when the number of entries reach 3/4 of the number of
// buckets. To guarantee that "InitSize" number of entries can be inserted
// in the table without growing, we allocate just what is needed here.
init(getMinBucketToReserveForEntries(InitSize));
return;
}
// Otherwise, initialize it with zero buckets to avoid the allocation.
TheTable = nullptr;
NumBuckets = 0;
NumItems = 0;
NumTombstones = 0;
}
void StringMapImpl::init(unsigned InitSize) {
assert((InitSize & (InitSize - 1)) == 0 &&
"Init Size must be a power of 2 or zero!");
unsigned NewNumBuckets = InitSize ? InitSize : 16;
NumItems = 0;
NumTombstones = 0;
TheTable = static_cast<StringMapEntryBase **>(safe_calloc(
NewNumBuckets + 1, sizeof(StringMapEntryBase **) + sizeof(unsigned)));
// Set the member only if TheTable was successfully allocated
NumBuckets = NewNumBuckets;
// Allocate one extra bucket, set it to look filled so the iterators stop at
// end.
TheTable[NumBuckets] = (StringMapEntryBase *)2;
}
/// LookupBucketFor - Look up the bucket that the specified string should end
/// up in. If it already exists as a key in the map, the Item pointer for the
/// specified bucket will be non-null. Otherwise, it will be null. In either
/// case, the FullHashValue field of the bucket will be set to the hash value
/// of the string.
unsigned StringMapImpl::LookupBucketFor(std::string_view Name) {
unsigned HTSize = NumBuckets;
if (HTSize == 0) { // Hash table unallocated so far?
init(16);
HTSize = NumBuckets;
}
unsigned FullHashValue = djbHash(Name, 0);
unsigned BucketNo = FullHashValue & (HTSize - 1);
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
unsigned ProbeAmt = 1;
int FirstTombstone = -1;
while (true) {
StringMapEntryBase *BucketItem = TheTable[BucketNo];
// If we found an empty bucket, this key isn't in the table yet, return it.
if (LLVM_LIKELY(!BucketItem)) {
// If we found a tombstone, we want to reuse the tombstone instead of an
// empty bucket. This reduces probing.
if (FirstTombstone != -1) {
HashTable[FirstTombstone] = FullHashValue;
return FirstTombstone;
}
HashTable[BucketNo] = FullHashValue;
return BucketNo;
}
if (BucketItem == getTombstoneVal()) {
// Skip over tombstones. However, remember the first one we see.
if (FirstTombstone == -1)
FirstTombstone = BucketNo;
} else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
// If the full hash value matches, check deeply for a match. The common
// case here is that we are only looking at the buckets (for item info
// being non-null and for the full hash value) not at the items. This
// is important for cache locality.
// Do the comparison like this because Name isn't necessarily
// null-terminated!
char *ItemStr = (char *)BucketItem + ItemSize;
if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) {
// We found a match!
return BucketNo;
}
}
// Okay, we didn't find the item. Probe to the next bucket.
BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1);
// Use quadratic probing, it has fewer clumping artifacts than linear
// probing and has good cache behavior in the common case.
++ProbeAmt;
}
}
/// FindKey - Look up the bucket that contains the specified key. If it exists
/// in the map, return the bucket number of the key. Otherwise return -1.
/// This does not modify the map.
int StringMapImpl::FindKey(std::string_view Key) const {
unsigned HTSize = NumBuckets;
if (HTSize == 0)
return -1; // Really empty table?
unsigned FullHashValue = djbHash(Key, 0);
unsigned BucketNo = FullHashValue & (HTSize - 1);
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
unsigned ProbeAmt = 1;
while (true) {
StringMapEntryBase *BucketItem = TheTable[BucketNo];
// If we found an empty bucket, this key isn't in the table yet, return.
if (LLVM_LIKELY(!BucketItem))
return -1;
if (BucketItem == getTombstoneVal()) {
// Ignore tombstones.
} else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
// If the full hash value matches, check deeply for a match. The common
// case here is that we are only looking at the buckets (for item info
// being non-null and for the full hash value) not at the items. This
// is important for cache locality.
// Do the comparison like this because NameStart isn't necessarily
// null-terminated!
char *ItemStr = (char *)BucketItem + ItemSize;
if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) {
// We found a match!
return BucketNo;
}
}
// Okay, we didn't find the item. Probe to the next bucket.
BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1);
// Use quadratic probing, it has fewer clumping artifacts than linear
// probing and has good cache behavior in the common case.
++ProbeAmt;
}
}
/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
/// delete it. This aborts if the value isn't in the table.
void StringMapImpl::RemoveKey(StringMapEntryBase *V) {
const char *VStr = (char *)V + ItemSize;
StringMapEntryBase *V2 = RemoveKey(std::string_view(VStr, V->getKeyLength()));
(void)V2;
assert(V == V2 && "Didn't find key?");
}
/// RemoveKey - Remove the StringMapEntry for the specified key from the
/// table, returning it. If the key is not in the table, this returns null.
StringMapEntryBase *StringMapImpl::RemoveKey(std::string_view Key) {
int Bucket = FindKey(Key);
if (Bucket == -1)
return nullptr;
StringMapEntryBase *Result = TheTable[Bucket];
TheTable[Bucket] = getTombstoneVal();
--NumItems;
++NumTombstones;
assert(NumItems + NumTombstones <= NumBuckets);
return Result;
}
/// RehashTable - Grow the table, redistributing values into the buckets with
/// the appropriate mod-of-hashtable-size.
unsigned StringMapImpl::RehashTable(unsigned BucketNo) {
unsigned NewSize;
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
// If the hash table is now more than 3/4 full, or if fewer than 1/8 of
// the buckets are empty (meaning that many are filled with tombstones),
// grow/rehash the table.
if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) {
NewSize = NumBuckets * 2;
} else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <=
NumBuckets / 8)) {
NewSize = NumBuckets;
} else {
return BucketNo;
}
unsigned NewBucketNo = BucketNo;
// Allocate one extra bucket which will always be non-empty. This allows the
// iterators to stop at end.
auto NewTableArray = static_cast<StringMapEntryBase **>(safe_calloc(
NewSize + 1, sizeof(StringMapEntryBase *) + sizeof(unsigned)));
unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1);
NewTableArray[NewSize] = (StringMapEntryBase *)2;
// Rehash all the items into their new buckets. Luckily :) we already have
// the hash values available, so we don't have to rehash any strings.
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *Bucket = TheTable[I];
if (Bucket && Bucket != getTombstoneVal()) {
// Fast case, bucket available.
unsigned FullHash = HashTable[I];
unsigned NewBucket = FullHash & (NewSize - 1);
if (!NewTableArray[NewBucket]) {
NewTableArray[FullHash & (NewSize - 1)] = Bucket;
NewHashArray[FullHash & (NewSize - 1)] = FullHash;
if (I == BucketNo)
NewBucketNo = NewBucket;
continue;
}
// Otherwise probe for a spot.
unsigned ProbeSize = 1;
do {
NewBucket = (NewBucket + ProbeSize++) & (NewSize - 1);
} while (NewTableArray[NewBucket]);
// Finally found a slot. Fill it in.
NewTableArray[NewBucket] = Bucket;
NewHashArray[NewBucket] = FullHash;
if (I == BucketNo)
NewBucketNo = NewBucket;
}
}
free(TheTable);
TheTable = NewTableArray;
NumBuckets = NewSize;
NumTombstones = 0;
return NewBucketNo;
}

View File

@@ -1,260 +0,0 @@
//===- WindowsSupport.h - Common Windows Include File -----------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file defines things specific to Windows implementations. In addition to
// providing some helpers for working with win32 APIs, this header wraps
// <windows.h> with some portability macros. Always include WindowsSupport.h
// instead of including <windows.h> directly.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//=== WARNING: Implementation here must contain only generic Win32 code that
//=== is guaranteed to work on *all* Win32 variants.
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_WINDOWSSUPPORT_H
#define WPIUTIL_WPI_WINDOWSSUPPORT_H
// mingw-w64 tends to define it as 0x0502 in its headers.
#undef _WIN32_WINNT
#undef _WIN32_IE
// Require at least Windows 7 API.
#define _WIN32_WINNT 0x0601
#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "wpi/SmallVector.h"
#include "wpi/StringExtras.h"
#include "wpi/Chrono.h"
#include "wpi/Compiler.h"
#include "wpi/ErrorHandling.h"
#include "wpi/VersionTuple.h"
#include <cassert>
#include <string>
#include <system_error>
#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS
#include <winternl.h>
#include <ntstatus.h>
// Must be included after windows.h
#include <wincrypt.h>
namespace wpi {
/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
/// GetVersionEx is deprecated, but this API exposes the build number which can
/// be useful for working around certain kernel bugs.
inline wpi::VersionTuple GetWindowsOSVersion() {
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
if (hMod) {
auto getVer = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (getVer) {
RTL_OSVERSIONINFOEXW info{};
info.dwOSVersionInfoSize = sizeof(info);
if (getVer((PRTL_OSVERSIONINFOW)&info) == ((NTSTATUS)0x00000000L)) {
return wpi::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
info.dwBuildNumber);
}
}
}
return wpi::VersionTuple(0, 0, 0, 0);
}
/// Determines if the program is running on Windows 8 or newer. This
/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't
/// yet have VersionHelpers.h, so we have our own helper.
inline bool RunningWindows8OrGreater() {
// Windows 8 is version 6.2, service pack 0.
return GetWindowsOSVersion() >= wpi::VersionTuple(6, 2, 0, 0);
}
/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
/// GetVersionEx is deprecated, but this API exposes the build number which can
/// be useful for working around certain kernel bugs.
wpi::VersionTuple GetWindowsOSVersion();
bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix);
// Include GetLastError() in a fatal error message.
LLVM_ATTRIBUTE_NORETURN inline void ReportLastErrorFatal(const char *Msg) {
std::string ErrMsg;
MakeErrMsg(&ErrMsg, Msg);
wpi::report_fatal_error(ErrMsg);
}
template <typename HandleTraits>
class ScopedHandle {
typedef typename HandleTraits::handle_type handle_type;
handle_type Handle;
ScopedHandle(const ScopedHandle &other) = delete;
void operator=(const ScopedHandle &other) = delete;
public:
ScopedHandle()
: Handle(HandleTraits::GetInvalid()) {}
explicit ScopedHandle(handle_type h)
: Handle(h) {}
~ScopedHandle() {
if (HandleTraits::IsValid(Handle))
HandleTraits::Close(Handle);
}
handle_type take() {
handle_type t = Handle;
Handle = HandleTraits::GetInvalid();
return t;
}
ScopedHandle &operator=(handle_type h) {
if (HandleTraits::IsValid(Handle))
HandleTraits::Close(Handle);
Handle = h;
return *this;
}
// True if Handle is valid.
explicit operator bool() const {
return HandleTraits::IsValid(Handle) ? true : false;
}
operator handle_type() const {
return Handle;
}
};
struct CommonHandleTraits {
typedef HANDLE handle_type;
static handle_type GetInvalid() {
return INVALID_HANDLE_VALUE;
}
static void Close(handle_type h) {
::CloseHandle(h);
}
static bool IsValid(handle_type h) {
return h != GetInvalid();
}
};
struct JobHandleTraits : CommonHandleTraits {
static handle_type GetInvalid() {
return NULL;
}
};
struct CryptContextTraits : CommonHandleTraits {
typedef HCRYPTPROV handle_type;
static handle_type GetInvalid() {
return 0;
}
static void Close(handle_type h) {
::CryptReleaseContext(h, 0);
}
static bool IsValid(handle_type h) {
return h != GetInvalid();
}
};
struct RegTraits : CommonHandleTraits {
typedef HKEY handle_type;
static handle_type GetInvalid() {
return NULL;
}
static void Close(handle_type h) {
::RegCloseKey(h);
}
static bool IsValid(handle_type h) {
return h != GetInvalid();
}
};
struct FindHandleTraits : CommonHandleTraits {
static void Close(handle_type h) {
::FindClose(h);
}
};
struct FileHandleTraits : CommonHandleTraits {};
typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
typedef ScopedHandle<CryptContextTraits> ScopedCryptContext;
typedef ScopedHandle<RegTraits> ScopedRegHandle;
typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
template <class T>
class SmallVectorImpl;
template <class T>
typename SmallVectorImpl<T>::const_pointer
c_str(SmallVectorImpl<T> &str) {
str.push_back(0);
str.pop_back();
return str.data();
}
namespace sys {
inline std::chrono::nanoseconds toDuration(FILETIME Time) {
ULARGE_INTEGER TimeInteger;
TimeInteger.LowPart = Time.dwLowDateTime;
TimeInteger.HighPart = Time.dwHighDateTime;
// FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
return std::chrono::nanoseconds(100 * TimeInteger.QuadPart);
}
inline TimePoint<> toTimePoint(FILETIME Time) {
ULARGE_INTEGER TimeInteger;
TimeInteger.LowPart = Time.dwLowDateTime;
TimeInteger.HighPart = Time.dwHighDateTime;
// Adjust for different epoch
TimeInteger.QuadPart -= 11644473600ll * 10000000;
// FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
return TimePoint<>(std::chrono::nanoseconds(100 * TimeInteger.QuadPart));
}
inline FILETIME toFILETIME(TimePoint<> TP) {
ULARGE_INTEGER TimeInteger;
TimeInteger.QuadPart = TP.time_since_epoch().count() / 100;
TimeInteger.QuadPart += 11644473600ll * 10000000;
FILETIME Time;
Time.dwLowDateTime = TimeInteger.LowPart;
Time.dwHighDateTime = TimeInteger.HighPart;
return Time;
}
} // end namespace sys
} // end namespace wpi.
#endif

View File

@@ -1,29 +0,0 @@
//===--- raw_os_ostream.cpp - Implement the raw_os_ostream class ----------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This implements support adapting raw_ostream to std::ostream.
//
//===----------------------------------------------------------------------===//
#include "wpi/raw_os_ostream.h"
#include <ostream>
using namespace wpi;
//===----------------------------------------------------------------------===//
// raw_os_ostream
//===----------------------------------------------------------------------===//
raw_os_ostream::~raw_os_ostream() {
flush();
}
void raw_os_ostream::write_impl(const char *Ptr, size_t Size) {
OS.write(Ptr, Size);
}
uint64_t raw_os_ostream::current_pos() const { return OS.tellp(); }

View File

@@ -1,737 +0,0 @@
//===--- raw_ostream.cpp - Implement the raw_ostream classes --------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This implements support for bulk buffered stream output.
//
//===----------------------------------------------------------------------===//
#ifdef _WIN32
#define _CRT_NONSTDC_NO_WARNINGS
#endif
#include "wpi/raw_ostream.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/StringExtras.h"
#include "wpi/Compiler.h"
#include "wpi/ErrorHandling.h"
#include "wpi/fs.h"
#include "wpi/MathExtras.h"
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <cstdio>
#include <iterator>
#include <sys/stat.h>
// <fcntl.h> may provide O_BINARY.
# include <fcntl.h>
#ifndef _WIN32
#include <unistd.h>
#include <sys/uio.h>
#endif
#if defined(__CYGWIN__)
#include <io.h>
#endif
#if defined(_MSC_VER)
#include <io.h>
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif
#endif
#ifdef _WIN32
#include "wpi/ConvertUTF.h"
#include "Windows/WindowsSupport.h"
#endif
using namespace wpi;
constexpr raw_ostream::Colors raw_ostream::BLACK;
constexpr raw_ostream::Colors raw_ostream::RED;
constexpr raw_ostream::Colors raw_ostream::GREEN;
constexpr raw_ostream::Colors raw_ostream::YELLOW;
constexpr raw_ostream::Colors raw_ostream::BLUE;
constexpr raw_ostream::Colors raw_ostream::MAGENTA;
constexpr raw_ostream::Colors raw_ostream::CYAN;
constexpr raw_ostream::Colors raw_ostream::WHITE;
constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR;
constexpr raw_ostream::Colors raw_ostream::RESET;
namespace {
// Find the length of an array.
template <class T, std::size_t N>
constexpr inline size_t array_lengthof(T (&)[N]) {
return N;
}
} // namespace
raw_ostream::~raw_ostream() {
// raw_ostream's subclasses should take care to flush the buffer
// in their destructors.
assert(OutBufCur == OutBufStart &&
"raw_ostream destructor called with non-empty buffer!");
if (BufferMode == BufferKind::InternalBuffer)
delete [] OutBufStart;
}
size_t raw_ostream::preferred_buffer_size() const {
// BUFSIZ is intended to be a reasonable default.
return BUFSIZ;
}
void raw_ostream::SetBuffered() {
// Ask the subclass to determine an appropriate buffer size.
if (size_t Size = preferred_buffer_size())
SetBufferSize(Size);
else
// It may return 0, meaning this stream should be unbuffered.
SetUnbuffered();
}
void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
BufferKind Mode) {
assert(((Mode == BufferKind::Unbuffered && !BufferStart && Size == 0) ||
(Mode != BufferKind::Unbuffered && BufferStart && Size != 0)) &&
"stream must be unbuffered or have at least one byte");
// Make sure the current buffer is free of content (we can't flush here; the
// child buffer management logic will be in write_impl).
assert(GetNumBytesInBuffer() == 0 && "Current buffer is non-empty!");
if (BufferMode == BufferKind::InternalBuffer)
delete [] OutBufStart;
OutBufStart = BufferStart;
OutBufEnd = OutBufStart+Size;
OutBufCur = OutBufStart;
BufferMode = Mode;
assert(OutBufStart <= OutBufEnd && "Invalid size!");
}
raw_ostream &raw_ostream::write_escaped(std::string_view Str,
bool UseHexEscapes) {
for (unsigned char c : Str) {
switch (c) {
case '\\':
*this << '\\' << '\\';
break;
case '\t':
*this << '\\' << 't';
break;
case '\n':
*this << '\\' << 'n';
break;
case '"':
*this << '\\' << '"';
break;
default:
if (isPrint(c)) {
*this << c;
break;
}
// Write out the escaped representation.
if (UseHexEscapes) {
*this << '\\' << 'x';
*this << hexdigit((c >> 4 & 0xF));
*this << hexdigit((c >> 0) & 0xF);
} else {
// Always use a full 3-character octal escape.
*this << '\\';
*this << char('0' + ((c >> 6) & 7));
*this << char('0' + ((c >> 3) & 7));
*this << char('0' + ((c >> 0) & 7));
}
}
}
return *this;
}
void raw_ostream::flush_nonempty() {
assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty.");
size_t Length = OutBufCur - OutBufStart;
OutBufCur = OutBufStart;
flush_tied_then_write(OutBufStart, Length);
}
raw_ostream &raw_ostream::write(unsigned char C) {
// Group exceptional cases into a single branch.
if (LLVM_UNLIKELY(OutBufCur >= OutBufEnd)) {
if (LLVM_UNLIKELY(!OutBufStart)) {
if (BufferMode == BufferKind::Unbuffered) {
flush_tied_then_write(reinterpret_cast<char *>(&C), 1);
return *this;
}
// Set up a buffer and start over.
SetBuffered();
return write(C);
}
flush_nonempty();
}
*OutBufCur++ = C;
return *this;
}
raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) {
// Group exceptional cases into a single branch.
if (LLVM_UNLIKELY(size_t(OutBufEnd - OutBufCur) < Size)) {
if (LLVM_UNLIKELY(!OutBufStart)) {
if (BufferMode == BufferKind::Unbuffered) {
flush_tied_then_write(Ptr, Size);
return *this;
}
// Set up a buffer and start over.
SetBuffered();
return write(Ptr, Size);
}
size_t NumBytes = OutBufEnd - OutBufCur;
// If the buffer is empty at this point we have a string that is larger
// than the buffer. Directly write the chunk that is a multiple of the
// preferred buffer size and put the remainder in the buffer.
if (LLVM_UNLIKELY(OutBufCur == OutBufStart)) {
assert(NumBytes != 0 && "undefined behavior");
size_t BytesToWrite = Size - (Size % NumBytes);
flush_tied_then_write(Ptr, BytesToWrite);
size_t BytesRemaining = Size - BytesToWrite;
if (BytesRemaining > size_t(OutBufEnd - OutBufCur)) {
// Too much left over to copy into our buffer.
return write(Ptr + BytesToWrite, BytesRemaining);
}
copy_to_buffer(Ptr + BytesToWrite, BytesRemaining);
return *this;
}
// We don't have enough space in the buffer to fit the string in. Insert as
// much as possible, flush and start over with the remainder.
copy_to_buffer(Ptr, NumBytes);
flush_nonempty();
return write(Ptr + NumBytes, Size - NumBytes);
}
copy_to_buffer(Ptr, Size);
return *this;
}
void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) {
assert(Size <= size_t(OutBufEnd - OutBufCur) && "Buffer overrun!");
// Handle short strings specially, memcpy isn't very good at very short
// strings.
switch (Size) {
case 4: OutBufCur[3] = Ptr[3]; LLVM_FALLTHROUGH;
case 3: OutBufCur[2] = Ptr[2]; LLVM_FALLTHROUGH;
case 2: OutBufCur[1] = Ptr[1]; LLVM_FALLTHROUGH;
case 1: OutBufCur[0] = Ptr[0]; LLVM_FALLTHROUGH;
case 0: break;
default:
memcpy(OutBufCur, Ptr, Size);
break;
}
OutBufCur += Size;
}
void raw_ostream::flush_tied_then_write(const char *Ptr, size_t Size) {
if (TiedStream)
TiedStream->flush();
write_impl(Ptr, Size);
}
template <char C>
static raw_ostream &write_padding(raw_ostream &OS, unsigned NumChars) {
static const char Chars[] = {C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C};
// Usually the indentation is small, handle it with a fastpath.
if (NumChars < array_lengthof(Chars))
return OS.write(Chars, NumChars);
while (NumChars) {
unsigned NumToWrite = std::min(NumChars,
(unsigned)array_lengthof(Chars)-1);
OS.write(Chars, NumToWrite);
NumChars -= NumToWrite;
}
return OS;
}
/// indent - Insert 'NumSpaces' spaces.
raw_ostream &raw_ostream::indent(unsigned NumSpaces) {
return write_padding<' '>(*this, NumSpaces);
}
/// write_zeros - Insert 'NumZeros' nulls.
raw_ostream &raw_ostream::write_zeros(unsigned NumZeros) {
return write_padding<'\0'>(*this, NumZeros);
}
void raw_ostream::anchor() {}
//===----------------------------------------------------------------------===//
// raw_fd_ostream
//===----------------------------------------------------------------------===//
static int getFD(std::string_view Filename, std::error_code &EC,
fs::CreationDisposition Disp, fs::FileAccess Access,
fs::OpenFlags Flags) {
assert((Access & fs::FA_Write) &&
"Cannot make a raw_ostream from a read-only descriptor!");
// Handle "-" as stdout. Note that when we do this, we consider ourself
// the owner of stdout and may set the "binary" flag globally based on Flags.
if (Filename == "-") {
EC = std::error_code();
// Change stdout's text/binary mode based on the Flags.
if (!(Flags & fs::OF_Text)) {
#if defined(_WIN32)
_setmode(_fileno(stdout), _O_BINARY);
#endif
}
return STDOUT_FILENO;
}
fs::file_t F;
if (Access & fs::FA_Read)
F = fs::OpenFileForReadWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags);
else
F = fs::OpenFileForWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags);
if (EC)
return -1;
int FD = fs::FileToFd(F, EC, Flags);
if (EC)
return -1;
return FD;
}
raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC)
: raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, fs::FA_Write,
fs::OF_None) {}
raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
fs::CreationDisposition Disp)
: raw_fd_ostream(Filename, EC, Disp, fs::FA_Write, fs::OF_None) {}
raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
fs::FileAccess Access)
: raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, Access,
fs::OF_None) {}
raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
fs::OpenFlags Flags)
: raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, fs::FA_Write,
Flags) {}
raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC,
fs::CreationDisposition Disp,
fs::FileAccess Access,
fs::OpenFlags Flags)
: raw_fd_ostream(getFD(Filename, EC, Disp, Access, Flags), true) {}
/// FD is the file descriptor that this writes to. If ShouldClose is true, this
/// closes the file when the stream is destroyed.
raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered,
OStreamKind K)
: raw_pwrite_stream(unbuffered, K), FD(fd), ShouldClose(shouldClose) {
if (FD < 0 ) {
ShouldClose = false;
return;
}
enable_colors(true);
// Do not attempt to close stdout or stderr. We used to try to maintain the
// property that tools that support writing file to stdout should not also
// write informational output to stdout, but in practice we were never able to
// maintain this invariant. Many features have been added to LLVM and clang
// (-fdump-record-layouts, optimization remarks, etc) that print to stdout, so
// users must simply be aware that mixed output and remarks is a possibility.
if (FD <= STDERR_FILENO)
ShouldClose = false;
#ifdef _WIN32
// Check if this is a console device. This is not equivalent to isatty.
IsWindowsConsole =
::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;
#endif
// Get the starting position.
off_t loc = ::lseek(FD, 0, SEEK_CUR);
#ifdef _WIN32
SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast<HANDLE>(::_get_osfhandle(FD))) != FILE_TYPE_PIPE;
#else
SupportsSeeking = loc != (off_t)-1;
#endif
if (!SupportsSeeking)
pos = 0;
else
pos = static_cast<uint64_t>(loc);
}
raw_fd_ostream::~raw_fd_ostream() {
if (FD >= 0) {
flush();
if (ShouldClose && ::close(FD) < 0)
error_detected(std::error_code(errno, std::generic_category()));
}
#ifdef __MINGW32__
// On mingw, global dtors should not call exit().
// report_fatal_error() invokes exit(). We know report_fatal_error()
// might not write messages to stderr when any errors were detected
// on FD == 2.
if (FD == 2) return;
#endif
// If there are any pending errors, report them now. Clients wishing
// to avoid report_fatal_error calls should check for errors with
// has_error() and clear the error flag with clear_error() before
// destructing raw_ostream objects which may have errors.
if (has_error())
report_fatal_error("IO failure on output stream: " + error().message(),
/*gen_crash_diag=*/false);
}
#if defined(_WIN32)
// The most reliable way to print unicode in a Windows console is with
// WriteConsoleW. To use that, first transcode from UTF-8 to UTF-16. This
// assumes that LLVM programs always print valid UTF-8 to the console. The data
// might not be UTF-8 for two major reasons:
// 1. The program is printing binary (-filetype=obj -o -), in which case it
// would have been gibberish anyway.
// 2. The program is printing text in a semi-ascii compatible codepage like
// shift-jis or cp1252.
//
// Most LLVM programs don't produce non-ascii text unless they are quoting
// user source input. A well-behaved LLVM program should either validate that
// the input is UTF-8 or transcode from the local codepage to UTF-8 before
// quoting it. If they don't, this may mess up the encoding, but this is still
// probably the best compromise we can make.
static bool write_console_impl(int FD, std::string_view Data) {
SmallVector<wchar_t, 256> WideText;
// Fall back to ::write if it wasn't valid UTF-8.
if (auto EC = sys::windows::UTF8ToUTF16(Data, WideText))
return false;
// On Windows 7 and earlier, WriteConsoleW has a low maximum amount of data
// that can be written to the console at a time.
size_t MaxWriteSize = WideText.size();
if (!RunningWindows8OrGreater())
MaxWriteSize = 32767;
size_t WCharsWritten = 0;
do {
size_t WCharsToWrite =
std::min(MaxWriteSize, WideText.size() - WCharsWritten);
DWORD ActuallyWritten;
bool Success =
::WriteConsoleW((HANDLE)::_get_osfhandle(FD), &WideText[WCharsWritten],
WCharsToWrite, &ActuallyWritten,
/*Reserved=*/nullptr);
// The most likely reason for WriteConsoleW to fail is that FD no longer
// points to a console. Fall back to ::write. If this isn't the first loop
// iteration, something is truly wrong.
if (!Success)
return false;
WCharsWritten += ActuallyWritten;
} while (WCharsWritten != WideText.size());
return true;
}
#endif
void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
assert(FD >= 0 && "File already closed.");
pos += Size;
#if defined(_WIN32)
// If this is a Windows console device, try re-encoding from UTF-8 to UTF-16
// and using WriteConsoleW. If that fails, fall back to plain write().
if (IsWindowsConsole)
if (write_console_impl(FD, std::string_view(Ptr, Size)))
return;
#endif
// The maximum write size is limited to INT32_MAX. A write
// greater than SSIZE_MAX is implementation-defined in POSIX,
// and Windows _write requires 32 bit input.
size_t MaxWriteSize = INT32_MAX;
#if defined(__linux__)
// It is observed that Linux returns EINVAL for a very large write (>2G).
// Make it a reasonably small value.
MaxWriteSize = 1024 * 1024 * 1024;
#endif
do {
size_t ChunkSize = std::min(Size, MaxWriteSize);
#ifdef _WIN32
int ret = ::_write(FD, Ptr, ChunkSize);
#else
ssize_t ret = ::write(FD, Ptr, ChunkSize);
#endif
if (ret < 0) {
// If it's a recoverable error, swallow it and retry the write.
//
// Ideally we wouldn't ever see EAGAIN or EWOULDBLOCK here, since
// raw_ostream isn't designed to do non-blocking I/O. However, some
// programs, such as old versions of bjam, have mistakenly used
// O_NONBLOCK. For compatibility, emulate blocking semantics by
// spinning until the write succeeds. If you don't want spinning,
// don't use O_NONBLOCK file descriptors with raw_ostream.
if (errno == EINTR || errno == EAGAIN
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
)
continue;
// Otherwise it's a non-recoverable error. Note it and quit.
error_detected(std::error_code(errno, std::generic_category()));
break;
}
// The write may have written some or all of the data. Update the
// size and buffer pointer to reflect the remainder that needs
// to be written. If there are no bytes left, we're done.
Ptr += ret;
Size -= ret;
} while (Size > 0);
}
void raw_fd_ostream::close() {
assert(ShouldClose);
ShouldClose = false;
flush();
if (::close(FD) < 0)
error_detected(std::error_code(errno, std::generic_category()));
FD = -1;
}
uint64_t raw_fd_ostream::seek(uint64_t off) {
assert(SupportsSeeking && "Stream does not support seeking!");
flush();
#ifdef _WIN32
pos = ::_lseeki64(FD, off, SEEK_SET);
#else
pos = ::lseek(FD, off, SEEK_SET);
#endif
if (pos == (uint64_t)-1)
error_detected(std::error_code(errno, std::generic_category()));
return pos;
}
void raw_fd_ostream::pwrite_impl(const char *Ptr, size_t Size,
uint64_t Offset) {
uint64_t Pos = tell();
seek(Offset);
write(Ptr, Size);
seek(Pos);
}
size_t raw_fd_ostream::preferred_buffer_size() const {
#if defined(_WIN32)
// Disable buffering for console devices. Console output is re-encoded from
// UTF-8 to UTF-16 on Windows, and buffering it would require us to split the
// buffer on a valid UTF-8 codepoint boundary. Terminal buffering is disabled
// below on most other OSs, so do the same thing on Windows and avoid that
// complexity.
if (IsWindowsConsole)
return 0;
return raw_ostream::preferred_buffer_size();
#elif !defined(__minix)
// Minix has no st_blksize.
assert(FD >= 0 && "File not yet open!");
struct stat statbuf;
if (fstat(FD, &statbuf) != 0)
return 0;
// If this is a terminal, don't use buffering. Line buffering
// would be a more traditional thing to do, but it's not worth
// the complexity.
if (S_ISCHR(statbuf.st_mode) && is_displayed())
return 0;
// Return the preferred block size.
return statbuf.st_blksize;
#else
return raw_ostream::preferred_buffer_size();
#endif
}
void raw_fd_ostream::anchor() {}
//===----------------------------------------------------------------------===//
// outs(), errs(), nulls()
//===----------------------------------------------------------------------===//
raw_fd_ostream &wpi::outs() {
// Set buffer settings to model stdout behavior.
std::error_code EC;
static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::OF_None);
assert(!EC);
return *S;
}
raw_fd_ostream &wpi::errs() {
// Set standard error to be unbuffered and tied to outs() by default.
static raw_fd_ostream* S = new raw_fd_ostream(STDERR_FILENO, false, true);
return *S;
}
/// nulls() - This returns a reference to a raw_ostream which discards output.
raw_ostream &wpi::nulls() {
static raw_null_ostream S;
return S;
}
//===----------------------------------------------------------------------===//
// File Streams
//===----------------------------------------------------------------------===//
raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC)
: raw_fd_ostream(getFD(Filename, EC, fs::CD_CreateAlways,
fs::FA_Write | fs::FA_Read,
fs::OF_None),
true, false, OStreamKind::OK_FDStream) {
if (EC)
return;
// Do not support non-seekable files.
if (!supportsSeeking())
EC = std::make_error_code(std::errc::invalid_argument);
}
bool raw_fd_stream::classof(const raw_ostream *OS) {
return OS->get_kind() == OStreamKind::OK_FDStream;
}
//===----------------------------------------------------------------------===//
// raw_string_ostream
//===----------------------------------------------------------------------===//
raw_string_ostream::~raw_string_ostream() {
flush();
}
void raw_string_ostream::write_impl(const char *Ptr, size_t Size) {
OS.append(Ptr, Size);
}
//===----------------------------------------------------------------------===//
// raw_svector_ostream
//===----------------------------------------------------------------------===//
uint64_t raw_svector_ostream::current_pos() const { return OS.size(); }
void raw_svector_ostream::write_impl(const char *Ptr, size_t Size) {
OS.append(Ptr, Ptr + Size);
}
void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size,
uint64_t Offset) {
memcpy(OS.data() + Offset, Ptr, Size);
}
//===----------------------------------------------------------------------===//
// raw_vector_ostream
//===----------------------------------------------------------------------===//
uint64_t raw_vector_ostream::current_pos() const { return OS.size(); }
void raw_vector_ostream::write_impl(const char *Ptr, size_t Size) {
OS.insert(OS.end(), Ptr, Ptr + Size);
}
void raw_vector_ostream::pwrite_impl(const char *Ptr, size_t Size,
uint64_t Offset) {
memcpy(OS.data() + Offset, Ptr, Size);
}
//===----------------------------------------------------------------------===//
// raw_usvector_ostream
//===----------------------------------------------------------------------===//
uint64_t raw_usvector_ostream::current_pos() const { return OS.size(); }
void raw_usvector_ostream::write_impl(const char *Ptr, size_t Size) {
OS.append(reinterpret_cast<const uint8_t *>(Ptr),
reinterpret_cast<const uint8_t *>(Ptr) + Size);
}
void raw_usvector_ostream::pwrite_impl(const char *Ptr, size_t Size,
uint64_t Offset) {
memcpy(OS.data() + Offset, Ptr, Size);
}
//===----------------------------------------------------------------------===//
// raw_uvector_ostream
//===----------------------------------------------------------------------===//
uint64_t raw_uvector_ostream::current_pos() const { return OS.size(); }
void raw_uvector_ostream::write_impl(const char *Ptr, size_t Size) {
OS.insert(OS.end(), reinterpret_cast<const uint8_t *>(Ptr),
reinterpret_cast<const uint8_t *>(Ptr) + Size);
}
void raw_uvector_ostream::pwrite_impl(const char *Ptr, size_t Size,
uint64_t Offset) {
memcpy(OS.data() + Offset, Ptr, Size);
}
//===----------------------------------------------------------------------===//
// raw_null_ostream
//===----------------------------------------------------------------------===//
raw_null_ostream::~raw_null_ostream() {
#ifndef NDEBUG
// ~raw_ostream asserts that the buffer is empty. This isn't necessary
// with raw_null_ostream, but it's better to have raw_null_ostream follow
// the rules than to change the rules just for raw_null_ostream.
flush();
#endif
}
void raw_null_ostream::write_impl(const char *Ptr, size_t Size) {
}
uint64_t raw_null_ostream::current_pos() const {
return 0;
}
void raw_null_ostream::pwrite_impl(const char *Ptr, size_t Size,
uint64_t Offset) {}
void raw_pwrite_stream::anchor() {}
void buffer_ostream::anchor() {}
void buffer_unique_ostream::anchor() {}

File diff suppressed because it is too large Load Diff