mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-27 02:01:42 +00:00
Update LLVM from stable upstream (#1653)
Replace CheckedMalloc with upstream safe_malloc.
This commit is contained in:
@@ -34,7 +34,7 @@ namespace wpi {
|
||||
|
||||
template<std::size_t Alignment, std::size_t Size>
|
||||
struct AlignedCharArray {
|
||||
LLVM_ALIGNAS(Alignment) char buffer[Size];
|
||||
alignas(Alignment) char buffer[Size];
|
||||
};
|
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <vector>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// ArrayRef - Represent a constant reference to an array (0 or more elements
|
||||
/// consecutively in memory), i.e. a start pointer and a length. It allows
|
||||
/// various APIs to take consecutive elements easily and conveniently.
|
||||
|
||||
63
wpiutil/src/main/native/include/wpi/Chrono.h
Normal file
63
wpiutil/src/main/native/include/wpi/Chrono.h
Normal file
@@ -0,0 +1,63 @@
|
||||
//===- llvm/Support/Chrono.h - Utilities for Timing Manipulation-*- 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_CHRONO_H
|
||||
#define WPIUTIL_WPI_CHRONO_H
|
||||
|
||||
#include "wpi/Compiler.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
class raw_ostream;
|
||||
|
||||
namespace sys {
|
||||
|
||||
/// A time point on the system clock. This is provided for two reasons:
|
||||
/// - to insulate us agains subtle differences in behavoir to differences in
|
||||
/// system clock precision (which is implementation-defined and differs between
|
||||
/// platforms).
|
||||
/// - to shorten the type name
|
||||
/// The default precision is nanoseconds. If need a specific precision specify
|
||||
/// it explicitly. If unsure, use the default. If you need a time point on a
|
||||
/// clock other than the system_clock, use std::chrono directly.
|
||||
template <typename D = std::chrono::nanoseconds>
|
||||
using TimePoint = std::chrono::time_point<std::chrono::system_clock, D>;
|
||||
|
||||
/// Convert a TimePoint to std::time_t
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE std::time_t toTimeT(TimePoint<> TP) {
|
||||
using namespace std::chrono;
|
||||
return system_clock::to_time_t(
|
||||
time_point_cast<system_clock::time_point::duration>(TP));
|
||||
}
|
||||
|
||||
/// Convert a std::time_t to a TimePoint
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE TimePoint<std::chrono::seconds>
|
||||
toTimePoint(std::time_t T) {
|
||||
using namespace std::chrono;
|
||||
return time_point_cast<seconds>(system_clock::from_time_t(T));
|
||||
}
|
||||
|
||||
/// Convert a std::time_t + nanoseconds to a TimePoint
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE TimePoint<>
|
||||
toTimePoint(std::time_t T, uint32_t nsec) {
|
||||
using namespace std::chrono;
|
||||
return time_point_cast<nanoseconds>(system_clock::from_time_t(T))
|
||||
+ nanoseconds(nsec);
|
||||
}
|
||||
|
||||
} // namespace sys
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_CHRONO_H
|
||||
@@ -287,6 +287,41 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression
|
||||
/// which causes the program to exit abnormally.
|
||||
#ifndef LLVM_BUILTIN_TRAP
|
||||
#if __has_builtin(__builtin_trap) || LLVM_GNUC_PREREQ(4, 3, 0)
|
||||
# define LLVM_BUILTIN_TRAP __builtin_trap()
|
||||
#elif defined(_MSC_VER)
|
||||
// The __debugbreak intrinsic is supported by MSVC, does not require forward
|
||||
// declarations involving platform-specific typedefs (unlike RaiseException),
|
||||
// results in a call to vectored exception handlers, and encodes to a short
|
||||
// instruction that still causes the trapping behavior we want.
|
||||
# define LLVM_BUILTIN_TRAP __debugbreak()
|
||||
#else
|
||||
# define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to
|
||||
/// an expression which causes the program to break while running
|
||||
/// under a debugger.
|
||||
#ifndef LLVM_BUILTIN_DEBUGTRAP
|
||||
#if __has_builtin(__builtin_debugtrap)
|
||||
# define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap()
|
||||
#elif defined(_MSC_VER)
|
||||
// The __debugbreak intrinsic is supported by MSVC and breaks while
|
||||
// running under the debugger, and also supports invoking a debugger
|
||||
// when the OS is configured appropriately.
|
||||
# define LLVM_BUILTIN_DEBUGTRAP __debugbreak()
|
||||
#else
|
||||
// Just continue execution when built with compilers that have no
|
||||
// support. This is a debugging aid and not intended to force the
|
||||
// program to abort if encountered.
|
||||
# define LLVM_BUILTIN_DEBUGTRAP
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_ASSUME_ALIGNED
|
||||
/// Returns a pointer with an assumed alignment.
|
||||
#ifndef LLVM_ASSUME_ALIGNED
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
// Wrap everything in namespace wpi so that programs can link with wpiutil and
|
||||
// their own version of the unicode libraries.
|
||||
@@ -245,6 +246,21 @@ bool convertUTF16ToUTF8String(ArrayRef<UTF16> SrcUTF16,
|
||||
bool convertUTF8ToUTF16String(StringRef SrcUTF8,
|
||||
SmallVectorImpl<UTF16> &DstUTF16);
|
||||
|
||||
#if defined(_WIN32)
|
||||
namespace sys {
|
||||
namespace windows {
|
||||
std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
|
||||
/// Convert to UTF16 from the current code page used in the system
|
||||
std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
|
||||
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
/// Convert from UTF16 to the current code page used in the system
|
||||
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
} // namespace windows
|
||||
} // namespace sys
|
||||
#endif
|
||||
|
||||
} /* end namespace wpi */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -46,9 +46,10 @@ struct DenseMapPair : public std::pair<KeyT, ValueT> {
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
template <
|
||||
typename KeyT, typename ValueT, typename KeyInfoT = DenseMapInfo<KeyT>,
|
||||
typename Bucket = detail::DenseMapPair<KeyT, ValueT>, bool IsConst = false>
|
||||
template <typename KeyT, typename ValueT,
|
||||
typename KeyInfoT = DenseMapInfo<KeyT>,
|
||||
typename Bucket = wpi::detail::DenseMapPair<KeyT, ValueT>,
|
||||
bool IsConst = false>
|
||||
class DenseMapIterator;
|
||||
|
||||
template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT,
|
||||
@@ -389,7 +390,7 @@ protected:
|
||||
setNumTombstones(other.getNumTombstones());
|
||||
|
||||
if (isPodLike<KeyT>::value && isPodLike<ValueT>::value)
|
||||
memcpy(getBuckets(), other.getBuckets(),
|
||||
memcpy(reinterpret_cast<void *>(getBuckets()), other.getBuckets(),
|
||||
getNumBuckets() * sizeof(BucketT));
|
||||
else
|
||||
for (size_t i = 0; i < getNumBuckets(); ++i) {
|
||||
@@ -627,9 +628,43 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Equality comparison for DenseMap.
|
||||
///
|
||||
/// Iterates over elements of LHS confirming that each (key, value) pair in LHS
|
||||
/// is also in RHS, and that no additional pairs are in RHS.
|
||||
/// Equivalent to N calls to RHS.find and N value comparisons. Amortized
|
||||
/// complexity is linear, worst case is O(N^2) (if every hash collides).
|
||||
template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT,
|
||||
typename BucketT>
|
||||
bool operator==(
|
||||
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &LHS,
|
||||
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &RHS) {
|
||||
if (LHS.size() != RHS.size())
|
||||
return false;
|
||||
|
||||
for (auto &KV : LHS) {
|
||||
auto I = RHS.find(KV.first);
|
||||
if (I == RHS.end() || I->second != KV.second)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Inequality comparison for DenseMap.
|
||||
///
|
||||
/// Equivalent to !(LHS == RHS). See operator== for performance notes.
|
||||
template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT,
|
||||
typename BucketT>
|
||||
bool operator!=(
|
||||
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &LHS,
|
||||
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &RHS) {
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
|
||||
template <typename KeyT, typename ValueT,
|
||||
typename KeyInfoT = DenseMapInfo<KeyT>,
|
||||
typename BucketT = detail::DenseMapPair<KeyT, ValueT>>
|
||||
typename BucketT = wpi::detail::DenseMapPair<KeyT, ValueT>>
|
||||
class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
|
||||
KeyT, ValueT, KeyInfoT, BucketT> {
|
||||
friend class DenseMapBase<DenseMap, KeyT, ValueT, KeyInfoT, BucketT>;
|
||||
@@ -664,6 +699,11 @@ public:
|
||||
this->insert(I, E);
|
||||
}
|
||||
|
||||
DenseMap(std::initializer_list<typename BaseT::value_type> Vals) {
|
||||
init(Vals.size());
|
||||
this->insert(Vals.begin(), Vals.end());
|
||||
}
|
||||
|
||||
~DenseMap() {
|
||||
this->destroyAll();
|
||||
operator delete(Buckets);
|
||||
@@ -786,7 +826,7 @@ private:
|
||||
|
||||
template <typename KeyT, typename ValueT, unsigned InlineBuckets = 4,
|
||||
typename KeyInfoT = DenseMapInfo<KeyT>,
|
||||
typename BucketT = detail::DenseMapPair<KeyT, ValueT>>
|
||||
typename BucketT = wpi::detail::DenseMapPair<KeyT, ValueT>>
|
||||
class SmallDenseMap
|
||||
: public DenseMapBase<
|
||||
SmallDenseMap<KeyT, ValueT, InlineBuckets, KeyInfoT, BucketT>, KeyT,
|
||||
|
||||
@@ -262,6 +262,13 @@ template <typename T> struct DenseMapInfo<ArrayRef<T>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct DenseMapInfo<hash_code> {
|
||||
static inline hash_code getEmptyKey() { return hash_code(-1); }
|
||||
static inline hash_code getTombstoneKey() { return hash_code(-2); }
|
||||
static unsigned getHashValue(hash_code val) { return val; }
|
||||
static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; }
|
||||
};
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // LLVM_ADT_DENSEMAPINFO_H
|
||||
|
||||
425
wpiutil/src/main/native/include/wpi/Endian.h
Normal file
425
wpiutil/src/main/native/include/wpi/Endian.h
Normal file
@@ -0,0 +1,425 @@
|
||||
//===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares generic functions to read and write endian specific data.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_ENDIAN_H
|
||||
#define WPIUTIL_WPI_ENDIAN_H
|
||||
|
||||
#include "wpi/AlignOf.h"
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/SwapByteOrder.h"
|
||||
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
#include <endian.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
namespace wpi {
|
||||
namespace support {
|
||||
|
||||
enum endianness {big, little, native};
|
||||
|
||||
// These are named values for common alignments.
|
||||
enum {aligned = 0, unaligned = 1};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// ::value is either alignment, or alignof(T) if alignment is 0.
|
||||
template<class T, int alignment>
|
||||
struct PickAlignment {
|
||||
enum { value = alignment == 0 ? alignof(T) : alignment };
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
namespace endian {
|
||||
|
||||
constexpr endianness system_endianness() {
|
||||
#ifdef _WIN32
|
||||
return little;
|
||||
#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
|
||||
return big;
|
||||
#else
|
||||
return little;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
inline value_type byte_swap(value_type value, endianness endian) {
|
||||
if ((endian != native) && (endian != system_endianness()))
|
||||
sys::swapByteOrder(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Swap the bytes of value to match the given endianness.
|
||||
template<typename value_type, endianness endian>
|
||||
inline value_type byte_swap(value_type value) {
|
||||
return byte_swap(value, endian);
|
||||
}
|
||||
|
||||
/// Read a value of a particular endianness from memory.
|
||||
template <typename value_type, std::size_t alignment>
|
||||
inline value_type read(const void *memory, endianness endian) {
|
||||
value_type ret;
|
||||
|
||||
memcpy(&ret,
|
||||
LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
sizeof(value_type));
|
||||
return byte_swap<value_type>(ret, endian);
|
||||
}
|
||||
|
||||
template<typename value_type,
|
||||
endianness endian,
|
||||
std::size_t alignment>
|
||||
inline value_type read(const void *memory) {
|
||||
return read<value_type, alignment>(memory, endian);
|
||||
}
|
||||
|
||||
/// Read a value of a particular endianness from a buffer, and increment the
|
||||
/// buffer past that value.
|
||||
template <typename value_type, std::size_t alignment, typename CharT>
|
||||
inline value_type readNext(const CharT *&memory, endianness endian) {
|
||||
value_type ret = read<value_type, alignment>(memory, endian);
|
||||
memory += sizeof(value_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename value_type, endianness endian, std::size_t alignment,
|
||||
typename CharT>
|
||||
inline value_type readNext(const CharT *&memory) {
|
||||
return readNext<value_type, alignment, CharT>(memory, endian);
|
||||
}
|
||||
|
||||
/// Write a value to memory with a particular endianness.
|
||||
template <typename value_type, std::size_t alignment>
|
||||
inline void write(void *memory, value_type value, endianness endian) {
|
||||
value = byte_swap<value_type>(value, endian);
|
||||
memcpy(LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
&value, sizeof(value_type));
|
||||
}
|
||||
|
||||
template<typename value_type,
|
||||
endianness endian,
|
||||
std::size_t alignment>
|
||||
inline void write(void *memory, value_type value) {
|
||||
write<value_type, alignment>(memory, value, endian);
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
using make_unsigned_t = typename std::make_unsigned<value_type>::type;
|
||||
|
||||
/// Read a value of a particular endianness from memory, for a location
|
||||
/// that starts at the given bit offset within the first byte.
|
||||
template <typename value_type, endianness endian, std::size_t alignment>
|
||||
inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
|
||||
assert(startBit < 8);
|
||||
if (startBit == 0)
|
||||
return read<value_type, endian, alignment>(memory);
|
||||
else {
|
||||
// Read two values and compose the result from them.
|
||||
value_type val[2];
|
||||
memcpy(&val[0],
|
||||
LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
sizeof(value_type) * 2);
|
||||
val[0] = byte_swap<value_type, endian>(val[0]);
|
||||
val[1] = byte_swap<value_type, endian>(val[1]);
|
||||
|
||||
// Shift bits from the lower value into place.
|
||||
make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
|
||||
// Mask off upper bits after right shift in case of signed type.
|
||||
make_unsigned_t<value_type> numBitsFirstVal =
|
||||
(sizeof(value_type) * 8) - startBit;
|
||||
lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1;
|
||||
|
||||
// Get the bits from the upper value.
|
||||
make_unsigned_t<value_type> upperVal =
|
||||
val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1);
|
||||
// Shift them in to place.
|
||||
upperVal <<= numBitsFirstVal;
|
||||
|
||||
return lowerVal | upperVal;
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a value to memory with a particular endianness, for a location
|
||||
/// that starts at the given bit offset within the first byte.
|
||||
template <typename value_type, endianness endian, std::size_t alignment>
|
||||
inline void writeAtBitAlignment(void *memory, value_type value,
|
||||
uint64_t startBit) {
|
||||
assert(startBit < 8);
|
||||
if (startBit == 0)
|
||||
write<value_type, endian, alignment>(memory, value);
|
||||
else {
|
||||
// Read two values and shift the result into them.
|
||||
value_type val[2];
|
||||
memcpy(&val[0],
|
||||
LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
sizeof(value_type) * 2);
|
||||
val[0] = byte_swap<value_type, endian>(val[0]);
|
||||
val[1] = byte_swap<value_type, endian>(val[1]);
|
||||
|
||||
// Mask off any existing bits in the upper part of the lower value that
|
||||
// we want to replace.
|
||||
val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
|
||||
make_unsigned_t<value_type> numBitsFirstVal =
|
||||
(sizeof(value_type) * 8) - startBit;
|
||||
make_unsigned_t<value_type> lowerVal = value;
|
||||
if (startBit > 0) {
|
||||
// Mask off the upper bits in the new value that are not going to go into
|
||||
// the lower value. This avoids a left shift of a negative value, which
|
||||
// is undefined behavior.
|
||||
lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1);
|
||||
// Now shift the new bits into place
|
||||
lowerVal <<= startBit;
|
||||
}
|
||||
val[0] |= lowerVal;
|
||||
|
||||
// Mask off any existing bits in the lower part of the upper value that
|
||||
// we want to replace.
|
||||
val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1);
|
||||
// Next shift the bits that go into the upper value into position.
|
||||
make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal;
|
||||
// Mask off upper bits after right shift in case of signed type.
|
||||
upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
|
||||
val[1] |= upperVal;
|
||||
|
||||
// Finally, rewrite values.
|
||||
val[0] = byte_swap<value_type, endian>(val[0]);
|
||||
val[1] = byte_swap<value_type, endian>(val[1]);
|
||||
memcpy(LLVM_ASSUME_ALIGNED(
|
||||
memory, (detail::PickAlignment<value_type, alignment>::value)),
|
||||
&val[0], sizeof(value_type) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace endian
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename value_type,
|
||||
endianness endian,
|
||||
std::size_t alignment>
|
||||
struct packed_endian_specific_integral {
|
||||
packed_endian_specific_integral() = default;
|
||||
|
||||
explicit packed_endian_specific_integral(value_type val) { *this = val; }
|
||||
|
||||
operator value_type() const {
|
||||
return endian::read<value_type, endian, alignment>(
|
||||
(const void*)Value.buffer);
|
||||
}
|
||||
|
||||
void operator=(value_type newValue) {
|
||||
endian::write<value_type, endian, alignment>(
|
||||
(void*)Value.buffer, newValue);
|
||||
}
|
||||
|
||||
packed_endian_specific_integral &operator+=(value_type newValue) {
|
||||
*this = *this + newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
packed_endian_specific_integral &operator-=(value_type newValue) {
|
||||
*this = *this - newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
packed_endian_specific_integral &operator|=(value_type newValue) {
|
||||
*this = *this | newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
packed_endian_specific_integral &operator&=(value_type newValue) {
|
||||
*this = *this & newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
AlignedCharArray<PickAlignment<value_type, alignment>::value,
|
||||
sizeof(value_type)> Value;
|
||||
|
||||
public:
|
||||
struct ref {
|
||||
explicit ref(void *Ptr) : Ptr(Ptr) {}
|
||||
|
||||
operator value_type() const {
|
||||
return endian::read<value_type, endian, alignment>(Ptr);
|
||||
}
|
||||
|
||||
void operator=(value_type NewValue) {
|
||||
endian::write<value_type, endian, alignment>(Ptr, NewValue);
|
||||
}
|
||||
|
||||
private:
|
||||
void *Ptr;
|
||||
};
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
using ulittle16_t =
|
||||
detail::packed_endian_specific_integral<uint16_t, little, unaligned>;
|
||||
using ulittle32_t =
|
||||
detail::packed_endian_specific_integral<uint32_t, little, unaligned>;
|
||||
using ulittle64_t =
|
||||
detail::packed_endian_specific_integral<uint64_t, little, unaligned>;
|
||||
|
||||
using little16_t =
|
||||
detail::packed_endian_specific_integral<int16_t, little, unaligned>;
|
||||
using little32_t =
|
||||
detail::packed_endian_specific_integral<int32_t, little, unaligned>;
|
||||
using little64_t =
|
||||
detail::packed_endian_specific_integral<int64_t, little, unaligned>;
|
||||
|
||||
using aligned_ulittle16_t =
|
||||
detail::packed_endian_specific_integral<uint16_t, little, aligned>;
|
||||
using aligned_ulittle32_t =
|
||||
detail::packed_endian_specific_integral<uint32_t, little, aligned>;
|
||||
using aligned_ulittle64_t =
|
||||
detail::packed_endian_specific_integral<uint64_t, little, aligned>;
|
||||
|
||||
using aligned_little16_t =
|
||||
detail::packed_endian_specific_integral<int16_t, little, aligned>;
|
||||
using aligned_little32_t =
|
||||
detail::packed_endian_specific_integral<int32_t, little, aligned>;
|
||||
using aligned_little64_t =
|
||||
detail::packed_endian_specific_integral<int64_t, little, aligned>;
|
||||
|
||||
using ubig16_t =
|
||||
detail::packed_endian_specific_integral<uint16_t, big, unaligned>;
|
||||
using ubig32_t =
|
||||
detail::packed_endian_specific_integral<uint32_t, big, unaligned>;
|
||||
using ubig64_t =
|
||||
detail::packed_endian_specific_integral<uint64_t, big, unaligned>;
|
||||
|
||||
using big16_t =
|
||||
detail::packed_endian_specific_integral<int16_t, big, unaligned>;
|
||||
using big32_t =
|
||||
detail::packed_endian_specific_integral<int32_t, big, unaligned>;
|
||||
using big64_t =
|
||||
detail::packed_endian_specific_integral<int64_t, big, unaligned>;
|
||||
|
||||
using aligned_ubig16_t =
|
||||
detail::packed_endian_specific_integral<uint16_t, big, aligned>;
|
||||
using aligned_ubig32_t =
|
||||
detail::packed_endian_specific_integral<uint32_t, big, aligned>;
|
||||
using aligned_ubig64_t =
|
||||
detail::packed_endian_specific_integral<uint64_t, big, aligned>;
|
||||
|
||||
using aligned_big16_t =
|
||||
detail::packed_endian_specific_integral<int16_t, big, aligned>;
|
||||
using aligned_big32_t =
|
||||
detail::packed_endian_specific_integral<int32_t, big, aligned>;
|
||||
using aligned_big64_t =
|
||||
detail::packed_endian_specific_integral<int64_t, big, aligned>;
|
||||
|
||||
using unaligned_uint16_t =
|
||||
detail::packed_endian_specific_integral<uint16_t, native, unaligned>;
|
||||
using unaligned_uint32_t =
|
||||
detail::packed_endian_specific_integral<uint32_t, native, unaligned>;
|
||||
using unaligned_uint64_t =
|
||||
detail::packed_endian_specific_integral<uint64_t, native, unaligned>;
|
||||
|
||||
using unaligned_int16_t =
|
||||
detail::packed_endian_specific_integral<int16_t, native, unaligned>;
|
||||
using unaligned_int32_t =
|
||||
detail::packed_endian_specific_integral<int32_t, native, unaligned>;
|
||||
using unaligned_int64_t =
|
||||
detail::packed_endian_specific_integral<int64_t, native, unaligned>;
|
||||
|
||||
namespace endian {
|
||||
|
||||
template <typename T> inline T read(const void *P, endianness E) {
|
||||
return read<T, unaligned>(P, E);
|
||||
}
|
||||
|
||||
template <typename T, endianness E> inline T read(const void *P) {
|
||||
return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P;
|
||||
}
|
||||
|
||||
inline uint16_t read16(const void *P, endianness E) {
|
||||
return read<uint16_t>(P, E);
|
||||
}
|
||||
inline uint32_t read32(const void *P, endianness E) {
|
||||
return read<uint32_t>(P, E);
|
||||
}
|
||||
inline uint64_t read64(const void *P, endianness E) {
|
||||
return read<uint64_t>(P, E);
|
||||
}
|
||||
|
||||
template <endianness E> inline uint16_t read16(const void *P) {
|
||||
return read<uint16_t, E>(P);
|
||||
}
|
||||
template <endianness E> inline uint32_t read32(const void *P) {
|
||||
return read<uint32_t, E>(P);
|
||||
}
|
||||
template <endianness E> inline uint64_t read64(const void *P) {
|
||||
return read<uint64_t, E>(P);
|
||||
}
|
||||
|
||||
inline uint16_t read16le(const void *P) { return read16<little>(P); }
|
||||
inline uint32_t read32le(const void *P) { return read32<little>(P); }
|
||||
inline uint64_t read64le(const void *P) { return read64<little>(P); }
|
||||
inline uint16_t read16be(const void *P) { return read16<big>(P); }
|
||||
inline uint32_t read32be(const void *P) { return read32<big>(P); }
|
||||
inline uint64_t read64be(const void *P) { return read64<big>(P); }
|
||||
|
||||
template <typename T> inline void write(void *P, T V, endianness E) {
|
||||
write<T, unaligned>(P, V, E);
|
||||
}
|
||||
|
||||
template <typename T, endianness E> inline void write(void *P, T V) {
|
||||
*(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V;
|
||||
}
|
||||
|
||||
inline void write16(void *P, uint16_t V, endianness E) {
|
||||
write<uint16_t>(P, V, E);
|
||||
}
|
||||
inline void write32(void *P, uint32_t V, endianness E) {
|
||||
write<uint32_t>(P, V, E);
|
||||
}
|
||||
inline void write64(void *P, uint64_t V, endianness E) {
|
||||
write<uint64_t>(P, V, E);
|
||||
}
|
||||
|
||||
template <endianness E> inline void write16(void *P, uint16_t V) {
|
||||
write<uint16_t, E>(P, V);
|
||||
}
|
||||
template <endianness E> inline void write32(void *P, uint32_t V) {
|
||||
write<uint32_t, E>(P, V);
|
||||
}
|
||||
template <endianness E> inline void write64(void *P, uint64_t V) {
|
||||
write<uint64_t, E>(P, V);
|
||||
}
|
||||
|
||||
inline void write16le(void *P, uint16_t V) { write16<little>(P, V); }
|
||||
inline void write32le(void *P, uint32_t V) { write32<little>(P, V); }
|
||||
inline void write64le(void *P, uint64_t V) { write64<little>(P, V); }
|
||||
inline void write16be(void *P, uint16_t V) { write16<big>(P, V); }
|
||||
inline void write32be(void *P, uint32_t V) { write32<big>(P, V); }
|
||||
inline void write64be(void *P, uint64_t V) { write64<big>(P, V); }
|
||||
|
||||
} // end namespace endian
|
||||
|
||||
} // end namespace support
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_ENDIAN_H
|
||||
87
wpiutil/src/main/native/include/wpi/Errc.h
Normal file
87
wpiutil/src/main/native/include/wpi/Errc.h
Normal file
@@ -0,0 +1,87 @@
|
||||
//===- llvm/Support/Errc.h - Defines the llvm::errc enum --------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// While std::error_code works OK on all platforms we use, there are some
|
||||
// some problems with std::errc that can be avoided by using our own
|
||||
// enumeration:
|
||||
//
|
||||
// * std::errc is a namespace in some implementations. That meas that ADL
|
||||
// doesn't work and it is sometimes necessary to write std::make_error_code
|
||||
// or in templates:
|
||||
// using std::make_error_code;
|
||||
// make_error_code(...);
|
||||
//
|
||||
// with this enum it is safe to always just use make_error_code.
|
||||
//
|
||||
// * Some implementations define fewer names than others. This header has
|
||||
// the intersection of all the ones we support.
|
||||
//
|
||||
// * std::errc is just marked with is_error_condition_enum. This means that
|
||||
// common patters like AnErrorCode == errc::no_such_file_or_directory take
|
||||
// 4 virtual calls instead of two comparisons.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_ERRC_H
|
||||
#define WPIUTIL_WPI_ERRC_H
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace wpi {
|
||||
enum class errc {
|
||||
argument_list_too_long = int(std::errc::argument_list_too_long),
|
||||
argument_out_of_domain = int(std::errc::argument_out_of_domain),
|
||||
bad_address = int(std::errc::bad_address),
|
||||
bad_file_descriptor = int(std::errc::bad_file_descriptor),
|
||||
broken_pipe = int(std::errc::broken_pipe),
|
||||
device_or_resource_busy = int(std::errc::device_or_resource_busy),
|
||||
directory_not_empty = int(std::errc::directory_not_empty),
|
||||
executable_format_error = int(std::errc::executable_format_error),
|
||||
file_exists = int(std::errc::file_exists),
|
||||
file_too_large = int(std::errc::file_too_large),
|
||||
filename_too_long = int(std::errc::filename_too_long),
|
||||
function_not_supported = int(std::errc::function_not_supported),
|
||||
illegal_byte_sequence = int(std::errc::illegal_byte_sequence),
|
||||
inappropriate_io_control_operation =
|
||||
int(std::errc::inappropriate_io_control_operation),
|
||||
interrupted = int(std::errc::interrupted),
|
||||
invalid_argument = int(std::errc::invalid_argument),
|
||||
invalid_seek = int(std::errc::invalid_seek),
|
||||
io_error = int(std::errc::io_error),
|
||||
is_a_directory = int(std::errc::is_a_directory),
|
||||
no_child_process = int(std::errc::no_child_process),
|
||||
no_lock_available = int(std::errc::no_lock_available),
|
||||
no_space_on_device = int(std::errc::no_space_on_device),
|
||||
no_such_device_or_address = int(std::errc::no_such_device_or_address),
|
||||
no_such_device = int(std::errc::no_such_device),
|
||||
no_such_file_or_directory = int(std::errc::no_such_file_or_directory),
|
||||
no_such_process = int(std::errc::no_such_process),
|
||||
not_a_directory = int(std::errc::not_a_directory),
|
||||
not_enough_memory = int(std::errc::not_enough_memory),
|
||||
not_supported = int(std::errc::not_supported),
|
||||
operation_not_permitted = int(std::errc::operation_not_permitted),
|
||||
permission_denied = int(std::errc::permission_denied),
|
||||
read_only_file_system = int(std::errc::read_only_file_system),
|
||||
resource_deadlock_would_occur = int(std::errc::resource_deadlock_would_occur),
|
||||
resource_unavailable_try_again =
|
||||
int(std::errc::resource_unavailable_try_again),
|
||||
result_out_of_range = int(std::errc::result_out_of_range),
|
||||
too_many_files_open_in_system = int(std::errc::too_many_files_open_in_system),
|
||||
too_many_files_open = int(std::errc::too_many_files_open),
|
||||
too_many_links = int(std::errc::too_many_links)
|
||||
};
|
||||
|
||||
inline std::error_code make_error_code(errc E) {
|
||||
return std::error_code(static_cast<int>(E), std::generic_category());
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <> struct is_error_code_enum<wpi::errc> : std::true_type {};
|
||||
}
|
||||
#endif
|
||||
38
wpiutil/src/main/native/include/wpi/Errno.h
Normal file
38
wpiutil/src/main/native/include/wpi/Errno.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//===- llvm/Support/Errno.h - Portable+convenient errno handling -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares some portable and convenient functions to deal with errno.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_ERRNO_H
|
||||
#define WPIUTIL_WPI_ERRNO_H
|
||||
|
||||
#include <cerrno>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace wpi {
|
||||
namespace sys {
|
||||
|
||||
template <typename FailT, typename Fun, typename... Args>
|
||||
inline auto RetryAfterSignal(const FailT &Fail, const Fun &F,
|
||||
const Args &... As) -> decltype(F(As...)) {
|
||||
decltype(F(As...)) Res;
|
||||
do {
|
||||
errno = 0;
|
||||
Res = F(As...);
|
||||
} while (Res == Fail && errno == EINTR);
|
||||
return Res;
|
||||
}
|
||||
|
||||
} // namespace sys
|
||||
} // namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_ERRNO_H
|
||||
1197
wpiutil/src/main/native/include/wpi/Error.h
Normal file
1197
wpiutil/src/main/native/include/wpi/Error.h
Normal file
File diff suppressed because it is too large
Load Diff
144
wpiutil/src/main/native/include/wpi/ErrorHandling.h
Normal file
144
wpiutil/src/main/native/include/wpi/ErrorHandling.h
Normal file
@@ -0,0 +1,144 @@
|
||||
//===- llvm/Support/ErrorHandling.h - Fatal error handling ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines an API used to indicate fatal error conditions. Non-fatal
|
||||
// errors (most of them) should be handled through LLVMContext.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_ERRORHANDLING_H
|
||||
#define WPIUTIL_WPI_ERRORHANDLING_H
|
||||
|
||||
#include "wpi/Compiler.h"
|
||||
#include <string>
|
||||
|
||||
namespace wpi {
|
||||
class StringRef;
|
||||
class Twine;
|
||||
|
||||
/// An error handler callback.
|
||||
typedef void (*fatal_error_handler_t)(void *user_data,
|
||||
const std::string& reason,
|
||||
bool gen_crash_diag);
|
||||
|
||||
/// install_fatal_error_handler - Installs a new error handler to be used
|
||||
/// whenever a serious (non-recoverable) error is encountered by LLVM.
|
||||
///
|
||||
/// If no error handler is installed the default is to print the error message
|
||||
/// to stderr, and call exit(1). If an error handler is installed then it is
|
||||
/// the handler's responsibility to log the message, it will no longer be
|
||||
/// printed to stderr. If the error handler returns, then exit(1) will be
|
||||
/// called.
|
||||
///
|
||||
/// It is dangerous to naively use an error handler which throws an exception.
|
||||
/// Even though some applications desire to gracefully recover from arbitrary
|
||||
/// faults, blindly throwing exceptions through unfamiliar code isn't a way to
|
||||
/// achieve this.
|
||||
///
|
||||
/// \param user_data - An argument which will be passed to the install error
|
||||
/// handler.
|
||||
void install_fatal_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr);
|
||||
|
||||
/// Restores default error handling behaviour.
|
||||
void remove_fatal_error_handler();
|
||||
|
||||
/// ScopedFatalErrorHandler - This is a simple helper class which just
|
||||
/// calls install_fatal_error_handler in its constructor and
|
||||
/// remove_fatal_error_handler in its destructor.
|
||||
struct ScopedFatalErrorHandler {
|
||||
explicit ScopedFatalErrorHandler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr) {
|
||||
install_fatal_error_handler(handler, user_data);
|
||||
}
|
||||
|
||||
~ScopedFatalErrorHandler() { remove_fatal_error_handler(); }
|
||||
};
|
||||
|
||||
/// Reports a serious error, calling any installed error handler. These
|
||||
/// functions are intended to be used for error conditions which are outside
|
||||
/// the control of the compiler (I/O errors, invalid user input, etc.)
|
||||
///
|
||||
/// If no error handler is installed the default is to print the message to
|
||||
/// standard error, followed by a newline.
|
||||
/// After the error handler is called this function will call exit(1), it
|
||||
/// does not return.
|
||||
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const char *reason,
|
||||
bool gen_crash_diag = true);
|
||||
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const std::string &reason,
|
||||
bool gen_crash_diag = true);
|
||||
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(StringRef reason,
|
||||
bool gen_crash_diag = true);
|
||||
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const Twine &reason,
|
||||
bool gen_crash_diag = true);
|
||||
|
||||
/// Installs a new bad alloc error handler that should be used whenever a
|
||||
/// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM.
|
||||
///
|
||||
/// The user can install a bad alloc handler, in order to define the behavior
|
||||
/// in case of failing allocations, e.g. throwing an exception. Note that this
|
||||
/// handler must not trigger any additional allocations itself.
|
||||
///
|
||||
/// If no error handler is installed the default is to print the error message
|
||||
/// to stderr, and call exit(1). If an error handler is installed then it is
|
||||
/// the handler's responsibility to log the message, it will no longer be
|
||||
/// printed to stderr. If the error handler returns, then exit(1) will be
|
||||
/// called.
|
||||
///
|
||||
///
|
||||
/// \param user_data - An argument which will be passed to the installed error
|
||||
/// handler.
|
||||
void install_bad_alloc_error_handler(fatal_error_handler_t handler,
|
||||
void *user_data = nullptr);
|
||||
|
||||
/// Restores default bad alloc error handling behavior.
|
||||
void remove_bad_alloc_error_handler();
|
||||
|
||||
void install_out_of_memory_new_handler();
|
||||
|
||||
/// Reports a bad alloc error, calling any user defined bad alloc
|
||||
/// error handler. In contrast to the generic 'report_fatal_error'
|
||||
/// functions, this function is expected to return, e.g. the user
|
||||
/// defined error handler throws an exception.
|
||||
///
|
||||
/// Note: When throwing an exception in the bad alloc handler, make sure that
|
||||
/// the following unwind succeeds, e.g. do not trigger additional allocations
|
||||
/// in the unwind chain.
|
||||
///
|
||||
/// If no error handler is installed (default), then a bad_alloc exception
|
||||
/// is thrown, if LLVM is compiled with exception support, otherwise an
|
||||
/// assertion is called.
|
||||
void report_bad_alloc_error(const char *Reason, bool GenCrashDiag = true);
|
||||
|
||||
/// This function calls abort(), and prints the optional message to stderr.
|
||||
/// Use the wpi_unreachable macro (that adds location info), instead of
|
||||
/// calling this function directly.
|
||||
LLVM_ATTRIBUTE_NORETURN void
|
||||
wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr,
|
||||
unsigned line = 0);
|
||||
}
|
||||
|
||||
/// Marks that the current location is not supposed to be reachable.
|
||||
/// In !NDEBUG builds, prints the message and location info to stderr.
|
||||
/// In NDEBUG builds, becomes an optimizer hint that the current location
|
||||
/// is not supposed to be reachable. On compilers that don't support
|
||||
/// such hints, prints a reduced message instead.
|
||||
///
|
||||
/// Use this instead of assert(0). It conveys intent more clearly and
|
||||
/// allows compilers to omit some unnecessary code.
|
||||
#ifndef NDEBUG
|
||||
#define wpi_unreachable(msg) \
|
||||
::wpi::wpi_unreachable_internal(msg, __FILE__, __LINE__)
|
||||
#elif defined(LLVM_BUILTIN_UNREACHABLE)
|
||||
#define wpi_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
|
||||
#else
|
||||
#define wpi_unreachable(msg) ::wpi::wpi_unreachable_internal()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -24,18 +24,6 @@
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// Stores a reference that can be changed.
|
||||
template <typename T>
|
||||
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<T> is a pointer-like class that represents the result of an
|
||||
@@ -71,7 +59,7 @@ class ErrorOr {
|
||||
|
||||
static const bool isRef = std::is_reference<T>::value;
|
||||
|
||||
using wrap = ReferenceStorage<typename std::remove_reference<T>::type>;
|
||||
using wrap = std::reference_wrapper<typename std::remove_reference<T>::type>;
|
||||
|
||||
public:
|
||||
using storage_type = typename std::conditional<isRef, wrap, T>::type;
|
||||
|
||||
@@ -27,9 +27,12 @@
|
||||
#ifndef WPIUTIL_WPI_FILESYSTEM_H
|
||||
#define WPIUTIL_WPI_FILESYSTEM_H
|
||||
|
||||
#include "wpi/Chrono.h"
|
||||
#include "wpi/SmallString.h"
|
||||
#include "wpi/StringRef.h"
|
||||
#include "wpi/Twine.h"
|
||||
#include "wpi/Error.h"
|
||||
#include "wpi/ErrorHandling.h"
|
||||
#include "wpi/ErrorOr.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
@@ -47,6 +50,15 @@ namespace wpi {
|
||||
namespace sys {
|
||||
namespace fs {
|
||||
|
||||
#if defined(_WIN32)
|
||||
// A Win32 HANDLE is a typedef of void*
|
||||
using file_t = void *;
|
||||
#else
|
||||
using file_t = int;
|
||||
#endif
|
||||
|
||||
extern const file_t kInvalidFile;
|
||||
|
||||
/// An enumeration for the file system's view of the type.
|
||||
enum class file_type {
|
||||
status_error,
|
||||
@@ -61,6 +73,13 @@ enum class file_type {
|
||||
type_unknown
|
||||
};
|
||||
|
||||
/// space_info - Self explanatory.
|
||||
struct space_info {
|
||||
uint64_t capacity;
|
||||
uint64_t free;
|
||||
uint64_t available;
|
||||
};
|
||||
|
||||
enum perms {
|
||||
no_perms = 0,
|
||||
owner_read = 0400,
|
||||
@@ -137,6 +156,8 @@ protected:
|
||||
#ifndef _WIN32
|
||||
time_t fs_st_atime = 0;
|
||||
time_t fs_st_mtime = 0;
|
||||
uint32_t fs_st_atime_nsec = 0;
|
||||
uint32_t fs_st_mtime_nsec = 0;
|
||||
uid_t fs_st_uid = 0;
|
||||
gid_t fs_st_gid = 0;
|
||||
off_t fs_st_size = 0;
|
||||
@@ -157,9 +178,12 @@ public:
|
||||
explicit basic_file_status(file_type Type) : Type(Type) {}
|
||||
|
||||
#ifndef _WIN32
|
||||
basic_file_status(file_type Type, perms Perms, time_t ATime, time_t MTime,
|
||||
basic_file_status(file_type Type, perms Perms, time_t ATime,
|
||||
uint32_t ATimeNSec, time_t MTime, uint32_t MTimeNSec,
|
||||
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_atime(ATime), fs_st_mtime(MTime),
|
||||
fs_st_atime_nsec(ATimeNSec), fs_st_mtime_nsec(MTimeNSec),
|
||||
fs_st_uid(UID), fs_st_gid(GID),
|
||||
fs_st_size(Size), Type(Type), Perms(Perms) {}
|
||||
#else
|
||||
basic_file_status(file_type Type, perms Perms, uint32_t LastAccessTimeHigh,
|
||||
@@ -177,6 +201,21 @@ public:
|
||||
file_type type() const { return Type; }
|
||||
perms permissions() const { return Perms; }
|
||||
|
||||
/// The file access time as reported from the underlying file system.
|
||||
///
|
||||
/// Also see comments on \c getLastModificationTime() related to the precision
|
||||
/// of the returned value.
|
||||
TimePoint<> getLastAccessedTime() const;
|
||||
|
||||
/// The file modification time as reported from the underlying file system.
|
||||
///
|
||||
/// The returned value allows for nanosecond precision but the actual
|
||||
/// resolution is an implementation detail of the underlying file system.
|
||||
/// There is no guarantee for what kind of resolution you can expect, the
|
||||
/// resolution can differ across platforms and even across mountpoints on the
|
||||
/// same machine.
|
||||
TimePoint<> getLastModificationTime() const;
|
||||
|
||||
#ifndef _WIN32
|
||||
uint32_t getUser() const { return fs_st_uid; }
|
||||
uint32_t getGroup() const { return fs_st_gid; }
|
||||
@@ -222,8 +261,11 @@ public:
|
||||
|
||||
#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),
|
||||
time_t ATime, uint32_t ATimeNSec,
|
||||
time_t MTime, uint32_t MTimeNSec,
|
||||
uid_t UID, gid_t GID, off_t Size)
|
||||
: basic_file_status(Type, Perms, ATime, ATimeNSec, MTime, MTimeNSec,
|
||||
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,
|
||||
@@ -256,10 +298,7 @@ public:
|
||||
/// relative/../path => <current-directory>/relative/../path
|
||||
///
|
||||
/// @param path A path that is modified to be an absolute path.
|
||||
/// @returns errc::success if \a path has been made absolute, otherwise a
|
||||
/// platform-specific error_code.
|
||||
std::error_code make_absolute(const Twine ¤t_directory,
|
||||
SmallVectorImpl<char> &path);
|
||||
void make_absolute(const Twine ¤t_directory, SmallVectorImpl<char> &path);
|
||||
|
||||
/// Make \a path an absolute path.
|
||||
///
|
||||
@@ -347,6 +386,14 @@ inline bool equivalent(const Twine &A, const Twine &B) {
|
||||
return !equivalent(A, B, result) && result;
|
||||
}
|
||||
|
||||
/// Does status represent a directory?
|
||||
///
|
||||
/// @param Path The path to get the type of.
|
||||
/// @param Follow For symbolic links, indicates whether to return the file type
|
||||
/// of the link itself, or of the target.
|
||||
/// @returns A value from the file_type enumeration indicating the type of file.
|
||||
file_type get_file_type(const Twine &Path, bool Follow = true);
|
||||
|
||||
/// Does status represent a directory?
|
||||
///
|
||||
/// @param status A basic_file_status previously returned from status.
|
||||
@@ -462,32 +509,55 @@ bool status_known(const basic_file_status &s);
|
||||
/// platform-specific error_code.
|
||||
std::error_code status_known(const Twine &path, bool &result);
|
||||
|
||||
enum CreationDisposition : unsigned {
|
||||
/// CD_CreateAlways - When opening a file:
|
||||
/// * If it already exists, truncate it.
|
||||
/// * If it does not already exist, create a new file.
|
||||
CD_CreateAlways = 0,
|
||||
|
||||
/// CD_CreateNew - When opening a file:
|
||||
/// * If it already exists, fail.
|
||||
/// * If it does not already exist, create a new file.
|
||||
CD_CreateNew = 1,
|
||||
|
||||
/// CD_OpenExisting - When opening a file:
|
||||
/// * If it already exists, open the file with the offset set to 0.
|
||||
/// * If it does not already exist, fail.
|
||||
CD_OpenExisting = 2,
|
||||
|
||||
/// CD_OpenAlways - When opening a file:
|
||||
/// * If it already exists, open the file with the offset set to 0.
|
||||
/// * If it does not already exist, create a new file.
|
||||
CD_OpenAlways = 3,
|
||||
};
|
||||
|
||||
enum FileAccess : unsigned {
|
||||
FA_Read = 1,
|
||||
FA_Write = 2,
|
||||
};
|
||||
|
||||
enum OpenFlags : unsigned {
|
||||
F_None = 0,
|
||||
|
||||
/// F_Excl - When opening a file, this flag makes raw_fd_ostream
|
||||
/// report an error if the file already exists.
|
||||
F_Excl = 1,
|
||||
|
||||
/// F_Append - When opening a file, if it already exists append to the
|
||||
/// existing file instead of returning an error. This may not be specified
|
||||
/// 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,
|
||||
OF_None = 0,
|
||||
F_None = 0, // For compatibility
|
||||
|
||||
/// The file should be opened in text mode on platforms that make this
|
||||
/// distinction.
|
||||
F_Text = 8,
|
||||
OF_Text = 1,
|
||||
F_Text = 1, // For compatibility
|
||||
|
||||
/// Open the file for read and write.
|
||||
F_RW = 16,
|
||||
/// The file should be opened in append mode.
|
||||
OF_Append = 2,
|
||||
F_Append = 2, // For compatibility
|
||||
|
||||
/// Delete the file on close. Only makes a difference on windows.
|
||||
F_Delete = 32
|
||||
OF_Delete = 4,
|
||||
|
||||
/// When a child process is launched, this file should remain open in the
|
||||
/// child process.
|
||||
OF_ChildInherit = 8,
|
||||
|
||||
/// Force files Atime to be updated on access. Only makes a difference on windows.
|
||||
OF_UpdateAtime = 16,
|
||||
};
|
||||
|
||||
inline OpenFlags operator|(OpenFlags A, OpenFlags B) {
|
||||
@@ -499,6 +569,95 @@ inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) {
|
||||
return A;
|
||||
}
|
||||
|
||||
inline FileAccess operator|(FileAccess A, FileAccess B) {
|
||||
return FileAccess(unsigned(A) | unsigned(B));
|
||||
}
|
||||
|
||||
inline FileAccess &operator|=(FileAccess &A, FileAccess B) {
|
||||
A = A | B;
|
||||
return A;
|
||||
}
|
||||
|
||||
/// @brief Opens a file with the specified creation disposition, access mode,
|
||||
/// and flags and returns a 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 Disp Value specifying the existing-file behavior.
|
||||
/// @param Access Value specifying whether to open the file in read, write, or
|
||||
/// read-write mode.
|
||||
/// @param Flags Additional flags.
|
||||
/// @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 openFile(const Twine &Name, int &ResultFD,
|
||||
CreationDisposition Disp, FileAccess Access,
|
||||
OpenFlags Flags, unsigned Mode = 0666);
|
||||
|
||||
/// @brief Opens a file with the specified creation disposition, access mode,
|
||||
/// and flags and returns a platform-specific file object.
|
||||
///
|
||||
/// The caller is responsible for closing the file object once they are
|
||||
/// finished with it.
|
||||
///
|
||||
/// @param Name The path of the file to open, relative or absolute.
|
||||
/// @param Disp Value specifying the existing-file behavior.
|
||||
/// @param Access Value specifying whether to open the file in read, write, or
|
||||
/// read-write mode.
|
||||
/// @param Flags Additional flags.
|
||||
/// @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.
|
||||
Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
|
||||
FileAccess Access, OpenFlags Flags,
|
||||
unsigned Mode = 0666);
|
||||
|
||||
/// @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.
|
||||
inline std::error_code
|
||||
openFileForWrite(const Twine &Name, int &ResultFD,
|
||||
CreationDisposition Disp = CD_CreateAlways,
|
||||
OpenFlags Flags = OF_None, unsigned Mode = 0666) {
|
||||
return openFile(Name, ResultFD, Disp, FA_Write, Flags, Mode);
|
||||
}
|
||||
|
||||
/// @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 freeing the file once they are
|
||||
/// finished with it.
|
||||
///
|
||||
/// @param Name The path of the file to open, relative or absolute.
|
||||
/// @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 a platform-specific file descriptor if \a Name has been opened,
|
||||
/// otherwise an error object.
|
||||
inline Expected<file_t> openNativeFileForWrite(const Twine &Name,
|
||||
CreationDisposition Disp,
|
||||
OpenFlags Flags,
|
||||
unsigned Mode = 0666) {
|
||||
return openNativeFile(Name, Disp, FA_Write, Flags, Mode);
|
||||
}
|
||||
|
||||
/// @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.
|
||||
@@ -514,8 +673,32 @@ inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) {
|
||||
/// @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);
|
||||
inline std::error_code openFileForReadWrite(const Twine &Name, int &ResultFD,
|
||||
CreationDisposition Disp,
|
||||
OpenFlags Flags,
|
||||
unsigned Mode = 0666) {
|
||||
return openFile(Name, ResultFD, Disp, FA_Write | FA_Read, Flags, Mode);
|
||||
}
|
||||
|
||||
/// @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 freeing the file once they are
|
||||
/// finished with it.
|
||||
///
|
||||
/// @param Name The path of the file to open, relative or absolute.
|
||||
/// @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 a platform-specific file descriptor if \a Name has been opened,
|
||||
/// otherwise an error object.
|
||||
inline Expected<file_t> openNativeFileForReadWrite(const Twine &Name,
|
||||
CreationDisposition Disp,
|
||||
OpenFlags Flags,
|
||||
unsigned Mode = 0666) {
|
||||
return openNativeFile(Name, Disp, FA_Write | FA_Read, Flags, Mode);
|
||||
}
|
||||
|
||||
/// @brief Opens the file with the given name in a read-only mode, returning
|
||||
/// its open file descriptor.
|
||||
@@ -532,10 +715,79 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
|
||||
/// @returns errc::success if \a Name has been opened, otherwise a
|
||||
/// platform-specific error_code.
|
||||
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
|
||||
OpenFlags Flags = OF_None,
|
||||
SmallVectorImpl<char> *RealPath = nullptr);
|
||||
|
||||
/// @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 freeing the file once they are
|
||||
/// finished with it.
|
||||
///
|
||||
/// @param Name The path of the file to open, relative or absolute.
|
||||
/// @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 a platform-specific file descriptor if \a Name has been opened,
|
||||
/// otherwise an error object.
|
||||
Expected<file_t>
|
||||
openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None,
|
||||
SmallVectorImpl<char> *RealPath = nullptr);
|
||||
|
||||
/// @brief Close the file object. This should be used instead of ::close for
|
||||
/// portability.
|
||||
///
|
||||
/// @param F On input, this is the file to close. On output, the file is
|
||||
/// set to kInvalidFile.
|
||||
void closeFile(file_t &F);
|
||||
|
||||
std::error_code getUniqueID(const Twine Path, UniqueID &Result);
|
||||
|
||||
/// This class represents a memory mapped file. It is based on
|
||||
/// boost::iostreams::mapped_file.
|
||||
class mapped_file_region {
|
||||
public:
|
||||
enum mapmode {
|
||||
readonly, ///< May only access map via const_data as read only.
|
||||
readwrite, ///< May access map via data and modify it. Written to path.
|
||||
priv ///< May modify via data, but changes are lost on destruction.
|
||||
};
|
||||
|
||||
private:
|
||||
/// Platform-specific mapping state.
|
||||
size_t Size;
|
||||
void *Mapping;
|
||||
#ifdef _WIN32
|
||||
void *FileHandle;
|
||||
#endif
|
||||
mapmode Mode;
|
||||
|
||||
std::error_code init(int FD, uint64_t Offset, mapmode Mode);
|
||||
|
||||
public:
|
||||
mapped_file_region() = delete;
|
||||
mapped_file_region(mapped_file_region&) = delete;
|
||||
mapped_file_region &operator =(mapped_file_region&) = delete;
|
||||
|
||||
/// \param fd An open file descriptor to map. mapped_file_region takes
|
||||
/// ownership if closefd is true. It must have been opended in the correct
|
||||
/// mode.
|
||||
mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset,
|
||||
std::error_code &ec);
|
||||
|
||||
~mapped_file_region();
|
||||
|
||||
size_t size() const;
|
||||
char *data() const;
|
||||
|
||||
/// Get a const view of the data. Modifying this memory has undefined
|
||||
/// behavior.
|
||||
const char *const_data() const;
|
||||
|
||||
/// \returns The minimum alignment offset must be.
|
||||
static int alignment();
|
||||
};
|
||||
|
||||
/// @}
|
||||
/// @name Iterators
|
||||
/// @{
|
||||
@@ -545,33 +797,37 @@ std::error_code getUniqueID(const Twine Path, UniqueID &Result);
|
||||
/// called.
|
||||
class directory_entry {
|
||||
std::string Path;
|
||||
bool FollowSymlinks;
|
||||
basic_file_status Status;
|
||||
file_type Type; // Most platforms can provide this.
|
||||
bool FollowSymlinks; // Affects the behavior of status().
|
||||
basic_file_status Status; // If available.
|
||||
|
||||
public:
|
||||
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) {}
|
||||
explicit directory_entry(const Twine &Path, bool FollowSymlinks = true,
|
||||
file_type Type = file_type::type_unknown,
|
||||
basic_file_status Status = basic_file_status())
|
||||
: Path(Path.str()), Type(Type), FollowSymlinks(FollowSymlinks),
|
||||
Status(Status) {}
|
||||
|
||||
directory_entry() = default;
|
||||
|
||||
void assign(const Twine &path, basic_file_status st = basic_file_status()) {
|
||||
Path = path.str();
|
||||
Status = st;
|
||||
}
|
||||
|
||||
void replace_filename(const Twine &filename,
|
||||
basic_file_status st = basic_file_status());
|
||||
void replace_filename(const Twine &Filename, file_type Type,
|
||||
basic_file_status Status = basic_file_status());
|
||||
|
||||
const std::string &path() const { return Path; }
|
||||
ErrorOr<basic_file_status> status() const;
|
||||
file_type type() const {
|
||||
if (Type != file_type::type_unknown)
|
||||
return Type;
|
||||
auto S = status();
|
||||
return S ? S->type() : file_type::type_unknown;
|
||||
}
|
||||
|
||||
bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; }
|
||||
bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); }
|
||||
bool operator< (const directory_entry& rhs) const;
|
||||
bool operator<=(const directory_entry& rhs) const;
|
||||
bool operator> (const directory_entry& rhs) const;
|
||||
bool operator>=(const directory_entry& rhs) const;
|
||||
bool operator==(const directory_entry& RHS) const { return Path == RHS.Path; }
|
||||
bool operator!=(const directory_entry& RHS) const { return !(*this == RHS); }
|
||||
bool operator< (const directory_entry& RHS) const;
|
||||
bool operator<=(const directory_entry& RHS) const;
|
||||
bool operator> (const directory_entry& RHS) const;
|
||||
bool operator>=(const directory_entry& RHS) const;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
@@ -609,7 +865,6 @@ public:
|
||||
SmallString<128> 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,
|
||||
@@ -618,7 +873,6 @@ public:
|
||||
State = std::make_shared<detail::DirIterState>();
|
||||
ec = detail::directory_iterator_construct(
|
||||
*State, de.path(), FollowSymlinks);
|
||||
update_error_code_for_current_entry(ec);
|
||||
}
|
||||
|
||||
/// Construct end iterator.
|
||||
@@ -627,7 +881,6 @@ public:
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -647,26 +900,6 @@ public:
|
||||
bool operator!=(const directory_iterator &RHS) const {
|
||||
return !(*this == RHS);
|
||||
}
|
||||
// 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<basic_file_status> status = State->CurrentEntry.status();
|
||||
if (!status)
|
||||
ec = status.getError();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
@@ -704,8 +937,15 @@ public:
|
||||
if (State->HasNoPushRequest)
|
||||
State->HasNoPushRequest = false;
|
||||
else {
|
||||
ErrorOr<basic_file_status> status = State->Stack.top()->status();
|
||||
if (status && is_directory(*status)) {
|
||||
file_type type = State->Stack.top()->type();
|
||||
if (type == file_type::symlink_file && Follow) {
|
||||
// Resolve the symlink: is it a directory to recurse into?
|
||||
ErrorOr<basic_file_status> status = State->Stack.top()->status();
|
||||
if (status)
|
||||
type = status->type();
|
||||
// Otherwise broken symlink, and we'll continue.
|
||||
}
|
||||
if (type == file_type::directory_file) {
|
||||
State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow));
|
||||
if (State->Stack.top() != end_itr) {
|
||||
++State->Level;
|
||||
@@ -772,8 +1012,6 @@ public:
|
||||
bool operator!=(const recursive_directory_iterator &RHS) const {
|
||||
return !(*this == RHS);
|
||||
}
|
||||
// Other members as required by
|
||||
// C++ Std, 24.1.1 Input iterators [input.iterators]
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -45,10 +45,12 @@
|
||||
#ifndef WPIUTIL_WPI_HASHING_H
|
||||
#define WPIUTIL_WPI_HASHING_H
|
||||
|
||||
#include "wpi/Endian.h"
|
||||
#include "wpi/SwapByteOrder.h"
|
||||
#include "wpi/type_traits.h"
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -131,7 +133,7 @@ hash_code hash_value(const std::basic_string<T> &arg);
|
||||
/// undone. This makes it thread-hostile and very hard to use outside of
|
||||
/// immediately on start of a simple program designed for reproducible
|
||||
/// behavior.
|
||||
void set_fixed_execution_hash_seed(size_t fixed_value);
|
||||
void set_fixed_execution_hash_seed(uint64_t fixed_value);
|
||||
|
||||
|
||||
// All of the implementation details of actually computing the various hash
|
||||
@@ -143,16 +145,16 @@ namespace detail {
|
||||
inline uint64_t fetch64(const char *p) {
|
||||
uint64_t result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
//if (sys::IsBigEndianHost)
|
||||
// sys::swapByteOrder(result);
|
||||
if (support::endian::system_endianness() == support::big)
|
||||
sys::swapByteOrder(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint32_t fetch32(const char *p) {
|
||||
uint32_t result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
//if (sys::IsBigEndianHost)
|
||||
// sys::swapByteOrder(result);
|
||||
if (support::endian::system_endianness() == support::big)
|
||||
sys::swapByteOrder(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -314,9 +316,9 @@ struct hash_state {
|
||||
/// This variable can be set using the \see wpi::set_fixed_execution_seed
|
||||
/// function. See that function for details. Do not, under any circumstances,
|
||||
/// set or read this variable.
|
||||
extern size_t fixed_seed_override;
|
||||
extern uint64_t fixed_seed_override;
|
||||
|
||||
inline size_t get_execution_seed() {
|
||||
inline uint64_t get_execution_seed() {
|
||||
// FIXME: This needs to be a per-execution seed. This is just a placeholder
|
||||
// implementation. Switching to a per-execution seed is likely to flush out
|
||||
// instability bugs and so will happen as its own commit.
|
||||
@@ -324,8 +326,7 @@ inline size_t get_execution_seed() {
|
||||
// However, if there is a fixed seed override set the first time this is
|
||||
// called, return that instead of the per-execution seed.
|
||||
const uint64_t seed_prime = 0xff51afd7ed558ccdULL;
|
||||
static size_t seed = fixed_seed_override ? fixed_seed_override
|
||||
: (size_t)seed_prime;
|
||||
static uint64_t seed = fixed_seed_override ? fixed_seed_override : seed_prime;
|
||||
return seed;
|
||||
}
|
||||
|
||||
@@ -400,7 +401,7 @@ bool store_and_advance(char *&buffer_ptr, char *buffer_end, const T& value,
|
||||
/// combining them, this (as an optimization) directly combines the integers.
|
||||
template <typename InputIteratorT>
|
||||
hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) {
|
||||
const size_t seed = get_execution_seed();
|
||||
const uint64_t seed = get_execution_seed();
|
||||
char buffer[64], *buffer_ptr = buffer;
|
||||
char *const buffer_end = std::end(buffer);
|
||||
while (first != last && store_and_advance(buffer_ptr, buffer_end,
|
||||
@@ -444,7 +445,7 @@ hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) {
|
||||
template <typename ValueT>
|
||||
typename std::enable_if<is_hashable_data<ValueT>::value, hash_code>::type
|
||||
hash_combine_range_impl(ValueT *first, ValueT *last) {
|
||||
const size_t seed = get_execution_seed();
|
||||
const uint64_t seed = get_execution_seed();
|
||||
const char *s_begin = reinterpret_cast<const char *>(first);
|
||||
const char *s_end = reinterpret_cast<const char *>(last);
|
||||
const size_t length = std::distance(s_begin, s_end);
|
||||
@@ -494,7 +495,7 @@ namespace detail {
|
||||
struct hash_combine_recursive_helper {
|
||||
char buffer[64];
|
||||
hash_state state;
|
||||
const size_t seed;
|
||||
const uint64_t seed;
|
||||
|
||||
public:
|
||||
/// Construct a recursive hash combining helper.
|
||||
|
||||
97
wpiutil/src/main/native/include/wpi/ManagedStatic.h
Normal file
97
wpiutil/src/main/native/include/wpi/ManagedStatic.h
Normal file
@@ -0,0 +1,97 @@
|
||||
//===-- llvm/Support/ManagedStatic.h - Static Global wrapper ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the ManagedStatic class and the wpi_shutdown() function.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_MANAGEDSTATIC_H
|
||||
#define WPIUTIL_WPI_MANAGEDSTATIC_H
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// object_creator - Helper method for ManagedStatic.
|
||||
template <class C> struct object_creator {
|
||||
static void *call() { return new C(); }
|
||||
};
|
||||
|
||||
/// object_deleter - Helper method for ManagedStatic.
|
||||
///
|
||||
template <typename T> struct object_deleter {
|
||||
static void call(void *Ptr) { delete (T *)Ptr; }
|
||||
};
|
||||
template <typename T, size_t N> struct object_deleter<T[N]> {
|
||||
static void call(void *Ptr) { delete[](T *)Ptr; }
|
||||
};
|
||||
|
||||
/// ManagedStaticBase - Common base class for ManagedStatic instances.
|
||||
class ManagedStaticBase {
|
||||
protected:
|
||||
// This should only be used as a static variable, which guarantees that this
|
||||
// will be zero initialized.
|
||||
mutable std::atomic<void *> Ptr;
|
||||
mutable void (*DeleterFn)(void*);
|
||||
mutable const ManagedStaticBase *Next;
|
||||
|
||||
void RegisterManagedStatic(void *(*creator)(), void (*deleter)(void*)) const;
|
||||
|
||||
public:
|
||||
/// isConstructed - Return true if this object has not been created yet.
|
||||
bool isConstructed() const { return Ptr != nullptr; }
|
||||
|
||||
void destroy() const;
|
||||
};
|
||||
|
||||
/// ManagedStatic - This transparently changes the behavior of global statics to
|
||||
/// be lazily constructed on demand (good for reducing startup times of dynamic
|
||||
/// libraries that link in LLVM components) and for making destruction be
|
||||
/// explicit through the wpi_shutdown() function call.
|
||||
///
|
||||
template <class C, class Creator = object_creator<C>,
|
||||
class Deleter = object_deleter<C>>
|
||||
class ManagedStatic : public ManagedStaticBase {
|
||||
public:
|
||||
// Accessors.
|
||||
C &operator*() {
|
||||
void *Tmp = Ptr.load(std::memory_order_acquire);
|
||||
if (!Tmp)
|
||||
RegisterManagedStatic(Creator::call, Deleter::call);
|
||||
|
||||
return *static_cast<C *>(Ptr.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
C *operator->() { return &**this; }
|
||||
|
||||
const C &operator*() const {
|
||||
void *Tmp = Ptr.load(std::memory_order_acquire);
|
||||
if (!Tmp)
|
||||
RegisterManagedStatic(Creator::call, Deleter::call);
|
||||
|
||||
return *static_cast<C *>(Ptr.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
const C *operator->() const { return &**this; }
|
||||
};
|
||||
|
||||
/// wpi_shutdown - Deallocate and destroy all ManagedStatic variables.
|
||||
void wpi_shutdown();
|
||||
|
||||
/// wpi_shutdown_obj - This is a simple helper class that calls
|
||||
/// wpi_shutdown() when it is destroyed.
|
||||
struct wpi_shutdown_obj {
|
||||
wpi_shutdown_obj() = default;
|
||||
~wpi_shutdown_obj() { wpi_shutdown(); }
|
||||
};
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_MANAGEDSTATIC_H
|
||||
@@ -25,7 +25,15 @@
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
// Declare these intrinsics manually rather including intrin.h. It's very
|
||||
// expensive, and MathExtras.h is popular.
|
||||
// #include <intrin.h>
|
||||
extern "C" {
|
||||
unsigned char _BitScanForward(unsigned long *_Index, unsigned long _Mask);
|
||||
unsigned char _BitScanForward64(unsigned long *_Index, unsigned __int64 _Mask);
|
||||
unsigned char _BitScanReverse(unsigned long *_Index, unsigned long _Mask);
|
||||
unsigned char _BitScanReverse64(unsigned long *_Index, unsigned __int64 _Mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace wpi {
|
||||
|
||||
49
wpiutil/src/main/native/include/wpi/MemAlloc.h
Normal file
49
wpiutil/src/main/native/include/wpi/MemAlloc.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//===- MemAlloc.h - Memory allocation functions -----------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
///
|
||||
/// This file defines counterparts of C library allocation functions defined in
|
||||
/// the namespace 'std'. The new allocation functions crash on allocation
|
||||
/// failure instead of returning null pointer.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_MEMALLOC_H
|
||||
#define WPIUTIL_WPI_MEMALLOC_H
|
||||
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/ErrorHandling.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_malloc(size_t Sz) {
|
||||
void *Result = std::malloc(Sz);
|
||||
if (Result == nullptr)
|
||||
report_bad_alloc_error("Allocation failed");
|
||||
return Result;
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_calloc(size_t Count,
|
||||
size_t Sz) {
|
||||
void *Result = std::calloc(Count, Sz);
|
||||
if (Result == nullptr)
|
||||
report_bad_alloc_error("Allocation failed");
|
||||
return Result;
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_realloc(void *Ptr, size_t Sz) {
|
||||
void *Result = std::realloc(Ptr, Sz);
|
||||
if (Result == nullptr)
|
||||
report_bad_alloc_error("Allocation failed");
|
||||
return Result;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "wpi/iterator.h"
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <system_error>
|
||||
|
||||
namespace wpi {
|
||||
namespace sys {
|
||||
@@ -360,22 +361,6 @@ void system_temp_directory(bool erasedOnReboot, SmallVectorImpl<char> &result);
|
||||
/// @result True if a home directory is set, false otherwise.
|
||||
bool home_directory(SmallVectorImpl<char> &result);
|
||||
|
||||
/// 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
|
||||
/// used to append additional directory names to the resulting path. Recommended
|
||||
/// pattern is <user_cache_directory>/<vendor>/<application>.
|
||||
///
|
||||
/// @param Result Holds the resulting path.
|
||||
/// @param Path1 Additional path to be appended to the user's cache directory
|
||||
/// path. "" can be used to append nothing.
|
||||
/// @param Path2 Second additional path to be appended.
|
||||
/// @param Path3 Third additional path to be appended.
|
||||
/// @result True if a cache directory path is set, false otherwise.
|
||||
bool user_cache_directory(SmallVectorImpl<char> &Result, const Twine &Path1,
|
||||
const Twine &Path2 = "", const Twine &Path3 = "");
|
||||
|
||||
/// Has root name?
|
||||
///
|
||||
/// root_name != ""
|
||||
@@ -467,6 +452,10 @@ StringRef remove_leading_dotslash(StringRef path, Style style = Style::native);
|
||||
bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot = false,
|
||||
Style style = Style::native);
|
||||
|
||||
#if defined(_WIN32)
|
||||
std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16);
|
||||
#endif
|
||||
|
||||
} // end namespace path
|
||||
} // end namespace sys
|
||||
} // end namespace wpi
|
||||
|
||||
@@ -112,6 +112,39 @@ template <> struct PointerLikeTypeTraits<uintptr_t> {
|
||||
enum { NumLowBitsAvailable = 0 };
|
||||
};
|
||||
|
||||
/// Provide suitable custom traits struct for function pointers.
|
||||
///
|
||||
/// Function pointers can't be directly given these traits as functions can't
|
||||
/// have their alignment computed with `alignof` and we need different casting.
|
||||
///
|
||||
/// To rely on higher alignment for a specialized use, you can provide a
|
||||
/// customized form of this template explicitly with higher alignment, and
|
||||
/// potentially use alignment attributes on functions to satisfy that.
|
||||
template <int Alignment, typename FunctionPointerT>
|
||||
struct FunctionPointerLikeTypeTraits {
|
||||
enum { NumLowBitsAvailable = detail::ConstantLog2<Alignment>::value };
|
||||
static inline void *getAsVoidPointer(FunctionPointerT P) {
|
||||
assert((reinterpret_cast<uintptr_t>(P) &
|
||||
~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 &&
|
||||
"Alignment not satisfied for an actual function pointer!");
|
||||
return reinterpret_cast<void *>(P);
|
||||
}
|
||||
static inline FunctionPointerT getFromVoidPointer(void *P) {
|
||||
return reinterpret_cast<FunctionPointerT>(P);
|
||||
}
|
||||
};
|
||||
|
||||
/// Provide a default specialization for function pointers that assumes 4-byte
|
||||
/// alignment.
|
||||
///
|
||||
/// We assume here that functions used with this are always at least 4-byte
|
||||
/// aligned. This means that, for example, thumb functions won't work or systems
|
||||
/// with weird unaligned function pointers won't work. But all practical systems
|
||||
/// we support satisfy this requirement.
|
||||
template <typename ReturnT, typename... ParamTs>
|
||||
struct PointerLikeTypeTraits<ReturnT (*)(ParamTs...)>
|
||||
: FunctionPointerLikeTypeTraits<4, ReturnT (*)(ParamTs...)> {};
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "wpi/iterator.h"
|
||||
#include "wpi/iterator_range.h"
|
||||
#include "wpi/optional.h"
|
||||
#include "wpi/ErrorHandling.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
@@ -52,6 +53,29 @@ using ValueOfRange = typename std::remove_reference<decltype(
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Extra additions to <type_traits>
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
template <typename T>
|
||||
struct negation : std::integral_constant<bool, !bool(T::value)> {};
|
||||
|
||||
template <typename...> struct conjunction : std::true_type {};
|
||||
template <typename B1> struct conjunction<B1> : B1 {};
|
||||
template <typename B1, typename... Bn>
|
||||
struct conjunction<B1, Bn...>
|
||||
: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
|
||||
|
||||
template <typename T> struct make_const_ptr {
|
||||
using type =
|
||||
typename std::add_pointer<typename std::add_const<T>::type>::type;
|
||||
};
|
||||
|
||||
template <typename T> struct make_const_ref {
|
||||
using type = typename std::add_lvalue_reference<
|
||||
typename std::add_const<T>::type>::type;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Extra additions to <functional>
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -176,6 +200,12 @@ void adl_swap(T &&lhs, T &&rhs) noexcept(
|
||||
adl_detail::adl_swap(std::forward<T>(lhs), std::forward<T>(rhs));
|
||||
}
|
||||
|
||||
/// Test whether \p RangeOrContainer is empty. Similar to C++17 std::empty.
|
||||
template <typename T>
|
||||
constexpr bool empty(const T &RangeOrContainer) {
|
||||
return adl_begin(RangeOrContainer) == adl_end(RangeOrContainer);
|
||||
}
|
||||
|
||||
// mapped_iterator - This is a simple iterator adapter that causes a function to
|
||||
// be applied whenever operator* is invoked on the iterator.
|
||||
|
||||
@@ -403,6 +433,8 @@ make_filter_range(RangeT &&Range, PredicateT Pred) {
|
||||
// forward declarations required by zip_shortest/zip_first
|
||||
template <typename R, typename UnaryPredicate>
|
||||
bool all_of(R &&range, UnaryPredicate P);
|
||||
template <typename R, typename UnaryPredicate>
|
||||
bool any_of(R &&range, UnaryPredicate P);
|
||||
|
||||
template <size_t... I> struct index_sequence;
|
||||
|
||||
@@ -553,6 +585,132 @@ detail::zippy<detail::zip_first, T, U, Args...> zip_first(T &&t, U &&u,
|
||||
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Iter>
|
||||
static Iter next_or_end(const Iter &I, const Iter &End) {
|
||||
if (I == End)
|
||||
return End;
|
||||
return std::next(I);
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
static auto deref_or_none(const Iter &I, const Iter &End)
|
||||
-> wpi::optional<typename std::remove_const<
|
||||
typename std::remove_reference<decltype(*I)>::type>::type> {
|
||||
if (I == End)
|
||||
return nullopt;
|
||||
return *I;
|
||||
}
|
||||
|
||||
template <typename Iter> struct ZipLongestItemType {
|
||||
using type =
|
||||
wpi::optional<typename std::remove_const<typename std::remove_reference<
|
||||
decltype(*std::declval<Iter>())>::type>::type>;
|
||||
};
|
||||
|
||||
template <typename... Iters> struct ZipLongestTupleType {
|
||||
using type = std::tuple<typename ZipLongestItemType<Iters>::type...>;
|
||||
};
|
||||
|
||||
template <typename... Iters>
|
||||
class zip_longest_iterator
|
||||
: public iterator_facade_base<
|
||||
zip_longest_iterator<Iters...>,
|
||||
typename std::common_type<
|
||||
std::forward_iterator_tag,
|
||||
typename std::iterator_traits<Iters>::iterator_category...>::type,
|
||||
typename ZipLongestTupleType<Iters...>::type,
|
||||
typename std::iterator_traits<typename std::tuple_element<
|
||||
0, std::tuple<Iters...>>::type>::difference_type,
|
||||
typename ZipLongestTupleType<Iters...>::type *,
|
||||
typename ZipLongestTupleType<Iters...>::type> {
|
||||
public:
|
||||
using value_type = typename ZipLongestTupleType<Iters...>::type;
|
||||
|
||||
private:
|
||||
std::tuple<Iters...> iterators;
|
||||
std::tuple<Iters...> end_iterators;
|
||||
|
||||
template <size_t... Ns>
|
||||
bool test(const zip_longest_iterator<Iters...> &other,
|
||||
index_sequence<Ns...>) const {
|
||||
return wpi::any_of(
|
||||
std::initializer_list<bool>{std::get<Ns>(this->iterators) !=
|
||||
std::get<Ns>(other.iterators)...},
|
||||
identity<bool>{});
|
||||
}
|
||||
|
||||
template <size_t... Ns> value_type deref(index_sequence<Ns...>) const {
|
||||
return value_type(
|
||||
deref_or_none(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
|
||||
}
|
||||
|
||||
template <size_t... Ns>
|
||||
decltype(iterators) tup_inc(index_sequence<Ns...>) const {
|
||||
return std::tuple<Iters...>(
|
||||
next_or_end(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
|
||||
}
|
||||
|
||||
public:
|
||||
zip_longest_iterator(std::pair<Iters &&, Iters &&>... ts)
|
||||
: iterators(std::forward<Iters>(ts.first)...),
|
||||
end_iterators(std::forward<Iters>(ts.second)...) {}
|
||||
|
||||
value_type operator*() { return deref(index_sequence_for<Iters...>{}); }
|
||||
|
||||
value_type operator*() const { return deref(index_sequence_for<Iters...>{}); }
|
||||
|
||||
zip_longest_iterator<Iters...> &operator++() {
|
||||
iterators = tup_inc(index_sequence_for<Iters...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const zip_longest_iterator<Iters...> &other) const {
|
||||
return !test(other, index_sequence_for<Iters...>{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args> class zip_longest_range {
|
||||
public:
|
||||
using iterator =
|
||||
zip_longest_iterator<decltype(adl_begin(std::declval<Args>()))...>;
|
||||
using iterator_category = typename iterator::iterator_category;
|
||||
using value_type = typename iterator::value_type;
|
||||
using difference_type = typename iterator::difference_type;
|
||||
using pointer = typename iterator::pointer;
|
||||
using reference = typename iterator::reference;
|
||||
|
||||
private:
|
||||
std::tuple<Args...> ts;
|
||||
|
||||
template <size_t... Ns> iterator begin_impl(index_sequence<Ns...>) const {
|
||||
return iterator(std::make_pair(adl_begin(std::get<Ns>(ts)),
|
||||
adl_end(std::get<Ns>(ts)))...);
|
||||
}
|
||||
|
||||
template <size_t... Ns> iterator end_impl(index_sequence<Ns...>) const {
|
||||
return iterator(std::make_pair(adl_end(std::get<Ns>(ts)),
|
||||
adl_end(std::get<Ns>(ts)))...);
|
||||
}
|
||||
|
||||
public:
|
||||
zip_longest_range(Args &&... ts_) : ts(std::forward<Args>(ts_)...) {}
|
||||
|
||||
iterator begin() const { return begin_impl(index_sequence_for<Args...>{}); }
|
||||
iterator end() const { return end_impl(index_sequence_for<Args...>{}); }
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// Iterate over two or more iterators at the same time. Iteration continues
|
||||
/// until all iterators reach the end. The wpi::optional only contains a value
|
||||
/// if the iterator has not reached the end.
|
||||
template <typename T, typename U, typename... Args>
|
||||
detail::zip_longest_range<T, U, Args...> zip_longest(T &&t, U &&u,
|
||||
Args &&... args) {
|
||||
return detail::zip_longest_range<T, U, Args...>(
|
||||
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Iterator wrapper that concatenates sequences together.
|
||||
///
|
||||
/// This can concatenate different iterators, even with different types, into
|
||||
@@ -575,18 +733,20 @@ class concat_iterator
|
||||
/// Note that something like iterator_range seems nice at first here, but the
|
||||
/// range properties are of little benefit and end up getting in the way
|
||||
/// because we need to do mutation on the current iterators.
|
||||
std::tuple<std::pair<IterTs, IterTs>...> IterPairs;
|
||||
std::tuple<IterTs...> Begins;
|
||||
std::tuple<IterTs...> Ends;
|
||||
|
||||
/// Attempts to increment a specific iterator.
|
||||
///
|
||||
/// Returns true if it was able to increment the iterator. Returns false if
|
||||
/// the iterator is already at the end iterator.
|
||||
template <size_t Index> bool incrementHelper() {
|
||||
auto &IterPair = std::get<Index>(IterPairs);
|
||||
if (IterPair.first == IterPair.second)
|
||||
auto &Begin = std::get<Index>(Begins);
|
||||
auto &End = std::get<Index>(Ends);
|
||||
if (Begin == End)
|
||||
return false;
|
||||
|
||||
++IterPair.first;
|
||||
++Begin;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -610,11 +770,12 @@ class concat_iterator
|
||||
/// dereferences the iterator and returns the address of the resulting
|
||||
/// reference.
|
||||
template <size_t Index> ValueT *getHelper() const {
|
||||
auto &IterPair = std::get<Index>(IterPairs);
|
||||
if (IterPair.first == IterPair.second)
|
||||
auto &Begin = std::get<Index>(Begins);
|
||||
auto &End = std::get<Index>(Ends);
|
||||
if (Begin == End)
|
||||
return nullptr;
|
||||
|
||||
return &*IterPair.first;
|
||||
return &*Begin;
|
||||
}
|
||||
|
||||
/// Finds the first non-end iterator, dereferences, and returns the resulting
|
||||
@@ -641,7 +802,7 @@ public:
|
||||
/// iterators.
|
||||
template <typename... RangeTs>
|
||||
explicit concat_iterator(RangeTs &&... Ranges)
|
||||
: IterPairs({std::begin(Ranges), std::end(Ranges)}...) {}
|
||||
: Begins(std::begin(Ranges)...), Ends(std::end(Ranges)...) {}
|
||||
|
||||
using BaseT::operator++;
|
||||
|
||||
@@ -653,7 +814,7 @@ public:
|
||||
ValueT &operator*() const { return get(index_sequence_for<IterTs...>()); }
|
||||
|
||||
bool operator==(const concat_iterator &RHS) const {
|
||||
return IterPairs == RHS.IterPairs;
|
||||
return Begins == RHS.Begins && Ends == RHS.Ends;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -722,6 +883,19 @@ struct less_second {
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Function object to apply a binary function to the first component of
|
||||
/// a std::pair.
|
||||
template<typename FuncTy>
|
||||
struct on_first {
|
||||
FuncTy func;
|
||||
|
||||
template <typename T>
|
||||
auto operator()(const T &lhs, const T &rhs) const
|
||||
-> decltype(func(lhs.first, rhs.first)) {
|
||||
return func(lhs.first, rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
// A subset of N3658. More stuff can be added as-needed.
|
||||
|
||||
/// Represents a compile-time sequence of integers.
|
||||
@@ -862,6 +1036,18 @@ void DeleteContainerSeconds(Container &C) {
|
||||
C.clear();
|
||||
}
|
||||
|
||||
/// Get the size of a range. This is a wrapper function around std::distance
|
||||
/// which is only enabled when the operation is O(1).
|
||||
template <typename R>
|
||||
auto size(R &&Range, typename std::enable_if<
|
||||
std::is_same<typename std::iterator_traits<decltype(
|
||||
Range.begin())>::iterator_category,
|
||||
std::random_access_iterator_tag>::value,
|
||||
void>::type * = nullptr)
|
||||
-> decltype(std::distance(Range.begin(), Range.end())) {
|
||||
return std::distance(Range.begin(), Range.end());
|
||||
}
|
||||
|
||||
/// Provide wrappers to std::for_each which take ranges instead of having to
|
||||
/// pass begin/end explicitly.
|
||||
template <typename R, typename UnaryPredicate>
|
||||
@@ -972,6 +1158,33 @@ auto lower_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) {
|
||||
return std::lower_bound(adl_begin(Range), adl_end(Range), I);
|
||||
}
|
||||
|
||||
template <typename R, typename ForwardIt, typename Compare>
|
||||
auto lower_bound(R &&Range, ForwardIt I, Compare C)
|
||||
-> decltype(adl_begin(Range)) {
|
||||
return std::lower_bound(adl_begin(Range), adl_end(Range), I, C);
|
||||
}
|
||||
|
||||
/// Provide wrappers to std::upper_bound which take ranges instead of having to
|
||||
/// pass begin/end explicitly.
|
||||
template <typename R, typename ForwardIt>
|
||||
auto upper_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) {
|
||||
return std::upper_bound(adl_begin(Range), adl_end(Range), I);
|
||||
}
|
||||
|
||||
template <typename R, typename ForwardIt, typename Compare>
|
||||
auto upper_bound(R &&Range, ForwardIt I, Compare C)
|
||||
-> decltype(adl_begin(Range)) {
|
||||
return std::upper_bound(adl_begin(Range), adl_end(Range), I, C);
|
||||
}
|
||||
/// Wrapper function around std::equal to detect if all elements
|
||||
/// in a container are same.
|
||||
template <typename R>
|
||||
bool is_splat(R &&Range) {
|
||||
size_t range_size = size(Range);
|
||||
return range_size != 0 && (range_size == 1 ||
|
||||
std::equal(adl_begin(Range) + 1, adl_end(Range), adl_begin(Range)));
|
||||
}
|
||||
|
||||
/// Given a range of type R, iterate the entire range and return a
|
||||
/// SmallVector with elements of the vector. This is useful, for example,
|
||||
/// when you want to iterate a range and then sort the results.
|
||||
@@ -993,18 +1206,6 @@ void erase_if(Container &C, UnaryPredicate P) {
|
||||
C.erase(remove_if(C, P), C.end());
|
||||
}
|
||||
|
||||
/// Get the size of a range. This is a wrapper function around std::distance
|
||||
/// which is only enabled when the operation is O(1).
|
||||
template <typename R>
|
||||
auto size(R &&Range, typename std::enable_if<
|
||||
std::is_same<typename std::iterator_traits<decltype(
|
||||
Range.begin())>::iterator_category,
|
||||
std::random_access_iterator_tag>::value,
|
||||
void>::type * = nullptr)
|
||||
-> decltype(std::distance(Range.begin(), Range.end())) {
|
||||
return std::distance(Range.begin(), Range.end());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Extra additions to <memory>
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -1217,6 +1418,40 @@ auto apply_tuple(F &&f, Tuple &&t) -> decltype(detail::apply_tuple_impl(
|
||||
Indices{});
|
||||
}
|
||||
|
||||
/// Return true if the sequence [Begin, End) has exactly N items. Runs in O(N)
|
||||
/// time. Not meant for use with random-access iterators.
|
||||
template <typename IterTy>
|
||||
bool hasNItems(
|
||||
IterTy &&Begin, IterTy &&End, unsigned N,
|
||||
typename std::enable_if<
|
||||
!std::is_same<
|
||||
typename std::iterator_traits<typename std::remove_reference<
|
||||
decltype(Begin)>::type>::iterator_category,
|
||||
std::random_access_iterator_tag>::value,
|
||||
void>::type * = nullptr) {
|
||||
for (; N; --N, ++Begin)
|
||||
if (Begin == End)
|
||||
return false; // Too few.
|
||||
return Begin == End;
|
||||
}
|
||||
|
||||
/// Return true if the sequence [Begin, End) has N or more items. Runs in O(N)
|
||||
/// time. Not meant for use with random-access iterators.
|
||||
template <typename IterTy>
|
||||
bool hasNItemsOrMore(
|
||||
IterTy &&Begin, IterTy &&End, unsigned N,
|
||||
typename std::enable_if<
|
||||
!std::is_same<
|
||||
typename std::iterator_traits<typename std::remove_reference<
|
||||
decltype(Begin)>::type>::iterator_category,
|
||||
std::random_access_iterator_tag>::value,
|
||||
void>::type * = nullptr) {
|
||||
for (; N; --N, ++Begin)
|
||||
if (Begin == End)
|
||||
return false; // Too few.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // LLVM_ADT_STLEXTRAS_H
|
||||
|
||||
@@ -18,20 +18,119 @@
|
||||
#include "wpi/SmallPtrSet.h"
|
||||
#include "wpi/SmallVector.h"
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/iterator.h"
|
||||
#include "wpi/type_traits.h"
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// SmallSetIterator - This class implements a const_iterator for SmallSet by
|
||||
/// delegating to the underlying SmallVector or Set iterators.
|
||||
template <typename T, unsigned N, typename C>
|
||||
class SmallSetIterator
|
||||
: public iterator_facade_base<SmallSetIterator<T, N, C>,
|
||||
std::forward_iterator_tag, T> {
|
||||
private:
|
||||
using SetIterTy = typename std::set<T, C>::const_iterator;
|
||||
using VecIterTy = typename SmallVector<T, N>::const_iterator;
|
||||
using SelfTy = SmallSetIterator<T, N, C>;
|
||||
|
||||
/// Iterators to the parts of the SmallSet containing the data. They are set
|
||||
/// depending on isSmall.
|
||||
union {
|
||||
SetIterTy SetIter;
|
||||
VecIterTy VecIter;
|
||||
};
|
||||
|
||||
bool isSmall;
|
||||
|
||||
public:
|
||||
SmallSetIterator(SetIterTy SetIter) : SetIter(SetIter), isSmall(false) {}
|
||||
|
||||
SmallSetIterator(VecIterTy VecIter) : VecIter(VecIter), isSmall(true) {}
|
||||
|
||||
// Spell out destructor, copy/move constructor and assignment operators for
|
||||
// MSVC STL, where set<T>::const_iterator is not trivially copy constructible.
|
||||
~SmallSetIterator() {
|
||||
if (isSmall)
|
||||
VecIter.~VecIterTy();
|
||||
else
|
||||
SetIter.~SetIterTy();
|
||||
}
|
||||
|
||||
SmallSetIterator(const SmallSetIterator &Other) : isSmall(Other.isSmall) {
|
||||
if (isSmall)
|
||||
VecIter = Other.VecIter;
|
||||
else
|
||||
// Use placement new, to make sure SetIter is properly constructed, even
|
||||
// if it is not trivially copy-able (e.g. in MSVC).
|
||||
new (&SetIter) SetIterTy(Other.SetIter);
|
||||
}
|
||||
|
||||
SmallSetIterator(SmallSetIterator &&Other) : isSmall(Other.isSmall) {
|
||||
if (isSmall)
|
||||
VecIter = std::move(Other.VecIter);
|
||||
else
|
||||
// Use placement new, to make sure SetIter is properly constructed, even
|
||||
// if it is not trivially copy-able (e.g. in MSVC).
|
||||
new (&SetIter) SetIterTy(std::move(Other.SetIter));
|
||||
}
|
||||
|
||||
SmallSetIterator& operator=(const SmallSetIterator& Other) {
|
||||
// Call destructor for SetIter, so it gets properly destroyed if it is
|
||||
// not trivially destructible in case we are setting VecIter.
|
||||
if (!isSmall)
|
||||
SetIter.~SetIterTy();
|
||||
|
||||
isSmall = Other.isSmall;
|
||||
if (isSmall)
|
||||
VecIter = Other.VecIter;
|
||||
else
|
||||
new (&SetIter) SetIterTy(Other.SetIter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SmallSetIterator& operator=(SmallSetIterator&& Other) {
|
||||
// Call destructor for SetIter, so it gets properly destroyed if it is
|
||||
// not trivially destructible in case we are setting VecIter.
|
||||
if (!isSmall)
|
||||
SetIter.~SetIterTy();
|
||||
|
||||
isSmall = Other.isSmall;
|
||||
if (isSmall)
|
||||
VecIter = std::move(Other.VecIter);
|
||||
else
|
||||
new (&SetIter) SetIterTy(std::move(Other.SetIter));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const SmallSetIterator &RHS) const {
|
||||
if (isSmall != RHS.isSmall)
|
||||
return false;
|
||||
if (isSmall)
|
||||
return VecIter == RHS.VecIter;
|
||||
return SetIter == RHS.SetIter;
|
||||
}
|
||||
|
||||
SmallSetIterator &operator++() { // Preincrement
|
||||
if (isSmall)
|
||||
VecIter++;
|
||||
else
|
||||
SetIter++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T &operator*() const { return isSmall ? *VecIter : *SetIter; }
|
||||
};
|
||||
|
||||
/// SmallSet - This maintains a set of unique values, optimizing for the case
|
||||
/// when the set is small (less than N). In this case, the set can be
|
||||
/// maintained with no mallocs. If the set gets large, we expand to using an
|
||||
/// std::set to maintain reasonable lookup times.
|
||||
///
|
||||
/// Note that this set does not provide a way to iterate over members in the
|
||||
/// set.
|
||||
template <typename T, unsigned N, typename C = std::less<T>>
|
||||
class SmallSet {
|
||||
/// Use a SmallVector to hold the elements here (even though it will never
|
||||
@@ -50,6 +149,7 @@ class SmallSet {
|
||||
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using const_iterator = SmallSetIterator<T, N, C>;
|
||||
|
||||
SmallSet() = default;
|
||||
|
||||
@@ -121,6 +221,18 @@ public:
|
||||
Set.clear();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
if (isSmall())
|
||||
return {Vector.begin()};
|
||||
return {Set.begin()};
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
if (isSmall())
|
||||
return {Vector.end()};
|
||||
return {Set.end()};
|
||||
}
|
||||
|
||||
private:
|
||||
bool isSmall() const { return Set.empty(); }
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "wpi/AlignOf.h"
|
||||
#include "wpi/Compiler.h"
|
||||
#include "wpi/MathExtras.h"
|
||||
#include "wpi/memory.h"
|
||||
#include "wpi/MemAlloc.h"
|
||||
#include "wpi/type_traits.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@@ -45,28 +45,44 @@ namespace wpi {
|
||||
/// This is all the non-templated stuff common to all SmallVectors.
|
||||
class SmallVectorBase {
|
||||
protected:
|
||||
void *BeginX, *EndX, *CapacityX;
|
||||
void *BeginX;
|
||||
unsigned Size = 0, Capacity;
|
||||
|
||||
protected:
|
||||
SmallVectorBase(void *FirstEl, size_t Size)
|
||||
: BeginX(FirstEl), EndX(FirstEl), CapacityX((char*)FirstEl+Size) {}
|
||||
SmallVectorBase() = delete;
|
||||
SmallVectorBase(void *FirstEl, size_t Capacity)
|
||||
: BeginX(FirstEl), Capacity(Capacity) {}
|
||||
|
||||
/// This is an implementation of the grow() method which only works
|
||||
/// on POD-like data types and is out of line to reduce code duplication.
|
||||
void grow_pod(void *FirstEl, size_t MinSizeInBytes, size_t TSize);
|
||||
void grow_pod(void *FirstEl, size_t MinCapacity, size_t TSize);
|
||||
|
||||
public:
|
||||
/// This returns size()*sizeof(T).
|
||||
size_t size_in_bytes() const {
|
||||
return size_t((char*)EndX - (char*)BeginX);
|
||||
}
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
size_t size() const { return Size; }
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
size_t capacity() const { return Capacity; }
|
||||
|
||||
/// capacity_in_bytes - This returns capacity()*sizeof(T).
|
||||
size_t capacity_in_bytes() const {
|
||||
return size_t((char*)CapacityX - (char*)BeginX);
|
||||
}
|
||||
LLVM_NODISCARD bool empty() const { return !Size; }
|
||||
|
||||
LLVM_NODISCARD bool empty() const { return BeginX == EndX; }
|
||||
/// Set the array size to \p N, which the current array must have enough
|
||||
/// capacity for.
|
||||
///
|
||||
/// This does not construct or destroy any elements in the vector.
|
||||
///
|
||||
/// Clients can use this in conjunction with capacity() to write past the end
|
||||
/// of the buffer when they know that more elements are available, and only
|
||||
/// update the size later. This avoids the cost of value initializing elements
|
||||
/// which will only be overwritten.
|
||||
void set_size(size_t Size) {
|
||||
assert(Size <= capacity());
|
||||
this->Size = Size;
|
||||
}
|
||||
};
|
||||
|
||||
/// Figure out the offset of the first element.
|
||||
template <class T, typename = void> struct SmallVectorAlignmentAndSize {
|
||||
AlignedCharArrayUnion<SmallVectorBase> Base;
|
||||
AlignedCharArrayUnion<T> FirstEl;
|
||||
};
|
||||
|
||||
/// This is the part of SmallVectorTemplateBase which does not depend on whether
|
||||
@@ -74,36 +90,34 @@ public:
|
||||
/// to avoid unnecessarily requiring T to be complete.
|
||||
template <typename T, typename = void>
|
||||
class SmallVectorTemplateCommon : public SmallVectorBase {
|
||||
private:
|
||||
template <typename, unsigned> friend struct SmallVectorStorage;
|
||||
|
||||
// Allocate raw space for N elements of type T. If T has a ctor or dtor, we
|
||||
// don't want it to be automatically run, so we need to represent the space as
|
||||
// something else. Use an array of char of sufficient alignment.
|
||||
using U = AlignedCharArrayUnion<T>;
|
||||
U FirstEl;
|
||||
/// Find the address of the first element. For this pointer math to be valid
|
||||
/// with small-size of 0 for T with lots of alignment, it's important that
|
||||
/// SmallVectorStorage is properly-aligned even for small-size of 0.
|
||||
void *getFirstEl() const {
|
||||
return const_cast<void *>(reinterpret_cast<const void *>(
|
||||
reinterpret_cast<const char *>(this) +
|
||||
offsetof(SmallVectorAlignmentAndSize<T>, FirstEl)));
|
||||
}
|
||||
// Space after 'FirstEl' is clobbered, do not add any instance vars after it.
|
||||
|
||||
protected:
|
||||
SmallVectorTemplateCommon(size_t Size) : SmallVectorBase(&FirstEl, Size) {}
|
||||
SmallVectorTemplateCommon(size_t Size)
|
||||
: SmallVectorBase(getFirstEl(), Size) {}
|
||||
|
||||
void grow_pod(size_t MinSizeInBytes, size_t TSize) {
|
||||
SmallVectorBase::grow_pod(&FirstEl, MinSizeInBytes, TSize);
|
||||
void grow_pod(size_t MinCapacity, size_t TSize) {
|
||||
SmallVectorBase::grow_pod(getFirstEl(), MinCapacity, TSize);
|
||||
}
|
||||
|
||||
/// Return true if this is a smallvector which has not had dynamic
|
||||
/// memory allocated for it.
|
||||
bool isSmall() const {
|
||||
return BeginX == static_cast<const void*>(&FirstEl);
|
||||
}
|
||||
bool isSmall() const { return BeginX == getFirstEl(); }
|
||||
|
||||
/// Put this vector in a state of being small.
|
||||
void resetToSmall() {
|
||||
BeginX = EndX = CapacityX = &FirstEl;
|
||||
BeginX = getFirstEl();
|
||||
Size = Capacity = 0; // FIXME: Setting Capacity to 0 is suspect.
|
||||
}
|
||||
|
||||
void setEnd(T *P) { this->EndX = P; }
|
||||
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
@@ -125,27 +139,20 @@ public:
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
const_iterator begin() const { return (const_iterator)this->BeginX; }
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
iterator end() { return (iterator)this->EndX; }
|
||||
iterator end() { return begin() + size(); }
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
const_iterator end() const { return (const_iterator)this->EndX; }
|
||||
const_iterator end() const { return begin() + size(); }
|
||||
|
||||
protected:
|
||||
iterator capacity_ptr() { return (iterator)this->CapacityX; }
|
||||
const_iterator capacity_ptr() const { return (const_iterator)this->CapacityX;}
|
||||
|
||||
public:
|
||||
// reverse iterator creation methods.
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
const_reverse_iterator rbegin() const{ return const_reverse_iterator(end()); }
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(begin());}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
size_type size() const { return end()-begin(); }
|
||||
size_type size_in_bytes() const { return size() * sizeof(T); }
|
||||
size_type max_size() const { return size_type(-1) / sizeof(T); }
|
||||
|
||||
/// Return the total number of elements in the currently allocated buffer.
|
||||
size_t capacity() const { return capacity_ptr() - begin(); }
|
||||
size_t capacity_in_bytes() const { return capacity() * sizeof(T); }
|
||||
|
||||
/// Return a pointer to the vector's buffer, even if empty().
|
||||
pointer data() { return pointer(begin()); }
|
||||
@@ -184,7 +191,7 @@ public:
|
||||
|
||||
/// SmallVectorTemplateBase<isPodLike = false> - This is where we put method
|
||||
/// implementations that are designed to work with non-POD-like T's.
|
||||
template <typename T, bool isPodLike>
|
||||
template <typename T, bool = isPodLike<T>::value>
|
||||
class SmallVectorTemplateBase : public SmallVectorTemplateCommon<T> {
|
||||
protected:
|
||||
SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon<T>(Size) {}
|
||||
@@ -218,21 +225,21 @@ protected:
|
||||
|
||||
public:
|
||||
void push_back(const T &Elt) {
|
||||
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
|
||||
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
|
||||
this->grow();
|
||||
::new ((void*) this->end()) T(Elt);
|
||||
this->setEnd(this->end()+1);
|
||||
this->set_size(this->size() + 1);
|
||||
}
|
||||
|
||||
void push_back(T &&Elt) {
|
||||
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
|
||||
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
|
||||
this->grow();
|
||||
::new ((void*) this->end()) T(::std::move(Elt));
|
||||
this->setEnd(this->end()+1);
|
||||
this->set_size(this->size() + 1);
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
this->setEnd(this->end()-1);
|
||||
this->set_size(this->size() - 1);
|
||||
this->end()->~T();
|
||||
}
|
||||
};
|
||||
@@ -240,13 +247,13 @@ public:
|
||||
// Define this out-of-line to dissuade the C++ compiler from inlining it.
|
||||
template <typename T, bool isPodLike>
|
||||
void SmallVectorTemplateBase<T, isPodLike>::grow(size_t MinSize) {
|
||||
size_t CurCapacity = this->capacity();
|
||||
size_t CurSize = this->size();
|
||||
if (MinSize > UINT32_MAX)
|
||||
report_bad_alloc_error("SmallVector capacity overflow during allocation");
|
||||
|
||||
// Always grow, even from zero.
|
||||
size_t NewCapacity = size_t(NextPowerOf2(CurCapacity+2));
|
||||
if (NewCapacity < MinSize)
|
||||
NewCapacity = MinSize;
|
||||
T *NewElts = static_cast<T*>(CheckedMalloc(NewCapacity*sizeof(T)));
|
||||
size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + 2));
|
||||
NewCapacity = std::min(std::max(NewCapacity, MinSize), size_t(UINT32_MAX));
|
||||
T *NewElts = static_cast<T*>(wpi::safe_malloc(NewCapacity*sizeof(T)));
|
||||
|
||||
// Move the elements over.
|
||||
this->uninitialized_move(this->begin(), this->end(), NewElts);
|
||||
@@ -258,9 +265,8 @@ void SmallVectorTemplateBase<T, isPodLike>::grow(size_t MinSize) {
|
||||
if (!this->isSmall())
|
||||
free(this->begin());
|
||||
|
||||
this->setEnd(NewElts+CurSize);
|
||||
this->BeginX = NewElts;
|
||||
this->CapacityX = this->begin()+NewCapacity;
|
||||
this->Capacity = NewCapacity;
|
||||
}
|
||||
|
||||
|
||||
@@ -302,33 +308,29 @@ protected:
|
||||
// use memcpy here. Note that I and E are iterators and thus might be
|
||||
// invalid for memcpy if they are equal.
|
||||
if (I != E)
|
||||
memcpy(Dest, I, (E - I) * sizeof(T));
|
||||
memcpy(reinterpret_cast<void *>(Dest), I, (E - I) * sizeof(T));
|
||||
}
|
||||
|
||||
/// Double the size of the allocated memory, guaranteeing space for at
|
||||
/// least one more element or MinSize if specified.
|
||||
void grow(size_t MinSize = 0) {
|
||||
this->grow_pod(MinSize*sizeof(T), sizeof(T));
|
||||
}
|
||||
void grow(size_t MinSize = 0) { this->grow_pod(MinSize, sizeof(T)); }
|
||||
|
||||
public:
|
||||
void push_back(const T &Elt) {
|
||||
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
|
||||
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
|
||||
this->grow();
|
||||
memcpy(this->end(), &Elt, sizeof(T));
|
||||
this->setEnd(this->end()+1);
|
||||
memcpy(reinterpret_cast<void *>(this->end()), &Elt, sizeof(T));
|
||||
this->set_size(this->size() + 1);
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
this->setEnd(this->end()-1);
|
||||
}
|
||||
void pop_back() { this->set_size(this->size() - 1); }
|
||||
};
|
||||
|
||||
/// This class consists of common code factored out of the SmallVector class to
|
||||
/// reduce code duplication based on the SmallVector 'N' template parameter.
|
||||
template <typename T>
|
||||
class SmallVectorImpl : public SmallVectorTemplateBase<T, isPodLike<T>::value> {
|
||||
using SuperClass = SmallVectorTemplateBase<T, isPodLike<T>::value>;
|
||||
class SmallVectorImpl : public SmallVectorTemplateBase<T> {
|
||||
using SuperClass = SmallVectorTemplateBase<T>;
|
||||
|
||||
public:
|
||||
using iterator = typename SuperClass::iterator;
|
||||
@@ -338,8 +340,7 @@ public:
|
||||
protected:
|
||||
// Default ctor - Initialize to empty.
|
||||
explicit SmallVectorImpl(unsigned N)
|
||||
: SmallVectorTemplateBase<T, isPodLike<T>::value>(N*sizeof(T)) {
|
||||
}
|
||||
: SmallVectorTemplateBase<T, isPodLike<T>::value>(N) {}
|
||||
|
||||
public:
|
||||
SmallVectorImpl(const SmallVectorImpl &) = delete;
|
||||
@@ -353,31 +354,31 @@ public:
|
||||
|
||||
void clear() {
|
||||
this->destroy_range(this->begin(), this->end());
|
||||
this->EndX = this->BeginX;
|
||||
this->Size = 0;
|
||||
}
|
||||
|
||||
void resize(size_type N) {
|
||||
if (N < this->size()) {
|
||||
this->destroy_range(this->begin()+N, this->end());
|
||||
this->setEnd(this->begin()+N);
|
||||
this->set_size(N);
|
||||
} else if (N > this->size()) {
|
||||
if (this->capacity() < N)
|
||||
this->grow(N);
|
||||
for (auto I = this->end(), E = this->begin() + N; I != E; ++I)
|
||||
new (&*I) T();
|
||||
this->setEnd(this->begin()+N);
|
||||
this->set_size(N);
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_type N, const T &NV) {
|
||||
if (N < this->size()) {
|
||||
this->destroy_range(this->begin()+N, this->end());
|
||||
this->setEnd(this->begin()+N);
|
||||
this->set_size(N);
|
||||
} else if (N > this->size()) {
|
||||
if (this->capacity() < N)
|
||||
this->grow(N);
|
||||
std::uninitialized_fill(this->end(), this->begin()+N, NV);
|
||||
this->setEnd(this->begin()+N);
|
||||
this->set_size(N);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,23 +403,23 @@ public:
|
||||
void append(in_iter in_start, in_iter in_end) {
|
||||
size_type NumInputs = std::distance(in_start, in_end);
|
||||
// Grow allocated space if needed.
|
||||
if (NumInputs > size_type(this->capacity_ptr()-this->end()))
|
||||
if (NumInputs > this->capacity() - this->size())
|
||||
this->grow(this->size()+NumInputs);
|
||||
|
||||
// Copy the new elements over.
|
||||
this->uninitialized_copy(in_start, in_end, this->end());
|
||||
this->setEnd(this->end() + NumInputs);
|
||||
this->set_size(this->size() + NumInputs);
|
||||
}
|
||||
|
||||
/// Add the specified range to the end of the SmallVector.
|
||||
void append(size_type NumInputs, const T &Elt) {
|
||||
// Grow allocated space if needed.
|
||||
if (NumInputs > size_type(this->capacity_ptr()-this->end()))
|
||||
if (NumInputs > this->capacity() - this->size())
|
||||
this->grow(this->size()+NumInputs);
|
||||
|
||||
// Copy the new elements over.
|
||||
std::uninitialized_fill_n(this->end(), NumInputs, Elt);
|
||||
this->setEnd(this->end() + NumInputs);
|
||||
this->set_size(this->size() + NumInputs);
|
||||
}
|
||||
|
||||
void append(std::initializer_list<T> IL) {
|
||||
@@ -432,7 +433,7 @@ public:
|
||||
clear();
|
||||
if (this->capacity() < NumElts)
|
||||
this->grow(NumElts);
|
||||
this->setEnd(this->begin()+NumElts);
|
||||
this->set_size(NumElts);
|
||||
std::uninitialized_fill(this->begin(), this->end(), Elt);
|
||||
}
|
||||
|
||||
@@ -479,7 +480,7 @@ public:
|
||||
iterator I = std::move(E, this->end(), S);
|
||||
// Drop the last elts.
|
||||
this->destroy_range(I, this->end());
|
||||
this->setEnd(I);
|
||||
this->set_size(I - this->begin());
|
||||
return(N);
|
||||
}
|
||||
|
||||
@@ -492,7 +493,7 @@ public:
|
||||
assert(I >= this->begin() && "Insertion iterator is out of bounds.");
|
||||
assert(I <= this->end() && "Inserting past the end of the vector.");
|
||||
|
||||
if (this->EndX >= this->CapacityX) {
|
||||
if (this->size() >= this->capacity()) {
|
||||
size_t EltNo = I-this->begin();
|
||||
this->grow();
|
||||
I = this->begin()+EltNo;
|
||||
@@ -501,12 +502,12 @@ public:
|
||||
::new ((void*) this->end()) T(::std::move(this->back()));
|
||||
// Push everything else over.
|
||||
std::move_backward(I, this->end()-1, this->end());
|
||||
this->setEnd(this->end()+1);
|
||||
this->set_size(this->size() + 1);
|
||||
|
||||
// If we just moved the element we're inserting, be sure to update
|
||||
// the reference.
|
||||
T *EltPtr = &Elt;
|
||||
if (I <= EltPtr && EltPtr < this->EndX)
|
||||
if (I <= EltPtr && EltPtr < this->end())
|
||||
++EltPtr;
|
||||
|
||||
*I = ::std::move(*EltPtr);
|
||||
@@ -522,7 +523,7 @@ public:
|
||||
assert(I >= this->begin() && "Insertion iterator is out of bounds.");
|
||||
assert(I <= this->end() && "Inserting past the end of the vector.");
|
||||
|
||||
if (this->EndX >= this->CapacityX) {
|
||||
if (this->size() >= this->capacity()) {
|
||||
size_t EltNo = I-this->begin();
|
||||
this->grow();
|
||||
I = this->begin()+EltNo;
|
||||
@@ -530,12 +531,12 @@ public:
|
||||
::new ((void*) this->end()) T(std::move(this->back()));
|
||||
// Push everything else over.
|
||||
std::move_backward(I, this->end()-1, this->end());
|
||||
this->setEnd(this->end()+1);
|
||||
this->set_size(this->size() + 1);
|
||||
|
||||
// If we just moved the element we're inserting, be sure to update
|
||||
// the reference.
|
||||
const T *EltPtr = &Elt;
|
||||
if (I <= EltPtr && EltPtr < this->EndX)
|
||||
if (I <= EltPtr && EltPtr < this->end())
|
||||
++EltPtr;
|
||||
|
||||
*I = *EltPtr;
|
||||
@@ -581,7 +582,7 @@ public:
|
||||
|
||||
// Move over the elements that we're about to overwrite.
|
||||
T *OldEnd = this->end();
|
||||
this->setEnd(this->end() + NumToInsert);
|
||||
this->set_size(this->size() + NumToInsert);
|
||||
size_t NumOverwritten = OldEnd-I;
|
||||
this->uninitialized_move(I, OldEnd, this->end()-NumOverwritten);
|
||||
|
||||
@@ -638,7 +639,7 @@ public:
|
||||
|
||||
// Move over the elements that we're about to overwrite.
|
||||
T *OldEnd = this->end();
|
||||
this->setEnd(this->end() + NumToInsert);
|
||||
this->set_size(this->size() + NumToInsert);
|
||||
size_t NumOverwritten = OldEnd-I;
|
||||
this->uninitialized_move(I, OldEnd, this->end()-NumOverwritten);
|
||||
|
||||
@@ -658,10 +659,10 @@ public:
|
||||
}
|
||||
|
||||
template <typename... ArgTypes> void emplace_back(ArgTypes &&... Args) {
|
||||
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
|
||||
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
|
||||
this->grow();
|
||||
::new ((void *)this->end()) T(std::forward<ArgTypes>(Args)...);
|
||||
this->setEnd(this->end() + 1);
|
||||
this->set_size(this->size() + 1);
|
||||
}
|
||||
|
||||
SmallVectorImpl &operator=(const SmallVectorImpl &RHS);
|
||||
@@ -680,20 +681,6 @@ public:
|
||||
return std::lexicographical_compare(this->begin(), this->end(),
|
||||
RHS.begin(), RHS.end());
|
||||
}
|
||||
|
||||
/// Set the array size to \p N, which the current array must have enough
|
||||
/// capacity for.
|
||||
///
|
||||
/// This does not construct or destroy any elements in the vector.
|
||||
///
|
||||
/// Clients can use this in conjunction with capacity() to write past the end
|
||||
/// of the buffer when they know that more elements are available, and only
|
||||
/// update the size later. This avoids the cost of value initializing elements
|
||||
/// which will only be overwritten.
|
||||
void set_size(size_type N) {
|
||||
assert(N <= this->capacity());
|
||||
this->setEnd(this->begin() + N);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -703,8 +690,8 @@ void SmallVectorImpl<T>::swap(SmallVectorImpl<T> &RHS) {
|
||||
// We can only avoid copying elements if neither vector is small.
|
||||
if (!this->isSmall() && !RHS.isSmall()) {
|
||||
std::swap(this->BeginX, RHS.BeginX);
|
||||
std::swap(this->EndX, RHS.EndX);
|
||||
std::swap(this->CapacityX, RHS.CapacityX);
|
||||
std::swap(this->Size, RHS.Size);
|
||||
std::swap(this->Capacity, RHS.Capacity);
|
||||
return;
|
||||
}
|
||||
if (RHS.size() > this->capacity())
|
||||
@@ -722,15 +709,15 @@ void SmallVectorImpl<T>::swap(SmallVectorImpl<T> &RHS) {
|
||||
if (this->size() > RHS.size()) {
|
||||
size_t EltDiff = this->size() - RHS.size();
|
||||
this->uninitialized_copy(this->begin()+NumShared, this->end(), RHS.end());
|
||||
RHS.setEnd(RHS.end()+EltDiff);
|
||||
RHS.set_size(RHS.size() + EltDiff);
|
||||
this->destroy_range(this->begin()+NumShared, this->end());
|
||||
this->setEnd(this->begin()+NumShared);
|
||||
this->set_size(NumShared);
|
||||
} else if (RHS.size() > this->size()) {
|
||||
size_t EltDiff = RHS.size() - this->size();
|
||||
this->uninitialized_copy(RHS.begin()+NumShared, RHS.end(), this->end());
|
||||
this->setEnd(this->end() + EltDiff);
|
||||
this->set_size(this->size() + EltDiff);
|
||||
this->destroy_range(RHS.begin()+NumShared, RHS.end());
|
||||
RHS.setEnd(RHS.begin()+NumShared);
|
||||
RHS.set_size(NumShared);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,7 +743,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::
|
||||
this->destroy_range(NewEnd, this->end());
|
||||
|
||||
// Trim.
|
||||
this->setEnd(NewEnd);
|
||||
this->set_size(RHSSize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -766,7 +753,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::
|
||||
if (this->capacity() < RHSSize) {
|
||||
// Destroy current elements.
|
||||
this->destroy_range(this->begin(), this->end());
|
||||
this->setEnd(this->begin());
|
||||
this->set_size(0);
|
||||
CurSize = 0;
|
||||
this->grow(RHSSize);
|
||||
} else if (CurSize) {
|
||||
@@ -779,7 +766,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::
|
||||
this->begin()+CurSize);
|
||||
|
||||
// Set end.
|
||||
this->setEnd(this->begin()+RHSSize);
|
||||
this->set_size(RHSSize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -793,8 +780,8 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
|
||||
this->destroy_range(this->begin(), this->end());
|
||||
if (!this->isSmall()) free(this->begin());
|
||||
this->BeginX = RHS.BeginX;
|
||||
this->EndX = RHS.EndX;
|
||||
this->CapacityX = RHS.CapacityX;
|
||||
this->Size = RHS.Size;
|
||||
this->Capacity = RHS.Capacity;
|
||||
RHS.resetToSmall();
|
||||
return *this;
|
||||
}
|
||||
@@ -811,7 +798,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
|
||||
|
||||
// Destroy excess elements and trim the bounds.
|
||||
this->destroy_range(NewEnd, this->end());
|
||||
this->setEnd(NewEnd);
|
||||
this->set_size(RHSSize);
|
||||
|
||||
// Clear the RHS.
|
||||
RHS.clear();
|
||||
@@ -826,7 +813,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
|
||||
if (this->capacity() < RHSSize) {
|
||||
// Destroy current elements.
|
||||
this->destroy_range(this->begin(), this->end());
|
||||
this->setEnd(this->begin());
|
||||
this->set_size(0);
|
||||
CurSize = 0;
|
||||
this->grow(RHSSize);
|
||||
} else if (CurSize) {
|
||||
@@ -839,22 +826,23 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
|
||||
this->begin()+CurSize);
|
||||
|
||||
// Set end.
|
||||
this->setEnd(this->begin()+RHSSize);
|
||||
this->set_size(RHSSize);
|
||||
|
||||
RHS.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Storage for the SmallVector elements which aren't contained in
|
||||
/// SmallVectorTemplateCommon. There are 'N-1' elements here. The remaining '1'
|
||||
/// element is in the base class. This is specialized for the N=1 and N=0 cases
|
||||
/// Storage for the SmallVector elements. This is specialized for the N=0 case
|
||||
/// to avoid allocating unnecessary storage.
|
||||
template <typename T, unsigned N>
|
||||
struct SmallVectorStorage {
|
||||
typename SmallVectorTemplateCommon<T>::U InlineElts[N - 1];
|
||||
AlignedCharArrayUnion<T> InlineElts[N];
|
||||
};
|
||||
template <typename T> struct SmallVectorStorage<T, 1> {};
|
||||
template <typename T> struct SmallVectorStorage<T, 0> {};
|
||||
|
||||
/// We need the storage to be properly aligned even for small-size of 0 so that
|
||||
/// the pointer math in \a SmallVectorTemplateCommon::getFirstEl() is
|
||||
/// well-defined.
|
||||
template <typename T> struct alignas(alignof(T)) SmallVectorStorage<T, 0> {};
|
||||
|
||||
/// This is a 'vector' (really, a variable-sized array), optimized
|
||||
/// for the case when the array is small. It contains some number of elements
|
||||
@@ -865,10 +853,7 @@ template <typename T> struct SmallVectorStorage<T, 0> {};
|
||||
/// Note that this does not attempt to be exception safe.
|
||||
///
|
||||
template <typename T, unsigned N>
|
||||
class SmallVector : public SmallVectorImpl<T> {
|
||||
/// Inline space for elements which aren't stored in the base class.
|
||||
SmallVectorStorage<T, N> Storage;
|
||||
|
||||
class SmallVector : public SmallVectorImpl<T>, SmallVectorStorage<T, N> {
|
||||
public:
|
||||
SmallVector() : SmallVectorImpl<T>(N) {}
|
||||
|
||||
|
||||
@@ -78,6 +78,26 @@ inline bool isAlpha(char C) {
|
||||
/// lowercase letter as classified by "C" locale.
|
||||
inline bool isAlnum(char C) { return isAlpha(C) || isDigit(C); }
|
||||
|
||||
/// Checks whether character \p C is valid ASCII (high bit is zero).
|
||||
inline bool isASCII(char C) { return static_cast<unsigned char>(C) <= 127; }
|
||||
|
||||
/// Checks whether all characters in S are ASCII.
|
||||
inline bool isASCII(wpi::StringRef S) {
|
||||
for (char C : S)
|
||||
if (LLVM_UNLIKELY(!isASCII(C)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Checks whether character \p C is printable.
|
||||
///
|
||||
/// Locale-independent version of the C standard library isprint whose results
|
||||
/// may differ on different platforms.
|
||||
inline bool isPrint(char C) {
|
||||
unsigned char UC = static_cast<unsigned char>(C);
|
||||
return (0x20 <= UC) && (UC <= 0x7E);
|
||||
}
|
||||
|
||||
/// Returns the corresponding lowercase character if \p x is uppercase.
|
||||
inline char toLower(char x) {
|
||||
if (x >= 'A' && x <= 'Z')
|
||||
@@ -109,22 +129,23 @@ inline std::string utohexstr(uint64_t X, bool LowerCase = false) {
|
||||
|
||||
/// Convert buffer \p Input to its hexadecimal representation.
|
||||
/// The returned string is double the size of \p Input.
|
||||
inline std::string toHex(StringRef Input) {
|
||||
inline std::string toHex(StringRef Input, bool LowerCase = false) {
|
||||
static const char *const LUT = "0123456789ABCDEF";
|
||||
const uint8_t Offset = LowerCase ? 32 : 0;
|
||||
size_t Length = Input.size();
|
||||
|
||||
std::string Output;
|
||||
Output.reserve(2 * Length);
|
||||
for (size_t i = 0; i < Length; ++i) {
|
||||
const unsigned char c = Input[i];
|
||||
Output.push_back(LUT[c >> 4]);
|
||||
Output.push_back(LUT[c & 15]);
|
||||
Output.push_back(LUT[c >> 4] | Offset);
|
||||
Output.push_back(LUT[c & 15] | Offset);
|
||||
}
|
||||
return Output;
|
||||
}
|
||||
|
||||
inline std::string toHex(ArrayRef<uint8_t> Input) {
|
||||
return toHex(toStringRef(Input));
|
||||
inline std::string toHex(ArrayRef<uint8_t> Input, bool LowerCase = false) {
|
||||
return toHex(toStringRef(Input), LowerCase);
|
||||
}
|
||||
|
||||
inline uint8_t hexFromNibbles(char MSB, char LSB) {
|
||||
@@ -264,9 +285,13 @@ inline StringRef getOrdinalSuffix(unsigned Val) {
|
||||
}
|
||||
}
|
||||
|
||||
/// PrintEscapedString - Print each character of the specified string, escaping
|
||||
/// it if it is not printable or if it is an escape char.
|
||||
void PrintEscapedString(StringRef Name, raw_ostream &Out);
|
||||
/// Print each character of the specified string, escaping it if it is not
|
||||
/// printable or if it is an escape char.
|
||||
void printEscapedString(StringRef Name, raw_ostream &Out);
|
||||
|
||||
/// Print each character of the specified string, escaping HTML special
|
||||
/// characters.
|
||||
void printHTMLEscaped(StringRef String, raw_ostream &Out);
|
||||
|
||||
/// printLowerCase - Print each character as lowercase if it is uppercase.
|
||||
void printLowerCase(StringRef String, raw_ostream &Out);
|
||||
@@ -377,4 +402,4 @@ inline std::string join_items(Sep Separator, Args &&... Items) {
|
||||
|
||||
} // end namespace wpi
|
||||
|
||||
#endif // LLVM_ADT_STRINGEXTRAS_H
|
||||
#endif // WPIUTIL_WPI_STRINGEXTRAS_H
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
#include "wpi/StringRef.h"
|
||||
#include "wpi/iterator.h"
|
||||
#include "wpi/iterator_range.h"
|
||||
#include "wpi/MemAlloc.h"
|
||||
#include "wpi/PointerLikeTypeTraits.h"
|
||||
#include "wpi/ErrorHandling.h"
|
||||
#include "wpi/deprecated.h"
|
||||
#include "wpi/memory.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
@@ -163,7 +164,7 @@ public:
|
||||
size_t AllocSize = sizeof(StringMapEntry) + KeyLength + 1;
|
||||
|
||||
StringMapEntry *NewItem =
|
||||
static_cast<StringMapEntry*>(CheckedMalloc(AllocSize));
|
||||
static_cast<StringMapEntry*>(safe_malloc(AllocSize));
|
||||
|
||||
// Construct the value.
|
||||
new (NewItem) StringMapEntry(KeyLength, std::forward<InitTy>(InitVals)...);
|
||||
@@ -358,12 +359,6 @@ public:
|
||||
return try_emplace(KV.first, std::move(KV.second));
|
||||
}
|
||||
|
||||
template <typename... ArgsTy>
|
||||
WPI_DEPRECATED("use try_emplace instead")
|
||||
std::pair<iterator, bool> emplace_second(StringRef Key, ArgsTy &&... Args) {
|
||||
return try_emplace(Key, std::forward<ArgsTy>(Args)...);
|
||||
}
|
||||
|
||||
/// Emplace a new element for the specified key into the map if the key isn't
|
||||
/// already in the map. The bool component of the returned pair is true
|
||||
/// if and only if the insertion takes place, and the iterator component of
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace wpi {
|
||||
/// empty - Check if the string is empty.
|
||||
LLVM_NODISCARD
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
bool empty() const noexcept { return size() == 0; }
|
||||
bool empty() const noexcept { return Length == 0; }
|
||||
|
||||
/// size - Get the string size.
|
||||
LLVM_NODISCARD
|
||||
@@ -683,10 +683,7 @@ namespace wpi {
|
||||
/// \returns The split substrings.
|
||||
LLVM_NODISCARD
|
||||
std::pair<StringRef, StringRef> split(char Separator) const {
|
||||
size_t Idx = find(Separator);
|
||||
if (Idx == npos)
|
||||
return std::make_pair(*this, StringRef());
|
||||
return std::make_pair(slice(0, Idx), slice(Idx+1, npos));
|
||||
return split(StringRef(&Separator, 1));
|
||||
}
|
||||
|
||||
/// Split into two substrings around the first occurrence of a separator
|
||||
@@ -707,6 +704,24 @@ namespace wpi {
|
||||
return std::make_pair(slice(0, Idx), slice(Idx + Separator.size(), npos));
|
||||
}
|
||||
|
||||
/// Split into two substrings around the last occurrence of a separator
|
||||
/// string.
|
||||
///
|
||||
/// If \p Separator is in the string, then the result is a pair (LHS, RHS)
|
||||
/// such that (*this == LHS + Separator + RHS) is true and RHS is
|
||||
/// minimal. If \p Separator is not in the string, then the result is a
|
||||
/// pair (LHS, RHS) where (*this == LHS) and (RHS == "").
|
||||
///
|
||||
/// \param Separator - The string to split on.
|
||||
/// \return - The split substrings.
|
||||
LLVM_NODISCARD
|
||||
std::pair<StringRef, StringRef> rsplit(StringRef Separator) const {
|
||||
size_t Idx = rfind(Separator);
|
||||
if (Idx == npos)
|
||||
return std::make_pair(*this, StringRef());
|
||||
return std::make_pair(slice(0, Idx), slice(Idx + Separator.size(), npos));
|
||||
}
|
||||
|
||||
/// Split into substrings around the occurrences of a separator string.
|
||||
///
|
||||
/// Each substring is stored in \p A. If \p MaxSplit is >= 0, at most
|
||||
@@ -754,10 +769,7 @@ namespace wpi {
|
||||
/// \return - The split substrings.
|
||||
LLVM_NODISCARD
|
||||
std::pair<StringRef, StringRef> rsplit(char Separator) const {
|
||||
size_t Idx = rfind(Separator);
|
||||
if (Idx == npos)
|
||||
return std::make_pair(*this, StringRef());
|
||||
return std::make_pair(slice(0, Idx), slice(Idx+1, npos));
|
||||
return rsplit(StringRef(&Separator, 1));
|
||||
}
|
||||
|
||||
/// Return string with consecutive \p Char characters starting from the
|
||||
|
||||
127
wpiutil/src/main/native/include/wpi/SwapByteOrder.h
Normal file
127
wpiutil/src/main/native/include/wpi/SwapByteOrder.h
Normal file
@@ -0,0 +1,127 @@
|
||||
//===- SwapByteOrder.h - Generic and optimized byte swaps -------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares generic and optimized functions to swap the byte order of
|
||||
// an integral type.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef WPIUTIL_WPI_SWAPBYTEORDER_H
|
||||
#define WPIUTIL_WPI_SWAPBYTEORDER_H
|
||||
|
||||
#include "wpi/Compiler.h"
|
||||
#include <cstddef>
|
||||
#include <stdint.h>
|
||||
#if defined(_MSC_VER) && !defined(_DEBUG)
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
namespace wpi {
|
||||
namespace sys {
|
||||
|
||||
/// SwapByteOrder_16 - This function returns a byte-swapped representation of
|
||||
/// the 16-bit argument.
|
||||
inline uint16_t SwapByteOrder_16(uint16_t value) {
|
||||
#if defined(_MSC_VER) && !defined(_DEBUG)
|
||||
// The DLL version of the runtime lacks these functions (bug!?), but in a
|
||||
// release build they're replaced with BSWAP instructions anyway.
|
||||
return _byteswap_ushort(value);
|
||||
#else
|
||||
uint16_t Hi = value << 8;
|
||||
uint16_t Lo = value >> 8;
|
||||
return Hi | Lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// SwapByteOrder_32 - This function returns a byte-swapped representation of
|
||||
/// the 32-bit argument.
|
||||
inline uint32_t SwapByteOrder_32(uint32_t value) {
|
||||
#if defined(__llvm__) || (LLVM_GNUC_PREREQ(4, 3, 0) && !defined(__ICC))
|
||||
return __builtin_bswap32(value);
|
||||
#elif defined(_MSC_VER) && !defined(_DEBUG)
|
||||
return _byteswap_ulong(value);
|
||||
#else
|
||||
uint32_t Byte0 = value & 0x000000FF;
|
||||
uint32_t Byte1 = value & 0x0000FF00;
|
||||
uint32_t Byte2 = value & 0x00FF0000;
|
||||
uint32_t Byte3 = value & 0xFF000000;
|
||||
return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// SwapByteOrder_64 - This function returns a byte-swapped representation of
|
||||
/// the 64-bit argument.
|
||||
inline uint64_t SwapByteOrder_64(uint64_t value) {
|
||||
#if defined(__llvm__) || (LLVM_GNUC_PREREQ(4, 3, 0) && !defined(__ICC))
|
||||
return __builtin_bswap64(value);
|
||||
#elif defined(_MSC_VER) && !defined(_DEBUG)
|
||||
return _byteswap_uint64(value);
|
||||
#else
|
||||
uint64_t Hi = SwapByteOrder_32(uint32_t(value));
|
||||
uint32_t Lo = SwapByteOrder_32(uint32_t(value >> 32));
|
||||
return (Hi << 32) | Lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline unsigned char getSwappedBytes(unsigned char C) { return C; }
|
||||
inline signed char getSwappedBytes(signed char C) { return C; }
|
||||
inline char getSwappedBytes(char C) { return C; }
|
||||
|
||||
inline unsigned short getSwappedBytes(unsigned short C) { return SwapByteOrder_16(C); }
|
||||
inline signed short getSwappedBytes( signed short C) { return SwapByteOrder_16(C); }
|
||||
|
||||
inline unsigned int getSwappedBytes(unsigned int C) { return SwapByteOrder_32(C); }
|
||||
inline signed int getSwappedBytes( signed int C) { return SwapByteOrder_32(C); }
|
||||
|
||||
#if __LONG_MAX__ == __INT_MAX__
|
||||
inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_32(C); }
|
||||
inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_32(C); }
|
||||
#elif __LONG_MAX__ == __LONG_LONG_MAX__
|
||||
inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_64(C); }
|
||||
inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_64(C); }
|
||||
#else
|
||||
#error "Unknown long size!"
|
||||
#endif
|
||||
|
||||
inline unsigned long long getSwappedBytes(unsigned long long C) {
|
||||
return SwapByteOrder_64(C);
|
||||
}
|
||||
inline signed long long getSwappedBytes(signed long long C) {
|
||||
return SwapByteOrder_64(C);
|
||||
}
|
||||
|
||||
inline float getSwappedBytes(float C) {
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} in, out;
|
||||
in.f = C;
|
||||
out.i = SwapByteOrder_32(in.i);
|
||||
return out.f;
|
||||
}
|
||||
|
||||
inline double getSwappedBytes(double C) {
|
||||
union {
|
||||
uint64_t i;
|
||||
double d;
|
||||
} in, out;
|
||||
in.d = C;
|
||||
out.i = SwapByteOrder_64(in.i);
|
||||
return out.d;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void swapByteOrder(T &Value) {
|
||||
Value = getSwappedBytes(Value);
|
||||
}
|
||||
|
||||
} // end namespace sys
|
||||
} // end namespace wpi
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "wpi/SmallVector.h"
|
||||
#include "wpi/StringRef.h"
|
||||
#include "wpi/ErrorHandling.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
154
wpiutil/src/main/native/include/wpi/VersionTuple.h
Normal file
154
wpiutil/src/main/native/include/wpi/VersionTuple.h
Normal file
@@ -0,0 +1,154 @@
|
||||
//===- VersionTuple.h - Version Number Handling -----------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Defines the llvm::VersionTuple class, which represents a version in
|
||||
/// the form major[.minor[.subminor]].
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef WPIUTIL_WPI_VERSIONTUPLE_H
|
||||
#define WPIUTIL_WPI_VERSIONTUPLE_H
|
||||
|
||||
#include "wpi/StringRef.h"
|
||||
#include "wpi/optional.h"
|
||||
#include "wpi/raw_ostream.h"
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/// Represents a version number in the form major[.minor[.subminor[.build]]].
|
||||
class VersionTuple {
|
||||
unsigned Major : 32;
|
||||
|
||||
unsigned Minor : 31;
|
||||
unsigned HasMinor : 1;
|
||||
|
||||
unsigned Subminor : 31;
|
||||
unsigned HasSubminor : 1;
|
||||
|
||||
unsigned Build : 31;
|
||||
unsigned HasBuild : 1;
|
||||
|
||||
public:
|
||||
VersionTuple()
|
||||
: Major(0), Minor(0), HasMinor(false), Subminor(0), HasSubminor(false),
|
||||
Build(0), HasBuild(false) {}
|
||||
|
||||
explicit VersionTuple(unsigned Major)
|
||||
: Major(Major), Minor(0), HasMinor(false), Subminor(0),
|
||||
HasSubminor(false), Build(0), HasBuild(false) {}
|
||||
|
||||
explicit VersionTuple(unsigned Major, unsigned Minor)
|
||||
: Major(Major), Minor(Minor), HasMinor(true), Subminor(0),
|
||||
HasSubminor(false), Build(0), HasBuild(false) {}
|
||||
|
||||
explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor)
|
||||
: Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
|
||||
HasSubminor(true), Build(0), HasBuild(false) {}
|
||||
|
||||
explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor,
|
||||
unsigned Build)
|
||||
: Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
|
||||
HasSubminor(true), Build(Build), HasBuild(true) {}
|
||||
|
||||
/// Determine whether this version information is empty
|
||||
/// (e.g., all version components are zero).
|
||||
bool empty() const {
|
||||
return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0;
|
||||
}
|
||||
|
||||
/// Retrieve the major version number.
|
||||
unsigned getMajor() const { return Major; }
|
||||
|
||||
/// Retrieve the minor version number, if provided.
|
||||
optional<unsigned> getMinor() const {
|
||||
if (!HasMinor)
|
||||
return nullopt;
|
||||
return Minor;
|
||||
}
|
||||
|
||||
/// Retrieve the subminor version number, if provided.
|
||||
optional<unsigned> getSubminor() const {
|
||||
if (!HasSubminor)
|
||||
return nullopt;
|
||||
return Subminor;
|
||||
}
|
||||
|
||||
/// Retrieve the build version number, if provided.
|
||||
optional<unsigned> getBuild() const {
|
||||
if (!HasBuild)
|
||||
return nullopt;
|
||||
return Build;
|
||||
}
|
||||
|
||||
/// Determine if two version numbers are equivalent. If not
|
||||
/// provided, minor and subminor version numbers are considered to be zero.
|
||||
friend bool operator==(const VersionTuple &X, const VersionTuple &Y) {
|
||||
return X.Major == Y.Major && X.Minor == Y.Minor &&
|
||||
X.Subminor == Y.Subminor && X.Build == Y.Build;
|
||||
}
|
||||
|
||||
/// Determine if two version numbers are not equivalent.
|
||||
///
|
||||
/// If not provided, minor and subminor version numbers are considered to be
|
||||
/// zero.
|
||||
friend bool operator!=(const VersionTuple &X, const VersionTuple &Y) {
|
||||
return !(X == Y);
|
||||
}
|
||||
|
||||
/// Determine whether one version number precedes another.
|
||||
///
|
||||
/// If not provided, minor and subminor version numbers are considered to be
|
||||
/// zero.
|
||||
friend bool operator<(const VersionTuple &X, const VersionTuple &Y) {
|
||||
return std::tie(X.Major, X.Minor, X.Subminor, X.Build) <
|
||||
std::tie(Y.Major, Y.Minor, Y.Subminor, Y.Build);
|
||||
}
|
||||
|
||||
/// Determine whether one version number follows another.
|
||||
///
|
||||
/// If not provided, minor and subminor version numbers are considered to be
|
||||
/// zero.
|
||||
friend bool operator>(const VersionTuple &X, const VersionTuple &Y) {
|
||||
return Y < X;
|
||||
}
|
||||
|
||||
/// Determine whether one version number precedes or is
|
||||
/// equivalent to another.
|
||||
///
|
||||
/// If not provided, minor and subminor version numbers are considered to be
|
||||
/// zero.
|
||||
friend bool operator<=(const VersionTuple &X, const VersionTuple &Y) {
|
||||
return !(Y < X);
|
||||
}
|
||||
|
||||
/// Determine whether one version number follows or is
|
||||
/// equivalent to another.
|
||||
///
|
||||
/// If not provided, minor and subminor version numbers are considered to be
|
||||
/// zero.
|
||||
friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) {
|
||||
return !(X < Y);
|
||||
}
|
||||
|
||||
/// Retrieve a string representation of the version number.
|
||||
std::string getAsString() const;
|
||||
|
||||
/// Try to parse the given string as a version number.
|
||||
/// \returns \c true if the string does not match the regular expression
|
||||
/// [0-9]+(\.[0-9]+){0,3}
|
||||
bool tryParse(StringRef string);
|
||||
};
|
||||
|
||||
/// Print a version number.
|
||||
raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V);
|
||||
|
||||
} // end namespace wpi
|
||||
#endif // WPIUTIL_WPI_VERSIONTUPLE_H
|
||||
@@ -202,9 +202,7 @@ template <
|
||||
typename ReferenceT = typename std::conditional<
|
||||
std::is_same<T, typename std::iterator_traits<
|
||||
WrappedIteratorT>::value_type>::value,
|
||||
typename std::iterator_traits<WrappedIteratorT>::reference, T &>::type,
|
||||
// Don't provide these, they are mostly to act as aliases below.
|
||||
typename WrappedTraitsT = std::iterator_traits<WrappedIteratorT>>
|
||||
typename std::iterator_traits<WrappedIteratorT>::reference, T &>::type>
|
||||
class iterator_adaptor_base
|
||||
: public iterator_facade_base<DerivedT, IteratorCategoryT, T,
|
||||
DifferenceTypeT, PointerT, ReferenceT> {
|
||||
@@ -288,7 +286,7 @@ template <typename WrappedIteratorT,
|
||||
decltype(**std::declval<WrappedIteratorT>())>::type>
|
||||
struct pointee_iterator
|
||||
: iterator_adaptor_base<
|
||||
pointee_iterator<WrappedIteratorT>, WrappedIteratorT,
|
||||
pointee_iterator<WrappedIteratorT, T>, WrappedIteratorT,
|
||||
typename std::iterator_traits<WrappedIteratorT>::iterator_category,
|
||||
T> {
|
||||
pointee_iterator() = default;
|
||||
@@ -311,8 +309,10 @@ make_pointee_range(RangeT &&Range) {
|
||||
template <typename WrappedIteratorT,
|
||||
typename T = decltype(&*std::declval<WrappedIteratorT>())>
|
||||
class pointer_iterator
|
||||
: public iterator_adaptor_base<pointer_iterator<WrappedIteratorT>,
|
||||
WrappedIteratorT, T> {
|
||||
: public iterator_adaptor_base<
|
||||
pointer_iterator<WrappedIteratorT, T>, WrappedIteratorT,
|
||||
typename std::iterator_traits<WrappedIteratorT>::iterator_category,
|
||||
T> {
|
||||
mutable T Ptr;
|
||||
|
||||
public:
|
||||
|
||||
@@ -59,9 +59,10 @@ template <typename T> iterator_range<T> make_range(std::pair<T, T> p) {
|
||||
return iterator_range<T>(std::move(p.first), std::move(p.second));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
iterator_range<decltype(begin(std::declval<T>()))> drop_begin(T &&t, int n) {
|
||||
return make_range(std::next(begin(t), n), end(t));
|
||||
template <typename T>
|
||||
iterator_range<decltype(adl_begin(std::declval<T>()))> drop_begin(T &&t,
|
||||
int n) {
|
||||
return make_range(std::next(adl_begin(t), n), adl_end(t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef WPIUTIL_WPI_MEMORY_H_
|
||||
#define WPIUTIL_WPI_MEMORY_H_
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/**
|
||||
* Wrapper around std::calloc that calls std::terminate on out of memory.
|
||||
* @param num number of objects to allocate
|
||||
* @param size number of bytes per object to allocate
|
||||
* @return Pointer to beginning of newly allocated memory.
|
||||
*/
|
||||
void* CheckedCalloc(size_t num, size_t size);
|
||||
|
||||
/**
|
||||
* Wrapper around std::malloc that calls std::terminate on out of memory.
|
||||
* @param size number of bytes to allocate
|
||||
* @return Pointer to beginning of newly allocated memory.
|
||||
*/
|
||||
void* CheckedMalloc(size_t size);
|
||||
|
||||
/**
|
||||
* Wrapper around std::realloc that calls std::terminate on out of memory.
|
||||
* @param ptr memory previously allocated
|
||||
* @param size number of bytes to allocate
|
||||
* @return Pointer to beginning of newly allocated memory.
|
||||
*/
|
||||
void* CheckedRealloc(void* ptr, size_t size);
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
#endif // WPIUTIL_WPI_MEMORY_H_
|
||||
@@ -34,7 +34,9 @@ class FormattedBytes;
|
||||
|
||||
namespace sys {
|
||||
namespace fs {
|
||||
enum FileAccess : unsigned;
|
||||
enum OpenFlags : unsigned;
|
||||
enum CreationDisposition : unsigned;
|
||||
} // end namespace fs
|
||||
} // end namespace sys
|
||||
|
||||
@@ -239,7 +241,7 @@ public:
|
||||
raw_ostream &write_hex(unsigned long long N);
|
||||
|
||||
/// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
|
||||
/// satisfy std::isprint into an escape sequence.
|
||||
/// satisfy wpi::isPrint into an escape sequence.
|
||||
raw_ostream &write_escaped(StringRef Str, bool UseHexEscapes = false);
|
||||
|
||||
raw_ostream &write(unsigned char C);
|
||||
@@ -389,12 +391,18 @@ class raw_fd_ostream : public raw_pwrite_stream {
|
||||
int FD;
|
||||
bool ShouldClose;
|
||||
|
||||
bool SupportsSeeking;
|
||||
|
||||
#ifdef _WIN32
|
||||
/// True if this fd refers to a Windows console device. Mintty and other
|
||||
/// terminal emulators are TTYs, but they are not consoles.
|
||||
bool IsWindowsConsole = false;
|
||||
#endif
|
||||
|
||||
std::error_code EC;
|
||||
|
||||
uint64_t pos;
|
||||
|
||||
bool SupportsSeeking;
|
||||
|
||||
/// See raw_ostream::write_impl.
|
||||
void write_impl(const char *Ptr, size_t Size) override;
|
||||
|
||||
@@ -419,15 +427,22 @@ public:
|
||||
/// \p Flags allows optional flags to control how the file will be opened.
|
||||
///
|
||||
/// As a special case, if Filename is "-", then the stream will use
|
||||
/// STDOUT_FILENO instead of opening a file. Note that it will still consider
|
||||
/// itself to own the file descriptor. In particular, it will close the
|
||||
/// file descriptor when it is done (this is necessary to detect
|
||||
/// output errors).
|
||||
/// STDOUT_FILENO instead of opening a file. This will not close the stdout
|
||||
/// descriptor.
|
||||
raw_fd_ostream(StringRef Filename, std::error_code &EC);
|
||||
raw_fd_ostream(StringRef Filename, std::error_code &EC,
|
||||
sys::fs::CreationDisposition Disp);
|
||||
raw_fd_ostream(StringRef Filename, std::error_code &EC,
|
||||
sys::fs::FileAccess Access);
|
||||
raw_fd_ostream(StringRef Filename, std::error_code &EC,
|
||||
sys::fs::OpenFlags Flags);
|
||||
raw_fd_ostream(StringRef Filename, std::error_code &EC,
|
||||
sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
|
||||
sys::fs::OpenFlags Flags);
|
||||
|
||||
/// FD is the file descriptor that this writes to. If ShouldClose is true,
|
||||
/// this closes the file when the stream is destroyed.
|
||||
/// this closes the file when the stream is destroyed. If FD is for stdout or
|
||||
/// stderr, it will not be closed.
|
||||
raw_fd_ostream(int fd, bool shouldClose, bool unbuffered=false);
|
||||
|
||||
~raw_fd_ostream() override;
|
||||
@@ -653,6 +668,8 @@ class buffer_ostream : public raw_svector_ostream {
|
||||
raw_ostream &OS;
|
||||
SmallVector<char, 0> Buffer;
|
||||
|
||||
virtual void anchor() override;
|
||||
|
||||
public:
|
||||
buffer_ostream(raw_ostream &OS) : raw_svector_ostream(Buffer), OS(OS) {}
|
||||
~buffer_ostream() override { OS << str(); }
|
||||
|
||||
Reference in New Issue
Block a user