omni/detail/VectorDetail.h

File members: omni/detail/VectorDetail.h

// Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "../../carb/cpp/TypeTraits.h"

namespace omni
{

namespace detail
{

template <class T>
using IsLegacyInputIterator =
    ::carb::cpp::conjunction<std::is_copy_constructible<T>,
                             std::is_copy_assignable<T>,
                             std::is_destructible<T>,
                             ::carb::cpp::is_swappable<T>,
                             std::is_reference<decltype(*std::declval<T>())>,
                             std::is_same<decltype(std::declval<T&>()++), T>,
                             std::is_same<bool, decltype(std::declval<T>() == std::declval<T>())>>;

template <class Elem, bool IsEnum = std::is_enum<Elem>::value>
struct UnwrapEnum
{
    using type = std::underlying_type_t<Elem>;
};

template <class Elem>
struct UnwrapEnum<Elem, false>
{
    using type = Elem;
};

template <class Elem>
using UnwrapEnum_t = typename UnwrapEnum<Elem>::type;

// Type discriminator for character types (char/signed char/unsigned char)
template <class T>
struct IsCharacter : std::false_type
{
};

template <>
struct IsCharacter<char> : std::true_type
{
};

template <>
struct IsCharacter<signed char> : std::true_type
{
};

template <>
struct IsCharacter<unsigned char> : std::true_type
{
};

template <class It, class T>
struct FillMemsetIsSafeHelper
{
    using value_type = typename std::iterator_traits<It>::value_type;

    using raw_type = UnwrapEnum_t<T>;
    using raw_value_type = UnwrapEnum_t<value_type>;

    using type = carb::cpp::bool_constant<carb::cpp::conjunction<
        std::is_pointer<It>,
        carb::cpp::disjunction<carb::cpp::conjunction<IsCharacter<raw_type>, IsCharacter<raw_value_type>>,
                               carb::cpp::conjunction<std::is_same<bool, raw_type>, std::is_same<bool, raw_value_type>>>,
        std::is_convertible<T, value_type>>::value>;
};

template <class It, class T>
inline typename FillMemsetIsSafeHelper<It, T>::type FillMemsetIsSafe(const It&, const T&)
{
    return {};
}

// Resolve an iterator class to a pointer
template <class Ptr>
inline auto resolvePtr(Ptr p)
{
    return std::addressof(*p);
}

template <class T>
inline T* resolvePtr(T* p)
{
    return p;
}

template <class It, class Alloc>
inline void implDestroyRange(It first, It last, Alloc& al, std::false_type)
{
    for (; first != last; ++first)
    {
        std::allocator_traits<Alloc>::destroy(al, resolvePtr(first));
    }
}

template <class It, class Alloc>
inline void implDestroyRange(It, It, Alloc&, std::true_type)
{
    // Trivial, nothing to do.
}

template <class Alloc>
inline void destroyRange(typename std::allocator_traits<Alloc>::pointer first,
                         typename std::allocator_traits<Alloc>::pointer last,
                         Alloc& al)
{
    using Val = typename Alloc::value_type;
    implDestroyRange(first, last, al, carb::cpp::bool_constant<std::is_trivially_destructible<Val>::value>{});
}

// The destructor will destroy everything emplaced, unless release() is called before destruction.
template <class It, class Alloc>
class BackoutOnThrow
{
public:
    CARB_PREVENT_COPY(BackoutOnThrow);

    BackoutOnThrow(It dest, Alloc& al) : first(dest), last(dest), al(al)
    {
    }

    ~BackoutOnThrow()
    {
        destroyRange(first, last, al);
    }

    template <class... Args>
    void emplace_back(Args&&... args)
    {
        std::allocator_traits<Alloc>::construct(al, resolvePtr(last), std::forward<Args>(args)...);
        ++last;
    }

