@@ -124,26 +124,34 @@ extension String {
124124 public func hasPrefix( _ prefix: String ) -> Bool {
125125 if _fastPath ( self . _guts. isNFCFastUTF8 && prefix. _guts. isNFCFastUTF8) {
126126 guard prefix. _guts. count <= self . _guts. count else { return false }
127- return prefix. _guts. withFastUTF8 { nfcPrefix in
127+ let isPrefix = prefix. _guts. withFastUTF8 { nfcPrefix in
128128 let prefixEnd = nfcPrefix. count
129129 return self . _guts. withFastUTF8 ( range: 0 ..< prefixEnd) { nfcSlicedSelf in
130130 return _binaryCompare ( nfcSlicedSelf, nfcPrefix) == 0
131131 }
132132 }
133+ let endIndex = Index ( _encodedOffset: prefix. _guts. count)
134+ // In addition to a byte comparison check, we also need to check that
135+ // the prefix ends on a grapheme cluster boundary of the String
136+ return isPrefix && self . _guts. isOnGraphemeClusterBoundary ( endIndex)
133137 }
134138
135139 return starts ( with: prefix)
136140 }
137141
138142 public func hasSuffix( _ suffix: String ) -> Bool {
139143 if _fastPath ( self . _guts. isNFCFastUTF8 && suffix. _guts. isNFCFastUTF8) {
140- guard suffix . _guts . count < = self . _guts. count else { return false }
141- return suffix . _guts . withFastUTF8 { nfcSuffix in
142- let suffixStart = self . _guts. count - nfcSuffix. count
144+ let suffixStart = self . _guts. count - suffix . _guts . count
145+ guard suffixStart >= 0 else { return false }
146+ let isSuffix = suffix . _guts. withFastUTF8 { nfcSuffix in
143147 return self . _guts. withFastUTF8 ( range: suffixStart..< self . _guts. count) {
144148 nfcSlicedSelf in return _binaryCompare ( nfcSlicedSelf, nfcSuffix) == 0
145149 }
146150 }
151+ let startIndex = Index ( _encodedOffset: suffixStart)
152+ // In addition to a byte comparison check, we also need to check that
153+ // the suffix starts on a grapheme cluster boundary of the String
154+ return isSuffix && self . _guts. isOnGraphemeClusterBoundary ( startIndex)
147155 }
148156
149157 return self . reversed ( ) . starts ( with: suffix. reversed ( ) )
0 commit comments