Skip to content

Commit ab0859d

Browse files
committed
Parity: NSCoding: NSCharacterSet
- Split initialization for CFCharacterSet so we can use it in init?(coder:) - Make CFCharacterSetPriv.h available to Swift so we can use its helpers to encode and decode as we do on Darwin.
1 parent 0c8dd8a commit ab0859d

File tree

5 files changed

+259
-41
lines changed

5 files changed

+259
-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
@@ -402,6 +404,7 @@ CF_CROSS_PLATFORM_EXPORT int _CFThreadGetName(char *_Nonnull buf, int length);
402404
CF_EXPORT Boolean _CFCharacterSetIsLongCharacterMember(CFCharacterSetRef theSet, UTF32Char theChar);
403405
CF_EXPORT CFCharacterSetRef _CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet);
404406
CF_EXPORT CFMutableCharacterSetRef _CFCharacterSetCreateMutableCopy(CFAllocatorRef alloc, CFCharacterSetRef theSet);
407+
CF_CROSS_PLATFORM_EXPORT void _CFCharacterSetInitCopyingSet(CFAllocatorRef alloc, CFMutableCharacterSetRef cset, CFCharacterSetRef theSet, bool isMutable, bool validateSubclasses);
405408

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

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: 173 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,46 @@ 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+
#else
40+
fileprivate let lastKnownPredefinedCharacterSetConstant = kCFCharacterSetNewline
2941
#endif
3042

43+
fileprivate func knownPredefinedCharacterSet(rawValue: Int) -> CFCharacterSetPredefinedSet? {
44+
if rawValue > 0 && rawValue <= lastKnownPredefinedCharacterSetConstant {
45+
#if _runtime(_ObjC)
46+
return CFCharacterSetPredefinedSet(rawValue: rawValue)
47+
#else
48+
return CFCharacterSetPredefinedSet(rawValue)
49+
#endif
50+
} else {
51+
return nil
52+
}
53+
}
54+
55+
fileprivate extension String {
56+
static let characterSetBitmapRepresentationKey = "NSBitmap"
57+
static let characterSetAltBitmapRepresentationKey = "NSBitmapObject"
58+
static let characterSetStringKey = "NSString"
59+
static let characterSetAltStringKey = "NSStringObject"
60+
static let characterSetRangeKey = "NSRange"
61+
static let characterSetBuiltinIDKey = "NSBuiltinID"
62+
static let characterSetNewBuiltinIDKey = "NSBuiltinID2"
63+
static let characterSetIsInvertedKey = "NSIsInverted"
64+
static let characterSetNewIsInvertedKey = "NSIsInverted2"
65+
}
3166

32-
open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
67+
68+
open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
3369
typealias CFType = CFCharacterSet
3470
private var _base = _CFInfo(typeID: CFCharacterSetGetTypeID())
3571
private var _hashValue = CFHashCode(0)
@@ -162,9 +198,141 @@ open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
162198
}
163199
}
164200

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

170338
open func characterIsMember(_ aCharacter: unichar) -> Bool {
@@ -226,10 +394,6 @@ open class NSCharacterSet : NSObject, NSCopying, NSMutableCopying, NSCoding {
226394
NSRequiresConcreteImplementation()
227395
}
228396
}
229-
230-
open func encode(with aCoder: NSCoder) {
231-
NSUnimplemented()
232-
}
233397
}
234398

235399
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:

TestFoundation/TestCharacterSet.swift

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,33 +51,6 @@ private struct Box: Equatable {
5151

5252
class TestCharacterSet : XCTestCase {
5353

54-
static var allTests: [(String, (TestCharacterSet) -> () throws -> Void)] {
55-
return [
56-
("testBasicConstruction", testBasicConstruction),
57-
("testMutability_copyOnWrite", testMutability_copyOnWrite),
58-
("testRanges", testRanges),
59-
("testInsertAndRemove", testInsertAndRemove),
60-
("testBasics", testBasics),
61-
("testClosedRanges_SR_2988", testClosedRanges_SR_2988),
62-
("test_Predefines", test_Predefines),
63-
("test_Range", test_Range),
64-
("test_String", test_String),
65-
("test_Bitmap", test_Bitmap),
66-
("test_AnnexPlanes", test_AnnexPlanes),
67-
("test_Planes", test_Planes),
68-
("test_InlineBuffer", test_InlineBuffer),
69-
("test_Equatable", test_Equatable),
70-
("test_Subtracting", test_Subtracting),
71-
("test_SubtractEmptySet", test_SubtractEmptySet),
72-
("test_SubtractNonEmptySet", test_SubtractNonEmptySet),
73-
("test_SymmetricDifference", test_SymmetricDifference),
74-
("test_formUnion", test_formUnion),
75-
("test_union", test_union),
76-
("test_SR5971", test_SR5971),
77-
("test_hashing", test_hashing),
78-
]
79-
}
80-
8154
let capitalA = UnicodeScalar(0x0041)! // LATIN CAPITAL LETTER A
8255
let capitalB = UnicodeScalar(0x0042)! // LATIN CAPITAL LETTER B
8356
let capitalC = UnicodeScalar(0x0043)! // LATIN CAPITAL LETTER C
@@ -383,4 +356,48 @@ class TestCharacterSet : XCTestCase {
383356
// complete.
384357
allowIncompleteHashing: true)
385358
}
359+
360+
let fixtures = [
361+
// Fixtures.characterSetEmpty,
362+
// Fixtures.characterSetRange,
363+
// Fixtures.characterSetString,
364+
Fixtures.characterSetBitmap,
365+
// Fixtures.characterSetBuiltin,
366+
]
367+
368+
func test_codingRoundtrip() throws {
369+
for fixture in fixtures {
370+
try fixture.assertValueRoundtripsInCoder()
371+
}
372+
}
373+
374+
static var allTests: [(String, (TestCharacterSet) -> () throws -> Void)] {
375+
return [
376+
("testBasicConstruction", testBasicConstruction),
377+
("testMutability_copyOnWrite", testMutability_copyOnWrite),
378+
("testRanges", testRanges),
379+
("testInsertAndRemove", testInsertAndRemove),
380+
("testBasics", testBasics),
381+
("testClosedRanges_SR_2988", testClosedRanges_SR_2988),
382+
("test_Predefines", test_Predefines),
383+
("test_Range", test_Range),
384+
("test_String", test_String),
385+
("test_Bitmap", test_Bitmap),
386+
("test_AnnexPlanes", test_AnnexPlanes),
387+
("test_Planes", test_Planes),
388+
("test_InlineBuffer", test_InlineBuffer),
389+
("test_Equatable", test_Equatable),
390+
("test_Subtracting", test_Subtracting),
391+
("test_SubtractEmptySet", test_SubtractEmptySet),
392+
("test_SubtractNonEmptySet", test_SubtractNonEmptySet),
393+
("test_SymmetricDifference", test_SymmetricDifference),
394+
("test_formUnion", test_formUnion),
395+
("test_union", test_union),
396+
("test_SR5971", test_SR5971),
397+
("test_hashing", test_hashing),
398+
("test_codingRoundtrip", test_codingRoundtrip),
399+
]
400+
}
401+
402+
386403
}

0 commit comments

Comments
 (0)