diff --git a/Sources/Foundation/NSCFString.swift b/Sources/Foundation/NSCFString.swift index 40ee579f42..bcc8eb3402 100644 --- a/Sources/Foundation/NSCFString.swift +++ b/Sources/Foundation/NSCFString.swift @@ -154,19 +154,24 @@ internal func _CFSwiftStringGetBytes(_ str: AnyObject, encoding: CFStringEncodin // TODO: Don't treat many encodings like they are UTF8 case CFStringEncoding(kCFStringEncodingUTF8), CFStringEncoding(kCFStringEncodingISOLatin1), CFStringEncoding(kCFStringEncodingMacRoman), CFStringEncoding(kCFStringEncodingASCII), CFStringEncoding(kCFStringEncodingNonLossyASCII): let encodingView = (str as! NSString).substring(with: NSRange(range)).utf8 + var converted = 0 if let buffer = buffer { for (idx, character) in encodingView.enumerated() { + if idx >= maxBufLen { break } buffer.advanced(by: idx).initialize(to: character) + converted += 1 } } - usedBufLen?.pointee = encodingView.count - convertedLength = encodingView.count + usedBufLen?.pointee = converted + convertedLength = converted case CFStringEncoding(kCFStringEncodingUTF16): let encodingView = (str as! NSString)._swiftObject.utf16 let start = encodingView.startIndex + var converted = 0 if let buffer = buffer { for idx in 0..= maxBufLen { break } // Since character is 2 bytes but the buffer is in term of 1 byte values, we have to split it up let character = encodingView[encodingView.index(start, offsetBy: idx + range.location)] #if _endian(big) @@ -178,11 +183,12 @@ internal func _CFSwiftStringGetBytes(_ str: AnyObject, encoding: CFStringEncodin #endif buffer.advanced(by: idx * 2).initialize(to: byte0) buffer.advanced(by: (idx * 2) + 1).initialize(to: byte1) + converted += 1 } } // Every character was 2 bytes - usedBufLen?.pointee = range.length * 2 - convertedLength = range.length + usedBufLen?.pointee = converted * 2 + convertedLength = converted default: fatalError("Attempted to get bytes of a Swift string using an unsupported encoding") diff --git a/Tests/Foundation/TestPropertyListSerialization.swift b/Tests/Foundation/TestPropertyListSerialization.swift index c01e0daf1c..d7ddb31fdc 100644 --- a/Tests/Foundation/TestPropertyListSerialization.swift +++ b/Tests/Foundation/TestPropertyListSerialization.swift @@ -7,6 +7,8 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +import CoreFoundation + class TestPropertyListSerialization : XCTestCase { func test_BasicConstruction() { let dict = NSMutableDictionary(capacity: 0) @@ -80,4 +82,33 @@ class TestPropertyListSerialization : XCTestCase { XCTAssertEqual(nserror.userInfo[NSDebugDescriptionErrorKey] as? String, "Cannot parse a NULL or zero-length data") } } + + func test_decodeOverflowUnicodeString() throws { + var native = "ФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФ" + let ns = native.withCString { + NSString(utf8String: $0)! + } + do { + var buffer = Array(repeating: 1, count: native.utf8.count) + buffer.withUnsafeMutableBufferPointer { bufferPtr in + var used: CFIndex = 0 + CFStringGetBytes(unsafeBitCast(ns, to: CFString.self), CFRangeMake(0, ns.length), CFStringBuiltInEncodings.UTF8.rawValue, 0xF, false, bufferPtr.baseAddress!, 10, &used) + XCTAssertEqual(used, 10) + } + for i in 10 ..< buffer.count { + XCTAssertEqual(buffer[i], 1) + } + } + do { + var buffer = Array(repeating: 1, count: native.utf8.count) + buffer.withUnsafeMutableBufferPointer { bufferPtr in + var used: CFIndex = 0 + CFStringGetBytes(unsafeBitCast(ns, to: CFString.self), CFRangeMake(0, ns.length), CFStringBuiltInEncodings.UTF16.rawValue, 0xF, false, bufferPtr.baseAddress!, 10, &used) + XCTAssertEqual(used, 10) + } + for i in 10 ..< buffer.count { + XCTAssertEqual(buffer[i], 1) + } + } + } }