Skip to content

Commit a3ab9e1

Browse files
authored
Merge pull request #2233 from millenomi/nscharacterset-nscoding
Parity: NSCoding: NSCharacterSet
2 parents dd396f1 + 397b030 commit a3ab9e1

File tree

6 files changed

+266
-41
lines changed

6 files changed

+266
-41
lines changed

CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include <CoreFoundation/CFURLSessionInterface.h>
2727
#include <CoreFoundation/CFDateIntervalFormatter.h>
2828
#include <CoreFoundation/ForFoundationOnly.h>
29+
#include <CoreFoundation/CFCharacterSetPriv.h>
30+
2931
#if DEPLOYMENT_TARGET_WINDOWS
3032
#define NOMINMAX
3133
#define WIN32_LEAN_AND_MEAN
@@ -403,6 +405,7 @@ CF_CROSS_PLATFORM_EXPORT int _CFThreadGetName(char *_Nonnull buf, int length);
403405
CF_EXPORT Boolean _CFCharacterSetIsLongCharacterMember(CFCharacterSetRef theSet, UTF32Char theChar);
404406
CF_EXPORT CFCharacterSetRef _CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet);
405407
CF_EXPORT CFMutableCharacterSetRef _CFCharacterSetCreateMutableCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet);
408+
CF_CROSS_PLATFORM_EXPORT void _CFCharacterSetInitCopyingSet(CFAllocatorRef alloc, CFMutableCharacterSetRef cset, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses);
406409

407410
CF_EXPORT _Nullable CFErrorRef CFReadStreamCopyError(CFReadStreamRef _Null_unspecified stream);
408411

CoreFoundation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ add_framework(CoreFoundation
139139
Parsing.subproj/CFXMLInterface.h
140140
PlugIn.subproj/CFBundlePriv.h
141141
Stream.subproj/CFStreamPriv.h
142+
String.subproj/CFCharacterSetPriv.h
142143
String.subproj/CFRegularExpression.h
143144
String.subproj/CFRunArray.h
144145
StringEncodings.subproj/CFStringEncodingConverter.h

CoreFoundation/String.subproj/CFCharacterSet.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,21 +1618,28 @@ CFMutableCharacterSetRef CFCharacterSetCreateMutable(CFAllocatorRef allocator) {
16181618
return cset;
16191619
}
16201620

1621+
CF_CROSS_PLATFORM_EXPORT void _CFCharacterSetInitCopyingSet(CFAllocatorRef alloc, CFMutableCharacterSetRef cset, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses);
1622+
16211623
static CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses) {
16221624
CFMutableCharacterSetRef cset;
1623-
1625+
16241626
if (validateSubclasses) {
16251627
CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCharacterSet, CFMutableCharacterSetRef , (NSCharacterSet *)theSet, mutableCopy);
16261628
CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFCharacterSet, CFMutableCharacterSetRef, (CFSwiftRef)theSet, NSCharacterSet.mutableCopy);
16271629

16281630
__CFGenericValidateType(theSet, _kCFRuntimeIDCFCharacterSet);
16291631
}
1630-
1632+
16311633
if (!isMutable && !__CFCSetIsMutable(theSet)) {
16321634
return (CFMutableCharacterSetRef)CFRetain(theSet);
16331635
}
1634-
1636+
16351637
cset = CFCharacterSetCreateMutable(alloc);
1638+
_CFCharacterSetInitCopyingSet(alloc, cset, theSet, isMutable, validateSubclasses);
1639+
return cset;
1640+
}
1641+
1642+
CF_CROSS_PLATFORM_EXPORT void _CFCharacterSetInitCopyingSet(CFAllocatorRef alloc, CFMutableCharacterSetRef cset, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses) {
16361643

16371644
__CFCSetPutClassType(cset, __CFCSetClassType(theSet));
16381645
__CFCSetPutHasHashValue(cset, __CFCSetHasHashValue(theSet));
@@ -1704,10 +1711,9 @@ static CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc,
17041711
} else if (__CFCSetAnnexIsInverted(theSet)) {
17051712
__CFCSetAnnexSetIsInverted(cset, true);
17061713
}
1707-
1708-
return cset;
17091714
}
17101715

1716+
17111717
CFCharacterSetRef CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet) {
17121718
return __CFCharacterSetCreateCopy(alloc, theSet, false, true);
17131719
}

Foundation/NSCharacterSet.swift

