summaryrefslogtreecommitdiff
path: root/src/mbgl/util
diff options
context:
space:
mode:
authorSudarsana Babu Nagineni <sudarsana.babu@mapbox.com>2018-07-30 15:30:57 +0300
committerSudarsana Babu Nagineni <sudarsana.babu@mapbox.com>2018-08-06 16:55:30 +0300
commit0130c58fdd74edbbfe668f3125a9377554d7d605 (patch)
tree6be1c1d25d146e9273f04f40f2283e18048c17ac /src/mbgl/util
parent7373abef92ed4911b91f9fca3d97784ed0d9e02e (diff)
downloadqtlocation-mapboxgl-0130c58fdd74edbbfe668f3125a9377554d7d605.tar.gz
Bump Mapbox GL Native
Bump version. mapbox-gl-native @ 377a6e42d687c419e6ae1012b8626336f5dfc1b6
Diffstat (limited to 'src/mbgl/util')
-rw-r--r--src/mbgl/util/chrono.cpp2
-rw-r--r--src/mbgl/util/event.cpp1
-rw-r--r--src/mbgl/util/http_header.cpp3
-rw-r--r--src/mbgl/util/interpolate.cpp3
-rw-r--r--src/mbgl/util/io.cpp25
-rw-r--r--src/mbgl/util/io.hpp4
-rw-r--r--src/mbgl/util/logging.cpp10
-rw-r--r--src/mbgl/util/stopwatch.hpp15
-rw-r--r--src/mbgl/util/tile_coordinate.hpp2
-rw-r--r--src/mbgl/util/tile_cover.cpp2
-rw-r--r--src/mbgl/util/tile_cover_impl.cpp129
-rw-r--r--src/mbgl/util/tile_cover_impl.hpp16
-rw-r--r--src/mbgl/util/token.hpp11
-rw-r--r--src/mbgl/util/url.cpp5
14 files changed, 144 insertions, 84 deletions
diff --git a/src/mbgl/util/chrono.cpp b/src/mbgl/util/chrono.cpp
index a880093b74..c304548cf1 100644
--- a/src/mbgl/util/chrono.cpp
+++ b/src/mbgl/util/chrono.cpp
@@ -1,6 +1,6 @@
#include <mbgl/util/chrono.hpp>
-#include <parsedate/parsedate.h>
+#include <parsedate/parsedate.hpp>
#include <cstdio>
#include <ctime>
diff --git a/src/mbgl/util/event.cpp b/src/mbgl/util/event.cpp
index 3a3be20f5c..0c08d72a8c 100644
--- a/src/mbgl/util/event.cpp
+++ b/src/mbgl/util/event.cpp
@@ -28,6 +28,7 @@ MBGL_DEFINE_ENUM(Event, {
{ Event::Android, "Android" },
{ Event::Crash, "Crash" },
{ Event::Glyph, "Glyph" },
+ { Event::Timing, "Timing" },
{ Event(-1), "Unknown" },
});
diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp
index 5921edfb14..4d9e2bf84c 100644
--- a/src/mbgl/util/http_header.cpp
+++ b/src/mbgl/util/http_header.cpp
@@ -7,7 +7,10 @@
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wshadow"
#pragma clang diagnostic push
+
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#pragma clang diagnostic ignored "-Wunknown-warning-option"
+#pragma clang diagnostic ignored "-Wtautological-constant-compare"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
diff --git a/src/mbgl/util/interpolate.cpp b/src/mbgl/util/interpolate.cpp
index 066fa9c462..6b5736f15f 100644
--- a/src/mbgl/util/interpolate.cpp
+++ b/src/mbgl/util/interpolate.cpp
@@ -13,7 +13,8 @@ float interpolationFactor(float base, Range<float> range, float z) {
} else if (base == 1.0f) {
return zoomProgress / zoomDiff;
} else {
- return (std::pow(base, zoomProgress) - 1) / (std::pow(base, zoomDiff) - 1);
+ return (std::pow(static_cast<double>(base), zoomProgress) - 1) /
+ (std::pow(static_cast<double>(base), zoomDiff) - 1);
}
}
diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp
index 6a6ed7b250..c84634ac88 100644
--- a/src/mbgl/util/io.cpp
+++ b/src/mbgl/util/io.cpp
@@ -2,6 +2,7 @@
#include <cstdio>
#include <cerrno>
+#include <cstring>
#include <iostream>
#include <sstream>
#include <fstream>
@@ -9,6 +10,10 @@
namespace mbgl {
namespace util {
+IOException::IOException(int err, const std::string& msg)
+ : std::runtime_error(msg + ": " + std::strerror(errno)), code(err) {
+}
+
void write_file(const std::string &filename, const std::string &data) {
FILE *fd = fopen(filename.c_str(), "wb");
if (fd) {
@@ -20,7 +25,7 @@ void write_file(const std::string &filename, const std::string &data) {
}
std::string read_file(const std::string &filename) {
- std::ifstream file(filename);
+ std::ifstream file(filename, std::ios::binary);
if (file.good()) {
std::stringstream data;
data << file.rdbuf();
@@ -31,7 +36,7 @@ std::string read_file(const std::string &filename) {
}
optional<std::string> readFile(const std::string &filename) {
- std::ifstream file(filename);
+ std::ifstream file(filename, std::ios::binary);
if (file.good()) {
std::stringstream data;
data << file.rdbuf();
@@ -42,9 +47,21 @@ optional<std::string> readFile(const std::string &filename) {
void deleteFile(const std::string& filename) {
const int ret = std::remove(filename.c_str());
- if (ret != 0) {
- throw IOException(errno, "failed to unlink file");
+ if (ret != 0 && errno != ENOENT) {
+ throw IOException(errno, "Could not delete file " + filename);
+ }
+}
+
+void copyFile(const std::string& destination, const std::string& source) {
+ std::ifstream src(source, std::ios::binary);
+ if (!src.good()) {
+ throw IOException(errno, "Cannot read file " + destination);
+ }
+ std::ofstream dst(destination, std::ios::binary);
+ if (!dst.good()) {
+ throw IOException(errno, "Cannot write file " + destination);
}
+ dst << src.rdbuf();
}
} // namespace util
diff --git a/src/mbgl/util/io.hpp b/src/mbgl/util/io.hpp
index 847271acf0..e628e82124 100644
--- a/src/mbgl/util/io.hpp
+++ b/src/mbgl/util/io.hpp
@@ -9,8 +9,7 @@ namespace mbgl {
namespace util {
struct IOException : std::runtime_error {
- IOException(int err, const char* msg) : std::runtime_error(msg), code(err) {
- }
+ IOException(int err, const std::string& msg);
const int code = 0;
};
@@ -19,6 +18,7 @@ std::string read_file(const std::string &filename);
optional<std::string> readFile(const std::string &filename);
void deleteFile(const std::string& filename);
+void copyFile(const std::string& destination, const std::string& source);
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/logging.cpp b/src/mbgl/util/logging.cpp
index 0552eb36cb..d322bd3670 100644
--- a/src/mbgl/util/logging.cpp
+++ b/src/mbgl/util/logging.cpp
@@ -38,8 +38,14 @@ void Log::record(EventSeverity severity, Event event, const char* format, ...) {
record(severity, event, -1, msg);
}
-void Log::record(EventSeverity severity, Event event, int64_t code) {
- record(severity, event, code, std::string());
+void Log::record(EventSeverity severity, Event event, int64_t code, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char msg[4096];
+ vsnprintf(msg, sizeof(msg), format, args);
+ va_end(args);
+
+ record(severity, event, code, std::string{ msg });
}
void Log::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) {
diff --git a/src/mbgl/util/stopwatch.hpp b/src/mbgl/util/stopwatch.hpp
index 6214dae958..0c91342a57 100644
--- a/src/mbgl/util/stopwatch.hpp
+++ b/src/mbgl/util/stopwatch.hpp
@@ -4,9 +4,24 @@
#include <mbgl/util/chrono.hpp>
#include <string>
+#include <sstream>
namespace mbgl {
namespace util {
+
+#ifdef MBGL_TIMING
+// Declare 'watch' as a shared_ptr so it can be captured by value in a lambda function
+#define MBGL_TIMING_START(watch) std::shared_ptr<util::stopwatch> watch = std::make_unique<util::stopwatch>(Event::Timing);
+#define MBGL_TIMING_FINISH(watch, message) \
+ do { \
+ std::stringstream messageStream; \
+ messageStream << message; \
+ watch->report(messageStream.str()); \
+ } while (0);
+#else
+#define MBGL_TIMING_START(watch)
+#define MBGL_TIMING_FINISH(watch, message)
+#endif
#ifndef DISABLE_STOPWATCH
class stopwatch {
diff --git a/src/mbgl/util/tile_coordinate.hpp b/src/mbgl/util/tile_coordinate.hpp
index bcd1c8444f..b6bdc5f590 100644
--- a/src/mbgl/util/tile_coordinate.hpp
+++ b/src/mbgl/util/tile_coordinate.hpp
@@ -20,7 +20,7 @@ public:
static TileCoordinate fromLatLng(double zoom, const LatLng& latLng) {
const double scale = std::pow(2.0, zoom);
- return { Projection::project(latLng, scale) / double(util::tileSize), zoom };
+ return { Projection::project(latLng, scale) / util::tileSize, zoom };
}
static TileCoordinate fromScreenCoordinate(const TransformState& state, double zoom, const ScreenCoordinate& screenCoordinate) {
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index 488e6b88ce..3f39e53d40 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -130,7 +130,7 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl,
} // namespace
int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) {
- zoom += std::log(util::tileSize / size) / std::log(2);
+ zoom += ::log2(util::tileSize / size);
if (type == style::SourceType::Raster || type == style::SourceType::Video) {
return ::round(zoom);
} else {
diff --git a/src/mbgl/util/tile_cover_impl.cpp b/src/mbgl/util/tile_cover_impl.cpp
index b3fc07f7dd..799ff2666a 100644
--- a/src/mbgl/util/tile_cover_impl.cpp
+++ b/src/mbgl/util/tile_cover_impl.cpp
@@ -17,8 +17,7 @@ struct TileSpan {
bool winding;
};
-
-// Find the first local minimum going forward in the list.
+// Reorder a ring of points such that it starts at a point with a local minimum y-coordinate
void start_list_on_local_minimum(PointList& points) {
auto prev_pt = std::prev(points.end(), 2);
auto pt = points.begin();
@@ -33,6 +32,8 @@ void start_list_on_local_minimum(PointList& points) {
next_pt++;
if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
}
+ if (pt == points.end())
+ return;
//Re-close linear rings with first_pt = last_pt
if (points.back() == points.front()) {
points.pop_back();
@@ -42,37 +43,25 @@ void start_list_on_local_minimum(PointList& points) {
}
//Create a bound towards a local maximum point, starting from pt.
+// Traverse from current pt until the next pt changes y-direction, and copy
+// all points from start to end (inclusive) into a Bound.
Bound create_bound_towards_maximum(PointList& points, PointList::iterator& pt) {
if (std::distance(pt, points.end()) < 2) { return {}; }
- if (std::distance(pt, points.end()) == 2) {
- Bound bnd;
- if (pt->y < std::next(pt)->y) {
- std::copy(pt, points.end(), std::back_inserter(bnd.points));
- bnd.winding = true;
- }
- else {
- std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points));
- bnd.winding = false;
- }
- pt = points.end();
- return bnd;
- }
+
const auto begin = pt;
- auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt);
- auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt);
- while (pt != points.end()) {
- if ((pt->y >= prev_pt->y) &&
- (pt->y > next_pt->y )) {
- break;
- }
- prev_pt = pt;
+ auto next_pt = std::next(begin);
+ while (pt->y <= next_pt->y) {
pt++;
next_pt++;
- if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ if (next_pt == points.end()) { pt++; break; }
+ }
+
+ const auto pt_distance = std::distance(begin, next_pt);
+ if (pt_distance < 2) {
+ return {};
}
Bound bnd;
- if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; };
bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt)));
std::copy(begin, next_pt, std::back_inserter(bnd.points));
bnd.winding = true;
@@ -80,37 +69,24 @@ Bound create_bound_towards_maximum(PointList& points, PointList::iterator& pt) {
}
//Create a bound towards a local minimum point, starting from pt.
+// Traverse from current pt until the next pt changes y-direction, and copy
+// all points from start to end (inclusive) into a Bound.
Bound create_bound_towards_minimum(PointList& points, PointList::iterator& pt) {
if (std::distance(pt, points.end()) < 2) { return {}; }
- if (std::distance(pt, points.end()) == 2) {
- Bound bnd;
- if (pt->y < std::next(pt)->y) {
- std::copy(pt, points.end(), std::back_inserter(bnd.points));
- bnd.winding = true;
- }
- else {
- std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points));
- bnd.winding = false;
- }
- pt = points.end();
- return bnd;
- }
+
auto begin = pt;
- auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt);
- auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt);
- while (pt != points.end()) {
- if ((pt->y <= prev_pt->y) &&
- (pt->y < next_pt->y)) {
- break;
- }
- prev_pt = pt;
+ auto next_pt = std::next(begin);
+ while (pt->y > next_pt->y) {
pt++;
next_pt++;
- if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ if (next_pt == points.end()) { pt++; break; }
}
+ const auto pt_distance = std::distance(begin, next_pt);
+ if (pt_distance < 2) {
+ return {};
+ }
Bound bnd;
- if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; };
bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt)));
//For bounds that start at a max, reverse copy so that all bounds start at a min
std::reverse_copy(begin, next_pt, std::back_inserter(bnd.points));
@@ -118,10 +94,14 @@ Bound create_bound_towards_minimum(PointList& points, PointList::iterator& pt) {
return bnd;
}
-//Build a map of bounds and their starting Y tile coordinate.
+// Given a set of points (ring or list) representing a shape, compute a set of
+// Bounds, where each Bound represents edges going from a local minima to a local
+// maxima point. The BoundsMap is an edge table indexed on the starting Y-tile
+// of each Bound.
void build_bounds_map(PointList& points, uint32_t maxTile, BoundsMap& et, bool closed = false) {
if (points.size() < 2) return;
- //While traversing closed rings, start the bounds at a local minimum
+ //While traversing closed rings, start the bounds at a local minimum.
+ // (For linestrings the starting point is always a local maxima/minima)
if (closed) {
start_list_on_local_minimum(points);
}
@@ -131,12 +111,12 @@ void build_bounds_map(PointList& points, uint32_t maxTile, BoundsMap& et, bool c
Bound to_max = create_bound_towards_maximum(points, pointsIter);
Bound to_min = create_bound_towards_minimum(points, pointsIter);
- if (to_max.points.size() > 0) {
+ if (to_max.points.size() >= 2) {
// Projections may result in values beyond the bounds, clamp to max tile coordinates
const auto y = static_cast<uint32_t>(std::floor(clamp(to_max.points.front().y, 0.0, (double)maxTile)));
et[y].push_back(to_max);
}
- if (to_min.points.size() > 0) {
+ if (to_min.points.size() >= 2) {
const auto y = static_cast<uint32_t>(std::floor(clamp(to_min.points.front().y, 0.0, (double)maxTile)));
et[y].push_back(to_min);
}
@@ -149,16 +129,19 @@ void update_span(TileSpan& xp, double x) {
xp.xmax = std::max(xp.xmax, static_cast<int32_t>(std::ceil(x)));
}
-//Build a vector of X tile-coordinates spanned by each bound.
-std::vector<TileSpan> scan_row(uint32_t y, Bounds& aet) {
+// Use the active bounds, an accumulation of all bounds that enter the y tile row,
+// or start in that row.
+// Iterate all points of a bound until it exits the row (or ends) and compute the
+// set of X tiles it spans across. The winding direction of the bound is also
+// captured for each span to later fill tiles between bounds for polygons
+std::vector<TileSpan> scan_row(uint32_t y, Bounds& activeBounds) {
std::vector<TileSpan> tile_range;
- tile_range.reserve(aet.size());
+ tile_range.reserve(activeBounds.size());
- for(Bound& b: aet) {
+ for(Bound& b: activeBounds) {
TileSpan xp = { INT_MAX, 0, b.winding };
double x;
const auto numEdges = b.points.size() - 1;
- assert(numEdges >= 1);
while (b.currentPoint < numEdges) {
x = b.interpolate(y);
update_span(xp, x);
@@ -170,7 +153,7 @@ std::vector<TileSpan> scan_row(uint32_t y, Bounds& aet) {
x = b.interpolate(y+1);
update_span(xp, x);
break;
- } else if(b.currentPoint == numEdges - 1) {
+ } else if (b.currentPoint == numEdges - 1) {
// For last edge, consider x-intercept at the end of the edge.
x = p1.x;
update_span(xp, x);
@@ -181,11 +164,11 @@ std::vector<TileSpan> scan_row(uint32_t y, Bounds& aet) {
}
// Erase bounds in the active table whose current edge ends inside this row,
// or there are no more edges
- auto bound = aet.begin();
- while (bound != aet.end()) {
+ auto bound = activeBounds.begin();
+ while (bound != activeBounds.end()) {
if ( bound->currentPoint == bound->points.size() - 1 &&
bound->points[bound->currentPoint].y <= y+1) {
- bound = aet.erase(bound);
+ bound = activeBounds.erase(bound);
} else {
bound++;
}
@@ -225,7 +208,7 @@ struct BuildBoundsMap {
BoundsMap operator()(const Point<double>&p) const {
Bound bnd;
auto point = p;
- if(project) {
+ if (project) {
point = Projection::project(LatLng{p.y, p.x}, zoom);
}
bnd.points.insert(bnd.points.end(), 2, point);
@@ -241,7 +224,7 @@ struct BuildBoundsMap {
for (const Point<double>& p: points) {
Bound bnd;
auto point = p;
- if(project) {
+ if (project) {
point = Projection::project(LatLng{p.y, p.x}, zoom);
}
bnd.points.insert(bnd.points.end(), 2, point);
@@ -302,20 +285,26 @@ TileCover::Impl::Impl(int32_t z, const Geometry<double>& geom, bool project)
tileX = tileXSpans.front().first;
}
+// Aggregate all Bounds that start in or enter into the next tileY row. Multi-geoms
+// may have discontinuity in the BoundMap, so skip forward to the next tileY row
+// when the current/next row has no more bounds in it.
+// Use scan_row to generate the tileX spans. Merge spans to avoid duplicate tiles
+// in TileCoverImpl::next(). For closed geometry, use the non-zero rule to expand
+// (fill) tiles between pairs of spans.
void TileCover::Impl::nextRow() {
- // Update AET for next row
+ // Update activeBounds for next row
if (currentBounds != boundsMap.end()) {
if (activeBounds.size() == 0 && currentBounds->first > tileY) {
//For multi-geoms: use the next row with an edge table starting point
tileY = currentBounds->first;
}
if (tileY == currentBounds->first) {
-
- std::move(currentBounds->second.begin(), currentBounds->second.end(), std::back_inserter(activeBounds));
+ std::move(currentBounds->second.begin(), currentBounds->second.end(),
+ std::back_inserter(activeBounds));
currentBounds++;
}
}
- //Scan aet and update currenRange with x_min, x_max pairs
+ //Scan the active bounds and update currentRange with x_min, x_max pairs
auto xps = util::scan_row(tileY, activeBounds);
if (xps.size() == 0) {
return;
@@ -339,7 +328,9 @@ void TileCover::Impl::nextRow() {
}
bool TileCover::Impl::hasNext() const {
- return (!tileXSpans.empty() && tileX < tileXSpans.front().second && tileY < (1u << zoom));
+ return (!tileXSpans.empty()
+ && tileX < tileXSpans.front().second
+ && tileY < (1u << zoom));
}
optional<UnwrappedTileID> TileCover::Impl::next() {
@@ -350,7 +341,7 @@ optional<UnwrappedTileID> TileCover::Impl::next() {
tileX++;
if (tileX >= tileXSpans.front().second) {
tileXSpans.pop();
- if(tileXSpans.empty()) {
+ if (tileXSpans.empty()) {
tileY++;
nextRow();
}
diff --git a/src/mbgl/util/tile_cover_impl.hpp b/src/mbgl/util/tile_cover_impl.hpp
index 7c16718984..e9c06e44aa 100644
--- a/src/mbgl/util/tile_cover_impl.hpp
+++ b/src/mbgl/util/tile_cover_impl.hpp
@@ -60,6 +60,22 @@ struct Bound {
}
};
+// Implements a modified scan-line algorithm to provide a streaming interface for
+// tile cover on arbitrary shapes.
+// A `BoundsMap` is genereted from the input geometry where each tuple indicates
+// the set of Bounds that start at a y tile coordinate. Each bound represents
+// a chain of edges from a local y-minima to a local y-maxima.
+// For each row, the activeBounds list aggregates all bounds that enter into or
+// begin in that row. This running list of bounds is scanned, capturing the
+// x-coordinates spanned by edges in a bound until the bound exits the row (or
+// ends). The result is a set of (possibly overlapping) min,max pairs of x coordinates
+// (spans). In the simplest case a span represents the x-coordinates at which a
+// single edge intersects the top and bottom of a tile row. Interior tiles of a
+// polygon are captured by merging spans using the non-zero rule.
+// The result of a scan using `nextRow()` is a list of spans (tileXSpans) of x-coordinates
+// that includes edges and interiors of polygons.
+// next() returns a tileID for each x-coordinate from (first, second] in each
+// span in tileXSpans.
class TileCover::Impl {
public:
Impl(int32_t z, const Geometry<double>& geom, bool project = true);
diff --git a/src/mbgl/util/token.hpp b/src/mbgl/util/token.hpp
index 149661e47e..dea12f9412 100644
--- a/src/mbgl/util/token.hpp
+++ b/src/mbgl/util/token.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mbgl/util/optional.hpp>
+
#include <map>
#include <string>
#include <algorithm>
@@ -25,7 +27,14 @@ std::string replaceTokens(const std::string &source, const Lookup &lookup) {
if (pos != end) {
for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++);
if (brace != end && *brace == '}') {
- result.append(lookup({ pos + 1, brace }));
+ std::string key { pos + 1, brace };
+ if (optional<std::string> replacement = lookup(key)) {
+ result.append(*replacement);
+ } else {
+ result.append("{");
+ result.append(key);
+ result.append("}");
+ }
pos = brace + 1;
} else {
result.append(pos, brace);
diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp
index 1f6dab9639..a4263502ef 100644
--- a/src/mbgl/util/url.cpp
+++ b/src/mbgl/util/url.cpp
@@ -130,7 +130,7 @@ Path::Path(const std::string& str, const size_t pos, const size_t count)
}
std::string transformURL(const std::string& tpl, const std::string& str, const URL& url) {
- auto result = util::replaceTokens(tpl, [&](const std::string& token) -> std::string {
+ auto result = util::replaceTokens(tpl, [&](const std::string& token) -> optional<std::string> {
if (token == "path") {
return str.substr(url.path.first, url.path.second);
} else if (token == "domain") {
@@ -146,8 +146,9 @@ std::string transformURL(const std::string& tpl, const std::string& str, const U
} else if (token == "extension") {
const Path path(str, url.path.first, url.path.second);
return str.substr(path.extension.first, path.extension.second);
+ } else {
+ return {};
}
- return "";
});
// Append the query string if it exists.