From 4794f3ce39dbdf7d1dee7c111269cb507b3d3ea9 Mon Sep 17 00:00:00 2001 From: Tristan Labelle Date: Tue, 5 Mar 2024 06:51:08 -0500 Subject: [PATCH] Changed HSTRING projection to use WindowsPreallocateStringBuffer to save an allocation and copy. --- .../WindowsRuntime/HSTRING+methods.swift | 23 +++++++++++++++---- .../{CWinRTCore.c => Functions.c} | 17 +++++++++++--- .../WindowsRuntime_ABI/include/CWinRTCore.h | 9 -------- .../WindowsRuntime_ABI/include/Functions.h | 11 +++++---- .../WindowsRuntime_ABI/include/WinRT.h | 4 +++- Support/Tests/HStringTests.swift | 22 ++++++++++++++++++ 6 files changed, 64 insertions(+), 22 deletions(-) rename Support/Sources/WindowsRuntime_ABI/{CWinRTCore.c => Functions.c} (75%) delete mode 100644 Support/Sources/WindowsRuntime_ABI/include/CWinRTCore.h create mode 100644 Support/Tests/HStringTests.swift diff --git a/Support/Sources/WindowsRuntime/HSTRING+methods.swift b/Support/Sources/WindowsRuntime/HSTRING+methods.swift index f711004d..cf6b52fe 100644 --- a/Support/Sources/WindowsRuntime/HSTRING+methods.swift +++ b/Support/Sources/WindowsRuntime/HSTRING+methods.swift @@ -3,12 +3,25 @@ import COM extension WindowsRuntime_ABI.SWRT_HString { public static func create(_ value: String) throws -> WindowsRuntime_ABI.SWRT_HString? { - let chars = Array(value.utf16) - return try chars.withUnsafeBufferPointer { - var result: WindowsRuntime_ABI.SWRT_HString? - try HResult.throwIfFailed(WindowsRuntime_ABI.SWRT_WindowsCreateString($0.baseAddress!, UInt32(chars.count), &result)) - return result + if value.isEmpty { return nil } + + let codeUnitCount = value.utf16.count + + // Preallocate and fill a UTF-16 HSTRING_BUFFER + var buffer: WindowsRuntime_ABI.SWRT_HStringBuffer? = nil + var pointer: UnsafeMutablePointer? = nil + try HResult.throwIfFailed(WindowsRuntime_ABI.SWRT_WindowsPreallocateStringBuffer(UInt32(codeUnitCount), &pointer, &buffer)) + guard let pointer else { throw HResult.Error.pointer } + _ = UnsafeMutableBufferPointer(start: pointer, count: codeUnitCount).initialize(from: value.utf16) + + var result: WindowsRuntime_ABI.SWRT_HString? + do { try HResult.throwIfFailed(WindowsRuntime_ABI.SWRT_WindowsPromoteStringBuffer(buffer, &result)) } + catch { + WindowsRuntime_ABI.SWRT_WindowsDeleteStringBuffer(buffer) + throw error } + + return result } public static func delete(_ value: WindowsRuntime_ABI.SWRT_HString?) { diff --git a/Support/Sources/WindowsRuntime_ABI/CWinRTCore.c b/Support/Sources/WindowsRuntime_ABI/Functions.c similarity index 75% rename from Support/Sources/WindowsRuntime_ABI/CWinRTCore.c rename to Support/Sources/WindowsRuntime_ABI/Functions.c index 58a4ebc2..1cc5be43 100644 --- a/Support/Sources/WindowsRuntime_ABI/CWinRTCore.c +++ b/Support/Sources/WindowsRuntime_ABI/Functions.c @@ -1,4 +1,3 @@ -#include "WinRT.h" #include "Functions.h" #include @@ -21,7 +20,7 @@ uint32_t SWRT_SysStringLen(SWRT_BStr pbstr) { } #include -SWRT_HResult SWRT_WindowsCreateString(const char16_t* sourceString, uint32_t length, SWRT_HString *string) { +SWRT_HResult SWRT_WindowsCreateString(const char16_t* sourceString, uint32_t length, SWRT_HString* string) { return (SWRT_HResult)WindowsCreateString((PCNZWCH)sourceString, (UINT32)length, (HSTRING*)string); } @@ -29,6 +28,10 @@ SWRT_HResult SWRT_WindowsDeleteString(SWRT_HString string) { return (SWRT_HResult)WindowsDeleteString((HSTRING)string); } +SWRT_HResult SWRT_WindowsDeleteStringBuffer(SWRT_HStringBuffer bufferHandle) { + return (SWRT_HResult)WindowsDeleteStringBuffer((HSTRING_BUFFER)bufferHandle); +} + SWRT_HResult SWRT_WindowsDuplicateString(SWRT_HString string, SWRT_HString *newString) { return (SWRT_HResult)WindowsDuplicateString((HSTRING)string, (HSTRING*)newString); } @@ -37,8 +40,16 @@ const char16_t* SWRT_WindowsGetStringRawBuffer(SWRT_HString string, uint32_t *le return (const char16_t*)WindowsGetStringRawBuffer((HSTRING)string, (UINT32*)length); } +SWRT_HResult SWRT_WindowsPreallocateStringBuffer(uint32_t length, char16_t** charBuffer, SWRT_HStringBuffer* bufferHandle) { + return (SWRT_HResult)WindowsPreallocateStringBuffer((UINT32)length, (PWSTR*)charBuffer, (HSTRING_BUFFER*)bufferHandle); +} + +SWRT_HResult SWRT_WindowsPromoteStringBuffer(SWRT_HStringBuffer bufferHandle, SWRT_HString* string) { + return (SWRT_HResult)WindowsPromoteStringBuffer((HSTRING_BUFFER)bufferHandle, (HSTRING*)string); +} + #include -SWRT_HResult SWRT_RoGetActivationFactory(SWRT_HString activatableClassId, SWRT_Guid* iid, void **factory) { +SWRT_HResult SWRT_RoGetActivationFactory(SWRT_HString activatableClassId, SWRT_Guid* iid, void** factory) { return (SWRT_HResult)RoGetActivationFactory((HSTRING)activatableClassId, (IID*)iid, factory); } diff --git a/Support/Sources/WindowsRuntime_ABI/include/CWinRTCore.h b/Support/Sources/WindowsRuntime_ABI/include/CWinRTCore.h deleted file mode 100644 index 60811a58..00000000 --- a/Support/Sources/WindowsRuntime_ABI/include/CWinRTCore.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "COM.h" -#include "WinRT.h" -#include "IBufferByteAccess.h" - -#include "SwiftCOMObject.h" - -#include "Functions.h" \ No newline at end of file diff --git a/Support/Sources/WindowsRuntime_ABI/include/Functions.h b/Support/Sources/WindowsRuntime_ABI/include/Functions.h index e5b0365c..b711141a 100644 --- a/Support/Sources/WindowsRuntime_ABI/include/Functions.h +++ b/Support/Sources/WindowsRuntime_ABI/include/Functions.h @@ -9,10 +9,13 @@ void SWRT_SysFreeString(SWRT_BStr bstrString); uint32_t SWRT_SysStringLen(SWRT_BStr pbstr); // winstring.h -SWRT_HResult SWRT_WindowsCreateString(const char16_t* sourceString, uint32_t length, SWRT_HString *string); +SWRT_HResult SWRT_WindowsCreateString(const char16_t* sourceString, uint32_t length, SWRT_HString* string); SWRT_HResult SWRT_WindowsDeleteString(SWRT_HString string); -SWRT_HResult SWRT_WindowsDuplicateString(SWRT_HString string, SWRT_HString *newString); -const char16_t* SWRT_WindowsGetStringRawBuffer(SWRT_HString string, uint32_t *length); +SWRT_HResult SWRT_WindowsDeleteStringBuffer(SWRT_HStringBuffer bufferHandle); +SWRT_HResult SWRT_WindowsDuplicateString(SWRT_HString string, SWRT_HString* newString); +const char16_t* SWRT_WindowsGetStringRawBuffer(SWRT_HString string, uint32_t* length); +SWRT_HResult SWRT_WindowsPreallocateStringBuffer(uint32_t length, char16_t** charBuffer, SWRT_HStringBuffer* bufferHandle); +SWRT_HResult SWRT_WindowsPromoteStringBuffer(SWRT_HStringBuffer bufferHandle, SWRT_HString* string); // roapi.h typedef enum SWRT_RO_INIT_TYPE { @@ -20,7 +23,7 @@ typedef enum SWRT_RO_INIT_TYPE { SWRT_RO_INIT_MULTITHREADED = 1 } SWRT_RO_INIT_TYPE; -SWRT_HResult SWRT_RoGetActivationFactory(SWRT_HString activatableClassId, SWRT_Guid* iid, void **factory); +SWRT_HResult SWRT_RoGetActivationFactory(SWRT_HString activatableClassId, SWRT_Guid* iid, void** factory); SWRT_HResult SWRT_RoInitialize(SWRT_RO_INIT_TYPE initType); void SWRT_RoUninitialize(); diff --git a/Support/Sources/WindowsRuntime_ABI/include/WinRT.h b/Support/Sources/WindowsRuntime_ABI/include/WinRT.h index 3d22adce..96d65095 100644 --- a/Support/Sources/WindowsRuntime_ABI/include/WinRT.h +++ b/Support/Sources/WindowsRuntime_ABI/include/WinRT.h @@ -4,9 +4,11 @@ // HSTRING struct SWRT_HString_ {}; - typedef struct SWRT_HString_* SWRT_HString; +struct SWRT_HStringBuffer_ {}; +typedef struct SWRT_HStringBuffer_* SWRT_HStringBuffer; + // TrustLevel typedef int32_t SWRT_TrustLevel; diff --git a/Support/Tests/HStringTests.swift b/Support/Tests/HStringTests.swift new file mode 100644 index 00000000..22475aa6 --- /dev/null +++ b/Support/Tests/HStringTests.swift @@ -0,0 +1,22 @@ +import XCTest +import WindowsRuntime + +internal final class HStringTests: XCTestCase { + func testEmptyString() throws { + XCTAssertNil(try HStringProjection.toABI("")) + XCTAssertEqual(HStringProjection.toSwift(nil), "") + } + + func testRoundTrip() throws { + func assertRoundTrip(_ str: String) throws{ + var abi = try HStringProjection.toABI(str) + let roundtripped = HStringProjection.toSwift(consuming: &abi) + XCTAssertEqual(str, roundtripped) + } + + try assertRoundTrip("") + try assertRoundTrip("a") + try assertRoundTrip("à") + try assertRoundTrip("☃") + } +} \ No newline at end of file