From 8fba6005c2e8613cebfac2c3394694881ab6e67a Mon Sep 17 00:00:00 2001 From: DevMiner Date: Mon, 2 Oct 2023 00:40:53 +0200 Subject: [PATCH] refactor: feature flags --- src/network/connection.cpp | 17 ++-- src/network/connection.h | 8 +- src/network/featureflags.h | 169 ++++++++++++++++++------------------- 3 files changed, 101 insertions(+), 93 deletions(-) diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 9daa0dea6..f0ce876c0 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -127,7 +127,8 @@ bool Connection::endPacket() { } bool Connection::beginBundle() { - MUST_TRANSFER_BOOL(m_ServerFeatures.has(ServerFeatures::PROTOCOL_BUNDLE_SUPPORT)); + MUST_TRANSFER_BOOL(m_ServerFeatures.has(EServerFeatureFlags::PROTOCOL_BUNDLE_SUPPORT + )); memset(m_Bundle, 0, sizeof(m_Bundle)); m_BundleInnerPosition = m_Bundle; @@ -142,7 +143,8 @@ bool Connection::beginBundle() { } bool Connection::sendBundle() { - MUST_TRANSFER_BOOL(m_ServerFeatures.has(ServerFeatures::PROTOCOL_BUNDLE_SUPPORT)); + MUST_TRANSFER_BOOL(m_ServerFeatures.has(EServerFeatureFlags::PROTOCOL_BUNDLE_SUPPORT + )); MUST_TRANSFER_BOOL(!m_IsBundle); MUST_TRANSFER_BOOL((m_BundlePacketCount > 0)); @@ -409,7 +411,8 @@ void Connection::sendFeatureFlags() { MUST(sendPacketType(PACKET_FEATURE_FLAGS)); MUST(sendPacketNumber()); - MUST(write(FirmwareFeatures::flags.data(), FirmwareFeatures::flags.size())); + auto packedFeatures = m_FirmwareFeatures.pack(); + MUST(write(packedFeatures.data(), packedFeatures.size())); MUST(endPacket()); } @@ -608,7 +611,7 @@ void Connection::searchForServer() { m_Connected = true; m_FeatureFlagsRequestAttempts = 0; - m_ServerFeatures = ServerFeatures{}; + m_ServerFeatures = FeatureFlags(); statusManager.setStatus(SlimeVR::Status::SERVER_CONNECTING, false); ledManager.off(); @@ -732,11 +735,13 @@ void Connection::update() { bool hadFlags = m_ServerFeatures.isAvailable(); uint32_t flagsLength = len - 12; - m_ServerFeatures = ServerFeatures::from(&m_Packet[12], flagsLength); + m_ServerFeatures + = FeatureFlags(&m_Packet[12], flagsLength); if (!hadFlags) { #if PACKET_BUNDLING != PACKET_BUNDLING_DISABLED - if (m_ServerFeatures.has(ServerFeatures::PROTOCOL_BUNDLE_SUPPORT)) { + if (m_ServerFeatures.has(EServerFeatureFlags::PROTOCOL_BUNDLE_SUPPORT + )) { m_Logger.debug("Server supports packet bundling"); } #endif diff --git a/src/network/connection.h b/src/network/connection.h index 0088c12b8..f9761505a 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -37,6 +37,9 @@ namespace Network { class Connection { public: + Connection() + : m_FirmwareFeatures(m_EnabledFirmwareFeatures) {} + void searchForServer(); void update(); void reset(); @@ -107,7 +110,7 @@ class Connection { ); #endif - const ServerFeatures& getServerFeatureFlags() { return m_ServerFeatures; } + const auto getServerFeatureFlags() { return m_ServerFeatures; } bool beginBundle(); bool endBundle(); @@ -166,7 +169,8 @@ class Connection { uint8_t m_FeatureFlagsRequestAttempts = 0; unsigned long m_FeatureFlagsRequestTimestamp = millis(); - ServerFeatures m_ServerFeatures{}; + FeatureFlags m_ServerFeatures; + FeatureFlags m_FirmwareFeatures; bool m_IsBundle = false; /* `53` is the maximum size of any packet that could be bundled, which is the diff --git a/src/network/featureflags.h b/src/network/featureflags.h index b0820be40..64148437b 100644 --- a/src/network/featureflags.h +++ b/src/network/featureflags.h @@ -1,103 +1,102 @@ + /* - SlimeVR Code is placed under the MIT license - Copyright (c) 2023 SlimeVR Contributors - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + SlimeVR Code is placed under the MIT license + Copyright (c) 2023 SlimeVR Contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. */ #ifndef SLIMEVR_FEATURE_FLAGS_H_ #define SLIMEVR_FEATURE_FLAGS_H_ -#include #include - -/** - * Bit packed flags, enum values start with 0 and indicate which bit it is. - * - * Change the enums and `flagsEnabled` inside to extend. -*/ -struct ServerFeatures { -public: - enum EServerFeatureFlags: uint32_t { - // Server can parse bundle packets: `PACKET_BUNDLE` = 100 (0x64). - PROTOCOL_BUNDLE_SUPPORT, - - // Add new flags here - - BITS_TOTAL, - }; - - bool has(EServerFeatureFlags flag) { - uint32_t bit = static_cast(flag); - return m_Available && (m_Flags[bit / 8] & (1 << (bit % 8))); - } - - /** - * Whether the server supports the "feature flags" feature, - * set to true when we've received flags packet from the server. - */ - bool isAvailable() { - return m_Available; - } - - static ServerFeatures from(uint8_t* received, uint32_t length) { - ServerFeatures res; - res.m_Available = true; - memcpy(res.m_Flags, received, std::min(static_cast(sizeof(res.m_Flags)), length)); - return res; - } - -private: - bool m_Available = false; - - uint8_t m_Flags[static_cast(EServerFeatureFlags::BITS_TOTAL) / 8 + 1]; +#include +#include +#include + +// I hate C++11 - they fixed this in C++14, but our compilers are old as the iceage +struct EnumClassHash { + template + std::size_t operator()(T t) const { + return static_cast(t); + } }; -class FirmwareFeatures { -public: - enum EFirmwareFeatureFlags: uint32_t { - // EXAMPLE_FEATURE, - B64_WIFI_SCANNING = 1, - - // Add new flags here +enum EServerFeatureFlags : uint32_t { + // Server can parse bundle packets: `PACKET_BUNDLE` = 100 (0x64). + PROTOCOL_BUNDLE_SUPPORT, - BITS_TOTAL, - }; + BITS_TOTAL, +}; - // Flags to send - static constexpr const std::initializer_list flagsEnabled = { - // EXAMPLE_FEATURE, - B64_WIFI_SCANNING, +enum class EFirmwareFeatureFlags : uint32_t { + // EXAMPLE_FEATURE, + B64_WIFI_SCANNING = 1, - // Add enabled flags here - }; + BITS_TOTAL, +}; - static constexpr auto flags = []{ - constexpr uint32_t flagsLength = EFirmwareFeatureFlags::BITS_TOTAL / 8 + 1; - std::array packed{}; +static const std::unordered_map + m_EnabledFirmwareFeatures = {{EFirmwareFeatureFlags::B64_WIFI_SCANNING, true}}; - for (uint32_t bit : flagsEnabled) { - packed[bit / 8] |= 1 << (bit % 8); - } +template +class FeatureFlags { +public: + static constexpr auto FLAG_BYTES + = ((static_cast(Flags::BITS_TOTAL)) + 7) / 8; + + FeatureFlags() + : m_Available(false) {} + FeatureFlags(uint8_t* packed, uint32_t length) + : m_Available(true) { + for (uint32_t bit = 0; bit < length * 8; bit++) { + auto posInPacked = bit / 8; + auto posInByte = bit % 8; + + m_Flags[static_cast(bit)] = packed[posInPacked] & (1 << posInByte); + } + } + FeatureFlags(std::unordered_map flags) + : m_Available(true) + , m_Flags(flags) {} + + std::array pack() { + std::array packed{}; + + for (auto& [flag, value] : m_Flags) { + auto posInPacked = static_cast(flag) / 8; + auto posInByte = static_cast(flag) % 8; + + if (value) { + packed[posInPacked] |= 1 << posInByte; + } + } + + return packed; + }; + + bool has(Flags flag) { return m_Flags[flag]; } + bool isAvailable() { return m_Available; } - return packed; - }(); +private: + bool m_Available = false; + std::unordered_map m_Flags{}; }; #endif