Der Quellcode zu CharacterSet
is available, actually. Die Quelle für contains
ist:
fileprivate func contains(_ member: Unicode.Scalar) -> Bool {
switch _backing {
case .immutable(let cs):
return CFCharacterSetIsLongCharacterMember(cs, member.value)
case .mutable(let cs):
return CFCharacterSetIsLongCharacterMember(cs, member.value)
}
}
es also im Grunde nur Anrufe durch CFCharacterSetIsLongCharacterMember
. Der Quellcode für diese is also available, although only for Yosemite (die Versionen für El Cap und Sierra sagen beide "Coming Soon"). Der Yosemite-Code schien jedoch mit dem übereinzustimmen, was ich bei der Demontage von Sierra gesehen hatte. Wie auch immer, der Code dafür sieht so aus:
Boolean CFCharacterSetIsLongCharacterMember(CFCharacterSetRef theSet, UTF32Char theChar) {
CFIndex length;
UInt32 plane = (theChar >> 16);
Boolean isAnnexInverted = false;
Boolean isInverted;
Boolean result = false;
CF_OBJC_FUNCDISPATCHV(__kCFCharacterSetTypeID, Boolean, (NSCharacterSet *)theSet, longCharacterIsMember:(UTF32Char)theChar);
__CFGenericValidateType(theSet, __kCFCharacterSetTypeID);
if (plane) {
CFCharacterSetRef annexPlane;
if (__CFCSetIsBuiltin(theSet)) {
isInverted = __CFCSetIsInverted(theSet);
return (CFUniCharIsMemberOf(theChar, __CFCSetBuiltinType(theSet)) ? !isInverted : isInverted);
}
isAnnexInverted = __CFCSetAnnexIsInverted(theSet);
if ((annexPlane = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, plane)) == NULL) {
if (!__CFCSetHasNonBMPPlane(theSet) && __CFCSetIsRange(theSet)) {
isInverted = __CFCSetIsInverted(theSet);
length = __CFCSetRangeLength(theSet);
return (length && __CFCSetRangeFirstChar(theSet) <= theChar && theChar < __CFCSetRangeFirstChar(theSet) + length ? !isInverted : isInverted);
} else {
return (isAnnexInverted ? true : false);
}
} else {
theSet = annexPlane;
theChar &= 0xFFFF;
}
}
isInverted = __CFCSetIsInverted(theSet);
switch (__CFCSetClassType(theSet)) {
case __kCFCharSetClassBuiltin:
result = (CFUniCharIsMemberOf(theChar, __CFCSetBuiltinType(theSet)) ? !isInverted : isInverted);
break;
case __kCFCharSetClassRange:
length = __CFCSetRangeLength(theSet);
result = (length && __CFCSetRangeFirstChar(theSet) <= theChar && theChar < __CFCSetRangeFirstChar(theSet) + length ? !isInverted : isInverted);
break;
case __kCFCharSetClassString:
result = ((length = __CFCSetStringLength(theSet)) ? (__CFCSetBsearchUniChar(__CFCSetStringBuffer(theSet), length, theChar) ? !isInverted : isInverted) : isInverted);
break;
case __kCFCharSetClassBitmap:
result = (__CFCSetCompactBitmapBits(theSet) ? (__CFCSetIsMemberBitmap(__CFCSetBitmapBits(theSet), theChar) ? true : false) : isInverted);
break;
case __kCFCharSetClassCompactBitmap:
result = (__CFCSetCompactBitmapBits(theSet) ? (__CFCSetIsMemberInCompactBitmap(__CFCSetCompactBitmapBits(theSet), theChar) ? true : false) : isInverted);
break;
default:
CFAssert1(0, __kCFLogAssertion, "%s: Internal inconsistency error: unknown character set type", __PRETTY_FUNCTION__); // We should never come here
return false; // To make compiler happy
}
return (result ? !isAnnexInverted : isAnnexInverted);
}
So können wir folgen und herausfinden, was los ist. Leider müssen wir unsere x86_64 Assembly Skills aus dem Weg räumen. Aber fürchte dich nicht, denn ich habe das schon für dich getan, denn anscheinend mache ich das an einem Freitagabend zum Spaß.
Eine hilfreiche Sache zu haben, ist die Datenstruktur:
struct __CFCharacterSet {
CFRuntimeBase _base;
CFHashCode _hashValue;
union {
struct {
CFIndex _type;
} _builtin;
struct {
UInt32 _firstChar;
CFIndex _length;
} _range;
struct {
UniChar *_buffer;
CFIndex _length;
} _string;
struct {
uint8_t *_bits;
} _bitmap;
struct {
uint8_t *_cBits;
} _compactBitmap;
} _variants;
CFCharSetAnnexStruct *_annex;
};
Wir müssen wissen, was zum Teufel CFRuntimeBase
ist auch:
typedef struct __CFRuntimeBase {
uintptr_t _cfisa;
uint8_t _cfinfo[4];
#if __LP64__
uint32_t _rc;
#endif
} CFRuntimeBase;
Und erraten, was! Es gibt auch einige Konstanten, die wir brauchen.
enum {
__kCFCharSetClassTypeMask = 0x0070,
__kCFCharSetClassBuiltin = 0x0000,
__kCFCharSetClassRange = 0x0010,
__kCFCharSetClassString = 0x0020,
__kCFCharSetClassBitmap = 0x0030,
__kCFCharSetClassSet = 0x0040,
__kCFCharSetClassCompactBitmap = 0x0040,
// irrelevant stuff redacted
};
Wir können dann auf CFCharacterSetIsLongCharacterMember
brechen und die Struktur log:
supersetA.contains(UnicodeScalar(128518)!)
(lldb) po [NSData dataWithBytes:$rdi length:48]
<21b3d2ad ffff1d00 90190000 02000000 00000000 00000000 06f60100 00000000 01000000 00000000 00000000 00000000>
oben auf den structs Basierend, können wir herausfinden, was dieser Zeichensatz aus ist. In diesem Fall ist der relevante Teil das erste Byte von cfinfo
von CFRuntimeBase
, die Bytes 9-12 sind. Das erste Byte davon, 0x90
enthält die Typinformation für den Zeichensatz. Es muss AND
Ed mit __kCFCharSetClassTypeMask
sein, die uns 0x10
erhält, die __kCFCharSetClassRange
ist.
Für diese Zeile:
supersetB.contains(UnicodeScalar(128518)!)
die Struktur ist:
(lldb) po [NSData dataWithBytes:$rdi length:48]
<21b3d2ad ffff1d00 a0190000 02000000 00000000 00000000 9066f000 01000000 02000000 00000000 00000000 00000000>
Dieses Mal Byte 9 0xa0
ist, die AND
ed mit der Maske 0x20
ist, __kCFCharSetClassString
.
An diesem Punkt schreien die Monty Python Besetzung "Get On With It!", Also lasst uns durch die Quelle gehen für CFCharacterSetIsLongCharacterMember
und sehen, was vor sich geht.
Skipping vorbei an all den CF_OBJC_FUNCDISPATCHV
Mist, kommen wir zu dieser Zeile:
if (plane) {
Dies wertet offensichtlich auf true in beiden Fällen. Nächster Test:
if (__CFCSetIsBuiltin(theSet)) {
Dieses Ergebnis falsch in beiden Fällen, da weder Typ __kCFCharSetClassBuiltin
war, so dass wir diesen Block überspringen.
isAnnexInverted = __CFCSetAnnexIsInverted(theSet);
In beiden Fällen waren die _annex
Zeiger null (alle Nullen am Ende der Struktur sehen), so ist dies false
.
Dieser Test wird true
aus dem gleichen Grund sein:
if ((annexPlane = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, plane)) == NULL) {
unter uns:
if (!__CFCSetHasNonBMPPlane(theSet) && __CFCSetIsRange(theSet)) {
Die __CFCSetHasNonBMPPlane
Makro überprüft _annex
, so dass falsch ist. Das Emoji ist natürlich nicht in der BMP-Ebene, so scheint dies tatsächlich falsch für beide Fälle, auch die, die das richtige Ergebnis zurückgegeben wurde.
__CFCSetIsRange
prüft, ob unser Typ __kCFCharSetClassRange
ist, der nur das erste Mal wahr ist. Das ist unser Punkt der Divergenz. Der zweite Aufruf von dieser, die das falsche Ergebnis erzeugt, kehrt in der nächsten Zeile:
return (isAnnexInverted ? true : false);
Und da die Anlage ist NULL
, was isAnnexInverted
als falsch, dies gibt false zurück.
Wie, um es zu beheben ... nun, kann ich nicht. Aber jetzt wissen wir, warum es passiert ist. Von dem, was ich sagen kann, ist das Hauptproblem, dass das Feld _annex
nicht gefüllt wird, wenn der Zeichensatz erstellt wird, und da der Anhang verwendet wird, um die Zeichen in Nicht-BMP-Ebenen zu verfolgen, denke ich, dass es sollte für beide Zeichensätze vorhanden sein. Übrigens werden diese Informationen wahrscheinlich in einem Fehlerbericht hilfreich sein, wenn Sie sich dazu entscheiden, file one (ich würde es gegen CoreFoundation, da das ist, wo das eigentliche Problem ist).
Scheint, einige Fehler von Foundation (oder Swift Standard Library) zu sein. Der 'supersetD'-Fall gibt mit Xcode 9 'true' zurück, so dass der Fehler von 'union (_ :)' in den neuesten SDKs behoben zu sein scheint. Problemumgehung: 'CharacterSet (charactersIn:" ") .union (Zeichensatz (ZeichenIn:" A "))'. – OOPer
Und es wird seltsamer: https://pastebin.com/zCrM1XUL.Ich habe etwas gegraben und Sie könnten '_CFCharacterSetIsLongCharacterMember' in 'CFCharacterSet.c' suchen, das ist was die contains-Methode tut (und ich bin mir ziemlich sicher, dass ich nicht viel davon verstehe). https://github.com/apple/swift-corelibs-foundation/blob/d95964015bed377baa99c3612281afa11bf06990/CoreFoundation/Stringsubproj/CFCharacterSet.c#L1716 – nyg
@nyg Ich verbrachte eine Menge Zeit herauszufinden, was alles, also wenn Sie Bin neugierig, sieh dir meine Antwort unten an. –