Skip to content

Commit

Permalink
revisiting CString related String extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Max Moiseev committed Dec 18, 2015
1 parent d2be97d commit f4aaece
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 70 deletions.
4 changes: 2 additions & 2 deletions stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -971,8 +971,8 @@ func _stdlib_getSystemVersionPlistPropertyImpl(
propertyName: UnsafePointer<CChar>) -> UnsafePointer<CChar>

func _stdlib_getSystemVersionPlistProperty(propertyName: String) -> String? {
return String.fromCString(
_stdlib_getSystemVersionPlistPropertyImpl(propertyName))
let cs = _stdlib_getSystemVersionPlistPropertyImpl(propertyName)
return (cs != nil) ? String(cString: cs) : Optional.None
}
#endif

Expand Down
2 changes: 1 addition & 1 deletion stdlib/private/SwiftPrivateDarwinExtras/Subprocess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public func spawnChild(args: [String])
&pid, Process.arguments[0], &fileActions, nil, $0, _getEnviron())
}
if spawnResult != 0 {
print(String.fromCString(strerror(spawnResult)))
print(String(cString: strerror(spawnResult)))
requirementFailure("swift_posix_spawn() failed")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public func _stdlib_mkstemps(inout template: String, _ suffixlen: CInt) -> CInt
let (fd, fileName) = utf8.withUnsafeMutableBufferPointer {
(utf8) -> (CInt, String) in
let fd = mkstemps(UnsafeMutablePointer(utf8.baseAddress), suffixlen)
let fileName = String.fromCString(UnsafePointer(utf8.baseAddress))!
let fileName = String(cString: UnsafePointer(utf8.baseAddress))
return (fd, fileName)
}
template = fileName
Expand Down
10 changes: 5 additions & 5 deletions stdlib/public/SDK/ObjectiveC/ObjectiveC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,19 @@ extension Selector : Equatable, Hashable {
extension Selector : CustomStringConvertible {
/// A textual representation of `self`.
public var description: String {
if let (s, _) = String.fromCStringRepairingIllFormedUTF8(
sel_getName(self)) {
return s
let name = sel_getName(self)
if name == nil {
return "<NULL>"
}
return "<NULL>"
return String(cString: name)
}
}

extension String {
/// Construct the C string representation of an Objective-C selector.
public init(_sel: Selector) {
// FIXME: This misses the ASCII optimization.
self = String.fromCString(sel_getName(_sel))!
self = String(cString: sel_getName(_sel))
}
}

Expand Down
71 changes: 49 additions & 22 deletions stdlib/public/core/CString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,65 @@
import SwiftShims

extension String {
/// Creates a new `String` by copying the nul-terminated UTF-8 data
/// referenced by a `CString`.

/// Create a new `String` by copying the nul-terminated UTF-8 data
/// referenced by a `cString`.
///
/// Returns `nil` if the `CString` is `NULL` or if it contains ill-formed
/// UTF-8 code unit sequences.
@warn_unused_result
public static func fromCString(cs: UnsafePointer<CChar>) -> String? {
if cs._isNull {
/// If `cString` contains ill-formed UTF-8 code unit sequences, replaces them
/// with replacement characters (U+FFFD).
///
/// - Requires: `cString != nil`
public init(cString: UnsafePointer<CChar>) {
_require(cString != nil, "cString must not be nil")
self = String.decodeCString(UnsafePointer(cString), `as`: UTF8.self,
repairingInvalidCodeUnits: true)!.result
}

/// Create a new `String` by copying the nul-terminated UTF-8 data
/// referenced by a `cString`.
///
/// Does not try to repair ill-formed UTF-8 code unit sequences, fails if any
/// such sequences are found.
///
/// - Requires: `cString != nil`
public init?(validatingUTF8 cString: UnsafePointer<CChar>) {
_require(cString != nil, "cString must not be nil")
guard let (result, _) = String.decodeCString(
UnsafePointer(cString),
`as`: UTF8.self,
repairingInvalidCodeUnits: false) else {
return nil
}
let len = Int(_swift_stdlib_strlen(cs))
return String._fromCodeUnitSequence(UTF8.self,
input: UnsafeBufferPointer(start: UnsafeMutablePointer(cs), length: len))
self = result
}

/// Create a new `String` by copying the nul-terminated UTF-8 data
/// referenced by a `CString`.
/// Create a new `String` by copying the nul-terminated data
/// referenced by a `cString` using `encoding`.
///
/// Returns `nil` if the `CString` is `NULL`. If `CString` contains
/// ill-formed UTF-8 code unit sequences, replaces them with replacement
/// characters (U+FFFD).
/// Returns `nil` if the `cString` is `NULL` or if it contains ill-formed code
/// units and no repairing has been requested. Otherwise replaces
/// ill-formed code units with replacement characters (U+FFFD).
@warn_unused_result
public static func fromCStringRepairingIllFormedUTF8(
cs: UnsafePointer<CChar>) -> (String, hadError: Bool)? {
if cs._isNull {
public static func decodeCString<Encoding : UnicodeCodec>(
cString: UnsafePointer<Encoding.CodeUnit>,
`as` encoding: Encoding.Type, // FIXME: unbacktick `at` whenever allowed
repairingInvalidCodeUnits isReparing: Bool = true)
-> (result: String, repairsMade: Bool)? {

if cString._isNull {
return nil
}
let len = Int(_swift_stdlib_strlen(cs))
let (result, hadError) = String._fromCodeUnitSequenceWithRepair(UTF8.self,
input: UnsafeBufferPointer(start: UnsafeMutablePointer(cs), length: len))
return (result, hadError: hadError)
let len = Int(_swift_stdlib_strlen(UnsafePointer(cString)))
let buffer = UnsafeBufferPointer<Encoding.CodeUnit>(
start: cString, length: len)

let (stringBuffer, hadError) = _StringBuffer.fromCodeUnits(
encoding, input: buffer, repairIllFormedSequences: isReparing)
return stringBuffer.map {
(result: String(_storage: $0), repairsMade: hadError)
}
}

}

/// From a non-`nil` `UnsafePointer` to a null-terminated string
Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/core/OutputStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ internal func _adHocPrint<T, TargetStream : OutputStream>(
target.write(")")

case let enumMirror as _EnumMirror:
if let caseName = String.fromCString(enumMirror.caseName) {
if enumMirror.caseName != nil {
let caseName = String(cString: enumMirror.caseName)
// Write the qualified type name in debugPrint.
if isDebugPrint {
target.write(_typeName(mirror.valueType))
Expand Down
3 changes: 1 addition & 2 deletions stdlib/public/core/Process.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ public enum Process {
// Use lazy initialization of static properties to safely initialize the
// public 'arguments' property on first use.
(0..<Int(argc)).map { i in
String.fromCStringRepairingIllFormedUTF8(unsafeArgv[i])
.map { $0.0 } ?? ""
String(cString: unsafeArgv[i])
}
}()

Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/Reflection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ struct _EnumMirror : _Mirror {
@_silgen_name("swift_EnumMirror_subscript")get
}
var summary: String {
let maybeCaseName = String.fromCString(self.caseName)
let maybeCaseName = String(validatingUTF8: self.caseName)
let typeName = _typeName(valueType)
if let caseName = maybeCaseName {
return typeName + "." + caseName
Expand Down
63 changes: 34 additions & 29 deletions test/1_stdlib/NSStringAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1340,7 +1340,7 @@ NSStringAPIs.test("smallestEncoding") {

func getHomeDir() -> String {
#if os(OSX)
return String.fromCString(getpwuid(getuid()).pointee.pw_dir)!
return String(cString: getpwuid(getuid()).pointee.pw_dir)
#elseif os(iOS) || os(tvOS) || os(watchOS)
// getpwuid() returns null in sandboxed apps under iOS simulator.
return NSHomeDirectory()
Expand Down Expand Up @@ -2190,64 +2190,69 @@ func asCCharArray(a: [UInt8]) -> [CChar] {
return a.map { CChar(bitPattern: $0) }
}

CStringTests.test("String.fromCString") {
do {
let s = getNullCString()
expectEmpty(String.fromCString(s))
}
CStringTests.test("String.init(validatingUTF8:)") {
do {
let (s, dealloc) = getASCIICString()
expectOptionalEqual("ab", String.fromCString(s))
expectOptionalEqual("ab", String(validatingUTF8: s))
dealloc()
}
do {
let (s, dealloc) = getNonASCIICString()
expectOptionalEqual("аб", String.fromCString(s))
expectOptionalEqual("аб", String(validatingUTF8: s))
dealloc()
}
do {
let (s, dealloc) = getIllFormedUTF8String1()
expectEmpty(String.fromCString(s))
expectEmpty(String(validatingUTF8: s))
dealloc()
}
}

CStringTests.test("String.fromCStringRepairingIllFormedUTF8") {
do {
let s = getNullCString()
let result = String.fromCStringRepairingIllFormedUTF8(s)
expectEmpty(result)
}
CStringTests.test("String(cString:)") {
do {
let (s, dealloc) = getASCIICString()
if let (result, hadError) = String.fromCStringRepairingIllFormedUTF8(s) {
expectOptionalEqual("ab", result)
expectFalse(hadError)
} else {
expectTrue(false, "Expected .Some()")
}
let result = String(cString: s)
expectEqual("ab", result)
dealloc()
}
do {
let (s, dealloc) = getNonASCIICString()
if let (result, hadError) = String.fromCStringRepairingIllFormedUTF8(s) {
expectOptionalEqual("аб", result)
expectFalse(hadError)
} else {
expectTrue(false, "Expected .Some()")
}
let result = String(cString: s)
expectEqual("аб", result)
dealloc()
}
do {
let (s, dealloc) = getIllFormedUTF8String1()
if let (result, hadError) = String.fromCStringRepairingIllFormedUTF8(s) {
let result = String(cString: s)
expectEqual("\u{41}\u{fffd}\u{fffd}\u{fffd}\u{41}", result)
dealloc()
}
}

CStringTests.test("String.decodeCString") {
do {
let s = getNullCString()
let result = String.decodeCString(UnsafePointer(s), `as`: UTF8.self)
expectEmpty(result)
}
do { // repairing
let (s, dealloc) = getIllFormedUTF8String1()
if let (result, repairsMade) = String.decodeCString(
UnsafePointer(s), `as`: UTF8.self, repairingInvalidCodeUnits: true) {
expectOptionalEqual("\u{41}\u{fffd}\u{fffd}\u{fffd}\u{41}", result)
expectTrue(hadError)
expectTrue(repairsMade)
} else {
expectTrue(false, "Expected .Some()")
}
dealloc()
}
do { // non repairing
let (s, dealloc) = getIllFormedUTF8String1()
let result = String.decodeCString(
UnsafePointer(s), `as`: UTF8.self, repairingInvalidCodeUnits: false)
expectEmpty(result)
dealloc()
}
}

runAllTests()
Expand Down
2 changes: 1 addition & 1 deletion test/Interpreter/SDK/autolinking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ if global() != 42 {

let RTLD_DEFAULT = UnsafeMutablePointer<Void>(bitPattern: -2)
if dlsym(RTLD_DEFAULT, "global") == nil {
print(String.fromCString(dlerror())!)
print(String(cString: dlerror()))
exit(EXIT_FAILURE)
}
#endif
4 changes: 2 additions & 2 deletions test/Interpreter/SDK/glob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ func Glob(g: String) -> Array<String> {
if rv == 0 {
var result = Array<String>()
for x in 0..<Int(gt.gl_pathc) {
var str = String.fromCString(gt.gl_pathv[x])
result.append(str!)
var str = String(cString: gt.gl_pathv[x])
result.append(str)
}
globfree(&gt)
return result
Expand Down
2 changes: 1 addition & 1 deletion test/Interpreter/SDK/libc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var readed = read(sourceFile, bytes, 11)
close(sourceFile)
assert(readed == 11)
bytes[11] = CChar(0)
print("the magic word is //\(String.fromCString(bytes)!)//")
print("the magic word is //\(String(cString: bytes))//")

// CHECK: O_CREAT|O_EXCL returned errno *17*
let errFile =
Expand Down
4 changes: 3 additions & 1 deletion test/Interpreter/SDK/objc_mangling.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ func checkProtocolName(proto: Protocol, _ name: String, _ mangled: String)

func checkIvarName(cls: AnyClass, _ name: String)
{
assert(name == String.fromCString(ivar_getName(class_getInstanceVariable(cls, name))))
let ivarName = ivar_getName(class_getInstanceVariable(cls, name))
let s = ivarName != nil ? String(cString: ivarName) : Optional.None
assert(name == s)
}


Expand Down
2 changes: 1 addition & 1 deletion validation-test/stdlib/OpenCLSDKOverlay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ tests.test("clSetKernelArgsListAPPLE") {
print("Error: Failed to build program executable!")
clGetProgramBuildInfo(
program, device_id, cl_program_build_info(CL_PROGRAM_BUILD_LOG), 2048, &buffer, &len)
print("\(String.fromCString(buffer)!)")
print("\(String(cString: buffer))")
exit(1)
}

Expand Down

0 comments on commit f4aaece

Please sign in to comment.