From c0670e8c4bbd857ba1e0c1054e5ea28b7e8789e8 Mon Sep 17 00:00:00 2001 From: Jon Wiswall Date: Sun, 25 May 2025 16:56:44 -0700 Subject: [PATCH 1/2] Enable combine/append of string view things String views --- CMakePresets.json | 29 +++++++++++----------- include/wil/resource.h | 51 +++++++++++++++++++++++++++++++++------ include/wil/stl.h | 23 ++++++++++++++++++ scripts/call-vcvars.cmd | 20 +++++++++++---- tests/FileSystemTests.cpp | 22 +++++++++++++++++ 5 files changed, 118 insertions(+), 27 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 61f23932..836f5905 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,6 +14,7 @@ "installDir": "${sourceDir}/build/install/${presetName}", "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "cacheVariables": { + "WIL_ENABLE_ASAN": true, "CMAKE_CONFIGURATION_TYPES": "Debug;RelWithDebInfo;Release;MinSizeRel", "CMAKE_CXX_COMPILER": "cl", "CMAKE_C_COMPILER": "cl" @@ -29,9 +30,19 @@ } }, "cacheVariables": { + "WIL_ENABLE_ASAN": false, "CMAKE_CXX_COMPILER": "clang-cl", "CMAKE_C_COMPILER": "clang-cl" } + }, + { + "name": "clang-release", + "inherits": "clang", + "hidden": false, + "cacheVariables": { + "WIL_ENABLE_ASAN": true, + "WIL_ENABLE_UBSAN": true + } } ], "buildPresets": [ @@ -39,19 +50,13 @@ "name": "msvc-debug", "displayName": "MSVC Debug", "configurePreset": "msvc", - "configuration": "Debug", - "cacheVariables": { - "WIL_ENABLE_ASAN": true - } + "configuration": "Debug" }, { "name": "msvc-release", "displayName": "MSVC Release (debuggable)", "configurePreset": "msvc", - "configuration": "RelWithDebInfo", - "cacheVariables": { - "WIL_ENABLE_ASAN": true - } + "configuration": "RelWithDebInfo" }, { "name": "clang-debug", @@ -62,12 +67,8 @@ { "name": "clang-release", "displayName": "clang Release (debuggable)", - "configurePreset": "clang", - "configuration": "RelWithDebInfo", - "cacheVariables": { - "WIL_ENABLE_ASAN": true, - "WIL_ENABLE_UBSAN": true - } + "configurePreset": "clang-release", + "configuration": "RelWithDebInfo" } ], "testPresets": [ diff --git a/include/wil/resource.h b/include/wil/resource.h index eb70fc30..140fed41 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -1836,6 +1836,28 @@ PCWSTR str_raw_ptr(const unique_any_t& val) return str_raw_ptr(val.get()); } +struct string_view_t +{ + wchar_t const* data; + size_t length; +}; + +inline string_view_t view_from_string(string_view_t const& s) +{ + return s; +} + +inline string_view_t view_from_string(PCWSTR str) +{ + return string_view_t{str, str ? wcslen(str) : 0}; +} + +template +inline string_view_t view_from_string(wil::unique_any_t const& str) +{ + return view_from_string(str.get()); +} + #if !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) /// @cond namespace details @@ -1847,12 +1869,12 @@ namespace details // Concatenate any number of strings together and store it in an automatically allocated string. If a string is present // in the input buffer, it is overwritten. template - HRESULT str_build_nothrow(string_type& result, _In_reads_(strCount) PCWSTR* strList, size_t strCount) + HRESULT str_build_nothrow_(string_type& result, _In_reads_(strCount) string_view_t const* strList, size_t strCount) { size_t lengthRequiredWithoutNull{}; for (auto& string : make_range(strList, strCount)) { - lengthRequiredWithoutNull += string ? wcslen(string) : 0; + lengthRequiredWithoutNull += string.length; } details::string_maker maker; @@ -1862,9 +1884,10 @@ namespace details auto bufferEnd = buffer + lengthRequiredWithoutNull + 1; for (auto& string : make_range(strList, strCount)) { - if (string) + if (string.data) { - RETURN_IF_FAILED(StringCchCopyExW(buffer, (bufferEnd - buffer), string, &buffer, nullptr, STRSAFE_IGNORE_NULLS)); + RETURN_IF_FAILED(StringCchCopyNExW( + buffer, (bufferEnd - buffer), string.data, string.length, &buffer, nullptr, STRSAFE_IGNORE_NULLS)); } } @@ -1872,12 +1895,12 @@ namespace details return S_OK; } - // NOTE: 'Strings' must all be PCWSTR, or convertible to PCWSTR, but C++ doesn't allow us to express that cleanly + // NOTE: 'Strings' must all be string_view_t, or convertible to string_view_t, but C++ doesn't allow us to express that cleanly template HRESULT str_build_nothrow(string_type& result, Strings... strings) { - PCWSTR localStrings[] = {strings...}; - return str_build_nothrow(result, localStrings, sizeof...(Strings)); + string_view_t localStrings[] = {strings...}; + return str_build_nothrow_(result, localStrings, ARRAYSIZE(localStrings)); } } // namespace details /// @endcond @@ -1888,7 +1911,7 @@ template HRESULT str_concat_nothrow(string_type& buffer, const strings&... str) { static_assert(sizeof...(str) > 0, "attempting to concatenate no strings"); - return details::str_build_nothrow(buffer, details::string_maker::get(buffer), str_raw_ptr(str)...); + return details::str_build_nothrow(buffer, view_from_string(details::string_maker::get(buffer)), view_from_string(str)...); } #endif // !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) @@ -4929,6 +4952,18 @@ inline PCWSTR str_raw_ptr(const unique_hstring& str) return str_raw_ptr(str.get()); } +inline string_view_t view_from_string(HSTRING str) +{ + UINT32 length = 0; + PCWSTR rawBuffer = WindowsGetStringRawBuffer(str, &length); + return string_view_t{rawBuffer, length}; +} + +inline string_view_t view_from_string(const unique_hstring& str) +{ + return view_from_string(str.get()); +} + #endif // __WIL__WINSTRING_H_ #if (defined(__WIL__WINSTRING_H_) && !defined(__WIL__WINSTRING_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) /// @cond diff --git a/include/wil/stl.h b/include/wil/stl.h index 9e3dd437..baa0d883 100644 --- a/include/wil/stl.h +++ b/include/wil/stl.h @@ -132,6 +132,16 @@ inline PCWSTR str_raw_ptr(const std::wstring& str) return str.c_str(); } +inline string_view_t view_from_string(std::wstring_view const& s) +{ + return string_view_t{s.data(), s.length()}; +} + +inline string_view_t view_from_string(std::wstring const& s) +{ + return string_view_t{s.data(), s.length()}; +} + #if __cpp_lib_string_view >= 201606L /** zstring_view. A zstring_view is identical to a std::string_view except it is always nul-terminated (unless empty). @@ -241,6 +251,19 @@ class basic_zstring_view : public std::basic_string_view using zstring_view = basic_zstring_view; using zwstring_view = basic_zstring_view; +// str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. +// This is the overload for std::wstring. Other overloads available in resource.h. +template +inline auto str_raw_ptr(basic_zstring_view str) +{ + return str.c_str(); +} + +inline string_view_t view_from_string(zwstring_view const& s) +{ + return string_view_t{s.data(), s.length()}; +} + inline namespace literals { constexpr zstring_view operator""_zv(const char* str, std::size_t len) noexcept diff --git a/scripts/call-vcvars.cmd b/scripts/call-vcvars.cmd index 924276b3..75def32f 100644 --- a/scripts/call-vcvars.cmd +++ b/scripts/call-vcvars.cmd @@ -2,10 +2,20 @@ :: NOTE: Intentionally not specifying 'setlocal' as we want the side-effects of calling 'vcvars' to affect the caller -:: NOTE: This is primarily intended to be used by the build pipelines, hence the hard-coded paths, and might not be -:: generally useful. The following check is intended to help diagnose such possible issues -if NOT EXIST "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" ( - echo ERROR: Could not locate 'vcvars' batch file. This script is intended to be run from a build machine & exit /B 1 +:: Use vswhere.exe to find the Visual Studio installation path +for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do ( + set VS_INSTALL_PATH=%%i +) + +if "%VS_INSTALL_PATH%"=="" ( + echo ERROR: Could not locate Visual Studio installation with required C++ tools using vswhere.exe + exit /B 1 +) + +set VCVARS_PATH=%VS_INSTALL_PATH%\VC\Auxiliary\Build\vcvarsall.bat +if NOT EXIST "%VCVARS_PATH%" ( + echo ERROR: Could not locate vcvarsall.bat at %VCVARS_PATH% + exit /B 1 ) set ARCH=%1 @@ -14,4 +24,4 @@ if /I "%ARCH%"=="x86" ( set ARCH=x64_x86 ) -call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %ARCH% +call "%VCVARS_PATH%" %ARCH% diff --git a/tests/FileSystemTests.cpp b/tests/FileSystemTests.cpp index 8c1fe901..922b8608 100644 --- a/tests/FileSystemTests.cpp +++ b/tests/FileSystemTests.cpp @@ -12,12 +12,21 @@ // TODO: str_raw_ptr is not two-phase name lookup clean (https://github.com/Microsoft/wil/issues/8) namespace wil { +struct string_view_t; + PCWSTR str_raw_ptr(HSTRING); +string_view_t view_from_string(HSTRING); #ifdef WIL_ENABLE_EXCEPTIONS PCWSTR str_raw_ptr(const std::wstring&); +string_view_t view_from_string(std::wstring const&); +string_view_t view_from_string(std::wstring_view const&); #endif } // namespace wil +#ifdef WIL_ENABLE_EXCEPTIONS +#include +#endif + #include #include @@ -528,6 +537,19 @@ TEST_CASE("FileSystemTests::VerifyStrConcat", "[filesystem]") REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test2.c_str(), test3)); REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL); } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Concat with views of things") + { + auto part1 = std::wstring_view(L"Test2"); + auto part2 = std::wstring(L"Test3"); + auto part3 = wil::zwstring_view(L"Test4"); + auto part4 = wil::make_unique_string_nothrow(L"Test5"); + wil::unique_cotaskmem_string combinedStringNT = wil::make_unique_string_nothrow(L"Test1"); + REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, part1, part2, part3, part4)); + REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, L"Test1Test2Test3Test4Test5", -1, TRUE) == CSTR_EQUAL); + } +#endif } TEST_CASE("FileSystemTests::VerifyStrPrintf", "[filesystem]") From 85a381ac50300609cd14f0a51d918eb2afee7d2f Mon Sep 17 00:00:00 2001 From: Jon Wiswall Date: Mon, 29 Dec 2025 16:49:53 -0800 Subject: [PATCH 2/2] Add a case --- tests/FileSystemTests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/FileSystemTests.cpp b/tests/FileSystemTests.cpp index 922b8608..e6d00b27 100644 --- a/tests/FileSystemTests.cpp +++ b/tests/FileSystemTests.cpp @@ -545,9 +545,10 @@ TEST_CASE("FileSystemTests::VerifyStrConcat", "[filesystem]") auto part2 = std::wstring(L"Test3"); auto part3 = wil::zwstring_view(L"Test4"); auto part4 = wil::make_unique_string_nothrow(L"Test5"); + auto part5 = wil::make_unique_string_nothrow(L"Test6"); wil::unique_cotaskmem_string combinedStringNT = wil::make_unique_string_nothrow(L"Test1"); - REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, part1, part2, part3, part4)); - REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, L"Test1Test2Test3Test4Test5", -1, TRUE) == CSTR_EQUAL); + REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, part1, part2, part3, part4, part5)); + REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, L"Test1Test2Test3Test4Test5Test6", -1, TRUE) == CSTR_EQUAL); } #endif }