    It release()
    {
        first = last;
        return last;
    }

private:
    It first;
    It last;
    Alloc& al;
};

struct GeneralPtrIteratorTag
{
};
struct TriviallyCopyablePtrIteratorTag : GeneralPtrIteratorTag
{
};
struct TrivialPtrIteratorTag : GeneralPtrIteratorTag
{
};

template <class A, class B>
struct IsSameSize : carb::cpp::bool_constant<sizeof(A) == sizeof(B)>
{
};

template <class A, class B>
struct BothOrNeitherBool : carb::cpp::bool_constant<std::is_same<bool, A>::value == std::is_same<bool, B>::value>
{
};

template <class Source, class Dest>
struct ImplPtrCategory
{
    using USource = UnwrapEnum_t<Source>;
    using UDest = UnwrapEnum_t<Dest>;
    using type = std::conditional_t<carb::cpp::conjunction<IsSameSize<USource, UDest>,
                                                           std::is_integral<USource>,
                                                           std::is_integral<UDest>,
                                                           BothOrNeitherBool<USource, UDest>>::value,
                                    TrivialPtrIteratorTag,
                                    GeneralPtrIteratorTag>;
};

template <class Elem>
struct ImplPtrCategory<Elem, Elem>
{
    using type =
        std::conditional_t<std::is_trivially_copyable<Elem>::value,
                           std::conditional_t<std::is_trivial<Elem>::value, TrivialPtrIteratorTag, TriviallyCopyablePtrIteratorTag>,
                           GeneralPtrIteratorTag>;
};

template <class Any>
struct ImplPtrCategory<Any*, const Any*>
{
    using type = TrivialPtrIteratorTag;
};

template <class Any>
struct ImplPtrCategory<Any*, volatile Any*>
{
    using type = TrivialPtrIteratorTag;
};

template <class Any>
struct ImplPtrCategory<Any*, const volatile Any*>
{
    using type = TrivialPtrIteratorTag;
};

template <class Source, class Dest>
inline GeneralPtrIteratorTag getPtrCopyCategory(const Source&, const Dest&)
{
    return {};
}

template <class Source, class Dest>
inline std::conditional_t<std::is_trivially_assignable<Dest&, Source&>::value,
                          typename ImplPtrCategory<std::remove_cv_t<Source>, std::remove_cv_t<Dest>>::type,
                          GeneralPtrIteratorTag>
getPtrCopyCategory(Source* const&, Dest* const&)
{
    return {};
}

template <class Source, class Dest>
inline GeneralPtrIteratorTag getPtrMoveCategory(const Source&, const Dest&)
{
    return {};
}

template <class Source, class Dest>
inline std::conditional_t<std::is_trivially_assignable<Dest&, Source>::value,
                          typename ImplPtrCategory<std::remove_cv_t<Source>, std::remove_cv_t<Dest>>::type,
                          GeneralPtrIteratorTag>
getPtrMoveCategory(Source* const&, Dest* const&)
{
    return {};
}

template <class It>
using UseMemsetValueConstruct = carb::cpp::bool_constant<
    carb::cpp::conjunction<std::is_pointer<It>,
                           std::is_scalar<typename std::iterator_traits<It>::value_type>,
                           carb::cpp::negation<std::is_volatile<typename std::iterator_traits<It>::value_type>>,
                           carb::cpp::negation<std::is_member_pointer<typename std::iterator_traits<It>::value_type>>>::value>;

template <class InIt, class OutIt>
inline OutIt copyMemmove(InIt first, InIt last, OutIt dest)
{
    const char* const firstCh = const_cast<const char*>(reinterpret_cast<const volatile char*>(first));
    const char* const lastCh = const_cast<const char*>(reinterpret_cast<const volatile char*>(last));
    char* const destCh = const_cast<char*>(reinterpret_cast<volatile char*>(dest));
    const auto count = size_t(lastCh - firstCh);
    std::memmove(destCh, firstCh, count);
    return reinterpret_cast<OutIt>(destCh + count);
}

template <class It1, class It2>
inline It2 copyMemmoveReverse(It1 first, It1 last, It2 dest)
{
    const char* const firstCh = const_cast<const char*>(reinterpret_cast<const volatile char*>(first));
    const char* const lastCh = const_cast<const char*>(reinterpret_cast<const volatile char*>(last));
    char* const destCh = const_cast<char*>(reinterpret_cast<volatile char*>(dest));
    const auto count = size_t(lastCh - firstCh);
    return static_cast<It2>(std::memmove(destCh - count, firstCh, count));
}

template <class It, class Diff, class Alloc>
inline It implUninitDefaultConstruct(It first, Diff count, Alloc&, std::true_type)
{
    char* const firstCh = reinterpret_cast<char*>(first);
    char* const lastCh = reinterpret_cast<char*>(first + count);
    std::memset(firstCh, 0, size_t(lastCh - firstCh));
    return first + count;
}

template <class It, class Diff, class Alloc>
inline It implUninitDefaultConstruct(It first, Diff count, Alloc& al, std::false_type)
{
    BackoutOnThrow<It, Alloc> backout{ first, al };
    for (; 0 < count; --count)
    {
        backout.emplace_back();
    }
    return backout.release();
}

template <class It, class Diff, class Alloc>
inline It implUninitFillN(
    const It first, Diff count, const typename std::iterator_traits<It>::value_type& val, Alloc& al, std::false_type)
{
    BackoutOnThrow<It, Alloc> backout{ first, al };
    for (; 0 < count; --count)
    {
        backout.emplace_back(val);
    }
    return backout.release();
}

template <class It, class Diff, class Alloc>
inline It implUninitFillN(
    const It first, Diff count, const typename std::iterator_traits<It>::value_type& val, Alloc&, std::true_type)
{
    std::memset(first, static_cast<unsigned char>(val), static_cast<size_t>(count));
    return first + count;
}

template <class InIt, class FwdIt, class Alloc>
inline FwdIt implUninitCopy(InIt first, InIt last, const FwdIt dest, Alloc& al, GeneralPtrIteratorTag)
{
    BackoutOnThrow<FwdIt, Alloc> backout{ dest, al };
    for (; first != last; ++first)
    {
        backout.emplace_back(*first);
    }
    return backout.release();
}

template <class A, class B, class Alloc>
inline B* implUninitCopy(A* const first, A* const last, B* const dest, Alloc&, TrivialPtrIteratorTag)
{
    return copyMemmove(first, last, dest);
}

template <class InIt, class FwdIt, class Alloc>
inline FwdIt implUninitMove(InIt first, const InIt last, const FwdIt dest, Alloc& al, GeneralPtrIteratorTag)
{
    BackoutOnThrow<FwdIt, Alloc> backout{ dest, al };
    for (; first != last; ++first)
    {
        backout.emplace_back(std::move(*first));
    }
    return backout.release();
}

template <class A, class B, class Alloc>
inline B* implUninitMove(A* const first, A* const last, B* const dest, Alloc&, TrivialPtrIteratorTag)
{
    return copyMemmove(first, last, dest);
}

template <class It, class Diff, class Alloc>
inline It uninitDefaultConstruct(It first, Diff count, Alloc& al)
{
    return implUninitDefaultConstruct(first, count, al, carb::cpp::bool_constant<UseMemsetValueConstruct<It>::value>{});
}

template <class InIt, class FwdIt, class Alloc>
inline FwdIt uninitCopy(const InIt first, const InIt last, FwdIt dest, Alloc& al)
{
    return implUninitCopy(first, last, dest, al, getPtrCopyCategory(first, dest));
}

template <class InIt, class FwdIt, class Alloc>
inline FwdIt uninitMove(const InIt first, const InIt last, FwdIt dest, Alloc& al)
{
    return implUninitMove(first, last, dest, al, getPtrMoveCategory(first, dest));
}

template <class It, class Diff, class Alloc>
inline It uninitFillN(const It first, const Diff count, const typename std::iterator_traits<It>::value_type& val, Alloc& al)
{
    return implUninitFillN(first, count, val, al, FillMemsetIsSafe(first, val));
}

} // namespace detail

} // namespace omni