Revert "stdlib: Fix hasPrefix,hasSuffix tests"

Revert "stdlib: Add back a test I removed"
Revert "Add test cases to exercise the native String vs cocoa buffer String path."
Revert "stdlib: Move the darwin String implementation over to use the ICU library."

This reverts commit r31477, r31476, r31475, r31474.

Commit r31474 broke the ASAN build.

Swift SVN r31488
This commit is contained in:
Arnold Schwaighofer
2015-08-26 13:09:03 +00:00
parent a49198c791
commit 2d8f29e710
12 changed files with 378 additions and 377 deletions

View File

@@ -2377,20 +2377,6 @@ public func expectEqualFunctionsForDomain<ArgumentType, Result : Equatable>(
}
}
public func expectEqualFunctionsForDomain2<ArgumentType, Result : Equatable>(
arguments: [ArgumentType], _ arguments2: [ArgumentType],
_ function1: (ArgumentType, ArgumentType) -> Result,
_ function2: (ArgumentType, ArgumentType) -> Result
) {
for a in arguments {
for a2 in arguments2 {
let expected = function1(a, a2)
let actual = function2(a, a2)
expectEqual(expected, actual, "where the argument is: \(a), \(a2)")
}
}
}
public func expectEqualMethodsForDomain<
SelfType, ArgumentType, Result : Equatable
>(

View File

@@ -32,7 +32,6 @@ __swift_size_t strlen(const char *s);
char *strcpy(char *__restrict__ dst, const char *__restrict__ src);
int strcmp(const char *s1, const char *s2);
int memcmp(const void *s1, const void *s2, __swift_size_t n);
void *malloc(__swift_size_t size);
int putchar(int c);
__swift_ssize_t read(int fildes, void *buf, __swift_size_t nbyte);

View File

@@ -73,6 +73,5 @@ __swift_int32_t _swift_stdlib_unicode_strToLower(
__swift_uint16_t *Destination, __swift_int32_t DestinationCapacity,
const __swift_uint16_t *Source, __swift_int32_t SourceLength);
extern const __swift_int8_t *_swift_stdlib_unicode_ascii_collation_table;
#endif

View File

@@ -10,8 +10,6 @@
//
//===----------------------------------------------------------------------===//
import SwiftShims
/// `Character` represents some Unicode grapheme cluster as
/// defined by a canonical, localized, or otherwise tailored
/// segmentation algorithm.
@@ -312,13 +310,7 @@ public func ==(lhs: Character, rhs: Character) -> Bool {
case let (.Small(lbits), .Small(rbits)) where
Bool(Builtin.cmp_uge_Int63(lbits, _minASCIICharReprBuiltin))
&& Bool(Builtin.cmp_uge_Int63(rbits, _minASCIICharReprBuiltin)):
let table: UnsafePointer<Int8> =
_swift_stdlib_unicode_ascii_collation_table
let leftKey = table[Int(Int8(Builtin.trunc_Int63_Int8(lbits)))]
let rightKey = table[Int(Int8(Builtin.trunc_Int63_Int8(rbits)))]
return Bool(leftKey == rightKey)
return Bool(Builtin.cmp_eq_Int63(lbits, rbits))
default:
// FIXME(performance): constructing two temporary strings is extremely
// wasteful and inefficient.
@@ -334,13 +326,7 @@ public func <(lhs: Character, rhs: Character) -> Bool {
// See String._lessThanASCII.
Bool(Builtin.cmp_uge_Int63(lbits, _minASCIICharReprBuiltin))
&& Bool(Builtin.cmp_uge_Int63(rbits, _minASCIICharReprBuiltin)):
let table: UnsafePointer<Int8> =
_swift_stdlib_unicode_ascii_collation_table
let leftKey = table[Int(Int8(Builtin.trunc_Int63_Int8(lbits)))]
let rightKey = table[Int(Int8(Builtin.trunc_Int63_Int8(rbits)))]
return Bool(leftKey - rightKey < 0)
return Bool(Builtin.cmp_ult_Int63(lbits, rbits))
default:
// FIXME(performance): constructing two temporary strings is extremely
// wasteful and inefficient.

View File

@@ -292,204 +292,131 @@ extension String {
}
}
#if _runtime(_ObjC)
/// Compare two strings using the Unicode collation algorithm in the
/// deterministic comparison mode. (The strings which are equivalent according
/// to their NFD form are considered equal. Strings which are equivalent
/// according to the plain Unicode collation algorithm are additionaly ordered
/// based on their NFD.)
///
/// See Unicode Technical Standard #10.
///
/// The behavior is equivalent to `NSString.compare()` with default options.
///
/// - returns:
/// * an unspecified value less than zero if `lhs < rhs`,
/// * zero if `lhs == rhs`,
/// * an unspecified value greater than zero if `lhs > rhs`.
@asmname("swift_stdlib_compareNSStringDeterministicUnicodeCollation")
public func _stdlib_compareNSStringDeterministicUnicodeCollation(
lhs: AnyObject, _ rhs: AnyObject
)-> Int32
#endif
extension String : Equatable {
}
@warn_unused_result
public func ==(lhs: String, rhs: String) -> Bool {
return _compareString(lhs, rhs) == 0
if lhs._core.isASCII && rhs._core.isASCII {
if lhs._core.count != rhs._core.count {
return false
}
return memcmp(
lhs._core.startASCII, rhs._core.startASCII,
rhs._core.count) == 0
}
return lhs._compareString(rhs) == 0
}
extension String : Comparable {
}
extension String {
#if _runtime(_ObjC)
/// Compare a NSString in 'lhs' to a native string in 'rhs'.
func _compareCocoaToNativeString(
lhs: _StringCore,
_ rhs: _StringCore
) -> Int {
_sanityCheck(
lhs.hasCocoaBuffer && rhs.hasContiguousStorage,
"Expect a cocoa and a native buffer")
// Copy the contents of the NSString to a temporary buffer.
let bufferSizeLhs = lhs.count
let bufferLhs = UnsafeMutablePointer<UTF16.CodeUnit>.alloc(bufferSizeLhs)
defer { bufferLhs.dealloc(bufferSizeLhs) }
_cocoaStringReadAll(lhs.cocoaBuffer!, bufferLhs)
if rhs.isASCII {
let rhsPtr = UnsafePointer<Int8>(rhs.startASCII)
return -Int(_swift_stdlib_unicode_compare_utf8_utf16(
rhsPtr, Int32(rhs.count),
bufferLhs, Int32(bufferSizeLhs)))
} else {
let rhsPtr = UnsafePointer<UTF16.CodeUnit>(rhs.startUTF16)
return Int(_swift_stdlib_unicode_compare_utf16_utf16(
bufferLhs, Int32(bufferSizeLhs),
rhsPtr, Int32(rhs.count)))
}
}
/// Compare two string buffers where at least one is a cococa buffer.
func _compareCocoaBuffer(lhs: _StringCore, _ rhs: _StringCore) -> Int {
switch(lhs.hasCocoaBuffer, rhs.hasCocoaBuffer) {
case (true, true):
// Copy the contents of the NSString to a temporary buffer.
let bufferSizeLhs = lhs.count
let bufferLhs =
UnsafeMutablePointer<UTF16.CodeUnit>.alloc(bufferSizeLhs)
let bufferSizeRhs = rhs.count
let bufferRhs =
UnsafeMutablePointer<UTF16.CodeUnit>.alloc(bufferSizeLhs)
defer {
bufferRhs.dealloc(bufferSizeRhs)
bufferLhs.dealloc(bufferSizeLhs)
}
_cocoaStringReadAll(lhs.cocoaBuffer!, bufferLhs)
_cocoaStringReadAll(rhs.cocoaBuffer!, bufferRhs)
return Int(_swift_stdlib_unicode_compare_utf16_utf16(
bufferLhs, Int32(bufferSizeLhs),
bufferRhs, Int32(bufferSizeRhs)))
case (true, false):
return _compareCocoaToNativeString(lhs, rhs)
case (false, true):
return -_compareCocoaToNativeString(rhs, lhs)
case (false, false):
_debugPreconditionFailure("Must have at least one cocoa buffer")
/// This is consistent with Foundation, but incorrect as defined by Unicode.
/// Unicode weights some ASCII punctuation in a different order than ASCII
/// value. Such as:
///
/// 0022 ; [*02FF.0020.0002] # QUOTATION MARK
/// 0023 ; [*038B.0020.0002] # NUMBER SIGN
/// 0025 ; [*038C.0020.0002] # PERCENT SIGN
/// 0026 ; [*0389.0020.0002] # AMPERSAND
/// 0027 ; [*02F8.0020.0002] # APOSTROPHE
///
/// - Precondition: Both `self` and `rhs` are ASCII strings.
@warn_unused_result
public // @testable
func _compareASCII(rhs: String) -> Int {
var compare = Int(memcmp(
self._core.startASCII, rhs._core.startASCII,
min(self._core.count, rhs._core.count)))
if compare == 0 {
compare = self._core.count - rhs._core.count
}
// This efficiently normalizes the result to -1, 0, or 1 to match the
// behaviour of NSString's compare function.
return (compare > 0 ? 1 : 0) - (compare < 0 ? 1 : 0)
}
#endif
/// Compares two strings with the Unicode Collation Algorithm.
@warn_unused_result
@inline(never)
@_semantics("stdlib_binary_only") // Hide the ICU dependency
@_semantics("stdlib_binary_only") // Hide the CF/ICU dependency
public // @testable
func _compareDeterministicUnicodeCollation(lhs: String, _ rhs: String) -> Int {
func _compareDeterministicUnicodeCollation(rhs: String) -> Int {
// Note: this operation should be consistent with equality comparison of
// Character.
#if _runtime(_ObjC)
if lhs._core.hasCocoaBuffer || rhs._core.hasCocoaBuffer {
return _compareCocoaBuffer(lhs._core, rhs._core)
}
#endif
switch (lhs._core.isASCII, rhs._core.isASCII) {
return Int(_stdlib_compareNSStringDeterministicUnicodeCollation(
_bridgeToObjectiveCImpl(), rhs._bridgeToObjectiveCImpl()))
#else
switch (_core.isASCII, rhs._core.isASCII) {
case (true, false):
let lhsPtr = UnsafePointer<Int8>(lhs._core.startASCII)
let lhsPtr = UnsafePointer<Int8>(_core.startASCII)
let rhsPtr = UnsafePointer<UTF16.CodeUnit>(rhs._core.startUTF16)
return Int(_swift_stdlib_unicode_compare_utf8_utf16(
lhsPtr, Int32(lhs._core.count), rhsPtr, Int32(rhs._core.count)))
lhsPtr, Int32(_core.count), rhsPtr, Int32(rhs._core.count)))
case (false, true):
// Just invert it and recurse for this case.
return -_compareDeterministicUnicodeCollation(rhs, lhs)
return -rhs._compareDeterministicUnicodeCollation(self)
case (false, false):
let lhsPtr = UnsafePointer<UTF16.CodeUnit>(lhs._core.startUTF16)
let lhsPtr = UnsafePointer<UTF16.CodeUnit>(_core.startUTF16)
let rhsPtr = UnsafePointer<UTF16.CodeUnit>(rhs._core.startUTF16)
return Int(_swift_stdlib_unicode_compare_utf16_utf16(
lhsPtr, Int32(lhs._core.count),
lhsPtr, Int32(_core.count),
rhsPtr, Int32(rhs._core.count)))
case (true, true):
return Int(_compare_ascii(lhs._core, rhs._core))
let lhsPtr = UnsafePointer<Int8>(_core.startASCII)
let rhsPtr = UnsafePointer<Int8>(rhs._core.startASCII)
return Int(_swift_stdlib_unicode_compare_utf8_utf8(
lhsPtr, Int32(_core.count),
rhsPtr, Int32(rhs._core.count)))
}
#endif
}
@warn_unused_result
public // @testable
func _compareString(lhs: String, _ rhs: String) -> Int {
// ASCII fast path.
let lhsCore = lhs._core
let rhsCore = rhs._core
if lhsCore.isASCII && rhsCore.isASCII {
return _compare_ascii(lhsCore, rhsCore)
func _compareString(rhs: String) -> Int {
#if _runtime(_ObjC)
// We only want to perform this optimization on objc runtimes. Elsewhere,
// we will make it follow the unicode collation algorithm even for ASCII.
if (_core.isASCII && rhs._core.isASCII) {
return _compareASCII(rhs)
}
return _compareDeterministicUnicodeCollation(lhs, rhs)
}
@warn_unused_result
@inline(never)
public func _compare_ascii(lhs: _StringCore, _ rhs: _StringCore) -> Int {
// The ascii table.
let table: UnsafePointer<Int8> = _swift_stdlib_unicode_ascii_collation_table
let lhsPtr = UnsafePointer<Int8>(lhs.startASCII)
let rhsPtr = UnsafePointer<Int8>(rhs.startASCII)
let leftLength = lhs.count
let rightLength = rhs.count
var posLeft = 0
var posRight = 0
// Two empty strings are equal.
while true {
// Skip zero collation keys. They don't participate in the ordering
// relation.
var leftKey: Int8 = 0
var rightKey: Int8 = 0
while posLeft < leftLength {
// Get the collation key.
leftKey = table[Int(lhsPtr[posLeft])]
if leftKey != 0 {
break
}
posLeft = posLeft &+ 1;
}
while posRight < rightLength {
// Get the collation key.
rightKey = table[Int(rhsPtr[posRight])]
if rightKey != 0 {
break
}
posRight = posRight &+ 1;
}
// Now we either reached the end of both strings, in which case the strings
// are equal.
if posLeft == leftLength && posRight == rightLength {
return 0
}
// Or we reached the end of the left string while there is still a non zero
// collation element in the right string.
if posLeft == leftLength {
return -1
}
// Or we reached the end of the right string while there is still a non zero
// collation element in the left string.
if posRight == rightLength {
return 1
}
// Or we have two characters that either have a difference, in which case we
// return the difference as the result.
let difference = leftKey - rightKey
if difference != 0 {
return Int(difference)
}
// Otherwise, we have a zero distance and the prefix so far is equal and we
// continue processing the remaining suffix.
posRight = posRight &+ 1
posLeft = posLeft &+ 1
#endif
return _compareDeterministicUnicodeCollation(rhs)
}
}
@warn_unused_result
public func <(lhs: String, rhs: String) -> Bool {
return _compareString(lhs, rhs) < 0
return lhs._compareString(rhs) < 0
}
// Support for copy-on-write
@@ -517,6 +444,16 @@ extension String {
}
}
#if _runtime(_ObjC)
@warn_unused_result
@asmname("swift_stdlib_NSStringNFDHashValue")
func _stdlib_NSStringNFDHashValue(str: AnyObject) -> Int
@warn_unused_result
@asmname("swift_stdlib_NSStringASCIIHashValue")
func _stdlib_NSStringASCIIHashValue(str: AnyObject) -> Int
#endif
extension String : Hashable {
/// The hash value.
///
@@ -527,19 +464,25 @@ extension String : Hashable {
/// hash value across program runs.
public var hashValue: Int {
#if _runtime(_ObjC)
// For cocoa backed strings copy the contents to a temporary.
// A possible optimization would be to try get a pointer to contigous
// storage of NSString if it exists and use that instead.
if self._core.hasCocoaBuffer {
let bufferSize = _core.count
let buffer = UnsafeMutablePointer<UTF16.CodeUnit>.alloc(bufferSize)
defer { buffer.dealloc(bufferSize) }
_cocoaStringReadAll(_core.cocoaBuffer!, buffer)
return _swift_stdlib_unicode_hash(
UnsafeMutablePointer<UInt16>(buffer),
Int32(_core.count))
}
// Mix random bits into NSString's hash so that clients don't rely on
// Swift.String.hashValue and NSString.hash being the same.
#if arch(i386) || arch(arm)
let hashOffset = Int(bitPattern: 0x88dd_cc21)
#else
let hashOffset = Int(bitPattern: 0x429b_1266_88dd_cc21)
#endif
// FIXME(performance): constructing a temporary NSString is extremely
// wasteful and inefficient.
let cocoaString = unsafeBitCast(
self._bridgeToObjectiveCImpl(), _NSStringCoreType.self)
// If we have an ASCII string, we do not need to normalize.
if self._core.isASCII {
return hashOffset ^ _stdlib_NSStringASCIIHashValue(cocoaString)
} else {
return hashOffset ^ _stdlib_NSStringNFDHashValue(cocoaString)
}
#else
if self._core.isASCII {
return _swift_stdlib_unicode_hash_ascii(
UnsafeMutablePointer<Int8>(_core.startASCII),
@@ -549,6 +492,7 @@ extension String : Hashable {
UnsafeMutablePointer<UInt16>(_core.startUTF16),
Int32(_core.count))
}
#endif
}
}
@@ -807,26 +751,34 @@ extension String {
}
}
}
#if _runtime(_ObjC)
@warn_unused_result
@asmname("swift_stdlib_NSStringLowercaseString")
func _stdlib_NSStringLowercaseString(str: AnyObject) -> _CocoaStringType
@warn_unused_result
@asmname("swift_stdlib_NSStringUppercaseString")
func _stdlib_NSStringUppercaseString(str: AnyObject) -> _CocoaStringType
#else
@warn_unused_result
internal func _nativeUnicodeLowercaseString(str: String) -> String {
let initialSize = str._core.count
var buffer = _StringBuffer(
capacity: initialSize, initialSize: initialSize, elementWidth: 2)
capacity: str._core.count, initialSize: str._core.count, elementWidth: 2)
// Try to write it out to the same length.
var dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
let correctSize = Int(_swift_stdlib_unicode_strToLower(
dest, Int32(initialSize),
str._core.startUTF16, Int32(initialSize)))
let dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
let z = _swift_stdlib_unicode_strToLower(
dest, Int32(str._core.count),
str._core.startUTF16, Int32(str._core.count))
let correctSize = Int(z)
// If more space is needed, do it again with the correct buffer size.
if correctSize != initialSize {
if correctSize != str._core.count {
buffer = _StringBuffer(
capacity: correctSize, initialSize: correctSize, elementWidth: 2)
dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
let dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
_swift_stdlib_unicode_strToLower(
dest, Int32(correctSize), str._core.startUTF16, Int32(initialSize))
dest, Int32(correctSize), str._core.startUTF16, Int32(str._core.count))
}
return String(_storage: buffer)
@@ -834,27 +786,28 @@ internal func _nativeUnicodeLowercaseString(str: String) -> String {
@warn_unused_result
internal func _nativeUnicodeUppercaseString(str: String) -> String {
let initialSize = str._core.count
var buffer = _StringBuffer(
capacity: initialSize, initialSize: initialSize, elementWidth: 2)
capacity: str._core.count, initialSize: str._core.count, elementWidth: 2)
// Try to write it out to the same length.
var dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
let correctSize = Int(_swift_stdlib_unicode_strToUpper(
dest, Int32(initialSize),
str._core.startUTF16, Int32(initialSize)))
let dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
let z = _swift_stdlib_unicode_strToUpper(
dest, Int32(str._core.count),
str._core.startUTF16, Int32(str._core.count))
let correctSize = Int(z)
// If more space is needed, do it again with the correct buffer size.
if correctSize != initialSize {
if correctSize != str._core.count {
buffer = _StringBuffer(
capacity: correctSize, initialSize: correctSize, elementWidth: 2)
dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
let dest = UnsafeMutablePointer<UTF16.CodeUnit>(buffer.start)
_swift_stdlib_unicode_strToUpper(
dest, Int32(correctSize), str._core.startUTF16, Int32(initialSize))
dest, Int32(correctSize), str._core.startUTF16, Int32(str._core.count))
}
return String(_storage: buffer)
}
#endif
// Unicode algorithms
extension String {
@@ -910,7 +863,12 @@ extension String {
return String(_storage: buffer)
}
#if _runtime(_ObjC)
return _cocoaStringToSwiftString_NonASCII(
_stdlib_NSStringLowercaseString(self._bridgeToObjectiveCImpl()))
#else
return _nativeUnicodeLowercaseString(self)
#endif
}
public var uppercaseString: String {
@@ -932,7 +890,12 @@ extension String {
return String(_storage: buffer)
}
#if _runtime(_ObjC)
return _cocoaStringToSwiftString_NonASCII(
_stdlib_NSStringUppercaseString(self._bridgeToObjectiveCImpl()))
#else
return _nativeUnicodeUppercaseString(self)
#endif
}
}

View File

@@ -39,16 +39,9 @@ if(SWIFT_HOST_VARIANT MATCHES "${SWIFT_DARWIN_VARIANTS}")
SwiftObject.mm
SwiftNativeNSXXXBase.mm.gyb
Remangle.cpp
Reflection.mm
Reflection.mm)
set(LLVM_OPTIONAL_SOURCES
UnicodeNormalization.cpp)
if(SWIFT_DARWIN_ICU_INCLUDE_PATH)
# The icu install needs to be configured with --disable-renaming, otherwise
# we get the version number postfixed to the api calls.
include_directories(${SWIFT_DARWIN_ICU_INCLUDE_PATH})
list(APPEND swift_runtime_compile_flags
"-DU_DISABLE_RENAMING=1")
endif()
set(swift_runtime_link_libraries icucore)
else()
find_package(ICU REQUIRED COMPONENTS uc i18n)
set(swift_runtime_unicode_normalization_sources
@@ -63,7 +56,7 @@ endif()
check_cxx_compiler_flag("-Werror -Wglobal-constructors" CXX_SUPPORTS_GLOBAL_CONSTRUCTORS_WARNING)
append_if(CXX_SUPPORTS_GLOBAL_CONSTRUCTORS_WARNING "-Wglobal-constructors" swift_runtime_compile_flags)
add_swift_library(swiftRuntime IS_STDLIB IS_STDLIB_CORE USE_INTERNAL_SDK
add_swift_library(swiftRuntime IS_STDLIB IS_STDLIB_CORE
Casting.cpp
Demangle.cpp
ErrorObject.cpp

View File

@@ -95,6 +95,31 @@ swift_stdlib_NSObject_isEqual(NSObject *NS_RELEASES_ARGUMENT lhs,
return Result;
}
extern "C" int32_t swift_stdlib_compareNSStringDeterministicUnicodeCollation(
NSString *NS_RELEASES_ARGUMENT lhs, NSString *NS_RELEASES_ARGUMENT rhs) {
// 'kCFCompareNonliteral' actually means "normalize to NFD".
int Result = CFStringCompare((__bridge CFStringRef)lhs,
(__bridge CFStringRef)rhs, kCFCompareNonliteral);
swift_unknownRelease(lhs);
swift_unknownRelease(rhs);
return Result;
}
extern "C" size_t
swift_stdlib_NSStringNFDHashValue(NSString *NS_RELEASES_ARGUMENT str) {
int Result = str.decomposedStringWithCanonicalMapping.hash;
swift_unknownRelease(str);
return Result;
}
// For strings we know only have ASCII
extern "C" size_t
swift_stdlib_NSStringASCIIHashValue(NSString *NS_RELEASES_ARGUMENT str) {
int Result = str.hash;
swift_unknownRelease(str);
return Result;
}
extern "C" bool swift_stdlib_NSStringHasPrefixNFD(NSString *theString,
NSString *prefix) {
auto Length = CFStringGetLength((__bridge CFStringRef)theString);
@@ -120,6 +145,20 @@ swift_stdlib_NSStringHasSuffixNFD(NSString *NS_RELEASES_ARGUMENT theString,
return Result;
}
extern "C" NS_RETURNS_RETAINED NSString *
swift_stdlib_NSStringLowercaseString(NSString *NS_RELEASES_ARGUMENT str) {
NSString *Result = objc_retain(str.lowercaseString);
swift_unknownRelease(str);
return Result;
}
extern "C" NS_RETURNS_RETAINED NSString *
swift_stdlib_NSStringUppercaseString(NSString *NS_RELEASES_ARGUMENT str) {
NSString *Result = objc_retain(str.uppercaseString);
swift_unknownRelease(str);
return Result;
}
extern "C" void swift_stdlib_CFSetGetValues(NSSet *NS_RELEASES_ARGUMENT set,
const void **values) {
CFSetGetValues((__bridge CFSetRef)set, values);

View File

@@ -1842,22 +1842,16 @@ NSStringAPIs.test("stringByApplyingTransform(_:reverse:)") {
struct ComparisonTest {
let expectedUnicodeCollation: ExpectedComparisonResult
let expectedHasPrefix: Bool
let expectedHasSuffix: Bool
let lhs: String
let rhs: String
let loc: SourceLoc
init(
hasPrefix: Bool,
hasSuffix: Bool,
_ expectedUnicodeCollation: ExpectedComparisonResult,
_ lhs: String, _ rhs: String,
file: String = __FILE__, line: UInt = __LINE__
) {
self.expectedUnicodeCollation = expectedUnicodeCollation
self.expectedHasPrefix = hasPrefix
self.expectedHasSuffix = hasSuffix
self.lhs = lhs
self.rhs = rhs
self.loc = SourceLoc(file, line, comment: "test data")
@@ -1865,23 +1859,18 @@ struct ComparisonTest {
}
let comparisonTests = [
ComparisonTest(hasPrefix: false, hasSuffix: false, .EQ, "", ""),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "", "a"),
ComparisonTest(.EQ, "", ""),
ComparisonTest(.LT, "", "a"),
// ASCII cases
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "t", "tt"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .GT, "tt", "t"),
// According to DUCET: a < A < b < B.
// This is NOT ascii order.
ComparisonTest(hasPrefix: false, hasSuffix: false, .GT, "T", "t"),
// According to DUCET \0 has no collation value and so is ignored in the
// ordering relation.
ComparisonTest(hasPrefix: false, hasSuffix: false, .EQ, "\u{0}", ""),
ComparisonTest(hasPrefix: false, hasSuffix: false, .EQ, "\u{1}", "\u{0}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "\r\n", "t"),
ComparisonTest(hasPrefix: false, hasSuffix: true, .GT, "\r\n", "\n"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .EQ, "\u{0}", "\u{0}\u{0}"),
ComparisonTest(hasPrefix: false, hasSuffix: true, .EQ, "\u{0}\u{0}", "\u{0}"),
ComparisonTest(.LT, "t", "tt"),
ComparisonTest(.GT, "t", "Tt"),
ComparisonTest(.GT, "\u{0}", ""),
ComparisonTest(.EQ, "\u{0}", "\u{0}"),
// Currently fails:
// ComparisonTest(.LT, "\r\n", "t"),
// ComparisonTest(.GT, "\r\n", "\n"),
// ComparisonTest(.LT, "\u{0}", "\u{0}\u{0}"),
// Whitespace
// U+000A LINE FEED (LF)
@@ -1890,60 +1879,59 @@ let comparisonTests = [
// U+0085 NEXT LINE (NEL)
// U+2028 LINE SEPARATOR
// U+2029 PARAGRAPH SEPARATOR
ComparisonTest(hasPrefix: false, hasSuffix: false, .GT, "\u{0085}", "\n"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .GT, "\u{000b}", "\n"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .GT, "\u{000c}", "\n"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .GT, "\u{2028}", "\n"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .GT, "\u{2029}", "\n"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .GT, "\r\n\r\n", "\r\n"),
ComparisonTest(.GT, "\u{0085}", "\n"),
ComparisonTest(.GT, "\u{000b}", "\n"),
ComparisonTest(.GT, "\u{000c}", "\n"),
ComparisonTest(.GT, "\u{2028}", "\n"),
ComparisonTest(.GT, "\u{2029}", "\n"),
ComparisonTest(.GT, "\r\n\r\n", "\r\n"),
// U+0301 COMBINING ACUTE ACCENT
// U+00E1 LATIN SMALL LETTER A WITH ACUTE
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "a\u{301}", "\u{e1}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "a", "a\u{301}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "a", "\u{e1}"),
ComparisonTest(.EQ, "a\u{301}", "\u{e1}"),
ComparisonTest(.LT, "a", "a\u{301}"),
ComparisonTest(.LT, "a", "\u{e1}"),
// U+304B HIRAGANA LETTER KA
// U+304C HIRAGANA LETTER GA
// U+3099 COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{304b}", "\u{304b}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{304c}", "\u{304c}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "\u{304b}", "\u{304c}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "\u{304b}", "\u{304c}\u{3099}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{304c}", "\u{304b}\u{3099}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "\u{304c}", "\u{304c}\u{3099}"),
ComparisonTest(.EQ, "\u{304b}", "\u{304b}"),
ComparisonTest(.EQ, "\u{304c}", "\u{304c}"),
ComparisonTest(.LT, "\u{304b}", "\u{304c}"),
ComparisonTest(.LT, "\u{304b}", "\u{304c}\u{3099}"),
ComparisonTest(.EQ, "\u{304c}", "\u{304b}\u{3099}"),
ComparisonTest(.LT, "\u{304c}", "\u{304c}\u{3099}"),
// U+212B ANGSTROM SIGN
// U+030A COMBINING RING ABOVE
// U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{212b}", "A\u{30a}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{212b}", "\u{c5}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "A\u{30a}", "\u{c5}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .GT, "A\u{30a}", "a"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "A", "A\u{30a}"),
ComparisonTest(.EQ, "\u{212b}", "A\u{30a}"),
ComparisonTest(.EQ, "\u{212b}", "\u{c5}"),
ComparisonTest(.EQ, "A\u{30a}", "\u{c5}"),
ComparisonTest(.LT, "A\u{30a}", "a"),
ComparisonTest(.LT, "A", "A\u{30a}"),
// U+2126 OHM SIGN
// U+03A9 GREEK CAPITAL LETTER OMEGA
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{2126}", "\u{03a9}"),
ComparisonTest(.EQ, "\u{2126}", "\u{03a9}"),
// U+0323 COMBINING DOT BELOW
// U+0307 COMBINING DOT ABOVE
// U+1E63 LATIN SMALL LETTER S WITH DOT BELOW
// U+1E69 LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{1e69}", "s\u{323}\u{307}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{1e69}", "s\u{307}\u{323}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{1e69}", "\u{1e63}\u{307}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{1e63}", "s\u{323}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{1e63}\u{307}", "s\u{323}\u{307}"),
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{1e63}\u{307}", "s\u{307}\u{323}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "s\u{323}", "\u{1e69}"),
ComparisonTest(.EQ, "\u{1e69}", "s\u{323}\u{307}"),
ComparisonTest(.EQ, "\u{1e69}", "s\u{307}\u{323}"),
ComparisonTest(.EQ, "\u{1e69}", "\u{1e63}\u{307}"),
ComparisonTest(.EQ, "\u{1e63}", "s\u{323}"),
ComparisonTest(.EQ, "\u{1e63}\u{307}", "s\u{323}\u{307}"),
ComparisonTest(.EQ, "\u{1e63}\u{307}", "s\u{307}\u{323}"),
ComparisonTest(.LT, "s\u{323}", "\u{1e69}"),
// U+FB01 LATIN SMALL LIGATURE FI
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{fb01}", "\u{fb01}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .LT, "fi", "\u{fb01}"),
ComparisonTest(.EQ, "\u{fb01}", "\u{fb01}"),
ComparisonTest(.LT, "fi", "\u{fb01}"),
// We don't perform Unicode collation in semi-stable mode.
// Test that Unicode collation is performed in deterministic mode.
//
// U+0301 COMBINING ACUTE ACCENT
// U+0341 COMBINING ACUTE TONE MARK
@@ -1956,31 +1944,16 @@ let comparisonTests = [
//
// U+0301 and U+0954 don't decompose in the canonical decomposition mapping.
// U+0341 has a canonical decomposition mapping of U+0301.
ComparisonTest(hasPrefix: true, hasSuffix: true, .EQ, "\u{0301}", "\u{0341}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .EQ, "\u{0301}", "\u{0954}"),
ComparisonTest(hasPrefix: false, hasSuffix: false, .EQ, "\u{0341}", "\u{0954}"),
ComparisonTest(.EQ, "\u{0301}", "\u{0341}"),
ComparisonTest(.LT, "\u{0301}", "\u{0954}"),
ComparisonTest(.LT, "\u{0341}", "\u{0954}"),
]
func forceUTF16String(var str: String) -> String {
if str._core.isASCII {
str.insert("\u{0130}", atIndex: str.startIndex)
str.removeAtIndex(str.startIndex)
}
return str
}
func toCocoaBackedString(str: String) -> String {
let utf16String = forceUTF16String(str)
return String(NSString(
characters: utf16String._core.startUTF16,
length: utf16String._core.count))
}
func checkStringComparisons(
expected: ExpectedComparisonResult, _ lhs: String, _ rhs: String,
_ stackTrace: SourceLocStack
func checkStringComparison(
expected: ExpectedComparisonResult,
_ lhs: String, _ rhs: String, _ stackTrace: SourceLocStack
) {
// String / String
expectEqual(expected.isEQ(), lhs == rhs, stackTrace: stackTrace)
expectEqual(expected.isNE(), lhs != rhs, stackTrace: stackTrace)
checkHashable(
@@ -1991,14 +1964,6 @@ func checkStringComparisons(
expectEqual(expected.isGE(), lhs >= rhs, stackTrace: stackTrace)
expectEqual(expected.isGT(), lhs > rhs, stackTrace: stackTrace)
checkComparable(expected, lhs, rhs, stackTrace: stackTrace.withCurrentLoc())
}
func checkStringComparison(
expected: ExpectedComparisonResult,
_ lhs: String, _ rhs: String, _ stackTrace: SourceLocStack
) {
// String / String
checkStringComparisons(expected, lhs, rhs, stackTrace.withCurrentLoc())
// NSString / NSString
let lhsNSString = lhs as NSString
@@ -2015,20 +1980,6 @@ func checkStringComparison(
checkHashable(
expectedEqualUnicodeScalars, lhsNSString, rhsNSString,
stackTrace: stackTrace.withCurrentLoc())
// Test cocoa backed Strings
let lhsWithCocoaBuffer = toCocoaBackedString(lhs)
let rhsWithCocoaBuffer = toCocoaBackedString(rhs)
// String (cocoa) / String (cocoa)
checkStringComparisons(expected, lhsWithCocoaBuffer, rhsWithCocoaBuffer,
stackTrace.withCurrentLoc())
// String / String (cocoa)
checkStringComparisons(expected, lhs, rhsWithCocoaBuffer,
stackTrace.withCurrentLoc())
// String (cocoa) / String
checkStringComparisons(expected, lhsWithCocoaBuffer, rhs,
stackTrace.withCurrentLoc())
}
NSStringAPIs.test("String.{Equatable,Hashable,Comparable}") {
@@ -2075,8 +2026,7 @@ NSStringAPIs.test("Character.{Equatable,Hashable,Comparable}") {
}
func checkHasPrefixHasSuffix(
expectedHasPrefix: Bool, _ expectedHasSuffix: Bool, _ lhs: String,
_ rhs: String, _ stackTrace: SourceLocStack
lhs: String, _ rhs: String, _ stackTrace: SourceLocStack
) {
if lhs == "" {
return
@@ -2087,18 +2037,49 @@ func checkHasPrefixHasSuffix(
return
}
expectEqual(expectedHasPrefix, lhs.hasPrefix(rhs), stackTrace: stackTrace)
// To determine the expected results, compare grapheme clusters,
// scalar-to-scalar, of the NFD form of the strings.
let lhsNFDGraphemeClusters =
lhs.decomposedStringWithCanonicalMapping.characters.map {
Array(String($0).unicodeScalars)
}
let rhsNFDGraphemeClusters =
rhs.decomposedStringWithCanonicalMapping.characters.map {
Array(String($0).unicodeScalars)
}
let expectHasPrefix = lhsNFDGraphemeClusters.startsWith(
rhsNFDGraphemeClusters, isEquivalent: (==))
let expectHasSuffix =
lhsNFDGraphemeClusters.lazy.reverse().startsWith(
rhsNFDGraphemeClusters.lazy.reverse(), isEquivalent: (==))
expectEqual(expectHasPrefix, lhs.hasPrefix(rhs), stackTrace: stackTrace)
expectEqual(
expectedHasPrefix, (lhs + "abc").hasPrefix(rhs), stackTrace: stackTrace)
expectEqual(expectedHasSuffix, lhs.hasSuffix(rhs), stackTrace: stackTrace)
expectHasPrefix, (lhs + "abc").hasPrefix(rhs), stackTrace: stackTrace)
expectEqual(expectHasSuffix, lhs.hasSuffix(rhs), stackTrace: stackTrace)
expectEqual(
expectedHasSuffix, ("abc" + lhs).hasSuffix(rhs), stackTrace: stackTrace)
expectHasSuffix, ("abc" + lhs).hasSuffix(rhs), stackTrace: stackTrace)
}
NSStringAPIs.test("hasPrefix,hasSuffix") {
for test in comparisonTests {
checkHasPrefixHasSuffix(test.expectedHasPrefix, test.expectedHasSuffix,
test.lhs, test.rhs, test.loc.withCurrentLoc())
checkHasPrefixHasSuffix(test.lhs, test.rhs, test.loc.withCurrentLoc())
checkHasPrefixHasSuffix(test.rhs, test.lhs, test.loc.withCurrentLoc())
}
}
NSStringAPIs.test("Failures{hasPrefix,hasSuffix}-CF")
.xfail(.Custom({ true }, reason: "rdar://problem/19034601")).code {
let test = ComparisonTest(.LT, "\u{0}", "\u{0}\u{0}")
checkHasPrefixHasSuffix(test.lhs, test.rhs, test.loc.withCurrentLoc())
}
NSStringAPIs.test("Failures{hasPrefix,hasSuffix}")
.xfail(.Custom({ true }, reason: "blocked on rdar://problem/19036555")).code {
let tests =
[ComparisonTest(.LT, "\r\n", "t"), ComparisonTest(.GT, "\r\n", "\n")]
tests.forEach {
checkHasPrefixHasSuffix($0.lhs, $0.rhs, $0.loc.withCurrentLoc())
}
}

View File

@@ -34,38 +34,38 @@ Reflection.test("Dictionary") {
var expected = ""
expected += "▿ 5 key/value pairs\n"
expected += " ▿ [0]: (2 elements)\n"
expected += " - .0: Five\n"
expected += " - .1: 5\n"
expected += " ▿ [1]: (2 elements)\n"
expected += " - .0: Two\n"
expected += " - .1: 2\n"
expected += " ▿ [2]: (2 elements)\n"
expected += " - .0: One\n"
expected += " - .1: 1\n"
expected += " ▿ [3]: (2 elements)\n"
expected += " - .0: Three\n"
expected += " - .1: 3\n"
expected += " ▿ [4]: (2 elements)\n"
expected += " - .0: Four\n"
expected += " - .1: 4\n"
expected += " ▿ [1]: (2 elements)\n"
expected += " - .0: One\n"
expected += " - .1: 1\n"
expected += " ▿ [2]: (2 elements)\n"
expected += " - .0: Two\n"
expected += " - .1: 2\n"
expected += " ▿ [3]: (2 elements)\n"
expected += " - .0: Five\n"
expected += " - .1: 5\n"
expected += " ▿ [4]: (2 elements)\n"
expected += " - .0: Three\n"
expected += " - .1: 3\n"
#elseif arch(x86_64) || arch(arm64)
var expected = ""
expected += "▿ 5 key/value pairs\n"
expected += " ▿ [0]: (2 elements)\n"
expected += " - .0: One\n"
expected += " - .1: 1\n"
expected += " ▿ [1]: (2 elements)\n"
expected += " - .0: Three\n"
expected += " - .1: 3\n"
expected += " ▿ [2]: (2 elements)\n"
expected += " - .0: Four\n"
expected += " - .1: 4\n"
expected += " ▿ [3]: (2 elements)\n"
expected += " - .0: Five\n"
expected += " - .1: 5\n"
expected += " ▿ [4]: (2 elements)\n"
expected += " ▿ [1]: (2 elements)\n"
expected += " - .0: Two\n"
expected += " - .1: 2\n"
expected += " ▿ [2]: (2 elements)\n"
expected += " - .0: One\n"
expected += " - .1: 1\n"
expected += " ▿ [3]: (2 elements)\n"
expected += " - .0: Three\n"
expected += " - .1: 3\n"
expected += " ▿ [4]: (2 elements)\n"
expected += " - .0: Four\n"
expected += " - .1: 4\n"
#else
fatalError("unipmelemented")
#endif

View File

@@ -920,6 +920,39 @@ var nsStringCanaryCount = 0
}
}
RuntimeFoundationWrappers.test(
"_stdlib_compareNSStringDeterministicUnicodeCollation/NoLeak"
) {
nsStringCanaryCount = 0
autoreleasepool {
let a = NSStringCanary()
let b = NSStringCanary()
expectEqual(2, nsStringCanaryCount)
_stdlib_compareNSStringDeterministicUnicodeCollation(a, b)
}
expectEqual(0, nsStringCanaryCount)
}
RuntimeFoundationWrappers.test("_stdlib_NSStringNFDHashValue/NoLeak") {
nsStringCanaryCount = 0
autoreleasepool {
let a = NSStringCanary()
expectEqual(1, nsStringCanaryCount)
_stdlib_NSStringNFDHashValue(a)
}
expectEqual(0, nsStringCanaryCount)
}
RuntimeFoundationWrappers.test("_stdlib_NSStringASCIIHashValue/NoLeak") {
nsStringCanaryCount = 0
autoreleasepool {
let a = NSStringCanary()
expectEqual(1, nsStringCanaryCount)
_stdlib_NSStringASCIIHashValue(a)
}
expectEqual(0, nsStringCanaryCount)
}
RuntimeFoundationWrappers.test("_stdlib_NSStringHasPrefixNFD/NoLeak") {
nsStringCanaryCount = 0
autoreleasepool {
@@ -942,6 +975,26 @@ RuntimeFoundationWrappers.test("_stdlib_NSStringHasSuffixNFD/NoLeak") {
expectEqual(0, nsStringCanaryCount)
}
RuntimeFoundationWrappers.test("_stdlib_NSStringLowercaseString/NoLeak") {
nsStringCanaryCount = 0
autoreleasepool {
let a = NSStringCanary()
expectEqual(1, nsStringCanaryCount)
_stdlib_NSStringLowercaseString(a)
}
expectEqual(0, nsStringCanaryCount)
}
RuntimeFoundationWrappers.test("_stdlib_NSStringUppercaseString/NoLeak") {
nsStringCanaryCount = 0
autoreleasepool {
let a = NSStringCanary()
expectEqual(1, nsStringCanaryCount)
_stdlib_NSStringUppercaseString(a)
}
expectEqual(0, nsStringCanaryCount)
}
RuntimeFoundationWrappers.test("_stdlib_CFStringCreateCopy/NoLeak") {
nsStringCanaryCount = 0
autoreleasepool {

View File

@@ -28,10 +28,12 @@ Algorithm.test("min,max") {
// condition.
}
Algorithm.test("sorted/strings") {
Algorithm.test("sorted/strings")
.xfail(.LinuxAny(reason: "String comparison: ICU vs. Foundation"))
.code {
expectEqual(
[ "apple", "Banana", "cherry" ],
[ "cherry", "Banana", "apple" ].sort())
[ "Banana", "apple", "cherry" ],
[ "apple", "Banana", "cherry" ].sort())
let s = ["apple", "Banana", "cherry"].sort() {
$0.characters.count > $1.characters.count

View File

@@ -888,9 +888,9 @@ StringTests.test("Conversions") {
StringTests.test(
"forall x: Int8, y: Int8 . x < 128 ==> x <ascii y == x <unicode y") {
let asciiDomain = (0..<128).map({ String(UnicodeScalar($0)) })
expectEqualFunctionsForDomain2(
expectEqualMethodsForDomain(
asciiDomain, asciiDomain,
_compareDeterministicUnicodeCollation, _compareString)
String._compareDeterministicUnicodeCollation, String._compareASCII)
}
StringTests.test("lowercaseString") {