diff --git a/src/app/clusters/door-lock-server/door-lock-server.cpp b/src/app/clusters/door-lock-server/door-lock-server.cpp index 7552841edfb5e6..3912b022f90065 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server.cpp @@ -1827,7 +1827,7 @@ EmberAfStatus DoorLockServer::createUser(chip::EndpointId endpointId, chip::Fabr return static_cast(DlStatus::kOccupied); } - const auto & newUserName = !userName.IsNull() ? userName.Value() : chip::CharSpan::fromCharString(""); + const auto & newUserName = !userName.IsNull() ? userName.Value() : ""_span; auto newUserUniqueId = userUniqueId.IsNull() ? 0xFFFFFFFF : userUniqueId.Value(); auto newUserStatus = userStatus.IsNull() ? UserStatusEnum::kOccupiedEnabled : userStatus.Value(); auto newUserType = userType.IsNull() ? UserTypeEnum::kUnrestrictedUser : userType.Value(); diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 9a59e89837329b..d4b958a5418c0f 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -2543,7 +2543,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio else { // Default to "XX", for lack of anything better. - countryCode = CharSpan::fromCharString("XX"); + countryCode = "XX"_span; } GeneralCommissioning::Commands::SetRegulatoryConfig::Type request; diff --git a/src/credentials/tests/TestFabricTable.cpp b/src/credentials/tests/TestFabricTable.cpp index 3bcb5411560e7a..2226b399e75bce 100644 --- a/src/credentials/tests/TestFabricTable.cpp +++ b/src/credentials/tests/TestFabricTable.cpp @@ -2261,32 +2261,32 @@ void TestFabricLabelChange(nlTestSuite * inSuite, void * inContext) // Second scope: set FabricLabel to "acme fabric", make sure it cannot be reverted { // Fabric label starts unset from prior scope - CharSpan fabricLabel = CharSpan::fromCharString("placeholder"); + CharSpan fabricLabel = "placeholder"_span; NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.GetFabricLabel(1, fabricLabel)); NL_TEST_ASSERT(inSuite, fabricLabel.size() == 0); // Set a valid name - NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.SetFabricLabel(1, CharSpan::fromCharString("acme fabric"))); + NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.SetFabricLabel(1, "acme fabric"_span)); NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.GetFabricLabel(1, fabricLabel)); - NL_TEST_ASSERT(inSuite, fabricLabel.data_equal(CharSpan::fromCharString("acme fabric")) == true); + NL_TEST_ASSERT(inSuite, fabricLabel.data_equal("acme fabric"_span) == true); // Revert pending fabric data. Should not revert name since nothing pending. fabricTable.RevertPendingFabricData(); - fabricLabel = CharSpan::fromCharString("placeholder"); + fabricLabel = "placeholder"_span; NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.GetFabricLabel(1, fabricLabel)); - NL_TEST_ASSERT(inSuite, fabricLabel.data_equal(CharSpan::fromCharString("acme fabric")) == true); + NL_TEST_ASSERT(inSuite, fabricLabel.data_equal("acme fabric"_span) == true); // Verify we fail to set too large a label (> kFabricLabelMaxLengthInBytes) - CharSpan fabricLabelTooBig = CharSpan::fromCharString("012345678901234567890123456789123456"); + CharSpan fabricLabelTooBig = "012345678901234567890123456789123456"_span; NL_TEST_ASSERT(inSuite, fabricLabelTooBig.size() > chip::kFabricLabelMaxLengthInBytes); NL_TEST_ASSERT(inSuite, fabricTable.SetFabricLabel(1, fabricLabelTooBig) == CHIP_ERROR_INVALID_ARGUMENT); - fabricLabel = CharSpan::fromCharString("placeholder"); + fabricLabel = "placeholder"_span; NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.GetFabricLabel(1, fabricLabel)); - NL_TEST_ASSERT(inSuite, fabricLabel.data_equal(CharSpan::fromCharString("acme fabric")) == true); + NL_TEST_ASSERT(inSuite, fabricLabel.data_equal("acme fabric"_span) == true); } // Third scope: set fabric label after an update, it sticks, but then goes back after revert @@ -2320,23 +2320,23 @@ void TestFabricLabelChange(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, fabricInfo->GetVendorId() == kVendorId); CharSpan fabricLabel = fabricInfo->GetFabricLabel(); - NL_TEST_ASSERT(inSuite, fabricLabel.data_equal(CharSpan::fromCharString("acme fabric")) == true); + NL_TEST_ASSERT(inSuite, fabricLabel.data_equal("acme fabric"_span) == true); } // Update fabric label - CharSpan fabricLabel = CharSpan::fromCharString("placeholder"); - NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.SetFabricLabel(1, CharSpan::fromCharString("roboto fabric"))); + CharSpan fabricLabel = "placeholder"_span; + NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.SetFabricLabel(1, "roboto fabric"_span)); - fabricLabel = CharSpan::fromCharString("placeholder"); + fabricLabel = "placeholder"_span; NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.GetFabricLabel(1, fabricLabel)); - NL_TEST_ASSERT(inSuite, fabricLabel.data_equal(CharSpan::fromCharString("roboto fabric")) == true); + NL_TEST_ASSERT(inSuite, fabricLabel.data_equal("roboto fabric"_span) == true); // Revert pending fabric data. Should revert name to "acme fabric" fabricTable.RevertPendingFabricData(); - fabricLabel = CharSpan::fromCharString("placeholder"); + fabricLabel = "placeholder"_span; NL_TEST_ASSERT_SUCCESS(inSuite, fabricTable.GetFabricLabel(1, fabricLabel)); - NL_TEST_ASSERT(inSuite, fabricLabel.data_equal(CharSpan::fromCharString("acme fabric")) == true); + NL_TEST_ASSERT(inSuite, fabricLabel.data_equal("acme fabric"_span) == true); } } diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp index 39d9998fa95b80..a79961dbe07c99 100644 --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -2414,13 +2414,10 @@ static void TestSubject_x509Extraction(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == subjectDN_Node02_02.AddAttribute_MatterFabricId(0xFAB000000000001D)); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == - subjectDN_Node02_02.AddAttribute_CommonName( - chip::CharSpan::fromCharString("TEST CERT COMMON NAME Attr for Node02_02"), false)); + subjectDN_Node02_02.AddAttribute_CommonName("TEST CERT COMMON NAME Attr for Node02_02"_span, false)); ChipDN subjectDN_Node02_04; NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == subjectDN_Node02_04.AddAttribute_MatterCASEAuthTag(0xABCE1002)); - NL_TEST_ASSERT(inSuite, - CHIP_NO_ERROR == - subjectDN_Node02_04.AddAttribute_CommonName(chip::CharSpan::fromCharString("TestCert02_04"), false)); + NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == subjectDN_Node02_04.AddAttribute_CommonName("TestCert02_04"_span, false)); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == subjectDN_Node02_04.AddAttribute_MatterFabricId(0xFAB000000000001D)); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == subjectDN_Node02_04.AddAttribute_MatterCASEAuthTag(0xABCD0003)); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == subjectDN_Node02_04.AddAttribute_MatterNodeId(0xDEDEDEDE00020004)); diff --git a/src/lib/core/Unchecked.h b/src/lib/core/Unchecked.h new file mode 100644 index 00000000000000..d7fab990268168 --- /dev/null +++ b/src/lib/core/Unchecked.h @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines the chip::Optional class to handle values which may + * or may not be present. + * + */ +#pragma once + +namespace chip { + +/// Unchecked is a disambiguation tag that can be used to provide and select a variant of a +/// constructor or other method that omits the runtime checks performed by the default variant. +struct UncheckedType +{ + explicit UncheckedType() = default; +}; +inline constexpr UncheckedType Unchecked{}; + +} // namespace chip diff --git a/src/lib/core/tests/TestOTAImageHeader.cpp b/src/lib/core/tests/TestOTAImageHeader.cpp index 928dc2618100b9..0a05799c74ca35 100644 --- a/src/lib/core/tests/TestOTAImageHeader.cpp +++ b/src/lib/core/tests/TestOTAImageHeader.cpp @@ -83,13 +83,13 @@ void TestHappyPath(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, header.mVendorId == 0xDEAD); NL_TEST_ASSERT(inSuite, header.mProductId == 0xBEEF); NL_TEST_ASSERT(inSuite, header.mSoftwareVersion == 0xFFFFFFFF); - NL_TEST_ASSERT(inSuite, header.mSoftwareVersionString.data_equal(CharSpan::fromCharString("1.0"))); + NL_TEST_ASSERT(inSuite, header.mSoftwareVersionString.data_equal("1.0"_span)); NL_TEST_ASSERT(inSuite, header.mPayloadSize == strlen("test payload")); NL_TEST_ASSERT(inSuite, header.mMinApplicableVersion.HasValue()); NL_TEST_ASSERT(inSuite, header.mMinApplicableVersion.Value() == 1); NL_TEST_ASSERT(inSuite, header.mMaxApplicableVersion.HasValue()); NL_TEST_ASSERT(inSuite, header.mMaxApplicableVersion.Value() == 2); - NL_TEST_ASSERT(inSuite, header.mReleaseNotesURL.data_equal(CharSpan::fromCharString("https://rn"))); + NL_TEST_ASSERT(inSuite, header.mReleaseNotesURL.data_equal("https://rn"_span)); NL_TEST_ASSERT(inSuite, header.mImageDigestType == OTAImageDigestType::kSha256); NL_TEST_ASSERT(inSuite, header.mImageDigest.size() == 256 / 8); } @@ -169,13 +169,13 @@ void TestSmallBlocks(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, header.mVendorId == 0xDEAD); NL_TEST_ASSERT(inSuite, header.mProductId == 0xBEEF); NL_TEST_ASSERT(inSuite, header.mSoftwareVersion == 0xFFFFFFFF); - NL_TEST_ASSERT(inSuite, header.mSoftwareVersionString.data_equal(CharSpan::fromCharString("1.0"))); + NL_TEST_ASSERT(inSuite, header.mSoftwareVersionString.data_equal("1.0"_span)); NL_TEST_ASSERT(inSuite, header.mPayloadSize == strlen("test payload")); NL_TEST_ASSERT(inSuite, header.mMinApplicableVersion.HasValue()); NL_TEST_ASSERT(inSuite, header.mMinApplicableVersion.Value() == 1); NL_TEST_ASSERT(inSuite, header.mMaxApplicableVersion.HasValue()); NL_TEST_ASSERT(inSuite, header.mMaxApplicableVersion.Value() == 2); - NL_TEST_ASSERT(inSuite, header.mReleaseNotesURL.data_equal(CharSpan::fromCharString("https://rn"))); + NL_TEST_ASSERT(inSuite, header.mReleaseNotesURL.data_equal("https://rn"_span)); NL_TEST_ASSERT(inSuite, header.mImageDigestType == OTAImageDigestType::kSha256); NL_TEST_ASSERT(inSuite, header.mImageDigest.size() == 256 / 8); } diff --git a/src/lib/support/Span.h b/src/lib/support/Span.h index cc6c082f4d69bb..aad5ffe4bfa5fe 100644 --- a/src/lib/support/Span.h +++ b/src/lib/support/Span.h @@ -18,11 +18,13 @@ #pragma once #include +#include #include #include -#include +#include #include +#include #include namespace chip { @@ -149,7 +151,10 @@ class Span return Span(reinterpret_cast(&bytes[1]), length); } - // Allow creating CharSpans from a character string. + // Creates a CharSpan from a null-terminated C character string. + // + // Note that for string literals, the user-defined `_span` string + // literal operator should be used instead, e.g. `"Hello"_span`. template ::value && std::is_same::value>> static Span fromCharString(U * chars) { @@ -162,11 +167,31 @@ class Span template bool operator==(const Span & other) const = delete; + // Creates a Span without checking whether databuf is a null pointer. + // + // Note: The normal (checked) constructor should be used for general use; + // this overload exists for special use cases where databuf is guaranteed + // to be valid (not null) and a constexpr constructor is required. + // + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61648 prevents making + // operator""_span a friend (and this constructor private). + + constexpr Span(UncheckedType tag, pointer databuf, size_t datalen) : mDataBuf(databuf), mDataLen(datalen) {} + private: pointer mDataBuf; size_t mDataLen; }; +inline namespace literals { + +inline constexpr Span operator"" _span(const char * literal, size_t size) +{ + return Span(Unchecked, literal, size); +} + +} // namespace literals + namespace detail { // To make FixedSpan (specifically various FixedByteSpan types) default constructible diff --git a/src/lib/support/tests/TestJsonToTlv.cpp b/src/lib/support/tests/TestJsonToTlv.cpp index 87f37ab4e052b1..c6f5df36333238 100644 --- a/src/lib/support/tests/TestJsonToTlv.cpp +++ b/src/lib/support/tests/TestJsonToTlv.cpp @@ -141,7 +141,7 @@ void TestConverter(nlTestSuite * inSuite, void * inContext) jsonString = "{\n" " \"1:STRING\" : \"hello\"\n" "}\n"; - ConvertJsonToTlvAndValidate(CharSpan::fromCharString("hello"), jsonString); + ConvertJsonToTlvAndValidate("hello"_span, jsonString); // Validated using https://base64.guru/converter/encode/hex const uint8_t byteBuf[] = { 0x01, 0x02, 0x03, 0x04, 0xff, 0xfe, 0x99, 0x88, 0xdd, 0xcd }; @@ -161,7 +161,7 @@ void TestConverter(nlTestSuite * inSuite, void * inContext) structVal.a = 20; structVal.b = true; structVal.d = byteBuf; - structVal.e = CharSpan::fromCharString("hello"); + structVal.e = "hello"_span; structVal.g = static_cast(1.0); structVal.h = static_cast(1.0); diff --git a/src/lib/support/tests/TestSpan.cpp b/src/lib/support/tests/TestSpan.cpp index bf192dd8e26ea1..303eb3f596637b 100644 --- a/src/lib/support/tests/TestSpan.cpp +++ b/src/lib/support/tests/TestSpan.cpp @@ -291,6 +291,14 @@ static void TestFromCharString(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, s1.data_equal(CharSpan(str, 3))); } +static void TestLiteral(nlTestSuite * inSuite, void * inContext) +{ + constexpr CharSpan literal = "HI!"_span; + NL_TEST_ASSERT(inSuite, literal.size() == 3); + NL_TEST_ASSERT(inSuite, literal.data_equal(CharSpan::fromCharString("HI!"))); + NL_TEST_ASSERT(inSuite, ""_span.size() == 0); +} + static void TestConversionConstructors(nlTestSuite * inSuite, void * inContext) { struct Foo @@ -330,6 +338,7 @@ static const nlTest sTests[] = { NL_TEST_DEF_FN(TestSubSpan), NL_TEST_DEF_FN(TestFromZclString), NL_TEST_DEF_FN(TestFromCharString), + NL_TEST_DEF_FN(TestLiteral), NL_TEST_DEF_FN(TestConversionConstructors), NL_TEST_SENTINEL(), }; diff --git a/src/lib/support/tests/TestStringSplitter.cpp b/src/lib/support/tests/TestStringSplitter.cpp index a8c41750211e1a..50b0ec094f0a0f 100644 --- a/src/lib/support/tests/TestStringSplitter.cpp +++ b/src/lib/support/tests/TestStringSplitter.cpp @@ -45,7 +45,7 @@ void TestStrdupSplitter(nlTestSuite * inSuite, void * inContext) StringSplitter splitter("single", ','); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("single"))); + NL_TEST_ASSERT(inSuite, out.data_equal("single"_span)); // next stays at nullptr also after valid data NL_TEST_ASSERT(inSuite, !splitter.Next(out)); @@ -59,11 +59,11 @@ void TestStrdupSplitter(nlTestSuite * inSuite, void * inContext) StringSplitter splitter("one,two,three", ','); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("one"))); + NL_TEST_ASSERT(inSuite, out.data_equal("one"_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("two"))); + NL_TEST_ASSERT(inSuite, out.data_equal("two"_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("three"))); + NL_TEST_ASSERT(inSuite, out.data_equal("three"_span)); NL_TEST_ASSERT(inSuite, !splitter.Next(out)); NL_TEST_ASSERT(inSuite, out.data() == nullptr); } @@ -73,15 +73,15 @@ void TestStrdupSplitter(nlTestSuite * inSuite, void * inContext) StringSplitter splitter("a**bc*d,e*f", '*'); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("a"))); + NL_TEST_ASSERT(inSuite, out.data_equal("a"_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("bc"))); + NL_TEST_ASSERT(inSuite, out.data_equal("bc"_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("d,e"))); + NL_TEST_ASSERT(inSuite, out.data_equal("d,e"_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("f"))); + NL_TEST_ASSERT(inSuite, out.data_equal("f"_span)); NL_TEST_ASSERT(inSuite, !splitter.Next(out)); } @@ -90,37 +90,37 @@ void TestStrdupSplitter(nlTestSuite * inSuite, void * inContext) StringSplitter splitter(",", ','); // Note that even though "" is nullptr right away, "," becomes two empty strings NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, !splitter.Next(out)); } { StringSplitter splitter("log,", ','); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("log"))); + NL_TEST_ASSERT(inSuite, out.data_equal("log"_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, !splitter.Next(out)); } { StringSplitter splitter(",log", ','); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString("log"))); + NL_TEST_ASSERT(inSuite, out.data_equal("log"_span)); NL_TEST_ASSERT(inSuite, !splitter.Next(out)); } { StringSplitter splitter(",,,", ','); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, splitter.Next(out)); - NL_TEST_ASSERT(inSuite, out.data_equal(CharSpan::fromCharString(""))); + NL_TEST_ASSERT(inSuite, out.data_equal(""_span)); NL_TEST_ASSERT(inSuite, !splitter.Next(out)); } } diff --git a/src/lib/support/tests/TestTlvToJson.cpp b/src/lib/support/tests/TestTlvToJson.cpp index 63dcf48c9fde16..be25ef1055d224 100644 --- a/src/lib/support/tests/TestTlvToJson.cpp +++ b/src/lib/support/tests/TestTlvToJson.cpp @@ -140,7 +140,7 @@ void TestConverter(nlTestSuite * inSuite, void * inContext) "}\n"; EncodeAndValidate(static_cast(1.0), jsonString); - CharSpan charSpan = CharSpan::fromCharString("hello"); + CharSpan charSpan = "hello"_span; jsonString = "{\n" " \"1:STRING\" : \"hello\"\n" "}\n";