[common] add BinarySearch module and Stringfy (#7294)

This commit adds set of generic binary search related functions in
OpenThread core under `BinarySearch` module. The binary search module
replaces and enhances the existing `Lookup` table module (which was
limited to search based on strings and mainly used by CLI to map from
command string to a handler function).

This commit also adds a new class `Stringfy` in `string.hpp` to help
convert from a set of `uint16_t` values (e.g., a non-sequential
`enum`) to strings using binary search in a table. The new methods are
used for converting `Coap::Code` and `IpProto` enumerations to
corresponding strings.
This commit is contained in:
Abtin Keshavarzian
2022-01-10 13:19:34 -08:00
committed by GitHub
parent 91d444c2c5
commit a4cba16c39
39 changed files with 409 additions and 517 deletions
+1 -1
View File
@@ -220,6 +220,7 @@ LOCAL_SRC_FILES := \
src/core/coap/coap_message.cpp \
src/core/coap/coap_secure.cpp \
src/core/common/appender.cpp \
src/core/common/binary_search.cpp \
src/core/common/crc16.cpp \
src/core/common/data.cpp \
src/core/common/error.cpp \
@@ -353,7 +354,6 @@ LOCAL_SRC_FILES := \
src/core/utils/heap.cpp \
src/core/utils/history_tracker.cpp \
src/core/utils/jam_detector.cpp \
src/core/utils/lookup_table.cpp \
src/core/utils/otns.cpp \
src/core/utils/parse_cmdline.cpp \
src/core/utils/ping_sender.cpp \
+1 -1
View File
@@ -293,7 +293,7 @@ void Interpreter::ProcessLine(char *aBuf)
}
#endif
command = Utils::LookupTable::Find(args[0].GetCString(), sCommands);
command = BinarySearch::Find(args[0].GetCString(), sCommands);
if (command != nullptr)
{
+2 -8
View File
@@ -76,8 +76,6 @@
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/type_traits.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
@@ -229,11 +227,7 @@ private:
static constexpr uint32_t kNetworkDiagnosticTimeoutMsecs = 5000;
static constexpr uint32_t kLocateTimeoutMsecs = 2500;
struct Command
{
const char *mName;
otError (Interpreter::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<Interpreter>;
template <typename ValueType> using GetHandler = ValueType (&)(otInstance *);
template <typename ValueType> using SetHandler = void (&)(otInstance *, ValueType);
@@ -888,7 +882,7 @@ private:
{"version", &Interpreter::ProcessVersion},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
const otCliCommand *mUserCommands;
uint8_t mUserCommandsLength;
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -41,8 +41,6 @@
#include <openthread/coap.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
namespace Cli {
@@ -79,11 +77,7 @@ private:
kMaxBufferSize = 16
};
struct Command
{
const char *mName;
otError (Coap::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<Coap>;
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
enum BlockType : uint8_t{
@@ -186,7 +180,7 @@ private:
{"stop", &Coap::ProcessStop},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
bool mUseDefaultRequestTxParameters;
bool mUseDefaultResponseTxParameters;
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -43,8 +43,6 @@
#include <openthread/coap_secure.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
#ifndef CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
#define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 0
@@ -87,11 +85,7 @@ private:
kPskIdMaxLength = 32
};
struct Command
{
const char *mName;
otError (CoapSecure::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<CoapSecure>;
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
enum BlockType : uint8_t{
@@ -175,7 +169,7 @@ private:
#endif
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
#if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
otCoapBlockwiseResource mResource;
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -39,8 +39,6 @@
#include <openthread/commissioner.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
@@ -81,11 +79,7 @@ private:
kDefaultJoinerTimeout = 120, ///< Default timeout for Joiners, in seconds.
};
struct Command
{
const char *mName;
otError (Commissioner::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<Commissioner>;
otError ProcessHelp(Arg aArgs[]);
otError ProcessAnnounce(Arg aArgs[]);
@@ -131,7 +125,7 @@ private:
{"state", &Commissioner::ProcessState}, {"stop", &Commissioner::ProcessStop},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
};
} // namespace Cli
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -41,8 +41,6 @@
#include <openthread/dataset.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
namespace Cli {
@@ -70,11 +68,7 @@ public:
otError Process(Arg aArgs[]);
private:
struct Command
{
const char *mName;
otError (Dataset::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<Dataset>;
otError Print(otOperationalDataset &aDataset);
@@ -136,7 +130,7 @@ private:
#endif
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
static otOperationalDataset sDataset;
};
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -40,8 +40,6 @@
#include "cli/cli_config.h"
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
@@ -81,11 +79,7 @@ private:
static constexpr uint16_t kShortAddrBroadcast = 0xffff;
static constexpr int8_t kInvalidRss = OT_RADIO_RSSI_INVALID;
struct Command
{
const char *mName;
otError (History::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<History>;
enum RxTx : uint8_t
{
@@ -123,7 +117,7 @@ private:
{"tx", &History::ProcessTx},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
};
} // namespace Cli
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -39,8 +39,6 @@
#include <openthread/joiner.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
#if OPENTHREAD_CONFIG_JOINER_ENABLE
@@ -76,11 +74,7 @@ public:
otError Process(Arg aArgs[]);
private:
struct Command
{
const char *mName;
otError (Joiner::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<Joiner>;
otError ProcessDiscerner(Arg aArgs[]);
otError ProcessHelp(Arg aArgs[]);
@@ -97,7 +91,7 @@ private:
{"start", &Joiner::ProcessStart}, {"state", &Joiner::ProcessState}, {"stop", &Joiner::ProcessStop},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
};
} // namespace Cli
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -39,8 +39,6 @@
#include <openthread/netdata.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
namespace Cli {
@@ -98,11 +96,7 @@ public:
void OutputService(const otServiceConfig &aConfig);
private:
struct Command
{
const char *mName;
otError (NetworkData::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<NetworkData>;
otError ProcessHelp(Arg aArgs[]);
#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
@@ -136,7 +130,7 @@ private:
#endif
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
};
} // namespace Cli
+20 -2
View File
@@ -41,6 +41,9 @@
#include <openthread/cli.h>
#include "cli_config.h"
#include "common/binary_search.hpp"
#include "common/string.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
@@ -79,6 +82,23 @@ public:
protected:
OutputBase(void) = default;
typedef Utils::CmdLineParser::Arg Arg;
template <typename Cli> struct CommandEntry
{
typedef otError (Cli::*Handler)(Arg aArgs[]);
int Compare(const char *aName) const { return strcmp(aName, mName); }
constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
{
return AreStringsInOrder(aFirst.mName, aSecond.mName);
}
const char *mName;
Handler mHandler;
};
};
/**
@@ -344,8 +364,6 @@ public:
}
protected:
typedef Utils::CmdLineParser::Arg Arg;
void OutputFormatV(const char *aFormat, va_list aArguments);
#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -41,8 +41,6 @@
#include "cli/cli_config.h"
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
@@ -81,11 +79,7 @@ private:
kIndentSize = 4,
};
struct Command
{
const char *mName;
otError (SrpClient::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<SrpClient>;
otError ProcessAutoStart(Arg aArgs[]);
otError ProcessCallback(Arg aArgs[]);
@@ -128,7 +122,7 @@ private:
{"stop", &SrpClient::ProcessStop},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
bool mCallbackEnabled;
};
+1 -1
View File
@@ -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);
+2 -8
View File
@@ -39,8 +39,6 @@
#include <openthread/srp_server.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
@@ -81,11 +79,7 @@ public:
private:
static constexpr uint8_t kIndentSize = 4;
struct Command
{
const char *mName;
otError (SrpServer::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<SrpServer>;
otError ProcessAddrMode(Arg aArgs[]);
otError ProcessDomain(Arg aArgs[]);
@@ -108,7 +102,7 @@ private:
{"service", &SrpServer::ProcessService}, {"state", &SrpServer::ProcessState},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
};
} // namespace Cli
+1 -1
View File
@@ -322,7 +322,7 @@ otError TcpExample::Process(Arg aArgs[])
VerifyOrExit(!aArgs[0].IsEmpty(), IgnoreError(ProcessHelp(nullptr)));
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
error = (this->*command->mHandler)(aArgs + 1);
+2 -8
View File
@@ -41,8 +41,6 @@
#include "cli/cli_config.h"
#include "cli/cli_output.hpp"
#include "common/time.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
namespace Cli {
@@ -73,11 +71,7 @@ public:
otError Process(Arg aArgs[]);
private:
struct Command
{
const char *mName;
otError (TcpExample::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<TcpExample>;
otError ProcessHelp(Arg aArgs[]);
otError ProcessInit(Arg aArgs[]);
@@ -131,7 +125,7 @@ private:
{"stoplistening", &TcpExample::ProcessStopListening},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
otTcpEndpoint mEndpoint;
otTcpListener mListener;
+1 -1
View File
@@ -277,7 +277,7 @@ otError UdpExample::Process(Arg aArgs[])
ExitNow();
}
command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
error = (this->*command->mHandler)(aArgs + 1);
+2 -8
View File
@@ -39,8 +39,6 @@
#include <openthread/udp.h>
#include "cli/cli_output.hpp"
#include "utils/lookup_table.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
namespace Cli {
@@ -71,11 +69,7 @@ public:
otError Process(Arg aArgs[]);
private:
struct Command
{
const char *mName;
otError (UdpExample::*mHandler)(Arg aArgs[]);
};
using Command = CommandEntry<UdpExample>;
otError ProcessHelp(Arg aArgs[]);
otError ProcessBind(Arg aArgs[]);
@@ -101,7 +95,7 @@ private:
{"send", &UdpExample::ProcessSend},
};
static_assert(Utils::LookupTable::IsSorted(sCommands), "Command Table is not sorted");
static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
bool mLinkSecurityEnabled;
otUdpSocket mSocket;
+4 -3
View File
@@ -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",
]
+2 -2
View File
@@ -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
)
+3 -3
View File
@@ -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 \
+32 -92
View File
@@ -438,100 +438,40 @@ exit:
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
const char *Message::CodeToString(void) const
{
const char *string;
static constexpr Stringify::Entry kCodeTable[] = {
{kCodeEmpty, "Empty"},
{kCodeGet, "Get"},
{kCodePost, "Post"},
{kCodePut, "Put"},
{kCodeDelete, "Delete"},
{kCodeCreated, "Created"},
{kCodeDeleted, "Deleted"},
{kCodeValid, "Valid"},
{kCodeChanged, "Changed"},
{kCodeContent, "Content"},
{kCodeContinue, "Continue"},
{kCodeBadRequest, "BadRequest"},
{kCodeUnauthorized, "Unauthorized"},
{kCodeBadOption, "BadOption"},
{kCodeForbidden, "Forbidden"},
{kCodeNotFound, "NotFound"},
{kCodeMethodNotAllowed, "MethodNotAllowed"},
{kCodeNotAcceptable, "NotAcceptable"},
{kCodeRequestIncomplete, "RequestIncomplete"},
{kCodePreconditionFailed, "PreconditionFailed"},
{kCodeRequestTooLarge, "RequestTooLarge"},
{kCodeUnsupportedFormat, "UnsupportedFormat"},
{kCodeInternalError, "InternalError"},
{kCodeNotImplemented, "NotImplemented"},
{kCodeBadGateway, "BadGateway"},
{kCodeServiceUnavailable, "ServiceUnavailable"},
{kCodeGatewayTimeout, "GatewayTimeout"},
{kCodeProxyNotSupported, "ProxyNotSupported"},
};
switch (GetCode())
{
case kCodeEmpty:
string = "Empty";
break;
case kCodeGet:
string = "Get";
break;
case kCodePost:
string = "Post";
break;
case kCodePut:
string = "Put";
break;
case kCodeDelete:
string = "Delete";
break;
case kCodeCreated:
string = "Created";
break;
case kCodeDeleted:
string = "Deleted";
break;
case kCodeValid:
string = "Valid";
break;
case kCodeChanged:
string = "Changed";
break;
case kCodeContent:
string = "Content";
break;
case kCodeContinue:
string = "Continue";
break;
case kCodeBadRequest:
string = "BadRequest";
break;
case kCodeUnauthorized:
string = "Unauthorized";
break;
case kCodeBadOption:
string = "BadOption";
break;
case kCodeForbidden:
string = "Forbidden";
break;
case kCodeNotFound:
string = "NotFound";
break;
case kCodeMethodNotAllowed:
string = "MethodNotAllowed";
break;
case kCodeNotAcceptable:
string = "NotAcceptable";
break;
case kCodeRequestIncomplete:
string = "RequestIncomplete";
break;
case kCodePreconditionFailed:
string = "PreconditionFailed";
break;
case kCodeRequestTooLarge:
string = "RequestTooLarge";
break;
case kCodeUnsupportedFormat:
string = "UnsupportedFormat";
break;
case kCodeInternalError:
string = "InternalError";
break;
case kCodeNotImplemented:
string = "NotImplemented";
break;
case kCodeBadGateway:
string = "BadGateway";
break;
case kCodeServiceUnavailable:
string = "ServiceUnavailable";
break;
case kCodeGatewayTimeout:
string = "GatewayTimeout";
break;
case kCodeProxyNotSupported:
string = "ProxyNotSupported";
break;
default:
string = "Unknown";
break;
}
static_assert(Stringify::IsSorted(kCodeTable), "kCodeTable is not sorted");
return string;
return Stringify::Lookup(GetCode(), kCodeTable, "Unknown");
}
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* Copyright (c) 2022, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,23 +28,20 @@
/**
* @file
* This file implements the lookup table (binary search) functionality.
* This file implements a generic binary search.
*/
#include <string.h>
#include "lookup_table.hpp"
#include "binary_search.hpp"
#include "common/code_utils.hpp"
namespace ot {
namespace Utils {
const void *LookupTable::Find(const char *aName,
const void *aTable,
uint16_t aLength,
uint16_t aTableEntrySize,
NameGetter aNameGetter)
const void *BinarySearch::Find(const void *aKey,
const void *aTable,
uint16_t aLength,
uint16_t aEntrySize,
Comparator aComparator)
{
const void *entry;
uint16_t left = 0;
@@ -56,17 +53,17 @@ const void *LookupTable::Find(const char *aName,
int compare;
// Note that `aTable` array entry type is not known here and
// only its size is given as `aTableEntrySize`. Based on this,
// we can calculate the pointer to the table entry at any index
// only its size is given as `aEntrySize`. Based on this, we
// can calculate the pointer to the table entry at any index
// (such as `[middle]`) which is then passed to the given
// `aNameGetter` function which knows how to cast the void
// pointer to proper entry type and get the enclosed `mName`
// field. This model keeps the implementation generic and
// re-usable allowing it to be used with any entry type.
// `aComparator` function which knows how to cast the void
// pointer to proper entry type and compare it with `aKey`.
// This model keeps the implementation generic and reusable
// allowing it to be used with any entry type.
entry = reinterpret_cast<const uint8_t *>(aTable) + aTableEntrySize * middle;
entry = reinterpret_cast<const uint8_t *>(aTable) + aEntrySize * middle;
compare = strcmp(aName, aNameGetter(entry));
compare = aComparator(aKey, entry);
if (compare == 0)
{
@@ -88,5 +85,4 @@ exit:
return entry;
}
} // namespace Utils
} // namespace ot
+127
View File
@@ -0,0 +1,127 @@
/*
* Copyright (c) 2022, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file provides a generic binary search and related helper functions.
*/
#ifndef BINARY_SEARCH_HPP_
#define BINARY_SEARCH_HPP_
#include "openthread-core-config.h"
#include <stdint.h>
namespace ot {
class BinarySearch
{
public:
/**
* This template method performs binary search in a given sorted table array to find an entry matching a given key.
*
* @note This method requires the array to be sorted, otherwise its behavior is undefined.
*
* @tparam Key The type of `Key` to search for.
* @tparam Entry The table `Entry` type.
* @tparam kLength The array length (number of entries in the array).
*
* The `Entry` class MUST provide the following method to compare the entry against a given key.
*
* int Entry::Compare(const Key &aKey) const;
*
* The return value indicates the comparison result between @p aKey and the entry (similar to `strcmp()`), i.e.,
* zero means perfect match, positive (> 0) indicates @p aKey is larger than entry, and negative indicates @p aKey
* is smaller than entry.
*
* @note In the common use of this method as `Find(key, kTable)` where `kTable` is a fixed size array, the
* template types/parameters do not need to be explicitly specified and can be inferred from the passed-in argument.
*
* @param[in] aKey The key to search for within the table.
* @param[in] aTable A reference to an array of `kLength` entries of type `Entry`
*
* @returns A pointer to the entry in the table if a match is found, otherwise `nullptr` (no match in table).
*
*/
template <typename Key, typename Entry, uint16_t kLength>
static const Entry *Find(const Key &aKey, const Entry (&aTable)[kLength])
{
return static_cast<const Entry *>(
Find(&aKey, &aTable[0], kLength, sizeof(aTable[0]), BinarySearch::Compare<Key, Entry>));
}
/**
* This template method indicates whether a given table array is sorted based or not.
*
* This method is `constexpr` and is intended for use in `static_assert`s to verify that a `constexpr` lookup table
* array is sorted. It is not recommended for use in other situations.
*
* @tparam Entry The table entry type.
* @tparam kLength The array length (number of entries in the array).
*
* The `Entry` class MUST provide the following `static` and `constexpr` method to compare two entries.
*
* constexpr static bool Entry::AreInOrder(const Entry &aFirst, const Entry &aSecond);
*
* The return value MUST be TRUE if the entries are in order, i.e. `aFirst < aSecond` and FALSE otherwise.
*
* @param[in] aTable A reference to an array of `kLength` entries on type `Entry`
*
* @retval TRUE If the entries in @p aTable are sorted.
* @retval FALSE If the entries in @p aTable are not sorted.
*
*/
template <typename Entry, uint16_t kLength> static constexpr bool IsSorted(const Entry (&aTable)[kLength])
{
return IsSorted(&aTable[0], kLength);
}
private:
typedef int (&Comparator)(const void *aKey, const void *aEntry);
template <typename Entry> static constexpr bool IsSorted(const Entry *aTable, uint16_t aLength)
{
return (aLength <= 1) ? true : Entry::AreInOrder(aTable[0], aTable[1]) && IsSorted(aTable + 1, aLength - 1);
}
template <typename Key, typename Entry> static int Compare(const void *aKey, const void *aEntry)
{
return static_cast<const Entry *>(aEntry)->Compare(*static_cast<const Key *>(aKey));
}
static const void *Find(const void *aKey,
const void *aTable,
uint16_t aLength,
uint16_t aEntrySize,
Comparator aComparator);
};
} // namespace ot
#endif // BINARY_SEARCH_HPP_
+96 -20
View File
@@ -40,6 +40,7 @@
#include <stdint.h>
#include <stdio.h>
#include "common/binary_search.hpp"
#include "common/code_utils.hpp"
#include "common/error.hpp"
@@ -204,6 +205,51 @@ char ToUppercase(char aChar);
*/
const char *ToYesNo(bool aBool);
/**
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
* Control characters are not allowed.
*
* @param[in] aString A null-terminated byte sequence.
*
* @retval TRUE The sequence is a valid UTF-8 string.
* @retval FALSE The sequence is not a valid UTF-8 string.
*
*/
bool IsValidUtf8String(const char *aString);
/**
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
* Control characters are not allowed.
*
* @param[in] aString A byte sequence.
* @param[in] aLength Length of the sequence.
*
* @retval TRUE The sequence is a valid UTF-8 string.
* @retval FALSE The sequence is not a valid UTF-8 string.
*
*/
bool IsValidUtf8String(const char *aString, size_t aLength);
/**
* This `constexpr` function checks whether two given C strings are in order (alphabetical order).
*
* This is intended for use from `static_assert`, e.g., checking if a lookup table entries are sorted. It is not
* recommended to use this function in other situations as it uses recursion so that it can be `constexpr`.
*
* @param[in] aFirst The first string.
* @param[in] aSecond The second string.
*
* @retval TRUE If first string is strictly before second string (alphabetical order).
* @retval FALSE If first string is not strictly before second string (alphabetical order).
*
*/
inline constexpr bool AreStringsInOrder(const char *aFirst, const char *aSecond)
{
return (*aFirst < *aSecond)
? true
: ((*aFirst > *aSecond) || (*aFirst == '\0') ? false : AreStringsInOrder(aFirst + 1, aSecond + 1));
}
/**
* This class implements writing to a string buffer.
*
@@ -339,29 +385,59 @@ private:
};
/**
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
* Control characters are not allowed.
*
* @param[in] aString A null-terminated byte sequence.
*
* @retval TRUE The sequence is a valid UTF-8 string.
* @retval FALSE The sequence is not a valid UTF-8 string.
* This class provides helper methods to convert from a set of `uint16_t` values (e.g., a non-sequential `enum`) to
* string using binary search in a lookup table.
*
*/
bool IsValidUtf8String(const char *aString);
class Stringify : public BinarySearch
{
public:
/**
* This class represents a entry in the lookup table.
*
*/
class Entry
{
friend class BinarySearch;
/**
* This function validates whether a given byte sequence (string) follows UTF-8 encoding.
* Control characters are not allowed.
*
* @param[in] aString A byte sequence.
* @param[in] aLength Length of the sequence.
*
* @retval TRUE The sequence is a valid UTF-8 string.
* @retval FALSE The sequence is not a valid UTF-8 string.
*
*/
bool IsValidUtf8String(const char *aString, size_t aLength);
public:
uint16_t mKey; ///< The key value.
const char *mString; ///< The associated string.
private:
int Compare(uint16_t aKey) const { return (aKey == mKey) ? 0 : ((aKey > mKey) ? 1 : -1); }
constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond)
{
return aFirst.mKey < aSecond.mKey;
}
};
/**
* This static method looks up a key in a given sorted table array (using binary search) and return the associated
* strings with the key.
*
* @note This method requires the array to be sorted, otherwise its behavior is undefined.
*
* @tparam kLength The array length (number of entries in the array).
*
* @param[in] aKey The key to search for within the table.
* @param[in] aTable A reference to an array of `kLength` entries.
* @param[in] aNotFound A C string to return if @p aKey was not found in the table.
*
* @returns The associated string with @p aKey in @p aTable if found, or @p aNotFound otherwise.
*
*/
template <uint16_t kLength>
static const char *Lookup(uint16_t aKey, const Entry (&aTable)[kLength], const char *aNotFound = "unknown")
{
const Entry *entry = BinarySearch::Find(aKey, aTable);
return (entry != nullptr) ? entry->mString : aNotFound;
}
Stringify(void) = delete;
};
/**
* @}
+7 -44
View File
@@ -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
-152
View File
@@ -1,152 +0,0 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for lookup table (binary search) functionality.
*/
#ifndef LOOKUP_TABLE_HPP_
#define LOOKUP_TABLE_HPP_
#include <stdint.h>
#include "common/error.hpp"
namespace ot {
namespace Utils {
/**
* This class implements lookup table (using binary search) functionality.
*
*/
class LookupTable
{
public:
/**
* This struct represents an example of a lookup table entry.
*
*/
struct Entry
{
/**
* This constructor initializes the entry with a given name.
*
* @param[in] aName The null-terminated name string with which to initialize the entry.
*
*/
constexpr explicit Entry(const char *aName)
: mName(aName)
{
}
const char *const mName;
};
/**
* This template method indicates whether a given entry table array is sorted based on entry's name string and in
* alphabetical order and contains not duplicate entries.
*
* This method is `constexpr` and is intended for use in `static_assert`s to verify that a `constexpr` lookup table
* array is sorted.
*
* @tparam EntryType The table entry type. The `EntryType` MUST provide `mName` member variable as a `const
* char *`. For example, this can be realized by `EntryType` being a subclass of `Entry`.
* @tparam kLength The array length (number of entries in the array).
*
* @note In the common use of this method as `IsSorted(sTable)` where sTable` is a fixed size array, the template
* types/parameters do not need to be explicitly specified and can be deduced from the passed-in argument.
*
* @param[in] aTable A reference to an array of `kLength` entries on type `EntryType`
*
* @retval TRUE If the entries in @p aTable are sorted (alphabetical order).
* @retval FALSE If the entries in @p aTable are not sorted.
*
*/
template <class EntryType, uint16_t kLength> static constexpr bool IsSorted(const EntryType (&aTable)[kLength])
{
return IsSorted(&aTable[0], kLength);
}
/**
* This template method performs binary search in a given sorted table array to find an entry matching a given name.
*
* @note This method requires the array to be sorted, otherwise its behavior is undefined.
*
* @tparam EntryType The table entry type. The `EntryType` MUST provide `mName` member variable as a `const
* char *`. For example, this can be realized by `EntryType` being a subclass of `Entry`.
* @tparam kLength The array length (number of entries in the array).
*
* @note In the common use of this method as `Find(name, sTable)` where sTable` is a fixed size array, the template
* types/parameters do not need to be explicitly specified and can be deduced from the passed-in argument.
*
* @param[in] aName A name string to search for within the table.
* @param[in] aTable A reference to an array of `kLength` entries on type `EntryType`
*
* @returns A pointer to the entry in the table if a match is found, otherwise `nullptr` (no match in table).
*
*/
template <class EntryType, uint16_t kLength>
static const EntryType *Find(const char *aName, const EntryType (&aTable)[kLength])
{
return reinterpret_cast<const EntryType *>(
Find(aName, &aTable[0], kLength, sizeof(aTable[0]), GetName<EntryType>));
}
private:
typedef const char *(&NameGetter)(const void *aPointer);
template <class EntryType> static const char *GetName(const void *aPointer)
{
return reinterpret_cast<const EntryType *>(aPointer)->mName;
}
template <class EntryType> static constexpr bool IsSorted(const EntryType *aTable, uint16_t aLength)
{
return (aLength <= 1) ? true
: AreInOrder(aTable[0].mName, aTable[1].mName) && IsSorted(aTable + 1, aLength - 1);
}
constexpr static bool AreInOrder(const char *aFirst, const char *aSecond)
{
return (*aFirst < *aSecond)
? true
: ((*aFirst > *aSecond) || (*aFirst == '\0') ? false : AreInOrder(aFirst + 1, aSecond + 1));
}
static const void *Find(const char *aName,
const void *aTable,
uint16_t aLength,
uint16_t aTableEntrySize,
NameGetter aNameGetter);
};
} // namespace Utils
} // namespace ot
#endif // LOOKUP_TABLE_HPP_
+21 -21
View File
@@ -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
)
+4 -4
View File
@@ -110,6 +110,7 @@ if OPENTHREAD_ENABLE_FTD
check_PROGRAMS += \
ot-test-aes \
ot-test-array \
ot-test-binary-search \
ot-test-checksum \
ot-test-child \
ot-test-child-table \
@@ -125,7 +126,6 @@ check_PROGRAMS += \
ot-test-ip-address \
ot-test-link-quality \
ot-test-linked-list \
ot-test-lookup-table \
ot-test-lowpan \
ot-test-mac-frame \
ot-test-macros \
@@ -188,6 +188,9 @@ ot_test_aes_SOURCES = $(COMMON_SOURCES) test_aes.cpp
ot_test_array_LDADD = $(COMMON_LDADD)
ot_test_array_SOURCES = $(COMMON_SOURCES) test_array.cpp
ot_test_binary_search_LDADD = $(COMMON_LDADD)
ot_test_binary_search_SOURCES = $(COMMON_SOURCES) test_binary_search.cpp
ot_test_checksum_LDADD = $(COMMON_LDADD)
ot_test_checksum_SOURCES = $(COMMON_SOURCES) test_checksum.cpp
@@ -236,9 +239,6 @@ ot_test_link_quality_SOURCES = $(COMMON_SOURCES) test_link_quality.cpp
ot_test_linked_list_LDADD = $(COMMON_LDADD)
ot_test_linked_list_SOURCES = $(COMMON_SOURCES) test_linked_list.cpp
ot_test_lookup_table_LDADD = $(COMMON_LDADD)
ot_test_lookup_table_SOURCES = $(COMMON_SOURCES) test_lookup_table.cpp
ot_test_lowpan_LDADD = $(COMMON_LDADD)
ot_test_lowpan_SOURCES = $(COMMON_SOURCES) test_lowpan.cpp
@@ -29,81 +29,69 @@
#include <string.h>
#include "test_platform.h"
#include "test_util.h"
#include <openthread/config.h>
#include "common/instance.hpp"
#include "utils/lookup_table.hpp"
#include "common/binary_search.hpp"
#include "common/string.hpp"
#include "test_util.h"
namespace ot {
typedef ot::Utils::LookupTable::Entry Entry;
struct TableEntryBase
void TestBinarySearch(void)
{
constexpr explicit TableEntryBase(uint8_t aValue)
: mValue(aValue)
static constexpr uint16_t kMaxNameSize = 30;
struct Entry
{
}
int Compare(const char *aName) const { return strcmp(aName, mName); }
uint8_t mValue;
};
constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond)
{
return AreStringsInOrder(aFirst.mName, aSecond.mName);
}
struct TableEntry : public TableEntryBase, public Entry
{
constexpr TableEntry(const char *aName, uint8_t aValue)
: TableEntryBase(aValue)
, Entry(aName)
, mUint16(0xabba)
, mUint8(aValue)
{
}
uint16_t mUint16;
uint8_t mUint8;
};
void TestLookupTable(void)
{
enum : uint16_t
{
kMaxNameSize = 30,
const char *mName;
uint8_t mRank;
};
constexpr TableEntry kTable[] = {
constexpr Entry kTable[] = {
{"arkham city", 9}, {"arkham knight", 7}, {"bloodborne", 10}, {"god of war", 10}, {"horizon", 9},
{"infamous", 7}, {"last guardian", 7}, {"last of us", 11}, {"last of us part 2", 8}, {"mass effect", 8},
{"sekiro", 10}, {"tomb raider", 9}, {"uncharted", 9},
};
constexpr Entry kUnsortedTable[] = {Entry("z"), Entry("a"), Entry("b")};
constexpr Entry kDuplicateEntryTable[] = {Entry("duplicate"), Entry("duplicate")};
constexpr Entry kUnsortedTable[] = {{"z", 0}, {"a", 0}, {"b", 0}};
constexpr Entry kDuplicateEntryTable[] = {{"duplicate", 1}, {"duplicate", 2}};
static_assert(ot::Utils::LookupTable::IsSorted(kTable), "LookupTable::IsSorted() failed");
static_assert(!ot::Utils::LookupTable::IsSorted(kUnsortedTable), "failed for unsorted table");
static_assert(!ot::Utils::LookupTable::IsSorted(kDuplicateEntryTable), "failed for table with duplicate entries");
static_assert(BinarySearch::IsSorted(kTable), "IsSorted() failed");
static_assert(!BinarySearch::IsSorted(kUnsortedTable), "failed for unsorted table");
static_assert(!BinarySearch::IsSorted(kDuplicateEntryTable), "failed for table with duplicate entries");
for (const TableEntry &tableEntry : kTable)
for (const Entry &tableEntry : kTable)
{
const TableEntry *entry;
char name[kMaxNameSize];
const Entry *entry;
char name[kMaxNameSize];
strcpy(name, tableEntry.mName);
entry = ot::Utils::LookupTable::Find(name, kTable);
VerifyOrQuit(entry == &tableEntry, "LookupTable::Find() failed");
entry = BinarySearch::Find(name, kTable);
VerifyOrQuit(entry == &tableEntry, "BinarySearch::Find() failed");
name[strlen(name) - 1] = '\0';
entry = ot::Utils::LookupTable::Find(name, kTable);
VerifyOrQuit(entry == nullptr, "LookupTable::Find() failed with non-matching name");
entry = BinarySearch::Find(name, kTable);
VerifyOrQuit(entry == nullptr, "BinarySearch::Find() failed with non-matching name");
}
VerifyOrQuit(ot::Utils::LookupTable::Find("dragon age", kTable) == nullptr, "failed with non-exiting match");
VerifyOrQuit(BinarySearch::Find("dragon age", kTable) == nullptr, "failed with non-exiting match");
}
} // namespace ot
int main(void)
{
TestLookupTable();
ot::TestBinarySearch();
printf("All tests passed\n");
return 0;
}
+7
View File
@@ -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)