carb/Version.h

File members: carb/Version.h

// Copyright (c) 2018-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 <cinttypes>
#include <cstdint>
#include <cstdio>
#include <type_traits>

// Note: Ideally this would be in Defines.h, but there is a weird circular dependency:
// Defines.h -> assert/IAssert.h -> Interface.h -> Version.h
#define CARB_ASSERT_INTEROP_SAFE(...)                                                                                  \
    static_assert(std::is_standard_layout<__VA_ARGS__>::value, "Must have standard layout to be interop safe");        \
    static_assert(std::is_trivially_copyable<__VA_ARGS__>::value, "Must be trivially copyable to be interop safe")

namespace carb
{

struct Version
{
    uint32_t major;
    uint32_t minor;
};
CARB_ASSERT_INTEROP_SAFE(Version);

constexpr Version fromHexVersion(uint32_t hexver) noexcept
{
    return Version{ hexver >> 16, hexver & 0xffff };
}

constexpr uint32_t toHexVersion(carb::Version ver) noexcept
{
    return (ver.major << 16) | (ver.minor);
}

constexpr bool operator<(const Version& lhs, const Version& rhs) noexcept
{
    if (lhs.major == rhs.major)
    {
        return lhs.minor < rhs.minor;
    }
    return lhs.major < rhs.major;
}

constexpr bool operator<=(const Version& lhs, const Version& rhs) noexcept
{
    if (lhs.major == rhs.major)
    {
        return lhs.minor <= rhs.minor;
    }
    return lhs.major < rhs.major;
}

constexpr bool operator==(const Version& lhs, const Version& rhs) noexcept
{
    return lhs.major == rhs.major && lhs.minor == rhs.minor;
}

constexpr bool operator!=(const Version& lhs, const Version& rhs) noexcept
{
    return !(lhs == rhs);
}

inline bool isVersionSemanticallyCompatible(const char* name, const Version& minimum, const Version& candidate)
{
    if (minimum.major != candidate.major)
    {
        return false;
    }
    else if (minimum.major == 0)
    {
        // Need to special case when major is equal but zero, then any difference in minor makes them
        // incompatible. See http://semver.org for details.
        // the case of version 0.x (major of 0), we are only going to "warn" the user of possible
        // incompatibility when a user asks for 0.x and we have an implementation 0.y (where y > x).
        // see https://nvidia-omniverse.atlassian.net/browse/CC-249
        if (minimum.minor > candidate.minor)
        {
            return false;
        }
        else if (minimum.minor < candidate.minor && name)
        {
            // using CARB_LOG maybe pointless, as logging may not be set up yet.
            fprintf(stderr,
                    "Warning: Possible version incompatibility. Attempting to load %s with version v%" PRIu32
                    ".%" PRIu32 " against v%" PRIu32 ".%" PRIu32 ".\n",
                    name, candidate.major, candidate.minor, minimum.major, minimum.minor);
        }
    }
    else if (minimum.minor > candidate.minor)
    {
        return false;
    }
    return true;
}

} // namespace carb