diff --git a/cameraserver/src/main/native/cpp/CameraServer.cpp b/cameraserver/src/main/native/cpp/CameraServer.cpp index 55c69e8bff..4b97c33641 100644 --- a/cameraserver/src/main/native/cpp/CameraServer.cpp +++ b/cameraserver/src/main/native/cpp/CameraServer.cpp @@ -620,7 +620,7 @@ cs::MjpegServer CameraServer::AddServer(wpi::StringRef name, int port) { void CameraServer::AddServer(const cs::VideoSink& server) { std::lock_guard lock(m_mutex); - m_sinks.emplace_second(server.GetName(), server); + m_sinks.try_emplace(server.GetName(), server); } void CameraServer::RemoveServer(wpi::StringRef name) { @@ -661,7 +661,7 @@ void CameraServer::AddCamera(const cs::VideoSource& camera) { std::string name = camera.GetName(); std::lock_guard lock(m_mutex); if (m_primarySourceName.empty()) m_primarySourceName = name; - m_sources.emplace_second(name, camera); + m_sources.try_emplace(name, camera); } void CameraServer::RemoveCamera(wpi::StringRef name) { diff --git a/ntcore/src/main/native/cpp/Storage_load.cpp b/ntcore/src/main/native/cpp/Storage_load.cpp index efbfbeea50..c05e029068 100644 --- a/ntcore/src/main/native/cpp/Storage_load.cpp +++ b/ntcore/src/main/native/cpp/Storage_load.cpp @@ -262,9 +262,9 @@ std::shared_ptr LoadPersistentImpl::ReadBooleanValue() { std::shared_ptr LoadPersistentImpl::ReadDoubleValue() { // need to convert to null-terminated string for std::strtod() - wpi::SmallString<64> buf; + wpi::SmallString<64> buf = m_line; char* end; - double v = std::strtod(m_line.c_str(buf), &end); + double v = std::strtod(buf.c_str(), &end); if (*end != '\0') { Warn("invalid double value"); return nullptr; @@ -318,9 +318,9 @@ std::shared_ptr LoadPersistentImpl::ReadDoubleArrayValue() { std::tie(tok, m_line) = m_line.split(','); tok = tok.trim(" \t"); // need to convert to null-terminated string for std::strtod() - wpi::SmallString<64> buf; + wpi::SmallString<64> buf = tok; char* end; - double v = std::strtod(tok.c_str(buf), &end); + double v = std::strtod(buf.c_str(), &end); if (*end != '\0') { Warn("invalid double value"); return nullptr; diff --git a/ntcore/src/main/native/cpp/Storage_save.cpp b/ntcore/src/main/native/cpp/Storage_save.cpp index a8a3b0a90f..3f352da82f 100644 --- a/ntcore/src/main/native/cpp/Storage_save.cpp +++ b/ntcore/src/main/native/cpp/Storage_save.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/wpilibc/src/main/native/cpp/SerialPort.cpp b/wpilibc/src/main/native/cpp/SerialPort.cpp index 377cbfe0b4..16d35ef30e 100644 --- a/wpilibc/src/main/native/cpp/SerialPort.cpp +++ b/wpilibc/src/main/native/cpp/SerialPort.cpp @@ -73,7 +73,7 @@ SerialPort::SerialPort(int baudRate, Port port, int dataBits, * @param stopBits The number of stop bits to use as defined by the enum * StopBits. */ -SerialPort::SerialPort(int baudRate, wpi::StringRef portName, Port port, +SerialPort::SerialPort(int baudRate, const wpi::Twine& portName, Port port, int dataBits, SerialPort::Parity parity, SerialPort::StopBits stopBits) { int32_t status = 0; @@ -81,7 +81,7 @@ SerialPort::SerialPort(int baudRate, wpi::StringRef portName, Port port, m_port = port; wpi::SmallVector buf; - const char* portNameC = portName.c_str(buf); + const char* portNameC = portName.toNullTerminatedStringRef(buf).data(); HAL_InitializeSerialPortDirect(static_cast(port), portNameC, &status); diff --git a/wpilibc/src/main/native/include/SerialPort.h b/wpilibc/src/main/native/include/SerialPort.h index 935bad69cc..994aed7fef 100644 --- a/wpilibc/src/main/native/include/SerialPort.h +++ b/wpilibc/src/main/native/include/SerialPort.h @@ -10,6 +10,7 @@ #include #include +#include #include #include "ErrorBase.h" @@ -58,7 +59,7 @@ class SerialPort : public ErrorBase { SerialPort(int baudRate, Port port = kOnboard, int dataBits = 8, Parity parity = kParity_None, StopBits stopBits = kStopBits_One); WPI_DEPRECATED("Will be removed for 2019") - SerialPort(int baudRate, wpi::StringRef portName, Port port = kOnboard, + SerialPort(int baudRate, const wpi::Twine& portName, Port port = kOnboard, int dataBits = 8, Parity parity = kParity_None, StopBits stopBits = kStopBits_One); ~SerialPort(); diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 2ebcbac622..6d4e1adb64 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -17,11 +17,13 @@ generatedFileExclude { src/main/native/include/wpi/DenseMap\.h$ src/main/native/include/wpi/DenseMapInfo\.h$ src/main/native/include/wpi/EpochTracker\.h$ + src/main/native/include/wpi/ErrorOr\.h$ src/main/native/include/wpi/FileSystem\.h$ src/main/native/include/wpi/Format\.h$ src/main/native/include/wpi/Hashing\.h$ src/main/native/include/wpi/IntrusiveRefCntPtr\.h$ src/main/native/include/wpi/MathExtras\.h$ + src/main/native/include/wpi/NativeFormatting\.h$ src/main/native/include/wpi/None\.h$ src/main/native/include/wpi/Optional\.h$ src/main/native/include/wpi/Path\.h$ @@ -36,6 +38,7 @@ generatedFileExclude { src/main/native/include/wpi/StringRef\.h$ src/main/native/include/wpi/Twine\.h$ src/main/native/include/wpi/WindowsError\.h$ + src/main/native/include/wpi/iterator\.h$ src/main/native/include/wpi/iterator_range\.h$ src/main/native/include/wpi/raw_os_ostream\.h$ src/main/native/include/wpi/raw_ostream\.h$ diff --git a/wpiutil/src/main/native/cpp/TCPStream.cpp b/wpiutil/src/main/native/cpp/TCPStream.cpp index 08c710996a..d4e36714ef 100644 --- a/wpiutil/src/main/native/cpp/TCPStream.cpp +++ b/wpiutil/src/main/native/cpp/TCPStream.cpp @@ -26,14 +26,16 @@ #include #ifdef _WIN32 -#include -#include +#include +#include #else #include #include #include #endif +#include + using namespace wpi; TCPStream::TCPStream(int sd, sockaddr_in* address) diff --git a/wpiutil/src/main/native/cpp/UDPClient.cpp b/wpiutil/src/main/native/cpp/UDPClient.cpp index c447b3f0a7..f20ec417dc 100644 --- a/wpiutil/src/main/native/cpp/UDPClient.cpp +++ b/wpiutil/src/main/native/cpp/UDPClient.cpp @@ -25,8 +25,8 @@ using namespace wpi; UDPClient::UDPClient(Logger& logger) : UDPClient("", logger) {} -UDPClient::UDPClient(StringRef address, Logger& logger) - : m_lsd(0), m_address(address), m_logger(logger) {} +UDPClient::UDPClient(const Twine& address, Logger& logger) + : m_lsd(0), m_address(address.str()), m_logger(logger) {} UDPClient::UDPClient(UDPClient&& other) : m_lsd(other.m_lsd), @@ -109,27 +109,27 @@ void UDPClient::shutdown() { } } -int UDPClient::send(ArrayRef data, StringRef server, int port) { +int UDPClient::send(ArrayRef data, const Twine& server, int port) { // server must be a resolvable IP address struct sockaddr_in addr; std::memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - if (server.size() > 0) { - SmallVector addr_store; - auto remoteAddr = server.c_str(addr_store); -#ifdef _WIN32 - int res = InetPton(AF_INET, remoteAddr, &(addr.sin_addr)); -#else - int res = inet_pton(AF_INET, remoteAddr, &(addr.sin_addr)); -#endif - if (res != 1) { - WPI_ERROR(m_logger, "could not resolve " << server << " address"); - return -1; - } - } else { + SmallVector addr_store; + StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store); + if (remoteAddr.empty()) { WPI_ERROR(m_logger, "server must be passed"); return -1; } + +#ifdef _WIN32 + int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr)); +#else + int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr)); +#endif + if (res != 1) { + WPI_ERROR(m_logger, "could not resolve " << server << " address"); + return -1; + } addr.sin_port = htons(port); // sendto should not block @@ -139,27 +139,27 @@ int UDPClient::send(ArrayRef data, StringRef server, int port) { return result; } -int UDPClient::send(StringRef data, StringRef server, int port) { +int UDPClient::send(StringRef data, const Twine& server, int port) { // server must be a resolvable IP address struct sockaddr_in addr; std::memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - if (server.size() > 0) { - SmallVector addr_store; - auto remoteAddr = server.c_str(addr_store); -#ifdef _WIN32 - int res = InetPton(AF_INET, remoteAddr, &(addr.sin_addr)); -#else - int res = inet_pton(AF_INET, remoteAddr, &(addr.sin_addr)); -#endif - if (res != 1) { - WPI_ERROR(m_logger, "could not resolve " << server << " address"); - return -1; - } - } else { + SmallVector addr_store; + StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store); + if (remoteAddr.empty()) { WPI_ERROR(m_logger, "server must be passed"); return -1; } + +#ifdef _WIN32 + int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr)); +#else + int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr)); +#endif + if (res != 1) { + WPI_ERROR(m_logger, "could not resolve " << server << " address"); + return -1; + } addr.sin_port = htons(port); // sendto should not block diff --git a/wpiutil/src/main/native/cpp/hostname.cpp b/wpiutil/src/main/native/cpp/hostname.cpp index e4862d1f74..e2c0990704 100644 --- a/wpiutil/src/main/native/cpp/hostname.cpp +++ b/wpiutil/src/main/native/cpp/hostname.cpp @@ -60,6 +60,6 @@ StringRef GetHostname(SmallVectorImpl& name) { name.clear(); name.append(tmpName, tmpName + std::strlen(tmpName) + 1); - return StringRef{name.data(), name.size(), true}; + return StringRef{name.data(), name.size()}; } } // namespace wpi diff --git a/wpiutil/src/main/native/cpp/json.cpp b/wpiutil/src/main/native/cpp/json.cpp index ef25164357..9becfdd63f 100644 --- a/wpiutil/src/main/native/cpp/json.cpp +++ b/wpiutil/src/main/native/cpp/json.cpp @@ -207,7 +207,7 @@ json::json(initializer_list_t init, std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) { auto element = element_ref.moved_or_copied(); - m_value.object->emplace_second( + m_value.object->try_emplace( *((*element.m_value.array)[0].m_value.string), std::move((*element.m_value.array)[1])); }); diff --git a/wpiutil/src/main/native/cpp/json_parser.cpp b/wpiutil/src/main/native/cpp/json_parser.cpp index 7c8b622fdd..e0d7db36c4 100644 --- a/wpiutil/src/main/native/cpp/json_parser.cpp +++ b/wpiutil/src/main/native/cpp/json_parser.cpp @@ -39,6 +39,7 @@ SOFTWARE. #include #include "wpi/Format.h" +#include "wpi/SmallString.h" #include "wpi/raw_istream.h" #include "wpi/raw_ostream.h" @@ -1604,7 +1605,7 @@ void json::parser::parse_internal(bool keep, json& result) if (keep and keep_tag and not value.is_discarded()) { - result.m_value.object->emplace_second(StringRef(key.data(), key.size()), std::move(value)); + result.m_value.object->try_emplace(StringRef(key.data(), key.size()), std::move(value)); } // comma -> next value diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp index 74fd8d430b..bf51c360c9 100644 --- a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp @@ -46,13 +46,42 @@ ------------------------------------------------------------------------ */ - #include "wpi/ConvertUTF.h" #ifdef CVTUTF_DEBUG #include #endif #include +/* + * 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; @@ -110,7 +139,7 @@ static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC * into an inline function. */ -extern "C" { + /* --------------------------------------------------------------------- */ ConversionResult ConvertUTF32toUTF16 ( @@ -272,9 +301,9 @@ ConversionResult ConvertUTF16toUTF8 ( target -= bytesToWrite; result = targetExhausted; break; } switch (bytesToWrite) { /* note: everything falls through. */ - case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */ - case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */ - case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */ + 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; @@ -325,9 +354,9 @@ ConversionResult ConvertUTF32toUTF8 ( target -= bytesToWrite; result = targetExhausted; break; } switch (bytesToWrite) { /* note: everything falls through. */ - case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */ - case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */ - case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */ + 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; @@ -356,8 +385,8 @@ static Boolean isLegalUTF8(const UTF8 *source, int length) { switch (length) { default: return false; /* Everything else falls through when "true"... */ - case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; /* FALLTHRU */ - case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; /* FALLTHRU */ + 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) { @@ -368,7 +397,6 @@ static Boolean isLegalUTF8(const UTF8 *source, int length) { case 0xF4: if (a > 0x8F) return false; break; default: if (a < 0x80) return false; } - /* FALLTHRU */ case 1: if (*source >= 0x80 && *source < 0xC2) return false; } @@ -532,11 +560,11 @@ ConversionResult ConvertUTF8toUTF16 ( * The cases all fall through. See "Note A" below. */ switch (extraBytesToRead) { - case 5: ch += *source++; ch <<= 6; /* FALLTHRU */ /* remember, illegal UTF-8 */ - case 4: ch += *source++; ch <<= 6; /* FALLTHRU */ /* remember, illegal UTF-8 */ - case 3: ch += *source++; ch <<= 6; /* FALLTHRU */ - case 2: ch += *source++; ch <<= 6; /* FALLTHRU */ - case 1: ch += *source++; ch <<= 6; /* FALLTHRU */ + 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]; @@ -636,11 +664,11 @@ static ConversionResult ConvertUTF8toUTF32Impl( * The cases all fall through. See "Note A" below. */ switch (extraBytesToRead) { - case 5: ch += *source++; ch <<= 6; /* FALLTHRU */ - case 4: ch += *source++; ch <<= 6; /* FALLTHRU */ - case 3: ch += *source++; ch <<= 6; /* FALLTHRU */ - case 2: ch += *source++; ch <<= 6; /* FALLTHRU */ - case 1: ch += *source++; ch <<= 6; /* FALLTHRU */ + 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]; @@ -687,8 +715,6 @@ ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, flags, /*InputIsPartial=*/false); } -} - /* --------------------------------------------------------------------- Note A. @@ -707,3 +733,7 @@ ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, similarly unrolled loops. --------------------------------------------------------------------- */ + +} // namespace llvm + +ConvertUTF_RESTORE_WARNINGS diff --git a/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp b/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp new file mode 100644 index 0000000000..075f31e90f --- /dev/null +++ b/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp @@ -0,0 +1,264 @@ +//===- NativeFormatting.cpp - Low level formatting helpers -------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "wpi/NativeFormatting.h" + +#include "wpi/ArrayRef.h" +#include "wpi/SmallString.h" +#include "wpi/StringExtras.h" +#include "wpi/Format.h" + +#include + +using namespace wpi; + +template +static int format_to_buffer(T Value, char (&Buffer)[N]) { + char *EndPtr = std::end(Buffer); + char *CurPtr = EndPtr; + + do { + *--CurPtr = '0' + char(Value % 10); + Value /= 10; + } while (Value); + return EndPtr - CurPtr; +} + +static void writeWithCommas(raw_ostream &S, ArrayRef Buffer) { + assert(!Buffer.empty()); + + ArrayRef ThisGroup; + int InitialDigits = ((Buffer.size() - 1) % 3) + 1; + ThisGroup = Buffer.take_front(InitialDigits); + S.write(ThisGroup.data(), ThisGroup.size()); + + Buffer = Buffer.drop_front(InitialDigits); + assert(Buffer.size() % 3 == 0); + while (!Buffer.empty()) { + S << ','; + ThisGroup = Buffer.take_front(3); + S.write(ThisGroup.data(), 3); + Buffer = Buffer.drop_front(3); + } +} + +template +static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits, + IntegerStyle Style, bool IsNegative) { + static_assert(std::is_unsigned::value, "Value is not unsigned!"); + + char NumberBuffer[128]; + std::memset(NumberBuffer, '0', sizeof(NumberBuffer)); + + size_t Len = 0; + Len = format_to_buffer(N, NumberBuffer); + + if (IsNegative) + S << '-'; + + if (Len < MinDigits && Style != IntegerStyle::Number) { + for (size_t I = Len; I < MinDigits; ++I) + S << '0'; + } + + if (Style == IntegerStyle::Number) { + writeWithCommas(S, ArrayRef(std::end(NumberBuffer) - Len, Len)); + } else { + S.write(std::end(NumberBuffer) - Len, Len); + } +} + +template +static void write_unsigned(raw_ostream &S, T N, size_t MinDigits, + IntegerStyle Style, bool IsNegative = false) { + // Output using 32-bit div/mod if possible. + if (N == static_cast(N)) + write_unsigned_impl(S, static_cast(N), MinDigits, Style, + IsNegative); + else + write_unsigned_impl(S, N, MinDigits, Style, IsNegative); +} + +template +static void write_signed(raw_ostream &S, T N, size_t MinDigits, + IntegerStyle Style) { + static_assert(std::is_signed::value, "Value is not signed!"); + + using UnsignedT = typename std::make_unsigned::type; + + if (N >= 0) { + write_unsigned(S, static_cast(N), MinDigits, Style); + return; + } + + UnsignedT UN = -(UnsignedT)N; + write_unsigned(S, UN, MinDigits, Style, true); +} + +void wpi::write_integer(raw_ostream &S, unsigned int N, size_t MinDigits, + IntegerStyle Style) { + write_unsigned(S, N, MinDigits, Style); +} + +void wpi::write_integer(raw_ostream &S, int N, size_t MinDigits, + IntegerStyle Style) { + write_signed(S, N, MinDigits, Style); +} + +void wpi::write_integer(raw_ostream &S, unsigned long N, size_t MinDigits, + IntegerStyle Style) { + write_unsigned(S, N, MinDigits, Style); +} + +void wpi::write_integer(raw_ostream &S, long N, size_t MinDigits, + IntegerStyle Style) { + write_signed(S, N, MinDigits, Style); +} + +void wpi::write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits, + IntegerStyle Style) { + write_unsigned(S, N, MinDigits, Style); +} + +void wpi::write_integer(raw_ostream &S, long long N, size_t MinDigits, + IntegerStyle Style) { + write_signed(S, N, MinDigits, Style); +} + +void wpi::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style, + Optional Width) { + const size_t kMaxWidth = 128u; + + size_t W = std::min(kMaxWidth, Width.getValueOr(0u)); + + unsigned Nibbles = (64 - countLeadingZeros(N) + 3) / 4; + bool Prefix = (Style == HexPrintStyle::PrefixLower || + Style == HexPrintStyle::PrefixUpper); + bool Upper = + (Style == HexPrintStyle::Upper || Style == HexPrintStyle::PrefixUpper); + unsigned PrefixChars = Prefix ? 2 : 0; + unsigned NumChars = + std::max(static_cast(W), std::max(1u, Nibbles) + PrefixChars); + + char NumberBuffer[kMaxWidth]; + ::memset(NumberBuffer, '0', wpi::array_lengthof(NumberBuffer)); + if (Prefix) + NumberBuffer[1] = 'x'; + char *EndPtr = NumberBuffer + NumChars; + char *CurPtr = EndPtr; + while (N) { + unsigned char x = static_cast(N) % 16; + *--CurPtr = hexdigit(x, !Upper); + N /= 16; + } + + S.write(NumberBuffer, NumChars); +} + +void wpi::write_double(raw_ostream &S, double N, FloatStyle Style, + Optional Precision) { + size_t Prec = Precision.getValueOr(getDefaultPrecision(Style)); + + if (std::isnan(N)) { + S << "nan"; + return; + } else if (std::isinf(N)) { + S << "INF"; + return; + } + + char Letter; + if (Style == FloatStyle::Exponent) + Letter = 'e'; + else if (Style == FloatStyle::ExponentUpper) + Letter = 'E'; + else + Letter = 'f'; + + SmallString<8> Spec; + wpi::raw_svector_ostream Out(Spec); + Out << "%." << Prec << Letter; + + if (Style == FloatStyle::Exponent || Style == FloatStyle::ExponentUpper) { +#ifdef _WIN32 +// On MSVCRT and compatible, output of %e is incompatible to Posix +// by default. Number of exponent digits should be at least 2. "%+03d" +// FIXME: Implement our formatter to here or Support/Format.h! +#if defined(__MINGW32__) + // FIXME: It should be generic to C++11. + if (N == 0.0 && std::signbit(N)) { + char NegativeZero[] = "-0.000000e+00"; + if (Style == FloatStyle::ExponentUpper) + NegativeZero[strlen(NegativeZero) - 4] = 'E'; + S << NegativeZero; + return; + } +#else + int fpcl = _fpclass(N); + + // negative zero + if (fpcl == _FPCLASS_NZ) { + char NegativeZero[] = "-0.000000e+00"; + if (Style == FloatStyle::ExponentUpper) + NegativeZero[strlen(NegativeZero) - 4] = 'E'; + S << NegativeZero; + return; + } +#endif + + char buf[32]; + unsigned len; + len = format(Spec.c_str(), N).snprint(buf, sizeof(buf)); + if (len <= sizeof(buf) - 2) { + if (len >= 5 && (buf[len - 5] == 'e' || buf[len - 5] == 'E') && + buf[len - 3] == '0') { + int cs = buf[len - 4]; + if (cs == '+' || cs == '-') { + int c1 = buf[len - 2]; + int c0 = buf[len - 1]; + if (isdigit(static_cast(c1)) && + isdigit(static_cast(c0))) { + // Trim leading '0': "...e+012" -> "...e+12\0" + buf[len - 3] = c1; + buf[len - 2] = c0; + buf[--len] = 0; + } + } + } + S << buf; + return; + } +#endif + } + + if (Style == FloatStyle::Percent) + N *= 100.0; + + char Buf[32]; + format(Spec.c_str(), N).snprint(Buf, sizeof(Buf)); + S << Buf; + if (Style == FloatStyle::Percent) + S << '%'; +} + +bool wpi::isPrefixedHexStyle(HexPrintStyle S) { + return (S == HexPrintStyle::PrefixLower || S == HexPrintStyle::PrefixUpper); +} + +size_t wpi::getDefaultPrecision(FloatStyle Style) { + switch (Style) { + case FloatStyle::Exponent: + case FloatStyle::ExponentUpper: + return 6; // Number of decimal places. + case FloatStyle::Fixed: + case FloatStyle::Percent: + return 2; // Number of decimal places. + } + LLVM_BUILTIN_UNREACHABLE; +} diff --git a/wpiutil/src/main/native/cpp/llvm/Path.cpp b/wpiutil/src/main/native/cpp/llvm/Path.cpp index 96e2c93b0a..12736b99be 100644 --- a/wpiutil/src/main/native/cpp/llvm/Path.cpp +++ b/wpiutil/src/main/native/cpp/llvm/Path.cpp @@ -30,16 +30,29 @@ using namespace wpi; namespace { using wpi::StringRef; using wpi::sys::path::is_separator; + using wpi::sys::path::Style; + inline Style real_style(Style style) { #ifdef _WIN32 - const char *separators = "\\/"; - const char preferred_separator = '\\'; + return (style == Style::posix) ? Style::posix : Style::windows; #else - const char separators = '/'; - const char preferred_separator = '/'; + return (style == Style::windows) ? Style::windows : Style::posix; #endif + } - StringRef find_first_component(StringRef path) { + inline const char *separators(Style style) { + if (real_style(style) == Style::windows) + return "\\/"; + return "/"; + } + + inline char preferred_separator(Style style) { + if (real_style(style) == Style::windows) + return '\\'; + return '/'; + } + + StringRef find_first_component(StringRef path, Style style) { // Look for this first component in the following order. // * empty (in this case we return an empty string) // * either C: or {//,\\}net. @@ -49,101 +62,94 @@ namespace { if (path.empty()) return path; -#ifdef _WIN32 - // C: - if (path.size() >= 2 && std::isalpha(static_cast(path[0])) && - path[1] == ':') - return path.substr(0, 2); -#endif + if (real_style(style) == Style::windows) { + // C: + if (path.size() >= 2 && + std::isalpha(static_cast(path[0])) && path[1] == ':') + return path.substr(0, 2); + } // //net - if ((path.size() > 2) && - is_separator(path[0]) && - path[0] == path[1] && - !is_separator(path[2])) { + if ((path.size() > 2) && is_separator(path[0], style) && + path[0] == path[1] && !is_separator(path[2], style)) { // Find the next directory separator. - size_t end = path.find_first_of(separators, 2); + size_t end = path.find_first_of(separators(style), 2); return path.substr(0, end); } // {/,\} - if (is_separator(path[0])) + if (is_separator(path[0], style)) return path.substr(0, 1); // * {file,directory}name - size_t end = path.find_first_of(separators); + size_t end = path.find_first_of(separators(style)); return path.substr(0, end); } - size_t filename_pos(StringRef str) { - if (str.size() == 2 && - is_separator(str[0]) && - str[0] == str[1]) - return 0; - - if (str.size() > 0 && is_separator(str[str.size() - 1])) + // Returns the first character of the filename in str. For paths ending in + // '/', it returns the position of the '/'. + size_t filename_pos(StringRef str, Style style) { + if (str.size() > 0 && is_separator(str[str.size() - 1], style)) return str.size() - 1; - size_t pos = str.find_last_of(separators, str.size() - 1); + size_t pos = str.find_last_of(separators(style), str.size() - 1); -#ifdef _WIN32 - if (pos == StringRef::npos) - pos = str.find_last_of(':', str.size() - 2); -#endif + if (real_style(style) == Style::windows) { + if (pos == StringRef::npos) + pos = str.find_last_of(':', str.size() - 2); + } - if (pos == StringRef::npos || - (pos == 1 && is_separator(str[0]))) + if (pos == StringRef::npos || (pos == 1 && is_separator(str[0], style))) return 0; return pos + 1; } - size_t root_dir_start(StringRef str) { + // Returns the position of the root directory in str. If there is no root + // directory in str, it returns StringRef::npos. + size_t root_dir_start(StringRef str, Style style) { // case "c:/" -#ifdef _WIN32 - if (str.size() > 2 && - str[1] == ':' && - is_separator(str[2])) - return 2; -#endif - - // case "//" - if (str.size() == 2 && - is_separator(str[0]) && - str[0] == str[1]) - return StringRef::npos; + if (real_style(style) == Style::windows) { + if (str.size() > 2 && str[1] == ':' && is_separator(str[2], style)) + return 2; + } // case "//net" - if (str.size() > 3 && - is_separator(str[0]) && - str[0] == str[1] && - !is_separator(str[2])) { - return str.find_first_of(separators, 2); + if (str.size() > 3 && is_separator(str[0], style) && str[0] == str[1] && + !is_separator(str[2], style)) { + return str.find_first_of(separators(style), 2); } // case "/" - if (str.size() > 0 && is_separator(str[0])) + if (str.size() > 0 && is_separator(str[0], style)) return 0; return StringRef::npos; } - size_t parent_path_end(StringRef path) { - size_t end_pos = filename_pos(path); + // Returns the position past the end of the "parent path" of path. The parent + // path will not end in '/', unless the parent is the root directory. If the + // path has no parent, 0 is returned. + size_t parent_path_end(StringRef path, Style style) { + size_t end_pos = filename_pos(path, style); - bool filename_was_sep = path.size() > 0 && is_separator(path[end_pos]); + bool filename_was_sep = + path.size() > 0 && is_separator(path[end_pos], style); - // Skip separators except for root dir. - size_t root_dir_pos = root_dir_start(path.substr(0, end_pos)); - - while(end_pos > 0 && - (end_pos - 1) != root_dir_pos && - is_separator(path[end_pos - 1])) + // Skip separators until we reach root dir (or the start of the string). + size_t root_dir_pos = root_dir_start(path, style); + while (end_pos > 0 && + (root_dir_pos == StringRef::npos || end_pos > root_dir_pos) && + is_separator(path[end_pos - 1], style)) --end_pos; - if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep) - return StringRef::npos; + if (end_pos == root_dir_pos && !filename_was_sep) { + // We've reached the root dir and the input path was *not* ending in a + // sequence of slashes. Include the root dir in the parent path. + return root_dir_pos + 1; + } + // Otherwise, just include before the last slash. return end_pos; } } // end unnamed namespace @@ -152,11 +158,12 @@ namespace wpi { namespace sys { namespace path { -const_iterator begin(StringRef path) { +const_iterator begin(StringRef path, Style style) { const_iterator i; i.Path = path; - i.Component = find_first_component(path); + i.Component = find_first_component(path, style); i.Position = 0; + i.S = style; return i; } @@ -181,32 +188,26 @@ const_iterator &const_iterator::operator++() { // Both POSIX and Windows treat paths that begin with exactly two separators // specially. - bool was_net = Component.size() > 2 && - is_separator(Component[0]) && - Component[1] == Component[0] && - !is_separator(Component[2]); + bool was_net = Component.size() > 2 && is_separator(Component[0], S) && + Component[1] == Component[0] && !is_separator(Component[2], S); // Handle separators. - if (is_separator(Path[Position])) { + if (is_separator(Path[Position], S)) { // Root dir. - if (was_net -#ifdef _WIN32 + if (was_net || // c:/ - || Component.endswith(":") -#endif - ) { + (real_style(S) == Style::windows && Component.endswith(":"))) { Component = Path.substr(Position, 1); return *this; } // Skip extra separators. - while (Position != Path.size() && - is_separator(Path[Position])) { + while (Position != Path.size() && is_separator(Path[Position], S)) { ++Position; } - // Treat trailing '/' as a '.'. - if (Position == Path.size()) { + // Treat trailing '/' as a '.', unless it is the root dir. + if (Position == Path.size() && Component != "/") { --Position; Component = "."; return *this; @@ -214,7 +215,7 @@ const_iterator &const_iterator::operator++() { } // Find next component. - size_t end_pos = Path.find_first_of(separators, Position); + size_t end_pos = Path.find_first_of(separators(S), Position); Component = Path.slice(Position, end_pos); return *this; @@ -228,10 +229,11 @@ ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const { return Position - RHS.Position; } -reverse_iterator rbegin(StringRef Path) { +reverse_iterator rbegin(StringRef Path, Style style) { reverse_iterator I; I.Path = Path; I.Position = Path.size(); + I.S = style; return ++I; } @@ -244,27 +246,25 @@ reverse_iterator rend(StringRef Path) { } reverse_iterator &reverse_iterator::operator++() { - // If we're at the end and the previous char was a '/', return '.' unless - // we are the root path. - size_t root_dir_pos = root_dir_start(Path); - if (Position == Path.size() && - Path.size() > root_dir_pos + 1 && - is_separator(Path[Position - 1])) { + size_t root_dir_pos = root_dir_start(Path, S); + + // Skip separators unless it's the root directory. + size_t end_pos = Position; + while (end_pos > 0 && (end_pos - 1) != root_dir_pos && + is_separator(Path[end_pos - 1], S)) + --end_pos; + + // Treat trailing '/' as a '.', unless it is the root dir. + if (Position == Path.size() && !Path.empty() && + is_separator(Path.back(), S) && + (root_dir_pos == StringRef::npos || end_pos - 1 > root_dir_pos)) { --Position; Component = "."; return *this; } - // Skip separators unless it's the root directory. - size_t end_pos = Position; - - while(end_pos > 0 && - (end_pos - 1) != root_dir_pos && - is_separator(Path[end_pos - 1])) - --end_pos; - // Find next separator. - size_t start_pos = filename_pos(Path.substr(0, end_pos)); + size_t start_pos = filename_pos(Path.substr(0, end_pos), S); Component = Path.slice(start_pos, end_pos); Position = start_pos; return *this; @@ -279,21 +279,15 @@ ptrdiff_t reverse_iterator::operator-(const reverse_iterator &RHS) const { return Position - RHS.Position; } -StringRef root_path(StringRef path) { - const_iterator b = begin(path), - pos = b, - e = end(path); +StringRef root_path(StringRef path, Style style) { + const_iterator b = begin(path, style), pos = b, e = end(path); if (b != e) { - bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0]; - bool has_drive = -#ifdef _WIN32 - b->endswith(":"); -#else - false; -#endif + bool has_net = + b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; + bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if (has_net || has_drive) { - if ((++pos != e) && is_separator((*pos)[0])) { + if ((++pos != e) && is_separator((*pos)[0], style)) { // {C:/,//net/}, so get the first two components. return path.substr(0, b->size() + pos->size()); } else { @@ -303,7 +297,7 @@ StringRef root_path(StringRef path) { } // POSIX style root directory. - if (is_separator((*b)[0])) { + if (is_separator((*b)[0], style)) { return *b; } } @@ -311,17 +305,12 @@ StringRef root_path(StringRef path) { return StringRef(); } -StringRef root_name(StringRef path) { - const_iterator b = begin(path), - e = end(path); +StringRef root_name(StringRef path, Style style) { + const_iterator b = begin(path, style), e = end(path); if (b != e) { - bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0]; - bool has_drive = -#ifdef _WIN32 - b->endswith(":"); -#else - false; -#endif + bool has_net = + b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; + bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if (has_net || has_drive) { // just {C:,//net}, return the first component. @@ -333,27 +322,21 @@ StringRef root_name(StringRef path) { return StringRef(); } -StringRef root_directory(StringRef path) { - const_iterator b = begin(path), - pos = b, - e = end(path); +StringRef root_directory(StringRef path, Style style) { + const_iterator b = begin(path, style), pos = b, e = end(path); if (b != e) { - bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0]; - bool has_drive = -#ifdef _WIN32 - b->endswith(":"); -#else - false; -#endif + bool has_net = + b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; + bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if ((has_net || has_drive) && // {C:,//net}, skip to the next component. - (++pos != e) && is_separator((*pos)[0])) { + (++pos != e) && is_separator((*pos)[0], style)) { return *pos; } // POSIX style root directory. - if (!has_net && is_separator((*b)[0])) { + if (!has_net && is_separator((*b)[0], style)) { return *b; } } @@ -362,15 +345,13 @@ StringRef root_directory(StringRef path) { return StringRef(); } -StringRef relative_path(StringRef path) { - StringRef root = root_path(path); +StringRef relative_path(StringRef path, Style style) { + StringRef root = root_path(path, style); return path.substr(root.size()); } -void append(SmallVectorImpl &path, const Twine &a, - const Twine &b, - const Twine &c, - const Twine &d) { +void append(SmallVectorImpl &path, Style style, const Twine &a, + const Twine &b, const Twine &c, const Twine &d) { SmallString<32> a_storage; SmallString<32> b_storage; SmallString<32> c_storage; @@ -383,13 +364,11 @@ void append(SmallVectorImpl &path, const Twine &a, if (!d.isTriviallyEmpty()) components.push_back(d.toStringRef(d_storage)); for (auto &component : components) { - bool path_has_sep = !path.empty() && is_separator(path[path.size() - 1]); - bool component_has_sep = !component.empty() && is_separator(component[0]); - bool is_root_name = has_root_name(component); - + bool path_has_sep = + !path.empty() && is_separator(path[path.size() - 1], style); if (path_has_sep) { // Strip separators from beginning of component. - size_t loc = component.find_first_not_of(separators); + size_t loc = component.find_first_not_of(separators(style)); StringRef c = component.substr(loc); // Append it. @@ -397,43 +376,52 @@ void append(SmallVectorImpl &path, const Twine &a, continue; } - if (!component_has_sep && !(path.empty() || is_root_name)) { + bool component_has_sep = + !component.empty() && is_separator(component[0], style); + if (!component_has_sep && + !(path.empty() || has_root_name(component, style))) { // Add a separator. - path.push_back(preferred_separator); + path.push_back(preferred_separator(style)); } path.append(component.begin(), component.end()); } } -void append(SmallVectorImpl &path, - const_iterator begin, const_iterator end) { - for (; begin != end; ++begin) - path::append(path, *begin); +void append(SmallVectorImpl &path, const Twine &a, const Twine &b, + const Twine &c, const Twine &d) { + append(path, Style::native, a, b, c, d); } -StringRef parent_path(StringRef path) { - size_t end_pos = parent_path_end(path); +void append(SmallVectorImpl &path, const_iterator begin, + const_iterator end, Style style) { + for (; begin != end; ++begin) + path::append(path, style, *begin); +} + +StringRef parent_path(StringRef path, Style style) { + size_t end_pos = parent_path_end(path, style); if (end_pos == StringRef::npos) return StringRef(); else return path.substr(0, end_pos); } -void remove_filename(SmallVectorImpl &path) { - size_t end_pos = parent_path_end(StringRef(path.begin(), path.size())); +void remove_filename(SmallVectorImpl &path, Style style) { + size_t end_pos = parent_path_end(StringRef(path.begin(), path.size()), style); if (end_pos != StringRef::npos) path.set_size(end_pos); } -void replace_extension(SmallVectorImpl &path, const Twine &extension) { +void replace_extension(SmallVectorImpl &path, const Twine &extension, + Style style) { StringRef p(path.begin(), path.size()); SmallString<32> ext_storage; StringRef ext = extension.toStringRef(ext_storage); // Erase existing extension. size_t pos = p.find_last_of('.'); - if (pos != StringRef::npos && pos >= filename_pos(p)) + if (pos != StringRef::npos && pos >= filename_pos(p, style)) path.set_size(pos); // Append '.' if needed. @@ -445,8 +433,8 @@ void replace_extension(SmallVectorImpl &path, const Twine &extension) { } void replace_path_prefix(SmallVectorImpl &Path, - const StringRef &OldPrefix, - const StringRef &NewPrefix) { + const StringRef &OldPrefix, const StringRef &NewPrefix, + Style style) { if (OldPrefix.empty() && NewPrefix.empty()) return; @@ -462,43 +450,58 @@ void replace_path_prefix(SmallVectorImpl &Path, StringRef RelPath = OrigPath.substr(OldPrefix.size()); SmallString<256> NewPath; - path::append(NewPath, NewPrefix); - path::append(NewPath, RelPath); + path::append(NewPath, style, NewPrefix); + path::append(NewPath, style, RelPath); Path.swap(NewPath); } -void native(const Twine &path, SmallVectorImpl &result) { +void native(const Twine &path, SmallVectorImpl &result, Style style) { assert((!path.isSingleStringRef() || path.getSingleStringRef().data() != result.data()) && "path and result are not allowed to overlap!"); // Clear result. result.clear(); path.toVector(result); - native(result); + native(result, style); } -void native(SmallVectorImpl &Path) { -#ifdef _WIN32 - std::replace(Path.begin(), Path.end(), '/', '\\'); -#else - for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) { - if (*PI == '\\') { - auto PN = PI + 1; - if (PN < PE && *PN == '\\') - ++PI; // increment once, the for loop will move over the escaped slash - else - *PI = '/'; +void native(SmallVectorImpl &Path, Style style) { + if (Path.empty()) + return; + if (real_style(style) == Style::windows) { + std::replace(Path.begin(), Path.end(), '/', '\\'); + if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) { + SmallString<128> PathHome; + home_directory(PathHome); + PathHome.append(Path.begin() + 1, Path.end()); + Path = PathHome; + } + } else { + for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) { + if (*PI == '\\') { + auto PN = PI + 1; + if (PN < PE && *PN == '\\') + ++PI; // increment once, the for loop will move over the escaped slash + else + *PI = '/'; + } } } -#endif } -StringRef filename(StringRef path) { - return *rbegin(path); +std::string convert_to_slash(StringRef path, Style style) { + if (real_style(style) != Style::windows) + return path; + + std::string s = path.str(); + std::replace(s.begin(), s.end(), '\\', '/'); + return s; } -StringRef stem(StringRef path) { - StringRef fname = filename(path); +StringRef filename(StringRef path, Style style) { return *rbegin(path, style); } + +StringRef stem(StringRef path, Style style) { + StringRef fname = filename(path, style); size_t pos = fname.find_last_of('.'); if (pos == StringRef::npos) return fname; @@ -510,8 +513,8 @@ StringRef stem(StringRef path) { return fname.substr(0, pos); } -StringRef extension(StringRef path) { - StringRef fname = filename(path); +StringRef extension(StringRef path, Style style) { + StringRef fname = filename(path, style); size_t pos = fname.find_last_of('.'); if (pos == StringRef::npos) return StringRef(); @@ -523,132 +526,134 @@ StringRef extension(StringRef path) { return fname.substr(pos); } -bool is_separator(char value) { - switch(value) { -#ifdef _WIN32 - case '\\': // fall through -#endif - case '/': return true; - default: return false; - } +bool is_separator(char value, Style style) { + if (value == '/') + return true; + if (real_style(style) == Style::windows) + return value == '\\'; + return false; } -static const char preferred_separator_string[] = { preferred_separator, '\0' }; - -StringRef get_separator() { - return preferred_separator_string; +StringRef get_separator(Style style) { + if (real_style(style) == Style::windows) + return "\\"; + return "/"; } -bool has_root_name(const Twine &path) { +bool has_root_name(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !root_name(p).empty(); + return !root_name(p, style).empty(); } -bool has_root_directory(const Twine &path) { +bool has_root_directory(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !root_directory(p).empty(); + return !root_directory(p, style).empty(); } -bool has_root_path(const Twine &path) { +bool has_root_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !root_path(p).empty(); + return !root_path(p, style).empty(); } -bool has_relative_path(const Twine &path) { +bool has_relative_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !relative_path(p).empty(); + return !relative_path(p, style).empty(); } -bool has_filename(const Twine &path) { +bool has_filename(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !filename(p).empty(); + return !filename(p, style).empty(); } -bool has_parent_path(const Twine &path) { +bool has_parent_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !parent_path(p).empty(); + return !parent_path(p, style).empty(); } -bool has_stem(const Twine &path) { +bool has_stem(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !stem(p).empty(); + return !stem(p, style).empty(); } -bool has_extension(const Twine &path) { +bool has_extension(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !extension(p).empty(); + return !extension(p, style).empty(); } -bool is_absolute(const Twine &path) { +bool is_absolute(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - bool rootDir = has_root_directory(p), -#ifdef _WIN32 - rootName = has_root_name(p); -#else - rootName = true; -#endif + bool rootDir = has_root_directory(p, style); + bool rootName = + (real_style(style) != Style::windows) || has_root_name(p, style); return rootDir && rootName; } -bool is_relative(const Twine &path) { return !is_absolute(path); } +bool is_relative(const Twine &path, Style style) { + return !is_absolute(path, style); +} -StringRef remove_leading_dotslash(StringRef Path) { +StringRef remove_leading_dotslash(StringRef Path, Style style) { // Remove leading "./" (or ".//" or "././" etc.) - while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1])) { + while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1], style)) { Path = Path.substr(2); - while (Path.size() > 0 && is_separator(Path[0])) + while (Path.size() > 0 && is_separator(Path[0], style)) Path = Path.substr(1); } return Path; } -static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot) { +static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot, + Style style) { SmallVector components; // Skip the root path, then look for traversal in the components. - StringRef rel = path::relative_path(path); - for (StringRef C : wpi::make_range(path::begin(rel), path::end(rel))) { + StringRef rel = path::relative_path(path, style); + for (StringRef C : + wpi::make_range(path::begin(rel, style), path::end(rel))) { if (C == ".") continue; - if (remove_dot_dot) { - if (C == "..") { - if (!components.empty()) - components.pop_back(); + // Leading ".." will remain in the path unless it's at the root. + if (remove_dot_dot && C == "..") { + if (!components.empty() && components.back() != "..") { + components.pop_back(); continue; } + if (path::is_absolute(path, style)) + continue; } components.push_back(C); } - SmallString<256> buffer = path::root_path(path); + SmallString<256> buffer = path::root_path(path, style); for (StringRef C : components) - path::append(buffer, C); + path::append(buffer, style, C); return buffer; } -bool remove_dots(SmallVectorImpl &path, bool remove_dot_dot) { +bool remove_dots(SmallVectorImpl &path, bool remove_dot_dot, + Style style) { StringRef p(path.data(), path.size()); - SmallString<256> result = remove_dots(p, remove_dot_dot); + SmallString<256> result = remove_dots(p, remove_dot_dot, style); if (result == path) return false; @@ -674,12 +679,9 @@ static std::error_code make_absolute(const Twine ¤t_directory, bool use_current_directory) { StringRef p(path.data(), path.size()); - bool rootDirectory = path::has_root_directory(p), -#ifdef _WIN32 - rootName = path::has_root_name(p); -#else - rootName = true; -#endif + bool rootDirectory = path::has_root_directory(p); + bool rootName = + (real_style(Style::native) != Style::windows) || path::has_root_name(p); // Already absolute. if (rootName && rootDirectory) @@ -736,15 +738,22 @@ std::error_code make_absolute(SmallVectorImpl &path) { return make_absolute(Twine(), path, false); } -bool exists(file_status status) { +bool exists(const basic_file_status &status) { return status_known(status) && status.type() != file_type::file_not_found; } -bool status_known(file_status s) { +bool status_known(const basic_file_status &s) { return s.type() != file_type::status_error; } -bool is_directory(file_status status) { +file_type get_file_type(const Twine &Path, bool Follow) { + file_status st; + if (status(Path, st, Follow)) + return file_type::status_error; + return st.type(); +} + +bool is_directory(const basic_file_status &status) { return status.type() == file_type::directory_file; } @@ -756,7 +765,7 @@ std::error_code is_directory(const Twine &path, bool &result) { return std::error_code(); } -bool is_regular_file(file_status status) { +bool is_regular_file(const basic_file_status &status) { return status.type() == file_type::regular_file; } @@ -768,7 +777,19 @@ std::error_code is_regular_file(const Twine &path, bool &result) { return std::error_code(); } -bool is_other(file_status status) { +bool is_symlink_file(const basic_file_status &status) { + return status.type() == file_type::symlink_file; +} + +std::error_code is_symlink_file(const Twine &path, bool &result) { + file_status st; + if (std::error_code ec = status(path, st, false)) + return ec; + result = is_symlink_file(st); + return std::error_code(); +} + +bool is_other(const basic_file_status &status) { return exists(status) && !is_regular_file(status) && !is_directory(status); @@ -782,15 +803,20 @@ std::error_code is_other(const Twine &Path, bool &Result) { return std::error_code(); } -void directory_entry::replace_filename(const Twine &filename, file_status st) { +void directory_entry::replace_filename(const Twine &filename, + basic_file_status st) { SmallString<128> path = path::parent_path(Path); path::append(path, filename); Path = path.str(); Status = st; } -std::error_code directory_entry::status(file_status &result) const { - return fs::status(Path, result); +ErrorOr getPermissions(const Twine &Path) { + file_status Status; + if (std::error_code EC = status(Path, Status)) + return EC; + + return Status.permissions(); } } // end namespace fs diff --git a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp index 6439263dc1..f91b6ebe8a 100644 --- a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp +++ b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp @@ -15,7 +15,9 @@ #include "wpi/SmallPtrSet.h" #include "wpi/DenseMapInfo.h" #include "wpi/MathExtras.h" +#include "wpi/memory.h" #include +#include #include using namespace wpi; @@ -30,8 +32,7 @@ void SmallPtrSetImplBase::shrink_and_clear() { NumNonEmpty = NumTombstones = 0; // Install the new array. Clear all the buckets to empty. - CurArray = (const void**)malloc(sizeof(void*) * CurArraySize); - assert(CurArray && "Failed to allocate memory?"); + CurArray = (const void**)CheckedMalloc(sizeof(void*) * CurArraySize); memset(CurArray, -1, CurArraySize*sizeof(void*)); } @@ -60,38 +61,13 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) { return std::make_pair(Bucket, true); } -bool SmallPtrSetImplBase::erase_imp(const void * Ptr) { - if (isSmall()) { - // Check to see if it is in the set. - for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty; APtr != E; - ++APtr) - if (*APtr == Ptr) { - // If it is in the set, replace this element. - *APtr = getTombstoneMarker(); - ++NumTombstones; - return true; - } - - return false; - } - - // Okay, we know we have space. Find a hash bucket. - void **Bucket = const_cast(FindBucketFor(Ptr)); - if (*Bucket != Ptr) return false; // Not in the set? - - // Set this as a tombstone. - *Bucket = getTombstoneMarker(); - ++NumTombstones; - return true; -} - const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const { unsigned Bucket = DenseMapInfo::getHashValue(Ptr) & (CurArraySize-1); unsigned ArraySize = CurArraySize; unsigned ProbeAmt = 1; const void *const *Array = CurArray; const void *const *Tombstone = nullptr; - while (1) { + 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. @@ -120,8 +96,7 @@ void SmallPtrSetImplBase::Grow(unsigned NewSize) { bool WasSmall = isSmall(); // Install the new array. Clear all the buckets to empty. - CurArray = (const void**)malloc(sizeof(void*) * NewSize); - assert(CurArray && "Failed to allocate memory?"); + CurArray = (const void**) CheckedMalloc(sizeof(void*) * NewSize); CurArraySize = NewSize; memset(CurArray, -1, NewSize*sizeof(void*)); @@ -148,8 +123,7 @@ SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage, CurArray = SmallArray; // Otherwise, allocate new heap space (unless we were the same size) } else { - CurArray = (const void**)malloc(sizeof(void*) * that.CurArraySize); - assert(CurArray && "Failed to allocate memory?"); + CurArray = (const void**)CheckedMalloc(sizeof(void*) * that.CurArraySize); } // Copy over the that array. diff --git a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp index 8e242167d1..faa5ba7713 100644 --- a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp +++ b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "wpi/SmallVector.h" +#include "wpi/memory.h" using namespace wpi; /// grow_pod - This is an implementation of the grow() method which only works @@ -25,15 +26,14 @@ void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSizeInBytes, void *NewElts; if (BeginX == FirstEl) { - NewElts = malloc(NewCapacityInBytes); + NewElts = CheckedMalloc(NewCapacityInBytes); // Copy the elements over. No need to run dtors on PODs. memcpy(NewElts, this->BeginX, CurSizeBytes); } else { // If this wasn't grown from the inline copy, grow the allocated space. - NewElts = realloc(this->BeginX, NewCapacityInBytes); + NewElts = CheckedRealloc(this->BeginX, NewCapacityInBytes); } - assert(NewElts && "Out of memory"); this->EndX = (char*)NewElts+CurSizeBytes; this->BeginX = NewElts; diff --git a/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp b/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp index 9c7d9db649..41c3305a8a 100644 --- a/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp +++ b/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp @@ -13,6 +13,7 @@ #include "wpi/StringExtras.h" #include "wpi/SmallVector.h" +#include "wpi/raw_ostream.h" using namespace wpi; /// StrInStrNoCase - Portable version of strcasestr. Locates the first @@ -56,3 +57,18 @@ void wpi::SplitString(StringRef Source, S = getToken(S.second, Delimiters); } } + +void wpi::PrintEscapedString(StringRef Name, raw_ostream &Out) { + for (unsigned i = 0, e = Name.size(); i != e; ++i) { + unsigned char C = Name[i]; + if (isprint(C) && C != '\\' && C != '"') + Out << C; + else + Out << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F); + } +} + +void wpi::printLowerCase(StringRef String, raw_ostream &Out) { + for (const char C : String) + Out << toLower(C); +} diff --git a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp index 5c750eb55e..ea91dbb98c 100644 --- a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp +++ b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp @@ -12,10 +12,12 @@ //===----------------------------------------------------------------------===// #include "wpi/StringMap.h" -#include "wpi/MathExtras.h" #include "wpi/StringExtras.h" #include "wpi/Compiler.h" +#include "wpi/MathExtras.h" +#include "wpi/memory.h" #include + using namespace wpi; /// Returns the number of buckets to allocate to ensure that the DenseMap can @@ -51,20 +53,23 @@ StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) { void StringMapImpl::init(unsigned InitSize) { assert((InitSize & (InitSize-1)) == 0 && "Init Size must be a power of 2 or zero!"); - NumBuckets = InitSize ? InitSize : 16; + + unsigned NewNumBuckets = InitSize ? InitSize : 16; NumItems = 0; NumTombstones = 0; - TheTable = (StringMapEntryBase **)calloc(NumBuckets+1, - sizeof(StringMapEntryBase **) + - sizeof(unsigned)); + TheTable = static_cast( + CheckedCalloc(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 @@ -82,7 +87,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) { unsigned ProbeAmt = 1; int FirstTombstone = -1; - while (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)) { @@ -136,7 +141,7 @@ int StringMapImpl::FindKey(StringRef Key) const { unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); unsigned ProbeAmt = 1; - while (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)) @@ -192,8 +197,6 @@ StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) { return Result; } - - /// RehashTable - Grow the table, redistributing values into the buckets with /// the appropriate mod-of-hashtable-size. unsigned StringMapImpl::RehashTable(unsigned BucketNo) { @@ -215,9 +218,9 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { unsigned NewBucketNo = BucketNo; // Allocate one extra bucket which will always be non-empty. This allows the // iterators to stop at end. - StringMapEntryBase **NewTableArray = - (StringMapEntryBase **)calloc(NewSize+1, sizeof(StringMapEntryBase *) + - sizeof(unsigned)); + auto NewTableArray = static_cast( + CheckedCalloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned))); + unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1); NewTableArray[NewSize] = (StringMapEntryBase*)2; diff --git a/wpiutil/src/main/native/cpp/llvm/StringRef.cpp b/wpiutil/src/main/native/cpp/llvm/StringRef.cpp index b785aea50f..ea44bead79 100644 --- a/wpiutil/src/main/native/cpp/llvm/StringRef.cpp +++ b/wpiutil/src/main/native/cpp/llvm/StringRef.cpp @@ -9,9 +9,11 @@ #include "wpi/StringRef.h" #include "wpi/Hashing.h" +#include "wpi/StringExtras.h" #include "wpi/SmallVector.h" #include #include +#include using namespace wpi; @@ -20,28 +22,12 @@ using namespace wpi; const size_t StringRef::npos; #endif -static char ascii_tolower(char x) noexcept { - if (x >= 'A' && x <= 'Z') - return x - 'A' + 'a'; - return x; -} - -static char ascii_toupper(char x) noexcept { - if (x >= 'a' && x <= 'z') - return x - 'a' + 'A'; - return x; -} - -static bool ascii_isdigit(char x) noexcept { - return x >= '0' && x <= '9'; -} - // 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 = ascii_tolower(LHS[I]); - unsigned char RHC = ascii_tolower(RHS[I]); + unsigned char LHC = toLower(LHS[I]); + unsigned char RHC = toLower(RHS[I]); if (LHC != RHC) return LHC < RHC ? -1 : 1; } @@ -50,36 +36,41 @@ static int ascii_strncasecmp(const char *LHS, const char *RHS, size_t Length) no /// compare_lower - Compare strings, ignoring case. int StringRef::compare_lower(StringRef RHS) const noexcept { - if (int Res = ascii_strncasecmp(Data, RHS.Data, std::min(size(), RHS.size()))) + if (int Res = ascii_strncasecmp(Data, RHS.Data, std::min(Length, RHS.Length))) return Res; - if (size() == RHS.size()) + if (Length == RHS.Length) return 0; - return size() < RHS.size() ? -1 : 1; + return Length < RHS.Length ? -1 : 1; } /// Check if this string starts with the given \p Prefix, ignoring case. bool StringRef::startswith_lower(StringRef Prefix) const noexcept { - return size() >= Prefix.size() && - ascii_strncasecmp(Data, Prefix.Data, Prefix.size()) == 0; + return Length >= Prefix.Length && + ascii_strncasecmp(Data, Prefix.Data, Prefix.Length) == 0; } /// Check if this string ends with the given \p Suffix, ignoring case. bool StringRef::endswith_lower(StringRef Suffix) const noexcept { - return size() >= Suffix.size() && - ascii_strncasecmp(end() - Suffix.size(), Suffix.Data, Suffix.size()) == 0; + return Length >= Suffix.Length && + ascii_strncasecmp(end() - Suffix.Length, Suffix.Data, Suffix.Length) == 0; +} + +size_t StringRef::find_lower(char C, size_t From) const noexcept { + char L = toLower(C); + return find_if([L](char D) { return toLower(D) == L; }, From); } /// compare_numeric - Compare strings, handle embedded numbers. int StringRef::compare_numeric(StringRef RHS) const noexcept { - for (size_t I = 0, E = std::min(size(), RHS.size()); I != E; ++I) { + for (size_t I = 0, E = std::min(Length, RHS.Length); I != E; ++I) { // Check for sequences of digits. - if (ascii_isdigit(Data[I]) && ascii_isdigit(RHS.Data[I])) { + if (isDigit(Data[I]) && isDigit(RHS.Data[I])) { // The longer sequence of numbers is considered larger. // This doesn't really handle prefixed zeros well. size_t J; for (J = I + 1; J != E + 1; ++J) { - bool ld = J < size() && ascii_isdigit(Data[J]); - bool rd = J < RHS.size() && ascii_isdigit(RHS.Data[J]); + bool ld = J < Length && isDigit(Data[J]); + bool rd = J < RHS.Length && isDigit(RHS.Data[J]); if (ld != rd) return rd ? -1 : 1; if (!rd) @@ -95,9 +86,9 @@ int StringRef::compare_numeric(StringRef RHS) const noexcept { if (Data[I] != RHS.Data[I]) return (unsigned char)Data[I] < (unsigned char)RHS.Data[I] ? -1 : 1; } - if (size() == RHS.size()) + if (Length == RHS.Length) return 0; - return size() < RHS.size() ? -1 : 1; + return Length < RHS.Length ? -1 : 1; } //===----------------------------------------------------------------------===// @@ -107,7 +98,7 @@ int StringRef::compare_numeric(StringRef RHS) const noexcept { std::string StringRef::lower() const { std::string Result(size(), char()); for (size_type i = 0, e = size(); i != e; ++i) { - Result[i] = ascii_tolower(Data[i]); + Result[i] = toLower(Data[i]); } return Result; } @@ -115,25 +106,11 @@ std::string StringRef::lower() const { std::string StringRef::upper() const { std::string Result(size(), char()); for (size_type i = 0, e = size(); i != e; ++i) { - Result[i] = ascii_toupper(Data[i]); + Result[i] = toUpper(Data[i]); } return Result; } -const char *StringRef::c_str(wpi::SmallVectorImpl& buf) const { - if (is_null_terminated()) { - // If null terminated, return data directly - return data(); - } else { - // If not null terminated, use SmallVectorImpl to store data - // copy data, and return a known null terminated string - buf.clear(); - buf.append(begin(), end()); - buf.push_back(0); - return buf.begin(); - } -} - //===----------------------------------------------------------------------===// // String Searching //===----------------------------------------------------------------------===// @@ -144,19 +121,23 @@ const char *StringRef::c_str(wpi::SmallVectorImpl& buf) const { /// \return - The index of the first occurrence of \arg Str, or npos if not /// found. size_t StringRef::find(StringRef Str, size_t From) const noexcept { - if (From > size()) + if (From > Length) return npos; + const char *Start = Data + From; + size_t Size = Length - From; + const char *Needle = Str.data(); size_t N = Str.size(); if (N == 0) return From; - - size_t Size = size() - From; if (Size < N) return npos; + if (N == 1) { + const char *Ptr = (const char *)::memchr(Start, Needle[0], Size); + return Ptr == nullptr ? npos : Ptr - Data; + } - const char *Start = Data + From; const char *Stop = Start + (Size - N + 1); // For short haystacks or unsupported needles fall back to the naive algorithm @@ -176,25 +157,49 @@ size_t StringRef::find(StringRef Str, size_t From) const noexcept { BadCharSkip[(uint8_t)Str[i]] = N-1-i; do { - if (std::memcmp(Start, Needle, N) == 0) - return Start - Data; + uint8_t Last = Start[N - 1]; + if (LLVM_UNLIKELY(Last == (uint8_t)Needle[N - 1])) + if (std::memcmp(Start, Needle, N - 1) == 0) + return Start - Data; // Otherwise skip the appropriate number of bytes. - Start += BadCharSkip[(uint8_t)Start[N-1]]; + Start += BadCharSkip[Last]; } while (Start < Stop); return npos; } +size_t StringRef::find_lower(StringRef Str, size_t From) const noexcept { + StringRef This = substr(From); + while (This.size() >= Str.size()) { + if (This.startswith_lower(Str)) + return From; + This = This.drop_front(); + ++From; + } + return npos; +} + +size_t StringRef::rfind_lower(char C, size_t From) const noexcept { + From = std::min(From, Length); + size_t i = From; + while (i != 0) { + --i; + if (toLower(Data[i]) == toLower(C)) + return i; + } + return npos; +} + /// rfind - Search for the last string \arg Str in the string. /// /// \return - The index of the last occurrence of \arg Str, or npos if not /// found. size_t StringRef::rfind(StringRef Str) const noexcept { size_t N = Str.size(); - if (N > size()) + if (N > Length) return npos; - for (size_t i = size() - N + 1, e = 0; i != e;) { + for (size_t i = Length - N + 1, e = 0; i != e;) { --i; if (substr(i, N).equals(Str)) return i; @@ -202,6 +207,18 @@ size_t StringRef::rfind(StringRef Str) const noexcept { return npos; } +size_t StringRef::rfind_lower(StringRef Str) const noexcept { + size_t N = Str.size(); + if (N > Length) + return npos; + for (size_t i = Length - N + 1, e = 0; i != e;) { + --i; + if (substr(i, N).equals_lower(Str)) + return i; + } + return npos; +} + /// find_first_of - Find the first character in the string that is in \arg /// Chars, or npos if not found. /// @@ -212,7 +229,7 @@ StringRef::size_type StringRef::find_first_of(StringRef Chars, for (size_type i = 0; i != Chars.size(); ++i) CharBits.set((unsigned char)Chars[i]); - for (size_type i = std::min(From, size()), e = size(); i != e; ++i) + for (size_type i = std::min(From, Length), e = Length; i != e; ++i) if (CharBits.test((unsigned char)Data[i])) return i; return npos; @@ -221,7 +238,7 @@ StringRef::size_type StringRef::find_first_of(StringRef Chars, /// find_first_not_of - Find the first character in the string that is not /// \arg C or npos if not found. StringRef::size_type StringRef::find_first_not_of(char C, size_t From) const noexcept { - for (size_type i = std::min(From, size()), e = size(); i != e; ++i) + for (size_type i = std::min(From, Length), e = Length; i != e; ++i) if (Data[i] != C) return i; return npos; @@ -237,7 +254,7 @@ StringRef::size_type StringRef::find_first_not_of(StringRef Chars, for (size_type i = 0; i != Chars.size(); ++i) CharBits.set((unsigned char)Chars[i]); - for (size_type i = std::min(From, size()), e = size(); i != e; ++i) + for (size_type i = std::min(From, Length), e = Length; i != e; ++i) if (!CharBits.test((unsigned char)Data[i])) return i; return npos; @@ -253,7 +270,7 @@ StringRef::size_type StringRef::find_last_of(StringRef Chars, for (size_type i = 0; i != Chars.size(); ++i) CharBits.set((unsigned char)Chars[i]); - for (size_type i = std::min(From, size()) - 1, e = -1; i != e; --i) + for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i) if (CharBits.test((unsigned char)Data[i])) return i; return npos; @@ -262,7 +279,7 @@ StringRef::size_type StringRef::find_last_of(StringRef Chars, /// find_last_not_of - Find the last character in the string that is not /// \arg C, or npos if not found. StringRef::size_type StringRef::find_last_not_of(char C, size_t From) const noexcept { - for (size_type i = std::min(From, size()) - 1, e = -1; i != e; --i) + for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i) if (Data[i] != C) return i; return npos; @@ -278,7 +295,7 @@ StringRef::size_type StringRef::find_last_not_of(StringRef Chars, for (size_type i = 0, e = Chars.size(); i != e; ++i) CharBits.set((unsigned char)Chars[i]); - for (size_type i = std::min(From, size()) - 1, e = -1; i != e; --i) + for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i) if (!CharBits.test((unsigned char)Data[i])) return i; return npos; @@ -346,15 +363,18 @@ void StringRef::split(SmallVectorImpl &A, char Separator, size_t StringRef::count(StringRef Str) const noexcept { size_t Count = 0; size_t N = Str.size(); - if (N > size()) + if (N > Length) return 0; - for (size_t i = 0, e = size() - N + 1; i != e; ++i) + for (size_t i = 0, e = Length - N + 1; i != e; ++i) if (substr(i, N).equals(Str)) ++Count; return Count; } static unsigned GetAutoSenseRadix(StringRef &Str) noexcept { + if (Str.empty()) + return 10; + if (Str.startswith("0x") || Str.startswith("0X")) { Str = Str.substr(2); return 16; @@ -370,17 +390,16 @@ static unsigned GetAutoSenseRadix(StringRef &Str) noexcept { return 8; } - if (Str.startswith("0")) + if (Str[0] == '0' && Str.size() > 1 && isDigit(Str[1])) { + Str = Str.substr(1); return 8; + } return 10; } - -/// GetAsUnsignedInteger - Workhorse method that converts a integer character -/// sequence of radix up to 36 to an unsigned long long value. -bool wpi::getAsUnsignedInteger(StringRef Str, unsigned Radix, - unsigned long long &Result) noexcept { +bool wpi::consumeUnsignedInteger(StringRef &Str, unsigned Radix, + unsigned long long &Result) noexcept { // Autosense radix if not specified. if (Radix == 0) Radix = GetAutoSenseRadix(Str); @@ -389,44 +408,51 @@ bool wpi::getAsUnsignedInteger(StringRef Str, unsigned Radix, if (Str.empty()) return true; // Parse all the bytes of the string given this radix. Watch for overflow. + StringRef Str2 = Str; Result = 0; - while (!Str.empty()) { + while (!Str2.empty()) { unsigned CharVal; - if (Str[0] >= '0' && Str[0] <= '9') - CharVal = Str[0]-'0'; - else if (Str[0] >= 'a' && Str[0] <= 'z') - CharVal = Str[0]-'a'+10; - else if (Str[0] >= 'A' && Str[0] <= 'Z') - CharVal = Str[0]-'A'+10; + 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 - return true; + break; - // If the parsed value is larger than the integer radix, the string is - // invalid. + // If the parsed value is larger than the integer radix, we cannot + // consume any more characters. if (CharVal >= Radix) - return true; + break; // Add in this character. unsigned long long PrevResult = Result; - Result = Result*Radix+CharVal; + Result = Result * Radix + CharVal; // Check for overflow by shifting back and seeing if bits were lost. - if (Result/Radix < PrevResult) + if (Result / Radix < PrevResult) return true; - Str = Str.substr(1); + Str2 = Str2.substr(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::getAsSignedInteger(StringRef Str, unsigned Radix, - long long &Result) noexcept { +bool wpi::consumeSignedInteger(StringRef &Str, unsigned Radix, + long long &Result) noexcept { unsigned long long ULLVal; // Handle positive strings first. if (Str.empty() || Str.front() != '-') { - if (getAsUnsignedInteger(Str, Radix, ULLVal) || + if (consumeUnsignedInteger(Str, Radix, ULLVal) || // Check for value so large it overflows a signed value. (long long)ULLVal < 0) return true; @@ -435,17 +461,46 @@ bool wpi::getAsSignedInteger(StringRef Str, unsigned Radix, } // Get the positive part of the value. - if (getAsUnsignedInteger(Str.substr(1), Radix, ULLVal) || + StringRef Str2 = Str.drop_front(1); + if (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. (long long)-ULLVal > 0) return true; + Str = Str2; Result = -ULLVal; return false; } +/// GetAsUnsignedInteger - Workhorse method that converts a integer character +/// sequence of radix up to 36 to an unsigned long long value. +bool wpi::getAsUnsignedInteger(StringRef Str, unsigned Radix, + unsigned long long &Result) noexcept { + if (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::getAsSignedInteger(StringRef Str, unsigned Radix, + long long &Result) noexcept { + if (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(); +} + +std::ostream &wpi::operator<<(std::ostream &os, StringRef string) { + os.write(string.data(), string.size()); + return os; +} + // Implementation of StringRef hashing. hash_code wpi::hash_value(StringRef S) { return hash_combine_range(S.begin(), S.end()); diff --git a/wpiutil/src/main/native/cpp/llvm/Twine.cpp b/wpiutil/src/main/native/cpp/llvm/Twine.cpp index 3802306f71..ac705ff136 100644 --- a/wpiutil/src/main/native/cpp/llvm/Twine.cpp +++ b/wpiutil/src/main/native/cpp/llvm/Twine.cpp @@ -160,10 +160,10 @@ void Twine::printRepr(raw_ostream &OS) const { OS << ")"; } -void Twine::dump() const { +LLVM_DUMP_METHOD void Twine::dump() const { print(errs()); } -void Twine::dumpRepr() const { +LLVM_DUMP_METHOD void Twine::dumpRepr() const { printRepr(errs()); } diff --git a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc index d9b4a34e77..a1ea5a1cbb 100644 --- a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc +++ b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc @@ -145,21 +145,20 @@ static std::error_code fillStatus(int StatRet, const struct stat &Status, else if (S_ISLNK(Status.st_mode)) Type = file_type::symlink_file; - perms Perms = static_cast(Status.st_mode); - Result = - file_status(Type, Perms, Status.st_dev, Status.st_ino, Status.st_atime, - Status.st_mtime, Status.st_uid, Status.st_gid, - Status.st_size); + perms Perms = static_cast(Status.st_mode) & all_perms; + Result = file_status(Type, Perms, Status.st_dev, Status.st_nlink, + Status.st_ino, Status.st_atime, Status.st_mtime, + Status.st_uid, Status.st_gid, Status.st_size); return std::error_code(); } -std::error_code status(const Twine &Path, file_status &Result) { +std::error_code status(const Twine &Path, file_status &Result, bool Follow) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); struct stat Status; - int StatRet = ::lstat(P.begin(), &Status); + int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status); return fillStatus(StatRet, Status, Result); } @@ -170,7 +169,8 @@ std::error_code status(int FD, file_status &Result) { } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path){ + StringRef path, + bool follow_symlinks) { SmallString<128> path_null(path); DIR *directory = ::opendir(path_null.c_str()); if (!directory) @@ -179,7 +179,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, it.IterationHandle = reinterpret_cast(directory); // Add something for replace_filename to replace. path::append(path_null, "."); - it.CurrentEntry = directory_entry(path_null.str()); + it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks); return directory_iterator_increment(it); } @@ -208,6 +208,13 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { return std::error_code(); } +ErrorOr directory_entry::status() const { + file_status s; + if (auto EC = fs::status(Path, s, FollowSymlinks)) + return EC; + return s; +} + #if !defined(F_GETPATH) static bool hasProcSelfFD() { // If we have a /proc filesystem mounted, we can quickly establish the @@ -260,6 +267,10 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, int OpenFlags = O_CREAT; +#ifdef O_CLOEXEC + OpenFlags |= O_CLOEXEC; +#endif + if (Flags & F_RW) OpenFlags |= O_RDWR; else @@ -267,7 +278,7 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, if (Flags & F_Append) OpenFlags |= O_APPEND; - else + else if (!(Flags & F_NoTrunc)) OpenFlags |= O_TRUNC; if (Flags & F_Excl) diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc index b7b037159e..3a170ebc4e 100644 --- a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc +++ b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc @@ -26,6 +26,7 @@ // These two headers must be included last, and make sure shlobj is required // after Windows.h to make sure it picks up our definition of _WIN32_WINNT #include "WindowsSupport.h" +#include #include #undef max @@ -38,6 +39,7 @@ using namespace wpi; using wpi::sys::windows::UTF8ToUTF16; +using wpi::sys::windows::CurCPToUTF16; using wpi::sys::windows::UTF16ToUTF8; using wpi::sys::path::widenPath; @@ -87,13 +89,16 @@ std::error_code widenPath(const Twine &Path8, return EC; FullPath.append(CurPath); } - // Traverse the requested path, canonicalizing . and .. as we go (because - // the \\?\ prefix is documented to treat them as real components). - // The iterators don't report separators and append() always attaches - // preferred_separator so we don't need to call native() on the result. + // Traverse the requested path, canonicalizing . and .. (because the \\?\ + // prefix is documented to treat them as real components). Ignore + // separators, which can be returned from the iterator if the path has a + // drive name. We don't need to call native() on the result since append() + // always attaches preferred_separator. for (wpi::sys::path::const_iterator I = wpi::sys::path::begin(Path8Str), E = wpi::sys::path::end(Path8Str); I != E; ++I) { + if (I->size() == 1 && is_separator((*I)[0])) + continue; if (I->size() == 1 && *I == ".") continue; if (I->size() == 2 && *I == "..") @@ -214,6 +219,15 @@ static bool isReservedName(StringRef path) { return false; } +static file_type file_type_from_attrs(DWORD Attrs) { + return (Attrs & FILE_ATTRIBUTE_DIRECTORY) ? file_type::directory_file + : file_type::regular_file; +} + +static perms perms_from_attrs(DWORD Attrs) { + return (Attrs & FILE_ATTRIBUTE_READONLY) ? (all_read | all_exe) : all_all; +} + static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { if (FileHandle == INVALID_HANDLE_VALUE) goto handle_status_error; @@ -243,19 +257,14 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { if (!::GetFileInformationByHandle(FileHandle, &Info)) goto handle_status_error; - { - file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - ? file_type::directory_file - : file_type::regular_file; - Result = - file_status(Type, Info.ftLastAccessTime.dwHighDateTime, - Info.ftLastAccessTime.dwLowDateTime, - Info.ftLastWriteTime.dwHighDateTime, - Info.ftLastWriteTime.dwLowDateTime, - Info.dwVolumeSerialNumber, Info.nFileSizeHigh, - Info.nFileSizeLow, Info.nFileIndexHigh, Info.nFileIndexLow); - return std::error_code(); - } + Result = file_status( + file_type_from_attrs(Info.dwFileAttributes), + perms_from_attrs(Info.dwFileAttributes), Info.nNumberOfLinks, + Info.ftLastAccessTime.dwHighDateTime, Info.ftLastAccessTime.dwLowDateTime, + Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime, + Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow, + Info.nFileIndexHigh, Info.nFileIndexLow); + return std::error_code(); handle_status_error: DWORD LastError = ::GetLastError(); @@ -269,7 +278,7 @@ handle_status_error: return mapWindowsError(LastError); } -std::error_code status(const Twine &path, file_status &result) { +std::error_code status(const Twine &path, file_status &result, bool Follow) { SmallString<128> path_storage; SmallVector path_utf16; @@ -286,28 +295,19 @@ std::error_code status(const Twine &path, file_status &result) { if (attr == INVALID_FILE_ATTRIBUTES) return getStatus(INVALID_HANDLE_VALUE, result); + DWORD Flags = FILE_FLAG_BACKUP_SEMANTICS; // Handle reparse points. - if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { - ScopedFileHandle h( - ::CreateFileW(path_utf16.begin(), - 0, // Attributes only. - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - 0)); - if (!h) - return getStatus(INVALID_HANDLE_VALUE, result); - } + if (!Follow && (attr & FILE_ATTRIBUTE_REPARSE_POINT)) + Flags |= FILE_FLAG_OPEN_REPARSE_POINT; ScopedFileHandle h( ::CreateFileW(path_utf16.begin(), 0, // Attributes only. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); - if (!h) - return getStatus(INVALID_HANDLE_VALUE, result); + NULL, OPEN_EXISTING, Flags, 0)); + if (!h) + return getStatus(INVALID_HANDLE_VALUE, result); - return getStatus(h, result); + return getStatus(h, result); } std::error_code status(int FD, file_status &Result) { @@ -315,8 +315,19 @@ std::error_code status(int FD, file_status &Result) { return getStatus(FileHandle, Result); } +static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) { + return basic_file_status(file_type_from_attrs(FindData->dwFileAttributes), + perms_from_attrs(FindData->dwFileAttributes), + FindData->ftLastAccessTime.dwHighDateTime, + FindData->ftLastAccessTime.dwLowDateTime, + FindData->ftLastWriteTime.dwHighDateTime, + FindData->ftLastWriteTime.dwLowDateTime, + FindData->nFileSizeHigh, FindData->nFileSizeLow); +} + std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path){ + StringRef path, + bool follow_symlinks) { SmallVector path_utf16; if (std::error_code ec = widenPath(path, path_utf16)) @@ -334,7 +345,9 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, // Get the first directory entry. WIN32_FIND_DATAW FirstFind; - ScopedFindHandle FindHandle(::FindFirstFileW(c_str(path_utf16), &FirstFind)); + ScopedFindHandle FindHandle(::FindFirstFileExW( + c_str(path_utf16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch, + NULL, FIND_FIRST_EX_LARGE_FETCH)); if (!FindHandle) return mapWindowsError(::GetLastError()); @@ -361,7 +374,8 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, it.IterationHandle = intptr_t(FindHandle.take()); SmallString<128> directory_entry_path(path); path::append(directory_entry_path, directory_entry_name_utf8); - it.CurrentEntry = directory_entry(directory_entry_path); + it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks, + status_from_find_data(&FirstFind)); return std::error_code(); } @@ -397,12 +411,18 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { directory_entry_path_utf8)) return ec; - it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8)); + it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8), + status_from_find_data(&FindData)); return std::error_code(); } +ErrorOr directory_entry::status() const { + return Status; +} + std::error_code openFileForRead(const Twine &Name, int &ResultFD, SmallVectorImpl *RealPath) { + ResultFD = -1; SmallVector PathUTF16; if (std::error_code EC = widenPath(Name, PathUTF16)) @@ -425,8 +445,8 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, return EC; } - int FD = ::_open_osfhandle(intptr_t(H), 0); - if (FD == -1) { + ResultFD = ::_open_osfhandle(intptr_t(H), 0); + if (ResultFD == -1) { ::CloseHandle(H); return mapWindowsError(ERROR_INVALID_HANDLE); } @@ -447,7 +467,6 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, } } - ResultFD = FD; return std::error_code(); } @@ -457,6 +476,7 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, assert((!(Flags & F_Excl) || !(Flags & F_Append)) && "Cannot specify both 'excl' and 'append' file creation flags!"); + ResultFD = -1; SmallVector PathUTF16; if (std::error_code EC = widenPath(Name, PathUTF16)) @@ -465,18 +485,24 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, DWORD CreationDisposition; if (Flags & F_Excl) CreationDisposition = CREATE_NEW; - else if (Flags & F_Append) + else if ((Flags & F_Append) || (Flags & F_NoTrunc)) CreationDisposition = OPEN_ALWAYS; else CreationDisposition = CREATE_ALWAYS; DWORD Access = GENERIC_WRITE; + DWORD Attributes = FILE_ATTRIBUTE_NORMAL; if (Flags & F_RW) Access |= GENERIC_READ; + if (Flags & F_Delete) { + Access |= DELETE; + Attributes |= FILE_FLAG_DELETE_ON_CLOSE; + } - HANDLE H = ::CreateFileW(PathUTF16.begin(), Access, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE H = + ::CreateFileW(PathUTF16.data(), Access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, CreationDisposition, Attributes, NULL); if (H == INVALID_HANDLE_VALUE) { DWORD LastError = ::GetLastError(); @@ -498,13 +524,12 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, if (Flags & F_Text) OpenFlags |= _O_TEXT; - int FD = ::_open_osfhandle(intptr_t(H), OpenFlags); - if (FD == -1) { + ResultFD = ::_open_osfhandle(intptr_t(H), OpenFlags); + if (ResultFD == -1) { ::CloseHandle(H); return mapWindowsError(ERROR_INVALID_HANDLE); } - ResultFD = FD; return std::error_code(); } @@ -577,23 +602,26 @@ void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { } // end namespace path namespace windows { -std::error_code UTF8ToUTF16(wpi::StringRef utf8, - wpi::SmallVectorImpl &utf16) { - if (!utf8.empty()) { - int len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(), - utf8.size(), utf16.begin(), 0); +std::error_code CodePageToUTF16(unsigned codepage, + wpi::StringRef original, + wpi::SmallVectorImpl &utf16) { + if (!original.empty()) { + int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), + original.size(), utf16.begin(), 0); - if (len == 0) + if (len == 0) { return mapWindowsError(::GetLastError()); + } utf16.reserve(len + 1); utf16.set_size(len); - len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(), - utf8.size(), utf16.begin(), utf16.size()); + len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), + original.size(), utf16.begin(), utf16.size()); - if (len == 0) + if (len == 0) { return mapWindowsError(::GetLastError()); + } } // Make utf16 null terminated. @@ -603,32 +631,44 @@ std::error_code UTF8ToUTF16(wpi::StringRef utf8, return std::error_code(); } +std::error_code UTF8ToUTF16(wpi::StringRef utf8, + wpi::SmallVectorImpl &utf16) { + return CodePageToUTF16(CP_UTF8, utf8, utf16); +} + +std::error_code CurCPToUTF16(wpi::StringRef curcp, + wpi::SmallVectorImpl &utf16) { + return CodePageToUTF16(CP_ACP, curcp, utf16); +} + static std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16, size_t utf16_len, - wpi::SmallVectorImpl &utf8) { + wpi::SmallVectorImpl &converted) { if (utf16_len) { // Get length. - int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, utf8.begin(), + int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(), 0, NULL, NULL); - if (len == 0) + if (len == 0) { return mapWindowsError(::GetLastError()); + } - utf8.reserve(len); - utf8.set_size(len); + converted.reserve(len); + converted.set_size(len); // Now do the actual conversion. - len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, utf8.data(), - utf8.size(), NULL, NULL); + len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(), + converted.size(), NULL, NULL); - if (len == 0) + if (len == 0) { return mapWindowsError(::GetLastError()); + } } - // Make utf8 null terminated. - utf8.push_back(0); - utf8.pop_back(); + // Make the new string null terminated. + converted.push_back(0); + converted.pop_back(); return std::error_code(); } @@ -639,8 +679,8 @@ std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, } std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len, - wpi::SmallVectorImpl &utf8) { - return UTF16ToCodePage(CP_ACP, utf16, utf16_len, utf8); + wpi::SmallVectorImpl &curcp) { + return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp); } } // end namespace windows diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h index 249a8814c6..2f78a4e343 100644 --- a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h +++ b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h @@ -39,10 +39,10 @@ #include "wpi/StringRef.h" #include "wpi/Twine.h" #include "wpi/Compiler.h" -#include -#include #include #include +#include +#include /// 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 @@ -199,6 +199,8 @@ std::error_code widenPath(const Twine &Path8, namespace windows { std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl &utf16); +/// Convert to UTF16 from the current code page used in the system +std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl &utf16); std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, SmallVectorImpl &utf8); /// Convert from UTF16 to the current code page used in the system diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp index 01ab4eb3e9..83d5eeedc9 100644 --- a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp +++ b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp @@ -12,15 +12,21 @@ //===----------------------------------------------------------------------===// #include "wpi/raw_ostream.h" +#include "wpi/STLExtras.h" #include "wpi/SmallString.h" #include "wpi/SmallVector.h" #include "wpi/StringExtras.h" #include "wpi/Compiler.h" +#include "wpi/FileSystem.h" #include "wpi/Format.h" #include "wpi/MathExtras.h" +#include "wpi/NativeFormatting.h" #include "wpi/WindowsError.h" +#include #include #include +#include +#include #include #include @@ -49,7 +55,7 @@ #endif #endif -#if defined(_WIN32) +#ifdef _WIN32 #include "Windows/WindowsSupport.h" #endif @@ -102,73 +108,28 @@ void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size, } raw_ostream &raw_ostream::operator<<(unsigned long N) { - // Zero is a special case. - if (N == 0) - return *this << '0'; - - char NumberBuffer[20]; - char *EndPtr = NumberBuffer+sizeof(NumberBuffer); - char *CurPtr = EndPtr; - - while (N) { - *--CurPtr = '0' + char(N % 10); - N /= 10; - } - return write(CurPtr, EndPtr-CurPtr); + write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); + return *this; } raw_ostream &raw_ostream::operator<<(long N) { - if (N < 0) { - *this << '-'; - // Avoid undefined behavior on LONG_MIN with a cast. - N = -(unsigned long)N; - } - - return this->operator<<(static_cast(N)); + write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); + return *this; } raw_ostream &raw_ostream::operator<<(unsigned long long N) { - // Output using 32-bit div/mod when possible. - if (N == static_cast(N)) - return this->operator<<(static_cast(N)); - - char NumberBuffer[20]; - char *EndPtr = std::end(NumberBuffer); - char *CurPtr = EndPtr; - - while (N) { - *--CurPtr = '0' + char(N % 10); - N /= 10; - } - return write(CurPtr, EndPtr-CurPtr); + write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); + return *this; } raw_ostream &raw_ostream::operator<<(long long N) { - if (N < 0) { - *this << '-'; - // Avoid undefined behavior on INT64_MIN with a cast. - N = -(unsigned long long)N; - } - - return this->operator<<(static_cast(N)); + write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); + return *this; } raw_ostream &raw_ostream::write_hex(unsigned long long N) { - // Zero is a special case. - if (N == 0) - return *this << '0'; - - char NumberBuffer[16]; - char *EndPtr = std::end(NumberBuffer); - char *CurPtr = EndPtr; - - while (N) { - unsigned char x = static_cast(N) % 16; - *--CurPtr = hexdigit(x, /*LowerCase*/true); - N /= 16; - } - - return write(CurPtr, EndPtr-CurPtr); + wpi::write_hex(*this, N, HexPrintStyle::Lower); + return *this; } raw_ostream &raw_ostream::write_escaped(StringRef Str, @@ -212,54 +173,15 @@ raw_ostream &raw_ostream::write_escaped(StringRef Str, } raw_ostream &raw_ostream::operator<<(const void *P) { - *this << '0' << 'x'; - - return write_hex((uintptr_t) P); + wpi::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower); + return *this; } raw_ostream &raw_ostream::operator<<(double N) { -#ifdef _WIN32 - // On MSVCRT and compatible, output of %e is incompatible to Posix - // by default. Number of exponent digits should be at least 2. "%+03d" - // FIXME: Implement our formatter to here or Support/Format.h! -#if defined(__MINGW32__) - // FIXME: It should be generic to C++11. - if (N == 0.0 && std::signbit(N)) - return *this << "-0.000000e+00"; -#else - int fpcl = _fpclass(N); - - // negative zero - if (fpcl == _FPCLASS_NZ) - return *this << "-0.000000e+00"; -#endif - - char buf[16]; - unsigned len; - len = format("%e", N).snprint(buf, sizeof(buf)); - if (len <= sizeof(buf) - 2) { - if (len >= 5 && buf[len - 5] == 'e' && buf[len - 3] == '0') { - int cs = buf[len - 4]; - if (cs == '+' || cs == '-') { - int c1 = buf[len - 2]; - int c0 = buf[len - 1]; - if (isdigit(static_cast(c1)) && - isdigit(static_cast(c0))) { - // Trim leading '0': "...e+012" -> "...e+12\0" - buf[len - 3] = c1; - buf[len - 2] = c0; - buf[--len] = 0; - } - } - } - return this->operator<<(buf); - } -#endif - return this->operator<<(format("%e", N)); + wpi::write_double(*this, N, FloatStyle::Exponent); + return *this; } - - void raw_ostream::flush_nonempty() { assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty."); size_t Length = OutBufCur - OutBufStart; @@ -336,10 +258,10 @@ void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) { // Handle short strings specially, memcpy isn't very good at very short // strings. switch (Size) { - case 4: OutBufCur[3] = Ptr[3]; // FALL THROUGH - case 3: OutBufCur[2] = Ptr[2]; // FALL THROUGH - case 2: OutBufCur[1] = Ptr[1]; // FALL THROUGH - case 1: OutBufCur[0] = Ptr[0]; // FALL THROUGH + 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); @@ -374,7 +296,7 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) { // space. Iterate until we win. SmallVector V; - while (1) { + while (true) { V.resize(NextBufferSize); // Try formatting into the SmallVector. @@ -391,82 +313,160 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) { } raw_ostream &raw_ostream::operator<<(const FormattedString &FS) { - unsigned Len = FS.Str.size(); - int PadAmount = FS.Width - Len; - if (FS.RightJustify && (PadAmount > 0)) - this->indent(PadAmount); - this->operator<<(FS.Str); - if (!FS.RightJustify && (PadAmount > 0)) + if (FS.Str.size() >= FS.Width || FS.Justify == FormattedString::JustifyNone) { + this->operator<<(FS.Str); + return *this; + } + const size_t Difference = FS.Width - FS.Str.size(); + switch (FS.Justify) { + case FormattedString::JustifyLeft: + this->operator<<(FS.Str); + this->indent(Difference); + break; + case FormattedString::JustifyRight: + this->indent(Difference); + this->operator<<(FS.Str); + break; + case FormattedString::JustifyCenter: { + int PadAmount = Difference / 2; this->indent(PadAmount); + this->operator<<(FS.Str); + this->indent(Difference - PadAmount); + break; + } + default: + assert(false && "Bad Justification"); + } return *this; } raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) { if (FN.Hex) { - unsigned Nibbles = (64 - countLeadingZeros(FN.HexValue)+3)/4; - unsigned PrefixChars = FN.HexPrefix ? 2 : 0; - unsigned Width = std::max(FN.Width, Nibbles + PrefixChars); - - char NumberBuffer[20] = "0x0000000000000000"; - if (!FN.HexPrefix) - NumberBuffer[1] = '0'; - char *EndPtr = NumberBuffer+Width; - char *CurPtr = EndPtr; - unsigned long long N = FN.HexValue; - while (N) { - unsigned char x = static_cast(N) % 16; - *--CurPtr = hexdigit(x, !FN.Upper); - N /= 16; - } - - return write(NumberBuffer, Width); + HexPrintStyle Style; + if (FN.Upper && FN.HexPrefix) + Style = HexPrintStyle::PrefixUpper; + else if (FN.Upper && !FN.HexPrefix) + Style = HexPrintStyle::Upper; + else if (!FN.Upper && FN.HexPrefix) + Style = HexPrintStyle::PrefixLower; + else + Style = HexPrintStyle::Lower; + wpi::write_hex(*this, FN.HexValue, Style, FN.Width); } else { - // Zero is a special case. - if (FN.DecValue == 0) { - this->indent(FN.Width-1); - return *this << '0'; - } - char NumberBuffer[32]; - char *EndPtr = NumberBuffer+sizeof(NumberBuffer); - char *CurPtr = EndPtr; - bool Neg = (FN.DecValue < 0); - uint64_t N = Neg ? -static_cast(FN.DecValue) : FN.DecValue; - while (N) { - *--CurPtr = '0' + char(N % 10); - N /= 10; - } - int Len = EndPtr - CurPtr; - int Pad = FN.Width - Len; - if (Neg) - --Pad; - if (Pad > 0) - this->indent(Pad); - if (Neg) - *this << '-'; - return write(CurPtr, Len); - } -} - - -/// indent - Insert 'NumSpaces' spaces. -raw_ostream &raw_ostream::indent(unsigned NumSpaces) { - static const char Spaces[] = " " - " " - " "; - - // Usually the indentation is small, handle it with a fastpath. - if (NumSpaces < array_lengthof(Spaces)) - return write(Spaces, NumSpaces); - - while (NumSpaces) { - unsigned NumToWrite = std::min(NumSpaces, - (unsigned)array_lengthof(Spaces)-1); - write(Spaces, NumToWrite); - NumSpaces -= NumToWrite; + wpi::SmallString<16> Buffer; + wpi::raw_svector_ostream Stream(Buffer); + wpi::write_integer(Stream, FN.DecValue, 0, IntegerStyle::Integer); + if (Buffer.size() < FN.Width) + indent(FN.Width - Buffer.size()); + (*this) << Buffer; } return *this; } +raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) { + if (FB.Bytes.empty()) + return *this; + + size_t LineIndex = 0; + auto Bytes = FB.Bytes; + const size_t Size = Bytes.size(); + HexPrintStyle HPS = FB.Upper ? HexPrintStyle::Upper : HexPrintStyle::Lower; + uint64_t OffsetWidth = 0; + if (FB.FirstByteOffset.hasValue()) { + // Figure out how many nibbles are needed to print the largest offset + // represented by this data set, so that we can align the offset field + // to the right width. + size_t Lines = Size / FB.NumPerLine; + uint64_t MaxOffset = *FB.FirstByteOffset + Lines * FB.NumPerLine; + unsigned Power = 0; + if (MaxOffset > 0) + Power = wpi::Log2_64_Ceil(MaxOffset); + OffsetWidth = std::max(4, wpi::alignTo(Power, 4) / 4); + } + + // The width of a block of data including all spaces for group separators. + unsigned NumByteGroups = + alignTo(FB.NumPerLine, FB.ByteGroupSize) / FB.ByteGroupSize; + unsigned BlockCharWidth = FB.NumPerLine * 2 + NumByteGroups - 1; + + while (!Bytes.empty()) { + indent(FB.IndentLevel); + + if (FB.FirstByteOffset.hasValue()) { + uint64_t Offset = FB.FirstByteOffset.getValue(); + wpi::write_hex(*this, Offset + LineIndex, HPS, OffsetWidth); + *this << ": "; + } + + auto Line = Bytes.take_front(FB.NumPerLine); + + size_t CharsPrinted = 0; + // Print the hex bytes for this line in groups + for (size_t I = 0; I < Line.size(); ++I, CharsPrinted += 2) { + if (I && (I % FB.ByteGroupSize) == 0) { + ++CharsPrinted; + *this << " "; + } + wpi::write_hex(*this, Line[I], HPS, 2); + } + + if (FB.ASCII) { + // Print any spaces needed for any bytes that we didn't print on this + // line so that the ASCII bytes are correctly aligned. + assert(BlockCharWidth >= CharsPrinted); + indent(BlockCharWidth - CharsPrinted + 2); + *this << "|"; + + // Print the ASCII char values for each byte on this line + for (uint8_t Byte : Line) { + if (isprint(Byte)) + *this << static_cast(Byte); + else + *this << '.'; + } + *this << '|'; + } + + Bytes = Bytes.drop_front(Line.size()); + LineIndex += Line.size(); + if (LineIndex < Size) + *this << '\n'; + } + return *this; +} + +template +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() {} //===----------------------------------------------------------------------===// // Formatted Output @@ -483,8 +483,7 @@ void format_object_base::home() { static int getFD(StringRef Filename, std::error_code &EC, sys::fs::OpenFlags Flags) { // Handle "-" as stdout. Note that when we do this, we consider ourself - // the owner of stdout. This means that we can do things like close the - // file descriptor when we're done and set the "binary" flag globally. + // the owner of stdout and may set the "binary" flag globally based on Flags. if (Filename == "-") { EC = std::error_code(); // If user requested binary then put stdout into binary mode if @@ -498,12 +497,10 @@ static int getFD(StringRef Filename, std::error_code &EC, } int FD; - EC = sys::fs::openFileForWrite(Filename, FD, Flags); if (EC) return -1; - EC = std::error_code(); return FD; } @@ -514,13 +511,21 @@ raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, /// 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) - : raw_pwrite_stream(unbuffered), FD(fd), ShouldClose(shouldClose), - Error(false) { + : raw_pwrite_stream(unbuffered), FD(fd), ShouldClose(shouldClose) { if (FD < 0 ) { ShouldClose = false; return; } + // 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; + // Get the starting position. off_t loc = ::lseek(FD, 0, SEEK_CUR); #ifdef _WIN32 @@ -539,7 +544,7 @@ raw_fd_ostream::~raw_fd_ostream() { if (FD >= 0) { flush(); if (ShouldClose && ::close(FD) < 0) - error_detected(); + error_detected(std::error_code(errno, std::generic_category())); } #ifdef __MINGW32__ @@ -551,25 +556,29 @@ raw_fd_ostream::~raw_fd_ostream() { #endif } - void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) { assert(FD >= 0 && "File already closed."); pos += Size; -#ifndef _WIN32 - bool ShouldWriteInChunks = false; -#else + // The maximum write size is limited to SSIZE_MAX because a write + // greater than SSIZE_MAX is implementation-defined in POSIX. + // Since SSIZE_MAX is not portable, we use SIZE_MAX >> 1 instead. + size_t MaxWriteSize = SIZE_MAX >> 1; + +#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; +#elif defined(_WIN32) // Writing a large size of output to Windows console returns ENOMEM. It seems // that, prior to Windows 8, WriteFile() is redirecting to WriteConsole(), and // the latter has a size limit (66000 bytes or less, depending on heap usage). - bool ShouldWriteInChunks = !!::_isatty(FD) && !RunningWindows8OrGreater(); + if (::_isatty(FD) && !RunningWindows8OrGreater()) + MaxWriteSize = 32767; #endif do { - size_t ChunkSize = Size; - if (ChunkSize > 32767 && ShouldWriteInChunks) - ChunkSize = 32767; - + size_t ChunkSize = std::min(Size, MaxWriteSize); #ifdef _WIN32 int ret = ::_write(FD, Ptr, ChunkSize); #else @@ -593,7 +602,7 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) { continue; // Otherwise it's a non-recoverable error. Note it and quit. - error_detected(); + error_detected(std::error_code(errno, std::generic_category())); break; } @@ -610,16 +619,20 @@ void raw_fd_ostream::close() { ShouldClose = false; flush(); if (::close(FD) < 0) - error_detected(); + 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(); + error_detected(std::error_code(errno, std::generic_category())); return pos; } @@ -651,6 +664,8 @@ size_t raw_fd_ostream::preferred_buffer_size() const { #endif } +void raw_fd_ostream::anchor() {} + //===----------------------------------------------------------------------===// // outs(), errs(), nulls() //===----------------------------------------------------------------------===// @@ -658,10 +673,7 @@ size_t raw_fd_ostream::preferred_buffer_size() const { /// outs() - This returns a reference to a raw_ostream for standard output. /// Use it like: outs() << "foo" << "bar"; raw_ostream &wpi::outs() { - // Set buffer settings to model stdout behavior. Delete the file descriptor - // when the program exits, forcing error detection. This means that if you - // ever call outs(), you can't open another raw_fd_ostream on stdout, as we'll - // close stdout twice and print an error the second time. + // Set buffer settings to model stdout behavior. std::error_code EC; static raw_fd_ostream S("-", EC, sys::fs::F_None); assert(!EC); @@ -682,7 +694,6 @@ raw_ostream &wpi::nulls() { return S; } - //===----------------------------------------------------------------------===// // raw_string_ostream //===----------------------------------------------------------------------===// @@ -778,3 +789,5 @@ uint64_t raw_null_ostream::current_pos() const { void raw_null_ostream::pwrite_impl(const char * /*Ptr*/, size_t /*Size*/, uint64_t /*Offset*/) {} + +void raw_pwrite_stream::anchor() {} diff --git a/wpiutil/src/main/native/cpp/memory.cpp b/wpiutil/src/main/native/cpp/memory.cpp new file mode 100644 index 0000000000..54e55c95b9 --- /dev/null +++ b/wpiutil/src/main/native/cpp/memory.cpp @@ -0,0 +1,43 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 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. */ +/*----------------------------------------------------------------------------*/ + +#include "wpi/memory.h" + +#include + +#include "wpi/raw_ostream.h" + +namespace wpi { + +void* CheckedCalloc(size_t num, size_t size) { + void* p = std::calloc(num, size); + if (!p) { + errs() << "FATAL: failed to allocate " << (num * size) << " bytes\n"; + std::terminate(); + } + return p; +} + +void* CheckedMalloc(size_t size) { + void* p = std::malloc(size == 0 ? 1 : size); + if (!p) { + errs() << "FATAL: failed to allocate " << size << " bytes\n"; + std::terminate(); + } + return p; +} + +void* CheckedRealloc(void* ptr, size_t size) { + void* p = std::realloc(ptr, size == 0 ? 1 : size); + if (!p) { + errs() << "FATAL: failed to allocate " << size << " bytes\n"; + std::terminate(); + } + return p; +} + +} // namespace wpi diff --git a/wpiutil/src/main/native/cpp/sha1.cpp b/wpiutil/src/main/native/cpp/sha1.cpp index e36eb45b53..c6236164f6 100644 --- a/wpiutil/src/main/native/cpp/sha1.cpp +++ b/wpiutil/src/main/native/cpp/sha1.cpp @@ -100,8 +100,8 @@ static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w, * Hash a single 512-bit block. This is the core of the algorithm. */ -static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS], - uint64_t& transforms) { +static void do_transform(uint32_t digest[], uint32_t block[BLOCK_INTS], + uint64_t& transforms) { /* Copy digest[] to working vars */ uint32_t a = digest[0]; uint32_t b = digest[1]; @@ -227,7 +227,7 @@ void SHA1::Update(raw_istream& is) { } uint32_t block[BLOCK_INTS]; buffer_to_block(buffer, block); - transform(digest, block, transforms); + do_transform(digest, block, transforms); buf_size = 0; } } @@ -251,7 +251,7 @@ static void finalize(uint32_t digest[], unsigned char* buffer, size_t& buf_size, buffer_to_block(buffer, block); if (buf_size > BLOCK_BYTES - 8) { - transform(digest, block, transforms); + do_transform(digest, block, transforms); for (size_t i = 0; i < BLOCK_INTS - 2; i++) { block[i] = 0; } @@ -260,7 +260,7 @@ static void finalize(uint32_t digest[], unsigned char* buffer, size_t& buf_size, /* Append total_bits, split this uint64_t into two uint32_t */ block[BLOCK_INTS - 1] = total_bits; block[BLOCK_INTS - 2] = (total_bits >> 32); - transform(digest, block, transforms); + do_transform(digest, block, transforms); /* Hex string */ static const char* const LUT = "0123456789abcdef"; diff --git a/wpiutil/src/main/native/include/wpi/AlignOf.h b/wpiutil/src/main/native/include/wpi/AlignOf.h index 9c81c42b85..4ce34cd02d 100644 --- a/wpiutil/src/main/native/include/wpi/AlignOf.h +++ b/wpiutil/src/main/native/include/wpi/AlignOf.h @@ -7,110 +7,24 @@ // //===----------------------------------------------------------------------===// // -// This file defines the AlignOf function that computes alignments for -// arbitrary types. +// This file defines the AlignedCharArray and AlignedCharArrayUnion classes. // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_ALIGNOF_H -#define LLVM_SUPPORT_ALIGNOF_H +#ifndef WPIUTIL_WPI_ALIGNOF_H +#define WPIUTIL_WPI_ALIGNOF_H #include "wpi/Compiler.h" #include -#include namespace wpi { -namespace detail { - -// For everything other than an abstract class we can calulate alignment by -// building a class with a single character and a member of the given type. -template ::value> -struct AlignmentCalcImpl { - char x; -#if defined(_MSC_VER) -// Disables "structure was padded due to __declspec(align())" warnings that are -// generated by any class using AlignOf with a manually specified alignment. -// Although the warning is disabled in the LLVM project we need this pragma -// as AlignOf.h is a published support header that's available for use -// out-of-tree, and we would like that to compile cleanly at /W4. -#pragma warning(suppress : 4324) -#endif - T t; -private: - AlignmentCalcImpl() = delete; -}; - -// Abstract base class helper, this will have the minimal alignment and size -// for any abstract class. We don't even define its destructor because this -// type should never be used in a way that requires it. -struct AlignmentCalcImplBase { - virtual ~AlignmentCalcImplBase() = 0; -}; - -// When we have an abstract class type, specialize the alignment computation -// engine to create another abstract class that derives from both an empty -// abstract base class and the provided type. This has the same effect as the -// above except that it handles the fact that we can't actually create a member -// of type T. -template -struct AlignmentCalcImpl : AlignmentCalcImplBase, T { - ~AlignmentCalcImpl() override = 0; -}; - -} // End detail namespace. - -/// AlignOf - A templated class that contains an enum value representing -/// the alignment of the template argument. For example, -/// AlignOf::Alignment represents the alignment of type "int". The -/// alignment calculated is the minimum alignment, and not necessarily -/// the "desired" alignment returned by GCC's __alignof__ (for example). Note -/// that because the alignment is an enum value, it can be used as a -/// compile-time constant (e.g., for template instantiation). -template -struct AlignOf { -#ifndef _MSC_VER - // Avoid warnings from GCC like: - // comparison between 'enum wpi::AlignOf::' and 'enum - // wpi::AlignOf::' [-Wenum-compare] - // by using constexpr instead of enum. - // (except on MSVC, since it doesn't support constexpr yet). - static constexpr unsigned Alignment = static_cast( - sizeof(detail::AlignmentCalcImpl) - sizeof(T)); -#else - enum { - Alignment = static_cast( - sizeof(::wpi::detail::AlignmentCalcImpl) - sizeof(T)) - }; -#endif - enum { Alignment_GreaterEqual_2Bytes = Alignment >= 2 ? 1 : 0 }; - enum { Alignment_GreaterEqual_4Bytes = Alignment >= 4 ? 1 : 0 }; - enum { Alignment_GreaterEqual_8Bytes = Alignment >= 8 ? 1 : 0 }; - enum { Alignment_GreaterEqual_16Bytes = Alignment >= 16 ? 1 : 0 }; - - enum { Alignment_LessEqual_2Bytes = Alignment <= 2 ? 1 : 0 }; - enum { Alignment_LessEqual_4Bytes = Alignment <= 4 ? 1 : 0 }; - enum { Alignment_LessEqual_8Bytes = Alignment <= 8 ? 1 : 0 }; - enum { Alignment_LessEqual_16Bytes = Alignment <= 16 ? 1 : 0 }; -}; - -#ifndef _MSC_VER -template constexpr unsigned AlignOf::Alignment; -#endif - -/// alignOf - A templated function that returns the minimum alignment of -/// of a type. This provides no extra functionality beyond the AlignOf -/// class besides some cosmetic cleanliness. Example usage: -/// alignOf() returns the alignment of an int. -template -inline unsigned alignOf() { return AlignOf::Alignment; } - /// \struct AlignedCharArray -/// \brief Helper for building an aligned character array type. +/// Helper for building an aligned character array type. /// /// This template is used to explicitly build up a collection of aligned /// character array types. We have to build these up using a macro and explicit -/// specialization to cope with old versions of MSVC and GCC where only an +/// specialization to cope with MSVC (at least till 2015) where only an /// integer literal can be used to specify an alignment constraint. Once built /// up here, we can then begin to indirect between these using normal C++ /// template parameters. @@ -118,41 +32,14 @@ inline unsigned alignOf() { return AlignOf::Alignment; } // MSVC requires special handling here. #ifndef _MSC_VER -#if __has_feature(cxx_alignas) template struct AlignedCharArray { - alignas(Alignment) char buffer[Size]; + LLVM_ALIGNAS(Alignment) char buffer[Size]; }; -#elif defined(__GNUC__) || defined(__IBM_ATTRIBUTES) -/// \brief Create a type with an aligned char buffer. -template -struct AlignedCharArray; - -#define LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(x) \ - template \ - struct AlignedCharArray { \ - __attribute__((aligned(x))) char buffer[Size]; \ - }; - -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(1) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(2) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(4) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(8) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(16) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(32) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(64) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(128) - -#undef LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT - -#else -# error No supported align as directive. -#endif - #else // _MSC_VER -/// \brief Create a type with an aligned char buffer. +/// Create a type with an aligned char buffer. template struct AlignedCharArray; @@ -237,7 +124,7 @@ union SizerImpl { }; } // end namespace detail -/// \brief This union template exposes a suitably aligned and sized character +/// This union template exposes a suitably aligned and sized character /// array member which can hold elements of any of up to ten types. /// /// These types may be arrays, structs, or any other types. The goal is to @@ -249,8 +136,8 @@ template struct AlignedCharArrayUnion : wpi::AlignedCharArray< - AlignOf >::Alignment, + alignof(wpi::detail::AlignerImpl), sizeof(::wpi::detail::SizerImpl)> { }; diff --git a/wpiutil/src/main/native/include/wpi/ArrayRef.h b/wpiutil/src/main/native/include/wpi/ArrayRef.h index 76454c96dc..1a84311e0c 100644 --- a/wpiutil/src/main/native/include/wpi/ArrayRef.h +++ b/wpiutil/src/main/native/include/wpi/ArrayRef.h @@ -1,4 +1,4 @@ -//===--- ArrayRef.h - Array Reference Wrapper -------------------*- C++ -*-===// +//===- ArrayRef.h - Array Reference Wrapper ---------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,13 +7,22 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_ARRAYREF_H -#define LLVM_ADT_ARRAYREF_H +#ifndef WPIUTIL_WPI_ARRAYREF_H +#define WPIUTIL_WPI_ARRAYREF_H -#include "wpi/Compiler.h" #include "wpi/Hashing.h" #include "wpi/None.h" #include "wpi/SmallVector.h" +#include "wpi/STLExtras.h" +#include "wpi/Compiler.h" +#include +#include +#include +#include +#include +#include +#include +#include #include namespace wpi { @@ -29,31 +38,30 @@ namespace wpi { /// This is intended to be trivially copyable, so it should be passed by /// value. template - class ArrayRef { + class LLVM_NODISCARD ArrayRef { public: - typedef const T *iterator; - typedef const T *const_iterator; - typedef size_t size_type; - typedef T value_type; - - typedef std::reverse_iterator reverse_iterator; + using iterator = const T *; + using const_iterator = const T *; + using size_type = size_t; + using reverse_iterator = std::reverse_iterator; + using value_type = T; private: /// The start of the array, in an external buffer. - const T *Data; + const T *Data = nullptr; /// The number of elements. - size_type Length; + size_type Length = 0; public: /// @name Constructors /// @{ /// Construct an empty ArrayRef. - /*implicit*/ ArrayRef() : Data(nullptr), Length(0) {} + /*implicit*/ ArrayRef() = default; /// Construct an empty ArrayRef from None. - /*implicit*/ ArrayRef(NoneType) : Data(nullptr), Length(0) {} + /*implicit*/ ArrayRef(NoneType) {} /// Construct an ArrayRef from a single element. /*implicit*/ ArrayRef(const T &OneElt) @@ -80,10 +88,14 @@ namespace wpi { /*implicit*/ ArrayRef(const std::vector &Vec) : Data(Vec.data()), Length(Vec.size()) {} + /// Construct an ArrayRef from a std::array + template + /*implicit*/ constexpr ArrayRef(const std::array &Arr) + : Data(Arr.data()), Length(N) {} + /// Construct an ArrayRef from a C array. template - /*implicit*/ LLVM_CONSTEXPR ArrayRef(const T (&Arr)[N]) - : Data(Arr), Length(N) {} + /*implicit*/ constexpr ArrayRef(const T (&Arr)[N]) : Data(Arr), Length(N) {} /// Construct an ArrayRef from a std::initializer_list. /*implicit*/ ArrayRef(const std::initializer_list &Vec) @@ -162,12 +174,6 @@ namespace wpi { return std::equal(begin(), end(), RHS.begin()); } - /// slice(n) - Chop off the first N elements of the array. - ArrayRef slice(size_t N) const { - assert(N <= size() && "Invalid specifier"); - return ArrayRef(data()+N, size()-N); - } - /// slice(n, m) - Chop off the first N elements of the array, and keep M /// elements in the array. ArrayRef slice(size_t N, size_t M) const { @@ -175,18 +181,59 @@ namespace wpi { return ArrayRef(data()+N, M); } - /// \brief Drop the first \p N elements of the array. + /// slice(n) - Chop off the first N elements of the array. + ArrayRef slice(size_t N) const { return slice(N, size() - N); } + + /// Drop the first \p N elements of the array. ArrayRef drop_front(size_t N = 1) const { assert(size() >= N && "Dropping more elements than exist"); return slice(N, size() - N); } - /// \brief Drop the last \p N elements of the array. + /// Drop the last \p N elements of the array. ArrayRef drop_back(size_t N = 1) const { assert(size() >= N && "Dropping more elements than exist"); return slice(0, size() - N); } + /// Return a copy of *this with the first N elements satisfying the + /// given predicate removed. + template ArrayRef drop_while(PredicateT Pred) const { + return ArrayRef(find_if_not(*this, Pred), end()); + } + + /// Return a copy of *this with the first N elements not satisfying + /// the given predicate removed. + template ArrayRef drop_until(PredicateT Pred) const { + return ArrayRef(find_if(*this, Pred), end()); + } + + /// Return a copy of *this with only the first \p N elements. + ArrayRef take_front(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_back(size() - N); + } + + /// Return a copy of *this with only the last \p N elements. + ArrayRef take_back(size_t N = 1) const { + if (N >= size()) + return *this; + return drop_front(size() - N); + } + + /// Return the first N elements of this Array that satisfy the given + /// predicate. + template ArrayRef take_while(PredicateT Pred) const { + return ArrayRef(begin(), find_if_not(*this, Pred)); + } + + /// Return the first N elements of this Array that don't satisfy the + /// given predicate. + template ArrayRef take_until(PredicateT Pred) const { + return ArrayRef(begin(), find_if(*this, Pred)); + } + /// @} /// @name Operator Overloads /// @{ @@ -195,6 +242,22 @@ namespace wpi { return Data[Index]; } + /// Disallow accidental assignment from a temporary. + /// + /// The declaration here is extra complicated so that "arrayRef = {}" + /// continues to select the move assignment operator. + template + typename std::enable_if::value, ArrayRef>::type & + operator=(U &&Temporary) = delete; + + /// Disallow accidental assignment from a temporary. + /// + /// The declaration here is extra complicated so that "arrayRef = {}" + /// continues to select the move assignment operator. + template + typename std::enable_if::value, ArrayRef>::type & + operator=(std::initializer_list) = delete; + /// @} /// @name Expensive Operations /// @{ @@ -225,14 +288,13 @@ namespace wpi { /// This is intended to be trivially copyable, so it should be passed by /// value. template - class MutableArrayRef : public ArrayRef { + class LLVM_NODISCARD MutableArrayRef : public ArrayRef { public: - typedef T *iterator; - - typedef std::reverse_iterator reverse_iterator; + using iterator = T *; + using reverse_iterator = std::reverse_iterator; /// Construct an empty MutableArrayRef. - /*implicit*/ MutableArrayRef() : ArrayRef() {} + /*implicit*/ MutableArrayRef() = default; /// Construct an empty MutableArrayRef from None. /*implicit*/ MutableArrayRef(NoneType) : ArrayRef() {} @@ -255,10 +317,14 @@ namespace wpi { /*implicit*/ MutableArrayRef(std::vector &Vec) : ArrayRef(Vec) {} + /// Construct an ArrayRef from a std::array + template + /*implicit*/ constexpr MutableArrayRef(std::array &Arr) + : ArrayRef(Arr) {} + /// Construct an MutableArrayRef from a C array. template - /*implicit*/ LLVM_CONSTEXPR MutableArrayRef(T (&Arr)[N]) - : ArrayRef(Arr) {} + /*implicit*/ constexpr MutableArrayRef(T (&Arr)[N]) : ArrayRef(Arr) {} T *data() const { return const_cast(ArrayRef::data()); } @@ -280,20 +346,19 @@ namespace wpi { return data()[this->size()-1]; } - /// slice(n) - Chop off the first N elements of the array. - MutableArrayRef slice(size_t N) const { - assert(N <= this->size() && "Invalid specifier"); - return MutableArrayRef(data()+N, this->size()-N); - } - /// slice(n, m) - Chop off the first N elements of the array, and keep M /// elements in the array. MutableArrayRef slice(size_t N, size_t M) const { - assert(N+M <= this->size() && "Invalid specifier"); - return MutableArrayRef(data()+N, M); + assert(N + M <= this->size() && "Invalid specifier"); + return MutableArrayRef(this->data() + N, M); } - /// \brief Drop the first \p N elements of the array. + /// slice(n) - Chop off the first N elements of the array. + MutableArrayRef slice(size_t N) const { + return slice(N, this->size() - N); + } + + /// Drop the first \p N elements of the array. MutableArrayRef drop_front(size_t N = 1) const { assert(this->size() >= N && "Dropping more elements than exist"); return slice(N, this->size() - N); @@ -304,6 +369,48 @@ namespace wpi { return slice(0, this->size() - N); } + /// Return a copy of *this with the first N elements satisfying the + /// given predicate removed. + template + MutableArrayRef drop_while(PredicateT Pred) const { + return MutableArrayRef(find_if_not(*this, Pred), end()); + } + + /// Return a copy of *this with the first N elements not satisfying + /// the given predicate removed. + template + MutableArrayRef drop_until(PredicateT Pred) const { + return MutableArrayRef(find_if(*this, Pred), end()); + } + + /// Return a copy of *this with only the first \p N elements. + MutableArrayRef take_front(size_t N = 1) const { + if (N >= this->size()) + return *this; + return drop_back(this->size() - N); + } + + /// Return a copy of *this with only the last \p N elements. + MutableArrayRef take_back(size_t N = 1) const { + if (N >= this->size()) + return *this; + return drop_front(this->size() - N); + } + + /// Return the first N elements of this Array that satisfy the given + /// predicate. + template + MutableArrayRef take_while(PredicateT Pred) const { + return MutableArrayRef(begin(), find_if_not(*this, Pred)); + } + + /// Return the first N elements of this Array that don't satisfy the + /// given predicate. + template + MutableArrayRef take_until(PredicateT Pred) const { + return MutableArrayRef(begin(), find_if(*this, Pred)); + } + /// @} /// @name Operator Overloads /// @{ @@ -313,6 +420,29 @@ namespace wpi { } }; + /// This is a MutableArrayRef that owns its array. + template class OwningArrayRef : public MutableArrayRef { + public: + OwningArrayRef() = default; + OwningArrayRef(size_t Size) : MutableArrayRef(new T[Size], Size) {} + + OwningArrayRef(ArrayRef Data) + : MutableArrayRef(new T[Data.size()], Data.size()) { + std::copy(Data.begin(), Data.end(), this->begin()); + } + + OwningArrayRef(OwningArrayRef &&Other) { *this = Other; } + + OwningArrayRef &operator=(OwningArrayRef &&Other) { + delete[] this->data(); + this->MutableArrayRef::operator=(Other); + Other.MutableArrayRef::operator=(MutableArrayRef()); + return *this; + } + + ~OwningArrayRef() { delete[] this->data(); } + }; + /// @name ArrayRef Convenience constructors /// @{ @@ -368,6 +498,18 @@ namespace wpi { return ArrayRef(Arr); } + /// Construct a MutableArrayRef from a single element. + template + MutableArrayRef makeMutableArrayRef(T &OneElt) { + return OneElt; + } + + /// Construct a MutableArrayRef from a pointer and length. + template + MutableArrayRef makeMutableArrayRef(T *data, size_t length) { + return MutableArrayRef(data, length); + } + /// @} /// @name ArrayRef Comparison Operators /// @{ @@ -386,13 +528,14 @@ namespace wpi { // ArrayRefs can be treated like a POD type. template struct isPodLike; - template struct isPodLike > { + template struct isPodLike> { static const bool value = true; }; template hash_code hash_value(ArrayRef S) { return hash_combine_range(S.begin(), S.end()); } + } // end namespace wpi #endif // LLVM_ADT_ARRAYREF_H diff --git a/wpiutil/src/main/native/include/wpi/Compiler.h b/wpiutil/src/main/native/include/wpi/Compiler.h index b7add94c32..6e13ef4d1f 100644 --- a/wpiutil/src/main/native/include/wpi/Compiler.h +++ b/wpiutil/src/main/native/include/wpi/Compiler.h @@ -12,8 +12,12 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_COMPILER_H -#define LLVM_SUPPORT_COMPILER_H +#ifndef WPIUTIL_WPI_COMPILER_H +#define WPIUTIL_WPI_COMPILER_H + +#if defined(_MSC_VER) +#include +#endif #ifndef __has_feature # define __has_feature(x) 0 @@ -27,12 +31,16 @@ # define __has_attribute(x) 0 #endif +#ifndef __has_cpp_attribute +# define __has_cpp_attribute(x) 0 +#endif + #ifndef __has_builtin # define __has_builtin(x) 0 #endif /// \macro LLVM_GNUC_PREREQ -/// \brief Extend the default __GNUC_PREREQ even if glibc's features.h isn't +/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't /// available. #ifndef LLVM_GNUC_PREREQ # if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) @@ -47,39 +55,116 @@ # endif #endif -#ifndef LLVM_CONSTEXPR -# ifdef _MSC_VER -# if _MSC_VER >= 1900 -# define LLVM_CONSTEXPR constexpr -# else -# define LLVM_CONSTEXPR -# endif -# elif defined(__has_feature) -# if __has_feature(cxx_constexpr) -# define LLVM_CONSTEXPR constexpr -# else -# define LLVM_CONSTEXPR -# endif -# elif defined(__GXX_EXPERIMENTAL_CXX0X__) -# define LLVM_CONSTEXPR constexpr -# elif defined(__has_constexpr) -# define LLVM_CONSTEXPR constexpr -# else -# define LLVM_CONSTEXPR -# endif +/// \macro LLVM_MSC_PREREQ +/// Is the compiler MSVC of at least the specified version? +/// The common \param version values to check for are: +/// * 1900: Microsoft Visual Studio 2015 / 14.0 +#ifndef LLVM_MSC_PREREQ +#ifdef _MSC_VER +#define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version)) + +// We require at least MSVC 2015. +#if !LLVM_MSC_PREREQ(1900) +#error wpiutil requires at least MSVC 2015. #endif -#ifndef LLVM_ATTRIBUTE_UNUSED_RESULT -#if __has_attribute(warn_unused_result) || LLVM_GNUC_PREREQ(3, 4, 0) -#define LLVM_ATTRIBUTE_UNUSED_RESULT __attribute__((__warn_unused_result__)) -#elif defined(_MSC_VER) -#define LLVM_ATTRIBUTE_UNUSED_RESULT _Check_return_ #else -#define LLVM_ATTRIBUTE_UNUSED_RESULT +#define LLVM_MSC_PREREQ(version) 0 #endif #endif -#ifndef LLVM_UNLIKELY +/// Does the compiler support ref-qualifiers for *this? +/// +/// Sadly, this is separate from just rvalue reference support because GCC +/// and MSVC implemented this later than everything else. +#ifndef LLVM_HAS_RVALUE_REFERENCE_THIS +#if __has_feature(cxx_rvalue_references) || LLVM_GNUC_PREREQ(4, 8, 1) +#define LLVM_HAS_RVALUE_REFERENCE_THIS 1 +#else +#define LLVM_HAS_RVALUE_REFERENCE_THIS 0 +#endif +#endif + +/// Expands to '&' if ref-qualifiers for *this are supported. +/// +/// This can be used to provide lvalue/rvalue overrides of member functions. +/// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS +#ifndef LLVM_LVALUE_FUNCTION +#if LLVM_HAS_RVALUE_REFERENCE_THIS +#define LLVM_LVALUE_FUNCTION & +#else +#define LLVM_LVALUE_FUNCTION +#endif +#endif + +#ifndef LLVM_PREFETCH +#if defined(__GNUC__) +#define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality) +#else +#define LLVM_PREFETCH(addr, rw, locality) +#endif +#endif + +#ifndef LLVM_ATTRIBUTE_USED +#if __has_attribute(used) || LLVM_GNUC_PREREQ(3, 1, 0) +#define LLVM_ATTRIBUTE_USED __attribute__((__used__)) +#else +#define LLVM_ATTRIBUTE_USED +#endif +#endif + +/// LLVM_NODISCARD - Warn if a type or return value is discarded. +#ifndef LLVM_NODISCARD +#if __cplusplus > 201402L && __has_cpp_attribute(nodiscard) +#define LLVM_NODISCARD [[nodiscard]] +#elif !__cplusplus +// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious +// error when __has_cpp_attribute is given a scoped attribute in C mode. +#define LLVM_NODISCARD +#elif __has_cpp_attribute(clang::warn_unused_result) +#define LLVM_NODISCARD [[clang::warn_unused_result]] +#else +#define LLVM_NODISCARD +#endif +#endif + +// Some compilers warn about unused functions. When a function is sometimes +// used or not depending on build settings (e.g. a function only called from +// within "assert"), this attribute can be used to suppress such warnings. +// +// However, it shouldn't be used for unused *variables*, as those have a much +// more portable solution: +// (void)unused_var_name; +// Prefer cast-to-void wherever it is sufficient. +#ifndef LLVM_ATTRIBUTE_UNUSED +#if __has_attribute(unused) || LLVM_GNUC_PREREQ(3, 1, 0) +#define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#else +#define LLVM_ATTRIBUTE_UNUSED +#endif +#endif + +#ifndef LLVM_READNONE +// Prior to clang 3.2, clang did not accept any spelling of +// __has_attribute(const), so assume it is supported. +#if defined(__clang__) || defined(__GNUC__) +// aka 'CONST' but following LLVM Conventions. +#define LLVM_READNONE __attribute__((__const__)) +#else +#define LLVM_READNONE +#endif +#endif + +#ifndef LLVM_READONLY +#if __has_attribute(pure) || defined(__GNUC__) +// aka 'PURE' but following LLVM Conventions. +#define LLVM_READONLY __attribute__((__pure__)) +#else +#define LLVM_READONLY +#endif +#endif + +#ifndef LLVM_LIKELY #if __has_builtin(__builtin_expect) || LLVM_GNUC_PREREQ(4, 0, 0) #define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) #define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) @@ -89,4 +174,253 @@ #endif #endif +/// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, +/// mark a method "not for inlining". +#ifndef LLVM_ATTRIBUTE_NOINLINE +#if __has_attribute(noinline) || LLVM_GNUC_PREREQ(3, 4, 0) +#define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define LLVM_ATTRIBUTE_NOINLINE +#endif +#endif + +/// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do +/// so, mark a method "always inline" because it is performance sensitive. GCC +/// 3.4 supported this but is buggy in various cases and produces unimplemented +/// errors, just use it in GCC 4.0 and later. +#ifndef LLVM_ATTRIBUTE_ALWAYS_INLINE +#if __has_attribute(always_inline) || LLVM_GNUC_PREREQ(4, 0, 0) +#define LLVM_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define LLVM_ATTRIBUTE_ALWAYS_INLINE inline +#endif +#endif + +#ifndef LLVM_ATTRIBUTE_NORETURN +#ifdef __GNUC__ +#define LLVM_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define LLVM_ATTRIBUTE_NORETURN +#endif +#endif + +#ifndef LLVM_ATTRIBUTE_RETURNS_NONNULL +#if __has_attribute(returns_nonnull) || LLVM_GNUC_PREREQ(4, 9, 0) +#define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_RETURNS_NONNULL _Ret_notnull_ +#else +#define LLVM_ATTRIBUTE_RETURNS_NONNULL +#endif +#endif + +/// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a +/// pointer that does not alias any other valid pointer. +#ifndef LLVM_ATTRIBUTE_RETURNS_NOALIAS +#ifdef __GNUC__ +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__)) +#elif defined(_MSC_VER) +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict) +#else +#define LLVM_ATTRIBUTE_RETURNS_NOALIAS +#endif +#endif + +/// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements. +#ifndef LLVM_FALLTHROUGH +#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough) +#define LLVM_FALLTHROUGH [[fallthrough]] +#elif __has_cpp_attribute(gnu::fallthrough) +#define LLVM_FALLTHROUGH [[gnu::fallthrough]] +#elif !__cplusplus +// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious +// error when __has_cpp_attribute is given a scoped attribute in C mode. +#define LLVM_FALLTHROUGH +#elif __has_cpp_attribute(clang::fallthrough) +#define LLVM_FALLTHROUGH [[clang::fallthrough]] +#else +#define LLVM_FALLTHROUGH +#endif +#endif + +/// LLVM_EXTENSION - Support compilers where we have a keyword to suppress +/// pedantic diagnostics. +#ifndef LLVM_EXTENSION +#ifdef __GNUC__ +#define LLVM_EXTENSION __extension__ +#else +#define LLVM_EXTENSION +#endif +#endif + +// LLVM_ATTRIBUTE_DEPRECATED(decl, "message") +#ifndef LLVM_ATTRIBUTE_DEPRECATED +#if __has_feature(attribute_deprecated_with_message) +# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ + decl __attribute__((deprecated(message))) +#elif defined(__GNUC__) +# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ + decl __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ + __declspec(deprecated(message)) decl +#else +# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ + decl +#endif +#endif + +/// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands +/// to an expression which states that it is undefined behavior for the +/// compiler to reach this point. Otherwise is not defined. +#ifndef LLVM_BUILTIN_UNREACHABLE +#if __has_builtin(__builtin_unreachable) || LLVM_GNUC_PREREQ(4, 5, 0) +# define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) +# define LLVM_BUILTIN_UNREACHABLE __assume(false) +#endif +#endif + +/// \macro LLVM_ASSUME_ALIGNED +/// Returns a pointer with an assumed alignment. +#ifndef LLVM_ASSUME_ALIGNED +#if __has_builtin(__builtin_assume_aligned) || LLVM_GNUC_PREREQ(4, 7, 0) +# define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a) +#elif defined(LLVM_BUILTIN_UNREACHABLE) +// As of today, clang does not support __builtin_assume_aligned. +# define LLVM_ASSUME_ALIGNED(p, a) \ + (((uintptr_t(p) % (a)) == 0) ? (p) : (LLVM_BUILTIN_UNREACHABLE, (p))) +#else +# define LLVM_ASSUME_ALIGNED(p, a) (p) +#endif +#endif + +/// \macro LLVM_ALIGNAS +/// Used to specify a minimum alignment for a structure or variable. +#ifndef LLVM_ALIGNAS +#if __GNUC__ && !__has_feature(cxx_alignas) && !LLVM_GNUC_PREREQ(4, 8, 1) +# define LLVM_ALIGNAS(x) __attribute__((aligned(x))) +#else +# define LLVM_ALIGNAS(x) alignas(x) +#endif +#endif + +/// \macro LLVM_PACKED +/// Used to specify a packed structure. +/// LLVM_PACKED( +/// struct A { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }); +/// +/// LLVM_PACKED_START +/// struct B { +/// int i; +/// int j; +/// int k; +/// long long l; +/// }; +/// LLVM_PACKED_END +#ifndef LLVM_PACKED +#ifdef _MSC_VER +# define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop)) +# define LLVM_PACKED_START __pragma(pack(push, 1)) +# define LLVM_PACKED_END __pragma(pack(pop)) +#else +# define LLVM_PACKED(d) d __attribute__((packed)) +# define LLVM_PACKED_START _Pragma("pack(push, 1)") +# define LLVM_PACKED_END _Pragma("pack(pop)") +#endif +#endif + +/// \macro LLVM_PTR_SIZE +/// A constant integer equivalent to the value of sizeof(void*). +/// Generally used in combination with LLVM_ALIGNAS or when doing computation in +/// the preprocessor. +#ifndef LLVM_PTR_SIZE +#ifdef __SIZEOF_POINTER__ +# define LLVM_PTR_SIZE __SIZEOF_POINTER__ +#elif defined(_WIN64) +# define LLVM_PTR_SIZE 8 +#elif defined(_WIN32) +# define LLVM_PTR_SIZE 4 +#elif defined(_MSC_VER) +# error "could not determine LLVM_PTR_SIZE as a constant int for MSVC" +#else +# define LLVM_PTR_SIZE sizeof(void *) +#endif +#endif + +/// \macro LLVM_NO_SANITIZE +/// Disable a particular sanitizer for a function. +#ifndef LLVM_NO_SANITIZE +#if __has_attribute(no_sanitize) +#define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND))) +#else +#define LLVM_NO_SANITIZE(KIND) +#endif +#endif + +/// Mark debug helper function definitions like dump() that should not be +/// stripped from debug builds. +/// Note that you should also surround dump() functions with +/// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always +/// get stripped in release builds. +// FIXME: Move this to a private config.h as it's not usable in public headers. +#ifndef LLVM_DUMP_METHOD +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED +#else +#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE +#endif +#endif + +/// \macro LLVM_PRETTY_FUNCTION +/// Gets a user-friendly looking function signature for the current scope +/// using the best available method on each platform. The exact format of the +/// resulting string is implementation specific and non-portable, so this should +/// only be used, for example, for logging or diagnostics. +#ifndef LLVM_PRETTY_FUNCTION +#if defined(_MSC_VER) +#define LLVM_PRETTY_FUNCTION __FUNCSIG__ +#elif defined(__GNUC__) || defined(__clang__) +#define LLVM_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define LLVM_PRETTY_FUNCTION __func__ +#endif +#endif + +/// \macro LLVM_THREAD_LOCAL +/// A thread-local storage specifier which can be used with globals, +/// extern globals, and static globals. +/// +/// This is essentially an extremely restricted analog to C++11's thread_local +/// support, and uses that when available. However, it falls back on +/// platform-specific or vendor-provided extensions when necessary. These +/// extensions don't support many of the C++11 thread_local's features. You +/// should only use this for PODs that you can statically initialize to +/// some constant value. In almost all circumstances this is most appropriate +/// for use with a pointer, integer, or small aggregation of pointers and +/// integers. +#ifndef LLVM_THREAD_LOCAL +#if __has_feature(cxx_thread_local) +#define LLVM_THREAD_LOCAL thread_local +#elif defined(_MSC_VER) +// MSVC supports this with a __declspec. +#define LLVM_THREAD_LOCAL __declspec(thread) +#else +// Clang, GCC, and other compatible compilers used __thread prior to C++11 and +// we only need the restricted functionality that provides. +#define LLVM_THREAD_LOCAL __thread +#endif +#endif + #endif diff --git a/wpiutil/src/main/native/include/wpi/ConvertUTF.h b/wpiutil/src/main/native/include/wpi/ConvertUTF.h index 41226335c2..c09e71a001 100644 --- a/wpiutil/src/main/native/include/wpi/ConvertUTF.h +++ b/wpiutil/src/main/native/include/wpi/ConvertUTF.h @@ -90,6 +90,17 @@ #ifndef LLVM_SUPPORT_CONVERTUTF_H #define LLVM_SUPPORT_CONVERTUTF_H +#include "wpi/ArrayRef.h" +#include "wpi/StringRef.h" + +#include +#include + +// Wrap everything in namespace wpi so that programs can link with wpiutil and +// their own version of the unicode libraries. + +namespace wpi { + /* --------------------------------------------------------------------- The following 4 definitions are compiler-specific. The C standard does not guarantee that wchar_t has at least @@ -127,11 +138,6 @@ typedef enum { lenientConversion } ConversionFlags; -/* This is for C++ and does no harm in C */ -#ifdef __cplusplus -extern "C" { -#endif - ConversionResult ConvertUTF8toUTF16 ( const UTF8** sourceStart, const UTF8* sourceEnd, UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); @@ -174,16 +180,9 @@ Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd); unsigned getNumBytesForUTF8(UTF8 firstByte); -#ifdef __cplusplus -} - /*************************************************************************/ /* Below are LLVM-specific wrappers of the functions above. */ -#include "wpi/ArrayRef.h" -#include "wpi/StringRef.h" - -namespace wpi { /** * Convert an Unicode code point to UTF8 sequence. @@ -249,7 +248,3 @@ bool convertUTF8ToUTF16String(StringRef SrcUTF8, } /* end namespace wpi */ #endif - -/* --------------------------------------------------------------------- */ - -#endif diff --git a/wpiutil/src/main/native/include/wpi/DenseMap.h b/wpiutil/src/main/native/include/wpi/DenseMap.h index 3d812c8ea2..2853217d38 100644 --- a/wpiutil/src/main/native/include/wpi/DenseMap.h +++ b/wpiutil/src/main/native/include/wpi/DenseMap.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_DENSEMAP_H -#define LLVM_ADT_DENSEMAP_H +#ifndef WPIUTIL_WPI_DENSEMAP_H +#define WPIUTIL_WPI_DENSEMAP_H #include "wpi/DenseMapInfo.h" #include "wpi/EpochTracker.h" @@ -23,16 +23,17 @@ #include "wpi/type_traits.h" #include #include -#include #include #include #include #include +#include #include namespace wpi { namespace detail { + // We extend a pair to allow users to override the bucket type with their own // implementation without requiring two members. template @@ -42,7 +43,8 @@ struct DenseMapPair : public std::pair { ValueT &getSecond() { return std::pair::second; } const ValueT &getSecond() const { return std::pair::second; } }; -} + +} // end namespace detail template < typename KeyT, typename ValueT, typename KeyInfoT = DenseMapInfo, @@ -52,31 +54,39 @@ class DenseMapIterator; template class DenseMapBase : public DebugEpochBase { -public: - typedef unsigned size_type; - typedef KeyT key_type; - typedef ValueT mapped_type; - typedef BucketT value_type; + template + using const_arg_type_t = typename const_pointer_or_const_ref::type; + +public: + using size_type = unsigned; + using key_type = KeyT; + using mapped_type = ValueT; + using value_type = BucketT; + + using iterator = DenseMapIterator; + using const_iterator = + DenseMapIterator; - typedef DenseMapIterator iterator; - typedef DenseMapIterator - const_iterator; inline iterator begin() { - // When the map is empty, avoid the overhead of AdvancePastEmptyBuckets(). - return empty() ? end() : iterator(getBuckets(), getBucketsEnd(), *this); + // When the map is empty, avoid the overhead of advancing/retreating past + // empty buckets. + if (empty()) + return end(); + return makeIterator(getBuckets(), getBucketsEnd(), *this); } inline iterator end() { - return iterator(getBucketsEnd(), getBucketsEnd(), *this, true); + return makeIterator(getBucketsEnd(), getBucketsEnd(), *this, true); } inline const_iterator begin() const { - return empty() ? end() - : const_iterator(getBuckets(), getBucketsEnd(), *this); + if (empty()) + return end(); + return makeConstIterator(getBuckets(), getBucketsEnd(), *this); } inline const_iterator end() const { - return const_iterator(getBucketsEnd(), getBucketsEnd(), *this, true); + return makeConstIterator(getBucketsEnd(), getBucketsEnd(), *this, true); } - bool LLVM_ATTRIBUTE_UNUSED_RESULT empty() const { + LLVM_NODISCARD bool empty() const { return getNumEntries() == 0; } unsigned size() const { return getNumEntries(); } @@ -102,37 +112,43 @@ public: } const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey(); - unsigned NumEntries = getNumEntries(); - for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) { - if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) { - if (!KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) { - P->getSecond().~ValueT(); - --NumEntries; - } + if (isPodLike::value && isPodLike::value) { + // Use a simpler loop when these are trivial types. + for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) P->getFirst() = EmptyKey; + } else { + unsigned NumEntries = getNumEntries(); + for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) { + if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) { + if (!KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) { + P->getSecond().~ValueT(); + --NumEntries; + } + P->getFirst() = EmptyKey; + } } + assert(NumEntries == 0 && "Node count imbalance!"); } - assert(NumEntries == 0 && "Node count imbalance!"); setNumEntries(0); setNumTombstones(0); } /// Return 1 if the specified key is in the map, 0 otherwise. - size_type count(const KeyT &Val) const { + size_type count(const_arg_type_t Val) const { const BucketT *TheBucket; return LookupBucketFor(Val, TheBucket) ? 1 : 0; } - iterator find(const KeyT &Val) { + iterator find(const_arg_type_t Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return iterator(TheBucket, getBucketsEnd(), *this, true); + return makeIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } - const_iterator find(const KeyT &Val) const { + const_iterator find(const_arg_type_t Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return const_iterator(TheBucket, getBucketsEnd(), *this, true); + return makeConstIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } @@ -145,20 +161,20 @@ public: iterator find_as(const LookupKeyT &Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return iterator(TheBucket, getBucketsEnd(), *this, true); + return makeIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } template const_iterator find_as(const LookupKeyT &Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return const_iterator(TheBucket, getBucketsEnd(), *this, true); + return makeConstIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } /// lookup - Return the entry for the specified key, or a default /// constructed value if no such entry exists. - ValueT lookup(const KeyT &Val) const { + ValueT lookup(const_arg_type_t Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return TheBucket->getSecond(); @@ -169,32 +185,51 @@ public: // If the key is already in the map, it returns false and doesn't update the // value. std::pair insert(const std::pair &KV) { - BucketT *TheBucket; - if (LookupBucketFor(KV.first, TheBucket)) - return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), - false); // Already in map. - - // Otherwise, insert the new element. - TheBucket = InsertIntoBucket(KV.first, KV.second, TheBucket); - return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), - true); + return try_emplace(KV.first, KV.second); } // Inserts key,value pair into the map if the key isn't already in the map. // If the key is already in the map, it returns false and doesn't update the // value. std::pair insert(std::pair &&KV) { + return try_emplace(std::move(KV.first), std::move(KV.second)); + } + + // Inserts key,value pair into the map if the key isn't already in the map. + // The value is constructed in-place if the key is not in the map, otherwise + // it is not moved. + template + std::pair try_emplace(KeyT &&Key, Ts &&... Args) { BucketT *TheBucket; - if (LookupBucketFor(KV.first, TheBucket)) - return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), - false); // Already in map. + if (LookupBucketFor(Key, TheBucket)) + return std::make_pair( + makeIterator(TheBucket, getBucketsEnd(), *this, true), + false); // Already in map. // Otherwise, insert the new element. - TheBucket = InsertIntoBucket(std::move(KV.first), - std::move(KV.second), - TheBucket); - return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), - true); + TheBucket = + InsertIntoBucket(TheBucket, std::move(Key), std::forward(Args)...); + return std::make_pair( + makeIterator(TheBucket, getBucketsEnd(), *this, true), + true); + } + + // Inserts key,value pair into the map if the key isn't already in the map. + // The value is constructed in-place if the key is not in the map, otherwise + // it is not moved. + template + std::pair try_emplace(const KeyT &Key, Ts &&... Args) { + BucketT *TheBucket; + if (LookupBucketFor(Key, TheBucket)) + return std::make_pair( + makeIterator(TheBucket, getBucketsEnd(), *this, true), + false); // Already in map. + + // Otherwise, insert the new element. + TheBucket = InsertIntoBucket(TheBucket, Key, std::forward(Args)...); + return std::make_pair( + makeIterator(TheBucket, getBucketsEnd(), *this, true), + true); } /// Alternate version of insert() which allows a different, and possibly @@ -207,14 +242,16 @@ public: const LookupKeyT &Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), - false); // Already in map. + return std::make_pair( + makeIterator(TheBucket, getBucketsEnd(), *this, true), + false); // Already in map. // Otherwise, insert the new element. - TheBucket = InsertIntoBucket(std::move(KV.first), std::move(KV.second), Val, - TheBucket); - return std::make_pair(iterator(TheBucket, getBucketsEnd(), *this, true), - true); + TheBucket = InsertIntoBucketWithLookup(TheBucket, std::move(KV.first), + std::move(KV.second), Val); + return std::make_pair( + makeIterator(TheBucket, getBucketsEnd(), *this, true), + true); } /// insert - Range insertion of pairs. @@ -224,7 +261,6 @@ public: insert(*I); } - bool erase(const KeyT &Val) { BucketT *TheBucket; if (!LookupBucketFor(Val, TheBucket)) @@ -249,7 +285,7 @@ public: if (LookupBucketFor(Key, TheBucket)) return *TheBucket; - return *InsertIntoBucket(Key, ValueT(), TheBucket); + return *InsertIntoBucket(TheBucket, Key); } ValueT &operator[](const KeyT &Key) { @@ -261,7 +297,7 @@ public: if (LookupBucketFor(Key, TheBucket)) return *TheBucket; - return *InsertIntoBucket(std::move(Key), ValueT(), TheBucket); + return *InsertIntoBucket(TheBucket, std::move(Key)); } ValueT &operator[](KeyT &&Key) { @@ -369,54 +405,83 @@ protected: static unsigned getHashValue(const KeyT &Val) { return KeyInfoT::getHashValue(Val); } + template static unsigned getHashValue(const LookupKeyT &Val) { return KeyInfoT::getHashValue(Val); } + static const KeyT getEmptyKey() { + static_assert(std::is_base_of::value, + "Must pass the derived type to this template!"); return KeyInfoT::getEmptyKey(); } + static const KeyT getTombstoneKey() { return KeyInfoT::getTombstoneKey(); } private: + iterator makeIterator(BucketT *P, BucketT *E, + DebugEpochBase &Epoch, + bool NoAdvance=false) { + return iterator(P, E, Epoch, NoAdvance); + } + + const_iterator makeConstIterator(const BucketT *P, const BucketT *E, + const DebugEpochBase &Epoch, + const bool NoAdvance=false) const { + return const_iterator(P, E, Epoch, NoAdvance); + } + unsigned getNumEntries() const { return static_cast(this)->getNumEntries(); } + void setNumEntries(unsigned Num) { static_cast(this)->setNumEntries(Num); } + void incrementNumEntries() { setNumEntries(getNumEntries() + 1); } + void decrementNumEntries() { setNumEntries(getNumEntries() - 1); } + unsigned getNumTombstones() const { return static_cast(this)->getNumTombstones(); } + void setNumTombstones(unsigned Num) { static_cast(this)->setNumTombstones(Num); } + void incrementNumTombstones() { setNumTombstones(getNumTombstones() + 1); } + void decrementNumTombstones() { setNumTombstones(getNumTombstones() - 1); } + const BucketT *getBuckets() const { return static_cast(this)->getBuckets(); } + BucketT *getBuckets() { return static_cast(this)->getBuckets(); } + unsigned getNumBuckets() const { return static_cast(this)->getNumBuckets(); } + BucketT *getBucketsEnd() { return getBuckets() + getNumBuckets(); } + const BucketT *getBucketsEnd() const { return getBuckets() + getNumBuckets(); } @@ -429,36 +494,19 @@ private: static_cast(this)->shrink_and_clear(); } - - BucketT *InsertIntoBucket(const KeyT &Key, const ValueT &Value, - BucketT *TheBucket) { + template + BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key, + ValueArgs &&... Values) { TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket); - TheBucket->getFirst() = Key; - ::new (&TheBucket->getSecond()) ValueT(Value); - return TheBucket; - } - - BucketT *InsertIntoBucket(const KeyT &Key, ValueT &&Value, - BucketT *TheBucket) { - TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket); - - TheBucket->getFirst() = Key; - ::new (&TheBucket->getSecond()) ValueT(std::move(Value)); - return TheBucket; - } - - BucketT *InsertIntoBucket(KeyT &&Key, ValueT &&Value, BucketT *TheBucket) { - TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket); - - TheBucket->getFirst() = std::move(Key); - ::new (&TheBucket->getSecond()) ValueT(std::move(Value)); + TheBucket->getFirst() = std::forward(Key); + ::new (&TheBucket->getSecond()) ValueT(std::forward(Values)...); return TheBucket; } template - BucketT *InsertIntoBucket(KeyT &&Key, ValueT &&Value, LookupKeyT &Lookup, - BucketT *TheBucket) { + BucketT *InsertIntoBucketWithLookup(BucketT *TheBucket, KeyT &&Key, + ValueT &&Value, LookupKeyT &Lookup) { TheBucket = InsertIntoBucketImpl(Key, Lookup, TheBucket); TheBucket->getFirst() = std::move(Key); @@ -530,7 +578,7 @@ private: unsigned BucketNo = getHashValue(Val) & (NumBuckets-1); unsigned ProbeAmt = 1; - while (1) { + while (true) { const BucketT *ThisBucket = BucketsPtr + BucketNo; // Found Val's bucket? If so, return it. if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) { @@ -584,10 +632,11 @@ template > class DenseMap : public DenseMapBase, KeyT, ValueT, KeyInfoT, BucketT> { + friend class DenseMapBase; + // Lift some types from the dependent base class into this class for // simplicity of referring to them. - typedef DenseMapBase BaseT; - friend class DenseMapBase; + using BaseT = DenseMapBase; BucketT *Buckets; unsigned NumEntries; @@ -702,6 +751,7 @@ private: unsigned getNumEntries() const { return NumEntries; } + void setNumEntries(unsigned Num) { NumEntries = Num; } @@ -709,6 +759,7 @@ private: unsigned getNumTombstones() const { return NumTombstones; } + void setNumTombstones(unsigned Num) { NumTombstones = Num; } @@ -740,10 +791,14 @@ class SmallDenseMap : public DenseMapBase< SmallDenseMap, KeyT, ValueT, KeyInfoT, BucketT> { + friend class DenseMapBase; + // Lift some types from the dependent base class into this class for // simplicity of referring to them. - typedef DenseMapBase BaseT; - friend class DenseMapBase; + using BaseT = DenseMapBase; + + static_assert(isPowerOf2_64(InlineBuckets), + "InlineBuckets must be a power of 2."); unsigned Small : 1; unsigned NumEntries : 31; @@ -967,14 +1022,17 @@ private: unsigned getNumEntries() const { return NumEntries; } + void setNumEntries(unsigned Num) { - assert(Num < INT_MAX && "Cannot support more than INT_MAX entries"); + // NumEntries is hardcoded to be 31 bits wide. + assert(Num < (1U << 31) && "Cannot support more than 1<<31 entries"); NumEntries = Num; } unsigned getNumTombstones() const { return NumTombstones; } + void setNumTombstones(unsigned Num) { NumTombstones = Num; } @@ -986,15 +1044,18 @@ private: // 'storage.buffer' static type is 'char *'. return reinterpret_cast(storage.buffer); } + BucketT *getInlineBuckets() { return const_cast( const_cast(this)->getInlineBuckets()); } + const LargeRep *getLargeRep() const { assert(!Small); // Note, same rule about aliasing as with getInlineBuckets. return reinterpret_cast(storage.buffer); } + LargeRep *getLargeRep() { return const_cast( const_cast(this)->getLargeRep()); @@ -1003,10 +1064,12 @@ private: const BucketT *getBuckets() const { return Small ? getInlineBuckets() : getLargeRep()->Buckets; } + BucketT *getBuckets() { return const_cast( const_cast(this)->getBuckets()); } + unsigned getNumBuckets() const { return Small ? InlineBuckets : getLargeRep()->NumBuckets; } @@ -1031,27 +1094,33 @@ private: template class DenseMapIterator : DebugEpochBase::HandleBase { - typedef DenseMapIterator ConstIterator; friend class DenseMapIterator; friend class DenseMapIterator; + using ConstIterator = DenseMapIterator; + public: - typedef ptrdiff_t difference_type; - typedef typename std::conditional::type - value_type; - typedef value_type *pointer; - typedef value_type &reference; - typedef std::forward_iterator_tag iterator_category; + using difference_type = ptrdiff_t; + using value_type = + typename std::conditional::type; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + private: - pointer Ptr, End; + pointer Ptr = nullptr; + pointer End = nullptr; + public: - DenseMapIterator() : Ptr(nullptr), End(nullptr) {} + DenseMapIterator() = default; DenseMapIterator(pointer Pos, pointer E, const DebugEpochBase &Epoch, bool NoAdvance = false) : DebugEpochBase::HandleBase(&Epoch), Ptr(Pos), End(E) { assert(isHandleInSync() && "invalid construction!"); - if (!NoAdvance) AdvancePastEmptyBuckets(); + + if (NoAdvance) return; + AdvancePastEmptyBuckets(); } // Converting ctor from non-const iterators to const iterators. SFINAE'd out @@ -1100,6 +1169,7 @@ public: private: void AdvancePastEmptyBuckets() { + assert(Ptr <= End); const KeyT Empty = KeyInfoT::getEmptyKey(); const KeyT Tombstone = KeyInfoT::getTombstoneKey(); @@ -1107,14 +1177,23 @@ private: KeyInfoT::isEqual(Ptr->getFirst(), Tombstone))) ++Ptr; } + + void RetreatPastEmptyBuckets() { + assert(Ptr >= End); + const KeyT Empty = KeyInfoT::getEmptyKey(); + const KeyT Tombstone = KeyInfoT::getTombstoneKey(); + + while (Ptr != End && (KeyInfoT::isEqual(Ptr[-1].getFirst(), Empty) || + KeyInfoT::isEqual(Ptr[-1].getFirst(), Tombstone))) + --Ptr; + } }; -template -static inline size_t -capacity_in_bytes(const DenseMap &X) { +template +inline size_t capacity_in_bytes(const DenseMap &X) { return X.getMemorySize(); } } // end namespace wpi -#endif +#endif // LLVM_ADT_DENSEMAP_H diff --git a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h index ec997d62c0..e4bf986e4c 100644 --- a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h +++ b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h @@ -11,14 +11,17 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_DENSEMAPINFO_H -#define LLVM_ADT_DENSEMAPINFO_H +#ifndef WPIUTIL_WPI_DENSEMAPINFO_H +#define WPIUTIL_WPI_DENSEMAPINFO_H #include "wpi/ArrayRef.h" #include "wpi/Hashing.h" #include "wpi/StringRef.h" #include "wpi/PointerLikeTypeTraits.h" -#include "wpi/type_traits.h" +#include +#include +#include +#include namespace wpi { @@ -30,36 +33,6 @@ struct DenseMapInfo { //static bool isEqual(const T &LHS, const T &RHS); }; -template struct CachedHash { - CachedHash(T Val) : Val(std::move(Val)) { - Hash = DenseMapInfo::getHashValue(Val); - } - CachedHash(T Val, unsigned Hash) : Val(std::move(Val)), Hash(Hash) {} - T Val; - unsigned Hash; -}; - -// Provide DenseMapInfo for all CachedHash. -template struct DenseMapInfo> { - static CachedHash getEmptyKey() { - T N = DenseMapInfo::getEmptyKey(); - return {N, 0}; - } - static CachedHash getTombstoneKey() { - T N = DenseMapInfo::getTombstoneKey(); - return {N, 0}; - } - static unsigned getHashValue(CachedHash Val) { - assert(!isEqual(Val, getEmptyKey()) && "Cannot hash the empty key!"); - assert(!isEqual(Val, getTombstoneKey()) && - "Cannot hash the tombstone key!"); - return Val.Hash; - } - static bool isEqual(CachedHash A, CachedHash B) { - return DenseMapInfo::isEqual(A.Val, B.Val); - } -}; - // Provide DenseMapInfo for all pointers. template struct DenseMapInfo { @@ -68,15 +41,18 @@ struct DenseMapInfo { Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; return reinterpret_cast(Val); } + static inline T* getTombstoneKey() { uintptr_t Val = static_cast(-2); Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; return reinterpret_cast(Val); } + static unsigned getHashValue(const T *PtrVal) { return (unsigned((uintptr_t)PtrVal) >> 4) ^ (unsigned((uintptr_t)PtrVal) >> 9); } + static bool isEqual(const T *LHS, const T *RHS) { return LHS == RHS; } }; @@ -85,16 +61,29 @@ template<> struct DenseMapInfo { static inline char getEmptyKey() { return ~0; } static inline char getTombstoneKey() { return ~0 - 1; } static unsigned getHashValue(const char& Val) { return Val * 37U; } + static bool isEqual(const char &LHS, const char &RHS) { return LHS == RHS; } }; +// Provide DenseMapInfo for unsigned shorts. +template <> struct DenseMapInfo { + static inline unsigned short getEmptyKey() { return 0xFFFF; } + static inline unsigned short getTombstoneKey() { return 0xFFFF - 1; } + static unsigned getHashValue(const unsigned short &Val) { return Val * 37U; } + + static bool isEqual(const unsigned short &LHS, const unsigned short &RHS) { + return LHS == RHS; + } +}; + // Provide DenseMapInfo for unsigned ints. template<> struct DenseMapInfo { static inline unsigned getEmptyKey() { return ~0U; } static inline unsigned getTombstoneKey() { return ~0U - 1; } static unsigned getHashValue(const unsigned& Val) { return Val * 37U; } + static bool isEqual(const unsigned& LHS, const unsigned& RHS) { return LHS == RHS; } @@ -104,9 +93,11 @@ template<> struct DenseMapInfo { template<> struct DenseMapInfo { static inline unsigned long getEmptyKey() { return ~0UL; } static inline unsigned long getTombstoneKey() { return ~0UL - 1L; } + static unsigned getHashValue(const unsigned long& Val) { return (unsigned)(Val * 37UL); } + static bool isEqual(const unsigned long& LHS, const unsigned long& RHS) { return LHS == RHS; } @@ -116,20 +107,31 @@ template<> struct DenseMapInfo { template<> struct DenseMapInfo { static inline unsigned long long getEmptyKey() { return ~0ULL; } static inline unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; } + static unsigned getHashValue(const unsigned long long& Val) { return (unsigned)(Val * 37ULL); } + static bool isEqual(const unsigned long long& LHS, const unsigned long long& RHS) { return LHS == RHS; } }; +// Provide DenseMapInfo for shorts. +template <> struct DenseMapInfo { + static inline short getEmptyKey() { return 0x7FFF; } + static inline short getTombstoneKey() { return -0x7FFF - 1; } + static unsigned getHashValue(const short &Val) { return Val * 37U; } + static bool isEqual(const short &LHS, const short &RHS) { return LHS == RHS; } +}; + // Provide DenseMapInfo for ints. template<> struct DenseMapInfo { static inline int getEmptyKey() { return 0x7fffffff; } static inline int getTombstoneKey() { return -0x7fffffff - 1; } static unsigned getHashValue(const int& Val) { return (unsigned)(Val * 37U); } + static bool isEqual(const int& LHS, const int& RHS) { return LHS == RHS; } @@ -140,10 +142,13 @@ template<> struct DenseMapInfo { static inline long getEmptyKey() { return (1UL << (sizeof(long) * 8 - 1)) - 1UL; } + static inline long getTombstoneKey() { return getEmptyKey() - 1L; } + static unsigned getHashValue(const long& Val) { return (unsigned)(Val * 37UL); } + static bool isEqual(const long& LHS, const long& RHS) { return LHS == RHS; } @@ -153,9 +158,11 @@ template<> struct DenseMapInfo { template<> struct DenseMapInfo { static inline long long getEmptyKey() { return 0x7fffffffffffffffLL; } static inline long long getTombstoneKey() { return -0x7fffffffffffffffLL-1; } + static unsigned getHashValue(const long long& Val) { return (unsigned)(Val * 37ULL); } + static bool isEqual(const long long& LHS, const long long& RHS) { return LHS == RHS; @@ -164,19 +171,21 @@ template<> struct DenseMapInfo { // Provide DenseMapInfo for all pairs whose members have info. template -struct DenseMapInfo > { - typedef std::pair Pair; - typedef DenseMapInfo FirstInfo; - typedef DenseMapInfo SecondInfo; +struct DenseMapInfo> { + using Pair = std::pair; + using FirstInfo = DenseMapInfo; + using SecondInfo = DenseMapInfo; static inline Pair getEmptyKey() { return std::make_pair(FirstInfo::getEmptyKey(), SecondInfo::getEmptyKey()); } + static inline Pair getTombstoneKey() { return std::make_pair(FirstInfo::getTombstoneKey(), SecondInfo::getTombstoneKey()); } + static unsigned getHashValue(const Pair& PairVal) { uint64_t key = (uint64_t)FirstInfo::getHashValue(PairVal.first) << 32 | (uint64_t)SecondInfo::getHashValue(PairVal.second); @@ -190,6 +199,7 @@ struct DenseMapInfo > { key ^= (key >> 31); return (unsigned)key; } + static bool isEqual(const Pair &LHS, const Pair &RHS) { return FirstInfo::isEqual(LHS.first, RHS.first) && SecondInfo::isEqual(LHS.second, RHS.second); @@ -202,16 +212,19 @@ template <> struct DenseMapInfo { return StringRef(reinterpret_cast(~static_cast(0)), 0); } + static inline StringRef getTombstoneKey() { return StringRef(reinterpret_cast(~static_cast(1)), 0); } + static unsigned getHashValue(StringRef Val) { assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!"); assert(Val.data() != getTombstoneKey().data() && "Cannot hash the tombstone key!"); return (unsigned)(hash_value(Val)); } + static bool isEqual(StringRef LHS, StringRef RHS) { if (RHS.data() == getEmptyKey().data()) return LHS.data() == getEmptyKey().data(); @@ -227,16 +240,19 @@ template struct DenseMapInfo> { return ArrayRef(reinterpret_cast(~static_cast(0)), size_t(0)); } + static inline ArrayRef getTombstoneKey() { return ArrayRef(reinterpret_cast(~static_cast(1)), size_t(0)); } + static unsigned getHashValue(ArrayRef Val) { assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!"); assert(Val.data() != getTombstoneKey().data() && "Cannot hash the tombstone key!"); return (unsigned)(hash_value(Val)); } + static bool isEqual(ArrayRef LHS, ArrayRef RHS) { if (RHS.data() == getEmptyKey().data()) return LHS.data() == getEmptyKey().data(); @@ -248,4 +264,4 @@ template struct DenseMapInfo> { } // end namespace wpi -#endif +#endif // LLVM_ADT_DENSEMAPINFO_H diff --git a/wpiutil/src/main/native/include/wpi/EpochTracker.h b/wpiutil/src/main/native/include/wpi/EpochTracker.h index 53d540de25..b26800b5e3 100644 --- a/wpiutil/src/main/native/include/wpi/EpochTracker.h +++ b/wpiutil/src/main/native/include/wpi/EpochTracker.h @@ -13,8 +13,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_EPOCH_TRACKER_H -#define LLVM_ADT_EPOCH_TRACKER_H +#ifndef WPIUTIL_WPI_EPOCH_TRACKER_H +#define WPIUTIL_WPI_EPOCH_TRACKER_H #include @@ -37,7 +37,7 @@ public: #else -/// \brief A base class for data structure classes wishing to make iterators +/// A base class for data structure classes wishing to make iterators /// ("handles") pointing into themselves fail-fast. When building without /// asserts, this class is empty and does nothing. /// @@ -52,15 +52,15 @@ class DebugEpochBase { public: DebugEpochBase() : Epoch(0) {} - /// \brief Calling incrementEpoch invalidates all handles pointing into the + /// Calling incrementEpoch invalidates all handles pointing into the /// calling instance. void incrementEpoch() { ++Epoch; } - /// \brief The destructor calls incrementEpoch to make use-after-free bugs + /// The destructor calls incrementEpoch to make use-after-free bugs /// more likely to crash deterministically. ~DebugEpochBase() { incrementEpoch(); } - /// \brief A base class for iterator classes ("handles") that wish to poll for + /// A base class for iterator classes ("handles") that wish to poll for /// iterator invalidating modifications in the underlying data structure. /// When LLVM is built without asserts, this class is empty and does nothing. /// @@ -78,12 +78,12 @@ public: explicit HandleBase(const DebugEpochBase *Parent) : EpochAddress(&Parent->Epoch), EpochAtCreation(Parent->Epoch) {} - /// \brief Returns true if the DebugEpochBase this Handle is linked to has + /// Returns true if the DebugEpochBase this Handle is linked to has /// not called incrementEpoch on itself since the creation of this /// HandleBase instance. bool isHandleInSync() const { return *EpochAddress == EpochAtCreation; } - /// \brief Returns a pointer to the epoch word stored in the data structure + /// Returns a pointer to the epoch word stored in the data structure /// this handle points into. Can be used to check if two iterators point /// into the same data structure. const void *getEpochAddress() const { return EpochAddress; } diff --git a/wpiutil/src/main/native/include/wpi/ErrorOr.h b/wpiutil/src/main/native/include/wpi/ErrorOr.h new file mode 100644 index 0000000000..1a878d16a4 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/ErrorOr.h @@ -0,0 +1,291 @@ +//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Provides ErrorOr smart pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef WPIUTIL_WPI_ERROROR_H +#define WPIUTIL_WPI_ERROROR_H + +#include "wpi/AlignOf.h" +#include +#include +#include +#include + +namespace wpi { + +/// Stores a reference that can be changed. +template +class ReferenceStorage { + T *Storage; + +public: + ReferenceStorage(T &Ref) : Storage(&Ref) {} + + operator T &() const { return *Storage; } + T &get() const { return *Storage; } +}; + +/// Represents either an error or a value T. +/// +/// ErrorOr is a pointer-like class that represents the result of an +/// operation. The result is either an error, or a value of type T. This is +/// designed to emulate the usage of returning a pointer where nullptr indicates +/// failure. However instead of just knowing that the operation failed, we also +/// have an error_code and optional user data that describes why it failed. +/// +/// It is used like the following. +/// \code +/// ErrorOr getBuffer(); +/// +/// auto buffer = getBuffer(); +/// if (error_code ec = buffer.getError()) +/// return ec; +/// buffer->write("adena"); +/// \endcode +/// +/// +/// Implicit conversion to bool returns true if there is a usable value. The +/// unary * and -> operators provide pointer like access to the value. Accessing +/// the value when there is an error has undefined behavior. +/// +/// When T is a reference type the behavior is slightly different. The reference +/// is held in a std::reference_wrapper::type>, and +/// there is special handling to make operator -> work as if T was not a +/// reference. +/// +/// T cannot be a rvalue reference. +template +class ErrorOr { + template friend class ErrorOr; + + static const bool isRef = std::is_reference::value; + + using wrap = ReferenceStorage::type>; + +public: + using storage_type = typename std::conditional::type; + +private: + using reference = typename std::remove_reference::type &; + using const_reference = const typename std::remove_reference::type &; + using pointer = typename std::remove_reference::type *; + using const_pointer = const typename std::remove_reference::type *; + +public: + template + ErrorOr(E ErrorCode, + typename std::enable_if::value || + std::is_error_condition_enum::value, + void *>::type = nullptr) + : HasError(true) { + new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); + } + + ErrorOr(std::error_code EC) : HasError(true) { + new (getErrorStorage()) std::error_code(EC); + } + + template + ErrorOr(OtherT &&Val, + typename std::enable_if::value>::type + * = nullptr) + : HasError(false) { + new (getStorage()) storage_type(std::forward(Val)); + } + + ErrorOr(const ErrorOr &Other) { + copyConstruct(Other); + } + + template + ErrorOr( + const ErrorOr &Other, + typename std::enable_if::value>::type * = + nullptr) { + copyConstruct(Other); + } + + template + explicit ErrorOr( + const ErrorOr &Other, + typename std::enable_if< + !std::is_convertible::value>::type * = nullptr) { + copyConstruct(Other); + } + + ErrorOr(ErrorOr &&Other) { + moveConstruct(std::move(Other)); + } + + template + ErrorOr( + ErrorOr &&Other, + typename std::enable_if::value>::type * = + nullptr) { + moveConstruct(std::move(Other)); + } + + // This might eventually need SFINAE but it's more complex than is_convertible + // & I'm too lazy to write it right now. + template + explicit ErrorOr( + ErrorOr &&Other, + typename std::enable_if::value>::type * = + nullptr) { + moveConstruct(std::move(Other)); + } + + ErrorOr &operator=(const ErrorOr &Other) { + copyAssign(Other); + return *this; + } + + ErrorOr &operator=(ErrorOr &&Other) { + moveAssign(std::move(Other)); + return *this; + } + + ~ErrorOr() { + if (!HasError) + getStorage()->~storage_type(); + } + + /// Return false if there is an error. + explicit operator bool() const { + return !HasError; + } + + reference get() { return *getStorage(); } + const_reference get() const { return const_cast *>(this)->get(); } + + std::error_code getError() const { + return HasError ? *getErrorStorage() : std::error_code(); + } + + pointer operator ->() { + return toPointer(getStorage()); + } + + const_pointer operator->() const { return toPointer(getStorage()); } + + reference operator *() { + return *getStorage(); + } + + const_reference operator*() const { return *getStorage(); } + +private: + template + void copyConstruct(const ErrorOr &Other) { + if (!Other.HasError) { + // Get the other value. + HasError = false; + new (getStorage()) storage_type(*Other.getStorage()); + } else { + // Get other's error. + HasError = true; + new (getErrorStorage()) std::error_code(Other.getError()); + } + } + + template + static bool compareThisIfSameType(const T1 &a, const T1 &b) { + return &a == &b; + } + + template + static bool compareThisIfSameType(const T1 &a, const T2 &b) { + return false; + } + + template + void copyAssign(const ErrorOr &Other) { + if (compareThisIfSameType(*this, Other)) + return; + + this->~ErrorOr(); + new (this) ErrorOr(Other); + } + + template + void moveConstruct(ErrorOr &&Other) { + if (!Other.HasError) { + // Get the other value. + HasError = false; + new (getStorage()) storage_type(std::move(*Other.getStorage())); + } else { + // Get other's error. + HasError = true; + new (getErrorStorage()) std::error_code(Other.getError()); + } + } + + template + void moveAssign(ErrorOr &&Other) { + if (compareThisIfSameType(*this, Other)) + return; + + this->~ErrorOr(); + new (this) ErrorOr(std::move(Other)); + } + + pointer toPointer(pointer Val) { + return Val; + } + + const_pointer toPointer(const_pointer Val) const { return Val; } + + pointer toPointer(wrap *Val) { + return &Val->get(); + } + + const_pointer toPointer(const wrap *Val) const { return &Val->get(); } + + storage_type *getStorage() { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast(TStorage.buffer); + } + + const storage_type *getStorage() const { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast(TStorage.buffer); + } + + std::error_code *getErrorStorage() { + assert(HasError && "Cannot get error when a value exists!"); + return reinterpret_cast(ErrorStorage.buffer); + } + + const std::error_code *getErrorStorage() const { + return const_cast *>(this)->getErrorStorage(); + } + + union { + AlignedCharArrayUnion TStorage; + AlignedCharArrayUnion ErrorStorage; + }; + bool HasError : 1; +}; + +template +typename std::enable_if::value || + std::is_error_condition_enum::value, + bool>::type +operator==(const ErrorOr &Err, E Code) { + return Err.getError() == Code; +} + +} // end namespace wpi + +#endif // LLVM_SUPPORT_ERROROR_H diff --git a/wpiutil/src/main/native/include/wpi/FileSystem.h b/wpiutil/src/main/native/include/wpi/FileSystem.h index b07c106160..626aaaab8e 100644 --- a/wpiutil/src/main/native/include/wpi/FileSystem.h +++ b/wpiutil/src/main/native/include/wpi/FileSystem.h @@ -24,21 +24,25 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_FILESYSTEM_H -#define LLVM_SUPPORT_FILESYSTEM_H +#ifndef WPIUTIL_WPI_FILESYSTEM_H +#define WPIUTIL_WPI_FILESYSTEM_H -#include "wpi/IntrusiveRefCntPtr.h" #include "wpi/SmallString.h" +#include "wpi/StringRef.h" #include "wpi/Twine.h" - -#include - +#include "wpi/ErrorOr.h" +#include +#include #include +#include #include +#include #include #include #include +#include + namespace wpi { namespace sys { namespace fs { @@ -78,6 +82,7 @@ enum perms { set_uid_on_exe = 04000, set_gid_on_exe = 02000, sticky_bit = 01000, + all_perms = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, perms_not_known = 0xFFFF }; @@ -99,8 +104,9 @@ inline perms &operator&=(perms &l, perms r) { return l; } inline perms operator~(perms x) { + // Avoid UB by explicitly truncating the (unsigned) ~ result. return static_cast( - static_cast(~static_cast(x))); + static_cast(~static_cast(x))); } class UniqueID { @@ -110,6 +116,7 @@ class UniqueID { public: UniqueID() = default; UniqueID(uint64_t Device, uint64_t File) : Device(Device), File(File) {} + bool operator==(const UniqueID &Other) const { return Device == Other.Device && File == Other.File; } @@ -117,99 +124,75 @@ public: bool operator<(const UniqueID &Other) const { return std::tie(Device, File) < std::tie(Other.Device, Other.File); } + uint64_t getDevice() const { return Device; } uint64_t getFile() const { return File; } }; -/// file_status - Represents the result of a call to stat and friends. It has -/// a platform-specific member to store the result. -class file_status -{ - #ifdef _WIN32 - uint32_t LastAccessedTimeHigh; - uint32_t LastAccessedTimeLow; - uint32_t LastWriteTimeHigh; - uint32_t LastWriteTimeLow; - uint32_t VolumeSerialNumber; - uint32_t FileSizeHigh; - uint32_t FileSizeLow; - uint32_t FileIndexHigh; - uint32_t FileIndexLow; +/// Represents the result of a call to directory_iterator::status(). This is a +/// subset of the information returned by a regular sys::fs::status() call, and +/// represents the information provided by Windows FileFirstFile/FindNextFile. +class basic_file_status { +protected: + #ifndef _WIN32 + time_t fs_st_atime = 0; + time_t fs_st_mtime = 0; + uid_t fs_st_uid = 0; + gid_t fs_st_gid = 0; + off_t fs_st_size = 0; #else - dev_t fs_st_dev; - ino_t fs_st_ino; - time_t fs_st_atime; - time_t fs_st_mtime; - uid_t fs_st_uid; - gid_t fs_st_gid; - off_t fs_st_size; + uint32_t LastAccessedTimeHigh = 0; + uint32_t LastAccessedTimeLow = 0; + uint32_t LastWriteTimeHigh = 0; + uint32_t LastWriteTimeLow = 0; + uint32_t FileSizeHigh = 0; + uint32_t FileSizeLow = 0; #endif - friend bool equivalent(file_status A, file_status B); - file_type Type; - perms Perms; + file_type Type = file_type::status_error; + perms Perms = perms_not_known; public: - #ifdef _WIN32 - file_status() - : LastAccessedTimeHigh(0), LastAccessedTimeLow(0), LastWriteTimeHigh(0), - LastWriteTimeLow(0), VolumeSerialNumber(0), FileSizeHigh(0), - FileSizeLow(0), FileIndexHigh(0), FileIndexLow(0), - Type(file_type::status_error), Perms(perms_not_known) {} + basic_file_status() = default; - file_status(file_type Type) - : LastAccessedTimeHigh(0), LastAccessedTimeLow(0), LastWriteTimeHigh(0), - LastWriteTimeLow(0), VolumeSerialNumber(0), FileSizeHigh(0), - FileSizeLow(0), FileIndexHigh(0), FileIndexLow(0), Type(Type), - Perms(perms_not_known) {} + explicit basic_file_status(file_type Type) : Type(Type) {} - file_status(file_type Type, uint32_t LastAccessTimeHigh, - uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh, - uint32_t LastWriteTimeLow, uint32_t VolumeSerialNumber, - uint32_t FileSizeHigh, uint32_t FileSizeLow, - uint32_t FileIndexHigh, uint32_t FileIndexLow) - : LastAccessedTimeHigh(LastAccessTimeHigh), LastAccessedTimeLow(LastAccessTimeLow), - LastWriteTimeHigh(LastWriteTimeHigh), - LastWriteTimeLow(LastWriteTimeLow), - VolumeSerialNumber(VolumeSerialNumber), FileSizeHigh(FileSizeHigh), - FileSizeLow(FileSizeLow), FileIndexHigh(FileIndexHigh), - FileIndexLow(FileIndexLow), Type(Type), Perms(perms_not_known) {} + #ifndef _WIN32 + basic_file_status(file_type Type, perms Perms, time_t ATime, time_t MTime, + uid_t UID, gid_t GID, off_t Size) + : fs_st_atime(ATime), fs_st_mtime(MTime), fs_st_uid(UID), fs_st_gid(GID), + fs_st_size(Size), Type(Type), Perms(Perms) {} #else - file_status() - : fs_st_dev(0), fs_st_ino(0), fs_st_atime(0), fs_st_mtime(0), - fs_st_uid(0), fs_st_gid(0), fs_st_size(0), - Type(file_type::status_error), Perms(perms_not_known) {} - - file_status(file_type Type) - : fs_st_dev(0), fs_st_ino(0), fs_st_atime(0), fs_st_mtime(0), - fs_st_uid(0), fs_st_gid(0), fs_st_size(0), Type(Type), - Perms(perms_not_known) {} - - file_status(file_type Type, perms Perms, dev_t Dev, ino_t Ino, time_t ATime, - time_t MTime, uid_t UID, gid_t GID, off_t Size) - : fs_st_dev(Dev), fs_st_ino(Ino), fs_st_atime(ATime), fs_st_mtime(MTime), - fs_st_uid(UID), fs_st_gid(GID), fs_st_size(Size), Type(Type), - Perms(Perms) {} + basic_file_status(file_type Type, perms Perms, uint32_t LastAccessTimeHigh, + uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh, + uint32_t LastWriteTimeLow, uint32_t FileSizeHigh, + uint32_t FileSizeLow) + : LastAccessedTimeHigh(LastAccessTimeHigh), + LastAccessedTimeLow(LastAccessTimeLow), + LastWriteTimeHigh(LastWriteTimeHigh), + LastWriteTimeLow(LastWriteTimeLow), FileSizeHigh(FileSizeHigh), + FileSizeLow(FileSizeLow), Type(Type), Perms(Perms) {} #endif // getters file_type type() const { return Type; } perms permissions() const { return Perms; } - UniqueID getUniqueID() const; - #ifdef _WIN32 - uint32_t getUser() const { - return 9999; // Not applicable to Windows, so... - } - uint32_t getGroup() const { - return 9999; // Not applicable to Windows, so... - } - uint64_t getSize() const { - return (uint64_t(FileSizeHigh) << 32) + FileSizeLow; - } - #else + #ifndef _WIN32 uint32_t getUser() const { return fs_st_uid; } uint32_t getGroup() const { return fs_st_gid; } uint64_t getSize() const { return fs_st_size; } + #else + uint32_t getUser() const { + return 9999; // Not applicable to Windows, so... + } + + uint32_t getGroup() const { + return 9999; // Not applicable to Windows, so... + } + + uint64_t getSize() const { + return (uint64_t(FileSizeHigh) << 32) + FileSizeLow; + } #endif // setters @@ -217,11 +200,54 @@ public: void permissions(perms p) { Perms = p; } }; +/// Represents the result of a call to sys::fs::status(). +class file_status : public basic_file_status { + friend bool equivalent(file_status A, file_status B); + + #ifndef _WIN32 + dev_t fs_st_dev = 0; + nlink_t fs_st_nlinks = 0; + ino_t fs_st_ino = 0; + #else + uint32_t NumLinks = 0; + uint32_t VolumeSerialNumber = 0; + uint32_t FileIndexHigh = 0; + uint32_t FileIndexLow = 0; + #endif + +public: + file_status() = default; + + explicit file_status(file_type Type) : basic_file_status(Type) {} + + #ifndef _WIN32 + file_status(file_type Type, perms Perms, dev_t Dev, nlink_t Links, ino_t Ino, + time_t ATime, time_t MTime, uid_t UID, gid_t GID, off_t Size) + : basic_file_status(Type, Perms, ATime, MTime, UID, GID, Size), + fs_st_dev(Dev), fs_st_nlinks(Links), fs_st_ino(Ino) {} + #else + file_status(file_type Type, perms Perms, uint32_t LinkCount, + uint32_t LastAccessTimeHigh, uint32_t LastAccessTimeLow, + uint32_t LastWriteTimeHigh, uint32_t LastWriteTimeLow, + uint32_t VolumeSerialNumber, uint32_t FileSizeHigh, + uint32_t FileSizeLow, uint32_t FileIndexHigh, + uint32_t FileIndexLow) + : basic_file_status(Type, Perms, LastAccessTimeHigh, LastAccessTimeLow, + LastWriteTimeHigh, LastWriteTimeLow, FileSizeHigh, + FileSizeLow), + NumLinks(LinkCount), VolumeSerialNumber(VolumeSerialNumber), + FileIndexHigh(FileIndexHigh), FileIndexLow(FileIndexLow) {} + #endif + + UniqueID getUniqueID() const; + uint32_t getLinkCount() const; +}; + /// @} /// @name Physical Operators /// @{ -/// @brief Make \a path an absolute path. +/// Make \a path an absolute path. /// /// Makes \a path absolute using the \a current_directory if it is not already. /// An empty \a path will result in the \a current_directory. @@ -235,7 +261,7 @@ public: std::error_code make_absolute(const Twine ¤t_directory, SmallVectorImpl &path); -/// @brief Make \a path an absolute path. +/// Make \a path an absolute path. /// /// Makes \a path absolute using the current directory if it is not already. An /// empty \a path will result in the current directory. @@ -248,7 +274,7 @@ std::error_code make_absolute(const Twine ¤t_directory, /// platform-specific error_code. std::error_code make_absolute(SmallVectorImpl &path); -/// @brief Get the current path. +/// Get the current path. /// /// @param result Holds the current path on return. /// @returns errc::success if the current path has been stored in result, @@ -259,23 +285,23 @@ std::error_code current_path(SmallVectorImpl &result); /// @name Physical Observers /// @{ -/// @brief Does file exist? +/// Does file exist? /// -/// @param status A file_status previously returned from stat. +/// @param status A basic_file_status previously returned from stat. /// @returns True if the file represented by status exists, false if it does /// not. -bool exists(file_status status); +bool exists(const basic_file_status &status); enum class AccessMode { Exist, Write, Execute }; -/// @brief Can the file be accessed? +/// Can the file be accessed? /// /// @param Path Input path. /// @returns errc::success if the path can be accessed, otherwise a /// platform-specific error_code. std::error_code access(const Twine &Path, AccessMode Mode); -/// @brief Does file exist? +/// Does file exist? /// /// @param Path Input path. /// @returns True if it exists, false otherwise. @@ -283,7 +309,7 @@ inline bool exists(const Twine &Path) { return !access(Path, AccessMode::Exist); } -/// @brief Can we write this file? +/// Can we write this file? /// /// @param Path Input path. /// @returns True if we can write to it, false otherwise. @@ -291,7 +317,7 @@ inline bool can_write(const Twine &Path) { return !access(Path, AccessMode::Write); } -/// @brief Do file_status's represent the same thing? +/// Do file_status's represent the same thing? /// /// @param A Input file_status. /// @param B Input file_status. @@ -302,7 +328,7 @@ inline bool can_write(const Twine &Path) { /// otherwise. bool equivalent(file_status A, file_status B); -/// @brief Do paths represent the same thing? +/// Do paths represent the same thing? /// /// assert(status_known(A) || status_known(B)); /// @@ -314,51 +340,51 @@ bool equivalent(file_status A, file_status B); /// platform-specific error_code. std::error_code equivalent(const Twine &A, const Twine &B, bool &result); -/// @brief Simpler version of equivalent for clients that don't need to +/// Simpler version of equivalent for clients that don't need to /// differentiate between an error and false. inline bool equivalent(const Twine &A, const Twine &B) { bool result; return !equivalent(A, B, result) && result; } -/// @brief Does status represent a directory? +/// Does status represent a directory? /// -/// @param status A file_status previously returned from status. +/// @param status A basic_file_status previously returned from status. /// @returns status.type() == file_type::directory_file. -bool is_directory(file_status status); +bool is_directory(const basic_file_status &status); -/// @brief Is path a directory? +/// Is path a directory? /// /// @param path Input path. -/// @param result Set to true if \a path is a directory, false if it is not. -/// Undefined otherwise. +/// @param result Set to true if \a path is a directory (after following +/// symlinks, false if it is not. Undefined otherwise. /// @returns errc::success if result has been successfully set, otherwise a /// platform-specific error_code. std::error_code is_directory(const Twine &path, bool &result); -/// @brief Simpler version of is_directory for clients that don't need to +/// Simpler version of is_directory for clients that don't need to /// differentiate between an error and false. inline bool is_directory(const Twine &Path) { bool Result; return !is_directory(Path, Result) && Result; } -/// @brief Does status represent a regular file? +/// Does status represent a regular file? /// -/// @param status A file_status previously returned from status. +/// @param status A basic_file_status previously returned from status. /// @returns status_known(status) && status.type() == file_type::regular_file. -bool is_regular_file(file_status status); +bool is_regular_file(const basic_file_status &status); -/// @brief Is path a regular file? +/// Is path a regular file? /// /// @param path Input path. -/// @param result Set to true if \a path is a regular file, false if it is not. -/// Undefined otherwise. +/// @param result Set to true if \a path is a regular file (after following +/// symlinks), false if it is not. Undefined otherwise. /// @returns errc::success if result has been successfully set, otherwise a /// platform-specific error_code. std::error_code is_regular_file(const Twine &path, bool &result); -/// @brief Simpler version of is_regular_file for clients that don't need to +/// Simpler version of is_regular_file for clients that don't need to /// differentiate between an error and false. inline bool is_regular_file(const Twine &Path) { bool Result; @@ -367,14 +393,38 @@ inline bool is_regular_file(const Twine &Path) { return Result; } -/// @brief Does this status represent something that exists but is not a -/// directory, regular file, or symlink? +/// Does status represent a symlink file? /// -/// @param status A file_status previously returned from status. -/// @returns exists(s) && !is_regular_file(s) && !is_directory(s) -bool is_other(file_status status); +/// @param status A basic_file_status previously returned from status. +/// @returns status_known(status) && status.type() == file_type::symlink_file. +bool is_symlink_file(const basic_file_status &status); -/// @brief Is path something that exists but is not a directory, +/// Is path a symlink file? +/// +/// @param path Input path. +/// @param result Set to true if \a path is a symlink file, false if it is not. +/// Undefined otherwise. +/// @returns errc::success if result has been successfully set, otherwise a +/// platform-specific error_code. +std::error_code is_symlink_file(const Twine &path, bool &result); + +/// Simpler version of is_symlink_file for clients that don't need to +/// differentiate between an error and false. +inline bool is_symlink_file(const Twine &Path) { + bool Result; + if (is_symlink_file(Path, Result)) + return false; + return Result; +} + +/// Does this status represent something that exists but is not a +/// directory or regular file? +/// +/// @param status A basic_file_status previously returned from status. +/// @returns exists(s) && !is_regular_file(s) && !is_directory(s) +bool is_other(const basic_file_status &status); + +/// Is path something that exists but is not a directory, /// regular file, or symlink? /// /// @param path Input path. @@ -384,24 +434,27 @@ bool is_other(file_status status); /// platform-specific error_code. std::error_code is_other(const Twine &path, bool &result); -/// @brief Get file status as if by POSIX stat(). +/// Get file status as if by POSIX stat(). /// /// @param path Input path. /// @param result Set to the file status. +/// @param follow When true, follows symlinks. Otherwise, the symlink itself is +/// statted. /// @returns errc::success if result has been successfully set, otherwise a /// platform-specific error_code. -std::error_code status(const Twine &path, file_status &result); +std::error_code status(const Twine &path, file_status &result, + bool follow = true); -/// @brief A version for when a file descriptor is already available. +/// A version for when a file descriptor is already available. std::error_code status(int FD, file_status &Result); -/// @brief Is status available? +/// Is status available? /// /// @param s Input file status. /// @returns True if status() != status_error. -bool status_known(file_status s); +bool status_known(const basic_file_status &s); -/// @brief Is status available? +/// Is status available? /// /// @param path Input path. /// @param result Set to true if status() != status_error. @@ -421,12 +474,20 @@ enum OpenFlags : unsigned { /// with F_Excl. F_Append = 2, + /// F_NoTrunc - When opening a file, if it already exists don't truncate + /// the file contents. F_Append implies F_NoTrunc, but F_Append seeks to + /// the end of the file, which F_NoTrunc doesn't. + F_NoTrunc = 4, + /// The file should be opened in text mode on platforms that make this /// distinction. - F_Text = 4, + F_Text = 8, /// Open the file for read and write. - F_RW = 8 + F_RW = 16, + + /// Delete the file on close. Only makes a difference on windows. + F_Delete = 32 }; inline OpenFlags operator|(OpenFlags A, OpenFlags B) { @@ -438,11 +499,41 @@ inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) { return A; } +/// @brief Opens the file with the given name in a write-only or read-write +/// mode, returning its open file descriptor. If the file does not exist, it +/// is created. +/// +/// The caller is responsible for closing the file descriptor once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param ResultFD If the file could be opened successfully, its descriptor +/// is stored in this location. Otherwise, this is set to -1. +/// @param Flags Additional flags used to determine whether the file should be +/// opened in, for example, read-write or in write-only mode. +/// @param Mode The access permissions of the file, represented in octal. +/// @returns errc::success if \a Name has been opened, otherwise a +/// platform-specific error_code. std::error_code openFileForWrite(const Twine &Name, int &ResultFD, OpenFlags Flags, unsigned Mode = 0666); +/// @brief Opens the file with the given name in a read-only mode, returning +/// its open file descriptor. +/// +/// The caller is responsible for closing the file descriptor once they are +/// finished with it. +/// +/// @param Name The path of the file to open, relative or absolute. +/// @param ResultFD If the file could be opened successfully, its descriptor +/// is stored in this location. Otherwise, this is set to -1. +/// @param RealPath If nonnull, extra work is done to determine the real path +/// of the opened file, and that path is stored in this +/// location. +/// @returns errc::success if \a Name has been opened, otherwise a +/// platform-specific error_code. std::error_code openFileForRead(const Twine &Name, int &ResultFD, SmallVectorImpl *RealPath = nullptr); + std::error_code getUniqueID(const Twine Path, UniqueID &Result); /// @} @@ -454,24 +545,26 @@ std::error_code getUniqueID(const Twine Path, UniqueID &Result); /// called. class directory_entry { std::string Path; - mutable file_status Status; + bool FollowSymlinks; + basic_file_status Status; public: - explicit directory_entry(const Twine &path, file_status st = file_status()) - : Path(path.str()) - , Status(st) {} + explicit directory_entry(const Twine &path, bool follow_symlinks = true, + basic_file_status st = basic_file_status()) + : Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {} - directory_entry() {} + directory_entry() = default; - void assign(const Twine &path, file_status st = file_status()) { + void assign(const Twine &path, basic_file_status st = basic_file_status()) { Path = path.str(); Status = st; } - void replace_filename(const Twine &filename, file_status st = file_status()); + void replace_filename(const Twine &filename, + basic_file_status st = basic_file_status()); const std::string &path() const { return Path; } - std::error_code status(file_status &result) const; + ErrorOr status() const; bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; } bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); } @@ -482,52 +575,59 @@ public: }; namespace detail { + struct DirIterState; - std::error_code directory_iterator_construct(DirIterState &, StringRef); + std::error_code directory_iterator_construct(DirIterState &, StringRef, bool); std::error_code directory_iterator_increment(DirIterState &); std::error_code directory_iterator_destruct(DirIterState &); - /// DirIterState - Keeps state for the directory_iterator. It is reference - /// counted in order to preserve InputIterator semantics on copy. - struct DirIterState : public RefCountedBase { - DirIterState() - : IterationHandle(0) {} - + /// Keeps state for the directory_iterator. + struct DirIterState { ~DirIterState() { directory_iterator_destruct(*this); } - intptr_t IterationHandle; + intptr_t IterationHandle = 0; directory_entry CurrentEntry; }; + } // end namespace detail /// directory_iterator - Iterates through the entries in path. There is no /// operator++ because we need an error_code. If it's really needed we can make /// it call report_fatal_error on error. class directory_iterator { - IntrusiveRefCntPtr State; + std::shared_ptr State; + bool FollowSymlinks = true; public: - explicit directory_iterator(const Twine &path, std::error_code &ec) { - State = new detail::DirIterState; + explicit directory_iterator(const Twine &path, std::error_code &ec, + bool follow_symlinks = true) + : FollowSymlinks(follow_symlinks) { + State = std::make_shared(); SmallString<128> path_storage; - ec = detail::directory_iterator_construct(*State, - path.toStringRef(path_storage)); + ec = detail::directory_iterator_construct( + *State, path.toStringRef(path_storage), FollowSymlinks); + update_error_code_for_current_entry(ec); } - explicit directory_iterator(const directory_entry &de, std::error_code &ec) { - State = new detail::DirIterState; - ec = detail::directory_iterator_construct(*State, de.path()); + explicit directory_iterator(const directory_entry &de, std::error_code &ec, + bool follow_symlinks = true) + : FollowSymlinks(follow_symlinks) { + State = std::make_shared(); + ec = detail::directory_iterator_construct( + *State, de.path(), FollowSymlinks); + update_error_code_for_current_entry(ec); } /// Construct end iterator. - directory_iterator() : State(nullptr) {} + directory_iterator() = default; // No operator++ because we need error_code. directory_iterator &increment(std::error_code &ec) { ec = directory_iterator_increment(*State); + update_error_code_for_current_entry(ec); return *this; } @@ -549,47 +649,64 @@ public: } // Other members as required by // C++ Std, 24.1.1 Input iterators [input.iterators] + +private: + // Checks if current entry is valid and populates error code. For example, + // current entry may not exist due to broken symbol links. + void update_error_code_for_current_entry(std::error_code &ec) { + // Bail out if error has already occured earlier to avoid overwriting it. + if (ec) + return; + + // Empty directory entry is used to mark the end of an interation, it's not + // an error. + if (State->CurrentEntry == directory_entry()) + return; + + ErrorOr status = State->CurrentEntry.status(); + if (!status) + ec = status.getError(); + } }; namespace detail { - /// RecDirIterState - Keeps state for the recursive_directory_iterator. It is - /// reference counted in order to preserve InputIterator semantics on copy. - struct RecDirIterState : public RefCountedBase { - RecDirIterState() - : Level(0) - , HasNoPushRequest(false) {} - std::stack > Stack; - uint16_t Level; - bool HasNoPushRequest; + /// Keeps state for the recursive_directory_iterator. + struct RecDirIterState { + std::stack> Stack; + uint16_t Level = 0; + bool HasNoPushRequest = false; }; + } // end namespace detail /// recursive_directory_iterator - Same as directory_iterator except for it /// recurses down into child directories. class recursive_directory_iterator { - IntrusiveRefCntPtr State; + std::shared_ptr State; + bool Follow; public: - recursive_directory_iterator() {} - explicit recursive_directory_iterator(const Twine &path, std::error_code &ec) - : State(new detail::RecDirIterState) { - State->Stack.push(directory_iterator(path, ec)); + recursive_directory_iterator() = default; + explicit recursive_directory_iterator(const Twine &path, std::error_code &ec, + bool follow_symlinks = true) + : State(std::make_shared()), + Follow(follow_symlinks) { + State->Stack.push(directory_iterator(path, ec, Follow)); if (State->Stack.top() == directory_iterator()) State.reset(); } + // No operator++ because we need error_code. recursive_directory_iterator &increment(std::error_code &ec) { - const directory_iterator end_itr; + const directory_iterator end_itr = {}; if (State->HasNoPushRequest) State->HasNoPushRequest = false; else { - file_status st; - if ((ec = State->Stack.top()->status(st))) return *this; - if (is_directory(st)) { - State->Stack.push(directory_iterator(*State->Stack.top(), ec)); - if (ec) return *this; + ErrorOr status = State->Stack.top()->status(); + if (status && is_directory(*status)) { + State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow)); if (State->Stack.top() != end_itr) { ++State->Level; return *this; @@ -627,7 +744,7 @@ public: assert(State && "Cannot pop an end iterator!"); assert(State->Level > 0 && "Cannot pop an iterator with level < 1"); - const directory_iterator end_itr; + const directory_iterator end_itr = {}; std::error_code ec; do { if (ec) { diff --git a/wpiutil/src/main/native/include/wpi/Format.h b/wpiutil/src/main/native/include/wpi/Format.h index b8537af741..a96a709d12 100644 --- a/wpiutil/src/main/native/include/wpi/Format.h +++ b/wpiutil/src/main/native/include/wpi/Format.h @@ -20,9 +20,10 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_FORMAT_H -#define LLVM_SUPPORT_FORMAT_H +#ifndef WPIUTIL_WPI_FORMAT_H +#define WPIUTIL_WPI_FORMAT_H +#include "wpi/ArrayRef.h" #include "wpi/STLExtras.h" #include "wpi/StringRef.h" #include @@ -71,10 +72,20 @@ public: }; /// These are templated helper classes used by the format function that -/// capture the object to be formated and the format string. When actually +/// capture the object to be formatted and the format string. When actually /// printed, this synthesizes the string into a temporary buffer provided and /// returns whether or not it is big enough. +// Helper to validate that format() parameters are scalars or pointers. +template struct validate_format_parameters; +template +struct validate_format_parameters { + static_assert(std::is_scalar::value, + "format can't be used with non fundamental / non pointer type"); + validate_format_parameters() { validate_format_parameters(); } +}; +template <> struct validate_format_parameters<> {}; + template class format_object final : public format_object_base { std::tuple Vals; @@ -98,7 +109,9 @@ class format_object final : public format_object_base { public: format_object(const char *fmt, const Ts &... vals) - : format_object_base(fmt), Vals(vals...) {} + : format_object_base(fmt), Vals(vals...) { + validate_format_parameters(); + } int snprint(char *Buffer, unsigned BufferSize) const override { return snprint_tuple(Buffer, BufferSize, index_sequence_for()); @@ -119,30 +132,39 @@ inline format_object format(const char *Fmt, const Ts &... Vals) { return format_object(Fmt, Vals...); } -/// This is a helper class used for left_justify() and right_justify(). +/// This is a helper class for left_justify, right_justify, and center_justify. class FormattedString { +public: + enum Justification { JustifyNone, JustifyLeft, JustifyRight, JustifyCenter }; + FormattedString(StringRef S, unsigned W, Justification J) + : Str(S), Width(W), Justify(J) {} + +private: StringRef Str; unsigned Width; - bool RightJustify; + Justification Justify; friend class raw_ostream; - -public: - FormattedString(StringRef S, unsigned W, bool R) - : Str(S), Width(W), RightJustify(R) { } }; /// left_justify - append spaces after string so total output is /// \p Width characters. If \p Str is larger that \p Width, full string /// is written with no padding. inline FormattedString left_justify(StringRef Str, unsigned Width) { - return FormattedString(Str, Width, false); + return FormattedString(Str, Width, FormattedString::JustifyLeft); } /// right_justify - add spaces before string so total output is /// \p Width characters. If \p Str is larger that \p Width, full string /// is written with no padding. inline FormattedString right_justify(StringRef Str, unsigned Width) { - return FormattedString(Str, Width, true); + return FormattedString(Str, Width, FormattedString::JustifyRight); +} + +/// center_justify - add spaces before and after string so total output is +/// \p Width characters. If \p Str is larger that \p Width, full string +/// is written with no padding. +inline FormattedString center_justify(StringRef Str, unsigned Width) { + return FormattedString(Str, Width, FormattedString::JustifyCenter); } /// This is a helper class used for format_hex() and format_decimal(). @@ -197,6 +219,46 @@ inline FormattedNumber format_decimal(int64_t N, unsigned Width) { return FormattedNumber(0, N, Width, false, false, false); } +class FormattedBytes { + ArrayRef Bytes; + + // If not None, display offsets for each line relative to starting value. + Optional FirstByteOffset; + uint32_t IndentLevel; // Number of characters to indent each line. + uint32_t NumPerLine; // Number of bytes to show per line. + uint8_t ByteGroupSize; // How many hex bytes are grouped without spaces + bool Upper; // Show offset and hex bytes as upper case. + bool ASCII; // Show the ASCII bytes for the hex bytes to the right. + friend class raw_ostream; + +public: + FormattedBytes(ArrayRef B, uint32_t IL, Optional O, + uint32_t NPL, uint8_t BGS, bool U, bool A) + : Bytes(B), FirstByteOffset(O), IndentLevel(IL), NumPerLine(NPL), + ByteGroupSize(BGS), Upper(U), ASCII(A) { + + if (ByteGroupSize > NumPerLine) + ByteGroupSize = NumPerLine; + } +}; + +inline FormattedBytes +format_bytes(ArrayRef Bytes, Optional FirstByteOffset = None, + uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4, + uint32_t IndentLevel = 0, bool Upper = false) { + return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine, + ByteGroupSize, Upper, false); +} + +inline FormattedBytes +format_bytes_with_ascii(ArrayRef Bytes, + Optional FirstByteOffset = None, + uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4, + uint32_t IndentLevel = 0, bool Upper = false) { + return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine, + ByteGroupSize, Upper, true); +} + } // end namespace wpi #endif diff --git a/wpiutil/src/main/native/include/wpi/Hashing.h b/wpiutil/src/main/native/include/wpi/Hashing.h index 06cec62c78..8ae30d1509 100644 --- a/wpiutil/src/main/native/include/wpi/Hashing.h +++ b/wpiutil/src/main/native/include/wpi/Hashing.h @@ -42,8 +42,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_HASHING_H -#define LLVM_ADT_HASHING_H +#ifndef WPIUTIL_WPI_HASHING_H +#define WPIUTIL_WPI_HASHING_H #include "wpi/type_traits.h" #include @@ -55,7 +55,7 @@ namespace wpi { -/// \brief An opaque object representing a hash code. +/// An opaque object representing a hash code. /// /// This object represents the result of hashing some entity. It is intended to /// be used to implement hashtables or other hashing-based data structures. @@ -71,14 +71,14 @@ class hash_code { size_t value; public: - /// \brief Default construct a hash_code. + /// Default construct a hash_code. /// Note that this leaves the value uninitialized. hash_code() = default; - /// \brief Form a hash code directly from a numerical value. + /// Form a hash code directly from a numerical value. hash_code(size_t value) : value(value) {} - /// \brief Convert the hash code to its numerical value for use. + /// Convert the hash code to its numerical value for use. /*explicit*/ operator size_t() const { return value; } friend bool operator==(const hash_code &lhs, const hash_code &rhs) { @@ -88,11 +88,11 @@ public: return lhs.value != rhs.value; } - /// \brief Allow a hash_code to be directly run through hash_value. + /// Allow a hash_code to be directly run through hash_value. friend size_t hash_value(const hash_code &code) { return code.value; } }; -/// \brief Compute a hash_code for any integer value. +/// Compute a hash_code for any integer value. /// /// Note that this function is intended to compute the same hash_code for /// a particular value without regard to the pre-promotion type. This is in @@ -103,21 +103,21 @@ template typename std::enable_if::value, hash_code>::type hash_value(T value); -/// \brief Compute a hash_code for a pointer's address. +/// Compute a hash_code for a pointer's address. /// /// N.B.: This hashes the *address*. Not the value and not the type. template hash_code hash_value(const T *ptr); -/// \brief Compute a hash_code for a pair of objects. +/// Compute a hash_code for a pair of objects. template hash_code hash_value(const std::pair &arg); -/// \brief Compute a hash_code for a standard string. +/// Compute a hash_code for a standard string. template hash_code hash_value(const std::basic_string &arg); -/// \brief Override the execution seed with a fixed value. +/// Override the execution seed with a fixed value. /// /// This hashing library uses a per-execution seed designed to change on each /// run with high probability in order to ensure that the hash codes are not @@ -162,7 +162,7 @@ static const uint64_t k1 = 0xb492b66fbe98f273ULL; static const uint64_t k2 = 0x9ae16a3b2f90404fULL; static const uint64_t k3 = 0xc949d7c7509e6557ULL; -/// \brief Bitwise right rotate. +/// Bitwise right rotate. /// Normally this will compile to a single instruction, especially if the /// shift is a manifest constant. inline uint64_t rotate(uint64_t val, size_t shift) { @@ -252,13 +252,13 @@ inline uint64_t hash_short(const char *s, size_t length, uint64_t seed) { return k2 ^ seed; } -/// \brief The intermediate state used during hashing. +/// The intermediate state used during hashing. /// Currently, the algorithm for computing hash codes is based on CityHash and /// keeps 56 bytes of arbitrary state. struct hash_state { uint64_t h0, h1, h2, h3, h4, h5, h6; - /// \brief Create a new hash_state structure and initialize it based on the + /// Create a new hash_state structure and initialize it based on the /// seed and the first 64-byte chunk. /// This effectively performs the initial mix. static hash_state create(const char *s, uint64_t seed) { @@ -270,7 +270,7 @@ struct hash_state { return state; } - /// \brief Mix 32-bytes from the input sequence into the 16-bytes of 'a' + /// Mix 32-bytes from the input sequence into the 16-bytes of 'a' /// and 'b', including whatever is already in 'a' and 'b'. static void mix_32_bytes(const char *s, uint64_t &a, uint64_t &b) { a += fetch64(s); @@ -282,7 +282,7 @@ struct hash_state { a += c; } - /// \brief Mix in a 64-byte buffer of data. + /// Mix in a 64-byte buffer of data. /// We mix all 64 bytes even when the chunk length is smaller, but we /// record the actual length. void mix(const char *s) { @@ -300,7 +300,7 @@ struct hash_state { std::swap(h2, h0); } - /// \brief Compute the final 64-bit hash code value based on the current + /// Compute the final 64-bit hash code value based on the current /// state and the length of bytes hashed. uint64_t finalize(size_t length) { return hash_16_bytes(hash_16_bytes(h3, h5) + shift_mix(h1) * k1 + h2, @@ -309,7 +309,7 @@ struct hash_state { }; -/// \brief A global, fixed seed-override variable. +/// A global, fixed seed-override variable. /// /// This variable can be set using the \see wpi::set_fixed_execution_seed /// function. See that function for details. Do not, under any circumstances, @@ -330,7 +330,7 @@ inline size_t get_execution_seed() { } -/// \brief Trait to indicate whether a type's bits can be hashed directly. +/// Trait to indicate whether a type's bits can be hashed directly. /// /// A type trait which is true if we want to combine values for hashing by /// reading the underlying data. It is false if values of this type must @@ -357,14 +357,14 @@ template struct is_hashable_data > (sizeof(T) + sizeof(U)) == sizeof(std::pair))> {}; -/// \brief Helper to get the hashable data representation for a type. +/// Helper to get the hashable data representation for a type. /// This variant is enabled when the type itself can be used. template typename std::enable_if::value, T>::type get_hashable_data(const T &value) { return value; } -/// \brief Helper to get the hashable data representation for a type. +/// Helper to get the hashable data representation for a type. /// This variant is enabled when we must first call hash_value and use the /// result as our data. template @@ -374,7 +374,7 @@ get_hashable_data(const T &value) { return hash_value(value); } -/// \brief Helper to store data from a value into a buffer and advance the +/// Helper to store data from a value into a buffer and advance the /// pointer into that buffer. /// /// This routine first checks whether there is enough space in the provided @@ -393,7 +393,7 @@ bool store_and_advance(char *&buffer_ptr, char *buffer_end, const T& value, return true; } -/// \brief Implement the combining of integral values into a hash_code. +/// Implement the combining of integral values into a hash_code. /// /// This overload is selected when the value type of the iterator is /// integral. Rather than computing a hash_code for each object and then @@ -433,7 +433,7 @@ hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) { return state.finalize(length); } -/// \brief Implement the combining of integral values into a hash_code. +/// Implement the combining of integral values into a hash_code. /// /// This overload is selected when the value type of the iterator is integral /// and when the input iterator is actually a pointer. Rather than computing @@ -468,7 +468,7 @@ hash_combine_range_impl(ValueT *first, ValueT *last) { } // namespace hashing -/// \brief Compute a hash_code for a sequence of values. +/// Compute a hash_code for a sequence of values. /// /// This hashes a sequence of values. It produces the same hash_code as /// 'hash_combine(a, b, c, ...)', but can run over arbitrary sized sequences @@ -484,7 +484,7 @@ hash_code hash_combine_range(InputIteratorT first, InputIteratorT last) { namespace hashing { namespace detail { -/// \brief Helper class to manage the recursive combining of hash_combine +/// Helper class to manage the recursive combining of hash_combine /// arguments. /// /// This class exists to manage the state and various calls involved in the @@ -497,14 +497,14 @@ struct hash_combine_recursive_helper { const size_t seed; public: - /// \brief Construct a recursive hash combining helper. + /// Construct a recursive hash combining helper. /// /// This sets up the state for a recursive hash combine, including getting /// the seed and buffer setup. hash_combine_recursive_helper() : seed(get_execution_seed()) {} - /// \brief Combine one chunk of data into the current in-flight hash. + /// Combine one chunk of data into the current in-flight hash. /// /// This merges one chunk of data into the hash. First it tries to buffer /// the data. If the buffer is full, it hashes the buffer into its @@ -545,7 +545,7 @@ public: return buffer_ptr; } - /// \brief Recursive, variadic combining method. + /// Recursive, variadic combining method. /// /// This function recurses through each argument, combining that argument /// into a single hash. @@ -558,7 +558,7 @@ public: return combine(length, buffer_ptr, buffer_end, args...); } - /// \brief Base case for recursive, variadic combining. + /// Base case for recursive, variadic combining. /// /// The base case when combining arguments recursively is reached when all /// arguments have been handled. It flushes the remaining buffer and @@ -586,7 +586,7 @@ public: } // namespace detail } // namespace hashing -/// \brief Combine values into a single hash_code. +/// Combine values into a single hash_code. /// /// This routine accepts a varying number of arguments of any type. It will /// attempt to combine them into a single hash_code. For user-defined types it @@ -608,7 +608,7 @@ template hash_code hash_combine(const Ts &...args) { namespace hashing { namespace detail { -/// \brief Helper to hash the value of a single integer. +/// Helper to hash the value of a single integer. /// /// Overloads for smaller integer types are not provided to ensure consistent /// behavior in the presence of integral promotions. Essentially, diff --git a/wpiutil/src/main/native/include/wpi/IntrusiveRefCntPtr.h b/wpiutil/src/main/native/include/wpi/IntrusiveRefCntPtr.h index 1a3219e391..c677c1077b 100644 --- a/wpiutil/src/main/native/include/wpi/IntrusiveRefCntPtr.h +++ b/wpiutil/src/main/native/include/wpi/IntrusiveRefCntPtr.h @@ -1,4 +1,4 @@ -//== llvm/ADT/IntrusiveRefCntPtr.h - Smart Refcounting Pointer ---*- C++ -*-==// +//==- llvm/ADT/IntrusiveRefCntPtr.h - Smart Refcounting Pointer --*- C++ -*-==// // // The LLVM Compiler Infrastructure // @@ -7,19 +7,54 @@ // //===----------------------------------------------------------------------===// // -// This file defines IntrusiveRefCntPtr, a template class that -// implements a "smart" pointer for objects that maintain their own -// internal reference count, and RefCountedBase/RefCountedBaseVPTR, two -// generic base classes for objects that wish to have their lifetimes -// managed using reference counting. +// This file defines the RefCountedBase, ThreadSafeRefCountedBase, and +// IntrusiveRefCntPtr classes. // -// IntrusiveRefCntPtr is similar to Boost's intrusive_ptr with added -// LLVM-style casting. +// IntrusiveRefCntPtr is a smart pointer to an object which maintains a +// reference count. (ThreadSafe)RefCountedBase is a mixin class that adds a +// refcount member variable and methods for updating the refcount. An object +// that inherits from (ThreadSafe)RefCountedBase deletes itself when its +// refcount hits zero. +// +// For example: +// +// class MyClass : public RefCountedBase {}; +// +// void foo() { +// // Constructing an IntrusiveRefCntPtr increases the pointee's refcount by +// // 1 (from 0 in this case). +// IntrusiveRefCntPtr Ptr1(new MyClass()); +// +// // Copying an IntrusiveRefCntPtr increases the pointee's refcount by 1. +// IntrusiveRefCntPtr Ptr2(Ptr1); +// +// // Constructing an IntrusiveRefCntPtr has no effect on the object's +// // refcount. After a move, the moved-from pointer is null. +// IntrusiveRefCntPtr Ptr3(std::move(Ptr1)); +// assert(Ptr1 == nullptr); +// +// // Clearing an IntrusiveRefCntPtr decreases the pointee's refcount by 1. +// Ptr2.reset(); +// +// // The object deletes itself when we return from the function, because +// // Ptr3's destructor decrements its refcount to 0. +// } +// +// You can use IntrusiveRefCntPtr with isa(), dyn_cast(), etc.: +// +// IntrusiveRefCntPtr Ptr(new MyClass()); +// OtherClass *Other = dyn_cast(Ptr); // Ptr.get() not required +// +// IntrusiveRefCntPtr works with any class that +// +// - inherits from (ThreadSafe)RefCountedBase, +// - has Retain() and Release() methods, or +// - specializes IntrusiveRefCntPtrInfo. // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_INTRUSIVEREFCNTPTR_H -#define LLVM_ADT_INTRUSIVEREFCNTPTR_H +#ifndef WPIUTIL_WPI_INTRUSIVEREFCNTPTR_H +#define WPIUTIL_WPI_INTRUSIVEREFCNTPTR_H #include #include @@ -27,261 +62,208 @@ namespace wpi { - template - class IntrusiveRefCntPtr; - -//===----------------------------------------------------------------------===// -/// RefCountedBase - A generic base class for objects that wish to -/// have their lifetimes managed using reference counts. Classes -/// subclass RefCountedBase to obtain such functionality, and are -/// typically handled with IntrusiveRefCntPtr "smart pointers" (see below) -/// which automatically handle the management of reference counts. -/// Objects that subclass RefCountedBase should not be allocated on -/// the stack, as invoking "delete" (which is called when the -/// reference count hits 0) on such objects is an error. -//===----------------------------------------------------------------------===// - template - class RefCountedBase { - mutable unsigned ref_cnt; - - public: - RefCountedBase() : ref_cnt(0) {} - RefCountedBase(const RefCountedBase &) : ref_cnt(0) {} - - void Retain() const { ++ref_cnt; } - void Release() const { - assert (ref_cnt > 0 && "Reference count is already zero."); - if (--ref_cnt == 0) delete static_cast(this); - } - }; - -//===----------------------------------------------------------------------===// -/// RefCountedBaseVPTR - A class that has the same function as -/// RefCountedBase, but with a virtual destructor. Should be used -/// instead of RefCountedBase for classes that already have virtual -/// methods to enforce dynamic allocation via 'new'. Classes that -/// inherit from RefCountedBaseVPTR can't be allocated on stack - -/// attempting to do this will produce a compile error. -//===----------------------------------------------------------------------===// - class RefCountedBaseVPTR { - mutable unsigned ref_cnt; - virtual void anchor(); - - protected: - RefCountedBaseVPTR() : ref_cnt(0) {} - RefCountedBaseVPTR(const RefCountedBaseVPTR &) : ref_cnt(0) {} - - virtual ~RefCountedBaseVPTR() {} - - void Retain() const { ++ref_cnt; } - void Release() const { - assert (ref_cnt > 0 && "Reference count is already zero."); - if (--ref_cnt == 0) delete this; - } - - template - friend struct IntrusiveRefCntPtrInfo; - }; - - - template struct IntrusiveRefCntPtrInfo { - static void retain(T *obj) { obj->Retain(); } - static void release(T *obj) { obj->Release(); } - }; - -/// \brief A thread-safe version of \c wpi::RefCountedBase. +/// A CRTP mixin class that adds reference counting to a type. /// -/// A generic base class for objects that wish to have their lifetimes managed -/// using reference counts. Classes subclass \c ThreadSafeRefCountedBase to -/// obtain such functionality, and are typically handled with -/// \c IntrusiveRefCntPtr "smart pointers" which automatically handle the -/// management of reference counts. -template -class ThreadSafeRefCountedBase { +/// The lifetime of an object which inherits from RefCountedBase is managed by +/// calls to Release() and Retain(), which increment and decrement the object's +/// refcount, respectively. When a Release() call decrements the refcount to 0, +/// the object deletes itself. +template class RefCountedBase { + mutable unsigned RefCount = 0; + +public: + RefCountedBase() = default; + RefCountedBase(const RefCountedBase &) {} + + void Retain() const { ++RefCount; } + + void Release() const { + assert(RefCount > 0 && "Reference count is already zero."); + if (--RefCount == 0) + delete static_cast(this); + } +}; + +/// A thread-safe version of \c RefCountedBase. +template class ThreadSafeRefCountedBase { mutable std::atomic RefCount; protected: ThreadSafeRefCountedBase() : RefCount(0) {} public: - void Retain() const { ++RefCount; } + void Retain() const { RefCount.fetch_add(1, std::memory_order_relaxed); } void Release() const { - int NewRefCount = --RefCount; + int NewRefCount = RefCount.fetch_sub(1, std::memory_order_acq_rel) - 1; assert(NewRefCount >= 0 && "Reference count was already zero."); if (NewRefCount == 0) - delete static_cast(this); + delete static_cast(this); } }; -//===----------------------------------------------------------------------===// -/// IntrusiveRefCntPtr - A template class that implements a "smart pointer" -/// that assumes the wrapped object has a reference count associated -/// with it that can be managed via calls to -/// IntrusivePtrAddRef/IntrusivePtrRelease. The smart pointers -/// manage reference counts via the RAII idiom: upon creation of -/// smart pointer the reference count of the wrapped object is -/// incremented and upon destruction of the smart pointer the -/// reference count is decremented. This class also safely handles -/// wrapping NULL pointers. +/// Class you can specialize to provide custom retain/release functionality for +/// a type. /// -/// Reference counting is implemented via calls to -/// Obj->Retain()/Obj->Release(). Release() is required to destroy -/// the object when the reference count reaches zero. Inheriting from -/// RefCountedBase/RefCountedBaseVPTR takes care of this -/// automatically. -//===----------------------------------------------------------------------===// - template - class IntrusiveRefCntPtr { - T* Obj; +/// Usually specializing this class is not necessary, as IntrusiveRefCntPtr +/// works with any type which defines Retain() and Release() functions -- you +/// can define those functions yourself if RefCountedBase doesn't work for you. +/// +/// One case when you might want to specialize this type is if you have +/// - Foo.h defines type Foo and includes Bar.h, and +/// - Bar.h uses IntrusiveRefCntPtr in inline functions. +/// +/// Because Foo.h includes Bar.h, Bar.h can't include Foo.h in order to pull in +/// the declaration of Foo. Without the declaration of Foo, normally Bar.h +/// wouldn't be able to use IntrusiveRefCntPtr, which wants to call +/// T::Retain and T::Release. +/// +/// To resolve this, Bar.h could include a third header, FooFwd.h, which +/// forward-declares Foo and specializes IntrusiveRefCntPtrInfo. Then +/// Bar.h could use IntrusiveRefCntPtr, although it still couldn't call any +/// functions on Foo itself, because Foo would be an incomplete type. +template struct IntrusiveRefCntPtrInfo { + static void retain(T *obj) { obj->Retain(); } + static void release(T *obj) { obj->Release(); } +}; - public: - typedef T element_type; +/// A smart pointer to a reference-counted object that inherits from +/// RefCountedBase or ThreadSafeRefCountedBase. +/// +/// This class increments its pointee's reference count when it is created, and +/// decrements its refcount when it's destroyed (or is changed to point to a +/// different object). +template class IntrusiveRefCntPtr { + T *Obj = nullptr; - explicit IntrusiveRefCntPtr() : Obj(nullptr) {} +public: + using element_type = T; - IntrusiveRefCntPtr(T* obj) : Obj(obj) { - retain(); - } + explicit IntrusiveRefCntPtr() = default; + IntrusiveRefCntPtr(T *obj) : Obj(obj) { retain(); } + IntrusiveRefCntPtr(const IntrusiveRefCntPtr &S) : Obj(S.Obj) { retain(); } + IntrusiveRefCntPtr(IntrusiveRefCntPtr &&S) : Obj(S.Obj) { S.Obj = nullptr; } - IntrusiveRefCntPtr(const IntrusiveRefCntPtr& S) : Obj(S.Obj) { - retain(); - } - - IntrusiveRefCntPtr(IntrusiveRefCntPtr&& S) : Obj(S.Obj) { - S.Obj = nullptr; - } - - template - IntrusiveRefCntPtr(IntrusiveRefCntPtr&& S) : Obj(S.get()) { - S.Obj = nullptr; - } - - template - IntrusiveRefCntPtr(const IntrusiveRefCntPtr& S) - : Obj(S.get()) { - retain(); - } - - IntrusiveRefCntPtr& operator=(IntrusiveRefCntPtr S) { - swap(S); - return *this; - } - - ~IntrusiveRefCntPtr() { release(); } - - T& operator*() const { return *Obj; } - - T* operator->() const { return Obj; } - - T* get() const { return Obj; } - - explicit operator bool() const { return Obj; } - - void swap(IntrusiveRefCntPtr& other) { - T* tmp = other.Obj; - other.Obj = Obj; - Obj = tmp; - } - - void reset() { - release(); - Obj = nullptr; - } - - void resetWithoutRelease() { - Obj = nullptr; - } - - private: - void retain() { if (Obj) IntrusiveRefCntPtrInfo::retain(Obj); } - void release() { if (Obj) IntrusiveRefCntPtrInfo::release(Obj); } - - template - friend class IntrusiveRefCntPtr; - }; - - template - inline bool operator==(const IntrusiveRefCntPtr& A, - const IntrusiveRefCntPtr& B) - { - return A.get() == B.get(); + template + IntrusiveRefCntPtr(IntrusiveRefCntPtr &&S) : Obj(S.get()) { + S.Obj = nullptr; } - template - inline bool operator!=(const IntrusiveRefCntPtr& A, - const IntrusiveRefCntPtr& B) - { - return A.get() != B.get(); + template + IntrusiveRefCntPtr(const IntrusiveRefCntPtr &S) : Obj(S.get()) { + retain(); } - template - inline bool operator==(const IntrusiveRefCntPtr& A, - U* B) - { - return A.get() == B; + ~IntrusiveRefCntPtr() { release(); } + + IntrusiveRefCntPtr &operator=(IntrusiveRefCntPtr S) { + swap(S); + return *this; } - template - inline bool operator!=(const IntrusiveRefCntPtr& A, - U* B) - { - return A.get() != B; + T &operator*() const { return *Obj; } + T *operator->() const { return Obj; } + T *get() const { return Obj; } + explicit operator bool() const { return Obj; } + + void swap(IntrusiveRefCntPtr &other) { + T *tmp = other.Obj; + other.Obj = Obj; + Obj = tmp; } - template - inline bool operator==(T* A, - const IntrusiveRefCntPtr& B) - { - return A == B.get(); + void reset() { + release(); + Obj = nullptr; } - template - inline bool operator!=(T* A, - const IntrusiveRefCntPtr& B) - { - return A != B.get(); + void resetWithoutRelease() { Obj = nullptr; } + +private: + void retain() { + if (Obj) + IntrusiveRefCntPtrInfo::retain(Obj); } - template - bool operator==(std::nullptr_t A, const IntrusiveRefCntPtr &B) { - return !B; + void release() { + if (Obj) + IntrusiveRefCntPtrInfo::release(Obj); } - template - bool operator==(const IntrusiveRefCntPtr &A, std::nullptr_t B) { - return B == A; + template friend class IntrusiveRefCntPtr; +}; + +template +inline bool operator==(const IntrusiveRefCntPtr &A, + const IntrusiveRefCntPtr &B) { + return A.get() == B.get(); +} + +template +inline bool operator!=(const IntrusiveRefCntPtr &A, + const IntrusiveRefCntPtr &B) { + return A.get() != B.get(); +} + +template +inline bool operator==(const IntrusiveRefCntPtr &A, U *B) { + return A.get() == B; +} + +template +inline bool operator!=(const IntrusiveRefCntPtr &A, U *B) { + return A.get() != B; +} + +template +inline bool operator==(T *A, const IntrusiveRefCntPtr &B) { + return A == B.get(); +} + +template +inline bool operator!=(T *A, const IntrusiveRefCntPtr &B) { + return A != B.get(); +} + +template +bool operator==(std::nullptr_t A, const IntrusiveRefCntPtr &B) { + return !B; +} + +template +bool operator==(const IntrusiveRefCntPtr &A, std::nullptr_t B) { + return B == A; +} + +template +bool operator!=(std::nullptr_t A, const IntrusiveRefCntPtr &B) { + return !(A == B); +} + +template +bool operator!=(const IntrusiveRefCntPtr &A, std::nullptr_t B) { + return !(A == B); +} + +// Make IntrusiveRefCntPtr work with dyn_cast, isa, and the other idioms from +// Casting.h. +template struct simplify_type; + +template struct simplify_type> { + using SimpleType = T *; + + static SimpleType getSimplifiedValue(IntrusiveRefCntPtr &Val) { + return Val.get(); } +}; - template - bool operator!=(std::nullptr_t A, const IntrusiveRefCntPtr &B) { - return !(A == B); +template struct simplify_type> { + using SimpleType = /*const*/ T *; + + static SimpleType getSimplifiedValue(const IntrusiveRefCntPtr &Val) { + return Val.get(); } - - template - bool operator!=(const IntrusiveRefCntPtr &A, std::nullptr_t B) { - return !(A == B); - } - -//===----------------------------------------------------------------------===// -// LLVM-style downcasting support for IntrusiveRefCntPtr objects -//===----------------------------------------------------------------------===// - - template struct simplify_type; - - template struct simplify_type > { - typedef T* SimpleType; - static SimpleType getSimplifiedValue(IntrusiveRefCntPtr& Val) { - return Val.get(); - } - }; - - template struct simplify_type > { - typedef /*const*/ T* SimpleType; - static SimpleType getSimplifiedValue(const IntrusiveRefCntPtr& Val) { - return Val.get(); - } - }; +}; } // end namespace wpi diff --git a/wpiutil/src/main/native/include/wpi/MathExtras.h b/wpiutil/src/main/native/include/wpi/MathExtras.h index a64845be0b..1ab43aa6ca 100644 --- a/wpiutil/src/main/native/include/wpi/MathExtras.h +++ b/wpiutil/src/main/native/include/wpi/MathExtras.h @@ -11,33 +11,108 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_MATHEXTRAS_H -#define LLVM_SUPPORT_MATHEXTRAS_H +#ifndef WPIUTIL_WPI_MATHEXTRAS_H +#define WPIUTIL_WPI_MATHEXTRAS_H #include "wpi/Compiler.h" #include #include #include +#include #include #include -#include #include +#include #ifdef _MSC_VER #include #endif namespace wpi { -/// \brief The behavior an operation has on an input of 0. +/// The behavior an operation has on an input of 0. enum ZeroBehavior { - /// \brief The returned value is undefined. + /// The returned value is undefined. ZB_Undefined, - /// \brief The returned value is numeric_limits::max() + /// The returned value is numeric_limits::max() ZB_Max, - /// \brief The returned value is numeric_limits::digits + /// The returned value is numeric_limits::digits ZB_Width }; +namespace detail { +template struct TrailingZerosCounter { + static std::size_t count(T Val, ZeroBehavior) { + if (!Val) + return std::numeric_limits::digits; + if (Val & 0x1) + return 0; + + // Bisection method. + std::size_t ZeroBits = 0; + T Shift = std::numeric_limits::digits >> 1; + T Mask = std::numeric_limits::max() >> Shift; + while (Shift) { + if ((Val & Mask) == 0) { + Val >>= Shift; + ZeroBits |= Shift; + } + Shift >>= 1; + Mask >>= Shift; + } + return ZeroBits; + } +}; + +#if __GNUC__ >= 4 || defined(_MSC_VER) +template struct TrailingZerosCounter { + static std::size_t count(T Val, ZeroBehavior ZB) { + if (ZB != ZB_Undefined && Val == 0) + return 32; + +#if __has_builtin(__builtin_ctz) || LLVM_GNUC_PREREQ(4, 0, 0) + return __builtin_ctz(Val); +#elif defined(_MSC_VER) + unsigned long Index; + _BitScanForward(&Index, Val); + return Index; +#endif + } +}; + +#if !defined(_MSC_VER) || defined(_M_X64) +template struct TrailingZerosCounter { + static std::size_t count(T Val, ZeroBehavior ZB) { + if (ZB != ZB_Undefined && Val == 0) + return 64; + +#if __has_builtin(__builtin_ctzll) || LLVM_GNUC_PREREQ(4, 0, 0) + return __builtin_ctzll(Val); +#elif defined(_MSC_VER) + unsigned long Index; + _BitScanForward64(&Index, Val); + return Index; +#endif + } +}; +#endif +#endif +} // namespace detail + +/// Count number of 0's from the least significant bit to the most +/// stopping at the first 1. +/// +/// Only unsigned integral types are allowed. +/// +/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are +/// valid arguments. +template +std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) { + static_assert(std::numeric_limits::is_integer && + !std::numeric_limits::is_signed, + "Only unsigned integral types are allowed."); + return wpi::detail::TrailingZerosCounter::count(Val, ZB); +} + namespace detail { template struct LeadingZerosCounter { static std::size_t count(T Val, ZeroBehavior) { @@ -92,7 +167,7 @@ template struct LeadingZerosCounter { #endif } // namespace detail -/// \brief Count number of 0's from the most significant bit to the least +/// Count number of 0's from the most significant bit to the least /// stopping at the first 1. /// /// Only unsigned integral types are allowed. @@ -104,10 +179,51 @@ std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "Only unsigned integral types are allowed."); - return detail::LeadingZerosCounter::count(Val, ZB); + return wpi::detail::LeadingZerosCounter::count(Val, ZB); } -/// \brief Get the index of the last set bit starting from the least +/// Get the index of the first set bit starting from the least +/// significant bit. +/// +/// Only unsigned integral types are allowed. +/// +/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are +/// valid arguments. +template T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) { + if (ZB == ZB_Max && Val == 0) + return std::numeric_limits::max(); + + return countTrailingZeros(Val, ZB_Undefined); +} + +/// Create a bitmask with the N right-most bits set to 1, and all other +/// bits set to 0. Only unsigned types are allowed. +template T maskTrailingOnes(unsigned N) { + static_assert(std::is_unsigned::value, "Invalid type!"); + const unsigned Bits = CHAR_BIT * sizeof(T); + assert(N <= Bits && "Invalid bit index"); + return N == 0 ? 0 : (T(-1) >> (Bits - N)); +} + +/// Create a bitmask with the N left-most bits set to 1, and all other +/// bits set to 0. Only unsigned types are allowed. +template T maskLeadingOnes(unsigned N) { + return ~maskTrailingOnes(CHAR_BIT * sizeof(T) - N); +} + +/// Create a bitmask with the N right-most bits set to 0, and all other +/// bits set to 1. Only unsigned types are allowed. +template T maskTrailingZeros(unsigned N) { + return maskLeadingOnes(CHAR_BIT * sizeof(T) - N); +} + +/// Create a bitmask with the N left-most bits set to 0, and all other +/// bits set to 1. Only unsigned types are allowed. +template T maskLeadingZeros(unsigned N) { + return maskTrailingOnes(CHAR_BIT * sizeof(T) - N); +} + +/// Get the index of the last set bit starting from the least /// significant bit. /// /// Only unsigned integral types are allowed. @@ -124,7 +240,7 @@ template T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) { (std::numeric_limits::digits - 1); } -/// \brief Macro compressed bit reversal table for 256 bits. +/// Macro compressed bit reversal table for 256 bits. /// /// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable static const unsigned char BitReverseTable256[256] = { @@ -137,7 +253,7 @@ static const unsigned char BitReverseTable256[256] = { #undef R6 }; -/// \brief Reverse the bits in \p Val. +/// Reverse the bits in \p Val. template T reverseBits(T Val) { unsigned char in[sizeof(Val)]; @@ -153,150 +269,165 @@ T reverseBits(T Val) { // type overloading so that signed and unsigned integers can be used without // ambiguity. -/// Hi_32 - This function returns the high 32 bits of a 64 bit value. -inline uint32_t Hi_32(uint64_t Value) { +/// Return the high 32 bits of a 64 bit value. +constexpr inline uint32_t Hi_32(uint64_t Value) { return static_cast(Value >> 32); } -/// Lo_32 - This function returns the low 32 bits of a 64 bit value. -inline uint32_t Lo_32(uint64_t Value) { +/// Return the low 32 bits of a 64 bit value. +constexpr inline uint32_t Lo_32(uint64_t Value) { return static_cast(Value); } -/// Make_64 - This functions makes a 64-bit integer from a high / low pair of -/// 32-bit integers. -inline uint64_t Make_64(uint32_t High, uint32_t Low) { +/// Make a 64-bit integer from a high / low pair of 32-bit integers. +constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { return ((uint64_t)High << 32) | (uint64_t)Low; } -/// isInt - Checks if an integer fits into the given bit width. -template -inline bool isInt(int64_t x) { +/// Checks if an integer fits into the given bit width. +template constexpr inline bool isInt(int64_t x) { return N >= 64 || (-(INT64_C(1)<<(N-1)) <= x && x < (INT64_C(1)<<(N-1))); } // Template specializations to get better code for common cases. -template<> -inline bool isInt<8>(int64_t x) { +template <> constexpr inline bool isInt<8>(int64_t x) { return static_cast(x) == x; } -template<> -inline bool isInt<16>(int64_t x) { +template <> constexpr inline bool isInt<16>(int64_t x) { return static_cast(x) == x; } -template<> -inline bool isInt<32>(int64_t x) { +template <> constexpr inline bool isInt<32>(int64_t x) { return static_cast(x) == x; } -/// isShiftedInt - Checks if a signed integer is an N bit number shifted -/// left by S. -template -inline bool isShiftedInt(int64_t x) { - return isInt(x) && (x % (1< +constexpr inline bool isShiftedInt(int64_t x) { + static_assert( + N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number."); + static_assert(N + S <= 64, "isShiftedInt with N + S > 64 is too wide."); + return isInt(x) && (x % (UINT64_C(1) << S) == 0); } -/// isUInt - Checks if an unsigned integer fits into the given bit width. -template -inline bool isUInt(uint64_t x) { - return N >= 64 || x < (UINT64_C(1)<<(N)); +/// Checks if an unsigned integer fits into the given bit width. +/// +/// This is written as two functions rather than as simply +/// +/// return N >= 64 || X < (UINT64_C(1) << N); +/// +/// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting +/// left too many places. +template +constexpr inline typename std::enable_if<(N < 64), bool>::type +isUInt(uint64_t X) { + static_assert(N > 0, "isUInt<0> doesn't make sense"); + return X < (UINT64_C(1) << (N)); } +template +constexpr inline typename std::enable_if= 64, bool>::type +isUInt(uint64_t X) { + return true; +} + // Template specializations to get better code for common cases. -template<> -inline bool isUInt<8>(uint64_t x) { +template <> constexpr inline bool isUInt<8>(uint64_t x) { return static_cast(x) == x; } -template<> -inline bool isUInt<16>(uint64_t x) { +template <> constexpr inline bool isUInt<16>(uint64_t x) { return static_cast(x) == x; } -template<> -inline bool isUInt<32>(uint64_t x) { +template <> constexpr inline bool isUInt<32>(uint64_t x) { return static_cast(x) == x; } -/// isShiftedUInt - Checks if a unsigned integer is an N bit number shifted -/// left by S. -template -inline bool isShiftedUInt(uint64_t x) { - return isUInt(x) && (x % (1< +constexpr inline bool isShiftedUInt(uint64_t x) { + static_assert( + N > 0, "isShiftedUInt<0> doesn't make sense (refers to a 0-bit number)"); + static_assert(N + S <= 64, + "isShiftedUInt with N + S > 64 is too wide."); + // Per the two static_asserts above, S must be strictly less than 64. So + // 1 << S is not undefined behavior. + return isUInt(x) && (x % (UINT64_C(1) << S) == 0); } /// Gets the maximum value for a N-bit unsigned integer. inline uint64_t maxUIntN(uint64_t N) { assert(N > 0 && N <= 64 && "integer width out of range"); - return (UINT64_C(1) << N) - 1; + // uint64_t(1) << 64 is undefined behavior, so we can't do + // (uint64_t(1) << N) - 1 + // without checking first that N != 64. But this works and doesn't have a + // branch. + return UINT64_MAX >> (64 - N); } /// Gets the minimum value for a N-bit signed integer. inline int64_t minIntN(int64_t N) { assert(N > 0 && N <= 64 && "integer width out of range"); - return -(INT64_C(1)<<(N-1)); + return -(UINT64_C(1)<<(N-1)); } /// Gets the maximum value for a N-bit signed integer. inline int64_t maxIntN(int64_t N) { assert(N > 0 && N <= 64 && "integer width out of range"); - return (INT64_C(1)<<(N-1)) - 1; + // This relies on two's complement wraparound when N == 64, so we convert to + // int64_t only at the very end to avoid UB. + return (UINT64_C(1) << (N - 1)) - 1; } -/// isUIntN - Checks if an unsigned integer fits into the given (dynamic) -/// bit width. +/// Checks if an unsigned integer fits into the given (dynamic) bit width. inline bool isUIntN(unsigned N, uint64_t x) { return N >= 64 || x <= maxUIntN(N); } -/// isIntN - Checks if an signed integer fits into the given (dynamic) -/// bit width. +/// Checks if an signed integer fits into the given (dynamic) bit width. inline bool isIntN(unsigned N, int64_t x) { return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N)); } -/// isMask_32 - This function returns true if the argument is a non-empty -/// sequence of ones starting at the least significant bit with the remainder -/// zero (32 bit version). Ex. isMask_32(0x0000FFFFU) == true. -inline bool isMask_32(uint32_t Value) { +/// Return true if the argument is a non-empty sequence of ones starting at the +/// least significant bit with the remainder zero (32 bit version). +/// Ex. isMask_32(0x0000FFFFU) == true. +constexpr inline bool isMask_32(uint32_t Value) { return Value && ((Value + 1) & Value) == 0; } -/// isMask_64 - This function returns true if the argument is a non-empty -/// sequence of ones starting at the least significant bit with the remainder -/// zero (64 bit version). -inline bool isMask_64(uint64_t Value) { +/// Return true if the argument is a non-empty sequence of ones starting at the +/// least significant bit with the remainder zero (64 bit version). +constexpr inline bool isMask_64(uint64_t Value) { return Value && ((Value + 1) & Value) == 0; } -/// isShiftedMask_32 - This function returns true if the argument contains a -/// non-empty sequence of ones with the remainder zero (32 bit version.) -/// Ex. isShiftedMask_32(0x0000FF00U) == true. -inline bool isShiftedMask_32(uint32_t Value) { +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true. +constexpr inline bool isShiftedMask_32(uint32_t Value) { return Value && isMask_32((Value - 1) | Value); } -/// isShiftedMask_64 - This function returns true if the argument contains a -/// non-empty sequence of ones with the remainder zero (64 bit version.) -inline bool isShiftedMask_64(uint64_t Value) { +/// Return true if the argument contains a non-empty sequence of ones with the +/// remainder zero (64 bit version.) +constexpr inline bool isShiftedMask_64(uint64_t Value) { return Value && isMask_64((Value - 1) | Value); } -/// isPowerOf2_32 - This function returns true if the argument is a power of -/// two > 0. Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.) -inline bool isPowerOf2_32(uint32_t Value) { +/// Return true if the argument is a power of two > 0. +/// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.) +constexpr inline bool isPowerOf2_32(uint32_t Value) { return Value && !(Value & (Value - 1)); } -/// isPowerOf2_64 - This function returns true if the argument is a power of two -/// > 0 (64 bit edition.) -inline bool isPowerOf2_64(uint64_t Value) { - return Value && !(Value & (Value - int64_t(1L))); +/// Return true if the argument is a power of two > 0 (64 bit edition.) +constexpr inline bool isPowerOf2_64(uint64_t Value) { + return Value && !(Value & (Value - 1)); } -/// \brief Count the number of ones from the most significant bit to the first +/// Count the number of ones from the most significant bit to the first /// zero bit. /// -/// Ex. CountLeadingOnes(0xFF0FFF00) == 8. +/// Ex. countLeadingOnes(0xFF0FFF00) == 8. /// Only unsigned integral types are allowed. /// /// \param ZB the behavior on an input of all ones. Only ZB_Width and @@ -306,7 +437,23 @@ std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "Only unsigned integral types are allowed."); - return countLeadingZeros(~Value, ZB); + return countLeadingZeros(~Value, ZB); +} + +/// Count the number of ones from the least significant bit to the first +/// zero bit. +/// +/// Ex. countTrailingOnes(0x00FF00FF) == 8. +/// Only unsigned integral types are allowed. +/// +/// \param ZB the behavior on an input of all ones. Only ZB_Width and +/// ZB_Undefined are valid arguments. +template +std::size_t countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) { + static_assert(std::numeric_limits::is_integer && + !std::numeric_limits::is_signed, + "Only unsigned integral types are allowed."); + return countTrailingZeros(~Value, ZB); } namespace detail { @@ -340,7 +487,7 @@ template struct PopulationCounter { }; } // namespace detail -/// \brief Count the number of set bits in a value. +/// Count the number of set bits in a value. /// Ex. countPopulation(0xF000F000) = 8 /// Returns 0 if the word is zero. template @@ -351,7 +498,7 @@ inline unsigned countPopulation(T Value) { return detail::PopulationCounter::count(Value); } -/// Log2 - This function returns the log base 2 of the specified value +/// Return the log base 2 of the specified value. inline double Log2(double Value) { #if defined(__ANDROID_API__) && __ANDROID_API__ < 18 return __builtin_log(Value) / __builtin_log(2.0); @@ -360,34 +507,33 @@ inline double Log2(double Value) { #endif } -/// Log2_32 - This function returns the floor log base 2 of the specified value, -/// -1 if the value is zero. (32 bit edition.) +/// Return the floor log base 2 of the specified value, -1 if the value is zero. +/// (32 bit edition.) /// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2 inline unsigned Log2_32(uint32_t Value) { return 31 - countLeadingZeros(Value); } -/// Log2_64 - This function returns the floor log base 2 of the specified value, -/// -1 if the value is zero. (64 bit edition.) +/// Return the floor log base 2 of the specified value, -1 if the value is zero. +/// (64 bit edition.) inline unsigned Log2_64(uint64_t Value) { return 63 - countLeadingZeros(Value); } -/// Log2_32_Ceil - This function returns the ceil log base 2 of the specified -/// value, 32 if the value is zero. (32 bit edition). +/// Return the ceil log base 2 of the specified value, 32 if the value is zero. +/// (32 bit edition). /// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3 inline unsigned Log2_32_Ceil(uint32_t Value) { return 32 - countLeadingZeros(Value - 1); } -/// Log2_64_Ceil - This function returns the ceil log base 2 of the specified -/// value, 64 if the value is zero. (64 bit edition.) +/// Return the ceil log base 2 of the specified value, 64 if the value is zero. +/// (64 bit edition.) inline unsigned Log2_64_Ceil(uint64_t Value) { return 64 - countLeadingZeros(Value - 1); } -/// GreatestCommonDivisor64 - Return the greatest common divisor of the two -/// values using Euclid's algorithm. +/// Return the greatest common divisor of the values using Euclid's algorithm. inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) { while (B) { uint64_t T = B; @@ -397,57 +543,45 @@ inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) { return A; } -/// BitsToDouble - This function takes a 64-bit integer and returns the bit -/// equivalent double. +/// This function takes a 64-bit integer and returns the bit equivalent double. inline double BitsToDouble(uint64_t Bits) { - union { - uint64_t L; - double D; - } T; - T.L = Bits; - return T.D; + double D; + static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes"); + memcpy(&D, &Bits, sizeof(Bits)); + return D; } -/// BitsToFloat - This function takes a 32-bit integer and returns the bit -/// equivalent float. +/// This function takes a 32-bit integer and returns the bit equivalent float. inline float BitsToFloat(uint32_t Bits) { - union { - uint32_t I; - float F; - } T; - T.I = Bits; - return T.F; + float F; + static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes"); + memcpy(&F, &Bits, sizeof(Bits)); + return F; } -/// DoubleToBits - This function takes a double and returns the bit -/// equivalent 64-bit integer. Note that copying doubles around -/// changes the bits of NaNs on some hosts, notably x86, so this -/// routine cannot be used if these bits are needed. +/// This function takes a double and returns the bit equivalent 64-bit integer. +/// Note that copying doubles around changes the bits of NaNs on some hosts, +/// notably x86, so this routine cannot be used if these bits are needed. inline uint64_t DoubleToBits(double Double) { - union { - uint64_t L; - double D; - } T; - T.D = Double; - return T.L; + uint64_t Bits; + static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes"); + memcpy(&Bits, &Double, sizeof(Double)); + return Bits; } -/// FloatToBits - This function takes a float and returns the bit -/// equivalent 32-bit integer. Note that copying floats around -/// changes the bits of NaNs on some hosts, notably x86, so this -/// routine cannot be used if these bits are needed. +/// This function takes a float and returns the bit equivalent 32-bit integer. +/// Note that copying floats around changes the bits of NaNs on some hosts, +/// notably x86, so this routine cannot be used if these bits are needed. inline uint32_t FloatToBits(float Float) { - union { - uint32_t I; - float F; - } T; - T.F = Float; - return T.I; + uint32_t Bits; + static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes"); + memcpy(&Bits, &Float, sizeof(Float)); + return Bits; } -/// MinAlign - A and B are either alignments or offsets. Return the minimum -/// alignment that may be assumed after adding the two together. -inline uint64_t MinAlign(uint64_t A, uint64_t B) { +/// A and B are either alignments or offsets. Return the minimum alignment that +/// may be assumed after adding the two together. +constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) { // The largest power of 2 that divides both A and B. // // Replace "-Value" by "1+~Value" in the following commented code to avoid @@ -456,7 +590,7 @@ inline uint64_t MinAlign(uint64_t A, uint64_t B) { return (A | B) & (1 + ~(A | B)); } -/// \brief Aligns \c Addr to \c Alignment bytes, rounding up. +/// Aligns \c Addr to \c Alignment bytes, rounding up. /// /// Alignment should be a power of two. This method rounds up, so /// alignAddr(7, 4) == 8 and alignAddr(8, 4) == 8. @@ -469,14 +603,14 @@ inline uintptr_t alignAddr(const void *Addr, size_t Alignment) { return (((uintptr_t)Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1)); } -/// \brief Returns the necessary adjustment for aligning \c Ptr to \c Alignment +/// Returns the necessary adjustment for aligning \c Ptr to \c Alignment /// bytes, rounding up. inline size_t alignmentAdjustment(const void *Ptr, size_t Alignment) { return alignAddr(Ptr, Alignment) - (uintptr_t)Ptr; } -/// NextPowerOf2 - Returns the next power of two (in 64-bits) -/// that is strictly greater than A. Returns zero on overflow. +/// Returns the next power of two (in 64-bits) that is strictly greater than A. +/// Returns zero on overflow. inline uint64_t NextPowerOf2(uint64_t A) { A |= (A >> 1); A |= (A >> 2); @@ -494,6 +628,14 @@ inline uint64_t PowerOf2Floor(uint64_t A) { return 1ull << (63 - countLeadingZeros(A, ZB_Undefined)); } +/// Returns the power of two which is greater than or equal to the given value. +/// Essentially, it is a ceil operation across the domain of powers of two. +inline uint64_t PowerOf2Ceil(uint64_t A) { + if (!A) + return 0; + return NextPowerOf2(A - 1); +} + /// Returns the next integer (mod 2**64) that is greater than or equal to /// \p Value and is a multiple of \p Align. \p Align must be non-zero. /// @@ -515,13 +657,40 @@ inline uint64_t PowerOf2Floor(uint64_t A) { /// alignTo(321, 255, 42) = 552 /// \endcode inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew = 0) { + assert(Align != 0u && "Align can't be 0."); Skew %= Align; return (Value + Align - 1 - Skew) / Align * Align + Skew; } +/// Returns the next integer (mod 2**64) that is greater than or equal to +/// \p Value and is a multiple of \c Align. \c Align must be non-zero. +template constexpr inline uint64_t alignTo(uint64_t Value) { + static_assert(Align != 0u, "Align must be non-zero"); + return (Value + Align - 1) / Align * Align; +} + +/// Returns the integer ceil(Numerator / Denominator). +inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) { + return alignTo(Numerator, Denominator) / Denominator; +} + +/// \c alignTo for contexts where a constant expression is required. +/// \sa alignTo +/// +/// \todo FIXME: remove when \c constexpr becomes really \c constexpr +template +struct AlignTo { + static_assert(Align != 0u, "Align must be non-zero"); + template + struct from_value { + static const uint64_t value = (Value + Align - 1) / Align * Align; + }; +}; + /// Returns the largest uint64_t less than or equal to \p Value and is /// \p Skew mod \p Align. \p Align must be non-zero inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) { + assert(Align != 0u && "Align can't be 0."); Skew %= Align; return (Value - Skew) / Align * Align + Skew; } @@ -533,42 +702,49 @@ inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) { return alignTo(Value, Align) - Value; } -/// SignExtend32 - Sign extend B-bit number x to 32-bit int. -/// Usage int32_t r = SignExtend32<5>(x); -template inline int32_t SignExtend32(uint32_t x) { - return int32_t(x << (32 - B)) >> (32 - B); -} - -/// \brief Sign extend number in the bottom B bits of X to a 32-bit int. +/// Sign-extend the number in the bottom B bits of X to a 32-bit integer. /// Requires 0 < B <= 32. -inline int32_t SignExtend32(uint32_t X, unsigned B) { +template constexpr inline int32_t SignExtend32(uint32_t X) { + static_assert(B > 0, "Bit width can't be 0."); + static_assert(B <= 32, "Bit width out of range."); return int32_t(X << (32 - B)) >> (32 - B); } -/// SignExtend64 - Sign extend B-bit number x to 64-bit int. -/// Usage int64_t r = SignExtend64<5>(x); -template inline int64_t SignExtend64(uint64_t x) { +/// Sign-extend the number in the bottom B bits of X to a 32-bit integer. +/// Requires 0 < B < 32. +inline int32_t SignExtend32(uint32_t X, unsigned B) { + assert(B > 0 && "Bit width can't be 0."); + assert(B <= 32 && "Bit width out of range."); + return int32_t(X << (32 - B)) >> (32 - B); +} + +/// Sign-extend the number in the bottom B bits of X to a 64-bit integer. +/// Requires 0 < B < 64. +template constexpr inline int64_t SignExtend64(uint64_t x) { + static_assert(B > 0, "Bit width can't be 0."); + static_assert(B <= 64, "Bit width out of range."); return int64_t(x << (64 - B)) >> (64 - B); } -/// \brief Sign extend number in the bottom B bits of X to a 64-bit int. -/// Requires 0 < B <= 64. +/// Sign-extend the number in the bottom B bits of X to a 64-bit integer. +/// Requires 0 < B < 64. inline int64_t SignExtend64(uint64_t X, unsigned B) { + assert(B > 0 && "Bit width can't be 0."); + assert(B <= 64 && "Bit width out of range."); return int64_t(X << (64 - B)) >> (64 - B); } -/// \brief Subtract two unsigned integers, X and Y, of type T and return their -/// absolute value. +/// Subtract two unsigned integers, X and Y, of type T and return the absolute +/// value of the result. template typename std::enable_if::value, T>::type AbsoluteDifference(T X, T Y) { return std::max(X, Y) - std::min(X, Y); } -/// \brief Add two unsigned integers, X and Y, of type T. -/// Clamp the result to the maximum representable value of T on overflow. -/// ResultOverflowed indicates if the result is larger than the maximum -/// representable value of type T. +/// Add two unsigned integers, X and Y, of type T. Clamp the result to the +/// maximum representable value of T on overflow. ResultOverflowed indicates if +/// the result is larger than the maximum representable value of type T. template typename std::enable_if::value, T>::type SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) { @@ -583,10 +759,9 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) { return Z; } -/// \brief Multiply two unsigned integers, X and Y, of type T. -/// Clamp the result to the maximum representable value of T on overflow. -/// ResultOverflowed indicates if the result is larger than the maximum -/// representable value of type T. +/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the +/// maximum representable value of T on overflow. ResultOverflowed indicates if +/// the result is larger than the maximum representable value of type T. template typename std::enable_if::value, T>::type SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) { @@ -629,12 +804,10 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) { return Z; } -/// \brief Multiply two unsigned integers, X and Y, and add the unsigned -/// integer, A to the product. Clamp the result to the maximum representable -/// value of T on overflow. ResultOverflowed indicates if the result is larger -/// than the maximum representable value of type T. -/// Note that this is purely a convenience function as there is no distinction -/// where overflow occurred in a 'fused' multiply-add for unsigned numbers. +/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to +/// the product. Clamp the result to the maximum representable value of T on +/// overflow. ResultOverflowed indicates if the result is larger than the +/// maximum representable value of type T. template typename std::enable_if::value, T>::type SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) { diff --git a/wpiutil/src/main/native/include/wpi/NativeFormatting.h b/wpiutil/src/main/native/include/wpi/NativeFormatting.h new file mode 100644 index 0000000000..a194027da5 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/NativeFormatting.h @@ -0,0 +1,49 @@ +//===- NativeFormatting.h - Low level formatting helpers ---------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef WPIUTIL_WPI_NATIVE_FORMATTING_H +#define WPIUTIL_WPI_NATIVE_FORMATTING_H + +#include "wpi/Optional.h" +#include "wpi/raw_ostream.h" + +#include + +namespace wpi { +enum class FloatStyle { Exponent, ExponentUpper, Fixed, Percent }; +enum class IntegerStyle { + Integer, + Number, +}; +enum class HexPrintStyle { Upper, Lower, PrefixUpper, PrefixLower }; + +size_t getDefaultPrecision(FloatStyle Style); + +bool isPrefixedHexStyle(HexPrintStyle S); + +void write_integer(raw_ostream &S, unsigned int N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, int N, size_t MinDigits, IntegerStyle Style); +void write_integer(raw_ostream &S, unsigned long N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, long N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits, + IntegerStyle Style); +void write_integer(raw_ostream &S, long long N, size_t MinDigits, + IntegerStyle Style); + +void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style, + Optional Width = None); +void write_double(raw_ostream &S, double D, FloatStyle Style, + Optional Precision = None); +} + +#endif + diff --git a/wpiutil/src/main/native/include/wpi/None.h b/wpiutil/src/main/native/include/wpi/None.h index 7d996945b9..9cefac9f29 100644 --- a/wpiutil/src/main/native/include/wpi/None.h +++ b/wpiutil/src/main/native/include/wpi/None.h @@ -13,14 +13,15 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_NONE_H -#define LLVM_ADT_NONE_H +#ifndef WPIUTIL_WPI_NONE_H +#define WPIUTIL_WPI_NONE_H namespace wpi { -/// \brief A simple null object to allow implicit construction of Optional +/// A simple null object to allow implicit construction of Optional /// and similar types without having to spell out the specialization's name. -enum class NoneType { None }; -const NoneType None = None; +// (constant value 1 in an attempt to workaround MSVC build issue... ) +enum class NoneType { None = 1 }; +const NoneType None = NoneType::None; } #endif diff --git a/wpiutil/src/main/native/include/wpi/Optional.h b/wpiutil/src/main/native/include/wpi/Optional.h index dabf09a752..30c8bbdace 100644 --- a/wpiutil/src/main/native/include/wpi/Optional.h +++ b/wpiutil/src/main/native/include/wpi/Optional.h @@ -1,4 +1,4 @@ -//===-- Optional.h - Simple variant for passing optional values ---*- C++ -*-=// +//===- Optional.h - Simple variant for passing optional values --*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -13,129 +13,187 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_OPTIONAL_H -#define LLVM_ADT_OPTIONAL_H +#ifndef WPIUTIL_WPI_OPTIONAL_H +#define WPIUTIL_WPI_OPTIONAL_H #include "wpi/None.h" #include "wpi/AlignOf.h" #include "wpi/Compiler.h" +#include "wpi/type_traits.h" +#include #include #include #include namespace wpi { -template -class Optional { +namespace optional_detail { +/// Storage for any type. +template struct OptionalStorage { AlignedCharArrayUnion storage; - bool hasVal; -public: - typedef T value_type; + bool hasVal = false; - Optional(NoneType) : hasVal(false) {} - explicit Optional() : hasVal(false) {} - Optional(const T &y) : hasVal(true) { - new (storage.buffer) T(y); - } - Optional(const Optional &O) : hasVal(O.hasVal) { + OptionalStorage() = default; + + OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); } + OptionalStorage(const OptionalStorage &O) : hasVal(O.hasVal) { if (hasVal) - new (storage.buffer) T(*O); + new (storage.buffer) T(*O.getPointer()); } - - Optional(T &&y) : hasVal(true) { + OptionalStorage(T &&y) : hasVal(true) { new (storage.buffer) T(std::forward(y)); } - Optional(Optional &&O) : hasVal(O) { - if (O) { - new (storage.buffer) T(std::move(*O)); - O.reset(); + OptionalStorage(OptionalStorage &&O) : hasVal(O.hasVal) { + if (O.hasVal) { + new (storage.buffer) T(std::move(*O.getPointer())); } } - Optional &operator=(T &&y) { + + OptionalStorage &operator=(T &&y) { if (hasVal) - **this = std::move(y); + *getPointer() = std::move(y); else { new (storage.buffer) T(std::move(y)); hasVal = true; } return *this; } - Optional &operator=(Optional &&O) { - if (!O) + OptionalStorage &operator=(OptionalStorage &&O) { + if (!O.hasVal) reset(); else { - *this = std::move(*O); - O.reset(); + *this = std::move(*O.getPointer()); } return *this; } - /// Create a new object by constructing it in place with the given arguments. - template - void emplace(ArgTypes &&...Args) { - reset(); - hasVal = true; - new (storage.buffer) T(std::forward(Args)...); - } - - static inline Optional create(const T* y) { - return y ? Optional(*y) : Optional(); - } - // FIXME: these assignments (& the equivalent const T&/const Optional& ctors) // could be made more efficient by passing by value, possibly unifying them // with the rvalue versions above - but this could place a different set of // requirements (notably: the existence of a default ctor) when implemented // in that way. Careful SFINAE to avoid such pitfalls would be required. - Optional &operator=(const T &y) { + OptionalStorage &operator=(const T &y) { if (hasVal) - **this = y; + *getPointer() = y; else { new (storage.buffer) T(y); hasVal = true; } return *this; } - - Optional &operator=(const Optional &O) { - if (!O) + OptionalStorage &operator=(const OptionalStorage &O) { + if (!O.hasVal) reset(); else - *this = *O; + *this = *O.getPointer(); return *this; } + ~OptionalStorage() { reset(); } + void reset() { if (hasVal) { - (**this).~T(); + (*getPointer()).~T(); hasVal = false; } } - ~Optional() { - reset(); + T *getPointer() { + assert(hasVal); + return reinterpret_cast(storage.buffer); + } + const T *getPointer() const { + assert(hasVal); + return reinterpret_cast(storage.buffer); + } +}; + +#if !defined(__GNUC__) || defined(__clang__) // GCC up to GCC7 miscompiles this. +/// Storage for trivially copyable types only. +template struct OptionalStorage { + AlignedCharArrayUnion storage; + bool hasVal = false; + + OptionalStorage() = default; + + OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); } + OptionalStorage &operator=(const T &y) { + *reinterpret_cast(storage.buffer) = y; + hasVal = true; + return *this; } - const T* getPointer() const { assert(hasVal); return reinterpret_cast(storage.buffer); } - T* getPointer() { assert(hasVal); return reinterpret_cast(storage.buffer); } - const T& getValue() const LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } - T& getValue() LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } + void reset() { hasVal = false; } +}; +#endif +} // namespace optional_detail - explicit operator bool() const { return hasVal; } - bool hasValue() const { return hasVal; } - const T* operator->() const { return getPointer(); } - T* operator->() { return getPointer(); } - const T& operator*() const LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } - T& operator*() LLVM_LVALUE_FUNCTION { assert(hasVal); return *getPointer(); } +template class Optional { + optional_detail::OptionalStorage::value> Storage; + +public: + using value_type = T; + + constexpr Optional() {} + constexpr Optional(NoneType) {} + + Optional(const T &y) : Storage(y) {} + Optional(const Optional &O) = default; + + Optional(T &&y) : Storage(std::forward(y)) {} + Optional(Optional &&O) = default; + + Optional &operator=(T &&y) { + Storage = std::move(y); + return *this; + } + Optional &operator=(Optional &&O) = default; + + /// Create a new object by constructing it in place with the given arguments. + template void emplace(ArgTypes &&... Args) { + reset(); + Storage.hasVal = true; + new (getPointer()) T(std::forward(Args)...); + } + + static inline Optional create(const T *y) { + return y ? Optional(*y) : Optional(); + } + + Optional &operator=(const T &y) { + Storage = y; + return *this; + } + Optional &operator=(const Optional &O) = default; + + void reset() { Storage.reset(); } + + const T *getPointer() const { + assert(Storage.hasVal); + return reinterpret_cast(Storage.storage.buffer); + } + T *getPointer() { + assert(Storage.hasVal); + return reinterpret_cast(Storage.storage.buffer); + } + const T &getValue() const LLVM_LVALUE_FUNCTION { return *getPointer(); } + T &getValue() LLVM_LVALUE_FUNCTION { return *getPointer(); } + + explicit operator bool() const { return Storage.hasVal; } + bool hasValue() const { return Storage.hasVal; } + const T *operator->() const { return getPointer(); } + T *operator->() { return getPointer(); } + const T &operator*() const LLVM_LVALUE_FUNCTION { return *getPointer(); } + T &operator*() LLVM_LVALUE_FUNCTION { return *getPointer(); } template - LLVM_CONSTEXPR T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION { + constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION { return hasValue() ? getValue() : std::forward(value); } #if LLVM_HAS_RVALUE_REFERENCE_THIS - T&& getValue() && { assert(hasVal); return std::move(*getPointer()); } - T&& operator*() && { assert(hasVal); return std::move(*getPointer()); } + T &&getValue() && { return std::move(*getPointer()); } + T &&operator*() && { return std::move(*getPointer()); } template T getValueOr(U &&value) && { @@ -144,24 +202,48 @@ public: #endif }; -template struct isPodLike; -template struct isPodLike > { +template struct isPodLike> { // An Optional is pod-like if T is. static const bool value = isPodLike::value; }; -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator==(const Optional &X, const Optional &Y); +template +bool operator==(const Optional &X, const Optional &Y) { + if (X && Y) + return *X == *Y; + return X.hasValue() == Y.hasValue(); +} + +template +bool operator!=(const Optional &X, const Optional &Y) { + return !(X == Y); +} + +template +bool operator<(const Optional &X, const Optional &Y) { + if (X && Y) + return *X < *Y; + return X.hasValue() < Y.hasValue(); +} + +template +bool operator<=(const Optional &X, const Optional &Y) { + return !(Y < X); +} + +template +bool operator>(const Optional &X, const Optional &Y) { + return Y < X; +} + +template +bool operator>=(const Optional &X, const Optional &Y) { + return !(X < Y); +} template bool operator==(const Optional &X, NoneType) { - return !X.hasValue(); + return !X; } template @@ -178,50 +260,86 @@ template bool operator!=(NoneType, const Optional &X) { return X != None; } -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator!=(const Optional &X, const Optional &Y); -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator<(const Optional &X, const Optional &Y); +template bool operator<(const Optional &X, NoneType) { + return false; +} -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator<=(const Optional &X, const Optional &Y); +template bool operator<(NoneType, const Optional &X) { + return X.hasValue(); +} -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator>=(const Optional &X, const Optional &Y); +template bool operator<=(const Optional &X, NoneType) { + return !(None < X); +} -/// \brief Poison comparison between two \c Optional objects. Clients needs to -/// explicitly compare the underlying values and account for empty \c Optional -/// objects. -/// -/// This routine will never be defined. It returns \c void to help diagnose -/// errors at compile time. -template -void operator>(const Optional &X, const Optional &Y); +template bool operator<=(NoneType, const Optional &X) { + return !(X < None); +} + +template bool operator>(const Optional &X, NoneType) { + return None < X; +} + +template bool operator>(NoneType, const Optional &X) { + return X < None; +} + +template bool operator>=(const Optional &X, NoneType) { + return None <= X; +} + +template bool operator>=(NoneType, const Optional &X) { + return X <= None; +} + +template bool operator==(const Optional &X, const T &Y) { + return X && *X == Y; +} + +template bool operator==(const T &X, const Optional &Y) { + return Y && X == *Y; +} + +template bool operator!=(const Optional &X, const T &Y) { + return !(X == Y); +} + +template bool operator!=(const T &X, const Optional &Y) { + return !(X == Y); +} + +template bool operator<(const Optional &X, const T &Y) { + return !X || *X < Y; +} + +template bool operator<(const T &X, const Optional &Y) { + return Y && X < *Y; +} + +template bool operator<=(const Optional &X, const T &Y) { + return !(Y < X); +} + +template bool operator<=(const T &X, const Optional &Y) { + return !(Y < X); +} + +template bool operator>(const Optional &X, const T &Y) { + return Y < X; +} + +template bool operator>(const T &X, const Optional &Y) { + return Y < X; +} + +template bool operator>=(const Optional &X, const T &Y) { + return !(X < Y); +} + +template bool operator>=(const T &X, const Optional &Y) { + return !(X < Y); +} } // end wpi namespace diff --git a/wpiutil/src/main/native/include/wpi/Path.h b/wpiutil/src/main/native/include/wpi/Path.h index 12ee92f654..8f6236902d 100644 --- a/wpiutil/src/main/native/include/wpi/Path.h +++ b/wpiutil/src/main/native/include/wpi/Path.h @@ -16,20 +16,21 @@ #ifndef WPIUTIL_WPI_PATH_H_ #define WPIUTIL_WPI_PATH_H_ -#include - -#include - #include "wpi/Twine.h" +#include "wpi/iterator.h" +#include +#include namespace wpi { namespace sys { namespace path { +enum class Style { windows, posix, native }; + /// @name Lexical Component Iterator /// @{ -/// @brief Path iterator. +/// Path iterator. /// /// This is an input iterator that iterates over the individual components in /// \a path. The traversal order is as follows: @@ -49,67 +50,67 @@ namespace path { /// C:\foo\bar => C:,/,foo,bar /// @endcode class const_iterator - : public std::iterator { + : public iterator_facade_base { StringRef Path; ///< The entire path. StringRef Component; ///< The current component. Not necessarily in Path. - size_t Position; ///< The iterators current position within Path. + size_t Position; ///< The iterators current position within Path. + Style S; ///< The path style to use. // An end iterator has Position = Path.size() + 1. - friend const_iterator begin(StringRef path); + friend const_iterator begin(StringRef path, Style style); friend const_iterator end(StringRef path); public: reference operator*() const { return Component; } - pointer operator->() const { return &Component; } - const_iterator& operator++(); // preincrement + const_iterator &operator++(); // preincrement bool operator==(const const_iterator &RHS) const; - bool operator!=(const const_iterator &RHS) const { return !(*this == RHS); } - /// @brief Difference in bytes between this and RHS. + /// Difference in bytes between this and RHS. ptrdiff_t operator-(const const_iterator &RHS) const; }; -/// @brief Reverse path iterator. +/// Reverse path iterator. /// /// This is an input iterator that iterates over the individual components in /// \a path in reverse order. The traversal order is exactly reversed from that /// of \a const_iterator class reverse_iterator - : public std::iterator { + : public iterator_facade_base { StringRef Path; ///< The entire path. StringRef Component; ///< The current component. Not necessarily in Path. - size_t Position; ///< The iterators current position within Path. + size_t Position; ///< The iterators current position within Path. + Style S; ///< The path style to use. - friend reverse_iterator rbegin(StringRef path); + friend reverse_iterator rbegin(StringRef path, Style style); friend reverse_iterator rend(StringRef path); public: reference operator*() const { return Component; } - pointer operator->() const { return &Component; } - reverse_iterator& operator++(); // preincrement + reverse_iterator &operator++(); // preincrement bool operator==(const reverse_iterator &RHS) const; - bool operator!=(const reverse_iterator &RHS) const { return !(*this == RHS); } - /// @brief Difference in bytes between this and RHS. + /// Difference in bytes between this and RHS. ptrdiff_t operator-(const reverse_iterator &RHS) const; }; -/// @brief Get begin iterator over \a path. +/// Get begin iterator over \a path. /// @param path Input path. /// @returns Iterator initialized with the first component of \a path. -const_iterator begin(StringRef path); +const_iterator begin(StringRef path, Style style = Style::native); -/// @brief Get end iterator over \a path. +/// Get end iterator over \a path. /// @param path Input path. /// @returns Iterator initialized to the end of \a path. const_iterator end(StringRef path); -/// @brief Get reverse begin iterator over \a path. +/// Get reverse begin iterator over \a path. /// @param path Input path. /// @returns Iterator initialized with the first reverse component of \a path. -reverse_iterator rbegin(StringRef path); +reverse_iterator rbegin(StringRef path, Style style = Style::native); -/// @brief Get reverse end iterator over \a path. +/// Get reverse end iterator over \a path. /// @param path Input path. /// @returns Iterator initialized to the reverse end of \a path. reverse_iterator rend(StringRef path); @@ -118,7 +119,7 @@ reverse_iterator rend(StringRef path); /// @name Lexical Modifiers /// @{ -/// @brief Remove the last component from \a path unless it is the root dir. +/// Remove the last component from \a path unless it is the root dir. /// /// @code /// directory/filename.cpp => directory/ @@ -128,9 +129,9 @@ reverse_iterator rend(StringRef path); /// @endcode /// /// @param path A path that is modified to not have a file component. -void remove_filename(SmallVectorImpl &path); +void remove_filename(SmallVectorImpl &path, Style style = Style::native); -/// @brief Replace the file extension of \a path with \a extension. +/// Replace the file extension of \a path with \a extension. /// /// @code /// ./filename.cpp => ./filename.extension @@ -142,9 +143,10 @@ void remove_filename(SmallVectorImpl &path); /// @param extension The extension to be added. It may be empty. It may also /// optionally start with a '.', if it does not, one will be /// prepended. -void replace_extension(SmallVectorImpl &path, const Twine &extension); +void replace_extension(SmallVectorImpl &path, const Twine &extension, + Style style = Style::native); -/// @brief Replace matching path prefix with another path. +/// Replace matching path prefix with another path. /// /// @code /// /foo, /old, /new => /foo @@ -158,10 +160,10 @@ void replace_extension(SmallVectorImpl &path, const Twine &extension); /// @param OldPrefix The path prefix to strip from \a Path. /// @param NewPrefix The path prefix to replace \a NewPrefix with. void replace_path_prefix(SmallVectorImpl &Path, - const StringRef &OldPrefix, - const StringRef &NewPrefix); + const StringRef &OldPrefix, const StringRef &NewPrefix, + Style style = Style::native); -/// @brief Append to path. +/// Append to path. /// /// @code /// /foo + bar/f => /foo/bar/f @@ -176,7 +178,10 @@ void append(SmallVectorImpl &path, const Twine &a, const Twine &c = "", const Twine &d = ""); -/// @brief Append to path. +void append(SmallVectorImpl &path, Style style, const Twine &a, + const Twine &b = "", const Twine &c = "", const Twine &d = ""); + +/// Append to path. /// /// @code /// /foo + [bar,f] => /foo/bar/f @@ -187,8 +192,8 @@ void append(SmallVectorImpl &path, const Twine &a, /// @param path Set to \a path + [\a begin, \a end). /// @param begin Start of components to append. /// @param end One past the end of components to append. -void append(SmallVectorImpl &path, - const_iterator begin, const_iterator end); +void append(SmallVectorImpl &path, const_iterator begin, + const_iterator end, Style style = Style::native); /// @} /// @name Transforms (or some other better name) @@ -200,20 +205,29 @@ void append(SmallVectorImpl &path, /// /// @param path A path that is transformed to native format. /// @param result Holds the result of the transformation. -void native(const Twine &path, SmallVectorImpl &result); +void native(const Twine &path, SmallVectorImpl &result, + Style style = Style::native); /// Convert path to the native form in place. This is used to give paths to /// users and operating system calls in the platform's normal way. For example, /// on Windows all '/' are converted to '\'. /// /// @param path A path that is transformed to native format. -void native(SmallVectorImpl &path); +void native(SmallVectorImpl &path, Style style = Style::native); + +/// Replaces backslashes with slashes if Windows. +/// +/// @param path processed path +/// @result The result of replacing backslashes with forward slashes if Windows. +/// On Unix, this function is a no-op because backslashes are valid path +/// chracters. +std::string convert_to_slash(StringRef path, Style style = Style::native); /// @} /// @name Lexical Observers /// @{ -/// @brief Get root name. +/// Get root name. /// /// @code /// //net/hello => //net @@ -223,9 +237,9 @@ void native(SmallVectorImpl &path); /// /// @param path Input path. /// @result The root name of \a path if it has one, otherwise "". -StringRef root_name(StringRef path); +StringRef root_name(StringRef path, Style style = Style::native); -/// @brief Get root directory. +/// Get root directory. /// /// @code /// /goo/hello => / @@ -236,17 +250,17 @@ StringRef root_name(StringRef path); /// @param path Input path. /// @result The root directory of \a path if it has one, otherwise /// "". -StringRef root_directory(StringRef path); +StringRef root_directory(StringRef path, Style style = Style::native); -/// @brief Get root path. +/// Get root path. /// /// Equivalent to root_name + root_directory. /// /// @param path Input path. /// @result The root path of \a path if it has one, otherwise "". -StringRef root_path(StringRef path); +StringRef root_path(StringRef path, Style style = Style::native); -/// @brief Get relative path. +/// Get relative path. /// /// @code /// C:\hello\world => hello\world @@ -256,9 +270,9 @@ StringRef root_path(StringRef path); /// /// @param path Input path. /// @result The path starting after root_path if one exists, otherwise "". -StringRef relative_path(StringRef path); +StringRef relative_path(StringRef path, Style style = Style::native); -/// @brief Get parent path. +/// Get parent path. /// /// @code /// / => @@ -268,9 +282,9 @@ StringRef relative_path(StringRef path); /// /// @param path Input path. /// @result The parent path of \a path if one exists, otherwise "". -StringRef parent_path(StringRef path); +StringRef parent_path(StringRef path, Style style = Style::native); -/// @brief Get filename. +/// Get filename. /// /// @code /// /foo.txt => foo.txt @@ -282,9 +296,9 @@ StringRef parent_path(StringRef path); /// @param path Input path. /// @result The filename part of \a path. This is defined as the last component /// of \a path. -StringRef filename(StringRef path); +StringRef filename(StringRef path, Style style = Style::native); -/// @brief Get stem. +/// Get stem. /// /// If filename contains a dot but not solely one or two dots, result is the /// substring of filename ending at (but not including) the last dot. Otherwise @@ -300,9 +314,9 @@ StringRef filename(StringRef path); /// /// @param path Input path. /// @result The stem of \a path. -StringRef stem(StringRef path); +StringRef stem(StringRef path, Style style = Style::native); -/// @brief Get extension. +/// Get extension. /// /// If filename contains a dot but not solely one or two dots, result is the /// substring of filename starting at (and including) the last dot, and ending @@ -316,20 +330,20 @@ StringRef stem(StringRef path); /// /// @param path Input path. /// @result The extension of \a path. -StringRef extension(StringRef path); +StringRef extension(StringRef path, Style style = Style::native); -/// @brief Check whether the given char is a path separator on the host OS. +/// Check whether the given char is a path separator on the host OS. /// /// @param value a character /// @result true if \a value is a path separator character on the host OS -bool is_separator(char value); +bool is_separator(char value, Style style = Style::native); -/// @brief Return the preferred separator for this platform. +/// Return the preferred separator for this platform. /// /// @result StringRef of the preferred separator, null-terminated. -StringRef get_separator(); +StringRef get_separator(Style style = Style::native); -/// @brief Get the typical temporary directory for the system, e.g., +/// Get the typical temporary directory for the system, e.g., /// "/var/tmp" or "C:/TEMP" /// /// @param erasedOnReboot Whether to favor a path that is erased on reboot @@ -340,13 +354,13 @@ StringRef get_separator(); /// @param result Holds the resulting path name. void system_temp_directory(bool erasedOnReboot, SmallVectorImpl &result); -/// @brief Get the user's home directory. +/// Get the user's home directory. /// /// @param result Holds the resulting path name. /// @result True if a home directory is set, false otherwise. bool home_directory(SmallVectorImpl &result); -/// @brief Get the user's cache directory. +/// Get the user's cache directory. /// /// Expect the resulting path to be a directory shared with other /// applications/services used by the user. Params \p Path1 to \p Path3 can be @@ -362,97 +376,99 @@ bool home_directory(SmallVectorImpl &result); bool user_cache_directory(SmallVectorImpl &Result, const Twine &Path1, const Twine &Path2 = "", const Twine &Path3 = ""); -/// @brief Has root name? +/// Has root name? /// /// root_name != "" /// /// @param path Input path. /// @result True if the path has a root name, false otherwise. -bool has_root_name(const Twine &path); +bool has_root_name(const Twine &path, Style style = Style::native); -/// @brief Has root directory? +/// Has root directory? /// /// root_directory != "" /// /// @param path Input path. /// @result True if the path has a root directory, false otherwise. -bool has_root_directory(const Twine &path); +bool has_root_directory(const Twine &path, Style style = Style::native); -/// @brief Has root path? +/// Has root path? /// /// root_path != "" /// /// @param path Input path. /// @result True if the path has a root path, false otherwise. -bool has_root_path(const Twine &path); +bool has_root_path(const Twine &path, Style style = Style::native); -/// @brief Has relative path? +/// Has relative path? /// /// relative_path != "" /// /// @param path Input path. /// @result True if the path has a relative path, false otherwise. -bool has_relative_path(const Twine &path); +bool has_relative_path(const Twine &path, Style style = Style::native); -/// @brief Has parent path? +/// Has parent path? /// /// parent_path != "" /// /// @param path Input path. /// @result True if the path has a parent path, false otherwise. -bool has_parent_path(const Twine &path); +bool has_parent_path(const Twine &path, Style style = Style::native); -/// @brief Has filename? +/// Has filename? /// /// filename != "" /// /// @param path Input path. /// @result True if the path has a filename, false otherwise. -bool has_filename(const Twine &path); +bool has_filename(const Twine &path, Style style = Style::native); -/// @brief Has stem? +/// Has stem? /// /// stem != "" /// /// @param path Input path. /// @result True if the path has a stem, false otherwise. -bool has_stem(const Twine &path); +bool has_stem(const Twine &path, Style style = Style::native); -/// @brief Has extension? +/// Has extension? /// /// extension != "" /// /// @param path Input path. /// @result True if the path has a extension, false otherwise. -bool has_extension(const Twine &path); +bool has_extension(const Twine &path, Style style = Style::native); -/// @brief Is path absolute? +/// Is path absolute? /// /// @param path Input path. /// @result True if the path is absolute, false if it is not. -bool is_absolute(const Twine &path); +bool is_absolute(const Twine &path, Style style = Style::native); -/// @brief Is path relative? +/// Is path relative? /// /// @param path Input path. /// @result True if the path is relative, false if it is not. -bool is_relative(const Twine &path); +bool is_relative(const Twine &path, Style style = Style::native); -/// @brief Remove redundant leading "./" pieces and consecutive separators. +/// Remove redundant leading "./" pieces and consecutive separators. /// /// @param path Input path. /// @result The cleaned-up \a path. -StringRef remove_leading_dotslash(StringRef path); +StringRef remove_leading_dotslash(StringRef path, Style style = Style::native); -/// @brief In-place remove any './' and optionally '../' components from a path. +/// In-place remove any './' and optionally '../' components from a path. /// /// @param path processed path -/// @param remove_dot_dot specify if '../' should be removed +/// @param remove_dot_dot specify if '../' (except for leading "../") should be +/// removed /// @result True if path was changed -bool remove_dots(SmallVectorImpl &path, bool remove_dot_dot = false); +bool remove_dots(SmallVectorImpl &path, bool remove_dot_dot = false, + Style style = Style::native); -} // namespace path -} // namespace sys -} // namespace wpi +} // end namespace path +} // end namespace sys +} // end namespace wpi -#endif // WPIUTIL_WPI_PATH_H_ +#endif diff --git a/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h b/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h index 3a27fa3e70..90989540e2 100644 --- a/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h +++ b/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h @@ -12,21 +12,18 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_POINTERLIKETYPETRAITS_H -#define LLVM_SUPPORT_POINTERLIKETYPETRAITS_H +#ifndef WPIUTIL_WPI_POINTERLIKETYPETRAITS_H +#define WPIUTIL_WPI_POINTERLIKETYPETRAITS_H -#include "wpi/AlignOf.h" #include +#include +#include namespace wpi { /// A traits type that is used to handle pointer types and things that are just /// wrappers for pointers as a uniform entity. -template class PointerLikeTypeTraits { - // getAsVoidPointer - // getFromVoidPointer - // getNumLowBitsAvailable -}; +template struct PointerLikeTypeTraits; namespace detail { /// A tiny meta function to compute the log2 of a compile time constant. @@ -34,21 +31,36 @@ template struct ConstantLog2 : std::integral_constant::value + 1> {}; template <> struct ConstantLog2<1> : std::integral_constant {}; -} + +// Provide a trait to check if T is pointer-like. +template struct HasPointerLikeTypeTraits { + static const bool value = false; +}; + +// sizeof(T) is valid only for a complete T. +template struct HasPointerLikeTypeTraits< + T, decltype((sizeof(PointerLikeTypeTraits) + sizeof(T)), void())> { + static const bool value = true; +}; + +template struct IsPointerLike { + static const bool value = HasPointerLikeTypeTraits::value; +}; + +template struct IsPointerLike { + static const bool value = true; +}; +} // namespace detail // Provide PointerLikeTypeTraits for non-cvr pointers. -template class PointerLikeTypeTraits { -public: +template struct PointerLikeTypeTraits { static inline void *getAsVoidPointer(T *P) { return P; } static inline T *getFromVoidPointer(void *P) { return static_cast(P); } - enum { - NumLowBitsAvailable = detail::ConstantLog2::Alignment>::value - }; + enum { NumLowBitsAvailable = detail::ConstantLog2::value }; }; -template <> class PointerLikeTypeTraits { -public: +template <> struct PointerLikeTypeTraits { static inline void *getAsVoidPointer(void *P) { return P; } static inline void *getFromVoidPointer(void *P) { return P; } @@ -62,11 +74,23 @@ public: enum { NumLowBitsAvailable = 2 }; }; +// Provide PointerLikeTypeTraits for const things. +template struct PointerLikeTypeTraits { + typedef PointerLikeTypeTraits NonConst; + + static inline const void *getAsVoidPointer(const T P) { + return NonConst::getAsVoidPointer(P); + } + static inline const T getFromVoidPointer(const void *P) { + return NonConst::getFromVoidPointer(const_cast(P)); + } + enum { NumLowBitsAvailable = NonConst::NumLowBitsAvailable }; +}; + // Provide PointerLikeTypeTraits for const pointers. -template class PointerLikeTypeTraits { +template struct PointerLikeTypeTraits { typedef PointerLikeTypeTraits NonConst; -public: static inline const void *getAsVoidPointer(const T *P) { return NonConst::getAsVoidPointer(const_cast(P)); } @@ -77,8 +101,7 @@ public: }; // Provide PointerLikeTypeTraits for uintptr_t. -template <> class PointerLikeTypeTraits { -public: +template <> struct PointerLikeTypeTraits { static inline void *getAsVoidPointer(uintptr_t P) { return reinterpret_cast(P); } diff --git a/wpiutil/src/main/native/include/wpi/STLExtras.h b/wpiutil/src/main/native/include/wpi/STLExtras.h index daaf4ab44a..de6c12d5ca 100644 --- a/wpiutil/src/main/native/include/wpi/STLExtras.h +++ b/wpiutil/src/main/native/include/wpi/STLExtras.h @@ -14,29 +14,51 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_ADT_STLEXTRAS_H -#define LLVM_ADT_STLEXTRAS_H - -#include // for std::all_of -#include -#include // for std::size_t -#include // for qsort -#include -#include -#include -#include // for std::pair +#ifndef WPIUTIL_WPI_STLEXTRAS_H +#define WPIUTIL_WPI_STLEXTRAS_H +#include "wpi/Optional.h" +#include "wpi/SmallVector.h" +#include "wpi/iterator.h" #include "wpi/iterator_range.h" -#include "wpi/Compiler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace wpi { +// Only used by compiler if both template types are the same. Useful when +// using SFINAE to test for the existence of member functions. +template struct SameType; + +namespace detail { + +template +using IterOfRange = decltype(std::begin(std::declval())); + +template +using ValueOfRange = typename std::remove_reference()))>::type; + +} // end namespace detail + //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// -template -struct identity : public std::unary_function { +template struct identity { + using argument_type = Ty; + Ty &operator()(Ty &self) const { return self; } @@ -45,15 +67,13 @@ struct identity : public std::unary_function { } }; -template -struct less_ptr : public std::binary_function { +template struct less_ptr { bool operator()(const Ty* left, const Ty* right) const { return *left < *right; } }; -template -struct greater_ptr : public std::binary_function { +template struct greater_ptr { bool operator()(const Ty* left, const Ty* right) const { return *right < *left; } @@ -69,7 +89,7 @@ template class function_ref; template class function_ref { - Ret (*callback)(intptr_t callable, Params ...params); + Ret (*callback)(intptr_t callable, Params ...params) = nullptr; intptr_t callable; template @@ -79,6 +99,9 @@ class function_ref { } public: + function_ref() = default; + function_ref(std::nullptr_t) {} + template function_ref(Callable &&callable, typename std::enable_if< @@ -86,125 +109,121 @@ public: function_ref>::value>::type * = nullptr) : callback(callback_fn::type>), callable(reinterpret_cast(&callable)) {} + Ret operator()(Params ...params) const { return callback(callable, std::forward(params)...); } + + explicit operator bool() const { return callback; } }; // deleter - Very very very simple method that is used to invoke operator // delete on something. It is used like this: // // for_each(V.begin(), B.end(), deleter); -// template inline void deleter(T *Ptr) { delete Ptr; } - - //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// -// mapped_iterator - This is a simple iterator adapter that causes a function to -// be dereferenced whenever operator* is invoked on the iterator. -// -template -class mapped_iterator { - RootIt current; - UnaryFunc Fn; -public: - typedef typename std::iterator_traits::iterator_category - iterator_category; - typedef typename std::iterator_traits::difference_type - difference_type; - typedef typename std::result_of< - UnaryFunc(decltype(*std::declval()))> - ::type value_type; +namespace adl_detail { - typedef void pointer; - //typedef typename UnaryFunc::result_type *pointer; - typedef void reference; // Can't modify value returned by fn +using std::begin; - typedef RootIt iterator_type; - - inline const RootIt &getCurrent() const { return current; } - inline const UnaryFunc &getFunc() const { return Fn; } - - inline explicit mapped_iterator(const RootIt &I, UnaryFunc F) - : current(I), Fn(F) {} - - inline value_type operator*() const { // All this work to do this - return Fn(*current); // little change - } - - mapped_iterator &operator++() { - ++current; - return *this; - } - mapped_iterator &operator--() { - --current; - return *this; - } - mapped_iterator operator++(int) { - mapped_iterator __tmp = *this; - ++current; - return __tmp; - } - mapped_iterator operator--(int) { - mapped_iterator __tmp = *this; - --current; - return __tmp; - } - mapped_iterator operator+(difference_type n) const { - return mapped_iterator(current + n, Fn); - } - mapped_iterator &operator+=(difference_type n) { - current += n; - return *this; - } - mapped_iterator operator-(difference_type n) const { - return mapped_iterator(current - n, Fn); - } - mapped_iterator &operator-=(difference_type n) { - current -= n; - return *this; - } - reference operator[](difference_type n) const { return *(*this + n); } - - bool operator!=(const mapped_iterator &X) const { return !operator==(X); } - bool operator==(const mapped_iterator &X) const { - return current == X.current; - } - bool operator<(const mapped_iterator &X) const { return current < X.current; } - - difference_type operator-(const mapped_iterator &X) const { - return current - X.current; - } -}; - -template -inline mapped_iterator -operator+(typename mapped_iterator::difference_type N, - const mapped_iterator &X) { - return mapped_iterator(X.getCurrent() - N, X.getFunc()); +template +auto adl_begin(ContainerTy &&container) + -> decltype(begin(std::forward(container))) { + return begin(std::forward(container)); } +using std::end; + +template +auto adl_end(ContainerTy &&container) + -> decltype(end(std::forward(container))) { + return end(std::forward(container)); +} + +using std::swap; + +template +void adl_swap(T &&lhs, T &&rhs) noexcept(noexcept(swap(std::declval(), + std::declval()))) { + swap(std::forward(lhs), std::forward(rhs)); +} + +} // end namespace adl_detail + +template +auto adl_begin(ContainerTy &&container) + -> decltype(adl_detail::adl_begin(std::forward(container))) { + return adl_detail::adl_begin(std::forward(container)); +} + +template +auto adl_end(ContainerTy &&container) + -> decltype(adl_detail::adl_end(std::forward(container))) { + return adl_detail::adl_end(std::forward(container)); +} + +template +void adl_swap(T &&lhs, T &&rhs) noexcept( + noexcept(adl_detail::adl_swap(std::declval(), std::declval()))) { + adl_detail::adl_swap(std::forward(lhs), std::forward(rhs)); +} + +// mapped_iterator - This is a simple iterator adapter that causes a function to +// be applied whenever operator* is invoked on the iterator. + +template ()(*std::declval()))> +class mapped_iterator + : public iterator_adaptor_base< + mapped_iterator, ItTy, + typename std::iterator_traits::iterator_category, + typename std::remove_reference::type> { +public: + mapped_iterator(ItTy U, FuncTy F) + : mapped_iterator::iterator_adaptor_base(std::move(U)), F(std::move(F)) {} + + ItTy getCurrent() { return this->I; } + + FuncReturnTy operator*() { return F(*this->I); } + +private: + FuncTy F; +}; // map_iterator - Provide a convenient way to create mapped_iterators, just like // make_pair is useful for creating pairs... -// template -inline mapped_iterator map_iterator(const ItTy &I, FuncTy F) { - return mapped_iterator(I, F); +inline mapped_iterator map_iterator(ItTy I, FuncTy F) { + return mapped_iterator(std::move(I), std::move(F)); } -/// \brief Metafunction to determine if type T has a member called rbegin(). -template struct has_rbegin { - template static char(&f(const U &, decltype(&U::rbegin)))[1]; - static char(&f(...))[2]; - const static bool value = sizeof(f(std::declval(), nullptr)) == 1; +/// Helper to determine if type T has a member called rbegin(). +template class has_rbegin_impl { + using yes = char[1]; + using no = char[2]; + + template + static yes& test(Inner *I, decltype(I->rbegin()) * = nullptr); + + template + static no& test(...); + +public: + static const bool value = sizeof(test(nullptr)) == sizeof(yes); +}; + +/// Metafunction to determine if T& or T has a member called rbegin(). +template +struct has_rbegin : has_rbegin_impl::type> { }; // Returns an iterator_range over the given container which iterates in reverse. @@ -235,11 +254,459 @@ auto reverse( wpi::make_reverse_iterator(std::begin(C))); } +/// An iterator adaptor that filters the elements of given inner iterators. +/// +/// The predicate parameter should be a callable object that accepts the wrapped +/// iterator's reference type and returns a bool. When incrementing or +/// decrementing the iterator, it will call the predicate on each element and +/// skip any where it returns false. +/// +/// \code +/// int A[] = { 1, 2, 3, 4 }; +/// auto R = make_filter_range(A, [](int N) { return N % 2 == 1; }); +/// // R contains { 1, 3 }. +/// \endcode +/// +/// Note: filter_iterator_base implements support for forward iteration. +/// filter_iterator_impl exists to provide support for bidirectional iteration, +/// conditional on whether the wrapped iterator supports it. +template +class filter_iterator_base + : public iterator_adaptor_base< + filter_iterator_base, + WrappedIteratorT, + typename std::common_type< + IterTag, typename std::iterator_traits< + WrappedIteratorT>::iterator_category>::type> { + using BaseT = iterator_adaptor_base< + filter_iterator_base, + WrappedIteratorT, + typename std::common_type< + IterTag, typename std::iterator_traits< + WrappedIteratorT>::iterator_category>::type>; + +protected: + WrappedIteratorT End; + PredicateT Pred; + + void findNextValid() { + while (this->I != End && !Pred(*this->I)) + BaseT::operator++(); + } + + // Construct the iterator. The begin iterator needs to know where the end + // is, so that it can properly stop when it gets there. The end iterator only + // needs the predicate to support bidirectional iteration. + filter_iterator_base(WrappedIteratorT Begin, WrappedIteratorT End, + PredicateT Pred) + : BaseT(Begin), End(End), Pred(Pred) { + findNextValid(); + } + +public: + using BaseT::operator++; + + filter_iterator_base &operator++() { + BaseT::operator++(); + findNextValid(); + return *this; + } +}; + +/// Specialization of filter_iterator_base for forward iteration only. +template +class filter_iterator_impl + : public filter_iterator_base { + using BaseT = filter_iterator_base; + +public: + filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End, + PredicateT Pred) + : BaseT(Begin, End, Pred) {} +}; + +/// Specialization of filter_iterator_base for bidirectional iteration. +template +class filter_iterator_impl + : public filter_iterator_base { + using BaseT = filter_iterator_base; + void findPrevValid() { + while (!this->Pred(*this->I)) + BaseT::operator--(); + } + +public: + using BaseT::operator--; + + filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End, + PredicateT Pred) + : BaseT(Begin, End, Pred) {} + + filter_iterator_impl &operator--() { + BaseT::operator--(); + findPrevValid(); + return *this; + } +}; + +namespace detail { + +template struct fwd_or_bidi_tag_impl { + using type = std::forward_iterator_tag; +}; + +template <> struct fwd_or_bidi_tag_impl { + using type = std::bidirectional_iterator_tag; +}; + +/// Helper which sets its type member to forward_iterator_tag if the category +/// of \p IterT does not derive from bidirectional_iterator_tag, and to +/// bidirectional_iterator_tag otherwise. +template struct fwd_or_bidi_tag { + using type = typename fwd_or_bidi_tag_impl::iterator_category>::value>::type; +}; + +} // namespace detail + +/// Defines filter_iterator to a suitable specialization of +/// filter_iterator_impl, based on the underlying iterator's category. +template +using filter_iterator = filter_iterator_impl< + WrappedIteratorT, PredicateT, + typename detail::fwd_or_bidi_tag::type>; + +/// Convenience function that takes a range of elements and a predicate, +/// and return a new filter_iterator range. +/// +/// FIXME: Currently if RangeT && is a rvalue reference to a temporary, the +/// lifetime of that temporary is not kept by the returned range object, and the +/// temporary is going to be dropped on the floor after the make_iterator_range +/// full expression that contains this function call. +template +iterator_range, PredicateT>> +make_filter_range(RangeT &&Range, PredicateT Pred) { + using FilterIteratorT = + filter_iterator, PredicateT>; + return make_range( + FilterIteratorT(std::begin(std::forward(Range)), + std::end(std::forward(Range)), Pred), + FilterIteratorT(std::end(std::forward(Range)), + std::end(std::forward(Range)), Pred)); +} + +// forward declarations required by zip_shortest/zip_first +template +bool all_of(R &&range, UnaryPredicate P); + +template struct index_sequence; + +template struct index_sequence_for; + +namespace detail { + +using std::declval; + +// We have to alias this since inlining the actual type at the usage site +// in the parameter list of iterator_facade_base<> below ICEs MSVC 2017. +template struct ZipTupleType { + using type = std::tuple())...>; +}; + +template +using zip_traits = iterator_facade_base< + ZipType, typename std::common_type::iterator_category...>::type, + // ^ TODO: Implement random access methods. + typename ZipTupleType::type, + typename std::iterator_traits>::type>::difference_type, + // ^ FIXME: This follows boost::make_zip_iterator's assumption that all + // inner iterators have the same difference_type. It would fail if, for + // instance, the second field's difference_type were non-numeric while the + // first is. + typename ZipTupleType::type *, + typename ZipTupleType::type>; + +template +struct zip_common : public zip_traits { + using Base = zip_traits; + using value_type = typename Base::value_type; + + std::tuple iterators; + +protected: + template value_type deref(index_sequence) const { + return value_type(*std::get(iterators)...); + } + + template + decltype(iterators) tup_inc(index_sequence) const { + return std::tuple(std::next(std::get(iterators))...); + } + + template + decltype(iterators) tup_dec(index_sequence) const { + return std::tuple(std::prev(std::get(iterators))...); + } + +public: + zip_common(Iters &&... ts) : iterators(std::forward(ts)...) {} + + value_type operator*() { return deref(index_sequence_for{}); } + + const value_type operator*() const { + return deref(index_sequence_for{}); + } + + ZipType &operator++() { + iterators = tup_inc(index_sequence_for{}); + return *reinterpret_cast(this); + } + + ZipType &operator--() { + static_assert(Base::IsBidirectional, + "All inner iterators must be at least bidirectional."); + iterators = tup_dec(index_sequence_for{}); + return *reinterpret_cast(this); + } +}; + +template +struct zip_first : public zip_common, Iters...> { + using Base = zip_common, Iters...>; + + bool operator==(const zip_first &other) const { + return std::get<0>(this->iterators) == std::get<0>(other.iterators); + } + + zip_first(Iters &&... ts) : Base(std::forward(ts)...) {} +}; + +template +class zip_shortest : public zip_common, Iters...> { + template + bool test(const zip_shortest &other, index_sequence) const { + return all_of(std::initializer_list{std::get(this->iterators) != + std::get(other.iterators)...}, + identity{}); + } + +public: + using Base = zip_common, Iters...>; + + zip_shortest(Iters &&... ts) : Base(std::forward(ts)...) {} + + bool operator==(const zip_shortest &other) const { + return !test(other, index_sequence_for{}); + } +}; + +template