Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a _span string literal operator for creating constexpr CharSpans #30042

Merged
merged 3 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/clusters/door-lock-server/door-lock-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1827,7 +1827,7 @@ EmberAfStatus DoorLockServer::createUser(chip::EndpointId endpointId, chip::Fabr
return static_cast<EmberAfStatus>(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();
Expand Down
2 changes: 1 addition & 1 deletion src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
30 changes: 15 additions & 15 deletions src/credentials/tests/TestFabricTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/crypto/tests/CHIPCryptoPALTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
36 changes: 36 additions & 0 deletions src/lib/core/Unchecked.h
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions src/lib/core/tests/TestOTAImageHeader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
29 changes: 27 additions & 2 deletions src/lib/support/Span.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
#pragma once

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <string.h>
#include <cstring>
#include <type_traits>

#include <lib/core/Unchecked.h>
#include <lib/support/CodeUtils.h>

namespace chip {
Expand Down Expand Up @@ -149,7 +151,10 @@ class Span
return Span(reinterpret_cast<T *>(&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 <class U, typename = std::enable_if_t<std::is_same<T, const U>::value && std::is_same<const char, T>::value>>
static Span fromCharString(U * chars)
{
Expand All @@ -162,11 +167,31 @@ class Span
template <typename U>
bool operator==(const Span<U> & other) const = delete;

// Creates a Span without checking whether databuf is a null pointer.
ksperling-apple marked this conversation as resolved.
Show resolved Hide resolved
//
// 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<const char> operator"" _span(const char * literal, size_t size)
{
return Span<const char>(Unchecked, literal, size);
}

} // namespace literals

namespace detail {

// To make FixedSpan (specifically various FixedByteSpan types) default constructible
Expand Down
4 changes: 2 additions & 2 deletions src/lib/support/tests/TestJsonToTlv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -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<float>(1.0);
structVal.h = static_cast<double>(1.0);

Expand Down
9 changes: 9 additions & 0 deletions src/lib/support/tests/TestSpan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
};
Expand Down
38 changes: 19 additions & 19 deletions src/lib/support/tests/TestStringSplitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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);
}
Expand All @@ -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));
}

Expand All @@ -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));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/support/tests/TestTlvToJson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ void TestConverter(nlTestSuite * inSuite, void * inContext)
"}\n";
EncodeAndValidate(static_cast<double>(1.0), jsonString);

CharSpan charSpan = CharSpan::fromCharString("hello");
CharSpan charSpan = "hello"_span;
jsonString = "{\n"
" \"1:STRING\" : \"hello\"\n"
"}\n";
Expand Down
Loading