Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[ios, macos] Use std::vector for MGLMultiPoint coordinate storage #6890

Merged
merged 5 commits into from
Nov 14, 2016

Conversation

boundsj
Copy link
Contributor

@boundsj boundsj commented Nov 3, 2016

Fixes #6580

cc @incanus @1ec5

@boundsj boundsj added iOS Mapbox Maps SDK for iOS refactor macOS Mapbox Maps SDK for macOS annotations Annotations on iOS and macOS or markers on Android release blocker Blocks the next final release labels Nov 3, 2016
@boundsj boundsj added this to the ios-v3.4.0 milestone Nov 3, 2016
@boundsj boundsj self-assigned this Nov 3, 2016
@mention-bot
Copy link

@boundsj, thanks for your PR! By analyzing the history of the files in this pull request, we identified @incanus, @1ec5 and @jfirebaugh to be potential reviewers.

@boundsj boundsj added the ⚠️ DO NOT MERGE Work in progress, proof of concept, or on hold label Nov 3, 2016
@boundsj
Copy link
Contributor Author

boundsj commented Nov 3, 2016

This won't actually work in practice without #6888 😬

@@ -24,40 +25,24 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUI

if (self)
{
[self setupWithCoordinates:coords count:count];
_count = count;
Copy link
Contributor

Choose a reason for hiding this comment

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

We don’t need to maintain a _count ivar as parallel state. Instead, implement -count as _coordinates.size().

@@ -24,40 +25,24 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUI

if (self)
{
[self setupWithCoordinates:coords count:count];
_count = count;
_coordinates.reserve(_count);
Copy link
Contributor

Choose a reason for hiding this comment

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

You can populate _coordinates in one go with some pointer arithmetic:

_coordinates = { coords, coords + count };

or:

_coordinates.assign(coords, coords + count);

- (CLLocationCoordinate2D)coordinate
{
assert(_count > 0);

return CLLocationCoordinate2DMake(_coordinates[0].latitude, _coordinates[0].longitude);
CLLocationCoordinate2D coordinate = _coordinates.at(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Structs like CLLocationCoordinate2D are passed by value, so there’s no need to explicitly copy the struct below. Just return this value (or _coordinates.front()).

{
free(_coordinates);
}

- (CLLocationCoordinate2D)coordinate
{
assert(_count > 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

While you’re here, can you change this to an NSAssert() so we get better diagnostics?

@@ -83,19 +73,14 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range

for (NSUInteger i = range.location; i < range.location + range.length; i++)
Copy link
Contributor

Choose a reason for hiding this comment

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

Replace this whole for loop with:

std::copy(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range), coords);

(Note the use of begin() instead of end() in the second parameter.)

[NSException raise:NSRangeException format:@"Reuse of existing coordinates array %p not supported", coords];
}
else if (range.length == 0)
if (range.length == 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

If the developer tries to replace no coordinates, this method should be a no-op instead of raising an exception. That may help to minimize the number of special cases the developer would need to implement.

@@ -24,40 +25,24 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUI

if (self)
Copy link
Contributor

Choose a reason for hiding this comment

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

It’s pretty meaningless to have a multipoint of zero points. In fact, we assert as much below. So we should detect that case early, in here, asserting if count is zero.

@@ -107,22 +92,29 @@ - (void)replaceCoordinatesInRange:(NSRange)range withCoordinates:(CLLocationCoor
}

[self willChangeValueForKey:@"coordinates"];
if (NSMaxRange(range) <= _count)
NSUInteger newCount = NSMaxRange(range);
Copy link
Contributor

@1ec5 1ec5 Nov 3, 2016

Choose a reason for hiding this comment

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

The way this method was implemented in the first place makes it possible to replace a segment at the end of the multipoint while appending. This is an antipattern that tends to mask off-by-one errors in client code. Methods such as -[NSMutableData replaceBytesInRange:withBytes:] raise an NSRangeException if the range exceeds the bounds of the receiver. I think this class should do likewise.

That would allow us to simplify this entire method to:

std::copy(coords, coords + range.length, _coordinates.begin() + range.location);

Plus an exception if NSMaxRange(range) > _coordinates.size() and the KVO guards. We’d need a separate -appendCoordinates:count::

_coordinates.insert(_coordinates.end(), coords, coords + count);

In the unlikely event that the developer really does need to replace the tail end while appending, they can do it in two steps. With #6583, we’ll introduce -replaceCoordinatesInRange:withCoordinates:count: to make it doable in one step:

_coordinates.erase(_coordinates.begin() + range.location, _coordinates.begin() + NSMaxRange(range));
_coordinates.insert(_coordinates.begin() + range.location, coords, coords + count);

(This is the most naïve approach; there are more performant approaches.)

@1ec5
Copy link
Contributor

1ec5 commented Nov 3, 2016

Even though MGLPointCollection is immutable, it would be great if we could migrate it over to std::vector too for consistency.

- (void)appendCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count
{
[self willChangeValueForKey:@"coordinates"];
_coordinates.insert(_coordinates.end(), *coords);
Copy link
Contributor

Choose a reason for hiding this comment

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

This appends only the first coordinate in the passed-in array. To append the entire array:

_coordinates.insert(_coordinates.end(), coords, coords + count);

{
CLLocationCoordinate2D *coords = self.coordinates;
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
for (NSUInteger i = 0; i < [self pointCount]; i++)
Copy link
Contributor

Choose a reason for hiding this comment

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

This will call -pointCount on every iteration. Use C++ fast enumeration instead:

for (auto coordinate : _coordinates) {
    …
}

@boundsj boundsj added ✓ ready for review and removed ⚠️ DO NOT MERGE Work in progress, proof of concept, or on hold labels Nov 10, 2016
@boundsj boundsj force-pushed the boundsj-use-vector-for-multipoint-coords branch from 887ebf5 to ccc14b8 Compare November 10, 2016 07:09
@boundsj boundsj merged commit 44e3988 into release-ios-v3.4.0 Nov 14, 2016
@boundsj boundsj deleted the boundsj-use-vector-for-multipoint-coords branch November 14, 2016 17:34
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
annotations Annotations on iOS and macOS or markers on Android iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS refactor release blocker Blocks the next final release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants