From 21208f84df2008d04d25b442dc97b02e8d45fe90 Mon Sep 17 00:00:00 2001 From: Mike Morris Date: Mon, 2 Feb 2015 16:56:48 -0500 Subject: [PATCH 01/50] bump mason --- .mason | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mason b/.mason index 3833ddbd7..3d9c0c829 160000 --- a/.mason +++ b/.mason @@ -1 +1 @@ -Subproject commit 3833ddbd71dfed6d6325429518bb22bbf94b7a97 +Subproject commit 3d9c0c829f19b3f133ea2fdbe0be8b2ba9ef465f From 33b4922220d19a5049072b59cbad01358099b1cf Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 2 Feb 2015 15:18:01 -0800 Subject: [PATCH 02/50] Adapt to https://github.com/mapbox/mason/commit/c104c2f9721b793535e4ec658afe2582e57be164 and https://github.com/mapbox/mason/commit/49fd47774032bf0f80efaad0143db3634385d2bf --- gyp/mbgl-android.gypi | 2 ++ gyp/mbgl-linux.gypi | 2 ++ 2 files changed, 4 insertions(+) diff --git a/gyp/mbgl-android.gypi b/gyp/mbgl-android.gypi index 0edc0ef43..85115910c 100644 --- a/gyp/mbgl-android.gypi +++ b/gyp/mbgl-android.gypi @@ -50,6 +50,8 @@ 'link_settings': { 'libraries': [ '<@(png_static_libs)', + '<@(jpeg_static_libs)', + '<@(nu_static_libs)' ], }, 'conditions': [ diff --git a/gyp/mbgl-linux.gypi b/gyp/mbgl-linux.gypi index 2bcc03ffa..c515e68be 100644 --- a/gyp/mbgl-linux.gypi +++ b/gyp/mbgl-linux.gypi @@ -48,6 +48,8 @@ 'link_settings': { 'libraries': [ '<@(png_static_libs)', + '<@(jpeg_static_libs)', + '<@(nu_static_libs)' ], }, 'conditions': [ From 33f611cdf8d22aacdb252dee7f262a644af10882 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 3 Feb 2015 13:54:12 +1100 Subject: [PATCH 03/50] Fix -W flags for boost Fixes #801 --- android/scripts/common.sh | 2 -- gyp/common.gypi | 7 +------ include/mbgl/platform/default/jpeg_reader.hpp | 6 ++++++ include/mbgl/platform/default/png_reader.hpp | 6 ++++++ scripts/flags.sh | 3 --- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/android/scripts/common.sh b/android/scripts/common.sh index b594fd244..5c214fe25 100755 --- a/android/scripts/common.sh +++ b/android/scripts/common.sh @@ -7,8 +7,6 @@ NAME=$1 export JOBS=2 export CC=clang CXX=clang++ -export CXXFLAGS="-Wno-unknown-warning-option -Wno-unused-local-typedefs -Wno-unknown-pragmas ${CXXFLAGS}"; - export MASON_PLATFORM=android mkdir ./android/java/app/src/main/res/raw/ diff --git a/gyp/common.gypi b/gyp/common.gypi index d219ba2f9..92cfb2ab1 100644 --- a/gyp/common.gypi +++ b/gyp/common.gypi @@ -22,18 +22,13 @@ '-Wno-variadic-macros', '-frtti', '-fexceptions', - '-Wno-error=unused-parameter', ], 'GCC_WARN_PEDANTIC': 'YES', 'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE', }, }], ['OS=="linux"', { - 'cflags_cc': [ - '-Wno-unknown-pragmas', # We are using '#pragma mark', but it is only available on Darwin. - '-Wno-literal-suffix', # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61653 - '-Wno-unknown-warning-option', - '-Wno-error=unused-parameter', + 'cflags_cc': ['-Wno-unknown-pragmas', # We are using '#pragma mark', but it is only available on Darwin. ], }], ], diff --git a/include/mbgl/platform/default/jpeg_reader.hpp b/include/mbgl/platform/default/jpeg_reader.hpp index 7122f24cd..e041c0a5d 100644 --- a/include/mbgl/platform/default/jpeg_reader.hpp +++ b/include/mbgl/platform/default/jpeg_reader.hpp @@ -9,7 +9,13 @@ extern "C" #include } +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wshadow" +#endif #include +#pragma GCC diagnostic pop namespace mbgl { namespace util { diff --git a/include/mbgl/platform/default/png_reader.hpp b/include/mbgl/platform/default/png_reader.hpp index 66debd4fb..649e7a75c 100644 --- a/include/mbgl/platform/default/png_reader.hpp +++ b/include/mbgl/platform/default/png_reader.hpp @@ -11,7 +11,13 @@ extern "C" #include #include +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wshadow" +#endif #include +#pragma GCC diagnostic pop namespace mbgl { namespace util { diff --git a/scripts/flags.sh b/scripts/flags.sh index 8703677d0..10cc88e5c 100755 --- a/scripts/flags.sh +++ b/scripts/flags.sh @@ -30,6 +30,3 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then # fi # fi fi - -# turn off a few warnings -export CXXFLAGS="-Wno-unknown-warning-option -Wno-unused-local-typedefs -Wno-unknown-pragmas ${CXXFLAGS}"; From 23dd403a25938f3257b63e3b6a8721d62afaa4ce Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Feb 2015 13:17:26 -0800 Subject: [PATCH 04/50] add wrapped padding to sprite for repeating images fixes #798 js: e732e784e41bef35bd40de27fdbfa3e8f9588038 --- src/mbgl/geometry/sprite_atlas.cpp | 37 +++++++++++++++++++---------- src/mbgl/geometry/sprite_atlas.hpp | 4 ++-- src/mbgl/renderer/symbol_bucket.cpp | 2 +- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index 35365c7e4..7b6184ae7 100644 --- a/src/mbgl/geometry/sprite_atlas.cpp +++ b/src/mbgl/geometry/sprite_atlas.cpp @@ -67,12 +67,24 @@ bool SpriteAtlas::resize(const float newRatio) { void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y, uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y, - const int width, const int height) { - src += src_y * src_stride + src_x; + const int width, const int height, const bool wrap) { dst += dst_y * dst_stride + dst_x; - for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) { - for (int x = 0; x < width; x++) { - dst[x] = src[x]; + if (wrap) { + dst -= dst_stride; + int srcI; + // add 1 pixel wrapped padding on each side of the image + for (int y = 0; y < height; y++, srcI = (((y + height) % height) + src_y) * src_stride + src_x, dst += dst_stride) { + for (int x = -1; x <= width; x++) { + dst[x] = src[srcI + ((x + width) % width)]; + } + } + + } else { + src += src_y * src_stride + src_x; + for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) { + for (int x = 0; x < width; x++) { + dst[x] = src[x]; + } } } } @@ -97,7 +109,7 @@ Rect SpriteAtlas::allocateImage(const size_t pixel_width return rect; } -Rect SpriteAtlas::getImage(const std::string& name) { +Rect SpriteAtlas::getImage(const std::string& name, const bool wrap) { std::lock_guard lock(mtx); auto rect_it = images.find(name); @@ -120,7 +132,7 @@ Rect SpriteAtlas::getImage(const std::string& name) { images.emplace(name, rect); - copy(rect, pos); + copy(rect, pos, wrap); return rect; } @@ -128,7 +140,7 @@ Rect SpriteAtlas::getImage(const std::string& name) { SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repeating) { std::lock_guard lock(mtx); - Rect rect = getImage(name); + Rect rect = getImage(name, repeating); if (repeating) { // When the image is repeating, get the correct position of the image, rather than the // one rounded up to 4 pixels. @@ -157,7 +169,7 @@ void SpriteAtlas::allocate() { } } -void SpriteAtlas::copy(const Rect& dst, const SpritePosition& src) { +void SpriteAtlas::copy(const Rect& dst, const SpritePosition& src, const bool wrap) { if (!sprite->raster) return; const uint32_t *src_img = reinterpret_cast(sprite->raster->getData()); if (!src_img) return; @@ -174,7 +186,8 @@ void SpriteAtlas::copy(const Rect& dst, const SpritePosition& src) { /* dest x */ dst.x * pixelRatio, /* dest y */ dst.y * pixelRatio, /* icon dimension */ src.width, - /* icon dimension */ src.height + /* icon dimension */ src.height, + /* wrap padding */ wrap ); dirty = true; @@ -188,7 +201,7 @@ void SpriteAtlas::setSprite(util::ptr sprite_) { if (!sprite->isLoaded()) return; util::erase_if(uninitialized, [this](const std::string &name) { - Rect dst = getImage(name); + Rect dst = getImage(name, false); const SpritePosition& src = sprite->getSpritePosition(name); if (!src) { if (debug::spriteWarnings) { @@ -198,7 +211,7 @@ void SpriteAtlas::setSprite(util::ptr sprite_) { } if (src.width == dst.w * pixelRatio && src.height == dst.h * pixelRatio && src.pixelRatio == pixelRatio) { - copy(dst, src); + copy(dst, src, false); return true; } else { if (debug::spriteWarnings) { diff --git a/src/mbgl/geometry/sprite_atlas.hpp b/src/mbgl/geometry/sprite_atlas.hpp index ebfd69edf..079c15cef 100644 --- a/src/mbgl/geometry/sprite_atlas.hpp +++ b/src/mbgl/geometry/sprite_atlas.hpp @@ -46,7 +46,7 @@ public: // This getter attempts to read the image from the sprite if it is already loaded. // In that case, it copies it into the sprite atlas and returns the dimensions. // Otherwise, it returns a 0/0/0/0 rect. - Rect getImage(const std::string& name); + Rect getImage(const std::string& name, const bool wrap); SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false); @@ -66,7 +66,7 @@ public: private: void allocate(); Rect allocateImage(size_t width, size_t height); - void copy(const Rect& dst, const SpritePosition& src); + void copy(const Rect& dst, const SpritePosition& src, const bool wrap); std::recursive_mutex mtx; float pixelRatio = 1.0f; diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index c6a7c8c4e..a5043a373 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -195,7 +195,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress // if feature has icon, get sprite atlas position if (feature.sprite.length()) { sprite.waitUntilLoaded(); - image = spriteAtlas.getImage(feature.sprite); + image = spriteAtlas.getImage(feature.sprite, false); if (sprite.getSpritePosition(feature.sprite).sdf) { sdfIcons = true; From 18d44402d42e68e0c5b6cad473103ea42d467ba7 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 3 Feb 2015 11:42:54 -0800 Subject: [PATCH 05/50] Fix OS X build --- ios/mapbox-gl-cocoa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa index 2e5bfbdcd..ff74b4e6b 160000 --- a/ios/mapbox-gl-cocoa +++ b/ios/mapbox-gl-cocoa @@ -1 +1 @@ -Subproject commit 2e5bfbdcd54c893864c263b41a27846627e10f0c +Subproject commit ff74b4e6b86f7caff021070482bff07511a7c0a7 From 35dbbc4d4266d6629a362e15dbef9cb121973625 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 3 Feb 2015 14:19:02 -0800 Subject: [PATCH 06/50] Mark mips-64 as allowed failure Refs #802 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9dccffe9e..2eb344834 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,6 +37,10 @@ matrix: - os: osx env: BUILDTYPE=Debug JOBS=8 compiler: clang + allow_failures: + - os: linux + env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=mips-64 TESTMUNK=no + compiler: clang env: global: From 89bbd29278b40e8de1e369ee54325d4d3098bf65 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 3 Feb 2015 13:54:12 +1100 Subject: [PATCH 07/50] Use std::chrono --- android/cpp/jni.cpp | 58 ++++++++-------- android/cpp/native_map_view.cpp | 2 +- .../java/com/mapbox/mapboxgl/lib/MapView.java | 20 +++--- .../mapbox/mapboxgl/lib/NativeMapView.java | 50 +++++++------- android/scripts/common.sh | 2 - gyp/common.gypi | 7 +- include/mbgl/android/native_map_view.hpp | 2 +- include/mbgl/map/map.hpp | 28 ++++---- include/mbgl/map/transform.hpp | 28 ++++---- include/mbgl/map/view.hpp | 4 +- include/mbgl/platform/default/glfw_view.hpp | 3 +- .../mbgl/platform/default/headless_view.hpp | 2 +- include/mbgl/platform/default/jpeg_reader.hpp | 6 ++ include/mbgl/platform/default/png_reader.hpp | 6 ++ include/mbgl/util/time.hpp | 28 -------- ios/mapbox-gl-cocoa | 2 +- platform/default/glfw_view.cpp | 8 +-- platform/default/headless_view.cpp | 2 +- scripts/flags.sh | 3 - src/mbgl/map/map.cpp | 47 +++++++------ src/mbgl/map/source.hpp | 4 +- src/mbgl/map/transform.cpp | 69 +++++++++---------- src/mbgl/renderer/frame_history.cpp | 23 ++++--- src/mbgl/renderer/frame_history.hpp | 12 ++-- src/mbgl/renderer/painter.cpp | 7 +- src/mbgl/renderer/painter.hpp | 7 +- src/mbgl/renderer/painter_fill.cpp | 4 +- src/mbgl/renderer/painter_line.cpp | 4 +- src/mbgl/renderer/painter_symbol.cpp | 3 +- src/mbgl/style/applied_class_properties.cpp | 8 +-- src/mbgl/style/applied_class_properties.hpp | 12 ++-- src/mbgl/style/property_transition.hpp | 7 +- src/mbgl/style/style.cpp | 9 ++- src/mbgl/style/style.hpp | 6 +- src/mbgl/style/style_layer.cpp | 34 ++++----- src/mbgl/style/style_layer.hpp | 15 ++-- src/mbgl/style/style_layer_group.cpp | 6 +- src/mbgl/style/style_layer_group.hpp | 5 +- src/mbgl/style/style_parser.cpp | 6 +- src/mbgl/util/raster.cpp | 7 +- src/mbgl/util/raster.hpp | 3 +- src/mbgl/util/stopwatch.cpp | 13 ++-- src/mbgl/util/stopwatch.hpp | 3 +- src/mbgl/util/time.cpp | 4 -- src/mbgl/util/transition.cpp | 2 +- src/mbgl/util/transition.hpp | 23 ++++--- 46 files changed, 289 insertions(+), 315 deletions(-) diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 9123d9aba..d3934921b 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -380,19 +380,19 @@ jobject JNICALL nativeGetClasses(JNIEnv *env, jobject obj, jlong nativeMapViewPt } void JNICALL nativeSetDefaultTransitionDuration(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, - jlong milliseconds) { + jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDefaultTransitionDuration"); assert(nativeMapViewPtr != 0); - assert(milliseconds >= 0); + assert(duration >= 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().setDefaultTransitionDuration(milliseconds); + nativeMapView->getMap().setDefaultTransitionDuration(std::chrono::milliseconds(duration)); } jlong JNICALL nativeGetDefaultTransitionDuration(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetDefaultTransitionDuration"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - return nativeMapView->getMap().getDefaultTransitionDuration(); + return std::chrono::duration_cast(nativeMapView->getMap().getDefaultTransitionDuration()).count(); } void JNICALL nativeSetStyleURL(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring url) { @@ -441,15 +441,15 @@ void JNICALL nativeCancelTransitions(JNIEnv *env, jobject obj, jlong nativeMapVi } void JNICALL nativeMoveBy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble dx, jdouble dy, - jdouble duration) { + jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeMoveBy"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().moveBy(dx, dy, duration); + nativeMapView->getMap().moveBy(dx, dy, std::chrono::milliseconds(duration)); } void JNICALL nativeSetLonLat(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject lonLat, - jdouble duration) { + jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetLonLat"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); @@ -466,7 +466,7 @@ void JNICALL nativeSetLonLat(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, j return; } - nativeMapView->getMap().setLonLat(lon, lat, duration); + nativeMapView->getMap().setLonLat(lon, lat, std::chrono::milliseconds(duration)); } jobject JNICALL nativeGetLonLat(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { @@ -507,19 +507,19 @@ void JNICALL nativeResetPosition(JNIEnv *env, jobject obj, jlong nativeMapViewPt } void JNICALL nativeScaleBy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble ds, jdouble cx, - jdouble cy, jdouble duration) { + jdouble cy, jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeScaleBy"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().scaleBy(ds, cx, cy, duration); + nativeMapView->getMap().scaleBy(ds, cx, cy, std::chrono::milliseconds(duration)); } void JNICALL nativeSetScale(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble scale, - jdouble cx, jdouble cy, jdouble duration) { + jdouble cx, jdouble cy, jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetScale"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().setScale(scale, cx, cy, duration); + nativeMapView->getMap().setScale(scale, cx, cy, std::chrono::milliseconds(duration)); } jdouble JNICALL nativeGetScale(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { @@ -530,11 +530,11 @@ jdouble JNICALL nativeGetScale(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) } void JNICALL -nativeSetZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble zoom, jdouble duration) { +nativeSetZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble zoom, jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetZoom"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().setZoom(zoom, duration); + nativeMapView->getMap().setZoom(zoom, std::chrono::milliseconds(duration)); } jdouble JNICALL nativeGetZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { @@ -545,7 +545,7 @@ jdouble JNICALL nativeGetZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) } void JNICALL nativeSetLonLatZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, - jobject lonLatZoom, jdouble duration) { + jobject lonLatZoom, jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetLonLatZoom"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); @@ -568,7 +568,7 @@ void JNICALL nativeSetLonLatZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPt return; } - nativeMapView->getMap().setLonLatZoom(lon, lat, zoom, duration); + nativeMapView->getMap().setLonLatZoom(lon, lat, zoom, std::chrono::milliseconds(duration)); } jobject JNICALL nativeGetLonLatZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) { @@ -623,19 +623,19 @@ jdouble JNICALL nativeGetMaxZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPt } void JNICALL nativeRotateBy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble sx, - jdouble sy, jdouble ex, jdouble ey, jdouble duration) { + jdouble sy, jdouble ex, jdouble ey, jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeRotateBy"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().rotateBy(sx, sy, ex, ey, duration); + nativeMapView->getMap().rotateBy(sx, sy, ex, ey, std::chrono::milliseconds(duration)); } void JNICALL nativeSetBearing(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble degrees, - jdouble duration) { + jlong duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetBearing"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().setBearing(degrees, duration); + nativeMapView->getMap().setBearing(degrees, std::chrono::milliseconds(duration)); } void JNICALL nativeSetBearing(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble degrees, @@ -889,20 +889,20 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { {"nativeGetAccessToken", "(J)Ljava/lang/String;", reinterpret_cast(&nativeGetAccessToken)}, {"nativeCancelTransitions", "(J)V", reinterpret_cast(&nativeCancelTransitions)}, - {"nativeMoveBy", "(JDDD)V", reinterpret_cast(&nativeMoveBy)}, - {"nativeSetLonLat", "(JLcom/mapbox/mapboxgl/lib/LonLat;D)V", + {"nativeMoveBy", "(JDDJ)V", reinterpret_cast(&nativeMoveBy)}, + {"nativeSetLonLat", "(JLcom/mapbox/mapboxgl/lib/LonLat;J)V", reinterpret_cast(&nativeSetLonLat)}, {"nativeGetLonLat", "(J)Lcom/mapbox/mapboxgl/lib/LonLat;", reinterpret_cast(&nativeGetLonLat)}, {"nativeStartPanning", "(J)V", reinterpret_cast(&nativeStartPanning)}, {"nativeStopPanning", "(J)V", reinterpret_cast(&nativeStopPanning)}, {"nativeResetPosition", "(J)V", reinterpret_cast(&nativeResetPosition)}, - {"nativeScaleBy", "(JDDDD)V", reinterpret_cast(&nativeScaleBy)}, - {"nativeSetScale", "(JDDDD)V", reinterpret_cast(&nativeSetScale)}, + {"nativeScaleBy", "(JDDDJ)V", reinterpret_cast(&nativeScaleBy)}, + {"nativeSetScale", "(JDDDJ)V", reinterpret_cast(&nativeSetScale)}, {"nativeGetScale", "(J)D", reinterpret_cast(&nativeGetScale)}, - {"nativeSetZoom", "(JDD)V", reinterpret_cast(&nativeSetZoom)}, + {"nativeSetZoom", "(JDJ)V", reinterpret_cast(&nativeSetZoom)}, {"nativeGetZoom", "(J)D", reinterpret_cast(&nativeGetZoom)}, - {"nativeSetLonLatZoom", "(JLcom/mapbox/mapboxgl/lib/LonLatZoom;D)V", + {"nativeSetLonLatZoom", "(JLcom/mapbox/mapboxgl/lib/LonLatZoom;J)V", reinterpret_cast(&nativeSetLonLatZoom)}, {"nativeGetLonLatZoom", "(J)Lcom/mapbox/mapboxgl/lib/LonLatZoom;", reinterpret_cast(&nativeGetLonLatZoom)}, @@ -911,10 +911,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { {"nativeStopPanning", "(J)V", reinterpret_cast(&nativeStopScaling)}, {"nativeGetMinZoom", "(J)D", reinterpret_cast(&nativeGetMinZoom)}, {"nativeGetMaxZoom", "(J)D", reinterpret_cast(&nativeGetMaxZoom)}, - {"nativeRotateBy", "(JDDDDD)V", reinterpret_cast(&nativeRotateBy)}, - {"nativeSetBearing", "(JDD)V", + {"nativeRotateBy", "(JDDDDJ)V", reinterpret_cast(&nativeRotateBy)}, + {"nativeSetBearing", "(JDJ)V", reinterpret_cast( - static_cast( + static_cast( &nativeSetBearing))}, {"nativeSetBearing", "(JDDD)V", reinterpret_cast( diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index e177fd497..71db99ff8 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -718,7 +718,7 @@ void NativeMapView::resume() { } } -void NativeMapView::notifyMapChange(mbgl::MapChange, mbgl::timestamp) { +void NativeMapView::notifyMapChange(mbgl::MapChange, std::chrono::steady_clock::duration) { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notifyMapChange()"); assert(vm != nullptr); diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 36e530a17..4c51df654 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -46,6 +46,9 @@ public class MapView extends SurfaceView { // Tag used for logging private static final String TAG = "MapView"; + // Used for animation + private static final long ANIMATION_DURATION = 300; + // Used for saving instance state private static final String STATE_CENTER_COORDINATE = "centerCoordinate"; private static final String STATE_CENTER_DIRECTION = "centerDirection"; @@ -188,7 +191,7 @@ public class MapView extends SurfaceView { if (!isInEditMode()) { if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)) { mZoomButtonsController = new ZoomButtonsController(this); - mZoomButtonsController.setZoomSpeed(300); + mZoomButtonsController.setZoomSpeed(ANIMATION_DURATION); mZoomButtonsController.setOnZoomListener(new OnZoomListener()); } @@ -213,7 +216,7 @@ public class MapView extends SurfaceView { } public void setCenterCoordinate(LonLat centerCoordinate, boolean animated) { - double duration = animated ? 0.3 : 0.0; + long duration = animated ? ANIMATION_DURATION : 0; mNativeMapView.setLonLat(centerCoordinate, duration); } @@ -223,7 +226,7 @@ public class MapView extends SurfaceView { public void setCenterCoordinate(LonLatZoom centerCoordinate, boolean animated) { - double duration = animated ? 0.3 : 0.0; + long duration = animated ? ANIMATION_DURATION : 0; mNativeMapView.setLonLatZoom(centerCoordinate, duration); } @@ -243,8 +246,7 @@ public class MapView extends SurfaceView { } public void setDirection(double direction, boolean animated) { - double duration = animated ? 0.3 : 0.0; - + long duration = animated ? ANIMATION_DURATION : 0; mNativeMapView.setBearing(-direction, duration); } @@ -265,7 +267,7 @@ public class MapView extends SurfaceView { } public void setZoomLevel(double zoomLevel, boolean animated) { - double duration = animated ? 0.3 : 0.0; + long duration = animated ? ANIMATION_DURATION : 0; mNativeMapView.setZoom(zoomLevel, duration); } @@ -565,10 +567,10 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); if (zoomIn) { - mNativeMapView.scaleBy(2.0, x / mScreenDensity, y / mScreenDensity, 0.3); + mNativeMapView.scaleBy(2.0, x / mScreenDensity, y / mScreenDensity, ANIMATION_DURATION); } else { // TODO two finger tap zoom out - mNativeMapView.scaleBy(0.5, x / mScreenDensity, y / mScreenDensity, 0.3); + mNativeMapView.scaleBy(0.5, x / mScreenDensity, y / mScreenDensity, ANIMATION_DURATION); } } @@ -695,7 +697,7 @@ public class MapView extends SurfaceView { // Cancel any animation mNativeMapView.cancelTransitions(); - mNativeMapView.moveBy(velocityX * duration / 2.0 / mScreenDensity, velocityY * duration / 2.0 / mScreenDensity, duration); + mNativeMapView.moveBy(velocityX * duration / 2.0 / mScreenDensity, velocityY * duration / 2.0 / mScreenDensity, (long)(duration * 1000.0f)); return true; } diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 845decbe8..de8e8df75 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -234,18 +234,18 @@ class NativeMapView { } public void moveBy(double dx, double dy) { - moveBy(dx, dy, 0.0); + moveBy(dx, dy, 0); } - public void moveBy(double dx, double dy, double duration) { + public void moveBy(double dx, double dy, long duration) { nativeMoveBy(mNativeMapViewPtr, dx, dy, duration); } public void setLonLat(LonLat lonLat) { - setLonLat(lonLat, 0.0); + setLonLat(lonLat, 0); } - public void setLonLat(LonLat lonLat, double duration) { + public void setLonLat(LonLat lonLat, long duration) { nativeSetLonLat(mNativeMapViewPtr, lonLat, duration); } @@ -270,10 +270,10 @@ class NativeMapView { } public void scaleBy(double ds, double cx, double cy) { - scaleBy(ds, cx, cy, 0.0); + scaleBy(ds, cx, cy, 0); } - public void scaleBy(double ds, double cx, double cy, double duration) { + public void scaleBy(double ds, double cx, double cy, long duration) { nativeScaleBy(mNativeMapViewPtr, ds, cx, cy, duration); } @@ -282,10 +282,10 @@ class NativeMapView { } public void setScale(double scale, double cx, double cy) { - setScale(scale, cx, cy, 0.0); + setScale(scale, cx, cy, 0); } - public void setScale(double scale, double cx, double cy, double duration) { + public void setScale(double scale, double cx, double cy, long duration) { nativeSetScale(mNativeMapViewPtr, scale, cx, cy, duration); } @@ -294,10 +294,10 @@ class NativeMapView { } public void setZoom(double zoom) { - setZoom(zoom, 0.0); + setZoom(zoom, 0); } - public void setZoom(double zoom, double duration) { + public void setZoom(double zoom, long duration) { nativeSetZoom(mNativeMapViewPtr, zoom, duration); } @@ -306,10 +306,10 @@ class NativeMapView { } public void setLonLatZoom(LonLatZoom lonLatZoom) { - setLonLatZoom(lonLatZoom, 0.0); + setLonLatZoom(lonLatZoom, 0); } - public void setLonLatZoom(LonLatZoom lonLatZoom, double duration) { + public void setLonLatZoom(LonLatZoom lonLatZoom, long duration) { nativeSetLonLatZoom(mNativeMapViewPtr, lonLatZoom, duration); } @@ -338,19 +338,19 @@ class NativeMapView { } public void rotateBy(double sx, double sy, double ex, double ey) { - rotateBy(sx, sy, ex, ey, 0.0); + rotateBy(sx, sy, ex, ey, 0); } public void rotateBy(double sx, double sy, double ex, double ey, - double duration) { + long duration) { nativeRotateBy(mNativeMapViewPtr, sx, sy, ex, ey, duration); } public void setBearing(double degrees) { - setBearing(degrees, 0.0); + setBearing(degrees, 0); } - public void setBearing(double degrees, double duration) { + public void setBearing(double degrees, long duration) { nativeSetBearing(mNativeMapViewPtr, degrees, duration); } @@ -468,7 +468,7 @@ class NativeMapView { private native List nativeGetClasses(long nativeMapViewPtr); private native void nativeSetDefaultTransitionDuration( - long nativeMapViewPtr, long milliseconds); + long nativeMapViewPtr, long duration); private native long nativeGetDefaultTransitionDuration(long nativeMapViewPtr); @@ -486,10 +486,10 @@ class NativeMapView { private native void nativeCancelTransitions(long nativeMapViewPtr); private native void nativeMoveBy(long nativeMapViewPtr, double dx, - double dy, double duration); + double dy, long duration); private native void nativeSetLonLat(long nativeMapViewPtr, LonLat lonLat, - double duration); + long duration); private native LonLat nativeGetLonLat(long nativeMapViewPtr); @@ -500,20 +500,20 @@ class NativeMapView { private native void nativeResetPosition(long nativeMapViewPtr); private native void nativeScaleBy(long nativeMapViewPtr, double ds, - double cx, double cy, double duration); + double cx, double cy, long duration); private native void nativeSetScale(long nativeMapViewPtr, double scale, - double cx, double cy, double duration); + double cx, double cy, long duration); private native double nativeGetScale(long nativeMapViewPtr); private native void nativeSetZoom(long nativeMapViewPtr, double zoom, - double duration); + long duration); private native double nativeGetZoom(long nativeMapViewPtr); private native void nativeSetLonLatZoom(long nativeMapViewPtr, - LonLatZoom lonLatZoom, double duration); + LonLatZoom lonLatZoom, long duration); private native LonLatZoom nativeGetLonLatZoom(long nativeMapViewPtr); @@ -528,10 +528,10 @@ class NativeMapView { private native double nativeGetMaxZoom(long nativeMapViewPtr); private native void nativeRotateBy(long nativeMapViewPtr, double sx, - double sy, double ex, double ey, double duration); + double sy, double ex, double ey, long duration); private native void nativeSetBearing(long nativeMapViewPtr, double degrees, - double duration); + long duration); private native void nativeSetBearing(long nativeMapViewPtr, double degrees, double cx, double cy); diff --git a/android/scripts/common.sh b/android/scripts/common.sh index b594fd244..5c214fe25 100755 --- a/android/scripts/common.sh +++ b/android/scripts/common.sh @@ -7,8 +7,6 @@ NAME=$1 export JOBS=2 export CC=clang CXX=clang++ -export CXXFLAGS="-Wno-unknown-warning-option -Wno-unused-local-typedefs -Wno-unknown-pragmas ${CXXFLAGS}"; - export MASON_PLATFORM=android mkdir ./android/java/app/src/main/res/raw/ diff --git a/gyp/common.gypi b/gyp/common.gypi index d219ba2f9..92cfb2ab1 100644 --- a/gyp/common.gypi +++ b/gyp/common.gypi @@ -22,18 +22,13 @@ '-Wno-variadic-macros', '-frtti', '-fexceptions', - '-Wno-error=unused-parameter', ], 'GCC_WARN_PEDANTIC': 'YES', 'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE', }, }], ['OS=="linux"', { - 'cflags_cc': [ - '-Wno-unknown-pragmas', # We are using '#pragma mark', but it is only available on Darwin. - '-Wno-literal-suffix', # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61653 - '-Wno-unknown-warning-option', - '-Wno-error=unused-parameter', + 'cflags_cc': ['-Wno-unknown-pragmas', # We are using '#pragma mark', but it is only available on Darwin. ], }], ], diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp index 8641d277b..c1bd085e9 100644 --- a/include/mbgl/android/native_map_view.hpp +++ b/include/mbgl/android/native_map_view.hpp @@ -25,7 +25,7 @@ public: void swap() override; void notify() override; - void notifyMapChange(mbgl::MapChange change, mbgl::timestamp delay) override; + void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) override; mbgl::Map &getMap(); mbgl::CachingHTTPFileSource &getFileSource(); diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 21fc8f301..62f0d6201 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -16,6 +15,7 @@ #include #include #include +#include namespace mbgl { @@ -83,8 +83,8 @@ public: void setClasses(const std::vector&); std::vector getClasses() const; - void setDefaultTransitionDuration(uint64_t milliseconds = 0); - uint64_t getDefaultTransitionDuration(); + void setDefaultTransitionDuration(std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + std::chrono::steady_clock::duration getDefaultTransitionDuration(); void setStyleURL(const std::string &url); void setStyleJSON(std::string newStyleJSON, const std::string &base = ""); std::string getStyleJSON() const; @@ -93,20 +93,20 @@ public: void cancelTransitions(); // Position - void moveBy(double dx, double dy, double duration = 0); - void setLonLat(double lon, double lat, double duration = 0); + void moveBy(double dx, double dy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void getLonLat(double &lon, double &lat) const; void startPanning(); void stopPanning(); void resetPosition(); // Scale - void scaleBy(double ds, double cx = -1, double cy = -1, double duration = 0); - void setScale(double scale, double cx = -1, double cy = -1, double duration = 0); + void scaleBy(double ds, double cx = -1, double cy = -1, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setScale(double scale, double cx = -1, double cy = -1, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); double getScale() const; - void setZoom(double zoom, double duration = 0); + void setZoom(double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); double getZoom() const; - void setLonLatZoom(double lon, double lat, double zoom, double duration = 0); + void setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void getLonLatZoom(double &lon, double &lat, double &zoom) const; void resetZoom(); void startScaling(); @@ -115,8 +115,8 @@ public: double getMaxZoom() const; // Rotation - void rotateBy(double sx, double sy, double ex, double ey, double duration = 0); - void setBearing(double degrees, double duration = 0); + void rotateBy(double sx, double sy, double ex, double ey, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setBearing(double degrees, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void setBearing(double degrees, double cx, double cy); double getBearing() const; void resetNorth(); @@ -129,7 +129,7 @@ public: bool getDebug() const; inline const TransformState &getState() const { return state; } - inline timestamp getTime() const { return animationTime; } + inline std::chrono::steady_clock::time_point getTime() const { return animationTime; } private: util::ptr getSprite(); @@ -215,10 +215,10 @@ private: std::string styleJSON = ""; std::vector classes; - std::atomic_uint_fast64_t defaultTransitionDuration; + std::chrono::steady_clock::duration defaultTransitionDuration; bool debug = false; - timestamp animationTime = 0; + std::chrono::steady_clock::time_point animationTime = std::chrono::steady_clock::time_point::min(); std::set> activeSources; }; diff --git a/include/mbgl/map/transform.hpp b/include/mbgl/map/transform.hpp index a42b7aa10..246270da1 100644 --- a/include/mbgl/map/transform.hpp +++ b/include/mbgl/map/transform.hpp @@ -1,7 +1,6 @@ #ifndef MBGL_MAP_TRANSFORM #define MBGL_MAP_TRANSFORM -#include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include namespace mbgl { @@ -25,18 +25,18 @@ public: uint16_t fb_width, uint16_t fb_height); // Position - void moveBy(double dx, double dy, timestamp duration = 0); - void setLonLat(double lon, double lat, timestamp duration = 0); - void setLonLatZoom(double lon, double lat, double zoom, timestamp duration = 0); + void moveBy(double dx, double dy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void getLonLat(double& lon, double& lat) const; void getLonLatZoom(double& lon, double& lat, double& zoom) const; void startPanning(); void stopPanning(); // Zoom - void scaleBy(double ds, double cx = -1, double cy = -1, timestamp duration = 0); - void setScale(double scale, double cx = -1, double cy = -1, timestamp duration = 0); - void setZoom(double zoom, timestamp duration = 0); + void scaleBy(double ds, double cx = -1, double cy = -1, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setScale(double scale, double cx = -1, double cy = -1, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setZoom(double zoom, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); double getZoom() const; double getScale() const; void startScaling(); @@ -45,8 +45,8 @@ public: double getMaxZoom() const; // Angle - void rotateBy(double sx, double sy, double ex, double ey, timestamp duration = 0); - void setAngle(double angle, timestamp duration = 0); + void rotateBy(double sx, double sy, double ex, double ey, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void setAngle(double angle, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void setAngle(double angle, double cx, double cy); double getAngle() const; void startRotating(); @@ -54,7 +54,7 @@ public: // Transitions bool needsTransition() const; - void updateTransitions(timestamp now); + void updateTransitions(std::chrono::steady_clock::time_point now); void cancelTransitions(); // Transform state @@ -64,10 +64,10 @@ public: private: // Functions prefixed with underscores will *not* perform any locks. It is the caller's // responsibility to lock this object. - void _moveBy(double dx, double dy, timestamp duration = 0); - void _setScale(double scale, double cx, double cy, timestamp duration = 0); - void _setScaleXY(double new_scale, double xn, double yn, timestamp duration = 0); - void _setAngle(double angle, timestamp duration = 0); + void _moveBy(double dx, double dy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void _setScale(double scale, double cx, double cy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void _setScaleXY(double new_scale, double xn, double yn, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); + void _setAngle(double angle, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void _clearPanning(); void _clearRotating(); void _clearScaling(); diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp index 75fcb4e64..b94b8c0b9 100644 --- a/include/mbgl/map/view.hpp +++ b/include/mbgl/map/view.hpp @@ -1,7 +1,7 @@ #ifndef MBGL_MAP_VIEW #define MBGL_MAP_VIEW -#include +#include namespace mbgl { @@ -44,7 +44,7 @@ public: // Notifies a watcher of map x/y/scale/rotation changes. // Must only be called from the same thread that caused the change. // Must not be called from the render thread. - virtual void notifyMapChange(MapChange change, timestamp delay = 0) = 0; + virtual void notifyMapChange(MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) = 0; protected: mbgl::Map *map = nullptr; diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp index 8ec282891..7156d4ff1 100644 --- a/include/mbgl/platform/default/glfw_view.hpp +++ b/include/mbgl/platform/default/glfw_view.hpp @@ -2,7 +2,6 @@ #define MBGL_COMMON_GLFW_VIEW #include -#include #ifdef NVIDIA #define GLFW_INCLUDE_ES2 @@ -19,7 +18,7 @@ public: void activate(); void deactivate(); void notify(); - void notifyMapChange(mbgl::MapChange change, mbgl::timestamp delay = 0); + void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()); static void key(GLFWwindow *window, int key, int scancode, int action, int mods); static void scroll(GLFWwindow *window, double xoffset, double yoffset); diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index 387011aa0..e29f529f3 100644 --- a/include/mbgl/platform/default/headless_view.hpp +++ b/include/mbgl/platform/default/headless_view.hpp @@ -31,7 +31,7 @@ public: std::unique_ptr readPixels(); void notify(); - void notifyMapChange(MapChange change, timestamp delay = 0); + void notifyMapChange(MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()); void activate(); void deactivate(); void swap(); diff --git a/include/mbgl/platform/default/jpeg_reader.hpp b/include/mbgl/platform/default/jpeg_reader.hpp index 7122f24cd..e041c0a5d 100644 --- a/include/mbgl/platform/default/jpeg_reader.hpp +++ b/include/mbgl/platform/default/jpeg_reader.hpp @@ -9,7 +9,13 @@ extern "C" #include } +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wshadow" +#endif #include +#pragma GCC diagnostic pop namespace mbgl { namespace util { diff --git a/include/mbgl/platform/default/png_reader.hpp b/include/mbgl/platform/default/png_reader.hpp index 66debd4fb..649e7a75c 100644 --- a/include/mbgl/platform/default/png_reader.hpp +++ b/include/mbgl/platform/default/png_reader.hpp @@ -11,7 +11,13 @@ extern "C" #include #include +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wshadow" +#endif #include +#pragma GCC diagnostic pop namespace mbgl { namespace util { diff --git a/include/mbgl/util/time.hpp b/include/mbgl/util/time.hpp index e8afbeff6..c2e931b10 100644 --- a/include/mbgl/util/time.hpp +++ b/include/mbgl/util/time.hpp @@ -7,41 +7,13 @@ namespace mbgl { -typedef uint64_t timestamp; - namespace util { - -// Returns time in nanoseconds since an arbitrary point in the past. This has -// no relation to the current time, but is continuously increasing, so -// comparisons between different timestamps produce meaningful values. -timestamp now(); - - // Returns the RFC1123 formatted date. E.g. "Tue, 04 Nov 2014 02:13:24 GMT" std::string rfc1123(std::time_t time); } -// used for time conversions -constexpr timestamp operator"" _nanoseconds (long double ns) { return ns; } -constexpr timestamp operator"" _nanosecond (long double ns) { return ns; } -constexpr timestamp operator"" _microseconds (long double us) { return us * 1e3; } -constexpr timestamp operator"" _microsecond (long double us) { return us * 1e3; } -constexpr timestamp operator"" _milliseconds (long double ms) { return ms * 1e6; } -constexpr timestamp operator"" _millisecond (long double ms) { return ms * 1e6; } -constexpr timestamp operator"" _seconds (long double s) { return s * 1e9; } -constexpr timestamp operator"" _second (long double s) { return s * 1e9; } - -constexpr timestamp operator"" _nanoseconds (unsigned long long ns) { return ns; } -constexpr timestamp operator"" _nanosecond (unsigned long long ns) { return ns; } -constexpr timestamp operator"" _microseconds (unsigned long long us) { return us * 1e3; } -constexpr timestamp operator"" _microsecond (unsigned long long us) { return us * 1e3; } -constexpr timestamp operator"" _milliseconds (unsigned long long ms) { return ms * 1e6; } -constexpr timestamp operator"" _millisecond (unsigned long long ms) { return ms * 1e6; } -constexpr timestamp operator"" _seconds (unsigned long long s) { return s * 1e9; } -constexpr timestamp operator"" _second (unsigned long long s) { return s * 1e9; } - } #endif diff --git a/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa index 2e5bfbdcd..08c15cac9 160000 --- a/ios/mapbox-gl-cocoa +++ b/ios/mapbox-gl-cocoa @@ -1 +1 @@ -Subproject commit 2e5bfbdcd54c893864c263b41a27846627e10f0c +Subproject commit 08c15cac9df7ac13fa87f45427a9661658befd1d diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 748f27ed2..8306229d4 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -174,7 +174,7 @@ void GLFWView::key(GLFWwindow *window, int key, int /*scancode*/, int action, in break; case GLFW_KEY_R: if (!mods) { - view->map->setDefaultTransitionDuration(300); + view->map->setDefaultTransitionDuration(std::chrono::milliseconds(300)); if (view->map->hasClass("night")) { view->map->removeClass("night"); } else { @@ -239,9 +239,9 @@ void GLFWView::mouseClick(GLFWwindow *window, int button, int action, int modifi double now = glfwGetTime(); if (now - view->lastClick < 0.4 /* ms */) { if (modifiers & GLFW_MOD_SHIFT) { - view->map->scaleBy(0.5, view->lastX, view->lastY, 0.5); + view->map->scaleBy(0.5, view->lastX, view->lastY, std::chrono::milliseconds(500)); } else { - view->map->scaleBy(2.0, view->lastX, view->lastY, 0.5); + view->map->scaleBy(2.0, view->lastX, view->lastY, std::chrono::milliseconds(500)); } } view->lastClick = now; @@ -301,7 +301,7 @@ void GLFWView::swap() { fps(); } -void GLFWView::notifyMapChange(mbgl::MapChange /*change*/, mbgl::timestamp /*delay*/) { +void GLFWView::notifyMapChange(mbgl::MapChange /*change*/, std::chrono::steady_clock::duration /*delay*/) { // no-op } diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index 148ff12a0..9df58f802 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -244,7 +244,7 @@ void HeadlessView::notify() { // no-op } -void HeadlessView::notifyMapChange(mbgl::MapChange /*change*/, mbgl::timestamp /*delay*/) { +void HeadlessView::notifyMapChange(mbgl::MapChange /*change*/, std::chrono::steady_clock::duration /*delay*/) { // no-op } diff --git a/scripts/flags.sh b/scripts/flags.sh index 8703677d0..10cc88e5c 100755 --- a/scripts/flags.sh +++ b/scripts/flags.sh @@ -30,6 +30,3 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then # fi # fi fi - -# turn off a few warnings -export CXXFLAGS="-Wno-unknown-warning-option -Wno-unused-local-typedefs -Wno-unknown-pragmas ${CXXFLAGS}"; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index ff584c54b..3359dd317 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -435,13 +434,13 @@ void Map::cancelTransitions() { #pragma mark - Position -void Map::moveBy(double dx, double dy, double duration) { - transform.moveBy(dx, dy, duration * 1_second); +void Map::moveBy(double dx, double dy, std::chrono::steady_clock::duration duration) { + transform.moveBy(dx, dy, duration); update(); } -void Map::setLonLat(double lon, double lat, double duration) { - transform.setLonLat(lon, lat, duration * 1_second); +void Map::setLonLat(double lon, double lat, std::chrono::steady_clock::duration duration) { + transform.setLonLat(lon, lat, duration); update(); } @@ -469,13 +468,13 @@ void Map::resetPosition() { #pragma mark - Scale -void Map::scaleBy(double ds, double cx, double cy, double duration) { - transform.scaleBy(ds, cx, cy, duration * 1_second); +void Map::scaleBy(double ds, double cx, double cy, std::chrono::steady_clock::duration duration) { + transform.scaleBy(ds, cx, cy, duration); update(); } -void Map::setScale(double scale, double cx, double cy, double duration) { - transform.setScale(scale, cx, cy, duration * 1_second); +void Map::setScale(double scale, double cx, double cy, std::chrono::steady_clock::duration duration) { + transform.setScale(scale, cx, cy, duration); update(); } @@ -483,8 +482,8 @@ double Map::getScale() const { return transform.getScale(); } -void Map::setZoom(double zoom, double duration) { - transform.setZoom(zoom, duration * 1_second); +void Map::setZoom(double zoom, std::chrono::steady_clock::duration duration) { + transform.setZoom(zoom, duration); update(); } @@ -492,8 +491,8 @@ double Map::getZoom() const { return transform.getZoom(); } -void Map::setLonLatZoom(double lon, double lat, double zoom, double duration) { - transform.setLonLatZoom(lon, lat, zoom, duration * 1_second); +void Map::setLonLatZoom(double lon, double lat, double zoom, std::chrono::steady_clock::duration duration) { + transform.setLonLatZoom(lon, lat, zoom, duration); update(); } @@ -526,13 +525,13 @@ double Map::getMaxZoom() const { #pragma mark - Rotation -void Map::rotateBy(double sx, double sy, double ex, double ey, double duration) { - transform.rotateBy(sx, sy, ex, ey, duration * 1_second); +void Map::rotateBy(double sx, double sy, double ex, double ey, std::chrono::steady_clock::duration duration) { + transform.rotateBy(sx, sy, ex, ey, duration); update(); } -void Map::setBearing(double degrees, double duration) { - transform.setAngle(-degrees * M_PI / 180, duration * 1_second); +void Map::setBearing(double degrees, std::chrono::steady_clock::duration duration) { + transform.setAngle(-degrees * M_PI / 180, duration); update(); } @@ -546,7 +545,7 @@ double Map::getBearing() const { } void Map::resetNorth() { - transform.setAngle(0, 500_milliseconds); + transform.setAngle(0, std::chrono::milliseconds(500)); update(); } @@ -618,14 +617,14 @@ std::vector Map::getClasses() const { return classes; } -void Map::setDefaultTransitionDuration(uint64_t milliseconds) { - defaultTransitionDuration = milliseconds; +void Map::setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) { + defaultTransitionDuration = duration; if (style) { - style->setDefaultTransitionDuration(milliseconds); + style->setDefaultTransitionDuration(duration); } } -uint64_t Map::getDefaultTransitionDuration() { +std::chrono::steady_clock::duration Map::getDefaultTransitionDuration() { return defaultTransitionDuration; } @@ -704,14 +703,14 @@ void Map::prepare() { } // Update transform transitions. - animationTime = util::now(); + animationTime = std::chrono::steady_clock::now(); if (transform.needsTransition()) { transform.updateTransitions(animationTime); } state = transform.currentState(); - animationTime = util::now(); + animationTime = std::chrono::steady_clock::now(); updateSources(); style->updateProperties(state.getNormalizedZoom(), animationTime); diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 8976f67b0..f0023afa0 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include namespace mbgl { @@ -75,7 +75,7 @@ private: bool loaded = false; // Stores the time when this source was most recently updated. - timestamp updated = 0; + std::chrono::steady_clock::time_point updated = std::chrono::steady_clock::time_point::min(); std::map> tiles; std::map> tile_data; diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index bf79d24b5..795253119 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -48,16 +47,16 @@ bool Transform::resize(const uint16_t w, const uint16_t h, const float ratio, #pragma mark - Position -void Transform::moveBy(const double dx, const double dy, const timestamp duration) { +void Transform::moveBy(const double dx, const double dy, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); _moveBy(dx, dy, duration); } -void Transform::_moveBy(const double dx, const double dy, const timestamp duration) { +void Transform::_moveBy(const double dx, const double dy, const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionWillChangeAnimated : MapChangeRegionWillChange); @@ -66,25 +65,25 @@ void Transform::_moveBy(const double dx, const double dy, const timestamp durati constrain(final.scale, final.y); - if (duration == 0) { + if (duration == std::chrono::steady_clock::duration::zero()) { current.x = final.x; current.y = final.y; } else { // Use a common start time for all of the transitions to avoid divergent transitions. - timestamp start = util::now(); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); transitions.emplace_front( std::make_shared>(current.x, final.x, current.x, start, duration)); transitions.emplace_front( std::make_shared>(current.y, final.y, current.y, start, duration)); } - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionDidChangeAnimated : MapChangeRegionDidChange, duration); } -void Transform::setLonLat(const double lon, const double lat, const timestamp duration) { +void Transform::setLonLat(const double lon, const double lat, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999); @@ -95,7 +94,7 @@ void Transform::setLonLat(const double lon, const double lat, const timestamp du } void Transform::setLonLatZoom(const double lon, const double lat, const double zoom, - const timestamp duration) { + const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); double new_scale = std::pow(2.0, zoom); @@ -131,8 +130,8 @@ void Transform::startPanning() { // Add a 200ms timeout for resetting this to false current.panning = true; - timestamp start = util::now(); - pan_timeout = std::make_shared>(false, current.panning, start, 200_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + pan_timeout = std::make_shared>(false, current.panning, start, std::chrono::steady_clock::duration(200)); transitions.emplace_front(pan_timeout); } @@ -152,7 +151,7 @@ void Transform::_clearPanning() { #pragma mark - Zoom -void Transform::scaleBy(const double ds, const double cx, const double cy, const timestamp duration) { +void Transform::scaleBy(const double ds, const double cx, const double cy, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); // clamp scale to min/max values @@ -167,13 +166,13 @@ void Transform::scaleBy(const double ds, const double cx, const double cy, const } void Transform::setScale(const double scale, const double cx, const double cy, - const timestamp duration) { + const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); _setScale(scale, cx, cy, duration); } -void Transform::setZoom(const double zoom, const timestamp duration) { +void Transform::setZoom(const double zoom, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); _setScale(std::pow(2.0, zoom), -1, -1, duration); @@ -198,8 +197,8 @@ void Transform::startScaling() { // Add a 200ms timeout for resetting this to false current.scaling = true; - timestamp start = util::now(); - scale_timeout = std::make_shared>(false, current.scaling, start, 200_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + scale_timeout = std::make_shared>(false, current.scaling, start, std::chrono::milliseconds(200)); transitions.emplace_front(scale_timeout); } @@ -231,7 +230,7 @@ void Transform::_clearScaling() { } } -void Transform::_setScale(double new_scale, double cx, double cy, const timestamp duration) { +void Transform::_setScale(double new_scale, double cx, double cy, const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. // Ensure that we don't zoom in further than the maximum allowed. @@ -265,10 +264,10 @@ void Transform::_setScale(double new_scale, double cx, double cy, const timestam } void Transform::_setScaleXY(const double new_scale, const double xn, const double yn, - const timestamp duration) { + const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionWillChangeAnimated : MapChangeRegionWillChange); @@ -278,13 +277,13 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl constrain(final.scale, final.y); - if (duration == 0) { + if (duration == std::chrono::steady_clock::duration::zero()) { current.scale = final.scale; current.x = final.x; current.y = final.y; } else { // Use a common start time for all of the transitions to avoid divergent transitions. - timestamp start = util::now(); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); transitions.emplace_front(std::make_shared>( current.scale, final.scale, current.scale, start, duration)); transitions.emplace_front( @@ -297,7 +296,7 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl Bc = s / 360; Cc = s / (2 * M_PI); - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionDidChangeAnimated : MapChangeRegionDidChange, duration); @@ -319,7 +318,7 @@ void Transform::constrain(double& scale, double& y) const { #pragma mark - Angle void Transform::rotateBy(const double start_x, const double start_y, const double end_x, - const double end_y, const timestamp duration) { + const double end_y, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); double center_x = current.width / 2, center_y = current.height / 2; @@ -349,7 +348,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl _setAngle(ang, duration); } -void Transform::setAngle(const double new_angle, const timestamp duration) { +void Transform::setAngle(const double new_angle, const std::chrono::steady_clock::duration duration) { std::lock_guard lock(mtx); _setAngle(new_angle, duration); @@ -363,20 +362,20 @@ void Transform::setAngle(const double new_angle, const double cx, const double c if (cx >= 0 && cy >= 0) { dx = (final.width / 2) - cx; dy = (final.height / 2) - cy; - _moveBy(dx, dy, 0); + _moveBy(dx, dy, std::chrono::steady_clock::duration::zero()); } - _setAngle(new_angle, 0); + _setAngle(new_angle, std::chrono::steady_clock::duration::zero()); if (cx >= 0 && cy >= 0) { - _moveBy(-dx, -dy, 0); + _moveBy(-dx, -dy, std::chrono::steady_clock::duration::zero()); } } -void Transform::_setAngle(double new_angle, const timestamp duration) { +void Transform::_setAngle(double new_angle, const std::chrono::steady_clock::duration duration) { // This is only called internally, so we don't need a lock here. - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionWillChangeAnimated : MapChangeRegionWillChange); @@ -387,15 +386,15 @@ void Transform::_setAngle(double new_angle, const timestamp duration) { final.angle = new_angle; - if (duration == 0) { + if (duration == std::chrono::steady_clock::duration::zero()) { current.angle = final.angle; } else { - timestamp start = util::now(); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); transitions.emplace_front(std::make_shared>( current.angle, final.angle, current.angle, start, duration)); } - view.notifyMapChange(duration ? + view.notifyMapChange(duration != std::chrono::steady_clock::duration::zero() ? MapChangeRegionDidChangeAnimated : MapChangeRegionDidChange, duration); @@ -414,8 +413,8 @@ void Transform::startRotating() { // Add a 200ms timeout for resetting this to false current.rotating = true; - timestamp start = util::now(); - rotate_timeout = std::make_shared>(false, current.rotating, start, 200_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + rotate_timeout = std::make_shared>(false, current.rotating, start, std::chrono::milliseconds(200)); transitions.emplace_front(rotate_timeout); } @@ -443,7 +442,7 @@ bool Transform::needsTransition() const { return !transitions.empty(); } -void Transform::updateTransitions(const timestamp now) { +void Transform::updateTransitions(const std::chrono::steady_clock::time_point now) { std::lock_guard lock(mtx); transitions.remove_if([now](const util::ptr &transition) { diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp index 8b69162a2..21925fa31 100644 --- a/src/mbgl/renderer/frame_history.cpp +++ b/src/mbgl/renderer/frame_history.cpp @@ -3,11 +3,11 @@ using namespace mbgl; // Record frame history that will be used to calculate fading params -void FrameHistory::record(timestamp now, float zoom) { +void FrameHistory::record(std::chrono::steady_clock::time_point now, float zoom) { // first frame ever if (!history.size()) { - history.emplace_back(FrameSnapshot{0, zoom}); - history.emplace_back(FrameSnapshot{0, zoom}); + history.emplace_back(FrameSnapshot{std::chrono::steady_clock::time_point::min(), zoom}); + history.emplace_back(FrameSnapshot{std::chrono::steady_clock::time_point::min(), zoom}); } if (history.size() > 0 || history.back().z != zoom) { @@ -15,7 +15,7 @@ void FrameHistory::record(timestamp now, float zoom) { } } -bool FrameHistory::needsAnimation(const timestamp duration) const { +bool FrameHistory::needsAnimation(const std::chrono::steady_clock::duration duration) const { if (!history.size()) { return false; } @@ -26,7 +26,7 @@ bool FrameHistory::needsAnimation(const timestamp duration) const { const FrameSnapshot &pivot = history.back(); int i = -1; - while ((int)history.size() > i + 1 && history[i + 1].t + duration < pivot.t) { + while ((int)history.size() > i + 1 && history[i + 1].now + duration < pivot.now) { i++; } @@ -47,16 +47,16 @@ bool FrameHistory::needsAnimation(const timestamp duration) const { return false; } -FadeProperties FrameHistory::getFadeProperties(timestamp duration) +FadeProperties FrameHistory::getFadeProperties(std::chrono::steady_clock::duration duration) { - const timestamp currentTime = util::now(); + const std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now(); // Remove frames until only one is outside the duration, or until there are only three - while (history.size() > 3 && history[1].t + duration < currentTime) { + while (history.size() > 3 && history[1].now + duration < currentTime) { history.pop_front(); } - if (history[1].t + duration < currentTime) { + if (history[1].now + duration < currentTime) { history[0].z = history[1].z; } @@ -69,12 +69,13 @@ FadeProperties FrameHistory::getFadeProperties(timestamp duration) // Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one // duration - float zoomDiff = endingZ - history[1].z, timeDiff = lastFrame.t - history[1].t; + float zoomDiff = endingZ - history[1].z; + std::chrono::duration timeDiff = lastFrame.now - history[1].now; float fadedist = zoomDiff / (timeDiff / duration); // At end of a zoom when the zoom stops changing continue pretending to zoom at that speed // bump is how much farther it would have been if it had continued zooming at the same rate - float bump = (currentTime - lastFrame.t) / duration * fadedist; + float bump = std::chrono::duration(currentTime - lastFrame.now) / duration * fadedist; return FadeProperties { fadedist, diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp index 61bb59da3..f56b39125 100644 --- a/src/mbgl/renderer/frame_history.hpp +++ b/src/mbgl/renderer/frame_history.hpp @@ -4,15 +4,15 @@ #include #include #include +#include #include -#include namespace mbgl { struct FrameSnapshot { - explicit inline FrameSnapshot(timestamp t_, float z_) : t(t_), z(z_) {} - float t; + explicit inline FrameSnapshot(std::chrono::steady_clock::time_point now_, float z_) : now(now_), z(z_) {} + std::chrono::steady_clock::time_point now; float z; }; @@ -26,10 +26,10 @@ struct FadeProperties { class FrameHistory { public: // Record frame history that will be used to calculate fading params - void record(timestamp now, float zoom); + void record(std::chrono::steady_clock::time_point now, float zoom); - bool needsAnimation(timestamp duration) const; - FadeProperties getFadeProperties(timestamp duration); + bool needsAnimation(std::chrono::steady_clock::duration duration) const; + FadeProperties getFadeProperties(std::chrono::steady_clock::duration duration); public: std::deque history; diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 0fb99826c..3f30e6d0f 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -35,7 +34,7 @@ Painter::~Painter() { } bool Painter::needsAnimation() const { - return frameHistory.needsAnimation(300); + return frameHistory.needsAnimation(std::chrono::milliseconds(300)); } void Painter::setup() { @@ -216,7 +215,7 @@ void Painter::prepareTile(const Tile& tile) { } void Painter::render(const Style& style, const std::set>& sources, - TransformState state_, timestamp time) { + TransformState state_, std::chrono::steady_clock::time_point time) { state = state_; clear(); @@ -461,7 +460,7 @@ mat4 Painter::translatedMatrix(const mat4& matrix, const std::array &t } } -void Painter::recordZoom(const timestamp time, const float zoom) { +void Painter::recordZoom(const std::chrono::steady_clock::time_point time, const float zoom) { frameHistory.record(time, zoom); if (lastZoom < 0) { diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 5a4207020..bbce58864 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace mbgl { @@ -78,7 +79,7 @@ public: void render(const Style& style, const std::set>& sources, TransformState state, - timestamp time); + std::chrono::steady_clock::time_point time); void renderLayers(util::ptr group); void renderLayer(util::ptr layer_desc, const Tile::ID* id = nullptr, const mat4* matrix = nullptr); @@ -139,7 +140,7 @@ private: mat4 translatedMatrix(const mat4& matrix, const std::array &translation, const Tile::ID &id, TranslateAnchorType anchor); void prepareTile(const Tile& tile); - void recordZoom(const timestamp time, const float zoom); + void recordZoom(const std::chrono::steady_clock::time_point time, const float zoom); template void renderSDF(SymbolBucket &bucket, @@ -193,7 +194,7 @@ private: const float strata_epsilon = 1.0f / (1 << 16); int lastIntegerZoom; - timestamp lastIntegerZoomTime = 0; + std::chrono::steady_clock::time_point lastIntegerZoomTime = std::chrono::steady_clock::time_point::min(); float lastZoom = -1; public: diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 5e8f46d34..45de66201 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -63,9 +63,9 @@ void Painter::renderFill(FillBucket& bucket, util::ptr layer_desc, c float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z); float mix; - float duration = 300 * 1_millisecond; + std::chrono::steady_clock::duration duration = std::chrono::milliseconds(300); const float fraction = std::fmod(float(state.getZoom()), 1.0f); - float t = std::min((util::now() - lastIntegerZoomTime) / duration, 1.0f); + float t = std::min(static_cast((std::chrono::steady_clock::now() - lastIntegerZoomTime) / duration), 1.0f); if (state.getZoom() > lastIntegerZoom) { // zooming in mix = fraction + (1.0f - fraction) * t; diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp index 3951f2e7c..1f69a0551 100644 --- a/src/mbgl/renderer/painter_line.cpp +++ b/src/mbgl/renderer/painter_line.cpp @@ -70,9 +70,9 @@ void Painter::renderLine(LineBucket& bucket, util::ptr layer_desc, c bucket.drawPoints(*linejoinShader); } - float duration = 300 * 1_millisecond; + std::chrono::steady_clock::duration duration = std::chrono::milliseconds(300); const float fraction = std::fmod(float(state.getZoom()), 1.0f); - float t = std::min((util::now() - lastIntegerZoomTime) / duration, 1.0f); + float t = std::min(static_cast(std::chrono::duration(std::chrono::steady_clock::now() - lastIntegerZoomTime) / duration), 1.0f); if (properties.dash_array.size()) { diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index d9e4a58c2..579a6d2e0 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -5,6 +5,7 @@ #include #include #include + #include using namespace mbgl; @@ -52,7 +53,7 @@ void Painter::renderSDF(SymbolBucket &bucket, sdfShader.u_angle = (int32_t)(angle + 256) % 256; sdfShader.u_zoom = (state.getNormalizedZoom() - zoomAdjust) * 10; // current zoom level - FadeProperties f = frameHistory.getFadeProperties(300_milliseconds); + FadeProperties f = frameHistory.getFadeProperties(std::chrono::milliseconds(300)); sdfShader.u_fadedist = f.fadedist * 10; sdfShader.u_minfadezoom = std::floor(f.minfadezoom * 10); sdfShader.u_maxfadezoom = std::floor(f.maxfadezoom * 10); diff --git a/src/mbgl/style/applied_class_properties.cpp b/src/mbgl/style/applied_class_properties.cpp index 9037c6ad5..4e5647ce4 100644 --- a/src/mbgl/style/applied_class_properties.cpp +++ b/src/mbgl/style/applied_class_properties.cpp @@ -2,7 +2,7 @@ namespace mbgl { -AppliedClassProperty::AppliedClassProperty(ClassID class_id, timestamp begin_, timestamp end_, const PropertyValue &value_) +AppliedClassProperty::AppliedClassProperty(ClassID class_id, std::chrono::steady_clock::time_point begin_, std::chrono::steady_clock::time_point end_, const PropertyValue &value_) : name(class_id), begin(begin_), end(end_), @@ -13,7 +13,7 @@ ClassID AppliedClassProperties::mostRecent() const { return properties.size() ? properties.back().name : ClassID::Fallback; } -void AppliedClassProperties::add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value) { +void AppliedClassProperties::add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value) { properties.emplace_back(class_id, begin, end, value); } @@ -23,7 +23,7 @@ bool AppliedClassProperties::hasTransitions() const { // Erase all items in the property list that are before a completed transition. // Then, if the only remaining property is a Fallback value, remove it too. -void AppliedClassProperties::cleanup(timestamp now) { +void AppliedClassProperties::cleanup(std::chrono::steady_clock::time_point now) { // Iterate backwards, but without using the rbegin/rend interface since we need forward // iterators to use .erase(). for (auto it = properties.end(), begin = properties.begin(); it != begin;) { @@ -49,4 +49,4 @@ bool AppliedClassProperties::empty() const { return properties.empty(); } -} \ No newline at end of file +} diff --git a/src/mbgl/style/applied_class_properties.hpp b/src/mbgl/style/applied_class_properties.hpp index 827f15a2a..ac086a224 100644 --- a/src/mbgl/style/applied_class_properties.hpp +++ b/src/mbgl/style/applied_class_properties.hpp @@ -3,20 +3,20 @@ #include #include -#include #include +#include namespace mbgl { class AppliedClassProperty { public: - AppliedClassProperty(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value); + AppliedClassProperty(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value); public: const ClassID name; - const timestamp begin; - const timestamp end; + const std::chrono::steady_clock::time_point begin; + const std::chrono::steady_clock::time_point end; const PropertyValue value; }; @@ -28,9 +28,9 @@ public: public: // Returns thie ID of the most recent ClassID mostRecent() const; - void add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value); + void add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value); bool hasTransitions() const; - void cleanup(timestamp now); + void cleanup(std::chrono::steady_clock::time_point now); bool empty() const; }; diff --git a/src/mbgl/style/property_transition.hpp b/src/mbgl/style/property_transition.hpp index 07b7cfe28..cf9167cc0 100644 --- a/src/mbgl/style/property_transition.hpp +++ b/src/mbgl/style/property_transition.hpp @@ -2,14 +2,15 @@ #define MBGL_STYLE_PROPERTY_TRANSITION #include +#include namespace mbgl { struct PropertyTransition { - uint16_t duration = 0; - uint16_t delay = 0; + std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero(); + std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero(); }; } -#endif \ No newline at end of file +#endif diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index ecff6fb5f..01781afe3 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -26,7 +25,7 @@ Style::Style() // for deleting the std::unique_ptr. Style::~Style() {} -void Style::updateProperties(float z, timestamp now) { +void Style::updateProperties(float z, std::chrono::steady_clock::time_point now) { uv::writelock lock(mtx); if (layers) { @@ -44,13 +43,13 @@ const std::string &Style::getSpriteURL() const { return sprite_url; } -void Style::setDefaultTransitionDuration(uint16_t duration_milliseconds) { - defaultTransition.duration = duration_milliseconds; +void Style::setDefaultTransitionDuration(std::chrono::steady_clock::duration duration) { + defaultTransition.duration = duration; } void Style::cascadeClasses(const std::vector& classes) { if (layers) { - layers->setClasses(classes, util::now(), defaultTransition); + layers->setClasses(classes, std::chrono::steady_clock::now(), defaultTransition); } } diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index e9205712e..af00f9710 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include namespace mbgl { @@ -31,9 +31,9 @@ public: void loadJSON(const uint8_t *const data); size_t layerCount() const; - void updateProperties(float z, timestamp t); + void updateProperties(float z, std::chrono::steady_clock::time_point now); - void setDefaultTransitionDuration(uint16_t duration_milliseconds = 0); + void setDefaultTransitionDuration(std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void cascadeClasses(const std::vector&); bool hasTransitions() const; diff --git a/src/mbgl/style/style_layer.cpp b/src/mbgl/style/style_layer.cpp index 262ca23af..3b82d7f94 100644 --- a/src/mbgl/style/style_layer.cpp +++ b/src/mbgl/style/style_layer.cpp @@ -14,7 +14,7 @@ bool StyleLayer::isBackground() const { return type == StyleLayerType::Background; } -void StyleLayer::setClasses(const std::vector &class_names, const timestamp now, +void StyleLayer::setClasses(const std::vector &class_names, const std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition) { // Stores all keys that we have already added transitions for. std::set already_applied; @@ -45,8 +45,8 @@ void StyleLayer::setClasses(const std::vector &class_names, const t if (appliedProperties.mostRecent() != ClassID::Fallback) { // This property key hasn't been set by a previous class, so we need to add a transition // to the fallback value for that key. - const timestamp begin = now + defaultTransition.delay * 1_millisecond; - const timestamp end = begin + defaultTransition.duration * 1_millisecond; + const std::chrono::steady_clock::time_point begin = now + defaultTransition.delay; + const std::chrono::steady_clock::time_point end = begin + defaultTransition.duration; const PropertyValue &value = PropertyFallbackValue::Get(key); appliedProperties.add(ClassID::Fallback, begin, end, value); } @@ -55,7 +55,7 @@ void StyleLayer::setClasses(const std::vector &class_names, const t // Helper function for applying all properties of a a single class that haven't been applied yet. void StyleLayer::applyClassProperties(const ClassID class_id, - std::set &already_applied, timestamp now, + std::set &already_applied, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition) { auto style_it = styles.find(class_id); if (style_it == styles.end()) { @@ -83,8 +83,8 @@ void StyleLayer::applyClassProperties(const ClassID class_id, if (appliedProperties.mostRecent() != class_id) { const PropertyTransition &transition = class_properties.getTransition(key, defaultTransition); - const timestamp begin = now + transition.delay * 1_millisecond; - const timestamp end = begin + transition.duration * 1_millisecond; + const std::chrono::steady_clock::time_point begin = now + transition.delay; + const std::chrono::steady_clock::time_point end = begin + transition.duration; const PropertyValue &value = property_pair.second; appliedProperties.add(class_id, begin, end, value); } @@ -115,7 +115,7 @@ private: }; template -void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const timestamp now) { +void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now) { auto it = appliedStyle.find(key); if (it != appliedStyle.end()) { AppliedClassProperties &applied = it->second; @@ -133,7 +133,7 @@ void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, c } template -void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const timestamp now) { +void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const std::chrono::steady_clock::time_point now) { auto it = appliedStyle.find(key); if (it != appliedStyle.end()) { AppliedClassProperties &applied = it->second; @@ -145,7 +145,7 @@ void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, cons target = mapbox::util::apply_visitor(evaluator, property.value); } else if (now >= property.begin) { // We overwrite the current property partially with the new value. - float progress = float(now - property.begin) / float(property.end - property.begin); + float progress = std::chrono::duration(now - property.begin) / (property.end - property.begin); target = util::interpolate(target, mapbox::util::apply_visitor(evaluator, property.value), progress); } else { // Do not apply this property because its transition hasn't begun yet. @@ -155,7 +155,7 @@ void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, cons } template <> -void StyleLayer::applyStyleProperties(const float z, const timestamp now) { +void StyleLayer::applyStyleProperties(const float z, const std::chrono::steady_clock::time_point now) { properties.set(); FillProperties &fill = properties.get(); applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, now); @@ -169,7 +169,7 @@ void StyleLayer::applyStyleProperties(const float z, const times } template <> -void StyleLayer::applyStyleProperties(const float z, const timestamp now) { +void StyleLayer::applyStyleProperties(const float z, const std::chrono::steady_clock::time_point now) { properties.set(); LineProperties &line = properties.get(); applyTransitionedStyleProperty(PropertyKey::LineOpacity, line.opacity, z, now); @@ -184,11 +184,11 @@ void StyleLayer::applyStyleProperties(const float z, const times applyStyleProperty(PropertyKey::LineImage, line.image, z, now); // for scaling dasharrays - applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), now + 10000); + applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), std::chrono::steady_clock::time_point::max()); } template <> -void StyleLayer::applyStyleProperties(const float z, const timestamp now) { +void StyleLayer::applyStyleProperties(const float z, const std::chrono::steady_clock::time_point now) { properties.set(); SymbolProperties &symbol = properties.get(); applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now); @@ -214,7 +214,7 @@ void StyleLayer::applyStyleProperties(const float z, const tim } template <> -void StyleLayer::applyStyleProperties(const float z, const timestamp now) { +void StyleLayer::applyStyleProperties(const float z, const std::chrono::steady_clock::time_point now) { properties.set(); RasterProperties &raster = properties.get(); applyTransitionedStyleProperty(PropertyKey::RasterOpacity, raster.opacity, z, now); @@ -227,7 +227,7 @@ void StyleLayer::applyStyleProperties(const float z, const tim } template <> -void StyleLayer::applyStyleProperties(const float z, const timestamp now) { +void StyleLayer::applyStyleProperties(const float z, const std::chrono::steady_clock::time_point now) { properties.set(); BackgroundProperties &background = properties.get(); applyTransitionedStyleProperty(PropertyKey::BackgroundOpacity, background.opacity, z, now); @@ -235,7 +235,7 @@ void StyleLayer::applyStyleProperties(const float z, const applyStyleProperty(PropertyKey::BackgroundImage, background.image, z, now); } -void StyleLayer::updateProperties(float z, const timestamp now) { +void StyleLayer::updateProperties(float z, const std::chrono::steady_clock::time_point now) { cleanupAppliedStyleProperties(now); switch (type) { @@ -258,7 +258,7 @@ bool StyleLayer::hasTransitions() const { } -void StyleLayer::cleanupAppliedStyleProperties(timestamp now) { +void StyleLayer::cleanupAppliedStyleProperties(std::chrono::steady_clock::time_point now) { auto it = appliedStyle.begin(); const auto end = appliedStyle.end(); while (it != end) { diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp index 7cf661ca3..b6cfe779c 100644 --- a/src/mbgl/style/style_layer.hpp +++ b/src/mbgl/style/style_layer.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace mbgl { @@ -35,10 +36,10 @@ public: // Updates the StyleProperties information in this layer by evaluating all // pending transitions and applied classes in order. - void updateProperties(float z, timestamp now); + void updateProperties(float z, std::chrono::steady_clock::time_point now); // Sets the list of classes and creates transitions to the currently applied values. - void setClasses(const std::vector &class_names, timestamp now, + void setClasses(const std::vector &class_names, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition); bool hasTransitions() const; @@ -46,16 +47,16 @@ public: private: // Applies all properties from a class, if they haven't been applied already. void applyClassProperties(ClassID class_id, std::set &already_applied, - timestamp now, const PropertyTransition &defaultTransition); + std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition); // Sets the properties of this object by evaluating all pending transitions and // aplied classes in order. - template void applyStyleProperties(float z, timestamp now); - template void applyStyleProperty(PropertyKey key, T &, float z, timestamp now); - template void applyTransitionedStyleProperty(PropertyKey key, T &, float z, timestamp now); + template void applyStyleProperties(float z, std::chrono::steady_clock::time_point now); + template void applyStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now); + template void applyTransitionedStyleProperty(PropertyKey key, T &, float z, std::chrono::steady_clock::time_point now); // Removes all expired style transitions. - void cleanupAppliedStyleProperties(timestamp now); + void cleanupAppliedStyleProperties(std::chrono::steady_clock::time_point now); public: // The name of this layer. diff --git a/src/mbgl/style/style_layer_group.cpp b/src/mbgl/style/style_layer_group.cpp index 0ca0fa0cc..3f1a4a511 100644 --- a/src/mbgl/style/style_layer_group.cpp +++ b/src/mbgl/style/style_layer_group.cpp @@ -2,7 +2,7 @@ namespace mbgl { -void StyleLayerGroup::setClasses(const std::vector &class_names, timestamp now, +void StyleLayerGroup::setClasses(const std::vector &class_names, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition) { for (const util::ptr &layer : layers) { if (layer) { @@ -11,10 +11,10 @@ void StyleLayerGroup::setClasses(const std::vector &class_names, ti } } -void StyleLayerGroup::updateProperties(float z, timestamp t) { +void StyleLayerGroup::updateProperties(float z, std::chrono::steady_clock::time_point now) { for (const util::ptr &layer: layers) { if (layer) { - layer->updateProperties(z, t); + layer->updateProperties(z, now); } } } diff --git a/src/mbgl/style/style_layer_group.hpp b/src/mbgl/style/style_layer_group.hpp index 1af6e23bd..3f5a20073 100644 --- a/src/mbgl/style/style_layer_group.hpp +++ b/src/mbgl/style/style_layer_group.hpp @@ -4,14 +4,15 @@ #include #include +#include namespace mbgl { class StyleLayerGroup { public: - void setClasses(const std::vector &class_names, timestamp now, + void setClasses(const std::vector &class_names, std::chrono::steady_clock::time_point now, const PropertyTransition &defaultTransition); - void updateProperties(float z, timestamp t); + void updateProperties(float z, std::chrono::steady_clock::time_point now); bool hasTransitions() const; public: diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index f048b24bd..e69388e3f 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -367,14 +367,14 @@ template<> std::tuple StyleParser::parseProperty(JSVal PropertyTransition transition; if (value.IsObject()) { if (value.HasMember("duration") && value["duration"].IsNumber()) { - transition.duration = value["duration"].GetUint(); + transition.duration = std::chrono::milliseconds(value["duration"].GetUint()); } if (value.HasMember("delay") && value["delay"].IsNumber()) { - transition.delay = value["delay"].GetUint(); + transition.delay = std::chrono::milliseconds(value["delay"].GetUint()); } } - if (transition.duration == 0 && transition.delay == 0) { + if (transition.duration == std::chrono::steady_clock::duration::zero() && transition.delay == std::chrono::steady_clock::duration::zero()) { return std::tuple { false, std::move(transition) }; } diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp index 3049d9228..724936972 100644 --- a/src/mbgl/util/raster.cpp +++ b/src/mbgl/util/raster.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -91,15 +90,15 @@ void Raster::bind(const GLuint custom_texture) { } void Raster::beginFadeInTransition() { - timestamp start = util::now(); - fade_transition = std::make_shared>(opacity, 1.0, opacity, start, 250_milliseconds); + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + fade_transition = std::make_shared>(opacity, 1.0, opacity, start, std::chrono::milliseconds(250)); } bool Raster::needsTransition() const { return fade_transition != nullptr; } -void Raster::updateTransitions(timestamp now) { +void Raster::updateTransitions(std::chrono::steady_clock::time_point now) { if (fade_transition->update(now) == util::transition::complete) { fade_transition = nullptr; } diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp index bbdfc5321..f9e275461 100644 --- a/src/mbgl/util/raster.hpp +++ b/src/mbgl/util/raster.hpp @@ -8,6 +8,7 @@ #include #include +#include typedef struct uv_loop_s uv_loop_t; @@ -34,7 +35,7 @@ public: // transitions void beginFadeInTransition(); bool needsTransition() const; - void updateTransitions(timestamp now); + void updateTransitions(std::chrono::steady_clock::time_point now); public: // loaded image dimensions diff --git a/src/mbgl/util/stopwatch.cpp b/src/mbgl/util/stopwatch.cpp index 14b4f4018..310bdb8ff 100644 --- a/src/mbgl/util/stopwatch.cpp +++ b/src/mbgl/util/stopwatch.cpp @@ -1,6 +1,5 @@ #ifndef DISABLE_STOPWATCH #include -#include #include #include @@ -10,21 +9,21 @@ using namespace mbgl::util; stopwatch::stopwatch(Event event_) - : event(event_), start(now()) {} + : event(event_), start(std::chrono::steady_clock::now()) {} stopwatch::stopwatch(EventSeverity severity_, Event event_) - : severity(severity_), event(event_), start(now()) {} + : severity(severity_), event(event_), start(std::chrono::steady_clock::now()) {} stopwatch::stopwatch(const std::string &name_, Event event_) - : name(name_), event(event_), start(now()) {} + : name(name_), event(event_), start(std::chrono::steady_clock::now()) {} stopwatch::stopwatch(const std::string &name_, EventSeverity severity_, Event event_) - : name(name_), severity(severity_), event(event_), start(now()) {} + : name(name_), severity(severity_), event(event_), start(std::chrono::steady_clock::now()) {} void stopwatch::report(const std::string &name_) { - timestamp duration = now() - start; + std::chrono::steady_clock::duration duration = std::chrono::steady_clock::now() - start; - Log::Record(severity, event, name_ + ": " + util::toString(double(duration) / 1_millisecond) + "ms"); + Log::Record(severity, event, name_ + ": " + util::toString(std::chrono::duration_cast(duration).count()) + "ms"); start += duration; } diff --git a/src/mbgl/util/stopwatch.hpp b/src/mbgl/util/stopwatch.hpp index 663bbb6fc..bfa384c07 100644 --- a/src/mbgl/util/stopwatch.hpp +++ b/src/mbgl/util/stopwatch.hpp @@ -4,6 +4,7 @@ #include #include +#include namespace mbgl { namespace util { @@ -22,7 +23,7 @@ private: const std::string name; EventSeverity severity = EventSeverity::Debug; Event event = Event::General; - uint64_t start; + std::chrono::steady_clock::time_point start; }; #else class stopwatch { diff --git a/src/mbgl/util/time.cpp b/src/mbgl/util/time.cpp index 1953975b1..94936acff 100644 --- a/src/mbgl/util/time.cpp +++ b/src/mbgl/util/time.cpp @@ -4,10 +4,6 @@ namespace mbgl { namespace util { -timestamp now() { - return uv_hrtime(); -} - static const char *week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; diff --git a/src/mbgl/util/transition.cpp b/src/mbgl/util/transition.cpp index e63a5c311..c47776c9b 100644 --- a/src/mbgl/util/transition.cpp +++ b/src/mbgl/util/transition.cpp @@ -10,7 +10,7 @@ UnitBezier ease(0, 0, 0.25, 1); transition::~transition() {} template -transition::state ease_transition::update(timestamp now) const { +transition::state ease_transition::update(std::chrono::steady_clock::time_point now) const { float t = progress(now); if (t >= 1) { value = to; diff --git a/src/mbgl/util/transition.hpp b/src/mbgl/util/transition.hpp index d6bbc9eba..fc1ecd23c 100644 --- a/src/mbgl/util/transition.hpp +++ b/src/mbgl/util/transition.hpp @@ -2,7 +2,7 @@ #define MBGL_UTIL_TRANSITION #include -#include +#include namespace mbgl { namespace util { @@ -14,34 +14,35 @@ public: complete }; - inline transition(timestamp start_, timestamp duration_) + inline transition(std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_) : start(start_), duration(duration_) {} - inline float progress(timestamp now) const { - if (duration == 0) return 1; + inline float progress(std::chrono::steady_clock::time_point now) const { + if (duration == std::chrono::steady_clock::duration::zero()) return 1; if (start > now) return 0; - return (float)(now - start) / duration; + return std::chrono::duration(now - start) / duration; } - virtual state update(timestamp now) const = 0; + virtual state update(std::chrono::steady_clock::time_point now) const = 0; virtual ~transition(); protected: - const timestamp start, duration; + const std::chrono::steady_clock::time_point start; + const std::chrono::steady_clock::duration duration; }; template class ease_transition : public transition { public: - ease_transition(T from_, T to_, T& value_, timestamp start_, timestamp duration_) + ease_transition(T from_, T to_, T& value_, std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_) : transition(start_, duration_), from(from_), to(to_), value(value_) {} - state update(timestamp now) const; + state update(std::chrono::steady_clock::time_point now) const; private: const T from, to; @@ -52,12 +53,12 @@ private: template class timeout : public transition { public: - timeout(T final_value_, T& value_, timestamp start_, timestamp duration_) + timeout(T final_value_, T& value_, std::chrono::steady_clock::time_point start_, std::chrono::steady_clock::duration duration_) : transition(start_, duration_), final_value(final_value_), value(value_) {} - state update(timestamp now) const { + state update(std::chrono::steady_clock::time_point now) const { if (progress(now) >= 1) { value = final_value; return complete; From 3de43f5f5813465cd1c4ccdeeffa829fd0258fbc Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 3 Feb 2015 14:21:26 -0800 Subject: [PATCH 08/50] fix uninitialized variable warning --- src/mbgl/geometry/sprite_atlas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index 7b6184ae7..a7491b0e3 100644 --- a/src/mbgl/geometry/sprite_atlas.cpp +++ b/src/mbgl/geometry/sprite_atlas.cpp @@ -71,7 +71,7 @@ void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, con dst += dst_y * dst_stride + dst_x; if (wrap) { dst -= dst_stride; - int srcI; + int srcI = 0; // add 1 pixel wrapped padding on each side of the image for (int y = 0; y < height; y++, srcI = (((y + height) % height) + src_y) * src_stride + src_x, dst += dst_stride) { for (int x = -1; x <= width; x++) { From 895d90409b66738f88d3cd7fd75c533fb92f922a Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Fri, 30 Jan 2015 16:38:32 -0800 Subject: [PATCH 09/50] Add functions for layout properties. --- src/mbgl/map/tile_parser.cpp | 105 +++++++++- src/mbgl/map/tile_parser.hpp | 6 + src/mbgl/renderer/symbol_bucket.cpp | 8 +- src/mbgl/style/applied_class_properties.cpp | 2 +- src/mbgl/style/applied_class_properties.hpp | 2 +- src/mbgl/style/property_fallback.cpp | 43 ++++ src/mbgl/style/property_key.hpp | 46 ++++- src/mbgl/style/property_value.hpp | 9 + src/mbgl/style/style_bucket.cpp | 16 +- src/mbgl/style/style_bucket.hpp | 23 ++- src/mbgl/style/style_parser.cpp | 213 ++++++++++++-------- src/mbgl/style/style_parser.hpp | 4 +- src/mbgl/text/placement.cpp | 4 +- 13 files changed, 365 insertions(+), 116 deletions(-) diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp index e8c5a4320..c3e7a8244 100644 --- a/src/mbgl/map/tile_parser.cpp +++ b/src/mbgl/map/tile_parser.cpp @@ -91,6 +91,100 @@ void TileParser::parseStyleLayers(util::ptr group) { } } +template +struct PropertyEvaluator { + typedef T result_type; + PropertyEvaluator(float z_) : z(z_) {} + + template ::value, int>::type = 0> + T operator()(const P &value) const { + return value; + } + + T operator()(const Function &value) const { + return mapbox::util::apply_visitor(FunctionEvaluator(z), value); + } + + template ::value, int>::type = 0> + T operator()(const P &) const { + return T(); + } + +private: + const float z; +}; + +template +void TileParser::applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &target, const float z) { + auto it = classProperties.properties.find(key); + if (it != classProperties.properties.end()) { + const PropertyEvaluator evaluator(z); + target = mapbox::util::apply_visitor(evaluator, it->second); + + } +} + +template <> +void TileParser::applyLayoutProperties(StyleBucket &bucket_desc, const float) { + bucket_desc.render.set(); + // no-op; Fill buckets don't currently have any applicable layout properties +} + +template<> +void TileParser::applyLayoutProperties(StyleBucket &bucket_desc, const float z) { + bucket_desc.render.set(); + StyleBucketLine &line = bucket_desc.render.get(); + applyLayoutProperty(PropertyKey::LineCap, bucket_desc.layout, line.cap, z); + applyLayoutProperty(PropertyKey::LineJoin, bucket_desc.layout, line.join, z); + applyLayoutProperty(PropertyKey::LineMiterLimit, bucket_desc.layout, line.miter_limit, z); + applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, line.round_limit, z); +} + +template<> +void TileParser::applyLayoutProperties(StyleBucket &bucket_desc, const float z) { + bucket_desc.render.set(); + StyleBucketSymbol &symbol = bucket_desc.render.get(); + applyLayoutProperty(PropertyKey::SymbolPlacement, bucket_desc.layout, symbol.placement, z); + if (symbol.placement == PlacementType::Line) { + symbol.icon.rotation_alignment = RotationAlignmentType::Map; + symbol.text.rotation_alignment = RotationAlignmentType::Map; + }; + applyLayoutProperty(PropertyKey::SymbolMinDistance, bucket_desc.layout, symbol.min_distance, z); + applyLayoutProperty(PropertyKey::SymbolAvoidEdges, bucket_desc.layout, symbol.avoid_edges, z); + + applyLayoutProperty(PropertyKey::IconAllowOverlap, bucket_desc.layout, symbol.icon.allow_overlap, z); + applyLayoutProperty(PropertyKey::IconIgnorePlacement, bucket_desc.layout, symbol.icon.ignore_placement, z); + applyLayoutProperty(PropertyKey::IconOptional, bucket_desc.layout, symbol.icon.optional, z); + applyLayoutProperty(PropertyKey::IconRotationAlignment, bucket_desc.layout, symbol.icon.rotation_alignment, z); + applyLayoutProperty(PropertyKey::IconMaxSize, bucket_desc.layout, symbol.icon.max_size, z); + applyLayoutProperty(PropertyKey::IconImage, bucket_desc.layout, symbol.icon.image, z); + applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, symbol.icon.padding, z); + applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, symbol.icon.rotate, z); + applyLayoutProperty(PropertyKey::IconKeepUpright, bucket_desc.layout, symbol.icon.keep_upright, z); + applyLayoutProperty(PropertyKey::IconOffsetX, bucket_desc.layout, symbol.icon.offset[0], z); + applyLayoutProperty(PropertyKey::IconOffsetY, bucket_desc.layout, symbol.icon.offset[1], z); + + applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, symbol.text.rotation_alignment, z); + applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, symbol.text.field, z); + applyLayoutProperty(PropertyKey::TextFont, bucket_desc.layout, symbol.text.font, z); + applyLayoutProperty(PropertyKey::TextMaxSize, bucket_desc.layout, symbol.text.max_size, z); + applyLayoutProperty(PropertyKey::TextMaxWidth, bucket_desc.layout, symbol.text.max_width, z); + applyLayoutProperty(PropertyKey::TextLineHeight, bucket_desc.layout, symbol.text.line_height, z); + applyLayoutProperty(PropertyKey::TextLetterSpacing, bucket_desc.layout, symbol.text.letter_spacing, z); + applyLayoutProperty(PropertyKey::TextMaxAngle, bucket_desc.layout, symbol.text.max_angle, z); + applyLayoutProperty(PropertyKey::TextRotate, bucket_desc.layout, symbol.text.rotate, z); + applyLayoutProperty(PropertyKey::TextPadding, bucket_desc.layout, symbol.text.padding, z); + applyLayoutProperty(PropertyKey::TextIgnorePlacement, bucket_desc.layout, symbol.text.ignore_placement, z); + applyLayoutProperty(PropertyKey::TextOptional, bucket_desc.layout, symbol.text.optional, z); + applyLayoutProperty(PropertyKey::TextJustify, bucket_desc.layout, symbol.text.justify, z); + applyLayoutProperty(PropertyKey::TextAnchor, bucket_desc.layout, symbol.text.anchor, z); + applyLayoutProperty(PropertyKey::TextKeepUpright, bucket_desc.layout, symbol.text.keep_upright, z); + applyLayoutProperty(PropertyKey::TextTransform, bucket_desc.layout, symbol.text.transform, z); + applyLayoutProperty(PropertyKey::TextOffsetX, bucket_desc.layout, symbol.text.offset[0], z); + applyLayoutProperty(PropertyKey::TextOffsetY, bucket_desc.layout, symbol.text.offset[1], z); + applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, symbol.text.allow_overlap, z); +} + std::unique_ptr TileParser::createBucket(util::ptr bucket_desc) { if (!bucket_desc) { fprintf(stderr, "missing bucket desc\n"); @@ -105,13 +199,16 @@ std::unique_ptr TileParser::createBucket(util::ptr bucket_d auto layer_it = vector_data.layers.find(bucket_desc->source_layer); if (layer_it != vector_data.layers.end()) { const VectorTileLayer &layer = layer_it->second; - if (bucket_desc->render.is()) { + if (bucket_desc->type == StyleLayerType::Fill) { + applyLayoutProperties(*bucket_desc, tile.id.z); return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { + } else if (bucket_desc->type == StyleLayerType::Line) { + applyLayoutProperties(*bucket_desc, tile.id.z); return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { + } else if (bucket_desc->type == StyleLayerType::Symbol) { + applyLayoutProperties(*bucket_desc, tile.id.z); return createSymbolBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { + } else if (bucket_desc->type == StyleLayerType::Raster) { return nullptr; } else { fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s' (source layer '%s')\n", bucket_desc->name.c_str(), bucket_desc->source_layer.c_str()); diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp index beae3af83..d10f9aa26 100644 --- a/src/mbgl/map/tile_parser.hpp +++ b/src/mbgl/map/tile_parser.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -48,6 +50,10 @@ public: private: bool obsolete() const; void parseStyleLayers(util::ptr group); + + template void applyLayoutProperties(StyleBucket &bucket, float z); + template void applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &, float z); + std::unique_ptr createBucket(util::ptr bucket_desc); std::unique_ptr createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill); diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index a5043a373..00ff4e4a3 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -177,13 +177,13 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress if (feature.label.length()) { shaping = fontStack.getShaping( /* string */ feature.label, - /* maxWidth */ properties.text.max_width, - /* lineHeight */ properties.text.line_height, + /* maxWidth: ems */ properties.text.max_width * 24, + /* lineHeight: ems */ properties.text.line_height * 24, /* horizontalAlign */ horizontalAlign, /* verticalAlign */ verticalAlign, /* justify */ justify, - /* spacing */ properties.text.letter_spacing, - /* translate */ properties.text.offset); + /* spacing: ems */ properties.text.letter_spacing * 24, + /* translate */ vec2(properties.text.offset[0], properties.text.offset[1])); // Add the glyphs we need for this label to the glyph atlas. if (shaping.size()) { diff --git a/src/mbgl/style/applied_class_properties.cpp b/src/mbgl/style/applied_class_properties.cpp index 4e5647ce4..6e304e69d 100644 --- a/src/mbgl/style/applied_class_properties.cpp +++ b/src/mbgl/style/applied_class_properties.cpp @@ -8,7 +8,7 @@ AppliedClassProperty::AppliedClassProperty(ClassID class_id, std::chrono::steady end(end_), value(value_) {} -// Returns thie ID of the most recent +// Returns the ID of the most recent ClassID AppliedClassProperties::mostRecent() const { return properties.size() ? properties.back().name : ClassID::Fallback; } diff --git a/src/mbgl/style/applied_class_properties.hpp b/src/mbgl/style/applied_class_properties.hpp index ac086a224..bd8409559 100644 --- a/src/mbgl/style/applied_class_properties.hpp +++ b/src/mbgl/style/applied_class_properties.hpp @@ -26,7 +26,7 @@ public: std::list properties; public: - // Returns thie ID of the most recent + // Returns the ID of the most recent ClassID mostRecent() const; void add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value); bool hasTransitions() const; diff --git a/src/mbgl/style/property_fallback.cpp b/src/mbgl/style/property_fallback.cpp index dc747b37e..5fc3ce1f0 100644 --- a/src/mbgl/style/property_fallback.cpp +++ b/src/mbgl/style/property_fallback.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace mbgl { @@ -52,6 +53,48 @@ const std::map PropertyFallbackValue::properties = { { PropertyKey::BackgroundOpacity, defaultStyleProperties().opacity }, { PropertyKey::BackgroundColor, defaultStyleProperties().color }, + + { PropertyKey::LineCap, defaultLayoutProperties().cap }, + { PropertyKey::LineJoin, defaultLayoutProperties().join }, + { PropertyKey::LineMiterLimit, defaultLayoutProperties().miter_limit }, + { PropertyKey::LineRoundLimit, defaultLayoutProperties().round_limit }, + + { PropertyKey::SymbolPlacement, defaultLayoutProperties().placement }, + { PropertyKey::SymbolMinDistance, defaultLayoutProperties().min_distance }, + { PropertyKey::SymbolAvoidEdges, defaultLayoutProperties().avoid_edges }, + + { PropertyKey::IconAllowOverlap, defaultLayoutProperties().icon.allow_overlap }, + { PropertyKey::IconIgnorePlacement, defaultLayoutProperties().icon.ignore_placement }, + { PropertyKey::IconOptional, defaultLayoutProperties().icon.optional }, + { PropertyKey::IconRotationAlignment, defaultLayoutProperties().icon.rotation_alignment }, + { PropertyKey::IconMaxSize, defaultLayoutProperties().icon.max_size }, + { PropertyKey::IconImage, defaultLayoutProperties().icon.image }, + { PropertyKey::IconRotate, defaultLayoutProperties().icon.rotate }, + { PropertyKey::IconPadding, defaultLayoutProperties().icon.padding }, + { PropertyKey::IconKeepUpright, defaultLayoutProperties().icon.keep_upright }, + { PropertyKey::IconOffsetX, defaultLayoutProperties().icon.offset[0] }, + { PropertyKey::IconOffsetY, defaultLayoutProperties().icon.offset[1] }, + + { PropertyKey::TextRotationAlignment, defaultLayoutProperties().text.rotation_alignment }, + { PropertyKey::TextField, defaultLayoutProperties().text.field }, + { PropertyKey::TextFont, defaultLayoutProperties().text.font }, + { PropertyKey::TextMaxSize, defaultLayoutProperties().text.max_size }, + { PropertyKey::TextMaxWidth, defaultLayoutProperties().text.max_width }, + { PropertyKey::TextLineHeight, defaultLayoutProperties().text.line_height }, + { PropertyKey::TextLetterSpacing, defaultLayoutProperties().text.letter_spacing }, + { PropertyKey::TextJustify, defaultLayoutProperties().text.justify }, + { PropertyKey::TextAnchor, defaultLayoutProperties().text.anchor }, + { PropertyKey::TextMaxAngle, defaultLayoutProperties().text.max_angle }, + { PropertyKey::TextRotate, defaultLayoutProperties().text.rotate }, + { PropertyKey::TextPadding, defaultLayoutProperties().text.padding }, + { PropertyKey::TextKeepUpright, defaultLayoutProperties().text.keep_upright }, + { PropertyKey::TextTransform, defaultLayoutProperties().text.transform }, + { PropertyKey::TextOffsetX, defaultLayoutProperties().text.offset[0] }, + { PropertyKey::TextOffsetY, defaultLayoutProperties().text.offset[1] }, + { PropertyKey::TextAllowOverlap, defaultLayoutProperties().text.allow_overlap }, + { PropertyKey::TextIgnorePlacement, defaultLayoutProperties().text.ignore_placement }, + { PropertyKey::TextOptional, defaultLayoutProperties().text.optional }, + }; const PropertyValue PropertyFallbackValue::defaultProperty = false; diff --git a/src/mbgl/style/property_key.hpp b/src/mbgl/style/property_key.hpp index e283f1157..f10607a7a 100644 --- a/src/mbgl/style/property_key.hpp +++ b/src/mbgl/style/property_key.hpp @@ -26,8 +26,16 @@ enum class PropertyKey { LineDashArray, // for transitions only LineImage, + LineCap, + LineJoin, + LineMiterLimit, + LineRoundLimit, + + SymbolPlacement, + SymbolMinDistance, + SymbolAvoidEdges, + IconOpacity, - IconRotate, IconSize, IconColor, IconHaloColor, @@ -38,6 +46,18 @@ enum class PropertyKey { IconTranslateY, IconTranslateAnchor, + IconAllowOverlap, + IconIgnorePlacement, + IconOptional, + IconRotationAlignment, + IconMaxSize, + IconImage, + IconPadding, + IconRotate, + IconKeepUpright, + IconOffsetX, + IconOffsetY, + TextOpacity, TextSize, TextColor, @@ -49,6 +69,26 @@ enum class PropertyKey { TextTranslateY, TextTranslateAnchor, + TextRotationAlignment, + TextField, + TextFont, + TextMaxSize, + TextMaxWidth, + TextLineHeight, + TextLetterSpacing, + TextMaxAngle, + TextRotate, + TextPadding, + TextIgnorePlacement, + TextOptional, + TextJustify, + TextAnchor, + TextKeepUpright, + TextTransform, + TextOffsetX, + TextOffsetY, + TextAllowOverlap, + RasterOpacity, RasterHueRotate, RasterBrightness, // for transitions only @@ -60,7 +100,9 @@ enum class PropertyKey { BackgroundOpacity, BackgroundColor, - BackgroundImage + BackgroundImage, + + Visibilty }; } diff --git a/src/mbgl/style/property_value.hpp b/src/mbgl/style/property_value.hpp index e017981de..ff5a2f868 100644 --- a/src/mbgl/style/property_value.hpp +++ b/src/mbgl/style/property_value.hpp @@ -13,6 +13,15 @@ typedef mapbox::util::variant< std::string, TranslateAnchorType, RotateAnchorType, + bool, + CapType, + JoinType, + VisibilityType, + PlacementType, + RotationAlignmentType, + TextTransformType, + TextJustifyType, + TextAnchorType, Function, Function, Function, diff --git a/src/mbgl/style/style_bucket.cpp b/src/mbgl/style/style_bucket.cpp index 9a40c2386..20d5d1147 100644 --- a/src/mbgl/style/style_bucket.cpp +++ b/src/mbgl/style/style_bucket.cpp @@ -2,14 +2,12 @@ namespace mbgl { -StyleBucket::StyleBucket(StyleLayerType type) { - switch (type) { - case StyleLayerType::Fill: render = StyleBucketFill{}; break; - case StyleLayerType::Line: render = StyleBucketLine{}; break; - case StyleLayerType::Symbol: render = StyleBucketSymbol{}; break; - case StyleLayerType::Raster: render = StyleBucketRaster{}; break; - default: break; - } -} +StyleBucket::StyleBucket(StyleLayerType type_) :type(type_) {} + +template<> const StyleBucketFill &defaultLayoutProperties() { static StyleBucketFill p; return p; } +template<> const StyleBucketLine &defaultLayoutProperties() { static StyleBucketLine p; return p; } +template<> const StyleBucketSymbol &defaultLayoutProperties() { static StyleBucketSymbol p; return p; } +template<> const StyleBucketRaster &defaultLayoutProperties() { static StyleBucketRaster p; return p; } +template<> const StyleBucketBackground &defaultLayoutProperties() { static StyleBucketBackground p; return p; } } \ No newline at end of file diff --git a/src/mbgl/style/style_bucket.hpp b/src/mbgl/style/style_bucket.hpp index 7a70e32c3..702d0286a 100644 --- a/src/mbgl/style/style_bucket.hpp +++ b/src/mbgl/style/style_bucket.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -52,7 +53,7 @@ public: float rotate = 0.0f; float padding = 2.0f; bool keep_upright = false; - vec2 offset = {0, 0}; + std::array offset = {{ 0, 0 }}; } icon; struct { @@ -60,9 +61,9 @@ public: std::string field; std::string font; float max_size = 16.0f; - float max_width = 15.0f * 24 /* em */; - float line_height = 1.2f * 24 /* em */; - float letter_spacing = 0.0f * 24 /* em */; + float max_width = 15.0f /* em */; + float line_height = 1.2f /* em */; + float letter_spacing = 0.0f /* em */; TextJustifyType justify = TextJustifyType::Center; TextAnchorType anchor = TextAnchorType::Center; float max_angle = 45.0f /* degrees */; @@ -71,7 +72,7 @@ public: float padding = 2.0f; bool keep_upright = true; TextTransformType transform = TextTransformType::None; - vec2 offset = {0, 0}; + std::array offset = {{ 0, 0 }}; bool allow_overlap = false; bool ignore_placement = false; bool optional = false; @@ -82,8 +83,12 @@ class StyleBucketRaster { public: }; +class StyleBucketBackground { +public: +}; + typedef mapbox::util::variant StyleBucketRender; + StyleBucketRaster, StyleBucketBackground, std::false_type> StyleBucketRender; class StyleBucket { @@ -91,18 +96,20 @@ public: typedef util::ptr Ptr; StyleBucket(StyleLayerType type); - + StyleLayerType type; std::string name; util::ptr style_source; std::string source_layer; FilterExpression filter; + ClassProperties layout; StyleBucketRender render = std::false_type(); float min_zoom = -std::numeric_limits::infinity(); float max_zoom = std::numeric_limits::infinity(); VisibilityType visibility = VisibilityType::Visible; }; - +template +const T &defaultLayoutProperties(); }; diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index e69388e3f..15aedd8ea 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -327,6 +327,18 @@ bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKe return parsed; } +template +void StyleParser::parseVisibility(StyleBucket &bucket, JSVal value) { + if (!value.HasMember("visibility")) { + return; + } else if (!value["visibility"].IsString()) { + Log::Warning(Event::ParseStyle, "value of 'visibility' must be a string"); + bucket.visibility = VisibilityType::Visible; + return; + } + bucket.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() }); +} + template bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) { if (!value.HasMember(property_name)) { @@ -345,6 +357,15 @@ template<> std::tuple StyleParser::parseProperty(JSVal value, return std::tuple { true, { value.GetString(), value.GetStringLength() } }; } +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsBool()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", property_name); + return std::tuple { false, true }; + } + + return std::tuple { true, value.GetBool() }; +} + template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); @@ -363,6 +384,69 @@ template<> std::tuple StyleParser::parseProperty { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; } +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple { false, CapType::Butt }; + } + + return std::tuple { true, CapTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple { false, JoinType::Miter }; + } + + return std::tuple { true, JoinTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple { false, PlacementType::Point }; + } + + return std::tuple { true, PlacementTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple { false, TextAnchorType::Center }; + } + + return std::tuple { true, TextAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple { false, TextJustifyType::Center }; + } + + return std::tuple { true, TextJustifyTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple { false, TextTransformType::None }; + } + + return std::tuple { true, TextTransformTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); + return std::tuple { false, RotationAlignmentType::Map }; + } + + return std::tuple { true, RotationAlignmentTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + template<> std::tuple StyleParser::parseProperty(JSVal value, const char */*property_name*/) { PropertyTransition transition; if (value.IsObject()) { @@ -642,6 +726,50 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) { parseOptionalProperty("background-image", Key::BackgroundImage, klass, value); } +void StyleParser::parseLayout(JSVal value, util::ptr &bucket) { + using Key = PropertyKey; + + parseVisibility(*bucket, value); + + parseOptionalProperty("line-cap", Key::LineCap, bucket->layout, value); + parseOptionalProperty("line-join", Key::LineJoin, bucket->layout, value); + parseOptionalProperty>("line-miter-limit", Key::LineMiterLimit, bucket->layout, value); + parseOptionalProperty>("line-round-limit", Key::LineRoundLimit, bucket->layout, value); + + parseOptionalProperty("symbol-placement", Key::SymbolPlacement, bucket->layout, value); + parseOptionalProperty>("symbol-min-distance", Key::SymbolMinDistance, bucket->layout, value); + parseOptionalProperty("symbol-avoid-edges", Key::SymbolAvoidEdges, bucket->layout, value); + parseOptionalProperty("icon-allow-overlap", Key::IconAllowOverlap, bucket->layout, value); + parseOptionalProperty("icon-ignore-placement", Key::IconIgnorePlacement, bucket->layout, value); + parseOptionalProperty("icon-optional", Key::IconOptional, bucket->layout, value); + parseOptionalProperty("icon-rotation-alignment", Key::IconRotationAlignment, bucket->layout, value); + parseOptionalProperty>("icon-max-size", Key::IconMaxSize, bucket->layout, value); + parseOptionalProperty("icon-image", Key::IconImage, bucket->layout, value); + parseOptionalProperty>("icon-rotate", Key::IconRotate, bucket->layout, value); + parseOptionalProperty>("icon-padding", Key::IconPadding, bucket->layout, value); + parseOptionalProperty("icon-keep-upright", Key::IconKeepUpright, bucket->layout, value); + parseOptionalProperty>("icon-offset", { Key::IconOffsetX, Key::IconOffsetY }, bucket->layout, value); + parseOptionalProperty("text-rotation-alignment", Key::TextRotationAlignment, bucket->layout, value); + parseOptionalProperty("text-field", Key::TextField, bucket->layout, value); + parseOptionalProperty("text-font", Key::TextFont, bucket->layout, value); + parseOptionalProperty>("text-max-size", Key::TextMaxSize, bucket->layout, value); + parseOptionalProperty>("text-max-width", Key::TextMaxWidth, bucket->layout, value); + parseOptionalProperty>("text-line-height", Key::TextLineHeight, bucket->layout, value); + parseOptionalProperty>("text-letter-spacing", Key::TextLetterSpacing, bucket->layout, value); + parseOptionalProperty("text-justify", Key::TextJustify, bucket->layout, value); + parseOptionalProperty("text-anchor", Key::TextAnchor, bucket->layout, value); + parseOptionalProperty>("text-max-angle", Key::TextMaxAngle, bucket->layout, value); + parseOptionalProperty>("text-rotate", Key::TextRotate, bucket->layout, value); + parseOptionalProperty>("text-padding", Key::TextPadding, bucket->layout, value); + parseOptionalProperty("text-keep-upright", Key::TextKeepUpright, bucket->layout, value); + parseOptionalProperty("text-transform", Key::TextTransform, bucket->layout, value); + parseOptionalProperty>("text-offset", { Key::TextOffsetX, Key::TextOffsetY }, bucket->layout, value); + parseOptionalProperty("text-allow-overlap", Key::TextAllowOverlap, bucket->layout, value); + parseOptionalProperty("text-ignore-placement", Key::TextIgnorePlacement, bucket->layout, value); + parseOptionalProperty("text-optional", Key::TextOptional, bucket->layout, value); + +} + void StyleParser::parseReference(JSVal value, util::ptr &layer) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", layer->id.c_str()); @@ -704,7 +832,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr &layer) { if (value.HasMember("layout")) { JSVal value_render = replaceConstant(value["layout"]); - parseLayout(value_render, layer); + parseLayout(value_render, layer->bucket); } if (value.HasMember("minzoom")) { @@ -726,89 +854,6 @@ void StyleParser::parseBucket(JSVal value, util::ptr &layer) { } } -void StyleParser::parseLayout(JSVal value, util::ptr &layer) { - if (!value.IsObject()) { - Log::Warning(Event::ParseStyle, "layout property of layer '%s' must be an object", layer->id.c_str()); - return; - } - - StyleBucket &bucket = *layer->bucket; - parseRenderProperty(value, bucket.visibility, "visibility"); - - switch (layer->type) { - case StyleLayerType::Fill: { - StyleBucketFill &render = bucket.render.get(); - - parseRenderProperty(value, render.winding, "fill-winding"); - } break; - - case StyleLayerType::Line: { - StyleBucketLine &render = bucket.render.get(); - - parseRenderProperty(value, render.cap, "line-cap"); - parseRenderProperty(value, render.join, "line-join"); - parseRenderProperty(value, render.miter_limit, "line-miter-limit"); - parseRenderProperty(value, render.round_limit, "line-round-limit"); - } break; - - case StyleLayerType::Symbol: { - StyleBucketSymbol &render = bucket.render.get(); - - parseRenderProperty(value, render.placement, "symbol-placement"); - if (render.placement == PlacementType::Line) { - // Change the default value in case of line placement. - render.text.rotation_alignment = RotationAlignmentType::Map; - render.icon.rotation_alignment = RotationAlignmentType::Map; - } - - parseRenderProperty(value, render.min_distance, "symbol-min-distance"); - parseRenderProperty(value, render.avoid_edges, "symbol-avoid-edges"); - - parseRenderProperty(value, render.icon.allow_overlap, "icon-allow-overlap"); - parseRenderProperty(value, render.icon.ignore_placement, "icon-ignore-placement"); - parseRenderProperty(value, render.icon.optional, "icon-optional"); - parseRenderProperty(value, render.icon.rotation_alignment, "icon-rotation-alignment"); - parseRenderProperty(value, render.icon.max_size, "icon-max-size"); - parseRenderProperty(value, render.icon.image, "icon-image"); - parseRenderProperty(value, render.icon.rotate, "icon-rotate"); - parseRenderProperty(value, render.icon.padding, "icon-padding"); - parseRenderProperty(value, render.icon.keep_upright, "icon-keep-upright"); - parseRenderProperty(value, render.icon.offset, "icon-offset"); - - - parseRenderProperty(value, render.text.rotation_alignment, "text-rotation-alignment"); - parseRenderProperty(value, render.text.field, "text-field"); - parseRenderProperty(value, render.text.font, "text-font"); - parseRenderProperty(value, render.text.max_size, "text-max-size"); - if (parseRenderProperty(value, render.text.max_width, "text-max-width")) { - render.text.max_width *= 24; // em - } - if (parseRenderProperty(value, render.text.line_height, "text-line-height")) { - render.text.line_height *= 24; // em - } - if (parseRenderProperty(value, render.text.letter_spacing, "text-letter-spacing")) { - render.text.letter_spacing *= 24; // em - } - parseRenderProperty(value, render.text.justify, "text-justify"); - parseRenderProperty(value, render.text.anchor, "text-anchor"); - parseRenderProperty(value, render.text.max_angle, "text-max-angle"); - parseRenderProperty(value, render.text.rotate, "text-rotate"); - parseRenderProperty(value, render.text.slant, "text-slant"); - parseRenderProperty(value, render.text.padding, "text-padding"); - parseRenderProperty(value, render.text.keep_upright, "text-keep-upright"); - parseRenderProperty(value, render.text.transform, "text-transform"); - parseRenderProperty(value, render.text.offset, "text-offset"); - parseRenderProperty(value, render.text.allow_overlap, "text-allow-overlap"); - parseRenderProperty(value, render.text.ignore_placement, "text-ignore-placement"); - parseRenderProperty(value, render.text.optional, "text-optional"); - } break; - - default: - // There are no render properties for these layer types. - break; - } -} - void StyleParser::parseSprite(JSVal value) { if (value.IsString()) { sprite = { value.GetString(), value.GetStringLength() }; diff --git a/src/mbgl/style/style_parser.hpp b/src/mbgl/style/style_parser.hpp index 42e22f945..24fd4292a 100644 --- a/src/mbgl/style/style_parser.hpp +++ b/src/mbgl/style/style_parser.hpp @@ -53,7 +53,7 @@ private: void parsePaint(JSVal, ClassProperties &properties); void parseReference(JSVal value, util::ptr &layer); void parseBucket(JSVal value, util::ptr &layer); - void parseLayout(JSVal value, util::ptr &layer); + void parseLayout(JSVal value, util::ptr &bucket); void parseSprite(JSVal value); void parseGlyphURL(JSVal value); @@ -65,6 +65,8 @@ private: // Parses optional properties into style class properties. template + void parseVisibility(StyleBucket &bucket, JSVal value); + template bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value); template bool parseOptionalProperty(const char *property_name, const std::vector &keys, ClassProperties &klass, JSVal value); diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index bc0f642c4..27e2b7010 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -131,8 +131,8 @@ GlyphBox getMergedBoxes(const GlyphBoxes &glyphs, const Anchor &anchor) { Placement Placement::getIcon(Anchor &anchor, const Rect &image, float boxScale, const std::vector &line, const StyleBucketSymbol &props) { - const float dx = props.icon.offset.x; - const float dy = props.icon.offset.y; + const float dx = props.icon.offset[0]; + const float dy = props.icon.offset[1]; float x1 = dx - image.originalW / 2.0f; float x2 = x1 + image.w; float y1 = dy - image.originalH / 2.0f; From 44be4862345af7a51f1e533da9cdf920496817b7 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 3 Feb 2015 17:05:29 -0800 Subject: [PATCH 10/50] Remove unused Fixes #786 Fixes #797 --- src/mbgl/style/style_bucket.hpp | 2 -- src/mbgl/style/types.hpp | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/src/mbgl/style/style_bucket.hpp b/src/mbgl/style/style_bucket.hpp index 702d0286a..08884c2e0 100644 --- a/src/mbgl/style/style_bucket.hpp +++ b/src/mbgl/style/style_bucket.hpp @@ -19,7 +19,6 @@ class Source; class StyleBucketFill { public: - WindingType winding = WindingType::NonZero; }; class StyleBucketLine { @@ -68,7 +67,6 @@ public: TextAnchorType anchor = TextAnchorType::Center; float max_angle = 45.0f /* degrees */; float rotate = 0.0f; - float slant = 0.0f; float padding = 2.0f; bool keep_upright = true; TextTransformType transform = TextTransformType::None; diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp index c0b873dcd..614927017 100644 --- a/src/mbgl/style/types.hpp +++ b/src/mbgl/style/types.hpp @@ -62,18 +62,6 @@ MBGL_DEFINE_ENUM_CLASS(VisibilityTypeClass, VisibilityType, { // ------------------------------------------------------------------------------------------------- -enum class WindingType : bool { - EvenOdd, - NonZero, -}; - -MBGL_DEFINE_ENUM_CLASS(WindingTypeClass, WindingType, { - { WindingType::EvenOdd, "even-odd" }, - { WindingType::NonZero, "non-zero" }, -}); - -// ------------------------------------------------------------------------------------------------- - enum class CapType : uint8_t { Round, Butt, From 78a7606d78f47873be90bc83c008f338e59ab6f0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 3 Feb 2015 18:35:02 -0800 Subject: [PATCH 11/50] try building against alt zlib-system2 package - refs mapbox/mason#66 --- configure | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 14982c211..93a303d45 100755 --- a/configure +++ b/configure @@ -21,7 +21,7 @@ case ${MASON_PLATFORM} in 'ios') SQLITE_VERSION=system LIBUV_VERSION=0.10.28 - ZLIB_VERSION=system + ZLIB_VERSION=system2 BOOST_VERSION=system ;; 'android') @@ -31,7 +31,7 @@ case ${MASON_PLATFORM} in OPENSSL_VERSION=1.0.1l LIBCURL_VERSION=7.40.0 LIBUV_VERSION=0.11.29 - ZLIB_VERSION=system + ZLIB_VERSION=system2 BOOST_VERSION=1.57.0 NUNICODE_VERSION=1.5.1 LIBZIP_VERSION=0.11.2 @@ -43,7 +43,7 @@ case ${MASON_PLATFORM} in LIBJPEG_VERSION=v9a LIBCURL_VERSION=system LIBUV_VERSION=0.10.28 - ZLIB_VERSION=system + ZLIB_VERSION=system2 BOOST_VERSION=system NUNICODE_VERSION=1.5.1 ;; From 734ce7aeb4770f1af26a9715307ff7aeb61fcecf Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 3 Feb 2015 18:52:38 -0800 Subject: [PATCH 12/50] stop allowing failures for mips-64 --- .travis.yml | 4 ---- configure | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2eb344834..9dccffe9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,10 +37,6 @@ matrix: - os: osx env: BUILDTYPE=Debug JOBS=8 compiler: clang - allow_failures: - - os: linux - env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=mips-64 TESTMUNK=no - compiler: clang env: global: diff --git a/configure b/configure index 93a303d45..14982c211 100755 --- a/configure +++ b/configure @@ -21,7 +21,7 @@ case ${MASON_PLATFORM} in 'ios') SQLITE_VERSION=system LIBUV_VERSION=0.10.28 - ZLIB_VERSION=system2 + ZLIB_VERSION=system BOOST_VERSION=system ;; 'android') @@ -31,7 +31,7 @@ case ${MASON_PLATFORM} in OPENSSL_VERSION=1.0.1l LIBCURL_VERSION=7.40.0 LIBUV_VERSION=0.11.29 - ZLIB_VERSION=system2 + ZLIB_VERSION=system BOOST_VERSION=1.57.0 NUNICODE_VERSION=1.5.1 LIBZIP_VERSION=0.11.2 @@ -43,7 +43,7 @@ case ${MASON_PLATFORM} in LIBJPEG_VERSION=v9a LIBCURL_VERSION=system LIBUV_VERSION=0.10.28 - ZLIB_VERSION=system2 + ZLIB_VERSION=system BOOST_VERSION=system NUNICODE_VERSION=1.5.1 ;; From b9bf66e67ed1d0d1b1d3163255cab099a6ba4a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Fri, 16 Jan 2015 14:04:41 +0100 Subject: [PATCH 13/50] rewrite storage layer to be independent of the Map's event loop --- gyp/mbgl-ios.gypi | 5 +- gyp/mbgl-osx.gypi | 7 +- include/mbgl/map/map.hpp | 8 + include/mbgl/storage/asset_request.hpp | 27 - .../mbgl/storage/caching_http_file_source.hpp | 62 -- .../mbgl/storage/default/asset_request.hpp | 24 + .../storage/default/default_file_source.hpp | 60 ++ include/mbgl/storage/default/http_context.hpp | 117 ++++ include/mbgl/storage/default/http_request.hpp | 26 + include/mbgl/storage/default/request.hpp | 46 ++ .../storage/default/shared_request_base.hpp | 89 +++ include/mbgl/storage/default/sqlite_cache.hpp | 52 ++ include/mbgl/storage/file_cache.hpp | 26 + include/mbgl/storage/file_source.hpp | 33 +- include/mbgl/storage/http_request_baton.hpp | 74 -- include/mbgl/storage/network_status.hpp | 25 + include/mbgl/storage/request.hpp | 41 -- include/mbgl/storage/request_callback.hpp | 22 - include/mbgl/storage/resource.hpp | 39 ++ include/mbgl/storage/resource_type.hpp | 18 - include/mbgl/storage/response.hpp | 14 +- include/mbgl/util/async_queue.hpp | 95 +++ include/mbgl/util/util.hpp | 15 + include/mbgl/util/uv.hpp | 12 +- include/mbgl/util/variant.hpp | 72 +- ios/mapbox-gl-cocoa | 2 +- macosx/main.mm | 13 +- platform/darwin/http_request_baton_cocoa.mm | 155 ----- platform/darwin/http_request_cocoa.mm | 388 +++++++++++ platform/default/asset_request_libuv.cpp | 246 +++---- platform/default/cache_database_tmp.cpp | 12 - platform/default/http_request_baton_curl.cpp | 629 ----------------- platform/default/http_request_curl.cpp | 640 ++++++++++++++++++ src/mbgl/map/map.cpp | 38 +- src/mbgl/map/raster_tile_data.cpp | 4 +- src/mbgl/map/raster_tile_data.hpp | 2 +- src/mbgl/map/source.cpp | 18 +- src/mbgl/map/source.hpp | 4 +- src/mbgl/map/sprite.cpp | 13 +- src/mbgl/map/tile_data.cpp | 31 +- src/mbgl/map/tile_data.hpp | 9 +- src/mbgl/map/vector_tile_data.cpp | 11 +- src/mbgl/map/vector_tile_data.hpp | 2 +- src/mbgl/storage/base_request.cpp | 87 --- src/mbgl/storage/base_request.hpp | 62 -- src/mbgl/storage/caching_http_file_source.cpp | 136 ---- src/mbgl/storage/default_file_source.cpp | 239 +++++++ src/mbgl/storage/http_request.cpp | 280 -------- src/mbgl/storage/http_request.hpp | 58 -- src/mbgl/storage/http_request_baton.cpp | 12 - src/mbgl/storage/network_status.cpp | 32 + src/mbgl/storage/request.cpp | 97 ++- src/mbgl/storage/response.cpp | 22 - src/mbgl/storage/sqlite_cache.cpp | 255 +++++++ src/mbgl/storage/sqlite_store.cpp | 228 ------- src/mbgl/storage/sqlite_store.hpp | 49 -- src/mbgl/style/style.hpp | 1 + src/mbgl/text/glyph_store.cpp | 33 +- src/mbgl/util/uv.cpp | 8 + 59 files changed, 2570 insertions(+), 2255 deletions(-) delete mode 100644 include/mbgl/storage/asset_request.hpp delete mode 100644 include/mbgl/storage/caching_http_file_source.hpp create mode 100644 include/mbgl/storage/default/asset_request.hpp create mode 100644 include/mbgl/storage/default/default_file_source.hpp create mode 100644 include/mbgl/storage/default/http_context.hpp create mode 100644 include/mbgl/storage/default/http_request.hpp create mode 100644 include/mbgl/storage/default/request.hpp create mode 100644 include/mbgl/storage/default/shared_request_base.hpp create mode 100644 include/mbgl/storage/default/sqlite_cache.hpp create mode 100644 include/mbgl/storage/file_cache.hpp delete mode 100644 include/mbgl/storage/http_request_baton.hpp create mode 100644 include/mbgl/storage/network_status.hpp delete mode 100644 include/mbgl/storage/request.hpp delete mode 100644 include/mbgl/storage/request_callback.hpp create mode 100644 include/mbgl/storage/resource.hpp delete mode 100644 include/mbgl/storage/resource_type.hpp create mode 100644 include/mbgl/util/async_queue.hpp create mode 100644 include/mbgl/util/util.hpp delete mode 100644 platform/darwin/http_request_baton_cocoa.mm create mode 100644 platform/darwin/http_request_cocoa.mm delete mode 100644 platform/default/cache_database_tmp.cpp delete mode 100644 platform/default/http_request_baton_curl.cpp create mode 100644 platform/default/http_request_curl.cpp delete mode 100644 src/mbgl/storage/base_request.cpp delete mode 100644 src/mbgl/storage/base_request.hpp delete mode 100644 src/mbgl/storage/caching_http_file_source.cpp create mode 100644 src/mbgl/storage/default_file_source.cpp delete mode 100644 src/mbgl/storage/http_request.cpp delete mode 100644 src/mbgl/storage/http_request.hpp delete mode 100644 src/mbgl/storage/http_request_baton.cpp create mode 100644 src/mbgl/storage/network_status.cpp delete mode 100644 src/mbgl/storage/response.cpp create mode 100644 src/mbgl/storage/sqlite_cache.cpp delete mode 100644 src/mbgl/storage/sqlite_store.cpp delete mode 100644 src/mbgl/storage/sqlite_store.hpp diff --git a/gyp/mbgl-ios.gypi b/gyp/mbgl-ios.gypi index b1a55c817..2bc9ec718 100644 --- a/gyp/mbgl-ios.gypi +++ b/gyp/mbgl-ios.gypi @@ -47,7 +47,10 @@ '../src', ], 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': [ '<@(uv_cflags)' ], + 'OTHER_CPLUSPLUSFLAGS': [ + '<@(uv_cflags)', + '-I<(boost_root)/include', + ], }, 'direct_dependent_settings': { 'include_dirs': [ diff --git a/gyp/mbgl-osx.gypi b/gyp/mbgl-osx.gypi index 98b66e62b..49135fd6c 100644 --- a/gyp/mbgl-osx.gypi +++ b/gyp/mbgl-osx.gypi @@ -12,7 +12,7 @@ '../platform/osx/cache_database_application_support.mm', '../platform/darwin/log_nslog.mm', '../platform/darwin/string_nsstring.mm', - '../platform/darwin/http_request_baton_cocoa.mm', + '../platform/darwin/http_request_cocoa.mm', '../platform/darwin/application_root.mm', '../platform/darwin/image.mm', '../platform/default/asset_request_libuv.cpp', @@ -22,7 +22,10 @@ '../src', ], 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': [ '<@(uv_cflags)' ], + 'OTHER_CPLUSPLUSFLAGS': [ + '<@(uv_cflags)', + '-I<(boost_root)/include', + ], }, 'direct_dependent_settings': { 'include_dirs': [ diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 62f0d6201..876b7b720 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -123,6 +123,10 @@ public: void startRotating(); void stopRotating(); + // API + void setAccessToken(const std::string &token); + const std::string &getAccessToken() const; + // Debug void setDebug(bool value); void toggleDebug(); @@ -160,7 +164,10 @@ private: Mode mode = Mode::None; +public: // TODO: make private again std::unique_ptr loop; + +private: std::unique_ptr workers; std::thread thread; std::unique_ptr asyncTerminate; @@ -214,6 +221,7 @@ private: std::string styleURL; std::string styleJSON = ""; std::vector classes; + std::string accessToken; std::chrono::steady_clock::duration defaultTransitionDuration; diff --git a/include/mbgl/storage/asset_request.hpp b/include/mbgl/storage/asset_request.hpp deleted file mode 100644 index 3114d41ad..000000000 --- a/include/mbgl/storage/asset_request.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MBGL_STORAGE_ASSET_REQUEST -#define MBGL_STORAGE_ASSET_REQUEST - -#include - -namespace mbgl { - -typedef struct uv_loop_s uv_loop_t; - -struct AssetRequestBaton; - -class AssetRequest : public BaseRequest { -public: - AssetRequest(const std::string &path, uv_loop_t *loop); - ~AssetRequest(); - - void cancel(); - -private: - AssetRequestBaton *ptr = nullptr; - - friend struct AssetRequestBaton; -}; - -} - -#endif diff --git a/include/mbgl/storage/caching_http_file_source.hpp b/include/mbgl/storage/caching_http_file_source.hpp deleted file mode 100644 index 655afa639..000000000 --- a/include/mbgl/storage/caching_http_file_source.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef MBGL_STORAGE_CACHING_HTTP_FILE_SOURCE -#define MBGL_STORAGE_CACHING_HTTP_FILE_SOURCE - -#include - -#include -#include - -typedef struct uv_messenger_s uv_messenger_t; - -namespace mbgl { - -class BaseRequest; -class SQLiteStore; - -class CachingHTTPFileSource : public FileSource { -public: - CachingHTTPFileSource(const std::string &path_); - virtual ~CachingHTTPFileSource(); - - // Stores and checks the libuv loop for requests - void setLoop(uv_loop_t*); - bool hasLoop(); - void clearLoop(); - - // Set the base path/URL for relative requests - void setBase(std::string); - - // Set the Mapbox API access token - void setAccessToken(std::string); - - // Get the Mapbox API access token - std::string getAccessToken() const; - - std::unique_ptr request(ResourceType type, const std::string &url); - - void prepare(std::function fn); - - // Call this when the network status reachability changed. - void setReachability(bool reachable); - -private: - std::thread::id threadId; - - // Mapbox API access token. - std::string accessToken; - - // Path to the cache database. - std::string path; - - // Stores a URL that is used as a base for loading resources with relative path. - std::string base; - - std::unordered_map> pending; - util::ptr store; - uv_loop_t *loop = nullptr; - uv_messenger_t *queue = nullptr; -}; - -} - -#endif diff --git a/include/mbgl/storage/default/asset_request.hpp b/include/mbgl/storage/default/asset_request.hpp new file mode 100644 index 000000000..c582c025f --- /dev/null +++ b/include/mbgl/storage/default/asset_request.hpp @@ -0,0 +1,24 @@ +#ifndef MBGL_STORAGE_DEFAULT_ASSET_REQUEST +#define MBGL_STORAGE_DEFAULT_ASSET_REQUEST + +#include "shared_request_base.hpp" + +namespace mbgl { + +class AssetRequest : public SharedRequestBase { +public: + AssetRequest(DefaultFileSource *source, const Resource &resource); + + void start(uv_loop_t *loop, std::unique_ptr response = nullptr); + void cancel(); + +private: + ~AssetRequest(); + void *ptr = nullptr; + + friend class AssetRequestImpl; +}; + +} + +#endif diff --git a/include/mbgl/storage/default/default_file_source.hpp b/include/mbgl/storage/default/default_file_source.hpp new file mode 100644 index 000000000..189ff7b9e --- /dev/null +++ b/include/mbgl/storage/default/default_file_source.hpp @@ -0,0 +1,60 @@ +#ifndef MBGL_STORAGE_DEFAULT_DEFAULT_FILE_SOURCE +#define MBGL_STORAGE_DEFAULT_DEFAULT_FILE_SOURCE + +#include +#include + +#include +#include +#include + +namespace mapbox { namespace util { template class variant; } } + +namespace mbgl { + +namespace util { template class AsyncQueue; } + +class SharedRequestBase; + +class DefaultFileSource : public FileSource { +public: + DefaultFileSource(FileCache *cache); + DefaultFileSource(FileCache *cache, uv_loop_t *loop); + ~DefaultFileSource(); + + Request *request(const Resource &resource, uv_loop_t *loop, Callback callback); + void cancel(Request *request); + void request(const Resource &resource, Callback callback); + + enum class CacheHint : uint8_t { Full, Refresh, No }; + void notify(SharedRequestBase *sharedRequest, const std::set &observers, + std::shared_ptr response, FileCache::Hint hint); + +private: + struct ActionDispatcher; + struct AddRequestAction; + struct RemoveRequestAction; + struct ResultAction; + struct StopAction; + using Action = + mapbox::util::variant; + using Queue = util::AsyncQueue; + + void process(AddRequestAction &action); + void process(RemoveRequestAction &action); + void process(ResultAction &action); + void process(StopAction &action); + + SharedRequestBase *find(const Resource &resource); + + std::unordered_map pending; + + uv_loop_t *loop = nullptr; + FileCache *cache = nullptr; + Queue *queue = nullptr; + std::thread thread; +}; + +} + +#endif diff --git a/include/mbgl/storage/default/http_context.hpp b/include/mbgl/storage/default/http_context.hpp new file mode 100644 index 000000000..18f17ef0c --- /dev/null +++ b/include/mbgl/storage/default/http_context.hpp @@ -0,0 +1,117 @@ +#ifndef MBGL_STORAGE_DEFAULT_HTTP_CONTEXT +#define MBGL_STORAGE_DEFAULT_HTTP_CONTEXT + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace mbgl { + +class HTTPRequest; + +// This is a template class that provides a per-thread Context object. It can be used by HTTP +// implementations to store global state. It also implements the NetworkStatus mechanism and +// triggers immediate retries on all requests waiting for network status changes. + +template +class HTTPContext : private util::noncopyable { +protected: + MBGL_STORE_THREAD(tid) + using Map = std::map>; + +public: + static Context *Get(uv_loop_t *loop); + +private: + static pthread_key_t key; + static pthread_once_t once; + +public: + HTTPContext(uv_loop_t *loop); + ~HTTPContext(); + + void addRequest(HTTPRequest *request); + void removeRequest(HTTPRequest *baton); + +public: + uv_loop_t *loop; + + // Will be fired when the network status becomes reachable. + uv_async_t *reachability = nullptr; + + // A list of all pending HTTPRequestImpls that we need to notify when the network status + // changes. + std::set requests; +}; + +template +Context *HTTPContext::Get(uv_loop_t *loop) { + pthread_once(&once, []() { + pthread_key_create(&key, [](void *ptr) { + assert(ptr); + delete reinterpret_cast(ptr); + }); + }); + auto contexts = reinterpret_cast(pthread_getspecific(key)); + if (!contexts) { + contexts = new Map(); + pthread_setspecific(key, contexts); + } + + // Now find a HTTPContext that matches the requested loop. + auto it = contexts->find(loop); + if (it == contexts->end()) { + auto result = contexts->emplace(loop, util::make_unique(loop)); + assert(result.second); // Make sure it was actually inserted. + return result.first->second.get(); + } else { + return it->second.get(); + } +} + +template +HTTPContext::HTTPContext(uv_loop_t *loop_) : loop(loop_) { + reachability = new uv_async_t; + reachability->data = this; + uv_async_init(loop, reachability, [](uv_async_t *async, int) { + for (auto request : reinterpret_cast(async->data)->requests) { + request->retryImmediately(); + } + }); + // Allow the loop to quit even though this handle is still active. + uv_unref(reinterpret_cast(reachability)); + NetworkStatus::Subscribe(reachability); +} + +template +HTTPContext::~HTTPContext() { + MBGL_VERIFY_THREAD(tid); + + assert(requests.empty()); + + NetworkStatus::Unsubscribe(reachability); + uv::close(reachability); +} + +template +void HTTPContext::addRequest(HTTPRequest *request) { + requests.insert(request); +} + +template +void HTTPContext::removeRequest(HTTPRequest *request) { + requests.erase(request); +} + +} + +#endif diff --git a/include/mbgl/storage/default/http_request.hpp b/include/mbgl/storage/default/http_request.hpp new file mode 100644 index 000000000..914e622f1 --- /dev/null +++ b/include/mbgl/storage/default/http_request.hpp @@ -0,0 +1,26 @@ +#ifndef MBGL_STORAGE_DEFAULT_HTTP_REQUEST +#define MBGL_STORAGE_DEFAULT_HTTP_REQUEST + +#include "shared_request_base.hpp" + +namespace mbgl { + +class HTTPRequest : public SharedRequestBase { +public: + HTTPRequest(DefaultFileSource *source, const Resource &resource); + + void start(uv_loop_t *loop, std::unique_ptr response = nullptr); + void cancel(); + + void retryImmediately(); + +private: + ~HTTPRequest(); + void *ptr = nullptr; + + friend class HTTPRequestImpl; +}; + +} + +#endif diff --git a/include/mbgl/storage/default/request.hpp b/include/mbgl/storage/default/request.hpp new file mode 100644 index 000000000..0ed544093 --- /dev/null +++ b/include/mbgl/storage/default/request.hpp @@ -0,0 +1,46 @@ +#ifndef MBGL_STORAGE_DEFAULT_REQUEST +#define MBGL_STORAGE_DEFAULT_REQUEST + +#include + +#include +#include + +#include + +typedef struct uv_async_s uv_async_t; +typedef struct uv_loop_s uv_loop_t; + +namespace mbgl { + +class Response; + +class Request : private util::noncopyable { + MBGL_STORE_THREAD(tid) + +public: + using Callback = std::function; + Request(const Resource &resource, uv_loop_t *loop, Callback callback); + ~Request(); + +public: + // May be called from any thread. + void notify(const std::shared_ptr &response); + void destruct(); + + // May be called only from the thread the Request was created in. + void cancel(); + +private: + uv_async_t *notify_async = nullptr; + uv_async_t *destruct_async = nullptr; + Callback callback; + std::shared_ptr response; + +public: + const Resource resource; +}; + +} + +#endif diff --git a/include/mbgl/storage/default/shared_request_base.hpp b/include/mbgl/storage/default/shared_request_base.hpp new file mode 100644 index 000000000..8591d66ba --- /dev/null +++ b/include/mbgl/storage/default/shared_request_base.hpp @@ -0,0 +1,89 @@ +#ifndef MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE +#define MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE + +#include +#include +#include +#include + +#include "default_file_source.hpp" + +#include +#include +#include + +typedef struct uv_loop_s uv_loop_t; + +namespace mbgl { + +class Request; +class Response; +class DefaultFileSource; + +class SharedRequestBase : private util::noncopyable { +protected: + MBGL_STORE_THREAD(tid) + +public: + SharedRequestBase(DefaultFileSource *source_, const Resource &resource_) + : resource(resource_), source(source_) {} + + virtual void start(uv_loop_t *loop, std::unique_ptr response = nullptr) = 0; + virtual void cancel() = 0; + + void notify(std::unique_ptr response, FileCache::Hint hint) { + MBGL_VERIFY_THREAD(tid); + + if (source) { + source->notify(this, observers, std::shared_ptr(response.release()), + hint); + } + } + + void subscribe(Request *request) { + MBGL_VERIFY_THREAD(tid); + + observers.insert(request); + } + + void unsubscribeAll() { + MBGL_VERIFY_THREAD(tid); + + source = nullptr; + observers.clear(); + cancel(); + } + + void unsubscribe(Request *request) { + MBGL_VERIFY_THREAD(tid); + + observers.erase(request); + + if (observers.empty()) { + // There are no observers anymore. We are initiating cancelation. + if (source) { + // First, remove this SharedRequestBase from the source. + source->notify(this, observers, nullptr, FileCache::Hint::No); + } + + // Then, initiate cancelation of this request + cancel(); + } + } + +protected: + virtual ~SharedRequestBase() { + MBGL_VERIFY_THREAD(tid); + } + +public: + const Resource resource; + +private: + DefaultFileSource *source = nullptr; + std::set observers; +}; + +} + +#endif diff --git a/include/mbgl/storage/default/sqlite_cache.hpp b/include/mbgl/storage/default/sqlite_cache.hpp new file mode 100644 index 000000000..8f2746561 --- /dev/null +++ b/include/mbgl/storage/default/sqlite_cache.hpp @@ -0,0 +1,52 @@ +#ifndef MBGL_STORAGE_DEFAULT_SQLITE_CACHE +#define MBGL_STORAGE_DEFAULT_SQLITE_CACHE + +#include + +#include +#include + +typedef struct uv_loop_s uv_loop_t; + +namespace mapbox { namespace util { template class variant; } } +namespace mapbox { namespace sqlite { class Database; class Statement; } } + +namespace mbgl { + +namespace util { template class AsyncQueue; } + +class SQLiteCache : public FileCache { + struct GetAction; + struct PutAction; + struct RefreshAction; + struct StopAction; + using Action = mapbox::util::variant; + using Queue = util::AsyncQueue; + +public: + SQLiteCache(const std::string &path = ":memory:"); + ~SQLiteCache(); + + void get(const Resource &resource, std::function)> callback); + void put(const Resource &resource, std::shared_ptr response, Hint hint); + +private: + struct ActionDispatcher; + void process(GetAction &action); + void process(PutAction &action); + void process(RefreshAction &action); + void process(StopAction &action); + + void createDatabase(); + + const std::string path; + uv_loop_t *loop = nullptr; + Queue *queue = nullptr; + std::thread thread; + std::unique_ptr<::mapbox::sqlite::Database> db; + std::unique_ptr<::mapbox::sqlite::Statement> getStmt, putStmt, refreshStmt; +}; + +} + +#endif diff --git a/include/mbgl/storage/file_cache.hpp b/include/mbgl/storage/file_cache.hpp new file mode 100644 index 000000000..38a82cc23 --- /dev/null +++ b/include/mbgl/storage/file_cache.hpp @@ -0,0 +1,26 @@ +#ifndef MBGL_STORAGE_FILE_CACHE +#define MBGL_STORAGE_FILE_CACHE + +#include + +#include + +namespace mbgl { + +struct Resource; +class Response; + +class FileCache : private util::noncopyable { +public: + virtual ~FileCache() = default; + + enum class Hint : uint8_t { Full, Refresh, No }; + + virtual void get(const Resource &resource, + std::function)> callback) = 0; + virtual void put(const Resource &resource, std::shared_ptr response, Hint hint) = 0; +}; + +} + +#endif diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp index 23a1462ae..8517d6e4a 100644 --- a/include/mbgl/storage/file_source.hpp +++ b/include/mbgl/storage/file_source.hpp @@ -1,29 +1,38 @@ #ifndef MBGL_STORAGE_FILE_SOURCE #define MBGL_STORAGE_FILE_SOURCE -#include -#include -#include +#include "response.hpp" +#include "resource.hpp" + +#include +#include +#include -#include #include typedef struct uv_loop_s uv_loop_t; - namespace mbgl { -class FileSource : public util::noncopyable { +class Request; + +class FileSource : private util::noncopyable { +protected: + MBGL_STORE_THREAD(tid) + public: virtual ~FileSource() = default; - virtual void setLoop(uv_loop_t*) = 0; - virtual bool hasLoop() = 0; - virtual void clearLoop() = 0; + using Callback = std::function; - virtual void setBase(std::string) = 0; - virtual std::unique_ptr request(ResourceType type, const std::string &url) = 0; - virtual void prepare(std::function fn) = 0; + // These can be called from any thread. The callback will be invoked in the loop. + // You can only cancel a request from the same thread it was created in. + virtual Request *request(const Resource &resource, uv_loop_t *loop, Callback callback) = 0; + virtual void cancel(Request *request) = 0; + + // These can be called from any thread. The callback will be invoked in an arbitrary other thread. + // You cannot cancel these requests. + virtual void request(const Resource &resource, Callback callback) = 0; }; } diff --git a/include/mbgl/storage/http_request_baton.hpp b/include/mbgl/storage/http_request_baton.hpp deleted file mode 100644 index 11abfb71d..000000000 --- a/include/mbgl/storage/http_request_baton.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef MBGL_STORAGE_HTTP_REQUEST_BATON -#define MBGL_STORAGE_HTTP_REQUEST_BATON - -#include -#include - -#include -#include - -typedef struct uv_async_s uv_async_t; - -namespace mbgl { - -class HTTPRequest; - -enum class HTTPResponseType : int8_t { - // This error probably won't be resolved by retrying anytime soon. We are giving up. - PermanentError = -5, - - // This error might be resolved by waiting some time (e.g. server issues). - // We are going to do an exponential back-off and will try again in a few seconds. - TemporaryError = -4, - - // This error was caused by a temporary error and it is likely that it will be resolved - // immediately. We are going to try again right away. This is like the TemporaryError, except - // that we will not perform exponential back-off. - SingularError = -3, - - // This error might be resolved once the network reachability status changes. - // We are going to watch the network status for changes and will retry as soon as the operating - // system notifies us of a network status change. - ConnectionError = -2, - - // The request was canceled programatically. - Canceled = -1, - - // The request is still in progress. - Unknown = 0, - - // The request returned data successfully. We retrieved and decoded the data successfully. - Successful = 1, - - // The request confirmed that the data wasn't changed. We already have the data. - NotModified = 2, -}; - -struct HTTPRequestBaton { - HTTPRequestBaton(const std::string &path); - ~HTTPRequestBaton(); - - const std::thread::id threadId; - const std::string path; - - HTTPRequest *request = nullptr; - uv_async_t *async = nullptr; - - HTTPResponseType type = HTTPResponseType::Unknown; - std::unique_ptr response; - - // Implementation specific use. - void *ptr = nullptr; - - // IMPLEMENT THESE 3 PLATFORM SPECIFIC FUNCTIONS: - - // Begin the HTTP request. Platform-specific implementation. - static void start(const util::ptr &ptr); - - // This will be called to stop/cancel the HTTP request (if possible). Platform-specific implementation. - static void stop(const util::ptr &ptr); -}; - -} - -#endif diff --git a/include/mbgl/storage/network_status.hpp b/include/mbgl/storage/network_status.hpp new file mode 100644 index 000000000..cac2ae193 --- /dev/null +++ b/include/mbgl/storage/network_status.hpp @@ -0,0 +1,25 @@ +#ifndef MBGL_STORAGE_NETWORK_STATUS +#define MBGL_STORAGE_NETWORK_STATUS + +#include +#include + +typedef struct uv_async_s uv_async_t; + +namespace mbgl { + +class NetworkStatus { +public: + static void Reachable(); + + static void Subscribe(uv_async_t *async); + static void Unsubscribe(uv_async_t *async); + +private: + static std::mutex mtx; + static std::set observers; +}; + +} + +#endif \ No newline at end of file diff --git a/include/mbgl/storage/request.hpp b/include/mbgl/storage/request.hpp deleted file mode 100644 index 845c9a6da..000000000 --- a/include/mbgl/storage/request.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MBGL_STORAGE_REQUEST -#define MBGL_STORAGE_REQUEST - -#include -#include - -#include - -#include -#include - -typedef struct uv_loop_s uv_loop_t; - -namespace mbgl { - -class BaseRequest; - -class Request { -private: - Request(const Request &) = delete; - Request(Request &&) = delete; - Request& operator=(const Request &) = delete; - Request& operator=(Request &&) = delete; - -public: - Request(const util::ptr &base); - ~Request(); - - void onload(CompletedCallback cb); - void oncancel(AbortedCallback cb); - void cancel(); - -private: - const std::thread::id thread_id; - util::ptr base; - std::forward_list callbacks; -}; - -} - -#endif \ No newline at end of file diff --git a/include/mbgl/storage/request_callback.hpp b/include/mbgl/storage/request_callback.hpp deleted file mode 100644 index 01427bd96..000000000 --- a/include/mbgl/storage/request_callback.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MBGL_STORAGE_REQUEST_CALLBACK -#define MBGL_STORAGE_REQUEST_CALLBACK - -#include - -#include - -namespace mbgl { - -class Response; - -using CompletedCallback = std::function; -using AbortedCallback = std::function; - -using Callback = mapbox::util::variant< - CompletedCallback, - AbortedCallback ->; - -} - -#endif diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp new file mode 100644 index 000000000..e499f84a2 --- /dev/null +++ b/include/mbgl/storage/resource.hpp @@ -0,0 +1,39 @@ +#ifndef MBGL_STORAGE_RESOURCE +#define MBGL_STORAGE_RESOURCE + +#include +#include + +namespace mbgl { + +struct Resource { + enum Kind : uint8_t { + Unknown = 0, + Tile = 1, + Glyphs = 2, + Image = 3, + JSON = 4, + }; + + const Kind kind; + const std::string url; + + inline bool operator==(const Resource &res) const { + return kind == res.kind && url == res.url; + } +}; + +} + +// Specialize std::hash for use in std::unordered_map +namespace std { + +template<> struct hash { + std::size_t operator()(mbgl::Resource const& r) const { + return std::hash()(r.url) ^ (std::hash()(r.kind) << 1); + } +}; + +} + +#endif diff --git a/include/mbgl/storage/resource_type.hpp b/include/mbgl/storage/resource_type.hpp deleted file mode 100644 index b7204a9fa..000000000 --- a/include/mbgl/storage/resource_type.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MBGL_STORAGE_RESOURCE_TYPE -#define MBGL_STORAGE_RESOURCE_TYPE - -#include - -namespace mbgl { - -enum class ResourceType : uint8_t { - Unknown = 0, - Tile = 1, - Glyphs = 2, - Image = 3, - JSON = 4 -}; - -} - -#endif diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 9357ad3c6..cf22d9002 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -2,25 +2,21 @@ #define MBGL_STORAGE_RESPONSE #include -#include namespace mbgl { - - class Response { public: - long code = 0; + enum Status : bool { Error, Successful }; + + Status status = Error; + std::string message; int64_t modified = 0; int64_t expires = 0; std::string etag; std::string data; - - std::string message; - - static int64_t parseCacheControl(const char *value); }; } -#endif \ No newline at end of file +#endif diff --git a/include/mbgl/util/async_queue.hpp b/include/mbgl/util/async_queue.hpp new file mode 100644 index 000000000..b3eaabc31 --- /dev/null +++ b/include/mbgl/util/async_queue.hpp @@ -0,0 +1,95 @@ +#ifndef MBGL_UTIL_ASYNC_QUEUE +#define MBGL_UTIL_ASYNC_QUEUE + +#include "std.hpp" + +#include + +#include +#include +#include +#include +#include + + +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 +#define UV_ASYNC_PARAMS(handle) uv_async_t *handle, int +#else +#define UV_ASYNC_PARAMS(handle) uv_async_t *handle +#endif + +namespace mbgl { +namespace util { + +template +class AsyncQueue { +public: + AsyncQueue(uv_loop_t *loop, std::function fn) : + callback(fn) { + async.data = this; + uv_async_init(loop, &async, [](UV_ASYNC_PARAMS(handle)) { + auto q = reinterpret_cast(handle->data); + q->process(); + }); + } + + void send(T &&data) { + { + std::lock_guard lock(mutex); + queue.push(util::make_unique(std::move(data))); + } + uv_async_send(&async); + } + + void send(std::unique_ptr data) { + { + std::lock_guard lock(mutex); + queue.push(std::move(data)); + } + uv_async_send(&async); + } + + void stop() { + uv_close((uv_handle_t *)&async, [](uv_handle_t *handle) { + delete reinterpret_cast(handle->data); + }); + } + + void ref() { + uv_ref((uv_handle_t *)&async); + } + + void unref() { + uv_unref((uv_handle_t *)&async); + } + +private: + ~AsyncQueue() { + } + + void process() { + std::unique_ptr item; + while (true) { + mutex.lock(); + if (queue.empty()) { + mutex.unlock(); + break; + } + item = std::move(queue.front()); + queue.pop(); + mutex.unlock(); + callback(*item); + } + } + +private: + std::mutex mutex; + uv_async_t async; + std::queue> queue; + std::function callback; +}; + +} +} + +#endif diff --git a/include/mbgl/util/util.hpp b/include/mbgl/util/util.hpp new file mode 100644 index 000000000..bf5dad3c0 --- /dev/null +++ b/include/mbgl/util/util.hpp @@ -0,0 +1,15 @@ +#ifndef MBGL_UTIL_UTIL +#define MBGL_UTIL_UTIL + +#include + +#ifndef NDEBUG +#include +#define MBGL_STORE_THREAD(tid) const std::thread::id tid = std::this_thread::get_id(); +#define MBGL_VERIFY_THREAD(tid) assert(tid == std::this_thread::get_id()); +#else +#define MBGL_STORE_THREAD(tid) +#define MBGL_VERIFY_THREAD(tid) +#endif + +#endif diff --git a/include/mbgl/util/uv.hpp b/include/mbgl/util/uv.hpp index f59037c1d..85f93e78b 100644 --- a/include/mbgl/util/uv.hpp +++ b/include/mbgl/util/uv.hpp @@ -3,10 +3,11 @@ #include +typedef struct uv_handle_s uv_handle_t; typedef struct uv_async_s uv_async_t; typedef struct uv_timer_s uv_timer_t; -typedef struct uv_handle_s uv_handle_t; typedef struct uv_loop_s uv_loop_t; +typedef struct uv_fs_s uv_fs_t; namespace uv { @@ -19,6 +20,15 @@ class worker; class mutex; class cond; +const char *getFileRequestError(uv_fs_t *req); + +template +void close(T *specific) { + uv_close(reinterpret_cast(specific), [](uv_handle_t *generic) { + delete reinterpret_cast(generic); + }); +} + } #endif diff --git a/include/mbgl/util/variant.hpp b/include/mbgl/util/variant.hpp index 2de195cd6..411f1918d 100644 --- a/include/mbgl/util/variant.hpp +++ b/include/mbgl/util/variant.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include "recursive_wrapper.hpp" #ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx @@ -34,7 +34,19 @@ // translates to 100 #define VARIANT_VERSION (VARIANT_MAJOR_VERSION*100000) + (VARIANT_MINOR_VERSION*100) + (VARIANT_PATCH_VERSION) -namespace mapbox { namespace util { namespace detail { +namespace mapbox { namespace util { + +// static visitor +template +struct static_visitor +{ + using result_type = R; +protected: + static_visitor() {} + ~static_visitor() {} +}; + +namespace detail { static constexpr std::size_t invalid_value = std::size_t(-1); @@ -109,18 +121,38 @@ struct select_type<0, T, Types...> using type = T; }; -} // namespace detail -// static visitor -template -struct static_visitor +template +struct enable_if_type { using type = R; }; + +template +struct result_of_unary_visit { - using result_type = R; -protected: - static_visitor() {} - ~static_visitor() {} + using type = typename std::result_of::type; }; +template +struct result_of_unary_visit::type > +{ + using type = typename F::result_type; +}; + +template +struct result_of_binary_visit +{ + using type = typename std::result_of::type; +}; + + +template +struct result_of_binary_visit::type > +{ + using type = typename F::result_type; +}; + + +} // namespace detail + template struct static_max; @@ -225,7 +257,7 @@ struct dispatcher; template struct dispatcher { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_unary_visit::type; VARIANT_INLINE static result_type apply_const(V const& v, F f) { if (v.get_type_index() == sizeof...(Types)) @@ -254,7 +286,7 @@ struct dispatcher template struct dispatcher { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_unary_visit::type; VARIANT_INLINE static result_type apply_const(V const&, F) { throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name()); @@ -273,7 +305,7 @@ struct binary_dispatcher_rhs; template struct binary_dispatcher_rhs { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_binary_visit::type; VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f) { if (rhs.get_type_index() == sizeof...(Types)) // call binary functor @@ -305,7 +337,7 @@ struct binary_dispatcher_rhs template struct binary_dispatcher_rhs { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_binary_visit::type; VARIANT_INLINE static result_type apply_const(V const&, V const&, F) { throw std::runtime_error("binary dispatch: FAIL"); @@ -323,7 +355,7 @@ struct binary_dispatcher_lhs; template struct binary_dispatcher_lhs { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_binary_visit::type; VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f) { if (lhs.get_type_index() == sizeof...(Types)) // call binary functor @@ -353,7 +385,7 @@ struct binary_dispatcher_lhs template struct binary_dispatcher_lhs { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_binary_visit::type; VARIANT_INLINE static result_type apply_const(V const&, V const&, F) { throw std::runtime_error("binary dispatch: FAIL"); @@ -371,7 +403,7 @@ struct binary_dispatcher; template struct binary_dispatcher { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_binary_visit::type; VARIANT_INLINE static result_type apply_const(V const& v0, V const& v1, F f) { if (v0.get_type_index() == sizeof...(Types)) @@ -416,7 +448,7 @@ struct binary_dispatcher template struct binary_dispatcher { - using result_type = typename F::result_type; + using result_type = typename detail::result_of_binary_visit::type; VARIANT_INLINE static result_type apply_const(V const&, V const&, F) { throw std::runtime_error("binary dispatch: FAIL"); @@ -448,7 +480,7 @@ struct less_comp }; template -class comparer : public static_visitor +class comparer { public: explicit comparer(Variant const& lhs) noexcept @@ -467,7 +499,7 @@ private: // operator<< helper template -class printer : public static_visitor<> +class printer { public: explicit printer(Out & out) diff --git a/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa index 905bca939..37c9f7267 160000 --- a/ios/mapbox-gl-cocoa +++ b/ios/mapbox-gl-cocoa @@ -1 +1 @@ -Subproject commit 905bca9394a6e4d6a89dcf0e10b4e25105fbe648 +Subproject commit 37c9f726752674f87758cd351316413f978f1606 diff --git a/macosx/main.mm b/macosx/main.mm index 277f8c44d..6956f04d6 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -3,7 +3,9 @@ #include #include #include -#include +#include +#include +#include #import @@ -74,9 +76,10 @@ int main() { mbgl::Log::Set(); GLFWView view; - mbgl::CachingHTTPFileSource fileSource(mbgl::platform::defaultCacheDatabase()); + + mbgl::SQLiteCache cache(mbgl::platform::defaultCacheDatabase()); + mbgl::DefaultFileSource fileSource(&cache); mbgl::Map map(view, fileSource); - mbgl::CachingHTTPFileSource *fileSourcePtr = &fileSource; URLHandler *handler = [[URLHandler alloc] init]; [handler setMap:&map]; @@ -86,7 +89,7 @@ int main() { // Notify map object when network reachability status changes. Reachability* reachability = [Reachability reachabilityForInternetConnection]; reachability.reachableBlock = ^(Reachability *) { - fileSourcePtr->setReachability(true); + mbgl::NetworkStatus::Reachable(); }; [reachability startNotifier]; @@ -99,7 +102,7 @@ int main() { // Set access token if present NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"]; if (!accessToken) mbgl::Log::Warning(mbgl::Event::Setup, "No access token set. Mapbox vector tiles won't work."); - if (accessToken) fileSource.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]); + if (accessToken) map.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]); // Load style map.setStyleURL("asset://styles/bright-v7.json"); diff --git a/platform/darwin/http_request_baton_cocoa.mm b/platform/darwin/http_request_baton_cocoa.mm deleted file mode 100644 index 4a59837e3..000000000 --- a/platform/darwin/http_request_baton_cocoa.mm +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include - -#import - -namespace mbgl { - -dispatch_once_t request_initialize = 0; -NSURLSession *session = nullptr; - -NSString *userAgent = nil; - -void HTTPRequestBaton::start(const util::ptr &ptr) { - assert(std::this_thread::get_id() == ptr->threadId); - - // Starts the request. - util::ptr baton = ptr; - - dispatch_once(&request_initialize, ^{ - NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; - sessionConfig.timeoutIntervalForResource = 30; - sessionConfig.HTTPMaximumConnectionsPerHost = 8; - sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; - sessionConfig.URLCache = nil; - - session = [NSURLSession sessionWithConfiguration:sessionConfig]; - - // Write user agent string - NSDictionary *systemVersion = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; - userAgent = [NSString stringWithFormat:@"MapboxGL/%d.%d.%d (+https://mapbox.com/mapbox-gl/; %s; %@ %@)", - version::major, version::minor, version::patch, version::revision, - [systemVersion objectForKey:@"ProductName"], - [systemVersion objectForKey:@"ProductVersion"] - ]; - }); - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@(baton->path.c_str())]]; - if (baton->response) { - if (!baton->response->etag.empty()) { - [request addValue:@(baton->response->etag.c_str()) forHTTPHeaderField:@"If-None-Match"]; - } else if (baton->response->modified) { - const std::string time = util::rfc1123(baton->response->modified); - [request addValue:@(time.c_str()) forHTTPHeaderField:@"If-Modified-Since"]; - } - } - - [request addValue:userAgent forHTTPHeaderField:@"User-Agent"]; - - NSURLSessionDataTask *task = [session dataTaskWithRequest:request - completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) { - if (error) { - if ([error code] == NSURLErrorCancelled) { - // The response code remains at 0 to indicate cancelation. - // In addition, we don't need any response object. - baton->response.reset(); - baton->type = HTTPResponseType::Canceled; - } else { - // TODO: Use different codes for host not found, timeout, invalid URL etc. - // These can be categorized in temporary and permanent errors. - baton->response = util::make_unique(); - baton->response->code = [(NSHTTPURLResponse *)res statusCode]; - baton->response->message = [[error localizedDescription] UTF8String]; - - switch ([error code]) { - case NSURLErrorBadServerResponse: // 5xx errors - baton->type = HTTPResponseType::TemporaryError; - break; - - case NSURLErrorTimedOut: - case NSURLErrorUserCancelledAuthentication: - baton->type = HTTPResponseType::SingularError; // retry immediately - break; - - case NSURLErrorNetworkConnectionLost: - case NSURLErrorCannotFindHost: - case NSURLErrorCannotConnectToHost: - case NSURLErrorDNSLookupFailed: - case NSURLErrorNotConnectedToInternet: - case NSURLErrorInternationalRoamingOff: - case NSURLErrorCallIsActive: - case NSURLErrorDataNotAllowed: - baton->type = HTTPResponseType::ConnectionError; - break; - - default: - baton->type = HTTPResponseType::PermanentError; - } - } - } else if ([res isKindOfClass:[NSHTTPURLResponse class]]) { - const long code = [(NSHTTPURLResponse *)res statusCode]; - if (code == 304) { - // Assume a Response object already exists. - assert(baton->response); - } else { - baton->response = util::make_unique(); - baton->response->code = code; - baton->response->data = {(const char *)[data bytes], [data length]}; - } - - if (code == 304) { - baton->type = HTTPResponseType::NotModified; - } else if (code == 200) { - baton->type = HTTPResponseType::Successful; - } else { - baton->type = HTTPResponseType::PermanentError; - } - - NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields]; - NSString *cache_control = [headers objectForKey:@"Cache-Control"]; - if (cache_control) { - baton->response->expires = Response::parseCacheControl([cache_control UTF8String]); - } - - NSString *last_modified = [headers objectForKey:@"Last-Modified"]; - if (last_modified) { - baton->response->modified = parse_date([last_modified UTF8String]); - } - - NSString *etag = [headers objectForKey:@"ETag"]; - if (etag) { - baton->response->etag = [etag UTF8String]; - } - } else { - // This should never happen. - baton->type = HTTPResponseType::PermanentError; - baton->response = util::make_unique(); - baton->response->code = -1; - baton->response->message = "response class is not NSHTTPURLResponse"; - } - - uv_async_send(baton->async); - }]; - - [task resume]; - - baton->ptr = const_cast(CFBridgingRetain(task)); -} - -void HTTPRequestBaton::stop(const util::ptr &ptr) { - assert(std::this_thread::get_id() == ptr->threadId); - assert(ptr->ptr); - - NSURLSessionDataTask *task = CFBridgingRelease(ptr->ptr); - ptr->ptr = nullptr; - [task cancel]; -} - -} diff --git a/platform/darwin/http_request_cocoa.mm b/platform/darwin/http_request_cocoa.mm new file mode 100644 index 000000000..163d4cf2d --- /dev/null +++ b/platform/darwin/http_request_cocoa.mm @@ -0,0 +1,388 @@ +#include +#include +#include +#include + +#include +#include + +#import + +#include +#include + +dispatch_once_t request_initialize = 0; +NSURLSession *session = nullptr; +NSString *userAgent = nil; + +namespace mbgl { + +enum class ResponseStatus : uint8_t { + // This error probably won't be resolved by retrying anytime soon. We are giving up. + PermanentError, + + // This error might be resolved by waiting some time (e.g. server issues). + // We are going to do an exponential back-off and will try again in a few seconds. + TemporaryError, + + // This error was caused by a temporary error and it is likely that it will be resolved + // immediately. We are going to try again right away. This is like the TemporaryError, except + // that we will not perform exponential back-off. + SingularError, + + // This error might be resolved once the network reachability status changes. + // We are going to watch the network status for changes and will retry as soon as the + // operating system notifies us of a network status change. + ConnectionError, + + // The request was canceled mid-way. + Canceled, + + // The request returned data successfully. We retrieved and decoded the data successfully. + Successful, + + // The request confirmed that the data wasn't changed. We already have the data. + NotModified, +}; + +// ------------------------------------------------------------------------------------------------- + +class HTTPCocoaContext; + +class HTTPRequestImpl { +public: + HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::unique_ptr response); + ~HTTPRequestImpl(); + + void cancel(); + + void start(); + void handleResult(NSData *data, NSURLResponse *res, NSError *error); + void handleResponse(); + + void retry(uint64_t timeout); + void retryImmediately(); + static void restart(uv_timer_t *timer, int); + +private: + HTTPCocoaContext *context = nullptr; + HTTPRequest *request = nullptr; + NSURLSessionDataTask *task = nullptr; + std::unique_ptr response; + std::unique_ptr existingResponse; + ResponseStatus status = ResponseStatus::PermanentError; + uv_async_t *async = nullptr; + int attempts = 0; + uv_timer_t *timer = nullptr; + enum : bool { PreemptImmediately, ExponentialBackoff } strategy = PreemptImmediately; + + static const int maxAttempts = 4; +}; + +// ------------------------------------------------------------------------------------------------- + +class HTTPCocoaContext : public HTTPContext { +public: + HTTPCocoaContext(uv_loop_t *loop); +}; + +template<> pthread_key_t HTTPContext::key{}; +template<> pthread_once_t HTTPContext::once = PTHREAD_ONCE_INIT; + +HTTPCocoaContext::HTTPCocoaContext(uv_loop_t *loop_) : HTTPContext(loop_) {} + +// ------------------------------------------------------------------------------------------------- + +HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, + std::unique_ptr existingResponse_) + : context(HTTPCocoaContext::Get(loop)), + request(request_), + existingResponse(std::move(existingResponse_)), + async(new uv_async_t) { + assert(request); + context->addRequest(request); + + async->data = this; + uv_async_init(loop, async, [](uv_async_t *as, int) { + auto impl = reinterpret_cast(as->data); + impl->handleResponse(); + }); + + start(); +} + +void HTTPRequestImpl::start() { + assert(!task); + + attempts++; + + @autoreleasepool { + NSMutableURLRequest *req = [[NSMutableURLRequest alloc] + initWithURL:[NSURL URLWithString:@(request->resource.url.c_str())]]; + if (existingResponse) { + if (!existingResponse->etag.empty()) { + [req addValue:@(existingResponse->etag.c_str()) forHTTPHeaderField:@"If-None-Match"]; + } else if (existingResponse->modified) { + const std::string time = util::rfc1123(existingResponse->modified); + [req addValue:@(time.c_str()) forHTTPHeaderField:@"If-Modified-Since"]; + } + } + + [req addValue:userAgent forHTTPHeaderField:@"User-Agent"]; + + task = [session dataTaskWithRequest:req + completionHandler:^(NSData *data, NSURLResponse *res, + NSError *error) { handleResult(data, res, error); }]; + [req release]; + [task resume]; + } +} + +void HTTPRequestImpl::handleResponse() { + task = nullptr; + + if (request) { + if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) { + strategy = ExponentialBackoff; + return retry((1 << (attempts - 1)) * 1000); + } else if (status == ResponseStatus::ConnectionError && attempts < maxAttempts) { + // By default, we will retry every 30 seconds (network change notification will + // preempt the timeout). + strategy = PreemptImmediately; + return retry(30000); + } + + // Actually return the response. + if (status == ResponseStatus::NotModified) { + request->notify(std::move(response), FileCache::Hint::Refresh); + } else { + request->notify(std::move(response), FileCache::Hint::Full); + } + + context->removeRequest(request); + delete request; + request = nullptr; + } + + delete this; +} + +void HTTPRequestImpl::cancel() { + context->removeRequest(request); + request = nullptr; + + [task cancel]; + task = nullptr; +} + +HTTPRequestImpl::~HTTPRequestImpl() { + assert(!task); + assert(async); + + uv::close(async); + + if (request) { + context->removeRequest(request); + request->ptr = nullptr; + } +} + +int64_t parseCacheControl(const char *value) { + if (value) { + unsigned long long seconds = 0; + // TODO: cache-control may contain other information as well: + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + if (std::sscanf(value, "max-age=%llu", &seconds) == 1) { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count() + + seconds; + } + } + + return 0; +} + +void HTTPRequestImpl::handleResult(NSData *data, NSURLResponse *res, NSError *error) { + if (error) { + if ([error code] == NSURLErrorCancelled) { + status = ResponseStatus::Canceled; + } else { + // TODO: Use different codes for host not found, timeout, invalid URL etc. + // These can be categorized in temporary and permanent errors. + response = util::make_unique(); + response->status = Response::Error; + response->message = [[error localizedDescription] UTF8String]; + + switch ([error code]) { + case NSURLErrorBadServerResponse: // 5xx errors + status = ResponseStatus::TemporaryError; + break; + + case NSURLErrorTimedOut: + case NSURLErrorUserCancelledAuthentication: + status = ResponseStatus::SingularError; // retry immediately + break; + + case NSURLErrorNetworkConnectionLost: + case NSURLErrorCannotFindHost: + case NSURLErrorCannotConnectToHost: + case NSURLErrorDNSLookupFailed: + case NSURLErrorNotConnectedToInternet: + case NSURLErrorInternationalRoamingOff: + case NSURLErrorCallIsActive: + case NSURLErrorDataNotAllowed: + status = ResponseStatus::ConnectionError; + break; + + default: + status = ResponseStatus::PermanentError; + } + } + } else if ([res isKindOfClass:[NSHTTPURLResponse class]]) { + const long responseCode = [(NSHTTPURLResponse *)res statusCode]; + + response = util::make_unique(); + response->data = {(const char *)[data bytes], [data length]}; + + NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields]; + NSString *cache_control = [headers objectForKey:@"Cache-Control"]; + if (cache_control) { + response->expires = parseCacheControl([cache_control UTF8String]); + } + + NSString *expires = [headers objectForKey:@"Expires"]; + if (expires) { + response->expires = parse_date([expires UTF8String]); + } + + NSString *last_modified = [headers objectForKey:@"Last-Modified"]; + if (last_modified) { + response->modified = parse_date([last_modified UTF8String]); + } + + NSString *etag = [headers objectForKey:@"ETag"]; + if (etag) { + response->etag = [etag UTF8String]; + } + + if (responseCode == 304) { + if (existingResponse) { + // We're going to reuse the old response object, but need to copy over the new + // expires value (if possible). + std::swap(response, existingResponse); + if (existingResponse->expires) { + response->expires = existingResponse->expires; + } + status = ResponseStatus::NotModified; + } else { + // This is an unsolicited 304 response and should only happen on malfunctioning + // HTTP servers. It likely doesn't include any data, but we don't have much options. + response->status = Response::Successful; + status = ResponseStatus::Successful; + } + } else if (responseCode == 200) { + response->status = Response::Successful; + status = ResponseStatus::Successful; + } else if (responseCode >= 500 && responseCode < 600) { + // Server errors may be temporary, so back off exponentially. + response->status = Response::Error; + response->message = "HTTP status code " + std::to_string(responseCode); + status = ResponseStatus::TemporaryError; + } else { + // We don't know how to handle any other errors, so declare them as permanently failing. + response->status = Response::Error; + response->message = "HTTP status code " + std::to_string(responseCode); + status = ResponseStatus::PermanentError; + } + } else { + // This should never happen. + status = ResponseStatus::PermanentError; + response = util::make_unique(); + response->status = Response::Error; + response->message = "response class is not NSHTTPURLResponse"; + } + + uv_async_send(async); +} + +void HTTPRequestImpl::retry(uint64_t timeout) { + response.reset(); + + assert(!timer); + timer = new uv_timer_t; + timer->data = this; + uv_timer_init(async->loop, timer); + uv_timer_start(timer, restart, timeout, 0); +} + +void HTTPRequestImpl::retryImmediately() { + // All batons get notified when the network status changed, but some of them + // might not actually wait for the network to become available again. + if (timer && strategy == PreemptImmediately) { + // Triggers the timer upon the next event loop iteration. + uv_timer_stop(timer); + uv_timer_start(timer, restart, 0, 0); + } +} + +void HTTPRequestImpl::restart(uv_timer_t *timer, int) { + // Restart the request. + auto impl = reinterpret_cast(timer->data); + + // Get rid of the timer. + impl->timer = nullptr; + uv::close(timer); + + impl->start(); +} + +// ------------------------------------------------------------------------------------------------- + +HTTPRequest::HTTPRequest(DefaultFileSource *source, const Resource &resource) + : SharedRequestBase(source, resource) { + // Global initialization. + dispatch_once(&request_initialize, ^{ + NSURLSessionConfiguration *sessionConfig = + [NSURLSessionConfiguration defaultSessionConfiguration]; + sessionConfig.timeoutIntervalForResource = 30; + sessionConfig.HTTPMaximumConnectionsPerHost = 8; + sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + sessionConfig.URLCache = nil; + + session = [NSURLSession sessionWithConfiguration:sessionConfig]; + + // Write user agent string + userAgent = @"MapboxGL"; + }); +} + +HTTPRequest::~HTTPRequest() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + reinterpret_cast(ptr)->cancel(); + } +} + +void HTTPRequest::start(uv_loop_t *loop, std::unique_ptr response) { + MBGL_VERIFY_THREAD(tid); + + assert(!ptr); + ptr = new HTTPRequestImpl(this, loop, std::move(response)); +} + +void HTTPRequest::retryImmediately() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + reinterpret_cast(ptr)->retryImmediately(); + } +} + +void HTTPRequest::cancel() { + MBGL_VERIFY_THREAD(tid); + + delete this; +} + +} diff --git a/platform/default/asset_request_libuv.cpp b/platform/default/asset_request_libuv.cpp index 0e0b7280a..36342bc87 100644 --- a/platform/default/asset_request_libuv.cpp +++ b/platform/default/asset_request_libuv.cpp @@ -1,19 +1,26 @@ -#include +#include #include -#include #include +#include +#include #include +#include -#include +#include + + +namespace algo = boost::algorithm; namespace mbgl { -struct AssetRequestBaton { - AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop); - ~AssetRequestBaton(); +class AssetRequestImpl { + MBGL_STORE_THREAD(tid) + +public: + AssetRequestImpl(AssetRequest *request, uv_loop_t *loop); + ~AssetRequestImpl(); - void cancel(); static void fileOpened(uv_fs_t *req); static void fileStated(uv_fs_t *req); static void fileRead(uv_fs_t *req); @@ -21,52 +28,32 @@ struct AssetRequestBaton { static void notifyError(uv_fs_t *req); static void cleanup(uv_fs_t *req); - const std::thread::id threadId; + AssetRequest *request = nullptr; + bool canceled = false; uv_fs_t req; uv_file fd = -1; - bool canceled = false; - std::string body; uv_buf_t buffer; + std::unique_ptr response; }; -AssetRequestBaton::AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop) - : threadId(std::this_thread::get_id()), request(request_) { - req.data = this; - uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, fileOpened); -} +AssetRequestImpl::~AssetRequestImpl() { + MBGL_VERIFY_THREAD(tid); -AssetRequestBaton::~AssetRequestBaton() { -} - -void AssetRequestBaton::cancel() { - canceled = true; - - // uv_cancel fails frequently when the request has already been started. - // In that case, we have to let it complete and check the canceled bool - // instead. - uv_cancel((uv_req_t *)&req); -} - -void AssetRequestBaton::notifyError(uv_fs_t *req) { - AssetRequestBaton *ptr = reinterpret_cast(req->data); - assert(std::this_thread::get_id() == ptr->threadId); - - if (ptr->request && req->result < 0 && !ptr->canceled && req->result != UV_ECANCELED) { - ptr->request->response = util::make_unique(); - ptr->request->response->code = req->result == UV_ENOENT ? 404 : 500; -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - ptr->request->response->message = uv_strerror(uv_last_error(req->loop)); -#else - ptr->request->response->message = uv_strerror(int(req->result)); -#endif - ptr->request->notify(); + if (request) { + request->ptr = nullptr; } } -void AssetRequestBaton::fileOpened(uv_fs_t *req) { - AssetRequestBaton *ptr = reinterpret_cast(req->data); - assert(std::this_thread::get_id() == ptr->threadId); +AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop) : request(request_) { + req.data = this; + uv_fs_open(loop, &req, (request->resource.url.substr(8)).c_str(), O_RDONLY, S_IRUSR, fileOpened); +} + +void AssetRequestImpl::fileOpened(uv_fs_t *req) { + assert(req->data); + auto self = reinterpret_cast(req->data); + MBGL_VERIFY_THREAD(self->tid); if (req->result < 0) { // Opening failed or was canceled. There isn't much left we can do. @@ -78,88 +65,93 @@ void AssetRequestBaton::fileOpened(uv_fs_t *req) { // We're going to reuse this handle, so we need to cleanup first. uv_fs_req_cleanup(req); - if (ptr->canceled || !ptr->request) { - // Either the AssetRequest object has been destructed, or the - // request was canceled. + if (self->canceled) { + // The request was canceled. uv_fs_close(req->loop, req, fd, fileClosed); } else { - ptr->fd = fd; + self->fd = fd; uv_fs_fstat(req->loop, req, fd, fileStated); } } } -void AssetRequestBaton::fileStated(uv_fs_t *req) { - AssetRequestBaton *ptr = reinterpret_cast(req->data); - assert(std::this_thread::get_id() == ptr->threadId); +void AssetRequestImpl::fileStated(uv_fs_t *req) { + assert(req->data); + auto self = reinterpret_cast(req->data); + MBGL_VERIFY_THREAD(self->tid); - if (req->result != 0 || ptr->canceled || !ptr->request) { + if (req->result != 0 || self->canceled) { // Stating failed or was canceled. We already have an open file handle // though, which we'll have to close. notifyError(req); uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, ptr->fd, fileClosed); + uv_fs_close(req->loop, req, self->fd, fileClosed); } else { #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - const uv_statbuf_t *stat = static_cast(req->ptr); + auto stat = static_cast(req->ptr); #else - const uv_stat_t *stat = static_cast(req->ptr); + auto stat = static_cast(req->ptr); #endif if (stat->st_size > std::numeric_limits::max()) { // File is too large for us to open this way because uv_buf's only support unsigned // ints as maximum size. - if (ptr->request) { - ptr->request->response = util::make_unique(); - ptr->request->response->code = UV_EFBIG; + auto response = util::make_unique(); + response->status = Response::Error; #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - ptr->request->response->message = uv_strerror(uv_err_t {UV_EFBIG, 0}); + response->message = uv_strerror(uv_err_t {UV_EFBIG, 0}); #else - ptr->request->response->message = uv_strerror(UV_EFBIG); + response->message = uv_strerror(UV_EFBIG); #endif - ptr->request->notify(); - } + assert(self->request); + self->request->notify(std::move(response), FileCache::Hint::No); + delete self->request; uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, ptr->fd, fileClosed); + uv_fs_close(req->loop, req, self->fd, fileClosed); } else { - const unsigned int size = (unsigned int)(stat->st_size); - ptr->body.resize(size); - ptr->buffer = uv_buf_init(const_cast(ptr->body.data()), size); + self->response = util::make_unique(); + self->response->modified = stat->st_mtimespec.tv_sec; + self->response->etag = std::to_string(stat->st_ino); + const auto size = (unsigned int)(stat->st_size); + self->response->data.resize(size); + self->buffer = uv_buf_init(const_cast(self->response->data.data()), size); uv_fs_req_cleanup(req); #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_fs_read(req->loop, req, ptr->fd, ptr->buffer.base, ptr->buffer.len, -1, fileRead); + uv_fs_read(req->loop, req, self->fd, self->buffer.base, self->buffer.len, -1, fileRead); #else - uv_fs_read(req->loop, req, ptr->fd, &ptr->buffer, 1, 0, fileRead); + uv_fs_read(req->loop, req, self->fd, &self->buffer, 1, 0, fileRead); #endif } } } -void AssetRequestBaton::fileRead(uv_fs_t *req) { - AssetRequestBaton *ptr = reinterpret_cast(req->data); - assert(std::this_thread::get_id() == ptr->threadId); +void AssetRequestImpl::fileRead(uv_fs_t *req) { + assert(req->data); + auto self = reinterpret_cast(req->data); + MBGL_VERIFY_THREAD(self->tid); - if (req->result < 0 || ptr->canceled || !ptr->request) { - // Reading failed or was canceled. We already have an open file handle + if (req->result < 0 || self->canceled) { + // Stating failed or was canceled. We already have an open file handle // though, which we'll have to close. notifyError(req); } else { // File was successfully read. - if (ptr->request) { - ptr->request->response = util::make_unique(); - ptr->request->response->code = 200; - ptr->request->response->data = std::move(ptr->body); - ptr->request->notify(); - } + self->response->status = Response::Successful; + assert(self->request); + self->request->notify(std::move(self->response), FileCache::Hint::No); + delete self->request; } uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, ptr->fd, fileClosed); + uv_fs_close(req->loop, req, self->fd, fileClosed); } -void AssetRequestBaton::fileClosed(uv_fs_t *req) { - assert(std::this_thread::get_id() == (reinterpret_cast(req->data))->threadId); +void AssetRequestImpl::fileClosed(uv_fs_t *req) { + assert(req->data); + auto self = reinterpret_cast(req->data); + MBGL_VERIFY_THREAD(self->tid); + (void(self)); // Silence unused variable error in Release mode if (req->result < 0) { // Closing the file failed. But there isn't anything we can do. @@ -168,56 +160,70 @@ void AssetRequestBaton::fileClosed(uv_fs_t *req) { cleanup(req); } -void AssetRequestBaton::cleanup(uv_fs_t *req) { - AssetRequestBaton *ptr = reinterpret_cast(req->data); - assert(std::this_thread::get_id() == ptr->threadId); +void AssetRequestImpl::notifyError(uv_fs_t *req) { + assert(req->data); + auto self = reinterpret_cast(req->data); + MBGL_VERIFY_THREAD(self->tid); - if (ptr->request) { - ptr->request->ptr = nullptr; + if (req->result < 0 && !self->canceled && req->result != UV_ECANCELED) { + auto response = util::make_unique(); + response->status = Response::Error; + response->message = uv::getFileRequestError(req); + assert(self->request); + self->request->notify(std::move(response), FileCache::Hint::No); + delete self->request; } +} +void AssetRequestImpl::cleanup(uv_fs_t *req) { + assert(req->data); + auto self = reinterpret_cast(req->data); + MBGL_VERIFY_THREAD(self->tid); uv_fs_req_cleanup(req); - delete ptr; - ptr = nullptr; + delete self; } +// ------------------------------------------------------------------------------------------------- -AssetRequest::AssetRequest(const std::string &path_, uv_loop_t *loop) - : BaseRequest(path_) { - if (!path.empty() && path[0] == '/') { - // This is an absolute path. We don't allow this. Note that this is not a way to absolutely - // prevent access to resources outside the application bundle; e.g. there could be symlinks - // in the application bundle that link to outside. We don't care about these. - response = util::make_unique(); - response->code = 403; - response->message = "Path is outside the application bundle"; - notify(); - } else { - // Note: The AssetRequestBaton object is deleted in AssetRequestBaton::cleanup(). - ptr = new AssetRequestBaton(this, platform::applicationRoot() + "/" + path, loop); - } -} - -void AssetRequest::cancel() { - assert(std::this_thread::get_id() == threadId); - - if (ptr) { - ptr->cancel(); - - // When deleting a AssetRequest object with a uv_fs_* call is in progress, we are making sure - // that the callback doesn't accidentally reference this object again. - ptr->request = nullptr; - ptr = nullptr; - } - - notify(); +AssetRequest::AssetRequest(DefaultFileSource *source, const Resource &resource) + : SharedRequestBase(source, resource) { + assert(algo::starts_with(resource.url, "asset://")); } AssetRequest::~AssetRequest() { - assert(std::this_thread::get_id() == threadId); - cancel(); + MBGL_VERIFY_THREAD(tid); - // Note: The AssetRequestBaton object is deleted in AssetRequestBaton::cleanup(). + if (ptr) { + reinterpret_cast(ptr)->request = nullptr; + } +} + +void AssetRequest::start(uv_loop_t *loop, std::unique_ptr response) { + MBGL_VERIFY_THREAD(tid); + + // We're ignoring the existing response if any. + (void(response)); + + assert(!ptr); + ptr = new AssetRequestImpl(this, loop); + // Note: the AssetRequestImpl deletes itself. +} + +void AssetRequest::cancel() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + reinterpret_cast(ptr)->canceled = true; + + // uv_cancel fails frequently when the request has already been started. + // In that case, we have to let it complete and check the canceled bool + // instead. The cancelation callback will delete the AssetRequest object. + uv_cancel((uv_req_t *)&reinterpret_cast(ptr)->req); + } else { + // This request is canceled before we called start. We're safe to delete + // ourselves now. + delete this; + } } } diff --git a/platform/default/cache_database_tmp.cpp b/platform/default/cache_database_tmp.cpp deleted file mode 100644 index 6132ace69..000000000 --- a/platform/default/cache_database_tmp.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -namespace mbgl { -namespace platform { - -// Returns the path to the default cache database on this system. -std::string defaultCacheDatabase() { - return "/tmp/mbgl-cache.db"; -} - -} -} diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp deleted file mode 100644 index 9e8cf6471..000000000 --- a/platform/default/http_request_baton_curl.cpp +++ /dev/null @@ -1,629 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#ifdef __ANDROID__ - #include - #include - #include -#endif - -#include -#include - -#include - -#include -#include -#include -#include - - -// Check curl library version. -const static bool curl_version_check = []() { - const auto version = curl_version_info(CURLVERSION_NOW); - if (version->version_num != LIBCURL_VERSION_NUM) { - throw std::runtime_error(mbgl::util::sprintf<96>( - "libcurl version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", - (LIBCURL_VERSION_NUM >> 16) & 0xFF, (LIBCURL_VERSION_NUM >> 8) & 0xFF, LIBCURL_VERSION_NUM & 0xFF, - (version->version_num >> 16) & 0xFF, (version->version_num >> 8) & 0xFF, version->version_num & 0xFF)); - } - return true; -}(); - - -// This file contains code from http://curl.haxx.se/libcurl/c/multi-uv.html: - -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* Example application code using the multi socket interface to download - multiple files at once, but instead of using curl_multi_perform and - curl_multi_wait, which uses select(), we use libuv. - It supports epoll, kqueue, etc. on unixes and fast IO completion ports on - Windows, which means, it should be very fast on all platforms.. - - Written by Clemens Gruber, based on an outdated example from uvbook and - some tests from libuv. - - Requires libuv and (of course) libcurl. - - See http://nikhilm.github.com/uvbook/ for more information on libuv. -*/ - -// Handles the request thread + messaging to the thread. -static uv_once_t once; -static uv_loop_t *loop = nullptr; -static uv_messenger_t start_messenger; -static uv_messenger_t stop_messenger; -static uv_thread_t thread; -static std::thread::id thread_id; - -// Used as the CURL timer function to periodically check for socket updates. -static uv_timer_t timeout; - -// CURL multi handle that we use to request multiple URLs at the same time, without having to block -// and spawn threads. -static CURLM *multi = nullptr; - -// CURL share handles are used for sharing session state (e.g.) -static uv_mutex_t share_mutex; -static CURLSH *share = nullptr; - -// A queue that we use for storing resuable CURL easy handles to avoid creating and destroying them -// all the time. -static std::queue handles; - -namespace mbgl { - -struct Context { - const util::ptr baton; - CURL *handle = nullptr; - curl_slist *headers = nullptr; - - Context(const util::ptr &baton_) : baton(baton_) { - assert(baton); - baton->ptr = this; - - if (!handles.empty()) { - handle = handles.front(); - handles.pop(); - } else { - handle = curl_easy_init(); - } - } - - ~Context() { - baton->ptr = nullptr; - - if (headers) { - curl_slist_free_all(headers); - headers = nullptr; - } - - CURLMcode error = curl_multi_remove_handle(multi, handle); - if (error != CURLM_OK) { - baton->response = util::make_unique(); - baton->response->code = -1; - baton->response->message = curl_multi_strerror(error); - } - - curl_easy_setopt(handle, CURLOPT_PRIVATE, nullptr); - curl_easy_reset(handle); - handles.push(handle); - handle = nullptr; - - if (baton->async) { - uv_async_send(baton->async); - baton->async = nullptr; - } - } -}; - -struct Socket { -private: - uv_poll_t poll_handle; - -public: - const curl_socket_t sockfd = 0; - -public: - Socket(curl_socket_t sockfd_) : sockfd(sockfd_) { - uv_poll_init_socket(loop, &poll_handle, sockfd); - poll_handle.data = this; - } - - void start(int events, uv_poll_cb cb) { - uv_poll_start(&poll_handle, events, cb); - } - - void stop() { - assert(poll_handle.data); - poll_handle.data = nullptr; - uv_poll_stop(&poll_handle); - uv_close((uv_handle_t *)&poll_handle, [](uv_handle_t *handle) { - delete (Socket *)handle->data; - }); - } - -private: - // Make the destructor private to ensure that we can only close the Socket - // with stop(), and disallow manual deletion. - ~Socket() { - assert(!poll_handle.data); - } -}; - -// Locks the CURL share handle -void curl_share_lock(CURL *, curl_lock_data, curl_lock_access, void *) { - uv_mutex_lock(&share_mutex); -} - -// Unlocks the CURL share handle -void curl_share_unlock(CURL *, curl_lock_data, void *) { - uv_mutex_unlock(&share_mutex); -} - -void check_multi_info() { - CURLMsg *message = nullptr; - int pending = 0; - - while ((message = curl_multi_info_read(multi, &pending))) { - switch (message->msg) { - case CURLMSG_DONE: { - Context *context = nullptr; - curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&context); - assert(context); - - auto baton = context->baton; - - // This request is complete. We are removing the pointer to the CURL easy handle again - // to prevent this request from getting canceled. - context->baton->ptr = nullptr; - - // Add human-readable error code - if (message->data.result != CURLE_OK) { - baton->response->message = curl_easy_strerror(message->data.result); - baton->response->code = -1; - - switch (message->data.result) { - case CURLE_COULDNT_RESOLVE_PROXY: - case CURLE_COULDNT_RESOLVE_HOST: - case CURLE_COULDNT_CONNECT: - baton->type = HTTPResponseType::ConnectionError; - break; - - case CURLE_OPERATION_TIMEDOUT: - baton->type = HTTPResponseType::TemporaryError; - break; - - default: - baton->type = HTTPResponseType::PermanentError; - } - } else { - long code = 0; - curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &code); - - if (code != 304) { - baton->response->code = code; - } - - if (code == 304) { - baton->type = HTTPResponseType::NotModified; - } else if (code == 200) { - baton->type = HTTPResponseType::Successful; - } else if (code >= 500 && code < 600) { - baton->type = HTTPResponseType::TemporaryError; - } else if (code >= 400 && code < 500) { - baton->type = HTTPResponseType::PermanentError; - } else { - assert(!"code must be either 200 or 304"); - } - } - - delete context; - } break; - - default: - // This should never happen, because there are no other message types. - throw std::runtime_error("CURLMSG returned unknown message type"); - } - } -} - -void curl_perform(uv_poll_t *req, int /* status */, int events) { - int flags = 0; - - uv_timer_stop(&timeout); - - if (events & UV_READABLE) { - flags |= CURL_CSELECT_IN; - } - if (events & UV_WRITABLE) { - flags |= CURL_CSELECT_OUT; - } - - Socket *context = (Socket *)req->data; - int running_handles = 0; - curl_multi_socket_action(multi, context->sockfd, flags, &running_handles); - - check_multi_info(); -} - -int handle_socket(CURL * /* handle */, curl_socket_t s, int action, void * /* userp */, void *socketp) { - Socket *socket = (Socket *)socketp; - if (!socket && action != CURL_POLL_REMOVE) { - socket = new Socket(s); - curl_multi_assign(multi, s, (void *)socket); - } - - switch (action) { - case CURL_POLL_IN: - socket->start(UV_READABLE, curl_perform); - break; - case CURL_POLL_OUT: - socket->start(UV_WRITABLE, curl_perform); - break; - case CURL_POLL_REMOVE: - if (socket) { - socket->stop(); - curl_multi_assign(multi, s, NULL); - } - break; - default: - abort(); - } - - return 0; -} - -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -void on_timeout(uv_timer_t * /* req */, int /* status */) { -#else -void on_timeout(uv_timer_t * /* req */) { -#endif - int running_handles; - CURLMcode error = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running_handles); - if (error != CURLM_OK) { - throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error)); - } - - check_multi_info(); -} - -void start_timeout(CURLM * /* multi */, long timeout_ms, void * /* userp */) { - if (timeout_ms <= 0) { - timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */ - } - uv_timer_start(&timeout, on_timeout, timeout_ms, 0); -} - -void thread_init(void *) { -#ifdef __APPLE__ - pthread_setname_np("CURL"); -#endif - thread_id = std::this_thread::get_id(); - - if (curl_global_init(CURL_GLOBAL_ALL)) { - throw std::runtime_error("Could not init cURL"); - } - - uv_timer_init(loop, &timeout); - - CURLSHcode share_error; - share = curl_share_init(); - - share_error = curl_share_setopt(share, CURLSHOPT_LOCKFUNC, curl_share_lock); - if (share_error != CURLSHE_OK) { - throw std::runtime_error(std::string("CURL share error: ") + curl_share_strerror(share_error)); - } - - share_error = curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, curl_share_unlock); - if (share_error != CURLSHE_OK) { - throw std::runtime_error(std::string("CURL share error: ") + curl_share_strerror(share_error)); - } - - CURLMcode multi_error; - multi = curl_multi_init(); - - multi_error = curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handle_socket); - if (multi_error != CURLM_OK) { - throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(multi_error)); - } - multi_error = curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, start_timeout); - if (multi_error != CURLM_OK) { - throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(multi_error)); - } - - // Main event loop. This will not return until the request loop is terminated. - uv_run(loop, UV_RUN_DEFAULT); - - curl_multi_cleanup(multi); - multi = nullptr; - - curl_share_cleanup(share); - share = nullptr; - - thread_id = std::thread::id(); -} - -// This function is called when we have new data for a request. We just append it to the string -// containing the previous data. -size_t curl_write_cb(void *const contents, const size_t size, const size_t nmemb, void *const userp) { - auto &response = *(std::unique_ptr *)userp; - assert(response); - response->data.append((char *)contents, size * nmemb); - return size * nmemb; -} - -// Compares the beginning of the (non-zero-terminated!) data buffer with the (zero-terminated!) -// header string. If the data buffer contains the header string at the beginning, it returns -// the length of the header string == begin of the value, otherwise it returns npos. -// The comparison of the header is ASCII-case-insensitive. -size_t header_matches(const char *const header, const char *const buffer, const size_t length) { - const size_t header_length = strlen(header); - if (length < header_length) return std::string::npos; - size_t i = 0; - while (i < length && i < header_length && std::tolower(buffer[i]) == header[i]) { - i++; - } - return i == header_length ? i : std::string::npos; -} - -size_t curl_header_cb(char * const buffer, const size_t size, const size_t nmemb, void *const userp) { - const size_t length = size * nmemb; - - auto &response = *(std::unique_ptr *)userp; - assert(response); - - size_t begin = std::string::npos; - if ((begin = header_matches("last-modified: ", buffer, length)) != std::string::npos) { - // Always overwrite the modification date; We might already have a value here from the - // Date header, but this one is more accurate. - const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n - response->modified = curl_getdate(value.c_str(), nullptr); - } else if ((begin = header_matches("etag: ", buffer, length)) != std::string::npos) { - response->etag = { buffer + begin, length - begin - 2 }; // remove \r\n - } else if ((begin = header_matches("cache-control: ", buffer, length)) != std::string::npos) { - const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n - response->expires = Response::parseCacheControl(value.c_str()); - } - - return length; -} - -// This function is called to load the CA bundle -// from http://curl.haxx.se/libcurl/c/cacertinmem.html -#ifdef __ANDROID__ -static CURLcode sslctx_function(CURL */*curl*/, void *sslctx, void */*parm*/) { - - int error = 0; - struct zip *apk = zip_open(mbgl::android::apkPath.c_str(), 0, &error); - if (apk == nullptr) { - return CURLE_SSL_CACERT_BADFILE; - } - - struct zip_file *apkFile = zip_fopen(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE); - if (apkFile == nullptr) { - zip_close(apk); - apk = nullptr; - return CURLE_SSL_CACERT_BADFILE; - } - - struct zip_stat stat; - if (zip_stat(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE, &stat) != 0) { - zip_fclose(apkFile); - apkFile = nullptr; - zip_close(apk); - apk = nullptr; - return CURLE_SSL_CACERT_BADFILE; - } - - if (stat.size > std::numeric_limits::max()) { - zip_fclose(apkFile); - apkFile = nullptr; - zip_close(apk); - apk = nullptr; - return CURLE_SSL_CACERT_BADFILE; - } - - const std::unique_ptr pem = util::make_unique(stat.size); - - if (static_cast(zip_fread(apkFile, reinterpret_cast(pem.get()), stat.size)) != stat.size) { - zip_fclose(apkFile); - apkFile = nullptr; - zip_close(apk); - apk = nullptr; - return CURLE_SSL_CACERT_BADFILE; - } - - // get a pointer to the X509 certificate store (which may be empty!) - X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx); - if (store == nullptr) { - return CURLE_SSL_CACERT_BADFILE; - } - - // get a BIO - BIO *bio = BIO_new_mem_buf(pem.get(), static_cast(stat.size)); - if (bio == nullptr) { - store = nullptr; - return CURLE_SSL_CACERT_BADFILE; - } - - // use it to read the PEM formatted certificate from memory into an X509 - // structure that SSL can use - X509 *cert = nullptr; - while (PEM_read_bio_X509(bio, &cert, 0, nullptr) != nullptr) { - if (cert == nullptr) { - BIO_free(bio); - bio = nullptr; - store = nullptr; - return CURLE_SSL_CACERT_BADFILE; - } - - // add our certificate to this store - if (X509_STORE_add_cert(store, cert) == 0) { - X509_free(cert); - cert = nullptr; - BIO_free(bio); - bio = nullptr; - store = nullptr; - return CURLE_SSL_CACERT_BADFILE; - } - - X509_free(cert); - cert = nullptr; - } - - // decrease reference counts - BIO_free(bio); - bio = nullptr; - - zip_fclose(apkFile); - apkFile = nullptr; - zip_close(apk); - apk = nullptr; - - // all set to go - return CURLE_OK; -} -#endif - -std::string buildUserAgentString() { -#ifdef __ANDROID__ - return util::sprintf<128>("MapboxGL/%d.%d.%d (+https://mapbox.com/mapbox-gl/; %s; %s %s)", - version::major, version::minor, version::patch, version::revision, "Android", mbgl::android::androidRelease.c_str()); -#else - utsname name; - uname(&name); - return util::sprintf<128>("MapboxGL/%d.%d.%d (+https://mapbox.com/mapbox-gl/; %s; %s %s)", - version::major, version::minor, version::patch, version::revision, name.sysname, name.release); -#endif -} - -// This function must run in the CURL thread. -void start_request(void *const ptr) { - assert(std::this_thread::get_id() == thread_id); - static const std::string userAgent = buildUserAgentString(); - - // The Context object stores information that we need to retain throughout the request, such - // as the actual CURL easy handle, the baton, and the list of headers. The Context itself is - // stored in both the CURL easy handle's PRIVATE field, and the baton's `ptr` field. - auto context = new Context(*(util::ptr *)ptr); - delete (util::ptr *)ptr; - - if (context->baton->response) { - if (!context->baton->response->etag.empty()) { - const std::string header = std::string("If-None-Match: ") + context->baton->response->etag; - context->headers = curl_slist_append(context->headers, header.c_str()); - } else if (context->baton->response->modified) { - const std::string time = - std::string("If-Modified-Since: ") + util::rfc1123(context->baton->response->modified); - context->headers = curl_slist_append(context->headers, time.c_str()); - } - } - - if (context->headers) { - curl_easy_setopt(context->handle, CURLOPT_HTTPHEADER, context->headers); - } - - if (!context->baton->response) { - context->baton->response = util::make_unique(); - } - - // Carry on the shared pointer in the private information of the CURL handle. - curl_easy_setopt(context->handle, CURLOPT_PRIVATE, context); -#ifndef __ANDROID__ - curl_easy_setopt(context->handle, CURLOPT_CAINFO, "ca-bundle.crt"); -#else - curl_easy_setopt(context->handle, CURLOPT_SSLCERTTYPE, "PEM"); - curl_easy_setopt(context->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); -#endif - curl_easy_setopt(context->handle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(context->handle, CURLOPT_URL, context->baton->path.c_str()); - curl_easy_setopt(context->handle, CURLOPT_WRITEFUNCTION, curl_write_cb); - curl_easy_setopt(context->handle, CURLOPT_WRITEDATA, &context->baton->response); - curl_easy_setopt(context->handle, CURLOPT_HEADERFUNCTION, curl_header_cb); - curl_easy_setopt(context->handle, CURLOPT_HEADERDATA, &context->baton->response); - curl_easy_setopt(context->handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"); - curl_easy_setopt(context->handle, CURLOPT_USERAGENT, userAgent.c_str()); - curl_easy_setopt(context->handle, CURLOPT_SHARE, share); - - // Start requesting the information. - curl_multi_add_handle(multi, context->handle); -} - -// This function must run in the CURL thread. -void stop_request(void *const ptr) { - assert(std::this_thread::get_id() == thread_id); - auto baton = *(util::ptr *)ptr; - delete (util::ptr *)ptr; - assert(baton); - - if (baton->async) { - baton->type = HTTPResponseType::Canceled; - - assert(baton->ptr); - - // We can still stop the request because it is still in progress. - delete (Context *)baton->ptr; - assert(!baton->ptr); - } else { - // If the async handle is gone, it means that the actual request has been completed before - // we got a chance to cancel it. In this case, this is a no-op. It is likely that - // the pointer below is the last lifeline of the HTTPRequestBaton. This means we're going - // to delete the HTTPRequestBaton in the current (CURL) thread. - } -} - -void create_thread() { - uv_mutex_init(&share_mutex); -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - loop = uv_loop_new(); -#else - loop = new uv_loop_t; - uv_loop_init(loop); -#endif - uv_messenger_init(loop, &start_messenger, start_request); - uv_messenger_init(loop, &stop_messenger, stop_request); - uv_thread_create(&thread, thread_init, nullptr); -} - -// This function must be run from the main thread (== where the HTTPRequestBaton was created) -void HTTPRequestBaton::start(const util::ptr &ptr) { - assert(std::this_thread::get_id() == ptr->threadId); - uv_once(&once, create_thread); - uv_messenger_send(&start_messenger, new util::ptr(ptr)); -} - -// This function must be run from the main thread (== where the HTTPRequestBaton was created) -void HTTPRequestBaton::stop(const util::ptr &ptr) { - assert(std::this_thread::get_id() == ptr->threadId); - uv_once(&once, create_thread); - uv_messenger_send(&stop_messenger, new util::ptr(ptr)); -} - -} diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp new file mode 100644 index 000000000..42764e492 --- /dev/null +++ b/platform/default/http_request_curl.cpp @@ -0,0 +1,640 @@ +#include +#include +#include + +#include + +#include + +#include +#include +#include + + +void handleError(CURLMcode code) { + if (code != CURLM_OK) { + throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(code)); + } +} + +void handleError(CURLcode code) { + if (code != CURLE_OK) { + throw std::runtime_error(std::string("CURL easy error: ") + curl_easy_strerror(code)); + } +} + +namespace mbgl { + +enum class ResponseStatus : int8_t { + // This error probably won't be resolved by retrying anytime soon. We are giving up. + PermanentError, + + // This error might be resolved by waiting some time (e.g. server issues). + // We are going to do an exponential back-off and will try again in a few seconds. + TemporaryError, + + // This error might be resolved once the network reachability status changes. + // We are going to watch the network status for changes and will retry as soon as the + // operating system notifies us of a network status change. + ConnectionError, + + // The request returned data successfully. We retrieved and decoded the data successfully. + Successful, + + // The request confirmed that the data wasn't changed. We already have the data. + NotModified, +}; + +class HTTPRequestImpl; + +class HTTPCURLContext : public HTTPContext { + +public: + HTTPCURLContext(uv_loop_t *loop); + ~HTTPCURLContext(); + + static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp); + static void perform(uv_poll_t *req, int status, int events); + static int startTimeout(CURLM *multi, long timeout_ms, void *userp); +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + static void onTimeout(uv_timer_t *req, int status); +#else + static void onTimeout(uv_timer_t *req); +#endif + + CURL *getHandle(); + void returnHandle(CURL *handle); + void checkMultiInfo(); + +public: + // Used as the CURL timer function to periodically check for socket updates. + uv_timer_t *timeout = nullptr; + + // CURL multi handle that we use to request multiple URLs at the same time, without having to + // block and spawn threads. + CURLM *multi = nullptr; + + // CURL share handles are used for sharing session state (e.g.) + CURLSH *share = nullptr; + + // A queue that we use for storing resuable CURL easy handles to avoid creating and destroying + // them all the time. + std::queue handles; +}; + + +class HTTPRequestImpl { + MBGL_STORE_THREAD(tid) + +public: + HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::unique_ptr response); + ~HTTPRequestImpl(); + + void handleResult(CURLcode code); + void abandon(); + void retryImmediately(); + +private: + static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp); + static size_t writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp); + + void retry(uint64_t timeout); + static void restart(uv_timer_t *timer, int); + void finish(ResponseStatus status); + void start(); + +private: + HTTPCURLContext *context = nullptr; + HTTPRequest *request = nullptr; + + // Will store the current response. + std::unique_ptr response; + + // In case of revalidation requests, this will store the old response. + std::unique_ptr existingResponse; + + CURL *handle = nullptr; + curl_slist *headers = nullptr; + + uv_timer_t *timer = nullptr; + enum : bool { PreemptImmediately, ExponentialBackoff } strategy = PreemptImmediately; + int attempts = 0; + + static const int maxAttempts = 4; +}; + + + +struct Socket { +private: + uv_poll_t poll; + +public: + HTTPCURLContext *context = nullptr; + const curl_socket_t sockfd = 0; + +public: + Socket(HTTPCURLContext *context_, curl_socket_t sockfd_) : context(context_), sockfd(sockfd_) { + assert(context); + uv_poll_init_socket(context->loop, &poll, sockfd); + poll.data = this; + } + + void start(int events, uv_poll_cb cb) { + uv_poll_start(&poll, events, cb); + } + + void stop() { + assert(poll.data); + uv_poll_stop(&poll); + uv_close((uv_handle_t *)&poll, [](uv_handle_t *handle) { + assert(handle->data); + delete reinterpret_cast(handle->data); + }); + } + +private: + // Make the destructor private to ensure that we can only close the Socket + // with stop(), and disallow manual deletion. + ~Socket() = default; +}; + +// ------------------------------------------------------------------------------------------------- + +template<> pthread_key_t HTTPContext::key{}; +template<> pthread_once_t HTTPContext::once = PTHREAD_ONCE_INIT; + +HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) : HTTPContext(loop_) { + if (curl_global_init(CURL_GLOBAL_ALL)) { + throw std::runtime_error("Could not init cURL"); + } + + timeout = new uv_timer_t; + timeout->data = this; + uv_timer_init(loop, timeout); + + share = curl_share_init(); + + multi = curl_multi_init(); + handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handleSocket)); + handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, this)); + handleError(curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, startTimeout)); + handleError(curl_multi_setopt(multi, CURLMOPT_TIMERDATA, this)); +} + +HTTPCURLContext::~HTTPCURLContext() { + curl_multi_cleanup(multi); + multi = nullptr; + + curl_share_cleanup(share); + share = nullptr; + + uv_timer_stop(timeout); + uv::close(timeout); +} + +CURL *HTTPCURLContext::getHandle() { + if (!handles.empty()) { + auto handle = handles.front(); + handles.pop(); + return handle; + } else { + return curl_easy_init(); + } +} + +void HTTPCURLContext::returnHandle(CURL *handle) { + curl_easy_reset(handle); + handles.push(handle); +} + +void HTTPCURLContext::checkMultiInfo() { + MBGL_VERIFY_THREAD(tid); + CURLMsg *message = nullptr; + int pending = 0; + + while ((message = curl_multi_info_read(multi, &pending))) { + switch (message->msg) { + case CURLMSG_DONE: { + HTTPRequestImpl *baton = nullptr; + curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton); + assert(baton); + baton->handleResult(message->data.result); + } break; + + default: + // This should never happen, because there are no other message types. + throw std::runtime_error("CURLMsg returned unknown message type"); + } + } +} + +void HTTPCURLContext::perform(uv_poll_t *req, int /* status */, int events) { + assert(req->data); + auto socket = reinterpret_cast(req->data); + auto context = socket->context; + MBGL_VERIFY_THREAD(context->tid); + + int flags = 0; + + if (events & UV_READABLE) { + flags |= CURL_CSELECT_IN; + } + if (events & UV_WRITABLE) { + flags |= CURL_CSELECT_OUT; + } + + + int running_handles = 0; + curl_multi_socket_action(context->multi, socket->sockfd, flags, &running_handles); + context->checkMultiInfo(); +} + +int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp, + void *socketp) { + auto socket = reinterpret_cast(socketp); + assert(userp); + auto context = reinterpret_cast(userp); + MBGL_VERIFY_THREAD(context->tid); + + if (!socket && action != CURL_POLL_REMOVE) { + socket = new Socket(context, s); + curl_multi_assign(context->multi, s, (void *)socket); + } + + switch (action) { + case CURL_POLL_IN: + socket->start(UV_READABLE, perform); + break; + case CURL_POLL_OUT: + socket->start(UV_WRITABLE, perform); + break; + case CURL_POLL_REMOVE: + if (socket) { + socket->stop(); + curl_multi_assign(context->multi, s, nullptr); + } + break; + default: + throw std::runtime_error("Unhandled CURL socket action"); + } + + return 0; +} + +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 +void HTTPCURLContext::onTimeout(uv_timer_t *req, int /* status */) { +#else +void HTTPCURLContext::onTimeout(uv_timer_t *req) { +#endif + assert(req->data); + auto context = reinterpret_cast(req->data); + MBGL_VERIFY_THREAD(context->tid); + int running_handles; + CURLMcode error = curl_multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles); + if (error != CURLM_OK) { + throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error)); + } + context->checkMultiInfo(); +} + +int HTTPCURLContext::startTimeout(CURLM * /* multi */, long timeout_ms, void *userp) { + assert(userp); + auto context = reinterpret_cast(userp); + MBGL_VERIFY_THREAD(context->tid); + if (timeout_ms < 0) { + // A timeout of 0 ms means that the timer will invoked in the next loop iteration. + timeout_ms = 0; + } + uv_timer_stop(context->timeout); + uv_timer_start(context->timeout, onTimeout, timeout_ms, 0); + return 0; +} + +// ------------------------------------------------------------------------------------------------- + +HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::unique_ptr response_) + : context(HTTPCURLContext::Get(loop)), + request(request_), + existingResponse(std::move(response_)), + handle(context->getHandle()) { + assert(request); + context->addRequest(request); + + // If there's already a response, set the correct etags/modified headers to make sure we are + // getting a 304 response if possible. This avoids redownloading unchanged data. + if (existingResponse) { + if (!existingResponse->etag.empty()) { + const std::string header = std::string("If-None-Match: ") + existingResponse->etag; + headers = curl_slist_append(headers, header.c_str()); + } else if (existingResponse->modified) { + const std::string time = + std::string("If-Modified-Since: ") + util::rfc1123(existingResponse->modified); + headers = curl_slist_append(headers, time.c_str()); + } + } + + if (headers) { + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + } + + handleError(curl_easy_setopt(handle, CURLOPT_PRIVATE, this)); + handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt")); + handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1)); + handleError(curl_easy_setopt(handle, CURLOPT_URL, request->resource.url.c_str())); + handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback)); + handleError(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this)); + handleError(curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback)); + handleError(curl_easy_setopt(handle, CURLOPT_HEADERDATA, this)); + handleError(curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate")); + handleError(curl_easy_setopt(handle, CURLOPT_USERAGENT, "MapboxGL/1.0")); + handleError(curl_easy_setopt(handle, CURLOPT_SHARE, context->share)); + + start(); +} + +void HTTPRequestImpl::abandon() { + if (request) { + context->removeRequest(request); + request = nullptr; + } +} + +void HTTPRequestImpl::start() { + // Count up the attempts. + attempts++; + + // Start requesting the information. + handleError(curl_multi_add_handle(context->multi, handle)); +} + +HTTPRequestImpl::~HTTPRequestImpl() { + MBGL_VERIFY_THREAD(tid); + + if (request) { + context->removeRequest(request); + request->ptr = nullptr; + } + + handleError(curl_multi_remove_handle(context->multi, handle)); + context->returnHandle(handle); + handle = nullptr; + + if (timer) { + // Stop the backoff timer to avoid re-triggering this request. + uv_timer_stop(timer); + uv::close(timer); + timer = nullptr; + } + + if (headers) { + curl_slist_free_all(headers); + headers = nullptr; + } +} + +// This function is called when we have new data for a request. We just append it to the string +// containing the previous data. +size_t HTTPRequestImpl::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) { + assert(userp); + auto impl = reinterpret_cast(userp); + MBGL_VERIFY_THREAD(impl->tid); + + if (!impl->response) { + impl->response = util::make_unique(); + } + + impl->response->data.append((char *)contents, size * nmemb); + return size * nmemb; +} + +// Compares the beginning of the (non-zero-terminated!) data buffer with the (zero-terminated!) +// header string. If the data buffer contains the header string at the beginning, it returns +// the length of the header string == begin of the value, otherwise it returns npos. +// The comparison of the header is ASCII-case-insensitive. +size_t headerMatches(const char *const header, const char *const buffer, const size_t length) { + const size_t headerLength = strlen(header); + if (length < headerLength) { + return std::string::npos; + } + size_t i = 0; + while (i < length && i < headerLength && std::tolower(buffer[i]) == std::tolower(header[i])) { + i++; + } + return i == headerLength ? i : std::string::npos; +} + +int64_t parseCacheControl(const char *value) { + if (value) { + unsigned long long seconds = 0; + // TODO: cache-control may contain other information as well: + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + if (std::sscanf(value, "max-age=%llu", &seconds) == 1) { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count() + + seconds; + } + } + + return 0; +} + +size_t HTTPRequestImpl::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) { + assert(userp); + auto baton = reinterpret_cast(userp); + MBGL_VERIFY_THREAD(baton->tid); + + if (!baton->response) { + baton->response = util::make_unique(); + } + + const size_t length = size * nmemb; + size_t begin = std::string::npos; + if ((begin = headerMatches("last-modified: ", buffer, length)) != std::string::npos) { + // Always overwrite the modification date; We might already have a value here from the + // Date header, but this one is more accurate. + const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n + baton->response->modified = curl_getdate(value.c_str(), nullptr); + } else if ((begin = headerMatches("etag: ", buffer, length)) != std::string::npos) { + baton->response->etag = { buffer + begin, length - begin - 2 }; // remove \r\n + } else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) { + const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n + baton->response->expires = parseCacheControl(value.c_str()); + } else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) { + const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n + baton->response->expires = curl_getdate(value.c_str(), nullptr); + } + + return length; +} + + +void HTTPRequestImpl::retry(uint64_t timeout) { + handleError(curl_multi_remove_handle(context->multi, handle)); + + response.reset(); + + assert(!timer); + timer = new uv_timer_t; + timer->data = this; + uv_timer_init(context->loop, timer); + uv_timer_start(timer, restart, timeout, 0); +} + +void HTTPRequestImpl::retryImmediately() { + // All batons get notified when the network status changed, but some of them + // might not actually wait for the network to become available again. + if (timer && strategy == PreemptImmediately) { + // Triggers the timer upon the next event loop iteration. + uv_timer_stop(timer); + uv_timer_start(timer, restart, 0, 0); + } +} + +void HTTPRequestImpl::restart(uv_timer_t *timer, int) { + // Restart the request. + auto baton = reinterpret_cast(timer->data); + + // Get rid of the timer. + baton->timer = nullptr; + uv::close(timer); + + baton->start(); +} + +void HTTPRequestImpl::finish(ResponseStatus status) { + if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) { + strategy = ExponentialBackoff; + return retry((1 << (attempts - 1)) * 1000); + } else if (status == ResponseStatus::ConnectionError && attempts < maxAttempts) { + // By default, we will retry every 30 seconds (network change notification will + // preempt the timeout). + strategy = PreemptImmediately; + return retry(30000); + } + + // Actually return the response. + if (status == ResponseStatus::NotModified) { + request->notify(std::move(response), FileCache::Hint::Refresh); + } else { + request->notify(std::move(response), FileCache::Hint::Full); + } + + delete request; + delete this; +} + +void HTTPRequestImpl::handleResult(CURLcode code) { + MBGL_VERIFY_THREAD(tid); + + if (!request) { + // In this case, it doesn't make sense to even process the response even further since + // the request was canceled anyway. + delete this; + return; + } + + // Make sure a response object exists in case we haven't got any headers + // or content. + if (!response) { + response = util::make_unique(); + } + + // Add human-readable error code + if (code != CURLE_OK) { + response->message = curl_easy_strerror(code); + + switch (code) { + case CURLE_COULDNT_RESOLVE_PROXY: + case CURLE_COULDNT_RESOLVE_HOST: + case CURLE_COULDNT_CONNECT: + response->status = Response::Error; + return finish(ResponseStatus::ConnectionError); + + case CURLE_OPERATION_TIMEDOUT: + response->status = Response::Error; + return finish(ResponseStatus::TemporaryError); + + default: + response->status = Response::Error; + return finish(ResponseStatus::PermanentError); + } + } else { + long responseCode = 0; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode); + + if (responseCode == 304) { + if (existingResponse) { + // We're going to reuse the old response object, but need to copy over the new + // expires value (if possible). + std::swap(response, existingResponse); + if (existingResponse->expires) { + response->expires = existingResponse->expires; + } + return finish(ResponseStatus::NotModified); + } else { + // This is an unsolicited 304 response and should only happen on malfunctioning + // HTTP servers. It likely doesn't include any data, but we don't have much options. + response->status = Response::Successful; + return finish(ResponseStatus::Successful); + } + } else if (responseCode == 200) { + response->status = Response::Successful; + return finish(ResponseStatus::Successful); + } else if (responseCode >= 500 && responseCode < 600) { + // Server errors may be temporary, so back off exponentially. + response->status = Response::Error; + response->message = "HTTP status code " + std::to_string(responseCode); + return finish(ResponseStatus::TemporaryError); + } else { + // We don't know how to handle any other errors, so declare them as permanently failing. + response->status = Response::Error; + response->message = "HTTP status code " + std::to_string(responseCode); + return finish(ResponseStatus::PermanentError); + } + } + + throw std::runtime_error("Response hasn't been handled"); +} + +// ------------------------------------------------------------------------------------------------- + +HTTPRequest::HTTPRequest(DefaultFileSource *source, const Resource &resource) + : SharedRequestBase(source, resource) { +} + +HTTPRequest::~HTTPRequest() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + reinterpret_cast(ptr)->abandon(); + } +} + +void HTTPRequest::start(uv_loop_t *loop, std::unique_ptr response) { + MBGL_VERIFY_THREAD(tid); + + assert(!ptr); + ptr = new HTTPRequestImpl(this, loop, std::move(response)); +} + +void HTTPRequest::retryImmediately() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + reinterpret_cast(ptr)->retryImmediately(); + } +} + +void HTTPRequest::cancel() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + delete reinterpret_cast(ptr); + ptr = nullptr; + } + + delete this; +} + +} diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 3359dd317..112499a48 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -127,9 +128,7 @@ Map::~Map() { } uv::worker &Map::getWorker() { - if (!workers) { - workers = util::make_unique(**loop, 4, "Tile Worker"); - } + assert(workers); return *workers; } @@ -153,8 +152,6 @@ void Map::start(bool startPaused) { workers.reset(); activeSources.clear(); - fileSource.clearLoop(); - terminating = true; // Closes all open handles on the loop. This means that the loop will automatically terminate. @@ -195,6 +192,8 @@ void Map::start(bool startPaused) { pthread_setname_np("Map"); #endif + workers = util::make_unique(**loop, 4, "Tile Worker"); + run(); #ifndef NDEBUG @@ -301,7 +300,6 @@ void Map::run() { mapThread = std::thread::id(); #endif mode = Mode::None; - fileSource.clearLoop(); } view.deactivate(); @@ -386,13 +384,14 @@ void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) { style = std::make_shared