summaryrefslogtreecommitdiff
path: root/platform/default
diff options
context:
space:
mode:
Diffstat (limited to 'platform/default')
-rw-r--r--platform/default/bidi.cpp17
-rw-r--r--platform/default/default_file_source.cpp41
-rw-r--r--platform/default/headless_backend_osmesa.cpp3
-rw-r--r--platform/default/image.cpp70
-rw-r--r--platform/default/jpeg_reader.cpp4
-rw-r--r--platform/default/local_file_source.cpp6
-rw-r--r--platform/default/mbgl/gl/headless_backend.cpp29
-rw-r--r--platform/default/mbgl/gl/headless_backend.hpp11
-rw-r--r--platform/default/mbgl/gl/offscreen_view.cpp60
-rw-r--r--platform/default/mbgl/gl/offscreen_view.hpp13
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp23
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp2
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp4
-rw-r--r--platform/default/mbgl/storage/offline_download.hpp2
-rw-r--r--platform/default/mbgl/util/default_thread_pool.cpp6
-rw-r--r--platform/default/mbgl/util/shared_thread_pool.cpp14
-rw-r--r--platform/default/mbgl/util/shared_thread_pool.hpp9
-rw-r--r--platform/default/online_file_source.cpp30
-rw-r--r--platform/default/png_reader.cpp18
-rw-r--r--platform/default/png_writer.cpp80
-rw-r--r--platform/default/sqlite3.cpp283
-rw-r--r--platform/default/sqlite3.hpp31
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 {