diff options
author | Hartmnt <hartmunt@protonmail.com> | 2022-08-29 19:13:59 +0300 |
---|---|---|
committer | Hartmnt <hartmunt@protonmail.com> | 2022-09-08 11:45:47 +0300 |
commit | 79762ce55e3bebf903a54f57adbefb517de537a0 (patch) | |
tree | ac79f144f40dad4fc8c71697f1ac6ced81a00132 /src/Version.h | |
parent | 7e959985ad713b919c4dbca0ab9bae5aa6dcc1ed (diff) |
FIX(client, server): Fix patch versions > 255
Previously, the Mumble version was encoded with a uint32
in the network protocol reserving 2 bytes for the major
component and 1 byte for each minor and patch.
The versioning format was changed to include a build
number in the patch field. With a recent update (1.4.274)
the patch field exceeded 255 for the first time and broke
the protocol version.
This commit completely reworks how the version is stored
internally and transfered with the network protocol.
The new version is a uint64 and consists of 4 fields with
2 bytes each. This allows each version component to reach
up to 65535. Furthermore, all instances of integer version
types have been replaced with the alias Version::full_t for
a better abstraction. Version literals have been replaced by
Version::fromComponents calls.
Fixes #5827
Diffstat (limited to 'src/Version.h')
-rw-r--r-- | src/Version.h | 99 |
1 files changed, 84 insertions, 15 deletions
diff --git a/src/Version.h b/src/Version.h index 7e6d07e7b..3dab5893e 100644 --- a/src/Version.h +++ b/src/Version.h @@ -7,8 +7,10 @@ #define MUMBLE_VERSION_H_ #include <cstdint> +#include <limits> -#include <QtCore/QString> +#include <QString> +#include <QVariant> #define MUMXTEXT(X) #X #define MUMTEXT(X) MUMXTEXT(X) @@ -17,26 +19,93 @@ namespace Version { -using exact_width_mumble_raw_version_t = std::uint32_t; -using mumble_raw_version_t = unsigned int; +enum class CompareMode { AtLeast, LessThan }; -static_assert(sizeof(exact_width_mumble_raw_version_t) <= sizeof(mumble_raw_version_t), - "Invalid scalar type used to represent Mumble versions"); +// +// The mumble version format (v2) is a uint64: +// major minor patch reserved/unused +// 0xFFFF 0xFFFF 0xFFFF 0xFFFF +// (big-endian) +// -constexpr mumble_raw_version_t UNKNOWN = 0; +using full_t = std::uint64_t; +using component_t = std::uint16_t; -unsigned int getRaw(const QString &version = QLatin1String(MUMTEXT(MUMBLE_VERSION))); -QString toString(mumble_raw_version_t version); -bool get(int *major, int *minor, int *patch, const QString &version = QLatin1String(MUMTEXT(MUMBLE_VERSION))); +constexpr full_t OFFSET_MAJOR = 48; +constexpr full_t OFFSET_MINOR = 32; +constexpr full_t OFFSET_PATCH = 16; +constexpr full_t OFFSET_UNUSED = 0; +constexpr full_t FIELD_MASK = 0xFFFF; +constexpr full_t FIELD_MAJOR = (FIELD_MASK << Version::OFFSET_MAJOR); +constexpr full_t FIELD_MINOR = (FIELD_MASK << Version::OFFSET_MINOR); +constexpr full_t FIELD_PATCH = (FIELD_MASK << Version::OFFSET_PATCH); +constexpr full_t FIELD_UNUSED = (FIELD_MASK << Version::OFFSET_UNUSED); -constexpr Version::mumble_raw_version_t toRaw(int major, int minor, int patch) { - return (major << 16) | (minor << 8) | patch; +constexpr full_t UNKNOWN = 0; + +QString getRelease(); +full_t fromString(const QString &version); +full_t fromConfig(const QVariant &config); +QString toString(full_t version); +QString toConfigString(full_t version); +bool getComponents(Version::component_t &major, Version::component_t &minor, Version::component_t &patch, + const QString &version); + +constexpr Version::component_t getMajor(Version::full_t version) { + return static_cast< Version::component_t >((version & Version::FIELD_MAJOR) >> Version::OFFSET_MAJOR); +} + +constexpr Version::component_t getMinor(Version::full_t version) { + return static_cast< Version::component_t >((version & Version::FIELD_MINOR) >> Version::OFFSET_MINOR); +} + +constexpr Version::component_t getPatch(Version::full_t version) { + return static_cast< Version::component_t >((version & Version::FIELD_PATCH) >> Version::OFFSET_PATCH); +} + +constexpr Version::full_t fromComponents(Version::component_t major, Version::component_t minor, + Version::component_t patch) { + return ((static_cast< full_t >(major) << Version::OFFSET_MAJOR) + | (static_cast< full_t >(minor) << Version::OFFSET_MINOR) + | (static_cast< full_t >(patch) << Version::OFFSET_PATCH)); +} + +constexpr full_t get() { + return fromComponents(MUMBLE_VERSION_MAJOR, MUMBLE_VERSION_MINOR, MUMBLE_VERSION_PATCH); +} + +constexpr void getComponents(Version::component_t &major, Version::component_t &minor, Version::component_t &patch, + Version::full_t version = get()) { + major = Version::getMajor(version); + minor = Version::getMinor(version); + patch = Version::getPatch(version); +} + +// +// Legacy versions: These versions are kept around for backward compatibility, but +// have since been replaced by other version formats. +// +// Mumble legacy version format (v1) is a uint32: +// major minor patch +// 0xFFFF 0xFF 0xFF +// (big-endian) +// + +constexpr Version::full_t fromLegacyVersion(std::uint32_t version) { + return fromComponents((version & 0xFFFF0000) >> 16, (version & 0xFF00) >> 8, version & 0xFF); } -constexpr void fromRaw(unsigned int version, int *major, int *minor, int *patch) { - *major = (version & 0xFFFF0000) >> 16; - *minor = (version & 0xFF00) >> 8; - *patch = (version & 0xFF); +constexpr std::uint32_t toLegacyVersion(Version::full_t version) { + // If any of the version components exceeds their allowed value range, they will + // be truncated to the highest representable value + return ((std::min(static_cast< std::uint32_t >(Version::getMajor(version)), + static_cast< std::uint32_t >(std::numeric_limits< std::uint16_t >::max())) + << 16) + | (std::min(static_cast< std::uint32_t >(Version::getMinor(version)), + static_cast< std::uint32_t >(std::numeric_limits< std::uint8_t >::max())) + << 8) + | std::min(static_cast< std::uint32_t >(Version::getPatch(version)), + static_cast< std::uint32_t >(std::numeric_limits< std::uint8_t >::max()))); } }; // namespace Version |