/* * Copyright (c) 2025. MadVoxel AG * All rights reserved. */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #pragma once #include #include #include #include #include #include #include namespace OpenVulkano { struct VersionDataCompact { std::array m_versionComponents; VersionDataCompact() = default; VersionDataCompact(const uint32_t major, const uint32_t minor, const uint32_t patch, const uint32_t build) : m_versionComponents({major, minor, patch, build}) {} [[nodiscard]] uint32_t GetComponent(const size_t compId) const { if (m_versionComponents.size() < compId + 1) return 0u; return m_versionComponents[compId]; } }; struct VersionDataExtendedData { std::vector m_versionComponents; std::vector m_tagComponents; uint64_t m_timestamp = 0, m_buildNumber = 0; VersionDataExtendedData() = default; VersionDataExtendedData(const std::string_view& versionString, bool ignoreTags); [[nodiscard]] uint32_t GetComponent(const size_t compId) const { if (m_versionComponents.size() < compId + 1) return 0u; return m_versionComponents[compId]; } std::optional GetCompactVersion(const bool force = false) const { if (!force) { if (m_versionComponents.size() > 3 && m_buildNumber != 0) return {}; if (m_versionComponents.size() > 4 || !m_tagComponents.empty()) return {}; } return VersionDataCompact(GetComponent(0), GetComponent(1), GetComponent(2), m_buildNumber ? m_buildNumber : GetComponent(3)); } }; struct VersionDataExtended { std::array m_leadComponents; std::unique_ptr m_data; VersionDataExtended() : m_leadComponents({ UINT32_MAX, UINT32_MAX }), m_data(std::make_unique()) {} VersionDataExtended(const VersionDataExtended& o) : m_leadComponents(o.m_leadComponents), m_data(std::make_unique(*o.m_data)) {} VersionDataExtended(VersionDataExtended&& o) noexcept : m_leadComponents(o.m_leadComponents), m_data(std::move(o.m_data)) { o.m_leadComponents = { 0, 0 }; } [[maybe_unused]] VersionDataExtended(VersionDataExtendedData&& data) : m_leadComponents({ UINT32_MAX, UINT32_MAX }), m_data(std::make_unique(std::move(data))) {} VersionDataExtended& operator=(VersionDataExtended&& o) noexcept { m_leadComponents = std::move(o.m_leadComponents); m_data = std::move(o.m_data); o.m_data = nullptr; o.m_leadComponents = { 0, 0 }; return *this; } VersionDataExtended& operator=(const VersionDataExtended& o) { m_leadComponents = o.m_leadComponents; m_data = std::make_unique(*o.m_data); return *this; } }; union VersionData { VersionDataCompact compact; VersionDataExtended extended; VersionData() { new (&compact)VersionDataCompact(); } VersionData(VersionData&& o) noexcept { if (o.IsExtended()) { new (&extended) VersionDataExtended(std::move(o.extended)); } else compact = o.compact; } VersionData(const VersionData& o) { if (o.IsExtended()) new (&extended)VersionDataExtended(o.extended); else compact = o.compact; } [[maybe_unused]] VersionData(const VersionDataCompact& other) { new (&compact)VersionDataCompact(other); } VersionData(VersionDataCompact&& other) { new (&compact) VersionDataCompact(std::move(other)); } [[maybe_unused]] VersionData(const VersionDataExtended& other) { new (&extended)VersionDataExtended(other); } [[maybe_unused]] VersionData(VersionDataExtended&& other) { new (&extended) VersionDataExtended(std::move(other)); } ~VersionData() { if (IsExtended()) { extended.~VersionDataExtended(); } } VersionData& operator =(const VersionDataCompact& other) { compact = other; return *this; } VersionData& operator =(VersionDataExtendedData&& other) { extended.m_leadComponents = { UINT32_MAX, UINT32_MAX }; extended.m_data = std::make_unique(std::move(other)); return *this; } VersionData& operator =(VersionData&& other) noexcept { Clear(); if (other.IsExtended()) { extended.m_leadComponents = { UINT32_MAX, UINT32_MAX }; extended.m_data = std::move(other.extended.m_data); } else { compact = other.compact; } return *this; } VersionData& operator =(const VersionData& other) { Clear(); if (other.IsExtended()) extended = other.extended; else compact = other.compact; return *this; } bool IsExtended() const { return compact.m_versionComponents[0] > INT32_MAX; } uint64_t GetTimestamp() const { if (IsExtended()) return extended.m_data->m_timestamp; return 0; } private: void Clear() { if (IsExtended()) { extended.~VersionDataExtended(); } new (&compact)VersionDataCompact(); } }; class Version { VersionData m_data; std::string m_versionString; bool IsExtendedData() const { return m_data.compact.m_versionComponents[0] > INT32_MAX; } public: Version() : Version(0, 0) {} template explicit Version(T major) requires std::is_integral_v : Version(major, 0) {} Version(uint32_t major, uint32_t minor, uint32_t patch = 0, uint32_t build = 0); Version(std::string_view versionString, bool ignoreTags = false); explicit Version(double version); Version(const Version& other) = default; Version(Version&& other) = default; Version& operator =(const Version& other) = default; Version& operator=(Version&& other) = default; [[nodiscard]] uint32_t GetComponent(const size_t compId) const { if (IsExtendedData()) return m_data.extended.m_data->GetComponent(compId); return m_data.compact.GetComponent(compId); } [[nodiscard]] uint32_t Major() const { return GetComponent(0); } [[nodiscard]] uint32_t Minor() const { return GetComponent(1); } [[nodiscard]] uint32_t Patch() const { return GetComponent(2); } [[nodiscard]] uint64_t Build() const { if (IsExtendedData()) return m_data.extended.m_data->m_buildNumber; return m_data.compact.m_versionComponents[3]; } [[nodiscard]] uint64_t Timestamp() const { return m_data.GetTimestamp(); } //[[nodiscard]] bool IsPreRelease() const { return m_preRelease; } [[nodiscard]] std::span GetTags() const { if (IsExtendedData()) return m_data.extended.m_data->m_tagComponents; return {}; } [[nodiscard]] std::span GetVersionComponents() const { if (IsExtendedData()) return m_data.extended.m_data->m_versionComponents; return m_data.compact.m_versionComponents; } //region Conversion operators [[nodiscard]] explicit operator uint32_t() const { return (Major() << 20) | ((Minor() & 0x3FF) << 10) | (Patch() & 0x3FF); } [[nodiscard]] operator const std::string&() const { return m_versionString; } [[nodiscard]] const std::string& String() const { return m_versionString; } //endregion //region Comparison operators [[nodiscard]] bool operator ==(const Version& rhs) const { return Compare(rhs) == 0; } [[nodiscard]] bool operator !=(const Version& rhs) const { return Compare(rhs); } [[nodiscard]] bool operator < (const Version& rhs) const { return Compare(rhs) < 0; } [[nodiscard]] bool operator <=(const Version& rhs) const { return Compare(rhs) < 1; } [[nodiscard]] bool operator > (const Version& rhs) const { return Compare(rhs) > 0; } [[nodiscard]] bool operator >=(const Version& rhs) const { return Compare(rhs) > -1; } //endregion private: [[nodiscard]] int Compare(const Version& other) const; [[nodiscard]] int CompareBuildNr(const Version& other) const; [[nodiscard]] int CompareTimestamp(const Version& other) const; [[nodiscard]] int CompareComponents(const Version& other) const; }; [[nodiscard]] [[maybe_unused]] static Version operator""_v(const char* str) { return { str }; } [[nodiscard]] [[maybe_unused]] static Version operator""_v(const char* str, size_t c) { return { std::string_view(str, c) }; } [[nodiscard]] [[maybe_unused]] static Version operator""_version(const char* str) { return { str }; } [[nodiscard]] [[maybe_unused]] static Version operator""_version(const char* str, size_t c) { return { std::string_view(str, c) }; } [[nodiscard]] [[maybe_unused]] static Version operator""_v(const long double val) { return Version(static_cast(val)); } [[nodiscard]] [[maybe_unused]] static Version operator""_version(const long double val) { return Version(static_cast(val)); } }