summaryrefslogtreecommitdiff
path: root/platform/default/include/mbgl
diff options
context:
space:
mode:
Diffstat (limited to 'platform/default/include/mbgl')
-rw-r--r--platform/default/include/mbgl/gl/headless_backend.hpp50
-rw-r--r--platform/default/include/mbgl/gl/headless_frontend.hpp61
-rw-r--r--platform/default/include/mbgl/map/map_snapshotter.hpp66
-rw-r--r--platform/default/include/mbgl/storage/file_source_request.hpp31
-rw-r--r--platform/default/include/mbgl/storage/merge_sideloaded.hpp53
-rw-r--r--platform/default/include/mbgl/storage/merge_sideloaded.js21
-rw-r--r--platform/default/include/mbgl/storage/merge_sideloaded.sql51
-rw-r--r--platform/default/include/mbgl/storage/offline_database.hpp130
-rw-r--r--platform/default/include/mbgl/storage/offline_download.hpp69
-rw-r--r--platform/default/include/mbgl/storage/offline_schema.hpp63
-rw-r--r--platform/default/include/mbgl/storage/offline_schema.js21
-rw-r--r--platform/default/include/mbgl/storage/offline_schema.sql64
-rw-r--r--platform/default/include/mbgl/storage/sqlite3.hpp180
-rw-r--r--platform/default/include/mbgl/text/unaccent.hpp13
-rw-r--r--platform/default/include/mbgl/util/default_styles.hpp30
-rw-r--r--platform/default/include/mbgl/util/default_thread_pool.hpp27
-rw-r--r--platform/default/include/mbgl/util/shared_thread_pool.hpp9
17 files changed, 939 insertions, 0 deletions
diff --git a/platform/default/include/mbgl/gl/headless_backend.hpp b/platform/default/include/mbgl/gl/headless_backend.hpp
new file mode 100644
index 0000000000..7757037533
--- /dev/null
+++ b/platform/default/include/mbgl/gl/headless_backend.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/renderer/renderer_backend.hpp>
+
+#include <memory>
+#include <functional>
+
+namespace mbgl {
+
+class HeadlessBackend : public RendererBackend {
+public:
+ HeadlessBackend(Size = { 256, 256 });
+ ~HeadlessBackend() override;
+
+ void bind() override;
+ Size getFramebufferSize() const override;
+ void updateAssumedState() override;
+
+ void setSize(Size);
+ PremultipliedImage readStillImage();
+
+ class Impl {
+ public:
+ virtual ~Impl() = default;
+ virtual gl::ProcAddress getExtensionFunctionPointer(const char*) = 0;
+ virtual void activateContext() = 0;
+ virtual void deactivateContext() {}
+ };
+
+private:
+ // Implementation specific functions
+ gl::ProcAddress getExtensionFunctionPointer(const char*) override;
+
+ void activate() override;
+ void deactivate() override;
+
+ void createImpl();
+
+private:
+ std::unique_ptr<Impl> impl;
+
+ Size size;
+ float pixelRatio;
+ bool active = false;
+
+ class View;
+ std::unique_ptr<View> view;
+};
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/gl/headless_frontend.hpp b/platform/default/include/mbgl/gl/headless_frontend.hpp
new file mode 100644
index 0000000000..18f0cfa537
--- /dev/null
+++ b/platform/default/include/mbgl/gl/headless_frontend.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <mbgl/map/camera.hpp>
+#include <mbgl/renderer/mode.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/gl/headless_backend.hpp>
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <memory>
+
+namespace mbgl {
+
+class FileSource;
+class Scheduler;
+class Renderer;
+class RendererBackend;
+class Map;
+class TransformState;
+
+class HeadlessFrontend : public RendererFrontend {
+public:
+ HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {}, GLContextMode mode = GLContextMode::Unique, const optional<std::string> localFontFamily = {});
+ HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {}, GLContextMode mode = GLContextMode::Unique, const optional<std::string> localFontFamily = {});
+ ~HeadlessFrontend() override;
+
+ void reset() override;
+ void update(std::shared_ptr<UpdateParameters>) override;
+ void setObserver(RendererObserver&) override;
+
+ Size getSize() const;
+ void setSize(Size);
+
+ Renderer* getRenderer();
+ RendererBackend* getBackend();
+ CameraOptions getCameraOptions();
+
+ bool hasImage(const std::string&);
+ bool hasLayer(const std::string&);
+ bool hasSource(const std::string&);
+
+ ScreenCoordinate pixelForLatLng(const LatLng&);
+ LatLng latLngForPixel(const ScreenCoordinate&);
+
+ PremultipliedImage readStillImage();
+ PremultipliedImage render(Map&);
+
+ optional<TransformState> getTransformState() const;
+
+private:
+ Size size;
+ float pixelRatio;
+
+ HeadlessBackend backend;
+ util::AsyncTask asyncInvalidate;
+
+ std::unique_ptr<Renderer> renderer;
+ std::shared_ptr<UpdateParameters> updateParameters;
+};
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/map/map_snapshotter.hpp b/platform/default/include/mbgl/map/map_snapshotter.hpp
new file mode 100644
index 0000000000..2deb2b3cda
--- /dev/null
+++ b/platform/default/include/mbgl/map/map_snapshotter.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/geo.hpp>
+
+#include <exception>
+#include <memory>
+#include <string>
+#include <vector>
+#include <functional>
+
+namespace mbgl {
+
+template<class> class ActorRef;
+struct CameraOptions;
+class FileSource;
+class Size;
+class LatLngBounds;
+
+namespace style {
+class Style;
+} // namespace style
+
+class MapSnapshotter {
+public:
+ MapSnapshotter(FileSource* fileSource,
+ std::shared_ptr<Scheduler> scheduler,
+ const std::pair<bool, std::string> style,
+ const Size&,
+ const float pixelRatio,
+ const optional<CameraOptions> cameraOptions,
+ const optional<LatLngBounds> region,
+ const optional<std::string> cacheDir = {},
+ const optional<std::string> localFontFamily = {});
+
+ ~MapSnapshotter();
+
+ void setStyleURL(const std::string& styleURL);
+ std::string getStyleURL() const;
+
+ void setStyleJSON(const std::string& styleJSON);
+ std::string getStyleJSON() const;
+
+ void setSize(const Size&);
+ Size getSize() const;
+
+ void setCameraOptions(const CameraOptions&);
+ CameraOptions getCameraOptions() const;
+
+ void setRegion(const LatLngBounds&);
+ LatLngBounds getRegion() const;
+
+ using PointForFn = std::function<ScreenCoordinate (const LatLng&)>;
+ using LatLngForFn = std::function<LatLng (const ScreenCoordinate&)>;
+ using Attributions = std::vector<std::string>;
+ using Callback = std::function<void (std::exception_ptr, PremultipliedImage, Attributions, PointForFn, LatLngForFn)>;
+ void snapshot(ActorRef<Callback>);
+
+private:
+ class Impl;
+ std::unique_ptr<util::Thread<Impl>> impl;
+};
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/storage/file_source_request.hpp b/platform/default/include/mbgl/storage/file_source_request.hpp
new file mode 100644
index 0000000000..6bd0d44df6
--- /dev/null
+++ b/platform/default/include/mbgl/storage/file_source_request.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/async_request.hpp>
+
+#include <memory>
+#include <functional>
+
+namespace mbgl {
+
+class Mailbox;
+
+class FileSourceRequest : public AsyncRequest {
+public:
+ FileSourceRequest(FileSource::Callback&& callback);
+ ~FileSourceRequest() final;
+
+ void onCancel(std::function<void()>&& callback);
+ void setResponse(const Response& res);
+
+ ActorRef<FileSourceRequest> actor();
+
+private:
+ FileSource::Callback responseCallback = nullptr;
+ std::function<void()> cancelCallback = nullptr;
+
+ std::shared_ptr<Mailbox> mailbox;
+};
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/storage/merge_sideloaded.hpp b/platform/default/include/mbgl/storage/merge_sideloaded.hpp
new file mode 100644
index 0000000000..494018c966
--- /dev/null
+++ b/platform/default/include/mbgl/storage/merge_sideloaded.hpp
@@ -0,0 +1,53 @@
+#pragma once
+
+// THIS IS A GENERATED FILE; EDIT merge_sideloaded.sql INSTEAD
+// To regenerate, run `node platform/default/mbgl/storage/merge_sideloaded.js`
+
+namespace mbgl {
+
+static constexpr const char* mergeSideloadedDatabaseSQL =
+"INSERT INTO regions\n"
+" SELECT DISTINCT NULL, sr.definition, sr.description\n"
+" FROM side.regions sr \n"
+" LEFT JOIN regions r ON sr.definition = r.definition AND sr.description IS r.description\n"
+" WHERE r.definition IS NULL;\n"
+"CREATE TEMPORARY TABLE region_mapping AS\n"
+" SELECT sr.id AS side_region_id,\n"
+" r.id AS main_region_id\n"
+" FROM side.regions sr\n"
+" JOIN regions r ON sr.definition = r.definition AND sr.description IS r.description;\n"
+"REPLACE INTO tiles\n"
+" SELECT t.id,\n"
+" st.url_template, st.pixel_ratio, st.z, st.x, st.y,\n"
+" st.expires, st.modified, st.etag, st.data, st.compressed, st.accessed, st.must_revalidate\n"
+" FROM (SELECT DISTINCT sti.* FROM side.region_tiles srt JOIN side.tiles sti ON srt.tile_id = sti.id)\n"
+" AS st\n"
+" LEFT JOIN tiles t ON st.url_template = t.url_template AND st.pixel_ratio = t.pixel_ratio AND st.z = t.z AND st.x = t.x AND st.y = t.y\n"
+" WHERE t.id IS NULL\n"
+" OR st.modified > t.modified;\n"
+"INSERT OR IGNORE INTO region_tiles\n"
+" SELECT rm.main_region_id, sti.id\n"
+" FROM side.region_tiles srt\n"
+" JOIN region_mapping rm ON srt.region_id = rm.side_region_id\n"
+" JOIN (SELECT t.id, st.id AS side_tile_id FROM side.tiles st\n"
+" JOIN tiles t ON st.url_template = t.url_template AND st.pixel_ratio = t.pixel_ratio AND st.z = t.z AND st.x = t.x AND st.y = t.y\n"
+" ) AS sti ON srt.tile_id = sti.side_tile_id;\n"
+"REPLACE INTO resources\n"
+" SELECT r.id, \n"
+" sr.url, sr.kind, sr.expires, sr.modified, sr.etag,\n"
+" sr.data, sr.compressed, sr.accessed, sr.must_revalidate\n"
+" FROM side.region_resources srr JOIN side.resources sr ON srr.resource_id = sr.id\n"
+" LEFT JOIN resources r ON sr.url = r.url\n"
+" WHERE r.id IS NULL\n"
+" OR sr.modified > r.modified;\n"
+"INSERT OR IGNORE INTO region_resources\n"
+" SELECT rm.main_region_id, sri.id\n"
+" FROM side.region_resources srr\n"
+" JOIN region_mapping rm ON srr.region_id = rm.side_region_id\n"
+" JOIN (SELECT r.id, sr.id AS side_resource_id FROM side.resources sr\n"
+" JOIN resources r ON sr.url = r.url) AS sri ON srr.resource_id = sri.side_resource_id;\n"
+" \n"
+"DROP TABLE region_mapping;\n"
+;
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/storage/merge_sideloaded.js b/platform/default/include/mbgl/storage/merge_sideloaded.js
new file mode 100644
index 0000000000..98d52eb8b3
--- /dev/null
+++ b/platform/default/include/mbgl/storage/merge_sideloaded.js
@@ -0,0 +1,21 @@
+var fs = require('fs');
+fs.writeFileSync('platform/default/mbgl/storage/merge_sideloaded.hpp', `#pragma once
+
+// THIS IS A GENERATED FILE; EDIT merge_sideloaded.sql INSTEAD
+// To regenerate, run \`node platform/default/mbgl/storage/merge_sideloaded.js\`
+
+namespace mbgl {
+
+static constexpr const char* mergeSideloadedDatabaseSQL =
+${fs.readFileSync('platform/default/mbgl/storage/merge_sideloaded.sql', 'utf8')
+ .replace(/ *--.*/g, '')
+ .split('\n')
+ .filter(a => a)
+ .map(line => '"' + line + '\\n"')
+ .join('\n')
+}
+;
+
+} // namespace mbgl
+`);
+
diff --git a/platform/default/include/mbgl/storage/merge_sideloaded.sql b/platform/default/include/mbgl/storage/merge_sideloaded.sql
new file mode 100644
index 0000000000..55345a6f15
--- /dev/null
+++ b/platform/default/include/mbgl/storage/merge_sideloaded.sql
@@ -0,0 +1,51 @@
+INSERT INTO regions
+ SELECT DISTINCT NULL, sr.definition, sr.description -- Merge duplicate regions
+ FROM side.regions sr
+ LEFT JOIN regions r ON sr.definition = r.definition AND sr.description IS r.description
+ WHERE r.definition IS NULL;
+
+CREATE TEMPORARY TABLE region_mapping AS
+ SELECT sr.id AS side_region_id,
+ r.id AS main_region_id
+ FROM side.regions sr
+ JOIN regions r ON sr.definition = r.definition AND sr.description IS r.description;
+
+--Insert /Update tiles
+REPLACE INTO tiles
+ SELECT t.id, -- use the old ID in case we run a REPLACE. If it doesn't exist yet, it'll be NULL which will auto-assign a new ID.
+ st.url_template, st.pixel_ratio, st.z, st.x, st.y,
+ st.expires, st.modified, st.etag, st.data, st.compressed, st.accessed, st.must_revalidate
+ FROM (SELECT DISTINCT sti.* FROM side.region_tiles srt JOIN side.tiles sti ON srt.tile_id = sti.id) -- ensure that we're only considering region tiles, and not ambient tiles.
+ AS st
+ LEFT JOIN tiles t ON st.url_template = t.url_template AND st.pixel_ratio = t.pixel_ratio AND st.z = t.z AND st.x = t.x AND st.y = t.y
+ WHERE t.id IS NULL -- only consider tiles that don't exist yet in the original database.
+ OR st.modified > t.modified; -- ...or tiles that are newer in the side loaded DB.
+
+-- Update region_tiles usage
+INSERT OR IGNORE INTO region_tiles
+ SELECT rm.main_region_id, sti.id
+ FROM side.region_tiles srt
+ JOIN region_mapping rm ON srt.region_id = rm.side_region_id
+ JOIN (SELECT t.id, st.id AS side_tile_id FROM side.tiles st
+ JOIN tiles t ON st.url_template = t.url_template AND st.pixel_ratio = t.pixel_ratio AND st.z = t.z AND st.x = t.x AND st.y = t.y
+ ) AS sti ON srt.tile_id = sti.side_tile_id;
+
+-- copy over resources
+REPLACE INTO resources
+ SELECT r.id,
+ sr.url, sr.kind, sr.expires, sr.modified, sr.etag,
+ sr.data, sr.compressed, sr.accessed, sr.must_revalidate
+ FROM side.region_resources srr JOIN side.resources sr ON srr.resource_id = sr.id --only consider region resources, and not ambient resources.
+ LEFT JOIN resources r ON sr.url = r.url
+ WHERE r.id IS NULL -- only consider resources that don't exist yet in the main database
+ OR sr.modified > r.modified; -- ...or resources that are newer in the side loaded DB.
+
+-- Update region_resources usage
+INSERT OR IGNORE INTO region_resources
+ SELECT rm.main_region_id, sri.id
+ FROM side.region_resources srr
+ JOIN region_mapping rm ON srr.region_id = rm.side_region_id
+ JOIN (SELECT r.id, sr.id AS side_resource_id FROM side.resources sr
+ JOIN resources r ON sr.url = r.url) AS sri ON srr.resource_id = sri.side_resource_id;
+
+DROP TABLE region_mapping; \ No newline at end of file
diff --git a/platform/default/include/mbgl/storage/offline_database.hpp b/platform/default/include/mbgl/storage/offline_database.hpp
new file mode 100644
index 0000000000..993f36a606
--- /dev/null
+++ b/platform/default/include/mbgl/storage/offline_database.hpp
@@ -0,0 +1,130 @@
+#pragma once
+
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/offline.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/mapbox.hpp>
+#include <mbgl/util/expected.hpp>
+
+#include <unordered_map>
+#include <memory>
+#include <string>
+#include <list>
+
+namespace mapbox {
+namespace sqlite {
+class Database;
+class Statement;
+class Query;
+class Exception;
+} // namespace sqlite
+} // namespace mapbox
+
+namespace mbgl {
+
+class Response;
+class TileID;
+
+namespace util {
+struct IOException;
+} // namespace util
+
+struct MapboxTileLimitExceededException : util::Exception {
+ MapboxTileLimitExceededException() : util::Exception("Mapbox tile limit exceeded") {}
+};
+
+class OfflineDatabase : private util::noncopyable {
+public:
+ // Limits affect ambient caching (put) only; resources required by offline
+ // regions are exempt.
+ OfflineDatabase(std::string path, uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
+ ~OfflineDatabase();
+
+ optional<Response> get(const Resource&);
+
+ // Return value is (inserted, stored size)
+ std::pair<bool, uint64_t> put(const Resource&, const Response&);
+
+ expected<OfflineRegions, std::exception_ptr> listRegions();
+
+ expected<OfflineRegion, std::exception_ptr> createRegion(const OfflineRegionDefinition&,
+ const OfflineRegionMetadata&);
+
+ expected<OfflineRegions, std::exception_ptr>
+ mergeDatabase(const std::string& sideDatabasePath);
+
+ expected<OfflineRegionMetadata, std::exception_ptr>
+ updateMetadata(const int64_t regionID, const OfflineRegionMetadata&);
+
+ std::exception_ptr deleteRegion(OfflineRegion&&);
+
+ // Return value is (response, stored size)
+ optional<std::pair<Response, uint64_t>> getRegionResource(int64_t regionID, const Resource&);
+ optional<int64_t> hasRegionResource(int64_t regionID, const Resource&);
+ uint64_t putRegionResource(int64_t regionID, const Resource&, const Response&);
+ void putRegionResources(int64_t regionID, const std::list<std::tuple<Resource, Response>>&, OfflineRegionStatus&);
+
+ expected<OfflineRegionDefinition, std::exception_ptr> getRegionDefinition(int64_t regionID);
+ expected<OfflineRegionStatus, std::exception_ptr> getRegionCompletedStatus(int64_t regionID);
+
+ void setOfflineMapboxTileCountLimit(uint64_t);
+ uint64_t getOfflineMapboxTileCountLimit();
+ bool offlineMapboxTileCountLimitExceeded();
+ uint64_t getOfflineMapboxTileCount();
+ bool exceedsOfflineMapboxTileCountLimit(const Resource&);
+
+private:
+ void initialize();
+ void handleError(const mapbox::sqlite::Exception&, const char* action);
+ void handleError(const util::IOException&, const char* action);
+
+ void removeExisting();
+ void removeOldCacheTable();
+ void createSchema();
+ void migrateToVersion5();
+ void migrateToVersion3();
+ void migrateToVersion6();
+
+ mapbox::sqlite::Statement& getStatement(const char *);
+
+ optional<std::pair<Response, uint64_t>> getTile(const Resource::TileData&);
+ optional<int64_t> hasTile(const Resource::TileData&);
+ bool putTile(const Resource::TileData&, const Response&,
+ const std::string&, bool compressed);
+
+ optional<std::pair<Response, uint64_t>> getResource(const Resource&);
+ optional<int64_t> hasResource(const Resource&);
+ bool putResource(const Resource&, const Response&,
+ const std::string&, bool compressed);
+
+ uint64_t putRegionResourceInternal(int64_t regionID, const Resource&, const Response&);
+
+ optional<std::pair<Response, uint64_t>> getInternal(const Resource&);
+ optional<int64_t> hasInternal(const Resource&);
+ std::pair<bool, uint64_t> putInternal(const Resource&, const Response&, bool evict);
+
+ // Return value is true iff the resource was previously unused by any other regions.
+ bool markUsed(int64_t regionID, const Resource&);
+
+ std::pair<int64_t, int64_t> getCompletedResourceCountAndSize(int64_t regionID);
+ std::pair<int64_t, int64_t> getCompletedTileCountAndSize(int64_t regionID);
+
+ const std::string path;
+ std::unique_ptr<mapbox::sqlite::Database> db;
+ std::unordered_map<const char *, const std::unique_ptr<mapbox::sqlite::Statement>> statements;
+
+ template <class T>
+ T getPragma(const char *);
+
+ uint64_t maximumCacheSize;
+
+ uint64_t offlineMapboxTileCountLimit = util::mapbox::DEFAULT_OFFLINE_TILE_COUNT_LIMIT;
+ optional<uint64_t> offlineMapboxTileCount;
+
+ bool evict(uint64_t neededFreeSize);
+};
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/storage/offline_download.hpp b/platform/default/include/mbgl/storage/offline_download.hpp
new file mode 100644
index 0000000000..1e77ff1d35
--- /dev/null
+++ b/platform/default/include/mbgl/storage/offline_download.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <mbgl/storage/offline.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/online_file_source.hpp>
+
+#include <list>
+#include <unordered_set>
+#include <memory>
+#include <deque>
+
+namespace mbgl {
+
+class OfflineDatabase;
+class FileSource;
+class AsyncRequest;
+class Response;
+class Tileset;
+
+namespace style {
+class Parser;
+} // namespace style
+
+/**
+ * Coordinates the request and storage of all resources for an offline region.
+
+ * @private
+ */
+class OfflineDownload {
+public:
+ OfflineDownload(int64_t id, OfflineRegionDefinition&&, OfflineDatabase& offline, OnlineFileSource& online);
+ ~OfflineDownload();
+
+ void setObserver(std::unique_ptr<OfflineRegionObserver>);
+ void setState(OfflineRegionDownloadState);
+
+ OfflineRegionStatus getStatus() const;
+
+private:
+ void activateDownload();
+ void continueDownload();
+ void deactivateDownload();
+
+ /*
+ * Ensure that the resource is stored in the database, requesting it if necessary.
+ * While the request is in progress, it is recorded in `requests`. If the download
+ * is deactivated, all in progress requests are cancelled.
+ */
+ void ensureResource(const Resource&, std::function<void (Response)> = {});
+
+ void onMapboxTileCountLimitExceeded();
+
+ int64_t id;
+ OfflineRegionDefinition definition;
+ OfflineDatabase& offlineDatabase;
+ OnlineFileSource& onlineFileSource;
+ OfflineRegionStatus status;
+ std::unique_ptr<OfflineRegionObserver> observer;
+
+ std::list<std::unique_ptr<AsyncRequest>> requests;
+ std::unordered_set<std::string> requiredSourceURLs;
+ std::deque<Resource> resourcesRemaining;
+ std::list<std::tuple<Resource, Response>> buffer;
+
+ void queueResource(Resource);
+ void queueTiles(style::SourceType, uint16_t tileSize, const Tileset&);
+};
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/storage/offline_schema.hpp b/platform/default/include/mbgl/storage/offline_schema.hpp
new file mode 100644
index 0000000000..e177d0dbd3
--- /dev/null
+++ b/platform/default/include/mbgl/storage/offline_schema.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+// THIS IS A GENERATED FILE; EDIT offline_schema.sql INSTEAD
+// To regenerate, run `node platform/default/mbgl/storage/offline_schema.js`
+
+namespace mbgl {
+
+static constexpr const char* offlineDatabaseSchema =
+"CREATE TABLE resources (\n"
+" id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n"
+" url TEXT NOT NULL,\n"
+" kind INTEGER NOT NULL,\n"
+" expires INTEGER,\n"
+" modified INTEGER,\n"
+" etag TEXT,\n"
+" data BLOB,\n"
+" compressed INTEGER NOT NULL DEFAULT 0,\n"
+" accessed INTEGER NOT NULL,\n"
+" must_revalidate INTEGER NOT NULL DEFAULT 0,\n"
+" UNIQUE (url)\n"
+");\n"
+"CREATE TABLE tiles (\n"
+" id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n"
+" url_template TEXT NOT NULL,\n"
+" pixel_ratio INTEGER NOT NULL,\n"
+" z INTEGER NOT NULL,\n"
+" x INTEGER NOT NULL,\n"
+" y INTEGER NOT NULL,\n"
+" expires INTEGER,\n"
+" modified INTEGER,\n"
+" etag TEXT,\n"
+" data BLOB,\n"
+" compressed INTEGER NOT NULL DEFAULT 0,\n"
+" accessed INTEGER NOT NULL,\n"
+" must_revalidate INTEGER NOT NULL DEFAULT 0,\n"
+" UNIQUE (url_template, pixel_ratio, z, x, y)\n"
+");\n"
+"CREATE TABLE regions (\n"
+" id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n"
+" definition TEXT NOT NULL,\n"
+" description BLOB\n"
+");\n"
+"CREATE TABLE region_resources (\n"
+" region_id INTEGER NOT NULL REFERENCES regions(id) ON DELETE CASCADE,\n"
+" resource_id INTEGER NOT NULL REFERENCES resources(id),\n"
+" UNIQUE (region_id, resource_id)\n"
+");\n"
+"CREATE TABLE region_tiles (\n"
+" region_id INTEGER NOT NULL REFERENCES regions(id) ON DELETE CASCADE,\n"
+" tile_id INTEGER NOT NULL REFERENCES tiles(id),\n"
+" UNIQUE (region_id, tile_id)\n"
+");\n"
+"CREATE INDEX resources_accessed\n"
+"ON resources (accessed);\n"
+"CREATE INDEX tiles_accessed\n"
+"ON tiles (accessed);\n"
+"CREATE INDEX region_resources_resource_id\n"
+"ON region_resources (resource_id);\n"
+"CREATE INDEX region_tiles_tile_id\n"
+"ON region_tiles (tile_id);\n"
+;
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/storage/offline_schema.js b/platform/default/include/mbgl/storage/offline_schema.js
new file mode 100644
index 0000000000..fdb7dc6405
--- /dev/null
+++ b/platform/default/include/mbgl/storage/offline_schema.js
@@ -0,0 +1,21 @@
+var fs = require('fs');
+fs.writeFileSync('platform/default/mbgl/storage/offline_schema.hpp', `#pragma once
+
+// THIS IS A GENERATED FILE; EDIT offline_schema.sql INSTEAD
+// To regenerate, run \`node platform/default/mbgl/storage/offline_schema.js\`
+
+namespace mbgl {
+
+static constexpr const char* offlineDatabaseSchema =
+${fs.readFileSync('platform/default/mbgl/storage/offline_schema.sql', 'utf8')
+ .replace(/ *--.*/g, '')
+ .split('\n')
+ .filter(a => a)
+ .map(line => '"' + line + '\\n"')
+ .join('\n')
+}
+;
+
+} // namespace mbgl
+`);
+
diff --git a/platform/default/include/mbgl/storage/offline_schema.sql b/platform/default/include/mbgl/storage/offline_schema.sql
new file mode 100644
index 0000000000..722b0e0451
--- /dev/null
+++ b/platform/default/include/mbgl/storage/offline_schema.sql
@@ -0,0 +1,64 @@
+CREATE TABLE resources ( -- Generic table for style, source, sprite, and glyph resources.
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+ url TEXT NOT NULL,
+ kind INTEGER NOT NULL,
+ expires INTEGER,
+ modified INTEGER,
+ etag TEXT,
+ data BLOB,
+ compressed INTEGER NOT NULL DEFAULT 0,
+ accessed INTEGER NOT NULL,
+ must_revalidate INTEGER NOT NULL DEFAULT 0,
+ UNIQUE (url)
+);
+
+CREATE TABLE tiles (
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+ url_template TEXT NOT NULL,
+ pixel_ratio INTEGER NOT NULL,
+ z INTEGER NOT NULL,
+ x INTEGER NOT NULL,
+ y INTEGER NOT NULL,
+ expires INTEGER,
+ modified INTEGER,
+ etag TEXT,
+ data BLOB,
+ compressed INTEGER NOT NULL DEFAULT 0,
+ accessed INTEGER NOT NULL,
+ must_revalidate INTEGER NOT NULL DEFAULT 0,
+ UNIQUE (url_template, pixel_ratio, z, x, y)
+);
+
+CREATE TABLE regions (
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+ definition TEXT NOT NULL, -- JSON formatted definition of region. Regions may be of variant types:
+ -- e.g. bbox and zoom range, route path, flyTo parameters, etc. Note that
+ -- the set of tiles required for a region may span multiple sources.
+ description BLOB -- User provided data in user-defined format
+);
+
+CREATE TABLE region_resources (
+ region_id INTEGER NOT NULL REFERENCES regions(id) ON DELETE CASCADE,
+ resource_id INTEGER NOT NULL REFERENCES resources(id),
+ UNIQUE (region_id, resource_id)
+);
+
+CREATE TABLE region_tiles (
+ region_id INTEGER NOT NULL REFERENCES regions(id) ON DELETE CASCADE,
+ tile_id INTEGER NOT NULL REFERENCES tiles(id),
+ UNIQUE (region_id, tile_id)
+);
+
+-- Indexes for efficient eviction queries
+
+CREATE INDEX resources_accessed
+ON resources (accessed);
+
+CREATE INDEX tiles_accessed
+ON tiles (accessed);
+
+CREATE INDEX region_resources_resource_id
+ON region_resources (resource_id);
+
+CREATE INDEX region_tiles_tile_id
+ON region_tiles (tile_id);
diff --git a/platform/default/include/mbgl/storage/sqlite3.hpp b/platform/default/include/mbgl/storage/sqlite3.hpp
new file mode 100644
index 0000000000..44dc746075
--- /dev/null
+++ b/platform/default/include/mbgl/storage/sqlite3.hpp
@@ -0,0 +1,180 @@
+#pragma once
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <chrono>
+#include <memory>
+#include <mapbox/variant.hpp>
+
+namespace mapbox {
+namespace sqlite {
+
+enum OpenFlag : int {
+ ReadOnly = 0b001,
+ ReadWriteCreate = 0b110,
+};
+
+enum class ResultCode : uint8_t {
+ OK = 0,
+ Error = 1,
+ Internal = 2,
+ Perm = 3,
+ Abort = 4,
+ Busy = 5,
+ Locked = 6,
+ NoMem = 7,
+ ReadOnly = 8,
+ Interrupt = 9,
+ IOErr = 10,
+ Corrupt = 11,
+ NotFound = 12,
+ Full = 13,
+ CantOpen = 14,
+ Protocol = 15,
+ Schema = 17,
+ TooBig = 18,
+ Constraint = 19,
+ Mismatch = 20,
+ Misuse = 21,
+ NoLFS = 22,
+ Auth = 23,
+ Range = 25,
+ NotADB = 26,
+};
+
+enum class ExtendedResultCode : uint8_t {
+ Unknown = 0,
+ ReadOnlyDBMoved = 4,
+};
+
+class Exception : public std::runtime_error {
+public:
+ Exception(ResultCode err, const char* msg) : Exception(static_cast<int>(err), msg) {}
+ Exception(int err, const char* msg) : Exception(err, std::string{ msg }) {}
+ Exception(int err, const std::string& msg)
+ : std::runtime_error(msg),
+ code(static_cast<ResultCode>(err)),
+ extendedCode(static_cast<ExtendedResultCode>(err >> 8)) {
+ }
+ const ResultCode code = ResultCode::OK;
+ const ExtendedResultCode extendedCode = ExtendedResultCode::Unknown;
+};
+
+class DatabaseImpl;
+class Statement;
+class StatementImpl;
+class Query;
+class Transaction;
+
+void setTempPath(const std::string&);
+
+class Database {
+private:
+ Database(std::unique_ptr<DatabaseImpl>);
+
+public:
+ Database(const Database &) = delete;
+ Database &operator=(const Database &) = delete;
+ static mapbox::util::variant<Database, Exception> tryOpen(const std::string &filename, int flags = 0);
+ static Database open(const std::string &filename, int flags = 0);
+
+ Database(Database &&);
+ ~Database();
+ Database &operator=(Database &&);
+
+ void setBusyTimeout(std::chrono::milliseconds);
+ void exec(const std::string &sql);
+
+private:
+ std::unique_ptr<DatabaseImpl> impl;
+
+ friend class Statement;
+ friend class Transaction;
+};
+
+// A Statement object represents a prepared statement that can be run repeatedly run with a Query object.
+class Statement {
+public:
+ Statement(Database& db, const char* sql);
+ Statement(const Statement&) = delete;
+ Statement(Statement&&) = delete;
+ Statement& operator=(const Statement&) = delete;
+ Statement& operator=(Statement&&) = delete;
+ ~Statement();
+
+ friend class Query;
+
+private:
+ std::unique_ptr<StatementImpl> impl;
+
+#ifndef NDEBUG
+ // This flag stores whether there exists a Query object that uses this prepared statement.
+ // There may only be one Query object at a time. Statement objects must outlive Query objects.
+ // While a Query object exists, a Statement object may not be moved or deleted.
+ bool used = false;
+#endif
+};
+
+// A Query object is used to run a database query with a prepared statement (stored in a Statement
+// object). There may only exist one Query object per Statement object. Query objects are designed
+// to be constructed and destroyed frequently.
+class Query {
+public:
+ Query(Statement&);
+ Query(const Query&) = delete;
+ Query(Query&&) = delete;
+ Query& operator=(const Query&) = delete;
+ Query& operator=(Query&&) = delete;
+ ~Query();
+
+ template <typename T>
+ void bind(int offset, T value);
+
+ // Text
+ void bind(int offset, const char*, std::size_t length, bool retain = true);
+ void bind(int offset, const std::string&, bool retain = true);
+
+ // Blob
+ void bindBlob(int offset, const void*, std::size_t length, bool retain = true);
+ void bindBlob(int offset, const std::vector<uint8_t>&, bool retain = true);
+
+ template <typename T>
+ T get(int offset);
+
+ bool run();
+ void reset();
+ void clearBindings();
+
+ int64_t lastInsertRowId() const;
+ uint64_t changes() const;
+
+private:
+ Statement& stmt;
+};
+
+class Transaction {
+public:
+ Transaction(const Transaction&) = delete;
+ Transaction(Transaction&&) = delete;
+ Transaction& operator=(const Transaction&) = delete;
+
+ enum Mode {
+ Deferred,
+ Immediate,
+ Exclusive
+ };
+
+ Transaction(Database&, Mode = Deferred);
+ ~Transaction();
+
+ void commit();
+ void rollback();
+
+private:
+ DatabaseImpl& dbImpl;
+ bool needRollback = true;
+};
+
+} // namespace sqlite
+} // namespace mapbox
diff --git a/platform/default/include/mbgl/text/unaccent.hpp b/platform/default/include/mbgl/text/unaccent.hpp
new file mode 100644
index 0000000000..85ac37a7de
--- /dev/null
+++ b/platform/default/include/mbgl/text/unaccent.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <string>
+
+namespace mbgl {
+namespace platform {
+
+// Non-locale-aware diacritic folding based on nunicode
+// Used as a fallback when locale-aware comparisons aren't available
+std::string unaccent(const std::string &string);
+
+} // namespace platform
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/util/default_styles.hpp b/platform/default/include/mbgl/util/default_styles.hpp
new file mode 100644
index 0000000000..13f08252a7
--- /dev/null
+++ b/platform/default/include/mbgl/util/default_styles.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <vector>
+#include <string>
+
+namespace mbgl {
+namespace util {
+namespace default_styles {
+
+struct DefaultStyle {
+ const char* url;
+ const char* name;
+ const unsigned currentVersion;
+};
+
+constexpr const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v10", "Streets", 10 };
+constexpr const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v10", "Outdoors", 10 };
+constexpr const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", "Light", 9 };
+constexpr const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark", 9 };
+constexpr const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 };
+constexpr const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets", 10 };
+
+const DefaultStyle orderedStyles[] = {
+ streets, outdoors, light, dark, satellite, satelliteStreets,
+};
+const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle);
+
+} // end namespace default_styles
+} // end namespace util
+} // end namespace mbgl
diff --git a/platform/default/include/mbgl/util/default_thread_pool.hpp b/platform/default/include/mbgl/util/default_thread_pool.hpp
new file mode 100644
index 0000000000..a14d16d771
--- /dev/null
+++ b/platform/default/include/mbgl/util/default_thread_pool.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <mbgl/actor/scheduler.hpp>
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace mbgl {
+
+class ThreadPool : public Scheduler {
+public:
+ ThreadPool(std::size_t count);
+ ~ThreadPool() override;
+
+ void schedule(std::weak_ptr<Mailbox>) override;
+
+private:
+ std::vector<std::thread> threads;
+ std::queue<std::weak_ptr<Mailbox>> queue;
+ std::mutex mutex;
+ std::condition_variable cv;
+ bool terminate { false };
+};
+
+} // namespace mbgl
diff --git a/platform/default/include/mbgl/util/shared_thread_pool.hpp b/platform/default/include/mbgl/util/shared_thread_pool.hpp
new file mode 100644
index 0000000000..04a3cb58d5
--- /dev/null
+++ b/platform/default/include/mbgl/util/shared_thread_pool.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <mbgl/util/default_thread_pool.hpp>
+
+namespace mbgl {
+
+std::shared_ptr<ThreadPool> sharedThreadPool();
+
+} // namespace mbgl