carb/RString.h

File members: carb/RString.h

// Copyright (c) 2021-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 "Defines.h"

#define RSTRINGENUM_FROM_RSTRING_H
#include "RStringEnum.inl"
#undef RSTRINGENUM_FROM_RSTRING_H

#include <memory> // for std::owner_before
#include <ostream> // for std::basic_ostream
#include <stdint.h>
#include <string>
#include <typeindex> // for std::hash

namespace carb
{

enum class RStringOp
{
    eRegister,

    eFindExisting,
};

// conversion to 'unsigned int:31' from 'uint32_t {aka unsigned int}' may alter its value
CARB_IGNOREWARNING_GNUC_WITH_PUSH("-Wconversion")

namespace detail
{
struct RStringBase
{
    CARB_VIZ uint32_t m_stringId : 31;
    unsigned m_uncased : 1;

    constexpr RStringBase(uint32_t stringId = 0, bool uncased = false) noexcept
        : m_stringId(stringId), m_uncased(uncased)
    {
    }
};

// NOTE: In order to satisfy the StandardLayoutType named requirement (required for ABI safety), all non-static data
// members and bit-fields must be declared in the same class. As such, this class must match RStringBase, but cannot
// inherit from RStringBase.
struct RStringKeyBase
{
    CARB_VIZ uint32_t m_stringId : 31;
    unsigned m_uncased : 1;
    CARB_VIZ int32_t m_number;

    constexpr RStringKeyBase(uint32_t stringId = 0, bool uncased = false, int32_t number = 0) noexcept
        : m_stringId(stringId), m_uncased(uncased), m_number(number)
    {
    }
};

CARB_IGNOREWARNING_GNUC_POP

// Validate assumptions
CARB_ASSERT_INTEROP_SAFE(RStringBase);
CARB_ASSERT_INTEROP_SAFE(RStringKeyBase);
static_assert(offsetof(RStringKeyBase, m_number) == sizeof(RStringBase), "Offset error");

template <bool Uncased, class Base = RStringBase>
class RStringTraits : protected Base
{
public:
    static constexpr bool IsUncased = Uncased;

    constexpr RStringTraits() noexcept;

    constexpr RStringTraits(eRString staticString) noexcept;

    RStringTraits(const char* str, RStringOp op);

    RStringTraits(const char* str, size_t len, RStringOp op);

    RStringTraits(const std::string& str, RStringOp op);

    RStringTraits(uint32_t stringId) noexcept;

    bool isValid() const noexcept;

    constexpr bool isEmpty() const noexcept;

    constexpr bool isUncased() const noexcept;

    constexpr uint32_t getStringId() const noexcept;

    size_t getHash() const;

    size_t getUncasedHash() const noexcept;

    const char* c_str() const noexcept;

    const char* data() const noexcept;

    size_t length() const noexcept;

#ifndef DOXYGEN_BUILD
    std::string toString() const;
#endif

    bool operator==(const RStringTraits& other) const;

    bool operator!=(const RStringTraits& other) const;

    bool owner_before(const RStringTraits& other) const;

    template <bool OtherUncased, class OtherBase>
    int compare(const RStringTraits<OtherUncased, OtherBase>& other) const;

    int compare(const char* s) const;

    int compare(size_t pos, size_t count, const char* s) const;

    int compare(size_t pos, size_t count, const char* s, size_t len) const;

    int compare(const std::string& s) const;

    int compare(size_t pos, size_t count, const std::string& s) const;
};
} // namespace detail

class RString;
class RStringU;
class RStringKey;
class RStringUKey;

class CARB_VIZ RString final : public detail::RStringTraits<false>
{
    using Base = detail::RStringTraits<false>;

public:
    using Base::IsUncased;

    constexpr RString() noexcept;

    constexpr RString(eRString staticString) noexcept;

    explicit RString(const char* str, RStringOp op = RStringOp::eRegister);

    explicit RString(const char* str, size_t len, RStringOp op = RStringOp::eRegister);

    explicit RString(const std::string& str, RStringOp op = RStringOp::eRegister);

    explicit RString(const RStringKey& other) noexcept;

    RStringU toUncased() const noexcept;

    RString truncate() const noexcept;

    RStringKey toRStringKey(int32_t number = 0) const;

    bool operator==(const RString& other) const noexcept;

    bool operator!=(const RString& other) const noexcept;

    bool owner_before(const RString& other) const noexcept;
};

class CARB_VIZ RStringU final : public detail::RStringTraits<true>
{
    using Base = detail::RStringTraits<true>;

public:
    using Base::IsUncased;

    constexpr RStringU() noexcept;

    constexpr RStringU(eRString staticString) noexcept;

    explicit RStringU(const char* str, RStringOp op = RStringOp::eRegister);

    explicit RStringU(const char* str, size_t len, RStringOp op = RStringOp::eRegister);

    explicit RStringU(const std::string& str, RStringOp op = RStringOp::eRegister);

    explicit RStringU(const RString& other);

    explicit RStringU(const RStringUKey& other);

    RStringU toUncased() const noexcept;

    RStringU truncate() const noexcept;

    RStringUKey toRStringKey(int32_t number = 0) const;

    bool operator==(const RStringU& other) const noexcept;

    bool operator!=(const RStringU& other) const noexcept;

    bool owner_before(const RStringU& other) const noexcept;
};

class CARB_VIZ RStringKey final : public detail::RStringTraits<false, detail::RStringKeyBase>
{
    using Base = detail::RStringTraits<false, detail::RStringKeyBase>;

public:
    using Base::IsUncased;

    constexpr RStringKey() noexcept;

    constexpr RStringKey(eRString staticString, int32_t number = 0) noexcept;

