From 29baacf3a5bb773d94d08d16b81c3cda45a44eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 29 Jan 2015 18:30:46 +0100 Subject: refactor makefile --- platform/default/asset_request_zip.cpp | 295 +++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 platform/default/asset_request_zip.cpp (limited to 'platform/default/asset_request_zip.cpp') 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 +#include +#include +#include +#include +#include + +#include "uv_zip.h" + +#include + +#include + +namespace algo = boost::algorithm; + +namespace mbgl { + +class AssetZipContext : public ThreadContext { +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 handles; +}; + +// ------------------------------------------------------------------------------------------------- + +template<> pthread_key_t ThreadContext::key{}; +template<> pthread_once_t ThreadContext::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; + 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(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(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(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(); + + // Allocate the space for reading the data. + response->data.resize(zip->stat->size); + buffer = uv_buf_init(const_cast(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->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(ptr)->cancel(); + } +} + +void AssetRequest::start(uv_loop_t *loop, std::unique_ptr 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(ptr)->cancel(); + } + + delete this; +} + +} -- cgit v1.2.1