Skip to content

Parity: NSCoding: NSCharacterSet #2233

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 3 commits into from
May 10, 2019
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
3 changes: 3 additions & 0 deletions CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include <CoreFoundation/CFURLSessionInterface.h>
#include <CoreFoundation/CFDateIntervalFormatter.h>
#include <CoreFoundation/ForFoundationOnly.h>
#include <CoreFoundation/CFCharacterSetPriv.h>

#if DEPLOYMENT_TARGET_WINDOWS
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
Expand Down Expand Up @@ -402,6 +404,7 @@ CF_CROSS_PLATFORM_EXPORT int _CFThreadGetName(char *_Nonnull buf, int length);
CF_EXPORT Boolean _CFCharacterSetIsLongCharacterMember(CFCharacterSetRef theSet, UTF32Char theChar);
CF_EXPORT CFCharacterSetRef _CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet);
CF_EXPORT CFMutableCharacterSetRef _CFCharacterSetCreateMutableCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet);
CF_CROSS_PLATFORM_EXPORT void _CFCharacterSetInitCopyingSet(CFAllocatorRef alloc, CFMutableCharacterSetRef cset, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses);

CF_EXPORT _Nullable CFErrorRef CFReadStreamCopyError(CFReadStreamRef _Null_unspecified stream);

Expand Down
1 change: 1 addition & 0 deletions CoreFoundation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ add_framework(CoreFoundation
Parsing.subproj/CFXMLInterface.h
PlugIn.subproj/CFBundlePriv.h
Stream.subproj/CFStreamPriv.h
String.subproj/CFCharacterSetPriv.h
String.subproj/CFRegularExpression.h
String.subproj/CFRunArray.h
StringEncodings.subproj/CFStringEncodingConverter.h
Expand Down
16 changes: 11 additions & 5 deletions CoreFoundation/String.subproj/CFCharacterSet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1618,21 +1618,28 @@ CFMutableCharacterSetRef CFCharacterSetCreateMutable(CFAllocatorRef allocator) {
return cset;
}

CF_CROSS_PLATFORM_EXPORT void _CFCharacterSetInitCopyingSet(CFAllocatorRef alloc, CFMutableCharacterSetRef cset, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses);

static CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses) {
CFMutableCharacterSetRef cset;

if (validateSubclasses) {
CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCharacterSet, CFMutableCharacterSetRef , (NSCharacterSet *)theSet, mutableCopy);
CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFCharacterSet, CFMutableCharacterSetRef, (CFSwiftRef)theSet, NSCharacterSet.mutableCopy);

__CFGenericValidateType(theSet, _kCFRuntimeIDCFCharacterSet);
}

if (!isMutable && !__CFCSetIsMutable(theSet)) {
return (CFMutableCharacterSetRef)CFRetain(theSet);
}

cset = CFCharacterSetCreateMutable(alloc);
_CFCharacterSetInitCopyingSet(alloc, cset, theSet, isMutable, validateSubclasses);
return cset;
}

CF_CROSS_PLATFORM_EXPORT void _CFCharacterSetInitCopyingSet(CFAllocatorRef alloc, CFMutableCharacterSetRef cset, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses) {

__CFCSetPutClassType(cset, __CFCSetClassType(theSet));
__CFCSetPutHasHashValue(cset, __CFCSetHasHashValue(theSet));
Expand Down Expand Up @@ -1704,10 +1711,9 @@ static CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc,
} else if (__CFCSetAnnexIsInverted(theSet)) {
__CFCSetAnnexSetIsInverted(cset, true);
}

return cset;
}


CFCharacterSetRef CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet) {
return __CFCharacterSetCreateCopy(alloc, theSet, false, true);
}
Expand Down
188 changes: 179 additions & 9 deletions Foundation/NSCharacterSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import CoreFoundation

#if os(macOS) || os(iOS)
#if _runtime(_ObjC) // if objc_enum works, restore the old names:
let kCFCharacterSetControl = CFCharacterSetPredefinedSet.control
let kCFCharacterSetWhitespace = CFCharacterSetPredefinedSet.whitespace
let kCFCharacterSetWhitespaceAndNewline = CFCharacterSetPredefinedSet.whitespaceAndNewline
Expand All @@ -26,10 +26,52 @@ let kCFCharacterSetCapitalizedLetter = CFCharacterSetPredefinedSet.capitalizedLe
let kCFCharacterSetSymbol = CFCharacterSetPredefinedSet.symbol
let kCFCharacterSetNewline = CFCharacterSetPredefinedSet.newline
let kCFCharacterSetIllegal = CFCharacterSetPredefinedSet.illegal

let kCFCharacterSetKeyedCodingTypeBitmap = CFCharacterSetKeyedCodingType.bitmap
let kCFCharacterSetKeyedCodingTypeBuiltin = CFCharacterSetKeyedCodingType.builtin
let kCFCharacterSetKeyedCodingTypeRange = CFCharacterSetKeyedCodingType.range
let kCFCharacterSetKeyedCodingTypeString = CFCharacterSetKeyedCodingType.string
let kCFCharacterSetKeyedCodingTypeBuiltinAndBitmap = CFCharacterSetKeyedCodingType.builtinAndBitmap
#endif

