Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy2
-rw-r--r--Readme.md2
-rw-r--r--include/afio/revision.hpp6
-rw-r--r--include/afio/v2.0/async_file_handle.hpp87
-rw-r--r--include/afio/v2.0/file_handle.hpp79
-rw-r--r--include/afio/v2.0/io_handle.hpp86
-rw-r--r--include/afio/v2.0/map_handle.hpp89
-rw-r--r--include/afio/v2.0/path_handle.hpp19
-rw-r--r--scripts/make_free_functions.py145
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
diff --git a/Readme.md b/Readme.md
index c73c3997..567db023 100644
--- a/Readme.md
+++ b/Readme.md
@@ -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 &section, 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)
+