mirror of
https://github.com/espressif/openthread.git
synced 2026-07-30 23:57:47 +00:00
[utils] adding 'LookupTable' class and use it in CLI (#5602)
This commit adds `LookupTable` module which provides helper methods to perform binary search in a generic sorted lookup table for specific entry matching a given name string. It also provides `constexpr` methods to check (at build-time) whether a lookup table array is sorted. This commit also updates CLI modules to use the `LookupTable` methods for command lookup. It also adds a unit test for the lookup table.
This commit is contained in:
@@ -285,6 +285,7 @@ LOCAL_SRC_FILES := \
|
||||
src/core/utils/child_supervision.cpp \
|
||||
src/core/utils/heap.cpp \
|
||||
src/core/utils/jam_detector.cpp \
|
||||
src/core/utils/lookup_table.cpp \
|
||||
src/core/utils/parse_cmdline.cpp \
|
||||
src/core/utils/slaac_address.cpp \
|
||||
src/lib/hdlc/hdlc.cpp \
|
||||
|
||||
+2
-33
@@ -328,8 +328,6 @@ otError Interpreter::ProcessHelp(uint8_t aArgsLength, char *aArgs[])
|
||||
OT_UNUSED_VARIABLE(aArgsLength);
|
||||
OT_UNUSED_VARIABLE(aArgs);
|
||||
|
||||
static_assert(IsArraySorted(sCommands, OT_ARRAY_LENGTH(sCommands)), "Command list is not sorted");
|
||||
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
OutputLine(command.mName);
|
||||
@@ -4201,35 +4199,6 @@ otError Interpreter::ProcessDiag(uint8_t aArgsLength, char *aArgs[])
|
||||
}
|
||||
#endif
|
||||
|
||||
const Interpreter::Command *Interpreter::FindCommand(const char *aName) const
|
||||
{
|
||||
const Command *rval = nullptr;
|
||||
uint16_t left = 0;
|
||||
uint16_t right = OT_ARRAY_LENGTH(sCommands);
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
uint16_t middle = (left + right) / 2;
|
||||
int compare = strcmp(aName, sCommands[middle].mName);
|
||||
|
||||
if (compare == 0)
|
||||
{
|
||||
rval = &sCommands[middle];
|
||||
break;
|
||||
}
|
||||
else if (compare > 0)
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
right = middle;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength)
|
||||
{
|
||||
char * aArgs[kMaxArgs] = {nullptr};
|
||||
@@ -4250,11 +4219,11 @@ void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength)
|
||||
OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands."));
|
||||
#endif
|
||||
|
||||
command = FindCommand(cmdName);
|
||||
command = Utils::LookupTable::Find(cmdName, sCommands);
|
||||
|
||||
if (command != nullptr)
|
||||
{
|
||||
OutputResult((this->*command->mCommand)(aArgsLength - 1, &aArgs[1]));
|
||||
OutputResult((this->*command->mHandler)(aArgsLength - 1, &aArgs[1]));
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
|
||||
+4
-13
@@ -61,6 +61,7 @@
|
||||
#include "common/instance.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "net/icmp6.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
@@ -290,10 +291,9 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Interpreter::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (Interpreter::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
const Command *FindCommand(const char *aName) const;
|
||||
otError ParsePingInterval(const char *aString, uint32_t &aInterval);
|
||||
static otError ParseJoinerDiscerner(char *aString, otJoinerDiscerner &aJoinerDiscerner);
|
||||
|
||||
@@ -568,17 +568,6 @@ private:
|
||||
}
|
||||
void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo);
|
||||
|
||||
constexpr static bool AreSorted(const char *aFirst, const char *aSecond)
|
||||
{
|
||||
return (*aFirst < *aSecond) ? true : ((*aFirst > *aSecond) ? false : AreSorted(aFirst + 1, aSecond + 1));
|
||||
}
|
||||
|
||||
constexpr static bool IsArraySorted(const Interpreter::Command *aList, uint16_t aLength)
|
||||
{
|
||||
return (aLength <= 1) ? true
|
||||
: AreSorted(aList[0].mName, aList[1].mName) && IsArraySorted(aList + 1, aLength - 1);
|
||||
}
|
||||
|
||||
static constexpr Command sCommands[] = {
|
||||
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
||||
{"bbr", &Interpreter::ProcessBackboneRouter},
|
||||
@@ -737,6 +726,8 @@ private:
|
||||
{"version", &Interpreter::ProcessVersion},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
const otCliCommand *mUserCommands;
|
||||
uint8_t mUserCommandsLength;
|
||||
uint16_t mPingLength;
|
||||
|
||||
+10
-35
@@ -43,24 +43,7 @@
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
const Coap::Command Coap::sCommands[] = {
|
||||
{"help", &Coap::ProcessHelp},
|
||||
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
|
||||
{"cancel", &Coap::ProcessCancel},
|
||||
#endif
|
||||
{"delete", &Coap::ProcessRequest},
|
||||
{"get", &Coap::ProcessRequest},
|
||||
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
|
||||
{"observe", &Coap::ProcessRequest},
|
||||
#endif
|
||||
{"parameters", &Coap::ProcessParameters},
|
||||
{"post", &Coap::ProcessRequest},
|
||||
{"put", &Coap::ProcessRequest},
|
||||
{"resource", &Coap::ProcessResource},
|
||||
{"set", &Coap::ProcessSet},
|
||||
{"start", &Coap::ProcessStart},
|
||||
{"stop", &Coap::ProcessStop},
|
||||
};
|
||||
constexpr Coap::Command Coap::sCommands[];
|
||||
|
||||
Coap::Coap(Interpreter &aInterpreter)
|
||||
: mInterpreter(aInterpreter)
|
||||
@@ -513,25 +496,17 @@ exit:
|
||||
|
||||
otError Coap::Process(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
otError error = OT_ERROR_INVALID_ARGS;
|
||||
const Command *command;
|
||||
|
||||
if (aArgsLength < 1)
|
||||
{
|
||||
IgnoreError(ProcessHelp(0, nullptr));
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
if (strcmp(aArgs[0], command.mName) == 0)
|
||||
{
|
||||
error = (this->*command.mCommand)(aArgsLength, aArgs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
VerifyOrExit(aArgsLength != 0, IgnoreError(ProcessHelp(0, nullptr)));
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0], sCommands);
|
||||
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
error = (this->*command->mHandler)(aArgsLength, aArgs);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
+24
-3
@@ -39,6 +39,7 @@
|
||||
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
|
||||
|
||||
#include "coap/coap_message.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
@@ -79,7 +80,7 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Coap::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (Coap::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
|
||||
@@ -124,8 +125,28 @@ private:
|
||||
return mUseDefaultResponseTxParameters ? nullptr : &mResponseTxParameters;
|
||||
}
|
||||
|
||||
static const Command sCommands[];
|
||||
Interpreter & mInterpreter;
|
||||
static constexpr Command sCommands[] = {
|
||||
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
|
||||
{"cancel", &Coap::ProcessCancel},
|
||||
#endif
|
||||
{"delete", &Coap::ProcessRequest},
|
||||
{"get", &Coap::ProcessRequest},
|
||||
{"help", &Coap::ProcessHelp},
|
||||
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
|
||||
{"observe", &Coap::ProcessRequest},
|
||||
#endif
|
||||
{"parameters", &Coap::ProcessParameters},
|
||||
{"post", &Coap::ProcessRequest},
|
||||
{"put", &Coap::ProcessRequest},
|
||||
{"resource", &Coap::ProcessResource},
|
||||
{"set", &Coap::ProcessSet},
|
||||
{"start", &Coap::ProcessStart},
|
||||
{"stop", &Coap::ProcessStop},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
Interpreter &mInterpreter;
|
||||
|
||||
bool mUseDefaultRequestTxParameters;
|
||||
bool mUseDefaultResponseTxParameters;
|
||||
|
||||
+10
-31
@@ -45,20 +45,7 @@
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
const CoapSecure::Command CoapSecure::sCommands[] = {
|
||||
{"help", &CoapSecure::ProcessHelp}, {"connect", &CoapSecure::ProcessConnect},
|
||||
{"delete", &CoapSecure::ProcessRequest}, {"disconnect", &CoapSecure::ProcessDisconnect},
|
||||
{"get", &CoapSecure::ProcessRequest}, {"post", &CoapSecure::ProcessRequest},
|
||||
{"put", &CoapSecure::ProcessRequest}, {"resource", &CoapSecure::ProcessResource},
|
||||
{"set", &CoapSecure::ProcessSet}, {"start", &CoapSecure::ProcessStart},
|
||||
{"stop", &CoapSecure::ProcessStop},
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
|
||||
{"psk", &CoapSecure::ProcessPsk},
|
||||
#endif
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
{"x509", &CoapSecure::ProcessX509},
|
||||
#endif
|
||||
};
|
||||
constexpr CoapSecure::Command CoapSecure::sCommands[];
|
||||
|
||||
CoapSecure::CoapSecure(Interpreter &aInterpreter)
|
||||
: mInterpreter(aInterpreter)
|
||||
@@ -410,25 +397,17 @@ otError CoapSecure::ProcessX509(uint8_t aArgsLength, char *aArgs[])
|
||||
|
||||
otError CoapSecure::Process(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
otError error = OT_ERROR_INVALID_ARGS;
|
||||
const Command *command;
|
||||
|
||||
if (aArgsLength < 1)
|
||||
{
|
||||
IgnoreError(ProcessHelp(0, nullptr));
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
if (strcmp(aArgs[0], command.mName) == 0)
|
||||
{
|
||||
error = (this->*command.mCommand)(aArgsLength, aArgs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
VerifyOrExit(aArgsLength != 0, IgnoreError(ProcessHelp(0, nullptr)));
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0], sCommands);
|
||||
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
error = (this->*command->mHandler)(aArgsLength, aArgs);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
#include "coap/coap_message.hpp"
|
||||
#include "coap/coap_secure.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
#ifndef CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
|
||||
#define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 0
|
||||
@@ -86,7 +87,7 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (CoapSecure::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (CoapSecure::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
void PrintPayload(otMessage *aMessage) const;
|
||||
@@ -118,8 +119,29 @@ private:
|
||||
static void HandleConnected(bool aConnected, void *aContext);
|
||||
void HandleConnected(bool aConnected);
|
||||
|
||||
static const Command sCommands[];
|
||||
Interpreter & mInterpreter;
|
||||
static constexpr Command sCommands[] = {
|
||||
{"connect", &CoapSecure::ProcessConnect},
|
||||
{"delete", &CoapSecure::ProcessRequest},
|
||||
{"disconnect", &CoapSecure::ProcessDisconnect},
|
||||
{"get", &CoapSecure::ProcessRequest},
|
||||
{"help", &CoapSecure::ProcessHelp},
|
||||
{"post", &CoapSecure::ProcessRequest},
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
|
||||
{"psk", &CoapSecure::ProcessPsk},
|
||||
#endif
|
||||
{"put", &CoapSecure::ProcessRequest},
|
||||
{"resource", &CoapSecure::ProcessResource},
|
||||
{"set", &CoapSecure::ProcessSet},
|
||||
{"start", &CoapSecure::ProcessStart},
|
||||
{"stop", &CoapSecure::ProcessStop},
|
||||
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
{"x509", &CoapSecure::ProcessX509},
|
||||
#endif
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
Interpreter &mInterpreter;
|
||||
|
||||
otCoapResource mResource;
|
||||
char mUriPath[kMaxUriLength];
|
||||
|
||||
@@ -40,14 +40,7 @@
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
const Commissioner::Command Commissioner::sCommands[] = {
|
||||
{"help", &Commissioner::ProcessHelp}, {"announce", &Commissioner::ProcessAnnounce},
|
||||
{"energy", &Commissioner::ProcessEnergy}, {"joiner", &Commissioner::ProcessJoiner},
|
||||
{"mgmtget", &Commissioner::ProcessMgmtGet}, {"mgmtset", &Commissioner::ProcessMgmtSet},
|
||||
{"panid", &Commissioner::ProcessPanId}, {"provisioningurl", &Commissioner::ProcessProvisioningUrl},
|
||||
{"sessionid", &Commissioner::ProcessSessionId}, {"start", &Commissioner::ProcessStart},
|
||||
{"state", &Commissioner::ProcessState}, {"stop", &Commissioner::ProcessStop},
|
||||
};
|
||||
constexpr Commissioner::Command Commissioner::sCommands[];
|
||||
|
||||
otError Commissioner::ProcessHelp(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
@@ -436,24 +429,17 @@ otError Commissioner::ProcessState(uint8_t aArgsLength, char *aArgs[])
|
||||
|
||||
otError Commissioner::Process(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
const Command *command;
|
||||
|
||||
if (aArgsLength < 1)
|
||||
{
|
||||
IgnoreError(ProcessHelp(0, nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
if (strcmp(aArgs[0], command.mName) == 0)
|
||||
{
|
||||
error = (this->*command.mCommand)(aArgsLength, aArgs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
VerifyOrExit(aArgsLength != 0, IgnoreError(ProcessHelp(0, nullptr)));
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0], sCommands);
|
||||
VerifyOrExit(command != nullptr, OT_NOOP);
|
||||
|
||||
error = (this->*command->mHandler)(aArgsLength, aArgs);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
|
||||
#include <openthread/commissioner.h>
|
||||
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
|
||||
|
||||
namespace ot {
|
||||
@@ -81,7 +83,7 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Commissioner::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (Commissioner::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
otError ProcessHelp(uint8_t aArgsLength, char *aArgs[]);
|
||||
@@ -119,8 +121,18 @@ private:
|
||||
|
||||
static const char *StateToString(otCommissionerState aState);
|
||||
|
||||
static const Command sCommands[];
|
||||
Interpreter & mInterpreter;
|
||||
static constexpr Command sCommands[] = {
|
||||
{"announce", &Commissioner::ProcessAnnounce}, {"energy", &Commissioner::ProcessEnergy},
|
||||
{"help", &Commissioner::ProcessHelp}, {"joiner", &Commissioner::ProcessJoiner},
|
||||
{"mgmtget", &Commissioner::ProcessMgmtGet}, {"mgmtset", &Commissioner::ProcessMgmtSet},
|
||||
{"panid", &Commissioner::ProcessPanId}, {"provisioningurl", &Commissioner::ProcessProvisioningUrl},
|
||||
{"sessionid", &Commissioner::ProcessSessionId}, {"start", &Commissioner::ProcessStart},
|
||||
{"state", &Commissioner::ProcessState}, {"stop", &Commissioner::ProcessStop},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
Interpreter &mInterpreter;
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
+8
-34
@@ -44,31 +44,8 @@
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
const Dataset::Command Dataset::sCommands[] = {
|
||||
{"help", &Dataset::ProcessHelp},
|
||||
{"active", &Dataset::ProcessActive},
|
||||
{"activetimestamp", &Dataset::ProcessActiveTimestamp},
|
||||
{"channel", &Dataset::ProcessChannel},
|
||||
{"channelmask", &Dataset::ProcessChannelMask},
|
||||
{"clear", &Dataset::ProcessClear},
|
||||
{"commit", &Dataset::ProcessCommit},
|
||||
{"delay", &Dataset::ProcessDelay},
|
||||
{"extpanid", &Dataset::ProcessExtPanId},
|
||||
{"init", &Dataset::ProcessInit},
|
||||
{"masterkey", &Dataset::ProcessMasterKey},
|
||||
{"meshlocalprefix", &Dataset::ProcessMeshLocalPrefix},
|
||||
{"mgmtgetcommand", &Dataset::ProcessMgmtGetCommand},
|
||||
{"mgmtsetcommand", &Dataset::ProcessMgmtSetCommand},
|
||||
{"networkname", &Dataset::ProcessNetworkName},
|
||||
{"panid", &Dataset::ProcessPanId},
|
||||
{"pending", &Dataset::ProcessPending},
|
||||
{"pendingtimestamp", &Dataset::ProcessPendingTimestamp},
|
||||
{"pskc", &Dataset::ProcessPskc},
|
||||
{"securitypolicy", &Dataset::ProcessSecurityPolicy},
|
||||
{"set", &Dataset::ProcessSet},
|
||||
};
|
||||
|
||||
otOperationalDataset Dataset::sDataset;
|
||||
constexpr Dataset::Command Dataset::sCommands[];
|
||||
otOperationalDataset Dataset::sDataset;
|
||||
|
||||
otError Dataset::Print(otOperationalDataset &aDataset)
|
||||
{
|
||||
@@ -175,21 +152,18 @@ otError Dataset::Print(otOperationalDataset &aDataset)
|
||||
|
||||
otError Dataset::Process(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
const Command *command;
|
||||
|
||||
if (aArgsLength == 0)
|
||||
{
|
||||
ExitNow(error = Print(sDataset));
|
||||
}
|
||||
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
if (strcmp(aArgs[0], command.mName) == 0)
|
||||
{
|
||||
error = (this->*command.mCommand)(aArgsLength - 1, aArgs + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
command = Utils::LookupTable::Find(aArgs[0], sCommands);
|
||||
VerifyOrExit(command != nullptr, OT_NOOP);
|
||||
|
||||
error = (this->*command->mHandler)(aArgsLength - 1, aArgs + 1);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
|
||||
+29
-2
@@ -40,6 +40,8 @@
|
||||
|
||||
#include <openthread/dataset.h>
|
||||
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
@@ -70,7 +72,7 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Dataset::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (Dataset::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
otError Print(otOperationalDataset &aDataset);
|
||||
@@ -97,9 +99,34 @@ private:
|
||||
otError ProcessSecurityPolicy(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError ProcessSet(uint8_t aArgsLength, char *aArgs[]);
|
||||
|
||||
static constexpr Command sCommands[] = {
|
||||
{"active", &Dataset::ProcessActive},
|
||||
{"activetimestamp", &Dataset::ProcessActiveTimestamp},
|
||||
{"channel", &Dataset::ProcessChannel},
|
||||
{"channelmask", &Dataset::ProcessChannelMask},
|
||||
{"clear", &Dataset::ProcessClear},
|
||||
{"commit", &Dataset::ProcessCommit},
|
||||
{"delay", &Dataset::ProcessDelay},
|
||||
{"extpanid", &Dataset::ProcessExtPanId},
|
||||
{"help", &Dataset::ProcessHelp},
|
||||
{"init", &Dataset::ProcessInit},
|
||||
{"masterkey", &Dataset::ProcessMasterKey},
|
||||
{"meshlocalprefix", &Dataset::ProcessMeshLocalPrefix},
|
||||
{"mgmtgetcommand", &Dataset::ProcessMgmtGetCommand},
|
||||
{"mgmtsetcommand", &Dataset::ProcessMgmtSetCommand},
|
||||
{"networkname", &Dataset::ProcessNetworkName},
|
||||
{"panid", &Dataset::ProcessPanId},
|
||||
{"pending", &Dataset::ProcessPending},
|
||||
{"pendingtimestamp", &Dataset::ProcessPendingTimestamp},
|
||||
{"pskc", &Dataset::ProcessPskc},
|
||||
{"securitypolicy", &Dataset::ProcessSecurityPolicy},
|
||||
{"set", &Dataset::ProcessSet},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
Interpreter &mInterpreter;
|
||||
|
||||
static const Command sCommands[];
|
||||
static otOperationalDataset sDataset;
|
||||
};
|
||||
|
||||
|
||||
+10
-20
@@ -42,10 +42,7 @@
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
const Joiner::Command Joiner::sCommands[] = {
|
||||
{"discerner", &Joiner::ProcessDiscerner}, {"help", &Joiner::ProcessHelp}, {"id", &Joiner::ProcessId},
|
||||
{"start", &Joiner::ProcessStart}, {"stop", &Joiner::ProcessStop},
|
||||
};
|
||||
constexpr Joiner::Command Joiner::sCommands[];
|
||||
|
||||
otError Joiner::ProcessDiscerner(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
@@ -139,24 +136,17 @@ otError Joiner::ProcessStop(uint8_t aArgsLength, char *aArgs[])
|
||||
|
||||
otError Joiner::Process(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
const Command *command;
|
||||
|
||||
if (aArgsLength < 1)
|
||||
{
|
||||
IgnoreError(ProcessHelp(0, nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
if (strcmp(aArgs[0], command.mName) == 0)
|
||||
{
|
||||
error = (this->*command.mCommand)(aArgsLength, aArgs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
VerifyOrExit(aArgsLength != 0, IgnoreError(ProcessHelp(0, nullptr)));
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0], sCommands);
|
||||
VerifyOrExit(command != nullptr, OT_NOOP);
|
||||
|
||||
error = (this->*command->mHandler)(aArgsLength, aArgs);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -38,6 +38,8 @@
|
||||
|
||||
#include <openthread/joiner.h>
|
||||
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_JOINER_ENABLE
|
||||
|
||||
namespace ot {
|
||||
@@ -76,7 +78,7 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (Joiner::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (Joiner::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
otError ProcessDiscerner(uint8_t aArgsLength, char *aArgs[]);
|
||||
@@ -88,8 +90,14 @@ private:
|
||||
static void HandleCallback(otError aError, void *aContext);
|
||||
void HandleCallback(otError aError);
|
||||
|
||||
static const Command sCommands[];
|
||||
Interpreter & mInterpreter;
|
||||
static constexpr Command sCommands[] = {
|
||||
{"discerner", &Joiner::ProcessDiscerner}, {"help", &Joiner::ProcessHelp}, {"id", &Joiner::ProcessId},
|
||||
{"start", &Joiner::ProcessStart}, {"stop", &Joiner::ProcessStop},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
Interpreter &mInterpreter;
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
@@ -44,14 +44,7 @@ using ot::Encoding::BigEndian::HostSwap16;
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
const NetworkData::Command NetworkData::sCommands[] = {
|
||||
{"help", &NetworkData::ProcessHelp},
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
||||
{"register", &NetworkData::ProcessRegister},
|
||||
#endif
|
||||
{"show", &NetworkData::ProcessShow},
|
||||
{"steeringdata", &NetworkData::ProcessSteeringData},
|
||||
};
|
||||
constexpr NetworkData::Command NetworkData::sCommands[];
|
||||
|
||||
NetworkData::NetworkData(Interpreter &aInterpreter)
|
||||
: mInterpreter(aInterpreter)
|
||||
@@ -318,24 +311,17 @@ otError NetworkData::ProcessShow(uint8_t aArgsLength, char *aArgs[])
|
||||
|
||||
otError NetworkData::Process(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
const Command *command;
|
||||
|
||||
if (aArgsLength < 1)
|
||||
{
|
||||
IgnoreError(ProcessHelp(0, nullptr));
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
if (strcmp(aArgs[0], command.mName) == 0)
|
||||
{
|
||||
error = (this->*command.mCommand)(aArgsLength - 1, aArgs + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
VerifyOrExit(aArgsLength != 0, IgnoreError(ProcessHelp(0, nullptr)));
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0], sCommands);
|
||||
VerifyOrExit(command != nullptr, OT_NOOP);
|
||||
|
||||
error = (this->*command->mHandler)(aArgsLength - 1, aArgs + 1);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
|
||||
#include <openthread/netdata.h>
|
||||
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
@@ -95,7 +97,7 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (NetworkData::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (NetworkData::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
otError ProcessHelp(uint8_t aArgsLength, char *aArgs[]);
|
||||
@@ -110,8 +112,18 @@ private:
|
||||
void OutputRoutes(void);
|
||||
void OutputServices(void);
|
||||
|
||||
static const Command sCommands[];
|
||||
Interpreter & mInterpreter;
|
||||
static constexpr Command sCommands[] = {
|
||||
{"help", &NetworkData::ProcessHelp},
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
||||
{"register", &NetworkData::ProcessRegister},
|
||||
#endif
|
||||
{"show", &NetworkData::ProcessShow},
|
||||
{"steeringdata", &NetworkData::ProcessSteeringData},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
Interpreter &mInterpreter;
|
||||
};
|
||||
|
||||
} // namespace Cli
|
||||
|
||||
+11
-24
@@ -44,13 +44,7 @@ using ot::Encoding::BigEndian::HostSwap16;
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
const UdpExample::Command UdpExample::sCommands[] = {{"help", &UdpExample::ProcessHelp},
|
||||
{"bind", &UdpExample::ProcessBind},
|
||||
{"close", &UdpExample::ProcessClose},
|
||||
{"connect", &UdpExample::ProcessConnect},
|
||||
{"linksecurity", &UdpExample::ProcessLinkSecurity},
|
||||
{"open", &UdpExample::ProcessOpen},
|
||||
{"send", &UdpExample::ProcessSend}};
|
||||
constexpr UdpExample::Command UdpExample::sCommands[];
|
||||
|
||||
UdpExample::UdpExample(Interpreter &aInterpreter)
|
||||
: mInterpreter(aInterpreter)
|
||||
@@ -289,24 +283,17 @@ exit:
|
||||
|
||||
otError UdpExample::Process(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_INVALID_COMMAND;
|
||||
otError error = OT_ERROR_INVALID_ARGS;
|
||||
const Command *command;
|
||||
|
||||
if (aArgsLength < 1)
|
||||
{
|
||||
IgnoreError(ProcessHelp(0, nullptr));
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const Command &command : sCommands)
|
||||
{
|
||||
if (strcmp(aArgs[0], command.mName) == 0)
|
||||
{
|
||||
error = (this->*command.mCommand)(aArgsLength - 1, aArgs + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
VerifyOrExit(aArgsLength != 0, IgnoreError(ProcessHelp(0, nullptr)));
|
||||
|
||||
command = Utils::LookupTable::Find(aArgs[0], sCommands);
|
||||
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
|
||||
|
||||
error = (this->*command->mHandler)(aArgsLength - 1, aArgs + 1);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
+16
-3
@@ -38,6 +38,8 @@
|
||||
|
||||
#include <openthread/udp.h>
|
||||
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Cli {
|
||||
|
||||
@@ -71,7 +73,7 @@ private:
|
||||
struct Command
|
||||
{
|
||||
const char *mName;
|
||||
otError (UdpExample::*mCommand)(uint8_t aArgsLength, char *aArgs[]);
|
||||
otError (UdpExample::*mHandler)(uint8_t aArgsLength, char *aArgs[]);
|
||||
};
|
||||
|
||||
enum PayloadType
|
||||
@@ -93,8 +95,19 @@ private:
|
||||
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
|
||||
void HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo);
|
||||
|
||||
static const Command sCommands[];
|
||||
Interpreter & mInterpreter;
|
||||
static constexpr Command sCommands[] = {
|
||||
{"bind", &UdpExample::ProcessBind},
|
||||
{"close", &UdpExample::ProcessClose},
|
||||
{"connect", &UdpExample::ProcessConnect},
|
||||
{"help", &UdpExample::ProcessHelp},
|
||||
{"linksecurity", &UdpExample::ProcessLinkSecurity},
|
||||
{"open", &UdpExample::ProcessOpen},
|
||||
{"send", &UdpExample::ProcessSend},
|
||||
};
|
||||
|
||||
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
|
||||
|
||||
Interpreter &mInterpreter;
|
||||
|
||||
bool mLinkSecurityEnabled;
|
||||
otUdpSocket mSocket;
|
||||
|
||||
@@ -571,6 +571,8 @@ openthread_core_files = [
|
||||
"utils/heap.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",
|
||||
@@ -604,6 +606,7 @@ openthread_radio_sources = [
|
||||
"radio/radio_callbacks.cpp",
|
||||
"radio/radio_platform.cpp",
|
||||
"thread/link_quality.cpp",
|
||||
"utils/lookup_table.cpp",
|
||||
"utils/parse_cmdline.cpp",
|
||||
]
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ set(COMMON_SOURCES
|
||||
utils/flash.cpp
|
||||
utils/heap.cpp
|
||||
utils/jam_detector.cpp
|
||||
utils/lookup_table.cpp
|
||||
utils/otns.cpp
|
||||
utils/parse_cmdline.cpp
|
||||
utils/slaac_address.cpp
|
||||
@@ -258,6 +259,7 @@ target_sources(openthread-radio PRIVATE
|
||||
radio/radio_callbacks.cpp
|
||||
radio/radio_platform.cpp
|
||||
thread/link_quality.cpp
|
||||
utils/lookup_table.cpp
|
||||
utils/parse_cmdline.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -258,6 +258,7 @@ SOURCES_COMMON = \
|
||||
utils/flash.cpp \
|
||||
utils/heap.cpp \
|
||||
utils/jam_detector.cpp \
|
||||
utils/lookup_table.cpp \
|
||||
utils/otns.cpp \
|
||||
utils/parse_cmdline.cpp \
|
||||
utils/slaac_address.cpp \
|
||||
@@ -293,6 +294,7 @@ libopenthread_radio_a_SOURCES = \
|
||||
radio/radio_callbacks.cpp \
|
||||
radio/radio_platform.cpp \
|
||||
thread/link_quality.cpp \
|
||||
utils/lookup_table.cpp \
|
||||
utils/parse_cmdline.cpp \
|
||||
$(NULL)
|
||||
|
||||
@@ -488,6 +490,7 @@ HEADERS_COMMON = \
|
||||
utils/flash.hpp \
|
||||
utils/heap.hpp \
|
||||
utils/jam_detector.hpp \
|
||||
utils/lookup_table.hpp \
|
||||
utils/otns.hpp \
|
||||
utils/parse_cmdline.hpp \
|
||||
utils/slaac_address.hpp \
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
#include "common/locator-getters.hpp"
|
||||
#include "common/random.hpp"
|
||||
#include "mac/mac_frame.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 implements the lookup table (binary search) functionality.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lookup_table.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 *entry;
|
||||
uint16_t left = 0;
|
||||
uint16_t right = aLength;
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
uint16_t middle = (left + right) / 2;
|
||||
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
|
||||
// (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.
|
||||
|
||||
entry = reinterpret_cast<const uint8_t *>(aTable) + aTableEntrySize * middle;
|
||||
|
||||
compare = strcmp(aName, aNameGetter(entry));
|
||||
|
||||
if (compare == 0)
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
else if (compare > 0)
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
right = middle;
|
||||
}
|
||||
}
|
||||
|
||||
entry = nullptr;
|
||||
|
||||
exit:
|
||||
return entry;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 <openthread/error.h>
|
||||
|
||||
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 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_
|
||||
@@ -271,6 +271,28 @@ target_link_libraries(test-linked-list
|
||||
|
||||
add_test(NAME test-linked-list COMMAND test-linked-list)
|
||||
|
||||
add_executable(test-lookup-table
|
||||
${COMMON_SOURCES}
|
||||
test_lookup_table.cpp
|
||||
)
|
||||
|
||||
target_include_directories(test-lookup-table
|
||||
PRIVATE
|
||||
${COMMON_INCLUDES}
|
||||
)
|
||||
|
||||
target_compile_options(test-lookup-table
|
||||
PRIVATE
|
||||
${COMMON_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(test-lookup-table
|
||||
PRIVATE
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_test(NAME test-lookup-table COMMAND test-lookup-table)
|
||||
|
||||
add_executable(test-lowpan
|
||||
${COMMON_SOURCES}
|
||||
test_lowpan.cpp
|
||||
@@ -592,6 +614,7 @@ set_target_properties(
|
||||
test-ip6-address
|
||||
test-link-quality
|
||||
test-linked-list
|
||||
test-lookup-table
|
||||
test-lowpan
|
||||
test-mac-frame
|
||||
test-message
|
||||
|
||||
@@ -116,6 +116,7 @@ check_PROGRAMS += \
|
||||
test-ip6-address \
|
||||
test-link-quality \
|
||||
test-linked-list \
|
||||
test-lookup-table \
|
||||
test-lowpan \
|
||||
test-mac-frame \
|
||||
test-message \
|
||||
@@ -203,6 +204,9 @@ test_link_quality_SOURCES = $(COMMON_SOURCES) test_link_quality.cpp
|
||||
test_linked_list_LDADD = $(COMMON_LDADD)
|
||||
test_linked_list_SOURCES = $(COMMON_SOURCES) test_linked_list.cpp
|
||||
|
||||
test_lookup_table_LDADD = $(COMMON_LDADD)
|
||||
test_lookup_table_SOURCES = $(COMMON_SOURCES) test_lookup_table.cpp
|
||||
|
||||
test_lowpan_LDADD = $(COMMON_LDADD)
|
||||
test_lowpan_SOURCES = $(COMMON_SOURCES) test_lowpan.cpp
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "test_platform.h"
|
||||
|
||||
#include <openthread/config.h>
|
||||
|
||||
#include "common/instance.hpp"
|
||||
#include "utils/lookup_table.hpp"
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
typedef ot::Utils::LookupTable::Entry Entry;
|
||||
|
||||
struct TableEntryBase
|
||||
{
|
||||
constexpr TableEntryBase(uint8_t aValue)
|
||||
: mValue(aValue)
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t mValue;
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
constexpr TableEntry 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[] = {{"z"}, {"a"}, {"b"}};
|
||||
constexpr Entry kDuplicateEntryTable[] = {"duplicate", "duplicate"};
|
||||
|
||||
static_assert(ot::Utils::LookupTable::IsSorted(kTable), "LookupTable::IsSorted() failed");
|
||||
static_assert(!ot::Utils::LookupTable::IsSorted(kUnsortedTable),
|
||||
"LookupTable::IsSorted() failed for unsorted table");
|
||||
static_assert(!ot::Utils::LookupTable::IsSorted(kDuplicateEntryTable),
|
||||
"LookupTable::IsSorted() failed for table with duplicate entries");
|
||||
|
||||
for (const TableEntry &tableEntry : kTable)
|
||||
{
|
||||
const TableEntry *entry;
|
||||
char name[kMaxNameSize];
|
||||
|
||||
strcpy(name, tableEntry.mName);
|
||||
|
||||
entry = ot::Utils::LookupTable::Find(name, kTable);
|
||||
VerifyOrQuit(entry == &tableEntry, "LookupTable::Find() failed");
|
||||
|
||||
name[strlen(name) - 1] = '\0';
|
||||
entry = ot::Utils::LookupTable::Find(name, kTable);
|
||||
VerifyOrQuit(entry == nullptr, "LookupTable::Find() failed with non-matching name");
|
||||
}
|
||||
|
||||
VerifyOrQuit(ot::Utils::LookupTable::Find("dragon age", kTable) == nullptr,
|
||||
"LookupTable::Find() failed with non exiting mathc");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TestLookupTable();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user