[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:
Abtin Keshavarzian
2020-10-02 17:35:35 -07:00
committed by GitHub
parent 946141ed32
commit 4db030c6c9
26 changed files with 602 additions and 260 deletions
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+25 -3
View File
@@ -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];
+10 -24
View File
@@ -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;
}
+15 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+11 -25
View File
@@ -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;
}
+15 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+3
View File
@@ -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",
]
+2
View File
@@ -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
)
+3
View File
@@ -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 \
-1
View File
@@ -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
+92
View File
@@ -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
+151
View File
@@ -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_
+23
View File
@@ -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
+4
View File
@@ -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
+112
View File
@@ -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;
}