Lines changed: 179 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import CoreFoundation
1212

13-
#if os(macOS) || os(iOS)
13+
#if _runtime(_ObjC) // if objc_enum works, restore the old names:
1414
let kCFCharacterSetControl = CFCharacterSetPredefinedSet.control
1515
let kCFCharacterSetWhitespace = CFCharacterSetPredefinedSet.whitespace
1616
let kCFCharacterSetWhitespaceAndNewline = CFCharacterSetPredefinedSet.whitespaceAndNewline
@@ -26,10 +26,52 @@ let kCFCharacterSetCapitalizedLetter = CFCharacterSetPredefinedSet.capitalizedLe
2626
let kCFCharacterSetSymbol = CFCharacterSetPredefinedSet.symbol
2727
let kCFCharacterSetNewline = CFCharacterSetPredefinedSet.newline
2828
let kCFCharacterSetIllegal = CFCharacterSetPredefinedSet.illegal
29+
30+
let kCFCharacterSetKeyedCodingTypeBitmap = CFCharacterSetKeyedCodingType.bitmap
31+
let kCFCharacterSetKeyedCodingTypeBuiltin = CFCharacterSetKeyedCodingType.builtin
32+
let kCFCharacterSetKeyedCodingTypeRange = CFCharacterSetKeyedCodingType.range
33+
let kCFCharacterSetKeyedCodingTypeString = CFCharacterSetKeyedCodingType.string
34+
let kCFCharacterSetKeyedCodingTypeBuiltinAndBitmap = CFCharacterSetKeyedCodingType.builtinAndBitmap
35+
#endif
36+
37+
#if _runtime(_ObjC)
38+
fileprivate let lastKnownPredefinedCharacterSetConstant = kCFCharacterSetNewline.rawValue
39+
40+
fileprivate extension Int {
41+
init(_ predefinedSet: CFCharacterSetPredefinedSet) {
42+
self = predefinedSet.rawValue
43+
}
44+
}
45+
#else
46+
fileprivate let lastKnownPredefinedCharacterSetConstant = kCFCharacterSetNewline
2947
#endif
3048

49+
fileprivate func knownPredefinedCharacterSet(rawValue: Int) -> CFCharacterSetPredefinedSet? {
50+
if rawValue > 0 && rawValue <= lastKnownPredefinedCharacterSetConstant {
51+
#if _runtime(_ObjC)
52+
return CFCharacterSetPredefinedSet(rawValue: rawValue)
53+
#else
54+
return CFCharacterSetPredefinedSet(rawValue)
55+
#endif
56+
} else {
57+
return nil
58+
}
59+
}
3160

32-
open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
61+
fileprivate extension String {
62+
static let characterSetBitmapRepresentationKey = "NSBitmap"
63+
static let characterSetAltBitmapRepresentationKey = "NSBitmapObject"
64+
static let characterSetStringKey = "NSString"
65+
static let characterSetAltStringKey = "NSStringObject"
66+
static let characterSetRangeKey = "NSRange"
67+
static let characterSetBuiltinIDKey = "NSBuiltinID"
68+
static let characterSetNewBuiltinIDKey = "NSBuiltinID2"
69+
static let characterSetIsInvertedKey = "NSIsInverted"
70+
static let characterSetNewIsInvertedKey = "NSIsInverted2"
71+
}
72+
73+
74+
open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
3375
typealias CFType = CFCharacterSet
3476
private var _base = _CFInfo(typeID: CFCharacterSetGetTypeID())
3577
private var _hashValue = CFHashCode(0)
@@ -162,9 +204,141 @@ open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
162204
}
163205
}
164206