#if _runtime(_ObjC)
fileprivate let lastKnownPredefinedCharacterSetConstant = kCFCharacterSetNewline.rawValue

fileprivate extension Int {
init(_ predefinedSet: CFCharacterSetPredefinedSet) {
self = predefinedSet.rawValue
}
}
#else
fileprivate let lastKnownPredefinedCharacterSetConstant = kCFCharacterSetNewline
#endif

fileprivate func knownPredefinedCharacterSet(rawValue: Int) -> CFCharacterSetPredefinedSet? {
if rawValue > 0 && rawValue <= lastKnownPredefinedCharacterSetConstant {
#if _runtime(_ObjC)
return CFCharacterSetPredefinedSet(rawValue: rawValue)
#else
return CFCharacterSetPredefinedSet(rawValue)
#endif
} else {
return nil
}
}

open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
fileprivate extension String {
static let characterSetBitmapRepresentationKey = "NSBitmap"
static let characterSetAltBitmapRepresentationKey = "NSBitmapObject"
static let characterSetStringKey = "NSString"
static let characterSetAltStringKey = "NSStringObject"
static let characterSetRangeKey = "NSRange"
static let characterSetBuiltinIDKey = "NSBuiltinID"
static let characterSetNewBuiltinIDKey = "NSBuiltinID2"
static let characterSetIsInvertedKey = "NSIsInverted"
static let characterSetNewIsInvertedKey = "NSIsInverted2"
}


open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
typealias CFType = CFCharacterSet
private var _base = _CFInfo(typeID: CFCharacterSetGetTypeID())
private var _hashValue = CFHashCode(0)
Expand Down Expand Up @@ -162,9 +204,141 @@ open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
}
}

public convenience required init(coder aDecoder: NSCoder) {
self.init(charactersIn: "")
NSUnimplemented()
open class var supportsSecureCoding: Bool { return true }

public required init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
fatalError("Decoding requires a NSCoder that allows keyed coding.")
}

func fail(code: CocoaError.Code = .coderReadCorrupt, _ message: String) {
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: code.rawValue, userInfo: [ NSLocalizedDescriptionKey: message ]))
}

let finalSet: CFCharacterSet

var builtinType = aDecoder.decodeInteger(forKey: .characterSetBuiltinIDKey)
if builtinType == 0 {
builtinType = aDecoder.decodeInteger(forKey: .characterSetNewBuiltinIDKey)
}

let isInverted = aDecoder.decodeBool(forKey: .characterSetIsInvertedKey)

let keyedUnarchiver = aDecoder as? NSKeyedUnarchiver
let containsCharacterSetStringKeyName = aDecoder.containsValue(forKey: .characterSetStringKey)
let containsCharacterSetStringAltKeyName = aDecoder.containsValue(forKey: .characterSetAltStringKey)

if let predefinedSet = knownPredefinedCharacterSet(rawValue: builtinType) {
guard let theSet = CFCharacterSetGetPredefined(predefinedSet) else {
fail("CFCharacterSetGetPredefined -- Predefined Character Set not found")
return nil
}

finalSet = theSet
} else if aDecoder.containsValue(forKey: .characterSetRangeKey) {
let range = UInt64(bitPattern: aDecoder.decodeInt64(forKey: .characterSetRangeKey))
guard let theSet = CFCharacterSetCreateWithCharactersInRange(kCFAllocatorSystemDefault, CFRangeMake(CFIndex(range >> 32), CFIndex(range & 0xFFFFFFFF))) else {
fail("CFCharacterSetCreateWithCharactersInRange -- Character Set creation with characters in range failed")
return nil
}

finalSet = theSet
} else if containsCharacterSetStringKeyName || containsCharacterSetStringAltKeyName {
let maybeResult: NSString?

if let keyedUnarchiver = keyedUnarchiver, containsCharacterSetStringKeyName {
maybeResult = keyedUnarchiver._decodePropertyListForKey(.characterSetStringKey) as? NSString
} else {
maybeResult = aDecoder.decodeObject(of: NSString.self, forKey: .characterSetAltStringKey)
}

guard let result = maybeResult else {
fail(code: .coderValueNotFound, "Decoder value not found")
return nil
}

guard let theSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorSystemDefault, result._cfObject) else {
fail("CFCharacterSetCreateWithCharactersInString -- Character Set creation with characters in string failed")
return nil
}

