summaryrefslogtreecommitdiff
path: root/platform/default/asset_request_zip.cpp
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2015-01-29 18:30:46 +0100
committerKonstantin Käfer <mail@kkaefer.com>2015-02-04 10:49:09 +0100
commit29baacf3a5bb773d94d08d16b81c3cda45a44eb6 (patch)
tree1dc3ca456151138ee5e8b7cf88b3afcecc3df1db /platform/default/asset_request_zip.cpp
parent3d51e116a84ee168975bcee8377e9156f77e2731 (diff)
downloadqtlocation-mapboxgl-29baacf3a5bb773d94d08d16b81c3cda45a44eb6.tar.gz
refactor makefile
Diffstat (limited to 'platform/default/asset_request_zip.cpp')
-rw-r--r--platform/default/asset_request_zip.cpp295
1 files changed, 295 insertions, 0 deletions
diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp
new file mode 100644
index 0000000000..9c1093c5ed
--- /dev/null
+++ b/platform/default/asset_request_zip.cpp
@@ -0,0 +1,295 @@
+#include <mbgl/storage/default/asset_request.hpp>
+#include <mbgl/storage/default/thread_context.hpp>
+#include <mbgl/android/jni.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/platform/platform.hpp>
+
+#include "uv_zip.h"
+
+#include <boost/algorithm/string.hpp>
+
+#include <forward_list>
+
+namespace algo = boost::algorithm;
+
+namespace mbgl {
+
+class AssetZipContext : public ThreadContext<AssetZipContext> {
+public:
+ AssetZipContext(uv_loop_t *loop);
+ ~AssetZipContext();
+
+ uv_zip_t *getHandle();
+ void returnHandle(uv_zip_t *zip);
+
+private:
+ // A list of resuable uv_zip handles to avoid creating and destroying them all the time.
+ std::forward_list<uv_zip_t *> handles;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+template<> pthread_key_t ThreadContext<AssetZipContext>::key{};
+template<> pthread_once_t ThreadContext<AssetZipContext>::once = PTHREAD_ONCE_INIT;
+
+AssetZipContext::AssetZipContext(uv_loop_t *loop_) : ThreadContext(loop_) {
+}
+
+uv_zip_t *AssetZipContext::getHandle() {
+ if (!handles.empty()) {
+ auto zip = handles.front();
+ handles.pop_front();
+ return zip;
+ } else {
+ return nullptr;
+ }
+}
+
+void AssetZipContext::returnHandle(uv_zip_t *zip) {
+ uv_zip_cleanup(zip);
+ handles.push_front(zip);
+}
+
+AssetZipContext::~AssetZipContext() {
+ // Close all zip handles
+ for (auto zip : handles) {
+ uv_zip_discard(loop, zip, [](uv_zip_t *zip_) {
+ uv_zip_cleanup(zip_);
+ delete zip_;
+ });
+ }
+ handles.clear();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class AssetRequestImpl {
+ MBGL_STORE_THREAD(tid)
+
+public:
+ AssetRequestImpl(AssetRequest *request, uv_loop_t *loop);
+ ~AssetRequestImpl();
+
+ void cancel();
+
+private:
+ AssetZipContext &context;
+ AssetRequest *request = nullptr;
+ const std::string path;
+ std::unique_ptr<Response> response;
+ uv_buf_t buffer;
+
+private:
+ void openZipArchive();
+ void archiveOpened(uv_zip_t *zip);
+ void fileStated(uv_zip_t *zip);
+ void fileOpened(uv_zip_t *zip);
+ void fileRead(uv_zip_t *zip);
+ void fileClosed(uv_zip_t *zip);
+ void cleanup(uv_zip_t *zip);
+
+ void notifyError(const char *message);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+AssetRequestImpl::~AssetRequestImpl() {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (request) {
+ request->ptr = nullptr;
+ }
+}
+
+AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop)
+ : context(*AssetZipContext::Get(loop)),
+ request(request_),
+ path(request->resource.url.substr(8)) {
+ auto zip = context.getHandle();
+ if (zip) {
+ archiveOpened(zip);
+ } else {
+ openZipArchive();
+ }
+}
+
+void AssetRequestImpl::openZipArchive() {
+ uv_fs_t *req = new uv_fs_t();
+ req->data = this;
+
+ // We're using uv_fs_open first to obtain a file descriptor. Then, uv_zip_fdopen will operate
+ // on a read-only file.
+ uv_fs_open(context.loop, req, platform::assetRoot().c_str(), O_RDONLY, S_IRUSR, [](uv_fs_t *fsReq) {
+ if (fsReq->result < 0) {
+ auto impl = reinterpret_cast<AssetRequestImpl *>(fsReq->data);
+ impl->notifyError(uv::getFileRequestError(fsReq));
+ delete impl;
+ } else {
+ uv_zip_t *zip = new uv_zip_t();
+ uv_zip_init(zip);
+ zip->data = fsReq->data;
+ uv_zip_fdopen(fsReq->loop, zip, uv_file(fsReq->result), 0, [](uv_zip_t *openZip) {
+ auto impl = reinterpret_cast<AssetRequestImpl *>(openZip->data);
+ if (openZip->result < 0) {
+ impl->notifyError(openZip->message);
+ delete openZip;
+ delete impl;
+ } else {
+ impl->archiveOpened(openZip);
+ }
+ });
+ }
+
+ uv_fs_req_cleanup(fsReq);
+ delete fsReq;
+ fsReq = nullptr;
+ });
+}
+
+#define INVOKE_MEMBER(name) \
+ [](uv_zip_t *zip_) { \
+ assert(zip_->data); \
+ reinterpret_cast<AssetRequestImpl *>(zip_->data)->name(zip_); \
+ }
+
+void AssetRequestImpl::archiveOpened(uv_zip_t *zip) {
+ MBGL_VERIFY_THREAD(tid);
+
+ zip->data = this;
+ uv_zip_stat(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileStated));
+}
+
+void AssetRequestImpl::fileStated(uv_zip_t *zip) {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (!request || zip->result < 0) {
+ // Stat failed, probably because the file doesn't exist.
+ if (zip->result < 0) {
+ notifyError(zip->message);
+ }
+ cleanup(zip);
+ } else if (!(zip->stat->valid & ZIP_STAT_SIZE)) {
+ // We couldn't obtain the size of the file.
+ notifyError("Could not determine file size in zip file");
+ cleanup(zip);
+ } else {
+ response = util::make_unique<Response>();
+
+ // Allocate the space for reading the data.
+ response->data.resize(zip->stat->size);
+ buffer = uv_buf_init(const_cast<char *>(response->data.data()), zip->stat->size);
+
+ // Get the modification time in case we have one.
+ if (zip->stat->valid & ZIP_STAT_MTIME) {
+ response->modified = zip->stat->mtime;
+ }
+
+ uv_zip_fopen(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileOpened));
+ }
+}
+
+void AssetRequestImpl::fileOpened(uv_zip_t *zip) {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (zip->result < 0) {
+ // Opening failed.
+ notifyError(zip->message);
+ cleanup(zip);
+ } else if (!request) {
+ // The request was canceled. Close the file again.
+ uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed));
+ } else {
+ uv_zip_fread(context.loop, zip, zip->file, &buffer, INVOKE_MEMBER(fileRead));
+ }
+}
+
+void AssetRequestImpl::fileRead(uv_zip_t *zip) {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (zip->result < 0) {
+ // Reading failed. We still have an open file handle though.
+ notifyError(zip->message);
+ } else if (request) {
+ response->status = Response::Successful;
+ request->notify(std::move(response), FileCache::Hint::No);
+ delete request;
+ assert(request == nullptr);
+ }
+
+ uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed));
+}
+
+void AssetRequestImpl::fileClosed(uv_zip_t *zip) {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (zip->result < 0) {
+ // Closing the file failed. But there isn't anything we can do.
+ }
+
+ cleanup(zip);
+}
+
+void AssetRequestImpl::cleanup(uv_zip_t *zip) {
+ MBGL_VERIFY_THREAD(tid);
+
+ context.returnHandle(zip);
+ delete this;
+}
+
+void AssetRequestImpl::notifyError(const char *message) {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (request) {
+ response = util::make_unique<Response>();
+ response->status = Response::Error;
+ response->message = message;
+ request->notify(std::move(response), FileCache::Hint::No);
+ delete request;
+ assert(request == nullptr);
+ } else {
+ // The request was already canceled and deleted.
+ }
+}
+
+void AssetRequestImpl::cancel() {
+ request = nullptr;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+AssetRequest::AssetRequest(DefaultFileSource *source_, const Resource &resource_)
+ : SharedRequestBase(source_, resource_) {
+ assert(algo::starts_with(resource.url, "asset://"));
+}
+
+AssetRequest::~AssetRequest() {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (ptr) {
+ reinterpret_cast<AssetRequestImpl *>(ptr)->cancel();
+ }
+}
+
+void AssetRequest::start(uv_loop_t *loop, std::unique_ptr<Response> response) {
+ MBGL_VERIFY_THREAD(tid);
+
+ // We're ignoring the existing response if any.
+ (void(response));
+
+ assert(!ptr);
+ ptr = new AssetRequestImpl(this, loop);
+ // Note: the AssetRequestImpl deletes itself.
+}
+
+void AssetRequest::cancel() {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (ptr) {
+ reinterpret_cast<AssetRequestImpl *>(ptr)->cancel();
+ }
+
+ delete this;
+}
+
+}