165-
public convenience required init(coder aDecoder: NSCoder) {
166-
self.init(charactersIn: "")
167-
NSUnimplemented()
207+
open class var supportsSecureCoding: Bool { return true }
208+
209+
public required init?(coder aDecoder: NSCoder) {
210+
guard aDecoder.allowsKeyedCoding else {
211+
fatalError("Decoding requires a NSCoder that allows keyed coding.")
212+
}
213+
214+
func fail(code: CocoaError.Code = .coderReadCorrupt, _ message: String) {
215+
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: code.rawValue, userInfo: [ NSLocalizedDescriptionKey: message ]))
216+
}
217+
218+
let finalSet: CFCharacterSet
219+
220+
var builtinType = aDecoder.decodeInteger(forKey: .characterSetBuiltinIDKey)
221+
if builtinType == 0 {
222+
builtinType = aDecoder.decodeInteger(forKey: .characterSetNewBuiltinIDKey)
223+
}
224+
225+
let isInverted = aDecoder.decodeBool(forKey: .characterSetIsInvertedKey)
226+
227+
let keyedUnarchiver = aDecoder as? NSKeyedUnarchiver
228+
let containsCharacterSetStringKeyName = aDecoder.containsValue(forKey: .characterSetStringKey)
229+
let containsCharacterSetStringAltKeyName = aDecoder.containsValue(forKey: .characterSetAltStringKey)
230+
231+
if let predefinedSet = knownPredefinedCharacterSet(rawValue: builtinType) {
232+
guard let theSet = CFCharacterSetGetPredefined(predefinedSet) else {
233+
fail("CFCharacterSetGetPredefined -- Predefined Character Set not found")
234+
return nil
235+
}
236+
237+
finalSet = theSet
238+
} else if aDecoder.containsValue(forKey: .characterSetRangeKey) {
239+
let range = UInt64(bitPattern: aDecoder.decodeInt64(forKey: .characterSetRangeKey))
240+
guard let theSet = CFCharacterSetCreateWithCharactersInRange(kCFAllocatorSystemDefault, CFRangeMake(CFIndex(range >> 32), CFIndex(range & 0xFFFFFFFF))) else {
241+
fail("CFCharacterSetCreateWithCharactersInRange -- Character Set creation with characters in range failed")
242+
return nil
243+
}
244+
245+
finalSet = theSet
246+
} else if containsCharacterSetStringKeyName || containsCharacterSetStringAltKeyName {
247+
let maybeResult: NSString?
248+
249+
if let keyedUnarchiver = keyedUnarchiver, containsCharacterSetStringKeyName {
250+
maybeResult = keyedUnarchiver._decodePropertyListForKey(.characterSetStringKey) as? NSString
251+
} else {
252+
maybeResult = aDecoder.decodeObject(of: NSString.self, forKey: .characterSetAltStringKey)
253+
}
254+
255+
guard let result = maybeResult else {
256+
fail(code: .coderValueNotFound, "Decoder value not found")
257+
return nil
258+
}
259+
260+
guard let theSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorSystemDefault, result._cfObject) else {
261+
fail("CFCharacterSetCreateWithCharactersInString -- Character Set creation with characters in string failed")
262+
return nil
263+
}
264+
265+
finalSet = theSet
266+
} else {
267+
let maybeResult: NSData?
268+
269+
if let keyedUnarchiver = keyedUnarchiver, keyedUnarchiver.containsValue(forKey: .characterSetBitmapRepresentationKey) {
270+
maybeResult = keyedUnarchiver._decodePropertyListForKey(.characterSetBitmapRepresentationKey) as? NSData
271+
} else {
272+
maybeResult = aDecoder.decodeObject(of: NSData.self, forKey: .characterSetAltBitmapRepresentationKey)
273+
}
274+
275+
guard let result = maybeResult else {
276+
fail(code: .coderValueNotFound, "Decoder value not found")
277+
return nil
278+
}
279+
280+
guard let theSet = CFCharacterSetCreateWithBitmapRepresentation(kCFAllocatorSystemDefault, result._cfObject) else {
281+
fail("CFCharacterSetCreateWithBitmapRepresentation -- Character Set creation with bitmap representation failed")
282+
return nil
283+
}
284+
285+
finalSet = theSet
286+
}
287+
288+
super.init()
289+
_CFCharacterSetInitCopyingSet(kCFAllocatorSystemDefault, _cfMutableObject, finalSet, type(of: self) is NSMutableCharacterSet.Type, false)
290+
if isInverted {
291+
_CFCharacterSetSetIsInverted(_cfMutableObject, isInverted)
292+
}
293+
}
294+
295+
public func encode(with aCoder: NSCoder) {
296+
guard aCoder.allowsKeyedCoding else {
297+
fatalError("Encoding requires a NSCoder that allows keyed coding.")
298+
}
299+
300+
var isInverted = _CFCharacterSetIsInverted(_cfObject)
301+
let keyedArchiver = aCoder as? NSKeyedArchiver
302+
303+
switch _CFCharacterSetGetKeyedCodingType(_cfObject) {
304+
case kCFCharacterSetKeyedCodingTypeBuiltin:
305+
aCoder.encode(Int(_CFCharacterSetGetKeyedCodingBuiltinType(_cfObject)), forKey: .characterSetBuiltinIDKey)
306+
307+
case kCFCharacterSetKeyedCodingTypeRange:
308+
let range = _CFCharacterSetGetKeyedCodingRange(_cfObject)
309+
310+
var value = UInt64(range.location)
311+
value <<= 32
312+
value |= UInt64(range.length)
313+
314+
aCoder.encode(Int64(bitPattern: value), forKey: .characterSetRangeKey)
315+
316+
case kCFCharacterSetKeyedCodingTypeString:
317+
let string = _CFCharacterSetCreateKeyedCodingString(_cfObject).takeRetainedValue()
318+
if let keyedArchiver = keyedArchiver {
319+
keyedArchiver._encodePropertyList(string._nsObject, forKey: .characterSetStringKey)
320+
} else {
321+
aCoder.encode(string._nsObject, forKey: .characterSetAltStringKey)
322+
}
323+
324+
case kCFCharacterSetKeyedCodingTypeBuiltinAndBitmap:
325+
aCoder.encode(Int(_CFCharacterSetGetKeyedCodingBuiltinType(_cfObject)), forKey: .characterSetNewBuiltinIDKey)
326+
if isInverted { aCoder.encode(true, forKey: .characterSetNewIsInvertedKey )}
327+
328+
fallthrough
329+
default:
330+
if let keyedArchiver = keyedArchiver {
331+
keyedArchiver._encodePropertyList(bitmapRepresentation._nsObject, forKey: .characterSetBitmapRepresentationKey)
332+
} else {
333+
aCoder.encode(bitmapRepresentation._nsObject, forKey: .characterSetAltBitmapRepresentationKey)
334+
}
335+
336+
isInverted = false
337+
}
338+
339+
if isInverted {
340+
aCoder.encode(true, forKey: .characterSetIsInvertedKey)
341+
}
168342
}
169343

