diff options
-rw-r--r-- | gyp/asset-zip.gypi | 1 | ||||
-rw-r--r-- | platform/default/asset_request_zip.cpp | 380 | ||||
-rw-r--r-- | platform/default/uv_zip.c | 228 | ||||
-rw-r--r-- | platform/default/uv_zip.h | 44 |
4 files changed, 217 insertions, 436 deletions
diff --git a/gyp/asset-zip.gypi b/gyp/asset-zip.gypi index e8854734e1..40459e516a 100644 --- a/gyp/asset-zip.gypi +++ b/gyp/asset-zip.gypi @@ -8,7 +8,6 @@ 'sources': [ '../platform/default/asset_request_zip.cpp', - '../platform/default/uv_zip.c', ], 'include_dirs': [ diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp index fb7a9c4a3e..b29166f673 100644 --- a/platform/default/asset_request_zip.cpp +++ b/platform/default/asset_request_zip.cpp @@ -1,115 +1,201 @@ #include <mbgl/storage/asset_context_base.hpp> -#include <mbgl/android/jni.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> -#include <mbgl/platform/log.hpp> #include <mbgl/util/chrono.hpp> #include <mbgl/util/util.hpp> -#include <mbgl/util/uv.hpp> -#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> -#include <uv.h> -#include "uv_zip.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include <zip.h> +#pragma GCC diagnostic pop -#include <map> #include <cassert> #include <forward_list> +#include <map> -namespace mbgl { +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> -class AssetZipContext : public AssetContextBase { -public: - explicit AssetZipContext(); - ~AssetZipContext(); +namespace { - RequestBase* createRequest(const Resource& resource, - RequestBase::Callback callback, - const std::string& assetRoot) final; +struct ZipHolder { + ZipHolder(struct zip* archive_) : archive(archive_) {} - uv_zip_t *getHandle(const std::string &path); - void returnHandle(const std::string &path, uv_zip_t *zip); + ~ZipHolder() { + if (archive) ::zip_discard(archive); + } - // A list of resuable uv_zip handles to avoid creating and destroying them all the time. - std::map<std::string, std::forward_list<uv_zip_t *>> handles; - uv_loop_t *loop; + struct zip* archive; }; -AssetZipContext::AssetZipContext() : loop(static_cast<uv_loop_t*>(util::RunLoop::getLoopHandle())) { -} +struct ZipFileHolder { + ZipFileHolder(struct zip_file* file_) : file(file_) {} -uv_zip_t *AssetZipContext::getHandle(const std::string &path) { - auto &list = handles[path]; - if (!list.empty()) { - auto zip = list.front(); - list.pop_front(); - return zip; - } else { - return nullptr; + ~ZipFileHolder() { + if (file) ::zip_fclose(file); } -} -void AssetZipContext::returnHandle(const std::string &path, uv_zip_t *zip) { - handles[path].push_front(zip); -} + struct zip_file* file; +}; -AssetZipContext::~AssetZipContext() { - // Close all zip handles - for (auto &list : handles) { - for (auto zip : list.second) { - uv_zip_discard(loop, zip, [](uv_zip_t *zip_) { - uv_zip_cleanup(zip_); - delete zip_; - }); +class ZipIOWorker { +public: + ZipIOWorker() = default; + + void zip_fdopen(const std::string& path, + std::function<void(std::unique_ptr<ZipHolder>)> cb) { + int fd = ::open(path.c_str(), O_RDONLY, S_IRUSR); + if (fd < 0) { + cb(std::make_unique<ZipHolder>(nullptr)); + } else { + int errorp; + struct zip* archive = ::zip_fdopen(fd, 0, &errorp); + cb(std::make_unique<ZipHolder>(archive)); } } - handles.clear(); + + void zip_stat(struct zip* archive, const std::string& path, + std::function<void (int, std::unique_ptr<struct zip_stat>)> cb) { + std::unique_ptr<struct zip_stat> buf = std::make_unique<struct zip_stat>(); + ::zip_stat_init(buf.get()); + + int ret = ::zip_stat(archive, path.c_str(), 0, buf.get()); + cb(ret, std::move(buf)); + } + + void zip_fopen(struct zip* archive, const std::string& path, + std::function<void (std::unique_ptr<ZipFileHolder>)> cb) { + struct zip_file* file = ::zip_fopen(archive, path.c_str(), 0); + cb(std::make_unique<ZipFileHolder>(file)); + } + + void zip_fread(struct zip_file* file, size_t size, + std::function<void (int, std::shared_ptr<std::string>)> cb) { + std::shared_ptr<std::string> buf = std::make_shared<std::string>(); + buf->resize(size); + + int ret = ::zip_fread(file, &buf->front(), size); + cb(ret, std::move(buf)); + } + + void zip_fclose(struct zip_file* file, std::function<void (int)> cb) { + cb(::zip_fclose(file)); + } +}; + } +namespace mbgl { + +using namespace std::placeholders; + +class AssetZipContext; + class AssetRequest : public RequestBase { MBGL_STORE_THREAD(tid) public: - AssetRequest(AssetZipContext&, const Resource&, Callback, const std::string& assetRoot); + AssetRequest(const Resource&, Callback, const std::string& assetRoot, + AssetZipContext* context, util::Thread<ZipIOWorker>* worker); ~AssetRequest(); + // RequestBase implementation. void cancel() final; private: - AssetZipContext &context; - bool cancelled = false; + void openArchive(); + void archiveOpened(std::unique_ptr<ZipHolder>); + void fileStated(int result, std::unique_ptr<struct zip_stat>); + void fileOpened(std::unique_ptr<ZipFileHolder>); + void fileRead(int result, std::shared_ptr<std::string>); + void fileClosed(); + + void close(); + void cleanup(); + + void notifyError(Response::Error::Reason, const char *message); + const std::string root; 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, Response::Error::Reason); + struct zip* archive = nullptr; + struct zip_file* file = nullptr; + size_t size = 0; + + AssetZipContext *context; + + util::Thread<ZipIOWorker> *worker; + std::unique_ptr<WorkRequest> request; +}; + +class AssetZipContext : public AssetContextBase { +public: + AssetZipContext(); + ~AssetZipContext(); + + RequestBase* createRequest(const Resource& resource, + RequestBase::Callback callback, + const std::string& assetRoot) final { + return new AssetRequest(resource, callback, assetRoot, this, &worker); + } + + struct zip *getHandle(const std::string &path); + void returnHandle(const std::string &path, struct zip *zip); + + // A list of resuable zip handles to avoid creating + // and destroying them all the time. + std::map<std::string, std::forward_list<struct zip*>> handles; + + util::Thread<ZipIOWorker> worker; + std::unique_ptr<WorkRequest> request; }; -RequestBase* AssetZipContext::createRequest(const Resource& resource, - RequestBase::Callback callback, - const std::string& assetRoot) { - return new AssetRequest(*this, resource, callback, assetRoot); +AssetZipContext::AssetZipContext() + : worker({"ZipIOWorker", util::ThreadType::Worker, util::ThreadPriority::Regular}) {} + +AssetZipContext::~AssetZipContext() { + // Close all zip handles + for (auto &list : handles) { + for (auto archive : list.second) { + zip_discard(archive); + } + } + + handles.clear(); } -AssetRequest::AssetRequest(AssetZipContext& context_, const Resource& resource_, Callback callback_, const std::string& assetRoot_) +struct zip* AssetZipContext::getHandle(const std::string &path) { + auto &list = handles[path]; + if (!list.empty()) { + auto archive = list.front(); + list.pop_front(); + return archive; + } else { + return nullptr; + } +} + +void AssetZipContext::returnHandle(const std::string &path, struct zip* archive) { + handles[path].push_front(archive); +} + +AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, + const std::string& assetRoot, AssetZipContext* context_, util::Thread<ZipIOWorker>* worker_) : RequestBase(resource_, callback_), + root(assetRoot), + path(std::string("assets/") + resource.url.substr(8)), context(context_), - root(assetRoot_), - path(std::string { "assets/" } + resource.url.substr(8)) { - auto zip = context.getHandle(root); - if (zip) { - archiveOpened(zip); + worker(worker_) { + archive = context->getHandle(root); + if (archive) { + request = worker->invokeWithCallback(&ZipIOWorker::zip_stat, + std::bind(&AssetRequest::fileStated, this, _1, _2), archive, path); } else { - openZipArchive(); + request = worker->invokeWithCallback(&ZipIOWorker::zip_fdopen, + std::bind(&AssetRequest::archiveOpened, this, _1), root); } } @@ -117,146 +203,114 @@ AssetRequest::~AssetRequest() { MBGL_VERIFY_THREAD(tid); } -void AssetRequest::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, root.c_str(), O_RDONLY, S_IRUSR, [](uv_fs_t *fsReq) { - if (fsReq->result < 0) { - auto impl = reinterpret_cast<AssetRequest *>(fsReq->data); - impl->notifyError(uv::getFileRequestError(fsReq), Response::Error::Reason::Connection); - 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<AssetRequest *>(openZip->data); - if (openZip->result < 0) { - impl->notifyError(openZip->message, Response::Error::Reason::Other); - delete openZip; - delete impl; - } else { - impl->archiveOpened(openZip); - } - }); - } +void AssetRequest::cancel() { + MBGL_VERIFY_THREAD(tid); - uv_fs_req_cleanup(fsReq); - delete fsReq; - fsReq = nullptr; - }); + request.reset(); + close(); } -#define INVOKE_MEMBER(name) \ - [](uv_zip_t *zip_) { \ - assert(zip_->data); \ - reinterpret_cast<AssetRequest *>(zip_->data)->name(zip_); \ - } - -void AssetRequest::archiveOpened(uv_zip_t *zip) { +void AssetRequest::archiveOpened(std::unique_ptr<ZipHolder> holder) { MBGL_VERIFY_THREAD(tid); - zip->data = this; - uv_zip_stat(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileStated)); + std::swap(archive, holder->archive); + + if (!archive) { + notifyError(Response::Error::Reason::Other, "Could not open zip archive"); + cleanup(); + } else { + request = worker->invokeWithCallback(&ZipIOWorker::zip_stat, + std::bind(&AssetRequest::fileStated, this, _1, _2), archive, path); + } } -void AssetRequest::fileStated(uv_zip_t *zip) { +void AssetRequest::fileStated(int result, std::unique_ptr<struct zip_stat> stat) { MBGL_VERIFY_THREAD(tid); - if (cancelled || zip->result < 0) { - // Stat failed, probably because the file doesn't exist. - if (zip->result < 0) { - notifyError(zip->message, Response::Error::Reason::NotFound); - } - 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", Response::Error::Reason::Other); - cleanup(zip); + if (result < 0 || !(stat->valid & ZIP_STAT_SIZE)) { + notifyError(Response::Error::Reason::NotFound, "Could not stat file in zip archive"); + cleanup(); } else { response = std::make_unique<Response>(); - // Allocate the space for reading the data. - auto data = std::make_shared<std::string>(); - data->resize(zip->stat->size); - buffer = uv_buf_init(const_cast<char *>(data->data()), zip->stat->size); - response->data = data; - - // Get the modification time in case we have one. - if (zip->stat->valid & ZIP_STAT_MTIME) { - response->modified = Seconds(zip->stat->mtime); + if (stat->valid & ZIP_STAT_MTIME) { + response->modified = Seconds(stat->mtime); } - if (zip->stat->valid & ZIP_STAT_INDEX) { - response->etag = std::to_string(zip->stat->index); + if (stat->valid & ZIP_STAT_INDEX) { + response->etag = std::to_string(stat->index); } - uv_zip_fopen(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileOpened)); + size = stat->size; + + request = worker->invokeWithCallback(&ZipIOWorker::zip_fopen, + std::bind(&AssetRequest::fileOpened, this, _1), archive, path); } } -void AssetRequest::fileOpened(uv_zip_t *zip) { +void AssetRequest::fileOpened(std::unique_ptr<ZipFileHolder> holder) { MBGL_VERIFY_THREAD(tid); - if (zip->result < 0) { - // Opening failed. - notifyError(zip->message, Response::Error::Reason::Other); - cleanup(zip); - } else if (cancelled) { - // The request was canceled. Close the file again. - uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); + std::swap(file, holder->file); + + if (!file) { + notifyError(Response::Error::Reason::NotFound, "Could not open file in zip archive"), + cleanup(); } else { - uv_zip_fread(context.loop, zip, zip->file, &buffer, INVOKE_MEMBER(fileRead)); + request = worker->invokeWithCallback(&ZipIOWorker::zip_fread, + std::bind(&AssetRequest::fileRead, this, _1, _2), file, size); } } -void AssetRequest::fileRead(uv_zip_t *zip) { +void AssetRequest::fileRead(int result, std::shared_ptr<std::string> data) { MBGL_VERIFY_THREAD(tid); - if (zip->result < 0) { - // Reading failed. We still have an open file handle though. - notifyError(zip->message, Response::Error::Reason::Other); - } else if (!cancelled) { + if (result < 0) { + notifyError(Response::Error::Reason::Other, "Could not read file in zip archive"); + } else { + response->data = data; notify(std::move(response)); } - uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); + close(); } -void AssetRequest::fileClosed(uv_zip_t *zip) { +void AssetRequest::fileClosed() { MBGL_VERIFY_THREAD(tid); - if (zip->result < 0) { - // Closing the file failed. But there isn't anything we can do. - } - - cleanup(zip); + cleanup(); } -void AssetRequest::cleanup(uv_zip_t *zip) { + +void AssetRequest::close() { MBGL_VERIFY_THREAD(tid); - context.returnHandle(root, zip); - delete this; + if (file) { + request = worker->invokeWithCallback(&ZipIOWorker::zip_fclose, + std::bind(&AssetRequest::fileClosed, this), file); + file = nullptr; + } else { + cleanup(); + } } -void AssetRequest::notifyError(const char *message, Response::Error::Reason reason) { +void AssetRequest::cleanup() { MBGL_VERIFY_THREAD(tid); - if (!cancelled) { - response = std::make_unique<Response>(); - response->error = std::make_unique<Response::Error>(reason, message); - notify(std::move(response)); - } else { - // The request was already canceled and deleted. + if (archive) { + context->returnHandle(root, archive); } + + delete this; } -void AssetRequest::cancel() { - cancelled = true; +void AssetRequest::notifyError(Response::Error::Reason reason, const char *message) { + MBGL_VERIFY_THREAD(tid); + + response = std::make_unique<Response>(); + response->error = std::make_unique<Response::Error>(reason, message); + + notify(std::move(response)); } std::unique_ptr<AssetContextBase> AssetContextBase::createContext() { diff --git a/platform/default/uv_zip.c b/platform/default/uv_zip.c deleted file mode 100644 index 3019477eb8..0000000000 --- a/platform/default/uv_zip.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "uv_zip.h" - -#include <assert.h> -#include <errno.h> -#include <string.h> - - -void uv__zip_open_error(uv_zip_t *zip, int error) { - assert(zip); - zip->result = -error; - if (zip->message) { - free((char *)zip->message); - zip->message = NULL; - } - const zip_uint64_t size = zip_error_to_str(NULL, 0, error, errno) + 1; - zip->message = malloc(size); - zip_error_to_str((char *)zip->message, size, error, errno); -} - -void uv__zip_store_error(uv_zip_t *zip, const char *message) { - assert(zip); - if (zip->message) { - free((char *)zip->message); - zip->message = NULL; - } - const unsigned long length = strlen(message) + 1; - zip->message = malloc(length); - strncpy((char *)zip->message, message, length); -} - -void uv__zip_error(uv_zip_t *zip) { - assert(zip); - int error; - zip_error_get(zip->archive, &error, NULL); - zip->result = -error; - uv__zip_store_error(zip, zip_strerror(zip->archive)); -} - -void uv__zip_file_error(uv_zip_t *zip) { - assert(zip); - int error; - zip_file_error_get(zip->file, &error, NULL); - zip->result = -error; - uv__zip_store_error(zip, zip_file_strerror(zip->file)); -} - -void uv__zip_work_fdopen(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(!zip->archive); - - // extract the fd - uv_file fd = *(uv_file *)zip->path; - free((uv_file *)zip->path); - zip->path = NULL; - - int error; - zip->archive = zip_fdopen(fd, zip->flags, &error); - if (!zip->archive) { - uv__zip_open_error(zip, error); - } else { - zip->result = 0; - } -} - -void uv__zip_work_stat(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->archive); - if (!zip->stat) { - zip->stat = malloc(sizeof(struct zip_stat)); - zip_stat_init(zip->stat); - } - if (0 != zip_stat(zip->archive, zip->path, zip->flags, zip->stat)) { - uv__zip_error(zip); - } -} - -void uv__zip_work_fopen(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->archive); - zip->file = zip_fopen(zip->archive, zip->path, zip->flags); - if (!zip->file) { - uv__zip_error(zip); - } -} - -void uv__zip_work_fread(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->file); - assert(zip->buf); - zip->result = zip_fread(zip->file, zip->buf->base, zip->buf->len); - if (zip->result < 0) { - uv__zip_file_error(zip); - } -} - -void uv__zip_work_fclose(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - assert(zip->file); - zip->result = zip_fclose(zip->file); - if (zip->result != 0) { - uv__zip_file_error(zip); - } else { - zip->file = NULL; - } -} - -void uv__zip_work_discard(uv_work_t *req) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip->archive); - zip_discard(zip->archive); - zip->archive = NULL; - zip->result = 0; -} - -void uv__zip_after_work(uv_work_t *req, int status) { - uv_zip_t *zip = (uv_zip_t *)req->data; - assert(zip); - if (zip->cb) { - zip->cb(zip); - } -} - -void uv_zip_init(uv_zip_t *zip) { - assert(zip); - zip->work.data = zip; - zip->message = NULL; - zip->stat = NULL; - uv_zip_cleanup(zip); -} - -void uv_zip_cleanup(uv_zip_t *zip) { - assert(zip); - zip->data = NULL; - zip->flags = 0; - zip->result = 0; - zip->path = NULL; - zip->cb = NULL; - zip->archive = NULL; - zip->file = NULL; - zip->buf = NULL; - - if (zip->message) { - free((char *)zip->message); - zip->message = NULL; - } - - if (zip->stat) { - free(zip->stat); - zip->stat = NULL; - } -} - -int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(fd); - zip->result = 0; - zip->path = malloc(sizeof(uv_file)); - *(uv_file *)zip->path = fd; - zip->flags = flags; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fdopen, uv__zip_after_work); -} - -int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(fname); - assert(strlen(fname)); - assert(zip->archive); - zip->result = 0; - zip->path = fname; - zip->flags = flags; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_stat, uv__zip_after_work); -} - -int uv_zip_fopen(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(fname); - assert(strlen(fname)); - assert(zip->archive); - zip->result = 0; - zip->path = fname; - zip->flags = flags; - zip->file = NULL; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fopen, uv__zip_after_work); -} - -int uv_zip_fclose(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(file); - assert(zip->archive); - zip->result = 0; - zip->file = file; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fclose, uv__zip_after_work); -} - -int uv_zip_fread(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_buf_t *buf, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(file); - assert(buf); - assert(zip->archive); - zip->result = 0; - zip->file = file; - zip->buf = buf; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_fread, uv__zip_after_work); -} - -int uv_zip_discard(uv_loop_t* loop, uv_zip_t *zip, uv_zip_cb cb) { - assert(loop); - assert(zip); - assert(!zip->file); - zip->result = 0; - zip->cb = cb; - return uv_queue_work(loop, &zip->work, uv__zip_work_discard, uv__zip_after_work); -} diff --git a/platform/default/uv_zip.h b/platform/default/uv_zip.h deleted file mode 100644 index 869a415f21..0000000000 --- a/platform/default/uv_zip.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef UV_ZIP -#define UV_ZIP - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdlib.h> -#include <uv.h> -#include <zip.h> - -typedef struct uv_zip_s uv_zip_t; - -typedef void (*uv_zip_cb)(uv_zip_t* req); - -struct uv_zip_s { - uv_work_t work; - ssize_t result; - const char *message; - struct zip *archive; - struct zip_file *file; - struct zip_stat *stat; - void *data; - zip_flags_t flags; - const char *path; - uv_zip_cb cb; - uv_buf_t *buf; -}; - -void uv_zip_init(uv_zip_t *zip); -void uv_zip_cleanup(uv_zip_t *zip); - -int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_cb cb); -int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb); -int uv_zip_fopen(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb); -int uv_zip_fread(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_buf_t *buf, uv_zip_cb cb); -int uv_zip_fclose(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_zip_cb cb); -int uv_zip_discard(uv_loop_t* loop, uv_zip_t *zip, uv_zip_cb cb); - -#ifdef __cplusplus -} -#endif - -#endif // UV_ZIP |