Skip to content

[string] Wean off of non-String-API. #1476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions Foundation/NSString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ open class NSString : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSC
}

public init(characters: UnsafePointer<unichar>, length: Int) {
_storage = String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length))
_storage = String(decoding: UnsafeBufferPointer(start: characters, count: length), as: UTF16.self)
}

public required convenience init(unicodeScalarLiteral value: StaticString) {
Expand Down Expand Up @@ -1166,16 +1166,8 @@ extension NSString {
}

public convenience init?(utf8String nullTerminatedCString: UnsafePointer<Int8>) {
let count = Int(strlen(nullTerminatedCString))
if let str = nullTerminatedCString.withMemoryRebound(to: UInt8.self, capacity: count, {
let buffer = UnsafeBufferPointer<UInt8>(start: $0, count: count)
return String._fromCodeUnitSequence(UTF8.self, input: buffer)
}) as String?
{
self.init(str)
} else {
return nil
}
guard let str = String(validatingUTF8: nullTerminatedCString) else { return nil }
self.init(str)
}

public convenience init(format: String, arguments argList: CVaListPointer) {
Expand Down Expand Up @@ -1357,23 +1349,19 @@ open class NSMutableString : NSString {
}

public required init(stringLiteral value: StaticString) {
if value.hasPointerRepresentation {
super.init(String._fromWellFormedCodeUnitSequence(UTF8.self, input: UnsafeBufferPointer(start: value.utf8Start, count: Int(value.utf8CodeUnitCount))))
} else {
var uintValue = value.unicodeScalar.value
super.init(String._fromWellFormedCodeUnitSequence(UTF32.self, input: UnsafeBufferPointer(start: &uintValue, count: 1)))
}
super.init(value.description)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does value.description just return a String representing StaticString? Is there a performance hit to not initializing directly from the contents?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value.description is direct initialization of a String with the same contents. It will be faster in the future than trying to do it yourself, as StaticString can poke at String's innards.

Currently, it will encode a single scalar as UTF-8 prior to initialization, and then allocate storage on the heap to store it. We could try to skip that transcoding temporarily, however, I'm about to land small strings and plan on supporting UTF-8 in them. In this case, we still want to encode as UTF-8 there, so we'd do a encoding anyways and skip the heap allocation. This will be better than what this code was doing before: before, even a single scalar gets a heap allocation.

It is equivalent for non-scalar StaticString.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, sounds reasonable, then!

}

public required init(string aString: String) {
super.init(aString)
}

internal func appendCharacters(_ characters: UnsafePointer<unichar>, length: Int) {
let str = String(decoding: UnsafeBufferPointer(start: characters, count: length), as: UTF16.self)
if type(of: self) == NSMutableString.self {
_storage.append(String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length)))
_storage.append(str)
} else {
replaceCharacters(in: NSRange(location: self.length, length: 0), with: String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length)))
replaceCharacters(in: NSRange(location: self.length, length: 0), with: str)
}
}

Expand Down
6 changes: 3 additions & 3 deletions Foundation/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@ extension String : _ObjectTypeBridgeable {
let buffer = UnsafeMutablePointer<UniChar>.allocate(capacity: length)
CFStringGetCharacters(cf, CFRangeMake(0, length), buffer)

let str = String._fromCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: buffer, count: length))
let str = String(decoding: UnsafeBufferPointer(start: buffer, count: length), as: UTF16.self)
buffer.deinitialize(count: length)
buffer.deallocate()
result = str
}
} else if type(of: source) == _NSCFConstantString.self {
let conststr = unsafeDowncast(source, to: _NSCFConstantString.self)
let str = String._fromCodeUnitSequence(UTF8.self, input: UnsafeBufferPointer(start: conststr._ptr, count: Int(conststr._length)))
let str = String(decoding: UnsafeBufferPointer(start: conststr._ptr, count: Int(conststr._length)), as: UTF8.self)
result = str
} else {
let len = source.length
var characters = [unichar](repeating: 0, count: len)
result = characters.withUnsafeMutableBufferPointer() { (buffer: inout UnsafeMutableBufferPointer<unichar>) -> String? in
source.getCharacters(buffer.baseAddress!, range: NSRange(location: 0, length: len))
return String._fromCodeUnitSequence(UTF16.self, input: buffer)
return String(decoding: buffer, as: UTF16.self)
}
}
return result != nil
Expand Down
7 changes: 3 additions & 4 deletions Foundation/XMLParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,7 @@ internal func _NSXMLParserStartElementNs(_ ctx: _CFXMLInterface, localname: Unsa
if numBytesWithoutTerminator > 0 {
let buffer = UnsafeBufferPointer(start: value,
count: numBytesWithoutTerminator)
attributeValue = String._fromCodeUnitSequence(UTF8.self,
input: buffer)!
attributeValue = String(decoding: buffer, as: UTF8.self)
}
attrDict[attributeQName] = attributeValue
}
Expand Down Expand Up @@ -352,8 +351,8 @@ internal func _NSXMLParserCharacters(_ ctx: _CFXMLInterface, ch: UnsafePointer<U
_CFXMLInterfaceResetRecursiveState(context)
} else {
if let delegate = parser.delegate {
let str = String._fromCodeUnitSequence(UTF8.self, input: UnsafeBufferPointer(start: ch, count: Int(len)))
delegate.parser(parser, foundCharacters: str!)
let str = String(decoding: UnsafeBufferPointer(start: ch, count: Int(len)), as: UTF8.self)
delegate.parser(parser, foundCharacters: str)
}
}
}
Expand Down