Skip to content

Commit 97af53f

Browse files
committed
CFAttributedString: keep attribute coalescing within the run array
CFAttributedStringSetAttributes passed the last touched run index to CFAttributedStringCoalesce as the length of the range to coalesce, so the scan could run past the end of the attribute array, comparing and then uncaching runs that were never allocated. That freed an attribute dictionary still referenced by a live run and later faulted when the run was uncached again. The coalesce scan now stops at the current run count. While here, refresh the local array pointer after an insertion that may reallocate the run array, and correct the run-removal shift, which stopped one element short and dropped the last run.
1 parent 90822ff commit 97af53f

1 file changed

Lines changed: 17 additions & 13 deletions

File tree

‎Source/CFAttributedString.c‎

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ RemoveAttributesAtIndex (CFMutableAttributedStringRef str, CFRange range)
540540

541541
cur = &working->_attribs[range.location];
542542
next = cur + range.length;
543-
stop = cur + (working->_attribCount - (range.location + range.length) - 1);
543+
stop = cur + (working->_attribCount - (range.location + range.length));
544544
while (cur < stop)
545545
*cur++ = *next++;
546546
working->_attribCount -= range.length;
@@ -572,29 +572,27 @@ CFAttributedStringCoalesce (CFMutableAttributedStringRef str, CFRange range)
572572
CFIndex end;
573573
Attr *array;
574574

575-
array = working->_attribs;
576-
if (range.location > 0)
577-
{
578-
if (array[range.location - 1].attrib == array[range.location].attrib)
579-
{
580-
RemoveAttributesAtIndex (str, CFRangeMake (range.location, 1));
581-
range.length -= 1;
582-
}
583-
}
584-
585575
/* Each step compares with the preceding entry, so never start at
586576
index 0 (there is nothing before it to coalesce with). */
587577
cur = range.location > 0 ? range.location : 1;
588578
end = range.location + range.length;
579+
if (end > working->_attribCount)
580+
end = working->_attribCount;
589581

590582
while (cur < end)
591583
{
584+
/* RemoveAttributesAtIndex may move or reallocate the attribute
585+
array, so read it afresh each time through the loop. */
586+
array = working->_attribs;
592587
if (array[cur - 1].attrib == array[cur].attrib)
593588
{
589+
/* Drop the duplicate run; the following run shifts down into
590+
cur, so re-check that slot rather than advancing. */
594591
RemoveAttributesAtIndex (str, CFRangeMake (cur, 1));
595592
end -= 1;
596593
}
597-
cur++;
594+
else
595+
cur++;
598596
}
599597
}
600598
}
@@ -792,7 +790,12 @@ CFAttributedStringSetAttributes (CFMutableAttributedStringRef str,
792790
*/
793791
if (rE.location + rE.length > rangeMax
794792
&& !CFEqual (array[idxE].attrib, repl))
795-
InsertAttributesAtIndex (str, idxE + 1, rangeMax, array[idxE].attrib);
793+
{
794+
InsertAttributesAtIndex (str, idxE + 1, rangeMax, array[idxE].attrib);
795+
/* InsertAttributesAtIndex may reallocate the attribute array, so
796+
refresh the local pointer before reading through it again. */
797+
array = str->_attribs;
798+
}
796799

797800
if (range.location == rS.location)
798801
{
@@ -809,6 +812,7 @@ CFAttributedStringSetAttributes (CFMutableAttributedStringRef str,
809812
cur += 1;
810813
idxE += 1;
811814
InsertAttributesAtIndex (str, cur, range.location, repl);
815+
array = str->_attribs;
812816
if (!clearOtherAttribs)
813817
SetAttributesAtIndex (str, cur, array[idxS].attrib);
814818
}

0 commit comments

Comments
 (0)