//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // String interop with C //===----------------------------------------------------------------------===// import SwiftShims extension String { /// Creates a new string by copying the null-terminated UTF-8 data referenced /// by the given pointer. /// /// If `cString` contains ill-formed UTF-8 code unit sequences, this /// initializer replaces them with the Unicode replacement character /// (`"\u{FFFD}"`). /// /// The following example calls this initializer with pointers to the /// contents of two different `CChar` arrays---the first with well-formed /// UTF-8 code unit sequences and the second with an ill-formed sequence at /// the end. /// /// let validUTF8: [CChar] = [67, 97, 102, -61, -87, 0] /// validUTF8.withUnsafeBufferPointer { ptr in /// let s = String(cString: ptr.baseAddress!) /// print(s) /// } /// // Prints "Café" /// /// let invalidUTF8: [CChar] = [67, 97, 102, -61, 0] /// invalidUTF8.withUnsafeBufferPointer { ptr in /// let s = String(cString: ptr.baseAddress!) /// print(s) /// } /// // Prints "Caf�" /// /// - Parameter nullTerminatedUTF8: /// A pointer to a null-terminated sequence of UTF-8 code units. public init(cString nullTerminatedUTF8: UnsafePointer) { let len = unsafe UTF8._nullCodeUnitOffset(in: nullTerminatedUTF8) let buffer = unsafe UnsafeBufferPointer(start: nullTerminatedUTF8, count: len) self = unsafe buffer.withMemoryRebound(to: UInt8.self) { unsafe String._fromUTF8Repairing($0).0 } } /// Creates a new string by copying the null-terminated UTF-8 data referenced /// by the given array. /// /// If `cString` contains ill-formed UTF-8 code unit sequences, this /// initializer replaces them with the Unicode replacement character /// (`"\u{FFFD}"`). /// /// - Note: This initializer is deprecated. Use the initializer /// `String.init(decoding: array, as: UTF8.self)` instead, /// remembering that "\0" is a valid character in Swift. /// /// - Parameter nullTerminatedUTF8: /// An array containing a null-terminated sequence of UTF-8 code units. @inlinable @_alwaysEmitIntoClient @available(swift, deprecated: 6, message: "Use String(decoding: array, as: UTF8.self) instead, after truncating the null termination." ) public init(cString nullTerminatedUTF8: [CChar]) { self = unsafe nullTerminatedUTF8.withUnsafeBufferPointer { unsafe $0.withMemoryRebound(to: UInt8.self, String.init(_checkingCString:)) } } @_alwaysEmitIntoClient internal init(_checkingCString bytes: UnsafeBufferPointer) { guard let length = unsafe bytes.firstIndex(of: 0) else { _preconditionFailure( "input of String.init(cString:) must be null-terminated" ) } self = unsafe String._fromUTF8Repairing( UnsafeBufferPointer( start: bytes.baseAddress._unsafelyUnwrappedUnchecked, count: length ) ).0 } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init(cString nullTerminatedUTF8: inout CChar) { guard nullTerminatedUTF8 == 0 else { _preconditionFailure( "input of String.init(cString:) must be null-terminated" ) } self = "" } /// Creates a new string by copying the null-terminated UTF-8 data referenced /// by the given pointer. /// /// This is identical to `init(cString: UnsafePointer)` but operates on /// an unsigned sequence of bytes. /// /// - Parameter nullTerminatedUTF8: /// A pointer to a null-terminated sequence of UTF-8 code units. public init(cString nullTerminatedUTF8: UnsafePointer) { let len = unsafe UTF8._nullCodeUnitOffset(in: nullTerminatedUTF8) self = unsafe String._fromUTF8Repairing( UnsafeBufferPointer(start: nullTerminatedUTF8, count: len)).0 } /// Creates a new string by copying the null-terminated UTF-8 data referenced /// by the given array. /// /// This is identical to `init(cString: [CChar])` but operates on /// an unsigned sequence of bytes. /// /// - Note: This initializer is deprecated. Use the initializer /// `String.init(decoding: array, as: UTF8.self)` instead, /// remembering that "\0" is a valid character in Swift. /// /// - Parameter nullTerminatedUTF8: /// An array containing a null-terminated UTF-8 code unit sequence. @inlinable @_alwaysEmitIntoClient @available(swift, deprecated: 6, message: "Use String(decoding: array, as: UTF8.self) instead, after truncating the null termination." ) public init(cString nullTerminatedUTF8: [UInt8]) { self = unsafe nullTerminatedUTF8.withUnsafeBufferPointer { unsafe String(_checkingCString: $0) } } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use a copy of the String argument") public init(cString nullTerminatedUTF8: String) { self = unsafe nullTerminatedUTF8.withCString(String.init(cString:)) } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init(cString nullTerminatedUTF8: inout UInt8) { guard nullTerminatedUTF8 == 0 else { _preconditionFailure( "input of String.init(cString:) must be null-terminated" ) } self = "" } /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given pointer. /// /// This initializer does not try to repair ill-formed UTF-8 code unit /// sequences. If any are found, the result of the initializer is `nil`. /// /// The following example calls this initializer with pointers to the /// contents of two different `CChar` arrays---the first with well-formed /// UTF-8 code unit sequences and the second with an ill-formed sequence at /// the end. /// /// let validUTF8: [CChar] = [67, 97, 102, -61, -87, 0] /// validUTF8.withUnsafeBufferPointer { ptr in /// let s = String(validatingCString: ptr.baseAddress!) /// print(s) /// } /// // Prints "Optional("Café")" /// /// let invalidUTF8: [CChar] = [67, 97, 102, -61, 0] /// invalidUTF8.withUnsafeBufferPointer { ptr in /// let s = String(validatingCString: ptr.baseAddress!) /// print(s) /// } /// // Prints "nil" /// /// - Parameter nullTerminatedUTF8: /// A pointer to a null-terminated sequence of UTF-8 code units. @_silgen_name("$sSS14validatingUTF8SSSgSPys4Int8VG_tcfC") public init?(validatingCString nullTerminatedUTF8: UnsafePointer) { let len = unsafe UTF8._nullCodeUnitOffset(in: nullTerminatedUTF8) let validated = unsafe nullTerminatedUTF8.withMemoryRebound( to: UInt8.self, capacity: len, { unsafe String._tryFromUTF8(UnsafeBufferPointer(start: $0, count: len)) } ) guard let validated else { return nil } self = validated } /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given pointer. /// /// This initializer does not try to repair ill-formed UTF-8 code unit /// sequences. If any are found, the result of the initializer is `nil`. /// /// The following example calls this initializer with pointers to the /// contents of two different `CChar` arrays---the first with well-formed /// UTF-8 code unit sequences and the second with an ill-formed sequence at /// the end. /// /// let validUTF8: [CChar] = [67, 97, 102, -61, -87, 0] /// validUTF8.withUnsafeBufferPointer { ptr in /// let s = String(validatingUTF8: ptr.baseAddress!) /// print(s) /// } /// // Prints "Optional("Café")" /// /// let invalidUTF8: [CChar] = [67, 97, 102, -61, 0] /// invalidUTF8.withUnsafeBufferPointer { ptr in /// let s = String(validatingUTF8: ptr.baseAddress!) /// print(s) /// } /// // Prints "nil" /// /// - Note: This initializer has been renamed. Use /// `String.init?(validatingCString:)` instead. /// /// - Parameter cString: /// A pointer to a null-terminated sequence of UTF-8 code units. @inlinable @_alwaysEmitIntoClient @available(swift, deprecated: 6, renamed: "String.init(validatingCString:)") @_silgen_name("_swift_se0405_String_validatingUTF8") public init?(validatingUTF8 cString: UnsafePointer) { unsafe self.init(validatingCString: cString) } /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given array. /// /// This initializer does not try to repair ill-formed UTF-8 code unit /// sequences. If any are found, the result of the initializer is `nil`. /// /// - Note: This initializer is deprecated. Use the initializer /// `String.init?(validating: array, as: UTF8.self)` instead, /// remembering that "\0" is a valid character in Swift. /// /// - Parameter nullTerminatedUTF8: /// An array containing a null-terminated sequence of UTF-8 code units. @inlinable @_alwaysEmitIntoClient @available(swift, deprecated: 6, message: "Use String(validating: array, as: UTF8.self) instead, after truncating the null termination." ) public init?(validatingCString nullTerminatedUTF8: [CChar]) { guard let length = nullTerminatedUTF8.firstIndex(of: 0) else { _preconditionFailure( "input of String.init(validatingCString:) must be null-terminated" ) } let string = unsafe nullTerminatedUTF8.prefix(length).withUnsafeBufferPointer { unsafe $0.withMemoryRebound(to: UInt8.self, String._tryFromUTF8(_:)) } guard let string else { return nil } self = string } /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given array. /// /// This initializer does not try to repair ill-formed UTF-8 code unit /// sequences. If any are found, the result of the initializer is `nil`. /// /// - Note: This initializer is deprecated. Use the initializer /// `String.init?(validating: array, as: UTF8.self)` instead, /// remembering that "\0" is a valid character in Swift. /// /// - Parameter cString: /// An array containing a null-terminated sequence of UTF-8 code units. @inlinable @_alwaysEmitIntoClient @available(swift, deprecated: 6, message: "Use String(validating: array, as: UTF8.self) instead, after truncating the null termination." ) public init?(validatingUTF8 cString: [CChar]) { self.init(validatingCString: cString) } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use a copy of the String argument") public init?(validatingCString nullTerminatedUTF8: String) { self = unsafe nullTerminatedUTF8.withCString(String.init(cString:)) } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use a copy of the String argument") public init?(validatingUTF8 cString: String) { self.init(validatingCString: cString) } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init?(validatingCString nullTerminatedUTF8: inout CChar) { guard nullTerminatedUTF8 == 0 else { _preconditionFailure( "input of String.init(validatingCString:) must be null-terminated" ) } self = "" } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init?(validatingUTF8 cString: inout CChar) { self.init(validatingCString: &cString) } /// Creates a new string by copying the null-terminated data referenced by /// the given pointer using the specified encoding. /// /// When you pass `true` as `isRepairing`, this method replaces ill-formed /// sequences with the Unicode replacement character (`"\u{FFFD}"`); /// otherwise, an ill-formed sequence causes this method to stop decoding /// and return `nil`. /// /// The following example calls this method with pointers to the contents of /// two different `CChar` arrays---the first with well-formed UTF-8 code /// unit sequences and the second with an ill-formed sequence at the end. /// /// let validUTF8: [UInt8] = [67, 97, 102, 195, 169, 0] /// validUTF8.withUnsafeBufferPointer { ptr in /// let s = String.decodeCString(ptr.baseAddress, /// as: UTF8.self, /// repairingInvalidCodeUnits: true) /// print(s) /// } /// // Prints "Optional((result: "Café", repairsMade: false))" /// /// let invalidUTF8: [UInt8] = [67, 97, 102, 195, 0] /// invalidUTF8.withUnsafeBufferPointer { ptr in /// let s = String.decodeCString(ptr.baseAddress, /// as: UTF8.self, /// repairingInvalidCodeUnits: true) /// print(s) /// } /// // Prints "Optional((result: "Caf�", repairsMade: true))" /// /// - Parameters: /// - cString: A pointer to a null-terminated sequence of /// code units encoded in `encoding`. /// - encoding: The Unicode encoding of the data referenced by `cString`. /// - isRepairing: Pass `true` to create a new string, even when the data /// referenced by `cString` contains ill-formed sequences. Ill-formed /// sequences are replaced with the Unicode replacement character /// (`"\u{FFFD}"`). Pass `false` to interrupt the creation of the new /// string if an ill-formed sequence is detected. /// - Returns: A tuple with the new string and a Boolean value that indicates /// whether any repairs were made. If `isRepairing` is `false` and an /// ill-formed sequence is detected, this method returns `nil`. @_specialize(where Encoding == Unicode.UTF8) @_specialize(where Encoding == Unicode.UTF16) @inlinable // Fold away specializations public static func decodeCString( _ cString: UnsafePointer?, as encoding: Encoding.Type, repairingInvalidCodeUnits isRepairing: Bool = true ) -> (result: String, repairsMade: Bool)? { guard let cPtr = unsafe cString else { return nil } if _fastPath(encoding == Unicode.UTF8.self) { let len = unsafe UTF8._nullCodeUnitOffset( in: UnsafeRawPointer(cPtr).assumingMemoryBound(to: UInt8.self) ) let bytes = unsafe UnsafeBufferPointer(start: cPtr, count: len) return unsafe bytes.withMemoryRebound(to: UInt8.self) { codeUnits in if isRepairing { return unsafe String._fromUTF8Repairing(codeUnits) } else if let str = unsafe String._tryFromUTF8(codeUnits) { return (str, false) } return nil } } var end = unsafe cPtr while unsafe end.pointee != 0 { unsafe end += 1 } let len = unsafe end - cPtr let codeUnits = unsafe UnsafeBufferPointer(start: cPtr, count: len) return unsafe String._fromCodeUnits( codeUnits, encoding: encoding, repair: isRepairing) } @inlinable @_alwaysEmitIntoClient public static func decodeCString( _ cString: [Encoding.CodeUnit], as encoding: Encoding.Type, repairingInvalidCodeUnits isRepairing: Bool = true ) -> (result: String, repairsMade: Bool)? { guard let length = cString.firstIndex(of: 0) else { _preconditionFailure( "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) } if _fastPath(encoding == Unicode.UTF8.self) { return unsafe cString.prefix(length).withUnsafeBufferPointer { buffer -> (result: String, repairsMade: Bool)? in return unsafe buffer.withMemoryRebound(to: UInt8.self) { codeUnits in if isRepairing { return unsafe String._fromUTF8Repairing(codeUnits) } else if let str = unsafe String._tryFromUTF8(codeUnits) { return (str, false) } return nil } } } return unsafe cString.prefix(length).withUnsafeBufferPointer { buf -> (result: String, repairsMade: Bool)? in unsafe String._fromCodeUnits(buf, encoding: encoding, repair: isRepairing) } } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use a copy of the String argument") public static func decodeCString( _ cString: String, as encoding: Encoding.Type, repairingInvalidCodeUnits isRepairing: Bool = true ) -> (result: String, repairsMade: Bool)? { return unsafe cString.withCString(encodedAs: encoding) { unsafe String.decodeCString( $0, as: encoding, repairingInvalidCodeUnits: isRepairing ) } } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public static func decodeCString( _ cString: inout Encoding.CodeUnit, as encoding: Encoding.Type, repairingInvalidCodeUnits isRepairing: Bool = true ) -> (result: String, repairsMade: Bool)? { guard cString == 0 else { _preconditionFailure( "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) } return ("", false) } /// Creates a new string by copying the null-terminated sequence of code units /// referenced by the given pointer. /// /// If `nullTerminatedCodeUnits` contains ill-formed code unit sequences, this /// initializer replaces them with the Unicode replacement character /// (`"\u{FFFD}"`). /// /// - Parameters: /// - nullTerminatedCodeUnits: A pointer to a null-terminated sequence of /// code units encoded in `encoding`. /// - encoding: The encoding in which the code units should be /// interpreted. @_specialize(where Encoding == Unicode.UTF8) @_specialize(where Encoding == Unicode.UTF16) @inlinable // Fold away specializations public init( decodingCString nullTerminatedCodeUnits: UnsafePointer, as encoding: Encoding.Type ) { self = unsafe String.decodeCString(nullTerminatedCodeUnits, as: encoding)!.0 } /// Creates a new string by copying the null-terminated sequence of code units /// referenced by the given array. /// /// If `nullTerminatedCodeUnits` contains ill-formed code unit sequences, this /// initializer replaces them with the Unicode replacement character /// (`"\u{FFFD}"`). /// /// - Note: This initializer is deprecated. Use the initializer /// `String.init(decoding: array, as: Encoding.self)` instead, /// remembering that "\0" is a valid character in Swift. /// /// - Parameters: /// - nullTerminatedCodeUnits: An array containing a null-terminated /// sequence of code units encoded in `encoding`. /// - encoding: The encoding in which the code units should be /// interpreted. @inlinable @_alwaysEmitIntoClient @available(swift, deprecated: 6, message: "Use String(decoding: array, as: Encoding.self) instead, after truncating the null termination." ) public init( decodingCString nullTerminatedCodeUnits: [Encoding.CodeUnit], as encoding: Encoding.Type ) { self = String.decodeCString(nullTerminatedCodeUnits, as: encoding)!.0 } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use a copy of the String argument") public init( decodingCString nullTerminatedCodeUnits: String, as encoding: Encoding.Type ) { self = unsafe nullTerminatedCodeUnits.withCString(encodedAs: encoding) { unsafe String(decodingCString: $0, as: encoding.self) } } @inlinable @_alwaysEmitIntoClient @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init( decodingCString nullTerminatedCodeUnits: inout Encoding.CodeUnit, as encoding: Encoding.Type ) { guard nullTerminatedCodeUnits == 0 else { _preconditionFailure( "input of String.init(decodingCString:as:) must be null-terminated" ) } self = "" } } extension UnsafePointer where Pointee == UInt8 { @inlinable internal var _asCChar: UnsafePointer { @inline(__always) get { return unsafe UnsafeRawPointer(self).assumingMemoryBound(to: CChar.self) } } } extension UnsafePointer where Pointee == CChar { @inlinable internal var _asUInt8: UnsafePointer { @inline(__always) get { return unsafe UnsafeRawPointer(self).assumingMemoryBound(to: UInt8.self) } } }