    explicit RStringKey(const char* str, RStringOp op = RStringOp::eRegister);

    RStringKey(int32_t number, const char* str, RStringOp op = RStringOp::eRegister);

    explicit RStringKey(const char* str, size_t len, RStringOp op = RStringOp::eRegister);

    explicit RStringKey(int32_t number, const char* str, size_t len, RStringOp op = RStringOp::eRegister);

    explicit RStringKey(const std::string& str, RStringOp op = RStringOp::eRegister);

    explicit RStringKey(int32_t number, const std::string& str, RStringOp op = RStringOp::eRegister);

    RStringKey(const RString& str, int32_t number = 0);

    RStringUKey toUncased() const noexcept;

    RString truncate() const noexcept;

    bool operator==(const RStringKey& other) const noexcept;

    bool operator!=(const RStringKey& other) const noexcept;

    bool owner_before(const RStringKey& other) const noexcept;

#ifndef DOXYGEN_BUILD // Sphinx warns about Duplicate C++ declaration
    size_t getHash() const;

    size_t getUncasedHash() const noexcept;
#endif

    std::string toString() const;

    int32_t getNumber() const noexcept;

    void setNumber(int32_t num) noexcept;

    int32_t& number() noexcept;

private:
    // Hide these functions since they are incomplete
    using Base::c_str;
    using Base::data;
    using Base::length;
};

class CARB_VIZ RStringUKey final : public detail::RStringTraits<true, detail::RStringKeyBase>
{
    using Base = detail::RStringTraits<true, detail::RStringKeyBase>;

public:
    using Base::IsUncased;

    constexpr RStringUKey() noexcept;

    constexpr RStringUKey(eRString staticString, int32_t number = 0) noexcept;

    RStringUKey(const char* str, RStringOp op = RStringOp::eRegister);

    RStringUKey(int32_t number, const char* str, RStringOp op = RStringOp::eRegister);

    explicit RStringUKey(const char* str, size_t len, RStringOp op = RStringOp::eRegister);

    explicit RStringUKey(int32_t number, const char* str, size_t len, RStringOp op = RStringOp::eRegister);

    explicit RStringUKey(const std::string& str, RStringOp op = RStringOp::eRegister);

    explicit RStringUKey(int32_t number, const std::string& str, RStringOp op = RStringOp::eRegister);

    RStringUKey(const RStringU& str, int32_t number = 0);

    explicit RStringUKey(const RStringKey& other);

    RStringUKey toUncased() const noexcept;

    RStringU truncate() const noexcept;

    bool operator==(const RStringUKey& other) const noexcept;

    bool operator!=(const RStringUKey& other) const noexcept;

    bool owner_before(const RStringUKey& other) const noexcept;

#ifndef DOXYGEN_BUILD // Sphinx warns about Duplicate C++ declaration
    size_t getHash() const;

    size_t getUncasedHash() const noexcept;
#endif

    std::string toString() const;

    int32_t getNumber() const noexcept;

    void setNumber(int32_t num) noexcept;

    int32_t& number() noexcept;

private:
    // Hide these functions since they are incomplete
    using Base::c_str;
    using Base::data;
    using Base::length;
};

// Can use ADL specialization for global operator<< for stream output

template <class CharT, class Traits>
::std::basic_ostream<CharT, Traits>& operator<<(::std::basic_ostream<CharT, Traits>& o, const RString& s)
{
    o << s.c_str();
    return o;
}

template <class CharT, class Traits>
::std::basic_ostream<CharT, Traits>& operator<<(::std::basic_ostream<CharT, Traits>& o, const RStringU& s)
{
    o << s.c_str();
    return o;
}

template <class CharT, class Traits>
::std::basic_ostream<CharT, Traits>& operator<<(::std::basic_ostream<CharT, Traits>& o, const RStringKey& s)
{
    o << s.toString();
    return o;
}

template <class CharT, class Traits>
::std::basic_ostream<CharT, Traits>& operator<<(::std::basic_ostream<CharT, Traits>& o, const RStringUKey& s)
{
    o << s.toString();
    return o;
}

} // namespace carb

// Specializations for std::hash and std::owner_less per type

template <>
struct std::hash<::carb::RString>
{
    size_t operator()(const ::carb::RString& v) const
    {
        return v.getHash();
    }
};

template <>
struct std::owner_less<::carb::RString>
{
    bool operator()(const ::carb::RString& lhs, const ::carb::RString& rhs) const
    {
        return lhs.owner_before(rhs);
    }
};

template <>
struct std::hash<::carb::RStringU>
{
    size_t operator()(const ::carb::RStringU& v) const
    {
        return v.getHash();
    }
};

template <>
struct std::owner_less<::carb::RStringU>
{
    bool operator()(const ::carb::RStringU& lhs, const ::carb::RStringU& rhs) const
    {
        return lhs.owner_before(rhs);
    }
};

template <>
struct std::hash<::carb::RStringKey>
{
    size_t operator()(const ::carb::RStringKey& v) const
    {
        return v.getHash();
    }
};

template <>
struct std::owner_less<::carb::RStringKey>
{
    bool operator()(const ::carb::RStringKey& lhs, const ::carb::RStringKey& rhs) const
    {
        return lhs.owner_before(rhs);
    }
};

template <>
struct std::hash<::carb::RStringUKey>
{
    size_t operator()(const ::carb::RStringUKey& v) const
    {
        return v.getHash();
    }
};

template <>
struct std::owner_less<::carb::RStringUKey>
{
    bool operator()(const ::carb::RStringUKey& lhs, const ::carb::RStringUKey& rhs) const
    {
        return lhs.owner_before(rhs);
    }
};

#include "RString.inl"