From 4b918f85f067144a7e280638bc1bfd6cd8187964 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 11 Jun 2018 09:10:52 -0700 Subject: [PATCH] [string] adding ot::String class (#2764) This commit adds a new template class `ot::String` which implements a fixed-length character string. This class is then used as return value of `ToString()` methods from different classes. This simplifies the `ToString()` implementation and its use. This commit also adds a unit test for `String`. --- etc/visual-studio/libopenthread.vcxproj | 2 + .../libopenthread.vcxproj.filters | 6 + etc/visual-studio/libopenthread_k.vcxproj | 3 + .../libopenthread_k.vcxproj.filters | 6 + src/core/Makefile.am | 3 + src/core/common/settings.cpp | 14 +- src/core/common/string.cpp | 77 +++++++ src/core/common/string.hpp | 215 ++++++++++++++++++ src/core/mac/mac.cpp | 46 ++-- src/core/mac/mac.hpp | 15 +- src/core/mac/mac_frame.cpp | 75 +++--- src/core/mac/mac_frame.hpp | 69 +++--- src/core/meshcop/commissioner.cpp | 13 +- src/core/meshcop/joiner.cpp | 4 +- src/core/net/ip6_address.cpp | 10 +- src/core/net/ip6_address.hpp | 16 +- src/core/thread/address_resolver.cpp | 56 ++--- src/core/thread/announce_sender.cpp | 5 +- src/core/thread/link_quality.cpp | 27 +-- src/core/thread/link_quality.hpp | 30 ++- src/core/thread/mesh_forwarder.cpp | 38 ++-- src/core/thread/mesh_forwarder_ftd.cpp | 7 +- src/core/thread/mle.cpp | 13 +- src/core/thread/mle_router.cpp | 7 +- src/core/utils/channel_manager.cpp | 20 +- tests/unit/Makefile.am | 5 + tests/unit/test_link_quality.cpp | 19 +- tests/unit/test_mac_frame.cpp | 12 +- tests/unit/test_string.cpp | 131 +++++++++++ 29 files changed, 631 insertions(+), 313 deletions(-) create mode 100644 src/core/common/string.cpp create mode 100644 src/core/common/string.hpp create mode 100644 tests/unit/test_string.cpp diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index 9f455ccf8..c845381a9 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -83,6 +83,7 @@ + @@ -169,6 +170,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index 1fd4e610a..d7efac078 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -147,6 +147,9 @@ Source Files\common + + Source Files\common + Source Files\common @@ -395,6 +398,9 @@ Header Files\common + + Header Files\common + Header Files\common diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index 50683daa3..7ea6ae8c2 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -45,6 +45,7 @@ OPENTHREAD_FTD=1; HAVE_STDBOOL_H=1; HAVE_STDINT_H=1; + OT_STRING_WINDOWS_WORKAROUND=1; OTBUILD; @@ -92,6 +93,7 @@ + @@ -201,6 +203,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index 288725a99..0716824fa 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -147,6 +147,9 @@ Source Files\common + + Source Files\common + Source Files\common @@ -395,6 +398,9 @@ Header Files\common + + Header Files\common + Header Files\common diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 6a90780ad..a1a50828d 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -130,6 +130,7 @@ SOURCES_COMMON = \ common/message.cpp \ common/notifier.cpp \ common/settings.cpp \ + common/string.cpp \ common/tasklet.cpp \ common/timer.cpp \ common/tlvs.cpp \ @@ -215,6 +216,7 @@ libopenthread_radio_a_SOURCES = \ common/locator.cpp \ common/logging.cpp \ common/message.cpp \ + common/string.cpp \ common/tasklet.cpp \ common/timer.cpp \ mac/mac_frame.cpp \ @@ -249,6 +251,7 @@ HEADERS_COMMON = \ common/owner-locator.hpp \ common/random.hpp \ common/settings.hpp \ + common/string.hpp \ common/tasklet.hpp \ common/timer.hpp \ common/tlvs.hpp \ diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp index 7cee7f085..2ad33310c 100644 --- a/src/core/common/settings.cpp +++ b/src/core/common/settings.cpp @@ -50,11 +50,9 @@ namespace ot { void SettingsBase::LogNetworkInfo(const char *aAction, const NetworkInfo &aNetworkInfo) const { - char string[Mac::Address::kAddressStringSize]; - otLogInfoCore(GetInstance(), "Non-volatile: %s NetworkInfo {rloc:0x%04x, extaddr:%s, role:%s, mode:0x%02x, keyseq:0x%x, ...", - aAction, aNetworkInfo.mRloc16, aNetworkInfo.mExtAddress.ToString(string, sizeof(string)), + aAction, aNetworkInfo.mRloc16, aNetworkInfo.mExtAddress.ToString().AsCString(), Mle::Mle::RoleToString(static_cast(aNetworkInfo.mRole)), aNetworkInfo.mDeviceMode, aNetworkInfo.mKeySequence); @@ -67,19 +65,15 @@ void SettingsBase::LogNetworkInfo(const char *aAction, const NetworkInfo &aNetwo void SettingsBase::LogParentInfo(const char *aAction, const ParentInfo &aParentInfo) const { - char string[Mac::Address::kAddressStringSize]; - otLogInfoCore(GetInstance(), "Non-volatile: %s ParentInfo {extaddr:%s}", aAction, - aParentInfo.mExtAddress.ToString(string, sizeof(string))); + aParentInfo.mExtAddress.ToString().AsCString()); } void SettingsBase::LogChildInfo(const char *aAction, const ChildInfo &aChildInfo) const { - char string[Mac::Address::kAddressStringSize]; - otLogInfoCore(GetInstance(), "Non-volatile: %s ChildInfo {rloc:0x%04x, extaddr:%s, timeout:%u, mode:0x%02x}", - aAction, aChildInfo.mRloc16, aChildInfo.mExtAddress.ToString(string, sizeof(string)), - aChildInfo.mTimeout, aChildInfo.mMode); + aAction, aChildInfo.mRloc16, aChildInfo.mExtAddress.ToString().AsCString(), aChildInfo.mTimeout, + aChildInfo.mMode); } #endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) diff --git a/src/core/common/string.cpp b/src/core/common/string.cpp new file mode 100644 index 000000000..b7dd42ca7 --- /dev/null +++ b/src/core/common/string.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements OpenThread String class. + */ + +#include "string.hpp" + +namespace ot { + +#if OT_STRING_WINDOWS_WORKAROUND + +otError StringBase::Write(char *, uint16_t, uint16_t &, const char *, va_list) +{ + // This temporarily works around the windows `openthread_k` build failure + // "unresolved external symbol vsnprintf" + return OT_ERROR_NOT_IMPLEMENTED; +} + +#else + +otError StringBase::Write(char *aBuffer, uint16_t aSize, uint16_t &aLength, const char *aFormat, va_list aArgs) +{ + otError error = OT_ERROR_NONE; + int len; + + len = vsnprintf(aBuffer + aLength, aSize - aLength, aFormat, aArgs); + + if (len < 0) + { + aLength = 0; + aBuffer[0] = 0; + error = OT_ERROR_INVALID_ARGS; + } + else if (len >= aSize - aLength) + { + aLength = aSize - 1; + error = OT_ERROR_NO_BUFS; + } + else + { + aLength += static_cast(len); + } + + return error; +}; + +#endif // OT_STRING_WINDOWS_WORKAROUND + +} // namespace ot \ No newline at end of file diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp new file mode 100644 index 000000000..ac76b1851 --- /dev/null +++ b/src/core/common/string.hpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2018, 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 defines OpenThread String class. + */ + +#ifndef STRING_HPP_ +#define STRING_HPP_ + +#include "openthread-core-config.h" + +#include +#include +#include "utils/wrap_string.h" + +#include + +#include "common/code_utils.hpp" + +namespace ot { + +/** + * @addtogroup core-string + * + * @brief + * This module includes definitions for OpenThread String class. + * + * @{ + * + */ + +/** + * This class defines the base class for `String`. + * + */ +class StringBase +{ +protected: + /** + * This method appends `printf()` style formatted data to a given character string buffer. + * + * @param[in] aBuffer A pointer to buffer containing the string. + * @param[in] aSize The size of the buffer (in bytes). + * @param[inout] aLength A reference to variable containing current length of string. On exit length is updated. + * @param[in] aFormat A pointer to the format string. + * @param[in] aArgs Arguments for the format specification. + * + * @retval OT_ERROR_NONE Updated the string successfully. + * @retval OT_ERROR_NO_BUFS String could not fit in the storage. + * @retval OT_ERROR_INVALID_ARGS Arguments do not match the format string. + */ + static otError Write(char *aBuffer, uint16_t aSize, uint16_t &aLength, const char *aFormat, va_list aArgs); +}; + +/** + * This class defines a fixed-size string. + * + */ +template class String : private StringBase +{ +public: + enum + { + kSize = SIZE, ///< Size (number of characters) in the buffer storage for the string. + }; + + /** + * This constructor initializes the `String` object as empty. + * + */ + String(void) + : mLength(0) + { + mBuffer[0] = 0; + } + + /** + * This constructor initializes the `String` object using `printf()` style formatted data. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ + String(const char *aFormat, ...) + : mLength(0) + { + va_list args; + va_start(args, aFormat); + Write(mBuffer, kSize, mLength, aFormat, args); + va_end(args); + } + + /** + * This method clears the string. + * + */ + void Clear(void) + { + mBuffer[0] = 0; + mLength = 0; + } + + /** + * This method gets the length of the string. + * + * Similar to `strlen()` the length does not include the null character at the end of the string. + * + * @returns The string length. + * + */ + uint16_t GetLength(void) const { return mLength; } + + /** + * This method returns the size (number of chars) in the buffer storage for the string. + * + * @returns The size of the buffer storage for the string. + * + */ + uint16_t GetSize(void) const { return kSize; } + + /** + * This method returns the string as a null-terminated C string. + * + * @returns The null-terminated C string. + * + */ + const char *AsCString(void) const { return mBuffer; } + + /** + * This method sets the string using `printf()` style formatted data. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + * @retval OT_ERROR_NONE Updated the string successfully. + * @retval OT_ERROR_NO_BUFS String could not fit in the storage. + * @retval OT_ERROR_INVALID_ARGS Arguments do not match the format string. + * + */ + otError Set(const char *aFormat, ...) + { + va_list args; + otError error; + + va_start(args, aFormat); + mLength = 0; + error = Write(mBuffer, kSize, mLength, aFormat, args); + va_end(args); + + return error; + } + + /** + * This method appends `printf()` style formatted data to the `String` object. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + * @retval OT_ERROR_NONE Updated the string successfully. + * @retval OT_ERROR_NO_BUFS String could not fit in the storage. + * @retval OT_ERROR_INVALID_ARGS Arguments do not match the format string. + * + */ + otError Append(const char *aFormat, ...) + { + va_list args; + otError error; + + va_start(args, aFormat); + error = Write(mBuffer, kSize, mLength, aFormat, args); + va_end(args); + + return error; + } + +private: + uint16_t mLength; + char mBuffer[kSize]; +}; + +/** + * @} + * + */ + +} // namespace ot + +#endif // STRING_HPP_ diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index f3353b9cf..de139441c 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -107,19 +107,14 @@ exit: return error; } -const char *ChannelMask::ToString(char *aBuffer, uint16_t aSize) const +ChannelMask::InfoString ChannelMask::ToString(void) const { - uint8_t channel = kChannelIteratorFirst; - bool addComma = false; - char * bufPtr = aBuffer; - size_t bufLen = aSize; - int len; - otError error; + InfoString string; + uint8_t channel = kChannelIteratorFirst; + bool addComma = false; + otError error; - len = snprintf(bufPtr, bufLen, "{"); - VerifyOrExit((len >= 0) && (static_cast(len) < bufLen)); - bufPtr += len; - bufLen -= static_cast(len); + string.Append("{"); error = GetNextChannel(channel); @@ -138,27 +133,18 @@ const char *ChannelMask::ToString(char *aBuffer, uint16_t aSize) const rangeEnd = channel; } - len = snprintf(bufPtr, bufLen, "%s%d", addComma ? ", " : " ", rangeStart); - VerifyOrExit((len >= 0) && (static_cast(len) < bufLen)); - bufPtr += len; - bufLen -= static_cast(len); - + string.Append("%s%d", addComma ? ", " : " ", rangeStart); addComma = true; if (rangeStart < rangeEnd) { - len = snprintf(bufPtr, bufLen, "%s%d", rangeEnd == rangeStart + 1 ? ", " : "-", rangeEnd); - VerifyOrExit((len >= 0) && (static_cast(len) < bufLen)); - bufPtr += len; - bufLen -= static_cast(len); + string.Append("%s%d", rangeEnd == rangeStart + 1 ? ", " : "-", rangeEnd); } } - len = snprintf(bufPtr, bufLen, " }"); - VerifyOrExit((len >= 0) && (static_cast(len) < bufLen)); + string.Append("}"); -exit: - return aBuffer; + return string; } Mac::Mac(Instance &aInstance) @@ -2240,8 +2226,6 @@ const char *Mac::OperationToString(Operation aOperation) void Mac::LogFrameRxFailure(const Frame *aFrame, otError aError) const { - char string[Frame::kInfoStringSize]; - if (aFrame == NULL) { otLogInfoMac(GetInstance(), "Frame rx failed, error:%s", otThreadErrorToString(aError)); @@ -2249,23 +2233,19 @@ void Mac::LogFrameRxFailure(const Frame *aFrame, otError aError) const else { otLogInfoMac(GetInstance(), "Frame rx failed, error:%s, %s", otThreadErrorToString(aError), - aFrame->ToInfoString(string, sizeof(string))); + aFrame->ToInfoString().AsCString()); } } void Mac::LogFrameTxFailure(const Frame &aFrame, otError aError) const { - char string[Frame::kInfoStringSize]; - otLogInfoMac(GetInstance(), "Frame tx failed, error:%s, attempt:%d/%d, %s", otThreadErrorToString(aError), - mTransmitAttempts, aFrame.GetMaxTxAttempts(), aFrame.ToInfoString(string, sizeof(string))); + mTransmitAttempts, aFrame.GetMaxTxAttempts(), aFrame.ToInfoString().AsCString()); } void Mac::LogBeacon(const char *aActionText, const BeaconPayload &aBeaconPayload) const { - char string[BeaconPayload::kInfoStringSize]; - - otLogInfoMac(GetInstance(), "%s Beacon, %s", aActionText, aBeaconPayload.ToInfoString(string, sizeof(string))); + otLogInfoMac(GetInstance(), "%s Beacon, %s", aActionText, aBeaconPayload.ToInfoString().AsCString()); } #else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 9c155b53c..2aecc053d 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -112,6 +112,12 @@ public: kInfoStringSize = 45, ///< Recommended buffer size to use with `ToString()`. }; + /** + * This type defines the fixed-length `String` object returned from `ToString()`. + * + */ + typedef String InfoString; + /** * This constructor initializes a `ChannelMask` instance. * @@ -249,7 +255,7 @@ public: bool operator!=(const ChannelMask &aAnother) const { return (mMask != aAnother.mMask); } /** - * This method converts the channel mask into a human-readable NULL-terminated string. + * This method converts the channel mask into a human-readable string. * * Examples of possible output: * - empty mask -> "{ }" @@ -258,13 +264,10 @@ public: * - multiple ranges -> "{ 11, 14-17, 20-22, 24, 25 }" * - no range -> "{ 14, 21, 26 }" * - * @param[out] aBuffer A pointer to a char buffer to output the string. - * @param[in] aSize Size of the buffer (number of bytes). - * - * @returns A pointer to the @p aBuffer. + * @returns An `InfoString` object representing the channel mask. * */ - const char *ToString(char *aBuffer, uint16_t aSize) const; + InfoString ToString(void) const; private: #if (OT_RADIO_CHANNEL_MIN >= 32) || (OT_RADIO_CHANNEL_MAX >= 32) diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 459f12a38..622dd0559 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -53,10 +53,9 @@ bool ExtAddress::operator!=(const ExtAddress &aOther) const return memcmp(m8, aOther.m8, sizeof(ExtAddress)) != 0; } -const char *ExtAddress::ToString(char *aBuf, uint16_t aSize) const +ExtAddress::InfoString ExtAddress::ToString(void) const { - snprintf(aBuf, aSize, "%02x%02x%02x%02x%02x%02x%02x%02x", m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]); - return aBuf; + return InfoString("%02x%02x%02x%02x%02x%02x%02x%02x", m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]); } void Address::SetExtended(const uint8_t *aBuffer, bool aReverse) @@ -76,26 +75,10 @@ void Address::SetExtended(const uint8_t *aBuffer, bool aReverse) } } -const char *Address::ToString(char *aBuf, uint16_t aSize) const +Address::InfoString Address::ToString(void) const { - const char *rval = aBuf; - - switch (mType) - { - case kTypeNone: - snprintf(aBuf, aSize, "None"); - break; - - case kTypeShort: - snprintf(aBuf, aSize, "0x%04x", GetShort()); - break; - - case kTypeExtended: - rval = GetExtended().ToString(aBuf, aSize); - break; - } - - return rval; + return (mType == kTypeExtended) ? GetExtended().ToString() + : (mType == kTypeNone ? InfoString("None") : InfoString("0x%04x", GetShort())); } otError Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl) @@ -932,29 +915,28 @@ const uint8_t *Frame::GetFooter(void) const return GetPsdu() + GetPsduLength() - GetFooterLength(); } -const char *Frame::ToInfoString(char *aBuf, uint16_t aSize) const +Frame::InfoString Frame::ToInfoString(void) const { - uint8_t type, commandId; - Address src, dst; - const char *typeStr; - char stringBuffer[10]; - char srcStringBuffer[Address::kAddressStringSize]; - char dstStringBuffer[Address::kAddressStringSize]; + InfoString string; + uint8_t commandId, type; + Address src, dst; + + string.Append("len:%d, seqnum:%d, type:", GetLength(), GetSequence()); type = GetType(); switch (type) { case kFcfFrameBeacon: - typeStr = "Beacon"; + string.Append("Beacon"); break; case kFcfFrameData: - typeStr = "Data"; + string.Append("Data"); break; case kFcfFrameAck: - typeStr = "Ack"; + string.Append("Ack"); break; case kFcfFrameMacCmd: @@ -966,47 +948,42 @@ const char *Frame::ToInfoString(char *aBuf, uint16_t aSize) const switch (commandId) { case kMacCmdDataRequest: - typeStr = "Cmd(DataReq)"; + string.Append("Cmd(DataReq)"); break; case kMacCmdBeaconRequest: - typeStr = "Cmd(BeaconReq)"; + string.Append("Cmd(BeaconReq)"); break; default: - snprintf(stringBuffer, sizeof(stringBuffer), "Cmd(%d)", commandId); - typeStr = stringBuffer; + string.Append("Cmd(%d)", commandId); break; } break; default: - snprintf(stringBuffer, sizeof(stringBuffer), "%d", type); - typeStr = stringBuffer; + string.Append("%d", type); break; } GetSrcAddr(src); GetDstAddr(dst); - snprintf(aBuf, aSize, "len:%d, seqnum:%d, type:%s, src:%s, dst:%s, sec:%s, ackreq:%s", GetLength(), GetSequence(), - typeStr, src.ToString(srcStringBuffer, sizeof(srcStringBuffer)), - dst.ToString(dstStringBuffer, sizeof(dstStringBuffer)), GetSecurityEnabled() ? "yes" : "no", - GetAckRequest() ? "yes" : "no"); + string.Append(", src:%s, dst:%s, sec:%s, ackreq:%s", src.ToString().AsCString(), dst.ToString().AsCString(), + GetSecurityEnabled() ? "yes" : "no", GetAckRequest() ? "yes" : "no"); - return aBuf; + return string; } -const char *BeaconPayload::ToInfoString(char *aBuf, uint16_t aSize) const +BeaconPayload::InfoString BeaconPayload::ToInfoString(void) const { const uint8_t *xpanid = GetExtendedPanId(); - snprintf(aBuf, aSize, "name:%s, xpanid:%02x%02x%02x%02x%02x%02x%02x%02x, id:%d ver:%d, joinable:%s, native:%s", - GetNetworkName(), xpanid[0], xpanid[1], xpanid[2], xpanid[3], xpanid[4], xpanid[5], xpanid[6], xpanid[7], - GetProtocolId(), GetProtocolVersion(), IsJoiningPermitted() ? "yes" : "no", IsNative() ? "yes" : "no"); - - return aBuf; + return InfoString("name:%s, xpanid:%02x%02x%02x%02x%02x%02x%02x%02x, id:%d ver:%d, joinable:%s, native:%s", + GetNetworkName(), xpanid[0], xpanid[1], xpanid[2], xpanid[3], xpanid[4], xpanid[5], xpanid[6], + xpanid[7], GetProtocolId(), GetProtocolVersion(), IsJoiningPermitted() ? "yes" : "no", + IsNative() ? "yes" : "no"); } } // namespace Mac diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 957e3d011..1aa7e5937 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -44,6 +44,7 @@ #include #include "common/encoding.hpp" +#include "common/string.hpp" namespace ot { @@ -83,6 +84,17 @@ OT_TOOL_PACKED_BEGIN class ExtAddress : public otExtAddress { public: + enum + { + kInfoStringSize = 17, // Max chars for the info string (`ToString()`). + }; + + /** + * This type defines the fixed-length `String` object returned from `ToString()`. + * + */ + typedef String InfoString; + /** * This method indicates whether or not the Group bit is set. * @@ -172,15 +184,12 @@ public: bool operator!=(const ExtAddress &aOther) const; /** - * This method converts an address to a NULL-terminated string. + * This method converts an address to a string. * - * @param[out] aBuf A pointer to a character buffer. - * @param[in] aSize The maximum size of the buffer. - * - * @returns A pointer to the character string buffer. + * @returns An `InfoString` containing the string representation of the Extended Address. * */ - const char *ToString(char *aBuf, uint16_t aSize) const; + InfoString ToString(void) const; private: enum @@ -197,10 +206,11 @@ private: class Address { public: - enum - { - kAddressStringSize = 18, ///< Max chars needed for a string representation of address (@sa ToString()). - }; + /** + * This type defines the fixed-length `String` object returned from `ToString()`. + * + */ + typedef ExtAddress::InfoString InfoString; /** * This enumeration specifies the IEEE 802.15.4 Address type. @@ -350,15 +360,12 @@ public: bool IsShortAddrInvalid(void) const { return ((mType == kTypeShort) && (GetShort() == kShortAddrInvalid)); } /** - * This method converts an address to a NULL-terminated string. + * This method converts an address to a null-terminated string * - * @param[out] aBuf A pointer to a character buffer. - * @param[in] aSize The maximum size of the buffer. - * - * @returns A pointer to the character string buffer. + * @returns A `String` representing the address. * */ - const char *ToString(char *aBuf, uint16_t aSize) const; + InfoString ToString(void) const; private: union @@ -449,6 +456,12 @@ public: kInfoStringSize = 110, ///< Max chars needed for the info string representation (@sa ToInfoString()). }; + /** + * This type defines the fixed-length `String` object returned from `ToInfoString()` method. + * + */ + typedef String InfoString; + /** * This method initializes the MAC header. * @@ -1003,15 +1016,12 @@ public: const uint8_t *GetFooter(void) const; /** - * This method returns information about the frame object as a NULL-terminated string. + * This method returns information about the frame object as an `InfoString` object. * - * @param[out] aBuf A pointer to the string buffer - * @param[in] aSize The maximum size of the string buffer. - * - * @returns A pointer to the char string buffer. + * @returns An `InfoString` containing info about the frame. * */ - const char *ToInfoString(char *aBuf, uint16_t aSize) const; + InfoString ToInfoString(void) const; private: enum @@ -1103,6 +1113,12 @@ public: kJoiningFlag = 1 << 0, ///< Joining Permitted flag. }; + /** + * This type defines the fixed-length `String` object returned from `ToInfoString()` method. + * + */ + typedef String InfoString; + /** * This method initializes the Beacon Payload. * @@ -1226,15 +1242,12 @@ public: void SetExtendedPanId(const uint8_t *aExtPanId) { memcpy(mExtendedPanId, aExtPanId, sizeof(mExtendedPanId)); } /** - * This method returns information about the Beacon as a NULL-terminated string. + * This method returns information about the Beacon as a `InfoString`. * - * @param[out] aBuf A pointer to the string buffer - * @param[in] aSize The maximum size of the string buffer. - * - * @returns A pointer to the char string buffer. + * @returns An `InfoString` representing the beacon payload. * */ - const char *ToInfoString(char *aBuf, uint16_t aSize) const; + InfoString ToInfoString(void) const; private: uint8_t mProtocolId; diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index 490254fac..252aff8fd 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -237,12 +237,7 @@ exit: { if (aEui64) { - char logString[Mac::Address::kAddressStringSize]; - - otLogInfoMeshCoP(GetInstance(), "Added Joiner (%s, %s)", aEui64->ToString(logString, sizeof(logString)), - aPSKd); - - OT_UNUSED_VARIABLE(logString); + otLogInfoMeshCoP(GetInstance(), "Added Joiner (%s, %s)", aEui64->ToString().AsCString(), aPSKd); } else { @@ -304,11 +299,7 @@ exit: { if (aEui64) { - char logString[Mac::Address::kAddressStringSize]; - - otLogInfoMeshCoP(GetInstance(), "Removed Joiner (%s)", aEui64->ToString(logString, sizeof(logString))); - - OT_UNUSED_VARIABLE(logString); + otLogInfoMeshCoP(GetInstance(), "Removed Joiner (%s)", aEui64->ToString().AsCString()); } else { diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index 08cb98c9f..394e7c037 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -196,11 +196,9 @@ void Joiner::HandleDiscoverResult(otActiveScanResult *aResult) if (aResult != NULL) { JoinerRouter joinerRouter; - char logString[Mac::Address::kAddressStringSize]; otLogDebgMeshCoP(GetInstance(), "Received Discovery Response (%s)", - static_cast(aResult->mExtAddress).ToString(logString, sizeof(logString))); - OT_UNUSED_VARIABLE(logString); + static_cast(aResult->mExtAddress).ToString().AsCString()); // Joining is disabled if the Steering Data is not included if (aResult->mSteeringData.mLength == 0) diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 101f4de65..d10c1ef48 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -318,13 +318,11 @@ exit: return error; } -const char *Address::ToString(char *aBuf, uint16_t aSize) const +Address::InfoString Address::ToString(void) const { - snprintf(aBuf, aSize, "%x:%x:%x:%x:%x:%x:%x:%x", HostSwap16(mFields.m16[0]), HostSwap16(mFields.m16[1]), - HostSwap16(mFields.m16[2]), HostSwap16(mFields.m16[3]), HostSwap16(mFields.m16[4]), - HostSwap16(mFields.m16[5]), HostSwap16(mFields.m16[6]), HostSwap16(mFields.m16[7])); - - return aBuf; + return InfoString("%x:%x:%x:%x:%x:%x:%x:%x", HostSwap16(mFields.m16[0]), HostSwap16(mFields.m16[1]), + HostSwap16(mFields.m16[2]), HostSwap16(mFields.m16[3]), HostSwap16(mFields.m16[4]), + HostSwap16(mFields.m16[5]), HostSwap16(mFields.m16[6]), HostSwap16(mFields.m16[7])); } } // namespace Ip6 diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index c6a96a3d2..21d44c44f 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -40,6 +40,7 @@ #include +#include "common/string.hpp" #include "mac/mac_frame.hpp" namespace ot { @@ -97,6 +98,12 @@ public: kGlobalScope = 14, ///< Global scope }; + /** + * This type defines the fixed-length `String` object returned from `ToString()`. + * + */ + typedef String InfoString; + /** * This method clears the IPv6 address by setting it to the Unspecified Address "::". * @@ -365,15 +372,12 @@ public: otError FromString(const char *aBuf); /** - * This method converts an IPv6 address object to a NULL-terminated string. + * This method converts an IPv6 address object to a string * - * @param[out] aBuf A pointer to the buffer. - * @param[in] aSize The maximum size of the buffer. - * - * @returns A pointer to the buffer. + * @returns An `InfoString` representing the IPv6 address. * */ - const char *ToString(char *aBuf, uint16_t aSize) const; + InfoString ToString(void) const; /** * This method returns the number of IPv6 prefix bits that match. diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 889cdd954..baedc6e8b 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -185,8 +185,6 @@ const char *AddressResolver::ConvertInvalidationReasonToString(InvalidationReaso void AddressResolver::InvalidateCacheEntry(Cache &aEntry, InvalidationReason aReason) { - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; - for (int i = 0; i < kCacheEntries; i++) { if (mCache[i].mAge > aEntry.mAge) @@ -198,14 +196,13 @@ void AddressResolver::InvalidateCacheEntry(Cache &aEntry, InvalidationReason aRe switch (aEntry.mState) { case Cache::kStateCached: - otLogInfoArp(GetInstance(), "Cache entry removed: %s, 0x%04x - %s", - aEntry.mTarget.ToString(stringBuffer, sizeof(stringBuffer)), aEntry.mRloc16, - ConvertInvalidationReasonToString(aReason)); + otLogInfoArp(GetInstance(), "Cache entry removed: %s, 0x%04x - %s", aEntry.mTarget.ToString().AsCString(), + aEntry.mRloc16, ConvertInvalidationReasonToString(aReason)); break; case Cache::kStateQuery: otLogInfoArp(GetInstance(), "Cache entry (query mode) removed: %s, timeout:%d, retry:%d - %s", - aEntry.mTarget.ToString(stringBuffer, sizeof(stringBuffer)), aEntry.mTimeout, aEntry.mRetryTimeout, + aEntry.mTarget.ToString().AsCString(), aEntry.mTimeout, aEntry.mRetryTimeout, ConvertInvalidationReasonToString(aReason)); break; @@ -216,7 +213,6 @@ void AddressResolver::InvalidateCacheEntry(Cache &aEntry, InvalidationReason aRe aEntry.mAge = kCacheEntries - 1; aEntry.mState = Cache::kStateInvalid; - OT_UNUSED_VARIABLE(stringBuffer); OT_UNUSED_VARIABLE(aReason); } @@ -231,8 +227,6 @@ void AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddre if (mCache[i].mRloc16 != aRloc16) { - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; - // not updating the age here is intentional because this cache entry is not actually being used mCache[i].mRloc16 = aRloc16; @@ -247,10 +241,8 @@ void AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddre GetNetif().GetMeshForwarder().HandleResolved(aEid, OT_ERROR_NONE); } - otLogInfoArp(GetInstance(), "Cache entry updated (snoop): %s, 0x%04x", - aEid.ToString(stringBuffer, sizeof(stringBuffer)), aRloc16); - - OT_UNUSED_VARIABLE(stringBuffer); + otLogInfoArp(GetInstance(), "Cache entry updated (snoop): %s, 0x%04x", aEid.ToString().AsCString(), + aRloc16); } ExitNow(); @@ -333,7 +325,6 @@ otError AddressResolver::SendAddressQuery(const Ip6::Address &aEid) Coap::Header header; ThreadTargetTlv targetTlv; Ip6::MessageInfo messageInfo; - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; header.Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST); header.AppendUriPathOptions(OT_URI_PATH_ADDRESS_QUERY); @@ -353,9 +344,7 @@ otError AddressResolver::SendAddressQuery(const Ip6::Address &aEid) SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo)); - otLogInfoArp(GetInstance(), "Sending address query for %s", aEid.ToString(stringBuffer, sizeof(stringBuffer))); - - OT_UNUSED_VARIABLE(stringBuffer); + otLogInfoArp(GetInstance(), "Sending address query for %s", aEid.ToString().AsCString()); exit: @@ -392,7 +381,6 @@ void AddressResolver::HandleAddressNotification(Coap::Header & aHeader, ThreadRloc16Tlv rloc16Tlv; ThreadLastTransactionTimeTlv lastTransactionTimeTlv; uint32_t lastTransactionTime; - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE && aHeader.GetCode() == OT_COAP_CODE_POST); @@ -415,8 +403,8 @@ void AddressResolver::HandleAddressNotification(Coap::Header & aHeader, } otLogInfoArp(GetInstance(), "Received address notification from 0x%04x for %s to 0x%04x", - HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]), - targetTlv.GetTarget().ToString(stringBuffer, sizeof(stringBuffer)), rloc16Tlv.GetRloc16()); + HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]), targetTlv.GetTarget().ToString().AsCString(), + rloc16Tlv.GetRloc16()); for (int i = 0; i < kCacheEntries; i++) { @@ -457,8 +445,8 @@ void AddressResolver::HandleAddressNotification(Coap::Header & aHeader, mCache[i].mState = Cache::kStateCached; MarkCacheEntryAsUsed(mCache[i]); - otLogInfoArp(GetInstance(), "Cache entry updated (notification): %s, 0x%04x, lastTrans:%d", stringBuffer, - rloc16Tlv.GetRloc16(), lastTransactionTime); + otLogInfoArp(GetInstance(), "Cache entry updated (notification): %s, 0x%04x, lastTrans:%d", + targetTlv.GetTarget().ToString().AsCString(), rloc16Tlv.GetRloc16(), lastTransactionTime); if (netif.GetCoap().SendEmptyAck(aHeader, aMessageInfo) == OT_ERROR_NONE) { @@ -470,8 +458,6 @@ void AddressResolver::HandleAddressNotification(Coap::Header & aHeader, } } - OT_UNUSED_VARIABLE(stringBuffer); - exit: return; } @@ -485,7 +471,6 @@ otError AddressResolver::SendAddressError(const ThreadTargetTlv & aTarget, Message * message; Coap::Header header; Ip6::MessageInfo messageInfo; - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; header.Init(aDestination == NULL ? OT_COAP_TYPE_NON_CONFIRMABLE : OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST); header.AppendUriPathOptions(OT_URI_PATH_ADDRESS_ERROR); @@ -512,10 +497,7 @@ otError AddressResolver::SendAddressError(const ThreadTargetTlv & aTarget, SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo)); - otLogInfoArp(GetInstance(), "Sending address error for target %s", - aTarget.GetTarget().ToString(stringBuffer, sizeof(stringBuffer))); - - OT_UNUSED_VARIABLE(stringBuffer); + otLogInfoArp(GetInstance(), "Sending address error for target %s", aTarget.GetTarget().ToString().AsCString()); exit: @@ -632,7 +614,6 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag ThreadTargetTlv targetTlv; ThreadMeshLocalEidTlv mlIidTlv; ThreadLastTransactionTimeTlv lastTransactionTimeTlv; - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_NON_CONFIRMABLE && aHeader.GetCode() == OT_COAP_CODE_POST); @@ -644,8 +625,7 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag lastTransactionTimeTlv.Init(); otLogInfoArp(GetInstance(), "Received address query from 0x%04x for target %s", - HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]), - targetTlv.GetTarget().ToString(stringBuffer, sizeof(stringBuffer))); + HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]), targetTlv.GetTarget().ToString().AsCString()); if (netif.IsUnicastAddress(targetTlv.GetTarget())) { @@ -672,8 +652,6 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag } } - OT_UNUSED_VARIABLE(stringBuffer); - exit: return; } @@ -689,7 +667,6 @@ void AddressResolver::SendAddressQueryResponse(const ThreadTargetTlv & Coap::Header header; ThreadRloc16Tlv rloc16Tlv; Ip6::MessageInfo messageInfo; - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST); header.AppendUriPathOptions(OT_URI_PATH_ADDRESS_NOTIFY); @@ -716,9 +693,7 @@ void AddressResolver::SendAddressQueryResponse(const ThreadTargetTlv & SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo)); otLogInfoArp(GetInstance(), "Sending address notification for target %s", - aTargetTlv.GetTarget().ToString(stringBuffer, sizeof(stringBuffer))); - - OT_UNUSED_VARIABLE(stringBuffer); + aTargetTlv.GetTarget().ToString().AsCString()); exit: @@ -736,7 +711,6 @@ void AddressResolver::HandleTimer(Timer &aTimer) void AddressResolver::HandleTimer(void) { bool continueTimer = false; - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; for (int i = 0; i < kCacheEntries; i++) { @@ -766,7 +740,7 @@ void AddressResolver::HandleTimer(void) } otLogInfoArp(GetInstance(), "Timed out waiting for address notification for %s, retry: %d", - mCache[i].mTarget.ToString(stringBuffer, sizeof(stringBuffer)), mCache[i].mRetryTimeout); + mCache[i].mTarget.ToString().AsCString(), mCache[i].mRetryTimeout); GetNetif().GetMeshForwarder().HandleResolved(mCache[i].mTarget, OT_ERROR_DROP); } @@ -781,8 +755,6 @@ void AddressResolver::HandleTimer(void) { mTimer.Start(kStateUpdatePeriod); } - - OT_UNUSED_VARIABLE(stringBuffer); } void AddressResolver::HandleIcmpReceive(void * aContext, diff --git a/src/core/thread/announce_sender.cpp b/src/core/thread/announce_sender.cpp index 3c7ca9941..8de079923 100644 --- a/src/core/thread/announce_sender.cpp +++ b/src/core/thread/announce_sender.cpp @@ -145,7 +145,6 @@ void AnnounceSender::CheckState(void) uint32_t interval = kRouterTxInterval; uint32_t period; Mac::ChannelMask channelMask; - char string[Mac::ChannelMask::kInfoStringSize]; switch (mle.GetRole()) { @@ -185,9 +184,7 @@ void AnnounceSender::CheckState(void) SendAnnounce(channelMask, 0, period, kMaxJitter); otLogInfoMle(GetInstance(), "Starting periodic MLE Announcements tx, period %u, mask %s", period, - channelMask.ToString(string, sizeof(string))); - - OT_UNUSED_VARIABLE(string); + channelMask.ToString().AsCString()); exit: return; diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp index cf2ea600d..06675405c 100644 --- a/src/core/thread/link_quality.cpp +++ b/src/core/thread/link_quality.cpp @@ -129,20 +129,15 @@ exit: return average; } -const char *RssAverager::ToString(char *aBuf, uint16_t aSize) const +RssAverager::InfoString RssAverager::ToString(void) const { - if (mCount == 0) - { - VerifyOrExit(aSize > 0); - *aBuf = 0; - } - else - { - snprintf(aBuf, aSize, "%d.%s", -(mAverage >> kPrecisionBitShift), kDigitsString[mAverage & kPrecisionBitMask]); - } + InfoString string; + + VerifyOrExit(mCount != 0); + string.Set("%d.%s", -(mAverage >> kPrecisionBitShift), kDigitsString[mAverage & kPrecisionBitMask]); exit: - return aBuf; + return string; } LinkQualityInfo::LinkQualityInfo(void) @@ -180,14 +175,10 @@ exit: return; } -const char *LinkQualityInfo::ToInfoString(char *aBuf, uint16_t aSize) const +LinkQualityInfo::InfoString LinkQualityInfo::ToInfoString(void) const { - char rssString[RssAverager::kStringSize]; - - snprintf(aBuf, aSize, "aveRss:%s, lastRss:%d, linkQuality:%d", mRssAverager.ToString(rssString, sizeof(rssString)), - GetLastRss(), GetLinkQuality()); - - return aBuf; + return InfoString("aveRss:%s, lastRss:%d, linkQuality:%d", mRssAverager.ToString().AsCString(), GetLastRss(), + GetLinkQuality()); } uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss) diff --git a/src/core/thread/link_quality.hpp b/src/core/thread/link_quality.hpp index 128a613d5..0f905ed39 100644 --- a/src/core/thread/link_quality.hpp +++ b/src/core/thread/link_quality.hpp @@ -39,6 +39,8 @@ #include #include +#include "common/string.hpp" + namespace ot { /** @@ -130,6 +132,12 @@ public: kStringSize = 10, ///< Max chars needed for a string representation of average (@sa ToString()). }; + /** + * This type defines the fixed-length `String` object returned from `ToString()`. + * + */ + typedef String InfoString; + /** * This method reset the averager and clears the average value. * @@ -180,13 +188,10 @@ public: * This method converts the current average RSS value to a human-readable string (e.g., "-80.375"). If the * average is unknown, empty string is returned. * - * @param[out] aBuf A pointer to the char buffer. - * @param[in] aSize The maximum size of the buffer. - * - * @returns A pointer to the char string buffer. + * @returns An `InfoString` object containing the string representation of average RSS. * */ - const char *ToString(char *aBuf, uint16_t aSize) const; + InfoString ToString(void) const; private: /* @@ -230,6 +235,12 @@ public: kInfoStringSize = 50, ///< Max chars needed for the info string representation (@sa ToInfoString()) }; + /** + * This type defines the fixed-length `String` object returned from `ToInfoString()`. + * + */ + typedef String InfoString; + /** * This constructor initializes the object. * @@ -269,15 +280,12 @@ public: uint16_t GetAverageRssRaw(void) const { return mRssAverager.GetRaw(); } /** - * This method converts the link quality info to NULL-terminated info/debug human-readable string. + * This method converts the link quality info to info/debug human-readable string. * - * @param[out] aBuf A pointer to the string buffer. - * @param[in] aSize The maximum size of the string buffer. - * - * @returns A pointer to the char string buffer. + * @returns An `InfoString` representing the link quality info. * */ - const char *ToInfoString(char *aBuf, uint16_t aSize) const; + InfoString ToInfoString(void) const; /** * This method returns the link margin. The link margin is calculated using the link's current average received diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index dee6b56b3..e6b12e081 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1435,8 +1435,6 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, const char * priorityText; bool shouldLogRss = false; bool shouldLogSrcDstAddresses = (OPENTHREAD_CONFIG_LOG_SRC_DST_IP_ADDRESSES != 0); - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; - char rssString[RssAverager::kStringSize]; VerifyOrExit(aMessage.GetType() == Message::kTypeIp6); @@ -1543,16 +1541,15 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, otLogInfoMac(GetInstance(), "%s IPv6 %s msg, len:%d, chksum:%04x%s%s, sec:%s%s%s, prio:%s%s%s", actionText, Ip6::Ip6::IpProtoToString(protocol), aMessage.GetLength(), checksum, (aMacAddress == NULL) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"), - (aMacAddress == NULL) ? "" : aMacAddress->ToString(stringBuffer, sizeof(stringBuffer)), + (aMacAddress == NULL) ? "" : aMacAddress->ToString().AsCString(), aMessage.IsLinkSecurityEnabled() ? "yes" : "no", (aError == OT_ERROR_NONE) ? "" : ", error:", (aError == OT_ERROR_NONE) ? "" : otThreadErrorToString(aError), priorityText, - shouldLogRss ? ", rss:" : "", - shouldLogRss ? aMessage.GetRssAverager().ToString(rssString, sizeof(rssString)) : ""); + shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : ""); if (shouldLogSrcDstAddresses) { - otLogInfoMac(GetInstance(), "src: %s", ip6Header.GetSource().ToString(stringBuffer, sizeof(stringBuffer))); - otLogInfoMac(GetInstance(), "dst: %s", ip6Header.GetDestination().ToString(stringBuffer, sizeof(stringBuffer))); + otLogInfoMac(GetInstance(), "src: %s", ip6Header.GetSource().ToString().AsCString()); + otLogInfoMac(GetInstance(), "dst: %s", ip6Header.GetDestination().ToString().AsCString()); } exit: @@ -1561,16 +1558,14 @@ exit: void MeshForwarder::LogFrame(const char *aActionText, const Mac::Frame &aFrame, otError aError) { - char stringBuffer[Mac::Frame::kInfoStringSize]; - if (aError != OT_ERROR_NONE) { otLogInfoMac(GetInstance(), "%s, aError:%s, %s", aActionText, otThreadErrorToString(aError), - aFrame.ToInfoString(stringBuffer, sizeof(stringBuffer))); + aFrame.ToInfoString().AsCString()); } else { - otLogInfoMac(GetInstance(), "%s, %s", aActionText, aFrame.ToInfoString(stringBuffer, sizeof(stringBuffer))); + otLogInfoMac(GetInstance(), "%s, %s", aActionText, aFrame.ToInfoString().AsCString()); } } @@ -1581,14 +1576,11 @@ void MeshForwarder::LogFragmentFrameDrop(otError aError, const Lowpan::FragmentHeader &aFragmentHeader, bool aIsSecure) { - char srcStringBuffer[Mac::Address::kAddressStringSize]; - char dstStringBuffer[Mac::Address::kAddressStringSize]; - - otLogInfoMac( - GetInstance(), "Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s", - otThreadErrorToString(aError), aFrameLength, aMacSource.ToString(srcStringBuffer, sizeof(srcStringBuffer)), - aMacDest.ToString(dstStringBuffer, sizeof(dstStringBuffer)), aFragmentHeader.GetDatagramTag(), - aFragmentHeader.GetDatagramOffset(), aFragmentHeader.GetDatagramSize(), aIsSecure ? "yes" : "no"); + otLogInfoMac(GetInstance(), + "Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s", + otThreadErrorToString(aError), aFrameLength, aMacSource.ToString().AsCString(), + aMacDest.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(), + aFragmentHeader.GetDatagramSize(), aIsSecure ? "yes" : "no"); } void MeshForwarder::LogLowpanHcFrameDrop(otError aError, @@ -1597,13 +1589,9 @@ void MeshForwarder::LogLowpanHcFrameDrop(otError aError, const Mac::Address &aMacDest, bool aIsSecure) { - char srcStringBuffer[Mac::Address::kAddressStringSize]; - char dstStringBuffer[Mac::Address::kAddressStringSize]; - otLogInfoMac(GetInstance(), "Dropping rx lowpan HC frame, error:%s, len:%d, src:%s, dst:%s, sec:%s", - otThreadErrorToString(aError), aFrameLength, - aMacSource.ToString(srcStringBuffer, sizeof(srcStringBuffer)), - aMacDest.ToString(dstStringBuffer, sizeof(dstStringBuffer)), aIsSecure ? "yes" : "no"); + otThreadErrorToString(aError), aFrameLength, aMacSource.ToString().AsCString(), + aMacDest.ToString().AsCString(), aIsSecure ? "yes" : "no"); } #else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 257caf1f8..f1d98e34e 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -923,15 +923,10 @@ exit: if (error != OT_ERROR_NONE) { - char srcStringBuffer[Mac::Address::kAddressStringSize]; - otLogInfoMac(GetInstance(), "Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", - otThreadErrorToString(error), aFrameLength, - aMacSource.ToString(srcStringBuffer, sizeof(srcStringBuffer)), + otThreadErrorToString(error), aFrameLength, aMacSource.ToString().AsCString(), aLinkInfo.mLinkSecurity ? "yes" : "no"); - OT_UNUSED_VARIABLE(srcStringBuffer); - if (message != NULL) { message->Free(); diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 96b497812..84e60aa1e 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -3854,11 +3854,7 @@ void Mle::UpdateParentSearchState(void) void Mle::LogMleMessage(const char *aLogString, const Ip6::Address &aAddress) const { -#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1) - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; - - otLogInfoMle(GetInstance(), "%s (%s)", aLogString, aAddress.ToString(stringBuffer, sizeof(stringBuffer))); -#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1) + otLogInfoMle(GetInstance(), "%s (%s)", aLogString, aAddress.ToString().AsCString()); OT_UNUSED_VARIABLE(aLogString); OT_UNUSED_VARIABLE(aAddress); @@ -3866,12 +3862,7 @@ void Mle::LogMleMessage(const char *aLogString, const Ip6::Address &aAddress) co void Mle::LogMleMessage(const char *aLogString, const Ip6::Address &aAddress, uint16_t aRloc) const { -#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1) - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; - - otLogInfoMle(GetInstance(), "%s (%s,0x%04x)", aLogString, aAddress.ToString(stringBuffer, sizeof(stringBuffer)), - aRloc); -#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1) + otLogInfoMle(GetInstance(), "%s (%s,0x%04x)", aLogString, aAddress.ToString().AsCString(), aRloc); OT_UNUSED_VARIABLE(aLogString); OT_UNUSED_VARIABLE(aAddress); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 2588787e3..ed41a2ca2 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1862,7 +1862,6 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse uint8_t storedCount = 0; uint16_t offset = 0; uint16_t end = 0; - char stringBuffer[Ip6::Address::kIp6AddressStringSize]; VerifyOrExit(aMessage.Read(aOffset, sizeof(tlv), &tlv) == sizeof(tlv), error = OT_ERROR_PARSE); VerifyOrExit(tlv.GetLength() <= (aMessage.GetLength() - aOffset - sizeof(tlv)), error = OT_ERROR_PARSE); @@ -1911,12 +1910,12 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse { storedCount++; otLogInfoMle(GetInstance(), "Child 0x%04x IPv6 address[%d]=%s", aChild.GetRloc16(), storedCount, - address.ToString(stringBuffer, sizeof(stringBuffer))); + address.ToString().AsCString()); } else { otLogWarnMle(GetInstance(), "Error %s adding IPv6 address %s to child 0x%04x", otThreadErrorToString(error), - address.ToString(stringBuffer, sizeof(stringBuffer)), aChild.GetRloc16()); + address.ToString().AsCString(), aChild.GetRloc16()); } if (address.IsMulticast()) @@ -1960,8 +1959,6 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse error = OT_ERROR_NONE; exit: - OT_UNUSED_VARIABLE(stringBuffer); - return error; } diff --git a/src/core/utils/channel_manager.cpp b/src/core/utils/channel_manager.cpp index b019d78cb..925bbae85 100644 --- a/src/core/utils/channel_manager.cpp +++ b/src/core/utils/channel_manager.cpp @@ -321,7 +321,6 @@ otError ChannelManager::FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccup Mac::ChannelMask supportedBest; uint16_t favoredOccupancy; uint16_t supportedOccupancy; - char string[Mac::ChannelMask::kInfoStringSize]; if (monitor.GetSampleCount() <= kMinChannelMonitorSampleCount) { @@ -337,9 +336,9 @@ otError ChannelManager::FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccup supportedBest = monitor.FindBestChannels(mSupportedChannelMask, supportedOccupancy); otLogInfoUtil(GetInstance(), "ChannelManager: Best favored %s, occupancy 0x%04x", - favoredBest.ToString(string, sizeof(string)), favoredOccupancy); + favoredBest.ToString().AsCString(), favoredOccupancy); otLogInfoUtil(GetInstance(), "ChannelManager: Best overall %s, occupancy 0x%04x", - supportedBest.ToString(string, sizeof(string)), supportedOccupancy); + supportedBest.ToString().AsCString(), supportedOccupancy); // Prefer favored channels unless there is no favored channel, // or the occupancy rate of the best favored channel is worse @@ -363,8 +362,6 @@ otError ChannelManager::FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccup aNewChannel = ChooseRandomChannel(favoredBest); aOccupancy = favoredOccupancy; - OT_UNUSED_VARIABLE(string); - exit: return error; } @@ -487,26 +484,17 @@ exit: void ChannelManager::SetSupportedChannels(uint32_t aChannelMask) { - char string[Mac::ChannelMask::kInfoStringSize]; - mSupportedChannelMask.SetMask(aChannelMask & OT_RADIO_SUPPORTED_CHANNELS); otLogInfoUtil(GetInstance(), "ChannelManager: Supported channels: %s", - mSupportedChannelMask.ToString(string, sizeof(string))); - - OT_UNUSED_VARIABLE(string); + mSupportedChannelMask.ToString().AsCString()); } void ChannelManager::SetFavoredChannels(uint32_t aChannelMask) { - char string[Mac::ChannelMask::kInfoStringSize]; - mFavoredChannelMask.SetMask(aChannelMask & OT_RADIO_SUPPORTED_CHANNELS); - otLogInfoUtil(GetInstance(), "ChannelManager: Favored channels: %s", - mFavoredChannelMask.ToString(string, sizeof(string))); - - OT_UNUSED_VARIABLE(string); + otLogInfoUtil(GetInstance(), "ChannelManager: Favored channels: %s", mFavoredChannelMask.ToString().AsCString()); } } // namespace Utils diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index fe6b5e04b..a9c66ba5d 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -98,6 +98,7 @@ check_PROGRAMS = \ test-pskc \ test-spinel-decoder \ test-spinel-encoder \ + test-string \ test-strlcat \ test-strlcpy \ test-strnlen \ @@ -184,6 +185,9 @@ test_priority_queue_SOURCES = test_platform.cpp test_priority_queue.cpp test_pskc_LDADD = $(COMMON_LDADD) test_pskc_SOURCES = test_platform.cpp test_pskc.cpp +test_string_LDADD = $(COMMON_LDADD) +test_string_SOURCES = test_platform.cpp test_string.cpp + test_strlcat_LDADD = $(COMMON_LDADD) test_strlcat_SOURCES = test_strlcat.c @@ -231,6 +235,7 @@ PRETTY_FILES = \ $(test_pskc_SOURCES) \ $(test_spinel_decoder_SOURCES) \ $(test_spinel_encoder_SOURCES) \ + $(test_string) \ $(test_strlcat_SOURCES) \ $(test_strlcpy_SOURCES) \ $(test_strnlen_SOURCES) \ diff --git a/tests/unit/test_link_quality.cpp b/tests/unit/test_link_quality.cpp index bb4a6052c..f9fa279e5 100644 --- a/tests/unit/test_link_quality.cpp +++ b/tests/unit/test_link_quality.cpp @@ -83,14 +83,7 @@ void VerifyRawRssValue(int8_t aAverage, uint16_t aRawValue) // This function prints the values in the passed in link info instance. It is invoked as the final step in test-case. void PrintOutcome(LinkQualityInfo &aLinkInfo) { - char stringBuf[LinkQualityInfo::kInfoStringSize]; - - VerifyOrQuit(aLinkInfo.ToInfoString(stringBuf, sizeof(stringBuf)) != NULL, "ToInfoString() returned NULL"); - - printf("%s", stringBuf); - - // This test-case succeeded. - printf(" -> PASS\n"); + printf("%s -> PASS \n", aLinkInfo.ToInfoString().AsCString()); } void TestLinkQualityData(RssTestData aRssData) @@ -139,16 +132,10 @@ void VerifyRawRssValue(RssAverager &aRssAverager) } } -// This function prints the values in the passed in link info instance. It is invoked as the final step in test-case. +// This function prints the values in the passed link info instance. It is invoked as the final step in test-case. void PrintOutcome(RssAverager &aRssAverager) { - char stringBuf[RssAverager::kStringSize]; - - VerifyOrQuit(aRssAverager.ToString(stringBuf, sizeof(stringBuf)) != NULL, "ToString() returned NULL"); - printf("%s", stringBuf); - - // This test-case succeeded. - printf(" -> PASS\n"); + printf("%s -> PASS\n", aRssAverager.ToString().AsCString()); } int8_t GetRandomRss(void) diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index e92ac34c4..aec0af8bc 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -136,16 +136,14 @@ void TestMacChannelMask(void) Mac::ChannelMask mask1; Mac::ChannelMask mask2(OT_RADIO_SUPPORTED_CHANNELS); - char stringBuffer[Mac::ChannelMask::kInfoStringSize]; - printf("Testing Mac::ChannelMask\n"); VerifyOrQuit(mask1.IsEmpty(), "ChannelMask.IsEmpty failed\n"); - printf("empty = %s\n", mask1.ToString(stringBuffer, sizeof(stringBuffer))); + printf("empty = %s\n", mask1.ToString().AsCString()); VerifyOrQuit(!mask2.IsEmpty(), "ChannelMask.IsEmpty failed\n"); VerifyOrQuit(mask2.GetMask() == OT_RADIO_SUPPORTED_CHANNELS, "ChannelMask.GetMask() failed\n"); - printf("all_channels = %s\n", mask2.ToString(stringBuffer, sizeof(stringBuffer))); + printf("all_channels = %s\n", mask2.ToString().AsCString()); mask1.SetMask(OT_RADIO_SUPPORTED_CHANNELS); VerifyOrQuit(!mask1.IsEmpty(), "ChannelMask.IsEmpty failed\n"); @@ -169,7 +167,7 @@ void TestMacChannelMask(void) mask1.AddChannel(channels1[index]); } - printf("channels1 = %s\n", mask1.ToString(stringBuffer, sizeof(stringBuffer))); + printf("channels1 = %s\n", mask1.ToString().AsCString()); VerifyOrQuit(!mask1.IsEmpty(), "ChannelMask.IsEmpty failed\n"); VerifyChannelMaskContent(mask1, channels1, sizeof(channels1)); @@ -181,7 +179,7 @@ void TestMacChannelMask(void) mask2.AddChannel(channels2[index]); } - printf("channels2 = %s\n", mask2.ToString(stringBuffer, sizeof(stringBuffer))); + printf("channels2 = %s\n", mask2.ToString().AsCString()); VerifyOrQuit(!mask2.IsEmpty(), "ChannelMask.IsEmpty failed\n"); VerifyChannelMaskContent(mask2, channels2, sizeof(channels2)); @@ -193,7 +191,7 @@ void TestMacChannelMask(void) mask2.AddChannel(channles4[0]); VerifyChannelMaskContent(mask2, channles4, sizeof(channles4)); - printf("channels4 = %s\n", mask2.ToString(stringBuffer, sizeof(stringBuffer))); + printf("channels4 = %s\n", mask2.ToString().AsCString()); mask1.Clear(); mask2.Clear(); diff --git a/tests/unit/test_string.cpp b/tests/unit/test_string.cpp new file mode 100644 index 000000000..60973cacc --- /dev/null +++ b/tests/unit/test_string.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_platform.h" + +#include +#include + +#include "test_util.h" +#include "common/code_utils.hpp" +#include "common/string.hpp" + +namespace ot { + +enum +{ + kStringSize = 10, +}; + +template +void PrintString(const char *aName, const String aString) +{ + printf("\t%s = [%d] \"%s\"\n", aName, aString.GetLength(), aString.AsCString()); +} + +void TestString(void) +{ + otError error; + String str1; + String str2("abc"); + String str3("%d", 12); + + printf("\nTest 1: String constructor\n"); + + VerifyOrQuit(str1.GetSize() == kStringSize, "GetSize() failed"); + + VerifyOrQuit(str1.GetLength() == 0, "GetLength() failed for empty string"); + VerifyOrQuit(str2.GetLength() == 3, "GetLength() failed"); + VerifyOrQuit(str3.GetLength() == 2, "GetLength() failed"); + + VerifyOrQuit(strcmp(str1.AsCString(), "") == 0, "String content is incorrect"); + VerifyOrQuit(strcmp(str2.AsCString(), "abc") == 0, "String content is incorrect"); + VerifyOrQuit(strcmp(str3.AsCString(), "12") == 0, "String content is incorrect"); + + PrintString("str1", str1); + PrintString("str2", str2); + PrintString("str3", str3); + + printf(" -- PASS\n"); + + printf("\nTest 2: String::Set() and String::Clear() method\n"); + + error = str1.Set("Hello"); + SuccessOrQuit(error, "String::Set() failed unexpectedly"); + VerifyOrQuit(str1.GetLength() == 5, "GetLength() failed for empty string"); + VerifyOrQuit(strcmp(str1.AsCString(), "Hello") == 0, "String content is incorrect"); + PrintString("str1", str1); + + str1.Clear(); + VerifyOrQuit(str1.GetLength() == 0, "GetLength() failed for empty string"); + VerifyOrQuit(strcmp(str1.AsCString(), "") == 0, "String content is incorrect"); + + str1.Set("%d", 12); + VerifyOrQuit(str1.GetLength() == 2, "GetLength() failed"); + VerifyOrQuit(strcmp(str1.AsCString(), "12") == 0, "String content is incorrect"); + PrintString("str1", str1); + + error = str1.Set("abcdefghijklmnopqratuvwxyzabcdefghijklmnopqratuvwxyz"); + VerifyOrQuit(error == OT_ERROR_NO_BUFS, "String::Set() did not handle overflow buffer correctly"); + PrintString("str1", str1); + + printf("\nTest 3: String::Append() method\n"); + + str2.Clear(); + VerifyOrQuit(str2.GetLength() == 0, "GetLength() failed for empty string"); + VerifyOrQuit(strcmp(str2.AsCString(), "") == 0, "String content is incorrect"); + + error = str2.Append("Hi"); + SuccessOrQuit(error, "String::Append() failed unexpectedly"); + VerifyOrQuit(str2.GetLength() == 2, "GetLength() failed"); + VerifyOrQuit(strcmp(str2.AsCString(), "Hi") == 0, "String content is incorrect"); + PrintString("str2", str2); + + error = str2.Append("%s%d", "!", 12); + SuccessOrQuit(error, "String::Append() failed unexpectedly"); + VerifyOrQuit(str2.GetLength() == 5, "GetLength() failed"); + VerifyOrQuit(strcmp(str2.AsCString(), "Hi!12") == 0, "String content is incorrect"); + PrintString("str2", str2); + + error = str2.Append("abcdefghijklmnopqratuvwxyzabcdefghijklmnopqratuvwxyz"); + VerifyOrQuit(error == OT_ERROR_NO_BUFS, "String::Append() did not handle overflow buffer correctly"); + PrintString("str2", str2); + + printf(" -- PASS\n"); +} + +} // namespace ot + +#ifdef ENABLE_TEST_MAIN +int main(void) +{ + ot::TestString(); + printf("\nAll tests passed.\n"); + return 0; +} +#endif