#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { class DatabaseFileSourceThread { public: DatabaseFileSourceThread(std::shared_ptr onlineFileSource_, const std::string& cachePath) : db(std::make_unique(cachePath)), onlineFileSource(std::move(onlineFileSource_)) {} void request(const Resource& resource, const ActorRef& req) { auto offlineResponse = db->get(resource); if (!offlineResponse) { offlineResponse.emplace(); offlineResponse->noContent = true; offlineResponse->error = std::make_unique(Response::Error::Reason::NotFound, "Not found in offline database"); } else if (!offlineResponse->isUsable()) { offlineResponse->error = std::make_unique(Response::Error::Reason::NotFound, "Cached resource is unusable"); } req.invoke(&FileSourceRequest::setResponse, *offlineResponse); } void setDatabasePath(const std::string& path, const std::function& callback) { db->changePath(path); if (callback) { callback(); } } void forward(const Resource& resource, const Response& response, const std::function& callback) { db->put(resource, response); if (callback) { callback(); } } void resetDatabase(const std::function& callback) { callback(db->resetDatabase()); } void packDatabase(const std::function& callback) { callback(db->pack()); } void runPackDatabaseAutomatically(bool autopack) { db->runPackDatabaseAutomatically(autopack); } void put(const Resource& resource, const Response& response) { db->put(resource, response); } void invalidateAmbientCache(const std::function& callback) { callback(db->invalidateAmbientCache()); } void clearAmbientCache(const std::function& callback) { callback(db->clearAmbientCache()); } void setMaximumAmbientCacheSize(uint64_t size, const std::function& callback) { callback(db->setMaximumAmbientCacheSize(size)); } void listRegions(const std::function)>& callback) { callback(db->listRegions()); } void createRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata, const std::function)>& callback) { callback(db->createRegion(definition, metadata)); } void mergeOfflineRegions(const std::string& sideDatabasePath, const std::function)>& callback) { callback(db->mergeDatabase(sideDatabasePath)); } void updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata, const std::function)>& callback) { callback(db->updateMetadata(regionID, metadata)); } void getRegionStatus(int64_t regionID, const std::function)>& callback) { if (auto download = getDownload(regionID)) { callback(download.value()->getStatus()); } else { callback(unexpected(download.error())); } } void deleteRegion(OfflineRegion region, const std::function& callback) { downloads.erase(region.getID()); callback(db->deleteRegion(std::move(region))); } void invalidateRegion(int64_t regionID, const std::function& callback) { callback(db->invalidateRegion(regionID)); } void setRegionObserver(int64_t regionID, std::unique_ptr observer) { if (auto download = getDownload(regionID)) { download.value()->setObserver(std::move(observer)); } } void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) { if (auto download = getDownload(regionID)) { download.value()->setState(state); } } void setOfflineMapboxTileCountLimit(uint64_t limit) { db->setOfflineMapboxTileCountLimit(limit); } void reopenDatabaseReadOnly(bool readOnly) { db->reopenDatabaseReadOnly(readOnly); } private: expected getDownload(int64_t regionID) { if (!onlineFileSource) { return unexpected( std::make_exception_ptr(std::runtime_error("Network file source unavailable."))); } auto it = downloads.find(regionID); if (it != downloads.end()) { return it->second.get(); } auto definition = db->getRegionDefinition(regionID); if (!definition) { return unexpected(definition.error()); } auto download = std::make_unique(regionID, std::move(definition.value()), *db, *onlineFileSource); return downloads.emplace(regionID, std::move(download)).first->second.get(); } std::unique_ptr db; std::map> downloads; std::shared_ptr onlineFileSource; }; class DatabaseFileSource::Impl { public: Impl(std::shared_ptr onlineFileSource, const std::string& cachePath) : thread(std::make_unique>( util::makeThreadPrioritySetter(platform::EXPERIMENTAL_THREAD_PRIORITY_DATABASE), "DatabaseFileSource", std::move(onlineFileSource), cachePath)) {} ActorRef actor() const { return thread->actor(); } void pause() { thread->pause(); } void resume() { thread->resume(); } private: const std::unique_ptr> thread; }; DatabaseFileSource::DatabaseFileSource(const ResourceOptions& options) : impl(std::make_unique(FileSourceManager::get()->getFileSource(FileSourceType::Network, options), options.cachePath())) {} DatabaseFileSource::~DatabaseFileSource() = default; std::unique_ptr DatabaseFileSource::request(const Resource& resource, Callback callback) { auto req = std::make_unique(std::move(callback)); impl->actor().invoke(&DatabaseFileSourceThread::request, resource, req->actor()); return req; } void DatabaseFileSource::forward(const Resource& res, const Response& response, std::function callback) { if (res.storagePolicy == Resource::StoragePolicy::Volatile) return; std::function wrapper; if (callback) { wrapper = Scheduler::GetCurrent()->bindOnce(std::move(callback)); } impl->actor().invoke(&DatabaseFileSourceThread::forward, res, response, std::move(wrapper)); } bool DatabaseFileSource::canRequest(const Resource& resource) const { return resource.hasLoadingMethod(Resource::LoadingMethod::Cache) && resource.url.rfind(mbgl::util::ASSET_PROTOCOL, 0) == std::string::npos && resource.url.rfind(mbgl::util::FILE_PROTOCOL, 0) == std::string::npos; } void DatabaseFileSource::setDatabasePath(const std::string& path, std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::setDatabasePath, path, std::move(callback)); } void DatabaseFileSource::resetDatabase(std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::resetDatabase, std::move(callback)); } void DatabaseFileSource::packDatabase(std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::packDatabase, std::move(callback)); } void DatabaseFileSource::runPackDatabaseAutomatically(bool autopack) { impl->actor().invoke(&DatabaseFileSourceThread::runPackDatabaseAutomatically, autopack); } void DatabaseFileSource::put(const Resource& resource, const Response& response) { impl->actor().invoke(&DatabaseFileSourceThread::put, resource, response); } void DatabaseFileSource::invalidateAmbientCache(std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::invalidateAmbientCache, std::move(callback)); } void DatabaseFileSource::clearAmbientCache(std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::clearAmbientCache, std::move(callback)); } void DatabaseFileSource::setMaximumAmbientCacheSize(uint64_t size, std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::setMaximumAmbientCacheSize, size, std::move(callback)); } void DatabaseFileSource::listOfflineRegions( std::function)> callback) { impl->actor().invoke(&DatabaseFileSourceThread::listRegions, std::move(callback)); } void DatabaseFileSource::createOfflineRegion( const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata, std::function)> callback) { impl->actor().invoke(&DatabaseFileSourceThread::createRegion, definition, metadata, std::move(callback)); } void DatabaseFileSource::mergeOfflineRegions( const std::string& sideDatabasePath, std::function)> callback) { impl->actor().invoke(&DatabaseFileSourceThread::mergeOfflineRegions, sideDatabasePath, std::move(callback)); } void DatabaseFileSource::updateOfflineMetadata( const int64_t regionID, const OfflineRegionMetadata& metadata, std::function)> callback) { impl->actor().invoke(&DatabaseFileSourceThread::updateMetadata, regionID, metadata, std::move(callback)); } void DatabaseFileSource::deleteOfflineRegion(const OfflineRegion& region, std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::deleteRegion, region, std::move(callback)); } void DatabaseFileSource::invalidateOfflineRegion(const OfflineRegion& region, std::function callback) { impl->actor().invoke(&DatabaseFileSourceThread::invalidateRegion, region.getID(), std::move(callback)); } void DatabaseFileSource::setOfflineRegionObserver(const OfflineRegion& region, std::unique_ptr observer) { impl->actor().invoke(&DatabaseFileSourceThread::setRegionObserver, region.getID(), std::move(observer)); } void DatabaseFileSource::setOfflineRegionDownloadState(const OfflineRegion& region, OfflineRegionDownloadState state) { impl->actor().invoke(&DatabaseFileSourceThread::setRegionDownloadState, region.getID(), state); } void DatabaseFileSource::getOfflineRegionStatus( const OfflineRegion& region, std::function)> callback) const { impl->actor().invoke(&DatabaseFileSourceThread::getRegionStatus, region.getID(), std::move(callback)); } void DatabaseFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const { impl->actor().invoke(&DatabaseFileSourceThread::setOfflineMapboxTileCountLimit, limit); } void DatabaseFileSource::setProperty(const std::string& key, const mapbox::base::Value& value) { if (key == READ_ONLY_MODE_KEY && value.getBool()) { impl->actor().invoke(&DatabaseFileSourceThread::reopenDatabaseReadOnly, *value.getBool()); } else { std::string message = "Resource provider does not support property " + key; Log::Error(Event::General, message.c_str()); } } void DatabaseFileSource::pause() { impl->pause(); } void DatabaseFileSource::resume() { impl->resume(); } } // namespace mbgl