finalSet = theSet
} else {
let maybeResult: NSData?

if let keyedUnarchiver = keyedUnarchiver, keyedUnarchiver.containsValue(forKey: .characterSetBitmapRepresentationKey) {
maybeResult = keyedUnarchiver._decodePropertyListForKey(.characterSetBitmapRepresentationKey) as? NSData
} else {
maybeResult = aDecoder.decodeObject(of: NSData.self, forKey: .characterSetAltBitmapRepresentationKey)
}

guard let result = maybeResult else {
fail(code: .coderValueNotFound, "Decoder value not found")
return nil
}

guard let theSet = CFCharacterSetCreateWithBitmapRepresentation(kCFAllocatorSystemDefault, result._cfObject) else {
fail("CFCharacterSetCreateWithBitmapRepresentation -- Character Set creation with bitmap representation failed")
return nil
}

finalSet = theSet
}

super.init()
_CFCharacterSetInitCopyingSet(kCFAllocatorSystemDefault, _cfMutableObject, finalSet, type(of: self) is NSMutableCharacterSet.Type, false)
if isInverted {
_CFCharacterSetSetIsInverted(_cfMutableObject, isInverted)
}
}

public func encode(with aCoder: NSCoder) {
guard aCoder.allowsKeyedCoding else {
fatalError("Encoding requires a NSCoder that allows keyed coding.")
}

var isInverted = _CFCharacterSetIsInverted(_cfObject)
let keyedArchiver = aCoder as? NSKeyedArchiver

switch _CFCharacterSetGetKeyedCodingType(_cfObject) {
case kCFCharacterSetKeyedCodingTypeBuiltin:
aCoder.encode(Int(_CFCharacterSetGetKeyedCodingBuiltinType(_cfObject)), forKey: .characterSetBuiltinIDKey)

case kCFCharacterSetKeyedCodingTypeRange:
let range = _CFCharacterSetGetKeyedCodingRange(_cfObject)

var value = UInt64(range.location)
value <<= 32
value |= UInt64(range.length)

aCoder.encode(Int64(bitPattern: value), forKey: .characterSetRangeKey)

case kCFCharacterSetKeyedCodingTypeString:
let string = _CFCharacterSetCreateKeyedCodingString(_cfObject).takeRetainedValue()
if let keyedArchiver = keyedArchiver {
keyedArchiver._encodePropertyList(string._nsObject, forKey: .characterSetStringKey)
} else {
aCoder.encode(string._nsObject, forKey: .characterSetAltStringKey)
}

case kCFCharacterSetKeyedCodingTypeBuiltinAndBitmap:
aCoder.encode(Int(_CFCharacterSetGetKeyedCodingBuiltinType(_cfObject)), forKey: .characterSetNewBuiltinIDKey)
if isInverted { aCoder.encode(true, forKey: .characterSetNewIsInvertedKey )}

fallthrough
default:
if let keyedArchiver = keyedArchiver {
keyedArchiver._encodePropertyList(bitmapRepresentation._nsObject, forKey: .characterSetBitmapRepresentationKey)
} else {
aCoder.encode(bitmapRepresentation._nsObject, forKey: .characterSetAltBitmapRepresentationKey)
}

isInverted = false
}

if isInverted {
aCoder.encode(true, forKey: .characterSetIsInvertedKey)
}
}

open func characterIsMember(_ aCharacter: unichar) -> Bool {
Expand Down Expand Up @@ -226,10 +400,6 @@ open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
NSRequiresConcreteImplementation()
}
}

open func encode(with aCoder: NSCoder) {
NSUnimplemented()
}
}

open class NSMutableCharacterSet : NSCharacterSet {
Expand Down
28 changes: 28 additions & 0 deletions TestFoundation/FixtureValues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,29 @@ enum Fixtures {
return NSMutableSet()
}

// ===== NSCharacterSet, NSMutableCharacterSet =====

static let characterSetEmpty = TypedFixture<NSCharacterSet>("NSCharacterSet-Empty") {
return NSCharacterSet()
}

static let characterSetRange = TypedFixture<NSCharacterSet>("NSCharacterSet-Range") {
return NSCharacterSet(range: NSMakeRange(0, 255))
}

static let characterSetString = TypedFixture<NSCharacterSet>("NSCharacterSet-String") {
return NSCharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyz")
}

static let characterSetBitmap = TypedFixture<NSCharacterSet>("NSCharacterSet-Bitmap") {
let someSet = NSCharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyz")
return NSCharacterSet(bitmapRepresentation: someSet.bitmapRepresentation)
}

static let characterSetBuiltin = TypedFixture<NSCharacterSet>("NSCharacterSet-Builtin") {
return NSCharacterSet.alphanumerics as NSCharacterSet
}

// ===== Fixture list =====

static let _listOfAllFixtures: [AnyFixture] = [
Expand All @@ -245,6 +268,11 @@ enum Fixtures {
AnyFixture(Fixtures.setEmpty),
AnyFixture(Fixtures.mutableSetOfNumbers),
AnyFixture(Fixtures.mutableSetEmpty),
AnyFixture(Fixtures.characterSetEmpty),
AnyFixture(Fixtures.characterSetRange),
AnyFixture(Fixtures.characterSetString),
AnyFixture(Fixtures.characterSetBitmap),
AnyFixture(Fixtures.characterSetBuiltin),
]

// This ensures that we do not have fixtures with duplicate identifiers:
Expand Down
Loading