diff options
-rw-r--r-- | .clang-tidy | 2 | ||||
-rw-r--r-- | Readme.md | 2 | ||||
-rw-r--r-- | include/afio/revision.hpp | 6 | ||||
-rw-r--r-- | include/afio/v2.0/async_file_handle.hpp | 87 | ||||
-rw-r--r-- | include/afio/v2.0/file_handle.hpp | 79 | ||||
-rw-r--r-- | include/afio/v2.0/io_handle.hpp | 86 | ||||
-rw-r--r-- | include/afio/v2.0/map_handle.hpp | 89 | ||||
-rw-r--r-- | include/afio/v2.0/path_handle.hpp | 19 | ||||
-rw-r--r-- | scripts/make_free_functions.py | 145 |
9 files changed, 509 insertions, 6 deletions
diff --git a/.clang-tidy b/.clang-tidy index 6fdc53fc..502eccb2 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: '*,-llvm-header-guard,-google-build-using-namespace,-clang-analyzer-alpha.clone.CloneChecker,-google-runtime-int,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-clang-analyzer-alpha.deadcode.UnreachableCode,-misc-use-after-move,-cppcoreguidelines-pro-type-vararg,-modernize-use-emplace,-cert-err60-cpp' +Checks: '*,-llvm-header-guard,-google-build-using-namespace,-clang-analyzer-alpha.clone.CloneChecker,-google-runtime-int,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-clang-analyzer-alpha.deadcode.UnreachableCode,-misc-use-after-move,-cppcoreguidelines-pro-type-vararg,-modernize-use-emplace,-cert-err60-cpp,-cppcoreguidelines-pro-type-union-access' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: true @@ -29,8 +29,6 @@ fuzzed, coverage calculated, bloat calculated, ABI dumped etc ### clang AST parser based todos which await me getting back into the clang AST parser: -- [ ] Implement [[bindlib::make_free]] which injects member functions into the enclosing -namespace. - [ ] C bindings for all AFIO v2 APIs. Write libclang parser which autogenerates SWIG interface files from the .hpp files using custom attributes to fill in the missing gaps. diff --git a/include/afio/revision.hpp b/include/afio/revision.hpp index 1f74be01..1d781d00 100644 --- a/include/afio/revision.hpp +++ b/include/afio/revision.hpp @@ -1,4 +1,4 @@ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define AFIO_PREVIOUS_COMMIT_REF 9d66fe89ffa6df2c1ad87989d32bb8f828fba7d3 -#define AFIO_PREVIOUS_COMMIT_DATE "2017-07-31 02:59:37 +00:00" -#define AFIO_PREVIOUS_COMMIT_UNIQUE 9d66fe89 +#define AFIO_PREVIOUS_COMMIT_REF 35354f85bfb82f53609f98d3dcf042fbc76cf493 +#define AFIO_PREVIOUS_COMMIT_DATE "2017-08-02 22:38:44 +00:00" +#define AFIO_PREVIOUS_COMMIT_UNIQUE 35354f85 diff --git a/include/afio/v2.0/async_file_handle.hpp b/include/afio/v2.0/async_file_handle.hpp index 084b9d34..4619861f 100644 --- a/include/afio/v2.0/async_file_handle.hpp +++ b/include/afio/v2.0/async_file_handle.hpp @@ -305,6 +305,93 @@ public: AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept override; }; +// BEGIN make_free_functions.py +/*! Create an async file handle opening access to a file on path +using the given io_service. + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<async_file_handle> async_file(io_service &service, const path_handle &base, async_file_handle::path_view_type _path, async_file_handle::mode _mode = async_file_handle::mode::read, async_file_handle::creation _creation = async_file_handle::creation::open_existing, async_file_handle::caching _caching = async_file_handle::caching::all, async_file_handle::flag flags = async_file_handle::flag::none) noexcept +{ + return async_file_handle::async_file(std::forward<decltype(service)>(service), std::forward<decltype(base)>(base), std::forward<decltype(_path)>(_path), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_creation)>(_creation), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags)); +} +/*! Create an async file handle creating a randomly named file on a path. +The file is opened exclusively with `creation::only_if_not_exist` so it +will never collide with nor overwrite any existing file. Note also +that caching defaults to temporary which hints to the OS to only +flush changes to physical storage as lately as possible. + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<async_file_handle> async_random_file(io_service &service, const path_handle &dirpath, async_file_handle::mode _mode = async_file_handle::mode::write, async_file_handle::caching _caching = async_file_handle::caching::temporary, async_file_handle::flag flags = async_file_handle::flag::none) noexcept +{ + return async_file_handle::async_random_file(std::forward<decltype(service)>(service), std::forward<decltype(dirpath)>(dirpath), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags)); +} +/*! Create an async file handle creating the named file on some path which +the OS declares to be suitable for temporary files. Most OSs are +very lazy about flushing changes made to these temporary files. +Note the default flags are to have the newly created file deleted +on first handle close. +Note also that an empty name is equivalent to calling +`async_random_file(temporary_files_directory())` and the creation +parameter is ignored. + +\note If the temporary file you are creating is not going to have its +path sent to another process for usage, this is the WRONG function +to use. Use `temp_inode()` instead, it is far more secure. + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<async_file_handle> async_temp_file(io_service &service, async_file_handle::path_view_type name = async_file_handle::path_view_type(), async_file_handle::mode _mode = async_file_handle::mode::write, async_file_handle::creation _creation = async_file_handle::creation::if_needed, async_file_handle::caching _caching = async_file_handle::caching::temporary, async_file_handle::flag flags = async_file_handle::flag::unlink_on_close) noexcept +{ + return async_file_handle::async_temp_file(std::forward<decltype(service)>(service), std::forward<decltype(name)>(name), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_creation)>(_creation), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags)); +} +/*! \em Securely create an async file handle creating a temporary anonymous inode in +the filesystem referred to by \em dirpath. The inode created has +no name nor accessible path on the filing system and ceases to +exist as soon as the last handle is closed, making it ideal for use as +a temporary file where other processes do not need to have access +to its contents via some path on the filing system (a classic use case +is for backing shared memory maps). + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<async_file_handle> async_temp_inode(io_service &service, async_file_handle::path_view_type dirpath = temporary_files_directory(), async_file_handle::mode _mode = async_file_handle::mode::write, async_file_handle::flag flags = async_file_handle::flag::none) noexcept +{ + return async_file_handle::async_temp_inode(std::forward<decltype(service)>(service), std::forward<decltype(dirpath)>(dirpath), std::forward<decltype(_mode)>(_mode), std::forward<decltype(flags)>(flags)); +} +/*! \brief Schedule a read to occur asynchronously. + +\return Either an io_state_ptr to the i/o in progress, or an error code. +\param self The object whose member function to call. +\param reqs A scatter-gather and offset request. +\param completion A callable to call upon i/o completion. Spec is void(async_file_handle *, io_result<buffers_type> &). +Note that buffers returned may not be buffers input, see documentation for read(). +\errors As for read(), plus ENOMEM. +\mallocs One calloc, one free. The allocation is unavoidable due to the need to store a type +erased completion handler of unknown type. +*/ +template <class CompletionRoutine> inline result<async_file_handle::io_state_ptr<CompletionRoutine, async_file_handle::buffers_type>> async_read(async_file_handle &self, async_file_handle::io_request<async_file_handle::buffers_type> reqs, CompletionRoutine &&completion) noexcept +{ + return self.async_read(std::forward<decltype(reqs)>(reqs), std::forward<decltype(completion)>(completion)); +} +/*! \brief Schedule a write to occur asynchronously. + +\return Either an io_state_ptr to the i/o in progress, or an error code. +\param self The object whose member function to call. +\param reqs A scatter-gather and offset request. +\param completion A callable to call upon i/o completion. Spec is void(async_file_handle *, io_result<const_buffers_type> &). +Note that buffers returned may not be buffers input, see documentation for write(). +\errors As for write(), plus ENOMEM. +\mallocs One calloc, one free. The allocation is unavoidable due to the need to store a type +erased completion handler of unknown type. +*/ +template <class CompletionRoutine> inline result<async_file_handle::io_state_ptr<CompletionRoutine, async_file_handle::const_buffers_type>> async_write(async_file_handle &self, async_file_handle::io_request<async_file_handle::const_buffers_type> reqs, CompletionRoutine &&completion) noexcept +{ + return self.async_write(std::forward<decltype(reqs)>(reqs), std::forward<decltype(completion)>(completion)); +} +// END make_free_functions.py + AFIO_V2_NAMESPACE_END #if AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS) diff --git a/include/afio/v2.0/file_handle.hpp b/include/afio/v2.0/file_handle.hpp index be120948..a28b911d 100644 --- a/include/afio/v2.0/file_handle.hpp +++ b/include/afio/v2.0/file_handle.hpp @@ -336,6 +336,85 @@ public: #endif }; +// BEGIN make_free_functions.py +/*! Create a file handle opening access to a file on path + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<file_handle> file(const path_handle &base, file_handle::path_view_type _path, file_handle::mode _mode = file_handle::mode::read, file_handle::creation _creation = file_handle::creation::open_existing, file_handle::caching _caching = file_handle::caching::all, + file_handle::flag flags = file_handle::flag::none) noexcept +{ + return file_handle::file(std::forward<decltype(base)>(base), std::forward<decltype(_path)>(_path), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_creation)>(_creation), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags)); +} +/*! Create a file handle creating a randomly named file on a path. +The file is opened exclusively with `creation::only_if_not_exist` so it +will never collide with nor overwrite any existing file. Note also +that caching defaults to temporary which hints to the OS to only +flush changes to physical storage as lately as possible. + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<file_handle> random_file(const path_handle &dirpath, file_handle::mode _mode = file_handle::mode::write, file_handle::caching _caching = file_handle::caching::temporary, file_handle::flag flags = file_handle::flag::none) noexcept +{ + return file_handle::random_file(std::forward<decltype(dirpath)>(dirpath), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags)); +} +/*! Create a file handle creating the named file on some path which +the OS declares to be suitable for temporary files. Most OSs are +very lazy about flushing changes made to these temporary files. +Note the default flags are to have the newly created file deleted +on first handle close. +Note also that an empty name is equivalent to calling +`random_file(temporary_files_directory())` and the creation +parameter is ignored. + +\note If the temporary file you are creating is not going to have its +path sent to another process for usage, this is the WRONG function +to use. Use `temp_inode()` instead, it is far more secure. + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<file_handle> temp_file(file_handle::path_view_type name = file_handle::path_view_type(), file_handle::mode _mode = file_handle::mode::write, file_handle::creation _creation = file_handle::creation::if_needed, file_handle::caching _caching = file_handle::caching::temporary, + file_handle::flag flags = file_handle::flag::unlink_on_close) noexcept +{ + return file_handle::temp_file(std::forward<decltype(name)>(name), std::forward<decltype(_mode)>(_mode), std::forward<decltype(_creation)>(_creation), std::forward<decltype(_caching)>(_caching), std::forward<decltype(flags)>(flags)); +} +/*! \em Securely create a file handle creating a temporary anonymous inode in +the filesystem referred to by \em dirpath. The inode created has +no name nor accessible path on the filing system and ceases to +exist as soon as the last handle is closed, making it ideal for use as +a temporary file where other processes do not need to have access +to its contents via some path on the filing system (a classic use case +is for backing shared memory maps). + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<file_handle> temp_inode(file_handle::path_view_type dirpath = temporary_files_directory(), file_handle::mode _mode = file_handle::mode::write, file_handle::flag flags = file_handle::flag::none) noexcept +{ + return file_handle::temp_inode(std::forward<decltype(dirpath)>(dirpath), std::forward<decltype(_mode)>(_mode), std::forward<decltype(flags)>(flags)); +} +/*! Return the current maximum permitted extent of the file. + +\errors Any of the values POSIX fstat() or GetFileInformationByHandleEx() can return. +*/ +inline result<file_handle::extent_type> length(const file_handle &self) noexcept +{ + return self.length(); +} +/*! Resize the current maximum permitted extent of the file to the given extent, avoiding any +new allocation of physical storage where supported. Note that on extents based filing systems +this will succeed even if there is insufficient free space on the storage medium. + +\return The bytes actually truncated to. +\param self The object whose member function to call. +\param newsize The bytes to truncate the file to. +\errors Any of the values POSIX ftruncate() or SetFileInformationByHandle() can return. +*/ +inline result<file_handle::extent_type> truncate(file_handle &self, file_handle::extent_type newsize) noexcept +{ + return self.truncate(std::forward<decltype(newsize)>(newsize)); +} +// END make_free_functions.py + AFIO_V2_NAMESPACE_END #if AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS) diff --git a/include/afio/v2.0/io_handle.hpp b/include/afio/v2.0/io_handle.hpp index f7d759f8..9bdc06b1 100644 --- a/include/afio/v2.0/io_handle.hpp +++ b/include/afio/v2.0/io_handle.hpp @@ -413,6 +413,92 @@ public: }; +// BEGIN make_free_functions.py +/*! \brief Read data from the open handle. + +\warning Depending on the implementation backend, **very** different buffers may be returned than you +supplied. You should **always** use the buffers returned and assume that they point to different +memory and that each buffer's size will have changed. + +\return The buffers read, which may not be the buffers input. The size of each scatter-gather +buffer is updated with the number of bytes of that buffer transferred, and the pointer to +the data may be \em completely different to what was submitted (e.g. it may point into a +memory map). +\param self The object whose member function to call. +\param reqs A scatter-gather and offset request. +\param d An optional deadline by which the i/o must complete, else it is cancelled. +Note function may return significantly after this deadline if the i/o takes long to cancel. +\errors Any of the values POSIX read() can return, `errc::timed_out`, `errc::operation_canceled`. `errc::not_supported` may be +returned if deadline i/o is not possible with this particular handle configuration (e.g. +reading from regular files on POSIX or reading from a non-overlapped HANDLE on Windows). +\mallocs The default synchronous implementation in file_handle performs no memory allocation. +The asynchronous implementation in async_file_handle performs one calloc and one free. +*/ +inline io_handle::io_result<io_handle::buffers_type> read(io_handle &self, io_handle::io_request<io_handle::buffers_type> reqs, deadline d = deadline()) noexcept +{ + return self.read(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d)); +} +/*! \brief Write data to the open handle. + +\warning Depending on the implementation backend, not all of the buffers input may be written and +the some buffers at the end of the returned buffers may return with zero bytes written. +For example, with a zeroed deadline, some backends may only consume as many buffers as the system has available write slots +for, thus for those backends this call is "non-blocking" in the sense that it will return immediately even if it +could not schedule a single buffer write. + +\return The buffers written, which may not be the buffers input. The size of each scatter-gather +buffer is updated with the number of bytes of that buffer transferred. +\param self The object whose member function to call. +\param reqs A scatter-gather and offset request. +\param d An optional deadline by which the i/o must complete, else it is cancelled. +Note function may return significantly after this deadline if the i/o takes long to cancel. +\errors Any of the values POSIX write() can return, `errc::timed_out`, `errc::operation_canceled`. `errc::not_supported` may be +returned if deadline i/o is not possible with this particular handle configuration (e.g. +writing to regular files on POSIX or writing to a non-overlapped HANDLE on Windows). +\mallocs The default synchronous implementation in file_handle performs no memory allocation. +The asynchronous implementation in async_file_handle performs one calloc and one free. +*/ +inline io_handle::io_result<io_handle::const_buffers_type> write(io_handle &self, io_handle::io_request<io_handle::const_buffers_type> reqs, deadline d = deadline()) noexcept +{ + return self.write(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d)); +} +/*! \brief Issue a write reordering barrier such that writes preceding the barrier will reach storage +before writes after this barrier. + +\warning **Assume that this call is a no-op**. It is not reliably implemented in many common use cases, +for example if your code is running inside a LXC container, or if the user has mounted the filing +system with non-default options. Instead open the handle with `caching::reads` which means that all +writes form a strict sequential order not completing until acknowledged by the storage device. +Filing system can and do use different algorithms to give much better performance with `caching::reads`, +some (e.g. ZFS) spectacularly better. + +\warning Let me repeat again: consider this call to be a **hint** to poke the kernel with a stick to +go start to do some work sooner rather than later. It may be ignored entirely. + +\warning For portability, you can only assume that barriers write order for a single handle +instance. You cannot assume that barriers write order across multiple handles to the same inode, or +across processes. + +\return The buffers barriered, which may not be the buffers input. The size of each scatter-gather +buffer is updated with the number of bytes of that buffer barriered. +\param self The object whose member function to call. +\param reqs A scatter-gather and offset request for what range to barrier. May be ignored on some platforms +which always write barrier the entire file. Supplying a default initialised reqs write barriers the entire file. +\param wait_for_device True if you want the call to wait until data reaches storage and that storage +has acknowledged the data is physically written. Slow. +\param and_metadata True if you want the call to sync the metadata for retrieving the writes before the +barrier after a sudden power loss event. Slow. +\param d An optional deadline by which the i/o must complete, else it is cancelled. +Note function may return significantly after this deadline if the i/o takes long to cancel. +\errors Any of the values POSIX fdatasync() or Windows NtFlushBuffersFileEx() can return. +\mallocs None. +*/ +inline io_handle::io_result<io_handle::const_buffers_type> barrier(io_handle &self, io_handle::io_request<io_handle::const_buffers_type> reqs = io_handle::io_request<io_handle::const_buffers_type>(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept +{ + return self.barrier(std::forward<decltype(reqs)>(reqs), std::forward<decltype(wait_for_device)>(wait_for_device), std::forward<decltype(and_metadata)>(and_metadata), std::forward<decltype(d)>(d)); +} +// END make_free_functions.py + AFIO_V2_NAMESPACE_END #if AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS) diff --git a/include/afio/v2.0/map_handle.hpp b/include/afio/v2.0/map_handle.hpp index a2bc04f6..ee385e33 100644 --- a/include/afio/v2.0/map_handle.hpp +++ b/include/afio/v2.0/map_handle.hpp @@ -350,6 +350,95 @@ public: }; +// BEGIN make_free_functions.py +/*! \brief Create a memory section. +\param backing The handle to use as backing storage. An invalid handle means to use the system page file as the backing storage. +\param maximum_size The maximum size this section can ever be. Zero means to use backing.length(). +\param _flag How to create the section. + +\errors Any of the values POSIX dup() or NtCreateSection() can return. +*/ +inline result<section_handle> section(file_handle &backing, section_handle::extent_type maximum_size = 0, section_handle::flag _flag = section_handle::flag::read | section_handle::flag::write) noexcept +{ + return section_handle::section(std::forward<decltype(backing)>(backing), std::forward<decltype(maximum_size)>(maximum_size), std::forward<decltype(_flag)>(_flag)); +} +//! \overload +inline result<section_handle> section(section_handle::extent_type maximum_size, file_handle &backing, section_handle::flag _flag = section_handle::flag::read | section_handle::flag::write) noexcept +{ + return section_handle::section(std::forward<decltype(maximum_size)>(maximum_size), std::forward<decltype(backing)>(backing), std::forward<decltype(_flag)>(_flag)); +} +//! \overload +inline result<section_handle> section(section_handle::extent_type maximum_size) noexcept +{ + return section_handle::section(std::forward<decltype(maximum_size)>(maximum_size)); +} +/*! Resize the current maximum permitted extent of the memory section to the given extent. + +\errors Any of the values NtExtendSection() can return. On POSIX this is a no op. +*/ +inline result<section_handle::extent_type> truncate(section_handle &self, section_handle::extent_type newsize) noexcept +{ + return self.truncate(std::forward<decltype(newsize)>(newsize)); +} +/*! Create new memory and map it into view. +\param bytes How many bytes to create and map. Typically will be rounded to a multiple of the page size (see utils::page_sizes()). +\param _flag The permissions with which to map the view which are constrained by the permissions of the memory section. `flag::none` can be useful for reserving virtual address space without committing system resources, use commit() to later change availability of memory. + +\note On Microsoft Windows this constructor uses the faster VirtualAlloc() which creates less versatile page backed memory. If you want anonymous memory +allocated from a paging file backed section instead, create a page file backed section and then a mapped view from that using +the other constructor. This makes available all those very useful VM tricks Windows can do with section mapped memory which +VirtualAlloc() memory cannot do. + +\errors Any of the values POSIX mmap() or NtMapViewOfSection() can return. +*/ +inline result<map_handle> map(map_handle::size_type bytes, section_handle::flag _flag = section_handle::flag::read | section_handle::flag::write) noexcept +{ + return map_handle::map(std::forward<decltype(bytes)>(bytes), std::forward<decltype(_flag)>(_flag)); +} +/*! Create a memory mapped view of a backing storage. +\param section A memory section handle specifying the backing storage to use. +\param bytes How many bytes to map (0 = the size of the memory section). Typically will be rounded to a multiple of the page size (see utils::page_sizes()). +\param offset The offset into the backing storage to map from. Typically needs to be at least a multiple of the page size (see utils::page_sizes()), on Windows it needs to be a multiple of the kernel memory allocation granularity (typically 64Kb). +\param _flag The permissions with which to map the view which are constrained by the permissions of the memory section. `flag::none` can be useful for reserving virtual address space without committing system resources, use commit() to later change availability of memory. + +\errors Any of the values POSIX mmap() or NtMapViewOfSection() can return. +*/ +inline result<map_handle> map(section_handle §ion, map_handle::size_type bytes = 0, map_handle::extent_type offset = 0, section_handle::flag _flag = section_handle::flag::read | section_handle::flag::write) noexcept +{ + return map_handle::map(std::forward<decltype(section)>(section), std::forward<decltype(bytes)>(bytes), std::forward<decltype(offset)>(offset), std::forward<decltype(_flag)>(_flag)); +} +/*! \brief Read data from the mapped view. + +\note Because this implementation never copies memory, you can pass in buffers with a null address. + +\return The buffers read, which will never be the buffers input because they will point into the mapped view. +The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred. +\param self The object whose member function to call. +\param reqs A scatter-gather and offset request. +\param d Ignored. +\errors None, though the various signals and structured exception throws common to using memory maps may occur. +\mallocs None. +*/ +inline map_handle::io_result<map_handle::buffers_type> read(map_handle &self, map_handle::io_request<map_handle::buffers_type> reqs, deadline d = deadline()) noexcept +{ + return self.read(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d)); +} +/*! \brief Write data to the mapped view. + +\return The buffers written, which will never be the buffers input because they will point at where the data was copied into the mapped view. +The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred. +\param self The object whose member function to call. +\param reqs A scatter-gather and offset request. +\param d Ignored. +\errors None, though the various signals and structured exception throws common to using memory maps may occur. +\mallocs None. +*/ +inline map_handle::io_result<map_handle::const_buffers_type> write(map_handle &self, map_handle::io_request<map_handle::const_buffers_type> reqs, deadline d = deadline()) noexcept +{ + return self.write(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d)); +} +// END make_free_functions.py + AFIO_V2_NAMESPACE_END #if AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS) diff --git a/include/afio/v2.0/path_handle.hpp b/include/afio/v2.0/path_handle.hpp index e85bd15d..ab9d1f8c 100644 --- a/include/afio/v2.0/path_handle.hpp +++ b/include/afio/v2.0/path_handle.hpp @@ -86,6 +86,25 @@ public: static AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<path_handle> path(path_view_type _path) noexcept { return path(path_handle(), _path); } }; +// BEGIN make_free_functions.py +/*! Create a path handle opening access to some location on the filing system. +Some operating systems provide a particularly lightweight method of doing this +(Linux: `O_PATH`, Windows: no access perms) which is much faster than opening +a directory. For other systems, we open a directory with read only permissions. + +\errors Any of the values POSIX open() or CreateFile() can return. +*/ +inline result<path_handle> path(const path_handle &base, path_handle::path_view_type _path) noexcept +{ + return path_handle::path(std::forward<decltype(base)>(base), std::forward<decltype(_path)>(_path)); +} +//! \overload +inline result<path_handle> path(path_handle::path_view_type _path) noexcept +{ + return path_handle::path(std::forward<decltype(_path)>(_path)); +} +// END make_free_functions.py + AFIO_V2_NAMESPACE_END #if AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS) diff --git a/scripts/make_free_functions.py b/scripts/make_free_functions.py new file mode 100644 index 00000000..39fc1a89 --- /dev/null +++ b/scripts/make_free_functions.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +# Look for all static member functions marked AFIO_MAKE_FREE_FUNCTION and make them free +# (C) 2017 Niall Douglas + +from __future__ import print_function +import sys, glob, re + +for header in glob.glob("../include/afio/*/*.hpp"): + with open(header, "rt") as ih: + lines = ih.readlines() + current_class = None + functions_to_be_freed = [] + functions_to_be_freed_begin = -1 + functions_to_be_freed_end = -1 + for lineidx in range(0, len(lines)): + if lines[lineidx].startswith('class'): + current_class = lines[lineidx][6:lines[lineidx].find(':')] + current_class = current_class.replace('AFIO_DECL', '').lstrip().rstrip() + if 'AFIO_MAKE_FREE_FUNCTION' in lines[lineidx] and re.match('\s*AFIO_MAKE_FREE_FUNCTION', lines[lineidx]): + function = '' + for n in range(1, 100): + function += lines[lineidx+n] + if lineidx+n+1 >= len(lines): + print(lines[lineidx:]) + raise Exception() + if '{' in lines[lineidx+n+1] or ';' in lines[lineidx+n]: + break + for n in range(1, 100): + if '//!' in lines[lineidx-n] or '/*!' in lines[lineidx-n]: + docs = ''.join(lines[lineidx-n:lineidx]) + # Remove any indent + docs = docs.replace('\n ', '\n')[2:] + break + functions_to_be_freed.append((current_class, function, docs)) + elif '// BEGIN make_free_functions.py' in lines[lineidx]: + functions_to_be_freed_begin = lineidx + elif '// END make_free_functions.py' in lines[lineidx]: + functions_to_be_freed_end = lineidx + if functions_to_be_freed: + if functions_to_be_freed_begin != -1: + del lines[functions_to_be_freed_begin+1:functions_to_be_freed_end] + else: + # Place just before the last AFIO_V2_NAMESPACE_END + functions_to_be_freed_begin = len(lines)-1 + while not lines[functions_to_be_freed_begin].startswith('AFIO_V2_NAMESPACE_END'): + functions_to_be_freed_begin = functions_to_be_freed_begin - 1 + lines.insert(functions_to_be_freed_begin, '// END make_free_functions.py\n\n') + lines.insert(functions_to_be_freed_begin, '// BEGIN make_free_functions.py\n') + idx = functions_to_be_freed_begin + 1 + for classname, function, docs in functions_to_be_freed: + #print((classname, function)) + function = function.replace('virtual ', '') + function = function.replace('override', '') + function = function.replace('AFIO_HEADERS_ONLY_MEMFUNC_SPEC ', '') + function = function.replace('AFIO_HEADERS_ONLY_VIRTUAL_SPEC ', '') + completion_template = 'template <class CompletionRoutine>' in function + function = function.replace('template <class CompletionRoutine> ', '') + is_static = 'static ' in function + function = function.replace('static ', '') + function = function.lstrip() + if not function.startswith('inline'): + function = 'inline ' + function + if completion_template: + function = 'template <class CompletionRoutine> ' + function + def replace(function, item): + function = function.replace(' '+item+' ', ' '+classname+'::'+item+' ') + function = function.replace('('+item+' ', '('+classname+'::'+item+' ') + function = function.replace('<'+item+'>', '<'+classname+'::'+item+'>') + function = function.replace(' '+item+'>', ' '+classname+'::'+item+'>') + return function + function = replace(function, 'path_view_type') + function = replace(function, 'extent_type') + function = replace(function, 'size_type') + function = replace(function, 'buffers_type') + function = replace(function, 'const_buffers_type') + function = function.replace('path_view_type()', classname+'::path_view_type()') + function = function.replace(' io_result<', ' '+classname+'::io_result<') + function = function.replace(' io_request<', ' '+classname+'::io_request<') + function = function.replace('(io_request<', '('+classname+'::io_request<') + function = function.replace('<io_state_ptr<', '<'+classname+'::io_state_ptr<') + function = function.replace(' mode ', ' '+classname+'::mode ') + function = function.replace(' mode::', ' '+classname+'::mode::') + function = function.replace(' creation ', ' '+classname+'::creation ') + function = function.replace(' creation::', ' '+classname+'::creation::') + function = function.replace(' caching ', ' '+classname+'::caching ') + function = function.replace(' caching::', ' '+classname+'::caching::') + function = function.replace(' flag ', ' '+classname+'::flag ') + function = function.replace(' flag::', ' '+classname+'::flag::') + if function[-2] == ';': + function=function[:-2] + elif '}' in function: + function = function[:function.rfind('{')] + function[function.rfind('}')+1:] + function = function.rstrip() + idx1 = function.rfind('const') + idx2 = function.rfind(')') + is_const = False + if idx1 > idx2: + function = function[:idx1] + function[idx1+6:] + function = function.rstrip() + is_const = True + idx1 = function.rfind('= 0') + if idx1 > idx2: + function = function[:idx1] + function[idx1+3:] + function = function.rstrip() + if is_static: + call = function + impl = "\n{\n return "+classname+"::" + else: + call = function + idx1 = function.find('(') + idx2 = function.rfind(')') + function = function[:idx1+1] + (('const ' + classname) if is_const else classname) + (' &self, ' if idx1+1 != idx2 else ' &self') + function[idx1+1:] + impl = "\n{\n return self." + idx1 = docs.find('\\param') + if idx1 != -1: + docs = docs[:idx1] + '\param self The object whose member function to call.\n' + docs[idx1:] + + #print(call) + call = re.sub(r'^.+?(?= [^ ]+\()', '', call) # remove anything before the function call + #print(call) + call = re.sub(r'(?<=\) ).*', '', call) # remove anything after the function call + #print(call) + call = re.sub(r'".*?"', '', call) # remove any string literals + #print(call) + call = re.sub(r'\s?=.+?(?:\(.*?\))?(?=[,)])', '', call) # remove all default initialisers + #print(call) + impl += re.match(r'.+\(', call).group(0).lstrip() + first = True + for par in re.finditer(r'\w*[,)]', call): + if not first: + impl += ' ' + else: + first = False + p = par.group(0) + if p == ')': + impl += ')' + else: + impl += 'std::forward<decltype('+p[:-1]+')>('+p[:-1]+')'+p[-1] + impl += ";\n}\n" + lines.insert(idx, function+impl) + lines.insert(idx, docs) + idx = idx + 2 + with open(header, "wt") as oh: + oh.writelines(lines) + |