diff options
Diffstat (limited to 'platform/default')
22 files changed, 455 insertions, 301 deletions
diff --git a/platform/default/bidi.cpp b/platform/default/bidi.cpp index 25b4dbe3a7..08a02ee60f 100644 --- a/platform/default/bidi.cpp +++ b/platform/default/bidi.cpp @@ -18,7 +18,7 @@ public: UBiDi* bidiText = nullptr; UBiDi* bidiLine = nullptr; }; - + BiDi::BiDi() : impl(std::make_unique<BiDiImpl>()) {} BiDi::~BiDi() = default; @@ -36,8 +36,9 @@ std::u16string applyArabicShaping(const std::u16string& input) { // Pre-flighting will always set U_BUFFER_OVERFLOW_ERROR errorCode = U_ZERO_ERROR; - auto outputText = std::make_unique<UChar[]>(outputLength); - u_shapeArabic(input.c_str(), static_cast<int32_t>(input.size()), outputText.get(), outputLength, + std::u16string outputText(outputLength, 0); + + u_shapeArabic(input.c_str(), static_cast<int32_t>(input.size()), &outputText[0], outputLength, (U_SHAPE_LETTERS_SHAPE & U_SHAPE_LETTERS_MASK) | (U_SHAPE_TEXT_DIRECTION_LOGICAL & U_SHAPE_TEXT_DIRECTION_MASK), &errorCode); @@ -46,7 +47,7 @@ std::u16string applyArabicShaping(const std::u16string& input) { if (U_FAILURE(errorCode)) return input; - return std::u16string(outputText.get(), outputLength); + return outputText; } void BiDi::mergeParagraphLineBreaks(std::set<size_t>& lineBreakPoints) { @@ -73,6 +74,8 @@ std::vector<std::u16string> BiDi::applyLineBreaking(std::set<std::size_t> lineBr mergeParagraphLineBreaks(lineBreakPoints); std::vector<std::u16string> transformedLines; + transformedLines.reserve(lineBreakPoints.size()); + std::size_t start = 0; for (std::size_t lineBreakPoint : lineBreakPoints) { transformedLines.push_back(getLine(start, lineBreakPoint)); @@ -108,12 +111,12 @@ std::u16string BiDi::getLine(std::size_t start, std::size_t end) { // Setting UBIDI_INSERT_LRM_FOR_NUMERIC would require // ubidi_getLength(pBiDi)+2*ubidi_countRuns(pBiDi) const int32_t outputLength = ubidi_getProcessedLength(impl->bidiLine); - auto outputText = std::make_unique<UChar[]>(outputLength); + std::u16string outputText(outputLength, 0); // UBIDI_DO_MIRRORING: Apply unicode mirroring of characters like parentheses // UBIDI_REMOVE_BIDI_CONTROLS: Now that all the lines are set, remove control characters so that // they don't show up on screen (some fonts have glyphs representing them) - ubidi_writeReordered(impl->bidiLine, outputText.get(), outputLength, + ubidi_writeReordered(impl->bidiLine, &outputText[0], outputLength, UBIDI_DO_MIRRORING | UBIDI_REMOVE_BIDI_CONTROLS, &errorCode); if (U_FAILURE(errorCode)) { @@ -121,7 +124,7 @@ std::u16string BiDi::getLine(std::size_t start, std::size_t end) { u_errorName(errorCode)); } - return std::u16string(outputText.get(), outputLength); + return outputText; } } // end namespace mbgl diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index c4222b5a12..20a3eadc8b 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -29,11 +29,11 @@ public: Impl(const std::string& cachePath, uint64_t maximumCacheSize) : offlineDatabase(cachePath, maximumCacheSize) { } - + void setAPIBaseURL(const std::string& url) { onlineFileSource.setAPIBaseURL(url); } - + std::string getAPIBaseURL() const{ return onlineFileSource.getAPIBaseURL(); } @@ -46,6 +46,10 @@ public: return onlineFileSource.getAccessToken(); } + void setResourceTransform(OnlineFileSource::ResourceTransform&& transform) { + onlineFileSource.setResourceTransform(std::move(transform)); + } + void listRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) { try { callback({}, offlineDatabase.listRegions()); @@ -63,7 +67,7 @@ public: callback(std::current_exception(), {}); } } - + void updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata, std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) { @@ -172,19 +176,30 @@ DefaultFileSource::DefaultFileSource(const std::string& cachePath, DefaultFileSource::~DefaultFileSource() = default; void DefaultFileSource::setAPIBaseURL(const std::string& baseURL) { - thread->invokeSync(&Impl::setAPIBaseURL, baseURL); + thread->invoke(&Impl::setAPIBaseURL, baseURL); + cachedBaseURL = baseURL; } - + std::string DefaultFileSource::getAPIBaseURL() const { - return thread->invokeSync(&Impl::getAPIBaseURL); + return cachedBaseURL; } - + void DefaultFileSource::setAccessToken(const std::string& accessToken) { - thread->invokeSync(&Impl::setAccessToken, accessToken); + thread->invoke(&Impl::setAccessToken, accessToken); + cachedAccessToken = accessToken; } std::string DefaultFileSource::getAccessToken() const { - return thread->invokeSync(&Impl::getAccessToken); + return cachedAccessToken; +} + +void DefaultFileSource::setResourceTransform(std::function<std::string(Resource::Kind, std::string&&)> transform) { + auto loop = util::RunLoop::Get(); + thread->invoke(&Impl::setResourceTransform, [loop, transform](Resource::Kind kind_, std::string&& url_, auto callback_) { + return loop->invokeWithCallback([transform](Resource::Kind kind, std::string&& url, auto callback) { + callback(transform(kind, std::move(url))); + }, kind_, std::move(url_), callback_); + }); } std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) { @@ -248,6 +263,14 @@ void DefaultFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const { thread->invokeSync(&Impl::setOfflineMapboxTileCountLimit, limit); } +void DefaultFileSource::pause() { + thread->pause(); +} + +void DefaultFileSource::resume() { + thread->resume(); +} + // For testing only: void DefaultFileSource::put(const Resource& resource, const Response& response) { diff --git a/platform/default/headless_backend_osmesa.cpp b/platform/default/headless_backend_osmesa.cpp index 081bddf170..8ec6079bd0 100644 --- a/platform/default/headless_backend_osmesa.cpp +++ b/platform/default/headless_backend_osmesa.cpp @@ -12,9 +12,6 @@ struct OSMesaImpl : public HeadlessBackend::Impl { } ~OSMesaImpl() { - if (glContext != OSMesaGetCurrentContext()) { - activateContext(); - } OSMesaDestroyContext(glContext); } diff --git a/platform/default/image.cpp b/platform/default/image.cpp index 84db1e9c71..ad9d83a08d 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -2,78 +2,8 @@ #include <mbgl/util/string.hpp> #include <mbgl/util/premultiply.hpp> -#include <png.h> - -template<size_t max, typename... Args> -static std::string sprintf(const char *msg, Args... args) { - char res[max]; - int len = snprintf(res, sizeof(res), msg, args...); - return std::string(res, len); -} - -const static bool png_version_check __attribute__((unused)) = []() { - const png_uint_32 version = png_access_version_number(); - if (version != PNG_LIBPNG_VER) { - throw std::runtime_error(sprintf<96>( - "libpng version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", - PNG_LIBPNG_VER / 10000, (PNG_LIBPNG_VER / 100) % 100, PNG_LIBPNG_VER % 100, - version / 10000, (version / 100) % 100, version % 100)); - } - return true; -}(); - namespace mbgl { -std::string encodePNG(const PremultipliedImage& pre) { - PremultipliedImage copy(pre.size); - std::copy(pre.data.get(), pre.data.get() + pre.bytes(), copy.data.get()); - - UnassociatedImage src = util::unpremultiply(std::move(copy)); - - png_voidp error_ptr = nullptr; - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, nullptr, nullptr); - if (!png_ptr) { - throw std::runtime_error("couldn't create png_ptr"); - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!png_ptr) { - png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); - throw std::runtime_error("couldn't create info_ptr"); - } - - png_set_IHDR(png_ptr, info_ptr, src.size.width, src.size.height, 8, PNG_COLOR_TYPE_RGB_ALPHA, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - jmp_buf *jmp_context = (jmp_buf *)png_get_error_ptr(png_ptr); - if (jmp_context) { - png_destroy_write_struct(&png_ptr, &info_ptr); - throw std::runtime_error("png error"); - } - - std::string result; - png_set_write_fn(png_ptr, &result, [](png_structp png_ptr_, png_bytep data, png_size_t length) { - std::string *out = static_cast<std::string *>(png_get_io_ptr(png_ptr_)); - out->append(reinterpret_cast<char *>(data), length); - }, nullptr); - - struct ptrs { - ptrs(size_t count) : rows(new png_bytep[count]) {} - ~ptrs() { delete[] rows; } - png_bytep *rows = nullptr; - } pointers(src.size.height); - - for (size_t i = 0; i < src.size.height; i++) { - pointers.rows[i] = src.data.get() + src.stride() * i; - } - - png_set_rows(png_ptr, info_ptr, pointers.rows); - png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - - return result; -} - #if !defined(__ANDROID__) && !defined(__APPLE__) PremultipliedImage decodeWebP(const uint8_t*, size_t); #endif // !defined(__ANDROID__) && !defined(__APPLE__) diff --git a/platform/default/jpeg_reader.cpp b/platform/default/jpeg_reader.cpp index 78c74f2fd7..c5e9d880c0 100644 --- a/platform/default/jpeg_reader.cpp +++ b/platform/default/jpeg_reader.cpp @@ -35,7 +35,7 @@ static boolean fill_input_buffer(j_decompress_ptr cinfo) { } static void skip(j_decompress_ptr cinfo, long count) { - if (count <= 0) return; //A zero or negative skip count should be treated as a no-op. + if (count <= 0) return; // A zero or negative skip count should be treated as a no-op. jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src); if (wrap->manager.bytes_in_buffer > 0 && count < static_cast<long>(wrap->manager.bytes_in_buffer)) @@ -48,7 +48,7 @@ static void skip(j_decompress_ptr cinfo, long count) { wrap->stream->seekg(count - wrap->manager.bytes_in_buffer, std::ios_base::cur); // trigger buffer fill wrap->manager.next_input_byte = nullptr; - wrap->manager.bytes_in_buffer = 0; //bytes_in_buffer may be zero on return. + wrap->manager.bytes_in_buffer = 0; // bytes_in_buffer may be zero on return. } } diff --git a/platform/default/local_file_source.cpp b/platform/default/local_file_source.cpp index 5686b453dc..93b42f5fa0 100644 --- a/platform/default/local_file_source.cpp +++ b/platform/default/local_file_source.cpp @@ -14,7 +14,7 @@ namespace { const char* protocol = "file://"; const std::size_t protocolLength = 7; - + } // namespace namespace mbgl { @@ -22,7 +22,7 @@ namespace mbgl { class LocalFileSource::Impl { public: void request(const std::string& url, FileSource::Callback callback) { - //Cut off the protocol + // Cut off the protocol std::string path = mbgl::util::percentDecode(url.substr(protocolLength)); Response response; @@ -58,7 +58,7 @@ LocalFileSource::~LocalFileSource() = default; std::unique_ptr<AsyncRequest> LocalFileSource::request(const Resource& resource, Callback callback) { return thread->invokeWithCallback(&Impl::request, resource.url, callback); } - + bool LocalFileSource::acceptsURL(const std::string& url) { return url.compare(0, protocolLength, protocol) == 0; } diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp index 0bfdf11c98..c105fd6b84 100644 --- a/platform/default/mbgl/gl/headless_backend.cpp +++ b/platform/default/mbgl/gl/headless_backend.cpp @@ -1,5 +1,7 @@ #include <mbgl/gl/headless_backend.hpp> #include <mbgl/gl/headless_display.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/map/backend_scope.hpp> #include <cassert> #include <stdexcept> @@ -8,17 +10,15 @@ namespace mbgl { HeadlessBackend::HeadlessBackend() { - activate(); } HeadlessBackend::HeadlessBackend(std::shared_ptr<HeadlessDisplay> display_) : display(std::move(display_)) { - activate(); } HeadlessBackend::~HeadlessBackend() { - deactivate(); - destroyContext(); + BackendScope scope(*this); + context.reset(); } void HeadlessBackend::activate() { @@ -31,7 +31,8 @@ void HeadlessBackend::activate() { createContext(); } - activateContext(); + assert(hasContext()); + impl->activateContext(); if (!extensionsLoaded) { gl::InitializeExtensions(initializeExtension); @@ -40,7 +41,8 @@ void HeadlessBackend::activate() { } void HeadlessBackend::deactivate() { - deactivateContext(); + assert(hasContext()); + impl->deactivateContext(); active = false; } @@ -48,21 +50,6 @@ void HeadlessBackend::invalidate() { assert(false); } -void HeadlessBackend::destroyContext() { - assert(hasContext()); - impl.reset(); -} - -void HeadlessBackend::activateContext() { - assert(hasContext()); - impl->activateContext(); -} - -void HeadlessBackend::deactivateContext() { - assert(hasContext()); - impl->deactivateContext(); -} - void HeadlessBackend::notifyMapChange(MapChange change) { if (mapChangeCallback) { mapChangeCallback(change); diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp index da8c55e044..e632d0feb6 100644 --- a/platform/default/mbgl/gl/headless_backend.hpp +++ b/platform/default/mbgl/gl/headless_backend.hpp @@ -18,8 +18,6 @@ public: ~HeadlessBackend() override; void invalidate() override; - void activate() override; - void deactivate() override; void notifyMapChange(MapChange) override; void setMapChangeCallback(std::function<void(MapChange)>&& cb) { mapChangeCallback = std::move(cb); } @@ -34,17 +32,14 @@ private: // Implementation specific functions static gl::glProc initializeExtension(const char*); + void activate() override; + void deactivate() override; + bool hasContext() const { return bool(impl); } bool hasDisplay(); void createContext(); -private: - void destroyContext(); - - void activateContext(); - void deactivateContext(); - std::unique_ptr<Impl> impl; std::shared_ptr<HeadlessDisplay> display; diff --git a/platform/default/mbgl/gl/offscreen_view.cpp b/platform/default/mbgl/gl/offscreen_view.cpp index 16faf6a4a9..a517cefad9 100644 --- a/platform/default/mbgl/gl/offscreen_view.cpp +++ b/platform/default/mbgl/gl/offscreen_view.cpp @@ -1,30 +1,64 @@ #include <mbgl/gl/offscreen_view.hpp> #include <mbgl/gl/context.hpp> +#include <mbgl/gl/framebuffer.hpp> +#include <mbgl/gl/renderbuffer.hpp> +#include <mbgl/util/optional.hpp> #include <cstring> #include <cassert> namespace mbgl { -OffscreenView::OffscreenView(gl::Context& context_, const Size size_) - : size(std::move(size_)), context(context_) { - assert(size); -} +class OffscreenView::Impl { +public: + Impl(gl::Context& context_, const Size size_) : context(context_), size(std::move(size_)) { + assert(size); + } -void OffscreenView::bind() { - if (!framebuffer) { - color = context.createRenderbuffer<gl::RenderbufferType::RGBA>(size); - depthStencil = context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size); - framebuffer = context.createFramebuffer(*color, *depthStencil); - } else { - context.bindFramebuffer = framebuffer->framebuffer; + void bind() { + if (!framebuffer) { + color = context.createRenderbuffer<gl::RenderbufferType::RGBA>(size); + depthStencil = context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size); + framebuffer = context.createFramebuffer(*color, *depthStencil); + } else { + context.bindFramebuffer = framebuffer->framebuffer; + } + + context.viewport = { 0, 0, size }; + } + + PremultipliedImage readStillImage() { + return context.readFramebuffer<PremultipliedImage>(size); + } + + const Size& getSize() const { + return size; } - context.viewport = { 0, 0, size }; +private: + gl::Context& context; + const Size size; + optional<gl::Framebuffer> framebuffer; + optional<gl::Renderbuffer<gl::RenderbufferType::RGBA>> color; + optional<gl::Renderbuffer<gl::RenderbufferType::DepthStencil>> depthStencil; +}; + +OffscreenView::OffscreenView(gl::Context& context, const Size size) + : impl(std::make_unique<Impl>(context, std::move(size))) { +} + +OffscreenView::~OffscreenView() = default; + +void OffscreenView::bind() { + impl->bind(); } PremultipliedImage OffscreenView::readStillImage() { - return context.readFramebuffer<PremultipliedImage>(size); + return impl->readStillImage(); +} + +const Size& OffscreenView::getSize() const { + return impl->getSize(); } } // namespace mbgl diff --git a/platform/default/mbgl/gl/offscreen_view.hpp b/platform/default/mbgl/gl/offscreen_view.hpp index 0e839e14cc..bf1a9889cd 100644 --- a/platform/default/mbgl/gl/offscreen_view.hpp +++ b/platform/default/mbgl/gl/offscreen_view.hpp @@ -1,9 +1,6 @@ #pragma once #include <mbgl/map/view.hpp> -#include <mbgl/gl/framebuffer.hpp> -#include <mbgl/gl/renderbuffer.hpp> -#include <mbgl/util/optional.hpp> #include <mbgl/util/image.hpp> namespace mbgl { @@ -15,19 +12,17 @@ class Context; class OffscreenView : public View { public: OffscreenView(gl::Context&, Size size = { 256, 256 }); + ~OffscreenView(); void bind() override; PremultipliedImage readStillImage(); -public: - const Size size; + const Size& getSize() const; private: - gl::Context& context; - optional<gl::Framebuffer> framebuffer; - optional<gl::Renderbuffer<gl::RenderbufferType::RGBA>> color; - optional<gl::Renderbuffer<gl::RenderbufferType::DepthStencil>> depthStencil; + class Impl; + const std::unique_ptr<Impl> impl; }; } // namespace mbgl diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 49359dbd39..02736f10a4 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -7,7 +7,6 @@ #include <mbgl/util/logging.hpp> #include "sqlite3.hpp" -#include <sqlite3.h> namespace mbgl { @@ -57,13 +56,13 @@ void OfflineDatabase::ensureSchema() { removeExisting(); connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); } catch (mapbox::sqlite::Exception& ex) { - if (ex.code != SQLITE_CANTOPEN && ex.code != SQLITE_NOTADB) { + if (ex.code != mapbox::sqlite::Exception::Code::CANTOPEN && ex.code != mapbox::sqlite::Exception::Code::NOTADB) { Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what()); throw; } try { - if (ex.code == SQLITE_NOTADB) { + if (ex.code == mapbox::sqlite::Exception::Code::NOTADB) { removeExisting(); } connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); @@ -313,7 +312,7 @@ bool OfflineDatabase::putResource(const Resource& resource, } update->run(); - if (db->changes() != 0) { + if (update->changes() != 0) { transaction.commit(); return false; } @@ -502,7 +501,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, } update->run(); - if (db->changes() != 0) { + if (update->changes() != 0) { transaction.commit(); return false; } @@ -567,7 +566,7 @@ OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& defin stmt->bindBlob(2, metadata); stmt->run(); - return OfflineRegion(db->lastInsertRowid(), definition, metadata); + return OfflineRegion(stmt->lastInsertRowId(), definition, metadata); } OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) { @@ -579,7 +578,7 @@ OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, co stmt->bindBlob(1, metadata); stmt->bind(2, regionID); stmt->run(); - + return metadata; } @@ -656,7 +655,7 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { insert->bind(6, tile.z); insert->run(); - if (db->changes() == 0) { + if (insert->changes() == 0) { return false; } @@ -693,7 +692,7 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { insert->bind(2, resource.url); insert->run(); - if (db->changes() == 0) { + if (insert->changes() == 0) { return false; } @@ -816,7 +815,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { return false; } Timestamp accessed = accessedStmt->get<Timestamp>(0); - + // clang-format off Statement stmt1 = getStatement( "DELETE FROM resources " @@ -830,7 +829,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { // clang-format on stmt1->bind(1, accessed); stmt1->run(); - uint64_t changes1 = db->changes(); + uint64_t changes1 = stmt1->changes(); // clang-format off Statement stmt2 = getStatement( @@ -845,7 +844,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { // clang-format on stmt2->bind(1, accessed); stmt2->run(); - uint64_t changes2 = db->changes(); + uint64_t changes2 = stmt2->changes(); // The cached value of offlineTileCount does not need to be updated // here because only non-offline tiles can be removed by eviction. diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 875677f7cf..57ffcee4eb 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -41,7 +41,7 @@ public: const OfflineRegionMetadata&); OfflineRegionMetadata updateMetadata(const int64_t regionID, const OfflineRegionMetadata&); - + void deleteRegion(OfflineRegion&&); // Return value is (response, stored size) diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 3edc75845c..c8aa98d874 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -252,7 +252,7 @@ void OfflineDownload::ensureResource(const Resource& resource, auto workRequestsIt = requests.insert(requests.begin(), nullptr); *workRequestsIt = util::RunLoop::Get()->invokeCancellable([=]() { requests.erase(workRequestsIt); - + auto getResourceSizeInDatabase = [&] () -> optional<int64_t> { if (!callback) { return offlineDatabase.hasRegionResource(id, resource); @@ -264,7 +264,7 @@ void OfflineDownload::ensureResource(const Resource& resource, callback(response->first); return response->second; }; - + optional<int64_t> offlineResponse = getResourceSizeInDatabase(); if (offlineResponse) { status.completedResourceCount++; diff --git a/platform/default/mbgl/storage/offline_download.hpp b/platform/default/mbgl/storage/offline_download.hpp index f29a053a87..c978ded931 100644 --- a/platform/default/mbgl/storage/offline_download.hpp +++ b/platform/default/mbgl/storage/offline_download.hpp @@ -47,7 +47,7 @@ private: */ void ensureResource(const Resource&, std::function<void (Response)> = {}); bool checkTileCountLimit(const Resource& resource); - + int64_t id; OfflineRegionDefinition definition; OfflineDatabase& offlineDatabase; diff --git a/platform/default/mbgl/util/default_thread_pool.cpp b/platform/default/mbgl/util/default_thread_pool.cpp index 92c0f06745..d3950bb8aa 100644 --- a/platform/default/mbgl/util/default_thread_pool.cpp +++ b/platform/default/mbgl/util/default_thread_pool.cpp @@ -1,12 +1,16 @@ #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/actor/mailbox.hpp> +#include <mbgl/util/platform.hpp> +#include <mbgl/util/string.hpp> namespace mbgl { ThreadPool::ThreadPool(std::size_t count) { threads.reserve(count); for (std::size_t i = 0; i < count; ++i) { - threads.emplace_back([this] () { + threads.emplace_back([this, i]() { + platform::setCurrentThreadName(std::string{ "Worker " } + util::toString(i + 1)); + while (true) { std::unique_lock<std::mutex> lock(mutex); diff --git a/platform/default/mbgl/util/shared_thread_pool.cpp b/platform/default/mbgl/util/shared_thread_pool.cpp new file mode 100644 index 0000000000..7a42df21de --- /dev/null +++ b/platform/default/mbgl/util/shared_thread_pool.cpp @@ -0,0 +1,14 @@ +#include "shared_thread_pool.hpp" + +namespace mbgl { + +std::shared_ptr<ThreadPool> sharedThreadPool() { + static std::weak_ptr<ThreadPool> weak; + auto pool = weak.lock(); + if (!pool) { + weak = pool = std::make_shared<ThreadPool>(4); + } + return pool; +} + +} // namespace mbgl diff --git a/platform/default/mbgl/util/shared_thread_pool.hpp b/platform/default/mbgl/util/shared_thread_pool.hpp new file mode 100644 index 0000000000..04a3cb58d5 --- /dev/null +++ b/platform/default/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 diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp index 0f2bc5ff56..a72b6f4efc 100644 --- a/platform/default/online_file_source.cpp +++ b/platform/default/online_file_source.cpp @@ -31,6 +31,7 @@ public: ~OnlineFileRequest() override; void networkIsReachableAgain(); + void schedule(); void schedule(optional<Timestamp> expires); void completed(Response); @@ -64,6 +65,19 @@ public: void add(OnlineFileRequest* request) { allRequests.insert(request); + if (resourceTransform) { + // When there's a Resource transform callback set, replace the resource with the + // transformed one before proceeding to schedule the request. + request->request = + resourceTransform(request->resource.kind, std::move(request->resource.url), + [request](std::string&& url) { + request->request.release(); + request->resource.url = std::move(url); + request->schedule(); + }); + } else { + request->schedule(); + } } void remove(OnlineFileRequest* request) { @@ -122,15 +136,19 @@ public: activateRequest(request); assert(pendingRequestsMap.size() == pendingRequestsList.size()); } - + bool isPending(OnlineFileRequest* request) { return pendingRequestsMap.find(request) != pendingRequestsMap.end(); } - + bool isActive(OnlineFileRequest* request) { return activeRequests.find(request) != activeRequests.end(); } + void setResourceTransform(ResourceTransform&& transform) { + resourceTransform = std::move(transform); + } + private: void networkIsReachableAgain() { for (auto& request : allRequests) { @@ -138,6 +156,8 @@ private: } } + ResourceTransform resourceTransform; + /** * The lifetime of a request is: * @@ -196,12 +216,18 @@ std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource return std::make_unique<OnlineFileRequest>(std::move(res), std::move(callback), *impl); } +void OnlineFileSource::setResourceTransform(ResourceTransform&& transform) { + impl->setResourceTransform(std::move(transform)); +} + OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSource::Impl& impl_) : impl(impl_), resource(std::move(resource_)), callback(std::move(callback_)) { impl.add(this); +} +void OnlineFileRequest::schedule() { // Force an immediate first request if we don't have an expiration time. if (resource.priorExpires) { schedule(resource.priorExpires); diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp index 5ae74d74db..d694f43405 100644 --- a/platform/default/png_reader.cpp +++ b/platform/default/png_reader.cpp @@ -11,6 +11,24 @@ extern "C" #include <png.h> } +template<size_t max, typename... Args> +static std::string sprintf(const char *msg, Args... args) { + char res[max]; + int len = snprintf(res, sizeof(res), msg, args...); + return std::string(res, len); +} + +const static bool png_version_check __attribute__((unused)) = []() { + const png_uint_32 version = png_access_version_number(); + if (version != PNG_LIBPNG_VER) { + throw std::runtime_error(sprintf<96>( + "libpng version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", + PNG_LIBPNG_VER / 10000, (PNG_LIBPNG_VER / 100) % 100, PNG_LIBPNG_VER % 100, + version / 10000, (version / 100) % 100, version % 100)); + } + return true; +}(); + namespace mbgl { static void user_error_fn(png_structp, png_const_charp error_msg) { diff --git a/platform/default/png_writer.cpp b/platform/default/png_writer.cpp new file mode 100644 index 0000000000..9ef9052158 --- /dev/null +++ b/platform/default/png_writer.cpp @@ -0,0 +1,80 @@ +#include <mbgl/util/compression.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/premultiply.hpp> + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include <boost/crc.hpp> +#pragma GCC diagnostic pop + +#include <cassert> +#include <cstring> + +#define NETWORK_BYTE_UINT32(value) \ + char(value >> 24), char(value >> 16), char(value >> 8), char(value >> 0) + +namespace { + +void addChunk(std::string& png, const char* type, const char* data = "", const uint32_t size = 0) { + assert(strlen(type) == 4); + + // Checksum encompasses type + data + boost::crc_32_type checksum; + checksum.process_bytes(type, 4); + checksum.process_bytes(data, size); + + const char length[4] = { NETWORK_BYTE_UINT32(size) }; + const char crc[4] = { NETWORK_BYTE_UINT32(checksum.checksum()) }; + + png.reserve(png.size() + 4 /* length */ + 4 /* type */ + size + 4 /* CRC */); + png.append(length, 4); + png.append(type, 4); + png.append(data, size); + png.append(crc, 4); +} + +} // namespace + +namespace mbgl { + +// Encode PNGs without libpng. +std::string encodePNG(const PremultipliedImage& pre) { + // Make copy of the image so that we can unpremultiply it. + const auto src = util::unpremultiply(pre.clone()); + + // PNG magic bytes + const char preamble[8] = { char(0x89), 'P', 'N', 'G', '\r', '\n', 0x1a, '\n' }; + + // IHDR chunk for our RGBA image. + const char ihdr[13] = { + NETWORK_BYTE_UINT32(src.size.width), // width + NETWORK_BYTE_UINT32(src.size.height), // height + 8, // bit depth == 8 bits + 6, // color type == RGBA + 0, // compression method == deflate + 0, // filter method == default + 0, // interlace method == none + }; + + // Prepare the (compressed) data chunk. + const auto stride = src.stride(); + std::string idat; + for (uint32_t y = 0; y < src.size.height; y++) { + // Every scanline needs to be prefixed with one byte that indicates the filter type. + idat.append(1, 0); // filter type 0 + idat.append((const char*)(src.data.get() + y * stride), stride); + } + idat = util::compress(idat); + + // Assemble the PNG. + std::string png; + png.reserve((8 /* preamble */) + (12 + 13 /* IHDR */) + + (12 + idat.size() /* IDAT */) + (12 /* IEND */)); + png.append(preamble, 8); + addChunk(png, "IHDR", ihdr, 13); + addChunk(png, "IDAT", idat.data(), static_cast<uint32_t>(idat.size())); + addChunk(png, "IEND"); + return png; +} + +} // namespace mbgl diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 670b7b84cc..498ac02b2e 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -12,9 +12,73 @@ namespace mapbox { namespace sqlite { +class DatabaseImpl { +public: + DatabaseImpl(const char* filename, int flags) + { + const int error = sqlite3_open_v2(filename, &db, flags, nullptr); + if (error != SQLITE_OK) { + const auto message = sqlite3_errmsg(db); + db = nullptr; + throw Exception { error, message }; + } + } + + ~DatabaseImpl() + { + if (!db) return; + + const int error = sqlite3_close(db); + if (error != SQLITE_OK) { + throw Exception { error, sqlite3_errmsg(db) }; + } + } + + sqlite3* db = nullptr; +}; + +class StatementImpl { +public: + StatementImpl(sqlite3* db, const char* sql) + { + const int error = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + if (error != SQLITE_OK) { + stmt = nullptr; + throw Exception { error, sqlite3_errmsg(db) }; + } + } + + ~StatementImpl() + { + if (!stmt) return; + + sqlite3_finalize(stmt); + } + + void check(int err) { + if (err != SQLITE_OK) { + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; + } + } + + sqlite3_stmt* stmt = nullptr; + int64_t lastInsertRowId = 0; + int64_t changes = 0; +}; + template <typename T> using optional = std::experimental::optional<T>; +static void errorLogCallback(void *, const int err, const char *msg) { + if (err == SQLITE_ERROR) { + mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err); + } else if (err == SQLITE_WARNING) { + mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err); + } else { + mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err); + } +} + const static bool sqliteVersionCheck __attribute__((unused)) = []() { if (sqlite3_libversion_number() / 1000000 != SQLITE_VERSION_NUMBER / 1000000) { char message[96]; @@ -25,94 +89,61 @@ const static bool sqliteVersionCheck __attribute__((unused)) = []() { } // Enable SQLite logging before initializing the database. - sqlite3_config(SQLITE_CONFIG_LOG, Database::errorLogCallback, nullptr); + sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, nullptr); return true; }(); -Database::Database(const std::string &filename, int flags) { - const int err = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr); - if (err != SQLITE_OK) { - const auto message = sqlite3_errmsg(db); - db = nullptr; - throw Exception { err, message }; - } +Database::Database(const std::string &filename, int flags) + : impl(std::make_unique<DatabaseImpl>(filename.c_str(), flags)) +{ } Database::Database(Database &&other) - : db(std::move(other.db)) {} + : impl(std::move(other.impl)) {} Database &Database::operator=(Database &&other) { - std::swap(db, other.db); + std::swap(impl, other.impl); 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::errorLogCallback(void *, const int err, const char *msg) { - if (err == SQLITE_ERROR) { - mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else if (err == SQLITE_WARNING) { - mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else { - mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err); - } + return impl.operator bool(); } void Database::setBusyTimeout(std::chrono::milliseconds timeout) { - assert(db); - const int err = sqlite3_busy_timeout(db, + assert(impl); + const int err = sqlite3_busy_timeout(impl->db, int(std::min<std::chrono::milliseconds::rep>(timeout.count(), std::numeric_limits<int>::max()))); if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(db) }; + throw Exception { err, sqlite3_errmsg(impl->db) }; } } void Database::exec(const std::string &sql) { - assert(db); + assert(impl); char *msg = nullptr; - const int err = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &msg); + const int err = sqlite3_exec(impl->db, sql.c_str(), nullptr, nullptr, &msg); if (msg) { const std::string message = msg; sqlite3_free(msg); throw Exception { err, message }; } else if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(db) }; + throw Exception { err, sqlite3_errmsg(impl->db) }; } } Statement Database::prepare(const char *query) { - assert(db); - return Statement(db, query); -} - -int64_t Database::lastInsertRowid() const { - assert(db); - return sqlite3_last_insert_rowid(db); + assert(impl); + return Statement(this, query); } -uint64_t Database::changes() const { - assert(db); - return sqlite3_changes(db); -} - -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) }; - } +Statement::Statement(Database *db, const char *sql) + : impl(std::make_unique<StatementImpl>(db->impl->db, sql)) +{ } Statement::Statement(Statement &&other) { @@ -120,99 +151,90 @@ Statement::Statement(Statement &&other) { } Statement &Statement::operator=(Statement &&other) { - std::swap(stmt, other.stmt); + std::swap(impl, other.impl); return *this; } Statement::~Statement() { - if (stmt) { - sqlite3_finalize(stmt); - } } Statement::operator bool() const { - return stmt != nullptr; -} - -void Statement::check(int err) { - if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; - } + return impl.operator bool(); } template <> void Statement::bind(int offset, std::nullptr_t) { - assert(stmt); - check(sqlite3_bind_null(stmt, offset)); + assert(impl); + impl->check(sqlite3_bind_null(impl->stmt, offset)); } template <> void Statement::bind(int offset, int8_t value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, int16_t value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, int32_t value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, int64_t value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, uint8_t value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, uint16_t value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, uint32_t value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, float value) { - assert(stmt); - check(sqlite3_bind_double(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_double(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, double value) { - assert(stmt); - check(sqlite3_bind_double(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_double(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, bool value) { - assert(stmt); - check(sqlite3_bind_int(stmt, offset, value)); + assert(impl); + impl->check(sqlite3_bind_int(impl->stmt, offset, value)); } template <> void Statement::bind(int offset, const char *value) { - assert(stmt); - check(sqlite3_bind_text(stmt, offset, value, -1, SQLITE_STATIC)); + assert(impl); + impl->check(sqlite3_bind_text(impl->stmt, offset, value, -1, SQLITE_STATIC)); } // We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they // was introduced in SQLite 3.8.7, and we need to support earlier versions: -// iOS 7.0: 3.7.13 +// iOS 8.0: 3.7.13 // iOS 8.2: 3.8.5 // According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, -// the first iOS version with 3.8.7+ was 9.0, with 3.8.10.2. +// the first iOS version with 3.8.7+ was 9.0, with 3.8.8. void Statement::bind(int offset, const char * value, std::size_t length, bool retain) { - assert(stmt); + assert(impl); if (length > std::numeric_limits<int>::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - check(sqlite3_bind_text(stmt, offset, value, int(length), + impl->check(sqlite3_bind_text(impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } @@ -221,11 +243,11 @@ void Statement::bind(int offset, const std::string& value, bool retain) { } void Statement::bindBlob(int offset, const void * value, std::size_t length, bool retain) { - assert(stmt); + assert(impl); if (length > std::numeric_limits<int>::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - check(sqlite3_bind_blob(stmt, offset, value, int(length), + impl->check(sqlite3_bind_blob(impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } @@ -235,9 +257,9 @@ void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool ret template <> void Statement::bind( - int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) { - assert(stmt); - check(sqlite3_bind_int64(stmt, offset, std::chrono::system_clock::to_time_t(value))); + int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) { + assert(impl); + impl->check(sqlite3_bind_int64(impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); } template <> void Statement::bind(int offset, optional<std::string> value) { @@ -260,60 +282,62 @@ void Statement::bind( } bool Statement::run() { - assert(stmt); - const int err = sqlite3_step(stmt); + assert(impl); + const int err = sqlite3_step(impl->stmt); + impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(impl->stmt)); + impl->changes = sqlite3_changes(sqlite3_db_handle(impl->stmt)); if (err == SQLITE_DONE) { return false; } else if (err == SQLITE_ROW) { return true; } else if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt)) }; + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(impl->stmt)) }; } else { return false; } } template <> int Statement::get(int offset) { - assert(stmt); - return sqlite3_column_int(stmt, offset); + assert(impl); + return sqlite3_column_int(impl->stmt, offset); } template <> int64_t Statement::get(int offset) { - assert(stmt); - return sqlite3_column_int64(stmt, offset); + assert(impl); + return sqlite3_column_int64(impl->stmt, offset); } template <> double Statement::get(int offset) { - assert(stmt); - return sqlite3_column_double(stmt, offset); + assert(impl); + return sqlite3_column_double(impl->stmt, offset); } template <> std::string Statement::get(int offset) { - assert(stmt); + assert(impl); return { - reinterpret_cast<const char *>(sqlite3_column_blob(stmt, offset)), - size_t(sqlite3_column_bytes(stmt, offset)) + reinterpret_cast<const char *>(sqlite3_column_blob(impl->stmt, offset)), + size_t(sqlite3_column_bytes(impl->stmt, offset)) }; } template <> std::vector<uint8_t> Statement::get(int offset) { - assert(stmt); - const uint8_t* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(stmt, offset)); - const uint8_t* end = begin + sqlite3_column_bytes(stmt, offset); + assert(impl); + const uint8_t* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(impl->stmt, offset)); + const uint8_t* end = begin + sqlite3_column_bytes(impl->stmt, offset); return { begin, end }; } template <> std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> Statement::get(int offset) { - assert(stmt); + assert(impl); return std::chrono::time_point_cast<std::chrono::seconds>( - std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt, offset))); + std::chrono::system_clock::from_time_t(sqlite3_column_int64(impl->stmt, offset))); } template <> optional<int64_t> Statement::get(int offset) { - assert(stmt); - if (sqlite3_column_type(stmt, offset) == SQLITE_NULL) { + assert(impl); + if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { return optional<int64_t>(); } else { return get<int64_t>(offset); @@ -321,8 +345,8 @@ template <> optional<int64_t> Statement::get(int offset) { } template <> optional<double> Statement::get(int offset) { - assert(stmt); - if (sqlite3_column_type(stmt, offset) == SQLITE_NULL) { + assert(impl); + if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { return optional<double>(); } else { return get<double>(offset); @@ -330,8 +354,8 @@ template <> optional<double> Statement::get(int offset) { } template <> optional<std::string> Statement::get(int offset) { - assert(stmt); - if (sqlite3_column_type(stmt, offset) == SQLITE_NULL) { + assert(impl); + if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { return optional<std::string>(); } else { return get<std::string>(offset); @@ -341,8 +365,8 @@ template <> optional<std::string> Statement::get(int offset) { template <> optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> Statement::get(int offset) { - assert(stmt); - if (sqlite3_column_type(stmt, offset) == SQLITE_NULL) { + assert(impl); + if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { return {}; } else { return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>( @@ -351,13 +375,24 @@ Statement::get(int offset) { } void Statement::reset() { - assert(stmt); - sqlite3_reset(stmt); + assert(impl); + sqlite3_reset(impl->stmt); } void Statement::clearBindings() { - assert(stmt); - sqlite3_clear_bindings(stmt); + assert(impl); + sqlite3_clear_bindings(impl->stmt); +} + +int64_t Statement::lastInsertRowId() const { + assert(impl); + return impl->lastInsertRowId; +} + +uint64_t Statement::changes() const { + assert(impl); + auto changes = impl->changes; + return (changes < 0 ? 0 : changes); } Transaction::Transaction(Database& db_, Mode mode) diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 8e4a4b971e..2cbc3cf48b 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -4,9 +4,7 @@ #include <vector> #include <stdexcept> #include <chrono> - -typedef struct sqlite3 sqlite3; -typedef struct sqlite3_stmt sqlite3_stmt; +#include <memory> namespace mapbox { namespace sqlite { @@ -22,12 +20,20 @@ enum OpenFlag : int { }; struct Exception : std::runtime_error { + enum Code : int { + OK = 0, + CANTOPEN = 14, + NOTADB = 26 + }; + Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {} Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {} - const int code = 0; + const int code = OK; }; +class DatabaseImpl; class Statement; +class StatementImpl; class Database { private: @@ -42,16 +48,14 @@ public: explicit operator bool() const; - static void errorLogCallback(void *arg, const int err, const char *msg); void setBusyTimeout(std::chrono::milliseconds); void exec(const std::string &sql); Statement prepare(const char *query); - int64_t lastInsertRowid() const; - uint64_t changes() const; - private: - sqlite3 *db = nullptr; + std::unique_ptr<DatabaseImpl> impl; + + friend class Statement; }; class Statement { @@ -59,10 +63,8 @@ private: Statement(const Statement &) = delete; Statement &operator=(const Statement &) = delete; - void check(int err); - public: - Statement(sqlite3 *db, const char *sql); + Statement(Database *db, const char *sql); Statement(Statement &&); ~Statement(); Statement &operator=(Statement &&); @@ -85,8 +87,11 @@ public: void reset(); void clearBindings(); + int64_t lastInsertRowId() const; + uint64_t changes() const; + private: - sqlite3_stmt *stmt = nullptr; + std::unique_ptr<StatementImpl> impl; }; class Transaction { |