From 242866b21a31fded3a091b66b47233f2de70cd98 Mon Sep 17 00:00:00 2001 From: Tony Parker Date: Wed, 19 Mar 2025 09:47:39 -0700 Subject: [PATCH 1/2] Add an upcall point to swift-corelibs-foundation for String encoding conversion --- .../String/StringProtocol+Essentials.swift | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift b/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift index 003fc486a..f607f9fab 100644 --- a/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift +++ b/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift @@ -91,6 +91,14 @@ extension UInt16 { // These provides concrete implementations for String and Substring, enhancing performance over generic StringProtocol. +#if !FOUNDATION_FRAMEWORK +@_spi(SwiftCorelibsFoundation) +dynamic public func _cfStringEncodingConvert(string: String, using encoding: UInt, allowLossyConversion: Bool) -> Data? { + // Dynamically replaced by swift-corelibs-foundation to implement encodings that we do not have Swift replacements for, yet + return nil +} +#endif + @available(FoundationPreview 0.4, *) extension String { public func data(using encoding: String.Encoding, allowLossyConversion: Bool = false) -> Data? { @@ -226,7 +234,7 @@ extension String { } return data + swapped - #if !FOUNDATION_FRAMEWORK +#if !FOUNDATION_FRAMEWORK case .isoLatin1: return try? Data(capacity: self.utf16.count) { buffer in for scalar in self.utf16 { @@ -245,13 +253,14 @@ extension String { buffer.appendElement(value) } } - #endif +#endif default: #if FOUNDATION_FRAMEWORK // Other encodings, defer to the CoreFoundation implementation return _ns.data(using: encoding.rawValue, allowLossyConversion: allowLossyConversion) #else - return nil + // Attempt an up-call into swift-corelibs-foundation, which can defer to the CoreFoundation implementation + return _cfStringEncodingConvert(string: self, using: encoding.rawValue, allowLossyConversion: allowLossyConversion) #endif } } From bb8c938db48e61c2c6a96be98f83c13751fdc2ce Mon Sep 17 00:00:00 2001 From: Tony Parker Date: Wed, 19 Mar 2025 10:17:19 -0700 Subject: [PATCH 2/2] Add upcall for conversion from bytes to String in non-swift-foundation encodings --- .../FoundationEssentials/String/String+IO.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Sources/FoundationEssentials/String/String+IO.swift b/Sources/FoundationEssentials/String/String+IO.swift index aee26c3d9..6c1c7f573 100644 --- a/Sources/FoundationEssentials/String/String+IO.swift +++ b/Sources/FoundationEssentials/String/String+IO.swift @@ -45,6 +45,13 @@ private struct ExtendingToUTF16Sequence> : Sequence { } } +#if !FOUNDATION_FRAMEWORK +@_spi(SwiftCorelibsFoundation) +dynamic public func _cfMakeStringFromBytes(_ bytes: UnsafeBufferPointer, encoding: UInt) -> String? { + // Provide swift-corelibs-foundation with an entry point to convert some bytes into a String + return nil +} +#endif @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension String { @@ -225,7 +232,12 @@ extension String { return nil } #else - return nil + if let string = (bytes.withContiguousStorageIfAvailable({ _cfMakeStringFromBytes($0, encoding: encoding.rawValue) }) ?? + Array(bytes).withUnsafeBufferPointer({ _cfMakeStringFromBytes($0, encoding: encoding.rawValue) })) { + self = string + } else { + return nil + } #endif } }