Prevent creation of invalid range in XBounds (fixes #4184 and #4049) #4592
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.
Issue Links 🔗
The issues #4184 and #4049 mention crashes which are caused by an invalid range in the XBounds. This fix might not really solve all underlying problems, but at least it will make it stop crashing. Might need some further investigation.
Goals ⚽
This PR should prevent crashes due to invalid ranges in BarLineScatterCandleBubbleRenderer.XBounds (max > min). An negative range will cause a crash when the iterator is called upon.
I traced the problem to the
set(chart, dataset, animator)
function inBarLineScatterCandleBubbleRenderer.XBounds
. IfentryFrom
is not nil butentryTo
is, and there are entries outside/below the visible range (or when the entries are not sorted), then theentryIndex(entry:)
function will/can return an index larger than zero. This results inmin
becoming larger than zero whilemax
will default to 0. This creates an invalid range.Implementation Details 🚧
I've made sure that when the
entryFrom
value is nil while theentryTo
values is non-nil, that the min value will be the max value (instead of the default 0). And whenentryFrom
is non-nil butentryTo
is nil, then the max value will be set to the min value to prevent a negative/invalid range.Testing Details 🔍
The test uses a ScatterChartView with a custom
axisMinimum
andaxisMaximum
and a simple dataset where two values are below/outside the range. The last value lies exactly on the lower range. This results in a nil value forentryTo
and a non-nil value forentryFrom
. And this would result in an invalid range with the previous code.The call stack is as follows:
entryForXValue(_:closestToY:rounding:) => entryIndex(x:closestToY:rounding:) => entryIndex(x:closestToY:rounding:) => Collection.partitioningIndex
.The
partitioningIndex
function relies on the premise that the collection is already partitioned/sorted(!). Would be good to add a notion somewhere in the documentation that it is a good idea to keep datasets sorted, so if anybody has a good idea of where I could put that, I'd be happy to add it in the PR. I don't have a lot of experience with the code base so I don't know exactly the ramifications of the proposed change, but at least it prevents some nasty crashes and I haven't seen any side effects yet in my (limited) testing.