[common] add Preference helper methods for 2-bit preference value (#8895)

This commit introduces a common `Preference` class which defines
constants and helper methods (e.g., to convert between an `int8_t`
preference and its 2-bit unsigned representation). This is used for
`RoutePreference` in Network Data entries and also for Parent
Priority in `ConnectivityTlv`.
This commit is contained in:
Abtin Keshavarzian
2023-03-23 18:47:57 -07:00
committed by GitHub
parent 84be135243
commit e8f3ec0fd3
10 changed files with 271 additions and 77 deletions
+2
View File
@@ -427,6 +427,8 @@ openthread_core_files = [
"common/owned_ptr.hpp",
"common/owning_list.hpp",
"common/pool.hpp",
"common/preference.cpp",
"common/preference.hpp",
"common/ptr_wrapper.hpp",
"common/random.cpp",
"common/random.hpp",
+1
View File
@@ -109,6 +109,7 @@ set(COMMON_SOURCES
common/log.cpp
common/message.cpp
common/notifier.cpp
common/preference.cpp
common/random.cpp
common/settings.cpp
common/string.cpp
+2
View File
@@ -199,6 +199,7 @@ SOURCES_COMMON = \
common/log.cpp \
common/message.cpp \
common/notifier.cpp \
common/preference.cpp \
common/random.cpp \
common/settings.cpp \
common/string.cpp \
@@ -472,6 +473,7 @@ HEADERS_COMMON = \
common/owned_ptr.hpp \
common/owning_list.hpp \
common/pool.hpp \
common/preference.hpp \
common/ptr_wrapper.hpp \
common/random.hpp \
common/retain_ptr.hpp \
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2023, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements methods for a signed preference value and its 2-bit unsigned representation.
*
*/
#include "preference.hpp"
namespace ot {
uint8_t Preference::To2BitUint(int8_t aPrf) { return (aPrf == 0) ? k2BitMedium : ((aPrf > 0) ? k2BitHigh : k2BitLow); }
int8_t Preference::From2BitUint(uint8_t a2BitUint)
{
static const int8_t kPreferences[] = {
/* 0 (00) -> */ kMedium,
/* 1 (01) -> */ kHigh,
/* 2 (10) -> */ kMedium, // Per RFC-4191, the reserved value (10) MUST be treated as (00)
/* 3 (11) -> */ kLow,
};
return kPreferences[a2BitUint & k2BitMask];
}
bool Preference::IsValid(int8_t aPrf) { return (aPrf == kHigh) || (aPrf == kMedium) || (aPrf == kLow); }
const char *Preference::ToString(int8_t aPrf) { return (aPrf == 0) ? "medium" : ((aPrf > 0) ? "high" : "low"); }
} // namespace ot
+135
View File
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2023, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for a signed preference value and its 2-bit unsigned representation used by
* Route Preference in RFC-4191.
*/
#ifndef PREFERENCE_HPP_
#define PREFERENCE_HPP_
#include "openthread-core-config.h"
#include "common/error.hpp"
namespace ot {
/**
* This class provides constants and static methods to convert between `int8_t` preference and its 2-bit unsigned
* representation.
*
*/
class Preference
{
public:
static constexpr int8_t kHigh = 1; ///< High preference.
static constexpr int8_t kMedium = 0; ///< Medium preference.
static constexpr int8_t kLow = -1; ///< Low preference.
/**
* This static method converts a signed preference value to its corresponding 2-bit `uint8_t` value.
*
* A positive @p aPrf is mapped to "High Preference", a negative @p aPrf is mapped to "Low Preference", and
* zero @p aPrf is mapped to "Medium Preference".
*
* @param[in] aPrf The preference to convert to `uint8_t`.
*
* @returns The 2-bit unsigned value representing @p aPrf.
*
*/
static uint8_t To2BitUint(int8_t aPrf);
/**
* This static method converts a 2-bit `uint8_t` value to a signed preference value `kHigh`, `kMedium`, and `kLow`.
*
* Only the first two bits (LSB) of @p a2BitUint are used and the rest of the bits are ignored.
*
* - `0b01` (or 1) is mapped to `kHigh`.
* - `0b00` (or 0) is mapped to `kMedium`.
* - `0b11` (or 3) is mapped to `kLow`.
* - `0b10` (or 2) is reserved for future and is also mapped to `kMedium` (this complies with RFC-4191 where
* the reserved value `0b10` MUST be treated as `0b00` for Route Preference).
*
* @param[in] a2BitUint The 2-bit unsigned value to convert from. Only two LSB bits are used and the reset are
* ignored.
*
* @returns The signed preference `kHigh`, `kMedium`, or `kLow` corresponding to @p a2BitUint.
*
*/
static int8_t From2BitUint(uint8_t a2BitUint);
/**
* This static method indicates whether a given `int8_t` preference value is valid, i.e., whether it has of the
* three values `kHigh`, `kMediumm`, or `kLow`.
*
* @param[in] aPrf The signed preference value to check.
*
* @retval TRUE if @p aPrf is valid.
* @retval FALSE if @p aPrf is not valid
*
*/
static bool IsValid(int8_t aPrf);
/**
* This static method indicates whether a given 2-bit `uint8_t` preference value is valid.
*
* @param[in] a2BitUint The 2-bit unsigned value to convert from. Only two LSB bits are used and the reset are
* ignored.
*
* @retval TRUE if the first 2 bits of @p a2BitUint are `0b00`, `0b01`, or `0b11`.
* @retval FALSE if the first 2 bits of @p a2BitUint are `0b01`.
*
*/
static bool Is2BitUintValid(uint8_t a2BitUint) { return ((a2BitUint & k2BitMask) != k2BitReserved); }
/**
* This static method converts a given preference to a human-readable string.
*
* @param[in] aPrf The preference to convert.
*
* @returns The string representation of @p aPrf.
*
*/
static const char *ToString(int8_t aPrf);
Preference(void) = delete;
private:
static constexpr uint8_t k2BitMask = 3;
static constexpr uint8_t k2BitHigh = 1; // 0b01
static constexpr uint8_t k2BitMedium = 0; // 0b00
static constexpr uint8_t k2BitLow = 3; // 0b11
static constexpr uint8_t k2BitReserved = 2; // 0b10
};
} // namespace ot
#endif // PREFERENCE_HPP_
+2 -29
View File
@@ -86,39 +86,12 @@ void ConnectivityTlv::IncrementLinkQuality(LinkQuality aLinkQuality)
int8_t ConnectivityTlv::GetParentPriority(void) const
{
int8_t priority = kParentPriorityUnspecified;
switch (mFlags & kFlagsParentPriorityMask)
{
case kFlagsPriorityHigh:
priority = kParentPriorityHigh;
break;
case kFlagsPriorityMedium:
priority = kParentPriorityMedium;
break;
case kFlagsPriorityLow:
priority = kParentPriorityLow;
default:
break;
}
return priority;
return Preference::From2BitUint(mFlags >> kFlagsParentPriorityOffset);
}
void ConnectivityTlv::SetParentPriority(int8_t aParentPriority)
{
if (aParentPriority > 0)
{
mFlags = kFlagsPriorityHigh;
}
else if (aParentPriority < 0)
{
mFlags = kFlagsPriorityLow;
}
else
{
mFlags = kFlagsPriorityMedium;
}
mFlags = static_cast<uint8_t>(Preference::To2BitUint(aParentPriority) << kFlagsParentPriorityOffset);
}
} // namespace Mle
+1 -4
View File
@@ -38,6 +38,7 @@
#include "common/encoding.hpp"
#include "common/message.hpp"
#include "common/preference.hpp"
#include "common/tlvs.hpp"
#include "meshcop/timestamp.hpp"
#include "net/ip6_address.hpp"
@@ -947,10 +948,6 @@ private:
static constexpr uint8_t kFlagsParentPriorityOffset = 6;
static constexpr uint8_t kFlagsParentPriorityMask = (3 << kFlagsParentPriorityOffset);
static constexpr uint8_t kFlagsPriorityHigh = 1 << kFlagsParentPriorityOffset; // (01) 1
static constexpr uint8_t kFlagsPriorityMedium = 0 << kFlagsParentPriorityOffset; // (00) 0
static constexpr uint8_t kFlagsPriorityLow = 3 << kFlagsParentPriorityOffset; // (11) -1
uint8_t mFlags;
uint8_t mLinkQuality3;
uint8_t mLinkQuality2;
-19
View File
@@ -39,25 +39,6 @@
namespace ot {
namespace NetworkData {
const char *RoutePreferenceToString(RoutePreference aPreference)
{
const char *str = "low";
switch (aPreference)
{
case kRoutePreferenceHigh:
str = "high";
break;
case kRoutePreferenceMedium:
str = "med";
break;
case kRoutePreferenceLow:
break;
}
return str;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
static bool IsPrefixValid(Instance &aInstance, const Ip6::Prefix &aPrefix)
+9 -25
View File
@@ -43,6 +43,7 @@
#include "common/data.hpp"
#include "common/debug.hpp"
#include "common/equatable.hpp"
#include "common/preference.hpp"
#include "net/ip6_address.hpp"
namespace ot {
@@ -90,6 +91,10 @@ enum RoutePreference : int8_t
kRoutePreferenceHigh = OT_ROUTE_PREFERENCE_HIGH, ///< High route preference.
};
static_assert(kRoutePreferenceHigh == Preference::kHigh, "kRoutePreferenceHigh is not valid");
static_assert(kRoutePreferenceMedium == Preference::kMedium, "kRoutePreferenceMedium is not valid");
static_assert(kRoutePreferenceLow == Preference::kLow, "kRoutePreferenceLow is not valid");
/**
* This enumeration represents the border router RLOC role filter used when searching for border routers in the Network
* Data.
@@ -112,10 +117,7 @@ enum RoleFilter : uint8_t
* @retval FALSE if @p aPref is not valid
*
*/
inline bool IsRoutePreferenceValid(int8_t aPref)
{
return (aPref == kRoutePreferenceLow) || (aPref == kRoutePreferenceMedium) || (aPref == kRoutePreferenceHigh);
}
inline bool IsRoutePreferenceValid(int8_t aPref) { return Preference::IsValid(aPref); }
/**
* This function coverts a route preference to a 2-bit unsigned value.
@@ -127,16 +129,7 @@ inline bool IsRoutePreferenceValid(int8_t aPref)
* @returns The 2-bit unsigned value representing @p aPref.
*
*/
inline uint8_t RoutePreferenceToValue(int8_t aPref)
{
constexpr uint8_t kHigh = 1; // 01
constexpr uint8_t kMedium = 0; // 00
constexpr uint8_t kLow = 3; // 11
OT_ASSERT(IsRoutePreferenceValid(aPref));
return (aPref == 0) ? kMedium : ((aPref > 0) ? kHigh : kLow);
}
inline uint8_t RoutePreferenceToValue(int8_t aPref) { return Preference::To2BitUint(aPref); }
/**
* This function coverts a 2-bit unsigned value to a route preference.
@@ -149,16 +142,7 @@ inline uint8_t RoutePreferenceToValue(int8_t aPref)
*/
inline RoutePreference RoutePreferenceFromValue(uint8_t aValue)
{
constexpr uint8_t kMask = 3; // First two bits.
static const RoutePreference kRoutePreferences[] = {
/* 0 (00) -> */ kRoutePreferenceMedium,
/* 1 (01) -> */ kRoutePreferenceHigh,
/* 2 (10) -> */ kRoutePreferenceMedium, // Per RFC-4191, the reserved value (10) MUST be treated as (00)
/* 3 (11) -> */ kRoutePreferenceLow,
};
return kRoutePreferences[aValue & kMask];
return static_cast<RoutePreference>(Preference::From2BitUint(aValue));
}
/**
@@ -169,7 +153,7 @@ inline RoutePreference RoutePreferenceFromValue(uint8_t aValue)
* @returns The string representation of @p aPreference.
*
*/
const char *RoutePreferenceToString(RoutePreference aPreference);
inline const char *RoutePreferenceToString(RoutePreference aPreference) { return Preference::ToString(aPreference); }
/**
* This class represents an On-mesh Prefix (Border Router) configuration.
+62
View File
@@ -34,6 +34,7 @@
#include "common/code_utils.hpp"
#include "common/num_utils.hpp"
#include "common/numeric_limits.hpp"
#include "common/preference.hpp"
#include "common/serial_number.hpp"
namespace ot {
@@ -152,6 +153,66 @@ void TestNumUtils(void)
printf("TestNumUtils() passed\n");
}
void TestPreference(void)
{
VerifyOrQuit(Preference::kHigh == 1);
VerifyOrQuit(Preference::kMedium == 0);
VerifyOrQuit(Preference::kLow == -1);
// To2BitUint()
VerifyOrQuit(Preference::To2BitUint(Preference::kHigh) == 0x1);
VerifyOrQuit(Preference::To2BitUint(Preference::kMedium) == 0x0);
VerifyOrQuit(Preference::To2BitUint(Preference::kLow) == 0x3);
VerifyOrQuit(Preference::To2BitUint(2) == 0x1);
VerifyOrQuit(Preference::To2BitUint(-2) == 0x3);
VerifyOrQuit(Preference::To2BitUint(127) == 0x1);
VerifyOrQuit(Preference::To2BitUint(-128) == 0x3);
// From2BitUint()
VerifyOrQuit(Preference::From2BitUint(0x1) == Preference::kHigh);
VerifyOrQuit(Preference::From2BitUint(0x0) == Preference::kMedium);
VerifyOrQuit(Preference::From2BitUint(0x3) == Preference::kLow);
VerifyOrQuit(Preference::From2BitUint(0x2) == Preference::kMedium);
VerifyOrQuit(Preference::From2BitUint(0x1 | 4) == Preference::kHigh);
VerifyOrQuit(Preference::From2BitUint(0x0 | 4) == Preference::kMedium);
VerifyOrQuit(Preference::From2BitUint(0x3 | 4) == Preference::kLow);
VerifyOrQuit(Preference::From2BitUint(0x2 | 4) == Preference::kMedium);
VerifyOrQuit(Preference::From2BitUint(0x1 | 0xfc) == Preference::kHigh);
VerifyOrQuit(Preference::From2BitUint(0x0 | 0xfc) == Preference::kMedium);
VerifyOrQuit(Preference::From2BitUint(0x3 | 0xfc) == Preference::kLow);
VerifyOrQuit(Preference::From2BitUint(0x2 | 0xfc) == Preference::kMedium);
// IsValid()
VerifyOrQuit(Preference::IsValid(Preference::kHigh));
VerifyOrQuit(Preference::IsValid(Preference::kMedium));
VerifyOrQuit(Preference::IsValid(Preference::kLow));
VerifyOrQuit(!Preference::IsValid(2));
VerifyOrQuit(!Preference::IsValid(-2));
VerifyOrQuit(!Preference::IsValid(127));
VerifyOrQuit(!Preference::IsValid(-128));
// Is2BitUintValid
VerifyOrQuit(Preference::Is2BitUintValid(0x1));
VerifyOrQuit(Preference::Is2BitUintValid(0x0));
VerifyOrQuit(Preference::Is2BitUintValid(0x3));
VerifyOrQuit(!Preference::Is2BitUintValid(0x2));
VerifyOrQuit(Preference::Is2BitUintValid(0x1 | 4));
VerifyOrQuit(Preference::Is2BitUintValid(0x0 | 4));
VerifyOrQuit(Preference::Is2BitUintValid(0x3 | 4));
VerifyOrQuit(!Preference::Is2BitUintValid(0x2 | 4));
VerifyOrQuit(Preference::Is2BitUintValid(0x1 | 0xfc));
VerifyOrQuit(Preference::Is2BitUintValid(0x0 | 0xfc));
VerifyOrQuit(Preference::Is2BitUintValid(0x3 | 0xfc));
VerifyOrQuit(!Preference::Is2BitUintValid(0x2 | 0xfc));
printf("TestPreference() passed\n");
}
} // namespace ot
int main(void)
@@ -161,6 +222,7 @@ int main(void)
ot::TestSerialNumber<uint32_t>("uint32_t");
ot::TestSerialNumber<uint64_t>("uint64_t");
ot::TestNumUtils();
ot::TestPreference();
printf("\nAll tests passed.\n");
return 0;
}