源代碼CharacterSet
is available, actually.的來源contains
是:
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)
}
}
因此,它基本上只是調用,直至CFCharacterSetIsLongCharacterMember
。該is also available, although only for Yosemite的源代碼(El Cap和Sierra的版本都表示「即將推出」)。然而,優勝美地的代碼似乎與我在Sierra上的反彙編中看到的相符。無論如何,代碼如下所示:
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);
}
因此,我們可以跟進,並找出發生了什麼事。不幸的是,我們必須破解我們的x86_64組裝技能才能做到這一點。但是不要害怕,因爲我已經爲你做了這件事,因爲顯然這是我在週五晚上爲了好玩而做的。
一個有用的事情已經是數據結構:
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;
};
我們需要知道到底什麼CFRuntimeBase
是,太:
typedef struct __CFRuntimeBase {
uintptr_t _cfisa;
uint8_t _cfinfo[4];
#if __LP64__
uint32_t _rc;
#endif
} CFRuntimeBase;
你猜怎麼着!還有一些我們需要的常量。
enum {
__kCFCharSetClassTypeMask = 0x0070,
__kCFCharSetClassBuiltin = 0x0000,
__kCFCharSetClassRange = 0x0010,
__kCFCharSetClassString = 0x0020,
__kCFCharSetClassBitmap = 0x0030,
__kCFCharSetClassSet = 0x0040,
__kCFCharSetClassCompactBitmap = 0x0040,
// irrelevant stuff redacted
};
然後我們就可以在CFCharacterSetIsLongCharacterMember
突破和日誌結構:
supersetA.contains(UnicodeScalar(128518)!)
(lldb) po [NSData dataWithBytes:$rdi length:48]
<21b3d2ad ffff1d00 90190000 02000000 00000000 00000000 06f60100 00000000 01000000 00000000 00000000 00000000>
基於上面的結構,我們可以計算出這是什麼字符集而成的。在這種情況下,相關部分將是從CFRuntimeBase
開始的cfinfo
的第一個字節,它們是字節9-12。這個的第一個字節0x90
包含字符集的類型信息。它需要AND
編輯與__kCFCharSetClassTypeMask
,這得到我們0x10
,這是__kCFCharSetClassRange
。
對於此行:
supersetB.contains(UnicodeScalar(128518)!)
的結構是:
(lldb) po [NSData dataWithBytes:$rdi length:48]
<21b3d2ad ffff1d00 a0190000 02000000 00000000 00000000 9066f000 01000000 02000000 00000000 00000000 00000000>
這次字節9是0xa0
,其AND
ED與掩模是0x20
,__kCFCharSetClassString
。
此時,Monty Python演員陣容尖叫着「Get On With It!」,所以讓我們來看看CFCharacterSetIsLongCharacterMember
的來源,看看發生了什麼。
跳過過去所有的CF_OBJC_FUNCDISPATCHV
廢話,我們得到這一行:
if (plane) {
這顯然計算結果爲真在這兩種情況下。下一個測試:
if (__CFCSetIsBuiltin(theSet)) {
此計算爲在這兩種情況下錯誤的,因爲無論是類型__kCFCharSetClassBuiltin
,所以我們跳過這個塊。
isAnnexInverted = __CFCSetAnnexIsInverted(theSet);
在這兩種情況下,_annex
指針爲空(見在結構的端部的所有零),所以這是false
。
本次測試將是true
出於同樣的原因:
if ((annexPlane = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(theSet, plane)) == NULL) {
帶着我們:
if (!__CFCSetHasNonBMPPlane(theSet) && __CFCSetIsRange(theSet)) {
的__CFCSetHasNonBMPPlane
宏檢查_annex
,所以這是假的。當然,表情符號不在BMP平面中,所以這實際上對於兩個個案都是錯誤的,即使是返回正確結果的情況也是如此。
__CFCSetIsRange
檢查我們的類型是否爲__kCFCharSetClassRange
,這是第一次。所以這是我們的分歧點。這樣做的第二次調用,產生不正確的結果,返回到下一行:
return (isAnnexInverted ? true : false);
而且由於附件是NULL
,造成isAnnexInverted
是假的,這個返回false。
至於如何解決它......好吧,我不能。但現在我們知道它爲什麼發生了。據我所知,主要的問題是在創建字符集時_annex
字段未被填充,並且由於附件似乎用於跟蹤非BMP平面中的字符,我認爲它應該對於兩個字符集都存在。順便說一下,如果您決定使用file one(我會將其歸檔於CoreFoundation,因爲這是實際問題所在),此信息可能對您的錯誤報告有所幫助。
似乎是基金會(或Swift標準庫)的一些錯誤。 'supersetD'的情況在Xcode 9中返回'true',所以'union(_ :)'的錯誤似乎在最新的SDK中得到修復。解決方法:'CharacterSet(charactersIn:「」).union(CharacterSet(charactersIn:「A」))''。 – OOPer
它變得更加怪異:https://pastebin.com/zCrM1XUL。我做了一些挖掘,你可能想看看'CFCharacterSet.c'中的'_CFCharacterSetIsLongCharacterMember',這就是contains方法的作用(我確信我不太瞭解它)。 https://github.com/apple/swift-corelibs-foundation/blob/d95964015bed377baa99c3612281afa11bf06990/CoreFoundation/String.subproj/CFCharacterSet.c#L1716 – nyg
@nyg我花了大把的時間搞清楚它是什麼,所以如果你我很好奇,看看下面的答案。 –