summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--common/Reachability.h112
-rw-r--r--common/Reachability.m527
-rw-r--r--common/foundation_request.h1
-rw-r--r--common/foundation_request.mm115
-rw-r--r--common/glfw_view.cpp8
-rw-r--r--common/glfw_view.hpp1
-rw-r--r--common/headless_view.cpp4
-rw-r--r--common/headless_view.hpp1
-rw-r--r--common/http_request_baton_cocoa.mm149
-rw-r--r--common/http_request_baton_curl.cpp465
-rw-r--r--common/ios.mm21
-rw-r--r--common/linux.cpp12
-rw-r--r--common/osx.mm31
-rwxr-xr-xconfigure9
-rw-r--r--include/mbgl/map/map.hpp80
-rw-r--r--include/mbgl/map/raster_tile_data.hpp8
-rw-r--r--include/mbgl/map/source.hpp10
-rw-r--r--include/mbgl/map/sprite.hpp6
-rw-r--r--include/mbgl/map/tile.hpp4
-rw-r--r--include/mbgl/map/tile_data.hpp17
-rw-r--r--include/mbgl/map/tile_parser.hpp30
-rw-r--r--include/mbgl/map/transform.hpp8
-rw-r--r--include/mbgl/map/vector_tile_data.hpp6
-rw-r--r--include/mbgl/map/view.hpp2
-rw-r--r--include/mbgl/platform/event.hpp2
-rw-r--r--include/mbgl/platform/platform.hpp21
-rw-r--r--include/mbgl/platform/request.hpp41
-rw-r--r--include/mbgl/renderer/bucket.hpp6
-rw-r--r--include/mbgl/renderer/debug_bucket.hpp3
-rw-r--r--include/mbgl/renderer/fill_bucket.hpp2
-rw-r--r--include/mbgl/renderer/line_bucket.hpp5
-rw-r--r--include/mbgl/renderer/painter.hpp17
-rw-r--r--include/mbgl/renderer/raster_bucket.hpp4
-rw-r--r--include/mbgl/renderer/symbol_bucket.hpp2
-rw-r--r--include/mbgl/storage/base_request.hpp62
-rw-r--r--include/mbgl/storage/file_request.hpp27
-rw-r--r--include/mbgl/storage/file_request_baton.hpp35
-rw-r--r--include/mbgl/storage/file_source.hpp55
-rw-r--r--include/mbgl/storage/http_request.hpp57
-rw-r--r--include/mbgl/storage/http_request_baton.hpp73
-rw-r--r--include/mbgl/storage/request.hpp39
-rw-r--r--include/mbgl/storage/request_callback.hpp22
-rw-r--r--include/mbgl/storage/resource_type.hpp18
-rw-r--r--include/mbgl/storage/response.hpp26
-rw-r--r--include/mbgl/storage/sqlite_store.hpp47
-rw-r--r--include/mbgl/style/style.hpp4
-rw-r--r--include/mbgl/style/style_bucket.hpp6
-rw-r--r--include/mbgl/style/style_layer.hpp7
-rw-r--r--include/mbgl/style/style_layer_group.hpp2
-rw-r--r--include/mbgl/style/style_parser.hpp18
-rw-r--r--include/mbgl/style/style_source.hpp14
-rw-r--r--include/mbgl/text/glyph_store.hpp14
-rw-r--r--include/mbgl/util/compression.hpp15
-rw-r--r--include/mbgl/util/filesource.hpp43
-rw-r--r--include/mbgl/util/interpolate.hpp2
-rw-r--r--include/mbgl/util/parsedate.h38
-rw-r--r--include/mbgl/util/ptr.hpp29
-rw-r--r--include/mbgl/util/queue.h92
-rw-r--r--include/mbgl/util/raster.hpp8
-rw-r--r--include/mbgl/util/sqlite3.hpp74
-rw-r--r--include/mbgl/util/uv-channel.h29
-rw-r--r--include/mbgl/util/uv-messenger.h32
-rw-r--r--include/mbgl/util/uv-worker.h42
-rw-r--r--include/mbgl/util/uv.hpp7
-rw-r--r--include/mbgl/util/uv_detail.hpp52
-rw-r--r--linux/mapboxgl-app.gyp5
-rw-r--r--macosx/main.mm14
-rw-r--r--macosx/mapboxgl-app.gyp7
-rw-r--r--mapboxgl.gyp159
-rwxr-xr-xscripts/flags.sh8
-rwxr-xr-xsetup-libraries.sh23
-rw-r--r--src/map/map.cpp217
-rw-r--r--src/map/raster_tile_data.cpp6
-rw-r--r--src/map/source.cpp39
-rw-r--r--src/map/sprite.cpp30
-rw-r--r--src/map/tile_data.cpp35
-rw-r--r--src/map/tile_parser.cpp39
-rw-r--r--src/map/transform.cpp2
-rw-r--r--src/map/vector_tile_data.cpp10
-rw-r--r--src/platform/request.cpp54
-rw-r--r--src/renderer/debug_bucket.cpp2
-rw-r--r--src/renderer/fill_bucket.cpp2
-rw-r--r--src/renderer/line_bucket.cpp2
-rw-r--r--src/renderer/painter.cpp4
-rw-r--r--src/renderer/painter_clipping.cpp4
-rw-r--r--src/renderer/painter_fill.cpp2
-rw-r--r--src/renderer/painter_line.cpp4
-rw-r--r--src/renderer/painter_raster.cpp4
-rw-r--r--src/renderer/painter_symbol.cpp2
-rw-r--r--src/renderer/raster_bucket.cpp4
-rw-r--r--src/renderer/symbol_bucket.cpp2
-rw-r--r--src/storage/base_request.cpp86
-rw-r--r--src/storage/file_request.cpp37
-rw-r--r--src/storage/file_request_baton.cpp142
-rw-r--r--src/storage/file_source.cpp105
-rw-r--r--src/storage/http_request.cpp270
-rw-r--r--src/storage/http_request_baton.cpp12
-rw-r--r--src/storage/request.cpp49
-rw-r--r--src/storage/response.cpp22
-rw-r--r--src/storage/sqlite_store.cpp227
-rw-r--r--src/style/style_layer_group.cpp6
-rw-r--r--src/style/style_parser.cpp32
-rw-r--r--src/text/glyph.cpp3
-rw-r--r--src/text/glyph_store.cpp43
-rw-r--r--src/util/compression.cpp81
-rw-r--r--src/util/filesource.cpp58
-rw-r--r--src/util/parsedate.c689
-rw-r--r--src/util/raster.cpp13
-rw-r--r--src/util/sqlite3.cpp166
-rw-r--r--src/util/uv-channel.c69
-rw-r--r--src/util/uv-messenger.c74
-rw-r--r--src/util/uv-worker.c166
-rw-r--r--src/util/uv.cpp12
-rw-r--r--test/fixtures/fixture_request.cpp130
115 files changed, 5031 insertions, 862 deletions
diff --git a/Makefile b/Makefile
index b7aa397e1a..584fe2a2e4 100644
--- a/Makefile
+++ b/Makefile
@@ -108,4 +108,4 @@ distclean: clean
-rm -rf ./mapnik-packaging/osx/out/universal
-find ./mapnik-packaging/osx/out/packages -type d ! -name 'packages' -maxdepth 1 -exec rm -rf {} \;
-.PHONY: mbgl test linux
+.PHONY: mbgl test linux build/test/Makefile
diff --git a/common/Reachability.h b/common/Reachability.h
new file mode 100644
index 0000000000..1cf7d2ecea
--- /dev/null
+++ b/common/Reachability.h
@@ -0,0 +1,112 @@
+/*
+ Copyright (c) 2011, Tony Million.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+#import <SystemConfiguration/SystemConfiguration.h>
+
+#import <sys/socket.h>
+#import <netinet/in.h>
+#import <netinet6/in6.h>
+#import <arpa/inet.h>
+#import <ifaddrs.h>
+#import <netdb.h>
+
+/**
+ * Does ARC support GCD objects?
+ * It does if the minimum deployment target is iOS 6+ or Mac OS X 8+
+ *
+ * @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h
+ **/
+#if OS_OBJECT_USE_OBJC
+#define NEEDS_DISPATCH_RETAIN_RELEASE 0
+#else
+#define NEEDS_DISPATCH_RETAIN_RELEASE 1
+#endif
+
+/**
+ * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
+ *
+ * @see http://nshipster.com/ns_enum-ns_options/
+ **/
+#ifndef NS_ENUM
+#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
+#endif
+
+extern NSString *const kReachabilityChangedNotification;
+
+typedef NS_ENUM(NSInteger, NetworkStatus) {
+ // Apple NetworkStatus Compatible Names.
+ NotReachable = 0,
+ ReachableViaWiFi = 2,
+ ReachableViaWWAN = 1
+};
+
+@class Reachability;
+
+typedef void (^NetworkReachable)(Reachability * reachability);
+typedef void (^NetworkUnreachable)(Reachability * reachability);
+
+@interface Reachability : NSObject
+
+@property (nonatomic, copy) NetworkReachable reachableBlock;
+@property (nonatomic, copy) NetworkUnreachable unreachableBlock;
+
+
+@property (nonatomic, assign) BOOL reachableOnWWAN;
+
++(Reachability*)reachabilityWithHostname:(NSString*)hostname;
+// This is identical to the function above, but is here to maintain
+//compatibility with Apples original code. (see .m)
++(Reachability*)reachabilityWithHostName:(NSString*)hostname;
++(Reachability*)reachabilityForInternetConnection;
++(Reachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress;
++(Reachability*)reachabilityForLocalWiFi;
+
+-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
+
+-(BOOL)startNotifier;
+-(void)stopNotifier;
+
+-(BOOL)isReachable;
+-(BOOL)isReachableViaWWAN;
+-(BOOL)isReachableViaWiFi;
+
+// WWAN may be available, but not active until a connection has been established.
+// WiFi may require a connection for VPN on Demand.
+-(BOOL)isConnectionRequired; // Identical DDG variant.
+-(BOOL)connectionRequired; // Apple's routine.
+// Dynamic, on demand connection?
+-(BOOL)isConnectionOnDemand;
+// Is user intervention required?
+-(BOOL)isInterventionRequired;
+
+-(NetworkStatus)currentReachabilityStatus;
+-(SCNetworkReachabilityFlags)reachabilityFlags;
+-(NSString*)currentReachabilityString;
+-(NSString*)currentReachabilityFlags;
+
+@end
diff --git a/common/Reachability.m b/common/Reachability.m
new file mode 100644
index 0000000000..c6f6388f52
--- /dev/null
+++ b/common/Reachability.m
@@ -0,0 +1,527 @@
+/*
+ Copyright (c) 2011, Tony Million.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "Reachability.h"
+
+
+NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
+
+@interface Reachability ()
+
+@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
+
+
+#if NEEDS_DISPATCH_RETAIN_RELEASE
+@property (nonatomic, assign) dispatch_queue_t reachabilitySerialQueue;
+#else
+@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
+#endif
+
+
+@property (nonatomic, strong) id reachabilityObject;
+
+-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
+-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
+
+@end
+
+static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
+{
+ return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
+#if TARGET_OS_IPHONE
+ (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
+#else
+ 'X',
+#endif
+ (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
+ (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
+ (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
+ (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
+ (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
+ (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
+ (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
+ (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
+}
+
+// Start listening for reachability notifications on the current run loop
+static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
+{
+#pragma unused (target)
+#if __has_feature(objc_arc)
+ Reachability *reachability = ((__bridge Reachability*)info);
+#else
+ Reachability *reachability = ((Reachability*)info);
+#endif
+
+ // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
+ // but what the heck eh?
+ @autoreleasepool
+ {
+ [reachability reachabilityChanged:flags];
+ }
+}
+
+
+@implementation Reachability
+
+@synthesize reachabilityRef;
+@synthesize reachabilitySerialQueue;
+
+@synthesize reachableOnWWAN;
+
+@synthesize reachableBlock;
+@synthesize unreachableBlock;
+
+@synthesize reachabilityObject;
+
+#pragma mark - Class Constructor Methods
+
++(Reachability*)reachabilityWithHostName:(NSString*)hostname
+{
+ return [Reachability reachabilityWithHostname:hostname];
+}
+
++(Reachability*)reachabilityWithHostname:(NSString*)hostname
+{
+ SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
+ if (ref)
+ {
+ id reachability = [[self alloc] initWithReachabilityRef:ref];
+
+#if __has_feature(objc_arc)
+ return reachability;
+#else
+ return [reachability autorelease];
+#endif
+
+ }
+
+ return nil;
+}
+
++(Reachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress
+{
+ SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
+ if (ref)
+ {
+ id reachability = [[self alloc] initWithReachabilityRef:ref];
+
+#if __has_feature(objc_arc)
+ return reachability;
+#else
+ return [reachability autorelease];
+#endif
+ }
+
+ return nil;
+}
+
++(Reachability *)reachabilityForInternetConnection
+{
+ struct sockaddr_in zeroAddress;
+ bzero(&zeroAddress, sizeof(zeroAddress));
+ zeroAddress.sin_len = sizeof(zeroAddress);
+ zeroAddress.sin_family = AF_INET;
+
+ return [self reachabilityWithAddress:&zeroAddress];
+}
+
++(Reachability*)reachabilityForLocalWiFi
+{
+ struct sockaddr_in localWifiAddress;
+ bzero(&localWifiAddress, sizeof(localWifiAddress));
+ localWifiAddress.sin_len = sizeof(localWifiAddress);
+ localWifiAddress.sin_family = AF_INET;
+ // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
+ localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
+
+ return [self reachabilityWithAddress:&localWifiAddress];
+}
+
+
+// Initialization methods
+
+-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
+{
+ self = [super init];
+ if (self != nil)
+ {
+ self.reachableOnWWAN = YES;
+ self.reachabilityRef = ref;
+ }
+
+ return self;
+}
+
+-(void)dealloc
+{
+ [self stopNotifier];
+
+ if(self.reachabilityRef)
+ {
+ CFRelease(self.reachabilityRef);
+ self.reachabilityRef = nil;
+ }
+
+ self.reachableBlock = nil;
+ self.unreachableBlock = nil;
+
+#if !(__has_feature(objc_arc))
+ [super dealloc];
+#endif
+
+
+}
+
+#pragma mark - Notifier Methods
+
+// Notifier
+// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
+// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
+// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
+
+-(BOOL)startNotifier
+{
+ SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
+
+ // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
+ // woah
+ self.reachabilityObject = self;
+
+
+
+ // First, we need to create a serial queue.
+ // We allocate this once for the lifetime of the notifier.
+ self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
+ if(self.reachabilitySerialQueue == nil)
+ {
+ return NO;
+ }
+
+#if __has_feature(objc_arc)
+ context.info = (__bridge void *)self;
+#else
+ context.info = (void *)self;
+#endif
+
+ if (!SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
+ {
+#ifdef DEBUG
+ NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
+#endif
+
+ // Clear out the dispatch queue
+ if(self.reachabilitySerialQueue)
+ {
+#if NEEDS_DISPATCH_RETAIN_RELEASE
+ dispatch_release(self.reachabilitySerialQueue);
+#endif
+ self.reachabilitySerialQueue = nil;
+ }
+
+ self.reachabilityObject = nil;
+
+ return NO;
+ }
+
+ // Set it as our reachability queue, which will retain the queue
+ if(!SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
+ {
+#ifdef DEBUG
+ NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
+#endif
+
+ // UH OH - FAILURE!
+
+ // First stop, any callbacks!
+ SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
+
+ // Then clear out the dispatch queue.
+ if(self.reachabilitySerialQueue)
+ {
+#if NEEDS_DISPATCH_RETAIN_RELEASE
+ dispatch_release(self.reachabilitySerialQueue);
+#endif
+ self.reachabilitySerialQueue = nil;
+ }
+
+ self.reachabilityObject = nil;
+
+ return NO;
+ }
+
+ return YES;
+}
+
+-(void)stopNotifier
+{
+ // First stop, any callbacks!
+ SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
+
+ // Unregister target from the GCD serial dispatch queue.
+ SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
+
+ if(self.reachabilitySerialQueue)
+ {
+#if NEEDS_DISPATCH_RETAIN_RELEASE
+ dispatch_release(self.reachabilitySerialQueue);
+#endif
+ self.reachabilitySerialQueue = nil;
+ }
+
+ self.reachabilityObject = nil;
+}
+
+#pragma mark - reachability tests
+
+// This is for the case where you flick the airplane mode;
+// you end up getting something like this:
+//Reachability: WR ct-----
+//Reachability: -- -------
+//Reachability: WR ct-----
+//Reachability: -- -------
+// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
+
+#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
+
+-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
+{
+ BOOL connectionUP = YES;
+
+ if(!(flags & kSCNetworkReachabilityFlagsReachable))
+ connectionUP = NO;
+
+ if( (flags & testcase) == testcase )
+ connectionUP = NO;
+
+#if TARGET_OS_IPHONE
+ if(flags & kSCNetworkReachabilityFlagsIsWWAN)
+ {
+ // We're on 3G.
+ if(!self.reachableOnWWAN)
+ {
+ // We don't want to connect when on 3G.
+ connectionUP = NO;
+ }
+ }
+#endif
+
+ return connectionUP;
+}
+
+-(BOOL)isReachable
+{
+ SCNetworkReachabilityFlags flags;
+
+ if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+ return NO;
+
+ return [self isReachableWithFlags:flags];
+}
+
+-(BOOL)isReachableViaWWAN
+{
+#if TARGET_OS_IPHONE
+
+ SCNetworkReachabilityFlags flags = 0;
+
+ if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ {
+ // Check we're REACHABLE
+ if(flags & kSCNetworkReachabilityFlagsReachable)
+ {
+ // Now, check we're on WWAN
+ if(flags & kSCNetworkReachabilityFlagsIsWWAN)
+ {
+ return YES;
+ }
+ }
+ }
+#endif
+
+ return NO;
+}
+
+-(BOOL)isReachableViaWiFi
+{
+ SCNetworkReachabilityFlags flags = 0;
+
+ if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ {
+ // Check we're reachable
+ if((flags & kSCNetworkReachabilityFlagsReachable))
+ {
+#if TARGET_OS_IPHONE
+ // Check we're NOT on WWAN
+ if((flags & kSCNetworkReachabilityFlagsIsWWAN))
+ {
+ return NO;
+ }
+#endif
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+
+// WWAN may be available, but not active until a connection has been established.
+// WiFi may require a connection for VPN on Demand.
+-(BOOL)isConnectionRequired
+{
+ return [self connectionRequired];
+}
+
+-(BOOL)connectionRequired
+{
+ SCNetworkReachabilityFlags flags;
+
+ if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ {
+ return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
+ }
+
+ return NO;
+}
+
+// Dynamic, on demand connection?
+-(BOOL)isConnectionOnDemand
+{
+ SCNetworkReachabilityFlags flags;
+
+ if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ {
+ return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
+ (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
+ }
+
+ return NO;
+}
+
+// Is user intervention required?
+-(BOOL)isInterventionRequired
+{
+ SCNetworkReachabilityFlags flags;
+
+ if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ {
+ return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
+ (flags & kSCNetworkReachabilityFlagsInterventionRequired));
+ }
+
+ return NO;
+}
+
+
+#pragma mark - reachability status stuff
+
+-(NetworkStatus)currentReachabilityStatus
+{
+ if([self isReachable])
+ {
+ if([self isReachableViaWiFi])
+ return ReachableViaWiFi;
+
+#if TARGET_OS_IPHONE
+ return ReachableViaWWAN;
+#endif
+ }
+
+ return NotReachable;
+}
+
+-(SCNetworkReachabilityFlags)reachabilityFlags
+{
+ SCNetworkReachabilityFlags flags = 0;
+
+ if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
+ {
+ return flags;
+ }
+
+ return 0;
+}
+
+-(NSString*)currentReachabilityString
+{
+ NetworkStatus temp = [self currentReachabilityStatus];
+
+ if(temp == reachableOnWWAN)
+ {
+ // Updated for the fact that we have CDMA phones now!
+ return NSLocalizedString(@"Cellular", @"");
+ }
+ if (temp == ReachableViaWiFi)
+ {
+ return NSLocalizedString(@"WiFi", @"");
+ }
+
+ return NSLocalizedString(@"No Connection", @"");
+}
+
+-(NSString*)currentReachabilityFlags
+{
+ return reachabilityFlags([self reachabilityFlags]);
+}
+
+#pragma mark - Callback function calls this method
+
+-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
+{
+ if([self isReachableWithFlags:flags])
+ {
+ if(self.reachableBlock)
+ {
+ self.reachableBlock(self);
+ }
+ }
+ else
+ {
+ if(self.unreachableBlock)
+ {
+ self.unreachableBlock(self);
+ }
+ }
+
+ // this makes sure the change notification happens on the MAIN THREAD
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification
+ object:self];
+ });
+}
+
+#pragma mark - Debug Description
+
+- (NSString *) description
+{
+ NSString *description = [NSString stringWithFormat:@"<%@: %#x>",
+ NSStringFromClass([self class]), (unsigned int) self];
+ return description;
+}
+
+@end
diff --git a/common/foundation_request.h b/common/foundation_request.h
deleted file mode 100644
index 0b8c4b8fb0..0000000000
--- a/common/foundation_request.h
+++ /dev/null
@@ -1 +0,0 @@
-#import <Foundation/Foundation.h>
diff --git a/common/foundation_request.mm b/common/foundation_request.mm
deleted file mode 100644
index b7eafdb96c..0000000000
--- a/common/foundation_request.mm
+++ /dev/null
@@ -1,115 +0,0 @@
-#import "foundation_request.h"
-
-#include "TargetConditionals.h"
-#if TARGET_OS_IPHONE
-#import <UIKit/UIKit.h>
-#include <atomic>
-#endif
-
-#include <memory>
-#include <string>
-#include <functional>
-#include <mbgl/platform/request.hpp>
-#include <mbgl/platform/platform.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/uv.hpp>
-
-dispatch_once_t request_initialize = 0;
-NSURLSession *session = nullptr;
-
-#if TARGET_OS_IPHONE
-std::atomic<int> active_tasks;
-#endif
-
-
-// We're using a child class to make sure ARC is working correctly, as well as to add activity
-// indicators on iOS.
-class FoundationRequest : public mbgl::platform::Request {
-public:
- FoundationRequest(const std::string &url,
- std::function<void(mbgl::platform::Response *)> callback,
- std::shared_ptr<uv::loop> loop)
- : Request(url, callback, loop) {
-#if TARGET_OS_IPHONE
- active_tasks++;
- dispatch_async(dispatch_get_main_queue(), ^(void) {
- [[UIApplication sharedApplication]
- setNetworkActivityIndicatorVisible:(active_tasks > 0)];
- });
-#endif
- }
-
- ~FoundationRequest() {
-#if TARGET_OS_IPHONE
- active_tasks--;
- dispatch_async(dispatch_get_main_queue(), ^(void) {
- [[UIApplication sharedApplication]
- setNetworkActivityIndicatorVisible:(active_tasks > 0)];
- });
-#endif
- }
-
- NSURLSessionDataTask *task = nullptr;
-};
-
-std::shared_ptr<mbgl::platform::Request>
-mbgl::platform::request_http(const std::string &url,
- std::function<void(Response *)> callback,
- std::shared_ptr<uv::loop> loop) {
- dispatch_once(&request_initialize, ^{
- NSURLSessionConfiguration *sessionConfig =
- [NSURLSessionConfiguration defaultSessionConfiguration];
- sessionConfig.timeoutIntervalForResource = 30;
- sessionConfig.HTTPMaximumConnectionsPerHost = 8;
- sessionConfig.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
-
- session = [NSURLSession sessionWithConfiguration:sessionConfig];
-
-#if TARGET_OS_IPHONE
- active_tasks = 0;
-#endif
- });
-
- std::shared_ptr<FoundationRequest> req =
- std::make_shared<FoundationRequest>(url, callback, loop);
-
- // Note that we are creating a new shared_ptr pointer(!) to make sure there is at least one
- // shared_ptr in existence while the NSURLSession is loading our data. We are making sure in the
- // callback that this pointer gets destroyed again.
- std::shared_ptr<Request> *req_ptr = new std::shared_ptr<Request>(req);
-
- NSURLSessionDataTask *task = [session
- dataTaskWithURL:[NSURL URLWithString:@(url.c_str())]
- completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
- if ([error code] == NSURLErrorCancelled) {
- // We intentionally cancelled this request. Make sure we clear the shared_ptr to resolve
- // the circular reference. We're referencing this shared_ptr by value so that the object
- // stays around until this completion handler is invoked.
- delete req_ptr;
-
- return;
- }
-
- if (!error && [response isKindOfClass:[NSHTTPURLResponse class]]) {
- (*req_ptr)->res->code = [(NSHTTPURLResponse *)response statusCode];
- (*req_ptr)->res->body = {(const char *)[data bytes], [data length]};
- } else {
- (*req_ptr)->res->error_message = [[error localizedDescription] UTF8String];
- }
-
- (*req_ptr)->complete();
-
- delete req_ptr;
- }];
-
- req->task = task;
-
- [task resume];
- return req;
-}
-
-void mbgl::platform::cancel_request_http(const std::shared_ptr<Request> &req) {
- if (req) {
- [((FoundationRequest *)(req.get()))->task cancel];
- }
-}
diff --git a/common/glfw_view.cpp b/common/glfw_view.cpp
index 0908786bb6..120faf4df1 100644
--- a/common/glfw_view.cpp
+++ b/common/glfw_view.cpp
@@ -186,7 +186,9 @@ int GLFWView::run() {
glfwWaitEvents();
}
- map->stop();
+ map->stop([](void *) {
+ glfwWaitEvents();
+ });
return 0;
}
@@ -199,6 +201,10 @@ void GLFWView::make_inactive() {
glfwMakeContextCurrent(nullptr);
}
+void GLFWView::notify() {
+ glfwPostEmptyEvent();
+}
+
void GLFWView::swap() {
glfwPostEmptyEvent();
diff --git a/common/glfw_view.hpp b/common/glfw_view.hpp
index 04085f7750..6e91c1125e 100644
--- a/common/glfw_view.hpp
+++ b/common/glfw_view.hpp
@@ -18,6 +18,7 @@ public:
void swap();
void make_active();
void make_inactive();
+ void notify();
void notify_map_change(mbgl::MapChange change, mbgl::timestamp delay = 0);
static void key(GLFWwindow *window, int key, int scancode, int action, int mods);
diff --git a/common/headless_view.cpp b/common/headless_view.cpp
index 3f945ee6aa..c2084ac90d 100644
--- a/common/headless_view.cpp
+++ b/common/headless_view.cpp
@@ -161,6 +161,10 @@ HeadlessView::~HeadlessView() {
#endif
}
+void HeadlessView::notify() {
+ // no-op
+}
+
void HeadlessView::notify_map_change(mbgl::MapChange /*change*/, mbgl::timestamp /*delay*/) {
// no-op
}
diff --git a/common/headless_view.hpp b/common/headless_view.hpp
index 600e9d51fa..d2fe75382a 100644
--- a/common/headless_view.hpp
+++ b/common/headless_view.hpp
@@ -28,6 +28,7 @@ public:
void resize(uint16_t width, uint16_t height, float pixelRatio);
const std::unique_ptr<uint32_t[]> readPixels();
+ void notify();
void notify_map_change(MapChange change, timestamp delay = 0);
void make_active();
void make_inactive();
diff --git a/common/http_request_baton_cocoa.mm b/common/http_request_baton_cocoa.mm
new file mode 100644
index 0000000000..a9992fad8c
--- /dev/null
+++ b/common/http_request_baton_cocoa.mm
@@ -0,0 +1,149 @@
+#include <mbgl/storage/http_request_baton.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/parsedate.h>
+
+#include <uv.h>
+
+#include <mbgl/util/uv.hpp>
+
+#import <Foundation/Foundation.h>
+#include <ctime>
+#include <xlocale.h>
+
+namespace mbgl {
+
+dispatch_once_t request_initialize = 0;
+NSURLSession *session = nullptr;
+
+void HTTPRequestBaton::start(const util::ptr<HTTPRequestBaton> &ptr) {
+ assert(uv_thread_self() == ptr->thread_id);
+
+ // Starts the request.
+ util::ptr<HTTPRequestBaton> baton = ptr;
+
+ // Create a C locale
+ static locale_t locale = newlocale(LC_ALL_MASK, nullptr, nullptr);
+
+ dispatch_once(&request_initialize, ^{
+ NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
+ sessionConfig.timeoutIntervalForResource = 30;
+ sessionConfig.HTTPMaximumConnectionsPerHost = 8;
+ sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
+ sessionConfig.URLCache = nil;
+
+ session = [NSURLSession sessionWithConfiguration:sessionConfig];
+ });
+
+ 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 time_t modified = baton->response->modified;
+ struct tm *timeinfo = std::gmtime(&modified);
+ char buffer[32];
+ strftime_l(buffer, 32, "%a, %d %b %Y %H:%M:%S GMT", timeinfo, locale);
+ [request addValue:@(buffer) forHTTPHeaderField:@"If-Modified-Since"];
+ }
+ }
+
+ 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 = std::make_unique<Response>();
+ 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 = std::make_unique<Response>();
+ 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 = std::make_unique<Response>();
+ baton->response->code = -1;
+ baton->response->message = "response class is not NSHTTPURLResponse";
+ }
+
+ uv_async_send(baton->async);
+ }];
+
+ [task resume];
+
+ baton->ptr = const_cast<void *>(CFBridgingRetain(task));
+}
+
+void HTTPRequestBaton::stop(const util::ptr<HTTPRequestBaton> &ptr) {
+ assert(uv_thread_self() == ptr->thread_id);
+ assert(ptr->ptr);
+
+ NSURLSessionDataTask *task = CFBridgingRelease(ptr->ptr);
+ ptr->ptr = nullptr;
+ [task cancel];
+}
+
+}
diff --git a/common/http_request_baton_curl.cpp b/common/http_request_baton_curl.cpp
new file mode 100644
index 0000000000..772aeb86e1
--- /dev/null
+++ b/common/http_request_baton_curl.cpp
@@ -0,0 +1,465 @@
+#include <mbgl/storage/http_request_baton.hpp>
+#include <mbgl/util/uv-messenger.h>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <uv.h>
+#include <curl/curl.h>
+
+#include <queue>
+#include <cassert>
+#include <cstring>
+#include <ctime>
+#include <xlocale.h>
+
+// This file contains code from http://curl.haxx.se/libcurl/c/multi-uv.html:
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, 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;
+static uv_messenger_t start_messenger;
+static uv_messenger_t stop_messenger;
+static uv_thread_t thread;
+static unsigned long 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<CURL *> handles;
+
+namespace mbgl {
+
+struct CURLContext {
+ util::ptr<HTTPRequestBaton> baton;
+ uv_poll_t *poll_handle = nullptr;
+ curl_socket_t sockfd = 0;
+ curl_slist *headers = nullptr;
+
+ CURLContext(const util::ptr<HTTPRequestBaton> &baton_) : baton(baton_) {
+ }
+
+ ~CURLContext() {
+ // We are destructing the poll handle in a CURL callback already.
+ assert(!poll_handle);
+
+ // Once the CURLContext gets destroyed, CURL doesn't need any headers anymore.
+ if (headers) {
+ curl_slist_free_all(headers);
+ headers = nullptr;
+ }
+ }
+};
+
+// 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);
+}
+
+// This function must run in the CURL thread.
+// It is either called when the request is completed, or when we try to cancel the request.
+void finish_request(const util::ptr<HTTPRequestBaton> &baton) {
+ assert(uv_thread_self() == thread_id);
+ if (baton->ptr) {
+ CURL *handle = (CURL *)baton->ptr;
+ CURLMcode error = curl_multi_remove_handle(multi, handle);
+ if (error != CURLM_OK) {
+ baton->response = std::make_unique<Response>();
+ baton->response->code = -1;
+ baton->response->message = curl_multi_strerror(error);
+ }
+
+ // Destroy the shared pointer. We still have one pointing to it
+ CURLContext *context = nullptr;
+ curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char *)&context);
+ curl_easy_setopt(handle, CURLOPT_PRIVATE, nullptr);
+ delete context;
+
+ // TODO: delete the headers object again.
+
+ curl_easy_reset(handle);
+ handles.push(handle);
+ baton->ptr = nullptr;
+ }
+}
+
+void curl_perform(uv_poll_t *req, int, int events) {
+ int running_handles;
+ int flags = 0;
+ CURLContext *context = (CURLContext *)req->data;
+ CURLMsg *message;
+ int pending;
+
+ uv_timer_stop(&timeout);
+
+ if (events & UV_READABLE) {
+ flags |= CURL_CSELECT_IN;
+ }
+ if (events & UV_WRITABLE) {
+ flags |= CURL_CSELECT_OUT;
+ }
+
+ curl_multi_socket_action(multi, context->sockfd, flags, &running_handles);
+
+ while ((message = curl_multi_info_read(multi, &pending))) {
+ switch (message->msg) {
+ case CURLMSG_DONE: {
+ CURLContext *context = nullptr;
+ curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&context);
+
+ // Make a copy so that the Baton stays around even after we are calling finish_request
+ util::ptr<HTTPRequestBaton> baton = context->baton;
+
+ // 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");
+ }
+ }
+
+ // We're currently in the CURL request thread.
+ finish_request(baton);
+
+ if (baton->async) {
+ uv_async_send(baton->async);
+ baton->async = nullptr;
+ }
+
+ break;
+ }
+
+ default:
+ // This should never happen, because there are no other message types.
+ throw std::runtime_error("CURLMSG returned unknown message type");
+ }
+ }
+}
+
+int handle_socket(CURL *handle, curl_socket_t sockfd, int action, void *, void *socketp) {
+ CURLContext *context = nullptr;
+ curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char *)&context);
+
+ if (!socketp && action != CURL_POLL_REMOVE) {
+ // We haven't initialized the socket yet, so we need to do this now.
+ assert(context->sockfd == 0);
+ context->sockfd = sockfd;
+ assert(!context->poll_handle);
+ context->poll_handle = new uv_poll_t;
+ uv_poll_init_socket(&loop, context->poll_handle, sockfd);
+ context->poll_handle->data = context;
+ curl_multi_assign(multi, sockfd, context);
+ }
+
+ if (context) {
+ if (action == CURL_POLL_IN || action == CURL_POLL_INOUT) {
+ uv_poll_start(context->poll_handle, UV_READABLE, curl_perform);
+ }
+ if (action == CURL_POLL_OUT || action == CURL_POLL_INOUT) {
+ uv_poll_start(context->poll_handle, UV_WRITABLE, curl_perform);
+ }
+ if (action == CURL_POLL_REMOVE && socketp) {
+ uv_poll_stop(context->poll_handle);
+ uv_close((uv_handle_t *)context->poll_handle, [](uv_handle_t *handle) {
+ delete (uv_poll_t *)handle;
+ });
+ context->poll_handle = nullptr;
+ curl_multi_assign(multi, sockfd, NULL);
+ }
+ }
+
+ return 0;
+}
+
+void on_timeout(uv_timer_t *) {
+ 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));
+ }
+}
+
+void start_timeout(CURLM *, long timeout_ms, void *) {
+ if (timeout_ms <= 0) {
+ on_timeout(&timeout);
+ } else {
+ uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
+ }
+}
+
+void thread_init(void *) {
+#ifdef __APPLE__
+ pthread_setname_np("CURL");
+#endif
+ thread_id = uv_thread_self();
+
+ 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 = -1;
+}
+
+// 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) {
+ ((std::string *)userp)->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;
+
+ Response *response = static_cast<Response *>(userp);
+
+ 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 must run in the CURL thread.
+void start_request(void *const ptr) {
+ assert(uv_thread_self() == thread_id);
+ std::unique_ptr<util::ptr<HTTPRequestBaton>> baton_guard { (util::ptr<HTTPRequestBaton> *)ptr };
+ util::ptr<HTTPRequestBaton> &baton = *baton_guard.get();
+ assert(baton);
+
+ // Create a C locale
+ static locale_t locale = newlocale(LC_ALL_MASK, nullptr, nullptr);
+
+ CURL *handle = nullptr;
+ if (!handles.empty()) {
+ handle = handles.front();
+ handles.pop();
+ } else {
+ handle = curl_easy_init();
+ }
+
+ baton->ptr = handle;
+
+ // Wrap this in a unique_ptr for now so that it destructs until we assign it the the CURL handle.
+ std::unique_ptr<CURLContext> context = std::make_unique<CURLContext>(baton);
+
+ if (baton->response) {
+ if (!baton->response->etag.empty()) {
+ const std::string header = std::string("If-None-Match: ") + baton->response->etag;
+ context->headers = curl_slist_append(context->headers, header.c_str());
+ } else if (baton->response->modified) {
+ const time_t modified = baton->response->modified;
+ struct tm *timeinfo = std::gmtime(&modified);
+ char buffer[64];
+ strftime_l(buffer, 64, "If-Modified-Since: %a, %d %b %Y %H:%M:%S GMT", timeinfo, locale);
+ context->headers = curl_slist_append(context->headers, buffer);
+ }
+ }
+
+ if (context->headers) {
+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, context->headers);
+ }
+
+ if (!baton->response) {
+ baton->response = std::make_unique<Response>();
+ }
+
+ // Carry on the shared pointer in the private information of the CURL handle.
+ curl_easy_setopt(handle, CURLOPT_PRIVATE, context.release());
+ curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt");
+ curl_easy_setopt(handle, CURLOPT_URL, baton->path.c_str());
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, &baton->response->data);
+ curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
+ curl_easy_setopt(handle, CURLOPT_HEADERDATA, baton->response.get());
+ curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");
+ curl_easy_setopt(handle, CURLOPT_SHARE, share);
+
+ // Start requesting the information.
+ curl_multi_add_handle(multi, handle);
+}
+
+// This function must run in the CURL thread.
+void stop_request(void *const ptr) {
+ assert(uv_thread_self() == thread_id);
+ std::unique_ptr<util::ptr<HTTPRequestBaton>> baton_guard { (util::ptr<HTTPRequestBaton> *)ptr };
+ util::ptr<HTTPRequestBaton> &baton = *baton_guard.get();
+ assert(baton);
+
+ if (baton->async) {
+ baton->type = HTTPResponseType::Canceled;
+
+ // We can still stop the request because it is still in progress.
+ finish_request(baton);
+
+ uv_async_send(baton->async);
+ baton->async = nullptr;
+ } 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);
+ uv_loop_init(&loop);
+ 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<HTTPRequestBaton> &ptr) {
+ assert(uv_thread_self() == ptr->thread_id);
+ uv_once(&once, create_thread);
+ uv_messenger_send(&start_messenger, new util::ptr<HTTPRequestBaton>(ptr));
+}
+
+// This function must be run from the main thread (== where the HTTPRequestBaton was created)
+void HTTPRequestBaton::stop(const util::ptr<HTTPRequestBaton> &ptr) {
+ assert(uv_thread_self() == ptr->thread_id);
+ uv_once(&once, create_thread);
+ uv_messenger_send(&stop_messenger, new util::ptr<HTTPRequestBaton>(ptr));
+}
+
+}
diff --git a/common/ios.mm b/common/ios.mm
new file mode 100644
index 0000000000..7989e73a4e
--- /dev/null
+++ b/common/ios.mm
@@ -0,0 +1,21 @@
+#import <Foundation/Foundation.h>
+
+#include <mbgl/platform/platform.hpp>
+
+namespace mbgl {
+namespace platform {
+
+// Returns the path to the default cache database on this system.
+std::string defaultCacheDatabase() {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+ if ([paths count] == 0) {
+ // Disable the cache if we don't have a location to write.
+ return "";
+ }
+
+ NSString *libraryDirectory = [paths objectAtIndex:0];
+ return [[libraryDirectory stringByAppendingPathComponent:@"cache.db"] UTF8String];
+}
+
+}
+}
diff --git a/common/linux.cpp b/common/linux.cpp
new file mode 100644
index 0000000000..6132ace692
--- /dev/null
+++ b/common/linux.cpp
@@ -0,0 +1,12 @@
+#include <mbgl/platform/platform.hpp>
+
+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/common/osx.mm b/common/osx.mm
new file mode 100644
index 0000000000..974ea537cb
--- /dev/null
+++ b/common/osx.mm
@@ -0,0 +1,31 @@
+#import <Foundation/Foundation.h>
+
+#include <mbgl/platform/platform.hpp>
+
+namespace mbgl {
+namespace platform {
+
+// Returns the path to the default cache database on this system.
+std::string defaultCacheDatabase() {
+ NSArray *paths =
+ NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
+ if ([paths count] == 0) {
+ // Disable the cache if we don't have a location to write.
+ return "";
+ }
+
+ NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Mapbox GL"];
+
+ if (![[NSFileManager defaultManager] createDirectoryAtPath:path
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:nil]) {
+ // Disable the cache if we couldn't create the directory.
+ return "";
+ }
+
+ return [[path stringByAppendingPathComponent:@"cache.db"] UTF8String];
+}
+
+}
+}
diff --git a/configure b/configure
index e8f880b543..b92f97758b 100755
--- a/configure
+++ b/configure
@@ -107,6 +107,14 @@ def configure_curl(o):
o['variables']['curl_libraries'] = ret[0].split()
o['variables']['curl_cflags'] = ret[1].split()
+def configure_sqlite3(o):
+ ret = pkg_config('sqlite3', options.pkgconfig_root)
+ if not ret:
+ sys.stderr.write('could not find sqlite3 with pkg-config')
+ sys.exit(-1)
+ o['variables']['sqlite3_libraries'] = ret[0].split()
+ o['variables']['sqlite3_cflags'] = ret[1].split()
+
def write(filename, data):
filename = os.path.join(root_dir, filename)
print "creating ", filename
@@ -129,6 +137,7 @@ if __name__ == '__main__':
configure_uv(output)
configure_png(output)
configure_curl(output)
+ configure_sqlite3(output)
pprint.pprint(output, indent=2)
write('config.gypi', "# Do not edit. Generated by the configure script.\n" +
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index ac33dba0ef..7e4687ea6f 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -7,11 +7,11 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/uv.hpp>
+#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <atomic>
#include <iosfwd>
-#include <memory>
#include <set>
#include <vector>
@@ -31,21 +31,31 @@ class FileSource;
class View;
class Map : private util::noncopyable {
+ typedef void (*stop_callback)(void *);
+
public:
explicit Map(View &view);
~Map();
- // Start/stop the map render thread
+ // Start the map render thread. It is asynchronous.
void start();
- void stop();
- // Runs the map event loop.
+ // Stop the map render thread. This call will block until the map rendering thread stopped.
+ // The optional callback function will be invoked repeatedly until the map thread is stopped.
+ // The callback function should wait until it is woken up again by view.notify(), otherwise
+ // this will be a busy waiting loop. The optional data parameter will be passed to the callback
+ // function.
+ void stop(stop_callback cb = nullptr, void *data = nullptr);
+
+ // Runs the map event loop. ONLY run this function when you want to get render a single frame
+ // with this map object. It will *not* spawn a separate thread and instead block until the
+ // frame is completely rendered.
void run();
// Triggers a lazy rerender: only performs a render when the map is not clean.
void rerender();
- void renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id = nullptr, const mat4* matrix = nullptr);
+ void renderLayer(util::ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id = nullptr, const mat4* matrix = nullptr);
// Forces a map update: always triggers a rerender.
void update();
@@ -65,7 +75,7 @@ public:
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height);
// Styling
- const std::set<std::shared_ptr<StyleSource>> getActiveSources() const;
+ const std::set<util::ptr<StyleSource>> getActiveSources() const;
void setAppliedClasses(const std::vector<std::string> &classes);
void toggleClass(const std::string &name);
const std::vector<std::string> &getAppliedClasses() const;
@@ -116,16 +126,20 @@ public:
void toggleDebug();
bool getDebug() const;
+ // Call this when the network reachability changed.
+ void setReachability(bool status);
+
public:
inline const TransformState &getState() const { return state; }
- inline std::shared_ptr<FileSource> getFileSource() const { return fileSource; }
- inline std::shared_ptr<Style> getStyle() const { return style; }
- inline std::shared_ptr<GlyphAtlas> getGlyphAtlas() { return glyphAtlas; }
- inline std::shared_ptr<GlyphStore> getGlyphStore() { return glyphStore; }
- inline std::shared_ptr<SpriteAtlas> getSpriteAtlas() { return spriteAtlas; }
- std::shared_ptr<Sprite> getSprite();
- inline std::shared_ptr<Texturepool> getTexturepool() { return texturepool; }
- inline std::shared_ptr<uv::loop> getLoop() { return loop; }
+ inline util::ptr<FileSource> getFileSource() const { return fileSource; }
+ inline util::ptr<Style> getStyle() const { return style; }
+ inline util::ptr<GlyphAtlas> getGlyphAtlas() { return glyphAtlas; }
+ inline util::ptr<GlyphStore> getGlyphStore() { return glyphStore; }
+ inline util::ptr<SpriteAtlas> getSpriteAtlas() { return spriteAtlas; }
+ util::ptr<Sprite> getSprite();
+ inline util::ptr<Texturepool> getTexturepool() { return texturepool; }
+ inline util::ptr<uv::loop> getLoop() { return loop; }
+ uv::worker &getWorker();
inline timestamp getAnimationTime() const { return animationTime; }
inline timestamp getTime() const { return animationTime; }
void updateTiles();
@@ -141,7 +155,7 @@ private:
void setup();
void updateSources();
- void updateSources(const std::shared_ptr<StyleLayerGroup> &group);
+ void updateSources(const util::ptr<StyleLayerGroup> &group);
void updateRenderState();
@@ -154,15 +168,16 @@ private:
// Unconditionally performs a render with the current map state.
void render();
- void renderLayers(std::shared_ptr<StyleLayerGroup> group);
+ void renderLayers(util::ptr<StyleLayerGroup> group);
private:
bool async = false;
- std::shared_ptr<uv::loop> loop;
+ util::ptr<uv::loop> loop;
+ std::unique_ptr<uv::worker> workers;
std::unique_ptr<uv::thread> thread;
- uv_async_t *async_terminate = nullptr;
- uv_async_t *async_render = nullptr;
- uv_async_t *async_cleanup = nullptr;
+ std::unique_ptr<uv_async_t> async_terminate;
+ std::unique_ptr<uv_async_t> async_render;
+ std::unique_ptr<uv_async_t> async_cleanup;
private:
// If cleared, the next time the render thread attempts to render the map, it will *actually*
@@ -177,24 +192,33 @@ private:
// ready for rendering.
std::atomic_flag is_rendered = ATOMIC_FLAG_INIT;
+ // Stores whether the map thread has been stopped already.
+ std::atomic_bool is_stopped;
+
public:
View &view;
private:
+#ifndef NDEBUG
+ const unsigned long main_thread;
+ unsigned long map_thread = -1;
+#endif
+
Transform transform;
TransformState state;
- std::shared_ptr<FileSource> fileSource;
+ util::ptr<FileSource> fileSource;
- std::shared_ptr<Style> style;
- std::shared_ptr<GlyphAtlas> glyphAtlas;
- std::shared_ptr<GlyphStore> glyphStore;
- std::shared_ptr<SpriteAtlas> spriteAtlas;
- std::shared_ptr<Sprite> sprite;
- std::shared_ptr<Texturepool> texturepool;
+ util::ptr<Style> style;
+ util::ptr<GlyphAtlas> glyphAtlas;
+ util::ptr<GlyphStore> glyphStore;
+ util::ptr<SpriteAtlas> spriteAtlas;
+ util::ptr<Sprite> sprite;
+ util::ptr<Texturepool> texturepool;
Painter painter;
+ std::string styleURL;
std::string styleJSON = "";
std::string accessToken = "";
@@ -203,7 +227,7 @@ private:
int indent = 0;
- std::set<std::shared_ptr<StyleSource>> activeSources;
+ std::set<util::ptr<StyleSource>> activeSources;
};
diff --git a/include/mbgl/map/raster_tile_data.hpp b/include/mbgl/map/raster_tile_data.hpp
index 98aa3baaf5..14833c0d84 100644
--- a/include/mbgl/map/raster_tile_data.hpp
+++ b/include/mbgl/map/raster_tile_data.hpp
@@ -5,8 +5,6 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
-#include <memory>
-
namespace mbgl {
class Map;
@@ -18,12 +16,12 @@ class RasterTileData : public TileData {
friend class TileParser;
public:
- RasterTileData(Tile::ID id, Map &map, const SourceInfo &source);
+ RasterTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source);
~RasterTileData();
virtual void parse();
- virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix);
- virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const;
+ virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
+ virtual bool hasData(util::ptr<StyleLayer> layer_desc) const;
protected:
StyleBucketRaster properties;
diff --git a/include/mbgl/map/source.hpp b/include/mbgl/map/source.hpp
index cb069db272..e74ddb9902 100644
--- a/include/mbgl/map/source.hpp
+++ b/include/mbgl/map/source.hpp
@@ -8,12 +8,12 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <forward_list>
#include <iosfwd>
#include <map>
-#include <memory>
namespace mbgl {
@@ -25,7 +25,7 @@ struct box;
class Source : public std::enable_shared_from_this<Source>, private util::noncopyable {
public:
- Source(SourceInfo& info);
+ Source(const util::ptr<SourceInfo>& info);
void load(Map &map);
@@ -33,8 +33,8 @@ public:
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
size_t getTileCount() const;
- void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc);
- void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
+ void render(Painter &painter, util::ptr<StyleLayer> layer_desc);
+ void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
void finishRender(Painter &painter);
std::forward_list<Tile::ID> getIDs() const;
@@ -54,7 +54,7 @@ private:
double getZoom(const TransformState &state) const;
- SourceInfo& info;
+ util::ptr<SourceInfo> info;
bool loaded = false;
// Stores the time when this source was most recently updated.
diff --git a/include/mbgl/map/sprite.hpp b/include/mbgl/map/sprite.hpp
index 6461a5e33d..454ebd8886 100644
--- a/include/mbgl/map/sprite.hpp
+++ b/include/mbgl/map/sprite.hpp
@@ -3,11 +3,11 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <atomic>
#include <iosfwd>
-#include <memory>
#include <string>
#include <unordered_map>
#include <future>
@@ -34,11 +34,11 @@ public:
class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
private:
struct Key {};
- void load(const std::shared_ptr<FileSource> &fileSource);
+ void load(const util::ptr<FileSource> &fileSource);
public:
Sprite(const Key &, const std::string& base_url, float pixelRatio);
- static std::shared_ptr<Sprite> Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource);
+ static util::ptr<Sprite> Create(const std::string& base_url, float pixelRatio, const util::ptr<FileSource> &fileSource);
const SpritePosition &getSpritePosition(const std::string& name) const;
diff --git a/include/mbgl/map/tile.hpp b/include/mbgl/map/tile.hpp
index 75ced7e384..b9f0556add 100644
--- a/include/mbgl/map/tile.hpp
+++ b/include/mbgl/map/tile.hpp
@@ -3,6 +3,7 @@
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <bitset>
@@ -10,7 +11,6 @@
#include <cstdint>
#include <forward_list>
#include <iosfwd>
-#include <memory>
#include <string>
namespace mbgl {
@@ -78,7 +78,7 @@ public:
const Tile::ID id;
ClipID clip;
mat4 matrix;
- std::shared_ptr<TileData> data;
+ util::ptr<TileData> data;
};
}
diff --git a/include/mbgl/map/tile_data.hpp b/include/mbgl/map/tile_data.hpp
index 9aaef84e04..07cf19c5c8 100644
--- a/include/mbgl/map/tile_data.hpp
+++ b/include/mbgl/map/tile_data.hpp
@@ -6,11 +6,11 @@
#include <mbgl/geometry/debug_font_buffer.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/ptr.hpp>
#include <atomic>
#include <exception>
#include <iosfwd>
-#include <memory>
#include <string>
namespace mbgl {
@@ -19,8 +19,7 @@ class Map;
class Painter;
class SourceInfo;
class StyleLayer;
-
-namespace platform { class Request; }
+class Request;
class TileData : public std::enable_shared_from_this<TileData>,
private util::noncopyable {
@@ -29,7 +28,7 @@ public:
struct geometry_too_long_exception : exception {};
public:
- typedef std::shared_ptr<TileData> Ptr;
+ typedef util::ptr<TileData> Ptr;
enum class State {
invalid,
@@ -41,7 +40,7 @@ public:
};
public:
- TileData(Tile::ID id, Map &map, const SourceInfo &source);
+ TileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source);
~TileData();
void request();
@@ -57,8 +56,8 @@ public:
virtual void beforeParse();
virtual void parse() = 0;
virtual void afterParse();
- virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) = 0;
- virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const = 0;
+ virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) = 0;
+ virtual bool hasData(util::ptr<StyleLayer> layer_desc) const = 0;
public:
@@ -69,10 +68,10 @@ protected:
Map &map;
public:
- const SourceInfo &source;
+ util::ptr<SourceInfo> source;
protected:
- std::weak_ptr<platform::Request> req;
+ std::unique_ptr<Request> req;
std::string data;
// Contains the tile ID string for painting debug information.
diff --git a/include/mbgl/map/tile_parser.hpp b/include/mbgl/map/tile_parser.hpp
index fa64dad6d5..ddb4b98820 100644
--- a/include/mbgl/map/tile_parser.hpp
+++ b/include/mbgl/map/tile_parser.hpp
@@ -4,10 +4,10 @@
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/style/filter_expression.hpp>
#include <mbgl/text/glyph.hpp>
+#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <iosfwd>
-#include <memory>
#include <string>
namespace mbgl {
@@ -32,11 +32,11 @@ class Collision;
class TileParser {
public:
TileParser(const std::string &data, VectorTileData &tile,
- const std::shared_ptr<const Style> &style,
- const std::shared_ptr<GlyphAtlas> &glyphAtlas,
- const std::shared_ptr<GlyphStore> &glyphStore,
- const std::shared_ptr<SpriteAtlas> &spriteAtlas,
- const std::shared_ptr<Sprite> &sprite);
+ const util::ptr<const Style> &style,
+ const util::ptr<GlyphAtlas> &glyphAtlas,
+ const util::ptr<GlyphStore> &glyphStore,
+ const util::ptr<SpriteAtlas> &spriteAtlas,
+ const util::ptr<Sprite> &sprite);
~TileParser();
public:
@@ -44,11 +44,11 @@ public:
private:
bool obsolete() const;
- void parseStyleLayers(std::shared_ptr<StyleLayerGroup> group);
- std::unique_ptr<Bucket> createBucket(std::shared_ptr<StyleBucket> bucket_desc);
+ void parseStyleLayers(util::ptr<StyleLayerGroup> group);
+ std::unique_ptr<Bucket> createBucket(util::ptr<StyleBucket> bucket_desc);
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill);
- std::unique_ptr<Bucket> createRasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster &raster);
+ std::unique_ptr<Bucket> createRasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster &raster);
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line);
std::unique_ptr<Bucket> createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol);
@@ -59,12 +59,12 @@ private:
VectorTileData& tile;
// Cross-thread shared data.
- std::shared_ptr<const Style> style;
- std::shared_ptr<GlyphAtlas> glyphAtlas;
- std::shared_ptr<GlyphStore> glyphStore;
- std::shared_ptr<SpriteAtlas> spriteAtlas;
- std::shared_ptr<Sprite> sprite;
- std::shared_ptr<Texturepool> texturePool;
+ util::ptr<const Style> style;
+ util::ptr<GlyphAtlas> glyphAtlas;
+ util::ptr<GlyphStore> glyphStore;
+ util::ptr<SpriteAtlas> spriteAtlas;
+ util::ptr<Sprite> sprite;
+ util::ptr<Texturepool> texturePool;
std::unique_ptr<Collision> collision;
};
diff --git a/include/mbgl/map/transform.hpp b/include/mbgl/map/transform.hpp
index d0f56b7fba..5404f333c1 100644
--- a/include/mbgl/map/transform.hpp
+++ b/include/mbgl/map/transform.hpp
@@ -98,10 +98,10 @@ private:
// cache values for spherical mercator math
double Bc, Cc;
- std::forward_list<std::shared_ptr<util::transition>> transitions;
- std::shared_ptr<util::transition> scale_timeout;
- std::shared_ptr<util::transition> rotate_timeout;
- std::shared_ptr<util::transition> pan_timeout;
+ std::forward_list<util::ptr<util::transition>> transitions;
+ util::ptr<util::transition> scale_timeout;
+ util::ptr<util::transition> rotate_timeout;
+ util::ptr<util::transition> pan_timeout;
};
}
diff --git a/include/mbgl/map/vector_tile_data.hpp b/include/mbgl/map/vector_tile_data.hpp
index deb628e485..9de666c84f 100644
--- a/include/mbgl/map/vector_tile_data.hpp
+++ b/include/mbgl/map/vector_tile_data.hpp
@@ -26,14 +26,14 @@ class VectorTileData : public TileData {
friend class TileParser;
public:
- VectorTileData(Tile::ID id, Map &map, const SourceInfo &source);
+ VectorTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source);
~VectorTileData();
virtual void beforeParse();
virtual void parse();
virtual void afterParse();
- virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix);
- virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const;
+ virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
+ virtual bool hasData(util::ptr<StyleLayer> layer_desc) const;
protected:
// Holds the actual geometries in this tile.
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
index 3e2f1a4b5a..395a05d435 100644
--- a/include/mbgl/map/view.hpp
+++ b/include/mbgl/map/view.hpp
@@ -45,6 +45,8 @@ public:
return 0;
}
+ virtual void notify() = 0;
+
// 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.
diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp
index 1676f40d2c..d2a06618a3 100644
--- a/include/mbgl/platform/event.hpp
+++ b/include/mbgl/platform/event.hpp
@@ -29,6 +29,7 @@ enum class Event : uint8_t {
ParseStyle,
ParseTile,
Render,
+ Database,
HttpRequest,
Sprite,
};
@@ -40,6 +41,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, {
{ Event::ParseStyle, "ParseStyle" },
{ Event::ParseTile, "ParseTile" },
{ Event::Render, "Render" },
+ { Event::Database, "Database" },
{ Event::HttpRequest, "HttpRequest" },
{ Event::Sprite, "Sprite" },
{ Event(-1), "Unknown" },
diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp
index 22405a4cfd..b7107bb9cd 100644
--- a/include/mbgl/platform/platform.hpp
+++ b/include/mbgl/platform/platform.hpp
@@ -4,7 +4,6 @@
#include <mbgl/util/uv.hpp>
#include <memory>
-#include <functional>
#include <string>
namespace mbgl {
@@ -12,30 +11,14 @@ namespace platform {
class Request;
-struct Response {
- Response(std::function<void(Response *)> callback) : callback(callback) {}
- int16_t code = -1;
- std::string body;
- std::string error_message;
- std::function<void(Response *)> callback;
-};
-
-// Makes an HTTP request of a URL, preferrably on a background thread, and calls a function with the
-// results in the original thread (which runs the libuv loop).
-// If the loop pointer is NULL, the callback function will be called on an arbitrary thread.
-// Returns a cancellable request.
-std::shared_ptr<Request> request_http(const std::string &url,
- std::function<void(Response *)> callback,
- std::shared_ptr<uv::loop> loop = nullptr);
-
// Uppercase a string, potentially using platform-specific routines.
std::string uppercase(const std::string &string);
// Lowercase a string, potentially using platform-specific routines.
std::string lowercase(const std::string &string);
-// Cancels an HTTP request.
-void cancel_request_http(const std::shared_ptr<Request> &req);
+// Returns the path to the default cache database on this system.
+std::string defaultCacheDatabase();
// Shows an alpha image with the specified dimensions in a named window.
void show_debug_image(std::string name, const char *data, size_t width, size_t height);
diff --git a/include/mbgl/platform/request.hpp b/include/mbgl/platform/request.hpp
deleted file mode 100644
index 2a231769c1..0000000000
--- a/include/mbgl/platform/request.hpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef MBGL_PLATFORM_REQUEST
-#define MBGL_PLATFORM_REQUEST
-
-#include <string>
-#include <functional>
-#include <memory>
-#include <atomic>
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/uv.hpp>
-
-namespace mbgl {
-namespace platform {
-
-struct Response;
-
-class Request : public std::enable_shared_from_this<Request>, private util::noncopyable {
-public:
- Request(const std::string &url,
- std::function<void(Response *)> callback,
- std::shared_ptr<uv::loop> loop);
- ~Request();
-
- void complete();
-
-private:
- static void complete(uv_async_t *async, int status);
-
-public:
- const std::string url;
- std::unique_ptr<Response> res;
- std::atomic<bool> cancelled;
-
-public:
- uv_async_t *async = nullptr;
- std::shared_ptr<uv::loop> loop;
-};
-}
-}
-
-#endif
diff --git a/include/mbgl/renderer/bucket.hpp b/include/mbgl/renderer/bucket.hpp
index 1391f6e3e3..696bfb1110 100644
--- a/include/mbgl/renderer/bucket.hpp
+++ b/include/mbgl/renderer/bucket.hpp
@@ -1,11 +1,11 @@
#ifndef MBGL_RENDERER_BUCKET
#define MBGL_RENDERER_BUCKET
-#include <string>
-#include <memory>
#include <mbgl/map/tile.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <string>
+
namespace mbgl {
class Painter;
@@ -13,7 +13,7 @@ class StyleLayer;
class Bucket : private util::noncopyable {
public:
- virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) = 0;
+ virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) = 0;
virtual bool hasData() const = 0;
virtual ~Bucket() {}
diff --git a/include/mbgl/renderer/debug_bucket.hpp b/include/mbgl/renderer/debug_bucket.hpp
index 660b7fcba8..fb6cfb4cae 100644
--- a/include/mbgl/renderer/debug_bucket.hpp
+++ b/include/mbgl/renderer/debug_bucket.hpp
@@ -6,7 +6,6 @@
#include <mbgl/geometry/vao.hpp>
#include <vector>
-#include <memory>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
@@ -20,7 +19,7 @@ class DebugBucket : public Bucket {
public:
DebugBucket(DebugFontBuffer& fontBuffer);
- virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
void drawLines(PlainShader& shader);
diff --git a/include/mbgl/renderer/fill_bucket.hpp b/include/mbgl/renderer/fill_bucket.hpp
index 15868e4092..ae766ec28d 100644
--- a/include/mbgl/renderer/fill_bucket.hpp
+++ b/include/mbgl/renderer/fill_bucket.hpp
@@ -44,7 +44,7 @@ public:
const StyleBucketFill& properties);
~FillBucket();
- virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
void addGeometry(pbf& data);
diff --git a/include/mbgl/renderer/line_bucket.hpp b/include/mbgl/renderer/line_bucket.hpp
index 8babb734ed..7337ca80ad 100644
--- a/include/mbgl/renderer/line_bucket.hpp
+++ b/include/mbgl/renderer/line_bucket.hpp
@@ -1,13 +1,12 @@
#ifndef MBGL_RENDERER_LINEBUCKET
#define MBGL_RENDERER_LINEBUCKET
-#include "bucket.hpp"
+#include <mbgl/renderer/bucket.hpp>
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/line_buffer.hpp>
#include <mbgl/style/style_bucket.hpp>
-#include <memory>
#include <vector>
namespace mbgl {
@@ -30,7 +29,7 @@ public:
PointElementsBuffer& pointElementsBuffer,
const StyleBucketLine& properties);
- virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
void addGeometry(pbf& data);
diff --git a/include/mbgl/renderer/painter.hpp b/include/mbgl/renderer/painter.hpp
index 15331b9b67..0f9bd79173 100644
--- a/include/mbgl/renderer/painter.hpp
+++ b/include/mbgl/renderer/painter.hpp
@@ -22,6 +22,7 @@
#include <mbgl/shader/gaussian_shader.hpp>
#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/ptr.hpp>
#include <map>
#include <unordered_map>
@@ -73,7 +74,7 @@ public:
void changeMatrix();
// Renders a particular layer from a tile.
- void renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix);
+ void renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
// Renders debug information for a tile.
void renderTileDebug(const Tile& tile);
@@ -83,11 +84,11 @@ public:
void renderDebugText(DebugBucket& bucket, const mat4 &matrix);
void renderDebugText(const std::vector<std::string> &strings);
- void renderFill(FillBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderSymbol(SymbolBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
- void renderBackground(std::shared_ptr<StyleLayer> layer_desc);
+ void renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderSymbol(SymbolBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ void renderBackground(util::ptr<StyleLayer> layer_desc);
float saturationFactor(float saturation);
float contrastFactor(float contrast);
@@ -97,7 +98,7 @@ public:
void renderPrerenderedTexture(RasterBucket &bucket, const mat4 &matrix, const RasterProperties& properties);
- void createPrerendered(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
+ void createPrerendered(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id);
void resize();
@@ -111,7 +112,7 @@ public:
// Configures the painter strata that is used for early z-culling of fragments.
void setStrata(float strata);
- void drawClippingMasks(const std::set<std::shared_ptr<StyleSource>> &sources);
+ void drawClippingMasks(const std::set<util::ptr<StyleSource>> &sources);
void drawClippingMask(const mat4& matrix, const ClipID& clip);
void resetFramebuffer();
diff --git a/include/mbgl/renderer/raster_bucket.hpp b/include/mbgl/renderer/raster_bucket.hpp
index 66cceac8e7..794da3b143 100644
--- a/include/mbgl/renderer/raster_bucket.hpp
+++ b/include/mbgl/renderer/raster_bucket.hpp
@@ -16,9 +16,9 @@ class VertexArrayObject;
class RasterBucket : public Bucket {
public:
- RasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster& properties);
+ RasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster& properties);
- virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
+ virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
bool setImage(const std::string &data);
diff --git a/include/mbgl/renderer/symbol_bucket.hpp b/include/mbgl/renderer/symbol_bucket.hpp
index c71d276456..42682401ef 100644
--- a/include/mbgl/renderer/symbol_bucket.hpp
+++ b/include/mbgl/renderer/symbol_bucket.hpp
@@ -56,7 +56,7 @@ class SymbolBucket : public Bucket {
public:
SymbolBucket(const StyleBucketSymbol &properties, Collision &collision);
- virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
+ virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
virtual bool hasData() const;
virtual bool hasTextData() const;
virtual bool hasIconData() const;
diff --git a/include/mbgl/storage/base_request.hpp b/include/mbgl/storage/base_request.hpp
new file mode 100644
index 0000000000..16ff24faa3
--- /dev/null
+++ b/include/mbgl/storage/base_request.hpp
@@ -0,0 +1,62 @@
+#ifndef MBGL_STORAGE_BASE_REQUEST
+#define MBGL_STORAGE_BASE_REQUEST
+
+#include <mbgl/storage/request_callback.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <string>
+#include <forward_list>
+#include <functional>
+
+
+typedef struct uv_loop_s uv_loop_t;
+typedef struct uv_async_s uv_async_t;
+
+namespace mbgl {
+
+class Response;
+class Request;
+
+class BaseRequest {
+private:
+ // Make noncopyable and immovable
+ BaseRequest(const BaseRequest &) = delete;
+ BaseRequest(BaseRequest &&) = delete;
+ BaseRequest& operator=(const BaseRequest &) = delete;
+ BaseRequest& operator=(BaseRequest &&) = delete;
+
+public:
+ BaseRequest(const std::string &path);
+ virtual ~BaseRequest();
+
+ Callback *add(Callback &&callback, const util::ptr<BaseRequest> &request);
+ void remove(Callback *callback);
+
+ // Must be called by subclasses when a valid Response object is available. It will notify
+ // all listeners.
+ void notify();
+
+ // This function is called when the request ought to be stopped. Any subclass must make sure this
+ // is also called in its destructor. Calling this function repeatedly must be safe.
+ // This function must call notify().
+ virtual void cancel() = 0;
+
+ // This function is called when the request should be reattempted immediately. This is typically
+ // reaction to a network status change.
+ virtual void retryImmediately();
+
+public:
+ const unsigned long thread_id;
+ const std::string path;
+ std::unique_ptr<Response> response;
+
+protected:
+ // This object may hold a shared_ptr to itself. It does this to prevent destruction of this object
+ // while a request is in progress.
+ util::ptr<BaseRequest> self;
+ std::forward_list<std::unique_ptr<Callback>> callbacks;
+};
+
+}
+
+#endif
diff --git a/include/mbgl/storage/file_request.hpp b/include/mbgl/storage/file_request.hpp
new file mode 100644
index 0000000000..2f883728ff
--- /dev/null
+++ b/include/mbgl/storage/file_request.hpp
@@ -0,0 +1,27 @@
+#ifndef MBGL_STORAGE_FILE_REQUEST
+#define MBGL_STORAGE_FILE_REQUEST
+
+#include <mbgl/storage/base_request.hpp>
+
+namespace mbgl {
+
+typedef struct uv_loop_s uv_loop_t;
+
+struct FileRequestBaton;
+
+class FileRequest : public BaseRequest {
+public:
+ FileRequest(const std::string &path, uv_loop_t *loop);
+ ~FileRequest();
+
+ void cancel();
+
+private:
+ FileRequestBaton *ptr = nullptr;
+
+ friend struct FileRequestBaton;
+};
+
+}
+
+#endif \ No newline at end of file
diff --git a/include/mbgl/storage/file_request_baton.hpp b/include/mbgl/storage/file_request_baton.hpp
new file mode 100644
index 0000000000..0f1968ca13
--- /dev/null
+++ b/include/mbgl/storage/file_request_baton.hpp
@@ -0,0 +1,35 @@
+#ifndef MBGL_STORAGE_FILE_REQUEST_BATON
+#define MBGL_STORAGE_FILE_REQUEST_BATON
+
+#include <mbgl/storage/file_request.hpp>
+
+#include <uv.h>
+
+namespace mbgl {
+
+struct FileRequestBaton {
+ FileRequestBaton(FileRequest *request_, const std::string &path, uv_loop_t *loop);
+ ~FileRequestBaton();
+
+ void cancel();
+ static void file_opened(uv_fs_t *req);
+ static void file_stated(uv_fs_t *req);
+ static void file_read(uv_fs_t *req);
+ static void file_closed(uv_fs_t *req);
+ static void notify_error(uv_fs_t *req);
+ static void cleanup(uv_fs_t *req);
+
+ const unsigned long thread_id;
+ FileRequest *request = nullptr;
+ uv_fs_t req;
+ uv_file fd = -1;
+ bool canceled = false;
+ std::string body;
+ uv_buf_t buffer;
+};
+
+
+}
+
+
+#endif
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
new file mode 100644
index 0000000000..4b17cd29a7
--- /dev/null
+++ b/include/mbgl/storage/file_source.hpp
@@ -0,0 +1,55 @@
+#ifndef MBGL_STORAGE_FILE_SOURCE
+#define MBGL_STORAGE_FILE_SOURCE
+
+#include <mbgl/storage/resource_type.hpp>
+#include <mbgl/storage/request.hpp>
+
+#include <string>
+#include <unordered_map>
+#include <functional>
+
+typedef struct uv_loop_s uv_loop_t;
+typedef struct uv_messenger_s uv_messenger_t;
+
+namespace mbgl {
+
+class BaseRequest;
+class SQLiteStore;
+
+class FileSource {
+private:
+ FileSource(const FileSource &) = delete;
+ FileSource(FileSource &&) = delete;
+ FileSource& operator=(const FileSource &) = delete;
+ FileSource& operator=(FileSource &&) = delete;
+
+public:
+ FileSource(uv_loop_t *loop, const std::string &path);
+ ~FileSource();
+
+public:
+ // Stores and retrieves the base path/URL for relative requests
+ void setBase(const std::string &value);
+ const std::string &getBase() const;
+
+ std::unique_ptr<Request> request(ResourceType type, const std::string &url);
+
+ void prepare(std::function<void()> fn);
+
+ void retryAllPending();
+
+private:
+ const unsigned long thread_id;
+
+ // Stores a URL that is used as a base for loading resources with relative path.
+ std::string base;
+
+ std::unordered_map<std::string, std::weak_ptr<BaseRequest>> pending;
+ util::ptr<SQLiteStore> store;
+ uv_loop_t *loop = nullptr;
+ uv_messenger_t *queue = nullptr;
+};
+
+}
+
+#endif
diff --git a/include/mbgl/storage/http_request.hpp b/include/mbgl/storage/http_request.hpp
new file mode 100644
index 0000000000..ec76f1147c
--- /dev/null
+++ b/include/mbgl/storage/http_request.hpp
@@ -0,0 +1,57 @@
+#ifndef MBGL_STORAGE_HTTP_REQUEST
+#define MBGL_STORAGE_HTTP_REQUEST
+
+#include <mbgl/storage/resource_type.hpp>
+#include <mbgl/storage/base_request.hpp>
+#include <mbgl/storage/http_request_baton.hpp>
+
+#include <string>
+#include <memory>
+#include <cassert>
+
+typedef struct uv_loop_s uv_loop_t;
+typedef struct uv_timer_s uv_timer_t;
+
+namespace mbgl {
+
+struct CacheRequestBaton;
+struct HTTPRequestBaton;
+struct CacheEntry;
+class SQLiteStore;
+
+class HTTPRequest : public BaseRequest {
+public:
+ HTTPRequest(ResourceType type, const std::string &path, uv_loop_t *loop, util::ptr<SQLiteStore> store);
+ ~HTTPRequest();
+
+ void cancel();
+ void retryImmediately();
+
+private:
+ void startCacheRequest();
+ void handleCacheResponse(std::unique_ptr<Response> &&response);
+ void startHTTPRequest(std::unique_ptr<Response> &&res);
+ void handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&response);
+
+ void retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout);
+
+ void removeCacheBaton();
+ void removeHTTPBaton();
+ void removeBackoffTimer();
+
+private:
+ const unsigned long thread_id;
+ uv_loop_t *const loop;
+ CacheRequestBaton *cache_baton = nullptr;
+ util::ptr<HTTPRequestBaton> http_baton;
+ uv_timer_t *backoff_timer = nullptr;
+ util::ptr<SQLiteStore> store;
+ const ResourceType type;
+ uint8_t attempts = 0;
+
+ friend struct HTTPRequestBaton;
+};
+
+}
+
+#endif \ No newline at end of file
diff --git a/include/mbgl/storage/http_request_baton.hpp b/include/mbgl/storage/http_request_baton.hpp
new file mode 100644
index 0000000000..279784ec25
--- /dev/null
+++ b/include/mbgl/storage/http_request_baton.hpp
@@ -0,0 +1,73 @@
+#ifndef MBGL_STORAGE_HTTP_REQUEST_BATON
+#define MBGL_STORAGE_HTTP_REQUEST_BATON
+
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <string>
+
+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 unsigned long thread_id;
+ const std::string path;
+
+ HTTPRequest *request = nullptr;
+ uv_async_t *async = nullptr;
+
+ HTTPResponseType type = HTTPResponseType::Unknown;
+ std::unique_ptr<Response> 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<HTTPRequestBaton> &ptr);
+
+ // This will be called to stop/cancel the HTTP request (if possible). Platform-specific implementation.
+ static void stop(const util::ptr<HTTPRequestBaton> &ptr);
+};
+
+}
+
+#endif
diff --git a/include/mbgl/storage/request.hpp b/include/mbgl/storage/request.hpp
new file mode 100644
index 0000000000..e603ee527a
--- /dev/null
+++ b/include/mbgl/storage/request.hpp
@@ -0,0 +1,39 @@
+#ifndef MBGL_STORAGE_REQUEST
+#define MBGL_STORAGE_REQUEST
+
+#include <mbgl/storage/request_callback.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <forward_list>
+
+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<BaseRequest> &base);
+ ~Request();
+
+ void onload(CompletedCallback cb);
+ void oncancel(AbortedCallback cb);
+ void cancel();
+
+private:
+ const unsigned long thread_id;
+ util::ptr<BaseRequest> base;
+ std::forward_list<Callback *> callbacks;
+};
+
+}
+
+#endif \ No newline at end of file
diff --git a/include/mbgl/storage/request_callback.hpp b/include/mbgl/storage/request_callback.hpp
new file mode 100644
index 0000000000..01427bd96d
--- /dev/null
+++ b/include/mbgl/storage/request_callback.hpp
@@ -0,0 +1,22 @@
+#ifndef MBGL_STORAGE_REQUEST_CALLBACK
+#define MBGL_STORAGE_REQUEST_CALLBACK
+
+#include <mbgl/util/variant.hpp>
+
+#include <functional>
+
+namespace mbgl {
+
+class Response;
+
+using CompletedCallback = std::function<void(const Response &)>;
+using AbortedCallback = std::function<void()>;
+
+using Callback = mapbox::util::variant<
+ CompletedCallback,
+ AbortedCallback
+>;
+
+}
+
+#endif
diff --git a/include/mbgl/storage/resource_type.hpp b/include/mbgl/storage/resource_type.hpp
new file mode 100644
index 0000000000..b7204a9fa1
--- /dev/null
+++ b/include/mbgl/storage/resource_type.hpp
@@ -0,0 +1,18 @@
+#ifndef MBGL_STORAGE_RESOURCE_TYPE
+#define MBGL_STORAGE_RESOURCE_TYPE
+
+#include <cstdint>
+
+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
new file mode 100644
index 0000000000..9357ad3c63
--- /dev/null
+++ b/include/mbgl/storage/response.hpp
@@ -0,0 +1,26 @@
+#ifndef MBGL_STORAGE_RESPONSE
+#define MBGL_STORAGE_RESPONSE
+
+#include <string>
+#include <ctime>
+
+namespace mbgl {
+
+
+
+class Response {
+public:
+ long code = 0;
+ 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
diff --git a/include/mbgl/storage/sqlite_store.hpp b/include/mbgl/storage/sqlite_store.hpp
new file mode 100644
index 0000000000..cb7730d0bf
--- /dev/null
+++ b/include/mbgl/storage/sqlite_store.hpp
@@ -0,0 +1,47 @@
+#ifndef MBGL_STORAGE_SQLITE_STORE
+#define MBGL_STORAGE_SQLITE_STORE
+
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/response.hpp>
+
+#include <uv.h>
+
+#include <string>
+
+typedef struct uv_worker_s uv_worker_t;
+
+namespace mapbox {
+namespace sqlite {
+class Database;
+}
+}
+
+namespace mbgl {
+
+class SQLiteStore {
+public:
+ SQLiteStore(uv_loop_t *loop, const std::string &path);
+ ~SQLiteStore();
+
+ typedef void (*GetCallback)(std::unique_ptr<Response> &&entry, void *ptr);
+
+ void get(const std::string &path, GetCallback cb, void *ptr);
+ void put(const std::string &path, ResourceType type, const Response &entry);
+ void updateExpiration(const std::string &path, int64_t expires);
+
+private:
+ void createSchema();
+ void closeDatabase();
+ static void runGet(uv_work_t *req);
+ static void runPut(uv_work_t *req);
+ static void deliverResult(uv_work_t *req, int status);
+
+private:
+ const unsigned long thread_id;
+ util::ptr<mapbox::sqlite::Database> db;
+ uv_worker_t *worker = nullptr;
+};
+
+}
+
+#endif
diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp
index c09de6ebba..56f318ecbb 100644
--- a/include/mbgl/style/style.hpp
+++ b/include/mbgl/style/style.hpp
@@ -6,6 +6,7 @@
#include <mbgl/util/time.hpp>
#include <mbgl/util/uv.hpp>
+#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <map>
@@ -13,7 +14,6 @@
#include <unordered_map>
#include <vector>
#include <set>
-#include <memory>
namespace mbgl {
@@ -49,7 +49,7 @@ public:
const std::string &getSpriteURL() const;
public:
- std::shared_ptr<StyleLayerGroup> layers;
+ util::ptr<StyleLayerGroup> layers;
std::vector<std::string> appliedClasses;
std::string glyph_url;
diff --git a/include/mbgl/style/style_bucket.hpp b/include/mbgl/style/style_bucket.hpp
index c2cde52aa5..c4a8f6037e 100644
--- a/include/mbgl/style/style_bucket.hpp
+++ b/include/mbgl/style/style_bucket.hpp
@@ -8,8 +8,8 @@
#include <mbgl/util/vec.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/ptr.hpp>
-#include <memory>
#include <forward_list>
namespace mbgl {
@@ -93,12 +93,12 @@ typedef mapbox::util::variant<StyleBucketFill, StyleBucketLine, StyleBucketSymbo
class StyleBucket {
public:
- typedef std::shared_ptr<StyleBucket> Ptr;
+ typedef util::ptr<StyleBucket> Ptr;
StyleBucket(StyleLayerType type);
std::string name;
- std::shared_ptr<StyleSource> style_source;
+ util::ptr<StyleSource> style_source;
std::string source_layer;
FilterExpression filter;
StyleBucketRender render = std::false_type();
diff --git a/include/mbgl/style/style_layer.hpp b/include/mbgl/style/style_layer.hpp
index 84981e3fb4..641dc1e71c 100644
--- a/include/mbgl/style/style_layer.hpp
+++ b/include/mbgl/style/style_layer.hpp
@@ -6,8 +6,9 @@
#include <mbgl/style/style_properties.hpp>
#include <mbgl/style/applied_class_properties.hpp>
+#include <mbgl/util/ptr.hpp>
+
#include <vector>
-#include <memory>
#include <string>
#include <map>
#include <set>
@@ -64,7 +65,7 @@ public:
// Bucket information, telling the renderer how to generate the geometries
// for this layer (feature property filters, tessellation instructions, ...).
- std::shared_ptr<StyleBucket> bucket;
+ util::ptr<StyleBucket> bucket;
// Contains all style classes that can be applied to this layer.
const std::map<ClassID, ClassProperties> styles;
@@ -80,7 +81,7 @@ public:
StyleProperties properties;
// Child layer array (if this layer has child layers).
- std::shared_ptr<StyleLayerGroup> layers;
+ util::ptr<StyleLayerGroup> layers;
};
}
diff --git a/include/mbgl/style/style_layer_group.hpp b/include/mbgl/style/style_layer_group.hpp
index 983dd136f0..1af6e23bd7 100644
--- a/include/mbgl/style/style_layer_group.hpp
+++ b/include/mbgl/style/style_layer_group.hpp
@@ -15,7 +15,7 @@ public:
bool hasTransitions() const;
public:
- std::vector<std::shared_ptr<StyleLayer>> layers;
+ std::vector<util::ptr<StyleLayer>> layers;
};
}
diff --git a/include/mbgl/style/style_parser.hpp b/include/mbgl/style/style_parser.hpp
index e4e1b7f632..fc253bb1dd 100644
--- a/include/mbgl/style/style_parser.hpp
+++ b/include/mbgl/style/style_parser.hpp
@@ -27,7 +27,7 @@ public:
void parse(JSVal document);
- std::shared_ptr<StyleLayerGroup> getLayers() {
+ util::ptr<StyleLayerGroup> getLayers() {
return root;
}
@@ -46,14 +46,14 @@ private:
void parseSources(JSVal value);
std::unique_ptr<StyleLayerGroup> createLayers(JSVal value);
- std::shared_ptr<StyleLayer> createLayer(JSVal value);
+ util::ptr<StyleLayer> createLayer(JSVal value);
void parseLayers();
- void parseLayer(std::pair<JSVal, std::shared_ptr<StyleLayer>> &pair);
+ void parseLayer(std::pair<JSVal, util::ptr<StyleLayer>> &pair);
void parseStyles(JSVal value, std::map<ClassID, ClassProperties> &styles);
void parseStyle(JSVal, ClassProperties &properties);
- void parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer);
- void parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer);
- void parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer);
+ void parseReference(JSVal value, util::ptr<StyleLayer> &layer);
+ void parseBucket(JSVal value, util::ptr<StyleLayer> &layer);
+ void parseRender(JSVal value, util::ptr<StyleLayer> &layer);
void parseSprite(JSVal value);
void parseGlyphURL(JSVal value);
@@ -94,13 +94,13 @@ private:
private:
std::unordered_map<std::string, const rapidjson::Value *> constants;
- std::unordered_map<std::string, const std::shared_ptr<StyleSource>> sources;
+ std::unordered_map<std::string, const util::ptr<StyleSource>> sources;
// This stores the root layer.
- std::shared_ptr<StyleLayerGroup> root;
+ util::ptr<StyleLayerGroup> root;
// This maps ids to Layer objects, with all items being at the root level.
- std::unordered_map<std::string, std::pair<JSVal, std::shared_ptr<StyleLayer>>> layers;
+ std::unordered_map<std::string, std::pair<JSVal, util::ptr<StyleLayer>>> layers;
// Store a stack of layers we're parsing right now. This is to prevent reference cycles.
std::forward_list<StyleLayer *> stack;
diff --git a/include/mbgl/style/style_source.hpp b/include/mbgl/style/style_source.hpp
index b598550c65..00c48431a1 100644
--- a/include/mbgl/style/style_source.hpp
+++ b/include/mbgl/style/style_source.hpp
@@ -2,18 +2,18 @@
#define MBGL_STYLE_STYLE_SOURCE
#include <mbgl/style/types.hpp>
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <rapidjson/document.h>
-#include <memory>
#include <vector>
#include <string>
-#include <rapidjson/document.h>
-
namespace mbgl {
class Source;
-class SourceInfo {
+class SourceInfo : private util::noncopyable {
public:
SourceType type = SourceType::Vector;
std::string url;
@@ -31,12 +31,12 @@ public:
class StyleSource : public std::enable_shared_from_this<StyleSource> {
public:
- SourceInfo info;
+ util::ptr<SourceInfo> info;
bool enabled = false;
- std::shared_ptr<Source> source;
+ util::ptr<Source> source;
- StyleSource(const SourceInfo &info)
+ StyleSource(const util::ptr<SourceInfo> &info)
: info(info)
{}
};
diff --git a/include/mbgl/text/glyph_store.hpp b/include/mbgl/text/glyph_store.hpp
index e0c0391c73..f8a7cb4d0c 100644
--- a/include/mbgl/text/glyph_store.hpp
+++ b/include/mbgl/text/glyph_store.hpp
@@ -4,6 +4,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/pbf.hpp>
#include <mbgl/util/vec.hpp>
+#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <vector>
@@ -47,8 +48,15 @@ private:
class GlyphPBF {
public:
- GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const std::shared_ptr<FileSource> &fileSource);
+ GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const util::ptr<FileSource> &fileSource);
+private:
+ GlyphPBF(const GlyphPBF &) = delete;
+ GlyphPBF(GlyphPBF &&) = delete;
+ GlyphPBF &operator=(const GlyphPBF &) = delete;
+ GlyphPBF &operator=(GlyphPBF &&) = delete;
+
+public:
void parse(FontStack &stack);
std::shared_future<GlyphPBF &> getFuture();
@@ -63,7 +71,7 @@ private:
// Manages Glyphrange PBF loading.
class GlyphStore {
public:
- GlyphStore(const std::shared_ptr<FileSource> &fileSource);
+ GlyphStore(const util::ptr<FileSource> &fileSource);
// Block until all specified GlyphRanges of the specified font stack are loaded.
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
@@ -82,7 +90,7 @@ public:
std::string glyphURL;
private:
- const std::shared_ptr<FileSource> fileSource;
+ const util::ptr<FileSource> fileSource;
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
std::mutex mtx;
diff --git a/include/mbgl/util/compression.hpp b/include/mbgl/util/compression.hpp
new file mode 100644
index 0000000000..a33b2476a7
--- /dev/null
+++ b/include/mbgl/util/compression.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_UTIL_COMPRESSION
+#define MBGL_UTIL_COMPRESSION
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+
+std::string compress(const std::string &raw);
+std::string decompress(const std::string &raw);
+
+}
+}
+
+#endif
diff --git a/include/mbgl/util/filesource.hpp b/include/mbgl/util/filesource.hpp
deleted file mode 100644
index 18c63ddfeb..0000000000
--- a/include/mbgl/util/filesource.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef MBGL_UTIL_FILESOURCE
-#define MBGL_UTIL_FILESOURCE
-
-#include <mbgl/util/uv.hpp>
-
-#include <string>
-#include <memory>
-#include <functional>
-
-namespace mbgl {
-
-namespace platform {
-struct Response;
-}
-
-enum class ResourceType : uint8_t {
- Unknown,
- Tile,
- Glyphs,
- Image,
- JSON
-};
-
-class FileSource {
-public:
- FileSource();
-
- void setBase(const std::string &value);
- const std::string &getBase() const;
-
- void load(ResourceType type, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop = nullptr);
-
-private:
- // Stores a URL that is used as a base for loading resources with relative path.
- std::string base;
-
- // Stores the absolute path to the cache directory.
- const std::string cache;
-};
-
-}
-
-#endif
diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp
index e8c3389350..c9232db4eb 100644
--- a/include/mbgl/util/interpolate.hpp
+++ b/include/mbgl/util/interpolate.hpp
@@ -1,6 +1,8 @@
#ifndef MBGL_UTIL_INTERPOLATE
#define MBGL_UTIL_INTERPOLATE
+#include <array>
+
namespace mbgl {
namespace util {
diff --git a/include/mbgl/util/parsedate.h b/include/mbgl/util/parsedate.h
new file mode 100644
index 0000000000..6905e361d4
--- /dev/null
+++ b/include/mbgl/util/parsedate.h
@@ -0,0 +1,38 @@
+#ifndef HEADER_PARSEDATE_H
+#define HEADER_PARSEDATE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, 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.
+ *
+ ***************************************************************************/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <time.h>
+
+time_t parse_date(const char *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADER_PARSEDATE_H */
diff --git a/include/mbgl/util/ptr.hpp b/include/mbgl/util/ptr.hpp
new file mode 100644
index 0000000000..6e02f956f3
--- /dev/null
+++ b/include/mbgl/util/ptr.hpp
@@ -0,0 +1,29 @@
+#ifndef MBGL_UTIL_PTR
+#define MBGL_UTIL_PTR
+
+#include <memory>
+#include <cassert>
+
+namespace mbgl {
+namespace util {
+
+template <typename T>
+class ptr : public ::std::shared_ptr<T> {
+public:
+ template <typename... Args>
+ inline ptr(Args &&... args)
+ : ::std::shared_ptr<T>(::std::forward<Args>(args)...) {}
+
+ inline auto operator->() const -> decltype(this->::std::shared_ptr<T>::operator->()) {
+ assert(*this);
+ return ::std::shared_ptr<T>::operator->();
+ }
+ inline auto operator*() const -> decltype(this->::std::shared_ptr<T>::operator*()) {
+ assert(*this);
+ return ::std::shared_ptr<T>::operator*();
+ }
+};
+}
+}
+
+#endif \ No newline at end of file
diff --git a/include/mbgl/util/queue.h b/include/mbgl/util/queue.h
new file mode 100644
index 0000000000..fe02b454ea
--- /dev/null
+++ b/include/mbgl/util/queue.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef QUEUE_H_
+#define QUEUE_H_
+
+typedef void *QUEUE[2];
+
+/* Private macros. */
+#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
+#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
+#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q)))
+#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q)))
+
+/* Public macros. */
+#define QUEUE_DATA(ptr, type, field) \
+ ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field)))
+
+#define QUEUE_FOREACH(q, h) \
+ for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
+
+#define QUEUE_EMPTY(q) \
+ ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
+
+#define QUEUE_HEAD(q) \
+ (QUEUE_NEXT(q))
+
+#define QUEUE_INIT(q) \
+ do { \
+ QUEUE_NEXT(q) = (q); \
+ QUEUE_PREV(q) = (q); \
+ } \
+ while (0)
+
+#define QUEUE_ADD(h, n) \
+ do { \
+ QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \
+ QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \
+ QUEUE_PREV(h) = QUEUE_PREV(n); \
+ QUEUE_PREV_NEXT(h) = (h); \
+ } \
+ while (0)
+
+#define QUEUE_SPLIT(h, q, n) \
+ do { \
+ QUEUE_PREV(n) = QUEUE_PREV(h); \
+ QUEUE_PREV_NEXT(n) = (n); \
+ QUEUE_NEXT(n) = (q); \
+ QUEUE_PREV(h) = QUEUE_PREV(q); \
+ QUEUE_PREV_NEXT(h) = (h); \
+ QUEUE_PREV(q) = (n); \
+ } \
+ while (0)
+
+#define QUEUE_INSERT_HEAD(h, q) \
+ do { \
+ QUEUE_NEXT(q) = QUEUE_NEXT(h); \
+ QUEUE_PREV(q) = (h); \
+ QUEUE_NEXT_PREV(q) = (q); \
+ QUEUE_NEXT(h) = (q); \
+ } \
+ while (0)
+
+#define QUEUE_INSERT_TAIL(h, q) \
+ do { \
+ QUEUE_NEXT(q) = (h); \
+ QUEUE_PREV(q) = QUEUE_PREV(h); \
+ QUEUE_PREV_NEXT(q) = (q); \
+ QUEUE_PREV(h) = (q); \
+ } \
+ while (0)
+
+#define QUEUE_REMOVE(q) \
+ do { \
+ QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \
+ QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \
+ } \
+ while (0)
+
+#endif /* QUEUE_H_ */
diff --git a/include/mbgl/util/raster.hpp b/include/mbgl/util/raster.hpp
index e526ffa5ac..7051c30091 100644
--- a/include/mbgl/util/raster.hpp
+++ b/include/mbgl/util/raster.hpp
@@ -4,11 +4,11 @@
#include <mbgl/util/transition.hpp>
#include <mbgl/util/texturepool.hpp>
#include <mbgl/util/image.hpp>
+#include <mbgl/util/ptr.hpp>
#include <mbgl/renderer/prerendered_texture.hpp>
#include <string>
#include <mutex>
-#include <memory>
typedef struct uv_loop_s uv_loop_t;
@@ -17,7 +17,7 @@ namespace mbgl {
class Raster : public std::enable_shared_from_this<Raster> {
public:
- Raster(const std::shared_ptr<Texturepool> &texturepool);
+ Raster(const util::ptr<Texturepool> &texturepool);
~Raster();
// load image data
@@ -57,7 +57,7 @@ private:
bool loaded = false;
// shared texture pool
- std::shared_ptr<Texturepool> texturepool;
+ util::ptr<Texturepool> texturepool;
// min/mag filter
uint32_t filter = 0;
@@ -66,7 +66,7 @@ private:
std::unique_ptr<util::Image> img;
// fade in transition
- std::shared_ptr<util::transition> fade_transition = nullptr;
+ util::ptr<util::transition> fade_transition = nullptr;
};
}
diff --git a/include/mbgl/util/sqlite3.hpp b/include/mbgl/util/sqlite3.hpp
new file mode 100644
index 0000000000..3e324f7ce1
--- /dev/null
+++ b/include/mbgl/util/sqlite3.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <string>
+#include <stdexcept>
+
+typedef struct sqlite3 sqlite3;
+typedef struct sqlite3_stmt sqlite3_stmt;
+
+namespace mapbox {
+namespace sqlite {
+
+enum OpenFlag : int {
+ ReadOnly = 0x00000001,
+ ReadWrite = 0x00000002,
+ Create = 0x00000004,
+ NoMutex = 0x00008000,
+ FullMutex = 0x00010000,
+ SharedCache = 0x00020000,
+ PrivateCache = 0x00040000,
+};
+
+struct Exception : std::runtime_error {
+ inline Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {}
+ const int code = 0;
+};
+
+class Statement;
+
+class Database {
+private:
+ Database(const Database &) = delete;
+ Database &operator=(const Database &) = delete;
+
+public:
+ Database(const std::string &filename, int flags = 0);
+ Database(Database &&);
+ ~Database();
+ Database &operator=(Database &&);
+
+ operator bool() const;
+
+ void exec(const std::string &sql);
+ Statement prepare(const char *query);
+
+private:
+ sqlite3 *db = nullptr;
+};
+
+class Statement {
+private:
+ Statement(const Statement &) = delete;
+ Statement &operator=(const Statement &) = delete;
+
+public:
+ Statement(sqlite3 *db, const char *sql);
+ Statement(Statement &&);
+ ~Statement();
+ Statement &operator=(Statement &&);
+
+ operator bool() const;
+
+ template <typename T> void bind(int offset, T value);
+ void bind(int offset, const std::string &value, bool retain = true);
+ template <typename T> T get(int offset);
+
+ bool run();
+ void reset();
+
+private:
+ sqlite3_stmt *stmt = nullptr;
+};
+
+}
+}
diff --git a/include/mbgl/util/uv-channel.h b/include/mbgl/util/uv-channel.h
new file mode 100644
index 0000000000..ea5c279f65
--- /dev/null
+++ b/include/mbgl/util/uv-channel.h
@@ -0,0 +1,29 @@
+#ifndef MBGL_UTIL_UV_CHANNEL
+#define MBGL_UTIL_UV_CHANNEL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <uv.h>
+
+// Taken from http://navaneeth.github.io/blog/2013/08/02/channels-in-libuv/
+
+typedef struct uv_chan_s uv_chan_t;
+
+struct uv_chan_s {
+ uv_mutex_t mutex;
+ uv_cond_t cond;
+ void *q[2];
+};
+
+int uv_chan_init(uv_chan_t *chan);
+void uv_chan_send(uv_chan_t *chan, void *data);
+void *uv_chan_receive(uv_chan_t *chan);
+void uv_chan_destroy(uv_chan_t *chan);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/mbgl/util/uv-messenger.h b/include/mbgl/util/uv-messenger.h
new file mode 100644
index 0000000000..82b8ef2d9c
--- /dev/null
+++ b/include/mbgl/util/uv-messenger.h
@@ -0,0 +1,32 @@
+#ifndef MBGL_UTIL_UV_MESSENGER
+#define MBGL_UTIL_UV_MESSENGER
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <uv.h>
+
+typedef struct uv_messenger_s uv_messenger_t;
+typedef void (*uv_messenger_cb)(void *arg);
+typedef void (*uv_messenger_stop_cb)(uv_messenger_t *msgr);
+
+struct uv_messenger_s {
+ uv_mutex_t mutex;
+ uv_async_t async;
+ uv_messenger_cb callback;
+ void *data;
+ void *queue[2];
+};
+
+int uv_messenger_init(uv_loop_t *loop, uv_messenger_t *msgr, uv_messenger_cb callback);
+void uv_messenger_send(uv_messenger_t *msgr, void *arg);
+void uv_messenger_stop(uv_messenger_t *msgr);
+void uv_messenger_ref(uv_messenger_t *msgr);
+void uv_messenger_unref(uv_messenger_t *msgr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/mbgl/util/uv-worker.h b/include/mbgl/util/uv-worker.h
new file mode 100644
index 0000000000..451b6fb04d
--- /dev/null
+++ b/include/mbgl/util/uv-worker.h
@@ -0,0 +1,42 @@
+#ifndef MBGL_UTIL_UV_WORKER
+#define MBGL_UTIL_UV_WORKER
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct uv_messenger_s uv_messenger_t;
+
+#include <mbgl/util/uv-channel.h>
+
+#include <stdlib.h>
+
+typedef struct uv_worker_s uv_worker_t;
+
+typedef void (*uv_worker_cb)(void *data);
+typedef void (*uv_worker_after_cb)(void *data);
+typedef void (*uv_worker_close_cb)(uv_worker_t *worker);
+
+struct uv_worker_s {
+#ifndef NDEBUG
+ unsigned long thread_id;
+#endif
+ uv_loop_t *loop;
+ uv_messenger_t *msgr;
+ uv_chan_t chan;
+ const char *name;
+ int count;
+ uv_worker_close_cb close_cb;
+ unsigned int active_items;
+};
+
+int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name);
+void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
+ uv_worker_after_cb after_work_cb);
+void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/mbgl/util/uv.hpp b/include/mbgl/util/uv.hpp
index 4ceb8b873f..3c533cfbf8 100644
--- a/include/mbgl/util/uv.hpp
+++ b/include/mbgl/util/uv.hpp
@@ -4,6 +4,7 @@
#include <string>
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;
@@ -11,9 +12,15 @@ namespace uv {
std::string cwd();
+struct deleter {
+ void operator()(uv_async_t *async);
+ void operator()(uv_timer_t *timer);
+};
+
class thread;
class rwlock;
class loop;
+class worker;
}
diff --git a/include/mbgl/util/uv_detail.hpp b/include/mbgl/util/uv_detail.hpp
index b3fdbb3719..a80423f822 100644
--- a/include/mbgl/util/uv_detail.hpp
+++ b/include/mbgl/util/uv_detail.hpp
@@ -1,21 +1,13 @@
#ifndef MBGL_UTIL_UV_DETAIL
#define MBGL_UTIL_UV_DETAIL
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/uv-worker.h>
+
#include <uv.h>
+
#include <functional>
#include <cassert>
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-parameter"
-#endif
-
-#include <boost/lockfree/queue.hpp>
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
#include <string>
@@ -112,6 +104,24 @@ private:
uv_once_t o = UV_ONCE_INIT;
};
+class worker {
+public:
+ inline worker(uv_loop_t *loop, unsigned int count, const char *name = nullptr) : w(new uv_worker_t) {
+ uv_worker_init(w, loop, count, name);
+ }
+ inline ~worker() {
+ uv_worker_close(w, [](uv_worker_t *worker) {
+ delete worker;
+ });
+ }
+ inline void add(void *data, uv_worker_cb work_cb, uv_worker_after_cb after_work_cb) {
+ uv_worker_send(w, data, work_cb, after_work_cb);
+ }
+
+private:
+ uv_worker_t *w;
+};
+
template <typename T>
class work {
public:
@@ -119,30 +129,26 @@ public:
typedef void (*after_work_callback)(T &object);
template<typename... Args>
- work(const std::shared_ptr<loop> &loop, work_callback work_cb, after_work_callback after_work_cb, Args&&... args)
- : loop(loop),
- data(std::forward<Args>(args)...),
+ work(worker &worker, work_callback work_cb, after_work_callback after_work_cb, Args&&... args)
+ : data(std::forward<Args>(args)...),
work_cb(work_cb),
after_work_cb(after_work_cb) {
- req.data = this;
- uv_queue_work(**loop, &req, do_work, after_work);
+ worker.add(this, do_work, after_work);
}
private:
- static void do_work(uv_work_t *req) {
- work<T> *w = static_cast<work<T> *>(req->data);
+ static void do_work(void *data) {
+ work<T> *w = reinterpret_cast<work<T> *>(data);
w->work_cb(w->data);
}
- static void after_work(uv_work_t *req, int) {
- work<T> *w = static_cast<work<T> *>(req->data);
+ static void after_work(void *data) {
+ work<T> *w = reinterpret_cast<work<T> *>(data);
w->after_work_cb(w->data);
delete w;
}
private:
- std::shared_ptr<uv::loop> loop;
- uv_work_t req;
T data;
work_callback work_cb;
after_work_callback after_work_cb;
diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gyp
index e19768caf9..dac1934d31 100644
--- a/linux/mapboxgl-app.gyp
+++ b/linux/mapboxgl-app.gyp
@@ -15,7 +15,8 @@
'../common/platform_default.cpp',
'../common/glfw_view.hpp',
'../common/glfw_view.cpp',
- '../common/curl_request.cpp',
+ '../common/http_request_baton_curl.cpp',
+ '../common/linux.cpp',
'../common/stderr_log.hpp',
'../common/stderr_log.cpp',
],
@@ -32,7 +33,6 @@
],
'OTHER_LDFLAGS': [
'<@(glfw3_libraries)',
- '<@(curl_libraries)',
],
}
},
@@ -46,7 +46,6 @@
'link_settings': {
'libraries': [
'<@(glfw3_libraries)',
- '<@(curl_libraries)',
'-lboost_regex'
],
},
diff --git a/macosx/main.mm b/macosx/main.mm
index 9ecc307c05..aeb49225cb 100644
--- a/macosx/main.mm
+++ b/macosx/main.mm
@@ -1,9 +1,12 @@
#include "../common/settings_nsuserdefaults.hpp"
#include "../common/glfw_view.hpp"
#include "../common/nslog_log.hpp"
+#include "../common/Reachability.h"
#import <Foundation/Foundation.h>
+#include <uv.h>
+
@interface URLHandler : NSObject
@property (nonatomic) mbgl::Map *map;
@@ -68,16 +71,25 @@
@end
int main() {
+ fprintf(stderr, "main thread: 0x%lx\n", uv_thread_self());
mbgl::Log::Set<mbgl::NSLogBackend>();
GLFWView view;
mbgl::Map map(view);
+ mbgl::Map *map_ptr = &map;
URLHandler *handler = [[URLHandler alloc] init];
[handler setMap:&map];
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:handler andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
+ // Notify map object when network reachability status changes.
+ Reachability* reachability = [Reachability reachabilityForInternetConnection];
+ reachability.reachableBlock = ^(Reachability *reachability) {
+ map_ptr->setReachability(true);
+ };
+ [reachability startNotifier];
+
// Load settings
mbgl::Settings_NSUserDefaults settings;
map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
@@ -97,6 +109,8 @@ int main() {
int ret = view.run();
+ [reachability stopNotifier];
+
// Save settings
map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
settings.bearing = map.getBearing();
diff --git a/macosx/mapboxgl-app.gyp b/macosx/mapboxgl-app.gyp
index da5c68ea35..51f591fdd0 100644
--- a/macosx/mapboxgl-app.gyp
+++ b/macosx/mapboxgl-app.gyp
@@ -15,8 +15,10 @@
'../common/platform_nsstring.mm',
'../common/glfw_view.hpp',
'../common/glfw_view.cpp',
- '../common/foundation_request.h',
- '../common/foundation_request.mm',
+ '../common/reachability.h',
+ '../common/reachability.m',
+ '../common/http_request_baton_cocoa.mm',
+ '../common/osx.mm',
'../common/nslog_log.hpp',
'../common/nslog_log.mm',
],
@@ -33,6 +35,7 @@
],
'OTHER_LDFLAGS': [
'<@(glfw3_libraries)',
+ '-framework SystemConfiguration',
],
'SDKROOT': 'macosx',
'INFOPLIST_FILE': 'Info.plist',
diff --git a/mapboxgl.gyp b/mapboxgl.gyp
index dcd89f3a76..68434bf2ba 100644
--- a/mapboxgl.gyp
+++ b/mapboxgl.gyp
@@ -31,7 +31,7 @@
'<(SHARED_INTERMEDIATE_DIR)/src/shader/shaders_gl.cpp',
'<(SHARED_INTERMEDIATE_DIR)/src/shader/shaders_gles2.cpp'
],
- 'include_dirs':[
+ 'include_dirs': [
'<(SHARED_INTERMEDIATE_DIR)/include/',
]
}
@@ -126,53 +126,70 @@
'<!@(find src -name "*.glsl")',
'bin/style.json'
],
- 'xcode_settings': {
- 'SDKROOT': 'macosx',
- 'SUPPORTED_PLATFORMS':['macosx'],
- 'MACOSX_DEPLOYMENT_TARGET':'10.9',
- 'PUBLIC_HEADERS_FOLDER_PATH': 'include',
- 'OTHER_CPLUSPLUSFLAGS':[
- '<@(png_cflags)',
- '<@(uv_cflags)',
- '-I<(boost_root)/include',
- ]
- },
- 'include_dirs':[
- './include'
- ],
- 'cflags': [
- '<@(png_cflags)',
- '-I<(boost_root)/include',
+ 'include_dirs': [
+ './include'
],
- 'direct_dependent_settings': {
- 'include_dirs':[
- './include',
- ],
- 'cflags': [
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'SDKROOT': 'macosx',
+ 'SUPPORTED_PLATFORMS': ['macosx'],
+ 'MACOSX_DEPLOYMENT_TARGET': '10.9',
+ 'PUBLIC_HEADERS_FOLDER_PATH': 'include',
+ 'OTHER_CPLUSPLUSFLAGS': [
'<@(png_cflags)',
'<@(uv_cflags)',
+ '<@(curl_cflags)',
+ '<@(sqlite3_cflags)',
+ '-I<(boost_root)/include',
+ ],
+ 'OTHER_CFLAGS': [
+ '<@(uv_cflags)',
+ ],
+ },
+ }, {
+ 'cflags': [
+ '<@(png_cflags)',
+ '<@(uv_cflags)',
+ '<@(curl_cflags)',
+ '<@(sqlite3_cflags)',
+ '-I<(boost_root)/include',
],
- 'xcode_settings': {
- 'OTHER_CPLUSPLUSFLAGS':[
- '<@(png_cflags)',
+ }]
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ './include',
+ ],
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS': [
'<@(uv_cflags)',
- ]
- },
- 'conditions': [
- ['OS == "mac"', {
- 'xcode_settings': {
- 'OTHER_LDFLAGS': [
- '<@(png_libraries)',
- '<@(uv_libraries)',
- ]
- }
- }, {
- 'libraries': [
+ '<@(curl_cflags)',
+ '-I<(boost_root)/include',
+ ],
+ 'OTHER_LDFLAGS': [
'<@(png_libraries)',
'<@(uv_libraries)',
+ '<@(curl_libraries)',
+ '<@(sqlite3_libraries)',
]
- }]
- ]
+ }
+ }, {
+ 'cflags': [
+ '<@(uv_cflags)',
+ '<@(curl_cflags)',
+ '-I<(boost_root)/include',
+ ],
+ 'libraries': [
+ '<@(png_libraries)',
+ '<@(uv_libraries)',
+ '<@(curl_libraries)',
+ '<@(sqlite3_libraries)',
+ ]
+ }]
+ ]
}
},
{
@@ -181,7 +198,7 @@
'type': 'static_library',
'hard_dependency': 1,
'dependencies': [
- 'shaders',
+ 'shaders',
],
'sources': [
'<!@(find src/ \( -name "*.cpp" ! -name shaders.hpp ! -name shaders_gles2.cpp ! -name shaders_gl.cpp \))',
@@ -195,56 +212,42 @@
],
'xcode_settings': {
'SDKROOT': 'iphoneos',
- 'SUPPORTED_PLATFORMS':['iphonesimulator','iphoneos'],
+ 'SUPPORTED_PLATFORMS': ['iphonesimulator','iphoneos'],
'ARCHS': [ "armv7", "armv7s", "arm64", "i386", "x86_64" ],
'TARGETED_DEVICE_FAMILY': '1,2',
'CODE_SIGN_IDENTITY': 'iPhone Developer',
'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
'PUBLIC_HEADERS_FOLDER_PATH': 'include',
- 'GCC_INPUT_FILETYPE':'sourcecode.cpp.cpp',
- 'OTHER_CPLUSPLUSFLAGS':[
+ 'GCC_INPUT_FILETYPE': 'sourcecode.cpp.cpp',
+ 'OTHER_CPLUSPLUSFLAGS': [
'<@(png_cflags)',
'<@(uv_cflags)',
+ '<@(curl_cflags)',
+ '<@(sqlite3_cflags)',
'-I<(boost_root)/include',
- ]
+ ],
},
- 'include_dirs':[
- './include'
- ],
- 'cflags': [
- '<@(png_cflags)',
- '<@(uv_cflags)',
- '-I<(boost_root)/include',
+ 'include_dirs': [
+ './include'
],
'direct_dependent_settings': {
- 'include_dirs':[
- './include'
+ 'include_dirs': [
+ './include',
+ ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '<@(png_libraries)',
+ '<@(uv_libraries)',
+ '<@(curl_libraries)',
+ '<@(sqlite3_libraries)',
],
- 'cflags': [
- '<@(png_cflags)',
- '<@(uv_cflags)',
+ 'OTHER_CPLUSPLUSFLAGS': [
+ '<@(uv_cflags)',
],
- 'xcode_settings': {
- 'OTHER_CPLUSPLUSFLAGS':[
- '<@(png_cflags)',
- '<@(uv_cflags)',
- ]
- },
- 'conditions': [
- ['OS == "mac"', {
- 'xcode_settings': {
- 'OTHER_LDFLAGS': [
- '<@(png_libraries)',
- '<@(uv_libraries)',
- ]
- }
- }, {
- 'libraries': [
- '<@(png_libraries)',
- '<@(uv_libraries)'
- ]
- }]
- ]
+ 'OTHER_CFLAGS': [
+ '<@(uv_cflags)',
+ ],
+ }
}
}
]
diff --git a/scripts/flags.sh b/scripts/flags.sh
index 0c0981cdc1..58aee18a04 100755
--- a/scripts/flags.sh
+++ b/scripts/flags.sh
@@ -16,12 +16,12 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
# some limited coverage
if [[ ${BUILDTYPE} == "Debug" ]]; then
if [[ ${CXX} == "g++" ]]; then
- export CXXFLAGS="-fsanitize=address ${CXXFLAGS}"
- export CFLAGS="-fsanitize=address ${CFLAGS}"
+ export CXXFLAGS="-fsanitize=address -g ${CXXFLAGS}"
+ export CFLAGS="-fsanitize=address -g ${CFLAGS}"
export LDFLAGS="-fsanitize=address ${LDFLAGS}"
elif [[ ${CXX} == "clang++" ]]; then
- export CXXFLAGS="-fsanitize=thread -fPIC ${CXXFLAGS}"
- export CFLAGS="-fsanitize=thread ${CFLAGS}"
+ export CXXFLAGS="-fsanitize=thread -g -fPIC ${CXXFLAGS}"
+ export CFLAGS="-fsanitize=thread -g ${CFLAGS}"
export LDFLAGS="-fsanitize=thread -pie ${LDFLAGS}"
fi
fi
diff --git a/setup-libraries.sh b/setup-libraries.sh
index ce0a230bcd..dc1b1643fa 100755
--- a/setup-libraries.sh
+++ b/setup-libraries.sh
@@ -53,6 +53,7 @@ NPM=$(which npm)
MP_HASH="e82e6c00ea5a0cda147e4c121a3c7751cae69ff8"
DIR_HASH=$(echo `pwd` | git hash-object --stdin)
+
if [ ! -d 'mapnik-packaging/' ]; then
git clone https://github.com/mapnik/mapnik-packaging.git
fi
@@ -66,7 +67,7 @@ export CXX11=true
if [ ${UNAME} = 'Darwin' ]; then
-if [ ! -z "${TRAVIS:-}" ]; then
+if [[ $TRAVIS ]]; then
if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
rm -rf out/build-cpp11-libcpp-x86_64-macosx
@@ -83,30 +84,40 @@ source iPhoneOS.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneOSs.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneOS64.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneSimulator.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneSimulator64.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source MacOSX.sh
@@ -114,6 +125,7 @@ export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
+ if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libssl.a ] ; then ./scripts/build_openssl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -d out/build-cpp11-libcpp-x86_64-macosx/include/boost ] ; then ./scripts/build_boost.sh `pwd`/../../src/ `pwd`/../../include/ `pwd`/../../linux/ `pwd`/../../common/ ; fi
@@ -121,8 +133,10 @@ export LIBUV_VERSION=0.10.28
# setup iOS universal libs
./scripts/make_universal.sh
+patch -p0 --forward < patches/curl-ios.diff || true
+echo "NOTE: One patch FAILURE is expected. The other should have been applied or ignored."
-if [ ! -z "${TRAVIS:-}" ] && [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
+if [[ $TRAVIS && $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY ]] ; then
tar -zcf out/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libcpp-universal
aws s3 cp --acl public-read out/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz s3://${AWS_S3_BUCKET}/dependencies/
tar -zcf out/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libcpp-x86_64-macosx
@@ -138,7 +152,7 @@ cd ../../
elif [ ${UNAME} = 'Linux' ]; then
-if [ ! -z "${TRAVIS:-}" ]; then
+if [[ $TRAVIS ]]; then
if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
rm -rf out/build-cpp11-libstdcpp-gcc-x86_64-linux
tar -xzf out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz
@@ -150,11 +164,12 @@ export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
+ if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libssl.a ] ; then ./scripts/build_openssl.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libboost_regex.a ] ; then ./scripts/build_boost.sh --with-regex ; fi
-if [ ! -z "${TRAVIS:-}" ] && [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
+if [[ $TRAVIS && $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY ]] ; then
if ! tar --compare -zf out/build-cpp11-libstdcpp-gcc-x86_64-linux.tar.gz ; then
tar -zcf out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libstdcpp-gcc-x86_64-linux
aws s3 cp --acl public-read out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz s3://${AWS_S3_BUCKET}/dependencies/
diff --git a/src/map/map.cpp b/src/map/map.cpp
index ad8749ff08..9034c6d5ac 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -19,11 +19,10 @@
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/util/texturepool.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
-#include <mbgl/util/filesource.hpp>
+#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/log.hpp>
#include <algorithm>
-#include <memory>
#include <iostream>
#define _USE_MATH_DEFINES
@@ -34,12 +33,15 @@ using namespace mbgl;
Map::Map(View& view)
: loop(std::make_shared<uv::loop>()),
thread(std::make_unique<uv::thread>()),
+ async_terminate(new uv_async_t()),
+ async_render(new uv_async_t()),
+ async_cleanup(new uv_async_t()),
view(view),
+#ifndef NDEBUG
+ main_thread(uv_thread_self()),
+#endif
transform(view),
- fileSource(std::make_shared<FileSource>()),
- style(std::make_shared<Style>()),
glyphAtlas(std::make_shared<GlyphAtlas>(1024, 1024)),
- glyphStore(std::make_shared<GlyphStore>(fileSource)),
spriteAtlas(std::make_shared<SpriteAtlas>(512, 512)),
texturepool(std::make_shared<Texturepool>()),
painter(*this) {
@@ -53,49 +55,93 @@ Map::Map(View& view)
}
Map::~Map() {
- // Clear the style first before the rest of the constructor deletes members of this object.
- // This is required because members of the style reference the Map object in their destructors.
- style.reset();
-
if (async) {
stop();
}
+
+ // Explicitly reset all pointers.
+ texturepool.reset();
+ sprite.reset();
+ spriteAtlas.reset();
+ glyphStore.reset();
+ glyphAtlas.reset();
+ style.reset();
+ fileSource.reset();
+ workers.reset();
+
+ uv_run(**loop, UV_RUN_DEFAULT);
+}
+
+uv::worker &Map::getWorker() {
+ if (!workers) {
+ workers = std::make_unique<uv::worker>(**loop, 4, "Tile Worker");
+ }
+ return *workers;
}
void Map::start() {
+ assert(uv_thread_self() == main_thread);
+ assert(!async);
+
// When starting map rendering in another thread, we perform async/continuously
// updated rendering. Only in these cases, we attach the async handlers.
async = true;
+ // Reset the flag.
+ is_stopped = false;
+
// Setup async notifications
- async_terminate = new uv_async_t();
- uv_async_init(**loop, async_terminate, terminate);
- async_terminate->data = **loop;
+ uv_async_init(**loop, async_terminate.get(), terminate);
+ async_terminate->data = this;
- async_render = new uv_async_t();
- uv_async_init(**loop, async_render, render);
+ uv_async_init(**loop, async_render.get(), render);
async_render->data = this;
- async_cleanup = new uv_async_t();
- uv_async_init(**loop, async_cleanup, cleanup);
+ uv_async_init(**loop, async_cleanup.get(), cleanup);
async_cleanup->data = this;
uv_thread_create(*thread, [](void *arg) {
Map *map = static_cast<Map *>(arg);
+#ifndef NDEBUG
+ map->map_thread = uv_thread_self();
+#endif
+#ifdef __APPLE__
+ pthread_setname_np("Map");
+#endif
map->run();
+#ifndef NDEBUG
+ map->map_thread = -1;
+#endif
+
+ // Make sure that the stop() function knows when to stop invoking the callback function.
+ map->is_stopped = true;
+ map->view.notify();
}, this);
}
-void Map::stop() {
- if (async_terminate != nullptr) {
- uv_async_send(async_terminate);
+void Map::stop(stop_callback cb, void *data) {
+ assert(uv_thread_self() == main_thread);
+ assert(main_thread != map_thread);
+ assert(async);
+
+ uv_async_send(async_terminate.get());
+
+ if (cb) {
+ // Wait until the render thread stopped. We are using this construct instead of plainly
+ // relying on the thread_join because the system might need to run things in the current
+ // thread that is required for the render thread to terminate correctly. This is for example
+ // the case with Cocoa's NSURLRequest. Otherwise, we will eventually deadlock because this
+ // thread (== main thread) is blocked. The callback function should use an efficient waiting
+ // function to avoid a busy waiting loop.
+ while (!is_stopped) {
+ cb(data);
+ }
}
+ // If a callback function was provided, this should return immediately because the thread has
+ // already finished executing.
uv_thread_join(*thread);
- // Run the event loop once to make sure our async delete handlers are called.
- uv_run(**loop, UV_RUN_ONCE);
-
async = false;
}
@@ -104,22 +150,35 @@ void Map::delete_async(uv_handle_t *handle, int status) {
}
void Map::run() {
+#ifndef NDEBUG
+ if (!async) {
+ map_thread = main_thread;
+ }
+#endif
+ assert(uv_thread_self() == map_thread);
+
setup();
prepare();
uv_run(**loop, UV_RUN_DEFAULT);
+ // Run the event loop once more to make sure our async delete handlers are called.
+ uv_run(**loop, UV_RUN_ONCE);
+
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
if (!async) {
render();
+#ifndef NDEBUG
+ map_thread = -1;
+#endif
}
}
void Map::rerender() {
// We only send render events if we want to continuously update the map
// (== async rendering).
- if (async && async_render != nullptr) {
- uv_async_send(async_render);
+ if (async) {
+ uv_async_send(async_render.get());
}
}
@@ -139,7 +198,7 @@ void Map::swapped() {
void Map::cleanup() {
if (async_cleanup != nullptr) {
- uv_async_send(async_cleanup);
+ uv_async_send(async_cleanup.get());
}
}
@@ -153,9 +212,20 @@ void Map::terminate() {
painter.terminate();
}
+void Map::setReachability(bool reachable) {
+ // Note: This function may be called from *any* thread.
+ if (reachable) {
+ if (fileSource) {
+ fileSource->prepare([&]() {
+ fileSource->retryAllPending();
+ });
+ }
+ }
+}
+
void Map::render(uv_async_t *async, int status) {
Map *map = static_cast<Map *>(async->data);
-
+ assert(uv_thread_self() == map->map_thread);
if (map->state.hasSize()) {
if (map->is_rendered.test_and_set() == false) {
@@ -175,44 +245,48 @@ void Map::render(uv_async_t *async, int status) {
void Map::terminate(uv_async_t *async, int status) {
// Closes all open handles on the loop. This means that the loop will automatically terminate.
- uv_loop_t *loop = static_cast<uv_loop_t *>(async->data);
- uv_walk(loop, [](uv_handle_t *handle, void */*arg*/) {
- if (!uv_is_closing(handle)) {
- uv_close(handle, NULL);
- }
- }, NULL);
+ Map *map = static_cast<Map *>(async->data);
+ assert(uv_thread_self() == map->map_thread);
+
+ // Remove all of these to make sure they are destructed in the correct thread.
+ map->glyphStore.reset();
+ map->fileSource.reset();
+ map->style.reset();
+ map->workers.reset();
+ map->activeSources.clear();
+
+ uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr);
+ uv_close((uv_handle_t *)map->async_render.get(), nullptr);
+ uv_close((uv_handle_t *)map->async_terminate.get(), nullptr);
}
#pragma mark - Setup
void Map::setup() {
+ assert(uv_thread_self() == map_thread);
view.make_active();
painter.setup();
view.make_inactive();
}
void Map::setStyleURL(const std::string &url) {
- fileSource->load(ResourceType::JSON, url, [&](platform::Response *res) {
- if (res->code == 200) {
- // Calculate the base
- const size_t pos = url.rfind('/');
- std::string base = "";
- if (pos != std::string::npos) {
- base = url.substr(0, pos + 1);
- }
-
- this->setStyleJSON(res->body, base);
- } else {
- Log::Error(Event::Setup, "loading style failed: %d (%s)", res->code, res->error_message.c_str());
- }
- }, loop);
+ // TODO: Make threadsafe.
+ styleURL = url;
}
void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
+ // TODO: Make threadsafe.
styleJSON.swap(newStyleJSON);
sprite.reset();
+ if (!style) {
+ style = std::make_shared<Style>();
+ }
style->loadJSON((const uint8_t *)styleJSON.c_str());
+ if (!fileSource) {
+ fileSource = std::make_shared<FileSource>(**loop, platform::defaultCacheDatabase());
+ glyphStore = std::make_shared<GlyphStore>(fileSource);
+ }
fileSource->setBase(base);
glyphStore->setURL(util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken()));
update();
@@ -223,6 +297,7 @@ std::string Map::getStyleJSON() const {
}
void Map::setAccessToken(std::string access_token) {
+ // TODO: Make threadsafe.
accessToken.swap(access_token);
}
@@ -230,7 +305,7 @@ std::string Map::getAccessToken() const {
return accessToken;
}
-std::shared_ptr<Sprite> Map::getSprite() {
+util::ptr<Sprite> Map::getSprite() {
const float pixelRatio = state.getPixelRatio();
const std::string &sprite_url = style->getSpriteURL();
if (!sprite || sprite->pixelRatio != pixelRatio) {
@@ -460,8 +535,10 @@ void Map::setDefaultTransitionDuration(uint64_t duration_milliseconds) {
}
void Map::updateSources() {
+ assert(uv_thread_self() == map_thread);
+
// First, disable all existing sources.
- for (const std::shared_ptr<StyleSource> &source : activeSources) {
+ for (const util::ptr<StyleSource> &source : activeSources) {
source->enabled = false;
}
@@ -469,7 +546,7 @@ void Map::updateSources() {
updateSources(style->layers);
// Then, construct or destroy the actual source object, depending on enabled state.
- for (const std::shared_ptr<StyleSource> &style_source : activeSources) {
+ for (const util::ptr<StyleSource> &style_source : activeSources) {
if (style_source->enabled) {
if (!style_source->source) {
style_source->source = std::make_shared<Source>(style_source->info);
@@ -481,20 +558,20 @@ void Map::updateSources() {
}
// Finally, remove all sources that are disabled.
- util::erase_if(activeSources, [](std::shared_ptr<StyleSource> source){
+ util::erase_if(activeSources, [](util::ptr<StyleSource> source){
return !source->enabled;
});
}
-const std::set<std::shared_ptr<StyleSource>> Map::getActiveSources() const {
+const std::set<util::ptr<StyleSource>> Map::getActiveSources() const {
return activeSources;
}
-void Map::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
+void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
if (!group) {
return;
}
- for (const std::shared_ptr<StyleLayer> &layer : group->layers) {
+ for (const util::ptr<StyleLayer> &layer : group->layers) {
if (!layer) continue;
if (layer->bucket) {
if (layer->bucket->style_source) {
@@ -507,7 +584,7 @@ void Map::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
}
void Map::updateTiles() {
- for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
+ for (const util::ptr<StyleSource> &source : getActiveSources()) {
source->source->update(*this);
}
}
@@ -515,13 +592,37 @@ void Map::updateTiles() {
void Map::updateRenderState() {
// Update all clipping IDs.
ClipIDGenerator generator;
- for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
+ for (const util::ptr<StyleSource> &source : getActiveSources()) {
generator.update(source->source->getLoadedTiles());
source->source->updateMatrices(painter.projMatrix, state);
}
}
void Map::prepare() {
+ if (!fileSource) {
+ fileSource = std::make_shared<FileSource>(**loop, platform::defaultCacheDatabase());
+ glyphStore = std::make_shared<GlyphStore>(fileSource);
+ }
+
+ if (!style) {
+ style = std::make_shared<Style>();
+
+ fileSource->request(ResourceType::JSON, styleURL)->onload([&](const Response &res) {
+ if (res.code == 200) {
+ // Calculate the base
+ const size_t pos = styleURL.rfind('/');
+ std::string base = "";
+ if (pos != std::string::npos) {
+ base = styleURL.substr(0, pos + 1);
+ }
+
+ setStyleJSON(res.data, base);
+ } else {
+ Log::Error(Event::Setup, "loading style failed: %ld (%s)", res.code, res.message.c_str());
+ }
+ });
+ }
+
// Update transform transitions.
animationTime = util::now();
if (transform.needsTransition()) {
@@ -568,7 +669,7 @@ void Map::render() {
// This guarantees that we have at least one function per tile called.
// When only rendering layers via the stylesheet, it's possible that we don't
// ever visit a tile during rendering.
- for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
+ for (const util::ptr<StyleSource> &source : getActiveSources()) {
source->source->finishRender(painter);
}
@@ -582,7 +683,7 @@ void Map::render() {
view.make_inactive();
}
-void Map::renderLayers(std::shared_ptr<StyleLayerGroup> group) {
+void Map::renderLayers(util::ptr<StyleLayerGroup> group) {
if (!group) {
// Make sure that we actually do have a layer group.
return;
@@ -625,7 +726,7 @@ void Map::renderLayers(std::shared_ptr<StyleLayerGroup> group) {
}
}
-void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id, const mat4* matrix) {
+void Map::renderLayer(util::ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id, const mat4* matrix) {
if (layer_desc->type == StyleLayerType::Background) {
// This layer defines a background color/image.
diff --git a/src/map/raster_tile_data.cpp b/src/map/raster_tile_data.cpp
index 182a614393..e7725b7abd 100644
--- a/src/map/raster_tile_data.cpp
+++ b/src/map/raster_tile_data.cpp
@@ -5,7 +5,7 @@
using namespace mbgl;
-RasterTileData::RasterTileData(Tile::ID id, Map &map, const SourceInfo &source)
+RasterTileData::RasterTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source)
: TileData(id, map, source),
bucket(map.getTexturepool(), properties) {
}
@@ -25,10 +25,10 @@ void RasterTileData::parse() {
}
}
-void RasterTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) {
+void RasterTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
bucket.render(painter, layer_desc, id, matrix);
}
-bool RasterTileData::hasData(std::shared_ptr<StyleLayer> /*layer_desc*/) const {
+bool RasterTileData::hasData(util::ptr<StyleLayer> /*layer_desc*/) const {
return bucket.hasData();
}
diff --git a/src/map/source.cpp b/src/map/source.cpp
index 083e931b7a..36f1a71c84 100644
--- a/src/map/source.cpp
+++ b/src/map/source.cpp
@@ -6,7 +6,7 @@
#include <mbgl/util/raster.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/texturepool.hpp>
-#include <mbgl/util/filesource.hpp>
+#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
@@ -20,7 +20,7 @@
namespace mbgl {
-Source::Source(SourceInfo& info)
+Source::Source(const util::ptr<SourceInfo>& info)
: info(info)
{
}
@@ -29,32 +29,33 @@ Source::Source(SourceInfo& info)
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
void Source::load(Map& map) {
- if (info.url.empty()) {
+ if (info->url.empty()) {
loaded = true;
return;
}
- std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken());
- std::shared_ptr<Source> source = shared_from_this();
+ std::string url = util::mapbox::normalizeSourceURL(info->url, map.getAccessToken());
+ util::ptr<Source> source = shared_from_this();
- map.getFileSource()->load(ResourceType::JSON, url, [source, &map](platform::Response *res) {
- if (res->code != 200) {
+ map.getFileSource()->request(ResourceType::JSON, url)->onload([source, &map](const Response &res) {
+ if (res.code != 200) {
Log::Warning(Event::General, "failed to load source TileJSON");
return;
}
rapidjson::Document d;
- d.Parse<0>(res->body.c_str());
+ d.Parse<0>(res.data.c_str());
if (d.HasParseError()) {
Log::Warning(Event::General, "invalid source TileJSON");
return;
}
- source->info.parseTileJSONProperties(d);
+ source->info->parseTileJSONProperties(d);
source->loaded = true;
map.update();
+
});
}
@@ -98,7 +99,7 @@ void Source::drawClippingMasks(Painter &painter) {
}
}
-void Source::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) {
+void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc) {
gl::group group(std::string("layer: ") + layer_desc->id);
for (const std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
Tile &tile = *pair.second;
@@ -108,7 +109,7 @@ void Source::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) {
}
}
-void Source::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
+void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
auto it = tiles.find(id);
if (it != tiles.end() && it->second->data && it->second->data->state == TileData::State::parsed) {
painter.renderTileLayer(*it->second, layer_desc, matrix);
@@ -183,9 +184,9 @@ TileData::State Source::addTile(Map &map, const Tile::ID& id) {
if (!new_tile.data) {
// If we don't find working tile data, we're just going to load it.
- if (info.type == SourceType::Vector) {
+ if (info->type == SourceType::Vector) {
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, info);
- } else if (info.type == SourceType::Raster) {
+ } else if (info->type == SourceType::Raster) {
new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, info);
} else {
throw std::runtime_error("source type not implemented");
@@ -199,7 +200,7 @@ TileData::State Source::addTile(Map &map, const Tile::ID& id) {
}
double Source::getZoom(const TransformState& state) const {
- double offset = std::log(util::tileSize / info.tile_size) / std::log(2);
+ double offset = std::log(util::tileSize / info->tile_size) / std::log(2);
offset += (state.getPixelRatio() > 1.0 ? 1 :0);
return state.getZoom() + offset;
}
@@ -211,8 +212,8 @@ int32_t Source::coveringZoomLevel(const TransformState& state) const {
std::forward_list<Tile::ID> Source::coveringTiles(const TransformState& state) const {
int32_t z = coveringZoomLevel(state);
- if (z < info.min_zoom) return {{}};
- if (z > info.max_zoom) z = info.max_zoom;
+ if (z < info->min_zoom) return {{}};
+ if (z > info->max_zoom) z = info->max_zoom;
// Map four viewport corners to pixel coordinates
box points = state.cornersToBox(z);
@@ -285,8 +286,8 @@ bool Source::updateTiles(Map &map) {
std::forward_list<Tile::ID> required = coveringTiles(map.getState());
// Determine the overzooming/underzooming amounts.
- int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info.min_zoom, info.max_zoom);
- int32_t maxCoveringZoom = util::clamp<int32_t>(zoom + 1, info.min_zoom, info.max_zoom);
+ int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info->min_zoom, info->max_zoom);
+ int32_t maxCoveringZoom = util::clamp<int32_t>(zoom + 1, info->min_zoom, info->max_zoom);
// Retain is a list of tiles that we shouldn't delete, even if they are not
// the most ideal tile for the current viewport. This may include tiles like
@@ -333,7 +334,7 @@ bool Source::updateTiles(Map &map) {
// Remove all the expired pointers from the set.
util::erase_if(tile_data, [&retain_data](std::pair<const Tile::ID, std::weak_ptr<TileData>> &pair) {
- const std::shared_ptr<TileData> tile = pair.second.lock();
+ const util::ptr<TileData> tile = pair.second.lock();
if (!tile) {
return true;
}
diff --git a/src/map/sprite.cpp b/src/map/sprite.cpp
index af9413a0e3..c069ece45a 100644
--- a/src/map/sprite.cpp
+++ b/src/map/sprite.cpp
@@ -5,7 +5,7 @@
#include <string>
#include <mbgl/platform/platform.hpp>
-#include <mbgl/util/filesource.hpp>
+#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/std.hpp>
@@ -22,8 +22,8 @@ SpritePosition::SpritePosition(uint16_t x, uint16_t y, uint16_t width, uint16_t
sdf(sdf) {
}
-std::shared_ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource) {
- std::shared_ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
+util::ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, const util::ptr<FileSource> &fileSource) {
+ util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
sprite->load(fileSource);
return sprite;
}
@@ -51,7 +51,7 @@ Sprite::operator bool() const {
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Sprite::load(const std::shared_ptr<FileSource> &fileSource) {
+void Sprite::load(const util::ptr<FileSource> &fileSource) {
if (!valid) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loadedImage = true;
@@ -60,30 +60,30 @@ void Sprite::load(const std::shared_ptr<FileSource> &fileSource) {
return;
}
- std::shared_ptr<Sprite> sprite = shared_from_this();
+ util::ptr<Sprite> sprite = shared_from_this();
- fileSource->load(ResourceType::JSON, jsonURL, [sprite](platform::Response *res) {
- if (res->code == 200) {
- sprite->body.swap(res->body);
+ fileSource->request(ResourceType::JSON, jsonURL)->onload([sprite](const Response &res) {
+ if (res.code == 200) {
+ sprite->body = res.data;
sprite->parseJSON();
sprite->complete();
} else {
- Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res->code, res->error_message.c_str());
+ Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res.code, res.message.c_str());
if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message)));
+ sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
}
});
- fileSource->load(ResourceType::Image, spriteURL, [sprite](platform::Response *res) {
- if (res->code == 200) {
- sprite->image.swap(res->body);
+ fileSource->request(ResourceType::Image, spriteURL)->onload([sprite](const Response &res) {
+ if (res.code == 200) {
+ sprite->image = res.data;
sprite->parseImage();
sprite->complete();
} else {
- Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res->code, res->error_message.c_str());
+ Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res.code, res.message.c_str());
if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message)));
+ sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
}
});
diff --git a/src/map/tile_data.cpp b/src/map/tile_data.cpp
index 57afb8dadb..44e3826bf5 100644
--- a/src/map/tile_data.cpp
+++ b/src/map/tile_data.cpp
@@ -4,12 +4,12 @@
#include <mbgl/util/token.hpp>
#include <mbgl/util/string.hpp>
-#include <mbgl/util/filesource.hpp>
+#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/uv_detail.hpp>
using namespace mbgl;
-TileData::TileData(Tile::ID id, Map &map, const SourceInfo &source)
+TileData::TileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source)
: id(id),
state(State::initial),
map(map),
@@ -29,10 +29,10 @@ const std::string TileData::toString() const {
}
void TileData::request() {
- if (source.tiles.empty())
+ if (source->tiles.empty())
return;
- std::string url = source.tiles[(id.x + id.y) % source.tiles.size()];
+ std::string url = source->tiles[(id.x + id.y) % source->tiles.size()];
url = util::replaceTokens(url, [&](const std::string &token) -> std::string {
if (token == "z") return std::to_string(id.z);
if (token == "x") return std::to_string(id.x);
@@ -45,21 +45,28 @@ void TileData::request() {
// Note: Somehow this feels slower than the change to request_http()
std::weak_ptr<TileData> weak_tile = shared_from_this();
- map.getFileSource()->load(ResourceType::Tile, url, [weak_tile, url](platform::Response *res) {
- std::shared_ptr<TileData> tile = weak_tile.lock();
+ req = map.getFileSource()->request(ResourceType::Tile, url);
+ req->onload([weak_tile, url](const Response &res) {
+ util::ptr<TileData> tile = weak_tile.lock();
if (!tile || tile->state == State::obsolete) {
// noop. Tile is obsolete and we're now just waiting for the refcount
// to drop to zero for destruction.
- } else if (res->code == 200) {
+ return;
+ }
+
+ // Clear the request object.
+ tile->req.reset();
+
+ if (res.code == 200) {
tile->state = State::loaded;
- tile->data.swap(res->body);
+ tile->data = res.data;
// Schedule tile parsing in another thread
tile->reparse();
} else {
#if defined(DEBUG)
- fprintf(stderr, "[%s] tile loading failed: %d, %s\n", url.c_str(), res->code, res->error_message.c_str());
+ fprintf(stderr, "[%s] tile loading failed: %ld, %s\n", url.c_str(), res.code, res.message.c_str());
#endif
}
});
@@ -68,7 +75,7 @@ void TileData::request() {
void TileData::cancel() {
if (state != State::obsolete) {
state = State::obsolete;
- platform::cancel_request_http(req.lock());
+ req.reset();
}
}
@@ -79,12 +86,12 @@ void TileData::reparse() {
// We're creating a new work request. The work request deletes itself after it executed
// the after work handler
- new uv::work<std::shared_ptr<TileData>>(
- map.getLoop(),
- [](std::shared_ptr<TileData> &tile) {
+ new uv::work<util::ptr<TileData>>(
+ map.getWorker(),
+ [](util::ptr<TileData> &tile) {
tile->parse();
},
- [](std::shared_ptr<TileData> &tile) {
+ [](util::ptr<TileData> &tile) {
tile->afterParse();
tile->map.update();
},
diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp
index 6923c1a422..3dc5cb9cef 100644
--- a/src/map/tile_parser.cpp
+++ b/src/map/tile_parser.cpp
@@ -38,19 +38,26 @@ namespace mbgl {
TileParser::~TileParser() = default;
TileParser::TileParser(const std::string &data, VectorTileData &tile,
- const std::shared_ptr<const Style> &style,
- const std::shared_ptr<GlyphAtlas> &glyphAtlas,
- const std::shared_ptr<GlyphStore> &glyphStore,
- const std::shared_ptr<SpriteAtlas> &spriteAtlas,
- const std::shared_ptr<Sprite> &sprite)
+ const util::ptr<const Style> &style_,
+ const util::ptr<GlyphAtlas> &glyphAtlas_,
+ const util::ptr<GlyphStore> &glyphStore_,
+ const util::ptr<SpriteAtlas> &spriteAtlas_,
+ const util::ptr<Sprite> &sprite_)
: vector_data(pbf((const uint8_t *)data.data(), data.size())),
tile(tile),
- style(style),
- glyphAtlas(glyphAtlas),
- glyphStore(glyphStore),
- spriteAtlas(spriteAtlas),
- sprite(sprite),
- collision(std::make_unique<Collision>(tile.id.z, 4096, tile.source.tile_size, tile.depth)) {
+ style(style_),
+ glyphAtlas(glyphAtlas_),
+ glyphStore(glyphStore_),
+ spriteAtlas(spriteAtlas_),
+ sprite(sprite_),
+ collision(std::make_unique<Collision>(tile.id.z, 4096, tile.source->tile_size, tile.depth)) {
+ assert(&tile != nullptr);
+ assert(style);
+ assert(glyphAtlas);
+ assert(glyphStore);
+ assert(spriteAtlas);
+ assert(sprite);
+ assert(collision);
}
void TileParser::parse() {
@@ -59,12 +66,12 @@ void TileParser::parse() {
bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; }
-void TileParser::parseStyleLayers(std::shared_ptr<StyleLayerGroup> group) {
+void TileParser::parseStyleLayers(util::ptr<StyleLayerGroup> group) {
if (!group) {
return;
}
- for (const std::shared_ptr<StyleLayer> &layer_desc : group->layers) {
+ for (const util::ptr<StyleLayer> &layer_desc : group->layers) {
// Cancel early when parsing.
if (obsolete()) {
return;
@@ -96,14 +103,14 @@ void TileParser::parseStyleLayers(std::shared_ptr<StyleLayerGroup> group) {
}
}
-std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bucket_desc) {
+std::unique_ptr<Bucket> TileParser::createBucket(util::ptr<StyleBucket> bucket_desc) {
if (!bucket_desc) {
fprintf(stderr, "missing bucket desc\n");
return nullptr;
}
// Skip this bucket if we are to not render this
- if (tile.id.z < std::floor(bucket_desc->min_zoom) && std::floor(bucket_desc->min_zoom) < tile.source.max_zoom) return nullptr;
+ if (tile.id.z < std::floor(bucket_desc->min_zoom) && std::floor(bucket_desc->min_zoom) < tile.source->max_zoom) return nullptr;
if (tile.id.z >= std::ceil(bucket_desc->max_zoom)) return nullptr;
auto layer_it = vector_data.layers.find(bucket_desc->source_layer);
@@ -157,7 +164,7 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& laye
return obsolete() ? nullptr : std::move(bucket);
}
-std::unique_ptr<Bucket> TileParser::createRasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster &raster) {
+std::unique_ptr<Bucket> TileParser::createRasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster &raster) {
std::unique_ptr<RasterBucket> bucket = std::make_unique<RasterBucket>(texturepool, raster);
return obsolete() ? nullptr : std::move(bucket);
}
diff --git a/src/map/transform.cpp b/src/map/transform.cpp
index 6c5e70cc2f..baa615b94a 100644
--- a/src/map/transform.cpp
+++ b/src/map/transform.cpp
@@ -466,7 +466,7 @@ bool Transform::needsTransition() const {
void Transform::updateTransitions(const timestamp now) {
uv::writelock lock(mtx);
- transitions.remove_if([now](const std::shared_ptr<util::transition> &transition) {
+ transitions.remove_if([now](const util::ptr<util::transition> &transition) {
return transition->update(now) == util::transition::complete;
});
}
diff --git a/src/map/vector_tile_data.cpp b/src/map/vector_tile_data.cpp
index 1f74cab1d7..48b46059a5 100644
--- a/src/map/vector_tile_data.cpp
+++ b/src/map/vector_tile_data.cpp
@@ -8,13 +8,13 @@
using namespace mbgl;
-VectorTileData::VectorTileData(Tile::ID id, Map &map, const SourceInfo &source)
+VectorTileData::VectorTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source)
: TileData(id, map, source),
- depth(id.z >= source.max_zoom ? map.getMaxZoom() - id.z : 1) {
+ depth(id.z >= source->max_zoom ? map.getMaxZoom() - id.z : 1) {
}
VectorTileData::~VectorTileData() {
- std::shared_ptr<GlyphAtlas> glyphAtlas = map.getGlyphAtlas();
+ util::ptr<GlyphAtlas> glyphAtlas = map.getGlyphAtlas();
if (glyphAtlas) {
glyphAtlas->removeGlyphs(id.to_uint64());
}
@@ -52,7 +52,7 @@ void VectorTileData::afterParse() {
parser.reset();
}
-void VectorTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) {
+void VectorTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
if (state == State::parsed && layer_desc->bucket) {
auto databucket_it = buckets.find(layer_desc->bucket->name);
if (databucket_it != buckets.end()) {
@@ -62,7 +62,7 @@ void VectorTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_
}
}
-bool VectorTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const {
+bool VectorTileData::hasData(util::ptr<StyleLayer> layer_desc) const {
if (state == State::parsed && layer_desc->bucket) {
auto databucket_it = buckets.find(layer_desc->bucket->name);
if (databucket_it != buckets.end()) {
diff --git a/src/platform/request.cpp b/src/platform/request.cpp
deleted file mode 100644
index 82e80b9a7c..0000000000
--- a/src/platform/request.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#include <mbgl/platform/request.hpp>
-#include <mbgl/platform/platform.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/uv_detail.hpp>
-
-using namespace mbgl::platform;
-
-Request::Request(const std::string &url,
- std::function<void(Response *)> callback,
- std::shared_ptr<uv::loop> loop)
- : url(url),
- res(std::make_unique<Response>(callback)),
- cancelled(false),
- loop(loop) {
- if (loop) {
- // Add a check handle without a callback to keep the default loop running.
- // We don't have a real handler attached to the default loop right from the
- // beginning, because we're using asynchronous messaging to perform the actual
- // request in the request thread. Only after the request is complete, we
- // create an actual work request that is attached to the default loop.
- async = new uv_async_t();
- async->data = new std::unique_ptr<Response>();
- uv_async_init(**loop, async, &complete);
- }
-}
-
-Request::~Request() {
-}
-
-void Request::complete() {
- if (loop) {
- // We're scheduling the response callback to be invoked in the event loop.
- // Since the Request object will be deleted before the callback is invoked,
- // we move over the Response object to be owned by the async handle.
- ((std::unique_ptr<Response> *)async->data)->swap(res);
- uv_async_send(async);
- } else {
- // We're calling the response callback immediately. We're currently on an
- // arbitrary thread, but that's okay.
- res->callback(res.get());
- }
-}
-
-void Request::complete(uv_async_t *async, int status) {
- Response *res = static_cast<std::unique_ptr<Response> *>(async->data)->get();
-
- res->callback(res);
-
- // We need to remove our async handle again to allow the main event loop to exit.
- uv_close((uv_handle_t *)async, [](uv_handle_t *handle) {
- delete static_cast<std::unique_ptr<Response> *>(handle->data);
- delete (uv_async_t *)handle;
- });
-}
diff --git a/src/renderer/debug_bucket.cpp b/src/renderer/debug_bucket.cpp
index a3ac329f49..699c1c1db9 100644
--- a/src/renderer/debug_bucket.cpp
+++ b/src/renderer/debug_bucket.cpp
@@ -14,7 +14,7 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer)
: fontBuffer(fontBuffer) {
}
-void DebugBucket::render(Painter& painter, std::shared_ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/, const mat4 &matrix) {
+void DebugBucket::render(Painter& painter, util::ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/, const mat4 &matrix) {
painter.renderDebugText(*this, matrix);
}
diff --git a/src/renderer/fill_bucket.cpp b/src/renderer/fill_bucket.cpp
index 875cc279b9..5358cae0b2 100644
--- a/src/renderer/fill_bucket.cpp
+++ b/src/renderer/fill_bucket.cpp
@@ -204,7 +204,7 @@ void FillBucket::tessellate() {
lineGroup.vertex_length += total_vertex_count;
}
-void FillBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void FillBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
painter.renderFill(*this, layer_desc, id, matrix);
}
diff --git a/src/renderer/line_bucket.cpp b/src/renderer/line_bucket.cpp
index 2e89a7c35d..3ef7411be6 100644
--- a/src/renderer/line_bucket.cpp
+++ b/src/renderer/line_bucket.cpp
@@ -341,7 +341,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
}
}
-void LineBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void LineBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
painter.renderLine(*this, layer_desc, id, matrix);
}
diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp
index 0d01fd1662..8988112585 100644
--- a/src/renderer/painter.cpp
+++ b/src/renderer/painter.cpp
@@ -197,7 +197,7 @@ void Painter::prepareTile(const Tile& tile) {
glStencilFunc(GL_EQUAL, ref, mask);
}
-void Painter::renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) {
+void Painter::renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
assert(tile.data);
if (tile.data->hasData(layer_desc) || layer_desc->type == StyleLayerType::Raster) {
gl::group group(util::sprintf<32>("render %d/%d/%d\n", tile.id.z, tile.id.y, tile.id.z));
@@ -206,7 +206,7 @@ void Painter::renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> laye
}
}
-void Painter::renderBackground(std::shared_ptr<StyleLayer> layer_desc) {
+void Painter::renderBackground(util::ptr<StyleLayer> layer_desc) {
const BackgroundProperties& properties = layer_desc->getProperties<BackgroundProperties>();
Color color = properties.color;
diff --git a/src/renderer/painter_clipping.cpp b/src/renderer/painter_clipping.cpp
index 45b14a2f78..dc625ded4e 100644
--- a/src/renderer/painter_clipping.cpp
+++ b/src/renderer/painter_clipping.cpp
@@ -6,7 +6,7 @@
using namespace mbgl;
-void Painter::drawClippingMasks(const std::set<std::shared_ptr<StyleSource>> &sources) {
+void Painter::drawClippingMasks(const std::set<util::ptr<StyleSource>> &sources) {
gl::group group("clipping masks");
useProgram(plainShader->program);
@@ -17,7 +17,7 @@ void Painter::drawClippingMasks(const std::set<std::shared_ptr<StyleSource>> &so
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
- for (const std::shared_ptr<StyleSource> &source : sources) {
+ for (const util::ptr<StyleSource> &source : sources) {
source->source->drawClippingMasks(*this);
}
diff --git a/src/renderer/painter_fill.cpp b/src/renderer/painter_fill.cpp
index 6d56b9077e..6c17ab037a 100644
--- a/src/renderer/painter_fill.cpp
+++ b/src/renderer/painter_fill.cpp
@@ -10,7 +10,7 @@
using namespace mbgl;
-void Painter::renderFill(FillBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void Painter::renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
// Abort early.
if (!bucket.hasData()) return;
diff --git a/src/renderer/painter_line.cpp b/src/renderer/painter_line.cpp
index 161abcd6ff..cd973b46d6 100644
--- a/src/renderer/painter_line.cpp
+++ b/src/renderer/painter_line.cpp
@@ -8,7 +8,7 @@
using namespace mbgl;
-void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) return;
if (!bucket.hasData()) return;
@@ -62,7 +62,7 @@ void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_d
bucket.drawPoints(*linejoinShader);
}
- const std::shared_ptr<Sprite> &sprite = map.getSprite();
+ const util::ptr<Sprite> &sprite = map.getSprite();
if (properties.image.size() && sprite) {
SpriteAtlasPosition imagePos = map.getSpriteAtlas()->getPosition(properties.image, *sprite);
diff --git a/src/renderer/painter_raster.cpp b/src/renderer/painter_raster.cpp
index 7f7afc3e84..c362042e55 100644
--- a/src/renderer/painter_raster.cpp
+++ b/src/renderer/painter_raster.cpp
@@ -9,7 +9,7 @@
using namespace mbgl;
-void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
+void Painter::renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
if (pass != RenderPass::Translucent) return;
const RasterProperties &properties = layer_desc->getProperties<RasterProperties>();
@@ -34,7 +34,7 @@ void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> lay
// call updateTiles to get parsed data for sublayers
map.updateTiles();
- for (const std::shared_ptr<StyleLayer> &layer : layer_desc->layers->layers) {
+ for (const util::ptr<StyleLayer> &layer : layer_desc->layers->layers) {
setOpaque();
map.renderLayer(layer, RenderPass::Opaque, &id, &preMatrix);
setTranslucent();
diff --git a/src/renderer/painter_symbol.cpp b/src/renderer/painter_symbol.cpp
index 4fce66217f..415dead70e 100644
--- a/src/renderer/painter_symbol.cpp
+++ b/src/renderer/painter_symbol.cpp
@@ -112,7 +112,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
}
}
-void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
+void Painter::renderSymbol(SymbolBucket &bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) {
return;
diff --git a/src/renderer/raster_bucket.cpp b/src/renderer/raster_bucket.cpp
index 492eea980d..fc5e3dd3c8 100644
--- a/src/renderer/raster_bucket.cpp
+++ b/src/renderer/raster_bucket.cpp
@@ -3,13 +3,13 @@
using namespace mbgl;
-RasterBucket::RasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster& properties)
+RasterBucket::RasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster& properties)
: properties(properties),
texture(properties),
raster(texturepool) {
}
-void RasterBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
+void RasterBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
painter.renderRaster(*this, layer_desc, id, matrix);
}
diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp
index 374ea2dc26..3a4b017ef3 100644
--- a/src/renderer/symbol_bucket.cpp
+++ b/src/renderer/symbol_bucket.cpp
@@ -22,7 +22,7 @@ namespace mbgl {
SymbolBucket::SymbolBucket(const StyleBucketSymbol &properties, Collision &collision)
: properties(properties), collision(collision) {}
-void SymbolBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc,
+void SymbolBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc,
const Tile::ID &id, const mat4 &matrix) {
painter.renderSymbol(*this, layer_desc, id, matrix);
}
diff --git a/src/storage/base_request.cpp b/src/storage/base_request.cpp
new file mode 100644
index 0000000000..e87679ae1b
--- /dev/null
+++ b/src/storage/base_request.cpp
@@ -0,0 +1,86 @@
+#include <mbgl/storage/base_request.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/storage/request.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+
+namespace mbgl {
+
+template <typename T, typename ...Args>
+void invoke(const std::forward_list<std::unique_ptr<Callback>> &list, Args&& ...args) {
+ for (const std::unique_ptr<Callback> &callback : list) {
+ assert(callback);
+ if (callback->is<T>()) {
+ callback->get<T>()(::std::forward<Args>(args)...);
+ }
+ }
+}
+
+BaseRequest::BaseRequest(const std::string &path_) : thread_id(uv_thread_self()), path(path_) {
+}
+
+// A base request can only be "canceled" by destroying the object. In that case, we'll have to
+// notify all cancel callbacks.
+BaseRequest::~BaseRequest() {
+ assert(thread_id == uv_thread_self());
+ notify();
+}
+
+void BaseRequest::retryImmediately() {
+ // no-op. override in child class.
+}
+
+void BaseRequest::notify() {
+ assert(thread_id == uv_thread_self());
+
+ // The parameter exists solely so that any calls to ->remove()
+ // are not going to cause deallocation of this object while this call is in progress.
+ util::ptr<BaseRequest> retain = self;
+
+ // Swap the lists so that it's safe for callbacks to call ->cancel()
+ // on the request object, which would modify the list.
+ const std::forward_list<std::unique_ptr<Callback>> list = std::move(callbacks);
+ callbacks.clear();
+
+ if (response) {
+ invoke<CompletedCallback>(list, *response);
+ } else {
+ invoke<AbortedCallback>(list);
+ }
+
+ self.reset();
+}
+
+Callback *BaseRequest::add(Callback &&callback, const util::ptr<BaseRequest> &request) {
+ assert(thread_id == uv_thread_self());
+ assert(this == request.get());
+
+ if (response) {
+ // We already have a response. Notify right away.
+ if (callback.is<CompletedCallback>()) {
+ callback.get<CompletedCallback>()(*response);
+ } else {
+ // We already know that this request was successful. The AbortedCallback will be discarded
+ // here since it would never be called.
+ }
+ return nullptr;
+ } else {
+ self = request;
+ callbacks.push_front(std::unique_ptr<Callback>(new Callback(std::move(callback))));
+ return callbacks.front().get();
+ }
+}
+
+void BaseRequest::remove(Callback *callback) {
+ assert(thread_id == uv_thread_self());
+ callbacks.remove_if([=](const std::unique_ptr<Callback> &cb) {
+ return cb.get() == callback;
+ });
+ if (callbacks.empty()) {
+ self.reset();
+ }
+}
+
+}
diff --git a/src/storage/file_request.cpp b/src/storage/file_request.cpp
new file mode 100644
index 0000000000..33398d5ef1
--- /dev/null
+++ b/src/storage/file_request.cpp
@@ -0,0 +1,37 @@
+#include <mbgl/storage/file_request.hpp>
+#include <mbgl/storage/file_request_baton.hpp>
+#include <mbgl/storage/response.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+
+#include <unistd.h>
+
+namespace mbgl {
+
+FileRequest::FileRequest(const std::string &path_, uv_loop_t *loop)
+ : BaseRequest(path_), ptr(new FileRequestBaton(this, path, loop)) {
+}
+
+void FileRequest::cancel() {
+ assert(thread_id == uv_thread_self());
+
+ if (ptr) {
+ ptr->cancel();
+
+ // When deleting a FileRequest 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();
+}
+
+FileRequest::~FileRequest() {
+ assert(thread_id == uv_thread_self());
+ cancel();
+}
+
+}
diff --git a/src/storage/file_request_baton.cpp b/src/storage/file_request_baton.cpp
new file mode 100644
index 0000000000..127c78f3e5
--- /dev/null
+++ b/src/storage/file_request_baton.cpp
@@ -0,0 +1,142 @@
+#include <mbgl/storage/file_request_baton.hpp>
+#include <mbgl/storage/file_request.hpp>
+#include <mbgl/storage/response.hpp>
+
+namespace mbgl {
+
+FileRequestBaton::FileRequestBaton(FileRequest *request_, const std::string &path, uv_loop_t *loop)
+ : thread_id(uv_thread_self()), request(request_) {
+ req.data = this;
+ uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, file_opened);
+}
+
+FileRequestBaton::~FileRequestBaton() {
+}
+
+void FileRequestBaton::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 FileRequestBaton::notify_error(uv_fs_t *req) {
+ FileRequestBaton *ptr = (FileRequestBaton *)req->data;
+ assert(ptr->thread_id == uv_thread_self());
+
+ if (ptr->request && req->result < 0 && !ptr->canceled && req->result != UV_ECANCELED) {
+ ptr->request->response = std::unique_ptr<Response>(new Response);
+ ptr->request->response->code = req->result == UV_ENOENT ? 404 : 500;
+ ptr->request->response->message = uv_strerror(int(req->result));
+ ptr->request->notify();
+ }
+}
+
+void FileRequestBaton::file_opened(uv_fs_t *req) {
+ FileRequestBaton *ptr = (FileRequestBaton *)req->data;
+ assert(ptr->thread_id == uv_thread_self());
+
+ if (req->result < 0) {
+ // Opening failed or was canceled. There isn't much left we can do.
+ notify_error(req);
+ cleanup(req);
+ } else {
+ const uv_file fd = uv_file(req->result);
+
+ // 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 FileRequest object has been destructed, or the
+ // request was canceled.
+ uv_fs_close(req->loop, req, fd, file_closed);
+ } else {
+ ptr->fd = fd;
+ uv_fs_fstat(req->loop, req, fd, file_stated);
+ }
+ }
+}
+
+void FileRequestBaton::file_stated(uv_fs_t *req) {
+ FileRequestBaton *ptr = (FileRequestBaton *)req->data;
+ assert(ptr->thread_id == uv_thread_self());
+
+ if (req->result < 0 || ptr->canceled || !ptr->request) {
+ // Stating failed or was canceled. We already have an open file handle
+ // though, which we'll have to close.
+ notify_error(req);
+
+ uv_fs_req_cleanup(req);
+ uv_fs_close(req->loop, req, ptr->fd, file_closed);
+ } else {
+ if (static_cast<const uv_stat_t *>(req->ptr)->st_size > std::numeric_limits<int>::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 = std::unique_ptr<Response>(new Response);
+ ptr->request->response->code = UV_EFBIG;
+ ptr->request->response->message = uv_strerror(UV_EFBIG);
+ ptr->request->notify();
+ }
+
+ uv_fs_req_cleanup(req);
+ uv_fs_close(req->loop, req, ptr->fd, file_closed);
+ } else {
+ const unsigned int size =
+ (unsigned int)(static_cast<const uv_stat_t *>(req->ptr)->st_size);
+ ptr->body.resize(size);
+ ptr->buffer = uv_buf_init(const_cast<char *>(ptr->body.data()), size);
+ uv_fs_req_cleanup(req);
+ uv_fs_read(req->loop, req, ptr->fd, &ptr->buffer, 1, 0, file_read);
+ }
+ }
+}
+
+void FileRequestBaton::file_read(uv_fs_t *req) {
+ FileRequestBaton *ptr = (FileRequestBaton *)req->data;
+ assert(ptr->thread_id == uv_thread_self());
+
+ if (req->result < 0 || ptr->canceled || !ptr->request) {
+ // Stating failed or was canceled. We already have an open file handle
+ // though, which we'll have to close.
+ notify_error(req);
+ } else {
+ // File was successfully read.
+ if (ptr->request) {
+ ptr->request->response = std::unique_ptr<Response>(new Response);
+ ptr->request->response->code = 200;
+ ptr->request->response->data = std::move(ptr->body);
+ ptr->request->notify();
+ }
+ }
+
+ uv_fs_req_cleanup(req);
+ uv_fs_close(req->loop, req, ptr->fd, file_closed);
+}
+
+void FileRequestBaton::file_closed(uv_fs_t *req) {
+ FileRequestBaton *ptr = (FileRequestBaton *)req->data;
+ assert(ptr->thread_id == uv_thread_self());
+
+ if (req->result < 0) {
+ // Closing the file failed. But there isn't anything we can do.
+ }
+
+ cleanup(req);
+}
+
+void FileRequestBaton::cleanup(uv_fs_t *req) {
+ FileRequestBaton *ptr = (FileRequestBaton *)req->data;
+ assert(ptr->thread_id == uv_thread_self());
+
+ if (ptr->request) {
+ ptr->request->ptr = nullptr;
+ }
+
+ uv_fs_req_cleanup(req);
+ delete ptr;
+}
+
+}
diff --git a/src/storage/file_source.cpp b/src/storage/file_source.cpp
new file mode 100644
index 0000000000..12d924416c
--- /dev/null
+++ b/src/storage/file_source.cpp
@@ -0,0 +1,105 @@
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/file_request.hpp>
+#include <mbgl/storage/http_request.hpp>
+#include <mbgl/storage/sqlite_store.hpp>
+#include <mbgl/util/uv-messenger.h>
+
+#include <uv.h>
+
+namespace mbgl {
+
+FileSource::FileSource(uv_loop_t *loop_, const std::string &path)
+ : thread_id(uv_thread_self()),
+ store(!path.empty() ? util::ptr<SQLiteStore>(new SQLiteStore(loop_, path)) : nullptr),
+ loop(loop_),
+ queue(new uv_messenger_t) {
+
+ uv_messenger_init(loop, queue, [](void *ptr) {
+ std::unique_ptr<std::function<void()>> fn { reinterpret_cast<std::function<void()> *>(ptr) };
+ (*fn)();
+ });
+ uv_unref((uv_handle_t *)&queue->async);
+}
+
+FileSource::~FileSource() {
+ assert(thread_id == uv_thread_self());
+ uv_messenger_stop(queue);
+ // NOTE: We don't need to delete the messenger since it will be deleted by the
+ // uv_messenger_stop() function.
+
+ util::ptr<BaseRequest> request;
+
+ // Send a cancel() message to all requests that we are still holding.
+ for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) {
+ if ((request = pair.second.lock())) {
+ request->cancel();
+ }
+ }
+}
+
+void FileSource::setBase(const std::string &value) {
+ assert(thread_id == uv_thread_self());
+ base = value;
+}
+
+const std::string &FileSource::getBase() const {
+ assert(thread_id == uv_thread_self());
+ return base;
+}
+
+std::unique_ptr<Request> FileSource::request(ResourceType type, const std::string &url) {
+ assert(thread_id == uv_thread_self());
+
+ // Make URL absolute.
+ const std::string absoluteURL = [&]() -> std::string {
+ const size_t separator = url.find("://");
+ if (separator == std::string::npos) {
+ // Relative URL.
+ return base + url;
+ } else {
+ return url;
+ }
+ }();
+
+ util::ptr<BaseRequest> request;
+
+ // First, try to find an existing Request object.
+ auto it = pending.find(absoluteURL);
+ if (it != pending.end()) {
+ request = it->second.lock();
+ }
+
+ if (!request) {
+ if (absoluteURL.substr(0, 7) == "file://") {
+ request = std::make_shared<FileRequest>(absoluteURL.substr(7), loop);
+ } else {
+ request = std::make_shared<HTTPRequest>(type, absoluteURL, loop, store);
+ }
+
+ pending.emplace(absoluteURL, request);
+ }
+
+ return std::unique_ptr<Request>(new Request(request));
+}
+
+void FileSource::prepare(std::function<void()> fn) {
+ if (thread_id == uv_thread_self()) {
+ fn();
+ } else {
+ uv_messenger_send(queue, new std::function<void()>(std::move(fn)));
+ }
+}
+
+void FileSource::retryAllPending() {
+ assert(thread_id == uv_thread_self());
+
+ util::ptr<BaseRequest> request;
+ for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) {
+ if ((request = pair.second.lock())) {
+ request->retryImmediately();
+ }
+ }
+
+}
+
+} \ No newline at end of file
diff --git a/src/storage/http_request.cpp b/src/storage/http_request.cpp
new file mode 100644
index 0000000000..1b799d4895
--- /dev/null
+++ b/src/storage/http_request.cpp
@@ -0,0 +1,270 @@
+#include <mbgl/storage/http_request.hpp>
+#include <mbgl/storage/sqlite_store.hpp>
+#include <mbgl/storage/http_request_baton.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+#include <chrono>
+
+namespace mbgl {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+
+struct CacheRequestBaton {
+ HTTPRequest *request = nullptr;
+ std::string path;
+ util::ptr<SQLiteStore> store;
+};
+
+HTTPRequest::HTTPRequest(ResourceType type_, const std::string &path, uv_loop_t *loop_, util::ptr<SQLiteStore> store_)
+ : BaseRequest(path), thread_id(uv_thread_self()), loop(loop_), store(store_), type(type_) {
+ if (store) {
+ startCacheRequest();
+ } else {
+ startHTTPRequest(nullptr);
+ }
+}
+
+void HTTPRequest::startCacheRequest() {
+ assert(uv_thread_self() == thread_id);
+
+ cache_baton = new CacheRequestBaton;
+ cache_baton->request = this;
+ cache_baton->path = path;
+ cache_baton->store = store;
+ store->get(path, [](std::unique_ptr<Response> &&response, void *ptr) {
+ // Wrap in a unique_ptr, so it'll always get auto-destructed.
+ std::unique_ptr<CacheRequestBaton> baton((CacheRequestBaton *)ptr);
+ if (baton->request) {
+ baton->request->cache_baton = nullptr;
+ baton->request->handleCacheResponse(std::move(response));
+ }
+ }, cache_baton);
+}
+
+void HTTPRequest::handleCacheResponse(std::unique_ptr<Response> &&res) {
+ assert(uv_thread_self() == thread_id);
+
+ if (res) {
+ // This entry was stored in the cache. Now determine if we need to revalidate.
+ const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch()).count();
+ if (res->expires > now) {
+ response = std::move(res);
+ notify();
+ // Note: after calling notify(), the request object may cease to exist.
+ // This HTTPRequest is completed.
+ return;
+ } else {
+ // TODO: notify with preliminary results.
+ }
+ }
+
+ startHTTPRequest(std::move(res));
+}
+
+void HTTPRequest::startHTTPRequest(std::unique_ptr<Response> &&res) {
+ assert(uv_thread_self() == thread_id);
+ assert(!http_baton);
+
+ http_baton = std::make_shared<HTTPRequestBaton>(path);
+ http_baton->request = this;
+ http_baton->async = new uv_async_t;
+ http_baton->response = std::move(res);
+ http_baton->async->data = new util::ptr<HTTPRequestBaton>(http_baton);
+
+ uv_async_init(loop, http_baton->async, [](uv_async_t *async) {
+ util::ptr<HTTPRequestBaton> &http_baton = *(util::ptr<HTTPRequestBaton> *)async->data;
+
+ if (http_baton->request) {
+ HTTPRequest *request = http_baton->request;
+ request->http_baton.reset();
+ http_baton->request = nullptr;
+ request->handleHTTPResponse(http_baton->type, std::move(http_baton->response));
+ }
+
+ delete (util::ptr<HTTPRequestBaton> *)async->data;
+ uv_close((uv_handle_t *)async, [](uv_handle_t *handle) {
+ uv_async_t *async = (uv_async_t *)handle;
+ delete async;
+ });
+ });
+ attempts++;
+ HTTPRequestBaton::start(http_baton);
+}
+
+
+
+void HTTPRequest::handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&res) {
+ assert(uv_thread_self() == thread_id);
+ assert(!http_baton);
+ assert(!response);
+
+ switch (responseType) {
+ // 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.
+ case HTTPResponseType::SingularError:
+ if (attempts >= 4) {
+ // Report as error after 4 attempts.
+ response = std::move(res);
+ notify();
+ } else if (attempts >= 2) {
+ // Switch to the back-off algorithm after the second failure.
+ retryHTTPRequest(std::move(res), (1 << attempts) * 1000);
+ return;
+ } else {
+ startHTTPRequest(std::move(res));
+ }
+ break;
+
+ // 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.
+ case HTTPResponseType::TemporaryError:
+ if (attempts >= 4) {
+ // Report error back after it failed completely.
+ response = std::move(res);
+ notify();
+ } else {
+ retryHTTPRequest(std::move(res), (1 << attempts) * 1000);
+ }
+ break;
+
+ // 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.
+ case HTTPResponseType::ConnectionError:
+
+ if (attempts >= 4) {
+ // Report error back after it failed completely.
+ response = std::move(res);
+ notify();
+ } else {
+ // By default, we will retry every 60 seconds.
+ retryHTTPRequest(std::move(res), 60000);
+ }
+ break;
+
+ // The request was canceled programatically.
+ case HTTPResponseType::Canceled:
+ response.reset();
+ notify();
+ break;
+
+ // This error probably won't be resolved by retrying anytime soon. We are giving up.
+ case HTTPResponseType::PermanentError:
+ response = std::move(res);
+ notify();
+ break;
+
+ // The request returned data successfully. We retrieved and decoded the data successfully.
+ case HTTPResponseType::Successful:
+ if (store) {
+ store->put(path, type, *res);
+ }
+ response = std::move(res);
+ notify();
+ break;
+
+ // The request confirmed that the data wasn't changed. We already have the data.
+ case HTTPResponseType::NotModified:
+ if (store) {
+ store->updateExpiration(path, res->expires);
+ }
+ response = std::move(res);
+ notify();
+ break;
+
+ default:
+ assert(!"Response wasn't set");
+ break;
+ }
+}
+
+using RetryBaton = std::pair<HTTPRequest *, std::unique_ptr<Response>>;
+
+void HTTPRequest::retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout) {
+ assert(uv_thread_self() == thread_id);
+ assert(!backoff_timer);
+ backoff_timer = new uv_timer_t();
+ uv_timer_init(loop, backoff_timer);
+ backoff_timer->data = new RetryBaton(this, std::move(res));
+ uv_timer_start(backoff_timer, [](uv_timer_t *timer) {
+ std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(timer->data) };
+ pair->first->startHTTPRequest(std::move(pair->second));
+ pair->first->backoff_timer = nullptr;
+ uv_timer_stop(timer);
+ uv_close((uv_handle_t *)timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
+ }, timeout, 0);
+}
+
+void HTTPRequest::removeHTTPBaton() {
+ assert(uv_thread_self() == thread_id);
+ if (http_baton) {
+ http_baton->request = nullptr;
+ HTTPRequestBaton::stop(http_baton);
+ http_baton.reset();
+ }
+}
+
+void HTTPRequest::removeCacheBaton() {
+ assert(uv_thread_self() == thread_id);
+ if (cache_baton) {
+ // Make sre that this object doesn't accidentally get accessed when it is destructed before
+ // the callback returned. They are being run in the same thread, so just setting it to
+ // null is sufficient.
+ // Note: We don't manually delete the CacheRequestBaton since it'll be deleted by the
+ // callback.
+ cache_baton->request = nullptr;
+ cache_baton = nullptr;
+ }
+}
+
+void HTTPRequest::removeBackoffTimer() {
+ assert(uv_thread_self() == thread_id);
+ if (backoff_timer) {
+ delete static_cast<RetryBaton *>(backoff_timer->data);
+ uv_timer_stop(backoff_timer);
+ uv_close((uv_handle_t *)backoff_timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
+ backoff_timer = nullptr;
+ }
+}
+
+void HTTPRequest::retryImmediately() {
+ assert(uv_thread_self() == thread_id);
+ if (!cache_baton && !http_baton) {
+ if (backoff_timer) {
+ // Retry immediately.
+ uv_timer_stop(backoff_timer);
+ std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(backoff_timer->data) };
+ assert(pair->first == this);
+ startHTTPRequest(std::move(pair->second));
+ uv_close((uv_handle_t *)backoff_timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
+ backoff_timer = nullptr;
+ } else {
+ assert(!"We should always have a backoff_timer when there are no batons");
+ }
+ }
+}
+
+void HTTPRequest::cancel() {
+ assert(uv_thread_self() == thread_id);
+ removeCacheBaton();
+ removeHTTPBaton();
+ removeBackoffTimer();
+ notify();
+}
+
+
+HTTPRequest::~HTTPRequest() {
+ assert(uv_thread_self() == thread_id);
+ cancel();
+}
+
+#pragma clang diagnostic pop
+
+}
diff --git a/src/storage/http_request_baton.cpp b/src/storage/http_request_baton.cpp
new file mode 100644
index 0000000000..02edae748f
--- /dev/null
+++ b/src/storage/http_request_baton.cpp
@@ -0,0 +1,12 @@
+#include <mbgl/storage/http_request_baton.hpp>
+#include <uv.h>
+
+namespace mbgl {
+
+HTTPRequestBaton::HTTPRequestBaton(const std::string &path_) : thread_id(uv_thread_self()), path(path_) {
+}
+
+HTTPRequestBaton::~HTTPRequestBaton() {
+}
+
+}
diff --git a/src/storage/request.cpp b/src/storage/request.cpp
new file mode 100644
index 0000000000..42bf87a849
--- /dev/null
+++ b/src/storage/request.cpp
@@ -0,0 +1,49 @@
+#include <mbgl/storage/request.hpp>
+#include <mbgl/storage/base_request.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+
+namespace mbgl {
+
+Request::Request(const util::ptr<BaseRequest> &base_)
+ : thread_id(uv_thread_self()), base(base_) {
+}
+
+Request::~Request() {
+ assert(thread_id == uv_thread_self());
+}
+
+void Request::onload(CompletedCallback cb) {
+ assert(thread_id == uv_thread_self());
+ if (base) {
+ Callback *callback = base->add(std::move(cb), base);
+ if (callback) {
+ callbacks.push_front(callback);
+ }
+ }
+}
+
+void Request::oncancel(AbortedCallback cb) {
+ assert(thread_id == uv_thread_self());
+ if (base) {
+ Callback *callback = base->add(std::move(cb), base);
+ if (callback) {
+ callbacks.push_front(callback);
+ }
+ }
+}
+
+void Request::cancel() {
+ assert(thread_id == uv_thread_self());
+ if (base) {
+ for (Callback *callback : callbacks) {
+ base->remove(callback);
+ }
+ base.reset();
+ }
+ callbacks.clear();
+}
+
+}
diff --git a/src/storage/response.cpp b/src/storage/response.cpp
new file mode 100644
index 0000000000..cdaf33e4e4
--- /dev/null
+++ b/src/storage/response.cpp
@@ -0,0 +1,22 @@
+#include <mbgl/storage/response.hpp>
+
+#include <chrono>
+
+namespace mbgl {
+
+int64_t Response::parseCacheControl(const char *value) {
+ if (value) {
+ uint64_t 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::seconds>(
+ std::chrono::system_clock::now().time_since_epoch()).count() +
+ seconds;
+ }
+ }
+
+ return -1;
+}
+
+}
diff --git a/src/storage/sqlite_store.cpp b/src/storage/sqlite_store.cpp
new file mode 100644
index 0000000000..763100f411
--- /dev/null
+++ b/src/storage/sqlite_store.cpp
@@ -0,0 +1,227 @@
+#include <mbgl/storage/sqlite_store.hpp>
+#include <mbgl/util/compression.hpp>
+#include <mbgl/util/sqlite3.hpp>
+
+#include <mbgl/util/uv-worker.h>
+
+#include <cassert>
+
+using namespace mapbox::sqlite;
+
+std::string removeAccessTokenFromURL(const std::string &url) {
+ const size_t token_start = url.find("access_token=");
+ // Ensure that token exists, isn't at the front and is preceded by either & or ?.
+ if (token_start == std::string::npos || token_start == 0 || !(url[token_start - 1] == '&' || url[token_start - 1] == '?')) {
+ return url;
+ }
+
+ const size_t token_end = url.find_first_of('&', token_start);
+ if (token_end == std::string::npos) {
+ // The token is the last query argument. We slice away the "&access_token=..." part
+ return url.substr(0, token_start - 1);
+ } else {
+ // We slice away the "access_token=...&" part.
+ return url.substr(0, token_start) + url.substr(token_end + 1);
+ }
+}
+
+std::string convertMapboxDomainsToProtocol(const std::string &url) {
+ const size_t protocol_separator = url.find("://");
+ if (protocol_separator == std::string::npos) {
+ return url;
+ }
+
+ const std::string protocol = url.substr(0, protocol_separator);
+ if (!(protocol == "http" || protocol == "https")) {
+ return url;
+ }
+
+ const size_t domain_begin = protocol_separator + 3;
+ const size_t path_separator = url.find("/", domain_begin);
+ if (path_separator == std::string::npos) {
+ return url;
+ }
+
+ const std::string domain = url.substr(domain_begin, path_separator - domain_begin);
+ if (domain.find(".tiles.mapbox.com") != std::string::npos) {
+ return "mapbox://" + url.substr(path_separator + 1);
+ } else {
+ return url;
+ }
+}
+
+std::string unifyMapboxURLs(const std::string &url) {
+ return removeAccessTokenFromURL(convertMapboxDomainsToProtocol(url));
+}
+
+namespace mbgl {
+
+SQLiteStore::SQLiteStore(uv_loop_t *loop, const std::string &path)
+ : thread_id(uv_thread_self()),
+ db(std::make_shared<Database>(path.c_str(), ReadWrite | Create)) {
+ createSchema();
+ worker = new uv_worker_t;
+ uv_worker_init(worker, loop, 1, "SQLite");
+}
+
+SQLiteStore::~SQLiteStore() {
+ // Nothing to do. This function needs to be here because we're forward-declaring
+ // Database, so we need the actual definition here to be able to properly destruct it.
+ if (worker) {
+ uv_worker_close(worker, [](uv_worker_t *worker) {
+ delete worker;
+ });
+ }
+}
+
+void SQLiteStore::createSchema() {
+ if (!db || !*db) {
+ return;
+ }
+
+ db->exec("CREATE TABLE IF NOT EXISTS `http_cache` ("
+ " `url` TEXT PRIMARY KEY NOT NULL,"
+ " `code` INTEGER NOT NULL,"
+ " `type` INTEGER NOT NULL,"
+ " `modified` INTEGER,"
+ " `etag` TEXT,"
+ " `expires` INTEGER,"
+ " `data` BLOB,"
+ " `compressed` INTEGER NOT NULL DEFAULT 0"
+ ");"
+ "CREATE INDEX IF NOT EXISTS `http_cache_type_idx` ON `http_cache` (`type`);");
+}
+
+struct GetBaton {
+ util::ptr<Database> db;
+ std::string path;
+ ResourceType type;
+ void *ptr = nullptr;
+ SQLiteStore::GetCallback callback = nullptr;
+ std::unique_ptr<Response> response;
+};
+
+void SQLiteStore::get(const std::string &path, GetCallback callback, void *ptr) {
+ assert(uv_thread_self() == thread_id);
+ if (!db || !*db) {
+ if (callback) {
+ callback(nullptr, ptr);
+ }
+ return;
+ }
+
+ GetBaton *get_baton = new GetBaton;
+ get_baton->db = db;
+ get_baton->path = path;
+ get_baton->ptr = ptr;
+ get_baton->callback = callback;
+
+ uv_worker_send(worker, get_baton, [](void *data) {
+ GetBaton *baton = (GetBaton *)data;
+ const std::string url = unifyMapboxURLs(baton->path);
+ // 0 1 2
+ Statement stmt = baton->db->prepare("SELECT `code`, `type`, `modified`, "
+ // 3 4 5 6
+ "`etag`, `expires`, `data`, `compressed` FROM `http_cache` WHERE `url` = ?");
+
+ stmt.bind(1, url.c_str());
+ if (stmt.run()) {
+ // There is data.
+ baton->response = std::unique_ptr<Response>(new Response);
+
+ baton->response->code = stmt.get<int>(0);
+ baton->type = ResourceType(stmt.get<int>(1));
+ baton->response->modified = stmt.get<int64_t>(2);
+ baton->response->etag = stmt.get<std::string>(3);
+ baton->response->expires = stmt.get<int64_t>(4);
+ baton->response->data = stmt.get<std::string>(5);
+ if (stmt.get<int>(6)) { // == compressed
+ baton->response->data = util::decompress(baton->response->data);
+ }
+ } else {
+ // There is no data.
+ // This is a noop.
+ }
+ }, [](void *data) {
+ std::unique_ptr<GetBaton> baton { (GetBaton *)data };
+ if (baton->callback) {
+ baton->callback(std::move(baton->response), baton->ptr);
+ }
+ });
+}
+
+
+struct PutBaton {
+ util::ptr<Database> db;
+ std::string path;
+ ResourceType type;
+ Response response;
+};
+
+void SQLiteStore::put(const std::string &path, ResourceType type, const Response &response) {
+ assert(uv_thread_self() == thread_id);
+ if (!db) return;
+
+ PutBaton *put_baton = new PutBaton;
+ put_baton->db = db;
+ put_baton->path = path;
+ put_baton->type = type;
+ put_baton->response = response;
+
+ uv_worker_send(worker, put_baton, [](void *data) {
+ PutBaton *baton = (PutBaton *)data;
+ const std::string url = unifyMapboxURLs(baton->path);
+ Statement stmt = baton->db->prepare("REPLACE INTO `http_cache` ("
+ // 1 2 3 4 5 6 7 8
+ "`url`, `code`, `type`, `modified`, `etag`, `expires`, `data`, `compressed`"
+ ") VALUES(?, ?, ?, ?, ?, ?, ?, ?)");
+ stmt.bind(1, url.c_str());
+ stmt.bind(2, int(baton->response.code));
+ stmt.bind(3, int(baton->type));
+ stmt.bind(4, baton->response.modified);
+ stmt.bind(5, baton->response.etag.c_str());
+ stmt.bind(6, baton->response.expires);
+
+ if (baton->type == ResourceType::Image) {
+ stmt.bind(7, baton->response.data, false); // do not retain the string internally.
+ stmt.bind(8, false);
+ } else {
+ stmt.bind(7, util::compress(baton->response.data), true); // retain the string internally.
+ stmt.bind(8, true);
+ }
+
+ stmt.run();
+ }, [](void *data) {
+ delete (PutBaton *)data;
+ });
+}
+
+struct ExpirationBaton {
+ util::ptr<Database> db;
+ std::string path;
+ int64_t expires;
+};
+
+void SQLiteStore::updateExpiration(const std::string &path, int64_t expires) {
+ assert(uv_thread_self() == thread_id);
+ if (!db || !*db) return;
+
+ ExpirationBaton *expiration_baton = new ExpirationBaton;
+ expiration_baton->db = db;
+ expiration_baton->path = path;
+ expiration_baton->expires = expires;
+
+ uv_worker_send(worker, expiration_baton, [](void *data) {
+ ExpirationBaton *baton = (ExpirationBaton *)data;
+ const std::string url = unifyMapboxURLs(baton->path);
+ Statement stmt = // 1 2
+ baton->db->prepare("UPDATE `http_cache` SET `expires` = ? WHERE `url` = ?");
+ stmt.bind<int64_t>(1, baton->expires);
+ stmt.bind(2, url.c_str());
+ stmt.run();
+ }, [](void *data) {
+ delete (ExpirationBaton *)data;
+ });
+}
+
+}
diff --git a/src/style/style_layer_group.cpp b/src/style/style_layer_group.cpp
index c7e4360d21..a731aebdcb 100644
--- a/src/style/style_layer_group.cpp
+++ b/src/style/style_layer_group.cpp
@@ -5,7 +5,7 @@ namespace mbgl {
void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, timestamp now,
const PropertyTransition &defaultTransition) {
- for (const std::shared_ptr<StyleLayer> &layer : layers) {
+ for (const util::ptr<StyleLayer> &layer : layers) {
if (layer) {
layer->setClasses(class_names, now, defaultTransition);
}
@@ -13,7 +13,7 @@ void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, ti
}
void StyleLayerGroup::updateProperties(float z, timestamp t) {
- for (const std::shared_ptr<StyleLayer> &layer: layers) {
+ for (const util::ptr<StyleLayer> &layer: layers) {
if (layer) {
layer->updateProperties(z, t);
}
@@ -21,7 +21,7 @@ void StyleLayerGroup::updateProperties(float z, timestamp t) {
}
bool StyleLayerGroup::hasTransitions() const {
- for (const std::shared_ptr<const StyleLayer> &layer: layers) {
+ for (const util::ptr<const StyleLayer> &layer: layers) {
if (layer) {
if (layer->hasTransitions()) {
return true;
diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp
index ad7694d9a9..2a64ab38f8 100644
--- a/src/style/style_parser.cpp
+++ b/src/style/style_parser.cpp
@@ -176,12 +176,12 @@ void StyleParser::parseSources(JSVal value) {
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
std::string name { itr->name.GetString(), itr->name.GetStringLength() };
- SourceInfo info;
+ util::ptr<SourceInfo> info = std::make_shared<SourceInfo>();
- parseRenderProperty<SourceTypeClass>(itr->value, info.type, "type");
- parseRenderProperty(itr->value, info.url, "url");
- parseRenderProperty(itr->value, info.tile_size, "tileSize");
- info.parseTileJSONProperties(itr->value);
+ parseRenderProperty<SourceTypeClass>(itr->value, info->type, "type");
+ parseRenderProperty(itr->value, info->url, "url");
+ parseRenderProperty(itr->value, info->tile_size, "tileSize");
+ info->parseTileJSONProperties(itr->value);
sources.emplace(std::move(name), std::make_shared<StyleSource>(info));
}
@@ -451,7 +451,7 @@ std::unique_ptr<StyleLayerGroup> StyleParser::createLayers(JSVal value) {
if (value.IsArray()) {
std::unique_ptr<StyleLayerGroup> group = std::make_unique<StyleLayerGroup>();
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
- std::shared_ptr<StyleLayer> layer = createLayer(value[i]);
+ util::ptr<StyleLayer> layer = createLayer(value[i]);
if (layer) {
group->layers.emplace_back(layer);
}
@@ -463,7 +463,7 @@ std::unique_ptr<StyleLayerGroup> StyleParser::createLayers(JSVal value) {
}
}
-std::shared_ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
+util::ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
if (value.IsObject()) {
if (!value.HasMember("id")) {
Log::Warning(Event::ParseStyle, "layer must have an id");
@@ -487,7 +487,7 @@ std::shared_ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
std::map<ClassID, ClassProperties> styles;
parseStyles(value, styles);
- std::shared_ptr<StyleLayer> layer = std::make_shared<StyleLayer>(
+ util::ptr<StyleLayer> layer = std::make_shared<StyleLayer>(
layer_id, std::move(styles));
if (value.HasMember("layers")) {
@@ -495,7 +495,7 @@ std::shared_ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
}
// Store the layer ID so we can reference it later.
- layers.emplace(layer_id, std::pair<JSVal, std::shared_ptr<StyleLayer>> { value, layer });
+ layers.emplace(layer_id, std::pair<JSVal, util::ptr<StyleLayer>> { value, layer });
return layer;
} else {
@@ -505,14 +505,14 @@ std::shared_ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
}
void StyleParser::parseLayers() {
- for (std::pair<const std::string, std::pair<JSVal, std::shared_ptr<StyleLayer>>> &pair : layers) {
+ for (std::pair<const std::string, std::pair<JSVal, util::ptr<StyleLayer>>> &pair : layers) {
parseLayer(pair.second);
}
}
-void StyleParser::parseLayer(std::pair<JSVal, std::shared_ptr<StyleLayer>> &pair) {
+void StyleParser::parseLayer(std::pair<JSVal, util::ptr<StyleLayer>> &pair) {
JSVal value = pair.first;
- std::shared_ptr<StyleLayer> &layer = pair.second;
+ util::ptr<StyleLayer> &layer = pair.second;
if (value.HasMember("type")) {
JSVal type = value["type"];
@@ -642,7 +642,7 @@ void StyleParser::parseStyle(JSVal value, ClassProperties &klass) {
parseOptionalProperty<Function<Color>>("background-color", Key::BackgroundColor, klass, value);
}
-void StyleParser::parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer) {
+void StyleParser::parseReference(JSVal value, util::ptr<StyleLayer> &layer) {
if (!value.IsString()) {
Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", layer->id.c_str());
return;
@@ -661,7 +661,7 @@ void StyleParser::parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer
stack.pop_front();
- std::shared_ptr<StyleLayer> reference = it->second.second;
+ util::ptr<StyleLayer> reference = it->second.second;
layer->type = reference->type;
@@ -676,7 +676,7 @@ void StyleParser::parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer
#pragma mark - Parse Bucket
-void StyleParser::parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer) {
+void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
layer->bucket = std::make_shared<StyleBucket>(layer->type);
// We name the buckets according to the layer that defined it.
@@ -822,7 +822,7 @@ std::vector<Value> StyleParser::parseValues(JSVal value) {
return values;
}
-void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer) {
+void StyleParser::parseRender(JSVal value, util::ptr<StyleLayer> &layer) {
if (!value.IsObject()) {
Log::Warning(Event::ParseStyle, "render property of layer '%s' must be an object", layer->id.c_str());
return;
diff --git a/src/text/glyph.cpp b/src/text/glyph.cpp
index 7dea7246d7..f02c710db2 100644
--- a/src/text/glyph.cpp
+++ b/src/text/glyph.cpp
@@ -3,9 +3,6 @@
namespace mbgl {
// Note: this only works for the BMP
-// Note: we could use a binary lookup table to get averaged constant time lookups, however,
-// most of our lookups are going to be within the first 3 ranges listed here, so this is
-// likely faster.
GlyphRange getGlyphRange(char32_t glyph) {
unsigned start = (glyph/256) * 256;
unsigned end = (start + 255);
diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp
index 783710d929..1723bd3d94 100644
--- a/src/text/glyph_store.cpp
+++ b/src/text/glyph_store.cpp
@@ -8,7 +8,7 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/util/math.hpp>
-#include <mbgl/util/filesource.hpp>
+#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <algorithm>
@@ -137,7 +137,7 @@ void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float m
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
}
-GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const std::shared_ptr<FileSource> &fileSource)
+GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const util::ptr<FileSource> &fileSource)
: future(promise.get_future().share())
{
// Load the glyph set URL
@@ -147,23 +147,26 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl
return "";
});
-#if defined(DEBUG)
- fprintf(stderr, "%s\n", url.c_str());
-#endif
-
- fileSource->load(ResourceType::Glyphs, url, [&](platform::Response *res) {
- if (res->code != 200) {
- // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
- const std::string msg = util::sprintf<255>("[ERROR] failed to load glyphs (%d): %s\n", res->code, res->error_message.c_str());
- promise.set_exception(std::make_exception_ptr(std::runtime_error(msg)));
- } else {
- // Transfer the data to the GlyphSet and signal its availability.
- // Once it is available, the caller will need to call parse() to actually
- // parse the data we received. We are not doing this here since this callback is being
- // called from another (unknown) thread.
- data.swap(res->body);
- promise.set_value(*this);
- }
+ // The prepare call jumps back to the main thread.
+ fileSource->prepare([&, url, fileSource] {
+ auto request = fileSource->request(ResourceType::Glyphs, url);
+ request->onload([&, url](const Response &res) {
+ if (res.code != 200) {
+ // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
+ const std::string msg = util::sprintf<255>("[ERROR] failed to load glyphs (%d): %s\n", res.code, res.message.c_str());
+ promise.set_exception(std::make_exception_ptr(std::runtime_error(msg)));
+ } else {
+ // Transfer the data to the GlyphSet and signal its availability.
+ // Once it is available, the caller will need to call parse() to actually
+ // parse the data we received. We are not doing this here since this callback is being
+ // called from another (unknown) thread.
+ data = res.data;
+ promise.set_value(*this);
+ }
+ });
+ request->oncancel([&]() {
+ promise.set_exception(std::make_exception_ptr(std::runtime_error("Loading glyphs was canceled")));
+ });
});
}
@@ -225,7 +228,7 @@ void GlyphPBF::parse(FontStack &stack) {
data.clear();
}
-GlyphStore::GlyphStore(const std::shared_ptr<FileSource> &fileSource) : fileSource(fileSource) {}
+GlyphStore::GlyphStore(const util::ptr<FileSource> &fileSource) : fileSource(fileSource) {}
void GlyphStore::setURL(const std::string &url) {
glyphURL = url;
diff --git a/src/util/compression.cpp b/src/util/compression.cpp
new file mode 100644
index 0000000000..de4e09764c
--- /dev/null
+++ b/src/util/compression.cpp
@@ -0,0 +1,81 @@
+#include <mbgl/util/compression.hpp>
+
+#include <zlib.h>
+
+#include <cstring>
+#include <stdexcept>
+
+namespace mbgl {
+namespace util {
+
+std::string compress(const std::string &raw) {
+ z_stream deflate_stream;
+ memset(&deflate_stream, 0, sizeof(deflate_stream));
+
+ // TODO: reuse z_streams
+ if (deflateInit(&deflate_stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ throw std::runtime_error("failed to initialize deflate");
+ }
+
+ deflate_stream.next_in = (Bytef *)raw.data();
+ deflate_stream.avail_in = raw.size();
+
+ std::string result;
+ char out[16384];
+
+ int code;
+ do {
+ deflate_stream.next_out = reinterpret_cast<Bytef *>(out);
+ deflate_stream.avail_out = sizeof(out);
+ code = deflate(&deflate_stream, Z_FINISH);
+ if (result.size() < deflate_stream.total_out) {
+ // append the block to the output string
+ result.append(out, deflate_stream.total_out - result.size());
+ }
+ } while (code == Z_OK);
+
+ deflateEnd(&deflate_stream);
+
+ if (code != Z_STREAM_END) {
+ throw std::runtime_error(deflate_stream.msg);
+ }
+
+ return result;
+}
+
+std::string decompress(const std::string &raw) {
+ z_stream inflate_stream;
+ memset(&inflate_stream, 0, sizeof(inflate_stream));
+
+ // TODO: reuse z_streams
+ if (inflateInit(&inflate_stream) != Z_OK) {
+ throw std::runtime_error("failed to initialize inflate");
+ }
+
+ inflate_stream.next_in = (Bytef *)raw.data();
+ inflate_stream.avail_in = raw.size();
+
+ std::string result;
+ char out[15384];
+
+ int code;
+ do {
+ inflate_stream.next_out = reinterpret_cast<Bytef *>(out);
+ inflate_stream.avail_out = sizeof(out);
+ code = inflate(&inflate_stream, 0);
+ // result.append(out, sizeof(out) - inflate_stream.avail_out);
+ if (result.size() < inflate_stream.total_out) {
+ result.append(out, inflate_stream.total_out - result.size());
+ }
+ } while (code == Z_OK);
+
+ inflateEnd(&inflate_stream);
+
+ if (code != Z_STREAM_END) {
+ throw std::runtime_error(inflate_stream.msg);
+ }
+
+ return result;
+}
+}
+}
diff --git a/src/util/filesource.cpp b/src/util/filesource.cpp
deleted file mode 100644
index 503d4dfb1d..0000000000
--- a/src/util/filesource.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <mbgl/util/filesource.hpp>
-#include <mbgl/platform/platform.hpp>
-
-#include <fstream>
-#include <sstream>
-
-namespace mbgl {
-
-FileSource::FileSource() {}
-
-
-void FileSource::setBase(const std::string &value) {
- base = value;
-}
-
-const std::string &FileSource::getBase() const {
- return base;
-}
-
-void FileSource::load(ResourceType /*type*/, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop) {
- // convert relative URLs to absolute URLs
-
- const std::string absoluteURL = [&]() -> std::string {
- const size_t separator = url.find("://");
- if (separator == std::string::npos) {
- // Relative URL.
- return base + url;
- } else {
- return url;
- }
- }();
-
- const size_t separator = absoluteURL.find("://");
- const std::string protocol = separator != std::string::npos ? absoluteURL.substr(0, separator) : "";
-
- if (protocol == "file") {
- // load from disk
- const std::string path = absoluteURL.substr(separator + 3);
- std::ifstream file(path);
-
- platform::Response response(callback);
- if (!file.good()) {
- response.error_message = "file not found (" + path + ")";
- } else {
- std::stringstream data;
- data << file.rdbuf();
- response.code = 200;
- response.body = data.str();
- }
-
- callback(&response);
- } else {
- // load from the internet
- platform::request_http(absoluteURL, callback, loop);
- }
-}
-
-} \ No newline at end of file
diff --git a/src/util/parsedate.c b/src/util/parsedate.c
new file mode 100644
index 0000000000..123c5c4e5f
--- /dev/null
+++ b/src/util/parsedate.c
@@ -0,0 +1,689 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, 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.
+ *
+ ***************************************************************************/
+/*
+ A brief summary of the date string formats this parser groks:
+
+ RFC 2616 3.3.1
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ we support dates without week day name:
+
+ 06 Nov 1994 08:49:37 GMT
+ 06-Nov-94 08:49:37 GMT
+ Nov 6 08:49:37 1994
+
+ without the time zone:
+
+ 06 Nov 1994 08:49:37
+ 06-Nov-94 08:49:37
+
+ weird order:
+
+ 1994 Nov 6 08:49:37 (GNU date fails)
+ GMT 08:49:37 06-Nov-94 Sunday
+ 94 6 Nov 08:49:37 (GNU date fails)
+
+ time left out:
+
+ 1994 Nov 6
+ 06-Nov-94
+ Sun Nov 6 94
+
+ unusual separators:
+
+ 1994.Nov.6
+ Sun/Nov/6/94/GMT
+
+ commonly used time zone names:
+
+ Sun, 06 Nov 1994 08:49:37 CET
+ 06 Nov 1994 08:49:37 EST
+
+ time zones specified using RFC822 style:
+
+ Sun, 12 Sep 2004 15:05:58 -0700
+ Sat, 11 Sep 2004 21:32:11 +0200
+
+ compact numerical date strings:
+
+ 20040912 15:05:58 -0700
+ 20040911 +0200
+
+*/
+
+#include <mbgl/util/parsedate.h>
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#define ERRNO (errno)
+#define SET_ERRNO(x) (errno = (x))
+
+
+/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
+ its behavior is altered by the current locale. */
+char raw_toupper(char in)
+{
+ switch (in) {
+ case 'a':
+ return 'A';
+ case 'b':
+ return 'B';
+ case 'c':
+ return 'C';
+ case 'd':
+ return 'D';
+ case 'e':
+ return 'E';
+ case 'f':
+ return 'F';
+ case 'g':
+ return 'G';
+ case 'h':
+ return 'H';
+ case 'i':
+ return 'I';
+ case 'j':
+ return 'J';
+ case 'k':
+ return 'K';
+ case 'l':
+ return 'L';
+ case 'm':
+ return 'M';
+ case 'n':
+ return 'N';
+ case 'o':
+ return 'O';
+ case 'p':
+ return 'P';
+ case 'q':
+ return 'Q';
+ case 'r':
+ return 'R';
+ case 's':
+ return 'S';
+ case 't':
+ return 'T';
+ case 'u':
+ return 'U';
+ case 'v':
+ return 'V';
+ case 'w':
+ return 'W';
+ case 'x':
+ return 'X';
+ case 'y':
+ return 'Y';
+ case 'z':
+ return 'Z';
+ }
+ return in;
+}
+
+/*
+ * raw_equal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * some further explanation to why this function is necessary.
+ *
+ * The function is capable of comparing a-z case insensitively even for
+ * non-ascii.
+ */
+
+int raw_equal(const char *first, const char *second)
+{
+ while(*first && *second) {
+ if(raw_toupper(*first) != raw_toupper(*second))
+ /* get out of the loop as soon as they don't match */
+ break;
+ first++;
+ second++;
+ }
+ /* we do the comparison here (possibly again), just to make sure that if the
+ loop above is skipped because one of the strings reached zero, we must not
+ return this as a successful match */
+ return (raw_toupper(*first) == raw_toupper(*second));
+}
+
+#define ISSPACE(x) (isspace((int) ((unsigned char)x)))
+#define ISDIGIT(x) (isdigit((int) ((unsigned char)x)))
+#define ISALNUM(x) (isalnum((int) ((unsigned char)x)))
+#define ISALPHA(x) (isalpha((int) ((unsigned char)x)))
+
+
+/*
+ * Redefine TRUE and FALSE too, to catch current use. With this
+ * change, 'bool found = 1' will give a warning on MIPSPro, but
+ * 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro,
+ * AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too.
+ */
+
+#ifndef TRUE
+#define TRUE true
+#endif
+#ifndef FALSE
+#define FALSE false
+#endif
+
+
+
+/*
+** signed long to signed int
+*/
+
+int clamp_to_int(long slnum)
+{
+ return slnum > INT_MAX ? INT_MAX : slnum < INT_MIN ? INT_MIN : slnum;
+}
+
+
+const char * const wkday[] =
+{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
+static const char * const weekday[] =
+{ "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday", "Sunday" };
+const char * const month[]=
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+struct tzinfo {
+ char name[5];
+ int offset; /* +/- in minutes */
+};
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK - a fine conversion
+ * PARSEDATE_FAIL - failed to convert
+ * PARSEDATE_LATER - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output);
+
+#define PARSEDATE_OK 0
+#define PARSEDATE_FAIL -1
+#define PARSEDATE_LATER 1
+#define PARSEDATE_SOONER 2
+
+/* Here's a bunch of frequently used time zone names. These were supported
+ by the old getdate parser. */
+#define tDAYZONE -60 /* offset for daylight savings time */
+static const struct tzinfo tz[]= {
+ {"GMT", 0}, /* Greenwich Mean */
+ {"UTC", 0}, /* Universal (Coordinated) */
+ {"WET", 0}, /* Western European */
+ {"BST", 0 tDAYZONE}, /* British Summer */
+ {"WAT", 60}, /* West Africa */
+ {"AST", 240}, /* Atlantic Standard */
+ {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */
+ {"EST", 300}, /* Eastern Standard */
+ {"EDT", 300 tDAYZONE}, /* Eastern Daylight */
+ {"CST", 360}, /* Central Standard */
+ {"CDT", 360 tDAYZONE}, /* Central Daylight */
+ {"MST", 420}, /* Mountain Standard */
+ {"MDT", 420 tDAYZONE}, /* Mountain Daylight */
+ {"PST", 480}, /* Pacific Standard */
+ {"PDT", 480 tDAYZONE}, /* Pacific Daylight */
+ {"YST", 540}, /* Yukon Standard */
+ {"YDT", 540 tDAYZONE}, /* Yukon Daylight */
+ {"HST", 600}, /* Hawaii Standard */
+ {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */
+ {"CAT", 600}, /* Central Alaska */
+ {"AHST", 600}, /* Alaska-Hawaii Standard */
+ {"NT", 660}, /* Nome */
+ {"IDLW", 720}, /* International Date Line West */
+ {"CET", -60}, /* Central European */
+ {"MET", -60}, /* Middle European */
+ {"MEWT", -60}, /* Middle European Winter */
+ {"MEST", -60 tDAYZONE}, /* Middle European Summer */
+ {"CEST", -60 tDAYZONE}, /* Central European Summer */
+ {"MESZ", -60 tDAYZONE}, /* Middle European Summer */
+ {"FWT", -60}, /* French Winter */
+ {"FST", -60 tDAYZONE}, /* French Summer */
+ {"EET", -120}, /* Eastern Europe, USSR Zone 1 */
+ {"WAST", -420}, /* West Australian Standard */
+ {"WADT", -420 tDAYZONE}, /* West Australian Daylight */
+ {"CCT", -480}, /* China Coast, USSR Zone 7 */
+ {"JST", -540}, /* Japan Standard, USSR Zone 8 */
+ {"EAST", -600}, /* Eastern Australian Standard */
+ {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */
+ {"GST", -600}, /* Guam Standard, USSR Zone 9 */
+ {"NZT", -720}, /* New Zealand */
+ {"NZST", -720}, /* New Zealand Standard */
+ {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */
+ {"IDLE", -720}, /* International Date Line East */
+ /* Next up: Military timezone names. RFC822 allowed these, but (as noted in
+ RFC 1123) had their signs wrong. Here we use the correct signs to match
+ actual military usage.
+ */
+ {"A", +1 * 60}, /* Alpha */
+ {"B", +2 * 60}, /* Bravo */
+ {"C", +3 * 60}, /* Charlie */
+ {"D", +4 * 60}, /* Delta */
+ {"E", +5 * 60}, /* Echo */
+ {"F", +6 * 60}, /* Foxtrot */
+ {"G", +7 * 60}, /* Golf */
+ {"H", +8 * 60}, /* Hotel */
+ {"I", +9 * 60}, /* India */
+ /* "J", Juliet is not used as a timezone, to indicate the observer's local
+ time */
+ {"K", +10 * 60}, /* Kilo */
+ {"L", +11 * 60}, /* Lima */
+ {"M", +12 * 60}, /* Mike */
+ {"N", -1 * 60}, /* November */
+ {"O", -2 * 60}, /* Oscar */
+ {"P", -3 * 60}, /* Papa */
+ {"Q", -4 * 60}, /* Quebec */
+ {"R", -5 * 60}, /* Romeo */
+ {"S", -6 * 60}, /* Sierra */
+ {"T", -7 * 60}, /* Tango */
+ {"U", -8 * 60}, /* Uniform */
+ {"V", -9 * 60}, /* Victor */
+ {"W", -10 * 60}, /* Whiskey */
+ {"X", -11 * 60}, /* X-ray */
+ {"Y", -12 * 60}, /* Yankee */
+ {"Z", 0}, /* Zulu, zero meridian, a.k.a. UTC */
+};
+
+/* returns:
+ -1 no day
+ 0 monday - 6 sunday
+*/
+
+static int checkday(const char *check, size_t len)
+{
+ int i;
+ const char * const *what;
+ bool found= FALSE;
+ if(len > 3)
+ what = &weekday[0];
+ else
+ what = &wkday[0];
+ for(i=0; i<7; i++) {
+ if(raw_equal(check, what[0])) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1;
+}
+
+static int checkmonth(const char *check)
+{
+ int i;
+ const char * const *what;
+ bool found= FALSE;
+
+ what = &month[0];
+ for(i=0; i<12; i++) {
+ if(raw_equal(check, what[0])) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1; /* return the offset or -1, no real offset is -1 */
+}
+
+/* return the time zone offset between GMT and the input one, in number
+ of seconds or -1 if the timezone wasn't found/legal */
+
+static int checktz(const char *check)
+{
+ unsigned int i;
+ const struct tzinfo *what;
+ bool found= FALSE;
+
+ what = tz;
+ for(i=0; i< sizeof(tz)/sizeof(tz[0]); i++) {
+ if(raw_equal(check, what->name)) {
+ found=TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?what->offset*60:-1;
+}
+
+static void skip(const char **date)
+{
+ /* skip everything that aren't letters or digits */
+ while(**date && !ISALNUM(**date))
+ (*date)++;
+}
+
+enum assume {
+ DATE_MDAY,
+ DATE_YEAR,
+ DATE_TIME
+};
+
+/* this is a clone of 'struct tm' but with all fields we don't need or use
+ cut out */
+struct my_tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+};
+
+/* struct tm to time since epoch in GMT time zone.
+ * This is similar to the standard mktime function but for GMT only, and
+ * doesn't suffer from the various bugs and portability problems that
+ * some systems' implementations have.
+ */
+static time_t my_timegm(struct my_tm *tm)
+{
+ static const int month_days_cumulative [12] =
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ int month, year, leap_days;
+
+ if(tm->tm_year < 70)
+ /* we don't support years before 1970 as they will cause this function
+ to return a negative value */
+ return -1;
+
+ year = tm->tm_year + 1900;
+ month = tm->tm_mon;
+ if(month < 0) {
+ year += (11 - month) / 12;
+ month = 11 - (11 - month) % 12;
+ }
+ else if(month >= 12) {
+ year -= month / 12;
+ month = month % 12;
+ }
+
+ leap_days = year - (tm->tm_mon <= 1);
+ leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
+ - (1969 / 4) + (1969 / 100) - (1969 / 400));
+
+ return ((((time_t) (year - 1970) * 365
+ + leap_days + month_days_cumulative [month] + tm->tm_mday - 1) * 24
+ + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
+}
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK - a fine conversion
+ * PARSEDATE_FAIL - failed to convert
+ * PARSEDATE_LATER - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output)
+{
+ time_t t = 0;
+ int wdaynum=-1; /* day of the week number, 0-6 (mon-sun) */
+ int monnum=-1; /* month of the year number, 0-11 */
+ int mdaynum=-1; /* day of month, 1 - 31 */
+ int hournum=-1;
+ int minnum=-1;
+ int secnum=-1;
+ int yearnum=-1;
+ int tzoff=-1;
+ struct my_tm tm;
+ enum assume dignext = DATE_MDAY;
+ const char *indate = date; /* save the original pointer */
+ int part = 0; /* max 6 parts */
+
+ while(*date && (part < 6)) {
+ bool found=FALSE;
+
+ skip(&date);
+
+ if(ISALPHA(*date)) {
+ /* a name coming up */
+ char buf[32]="";
+ size_t len;
+ if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz]", buf))
+ len = strlen(buf);
+ else
+ len = 0;
+
+ if(wdaynum == -1) {
+ wdaynum = checkday(buf, len);
+ if(wdaynum != -1)
+ found = TRUE;
+ }
+ if(!found && (monnum == -1)) {
+ monnum = checkmonth(buf);
+ if(monnum != -1)
+ found = TRUE;
+ }
+
+ if(!found && (tzoff == -1)) {
+ /* this just must be a time zone string */
+ tzoff = checktz(buf);
+ if(tzoff != -1)
+ found = TRUE;
+ }
+
+ if(!found)
+ return PARSEDATE_FAIL; /* bad string */
+
+ date += len;
+ }
+ else if(ISDIGIT(*date)) {
+ /* a digit */
+ int val;
+ char *end;
+ if((secnum == -1) &&
+ (3 == sscanf(date, "%02d:%02d:%02d", &hournum, &minnum, &secnum))) {
+ /* time stamp! */
+ date += 8;
+ }
+ else if((secnum == -1) &&
+ (2 == sscanf(date, "%02d:%02d", &hournum, &minnum))) {
+ /* time stamp without seconds */
+ date += 5;
+ secnum = 0;
+ }
+ else {
+ long lval;
+ int error;
+ int old_errno;
+
+ old_errno = ERRNO;
+ SET_ERRNO(0);
+ lval = strtol(date, &end, 10);
+ error = ERRNO;
+ if(error != old_errno)
+ SET_ERRNO(old_errno);
+
+ if(error)
+ return PARSEDATE_FAIL;
+
+#if LONG_MAX != INT_MAX
+ if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
+ return PARSEDATE_FAIL;
+#endif
+
+ val = clamp_to_int(lval);
+
+ if((tzoff == -1) &&
+ ((end - date) == 4) &&
+ (val <= 1400) &&
+ (indate< date) &&
+ ((date[-1] == '+' || date[-1] == '-'))) {
+ /* four digits and a value less than or equal to 1400 (to take into
+ account all sorts of funny time zone diffs) and it is preceded
+ with a plus or minus. This is a time zone indication. 1400 is
+ picked since +1300 is frequently used and +1400 is mentioned as
+ an edge number in the document "ISO C 200X Proposal: Timezone
+ Functions" at http://david.tribble.com/text/c0xtimezone.html If
+ anyone has a more authoritative source for the exact maximum time
+ zone offsets, please speak up! */
+ found = TRUE;
+ tzoff = (val/100 * 60 + val%100)*60;
+
+ /* the + and - prefix indicates the local time compared to GMT,
+ this we need ther reversed math to get what we want */
+ tzoff = date[-1]=='+'?-tzoff:tzoff;
+ }
+
+ if(((end - date) == 8) &&
+ (yearnum == -1) &&
+ (monnum == -1) &&
+ (mdaynum == -1)) {
+ /* 8 digits, no year, month or day yet. This is YYYYMMDD */
+ found = TRUE;
+ yearnum = val/10000;
+ monnum = (val%10000)/100-1; /* month is 0 - 11 */
+ mdaynum = val%100;
+ }
+
+ if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
+ if((val > 0) && (val<32)) {
+ mdaynum = val;
+ found = TRUE;
+ }
+ dignext = DATE_YEAR;
+ }
+
+ if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
+ yearnum = val;
+ found = TRUE;
+ if(yearnum < 1900) {
+ if(yearnum > 70)
+ yearnum += 1900;
+ else
+ yearnum += 2000;
+ }
+ if(mdaynum == -1)
+ dignext = DATE_MDAY;
+ }
+
+ if(!found)
+ return PARSEDATE_FAIL;
+
+ date = end;
+ }
+ }
+
+ part++;
+ }
+
+ if(-1 == secnum)
+ secnum = minnum = hournum = 0; /* no time, make it zero */
+
+ if((-1 == mdaynum) ||
+ (-1 == monnum) ||
+ (-1 == yearnum))
+ /* lacks vital info, fail */
+ return PARSEDATE_FAIL;
+
+#if SIZEOF_TIME_T < 5
+ /* 32 bit time_t can only hold dates to the beginning of 2038 */
+ if(yearnum > 2037) {
+ *output = 0x7fffffff;
+ return PARSEDATE_LATER;
+ }
+#endif
+
+ if(yearnum < 1970) {
+ *output = 0;
+ return PARSEDATE_SOONER;
+ }
+
+ if((mdaynum > 31) || (monnum > 11) ||
+ (hournum > 23) || (minnum > 59) || (secnum > 60))
+ return PARSEDATE_FAIL; /* clearly an illegal date */
+
+ tm.tm_sec = secnum;
+ tm.tm_min = minnum;
+ tm.tm_hour = hournum;
+ tm.tm_mday = mdaynum;
+ tm.tm_mon = monnum;
+ tm.tm_year = yearnum - 1900;
+
+ /* my_timegm() returns a time_t. time_t is often 32 bits, even on many
+ architectures that feature 64 bit 'long'.
+
+ Some systems have 64 bit time_t and deal with years beyond 2038. However,
+ even on some of the systems with 64 bit time_t mktime() returns -1 for
+ dates beyond 03:14:07 UTC, January 19, 2038. (Such as AIX 5100-06)
+ */
+ t = my_timegm(&tm);
+
+ /* time zone adjust (cast t to int to compare to negative one) */
+ if(-1 != (int)t) {
+
+ /* Add the time zone diff between local time zone and GMT. */
+ long delta = (long)(tzoff!=-1?tzoff:0);
+
+ if((delta>0) && (t > LONG_MAX - delta))
+ return -1; /* time_t overflow */
+
+ t += delta;
+ }
+
+ *output = t;
+
+ return PARSEDATE_OK;
+}
+
+time_t parse_date(const char *p)
+{
+ time_t parsed;
+ int rc = parsedate(p, &parsed);
+
+ switch(rc) {
+ case PARSEDATE_OK:
+ case PARSEDATE_LATER:
+ case PARSEDATE_SOONER:
+ return parsed;
+ }
+ /* everything else is fail */
+ return -1;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/util/raster.cpp b/src/util/raster.cpp
index 9a71573d01..7b52c51037 100644
--- a/src/util/raster.cpp
+++ b/src/util/raster.cpp
@@ -1,20 +1,19 @@
-#include <mbgl/util/raster.hpp>
-
-#include <memory>
-#include <cassert>
-#include <cstring>
-
#include <mbgl/platform/platform.hpp>
#include <mbgl/platform/gl.hpp>
+
+#include <mbgl/util/raster.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/std.hpp>
#include <png.h>
+#include <cassert>
+#include <cstring>
+
using namespace mbgl;
-Raster::Raster(const std::shared_ptr<Texturepool> &texturepool)
+Raster::Raster(const util::ptr<Texturepool> &texturepool)
: texturepool(texturepool)
{}
diff --git a/src/util/sqlite3.cpp b/src/util/sqlite3.cpp
new file mode 100644
index 0000000000..19e0ce9c79
--- /dev/null
+++ b/src/util/sqlite3.cpp
@@ -0,0 +1,166 @@
+#include <mbgl/util/sqlite3.hpp>
+#include <sqlite3.h>
+
+#include <cassert>
+
+namespace mapbox {
+namespace sqlite {
+
+Database::Database(const std::string &filename, int flags) {
+ const int err = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr);
+ if (err != SQLITE_OK) {
+ Exception ex { err, sqlite3_errmsg(db) };
+ db = nullptr;
+ throw ex;
+ }
+}
+
+Database::Database(Database &&other) {
+ *this = std::move(other);
+}
+
+Database &Database::operator=(Database &&other) {
+ std::swap(db, other.db);
+ return *this;
+}
+
+Database::~Database() {
+ if (db) {
+ const int err = sqlite3_close(db);
+ if (err != SQLITE_OK) {
+ throw Exception { err, sqlite3_errmsg(db) };
+ }
+ }
+}
+
+Database::operator bool() const {
+ return db != nullptr;
+}
+
+void Database::exec(const std::string &sql) {
+ assert(db);
+ char *msg = nullptr;
+ const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg);
+ if (msg) {
+ Exception ex { err, msg };
+ sqlite3_free(msg);
+ throw ex;
+ } else if (err != SQLITE_OK) {
+ throw Exception { err, sqlite3_errmsg(db) };
+ }
+}
+
+Statement Database::prepare(const char *query) {
+ assert(db);
+ return std::move(Statement(db, query));
+}
+
+Statement::Statement(sqlite3 *db, const char *sql) {
+ const int err = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
+ if (err != SQLITE_OK) {
+ stmt = nullptr;
+ throw Exception { err, sqlite3_errmsg(db) };
+ }
+}
+
+#define CHECK_SQLITE_OK(err) \
+ if (err != SQLITE_OK) { \
+ throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; \
+ }
+
+Statement::Statement(Statement &&other) {
+ *this = std::move(other);
+}
+
+Statement &Statement::operator=(Statement &&other) {
+ std::swap(stmt, other.stmt);
+ return *this;
+}
+
+Statement::~Statement() {
+ if (stmt) {
+ const int err = sqlite3_finalize(stmt);
+ CHECK_SQLITE_OK(err)
+ }
+}
+
+Statement::operator bool() const {
+ return stmt != nullptr;
+}
+
+#define BIND_3(type, value) \
+ assert(stmt); \
+ const int err = sqlite3_bind_##type(stmt, offset, value); \
+ CHECK_SQLITE_OK(err)
+
+#define BIND_5(type, value, length, param) \
+ assert(stmt); \
+ const int err = sqlite3_bind_##type(stmt, offset, value, length, param); \
+ CHECK_SQLITE_OK(err)
+
+template <> void Statement::bind(int offset, int value) {
+ BIND_3(int, value)
+}
+
+template <> void Statement::bind(int offset, int64_t value) {
+ BIND_3(int64, value)
+}
+
+template <> void Statement::bind(int offset, double value) {
+ BIND_3(double, value)
+}
+
+template <> void Statement::bind(int offset, bool value) {
+ BIND_3(int, value)
+}
+
+template <> void Statement::bind(int offset, const char *value) {
+ BIND_5(text, value, -1, nullptr)
+}
+
+void Statement::bind(int offset, const std::string &value, bool retain) {
+ BIND_5(blob, value.data(), int(value.size()), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)
+}
+
+bool Statement::run() {
+ assert(stmt);
+ const int err = sqlite3_step(stmt);
+ if (err == SQLITE_DONE) {
+ return false;
+ } else if (err == SQLITE_ROW) {
+ return true;
+ } else {
+ throw std::runtime_error("failed to run statement");
+ }
+}
+
+template <> int Statement::get(int offset) {
+ assert(stmt);
+ return sqlite3_column_int(stmt, offset);
+}
+
+template <> int64_t Statement::get(int offset) {
+ assert(stmt);
+ return sqlite3_column_int64(stmt, offset);
+}
+
+template <> double Statement::get(int offset) {
+ assert(stmt);
+ return sqlite3_column_double(stmt, offset);
+}
+
+template <> std::string Statement::get(int offset) {
+ assert(stmt);
+ return {
+ reinterpret_cast<const char *>(sqlite3_column_blob(stmt, offset)),
+ size_t(sqlite3_column_bytes(stmt, offset))
+ };
+}
+
+void Statement::reset() {
+ assert(stmt);
+ sqlite3_reset(stmt);
+}
+
+}
+}
diff --git a/src/util/uv-channel.c b/src/util/uv-channel.c
new file mode 100644
index 0000000000..4e3b9fa5ff
--- /dev/null
+++ b/src/util/uv-channel.c
@@ -0,0 +1,69 @@
+#include <mbgl/util/uv-channel.h>
+#include <mbgl/util/queue.h>
+
+#include <stdlib.h>
+
+// Taken from http://navaneeth.github.io/blog/2013/08/02/channels-in-libuv/
+
+typedef struct {
+ void *data;
+ void *active_queue[2];
+} uv__chan_item_t;
+
+int uv_chan_init(uv_chan_t *chan) {
+ int r = uv_mutex_init(&chan->mutex);
+ if (r == -1)
+ return r;
+
+ QUEUE_INIT(&chan->q);
+
+ return uv_cond_init(&chan->cond);
+}
+
+void uv_chan_send(uv_chan_t *chan, void *data) {
+ uv__chan_item_t *item = (uv__chan_item_t *)malloc(sizeof(uv__chan_item_t));
+ item->data = data;
+
+ uv_mutex_lock(&chan->mutex);
+ QUEUE_INSERT_TAIL(&chan->q, &item->active_queue);
+ uv_cond_signal(&chan->cond);
+ uv_mutex_unlock(&chan->mutex);
+}
+
+void *uv_chan_receive(uv_chan_t *chan) {
+ uv__chan_item_t *item;
+ QUEUE *head;
+ void *data = NULL;
+
+ uv_mutex_lock(&chan->mutex);
+ while (QUEUE_EMPTY(&chan->q)) {
+ uv_cond_wait(&chan->cond, &chan->mutex);
+ }
+
+ head = QUEUE_HEAD(&chan->q);
+ item = QUEUE_DATA(head, uv__chan_item_t, active_queue);
+ data = item->data;
+ QUEUE_REMOVE(head);
+ free(item);
+ uv_mutex_unlock(&chan->mutex);
+ return data;
+}
+
+void uv_chan_clear(uv_chan_t *chan) {
+ uv_mutex_lock(&chan->mutex);
+ uv__chan_item_t *item = NULL;
+ QUEUE *head = NULL;
+ while (!QUEUE_EMPTY(&chan->q)) {
+ head = QUEUE_HEAD(&chan->q);
+ item = QUEUE_DATA(head, uv__chan_item_t, active_queue);
+ QUEUE_REMOVE(head);
+ free(item);
+ }
+ uv_mutex_unlock(&chan->mutex);
+}
+
+void uv_chan_destroy(uv_chan_t *chan) {
+ uv_chan_clear(chan);
+ uv_cond_destroy(&chan->cond);
+ uv_mutex_destroy(&chan->mutex);
+}
diff --git a/src/util/uv-messenger.c b/src/util/uv-messenger.c
new file mode 100644
index 0000000000..a25c84dc59
--- /dev/null
+++ b/src/util/uv-messenger.c
@@ -0,0 +1,74 @@
+#include <mbgl/util/uv-messenger.h>
+#include <mbgl/util/queue.h>
+
+#include <stdlib.h>
+
+typedef struct {
+ void *data;
+ void *queue[2];
+} uv__messenger_item_t;
+
+void uv__messenger_callback(uv_async_t *async) {
+ uv_messenger_t *msgr = (uv_messenger_t *)async->data;
+
+ uv__messenger_item_t *item;
+ QUEUE *head;
+
+ while (1) {
+ uv_mutex_lock(&msgr->mutex);
+ if (QUEUE_EMPTY(&msgr->queue)) {
+ uv_mutex_unlock(&msgr->mutex);
+ break;
+ }
+
+ head = QUEUE_HEAD(&msgr->queue);
+ item = QUEUE_DATA(head, uv__messenger_item_t, queue);
+ QUEUE_REMOVE(head);
+ uv_mutex_unlock(&msgr->mutex);
+
+ msgr->callback(item->data);
+
+ free(item);
+ }
+}
+
+int uv_messenger_init(uv_loop_t *loop, uv_messenger_t *msgr, uv_messenger_cb callback) {
+ int ret = uv_mutex_init(&msgr->mutex);
+ if (ret < 0) {
+ return ret;
+ }
+
+ msgr->callback = callback;
+
+ QUEUE_INIT(&msgr->queue);
+
+ msgr->async.data = msgr;
+ return uv_async_init(loop, &msgr->async, uv__messenger_callback);
+}
+
+void uv_messenger_send(uv_messenger_t *msgr, void *data) {
+ uv__messenger_item_t *item = (uv__messenger_item_t *)malloc(sizeof(uv__messenger_item_t));
+ item->data = data;
+
+ uv_mutex_lock(&msgr->mutex);
+ QUEUE_INSERT_TAIL(&msgr->queue, &item->queue);
+ uv_mutex_unlock(&msgr->mutex);
+
+ uv_async_send(&msgr->async);
+}
+
+void uv_messenger_ref(uv_messenger_t *msgr) {
+ uv_ref((uv_handle_t *)&msgr->async);
+}
+
+void uv_messenger_unref(uv_messenger_t *msgr) {
+ uv_unref((uv_handle_t *)&msgr->async);
+}
+
+void uv__messenger_stop_callback(uv_handle_t *handle) {
+ free((uv_messenger_t *)handle->data);
+}
+
+void uv_messenger_stop(uv_messenger_t *msgr) {
+ uv_close((uv_handle_t *)&msgr->async, uv__messenger_stop_callback);
+}
diff --git a/src/util/uv-worker.c b/src/util/uv-worker.c
new file mode 100644
index 0000000000..8b0cc6dda7
--- /dev/null
+++ b/src/util/uv-worker.c
@@ -0,0 +1,166 @@
+#include <mbgl/util/uv-worker.h>
+#include <mbgl/util/uv-messenger.h>
+
+#include <stdio.h>
+#include <assert.h>
+
+typedef struct uv__worker_item_s uv__worker_item_t;
+struct uv__worker_item_s {
+ uv_worker_t *worker;
+ void *data;
+ uv_worker_cb work_cb;
+ uv_worker_after_cb after_work_cb;
+};
+
+typedef struct uv__worker_thread_s uv__worker_thread_t;
+struct uv__worker_thread_s {
+ uv_worker_t *worker;
+ uv_thread_t thread;
+};
+
+void uv__worker_thread_finished(uv__worker_thread_t *worker_thread) {
+ uv_worker_t *worker = worker_thread->worker;
+
+#ifndef NDEBUG
+ assert(uv_thread_self() == worker->thread_id);
+#endif
+
+ // This should at most block very briefly. We are sending the termination
+ // notification as the last thing in the worker thread, so by now the thread
+ // has probably terminated already. If not, the waiting time should be
+ // extremely short.
+ uv_thread_join(&worker_thread->thread);
+
+ assert(worker->count > 0);
+ worker->count--;
+ if (worker->count == 0) {
+ uv_chan_destroy(&worker->chan);
+ uv_messenger_stop(worker->msgr);
+ if (worker->close_cb) {
+ worker->close_cb(worker);
+ }
+ }
+}
+
+void uv__worker_after(void *ptr) {
+ uv__worker_item_t *item = (uv__worker_item_t *)ptr;
+
+ if (item->work_cb) {
+ // We are finishing a regular work request.
+ if (item->after_work_cb) {
+ assert(item->after_work_cb);
+ item->after_work_cb(item->data);
+ }
+ uv_worker_t *worker = item->worker;
+ assert(worker->active_items > 0);
+ if (--worker->active_items == 0) {
+ uv_messenger_unref(worker->msgr);
+ }
+ } else {
+ // This is a worker thread termination.
+ uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)item->data;
+ uv__worker_thread_finished(worker_thread);
+ free(worker_thread);
+ }
+
+ free(item);
+}
+
+void uv__worker_thread_loop(void *ptr) {
+ uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)ptr;
+ uv_worker_t *worker = worker_thread->worker;
+
+#ifdef __APPLE__
+ if (worker->name) {
+ pthread_setname_np(worker->name);
+ }
+#endif
+
+ uv__worker_item_t *item = NULL;
+ while ((item = (uv__worker_item_t *)uv_chan_receive(&worker->chan)) != NULL) {
+ assert(item->work_cb);
+ item->work_cb(item->data);
+
+ // Trigger the after callback in the main thread.
+ uv_messenger_send(worker->msgr, item);
+ }
+
+ // Make sure to close all other workers too.
+ uv_chan_send(&worker->chan, NULL);
+
+ // Create a new worker item that acts as a terminate flag for this thread.
+ item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t));
+ item->data = worker_thread;
+ item->work_cb = NULL;
+ item->after_work_cb = NULL;
+ uv_messenger_send(worker->msgr, item);
+}
+
+int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name) {
+#ifndef NDEBUG
+ worker->thread_id = uv_thread_self();
+#endif
+ worker->loop = loop;
+ worker->name = name;
+ worker->count = 0;
+ worker->close_cb = NULL;
+ worker->active_items = 0;
+ worker->msgr = (uv_messenger_t *)malloc(sizeof(uv_messenger_t));
+ int ret = uv_messenger_init(loop, worker->msgr, uv__worker_after);
+ if (ret < 0) {
+ free(worker->msgr);
+ return ret;
+ }
+ uv_messenger_unref(worker->msgr);
+ ret = uv_chan_init(&worker->chan);
+ if (ret < 0) return ret;
+
+ // Initialize all worker threads.
+ int i;
+ for (i = 0; i < count; i++) {
+ uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)malloc(sizeof(uv__worker_thread_t));
+ worker_thread->worker = worker;
+ ret = uv_thread_create(&worker_thread->thread, uv__worker_thread_loop, worker_thread);
+ if (ret < 0) return ret;
+ worker->count++;
+ }
+
+ return 0;
+}
+
+void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
+ uv_worker_after_cb after_work_cb) {
+#ifndef NDEBUG
+ assert(uv_thread_self() == worker->thread_id);
+#endif
+
+ // It doesn't make sense to not provide a work callback. On the other hand, the after_work_cb
+ // may be NULL. In that case, there will be no callback called in the current thread and the
+ // worker item will instead be freed in the worker thread.
+ assert(work_cb);
+
+ uv__worker_item_t *item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t));
+ item->worker = worker;
+ item->work_cb = work_cb;
+ item->after_work_cb = after_work_cb;
+ item->data = data;
+ uv_chan_send(&worker->chan, item);
+ if (worker->active_items++ == 0) {
+ uv_messenger_ref(worker->msgr);
+ }
+}
+
+void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb) {
+#ifndef NDEBUG
+ assert(uv_thread_self() == worker->thread_id);
+#endif
+
+ // Prevent double calling.
+ assert(worker->close_cb == NULL);
+
+ worker->close_cb = close_cb;
+ uv_chan_send(&worker->chan, NULL);
+ if (worker->active_items++ == 0) {
+ uv_messenger_ref(worker->msgr);
+ }
+}
diff --git a/src/util/uv.cpp b/src/util/uv.cpp
index 94f074bfa1..6e15ac4537 100644
--- a/src/util/uv.cpp
+++ b/src/util/uv.cpp
@@ -16,4 +16,16 @@ std::string cwd() {
return dir;
}
+void deleter::operator()(uv_async_t *async) {
+ uv_close((uv_handle_t *)async, [](uv_handle_t *handle) {
+ delete (uv_async_t *)handle;
+ });
+}
+
+void deleter::operator()(uv_timer_t *timer) {
+ uv_close((uv_handle_t *)timer, [](uv_handle_t *handle) {
+ delete (uv_timer_t *)handle;
+ });
+}
+
}
diff --git a/test/fixtures/fixture_request.cpp b/test/fixtures/fixture_request.cpp
index 417958a33d..b37cd92bee 100644
--- a/test/fixtures/fixture_request.cpp
+++ b/test/fixtures/fixture_request.cpp
@@ -1,10 +1,16 @@
-#include <mbgl/platform/platform.hpp>
-#include <mbgl/platform/request.hpp>
-#include <mbgl/util/uv_detail.hpp>
+#include <mbgl/storage/http_request_baton.hpp>
+#include <mbgl/storage/file_request_baton.hpp>
+#include <mbgl/storage/response.hpp>
#include <mbgl/util/url.hpp>
+#include <mbgl/util/std.hpp>
#include <mbgl/platform/log.hpp>
#include <iostream>
+#include <uv.h>
+
+#include <cassert>
+
+
const std::string base_directory = []{
std::string fn = __FILE__;
fn.erase(fn.find_last_of("/"));
@@ -13,110 +19,48 @@ const std::string base_directory = []{
return fn + "/node_modules/mapbox-gl-test-suite/";
}();
-
namespace mbgl {
-std::shared_ptr<platform::Request>
-platform::request_http(const std::string &url,
- std::function<void(Response *)> callback,
- std::shared_ptr<uv::loop> loop) {
- uv_loop_t *l = nullptr;
- if (loop) {
- l = **loop;
- } else {
- l = uv_default_loop();
- }
+void HTTPRequestBaton::start(const util::ptr<HTTPRequestBaton> &baton) {
+ assert(uv_thread_self() == baton->thread_id);
- std::string clean_url = util::percentDecode(url);
+ std::string clean_url = util::percentDecode(baton->path);
if (clean_url.find("local://") == 0) {
clean_url = base_directory + clean_url.substr(8);
}
- std::shared_ptr<Request> req = std::make_shared<Request>(url, callback, loop);
-
- int err;
-
- std::string body;
- FILE *file = std::fopen(clean_url.c_str(),"rb");
-
- if (file != NULL)
- {
- std::fseek(file, 0, SEEK_END);
- std::size_t file_size = std::ftell(file);
- std::fseek(file, 0, SEEK_SET);
- body.resize(file_size);
- std::fread(&body[0], file_size, 1, file);
- std::fclose(file);
- }
-
- /*
- uv_fs_t open_req;
- err = uv_fs_open(l, &open_req, clean_url.c_str(), O_RDONLY, S_IRUSR, nullptr);
- uv_fs_req_cleanup(&open_req);
- if (err < 0) {
- req->res->code = err;
- req->res->error_message = uv_strerror(uv_last_error(l));
- Log::Warning(Event::HttpRequest, err, url + ": " + uv_strerror(uv_last_error(l)));
- req->complete();
- return req;
- }
- uv_file fd = err;
-
- uv_fs_t stat_req;
- uv_fs_fstat(l, &stat_req, fd, nullptr);
- uv_fs_req_cleanup(&stat_req);
- err = uv_fs_fstat(l, &stat_req, fd, nullptr);
- if (err < 0) {
- req->res->code = err;
- req->res->error_message = uv_strerror(uv_last_error(l));
- Log::Warning(Event::HttpRequest, err, url + ": " + uv_strerror(uv_last_error(l)));
- req->complete();
- return req;
- }
-
- const uint64_t size = static_cast<const uv_statbuf_t*>(stat_req.ptr)->st_size;
-
-
- std::string body;
- body.resize(size);
- uv_buf_t uvbuf = uv_buf_init(const_cast<char *>(body.data()), body.size());
-
- uv_fs_t read_req;
- err = uv_fs_read(l, &read_req, fd, &uvbuf, 1, 0, nullptr);
- uv_fs_req_cleanup(&read_req);
- if (err < 0) {
- req->res->code = err;
- req->res->error_message = uv_strerror(uv_last_error(l));
- Log::Warning(Event::HttpRequest, err, url + ": " + uv_strerror(uv_last_error(l)));
- req->complete();
- return req;
+ baton->response = std::make_unique<Response>();
+ FILE *file = fopen(clean_url.c_str(),"rb");
+ if (file != NULL) {
+ fseek(file, 0, SEEK_END);
+ const size_t size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ baton->response->data.resize(size);
+ fread(&baton->response->data[0], size, 1, file);
+ fclose(file);
+
+ baton->response->code = 200;
+ baton->type = HTTPResponseType::Successful;
+ } else {
+ baton->type = HTTPResponseType::PermanentError;
+ baton->response->code = 404;
}
+ uv_async_send(baton->async);
+}
- uv_fs_t close_req;
- err = uv_fs_close(l, &close_req, fd, nullptr);
- uv_fs_req_cleanup(&close_req);
- if (err < 0) {
- req->res->code = err;
- req->res->error_message = uv_strerror(uv_last_error(l));
- Log::Warning(Event::HttpRequest, err, url + ": " + uv_strerror(uv_last_error(l)));
- req->complete();
- return req;
- }
- */
+void HTTPRequestBaton::stop(const util::ptr<HTTPRequestBaton> &baton) {
+ fprintf(stderr, "HTTP request cannot be canceled because it is answered immediately");
+ abort();
+}
- req->res->body.swap(body);
- req->res->code = 200;
- Log::Info(Event::HttpRequest, 200, url);
- req->complete();
+namespace platform {
- return req;
+std::string defaultCacheDatabase() {
+ // Disables the cache.
+ return "";
}
-void platform::cancel_request_http(const std::shared_ptr<Request> &req) {
- if (req) {
- req->cancelled = true;
- }
}
}