diff --git a/Makefile b/Makefile index a42f135..6a23304 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,11 @@ INCLUDE = include PROJECT=shuffle3 +# Link to these libraries dynamicalls +SHARED_LIBS= +# Link to these libraries statically +STATIC_LIBS=fmt + # Currently supported: # _FS_SPILL_BUFFER: Use file backed buffer instead of memory backed one for unshuffling. See `shuffle3.h`. # Setting the value to `DYN` enables the dynamic buffer, setting it to `MAP` enabled memory-mapped buffer. `MAP` is usually the fastest of the 3 modes. @@ -34,7 +39,7 @@ CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors CFLAGS += $(COMMON_FLAGS) --std=gnu11 CXXFLAGS += $(COMMON_FLAGS) --std=gnu++23 -fno-exceptions # XXX: We might need exceptions soon, for OOP usage, because we try multiple approaches from most efficient to least. -LDFLAGS += +LDFLAGS += $(addsuffix .a,$(addprefix -l:lib,$(STATIC_LIBS))) $(addprefix -l,$(SHARED_LIBS)) STRIP=strip diff --git a/include/perc.h b/include/perc.h index f12b739..6f62e37 100644 --- a/include/perc.h +++ b/include/perc.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + #ifndef __cplusplus #error "C++ header only" #endif @@ -11,6 +15,153 @@ namespace pr { T* take(T*& ptr) noexcept { return std::exchange(ptr, nullptr); } } + using increment_t = long double; + + template + concept Bar = requires(T& v) { + { v.spin(increment_t(0)) }; + + }; + + template + struct Carrier; + + struct Dynamic { + template friend class Carrier; + + constexpr auto make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v>); + + constexpr virtual void spin(increment_t) =0; + + constexpr virtual ~Dynamic() {} + private: + constexpr Dynamic() noexcept {} + }; + template + struct Carrier : public virtual Dynamic { + using held_type = B; + + template requires(std::constructible_from) + constexpr Carrier(Args&&... args) noexcept(std::is_nothrow_constructible_v) + : Dynamic(), member_(std::forward(args)) {} + constexpr Carrier(Carrier&& m) noexcept(std::is_nothrow_move_constructible_v) + requires(std::is_move_constructible_v) + : Dynamic(), member_(std::move(m.member_)) {} + constexpr Carrier(Carrier const& c) noexcept(std::is_nothrow_copy_constructible_v) + requires(std::is_copy_constructible_v) + : Dynamic(), member_(c.member_) {} + + constexpr Carrier& operator=(Carrier&& m) noexcept(std::is_nothrow_move_assignable_v) requires(std::is_move_assignable_v) + { if(this != &m) member_ = std::move(m.member_); return *this; } + + constexpr Carrier& operator=(Carrier const& c) noexcept(std::is_nothrow_copy_assignable_v) requires(std::is_copy_assignable_v) + { if(this != &m) member_ = c.member_; return *this; } + + constexpr void spin(increment_t a) override { member_.spin(a); } + template requires(std::is_invocable_v) + constexpr void spin(increment_t a, Args&&... args) noexcept(std::is_nothrow_invocable_v) + { return member_.spin(a, std::forward(args)...); } + + constexpr virtual ~Carrier() {} + private: + B member_; + }; + template + Carrier(T) -> Carrier; + template + Carrier(T const&) -> Carrier; + template + Carrier(T&&) -> Carrier; + + constexpr auto Dynamic::make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v>) { return Carrier(std::move(bar)); } + + template + constexpr std::unique_ptr make_dyn(T&& bar) noexcept(std::is_nothrow_move_constructible_v) + { + template Inst> + constexpr bool is_inst_of = requires{ + requires(std::is_same_v>); + }; + template Ptr = std::unique_ptr> + constexpr Ptr static_ucast(Ptr&& from) noexcept + { return Ptr{static_cast(std::move(from).release())}; } + + template + constexpr std::shared_ptr static_ucast(std::shared_ptr&& from) noexcept + { return std::static_pointer_cast(std::move(from)); } + + if constexpr(is_inst_of) return static_ucast(std::make_unique(std::move(bar))); + else return static_ucast(std::make_unique>(std::move(bar))); + } + + constexpr void spin(Bar auto& bar, std::convertible_to auto&& a) { return bar.spin(increment_t(a)); } + + struct None { + constexpr None() noexcept = default; + constexpr ~None() noexcept = default; + + constexpr void spin(increment_t) noexcept {} + }; + + // A bounded progress-bar + struct Progress { + using fract_t = increment_t; + constexpr static inline bool DEFAULT_WIDTH = 50; + constexpr static inline fract_t DEFAULT_TERM_FRACT=1.0l/3.0l; + + [[gnu::nonnull(1)]] + Progress(FILE* p = stdout) noexcept; + + Progress(const Progress& p) noexcept; + Progress(Progress&& p) noexcept; + + Progress& operator=(Progress const& c) noexcept; + Progress& operator=(Progress&& m) noexcept; + + virtual ~Progress(); + + std::string& tag() const noexcept; + const std::string& tag() const noexcept; + + FILE* output() const noexcept; + FILE*& output() noexcept; + + size_t& width() noexcept; + size_t width() const noexcept; + + fract_t& fraction() noexcept; + fract_t fraction() const noexcept; + + fract_t percentage() const noexcept; + inline auto percentage() noexcept { + struct v final { + Progress* p_; + v(Progress* p) noexcept : p_(p) {} + + v(v&&) noexcept = default; + v(v const&) noexcept = default; + v& operator=(v const&) noexcept = default; + v& operator=(v&&) noexcept = default; + ~v() noexcept = default; + + const v& operator=(fract_t f) const noexcept { p_->percentage(f); return *this; } + operator fract_t() const noexcept { return p_->percentage(); } + }; + return v(this); + } + + void spin(increment_t by, bool render=true, bool flush=true) noexcept; + void render(bool flush = true) const noexcept; + private: + void percentage(fract_t fract) noexcept; + + struct _impl; + std::shared_ptr<_impl> inner_; + }; + + //TODO: Spinny bar thing (unbounded) + +/* struct Bar { Bar() noexcept = default; Bar(const Bar&) noexcept = default; @@ -74,4 +225,5 @@ namespace pr { using base_t::_num; size_t _max; }; +*/ } diff --git a/src/perc.cpp b/src/perc.cpp new file mode 100644 index 0000000..ed5f9c5 --- /dev/null +++ b/src/perc.cpp @@ -0,0 +1,114 @@ + +#include +#include + +#include + +#include + +#include + +namespace { + size_t twidth(int fd = STDIN_FILENO, size_t or = Progress::DEFAULT_WIDTH) noexcept + { + struct winsize w; + if(ioctl(fd, TIOCGWINSZ, &w) == -1) return or; + return size_t(w.ws_col); + } + + size_t def_bar_width(size_t tw) noexcept + { + return size_t(std::round(DEFAULT_TERM_FRACT * fract_t(tw))); + } +} + +namespace pr { + + struct Progress::_impl { + Progress::fract_t fract; // 0..=1 + FILE* output; + size_t width; + std::string aux; + }; + + size_t& Progress::width() noexcept { return inner_->width; } + size_t Progress::width() const noexcept { return inner_->width; } + + fract_t& Progress::fraction() noexcept { return inner_->fract; } + fract_t Progress::fraction() const noexcept { return inner_->fract; } + + fract_t Progress::percentage() const noexcept { return inner_->fract * 100.0l; } + void Progress::percentage(fract_t fract) noexcept { inner_->fract = fract / 100.0l; } + + std::string& Progress::tag() noexcept { return inner_->aux; } + const std::string& Progress::tag() const noexcept { return inner_->aux; } + + FILE* Progress::output() const noexcept { return inner_->output; } + FILE*& Progress::output() noexcept { return inner_->output; } + + void Progress::render(bool flush) const noexcept + { + constinit thread_local static std::vector buffer{}; + + const auto& inner = *inner_; + FILE* out = inner.output; + auto wf = fract_t(inner.width) * inner.fract; + auto perc = percentage(); + size_t cf = size_t(std::floor(wf)); + + + auto print = [] (F f, auto&&... args) { + return fmt::format_to(std::back_inserter(buffer), std::forward(f), std::forward(args)...); + }; + + buffer.clear(); + // Render bar + buffer.push_back('['); + for(size_t i=0;i(buffer.data())); + // Flush output stream + if(flush) fflush(out); + } + void Progress::spin(increment_t by, bool r, bool f) noexcept + { + auto& inner = *inner_; + inner.fract+=by; + if(inner.fract > 1.0l) + inner.fract=1.0l; + if(r) this->render(f); + } + + [[gnu::nonnull(1)]] + Progress::Progress(FILE* p) noexcept + : inner_(std::make_unique<_impl>(0, p, def_bar_width(twidth(fileno(p))) )) {} + + Progress::Progress(const Progress& p) noexcept + : inner_(p.inner_ ? std::make_unique<_impl>(*p.inner_) : nullptr) {} + + Progress::Progress(Progress&& p) noexcept + : inner_(std::move(p.inner_)) {} + + Progress& Progress::operator=(Progress&& p) noexcept + { + if(this != &p) inner_ = std::move(p.inner_); + return *this; + } + + Progress& Progress::operator=(Progress const& p) noexcept + { + if(this != &p) inner_ = p.inner_; + return *this; + } + + Progress::~Progress() { + if(inner_) + fputc('\n', inner_->output); + } +}