170344
open func characterIsMember(_ aCharacter: unichar) -> Bool {
@@ -226,10 +400,6 @@ open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
226400
NSRequiresConcreteImplementation()
227401
}
228402
}
229-
230-
open func encode(with aCoder: NSCoder) {
231-
NSUnimplemented()
232-
}
233403
}
234404

235405
open class NSMutableCharacterSet : NSCharacterSet {

TestFoundation/FixtureValues.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,29 @@ enum Fixtures {
220220
return NSMutableSet()
221221
}
222222

223+
// ===== NSCharacterSet, NSMutableCharacterSet =====
224+
225+
static let characterSetEmpty = TypedFixture<NSCharacterSet>("NSCharacterSet-Empty") {
226+
return NSCharacterSet()
227+
}
228+
229+
static let characterSetRange = TypedFixture<NSCharacterSet>("NSCharacterSet-Range") {
230+
return NSCharacterSet(range: NSMakeRange(0, 255))
231+
}
232+
233+
static let characterSetString = TypedFixture<NSCharacterSet>("NSCharacterSet-String") {
234+
return NSCharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyz")
235+
}
236+
237+
static let characterSetBitmap = TypedFixture<NSCharacterSet>("NSCharacterSet-Bitmap") {
238+
let someSet = NSCharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyz")
239+
return NSCharacterSet(bitmapRepresentation: someSet.bitmapRepresentation)
240+
}
241+
242+
static let characterSetBuiltin = TypedFixture<NSCharacterSet>("NSCharacterSet-Builtin") {
243+
return NSCharacterSet.alphanumerics as NSCharacterSet
244+
}
245+
223246
// ===== Fixture list =====
224247

225248
static let _listOfAllFixtures: [AnyFixture] = [
@@ -245,6 +268,11 @@ enum Fixtures {
245268
AnyFixture(Fixtures.setEmpty),
246269
AnyFixture(Fixtures.mutableSetOfNumbers),
247270
AnyFixture(Fixtures.mutableSetEmpty),
271+
AnyFixture(Fixtures.characterSetEmpty),
272+
AnyFixture(Fixtures.characterSetRange),
273+
AnyFixture(Fixtures.characterSetString),
274+
AnyFixture(Fixtures.characterSetBitmap),
275+
AnyFixture(Fixtures.characterSetBuiltin),
248276
]
249277

250278
// This ensures that we do not have fixtures with duplicate identifiers:

0 commit comments

Comments
 (0)