Skip to content

O(n) replacement in NSString.replaceOccurrances #1620

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

gmilos
Copy link
Contributor

@gmilos gmilos commented Jun 29, 2018

@gmilos gmilos requested a review from phausler June 29, 2018 16:59
@spevans
Copy link
Contributor

spevans commented Jun 29, 2018

I don't see any existing tests for this method, could you add some test cases?

@gmilos
Copy link
Contributor Author

gmilos commented Jun 29, 2018

@spevans these tests exercise the change:
https://github.com/apple/swift-corelibs-foundation/blob/f0135163775f8d9852d1c96e2342a40a8a2a9d4f/TestFoundation/TestNSString.swift#L1217-L1252
The change is only meant to affect performance, and there aren't any perf tests for foundation (or an infrastructure for it), so I didn't really feel I can actually test my change properly. Correctness should be tested with the above though.

for cnt in 0..<numOccurrences {
let rangePtr = CFArrayGetValueAtIndex(findResults, backwards ? cnt : numOccurrences - cnt - 1)
replaceCharacters(in: NSRange(rangePtr!.load(as: CFRange.self)), with: replacement)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing this changes the behavior for subclassing

Copy link
Contributor Author

@gmilos gmilos Jul 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phausler You're right. Not using replaceCharacters will change behaviour for subclassing, but using it will make replaceOccurrences O(n*m). We'd need non-contiguous _storage to resolve that performance issue.

Can you suggest a way out?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the concrete class is NsMutableString itself so ‘type(of: self) === NSMutableString.self’ would be true for the concrete case but not the subclass case

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking the class seems like a reasonable workaround to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use something like the following but specialise the substring(with:) for NSMutableString to avoid the String() copy it uses:

var newStorage = ""
var currentIndex = 0
for cnt in 0..<numOccurrences {
    let rangePtr = CFArrayGetValueAtIndex(findResults, backwards ? numOccurrences - cnt - 1 : cnt)
    let range = NSRange(rangePtr!.load(as: CFRange.self))
    let copyRange = NSRange(location: currentIndex, length: range.location)
    newStorage += substring(with: copyRange)
    newStorage += replacement
    currentIndex = range.location + range.length
}
let copyRange = NSRange(location: currentIndex, length: length - currentIndex)
newStorage += substring(with: copyRange)
setString(newStorage)

@@ -1423,10 +1423,20 @@ extension NSMutableString {

if let findResults = CFStringCreateArrayWithFindResults(kCFAllocatorSystemDefault, _cfObject, target._cfObject, CFRange(searchRange), options._cfValue(true)) {
let numOccurrences = CFArrayGetCount(findResults)
let start = _storage.utf16.startIndex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_storage should only be accessed in concrete methods, it may not always be there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be tricky, without factory patterns which allow us to return a special NSMutableString subclass.

@gmilos gmilos force-pushed the SR-7749-NSString.replaceOccurrances-improvements branch from 8a75d5a to 23a8abf Compare July 31, 2018 14:52
@gmilos
Copy link
Contributor Author

gmilos commented Jul 31, 2018

@phausler @parkera I updated the patch (finally), testing for NSMutableString type, as per your suggestion.

@parkera
Copy link
Contributor

parkera commented Jul 31, 2018

@swift-ci please test and merge

@alblue
Copy link
Contributor

alblue commented Aug 7, 2018

@swift-ci please test and merge

@kevints
Copy link
Contributor

kevints commented Oct 9, 2018

@gmilos @alblue Looks like this was never merged?

@millenomi
Copy link
Contributor

Thanks for the ping. I’ll put it back on its merry way when CI unblocks.

@millenomi
Copy link
Contributor

@swift-ci please test and merge

@kevints
Copy link
Contributor

kevints commented Oct 11, 2018

@gmilos looks like this change needs to be rebased


override func character(at index: Int) -> unichar {
let start = _storage.utf16.startIndex
return _storage.utf16[start.advanced(by: index)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style of indexing is not available in 4.2 and 5.0. Can we fix this?

@alblue
Copy link
Contributor

alblue commented Nov 8, 2018

@gmilos can you rebase your change so that we can retest?

@weissi
Copy link
Contributor

weissi commented Mar 8, 2019

ping @gmilos

1 similar comment
@weissi
Copy link
Contributor

weissi commented Apr 12, 2019

ping @gmilos

kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 9, 2019
kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 10, 2019
kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 10, 2019
kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 10, 2019
kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 10, 2019
@kevints
Copy link
Contributor

kevints commented Oct 10, 2019

Thanks for the patch @gmilos, I've rebased it in #2534.

@kevints kevints closed this Oct 10, 2019
kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 15, 2019
…n corelibs-foundation

This supercedes swiftlang#1620.

(cherry picked from commit 4cd59de)
kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 15, 2019
…th:) in corelibs-foundation

This supercedes swiftlang#1620.

(cherry picked from commit 4cd59de)
kevints added a commit to kevints/swift-corelibs-foundation that referenced this pull request Oct 15, 2019
…th:) in corelibs-foundation

This supercedes swiftlang#1620.

(cherry picked from commit 4cd59de)
(cherry picked from commit 1e71e90)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants