diff --git a/Android.mk b/Android.mk index fbbec045b..b340bea93 100644 --- a/Android.mk +++ b/Android.mk @@ -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 \ diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index e7259aefe..8f2fa6c55 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -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) { diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 1dbba05b5..f7edb91db 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -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; template using GetHandler = ValueType (&)(otInstance *); template 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; diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp index 56175f96c..20e88cf85 100644 --- a/src/cli/cli_coap.cpp +++ b/src/cli/cli_coap.cpp @@ -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); diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp index f1d158cf9..022bff0ba 100644 --- a/src/cli/cli_coap.hpp +++ b/src/cli/cli_coap.hpp @@ -41,8 +41,6 @@ #include #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; #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; diff --git a/src/cli/cli_coap_secure.cpp b/src/cli/cli_coap_secure.cpp index f6848948f..aae479829 100644 --- a/src/cli/cli_coap_secure.cpp +++ b/src/cli/cli_coap_secure.cpp @@ -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); diff --git a/src/cli/cli_coap_secure.hpp b/src/cli/cli_coap_secure.hpp index 18f789780..3bd487290 100644 --- a/src/cli/cli_coap_secure.hpp +++ b/src/cli/cli_coap_secure.hpp @@ -43,8 +43,6 @@ #include #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; #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; diff --git a/src/cli/cli_commissioner.cpp b/src/cli/cli_commissioner.cpp index 180227cc9..e8596fc34 100644 --- a/src/cli/cli_commissioner.cpp +++ b/src/cli/cli_commissioner.cpp @@ -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); diff --git a/src/cli/cli_commissioner.hpp b/src/cli/cli_commissioner.hpp index 175c5cde1..7360818e5 100644 --- a/src/cli/cli_commissioner.hpp +++ b/src/cli/cli_commissioner.hpp @@ -39,8 +39,6 @@ #include #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; 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 diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp index 158a6c90e..61641a243 100644 --- a/src/cli/cli_dataset.cpp +++ b/src/cli/cli_dataset.cpp @@ -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); diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp index b712c6cf2..d32afdd56 100644 --- a/src/cli/cli_dataset.hpp +++ b/src/cli/cli_dataset.hpp @@ -41,8 +41,6 @@ #include #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; 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; }; diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp index ca25134e2..e5e12c9e9 100644 --- a/src/cli/cli_history.cpp +++ b/src/cli/cli_history.cpp @@ -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); diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp index ac922a212..95cd1a312 100644 --- a/src/cli/cli_history.hpp +++ b/src/cli/cli_history.hpp @@ -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; 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 diff --git a/src/cli/cli_joiner.cpp b/src/cli/cli_joiner.cpp index fdbee48f9..a4c6e73b2 100644 --- a/src/cli/cli_joiner.cpp +++ b/src/cli/cli_joiner.cpp @@ -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); diff --git a/src/cli/cli_joiner.hpp b/src/cli/cli_joiner.hpp index de354921d..f86a62dbb 100644 --- a/src/cli/cli_joiner.hpp +++ b/src/cli/cli_joiner.hpp @@ -39,8 +39,6 @@ #include #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; 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 diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index 9765fb240..0634aa18b 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -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); diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp index e8304556c..a45484a06 100644 --- a/src/cli/cli_network_data.hpp +++ b/src/cli/cli_network_data.hpp @@ -39,8 +39,6 @@ #include #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; 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 diff --git a/src/cli/cli_output.hpp b/src/cli/cli_output.hpp index 83e0e8b6f..a7aa402af 100644 --- a/src/cli/cli_output.hpp +++ b/src/cli/cli_output.hpp @@ -41,6 +41,9 @@ #include #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 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 diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp index ea54a6264..18ea4ec06 100644 --- a/src/cli/cli_srp_client.cpp +++ b/src/cli/cli_srp_client.cpp @@ -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); diff --git a/src/cli/cli_srp_client.hpp b/src/cli/cli_srp_client.hpp index deafedd34..5f73ae6f0 100644 --- a/src/cli/cli_srp_client.hpp +++ b/src/cli/cli_srp_client.hpp @@ -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; 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; }; diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp index ac9d9dcb5..74930419b 100644 --- a/src/cli/cli_srp_server.cpp +++ b/src/cli/cli_srp_server.cpp @@ -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); diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp index 60e9f9b08..03e5293df 100644 --- a/src/cli/cli_srp_server.hpp +++ b/src/cli/cli_srp_server.hpp @@ -39,8 +39,6 @@ #include #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; 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 diff --git a/src/cli/cli_tcp.cpp b/src/cli/cli_tcp.cpp index f0d5059f8..920c098e4 100644 --- a/src/cli/cli_tcp.cpp +++ b/src/cli/cli_tcp.cpp @@ -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); diff --git a/src/cli/cli_tcp.hpp b/src/cli/cli_tcp.hpp index 7972b6147..4bebc9632 100644 --- a/src/cli/cli_tcp.hpp +++ b/src/cli/cli_tcp.hpp @@ -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; 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; diff --git a/src/cli/cli_udp.cpp b/src/cli/cli_udp.cpp index 06ae9614f..62ad05790 100644 --- a/src/cli/cli_udp.cpp +++ b/src/cli/cli_udp.cpp @@ -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); diff --git a/src/cli/cli_udp.hpp b/src/cli/cli_udp.hpp index 85a4ba17c..11dfc57dc 100644 --- a/src/cli/cli_udp.hpp +++ b/src/cli/cli_udp.hpp @@ -39,8 +39,6 @@ #include #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; 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; diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 409e435af..a7969045b 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -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", ] diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 826965f59..16090bacd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 ) diff --git a/src/core/Makefile.am b/src/core/Makefile.am index b969780e1..a3cba67c5 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -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 \ diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp index 5753bf7b5..5d02662a7 100644 --- a/src/core/coap/coap_message.cpp +++ b/src/core/coap/coap_message.cpp @@ -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 diff --git a/src/core/utils/lookup_table.cpp b/src/core/common/binary_search.cpp similarity index 68% rename from src/core/utils/lookup_table.cpp rename to src/core/common/binary_search.cpp index e4e8dee6b..58256e0da 100644 --- a/src/core/utils/lookup_table.cpp +++ b/src/core/common/binary_search.cpp @@ -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 - -#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(aTable) + aTableEntrySize * middle; + entry = reinterpret_cast(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 diff --git a/src/core/common/binary_search.hpp b/src/core/common/binary_search.hpp new file mode 100644 index 000000000..0e05ab540 --- /dev/null +++ b/src/core/common/binary_search.hpp @@ -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 + +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 + static const Entry *Find(const Key &aKey, const Entry (&aTable)[kLength]) + { + return static_cast( + Find(&aKey, &aTable[0], kLength, sizeof(aTable[0]), BinarySearch::Compare)); + } + + /** + * 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 static constexpr bool IsSorted(const Entry (&aTable)[kLength]) + { + return IsSorted(&aTable[0], kLength); + } + +private: + typedef int (&Comparator)(const void *aKey, const void *aEntry); + + template 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 static int Compare(const void *aKey, const void *aEntry) + { + return static_cast(aEntry)->Compare(*static_cast(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_ diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp index 121591bd8..6a5160031 100644 --- a/src/core/common/string.hpp +++ b/src/core/common/string.hpp @@ -40,6 +40,7 @@ #include #include +#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 + 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; +}; /** * @} diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 64154e2b4..0fa07de96 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -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 diff --git a/src/core/utils/lookup_table.hpp b/src/core/utils/lookup_table.hpp deleted file mode 100644 index a1451727b..000000000 --- a/src/core/utils/lookup_table.hpp +++ /dev/null @@ -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 - -#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 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 - static const EntryType *Find(const char *aName, const EntryType (&aTable)[kLength]) - { - return reinterpret_cast( - Find(aName, &aTable[0], kLength, sizeof(aTable[0]), GetName)); - } - -private: - typedef const char *(&NameGetter)(const void *aPointer); - - template static const char *GetName(const void *aPointer) - { - return reinterpret_cast(aPointer)->mName; - } - - template 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_ diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 1635cd836..4e549aa00 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -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 ) diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 30026a21e..c15f90cca 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -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 diff --git a/tests/unit/test_lookup_table.cpp b/tests/unit/test_binary_search.cpp similarity index 56% rename from tests/unit/test_lookup_table.cpp rename to tests/unit/test_binary_search.cpp index b1310b630..bf05d9f63 100644 --- a/tests/unit/test_lookup_table.cpp +++ b/tests/unit/test_binary_search.cpp @@ -29,81 +29,69 @@ #include #include "test_platform.h" +#include "test_util.h" #include -#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; } diff --git a/tests/unit/test_string.cpp b/tests/unit/test_string.cpp index 8af8188aa..b32a11126 100644 --- a/tests/unit/test_string.cpp +++ b/tests/unit/test_string.cpp @@ -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)