mirror of
https://github.com/espressif/openthread.git
synced 2026-10-02 22:07:36 +00:00
[common] add BinarySearch module and Stringfy (#7294)
This commit adds set of generic binary search related functions in OpenThread core under `BinarySearch` module. The binary search module replaces and enhances the existing `Lookup` table module (which was limited to search based on strings and mainly used by CLI to map from command string to a handler function). This commit also adds a new class `Stringfy` in `string.hpp` to help convert from a set of `uint16_t` values (e.g., a non-sequential `enum`) to strings using binary search in a table. The new methods are used for converting `Coap::Code` and `IpProto` enumerations to corresponding strings.
This commit is contained in:
+1
-1
@@ -220,6 +220,7 @@ LOCAL_SRC_FILES := \
|
||||
src/core/coap/coap_message.cpp \
|
||||
src/core/coap/coap_secure.cpp \
|
||||
src/core/common/appender.cpp \
|
||||
src/core/common/binary_search.cpp \
|
||||
src/core/common/crc16.cpp \
|
||||
src/core/common/data.cpp \
|
||||
src/core/common/error.cpp \
|
||||
@@ -353,7 +354,6 @@ LOCAL_SRC_FILES := \
|
||||
src/core/utils/heap.cpp \
|
||||
src/core/utils/history_tracker.cpp \
|
||||
src/core/utils/jam_detector.cpp \
|
||||
src/core/utils/lookup_table.cpp \
|
||||
src/core/utils/otns.cpp \
|
||||
src/core/utils/parse_cmdline.cpp \
|
||||
src/core/utils/ping_sender.cpp \
|
||||
|
||||
+1
-1
@@ -293,7 +293,7 @@ void Interpreter::ProcessLine(char *aBuf)
|
||||
}
|
||||
#endif
|
||||
|
||||
command = Utils::LookupTable::Find(args[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(args[0].GetCString(), sCommands);
|
||||
|
||||
if (command != nullptr)
|
||||
{
|
||||
|
||||
+2
-8
@@ -76,8 +76,6 @@
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "common/type_traits.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
@@ -229,11 +227,7 @@ private:
|
||||
static constexpr uint32_t kNetworkDiagnosticTimeoutMsecs = 5000;
|
||||
static constexpr uint32_t kLocateTimeoutMsecs = 2500;
|
||||
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Interpreter::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<Interpreter>;
|
||||
|
||||
template <typename ValueType> using GetHandler = ValueType (&)(otInstance *);
|
||||
template <typename ValueType> using SetHandler = void (&)(otInstance *, ValueType);
|
||||
@@ -888,7 +882,7 @@ private:
|
||||
{"version", &Interpreter::ProcessVersion},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
const otCliCommand *mUserCommands;
|
||||
uint8_t mUserCommandsLength;
|
||||
|
||||
@@ -586,7 +586,7 @@ otError Coap::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -41,8 +41,6 @@
|
||||
#include <openthread/coap.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
@@ -79,11 +77,7 @@ private:
|
||||
kMaxBufferSize = 16
|
||||
};
|
||||
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Coap::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<Coap>;
|
||||
|
||||
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
|
||||
enum BlockType : uint8_t{
|
||||
@@ -186,7 +180,7 @@ private:
|
||||
{"stop", &Coap::ProcessStop},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
bool mUseDefaultRequestTxParameters;
|
||||
bool mUseDefaultResponseTxParameters;
|
||||
|
||||
@@ -468,7 +468,7 @@ otError CoapSecure::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
#include <openthread/coap_secure.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#ifndef CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
|
||||
#define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 0
|
||||
@@ -87,11 +85,7 @@ private:
|
||||
kPskIdMaxLength = 32
|
||||
};
|
||||
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (CoapSecure::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<CoapSecure>;
|
||||
|
||||
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
|
||||
enum BlockType : uint8_t{
|
||||
@@ -175,7 +169,7 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
|
||||
otCoapBlockwiseResource mResource;
|
||||
|
||||
@@ -403,7 +403,7 @@ otError Commissioner::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include <openthread/commissioner.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
|
||||
|
||||
@@ -81,11 +79,7 @@ private:
|
||||
kDefaultJoinerTimeout = 120, ///< Default timeout for Joiners, in seconds.
|
||||
};
|
||||
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Commissioner::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<Commissioner>;
|
||||
|
||||
otError ProcessHelp(Arg aArgs[]);
|
||||
otError ProcessAnnounce(Arg aArgs[]);
|
||||
@@ -131,7 +125,7 @@ private:
|
||||
{"state", &Commissioner::ProcessState}, {"stop", &Commissioner::ProcessStop},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
@@ -129,7 +129,7 @@ otError Dataset::Process(Arg aArgs[])
|
||||
ExitNow(error = Print(sDataset));
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -41,8 +41,6 @@
|
||||
#include <openthread/dataset.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
@@ -70,11 +68,7 @@ public:
|
||||
otError Process(Arg aArgs[]);
|
||||
|
||||
private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Dataset::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<Dataset>;
|
||||
|
||||
otError Print(otOperationalDataset &aDataset);
|
||||
|
||||
@@ -136,7 +130,7 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
static otOperationalDataset sDataset;
|
||||
};
|
||||
|
||||
@@ -582,7 +582,7 @@ otError History::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
|
||||
#include "cli/cli_config.h"
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
||||
|
||||
@@ -81,11 +79,7 @@ private:
|
||||
static constexpr uint16_t kShortAddrBroadcast = 0xffff;
|
||||
static constexpr int8_t kInvalidRss = OT_RADIO_RSSI_INVALID;
|
||||
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (History::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<History>;
|
||||
|
||||
enum RxTx : uint8_t
|
||||
{
|
||||
@@ -123,7 +117,7 @@ private:
|
||||
{"tx", &History::ProcessTx},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
@@ -148,7 +148,7 @@ otError Joiner::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include <openthread/joiner.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_JOINER_ENABLE
|
||||
|
||||
@@ -76,11 +74,7 @@ public:
|
||||
otError Process(Arg aArgs[]);
|
||||
|
||||
private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Joiner::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<Joiner>;
|
||||
|
||||
otError ProcessDiscerner(Arg aArgs[]);
|
||||
otError ProcessHelp(Arg aArgs[]);
|
||||
@@ -97,7 +91,7 @@ private:
|
||||
{"start", &Joiner::ProcessStart}, {"state", &Joiner::ProcessState}, {"stop", &Joiner::ProcessStop},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
@@ -428,7 +428,7 @@ otError NetworkData::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include <openthread/netdata.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
@@ -98,11 +96,7 @@ public:
|
||||
void OutputService(const otServiceConfig &aConfig);
|
||||
|
||||
private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (NetworkData::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<NetworkData>;
|
||||
|
||||
otError ProcessHelp(Arg aArgs[]);
|
||||
#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
|
||||
@@ -136,7 +130,7 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
+20
-2
@@ -41,6 +41,9 @@
|
||||
#include <openthread/cli.h>
|
||||
|
||||
#include "cli_config.h"
|
||||
|
||||
#include "common/binary_search.hpp"
|
||||
#include "common/string.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -79,6 +82,23 @@ public:
|
||||
|
||||
protected:
|
||||
OutputBase(void) = default;
|
||||
|
||||
typedef Utils::CmdLineParser::Arg Arg;
|
||||
|
||||
template <typename Cli> struct CommandEntry
|
||||
{
|
||||
typedef otError (Cli::*Handler)(Arg aArgs[]);
|
||||
|
||||
int Compare(const char *aName) const { return strcmp(aName, mName); }
|
||||
|
||||
constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
|
||||
{
|
||||
return AreStringsInOrder(aFirst.mName, aSecond.mName);
|
||||
}
|
||||
|
||||
const char *mName;
|
||||
Handler mHandler;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -344,8 +364,6 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef Utils::CmdLineParser::Arg Arg;
|
||||
|
||||
void OutputFormatV(const char *aFormat, va_list aArguments);
|
||||
|
||||
#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
|
||||
|
||||
@@ -77,7 +77,7 @@ otError SrpClient::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -41,8 +41,6 @@
|
||||
|
||||
#include "cli/cli_config.h"
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
|
||||
|
||||
@@ -81,11 +79,7 @@ private:
|
||||
kIndentSize = 4,
|
||||
};
|
||||
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (SrpClient::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<SrpClient>;
|
||||
|
||||
otError ProcessAutoStart(Arg aArgs[]);
|
||||
otError ProcessCallback(Arg aArgs[]);
|
||||
@@ -128,7 +122,7 @@ private:
|
||||
{"stop", &SrpClient::ProcessStop},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
bool mCallbackEnabled;
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ otError SrpServer::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include <openthread/srp_server.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
||||
|
||||
@@ -81,11 +79,7 @@ public:
|
||||
private:
|
||||
static constexpr uint8_t kIndentSize = 4;
|
||||
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (SrpServer::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<SrpServer>;
|
||||
|
||||
otError ProcessAddrMode(Arg aArgs[]);
|
||||
otError ProcessDomain(Arg aArgs[]);
|
||||
@@ -108,7 +102,7 @@ private:
|
||||
{"service", &SrpServer::ProcessService}, {"state", &SrpServer::ProcessState},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
+1
-1
@@ -322,7 +322,7 @@ otError TcpExample::Process(Arg aArgs[])
|
||||
|
||||
VerifyOrExit(!aArgs[0].IsEmpty(), IgnoreError(ProcessHelp(nullptr)));
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
+2
-8
@@ -41,8 +41,6 @@
|
||||
#include "cli/cli_config.h"
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "common/time.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
@@ -73,11 +71,7 @@ public:
|
||||
otError Process(Arg aArgs[]);
|
||||
|
||||
private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (TcpExample::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<TcpExample>;
|
||||
|
||||
otError ProcessHelp(Arg aArgs[]);
|
||||
otError ProcessInit(Arg aArgs[]);
|
||||
@@ -131,7 +125,7 @@ private:
|
||||
{"stoplistening", &TcpExample::ProcessStopListening},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
otTcpEndpoint mEndpoint;
|
||||
otTcpListener mListener;
|
||||
|
||||
+1
-1
@@ -277,7 +277,7 @@ otError UdpExample::Process(Arg aArgs[])
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
|
||||
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
|
||||
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
error = (this->*command->mHandler)(aArgs + 1);
|
||||
|
||||
+2
-8
@@ -39,8 +39,6 @@
|
||||
#include <openthread/udp.h>
|
||||
|
||||
#include "cli/cli_output.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
@@ -71,11 +69,7 @@ public:
|
||||
otError Process(Arg aArgs[]);
|
||||
|
||||
private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (UdpExample::*mHandler)(Arg aArgs[]);
|
||||
};
|
||||
using Command = CommandEntry<UdpExample>;
|
||||
|
||||
otError ProcessHelp(Arg aArgs[]);
|
||||
otError ProcessBind(Arg aArgs[]);
|
||||
@@ -101,7 +95,7 @@ private:
|
||||
{"send", &UdpExample::ProcessSend},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
bool mLinkSecurityEnabled;
|
||||
otUdpSocket mSocket;
|
||||
|
||||
+4
-3
@@ -376,6 +376,8 @@ openthread_core_files = [
|
||||
"common/arg_macros.hpp",
|
||||
"common/array.hpp",
|
||||
"common/as_core_type.hpp",
|
||||
"common/binary_search.cpp",
|
||||
"common/binary_search.hpp",
|
||||
"common/bit_vector.hpp",
|
||||
"common/clearable.hpp",
|
||||
"common/code_utils.hpp",
|
||||
@@ -670,8 +672,6 @@ openthread_core_files = [
|
||||
"utils/history_tracker.hpp",
|
||||
"utils/jam_detector.cpp",
|
||||
"utils/jam_detector.hpp",
|
||||
"utils/lookup_table.cpp",
|
||||
"utils/lookup_table.hpp",
|
||||
"utils/otns.cpp",
|
||||
"utils/otns.hpp",
|
||||
"utils/parse_cmdline.cpp",
|
||||
@@ -692,6 +692,8 @@ openthread_radio_sources = [
|
||||
"api/logging_api.cpp",
|
||||
"api/random_noncrypto_api.cpp",
|
||||
"api/tasklet_api.cpp",
|
||||
"common/binary_search.cpp",
|
||||
"common/binary_search.hpp",
|
||||
"common/error.hpp",
|
||||
"common/instance.cpp",
|
||||
"common/logging.cpp",
|
||||
@@ -714,7 +716,6 @@ openthread_radio_sources = [
|
||||
"radio/radio_callbacks.cpp",
|
||||
"radio/radio_platform.cpp",
|
||||
"thread/link_quality.cpp",
|
||||
"utils/lookup_table.cpp",
|
||||
"utils/parse_cmdline.cpp",
|
||||
]
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ set(COMMON_SOURCES
|
||||
coap/coap_message.cpp
|
||||
coap/coap_secure.cpp
|
||||
common/appender.cpp
|
||||
common/binary_search.cpp
|
||||
common/crc16.cpp
|
||||
common/data.cpp
|
||||
common/error.cpp
|
||||
@@ -226,7 +227,6 @@ set(COMMON_SOURCES
|
||||
utils/heap.cpp
|
||||
utils/history_tracker.cpp
|
||||
utils/jam_detector.cpp
|
||||
utils/lookup_table.cpp
|
||||
utils/otns.cpp
|
||||
utils/parse_cmdline.cpp
|
||||
utils/ping_sender.cpp
|
||||
@@ -242,6 +242,7 @@ set(RADIO_COMMON_SOURCES
|
||||
api/logging_api.cpp
|
||||
api/random_noncrypto_api.cpp
|
||||
api/tasklet_api.cpp
|
||||
common/binary_search.cpp
|
||||
common/error.cpp
|
||||
common/instance.cpp
|
||||
common/logging.cpp
|
||||
@@ -264,7 +265,6 @@ set(RADIO_COMMON_SOURCES
|
||||
radio/radio_callbacks.cpp
|
||||
radio/radio_platform.cpp
|
||||
thread/link_quality.cpp
|
||||
utils/lookup_table.cpp
|
||||
utils/parse_cmdline.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ SOURCES_COMMON = \
|
||||
coap/coap_message.cpp \
|
||||
coap/coap_secure.cpp \
|
||||
common/appender.cpp \
|
||||
common/binary_search.cpp \
|
||||
common/crc16.cpp \
|
||||
common/data.cpp \
|
||||
common/error.cpp \
|
||||
@@ -316,7 +317,6 @@ SOURCES_COMMON = \
|
||||
utils/heap.cpp \
|
||||
utils/history_tracker.cpp \
|
||||
utils/jam_detector.cpp \
|
||||
utils/lookup_table.cpp \
|
||||
utils/otns.cpp \
|
||||
utils/parse_cmdline.cpp \
|
||||
utils/ping_sender.cpp \
|
||||
@@ -332,6 +332,7 @@ RADIO_SOURCES_COMMON = \
|
||||
api/logging_api.cpp \
|
||||
api/random_noncrypto_api.cpp \
|
||||
api/tasklet_api.cpp \
|
||||
common/binary_search.cpp \
|
||||
common/error.cpp \
|
||||
common/instance.cpp \
|
||||
common/logging.cpp \
|
||||
@@ -354,7 +355,6 @@ RADIO_SOURCES_COMMON = \
|
||||
radio/radio_callbacks.cpp \
|
||||
radio/radio_platform.cpp \
|
||||
thread/link_quality.cpp \
|
||||
utils/lookup_table.cpp \
|
||||
utils/parse_cmdline.cpp \
|
||||
$(NULL)
|
||||
|
||||
@@ -420,6 +420,7 @@ HEADERS_COMMON = \
|
||||
common/arg_macros.hpp \
|
||||
common/array.hpp \
|
||||
common/as_core_type.hpp \
|
||||
common/binary_search.hpp \
|
||||
common/bit_vector.hpp \
|
||||
common/clearable.hpp \
|
||||
common/code_utils.hpp \
|
||||
@@ -618,7 +619,6 @@ HEADERS_COMMON = \
|
||||
utils/heap.hpp \
|
||||
utils/history_tracker.hpp \
|
||||
utils/jam_detector.hpp \
|
||||
utils/lookup_table.hpp \
|
||||
utils/otns.hpp \
|
||||
utils/parse_cmdline.hpp \
|
||||
utils/ping_sender.hpp \
|
||||
|
||||
@@ -438,100 +438,40 @@ exit:
|
||||
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
|
||||
const char *Message::CodeToString(void) const
|
||||
{
|
||||
const char *string;
|
||||
static constexpr Stringify::Entry kCodeTable[] = {
|
||||
{kCodeEmpty, "Empty"},
|
||||
{kCodeGet, "Get"},
|
||||
{kCodePost, "Post"},
|
||||
{kCodePut, "Put"},
|
||||
{kCodeDelete, "Delete"},
|
||||
{kCodeCreated, "Created"},
|
||||
{kCodeDeleted, "Deleted"},
|
||||
{kCodeValid, "Valid"},
|
||||
{kCodeChanged, "Changed"},
|
||||
{kCodeContent, "Content"},
|
||||
{kCodeContinue, "Continue"},
|
||||
{kCodeBadRequest, "BadRequest"},
|
||||
{kCodeUnauthorized, "Unauthorized"},
|
||||
{kCodeBadOption, "BadOption"},
|
||||
{kCodeForbidden, "Forbidden"},
|
||||
{kCodeNotFound, "NotFound"},
|
||||
{kCodeMethodNotAllowed, "MethodNotAllowed"},
|
||||
{kCodeNotAcceptable, "NotAcceptable"},
|
||||
{kCodeRequestIncomplete, "RequestIncomplete"},
|
||||
{kCodePreconditionFailed, "PreconditionFailed"},
|
||||
{kCodeRequestTooLarge, "RequestTooLarge"},
|
||||
{kCodeUnsupportedFormat, "UnsupportedFormat"},
|
||||
{kCodeInternalError, "InternalError"},
|
||||
{kCodeNotImplemented, "NotImplemented"},
|
||||
{kCodeBadGateway, "BadGateway"},
|
||||
{kCodeServiceUnavailable, "ServiceUnavailable"},
|
||||
{kCodeGatewayTimeout, "GatewayTimeout"},
|
||||
{kCodeProxyNotSupported, "ProxyNotSupported"},
|
||||
};
|
||||
|
||||
switch (GetCode())
|
||||
{
|
||||
case kCodeEmpty:
|
||||
string = "Empty";
|
||||
break;
|
||||
case kCodeGet:
|
||||
string = "Get";
|
||||
break;
|
||||
case kCodePost:
|
||||
string = "Post";
|
||||
break;
|
||||
case kCodePut:
|
||||
string = "Put";
|
||||
break;
|
||||
case kCodeDelete:
|
||||
string = "Delete";
|
||||
break;
|
||||
case kCodeCreated:
|
||||
string = "Created";
|
||||
break;
|
||||
case kCodeDeleted:
|
||||
string = "Deleted";
|
||||
break;
|
||||
case kCodeValid:
|
||||
string = "Valid";
|
||||
break;
|
||||
case kCodeChanged:
|
||||
string = "Changed";
|
||||
break;
|
||||
case kCodeContent:
|
||||
string = "Content";
|
||||
break;
|
||||
case kCodeContinue:
|
||||
string = "Continue";
|
||||
break;
|
||||
case kCodeBadRequest:
|
||||
string = "BadRequest";
|
||||
break;
|
||||
case kCodeUnauthorized:
|
||||
string = "Unauthorized";
|
||||
break;
|
||||
case kCodeBadOption:
|
||||
string = "BadOption";
|
||||
break;
|
||||
case kCodeForbidden:
|
||||
string = "Forbidden";
|
||||
break;
|
||||
case kCodeNotFound:
|
||||
string = "NotFound";
|
||||
break;
|
||||
case kCodeMethodNotAllowed:
|
||||
string = "MethodNotAllowed";
|
||||
break;
|
||||
case kCodeNotAcceptable:
|
||||
string = "NotAcceptable";
|
||||
break;
|
||||
case kCodeRequestIncomplete:
|
||||
string = "RequestIncomplete";
|
||||
break;
|
||||
case kCodePreconditionFailed:
|
||||
string = "PreconditionFailed";
|
||||
break;
|
||||
case kCodeRequestTooLarge:
|
||||
string = "RequestTooLarge";
|
||||
break;
|
||||
case kCodeUnsupportedFormat:
|
||||
string = "UnsupportedFormat";
|
||||
break;
|
||||
case kCodeInternalError:
|
||||
string = "InternalError";
|
||||
break;
|
||||
case kCodeNotImplemented:
|
||||
string = "NotImplemented";
|
||||
break;
|
||||
case kCodeBadGateway:
|
||||
string = "BadGateway";
|
||||
break;
|
||||
case kCodeServiceUnavailable:
|
||||
string = "ServiceUnavailable";
|
||||
break;
|
||||
case kCodeGatewayTimeout:
|
||||
string = "GatewayTimeout";
|
||||
break;
|
||||
case kCodeProxyNotSupported:
|
||||
string = "ProxyNotSupported";
|
||||
break;
|
||||
default:
|
||||
string = "Unknown";
|
||||
break;
|
||||
}
|
||||
static_assert(Stringify::IsSorted(kCodeTable), "kCodeTable is not sorted");
|
||||
|
||||
return string;
|
||||
return Stringify::Lookup(GetCode(), kCodeTable, "Unknown");
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, The OpenThread Authors.
|
||||
* Copyright (c) 2022, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,23 +28,20 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file implements the lookup table (binary search) functionality.
|
||||
* This file implements a generic binary search.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lookup_table.hpp"
|
||||
#include "binary_search.hpp"
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Utils {
|
||||
|
||||
const void *LookupTable::Find(const char *aName,
|
||||
const void *aTable,
|
||||
uint16_t aLength,
|
||||
uint16_t aTableEntrySize,
|
||||
NameGetter aNameGetter)
|
||||
const void *BinarySearch::Find(const void *aKey,
|
||||
const void *aTable,
|
||||
uint16_t aLength,
|
||||
uint16_t aEntrySize,
|
||||
Comparator aComparator)
|
||||
{
|
||||
const void *entry;
|
||||
uint16_t left = 0;
|
||||
@@ -56,17 +53,17 @@ const void *LookupTable::Find(const char *aName,
|
||||
int compare;
|
||||
|
||||
// Note that `aTable` array entry type is not known here and
|
||||
// only its size is given as `aTableEntrySize`. Based on this,
|
||||
// we can calculate the pointer to the table entry at any index
|
||||
// only its size is given as `aEntrySize`. Based on this, we
|
||||
// can calculate the pointer to the table entry at any index
|
||||
// (such as `[middle]`) which is then passed to the given
|
||||
// `aNameGetter` function which knows how to cast the void
|
||||
// pointer to proper entry type and get the enclosed `mName`
|
||||
// field. This model keeps the implementation generic and
|
||||
// re-usable allowing it to be used with any entry type.
|
||||
// `aComparator` function which knows how to cast the void
|
||||
// pointer to proper entry type and compare it with `aKey`.
|
||||
// This model keeps the implementation generic and reusable
|
||||
// allowing it to be used with any entry type.
|
||||
|
||||
entry = reinterpret_cast<const uint8_t *>(aTable) + aTableEntrySize * middle;
|
||||
entry = reinterpret_cast<const uint8_t *>(aTable) + aEntrySize * middle;
|
||||
|
||||
compare = strcmp(aName, aNameGetter(entry));
|
||||
compare = aComparator(aKey, entry);
|
||||
|
||||
if (compare == 0)
|
||||
{
|
||||
@@ -88,5 +85,4 @@ exit:
|
||||
return entry;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 provides a generic binary search and related helper functions.
|
||||
*/
|
||||
|
||||
#ifndef BINARY_SEARCH_HPP_
|
||||
#define BINARY_SEARCH_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ot {
|
||||
|
||||
class BinarySearch
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This template method performs binary search in a given sorted table array to find an entry matching a given key.
|
||||
*
|
||||
* @note This method requires the array to be sorted, otherwise its behavior is undefined.
|
||||
*
|
||||
* @tparam Key The type of `Key` to search for.
|
||||
* @tparam Entry The table `Entry` type.
|
||||
* @tparam kLength The array length (number of entries in the array).
|
||||
*
|
||||
* The `Entry` class MUST provide the following method to compare the entry against a given key.
|
||||
*
|
||||
* int Entry::Compare(const Key &aKey) const;
|
||||
*
|
||||
* The return value indicates the comparison result between @p aKey and the entry (similar to `strcmp()`), i.e.,
|
||||
* zero means perfect match, positive (> 0) indicates @p aKey is larger than entry, and negative indicates @p aKey
|
||||
* is smaller than entry.
|
||||
*
|
||||
* @note In the common use of this method as `Find(key, kTable)` where `kTable` is a fixed size array, the
|
||||
* template types/parameters do not need to be explicitly specified and can be inferred from the passed-in argument.
|
||||
*
|
||||
* @param[in] aKey The key to search for within the table.
|
||||
* @param[in] aTable A reference to an array of `kLength` entries of type `Entry`
|
||||
*
|
||||
* @returns A pointer to the entry in the table if a match is found, otherwise `nullptr` (no match in table).
|
||||
*
|
||||
*/
|
||||
template <typename Key, typename Entry, uint16_t kLength>
|
||||
static const Entry *Find(const Key &aKey, const Entry (&aTable)[kLength])
|
||||
{
|
||||
return static_cast<const Entry *>(
|
||||
Find(&aKey, &aTable[0], kLength, sizeof(aTable[0]), BinarySearch::Compare<Key, Entry>));
|
||||
}
|
||||
|
||||
/**
|
||||
* This template method indicates whether a given table array is sorted based or not.
|
||||
*
|
||||
* This method is `constexpr` and is intended for use in `static_assert`s to verify that a `constexpr` lookup table
|
||||
* array is sorted. It is not recommended for use in other situations.
|
||||
*
|
||||
* @tparam Entry The table entry type.
|
||||
* @tparam kLength The array length (number of entries in the array).
|
||||
*
|
||||
* The `Entry` class MUST provide the following `static` and `constexpr` method to compare two entries.
|
||||
*
|
||||
* constexpr static bool Entry::AreInOrder(const Entry &aFirst, const Entry &aSecond);
|
||||
*
|
||||
* The return value MUST be TRUE if the entries are in order, i.e. `aFirst < aSecond` and FALSE otherwise.
|
||||
*
|
||||
* @param[in] aTable A reference to an array of `kLength` entries on type `Entry`
|
||||
*
|
||||
* @retval TRUE If the entries in @p aTable are sorted.
|
||||
* @retval FALSE If the entries in @p aTable are not sorted.
|
||||
*
|
||||
*/
|
||||
template <typename Entry, uint16_t kLength> static constexpr bool IsSorted(const Entry (&aTable)[kLength])
|
||||
{
|
||||
return IsSorted(&aTable[0], kLength);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef int (&Comparator)(const void *aKey, const void *aEntry);
|
||||
|
||||
template <typename Entry> static constexpr bool IsSorted(const Entry *aTable, uint16_t aLength)
|
||||
{
|
||||
return (aLength <= 1) ? true : Entry::AreInOrder(aTable[0], aTable[1]) && IsSorted(aTable + 1, aLength - 1);
|
||||
}
|
||||
|
||||
template <typename Key, typename Entry> static int Compare(const void *aKey, const void *aEntry)
|
||||
{
|
||||
return static_cast<const Entry *>(aEntry)->Compare(*static_cast<const Key *>(aKey));
|
||||
}
|
||||
|
||||
static const void *Find(const void *aKey,
|
||||
const void *aTable,
|
||||
uint16_t aLength,
|
||||
uint16_t aEntrySize,
|
||||
Comparator aComparator);
|
||||
};
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // BINARY_SEARCH_HPP_
|
||||
+96
-20
@@ -40,6 +40,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/binary_search.hpp"
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/error.hpp"
|
||||
|
||||
@@ -204,6 +205,51 @@ char ToUppercase(char aChar);
|
||||
*/
|
||||
const char *ToYesNo(bool aBool);
|
||||
|
||||
/**
|
||||
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
|
||||
* Control characters are not allowed.
|
||||
*
|
||||
* @param[in] aString A null-terminated byte sequence.
|
||||
*
|
||||
* @retval TRUE The sequence is a valid UTF-8 string.
|
||||
* @retval FALSE The sequence is not a valid UTF-8 string.
|
||||
*
|
||||
*/
|
||||
bool IsValidUtf8String(const char *aString);
|
||||
|
||||
/**
|
||||
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
|
||||
* Control characters are not allowed.
|
||||
*
|
||||
* @param[in] aString A byte sequence.
|
||||
* @param[in] aLength Length of the sequence.
|
||||
*
|
||||
* @retval TRUE The sequence is a valid UTF-8 string.
|
||||
* @retval FALSE The sequence is not a valid UTF-8 string.
|
||||
*
|
||||
*/
|
||||
bool IsValidUtf8String(const char *aString, size_t aLength);
|
||||
|
||||
/**
|
||||
* This `constexpr` function checks whether two given C strings are in order (alphabetical order).
|
||||
*
|
||||
* This is intended for use from `static_assert`, e.g., checking if a lookup table entries are sorted. It is not
|
||||
* recommended to use this function in other situations as it uses recursion so that it can be `constexpr`.
|
||||
*
|
||||
* @param[in] aFirst The first string.
|
||||
* @param[in] aSecond The second string.
|
||||
*
|
||||
* @retval TRUE If first string is strictly before second string (alphabetical order).
|
||||
* @retval FALSE If first string is not strictly before second string (alphabetical order).
|
||||
*
|
||||
*/
|
||||
inline constexpr bool AreStringsInOrder(const char *aFirst, const char *aSecond)
|
||||
{
|
||||
return (*aFirst < *aSecond)
|
||||
? true
|
||||
: ((*aFirst > *aSecond) || (*aFirst == '\0') ? false : AreStringsInOrder(aFirst + 1, aSecond + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements writing to a string buffer.
|
||||
*
|
||||
@@ -339,29 +385,59 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
|
||||
* Control characters are not allowed.
|
||||
*
|
||||
* @param[in] aString A null-terminated byte sequence.
|
||||
*
|
||||
* @retval TRUE The sequence is a valid UTF-8 string.
|
||||
* @retval FALSE The sequence is not a valid UTF-8 string.
|
||||
* This class provides helper methods to convert from a set of `uint16_t` values (e.g., a non-sequential `enum`) to
|
||||
* string using binary search in a lookup table.
|
||||
*
|
||||
*/
|
||||
bool IsValidUtf8String(const char *aString);
|
||||
class Stringify : public BinarySearch
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This class represents a entry in the lookup table.
|
||||
*
|
||||
*/
|
||||
class Entry
|
||||
{
|
||||
friend class BinarySearch;
|
||||
|
||||
/**
|
||||
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
|
||||
* Control characters are not allowed.
|
||||
*
|
||||
* @param[in] aString A byte sequence.
|
||||
* @param[in] aLength Length of the sequence.
|
||||
*
|
||||
* @retval TRUE The sequence is a valid UTF-8 string.
|
||||
* @retval FALSE The sequence is not a valid UTF-8 string.
|
||||
*
|
||||
*/
|
||||
bool IsValidUtf8String(const char *aString, size_t aLength);
|
||||
public:
|
||||
uint16_t mKey; ///< The key value.
|
||||
const char *mString; ///< The associated string.
|
||||
|
||||
private:
|
||||
int Compare(uint16_t aKey) const { return (aKey == mKey) ? 0 : ((aKey > mKey) ? 1 : -1); }
|
||||
|
||||
constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond)
|
||||
{
|
||||
return aFirst.mKey < aSecond.mKey;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This static method looks up a key in a given sorted table array (using binary search) and return the associated
|
||||
* strings with the key.
|
||||
*
|
||||
* @note This method requires the array to be sorted, otherwise its behavior is undefined.
|
||||
*
|
||||
* @tparam kLength The array length (number of entries in the array).
|
||||
*
|
||||
* @param[in] aKey The key to search for within the table.
|
||||
* @param[in] aTable A reference to an array of `kLength` entries.
|
||||
* @param[in] aNotFound A C string to return if @p aKey was not found in the table.
|
||||
*
|
||||
* @returns The associated string with @p aKey in @p aTable if found, or @p aNotFound otherwise.
|
||||
*
|
||||
*/
|
||||
template <uint16_t kLength>
|
||||
static const char *Lookup(uint16_t aKey, const Entry (&aTable)[kLength], const char *aNotFound = "unknown")
|
||||
{
|
||||
const Entry *entry = BinarySearch::Find(aKey, aTable);
|
||||
|
||||
return (entry != nullptr) ? entry->mString : aNotFound;
|
||||
}
|
||||
|
||||
Stringify(void) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
||||
+7
-44
@@ -1510,52 +1510,15 @@ exit:
|
||||
|
||||
const char *Ip6::IpProtoToString(uint8_t aIpProto)
|
||||
{
|
||||
const char *retval;
|
||||
static constexpr Stringify::Entry kIpProtoTable[] = {
|
||||
{kProtoHopOpts, "HopOpts"}, {kProtoTcp, "TCP"}, {kProtoUdp, "UDP"},
|
||||
{kProtoIp6, "IP6"}, {kProtoRouting, "Routing"}, {kProtoFragment, "Frag"},
|
||||
{kProtoIcmp6, "ICMP6"}, {kProtoNone, "None"}, {kProtoDstOpts, "DstOpts"},
|
||||
};
|
||||
|
||||
switch (aIpProto)
|
||||
{
|
||||
case kProtoHopOpts:
|
||||
retval = "HopOpts";
|
||||
break;
|
||||
static_assert(Stringify::IsSorted(kIpProtoTable), "kIpProtoTable is not sorted");
|
||||
|
||||
case kProtoTcp:
|
||||
retval = "TCP";
|
||||
break;
|
||||
|
||||
case kProtoUdp:
|
||||
retval = "UDP";
|
||||
break;
|
||||
|
||||
case kProtoIp6:
|
||||
retval = "IP6";
|
||||
break;
|
||||
|
||||
case kProtoRouting:
|
||||
retval = "Routing";
|
||||
break;
|
||||
|
||||
case kProtoFragment:
|
||||
retval = "Frag";
|
||||
break;
|
||||
|
||||
case kProtoIcmp6:
|
||||
retval = "ICMP6";
|
||||
break;
|
||||
|
||||
case kProtoNone:
|
||||
retval = "None";
|
||||
break;
|
||||
|
||||
case kProtoDstOpts:
|
||||
retval = "DstOpts";
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
return Stringify::Lookup(aIpProto, kIpProtoTable, "Unknown");
|
||||
}
|
||||
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 lookup table (binary search) functionality.
|
||||
*/
|
||||
|
||||
#ifndef LOOKUP_TABLE_HPP_
|
||||
#define LOOKUP_TABLE_HPP_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/error.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Utils {
|
||||
|
||||
/**
|
||||
* This class implements lookup table (using binary search) functionality.
|
||||
*
|
||||
*/
|
||||
class LookupTable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This struct represents an example of a lookup table entry.
|
||||
*
|
||||
*/
|
||||
struct Entry
|
||||
{
|
||||
/**
|
||||
* This constructor initializes the entry with a given name.
|
||||
*
|
||||
* @param[in] aName The null-terminated name string with which to initialize the entry.
|
||||
*
|
||||
*/
|
||||
constexpr explicit Entry(const char *aName)
|
||||
: mName(aName)
|
||||
{
|
||||
}
|
||||
|
||||
const char *const mName;
|
||||
};
|
||||
|
||||
/**
|
||||
* This template method indicates whether a given entry table array is sorted based on entry's name string and in
|
||||
* alphabetical order and contains not duplicate entries.
|
||||
*
|
||||
* This method is `constexpr` and is intended for use in `static_assert`s to verify that a `constexpr` lookup table
|
||||
* array is sorted.
|
||||
*
|
||||
* @tparam EntryType The table entry type. The `EntryType` MUST provide `mName` member variable as a `const
|
||||
* char *`. For example, this can be realized by `EntryType` being a subclass of `Entry`.
|
||||
* @tparam kLength The array length (number of entries in the array).
|
||||
*
|
||||
* @note In the common use of this method as `IsSorted(sTable)` where sTable` is a fixed size array, the template
|
||||
* types/parameters do not need to be explicitly specified and can be deduced from the passed-in argument.
|
||||
*
|
||||
* @param[in] aTable A reference to an array of `kLength` entries on type `EntryType`
|
||||
*
|
||||
* @retval TRUE If the entries in @p aTable are sorted (alphabetical order).
|
||||
* @retval FALSE If the entries in @p aTable are not sorted.
|
||||
*
|
||||
*/
|
||||
template <class EntryType, uint16_t kLength> static constexpr bool IsSorted(const EntryType (&aTable)[kLength])
|
||||
{
|
||||
return IsSorted(&aTable[0], kLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* This template method performs binary search in a given sorted table array to find an entry matching a given name.
|
||||
*
|
||||
* @note This method requires the array to be sorted, otherwise its behavior is undefined.
|
||||
*
|
||||
* @tparam EntryType The table entry type. The `EntryType` MUST provide `mName` member variable as a `const
|
||||
* char *`. For example, this can be realized by `EntryType` being a subclass of `Entry`.
|
||||
* @tparam kLength The array length (number of entries in the array).
|
||||
*
|
||||
* @note In the common use of this method as `Find(name, sTable)` where sTable` is a fixed size array, the template
|
||||
* types/parameters do not need to be explicitly specified and can be deduced from the passed-in argument.
|
||||
*
|
||||
* @param[in] aName A name string to search for within the table.
|
||||
* @param[in] aTable A reference to an array of `kLength` entries on type `EntryType`
|
||||
*
|
||||
* @returns A pointer to the entry in the table if a match is found, otherwise `nullptr` (no match in table).
|
||||
*
|
||||
*/
|
||||
template <class EntryType, uint16_t kLength>
|
||||
static const EntryType *Find(const char *aName, const EntryType (&aTable)[kLength])
|
||||
{
|
||||
return reinterpret_cast<const EntryType *>(
|
||||
Find(aName, &aTable[0], kLength, sizeof(aTable[0]), GetName<EntryType>));
|
||||
}
|
||||
|
||||
private:
|
||||
typedef const char *(&NameGetter)(const void *aPointer);
|
||||
|
||||
template <class EntryType> static const char *GetName(const void *aPointer)
|
||||
{
|
||||
return reinterpret_cast<const EntryType *>(aPointer)->mName;
|
||||
}
|
||||
|
||||
template <class EntryType> static constexpr bool IsSorted(const EntryType *aTable, uint16_t aLength)
|
||||
{
|
||||
return (aLength <= 1) ? true
|
||||
: AreInOrder(aTable[0].mName, aTable[1].mName) && IsSorted(aTable + 1, aLength - 1);
|
||||
}
|
||||
|
||||
constexpr static bool AreInOrder(const char *aFirst, const char *aSecond)
|
||||
{
|
||||
return (*aFirst < *aSecond)
|
||||
? true
|
||||
: ((*aFirst > *aSecond) || (*aFirst == '\0') ? false : AreInOrder(aFirst + 1, aSecond + 1));
|
||||
}
|
||||
|
||||
static const void *Find(const char *aName,
|
||||
const void *aTable,
|
||||
uint16_t aLength,
|
||||
uint16_t aTableEntrySize,
|
||||
NameGetter aNameGetter);
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace ot
|
||||
|
||||
#endif // LOOKUP_TABLE_HPP_
|
||||
+21
-21
@@ -107,6 +107,27 @@ target_link_libraries(ot-test-array
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_executable(ot-test-binary-search
|
||||
test_binary_search.cpp
|
||||
)
|
||||
|
||||
target_include_directories(ot-test-binary-search
|
||||
PRIVATE
|
||||
${COMMON_INCLUDES}
|
||||
)
|
||||
|
||||
target_compile_options(ot-test-binary-search
|
||||
PRIVATE
|
||||
${COMMON_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(ot-test-binary-search
|
||||
PRIVATE
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_test(NAME ot-test-binary-search COMMAND ot-test-binary-search)
|
||||
|
||||
add_executable(ot-test-child
|
||||
test_child.cpp
|
||||
)
|
||||
@@ -430,27 +451,6 @@ target_link_libraries(ot-test-linked-list
|
||||
|
||||
add_test(NAME ot-test-linked-list COMMAND ot-test-linked-list)
|
||||
|
||||
add_executable(ot-test-lookup-table
|
||||
test_lookup_table.cpp
|
||||
)
|
||||
|
||||
target_include_directories(ot-test-lookup-table
|
||||
PRIVATE
|
||||
${COMMON_INCLUDES}
|
||||
)
|
||||
|
||||
target_compile_options(ot-test-lookup-table
|
||||
PRIVATE
|
||||
${COMMON_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(ot-test-lookup-table
|
||||
PRIVATE
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_test(NAME ot-test-lookup-table COMMAND ot-test-lookup-table)
|
||||
|
||||
add_executable(ot-test-lowpan
|
||||
test_lowpan.cpp
|
||||
)
|
||||
|
||||
@@ -110,6 +110,7 @@ if OPENTHREAD_ENABLE_FTD
|
||||
check_PROGRAMS += \
|
||||
ot-test-aes \
|
||||
ot-test-array \
|
||||
ot-test-binary-search \
|
||||
ot-test-checksum \
|
||||
ot-test-child \
|
||||
ot-test-child-table \
|
||||
@@ -125,7 +126,6 @@ check_PROGRAMS += \
|
||||
ot-test-ip-address \
|
||||
ot-test-link-quality \
|
||||
ot-test-linked-list \
|
||||
ot-test-lookup-table \
|
||||
ot-test-lowpan \
|
||||
ot-test-mac-frame \
|
||||
ot-test-macros \
|
||||
@@ -188,6 +188,9 @@ ot_test_aes_SOURCES = $(COMMON_SOURCES) test_aes.cpp
|
||||
ot_test_array_LDADD = $(COMMON_LDADD)
|
||||
ot_test_array_SOURCES = $(COMMON_SOURCES) test_array.cpp
|
||||
|
||||
ot_test_binary_search_LDADD = $(COMMON_LDADD)
|
||||
ot_test_binary_search_SOURCES = $(COMMON_SOURCES) test_binary_search.cpp
|
||||
|
||||
ot_test_checksum_LDADD = $(COMMON_LDADD)
|
||||
ot_test_checksum_SOURCES = $(COMMON_SOURCES) test_checksum.cpp
|
||||
|
||||
@@ -236,9 +239,6 @@ ot_test_link_quality_SOURCES = $(COMMON_SOURCES) test_link_quality.cpp
|
||||
ot_test_linked_list_LDADD = $(COMMON_LDADD)
|
||||
ot_test_linked_list_SOURCES = $(COMMON_SOURCES) test_linked_list.cpp
|
||||
|
||||
ot_test_lookup_table_LDADD = $(COMMON_LDADD)
|
||||
ot_test_lookup_table_SOURCES = $(COMMON_SOURCES) test_lookup_table.cpp
|
||||
|
||||
ot_test_lowpan_LDADD = $(COMMON_LDADD)
|
||||
ot_test_lowpan_SOURCES = $(COMMON_SOURCES) test_lowpan.cpp
|
||||
|
||||
|
||||
@@ -29,81 +29,69 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "test_platform.h"
|
||||
#include "test_util.h"
|
||||
|
||||
#include <openthread/config.h>
|
||||
|
||||
#include "common/instance.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
#include "common/binary_search.hpp"
|
||||
#include "common/string.hpp"
|
||||
|
||||
#include "test_util.h"
|
||||
namespace ot {
|
||||
|
||||
typedef ot::Utils::LookupTable::Entry Entry;
|
||||
|
||||
struct TableEntryBase
|
||||
void TestBinarySearch(void)
|
||||
{
|
||||
constexpr explicit TableEntryBase(uint8_t aValue)
|
||||
: mValue(aValue)
|
||||
static constexpr uint16_t kMaxNameSize = 30;
|
||||
|
||||
struct Entry
|
||||
{
|
||||
}
|
||||
int Compare(const char *aName) const { return strcmp(aName, mName); }
|
||||
|
||||
uint8_t mValue;
|
||||
};
|
||||
constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond)
|
||||
{
|
||||
return AreStringsInOrder(aFirst.mName, aSecond.mName);
|
||||
}
|
||||
|
||||
struct TableEntry : public TableEntryBase, public Entry
|
||||
{
|
||||
constexpr TableEntry(const char *aName, uint8_t aValue)
|
||||
: TableEntryBase(aValue)
|
||||
, Entry(aName)
|
||||
, mUint16(0xabba)
|
||||
, mUint8(aValue)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t mUint16;
|
||||
uint8_t mUint8;
|
||||
};
|
||||
|
||||
void TestLookupTable(void)
|
||||
{
|
||||
enum : uint16_t
|
||||
{
|
||||
kMaxNameSize = 30,
|
||||
const char *mName;
|
||||
uint8_t mRank;
|
||||
};
|
||||
|
||||
constexpr TableEntry kTable[] = {
|
||||
constexpr Entry kTable[] = {
|
||||
{"arkham city", 9}, {"arkham knight", 7}, {"bloodborne", 10}, {"god of war", 10}, {"horizon", 9},
|
||||
{"infamous", 7}, {"last guardian", 7}, {"last of us", 11}, {"last of us part 2", 8}, {"mass effect", 8},
|
||||
{"sekiro", 10}, {"tomb raider", 9}, {"uncharted", 9},
|
||||
};
|
||||
|
||||
constexpr Entry kUnsortedTable[] = {Entry("z"), Entry("a"), Entry("b")};
|
||||
constexpr Entry kDuplicateEntryTable[] = {Entry("duplicate"), Entry("duplicate")};
|
||||
constexpr Entry kUnsortedTable[] = {{"z", 0}, {"a", 0}, {"b", 0}};
|
||||
constexpr Entry kDuplicateEntryTable[] = {{"duplicate", 1}, {"duplicate", 2}};
|
||||
|
||||
static_assert(ot::Utils::LookupTable::IsSorted(kTable), "LookupTable::IsSorted() failed");
|
||||
static_assert(!ot::Utils::LookupTable::IsSorted(kUnsortedTable), "failed for unsorted table");
|
||||
static_assert(!ot::Utils::LookupTable::IsSorted(kDuplicateEntryTable), "failed for table with duplicate entries");
|
||||
static_assert(BinarySearch::IsSorted(kTable), "IsSorted() failed");
|
||||
static_assert(!BinarySearch::IsSorted(kUnsortedTable), "failed for unsorted table");
|
||||
static_assert(!BinarySearch::IsSorted(kDuplicateEntryTable), "failed for table with duplicate entries");
|
||||
|
||||
for (const TableEntry &tableEntry : kTable)
|
||||
for (const Entry &tableEntry : kTable)
|
||||
{
|
||||
const TableEntry *entry;
|
||||
char name[kMaxNameSize];
|
||||
const Entry *entry;
|
||||
char name[kMaxNameSize];
|
||||
|
||||
strcpy(name, tableEntry.mName);
|
||||
|
||||
entry = ot::Utils::LookupTable::Find(name, kTable);
|
||||
VerifyOrQuit(entry == &tableEntry, "LookupTable::Find() failed");
|
||||
entry = BinarySearch::Find(name, kTable);
|
||||
VerifyOrQuit(entry == &tableEntry, "BinarySearch::Find() failed");
|
||||
|
||||
name[strlen(name) - 1] = '\0';
|
||||
entry = ot::Utils::LookupTable::Find(name, kTable);
|
||||
VerifyOrQuit(entry == nullptr, "LookupTable::Find() failed with non-matching name");
|
||||
|
||||
entry = BinarySearch::Find(name, kTable);
|
||||
VerifyOrQuit(entry == nullptr, "BinarySearch::Find() failed with non-matching name");
|
||||
}
|
||||
|
||||
VerifyOrQuit(ot::Utils::LookupTable::Find("dragon age", kTable) == nullptr, "failed with non-exiting match");
|
||||
VerifyOrQuit(BinarySearch::Find("dragon age", kTable) == nullptr, "failed with non-exiting match");
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TestLookupTable();
|
||||
ot::TestBinarySearch();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -309,6 +309,13 @@ void TestStringToLowercase(void)
|
||||
printf(" -- PASS\n");
|
||||
}
|
||||
|
||||
static_assert(ot::AreStringsInOrder("a", "b"), "AreStringsInOrder() failed");
|
||||
static_assert(ot::AreStringsInOrder("aa", "aaa"), "AreStringsInOrder() failed");
|
||||
static_assert(ot::AreStringsInOrder("", "a"), "AreStringsInOrder() failed");
|
||||
static_assert(!ot::AreStringsInOrder("cd", "cd"), "AreStringsInOrder() failed");
|
||||
static_assert(!ot::AreStringsInOrder("z", "abcd"), "AreStringsInOrder() failed");
|
||||
static_assert(!ot::AreStringsInOrder("0", ""), "AreStringsInOrder() failed");
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
|
||||
Reference in New Issue
Block a user