Fix: CFAttributedString substring, attribute removal and replacement - #152
Open
DTW-Thalion wants to merge 2 commits into
Open
Fix: CFAttributedString substring, attribute removal and replacement#152DTW-Thalion wants to merge 2 commits into
DTW-Thalion wants to merge 2 commits into
Conversation
This was referenced Jul 22, 2026
Setting an attribute across a string that holds two or more runs crashed. GSBSearch set its upper bound to mid - 1, skipping index 0 and returning -1 for any key below the first entry, and the callers indexed the run array with that value. Restore the usual binary search bound. The mutable and immutable instance sizes subtracted the struct size from CFRuntimeClass instead of subtracting CFRuntimeBase from the struct. Instances were allocated too small and _isEditing held garbage. Correct both size macros and initialise _isEditing. The attribute cache is a bag keyed on the set pointer. Reusing a cached set left its count unchanged, so releasing one run freed a set that another run still referenced. Count cached references through a new GSHashTableAddValueCounted, and give CFBag the same counting so a repeated value raises its multiplicity. Growing and shrinking the run array used the wrong element count and left the capacity stale, and coalescing adjacent equal runs read past the start of the array. Correct the reallocation sizes and the coalesce bounds. Implement CFAttributedStringGetAttributeAndLongestEffectiveRange and CFAttributedStringGetAttributesAndLongestEffectiveRange, which returned NULL.
…ment CFAttributedStringCreateWithSubstring built its result by replacing the copy's characters with the source's whole string and then applying the source effective ranges directly, which reached past the end of the shorter copy. Seed the copy from the substring over range and shift each source run into the copy's coordinates. CFAttributedStringRemoveAttribute and CFAttributedStringReplaceAttributedString were empty. Remove the named attribute from every run overlapping the range, and splice the replacement string's characters and its own attributes into the range. CFAttributedStringCreateInlined stored a run count of one and numbered the runs from zero, so copying a string that held more than one run collapsed it onto the first run. Store the real run count and each run's own start index.
DTW-Thalion
force-pushed
the
fix-cfattributedstring-backlog
branch
from
July 24, 2026 15:22
4cbb9bb to
2de51ed
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CFAttributedStringCreateWithSubstring built its result by replacing the
copy's characters with the source's whole string and then applying the
source effective ranges directly, which reached past the end of the
shorter copy. It now seeds the copy from the substring over range and
shifts each source run into the copy's coordinates.
CFAttributedStringRemoveAttribute and CFAttributedStringReplaceAttributedString
were empty. The first removes the named attribute from every run
overlapping the range and leaves the other attributes in place. The
second replaces the characters in the range and applies the replacement
string's own attributes over them.
CFAttributedStringCreateInlined recorded a run count of one and numbered
the runs from zero, so CFAttributedStringCreateCopy collapsed any string
with more than one run onto its first run. It now stores the real run
count and each run's own start index.
Tests/CFAttributedString/backlog.m covers the three functions against
values taken from Apple CoreFoundation.
Dependencies and merge order
This builds on two other open pull requests and cannot stand alone. The
substring and removal paths go through the clearing branch of
CFAttributedStringSetAttributes (fixed in #150) and the multi-run run
storage (fixed in #151).
Merge order: #150 and #151 first (either order), then this one.
The branch is stacked on #151, so this diff currently also contains
#151's commit and the single ReplaceAttributesAtIndex line from #150.
Once #150 and #151 land I will rebase this onto master, after which it
reduces to the four changes above plus backlog.m. Please do not merge
this before #150 and #151.