memory-safe dir ranges

master
Daniel Kolesa 2018-04-16 01:58:59 +02:00
parent 1668750308
commit f395562734
3 changed files with 80 additions and 90 deletions

View File

@ -66,8 +66,8 @@ static fs::path OSTD_SHARED_LIB = "libostd.so";
static fs::path OSTD_STATIC_LIB = "libostd.a"; static fs::path OSTD_STATIC_LIB = "libostd.a";
static std::string DEFAULT_CXXFLAGS = "-std=c++1z -I. -O2 -Wall -Wextra " static std::string DEFAULT_CXXFLAGS = "-std=c++1z -I. -O2 -Wall -Wextra "
"-Wshadow -Wold-style-cast -fPIC "; "-Wshadow -Wold-style-cast -fPIC "
"-D_FILE_OFFSET_BITS=64" "-D_FILE_OFFSET_BITS=64";
static std::string DEFAULT_LDFLAGS = "-pthread"; static std::string DEFAULT_LDFLAGS = "-pthread";
static std::string DEFAULT_ASFLAGS = "-fPIC"; static std::string DEFAULT_ASFLAGS = "-fPIC";

View File

@ -26,6 +26,7 @@
#include <stack> #include <stack>
#include <list> #include <list>
#include <utility> #include <utility>
#include <memory>
#include <initializer_list> #include <initializer_list>
#include <type_traits> #include <type_traits>
#include <system_error> #include <system_error>
@ -724,6 +725,51 @@ private:
}; };
namespace detail { namespace detail {
struct OSTD_EXPORT dir_range_impl {
void open(path const &p);
void close() noexcept;
void read_next();
bool empty() const noexcept {
return p_current.path().empty();
}
directory_entry const &front() const noexcept {
return p_current;
}
~dir_range_impl() {
close();
}
directory_entry p_current{};
path p_dir{};
void *p_handle = nullptr;
};
struct OSTD_EXPORT rdir_range_impl {
using hstack = std::stack<void *, std::list<void *>>;
void open(path const &p);
void close() noexcept;
void read_next();
bool empty() const noexcept {
return p_current.path().empty();
}
directory_entry const &front() const noexcept {
return p_current;
}
~rdir_range_impl() {
close();
}
directory_entry p_current{};
path p_dir{};
hstack p_handles{};
};
} }
struct directory_range: input_range<directory_range> { struct directory_range: input_range<directory_range> {
@ -733,47 +779,26 @@ struct directory_range: input_range<directory_range> {
using size_type = std::size_t; using size_type = std::size_t;
directory_range() = delete; directory_range() = delete;
directory_range(path const &p) { directory_range(path const &p):
open(p); p_impl{std::make_shared<detail::dir_range_impl>()}
} {
p_impl->open(p);
directory_range(directory_range const &r):
p_current(r.p_current), p_dir(r.p_dir),
p_handle(r.p_handle), p_owned(false)
{}
~directory_range() {
close();
}
directory_range &operator=(directory_range const &r) noexcept {
close();
p_handle = r.p_handle;
p_owned = false;
return *this;
} }
bool empty() const noexcept { bool empty() const noexcept {
return p_current.path().empty(); return p_impl->empty();
} }
void pop_front() { void pop_front() {
read_next(); p_impl->read_next();
} }
reference front() const noexcept { reference front() const noexcept {
return p_current; return p_impl->front();
} }
private: private:
OSTD_EXPORT void open(path const &p); std::shared_ptr<detail::dir_range_impl> p_impl;
OSTD_EXPORT void close() noexcept;
OSTD_EXPORT void read_next();
directory_entry p_current{};
path p_dir{};
void *p_handle = nullptr;
bool p_owned = false;
}; };
struct recursive_directory_range: input_range<recursive_directory_range> { struct recursive_directory_range: input_range<recursive_directory_range> {
@ -783,52 +808,26 @@ struct recursive_directory_range: input_range<recursive_directory_range> {
using size_type = std::size_t; using size_type = std::size_t;
recursive_directory_range() = delete; recursive_directory_range() = delete;
recursive_directory_range(path const &p) { recursive_directory_range(path const &p):
open(p); p_impl{std::make_shared<detail::rdir_range_impl>()}
} {
p_impl->open(p);
recursive_directory_range(recursive_directory_range const &r):
p_current(r.p_current), p_dir(r.p_dir),
p_stack(r.p_stack), p_owned(false)
{}
~recursive_directory_range() {
close();
}
recursive_directory_range &operator=(
recursive_directory_range const &r
) noexcept {
close();
p_stack = r.p_stack;
p_owned = false;
return *this;
} }
bool empty() const noexcept { bool empty() const noexcept {
return p_current.path().empty(); return p_impl->empty();
} }
void pop_front() { void pop_front() {
read_next(); p_impl->read_next();
} }
reference front() const noexcept { reference front() const noexcept {
return p_current; return p_impl->front();
} }
private: private:
using hstack = std::stack<void *, std::list<void *>>; std::shared_ptr<detail::rdir_range_impl> p_impl;
OSTD_EXPORT void open(path const &p);
OSTD_EXPORT void close() noexcept;
OSTD_EXPORT void read_next();
directory_entry p_current{};
path p_dir{};
hstack p_handles{};
hstack *p_stack = nullptr;
bool p_owned = false;
}; };
OSTD_EXPORT path cwd(); OSTD_EXPORT path cwd();

View File

@ -11,6 +11,7 @@
namespace ostd { namespace ostd {
namespace fs { namespace fs {
namespace detail {
static void dir_read_next(DIR *dh, directory_entry &cur, path const &base) { static void dir_read_next(DIR *dh, directory_entry &cur, path const &base) {
struct dirent d; struct dirent d;
@ -37,7 +38,7 @@ static void dir_read_next(DIR *dh, directory_entry &cur, path const &base) {
/* dir range */ /* dir range */
OSTD_EXPORT void directory_range::open(path const &p) { OSTD_EXPORT void dir_range_impl::open(path const &p) {
DIR *d = opendir(p.string().data()); DIR *d = opendir(p.string().data());
if (!d) { if (!d) {
/* FIXME: throw */ /* FIXME: throw */
@ -45,50 +46,39 @@ OSTD_EXPORT void directory_range::open(path const &p) {
} }
p_dir = p; p_dir = p;
p_handle = d; p_handle = d;
p_owned = true;
read_next(); read_next();
} }
OSTD_EXPORT void directory_range::close() noexcept { OSTD_EXPORT void dir_range_impl::close() noexcept {
if (!p_owned) {
return;
}
closedir(static_cast<DIR *>(p_handle)); closedir(static_cast<DIR *>(p_handle));
p_owned = false;
} }
OSTD_EXPORT void directory_range::read_next() { OSTD_EXPORT void dir_range_impl::read_next() {
dir_read_next(static_cast<DIR *>(p_handle), p_current, p_dir); dir_read_next(static_cast<DIR *>(p_handle), p_current, p_dir);
} }
/* recursive dir range */ /* recursive dir range */
OSTD_EXPORT void recursive_directory_range::open(path const &p) { OSTD_EXPORT void rdir_range_impl::open(path const &p) {
DIR *d = opendir(p.string().data()); DIR *d = opendir(p.string().data());
if (!d) { if (!d) {
/* FIXME: throw */ /* FIXME: throw */
abort(); abort();
} }
p_dir = p; p_dir = p;
p_stack = &p_handles; p_handles.push(d);
p_stack->push(d);
p_owned = true;
read_next(); read_next();
} }
OSTD_EXPORT void recursive_directory_range::close() noexcept { OSTD_EXPORT void rdir_range_impl::close() noexcept {
if (!p_owned) {
return;
}
while (!p_handles.empty()) { while (!p_handles.empty()) {
closedir(static_cast<DIR *>(p_handles.top())); closedir(static_cast<DIR *>(p_handles.top()));
p_handles.pop(); p_handles.pop();
} }
p_owned = false;
} }
OSTD_EXPORT void recursive_directory_range::read_next() { OSTD_EXPORT void rdir_range_impl::read_next() {
if (p_stack->empty()) { if (p_handles.empty()) {
return; return;
} }
struct stat sb; struct stat sb;
@ -106,7 +96,7 @@ OSTD_EXPORT void recursive_directory_range::read_next() {
dir_read_next(nd, curd, based); dir_read_next(nd, curd, based);
if (!curd.path().empty()) { if (!curd.path().empty()) {
p_dir = based; p_dir = based;
p_stack->push(nd); p_handles.push(nd);
p_current = curd; p_current = curd;
return; return;
} else { } else {
@ -114,20 +104,21 @@ OSTD_EXPORT void recursive_directory_range::read_next() {
} }
} }
/* didn't recurse into a directory, go to next file */ /* didn't recurse into a directory, go to next file */
dir_read_next(static_cast<DIR *>(p_stack->top()), p_current, p_dir); dir_read_next(static_cast<DIR *>(p_handles.top()), p_current, p_dir);
/* end of dir, pop while at it */ /* end of dir, pop while at it */
if (p_current.path().empty()) { if (p_current.path().empty()) {
closedir(static_cast<DIR *>(p_stack->top())); closedir(static_cast<DIR *>(p_handles.top()));
p_stack->pop(); p_handles.pop();
if (!p_stack->empty()) { if (!p_handles.empty()) {
/* got back to another dir, read next so it's valid */ /* got back to another dir, read next so it's valid */
p_dir.remove_name(); p_dir.remove_name();
dir_read_next(static_cast<DIR *>(p_stack->top()), p_current, p_dir); dir_read_next(static_cast<DIR *>(p_handles.top()), p_current, p_dir);
} else { } else {
p_current.clear(); p_current.clear();
} }
} }
} }
} /* namespace detail */
} /* namesapce fs */ } /* namesapce fs */
} /* namespace ostd */ } /* namespace ostd */