diff --git a/Sources/FoundationEssentials/String/String+IO.swift b/Sources/FoundationEssentials/String/String+IO.swift index 4dd1be2bf..559e84cd7 100644 --- a/Sources/FoundationEssentials/String/String+IO.swift +++ b/Sources/FoundationEssentials/String/String+IO.swift @@ -18,6 +18,13 @@ internal import _FoundationCShims fileprivate let stringEncodingAttributeName = "com.apple.TextEncoding" +#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 { @@ -195,7 +202,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 } } diff --git a/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift b/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift index 4da60c1a6..4c99c1599 100644 --- a/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift +++ b/Sources/FoundationEssentials/String/StringProtocol+Essentials.swift @@ -85,6 +85,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? { @@ -220,7 +228,7 @@ extension String { } return data + swapped - #if !FOUNDATION_FRAMEWORK +#if !FOUNDATION_FRAMEWORK case .isoLatin1: // ISO Latin 1 encodes code points 0x0 through 0xFF (a maximum of 2 UTF-8 scalars per ISO Latin 1 Scalar) // The UTF-8 count is a cheap, reasonable starting capacity as it is precise for the all-ASCII case and it will only over estimate by 1 byte per non-ASCII character @@ -241,13 +249,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 } }