Skip to content

Commit 692a0d4

Browse files
Lance Parkerairspeedswift
authored andcommitted
Properly promote stack buffer to heap buffer when necessary (#20668)
1 parent ebde767 commit 692a0d4

File tree

2 files changed

+92
-78
lines changed

2 files changed

+92
-78
lines changed

stdlib/public/core/NormalizedCodeUnitIterator.swift

Lines changed: 77 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,13 @@ internal
227227
struct _NormalizedUTF16CodeUnitIterator: IteratorProtocol {
228228
internal typealias CodeUnit = UInt16
229229
var segmentBuffer = _FixedArray16<CodeUnit>(allZeros:())
230-
var overflowBuffer: [CodeUnit]? = nil
231-
var normalizationBuffer: [CodeUnit]? = nil
230+
var normalizationBuffer = _FixedArray16<CodeUnit>(allZeros:())
231+
var segmentHeapBuffer: [CodeUnit]? = nil
232+
var normalizationHeapBuffer: [CodeUnit]? = nil
232233
var source: _SegmentSource
233234

234235
var segmentBufferIndex = 0
235236
var segmentBufferCount = 0
236-
var overflowBufferIndex = 0
237-
var overflowBufferCount = 0
238237

239238
init(_ guts: _StringGuts, _ range: Range<String.Index>) {
240239
source = _ForeignStringGutsSource(guts, range)
@@ -350,83 +349,86 @@ struct _NormalizedUTF16CodeUnitIterator: IteratorProtocol {
350349

351350
mutating func next() -> UInt16? {
352351
if segmentBufferCount == segmentBufferIndex {
352+
if source.isEmpty {
353+
return nil
354+
}
353355
segmentBuffer = _FixedArray16<CodeUnit>(allZeros:())
354356
segmentBufferCount = 0
355357
segmentBufferIndex = 0
356358
}
357-
358-
if overflowBufferCount == overflowBufferIndex {
359-
overflowBufferCount = 0
360-
overflowBufferIndex = 0
359+
360+
if segmentBufferCount == 0 {
361+
segmentBufferCount = normalizeFromSource()
361362
}
362363

363-
if source.isEmpty
364-
&& segmentBufferCount == 0
365-
&& overflowBufferCount == 0 {
366-
// Our source of code units to normalize is empty and our buffers from
367-
// previous normalizations are also empty.
368-
return nil
364+
guard segmentBufferIndex < segmentBufferCount else { return nil }
365+
366+
defer { segmentBufferIndex += 1 }
367+
if _slowPath(segmentHeapBuffer != nil) {
368+
return segmentHeapBuffer![segmentBufferIndex]
369369
}
370-
if segmentBufferCount == 0 && overflowBufferCount == 0 {
371-
//time to fill a buffer if possible. Otherwise we are done, return nil
372-
// Normalize segment, and then compare first code unit
373-
var intermediateBuffer = _FixedArray16<CodeUnit>(allZeros:())
374-
if overflowBuffer == nil,
375-
let filled = source.tryFill(into: &intermediateBuffer)
376-
{
377-
guard let count = _tryNormalize(
378-
_castOutputBuffer(&intermediateBuffer,
379-
endingAt: filled),
380-
into: &segmentBuffer
381-
)
382-
else {
383-
fatalError("Output buffer was not big enough, this should not happen")
384-
}
385-
segmentBufferCount = count
386-
} else {
387-
if overflowBuffer == nil {
388-
let size = source.remaining * _Normalization._maxNFCExpansionFactor
389-
overflowBuffer = Array(repeating: 0, count: size)
390-
normalizationBuffer = Array(repeating:0, count: size)
391-
}
392-
393-
guard let count = normalizationBuffer!.withUnsafeMutableBufferPointer({
394-
(normalizationBufferPtr) -> Int? in
395-
guard let filled = source.tryFill(into: normalizationBufferPtr)
396-
else {
397-
fatalError("Invariant broken, buffer should have space")
398-
}
399-
return overflowBuffer!.withUnsafeMutableBufferPointer {
400-
(overflowBufferPtr) -> Int? in
401-
return _tryNormalize(
402-
UnsafeBufferPointer(rebasing: normalizationBufferPtr[..<filled]),
403-
into: overflowBufferPtr
404-
)
405-
}
406-
}) else {
407-
fatalError("Invariant broken, overflow buffer should have space")
408-
}
409-
410-
overflowBufferCount = count
370+
return segmentBuffer[segmentBufferIndex]
371+
}
372+
373+
mutating func normalizeFromSource() -> Int {
374+
if segmentHeapBuffer == nil,
375+
let filled = source.tryFill(into: &normalizationBuffer)
376+
{
377+
if let count = _tryNormalize(
378+
_castOutputBuffer(&normalizationBuffer,
379+
endingAt: filled),
380+
into: &segmentBuffer
381+
) {
382+
return count
411383
}
384+
return normalizeWithHeapBuffers(filled)
412385
}
413-
414-
//exactly one of the buffers should have code units for us to return
415-
_internalInvariant((segmentBufferCount == 0)
416-
!= ((overflowBuffer?.count ?? 0) == 0))
417-
418-
if segmentBufferIndex < segmentBufferCount {
419-
let index = segmentBufferIndex
420-
segmentBufferIndex += 1
421-
return segmentBuffer[index]
422-
} else if overflowBufferIndex < overflowBufferCount {
423-
_internalInvariant(overflowBufferIndex < overflowBuffer!.count)
424-
let index = overflowBufferIndex
425-
overflowBufferIndex += 1
426-
return overflowBuffer![index]
427-
} else {
428-
return nil
386+
return normalizeWithHeapBuffers()
387+
}
388+
389+
//This handles normalization from an intermediate buffer to the heap segment
390+
//buffer. This can get called in 3 situations:
391+
//* We've already transitioned to heap buffers
392+
//* We attempted to fill the pre-normal stack buffer but there was not enough
393+
//. room, so we need to promote both and then attempt the fill again.
394+
//* The fill for the stack buffer succeeded, but the normalization didn't. In
395+
// this case, we want to first copy the contents of the stack buffer that
396+
// we filled into the new heap buffer. The stackBufferCount
397+
// parameter signals that we need to do this copy, thus skipping the fill
398+
// that we would normally do before normalization.
399+
mutating func normalizeWithHeapBuffers(
400+
_ stackBufferCount: Int? = nil
401+
) -> Int {
402+
if segmentHeapBuffer == nil {
403+
_internalInvariant(normalizationHeapBuffer == nil)
404+
let preFilledBufferCount = stackBufferCount ?? 0
405+
let size = (source.remaining + preFilledBufferCount)
406+
* _Normalization._maxNFCExpansionFactor
407+
segmentHeapBuffer = Array(repeating: 0, count: size)
408+
normalizationHeapBuffer = Array(repeating:0, count: size)
409+
for i in 0..<preFilledBufferCount {
410+
normalizationHeapBuffer![i] = normalizationBuffer[i]
411+
}
412+
}
413+
414+
guard let count = normalizationHeapBuffer!.withUnsafeMutableBufferPointer({
415+
(normalizationHeapBufferPtr) -> Int? in
416+
guard let filled = stackBufferCount ??
417+
source.tryFill(into: normalizationHeapBufferPtr)
418+
else {
419+
fatalError("Invariant broken, buffer should have space")
420+
}
421+
return segmentHeapBuffer!.withUnsafeMutableBufferPointer {
422+
(segmentHeapBufferPtr) -> Int? in
423+
return _tryNormalize(
424+
UnsafeBufferPointer(rebasing: normalizationHeapBufferPtr[..<filled]),
425+
into: segmentHeapBufferPtr
426+
)
427+
}
428+
}) else {
429+
fatalError("Invariant broken, overflow buffer should have space")
429430
}
431+
return count
430432
}
431433
}
432434

@@ -635,22 +637,19 @@ extension _NormalizedUTF8CodeUnitIterator_2 {
635637
let result = _lexicographicalCompare(cu, otherCU)
636638
if result == .equal {
637639
continue
638-
} else {
639-
return result
640640
}
641-
} else {
642-
//other returned nil, we are greater
643-
return .greater
641+
return result
644642
}
643+
//other returned nil, we are greater
644+
return .greater
645645
}
646646

647647
//we ran out of code units, either we are equal, or only we ran out and
648648
//other is greater
649649
if let _ = mutableOther.next() {
650650
return .less
651-
} else {
652-
return .equal
653651
}
652+
return .equal
654653
}
655654
}
656655

validation-test/stdlib/String.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,4 +2183,19 @@ for test in comparisonTestCases {
21832183
}
21842184
}
21852185

2186+
StringTests.test("NormalizationBufferCrashRegressionTest") {
2187+
let str = "\u{0336}\u{0344}\u{0357}\u{0343}\u{0314}\u{0351}\u{0340}\u{0300}\u{0340}\u{0360}\u{0314}\u{0357}\u{0315}\u{0301}\u{0344}a"
2188+
let set = Set([str])
2189+
2190+
expectTrue(set.contains(str))
2191+
}
2192+
2193+
StringTests.test("NormalizationCheck") {
2194+
let str = "\u{0336}\u{0344}\u{0357}\u{0343}\u{0314}\u{0351}\u{0340}\u{0300}\u{0340}\u{0360}\u{0314}\u{0357}\u{0315}\u{0301}\u{0344}a"
2195+
let nfcCodeUnits = str._nfcCodeUnits
2196+
let expectedCodeUnits: [UInt8] = [0xCC, 0xB6, 0xCC, 0x88, 0xCC, 0x81, 0xCD, 0x97, 0xCC, 0x93, 0xCC, 0x94, 0xCD, 0x91, 0xCC, 0x80, 0xCC, 0x80, 0xCC, 0x80, 0xCC, 0x94, 0xCD, 0x97, 0xCC, 0x81, 0xCC, 0x88, 0xCC, 0x81, 0xCC, 0x95, 0xCD, 0xA0, 0x61]
2197+
2198+
expectEqual(expectedCodeUnits, nfcCodeUnits)
2199+
}
2200+
21862201
runAllTests()

0 commit comments

Comments
 (0)