diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ab761bf..332b04cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ endif() # Project configuration # -project(uvw VERSION 1.15.0) +project(uvw VERSION 1.16.0) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) diff --git a/cmake/in/deps.in b/cmake/in/deps.in index 0532a1d1..4cbe4c33 100644 --- a/cmake/in/deps.in +++ b/cmake/in/deps.in @@ -17,7 +17,7 @@ ExternalProject_Add( ExternalProject_Add( libuv GIT_REPOSITORY https://github.com/libuv/libuv.git - GIT_TAG v1.27.0 + GIT_TAG v1.28.0 SOURCE_DIR @LIBUV_DEPS_DIR@ CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/conanfile.py b/conanfile.py index 58cb64cc..5fe67d5f 100644 --- a/conanfile.py +++ b/conanfile.py @@ -14,7 +14,7 @@ class UVMConan(ConanFile): exports = "LICENSE" exports_sources = "src/*" no_copy_source = True - requires = "libuv/1.27.0@bincrafters/stable" + requires = "libuv/1.28.0@bincrafters/stable" def package(self): self.copy(pattern="LICENSE", dst="licenses") diff --git a/src/uvw/fs.hpp b/src/uvw/fs.hpp index 53408be0..8088fb23 100644 --- a/src/uvw/fs.hpp +++ b/src/uvw/fs.hpp @@ -49,7 +49,10 @@ enum class UVFsType: std::underlying_type_t { FCHOWN = UV_FS_FCHOWN, REALPATH = UV_FS_REALPATH, COPYFILE = UV_FS_COPYFILE, - LCHOWN = UV_FS_LCHOWN + LCHOWN = UV_FS_LCHOWN, + OPENDIR = UV_FS_OPENDIR, + READDIR = UV_FS_READDIR, + CLOSEDIR = UV_FS_CLOSEDIR }; @@ -143,6 +146,9 @@ enum class UVSymLinkFlags: int { * * `FsRequest::Type::REALPATH` * * `FsRequest::Type::COPYFILE` * * `FsRequest::Type::LCHOWN` + * * `FsRequest::Type::OPENDIR` + * * `FsRequest::Type::READDIR` + * * `FsRequest::Type::CLOSEDIR` * * It will be emitted by FsReq and/or FileReq according with their * functionalities. @@ -297,6 +303,26 @@ struct FsEvent { }; +/** + * @brief FsEvent event specialization for `FsRequest::Type::READDIR`. + * + * It will be emitted by FsReq and/or FileReq according with their + * functionalities. + */ +template<> +struct FsEvent { + using EntryType = details::UVDirentTypeT; + + FsEvent(const char *name, EntryType type, bool eos) noexcept + : name{name}, type{type}, eos{eos} + {} + + const char * name; /*!< The name of the last entry. */ + EntryType type; /*!< The entry type. */ + bool eos; /*!< True if there a no more entries to read. */ +}; + + /** * @brief Base class for FsReq and/or FileReq. * @@ -342,7 +368,6 @@ public: using Time = std::chrono::duration; using Type = details::UVFsType; using EntryType = details::UVDirentTypeT; - using Entry = std::pair; using Request::Request; }; @@ -833,6 +858,17 @@ class FsReq final: public FsRequest { else { ptr->publish(FsEvent{req->path, static_cast(req->ptr), static_cast(req->result)}); } } + static void fsReaddirCallback(uv_fs_t *req) { + auto ptr = reserve(req); + + if(req->result < 0) { + ptr->publish(ErrorEvent{req->result}); + } else { + auto *dir = static_cast(req->ptr); + ptr->publish(FsEvent{dir->dirents[0].name, static_cast(dir->dirents[0].type), !req->result}); + } + } + public: using CopyFile = details::UVCopyFileFlags; using SymLink = details::UVSymLinkFlags; @@ -974,10 +1010,10 @@ public: /** * @brief Gets entries populated with the next directory entry data. * - * Returns instances of Entry, that is an alias for a pair where: + * Returns a composed value where: * * * The first parameter indicates the entry type (see below). - * * The second parameter is a `std::string` that contains the actual value. + * * The second parameter is a string that contains the actual value. * * Available entry types are: * @@ -998,16 +1034,18 @@ public: * * * The first parameter is a boolean value that indicates if the current * entry is still valid. - * * The second parameter is an instance of `Entry` (see above). + * * The second parameter is a composed value (see above). */ - std::pair scandirNext() { - uv_dirent_t dirent; - std::pair ret{false, { EntryType::UNKNOWN, "" }}; - auto res = uv_fs_scandir_next(get(), &dirent); + std::pair> scandirNext() { + std::pair> ret{false, { EntryType::UNKNOWN, nullptr }}; + + // we cannot use cleanupAndInvokeSync because of the return value of uv_fs_scandir_next + uv_fs_req_cleanup(get()); + auto res = uv_fs_scandir_next(get(), dirents); if(UV_EOF != res) { - ret.second.first = static_cast(dirent.type); - ret.second.second = dirent.name; + ret.second.first = static_cast(dirents[0].type); + ret.second.second = dirents[0].name; ret.first = true; } @@ -1410,6 +1448,131 @@ public: cleanupAndInvokeSync(&uv_fs_lchown, parent(), req, path.data(), uid, gid); return !(req->result < 0); } + + /** + * @brief Opens a path asynchronously as a directory stream. + * + * Emit a `FsEvent` event when completed.
+ * Emit an ErrorEvent event in case of errors. + * + * The contents of the directory can be iterated over by means of the + * `readdir` od `readdirSync` member functions. The memory allocated by this + * function must be freed by calling `closedir` or `closedirSync`. + * + * @param path The path to open as a directory stream. + */ + void opendir(std::string path) { + cleanupAndInvoke(&uv_fs_opendir, parent(), get(), path.data(), &fsGenericCallback); + } + + /** + * @brief Opens a path synchronously as a directory stream. + * + * The contents of the directory can be iterated over by means of the + * `readdir` od `readdirSync` member functions. The memory allocated by this + * function must be freed by calling `closedir` or `closedirSync`. + * + * @param path The path to open as a directory stream. + * @return True in case of success, false otherwise. + */ + bool opendirSync(std::string path) { + auto req = get(); + cleanupAndInvokeSync(&uv_fs_opendir, parent(), req, path.data()); + return !(req->result < 0); + } + + /** + * @brief Closes asynchronously a directory stream. + * + * Emit a `FsEvent` event when completed.
+ * Emit an ErrorEvent event in case of errors. + * + * It frees also the memory allocated internally when a path has been opened + * as a directory stream. + */ + void closedir() { + auto req = get(); + auto *dir = static_cast(req->ptr); + cleanupAndInvoke(&uv_fs_closedir, parent(), req, dir, &fsGenericCallback); + } + + /** + * @brief Closes synchronously a directory stream. + * + * It frees also the memory allocated internally when a path has been opened + * as a directory stream. + * + * @return True in case of success, false otherwise. + */ + bool closedirSync() { + auto req = get(); + auto *dir = static_cast(req->ptr); + cleanupAndInvokeSync(&uv_fs_closedir, parent(), req, dir); + return !(req->result < 0); + } + + /** + * @brief Iterates asynchronously over a directory stream one entry at a + * time. + * + * Emit a `FsEvent` event when completed.
+ * Emit an ErrorEvent event in case of errors. + * + * This function isn't thread safe. Moreover, it doesn't return the `.` and + * `..` entries. + */ + void readdir() { + auto req = get(); + auto *dir = static_cast(req->ptr); + dir->dirents = dirents; + dir->nentries = 1; + cleanupAndInvoke(&uv_fs_readdir, parent(), req, dir, &fsReaddirCallback); + } + + /** + * @brief Iterates synchronously over a directory stream one entry at a + * time. + * + * Returns a composed value where: + * + * * The first parameter indicates the entry type (see below). + * * The second parameter is a string that contains the actual value. + * + * Available entry types are: + * + * * `FsReq::EntryType::UNKNOWN` + * * `FsReq::EntryType::FILE` + * * `FsReq::EntryType::DIR` + * * `FsReq::EntryType::LINK` + * * `FsReq::EntryType::FIFO` + * * `FsReq::EntryType::SOCKET` + * * `FsReq::EntryType::CHAR` + * * `FsReq::EntryType::BLOCK` + * + * See the official + * [documentation](http://docs.libuv.org/en/v1.x/fs.html#c.uv_dirent_t) + * for further details. + * + * This function isn't thread safe. Moreover, it doesn't return the `.` and + * `..` entries. + * + * @return A pair where: + * + * * The first parameter is a boolean value that indicates if the current + * entry is still valid. + * * The second parameter is a composed value (see above). + */ + std::pair> readdirSync() { + auto req = get(); + auto *dir = static_cast(req->ptr); + dir->dirents = dirents; + dir->nentries = 1; + cleanupAndInvokeSync(&uv_fs_readdir, parent(), req, dir); + return {req->result, { static_cast(dirents[0].type), dirents[0].name }}; + } + +private: + uv_dirent_t dirents[1]; }; diff --git a/src/uvw/util.hpp b/src/uvw/util.hpp index 7c23dd86..f0edd34a 100644 --- a/src/uvw/util.hpp +++ b/src/uvw/util.hpp @@ -211,6 +211,7 @@ using Uid = uv_uid_t; /*!< Library equivalent for uv_uid_t. */ using Gid = uv_gid_t; /*!< Library equivalent for uv_gid_t. */ using TimeVal = uv_timeval_t; /*!< Library equivalent for uv_timeval_t. */ +using TimeVal64 = uv_timeval64_t; /*!< Library equivalent for uv_timeval64_t. */ using RUsage = uv_rusage_t; /*!< Library equivalent for uv_rusage_t. */ @@ -287,8 +288,8 @@ private: * * \sa Utilities::uname */ -struct UName { - UName(std::shared_ptr utsname): utsname{utsname} {} +struct UtsName { + UtsName(std::shared_ptr utsname): utsname{utsname} {} /** * @brief Gets the operating system name (like "Linux"). @@ -579,7 +580,7 @@ struct Utilities { * * @return Name and information about the current kernel. */ - static UName uname() noexcept { + static UtsName uname() noexcept { auto ptr = std::make_shared(); uv_os_uname(ptr.get()); return ptr; @@ -958,6 +959,17 @@ struct Utilities { static bool chdir(const std::string &dir) noexcept { return (0 == uv_chdir(dir.data())); } + + /** + * @brief Cross-platform implementation of + * [`gettimeofday`](https://linux.die.net/man/2/gettimeofday) + * @return The current time. + */ + static TimeVal64 timeOfDay() { + uv_timeval64_t ret; + uv_gettimeofday(&ret); + return ret; + } };