From 548fd17fc0c2db4d833e3ccc32b75275e4ab59c4 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 6 Aug 2025 19:08:54 +1000 Subject: [PATCH 01/61] create stream_listener --- include/warthog/io/stream_listener.h | 59 ++++++++++++++++++++++++++++ src/CMakeLists.txt | 1 + src/io/listeners.cpp | 32 +++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 include/warthog/io/stream_listener.h create mode 100644 src/io/listeners.cpp diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_listener.h new file mode 100644 index 0000000..16e8211 --- /dev/null +++ b/include/warthog/io/stream_listener.h @@ -0,0 +1,59 @@ +#ifndef WARTHOG_IO_STREAM_LISTENER_H +#define WARTHOG_IO_STREAM_LISTENER_H + +// listener/stream_listener.h +// +// A search listener is a callback class that executes specialised +// code for partiular search events, such as when: +// - a node is generated +// - a node is expanded +// - a node is relaxed +// +// This class implements dummy listener with empty event handlers. +// +// @author: Ryan Hechenberger +// @created: 2025-08-01 +// + +#include +#include +#include +#include +#include + +namespace warthog::io +{ + +class stream_listener +{ +public: + using shared_stream_t = std::shared_ptr; + stream_listener() = default; + stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + stream_listener(std::ostream& stream); + stream_listener(const shared_stream_t& stream); + ~stream_listener(); + + void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + void stream(std::ostream& stream); + void stream_share(const shared_stream_t& stream); + void stream_share(const stream_listener& stream); + void stream_stdin(); + void stream_stderr(); + void clear_stream(); + + operator bool() const noexcept { return stream_ != nullptr; } + + std::ostream& stream() noexcept { assert(stream_ != nullptr); return *stream_; } + const shared_stream_t& shared_stream() noexcept { return shared_stream_; } + +private: + std::ostream* stream_ = nullptr; + shared_stream_t shared_stream_; +}; + +using void_listener = stream_listener; + +} // namespace warthog::io + +#endif // WARTHOG_IO_STREAM_LISTENER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f53bdd..20fc3a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ geometry/geography.cpp geometry/geom.cpp io/grid.cpp +io/listeners.cpp memory/node_pool.cpp diff --git a/src/io/listeners.cpp b/src/io/listeners.cpp new file mode 100644 index 0000000..9ff0e1c --- /dev/null +++ b/src/io/listeners.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +namespace warthog::io +{ + +stream_listener::stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + shared_stream_ = std::make_shared(filename, mode); + stream_ = shared_stream_.get(); +} +stream_listener::stream_listener(std::ostream& stream) +{ + stream_ = &stream; +} +stream_listener::stream_listener(const shared_stream_t& stream) +{ + shared_stream_ = stream; + stream_ = shared_stream_.get(); +} +stream_listener::~stream_listener() = default; + +void stream_open(const std::filesystem::path& filename); +void stream(std::ostream& stream); +void stream_share(const shared_stream_t& stream); +void stream_share(const stream_listener& stream); +void stream_stdin(); +void stream_stderr(); +void clear_stream(); + +} // namespace warthog::io From 0af8334de9c428f415f55bc74e5cf8eea39b0e89 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 6 Aug 2025 21:02:39 +1000 Subject: [PATCH 02/61] added listener functionality --- include/warthog/io/listener.h | 43 +++++++++++++++++++ include/warthog/io/stream_listener.h | 2 +- .../warthog/search/unidirectional_search.h | 29 ++++++------- src/io/listeners.cpp | 43 ++++++++++++++++--- 4 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 include/warthog/io/listener.h diff --git a/include/warthog/io/listener.h b/include/warthog/io/listener.h new file mode 100644 index 0000000..e6ee83b --- /dev/null +++ b/include/warthog/io/listener.h @@ -0,0 +1,43 @@ +#ifndef WARTHOG_IO_LISTENER_H +#define WARTHOG_IO_LISTENER_H + +// listener/stream_listener.h +// +// The lister base class, has many invoker functions for set listen events +// +// This class implements dummy listener with empty event handlers. +// +// @author: Ryan Hechenberger +// @created: 2025-08-06 +// + +#include + +#define WARTHOG_LISTENER_DEFINE(func_name) \ +template \ +concept listener_has_##func_name = requires(Listener L, Args&&... args) \ +{ \ + { L.generate_node(std::forward(args)...) }; \ +}; \ +template \ +void listener_##func_name(Listeners& L, Args&&... args) \ +{ \ + if constexpr (I < std::tuple_size_v) { \ + using T = std::tuple_element_t; \ + if constexpr (listener_has_##func_name ) { \ + std::get(L).func_name(std::forward(args)...); \ + } \ + listener_##func_name (L, std::forward(args)...); \ + } \ +} + +namespace warthog::io +{ + +WARTHOG_LISTENER_DEFINE(generate_node) +WARTHOG_LISTENER_DEFINE(expand_node) +WARTHOG_LISTENER_DEFINE(relax_node) + +} // namespace warthog::io + +#endif // WARTHOG_IO_LISTENER_H diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_listener.h index 16e8211..8a433cc 100644 --- a/include/warthog/io/stream_listener.h +++ b/include/warthog/io/stream_listener.h @@ -38,7 +38,7 @@ class stream_listener void stream(std::ostream& stream); void stream_share(const shared_stream_t& stream); void stream_share(const stream_listener& stream); - void stream_stdin(); + void stream_stdout(); void stream_stderr(); void clear_stream(); diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index b8fec89..1138fbd 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -10,12 +10,12 @@ // @created: 2021-10-13 // -#include "dummy_listener.h" #include "problem_instance.h" #include "search.h" #include "search_parameters.h" #include "solution.h" #include "uds_traits.h" +#include #include #include #include @@ -41,7 +41,7 @@ namespace warthog::search // used determine if a search should continue or terminate. // (default: search for any solution, until OPEN is exhausted) template< - class H, class E, class Q = util::pqueue_min, class L = dummy_listener, + typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>, admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> @@ -49,9 +49,8 @@ class unidirectional_search { public: unidirectional_search( - H* heuristic, E* expander, Q* queue, L* listener = nullptr) - : heuristic_(heuristic), expander_(expander), open_(queue), - listener_(listener) + H* heuristic, E* expander, Q* queue, L listeners = L{}) + : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) { } ~unidirectional_search() { } @@ -113,10 +112,10 @@ class unidirectional_search } } - void - set_listener(L* listener) + L& + get_listeners() noexcept { - listener_ = listener; + return listeners_; } E* @@ -151,7 +150,7 @@ class unidirectional_search H* heuristic_; E* expander_; Q* open_; - L* listener_; + [[no_unique_address]] L listeners_; // no copy ctor unidirectional_search(const unidirectional_search& other) { } @@ -223,7 +222,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - listener_->generate_node(0, start, 0, UINT32_MAX); + io::listener_generate_node(listeners_, nullptr, start, nullptr, UINT32_MAX); user(pi->verbose_, pi); trace(pi->verbose_, "Start node:", *start); update_ub(start, sol, pi); @@ -247,7 +246,7 @@ class unidirectional_search current->set_expanded(true); // NB: set before generating succ sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); - listener_->expand_node(current); + io::listener_expand_node(listeners_, current); trace(pi->verbose_, "Expanding:", *current); // Generate successors of the current node @@ -258,7 +257,7 @@ class unidirectional_search expander_->get_successor(i, n, cost_to_n); sol->met_.nodes_generated_++; cost_t gval = current->get_g() + cost_to_n; - listener_->generate_node(current, n, gval, i); + io::listener_generate_node(listeners_, current, n, gval, i); // Generate new search nodes, provided they're not // dominated by the current upperbound @@ -282,7 +281,7 @@ class unidirectional_search < sol->sum_of_edge_costs_) { n->relax(gval, current->get_id()); - listener_->relax_node(n); + io::listener_relax_node(listeners_, n); if(open_->contains(n)) { @@ -327,10 +326,10 @@ class unidirectional_search }; template< - class H, class E, class Q = util::pqueue_min, class L = dummy_listener> + typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>> unidirectional_search( H* heuristic, E* expander, Q* queue, - L* listener = nullptr) -> unidirectional_search; + L listeners = L{}) -> unidirectional_search; } // namespace warthog::search diff --git a/src/io/listeners.cpp b/src/io/listeners.cpp index 9ff0e1c..32d1f04 100644 --- a/src/io/listeners.cpp +++ b/src/io/listeners.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace warthog::io { @@ -21,12 +22,40 @@ stream_listener::stream_listener(const shared_stream_t& stream) } stream_listener::~stream_listener() = default; -void stream_open(const std::filesystem::path& filename); -void stream(std::ostream& stream); -void stream_share(const shared_stream_t& stream); -void stream_share(const stream_listener& stream); -void stream_stdin(); -void stream_stderr(); -void clear_stream(); +void stream_listener::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + shared_stream_ = std::make_shared(filename, mode); + stream_ = shared_stream_.get(); +} +void stream_listener::stream(std::ostream& stream) +{ + stream_ = &stream; + shared_stream_ = nullptr; +} +void stream_listener::stream_share(const shared_stream_t& stream) +{ + shared_stream_ = stream; + stream_ = shared_stream_.get(); +} +void stream_listener::stream_share(const stream_listener& stream) +{ + shared_stream_ = stream.shared_stream_; + stream_ = stream.stream_; +} +void stream_listener::stream_stdout() +{ + shared_stream_ = nullptr; + stream_ = &std::cout; +} +void stream_listener::stream_stderr() +{ + shared_stream_ = nullptr; + stream_ = &std::cerr; +} +void stream_listener::clear_stream() +{ + shared_stream_ = nullptr; + stream_ = nullptr; +} } // namespace warthog::io From 1524765249b84e0882e5fee1f2792569c2c4cd23 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 Aug 2025 16:12:59 +1000 Subject: [PATCH 03/61] trace for grid --- include/warthog/io/grid_trace.h | 63 +++++++++++++++++++ include/warthog/io/listener.h | 23 ++++++- include/warthog/io/posthoc_listener.h | 56 +++++++++++++++++ include/warthog/io/stream_listener.h | 4 +- .../warthog/search/unidirectional_search.h | 13 ++-- src/CMakeLists.txt | 1 + src/io/listeners.cpp | 10 +++ 7 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 include/warthog/io/grid_trace.h create mode 100644 include/warthog/io/posthoc_listener.h diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h new file mode 100644 index 0000000..a2c8a08 --- /dev/null +++ b/include/warthog/io/grid_trace.h @@ -0,0 +1,63 @@ +#ifndef WARTHOG_IO_GRID_TRACE_H +#define WARTHOG_IO_GRID_TRACE_H + +// listener/grid_trace.h +// +// @author: Ryan Hechenberger +// @created: 2025-08-07 +// + +#include "posthoc_listener.h" +#include +#include +#include +#include + +namespace warthog::io +{ + +/// @brief class that produces a posthoc trace for the gridmap domain +class grid_trace : public posthoc_listener +{ +public: + using node = search::search_node; + using posthoc_listener::posthoc_listener; + + void set_grid(domain::gridmap* grid) noexcept + { + grid_ = grid; + } + + void print_posthoc_header() override; + + /// @brief Checks that grid_ != nullptr + void event(const char* name) const + { + if (grid_ == nullptr) { + throw std::logic_error("grid_trace::grid_ is null"); + } + } + + void begin_search(int id, const search::search_problem_instance& pi); + + void + expand_node(const node& current) const; + + void + relax_node(const node& current) const; + + void + generate_node( + const node* parent, const node& child, cost_t edge_cost, + uint32_t edge_id) const; + + void + close_node(const node& current) const; + +protected: + domain::gridmap* grid_; +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_GRID_TRACE_H diff --git a/include/warthog/io/listener.h b/include/warthog/io/listener.h index e6ee83b..ffb0b18 100644 --- a/include/warthog/io/listener.h +++ b/include/warthog/io/listener.h @@ -13,30 +13,47 @@ #include -#define WARTHOG_LISTENER_DEFINE(func_name) \ +#define WARTHOG_LISTENER_HAS_FN(func_name) \ template \ concept listener_has_##func_name = requires(Listener L, Args&&... args) \ { \ - { L.generate_node(std::forward(args)...) }; \ -}; \ + { L.func_name(std::forward(args)...) }; \ +}; +#define WARTHOG_LISTENER_FN(func_name) \ template \ void listener_##func_name(Listeners& L, Args&&... args) \ { \ if constexpr (I < std::tuple_size_v) { \ using T = std::tuple_element_t; \ if constexpr (listener_has_##func_name ) { \ + /* call event first if present */ \ + if constexpr (listener_has_event ) std::get(L).event(#func_name); \ std::get(L).func_name(std::forward(args)...); \ + } else { \ + if constexpr (listener_has_missing_event ) \ + std::get(L).missing_event(#func_name); \ } \ listener_##func_name (L, std::forward(args)...); \ } \ } +#define WARTHOG_LISTENER_DEFINE(func_name) \ + WARTHOG_LISTENER_HAS_FN(func_name) \ + WARTHOG_LISTENER_FN(func_name) + namespace warthog::io { +// functions used by WARTHOG_LISTENER_FN +WARTHOG_LISTENER_HAS_FN(event) +WARTHOG_LISTENER_HAS_FN(missing_event) + +WARTHOG_LISTENER_DEFINE(begin_search) +WARTHOG_LISTENER_DEFINE(end_search) WARTHOG_LISTENER_DEFINE(generate_node) WARTHOG_LISTENER_DEFINE(expand_node) WARTHOG_LISTENER_DEFINE(relax_node) +WARTHOG_LISTENER_DEFINE(close_node) } // namespace warthog::io diff --git a/include/warthog/io/posthoc_listener.h b/include/warthog/io/posthoc_listener.h new file mode 100644 index 0000000..14447b3 --- /dev/null +++ b/include/warthog/io/posthoc_listener.h @@ -0,0 +1,56 @@ +#ifndef WARTHOG_IO_POSTHOC_LISTENER_H +#define WARTHOG_IO_POSTHOC_LISTENER_H + +// listener/posthoc_listener.h +// +// @author: Ryan Hechenberger +// @created: 2025-08-07 +// + +#include "stream_listener.h" + +namespace warthog::io +{ + +/// @brief base posthoc listener class. +class posthoc_listener : public stream_listener +{ +public: + using stream_listener::stream_listener; + + // will print the header if not already printed + void event(const char*); + + virtual void print_posthoc_header(); + + template + void begin_search(int id, Args&&...) + { + do_trace_ = false; + if (done_trace_) return; // do not repeat a trace + if (stream_listener::operator bool() && id == search_id_) { + do_trace_ = true; + done_trace_ = true; + print_posthoc_header(); + } + } + template + void end_search(Args&&...) + { + do_trace_ = false; + } + + operator bool() const noexcept + { + return do_trace_; + } + +protected: + int search_id_ = 0; + bool do_trace_ = false; + bool done_trace_ = false; +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_POSTHOC_LISTENER_H diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_listener.h index 8a433cc..809e9d8 100644 --- a/include/warthog/io/stream_listener.h +++ b/include/warthog/io/stream_listener.h @@ -44,8 +44,8 @@ class stream_listener operator bool() const noexcept { return stream_ != nullptr; } - std::ostream& stream() noexcept { assert(stream_ != nullptr); return *stream_; } - const shared_stream_t& shared_stream() noexcept { return shared_stream_; } + std::ostream& stream() const noexcept { assert(stream_ != nullptr); return *stream_; } + const shared_stream_t& shared_stream() const noexcept { return shared_stream_; } private: std::ostream* stream_ = nullptr; diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 1138fbd..3417095 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -211,6 +211,8 @@ class unidirectional_search mytimer.start(); open_->clear(); + io::listener_begin_search(listeners_, static_cast(pi->instance_id_), *pi); + // initialise the start node and push to OPEN { if(pi->start_ == pad_id::max()) { return; } @@ -222,7 +224,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - io::listener_generate_node(listeners_, nullptr, start, nullptr, UINT32_MAX); + io::listener_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); user(pi->verbose_, pi); trace(pi->verbose_, "Start node:", *start); update_ub(start, sol, pi); @@ -246,7 +248,7 @@ class unidirectional_search current->set_expanded(true); // NB: set before generating succ sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); - io::listener_expand_node(listeners_, current); + io::listener_expand_node(listeners_, *current); trace(pi->verbose_, "Expanding:", *current); // Generate successors of the current node @@ -257,18 +259,18 @@ class unidirectional_search expander_->get_successor(i, n, cost_to_n); sol->met_.nodes_generated_++; cost_t gval = current->get_g() + cost_to_n; - io::listener_generate_node(listeners_, current, n, gval, i); // Generate new search nodes, provided they're not // dominated by the current upperbound if(n->get_search_number() != current->get_search_number()) { initialise_node_(n, current->get_id(), gval, pi, par, sol); - if(n->get_f() < sol->sum_of_edge_costs_) + if(n->get_f() <= sol->sum_of_edge_costs_) { open_->push(n); trace(pi->verbose_, "Generate:", *n); update_ub(current, sol, pi); + io::listener_generate_node(listeners_, current, *n, gval, i); continue; } } @@ -281,7 +283,7 @@ class unidirectional_search < sol->sum_of_edge_costs_) { n->relax(gval, current->get_id()); - io::listener_relax_node(listeners_, n); + io::listener_relax_node(listeners_, *n); if(open_->contains(n)) { @@ -308,6 +310,7 @@ class unidirectional_search // patched until AC FC RP reworked sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); } + io::listener_close_node(listeners_, *current); } sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 20fc3a8..5abf0b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ geometry/geom.cpp io/grid.cpp io/listeners.cpp +io/traces.cpp memory/node_pool.cpp diff --git a/src/io/listeners.cpp b/src/io/listeners.cpp index 32d1f04..18b0456 100644 --- a/src/io/listeners.cpp +++ b/src/io/listeners.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -58,4 +59,13 @@ void stream_listener::clear_stream() stream_ = nullptr; } +void posthoc_listener::print_posthoc_header() +{ + if (*this) { + stream() << R"posthoc(version: 1.4.0 +events: +)posthoc"; + } +} + } // namespace warthog::io From e6c0f8826661802f982c2bebaa0c47a890966b5a Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 Aug 2025 16:13:13 +1000 Subject: [PATCH 04/61] trace for grid --- src/io/traces.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/io/traces.cpp diff --git a/src/io/traces.cpp b/src/io/traces.cpp new file mode 100644 index 0000000..1bd22ce --- /dev/null +++ b/src/io/traces.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +namespace warthog::io +{ + + +void grid_trace::print_posthoc_header() +{ + if (*this) { + stream() << R"posthoc(version: 1.4.0 +views: + cell: + - $: rect + width: 1 + height: 1 + x: ${{$.x}} + y: ${{$.y}} + fill: ${{$.fill}} + clear: ${{ $.clear }} + main: + - $: cell + $if: ${{ $.type == 'source' }} + fill: green + clear: false + - $: cell + $if: ${{ $.type == 'destination' }} + fill: red + clear: false + - $: cell + $if: ${{ $.type == 'expand' }} + fill: blue + clear: close + - $: cell + $if: ${{ $.type == 'generate' }} + fill: orange + clear: true +pivot: + x: ${{ $.x + 0.5 }} + y: ${{ $.y + 0.5 }} + scale: 1 +events: +)posthoc"; + } +} + +void grid_trace::begin_search(int id, const search::search_problem_instance& pi) +{ + posthoc_listener::begin_search(id); + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(pi.start_, x, y); + stream() << std::format(" - {{ type: source, id: {}, x: {}, y: {} }}\n", + pi.start_.id, x, y + ); + grid_->to_unpadded_xy(pi.target_, x, y); + stream() << std::format(" - {{ type: destination, id: {}, x: {}, y: {} }}\n", + pi.target_.id, x, y + ); + } +} + +void +grid_trace::expand_node(const node& current) const +{ + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(current.get_id(), x, y); + stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, x, y, current.get_f(), current.get_g() + ); + } +} + +void +grid_trace::relax_node(const node& current) const +{ + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(current.get_id(), x, y); + stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, x, y, current.get_f(), current.get_g() + ); + } +} + + +void +grid_trace::close_node(const node& current) const +{ + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(current.get_id(), x, y); + stream() << std::format(" - {{ type: close, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, x, y, current.get_f(), current.get_g() + ); + } +} + + +void +grid_trace::generate_node(const node* parent, const node& child, cost_t, uint32_t) const +{ + if (*this) { + assert(grid_ != nullptr); + std::string pid; + if (parent != nullptr) { + pid = std::format(", pId: {}", parent->get_id().id); + } + uint32_t x, y; + grid_->to_unpadded_xy(child.get_id(), x, y); + stream() << std::format(" - {{ type: generate, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + child.get_id().id, pid, x, y, child.get_f(), child.get_g() + ); + } +} + +} // namespace warthog::io From f81c8adecd077070fdb2c63efa65c07e48c94dd2 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 5 Sep 2025 13:31:01 +1000 Subject: [PATCH 05/61] rename listener to observer --- include/warthog/io/{listener.h => observer.h} | 0 include/warthog/io/{posthoc_listener.h => posthoc_trace.h} | 0 include/warthog/io/{stream_listener.h => stream_observer.h} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename include/warthog/io/{listener.h => observer.h} (100%) rename include/warthog/io/{posthoc_listener.h => posthoc_trace.h} (100%) rename include/warthog/io/{stream_listener.h => stream_observer.h} (100%) diff --git a/include/warthog/io/listener.h b/include/warthog/io/observer.h similarity index 100% rename from include/warthog/io/listener.h rename to include/warthog/io/observer.h diff --git a/include/warthog/io/posthoc_listener.h b/include/warthog/io/posthoc_trace.h similarity index 100% rename from include/warthog/io/posthoc_listener.h rename to include/warthog/io/posthoc_trace.h diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_observer.h similarity index 100% rename from include/warthog/io/stream_listener.h rename to include/warthog/io/stream_observer.h From d439de10e2c72de896191bc3e2b668c587ab09ad Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 5 Sep 2025 14:45:42 +1000 Subject: [PATCH 06/61] rename src observers --- src/io/{listeners.cpp => observer.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/io/{listeners.cpp => observer.cpp} (100%) diff --git a/src/io/listeners.cpp b/src/io/observer.cpp similarity index 100% rename from src/io/listeners.cpp rename to src/io/observer.cpp From 8232fddb009b089be83527e4d28dbe17870ec8bd Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 11 Sep 2025 16:56:04 +1000 Subject: [PATCH 07/61] listener renamed to observer --- include/warthog/io/grid_trace.h | 6 +- include/warthog/io/observer.h | 57 ++++++++++--------- include/warthog/io/posthoc_trace.h | 33 +++++++---- include/warthog/io/stream_observer.h | 22 +++---- .../warthog/search/unidirectional_search.h | 14 ++--- src/CMakeLists.txt | 4 +- src/io/observer.cpp | 28 ++++----- src/io/traces.cpp | 2 +- 8 files changed, 89 insertions(+), 77 deletions(-) diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index a2c8a08..a95b7a7 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -7,7 +7,7 @@ // @created: 2025-08-07 // -#include "posthoc_listener.h" +#include "posthoc_trace.h" #include #include #include @@ -17,11 +17,11 @@ namespace warthog::io { /// @brief class that produces a posthoc trace for the gridmap domain -class grid_trace : public posthoc_listener +class grid_trace : public posthoc_trace { public: using node = search::search_node; - using posthoc_listener::posthoc_listener; + using posthoc_trace::posthoc_trace; void set_grid(domain::gridmap* grid) noexcept { diff --git a/include/warthog/io/observer.h b/include/warthog/io/observer.h index ffb0b18..f2e9e57 100644 --- a/include/warthog/io/observer.h +++ b/include/warthog/io/observer.h @@ -1,11 +1,17 @@ -#ifndef WARTHOG_IO_LISTENER_H -#define WARTHOG_IO_LISTENER_H +#ifndef WARTHOG_IO_OBSERVER_H +#define WARTHOG_IO_OBSERVER_H -// listener/stream_listener.h +// io/observer.h // -// The lister base class, has many invoker functions for set listen events +// The observer pattern defines methods to tightly-bind user provided observers to certain event patterns. +// Provide a tuple of observers, where some event is triggered will notifiy all observers with function of event name that is callable. // -// This class implements dummy listener with empty event handlers. +// These function names must be registered before use, common ones registered here. +// +// To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). +// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observer. +// This will run through each element in tuple (i) and call i.[function](args...) if able. +// If i.event([function],args...) is a valid callable, calls this function first, also tries i.event([function]). // // @author: Ryan Hechenberger // @created: 2025-08-06 @@ -13,48 +19,45 @@ #include -#define WARTHOG_LISTENER_HAS_FN(func_name) \ +#define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ template \ -concept listener_has_##func_name = requires(Listener L, Args&&... args) \ +concept observer_has_##func_name = requires(Listener L, Args&&... args) \ { \ { L.func_name(std::forward(args)...) }; \ }; -#define WARTHOG_LISTENER_FN(func_name) \ +#define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ template \ -void listener_##func_name(Listeners& L, Args&&... args) \ +void observer_##func_name(Listeners& L, Args&&... args) \ { \ if constexpr (I < std::tuple_size_v) { \ using T = std::tuple_element_t; \ - if constexpr (listener_has_##func_name ) { \ - /* call event first if present */ \ - if constexpr (listener_has_event ) std::get(L).event(#func_name); \ + constexpr bool has_event = observer_has_##func_name ; \ + if constexpr (observer_has_event) { std::get(L).event( #func_name , std::forward(args)... ); } \ + else if constexpr (observer_has_event) { std::get(L).event( #func_name ); } \ + if constexpr (has_event) { \ std::get(L).func_name(std::forward(args)...); \ - } else { \ - if constexpr (listener_has_missing_event ) \ - std::get(L).missing_event(#func_name); \ } \ listener_##func_name (L, std::forward(args)...); \ } \ } -#define WARTHOG_LISTENER_DEFINE(func_name) \ - WARTHOG_LISTENER_HAS_FN(func_name) \ - WARTHOG_LISTENER_FN(func_name) +#define WARTHOG_OBSERVER_DEFINE(func_name) \ + WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ + WARTHOG_OBSERVER_DEFINE_CALL(func_name) namespace warthog::io { // functions used by WARTHOG_LISTENER_FN -WARTHOG_LISTENER_HAS_FN(event) -WARTHOG_LISTENER_HAS_FN(missing_event) +WARTHOG_OBSERVER_DEFINE_HAS(event) -WARTHOG_LISTENER_DEFINE(begin_search) -WARTHOG_LISTENER_DEFINE(end_search) -WARTHOG_LISTENER_DEFINE(generate_node) -WARTHOG_LISTENER_DEFINE(expand_node) -WARTHOG_LISTENER_DEFINE(relax_node) -WARTHOG_LISTENER_DEFINE(close_node) +WARTHOG_OBSERVER_DEFINE(begin_search) +WARTHOG_OBSERVER_DEFINE(end_search) +WARTHOG_OBSERVER_DEFINE(generate_node) +WARTHOG_OBSERVER_DEFINE(expand_node) +WARTHOG_OBSERVER_DEFINE(relax_node) +WARTHOG_OBSERVER_DEFINE(close_node) } // namespace warthog::io -#endif // WARTHOG_IO_LISTENER_H +#endif // WARTHOG_IO_OBSERVER_H diff --git a/include/warthog/io/posthoc_trace.h b/include/warthog/io/posthoc_trace.h index 14447b3..04f8627 100644 --- a/include/warthog/io/posthoc_trace.h +++ b/include/warthog/io/posthoc_trace.h @@ -1,34 +1,43 @@ -#ifndef WARTHOG_IO_POSTHOC_LISTENER_H -#define WARTHOG_IO_POSTHOC_LISTENER_H +#ifndef WARTHOG_IO_POSTHOC_TRACE_H +#define WARTHOG_IO_POSTHOC_TRACE_H -// listener/posthoc_listener.h +// io/posthoc_trace.h +// +// stream_observer that outputs a trace for use with posthoc visuliser. +// See https://posthoc-app.pathfinding.ai/ // // @author: Ryan Hechenberger // @created: 2025-08-07 // -#include "stream_listener.h" +#include "stream_observer.h" namespace warthog::io { -/// @brief base posthoc listener class. -class posthoc_listener : public stream_listener +/// @brief base posthoc observer class. +/// +/// event begin_search and end_search will setup the trace to print only a specified. +/// Inherit to create new trace format by overriding print_posthoc_header for custom header. +/// Add own events to print posthoc event to stream() if (*this) holds true, +/// (*this) holds true iff id is on search_id between begin_search and end_search the first time only. +class posthoc_trace : public stream_observer { public: - using stream_listener::stream_listener; - - // will print the header if not already printed - void event(const char*); + using stream_observer::stream_observer; + // override print the header if not already printed virtual void print_posthoc_header(); + int search_id() const noexcept { return search_id_; } + void search_id(int sid) noexcept { search_id_ = sid; } + template void begin_search(int id, Args&&...) { do_trace_ = false; if (done_trace_) return; // do not repeat a trace - if (stream_listener::operator bool() && id == search_id_) { + if (stream_observer::operator bool() && id == search_id_) { do_trace_ = true; done_trace_ = true; print_posthoc_header(); @@ -53,4 +62,4 @@ class posthoc_listener : public stream_listener } // namespace warthog::io -#endif // WARTHOG_IO_POSTHOC_LISTENER_H +#endif // WARTHOG_IO_POSTHOC_TRACE_H diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index 809e9d8..9229b07 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -1,5 +1,5 @@ -#ifndef WARTHOG_IO_STREAM_LISTENER_H -#define WARTHOG_IO_STREAM_LISTENER_H +#ifndef WARTHOG_IO_STEAM_OBSERVER_H +#define WARTHOG_IO_STEAM_OBSERVER_H // listener/stream_listener.h // @@ -24,20 +24,20 @@ namespace warthog::io { -class stream_listener +class stream_observer { public: using shared_stream_t = std::shared_ptr; - stream_listener() = default; - stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); - stream_listener(std::ostream& stream); - stream_listener(const shared_stream_t& stream); - ~stream_listener(); + stream_observer() = default; + stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + stream_observer(std::ostream& stream); + stream_observer(const shared_stream_t& stream); + ~stream_observer(); void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); void stream(std::ostream& stream); void stream_share(const shared_stream_t& stream); - void stream_share(const stream_listener& stream); + void stream_share(const stream_observer& stream); void stream_stdout(); void stream_stderr(); void clear_stream(); @@ -52,8 +52,8 @@ class stream_listener shared_stream_t shared_stream_; }; -using void_listener = stream_listener; +using void_listener = stream_observer; } // namespace warthog::io -#endif // WARTHOG_IO_STREAM_LISTENER_H +#endif // WARTHOG_IO_STEAM_OBSERVER_H diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 3417095..25b2803 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -15,7 +15,7 @@ #include "search_parameters.h" #include "solution.h" #include "uds_traits.h" -#include +#include #include #include #include @@ -211,7 +211,7 @@ class unidirectional_search mytimer.start(); open_->clear(); - io::listener_begin_search(listeners_, static_cast(pi->instance_id_), *pi); + io::observer_begin_search(listeners_, static_cast(pi->instance_id_), *pi); // initialise the start node and push to OPEN { @@ -224,7 +224,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - io::listener_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); + io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); user(pi->verbose_, pi); trace(pi->verbose_, "Start node:", *start); update_ub(start, sol, pi); @@ -248,7 +248,7 @@ class unidirectional_search current->set_expanded(true); // NB: set before generating succ sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); - io::listener_expand_node(listeners_, *current); + io::observer_expand_node(listeners_, *current); trace(pi->verbose_, "Expanding:", *current); // Generate successors of the current node @@ -270,7 +270,7 @@ class unidirectional_search open_->push(n); trace(pi->verbose_, "Generate:", *n); update_ub(current, sol, pi); - io::listener_generate_node(listeners_, current, *n, gval, i); + io::observer_generate_node(listeners_, current, *n, gval, i); continue; } } @@ -283,7 +283,7 @@ class unidirectional_search < sol->sum_of_edge_costs_) { n->relax(gval, current->get_id()); - io::listener_relax_node(listeners_, *n); + io::observer_relax_node(listeners_, *n); if(open_->contains(n)) { @@ -310,7 +310,7 @@ class unidirectional_search // patched until AC FC RP reworked sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); } - io::listener_close_node(listeners_, *current); + io::observer_close_node(listeners_, *current); } sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5abf0b7..9420f49 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,8 @@ geometry/geography.cpp geometry/geom.cpp io/grid.cpp -io/listeners.cpp +io/log.cpp +io/observer.cpp io/traces.cpp memory/node_pool.cpp @@ -28,5 +29,4 @@ util/gm_parser.cpp util/helpers.cpp util/scenario_manager.cpp util/timer.cpp - ) diff --git a/src/io/observer.cpp b/src/io/observer.cpp index 18b0456..fd8567f 100644 --- a/src/io/observer.cpp +++ b/src/io/observer.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -7,59 +7,59 @@ namespace warthog::io { -stream_listener::stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode) +stream_observer::stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); stream_ = shared_stream_.get(); } -stream_listener::stream_listener(std::ostream& stream) +stream_observer::stream_observer(std::ostream& stream) { stream_ = &stream; } -stream_listener::stream_listener(const shared_stream_t& stream) +stream_observer::stream_observer(const shared_stream_t& stream) { shared_stream_ = stream; stream_ = shared_stream_.get(); } -stream_listener::~stream_listener() = default; +stream_observer::~stream_observer() = default; -void stream_listener::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void stream_observer::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); stream_ = shared_stream_.get(); } -void stream_listener::stream(std::ostream& stream) +void stream_observer::stream(std::ostream& stream) { stream_ = &stream; shared_stream_ = nullptr; } -void stream_listener::stream_share(const shared_stream_t& stream) +void stream_observer::stream_share(const shared_stream_t& stream) { shared_stream_ = stream; stream_ = shared_stream_.get(); } -void stream_listener::stream_share(const stream_listener& stream) +void stream_observer::stream_share(const stream_observer& stream) { shared_stream_ = stream.shared_stream_; stream_ = stream.stream_; } -void stream_listener::stream_stdout() +void stream_observer::stream_stdout() { shared_stream_ = nullptr; stream_ = &std::cout; } -void stream_listener::stream_stderr() +void stream_observer::stream_stderr() { shared_stream_ = nullptr; stream_ = &std::cerr; } -void stream_listener::clear_stream() +void stream_observer::clear_stream() { shared_stream_ = nullptr; stream_ = nullptr; } -void posthoc_listener::print_posthoc_header() +void posthoc_trace::print_posthoc_header() { if (*this) { stream() << R"posthoc(version: 1.4.0 diff --git a/src/io/traces.cpp b/src/io/traces.cpp index 1bd22ce..fb58898 100644 --- a/src/io/traces.cpp +++ b/src/io/traces.cpp @@ -47,7 +47,7 @@ void grid_trace::print_posthoc_header() void grid_trace::begin_search(int id, const search::search_problem_instance& pi) { - posthoc_listener::begin_search(id); + posthoc_trace::begin_search(id); if (*this) { assert(grid_ != nullptr); uint32_t x, y; From 0c755f7a5f98e38213de69e1086e65ca5e2d027a Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 11 Sep 2025 16:56:15 +1000 Subject: [PATCH 08/61] added first part of log --- include/warthog/io/log.h | 92 ++++++++++++++++++++++++++++++++++++++ src/io/log.cpp | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 include/warthog/io/log.h create mode 100644 src/io/log.cpp diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h new file mode 100644 index 0000000..391a6ae --- /dev/null +++ b/include/warthog/io/log.h @@ -0,0 +1,92 @@ +#ifndef WARTHOG_IO_OBSERVER_H +#define WARTHOG_IO_OBSERVER_H + +// io/log.h +// +// Logger class. +// +// @author: Ryan Hechenberger +// @created: 2025-09-09 +// + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WARTHOG_LOG +#define WARTHOG_DEFAULT_LOG_LEVEL WARTHOG_LOG +#elif !defined(NDEBUG) +#define WARTHOG_DEFAULT_LOG_LEVEL 1 +#else +#define WARTHOG_DEFAULT_LOG_LEVEL 4 +#endif + +#define WARTHOG_TIME_FORMAT "%FT%T%Z" + +namespace warthog::io +{ + + +enum class log_level +{ + TRACE = 0, + DEBUG = 1, + INFORMATION = 2, + WARNING = 3, + ERROR = 4, + CRITICAL = 5, + NONE = 6 +}; + +struct log_sink +{ + using call_type = void (void*, std::string_view msg); + void* data = nullptr; + std::array(log_level::NONE)> call = {}; + + void log(log_level level, std::string_view msg) + { + if (static_cast(level) < call.size() && (std::bit_cast(data) | std::bit_cast(call[static_cast(level)])) != 0) + { + (*call[static_cast(level)])(data, msg); + } + } + + const log_sink& sink() const noexcept { return *this; } +}; + +class log_stream_sink : protected log_sink +{ +public: + log_stream_sink(); + log_stream_sink(bool err); + log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + log_stream_sink(std::ostream& stream); + ~log_stream_sink() = default; + + void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + void open(std::ostream& stream); + void open_stderr(); + + static void write_trace(void*, std::string_view msg); + static void write_debug(void*, std::string_view msg); + static void write_information(void*, std::string_view msg); + static void write_warning(void*, std::string_view msg); + static void write_error(void*, std::string_view msg); + static void write_critical(void*, std::string_view msg); + + using log_sink::sink; + +protected: + std::ostream* log_stream_ = nullptr; + std::unique_ptr owned_file_; + std::mutex lock_; +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_OBSERVER_H diff --git a/src/io/log.cpp b/src/io/log.cpp new file mode 100644 index 0000000..9b849c9 --- /dev/null +++ b/src/io/log.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +namespace warthog::io +{ + +log_stream_sink::log_stream_sink() : log_sink{nullptr, + {{&log_stream_sink::write_trace,&log_stream_sink::write_debug, + &log_stream_sink::write_information,&log_stream_sink::write_warning, + &log_stream_sink::write_error,&log_stream_sink::write_critical}}} +{ } + +log_stream_sink::log_stream_sink(bool err) +{ + if (err) { + data = this; + log_stream_ = &std::cerr; + } +} +log_stream_sink::log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + data = this; + owned_file_ = std::make_unique(filename, mode); + log_stream_ = owned_file_.get(); +} +log_stream_sink::log_stream_sink(std::ostream& stream) +{ + data = this; + log_stream_ = &stream; +} + +void log_stream_sink::open(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + data = this; + owned_file_ = std::make_unique(filename, mode); + log_stream_ = owned_file_.get(); +} +void log_stream_sink::open(std::ostream& stream) +{ + data = this; + owned_file_.release(); + log_stream_ = &stream; +} +void log_stream_sink::open_stderr() +{ + data = this; + owned_file_.release(); + log_stream_ = &std::cerr; +} + +void log_stream_sink::write_trace(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_debug(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_information(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_warning(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_error(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_critical(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}", std::chrono::utc_clock::now(), msg); +} + +} // namespace warthog::io From 7660b0d84ed4a78f004b7860f20c3c0bc89a8a4f Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 12 Sep 2025 15:39:27 +1000 Subject: [PATCH 09/61] log in debug phase --- apps/warthog.cpp | 3 + include/warthog/io/log.h | 147 +++++++++++++++++++++++++++++++++++---- src/io/log.cpp | 87 +++++++++++++++++++---- 3 files changed, 210 insertions(+), 27 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index a5d4ef1..9269481 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "cfg.h" #include @@ -111,6 +112,8 @@ run_experiments( warthog::util::scenario_manager& scenmgr, bool verbose, bool checkopt, std::ostream& out) { + WARTHOG_DEBUG(warthog::io::glog(), alg_name); + WARTHOG_WARNING_FMT(warthog::io::glog(), "Search with {} verbose {} and ceckopt {}", alg_name, verbose, checkopt); warthog::search::search_parameters par; warthog::search::solution sol; auto* expander = algo.get_expander(); diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 391a6ae..919ef16 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -1,5 +1,5 @@ -#ifndef WARTHOG_IO_OBSERVER_H -#define WARTHOG_IO_OBSERVER_H +#ifndef WARTHOG_IO_LOG_H +#define WARTHOG_IO_LOG_H // io/log.h // @@ -16,21 +16,23 @@ #include #include #include +#include +#include +#include #ifdef WARTHOG_LOG #define WARTHOG_DEFAULT_LOG_LEVEL WARTHOG_LOG #elif !defined(NDEBUG) #define WARTHOG_DEFAULT_LOG_LEVEL 1 #else -#define WARTHOG_DEFAULT_LOG_LEVEL 4 +#define WARTHOG_DEFAULT_LOG_LEVEL 2 #endif -#define WARTHOG_TIME_FORMAT "%FT%T%Z" +#define WARTHOG_TIME_FORMAT "%FT%TZ" namespace warthog::io { - enum class log_level { TRACE = 0, @@ -48,24 +50,52 @@ struct log_sink void* data = nullptr; std::array(log_level::NONE)> call = {}; - void log(log_level level, std::string_view msg) + constexpr void log(log_level level, std::string_view msg) const { - if (static_cast(level) < call.size() && (std::bit_cast(data) | std::bit_cast(call[static_cast(level)])) != 0) + if (can_log(level)) { (*call[static_cast(level)])(data, msg); } } + constexpr bool can_log(log_level level) const noexcept + { + return static_cast(level) < call.size() && data != nullptr && call[static_cast(level)] != nullptr; + } - const log_sink& sink() const noexcept { return *this; } + constexpr const log_sink& sink() const noexcept { return *this; } }; -class log_stream_sink : protected log_sink +/// sink to cout|cerr +/// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. +/// User provided file stream is not thread safe. +struct log_std_sink : log_sink +{ + // cerr = true will sink to cerr, false to cout + log_std_sink(); + log_std_sink(bool cerr = true); + log_std_sink(std::ostream* stream); + static void write_trace(void*, std::string_view msg); + static void write_debug(void*, std::string_view msg); + static void write_information(void*, std::string_view msg); + static void write_warning(void*, std::string_view msg); + static void write_error(void*, std::string_view msg); + static void write_critical(void*, std::string_view msg); +}; + +template +concept LogSink = requires (Sink S, log_level level, std::string_view msg) +{ + S.log(level, msg); + { S.can_log(level) } -> std::same_as; + { std::as_const(S).sink() } -> std::convertible_to; +}; + +class log_stream_sink : public log_sink { public: log_stream_sink(); - log_stream_sink(bool err); log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); - log_stream_sink(std::ostream& stream); + log_stream_sink(std::ostream* stream); ~log_stream_sink() = default; void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); @@ -79,6 +109,8 @@ class log_stream_sink : protected log_sink static void write_error(void*, std::string_view msg); static void write_critical(void*, std::string_view msg); + using log_sink::log; + using log_sink::can_log; using log_sink::sink; protected: @@ -86,7 +118,98 @@ class log_stream_sink : protected log_sink std::unique_ptr owned_file_; std::mutex lock_; }; +static_assert(LogSink, "log_stream_sink must be a log_sink."); + +template (WARTHOG_DEFAULT_LOG_LEVEL)> +struct logger : log_sink +{ + constexpr logger() noexcept = default; + constexpr logger(log_sink sink) : log_sink(sink) + { + for (auto& c : call | std::views::take(static_cast(MinLevel))) { + c = nullptr; + } + } + + constexpr logger& operator=(const logger&) noexcept = default; + constexpr logger& operator=(log_sink sink) noexcept + { + for (auto& c : call | std::views::take(static_cast(MinLevel))) { + c = nullptr; + } + return *this; + } + + constexpr void set_min_level(log_level level) noexcept + { + assert(static_cast(level) <= static_cast(log_level::NONE)); + for (auto& c : call | std::views::take(static_cast(level))) { + c = nullptr; + } + } + + constexpr static log_level min_level = MinLevel; + template + constexpr static bool supports_level = MinLevel != log_level::NONE && static_cast(L) >= static_cast(MinLevel); +}; + +namespace details { +template +struct is_logger : std::false_type +{ }; +template +struct is_logger> : std::true_type +{ }; +}; + +template +concept Logger = details::is_logger::value; +template +concept LoggerLevel = Logger && requires { + requires Log::template supports_level; +}; + +#define WARTHOG_LOG(lg,level,msg) lg.log(msg) + +#define WARTHOG_LOG_LEVEL_(lg,level,msg) \ +{if constexpr (::warthog::io::Logger) { \ + if constexpr (::warthog::io::LoggerLevel) { \ + lg.log(level, msg); \ + } \ +} else { \ + lg.log(level, msg); \ +}} + +#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::TRACE,msg) +#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::DEBUG,msg) +#define WARTHOG_INFORMATION(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::INFORMATION,msg) +#define WARTHOG_WARNING(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::WARNING,msg) +#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::ERROR,msg) +#define WARTHOG_CRITICAL(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::CRITICAL,msg) + +#define WARTHOG_LOG_LEVEL_FMT_(lg,level,...) \ +{if constexpr (::warthog::io::Logger) { \ + if constexpr (::warthog::io::LoggerLevel) { \ + lg.log(level, std::format(__VA_ARGS__)); \ + } \ +} else { \ + lg.log(level, std::format(__VA_ARGS__)); \ +}} + +#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::TRACE,__VA_ARGS__) +#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) +#define WARTHOG_INFORMATION_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) +#define WARTHOG_WARNING_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::WARNING,__VA_ARGS__) +#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::ERROR,__VA_ARGS__) +#define WARTHOG_CRITICAL_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) + +// global logger + +using global_logger = logger<>; +global_logger& glog(); +const log_sink& glogs(); +void set_glog(log_sink log); } // namespace warthog::io -#endif // WARTHOG_IO_OBSERVER_H +#endif // WARTHOG_IO_LOG_H diff --git a/src/io/log.cpp b/src/io/log.cpp index 9b849c9..255beab 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -12,23 +12,16 @@ log_stream_sink::log_stream_sink() : log_sink{nullptr, &log_stream_sink::write_error,&log_stream_sink::write_critical}}} { } -log_stream_sink::log_stream_sink(bool err) -{ - if (err) { - data = this; - log_stream_ = &std::cerr; - } -} log_stream_sink::log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode) { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -log_stream_sink::log_stream_sink(std::ostream& stream) +log_stream_sink::log_stream_sink(std::ostream* stream) { data = this; - log_stream_ = &stream; + log_stream_ = stream; } void log_stream_sink::open(const std::filesystem::path& filename, std::ios_base::openmode mode) @@ -50,47 +43,111 @@ void log_stream_sink::open_stderr() log_stream_ = &std::cerr; } +// log_stream_sink + +log_std_sink::log_std_sink() : log_std_sink(nullptr) +{ } +log_std_sink::log_std_sink(bool cerr) : log_std_sink(cerr ? &std::cerr : &std::cout) +{ } +log_std_sink::log_std_sink(std::ostream* stream) : log_sink{ + stream, + {{&log_std_sink::write_trace,&log_std_sink::write_debug, + &log_std_sink::write_information,&log_std_sink::write_warning, + &log_std_sink::write_error,&log_std_sink::write_critical}}} +{ } + +void log_std_sink::write_trace(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_debug(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_information(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_warning(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_error(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_critical(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} + void log_stream_sink::write_trace(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_debug(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_information(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_warning(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_error(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_critical(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} + +global_logger global_logger_(log_std_sink(true).sink()); +global_logger& glog() +{ + return global_logger_; +} +const log_sink& glogs() +{ + return global_logger_.sink(); +} +void set_glog(log_sink log) +{ + global_logger_ = log; } } // namespace warthog::io From 7a3fb8775a8662ec3fb8200c6ea6bb3e58c60cb4 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 18 Sep 2025 10:07:09 +1000 Subject: [PATCH 10/61] global logger works with uds --- apps/warthog.cpp | 3 - include/warthog/io/log.h | 120 +++++++++++++----- include/warthog/search/search_node.h | 20 +++ .../warthog/search/unidirectional_search.h | 12 +- include/warthog/util/vec_io.h | 6 +- src/io/log.cpp | 72 +++++------ 6 files changed, 154 insertions(+), 79 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 9269481..a5d4ef1 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "cfg.h" #include @@ -112,8 +111,6 @@ run_experiments( warthog::util::scenario_manager& scenmgr, bool verbose, bool checkopt, std::ostream& out) { - WARTHOG_DEBUG(warthog::io::glog(), alg_name); - WARTHOG_WARNING_FMT(warthog::io::glog(), "Search with {} verbose {} and ceckopt {}", alg_name, verbose, checkopt); warthog::search::search_parameters par; warthog::search::solution sol; auto* expander = algo.get_expander(); diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 919ef16..0b51ed1 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -68,12 +68,12 @@ struct log_sink /// sink to cout|cerr /// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. /// User provided file stream is not thread safe. -struct log_std_sink : log_sink +struct log_sink_std : log_sink { // cerr = true will sink to cerr, false to cout - log_std_sink(); - log_std_sink(bool cerr = true); - log_std_sink(std::ostream* stream); + log_sink_std(); + log_sink_std(bool cerr = true); + log_sink_std(std::ostream* stream); static void write_trace(void*, std::string_view msg); static void write_debug(void*, std::string_view msg); static void write_information(void*, std::string_view msg); @@ -90,13 +90,13 @@ concept LogSink = requires (Sink S, log_level level, std::string_view msg) { std::as_const(S).sink() } -> std::convertible_to; }; -class log_stream_sink : public log_sink +class log_sink_stream : public log_sink { public: - log_stream_sink(); - log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); - log_stream_sink(std::ostream* stream); - ~log_stream_sink() = default; + log_sink_stream(); + log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + log_sink_stream(std::ostream* stream); + ~log_sink_stream() = default; void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); void open(std::ostream& stream); @@ -118,7 +118,7 @@ class log_stream_sink : public log_sink std::unique_ptr owned_file_; std::mutex lock_; }; -static_assert(LogSink, "log_stream_sink must be a log_sink."); +static_assert(LogSink, "log_stream_sink must be a log_sink."); template (WARTHOG_DEFAULT_LOG_LEVEL)> struct logger : log_sink @@ -171,45 +171,105 @@ concept LoggerLevel = Logger && requires { #define WARTHOG_LOG(lg,level,msg) lg.log(msg) -#define WARTHOG_LOG_LEVEL_(lg,level,msg) \ +#define WARTHOG_LOG_LEVEL_(cond,lg,level,msg) \ {if constexpr (::warthog::io::Logger) { \ if constexpr (::warthog::io::LoggerLevel) { \ - lg.log(level, msg); \ + if (cond) \ + lg.log(level, msg); \ } \ } else { \ - lg.log(level, msg); \ + if (cond) \ + lg.log(level, msg); \ }} -#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::TRACE,msg) -#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::DEBUG,msg) -#define WARTHOG_INFORMATION(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::INFORMATION,msg) -#define WARTHOG_WARNING(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::WARNING,msg) -#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::ERROR,msg) -#define WARTHOG_CRITICAL(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::CRITICAL,msg) +#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::TRACE,msg) +#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::DEBUG,msg) +#define WARTHOG_INFO(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::INFORMATION,msg) +#define WARTHOG_WARN(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::WARNING,msg) +#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::ERROR,msg) +#define WARTHOG_CRIT(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::CRITICAL,msg) -#define WARTHOG_LOG_LEVEL_FMT_(lg,level,...) \ +#define WARTHOG_TRACE_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::TRACE,msg) +#define WARTHOG_DEBUG_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::DEBUG,msg) +#define WARTHOG_INFO_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::INFORMATION,msg) +#define WARTHOG_WARN_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::WARNING,msg) +#define WARTHOG_ERROR_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::ERROR,msg) +#define WARTHOG_CRIT_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::CRITICAL,msg) + +#define WARTHOG_LOG_LEVEL_FMT_(cond,lg,level,...) \ {if constexpr (::warthog::io::Logger) { \ if constexpr (::warthog::io::LoggerLevel) { \ - lg.log(level, std::format(__VA_ARGS__)); \ + if (cond) \ + lg.log(level, std::format(__VA_ARGS__)); \ } \ } else { \ - lg.log(level, std::format(__VA_ARGS__)); \ + if (cond) \ + lg.log(level, std::format(__VA_ARGS__)); \ }} -#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::TRACE,__VA_ARGS__) -#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) -#define WARTHOG_INFORMATION_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) -#define WARTHOG_WARNING_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::WARNING,__VA_ARGS__) -#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::ERROR,__VA_ARGS__) -#define WARTHOG_CRITICAL_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) +#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) +#define WARTHOG_INFO_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) +#define WARTHOG_WARN_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) +#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) +#define WARTHOG_CRIT_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) + +#define WARTHOG_TRACE_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) +#define WARTHOG_DEBUG_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) +#define WARTHOG_INFO_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) +#define WARTHOG_WARN_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) +#define WARTHOG_ERROR_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) +#define WARTHOG_CRIT_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) // global logger -using global_logger = logger<>; -global_logger& glog(); +using global_logger_type = logger<>; +global_logger_type& glog(); const log_sink& glogs(); void set_glog(log_sink log); +template +struct global_logger : logger +{ + using logger = typename global_logger::logger; + constexpr global_logger() : logger(glog().sink()) + { } + + constexpr global_logger& operator=(const global_logger&) noexcept = default; + + void resync_global() + { + static_cast(*this) = glog().sink(); + } +}; + +// defines to the global logger + +#define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(),msg) +#define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(),msg) +#define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(),msg) +#define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(),msg) +#define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(),msg) +#define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(),msg) +#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(::warthog::io::glog(),msg) +#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(::warthog::io::glog(),msg) +#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(::warthog::io::glog(),msg) +#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(::warthog::io::glog(),msg) +#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(::warthog::io::glog(),msg) +#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(::warthog::io::glog(),msg) +#define WARTHOG_GTRACE_FMT(...) WARTHOG_TRACE_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GDEBUG_FMT(...) WARTHOG_DEBUG_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GINFO_FMT(...) WARTHOG_INFO_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GWARN_FMT(...) WARTHOG_WARN_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GERROR_FMT(...) WARTHOG_ERROR_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GCRIT_FMT(...) WARTHOG_CRIT_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GTRACE_FMT_IF(cond,...) WARTHOG_TRACE_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GDEBUG_FMT_IF(cond,...) WARTHOG_DEBUG_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GINFO_FMT_IF(cond,...) WARTHOG_INFO_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GWARN_FMT_IF(cond,...) WARTHOG_WARN_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GERROR_FMT_IF(cond,...) WARTHOG_ERROR_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GCRIT_FMT_IF(cond,...) WARTHOG_CRIT_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) + } // namespace warthog::io #endif // WARTHOG_IO_LOG_H diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index a0cddf9..7ac705f 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -9,6 +9,7 @@ #include #include +#include #include @@ -273,4 +274,23 @@ struct cmp_less_search_node_f_only std::ostream& operator<<(std::ostream& str, const warthog::search::search_node& sn); +template <> +struct std::formatter<::warthog::search::search_node, char> +{ + template + constexpr auto parse(ParseContext& ctx) const + { + return ctx.begin(); + } + + template + FmtContext::iterator format(const ::warthog::search::search_node& s, FmtContext& ctx) const + { + return std::format_to(ctx.out(), "search_node id:{} p_id:{} g:{} f:{} ub:{} expanded:{} search_number:{}", + s.get_id().id, s.get_parent().id, + s.get_g(), s.get_f(), s.get_ub(), + s.get_expanded(), s.get_search_number()); + } +}; + #endif // WARTHOG_SEARCH_SEARCH_NODE_H diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 25b2803..3418490 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -226,7 +226,7 @@ class unidirectional_search open_->push(start); io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); user(pi->verbose_, pi); - trace(pi->verbose_, "Start node:", *start); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Start node: {}", *start); update_ub(start, sol, pi); } @@ -249,7 +249,7 @@ class unidirectional_search sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); io::observer_expand_node(listeners_, *current); - trace(pi->verbose_, "Expanding:", *current); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Expanding: {}", *current); // Generate successors of the current node search_node* n = nullptr; @@ -268,7 +268,7 @@ class unidirectional_search if(n->get_f() <= sol->sum_of_edge_costs_) { open_->push(n); - trace(pi->verbose_, "Generate:", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Generate: {}", *n); update_ub(current, sol, pi); io::observer_generate_node(listeners_, current, *n, gval, i); continue; @@ -288,7 +288,7 @@ class unidirectional_search if(open_->contains(n)) { open_->decrease_key(n); - trace(pi->verbose_, "Updating;", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Updating: {}", *n); update_ub(current, sol, pi); continue; } @@ -296,14 +296,14 @@ class unidirectional_search if(reopen()) { open_->push(n); - trace(pi->verbose_, "Reopen;", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Reopen: {}", *n); update_ub(current, sol, pi); sol->met_.nodes_reopen_++; continue; } } } - trace(pi->verbose_, "Dominated;", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Dominated: {}", *n); } if constexpr(FC == feasibility_criteria::until_cutoff) { diff --git a/include/warthog/util/vec_io.h b/include/warthog/util/vec_io.h index 6d82c92..8f0e24c 100644 --- a/include/warthog/util/vec_io.h +++ b/include/warthog/util/vec_io.h @@ -1,7 +1,7 @@ #ifndef WARTHOG_UTIL_VEC_IO_H #define WARTHOG_UTIL_VEC_IO_H -#include "log.h" +#include #include #include #include @@ -95,9 +95,7 @@ load_vector(std::FILE* file) size_t stuffRead = std::fread(&v[0], sizeof(T), s, file); if((int)stuffRead != s) { - error( - "we were expecting to read ", s, " but we read", stuffRead, - "elements instead"); + WARTHOG_GERROR_FMT("we were expecting to read {} but we read {} elements instead", s, stuffRead); throw std::runtime_error("std::fread failed"); } diff --git a/src/io/log.cpp b/src/io/log.cpp index 255beab..3326041 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -6,138 +6,138 @@ namespace warthog::io { -log_stream_sink::log_stream_sink() : log_sink{nullptr, - {{&log_stream_sink::write_trace,&log_stream_sink::write_debug, - &log_stream_sink::write_information,&log_stream_sink::write_warning, - &log_stream_sink::write_error,&log_stream_sink::write_critical}}} +log_sink_stream::log_sink_stream() : log_sink{nullptr, + {{&log_sink_stream::write_trace,&log_sink_stream::write_debug, + &log_sink_stream::write_information,&log_sink_stream::write_warning, + &log_sink_stream::write_error,&log_sink_stream::write_critical}}} { } -log_stream_sink::log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode) +log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -log_stream_sink::log_stream_sink(std::ostream* stream) +log_sink_stream::log_sink_stream(std::ostream* stream) { data = this; log_stream_ = stream; } -void log_stream_sink::open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void log_sink_stream::open(const std::filesystem::path& filename, std::ios_base::openmode mode) { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -void log_stream_sink::open(std::ostream& stream) +void log_sink_stream::open(std::ostream& stream) { data = this; owned_file_.release(); log_stream_ = &stream; } -void log_stream_sink::open_stderr() +void log_sink_stream::open_stderr() { data = this; owned_file_.release(); log_stream_ = &std::cerr; } -// log_stream_sink +// log_sink_stream -log_std_sink::log_std_sink() : log_std_sink(nullptr) +log_sink_std::log_sink_std() : log_sink_std(nullptr) { } -log_std_sink::log_std_sink(bool cerr) : log_std_sink(cerr ? &std::cerr : &std::cout) +log_sink_std::log_sink_std(bool cerr) : log_sink_std(cerr ? &std::cerr : &std::cout) { } -log_std_sink::log_std_sink(std::ostream* stream) : log_sink{ +log_sink_std::log_sink_std(std::ostream* stream) : log_sink{ stream, - {{&log_std_sink::write_trace,&log_std_sink::write_debug, - &log_std_sink::write_information,&log_std_sink::write_warning, - &log_std_sink::write_error,&log_std_sink::write_critical}}} + {{&log_sink_std::write_trace,&log_sink_std::write_debug, + &log_sink_std::write_information,&log_sink_std::write_warning, + &log_sink_std::write_error,&log_sink_std::write_critical}}} { } -void log_std_sink::write_trace(void* logger, std::string_view msg) +void log_sink_std::write_trace(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_debug(void* logger, std::string_view msg) +void log_sink_std::write_debug(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_information(void* logger, std::string_view msg) +void log_sink_std::write_information(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_warning(void* logger, std::string_view msg) +void log_sink_std::write_warning(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_error(void* logger, std::string_view msg) +void log_sink_std::write_error(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_critical(void* logger, std::string_view msg) +void log_sink_std::write_critical(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_trace(void* logger, std::string_view msg) +void log_sink_stream::write_trace(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_debug(void* logger, std::string_view msg) +void log_sink_stream::write_debug(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_information(void* logger, std::string_view msg) +void log_sink_stream::write_information(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_warning(void* logger, std::string_view msg) +void log_sink_stream::write_warning(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_error(void* logger, std::string_view msg) +void log_sink_stream::write_error(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_critical(void* logger, std::string_view msg) +void log_sink_stream::write_critical(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -global_logger global_logger_(log_std_sink(true).sink()); -global_logger& glog() +global_logger_type global_logger_(log_sink_std(true).sink()); +global_logger_type& glog() { return global_logger_; } From 8dd72e449b9d574355b35e54cdcedbca66d19937 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 18 Sep 2025 11:00:03 +1000 Subject: [PATCH 11/61] update time to local time --- include/warthog/io/log.h | 3 ++- src/io/log.cpp | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 0b51ed1..4942bd9 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -28,7 +28,8 @@ #define WARTHOG_DEFAULT_LOG_LEVEL 2 #endif -#define WARTHOG_TIME_FORMAT "%FT%TZ" +#define WARTHOG_LOG_TIME_FORMAT "%F %T" +#define WARTHOG_LOG_NOW (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) namespace warthog::io { diff --git a/src/io/log.cpp b/src/io/log.cpp index 3326041..cae584c 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -60,37 +60,37 @@ void log_sink_std::write_trace(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_debug(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_information(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_warning(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_error(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_critical(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_trace(void* logger, std::string_view msg) @@ -98,42 +98,42 @@ void log_sink_stream::write_trace(void* logger, std::string_view msg) log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_debug(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_information(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_warning(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_error(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_critical(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } global_logger_type global_logger_(log_sink_std(true).sink()); From 7eb6f450d031e2949ac3dbe5dd6d648b9635d890 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 1 Oct 2025 08:28:00 +1000 Subject: [PATCH 12/61] update log interface --- src/io/log.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/log.cpp b/src/io/log.cpp index cae584c..331d61b 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -12,13 +12,13 @@ log_sink_stream::log_sink_stream() : log_sink{nullptr, &log_sink_stream::write_error,&log_sink_stream::write_critical}}} { } -log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) +log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) : log_sink_stream() { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -log_sink_stream::log_sink_stream(std::ostream* stream) +log_sink_stream::log_sink_stream(std::ostream* stream) : log_sink_stream() { data = this; log_stream_ = stream; From 798de27177e524f73ad67cce8ca1370d4890c46f Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 1 Oct 2025 13:05:42 +1000 Subject: [PATCH 13/61] ported to new logger --- include/warthog/io/log.h | 29 ++- include/warthog/search/problem_instance.h | 25 +- include/warthog/search/search_metrics.h | 6 +- include/warthog/search/uds_traits.h | 14 +- .../warthog/search/unidirectional_search.h | 31 +-- include/warthog/util/log.h | 237 ------------------ src/io/log.cpp | 38 +-- src/search/problem_instance.cpp | 9 +- src/search/search_metrics.cpp | 4 + src/util/file_utils.cpp | 1 - 10 files changed, 77 insertions(+), 317 deletions(-) delete mode 100644 include/warthog/util/log.h diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 4942bd9..fea3251 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -66,6 +66,14 @@ struct log_sink constexpr const log_sink& sink() const noexcept { return *this; } }; +template +concept LogSink = requires (Sink S, log_level level, std::string_view msg) +{ + S.log(level, msg); + { S.can_log(level) } -> std::same_as; + { std::as_const(S).sink() } -> std::convertible_to; +}; + /// sink to cout|cerr /// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. /// User provided file stream is not thread safe. @@ -74,7 +82,6 @@ struct log_sink_std : log_sink // cerr = true will sink to cerr, false to cout log_sink_std(); log_sink_std(bool cerr = true); - log_sink_std(std::ostream* stream); static void write_trace(void*, std::string_view msg); static void write_debug(void*, std::string_view msg); static void write_information(void*, std::string_view msg); @@ -83,14 +90,6 @@ struct log_sink_std : log_sink static void write_critical(void*, std::string_view msg); }; -template -concept LogSink = requires (Sink S, log_level level, std::string_view msg) -{ - S.log(level, msg); - { S.can_log(level) } -> std::same_as; - { std::as_const(S).sink() } -> std::convertible_to; -}; - class log_sink_stream : public log_sink { public: @@ -252,12 +251,12 @@ struct global_logger : logger #define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(),msg) #define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(),msg) #define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(),msg) -#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(::warthog::io::glog(),msg) -#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(::warthog::io::glog(),msg) -#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(::warthog::io::glog(),msg) -#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(::warthog::io::glog(),msg) -#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(::warthog::io::glog(),msg) -#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(::warthog::io::glog(),msg) +#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(cond,::warthog::io::glog(),msg) #define WARTHOG_GTRACE_FMT(...) WARTHOG_TRACE_FMT(::warthog::io::glog(),__VA_ARGS__) #define WARTHOG_GDEBUG_FMT(...) WARTHOG_DEBUG_FMT(::warthog::io::glog(),__VA_ARGS__) #define WARTHOG_GINFO_FMT(...) WARTHOG_INFO_FMT(::warthog::io::glog(),__VA_ARGS__) diff --git a/include/warthog/search/problem_instance.h b/include/warthog/search/problem_instance.h index 9a7a271..eb16556 100644 --- a/include/warthog/search/problem_instance.h +++ b/include/warthog/search/problem_instance.h @@ -85,12 +85,29 @@ convert_problem_instance_to_search(const problem_instance& pi, Domain& d) start, target, pi.instance_id_, pi.verbose_, pi.extra_params_}; } -} // namespace warthog::search - std::ostream& -operator<<(std::ostream& str, const warthog::search::problem_instance& pi); +operator<<(std::ostream& str, const problem_instance& pi); std::ostream& operator<<( - std::ostream& str, const warthog::search::search_problem_instance& pi); + std::ostream& str, const search_problem_instance& pi); + +} // namespace warthog::search + +template<::warthog::Identity STATE> +struct std::formatter<::warthog::search::problem_instance_base, char> +{ + template + constexpr auto parse(ParseContext& ctx) const + { + return ctx.begin(); + } + + template + FmtContext::iterator format(const ::warthog::search::problem_instance_base& pi, FmtContext& ctx) const + { + return std::format_to(ctx.out(), "problem instance[{}]; start:{} target:{} search_id:{}", + typeid(typename STATE::tag).name(), pi.start_.id, pi.target_.id, pi.instance_id_); + } +}; #endif // WARTHOG_SEARCH_PROBLEM_INSTANCE_H diff --git a/include/warthog/search/search_metrics.h b/include/warthog/search/search_metrics.h index d54108b..76b8a43 100644 --- a/include/warthog/search/search_metrics.h +++ b/include/warthog/search/search_metrics.h @@ -62,9 +62,9 @@ struct search_metrics // warthog::search_metrics& met); }; -} // namespace warthog::search - std::ostream& -operator<<(std::ostream& str, const warthog::search::search_metrics& met); +operator<<(std::ostream& str, const search_metrics& met); + +} // namespace warthog::search #endif // WARTHOG_SEARCH_SEARCH_METRICS_H diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 8e51229..b22378e 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -13,7 +13,7 @@ // #include "search_metrics.h" -#include +#include namespace warthog::search { @@ -99,25 +99,19 @@ feasible( if(next->get_f() > par->get_max_cost_cutoff()) { - info( - par->verbose_, "cost cutoff", next->get_f(), ">", - par->get_max_cost_cutoff()); + WARTHOG_GINFO_FMT_IF(par->verbose_, "cost cutoff {} > {}", next->get_f(), par->get_max_cost_cutoff()); return false; } if(met->nodes_expanded_ >= par->get_max_expansions_cutoff()) { - info( - par->verbose_, "expansions cutoff", met->nodes_expanded_, ">", - par->get_max_expansions_cutoff()); + WARTHOG_GINFO_FMT_IF(par->verbose_, "expansions cutoff {} > {}", met->nodes_expanded_, par->get_max_expansions_cutoff()); return false; } if(met->time_elapsed_nano_ > par->get_max_time_cutoff()) { - info( - par->verbose_, "time cutoff", met->time_elapsed_nano_, ">", - par->get_max_time_cutoff()); + WARTHOG_GINFO_FMT_IF(par->verbose_, "time cutoff {} > {}", met->time_elapsed_nano_, par->get_max_time_cutoff()); return false; } diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 3418490..3ab8c8c 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -95,21 +94,6 @@ class unidirectional_search sol->s_node_->get_id(), spi->target_, &sol->path_); heuristic_->h(&hv); } - - DO_ON_DEBUG_IF(spi->verbose_) - { - for(auto& node_id : sol->path_) - { - int32_t x, y; - expander_->get_xy(node_id, x, y); - std::cerr << "final path: (" << x << ", " << y << ")..."; - search_node* n - = expander_->generate(expander_->unget_state(node_id)); - assert(n->get_search_number() == spi->instance_id_); - n->print(std::cerr); - std::cerr << std::endl; - } - } } L& @@ -198,9 +182,7 @@ class unidirectional_search if(n->get_ub() < sol->met_.ub_) { sol->met_.ub_ = n->get_ub(); - debug( - pi->verbose_, "NEW UB:", "Incumbent Cost", - sol->sum_of_edge_costs_); + WARTHOG_GDEBUG_FMT_IF(pi->verbose_, "NEW UB: Incumbent Cost {}", sol->sum_of_edge_costs_); } } @@ -225,7 +207,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); - user(pi->verbose_, pi); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "{}", *pi); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Start node: {}", *start); update_ub(start, sol, pi); } @@ -317,14 +299,7 @@ class unidirectional_search sol->met_.nodes_surplus_ = open_->size(); sol->met_.heap_ops_ = open_->get_heap_ops(); - DO_ON_DEBUG_IF(pi->verbose_) - { - if(sol->sum_of_edge_costs_ == warthog::COST_MAX) - { - warning(pi->verbose_, "Search failed; no solution exists."); - } - else { user(pi->verbose_, "Solution found", *sol->s_node_); } - } + WARTHOG_GINFO_IF(pi->verbose_ && sol->sum_of_edge_costs_ == warthog::COST_MAX, "Search failed; no solution exists."); } }; diff --git a/include/warthog/util/log.h b/include/warthog/util/log.h deleted file mode 100644 index 90dfcae..0000000 --- a/include/warthog/util/log.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - * @file - * - * Simple log utility. Can work both on preprocessor time or at compile time - * - * All this function works only if: - * @li the macro @c DEBUG is active OR; - * @li the macro @c NDEBUG is not active; - * - * Otherwise, they will become just empty (or can be easily optimized away) - * - * @date Oct 1, 2018 - * @author Massimo Bono - */ - -#ifndef WARTHOG_UTIL_LOG_H -#define WARTHOG_UTIL_LOG_H - -#include "file_utils.h" -#include "macros.h" -#include -#include - -#ifndef QUICK_LOG -#define QUICK_LOG 0 -#endif - -#if QUICK_LOG <= 0 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define debug(p, ...) _abstractLog("DEBUG", p, __VA_ARGS__) -#else -#define debug(...) {}; -#endif - -#if QUICK_LOG <= 1 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define trace(p, ...) _abstractLog("TRACE", p, __VA_ARGS__) -#else -#define trace(...) {}; -#endif - -#if QUICK_LOG <= 4 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define info(p, ...) _abstractLog("INFO ", p, __VA_ARGS__) -#else -#define info(...) {}; -#endif - -#if QUICK_LOG <= 5 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define user(p, ...) _abstractLog("USER ", p, __VA_ARGS__) -#else -#define user(...) {}; -#endif - -#if QUICK_LOG <= 6 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define warning(p, ...) _abstractLog("WARN ", p, __VA_ARGS__) -#else -#define warning(...) {}; -#endif - -#if QUICK_LOG <= 7 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define error(...) _abstractLog("ERROR", true, __VA_ARGS__) -#else -#define error(...) {}; -#endif - -#if QUICK_LOG <= 8 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define critical(...) _abstractLog("CRTCL", true, __VA_ARGS__) -#else -#define critical(...) {}; -#endif - -/** - * like ::log but will log only if at runtime the @c expr will evaluate to true - * - * Will impact performances (even if by little) even if the log is turned off - * - * @code - * clog(true)("hello ", this->name); - * @endcode - * - * @param[in] expr the expression to be evaluated - * @param[in] ... input for ::log - */ -#define clog(expr) _clog1(expr) - -#define _clog1(expr) \ - if(expr) \ - { \ - _clog2 -#define _clog2(...) \ - debug(__VA_ARGS__); \ - } - -/** - * Condition Statement Log utility allows to perform some previous statement - * before logging - * - * This logging macro is useful when your logging needs some local variable - * - * @code - * cslog(true)(int a = 5)("a is %d", a); - * @endcode - * - * @param[in] expr the condition to check if the log is enabled - */ -#define cslog(expr) _cslog1(expr) -#define _cslog1(expr) \ - if(expr) \ - { \ - _cslog2 -#define _cslog2(...) \ - __VA_ARGS__; \ - _cslog3 -#define _cslog3(...) \ - debug(__VA_ARGS__); \ - } - -#define _LOG_OP(context, index, x) x -#define _LOG_COMB(context, x, y) x << " " << y - -#define _debug(...) FOR_EACH(, _LOG_OP, _LOG_COMB, __VA_ARGS__) - -#if defined(DEBUG) || !defined(NDEBUG) -/** - * Perform some work if debug has been enabled - * - * - */ -#define DO_ON_DEBUG_IF(expr) if(expr) -#define DO_ON_DEBUG if(true) - -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] level the name of the log level - * @param[in] ... the entries to put on stream - */ -#define _abstractLog(level, p, ...) \ - { \ - constexpr auto src = std::source_location::current(); \ - if(p) \ - std::cerr << "[" << level << "] " \ - << warthog::util::getBaseName_(src.file_name()) << "@" \ - << __func__ << ":" << src.line() << " " \ - << _debug(__VA_ARGS__) << std::endl; \ - } - -#else - -#define DO_ON_DEBUG_IF(expr) if(false) -#define DO_ON_DEBUG if(false) -#define _abstractLog(level, ...) {}; - -#endif - -#endif // WARTHOG_UTIL_LOG_H diff --git a/src/io/log.cpp b/src/io/log.cpp index 331d61b..d265548 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -45,12 +45,14 @@ void log_sink_stream::open_stderr() // log_sink_stream -log_sink_std::log_sink_std() : log_sink_std(nullptr) -{ } -log_sink_std::log_sink_std(bool cerr) : log_sink_std(cerr ? &std::cerr : &std::cout) +log_sink_std::log_sink_std() : log_sink{ + nullptr, + {{&log_sink_std::write_trace,&log_sink_std::write_debug, + &log_sink_std::write_information,&log_sink_std::write_warning, + &log_sink_std::write_error,&log_sink_std::write_critical}}} { } -log_sink_std::log_sink_std(std::ostream* stream) : log_sink{ - stream, +log_sink_std::log_sink_std(bool cerr) : log_sink{ + cerr ? &std::cerr : &std::cout, {{&log_sink_std::write_trace,&log_sink_std::write_debug, &log_sink_std::write_information,&log_sink_std::write_warning, &log_sink_std::write_error,&log_sink_std::write_critical}}} @@ -96,47 +98,49 @@ void log_sink_std::write_critical(void* logger, std::string_view msg) void log_sink_stream::write_trace(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_trace(log_stream->log_stream_, msg); } void log_sink_stream::write_debug(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_debug(log_stream->log_stream_, msg); } void log_sink_stream::write_information(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_information(log_stream->log_stream_, msg); } void log_sink_stream::write_warning(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_warning(log_stream->log_stream_, msg); } void log_sink_stream::write_error(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_error(log_stream->log_stream_, msg); } void log_sink_stream::write_critical(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_critical(log_stream->log_stream_, msg); } +namespace { global_logger_type global_logger_(log_sink_std(true).sink()); +} global_logger_type& glog() { return global_logger_; diff --git a/src/search/problem_instance.cpp b/src/search/problem_instance.cpp index 2239a7f..7022a1a 100644 --- a/src/search/problem_instance.cpp +++ b/src/search/problem_instance.cpp @@ -1,7 +1,10 @@ #include +namespace warthog::search +{ + std::ostream& -operator<<(std::ostream& str, const warthog::search::problem_instance& pi) +operator<<(std::ostream& str, const problem_instance& pi) { pi.print(str); @@ -10,9 +13,11 @@ operator<<(std::ostream& str, const warthog::search::problem_instance& pi) std::ostream& operator<<( - std::ostream& str, const warthog::search::search_problem_instance& pi) + std::ostream& str, const search_problem_instance& pi) { pi.print(str); return str; } + +} diff --git a/src/search/search_metrics.cpp b/src/search/search_metrics.cpp index a2a9fb7..910ac13 100644 --- a/src/search/search_metrics.cpp +++ b/src/search/search_metrics.cpp @@ -1,5 +1,7 @@ #include +namespace warthog::search { + std::ostream& operator<<(std::ostream& str, const warthog::search::search_metrics& met) { @@ -11,3 +13,5 @@ operator<<(std::ostream& str, const warthog::search::search_metrics& met) << " lb=" << met.lb_ << " ub=" << met.ub_; return str; } + +} diff --git a/src/util/file_utils.cpp b/src/util/file_utils.cpp index 1944804..006f472 100644 --- a/src/util/file_utils.cpp +++ b/src/util/file_utils.cpp @@ -9,7 +9,6 @@ #include #include #include -#include namespace warthog::util { From 47bf302de38763329e9a43bb6337e15161074950 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 11:34:18 +1000 Subject: [PATCH 14/61] added logger comments --- cmake/headers.cmake | 2 +- include/warthog/io/log.h | 88 +++++++++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/cmake/headers.cmake b/cmake/headers.cmake index b7d60c5..46e1ba6 100644 --- a/cmake/headers.cmake +++ b/cmake/headers.cmake @@ -27,6 +27,7 @@ include/warthog/heuristic/octile_heuristic.h include/warthog/heuristic/zero_heuristic.h include/warthog/io/grid.h +include/warthog/io/log.h include/warthog/memory/arraylist.h include/warthog/memory/bittable.h @@ -56,7 +57,6 @@ include/warthog/util/file_utils.h include/warthog/util/gm_parser.h include/warthog/util/helpers.h include/warthog/util/intrin.h -include/warthog/util/log.h include/warthog/util/macros.h include/warthog/util/pqueue.h include/warthog/util/scenario_manager.h diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index fea3251..7569d38 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -3,7 +3,36 @@ // io/log.h // -// Logger class. +// Logging utility. +// +// The log_sink is a non-owning copyable struct that points to the data and logging function calls. +// All logging is performed through log_sink. +// All classes here are thread safe, follow comments for outlaying cases. +// +// Special classes inherit log_sink to provide default functionality. +// log_sink_std should be used to write to std::cout and std::cerr. +// log_sink_stream pipe to a stream or open a file stream. +// +// The logs are made to be logged to a certain log_level. +// The WARTHOG_TRACE and others in this header provide interfaces to log to a special +// logger<> class. This logger<> class knows at compile time the minimum level to log, +// and with the use of a macro will be compiled out if that level was not set at compile time +// when used with the logging macros. +// +// The global logger can be acquired with the glog() (logger<>) or glogs() (log_sink), and set with +// set_glog(log_sink). The log level of the global logger is set though the WARTHOG_LOG definition, +// with a default of 1(debug) for debug builds, and 2(information) for release (NDEBUG defined). +// Macros like WARTHOG_GTRACE will automatically write to the global logger. +// +// The _IF will only log if a runtime if true. +// The _FMT uses the std::format from C++20 to format the output messages. +// The logging utilities uses dynamic memory std::strings to produce the final log message strings, +// thus is better to be disabled for release builds through the log level. +// +// Log messages produce a messaged as "[TIME LEVEL] msg". +// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be overridden with +// define cstring WARTHOG_LOG_TIME. +// Clock is in local system time, but can be set to UTC by definiong WARTHOG_LOG_CLOCK_UTC. // // @author: Ryan Hechenberger // @created: 2025-09-09 @@ -20,6 +49,7 @@ #include #include +// default log levels, also for global logger #ifdef WARTHOG_LOG #define WARTHOG_DEFAULT_LOG_LEVEL WARTHOG_LOG #elif !defined(NDEBUG) @@ -28,8 +58,19 @@ #define WARTHOG_DEFAULT_LOG_LEVEL 2 #endif +// specify the log time format +#ifdef WARTHOG_LOG_TIME +#define WARTHOG_LOG_TIME_FORMAT WARTHOG_LOG_TIME +#else #define WARTHOG_LOG_TIME_FORMAT "%F %T" +#endif + +// set to use utc time instead of local +#ifdef WARTHOG_LOG_CLOCK_UTC +#define WARTHOG_LOG_NOW (std::chrono::utc_clock::now()) +#else #define WARTHOG_LOG_NOW (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) +#endif namespace warthog::io { @@ -45,6 +86,11 @@ enum class log_level NONE = 6 }; +/// @brief the log class used to write out to a log. +/// +/// Takes a pointer for data (must not be null to write). +/// The array call is the logger for each level. +/// log will write if able (data != null && call[level] != null). struct log_sink { using call_type = void (void*, std::string_view msg); @@ -66,6 +112,7 @@ struct log_sink constexpr const log_sink& sink() const noexcept { return *this; } }; +/// @brief concept that check if class is shaped with a log_sink. template concept LogSink = requires (Sink S, log_level level, std::string_view msg) { @@ -74,9 +121,10 @@ concept LogSink = requires (Sink S, log_level level, std::string_view msg) { std::as_const(S).sink() } -> std::convertible_to; }; -/// sink to cout|cerr -/// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. -/// User provided file stream is not thread safe. +/// @brief sink to cout|cerr +/// +/// Is thread safe as long as std::ios_base::sync_with_stdio(false) has not been called. +/// Can copy go log_sink and then be destructed without issue. struct log_sink_std : log_sink { // cerr = true will sink to cerr, false to cout @@ -90,6 +138,10 @@ struct log_sink_std : log_sink static void write_critical(void*, std::string_view msg); }; +/// @brief sink to a passed stream, or open and own a filestream. +/// +/// This class is thread-safe through use of a mutex. +/// Must be kept in scope at all times while its log_sink is in use. class log_sink_stream : public log_sink { public: @@ -109,10 +161,6 @@ class log_sink_stream : public log_sink static void write_error(void*, std::string_view msg); static void write_critical(void*, std::string_view msg); - using log_sink::log; - using log_sink::can_log; - using log_sink::sink; - protected: std::ostream* log_stream_ = nullptr; std::unique_ptr owned_file_; @@ -120,6 +168,15 @@ class log_sink_stream : public log_sink }; static_assert(LogSink, "log_stream_sink must be a log_sink."); +/// @brief the logger class that manipulates a log_sink. +/// @tparam MinLevel the minimum logging level, derived at compile time. +/// Defaults to the programs default log level. +/// +/// A wrapper class for a log_sink that allows to control the log_level. +/// If just using a log_sink, it will always log at every level. +/// This class is handled specially by the logging macros to only log if log_level +/// is a min_level. +/// When setting a long_sink, will clear all pointers below MinLevel. template (WARTHOG_DEFAULT_LOG_LEVEL)> struct logger : log_sink { @@ -140,6 +197,8 @@ struct logger : log_sink return *this; } + /// @brief Overrides MinLevel to level. Can only raise, not lower it. + /// @param level constexpr void set_min_level(log_level level) noexcept { assert(static_cast(level) <= static_cast(log_level::NONE)); @@ -182,6 +241,7 @@ concept LoggerLevel = Logger && requires { lg.log(level, msg); \ }} +// Write msg (string_view) to log lg. #define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::TRACE,msg) #define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::DEBUG,msg) #define WARTHOG_INFO(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::INFORMATION,msg) @@ -189,6 +249,7 @@ concept LoggerLevel = Logger && requires { #define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::ERROR,msg) #define WARTHOG_CRIT(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::CRITICAL,msg) +// Write msg (string_view) to log lg if runtime cond is true. #define WARTHOG_TRACE_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::TRACE,msg) #define WARTHOG_DEBUG_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::DEBUG,msg) #define WARTHOG_INFO_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::INFORMATION,msg) @@ -207,6 +268,7 @@ concept LoggerLevel = Logger && requires { lg.log(level, std::format(__VA_ARGS__)); \ }} +// Write formatted message to log, pass as format,args... #define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) #define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) #define WARTHOG_INFO_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) @@ -214,6 +276,7 @@ concept LoggerLevel = Logger && requires { #define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) #define WARTHOG_CRIT_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +// Write formatted message to log if cond is true, pass as format,args... #define WARTHOG_TRACE_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) #define WARTHOG_DEBUG_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) #define WARTHOG_INFO_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) @@ -224,10 +287,15 @@ concept LoggerLevel = Logger && requires { // global logger using global_logger_type = logger<>; +/// @brief get the global logger, default is set to std::cerr through log_sink_std global_logger_type& glog(); +/// @brief get the global log_sink const log_sink& glogs(); +/// @brief sets the global log_sink void set_glog(log_sink log); +/// @brief A logger that sets itself to glog() +/// @tparam MinLevel template struct global_logger : logger { @@ -237,14 +305,14 @@ struct global_logger : logger constexpr global_logger& operator=(const global_logger&) noexcept = default; + /// @brief Updates the sink to the new glog sink. Must be called if set_glog was used. void resync_global() { static_cast(*this) = glog().sink(); } }; -// defines to the global logger - +// the global version of the marcos, omits the passing of a logger. #define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(),msg) #define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(),msg) #define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(),msg) From ecfc59db60c714e279d04ee2f1d6f91a8f7c44fd Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 15:23:47 +1000 Subject: [PATCH 15/61] update posthoc trace with comments and trace rendering; added support in warthog-app --- apps/cfg.cpp | 2 +- apps/cfg.h | 2 +- apps/warthog.cpp | 93 +++++++++++++++++----------- cmake/config.h.in | 1 + include/warthog/io/grid_trace.h | 14 ++--- include/warthog/io/observer.h | 17 +++-- include/warthog/io/posthoc_trace.h | 10 ++- include/warthog/io/stream_observer.h | 27 +++++--- src/io/traces.cpp | 36 ++++++++--- test.cpp | 21 +++++++ 10 files changed, 154 insertions(+), 69 deletions(-) create mode 100644 test.cpp diff --git a/apps/cfg.cpp b/apps/cfg.cpp index 12ff66b..a9d9f52 100644 --- a/apps/cfg.cpp +++ b/apps/cfg.cpp @@ -39,7 +39,7 @@ warthog::util::cfg::parse_args( std::string warthog::util::cfg::get_param_value(std::string param_name) { - std::string ret(""); + std::string ret; std::map>::iterator it = params_.find(param_name); if(it != params_.end()) diff --git a/apps/cfg.h b/apps/cfg.h index 3f804d9..8a1e8c0 100644 --- a/apps/cfg.h +++ b/apps/cfg.h @@ -1,7 +1,7 @@ #ifndef WARTHOG_APP_CFG_H #define WARTHOG_APP_CFG_H -#include "getopt.h" +#include #include #include diff --git a/apps/warthog.cpp b/apps/warthog.cpp index a5d4ef1..d3a7d40 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -20,6 +20,9 @@ #include #include #include +#ifdef WARTHOG_POSTHOC +#include +#endif #include "cfg.h" #include @@ -44,6 +47,16 @@ int checkopt = 0; int verbose = 0; // display program help on startup int print_help = 0; +// run only this query, or -1 for all +int filter_id = -1; +#ifdef WARTHOG_POSTHOC +// write trace to file, empty string to disable +std::string trace_file; +using listener_grid = ::warthog::io::grid_trace; +using listener_type = std::tuple; +#else +using listener_type = std::tuple<>; +#endif void help(std::ostream& out) @@ -66,6 +79,10 @@ help(std::ostream& out) "values in the scen file)\n" << "\t--verbose (optional; prints debugging info when compiled " "with debug symbols)\n" + << "\t--filter [id] (optional; run only query [id])\n" +#ifdef WARTHOG_POSTHOC + << "\t--trace [.trace.yaml file] (optional; write posthoc trace for first query to [file])\n" +#endif << "Invoking the program this way solves all instances in [scen " "file] with algorithm [alg]\n" << "Currently recognised values for [alg]:\n" @@ -111,13 +128,28 @@ run_experiments( warthog::util::scenario_manager& scenmgr, bool verbose, bool checkopt, std::ostream& out) { + WARTHOG_GINFO_FMT("start search with algorithm {}", alg_name); warthog::search::search_parameters par; warthog::search::solution sol; auto* expander = algo.get_expander(); if(expander == nullptr) return 1; + +#ifdef WARTHOG_POSTHOC + if constexpr (std::same_as>) { + if (!trace_file.empty()) { + listener_grid& l = std::get(algo.get_listeners()); + l.stream_open(trace_file); + l.search_id(0); + l.set_grid(expander->get_map()); + } + } +#endif + out << "id\talg\texpanded\tgenerated\treopen\tsurplus\theapops" << "\tnanos\tplen\tpcost\tscost\tmap\n"; - for(unsigned int i = 0; i < scenmgr.num_experiments(); i++) + for(uint32_t i = filter_id >= 0 ? static_cast(filter_id) : 0, + ie = filter_id >= 0 ? i+1 : static_cast(scenmgr.num_experiments()); + i < ie; i++) { warthog::util::experiment* exp = scenmgr.get_experiment(i); @@ -141,10 +173,14 @@ run_experiments( if(checkopt) { - if(!check_optimality(sol, exp)) return 4; + if(!check_optimality(sol, exp)) { + WARTHOG_GCRIT("search error: failed suboptimal 4"); + return 4; + } } } + WARTHOG_GINFO_FMT("search complete; total memory: {}", algo.mem() + scenmgr.mem()); return 0; } @@ -158,17 +194,11 @@ run_astar( warthog::heuristic::octile_heuristic heuristic(map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open); + warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } int @@ -182,17 +212,11 @@ run_astar4c( map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open); + warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } int @@ -205,17 +229,11 @@ run_dijkstra( warthog::heuristic::zero_heuristic heuristic; warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open); + warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } int @@ -242,13 +260,7 @@ run_wgm_astar( int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } } // namespace @@ -258,13 +270,17 @@ main(int argc, char** argv) { // parse arguments warthog::util::param valid_args[] - = {{"alg", required_argument, 0, 1}, + = {{"alg", required_argument, 0, 0}, {"scen", required_argument, 0, 0}, - {"map", required_argument, 0, 1}, + {"map", required_argument, 0, 0}, // {"gen", required_argument, 0, 3}, {"help", no_argument, &print_help, 1}, {"checkopt", no_argument, &checkopt, 1}, {"verbose", no_argument, &verbose, 1}, + {"filter", required_argument, &filter_id, 1}, +#ifdef WARTHOG_POSTHOC + {"trace", required_argument, 0, 0}, +#endif {"costs", required_argument, 0, 1}, {0, 0, 0, 0}}; @@ -283,6 +299,13 @@ main(int argc, char** argv) std::string mapfile = cfg.get_param_value("map"); std::string costfile = cfg.get_param_value("costs"); + if (filter_id == 1) { + filter_id = std::stoi(cfg.get_param_value("filter")); + } +#ifdef WARTHOG_POSTHOC + trace_file = cfg.get_param_value("trace"); +#endif + // if(gen != "") // { // warthog::util::scenario_manager sm; diff --git a/cmake/config.h.in b/cmake/config.h.in index b2c1f33..85e3736 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -5,6 +5,7 @@ #define WARTHOG_VERSION_MINOR @CMAKE_PROJECT_VERSION_MINOR@ #define WARTHOG_VERSION_REVISON @CMAKE_PROJECT_VERSION_PATCH@ +#cmakedefine WARTHOG_POSTHOC #cmakedefine WARTHOG_INT128 #cmakedefine WARTHOG_INTRIN_ALL #cmakedefine WARTHOG_BMI diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index a95b7a7..7a9f8cb 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -1,7 +1,9 @@ #ifndef WARTHOG_IO_GRID_TRACE_H #define WARTHOG_IO_GRID_TRACE_H -// listener/grid_trace.h +// io/grid_trace.h +// +// Basic posthoc_trace for use with gridmap. // // @author: Ryan Hechenberger // @created: 2025-08-07 @@ -16,7 +18,7 @@ namespace warthog::io { -/// @brief class that produces a posthoc trace for the gridmap domain +/// @brief class that produces a posthoc trace for the gridmap domain, grid must be set. class grid_trace : public posthoc_trace { public: @@ -30,14 +32,6 @@ class grid_trace : public posthoc_trace void print_posthoc_header() override; - /// @brief Checks that grid_ != nullptr - void event(const char* name) const - { - if (grid_ == nullptr) { - throw std::logic_error("grid_trace::grid_ is null"); - } - } - void begin_search(int id, const search::search_problem_instance& pi); void diff --git a/include/warthog/io/observer.h b/include/warthog/io/observer.h index f2e9e57..b14b856 100644 --- a/include/warthog/io/observer.h +++ b/include/warthog/io/observer.h @@ -9,7 +9,7 @@ // These function names must be registered before use, common ones registered here. // // To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). -// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observer. +// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observers. // This will run through each element in tuple (i) and call i.[function](args...) if able. // If i.event([function],args...) is a valid callable, calls this function first, also tries i.event([function]). // @@ -19,28 +19,37 @@ #include +/// @brief Creates a concept observer_has_[func_name] that checks of function Listener.func_name(args...) is callable. #define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ template \ concept observer_has_##func_name = requires(Listener L, Args&&... args) \ { \ { L.func_name(std::forward(args)...) }; \ }; +/// @brief Creates function observer_[func_name] that calls Listener.[func_name] is callable. +/// WARTHOG_OBSERVER_DEFINE_HAS([func_name]) must be defined. +/// +/// The created function observer_[func_name] loops through all listeners in tuple L. +/// For each it will try to call: +/// - event([func_name], args...) if able otherwise event([func_name]) if able +/// - [func_name](args...) if able #define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ template \ void observer_##func_name(Listeners& L, Args&&... args) \ { \ if constexpr (I < std::tuple_size_v) { \ using T = std::tuple_element_t; \ - constexpr bool has_event = observer_has_##func_name ; \ + constexpr bool has_func = observer_has_##func_name ; \ if constexpr (observer_has_event) { std::get(L).event( #func_name , std::forward(args)... ); } \ else if constexpr (observer_has_event) { std::get(L).event( #func_name ); } \ - if constexpr (has_event) { \ + if constexpr (has_func) { \ std::get(L).func_name(std::forward(args)...); \ } \ - listener_##func_name (L, std::forward(args)...); \ + observer_##func_name (L, std::forward(args)...); \ } \ } +/// @brief Defines a observer_[func_name] function and observer_has_[func_name] concept. #define WARTHOG_OBSERVER_DEFINE(func_name) \ WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ WARTHOG_OBSERVER_DEFINE_CALL(func_name) diff --git a/include/warthog/io/posthoc_trace.h b/include/warthog/io/posthoc_trace.h index 04f8627..c1e9c4c 100644 --- a/include/warthog/io/posthoc_trace.h +++ b/include/warthog/io/posthoc_trace.h @@ -17,10 +17,11 @@ namespace warthog::io /// @brief base posthoc observer class. /// +/// Set the search_id to set which search the trace is printed for, by default will not trace. /// event begin_search and end_search will setup the trace to print only a specified. /// Inherit to create new trace format by overriding print_posthoc_header for custom header. /// Add own events to print posthoc event to stream() if (*this) holds true, -/// (*this) holds true iff id is on search_id between begin_search and end_search the first time only. +/// (*this) holds true iff id == search_id for event begin_search once, and ends at end_search. class posthoc_trace : public stream_observer { public: @@ -32,6 +33,9 @@ class posthoc_trace : public stream_observer int search_id() const noexcept { return search_id_; } void search_id(int sid) noexcept { search_id_ = sid; } + /// @brief begin_search event, if overridden then must still be called first (only id argument is needed), + /// then checked if can write to stream. + /// @param id the search id, (*this) will become true if id == search_id_ && done_trace_ == false template void begin_search(int id, Args&&...) { @@ -43,19 +47,21 @@ class posthoc_trace : public stream_observer print_posthoc_header(); } } + /// @brief end_search event, if overridden then must still be called first (no args needed). template void end_search(Args&&...) { do_trace_ = false; } + /// @brief returns true if trace can be outputted, false otherwise. operator bool() const noexcept { return do_trace_; } protected: - int search_id_ = 0; + int search_id_ = -1; bool do_trace_ = false; bool done_trace_ = false; }; diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index 9229b07..afb8216 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -1,15 +1,14 @@ #ifndef WARTHOG_IO_STEAM_OBSERVER_H #define WARTHOG_IO_STEAM_OBSERVER_H -// listener/stream_listener.h +// io/stream_observer.h // -// A search listener is a callback class that executes specialised -// code for partiular search events, such as when: -// - a node is generated -// - a node is expanded -// - a node is relaxed +// The stream observer is a base class for observers that can open and own a filestream, +// or pass another filestream. // -// This class implements dummy listener with empty event handlers. +// Is designed to be used with observer tuples. +// Inherited class will call stream() to get the current stream for output. +// Using the observer methodology, event functions will be given that will write to output in certain ways. // // @author: Ryan Hechenberger // @created: 2025-08-01 @@ -24,6 +23,10 @@ namespace warthog::io { +/// @brief A base-stream observer. +/// +/// Inherit and give event functions for observers to call. +/// shared_stream_t is a shared_ptr to an std::ostream, and can be passed around to other stream_observer. class stream_observer { public: @@ -34,16 +37,24 @@ class stream_observer stream_observer(const shared_stream_t& stream); ~stream_observer(); + /// @brief open a file stream, store in a shared_stream_t void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + /// @brief pass an existing stream, must remain in void stream(std::ostream& stream); + /// @brief pass a shared stream void stream_share(const shared_stream_t& stream); + /// @brief copy stream from another stream_observer void stream_share(const stream_observer& stream); + /// @brief sets to std::cout void stream_stdout(); + /// @brief sets to std::cerr void stream_stderr(); + /// @brief unsets the stream void clear_stream(); operator bool() const noexcept { return stream_ != nullptr; } + /// @brief undefined behaviour if no stream is open (asserts on debug) std::ostream& stream() const noexcept { assert(stream_ != nullptr); return *stream_; } const shared_stream_t& shared_stream() const noexcept { return shared_stream_; } @@ -52,8 +63,6 @@ class stream_observer shared_stream_t shared_stream_; }; -using void_listener = stream_observer; - } // namespace warthog::io #endif // WARTHOG_IO_STEAM_OBSERVER_H diff --git a/src/io/traces.cpp b/src/io/traces.cpp index fb58898..0a78304 100644 --- a/src/io/traces.cpp +++ b/src/io/traces.cpp @@ -28,10 +28,18 @@ void grid_trace::print_posthoc_header() $if: ${{ $.type == 'destination' }} fill: red clear: false + - $: cell + $if: ${{ $.type == 'expand' }} + fill: cyan + clear: false - $: cell $if: ${{ $.type == 'expand' }} fill: blue clear: close + - $: cell + $if: ${{ $.type == 'generate' }} + fill: purple + clear: false - $: cell $if: ${{ $.type == 'generate' }} fill: orange @@ -49,7 +57,9 @@ void grid_trace::begin_search(int id, const search::search_problem_instance& pi) { posthoc_trace::begin_search(id); if (*this) { - assert(grid_ != nullptr); + if (grid_ == nullptr) { + throw std::logic_error("grid_trace::grid_ is null"); + } uint32_t x, y; grid_->to_unpadded_xy(pi.start_, x, y); stream() << std::format(" - {{ type: source, id: {}, x: {}, y: {} }}\n", @@ -67,10 +77,14 @@ grid_trace::expand_node(const node& current) const { if (*this) { assert(grid_ != nullptr); + std::string pid; + if (auto cpid = current.get_parent(); !cpid.is_none()) { + pid = std::format(", pId: {}", cpid.id); + } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, x, y, current.get_f(), current.get_g() + stream() << std::format(" - {{ type: expand, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g() ); } } @@ -80,10 +94,14 @@ grid_trace::relax_node(const node& current) const { if (*this) { assert(grid_ != nullptr); + std::string pid; + if (auto cpid = current.get_parent(); !cpid.is_none()) { + pid = std::format(", pId: {}", cpid.id); + } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, x, y, current.get_f(), current.get_g() + stream() << std::format(" - {{ type: relax, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g() ); } } @@ -94,10 +112,14 @@ grid_trace::close_node(const node& current) const { if (*this) { assert(grid_ != nullptr); + std::string pid; + if (auto cpid = current.get_parent(); !cpid.is_none()) { + pid = std::format(", pId: {}", cpid.id); + } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: close, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, x, y, current.get_f(), current.get_g() + stream() << std::format(" - {{ type: close, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g() ); } } diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..ee3de6e --- /dev/null +++ b/test.cpp @@ -0,0 +1,21 @@ +#include + +enum class X { + a, + b, + c +}; + +template +concept is_c = a == X::c; + +int main() +{ + int a; + std::cin >> a; + if constexpr (is_c(a)>) { + std::cout << "done\n"; + } else { + std::cout << "bad\n"; + } +} From 10bdd85c414876cc821c852b9250c3830e90ba8f Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 15:24:11 +1000 Subject: [PATCH 16/61] added expanded verbose message --- include/warthog/search/unidirectional_search.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 3ab8c8c..cb41aaa 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -293,6 +293,7 @@ class unidirectional_search sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); } io::observer_close_node(listeners_, *current); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Expanded: {}", *current); } sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); From 904dfb2c3aaf554724f7f5f1c4dbb016fabc8291 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 15:24:25 +1000 Subject: [PATCH 17/61] missing commit --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a564afe..e24eb04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set_property(GLOBAL PROPERTY WARTHOG_warthog-core ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +option(WARTHOG_POSTHOC "Compile trace support generation for posthoc" OFF) option(WARTHOG_INT128 "Enable support for __int128 on gcc and clang" OFF) option(WARTHOG_BMI "Enable support cpu BMI for WARTHOG_INTRIN_HAS(BMI)" OFF) option(WARTHOG_BMI2 "Enable support cpu BMI2 for WARTHOG_INTRIN_HAS(BMI2), use for Zen 3+" OFF) From 141532f7647aec4275db06b9e933a57da12829ea Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Sun, 5 Oct 2025 14:41:19 +1100 Subject: [PATCH 18/61] removed accidental file from git --- test.cpp | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 test.cpp diff --git a/test.cpp b/test.cpp deleted file mode 100644 index ee3de6e..0000000 --- a/test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -enum class X { - a, - b, - c -}; - -template -concept is_c = a == X::c; - -int main() -{ - int a; - std::cin >> a; - if constexpr (is_c(a)>) { - std::cout << "done\n"; - } else { - std::cout << "bad\n"; - } -} From 885ac3199a006cb403ffdce26d2f1db910912526 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 03:31:33 +0000 Subject: [PATCH 19/61] auto clang-format action --- apps/warthog.cpp | 39 +- include/warthog/io/grid_trace.h | 18 +- include/warthog/io/log.h | 441 +++++++++++------- include/warthog/io/observer.h | 83 ++-- include/warthog/io/posthoc_trace.h | 62 ++- include/warthog/io/stream_observer.h | 58 ++- include/warthog/search/problem_instance.h | 19 +- include/warthog/search/search_node.h | 24 +- include/warthog/search/uds_traits.h | 12 +- .../warthog/search/unidirectional_search.h | 39 +- include/warthog/util/vec_io.h | 6 +- src/io/log.cpp | 129 +++-- src/io/observer.cpp | 53 ++- src/io/traces.cpp | 83 ++-- src/search/problem_instance.cpp | 3 +- src/search/search_metrics.cpp | 3 +- 16 files changed, 668 insertions(+), 404 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index d3a7d40..0db3479 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -79,9 +79,10 @@ help(std::ostream& out) "values in the scen file)\n" << "\t--verbose (optional; prints debugging info when compiled " "with debug symbols)\n" - << "\t--filter [id] (optional; run only query [id])\n" + << "\t--filter [id] (optional; run only query [id])\n" #ifdef WARTHOG_POSTHOC - << "\t--trace [.trace.yaml file] (optional; write posthoc trace for first query to [file])\n" + << "\t--trace [.trace.yaml file] (optional; write posthoc trace for " + "first query to [file])\n" #endif << "Invoking the program this way solves all instances in [scen " "file] with algorithm [alg]\n" @@ -135,8 +136,12 @@ run_experiments( if(expander == nullptr) return 1; #ifdef WARTHOG_POSTHOC - if constexpr (std::same_as>) { - if (!trace_file.empty()) { + if constexpr(std::same_as< + listener_type, + std::remove_cvref_t>) + { + if(!trace_file.empty()) + { listener_grid& l = std::get(algo.get_listeners()); l.stream_open(trace_file); l.search_id(0); @@ -147,9 +152,11 @@ run_experiments( out << "id\talg\texpanded\tgenerated\treopen\tsurplus\theapops" << "\tnanos\tplen\tpcost\tscost\tmap\n"; - for(uint32_t i = filter_id >= 0 ? static_cast(filter_id) : 0, - ie = filter_id >= 0 ? i+1 : static_cast(scenmgr.num_experiments()); - i < ie; i++) + for(uint32_t i = filter_id >= 0 ? static_cast(filter_id) : 0, + ie = filter_id >= 0 + ? i + 1 + : static_cast(scenmgr.num_experiments()); + i < ie; i++) { warthog::util::experiment* exp = scenmgr.get_experiment(i); @@ -173,14 +180,16 @@ run_experiments( if(checkopt) { - if(!check_optimality(sol, exp)) { + if(!check_optimality(sol, exp)) + { WARTHOG_GCRIT("search error: failed suboptimal 4"); return 4; } } } - WARTHOG_GINFO_FMT("search complete; total memory: {}", algo.mem() + scenmgr.mem()); + WARTHOG_GINFO_FMT( + "search complete; total memory: {}", algo.mem() + scenmgr.mem()); return 0; } @@ -194,7 +203,8 @@ run_astar( warthog::heuristic::octile_heuristic heuristic(map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); + warthog::search::unidirectional_search astar( + &heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); @@ -212,7 +222,8 @@ run_astar4c( map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); + warthog::search::unidirectional_search astar( + &heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); @@ -229,7 +240,8 @@ run_dijkstra( warthog::heuristic::zero_heuristic heuristic; warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); + warthog::search::unidirectional_search astar( + &heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); @@ -299,7 +311,8 @@ main(int argc, char** argv) std::string mapfile = cfg.get_param_value("map"); std::string costfile = cfg.get_param_value("costs"); - if (filter_id == 1) { + if(filter_id == 1) + { filter_id = std::stoi(cfg.get_param_value("filter")); } #ifdef WARTHOG_POSTHOC diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index 7a9f8cb..500a375 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -10,29 +10,33 @@ // #include "posthoc_trace.h" -#include -#include -#include #include +#include +#include +#include namespace warthog::io { -/// @brief class that produces a posthoc trace for the gridmap domain, grid must be set. +/// @brief class that produces a posthoc trace for the gridmap domain, grid +/// must be set. class grid_trace : public posthoc_trace { public: using node = search::search_node; using posthoc_trace::posthoc_trace; - void set_grid(domain::gridmap* grid) noexcept + void + set_grid(domain::gridmap* grid) noexcept { grid_ = grid; } - void print_posthoc_header() override; + void + print_posthoc_header() override; - void begin_search(int id, const search::search_problem_instance& pi); + void + begin_search(int id, const search::search_problem_instance& pi); void expand_node(const node& current) const; diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 7569d38..66320bc 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -5,49 +5,51 @@ // // Logging utility. // -// The log_sink is a non-owning copyable struct that points to the data and logging function calls. -// All logging is performed through log_sink. -// All classes here are thread safe, follow comments for outlaying cases. +// The log_sink is a non-owning copyable struct that points to the data and +// logging function calls. All logging is performed through log_sink. All +// classes here are thread safe, follow comments for outlaying cases. // // Special classes inherit log_sink to provide default functionality. // log_sink_std should be used to write to std::cout and std::cerr. // log_sink_stream pipe to a stream or open a file stream. // // The logs are made to be logged to a certain log_level. -// The WARTHOG_TRACE and others in this header provide interfaces to log to a special -// logger<> class. This logger<> class knows at compile time the minimum level to log, -// and with the use of a macro will be compiled out if that level was not set at compile time -// when used with the logging macros. +// The WARTHOG_TRACE and others in this header provide interfaces to log to a +// special logger<> class. This logger<> class knows at compile time the +// minimum level to log, and with the use of a macro will be compiled out if +// that level was not set at compile time when used with the logging macros. // -// The global logger can be acquired with the glog() (logger<>) or glogs() (log_sink), and set with -// set_glog(log_sink). The log level of the global logger is set though the WARTHOG_LOG definition, -// with a default of 1(debug) for debug builds, and 2(information) for release (NDEBUG defined). -// Macros like WARTHOG_GTRACE will automatically write to the global logger. +// The global logger can be acquired with the glog() (logger<>) or glogs() +// (log_sink), and set with set_glog(log_sink). The log level of the global +// logger is set though the WARTHOG_LOG definition, with a default of 1(debug) +// for debug builds, and 2(information) for release (NDEBUG defined). Macros +// like WARTHOG_GTRACE will automatically write to the global logger. // // The _IF will only log if a runtime if true. // The _FMT uses the std::format from C++20 to format the output messages. -// The logging utilities uses dynamic memory std::strings to produce the final log message strings, -// thus is better to be disabled for release builds through the log level. +// The logging utilities uses dynamic memory std::strings to produce the final +// log message strings, thus is better to be disabled for release builds +// through the log level. // // Log messages produce a messaged as "[TIME LEVEL] msg". -// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be overridden with -// define cstring WARTHOG_LOG_TIME. -// Clock is in local system time, but can be set to UTC by definiong WARTHOG_LOG_CLOCK_UTC. +// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be +// overridden with define cstring WARTHOG_LOG_TIME. Clock is in local system +// time, but can be set to UTC by definiong WARTHOG_LOG_CLOCK_UTC. // // @author: Ryan Hechenberger // @created: 2025-09-09 // -#include -#include -#include -#include #include #include +#include +#include +#include #include #include +#include #include -#include +#include // default log levels, also for global logger #ifdef WARTHOG_LOG @@ -69,7 +71,8 @@ #ifdef WARTHOG_LOG_CLOCK_UTC #define WARTHOG_LOG_NOW (std::chrono::utc_clock::now()) #else -#define WARTHOG_LOG_NOW (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) +#define WARTHOG_LOG_NOW \ + (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) #endif namespace warthog::io @@ -77,13 +80,13 @@ namespace warthog::io enum class log_level { - TRACE = 0, - DEBUG = 1, + TRACE = 0, + DEBUG = 1, INFORMATION = 2, - WARNING = 3, - ERROR = 4, - CRITICAL = 5, - NONE = 6 + WARNING = 3, + ERROR = 4, + CRITICAL = 5, + NONE = 6 }; /// @brief the log class used to write out to a log. @@ -93,29 +96,32 @@ enum class log_level /// log will write if able (data != null && call[level] != null). struct log_sink { - using call_type = void (void*, std::string_view msg); - void* data = nullptr; + using call_type = void(void*, std::string_view msg); + void* data = nullptr; std::array(log_level::NONE)> call = {}; - constexpr void log(log_level level, std::string_view msg) const + constexpr void + log(log_level level, std::string_view msg) const { - if (can_log(level)) - { - (*call[static_cast(level)])(data, msg); - } + if(can_log(level)) { (*call[static_cast(level)])(data, msg); } } - constexpr bool can_log(log_level level) const noexcept + constexpr bool + can_log(log_level level) const noexcept { - return static_cast(level) < call.size() && data != nullptr && call[static_cast(level)] != nullptr; + return static_cast(level) < call.size() && data != nullptr + && call[static_cast(level)] != nullptr; } - constexpr const log_sink& sink() const noexcept { return *this; } + constexpr const log_sink& + sink() const noexcept + { + return *this; + } }; /// @brief concept that check if class is shaped with a log_sink. -template -concept LogSink = requires (Sink S, log_level level, std::string_view msg) -{ +template +concept LogSink = requires(Sink S, log_level level, std::string_view msg) { S.log(level, msg); { S.can_log(level) } -> std::same_as; { std::as_const(S).sink() } -> std::convertible_to; @@ -123,19 +129,25 @@ concept LogSink = requires (Sink S, log_level level, std::string_view msg) /// @brief sink to cout|cerr /// -/// Is thread safe as long as std::ios_base::sync_with_stdio(false) has not been called. -/// Can copy go log_sink and then be destructed without issue. +/// Is thread safe as long as std::ios_base::sync_with_stdio(false) has not +/// been called. Can copy go log_sink and then be destructed without issue. struct log_sink_std : log_sink { // cerr = true will sink to cerr, false to cout log_sink_std(); log_sink_std(bool cerr = true); - static void write_trace(void*, std::string_view msg); - static void write_debug(void*, std::string_view msg); - static void write_information(void*, std::string_view msg); - static void write_warning(void*, std::string_view msg); - static void write_error(void*, std::string_view msg); - static void write_critical(void*, std::string_view msg); + static void + write_trace(void*, std::string_view msg); + static void + write_debug(void*, std::string_view msg); + static void + write_information(void*, std::string_view msg); + static void + write_warning(void*, std::string_view msg); + static void + write_error(void*, std::string_view msg); + static void + write_critical(void*, std::string_view msg); }; /// @brief sink to a passed stream, or open and own a filestream. @@ -146,20 +158,35 @@ class log_sink_stream : public log_sink { public: log_sink_stream(); - log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + log_sink_stream( + const std::filesystem::path& filename, + std::ios_base::openmode mode + = std::ios_base::out | std::ios_base::app); log_sink_stream(std::ostream* stream); ~log_sink_stream() = default; - void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); - void open(std::ostream& stream); - void open_stderr(); - - static void write_trace(void*, std::string_view msg); - static void write_debug(void*, std::string_view msg); - static void write_information(void*, std::string_view msg); - static void write_warning(void*, std::string_view msg); - static void write_error(void*, std::string_view msg); - static void write_critical(void*, std::string_view msg); + void + open( + const std::filesystem::path& filename, + std::ios_base::openmode mode + = std::ios_base::out | std::ios_base::app); + void + open(std::ostream& stream); + void + open_stderr(); + + static void + write_trace(void*, std::string_view msg); + static void + write_debug(void*, std::string_view msg); + static void + write_information(void*, std::string_view msg); + static void + write_warning(void*, std::string_view msg); + static void + write_error(void*, std::string_view msg); + static void + write_critical(void*, std::string_view msg); protected: std::ostream* log_stream_ = nullptr; @@ -174,169 +201,249 @@ static_assert(LogSink, "log_stream_sink must be a log_sink."); /// /// A wrapper class for a log_sink that allows to control the log_level. /// If just using a log_sink, it will always log at every level. -/// This class is handled specially by the logging macros to only log if log_level -/// is a min_level. -/// When setting a long_sink, will clear all pointers below MinLevel. -template (WARTHOG_DEFAULT_LOG_LEVEL)> +/// This class is handled specially by the logging macros to only log if +/// log_level is a min_level. When setting a long_sink, will clear all pointers +/// below MinLevel. +template< + log_level MinLevel = static_cast(WARTHOG_DEFAULT_LOG_LEVEL)> struct logger : log_sink { constexpr logger() noexcept = default; constexpr logger(log_sink sink) : log_sink(sink) { - for (auto& c : call | std::views::take(static_cast(MinLevel))) { + for(auto& c : call | std::views::take(static_cast(MinLevel))) + { c = nullptr; } } - - constexpr logger& operator=(const logger&) noexcept = default; - constexpr logger& operator=(log_sink sink) noexcept + + constexpr logger& + operator=(const logger&) noexcept + = default; + constexpr logger& + operator=(log_sink sink) noexcept { - for (auto& c : call | std::views::take(static_cast(MinLevel))) { + for(auto& c : call | std::views::take(static_cast(MinLevel))) + { c = nullptr; } return *this; } /// @brief Overrides MinLevel to level. Can only raise, not lower it. - /// @param level - constexpr void set_min_level(log_level level) noexcept + /// @param level + constexpr void + set_min_level(log_level level) noexcept { - assert(static_cast(level) <= static_cast(log_level::NONE)); - for (auto& c : call | std::views::take(static_cast(level))) { + assert( + static_cast(level) + <= static_cast(log_level::NONE)); + for(auto& c : call | std::views::take(static_cast(level))) + { c = nullptr; } } constexpr static log_level min_level = MinLevel; - template - constexpr static bool supports_level = MinLevel != log_level::NONE && static_cast(L) >= static_cast(MinLevel); + template + constexpr static bool supports_level = MinLevel != log_level::NONE + && static_cast(L) >= static_cast(MinLevel); }; -namespace details { -template +namespace details +{ +template struct is_logger : std::false_type { }; -template +template struct is_logger> : std::true_type { }; }; -template +template concept Logger = details::is_logger::value; -template -concept LoggerLevel = Logger && requires { - requires Log::template supports_level; -}; - -#define WARTHOG_LOG(lg,level,msg) lg.log(msg) - -#define WARTHOG_LOG_LEVEL_(cond,lg,level,msg) \ -{if constexpr (::warthog::io::Logger) { \ - if constexpr (::warthog::io::LoggerLevel) { \ - if (cond) \ - lg.log(level, msg); \ - } \ -} else { \ - if (cond) \ - lg.log(level, msg); \ -}} +template +concept LoggerLevel + = Logger && requires { requires Log::template supports_level; }; + +#define WARTHOG_LOG(lg, level, msg) lg.log(msg) + +#define WARTHOG_LOG_LEVEL_(cond, lg, level, msg) \ + { \ + if constexpr(::warthog::io::Logger) \ + { \ + if constexpr(::warthog::io::LoggerLevel) \ + { \ + if(cond) lg.log(level, msg); \ + } \ + } \ + else \ + { \ + if(cond) lg.log(level, msg); \ + } \ + } // Write msg (string_view) to log lg. -#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::TRACE,msg) -#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::DEBUG,msg) -#define WARTHOG_INFO(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::INFORMATION,msg) -#define WARTHOG_WARN(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::WARNING,msg) -#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::ERROR,msg) -#define WARTHOG_CRIT(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::CRITICAL,msg) +#define WARTHOG_TRACE(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::TRACE, msg) +#define WARTHOG_DEBUG(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::DEBUG, msg) +#define WARTHOG_INFO(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::INFORMATION, msg) +#define WARTHOG_WARN(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::WARNING, msg) +#define WARTHOG_ERROR(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::ERROR, msg) +#define WARTHOG_CRIT(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::CRITICAL, msg) // Write msg (string_view) to log lg if runtime cond is true. -#define WARTHOG_TRACE_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::TRACE,msg) -#define WARTHOG_DEBUG_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::DEBUG,msg) -#define WARTHOG_INFO_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::INFORMATION,msg) -#define WARTHOG_WARN_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::WARNING,msg) -#define WARTHOG_ERROR_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::ERROR,msg) -#define WARTHOG_CRIT_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::CRITICAL,msg) - -#define WARTHOG_LOG_LEVEL_FMT_(cond,lg,level,...) \ -{if constexpr (::warthog::io::Logger) { \ - if constexpr (::warthog::io::LoggerLevel) { \ - if (cond) \ - lg.log(level, std::format(__VA_ARGS__)); \ - } \ -} else { \ - if (cond) \ - lg.log(level, std::format(__VA_ARGS__)); \ -}} +#define WARTHOG_TRACE_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::TRACE, msg) +#define WARTHOG_DEBUG_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::DEBUG, msg) +#define WARTHOG_INFO_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::INFORMATION, msg) +#define WARTHOG_WARN_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::WARNING, msg) +#define WARTHOG_ERROR_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::ERROR, msg) +#define WARTHOG_CRIT_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::CRITICAL, msg) + +#define WARTHOG_LOG_LEVEL_FMT_(cond, lg, level, ...) \ + { \ + if constexpr(::warthog::io::Logger) \ + { \ + if constexpr(::warthog::io::LoggerLevel) \ + { \ + if(cond) lg.log(level, std::format(__VA_ARGS__)); \ + } \ + } \ + else \ + { \ + if(cond) lg.log(level, std::format(__VA_ARGS__)); \ + } \ + } // Write formatted message to log, pass as format,args... -#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) -#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) -#define WARTHOG_INFO_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) -#define WARTHOG_WARN_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) -#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) -#define WARTHOG_CRIT_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +#define WARTHOG_TRACE_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::TRACE, __VA_ARGS__) +#define WARTHOG_DEBUG_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::DEBUG, __VA_ARGS__) +#define WARTHOG_INFO_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::INFORMATION, __VA_ARGS__) +#define WARTHOG_WARN_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::WARNING, __VA_ARGS__) +#define WARTHOG_ERROR_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::ERROR, __VA_ARGS__) +#define WARTHOG_CRIT_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::CRITICAL, __VA_ARGS__) // Write formatted message to log if cond is true, pass as format,args... -#define WARTHOG_TRACE_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) -#define WARTHOG_DEBUG_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) -#define WARTHOG_INFO_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) -#define WARTHOG_WARN_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) -#define WARTHOG_ERROR_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) -#define WARTHOG_CRIT_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +#define WARTHOG_TRACE_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::TRACE, __VA_ARGS__) +#define WARTHOG_DEBUG_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::DEBUG, __VA_ARGS__) +#define WARTHOG_INFO_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::INFORMATION, __VA_ARGS__) +#define WARTHOG_WARN_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::WARNING, __VA_ARGS__) +#define WARTHOG_ERROR_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::ERROR, __VA_ARGS__) +#define WARTHOG_CRIT_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::CRITICAL, __VA_ARGS__) // global logger using global_logger_type = logger<>; -/// @brief get the global logger, default is set to std::cerr through log_sink_std -global_logger_type& glog(); +/// @brief get the global logger, default is set to std::cerr through +/// log_sink_std +global_logger_type& +glog(); /// @brief get the global log_sink -const log_sink& glogs(); +const log_sink& +glogs(); /// @brief sets the global log_sink -void set_glog(log_sink log); +void +set_glog(log_sink log); /// @brief A logger that sets itself to glog() -/// @tparam MinLevel -template +/// @tparam MinLevel +template struct global_logger : logger { using logger = typename global_logger::logger; - constexpr global_logger() : logger(glog().sink()) - { } - - constexpr global_logger& operator=(const global_logger&) noexcept = default; + constexpr global_logger() : logger(glog().sink()) { } + + constexpr global_logger& + operator=(const global_logger&) noexcept + = default; - /// @brief Updates the sink to the new glog sink. Must be called if set_glog was used. - void resync_global() + /// @brief Updates the sink to the new glog sink. Must be called if + /// set_glog was used. + void + resync_global() { static_cast(*this) = glog().sink(); } }; // the global version of the marcos, omits the passing of a logger. -#define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(),msg) -#define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(),msg) -#define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(),msg) -#define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(),msg) -#define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(),msg) -#define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(),msg) -#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GTRACE_FMT(...) WARTHOG_TRACE_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GDEBUG_FMT(...) WARTHOG_DEBUG_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GINFO_FMT(...) WARTHOG_INFO_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GWARN_FMT(...) WARTHOG_WARN_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GERROR_FMT(...) WARTHOG_ERROR_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GCRIT_FMT(...) WARTHOG_CRIT_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GTRACE_FMT_IF(cond,...) WARTHOG_TRACE_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GDEBUG_FMT_IF(cond,...) WARTHOG_DEBUG_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GINFO_FMT_IF(cond,...) WARTHOG_INFO_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GWARN_FMT_IF(cond,...) WARTHOG_WARN_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GERROR_FMT_IF(cond,...) WARTHOG_ERROR_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GCRIT_FMT_IF(cond,...) WARTHOG_CRIT_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(), msg) +#define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(), msg) +#define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(), msg) +#define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(), msg) +#define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(), msg) +#define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(), msg) +#define WARTHOG_GTRACE_IF(cond, msg) \ + WARTHOG_TRACE_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GDEBUG_IF(cond, msg) \ + WARTHOG_DEBUG_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GINFO_IF(cond, msg) \ + WARTHOG_INFO_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GWARN_IF(cond, msg) \ + WARTHOG_WARN_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GERROR_IF(cond, msg) \ + WARTHOG_ERROR_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GCRIT_IF(cond, msg) \ + WARTHOG_CRIT_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GTRACE_FMT(...) \ + WARTHOG_TRACE_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GDEBUG_FMT(...) \ + WARTHOG_DEBUG_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GINFO_FMT(...) \ + WARTHOG_INFO_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GWARN_FMT(...) \ + WARTHOG_WARN_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GERROR_FMT(...) \ + WARTHOG_ERROR_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GCRIT_FMT(...) \ + WARTHOG_CRIT_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GTRACE_FMT_IF(cond, ...) \ + WARTHOG_TRACE_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GDEBUG_FMT_IF(cond, ...) \ + WARTHOG_DEBUG_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GINFO_FMT_IF(cond, ...) \ + WARTHOG_INFO_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GWARN_FMT_IF(cond, ...) \ + WARTHOG_WARN_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GERROR_FMT_IF(cond, ...) \ + WARTHOG_ERROR_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GCRIT_FMT_IF(cond, ...) \ + WARTHOG_CRIT_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) } // namespace warthog::io diff --git a/include/warthog/io/observer.h b/include/warthog/io/observer.h index b14b856..b8e3402 100644 --- a/include/warthog/io/observer.h +++ b/include/warthog/io/observer.h @@ -3,15 +3,19 @@ // io/observer.h // -// The observer pattern defines methods to tightly-bind user provided observers to certain event patterns. -// Provide a tuple of observers, where some event is triggered will notifiy all observers with function of event name that is callable. +// The observer pattern defines methods to tightly-bind user provided observers +// to certain event patterns. Provide a tuple of observers, where some event is +// triggered will notifiy all observers with function of event name that is +// callable. // -// These function names must be registered before use, common ones registered here. +// These function names must be registered before use, common ones registered +// here. // // To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). -// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observers. -// This will run through each element in tuple (i) and call i.[function](args...) if able. -// If i.event([function],args...) is a valid callable, calls this function first, also tries i.event([function]). +// Invoke event with observer_[function](listeners, args...) where listeners +// are tuple of observers. This will run through each element in tuple (i) and +// call i.[function](args...) if able. If i.event([function],args...) is a +// valid callable, calls this function first, also tries i.event([function]). // // @author: Ryan Hechenberger // @created: 2025-08-06 @@ -19,39 +23,50 @@ #include -/// @brief Creates a concept observer_has_[func_name] that checks of function Listener.func_name(args...) is callable. -#define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ -template \ -concept observer_has_##func_name = requires(Listener L, Args&&... args) \ -{ \ - { L.func_name(std::forward(args)...) }; \ -}; -/// @brief Creates function observer_[func_name] that calls Listener.[func_name] is callable. +/// @brief Creates a concept observer_has_[func_name] that checks of function +/// Listener.func_name(args...) is callable. +#define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ + template \ + concept observer_has_##func_name = requires(Listener L, Args&&... args) { \ + { L.func_name(std::forward(args)...) }; \ + }; +/// @brief Creates function observer_[func_name] that calls +/// Listener.[func_name] is callable. /// WARTHOG_OBSERVER_DEFINE_HAS([func_name]) must be defined. /// -/// The created function observer_[func_name] loops through all listeners in tuple L. -/// For each it will try to call: +/// The created function observer_[func_name] loops through all listeners in +/// tuple L. For each it will try to call: /// - event([func_name], args...) if able otherwise event([func_name]) if able /// - [func_name](args...) if able -#define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ -template \ -void observer_##func_name(Listeners& L, Args&&... args) \ -{ \ - if constexpr (I < std::tuple_size_v) { \ - using T = std::tuple_element_t; \ - constexpr bool has_func = observer_has_##func_name ; \ - if constexpr (observer_has_event) { std::get(L).event( #func_name , std::forward(args)... ); } \ - else if constexpr (observer_has_event) { std::get(L).event( #func_name ); } \ - if constexpr (has_func) { \ - std::get(L).func_name(std::forward(args)...); \ - } \ - observer_##func_name (L, std::forward(args)...); \ - } \ -} +#define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ + template \ + void observer_##func_name(Listeners& L, Args&&... args) \ + { \ + if constexpr(I < std::tuple_size_v) \ + { \ + using T = std::tuple_element_t; \ + constexpr bool has_func = observer_has_##func_name; \ + if constexpr(observer_has_event) \ + { \ + std::get(L).event( \ + #func_name, std::forward(args)...); \ + } \ + else if constexpr(observer_has_event) \ + { \ + std::get(L).event(#func_name); \ + } \ + if constexpr(has_func) \ + { \ + std::get(L).func_name(std::forward(args)...); \ + } \ + observer_##func_name(L, std::forward(args)...); \ + } \ + } -/// @brief Defines a observer_[func_name] function and observer_has_[func_name] concept. -#define WARTHOG_OBSERVER_DEFINE(func_name) \ - WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ +/// @brief Defines a observer_[func_name] function and observer_has_[func_name] +/// concept. +#define WARTHOG_OBSERVER_DEFINE(func_name) \ + WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ WARTHOG_OBSERVER_DEFINE_CALL(func_name) namespace warthog::io diff --git a/include/warthog/io/posthoc_trace.h b/include/warthog/io/posthoc_trace.h index c1e9c4c..a36a1c3 100644 --- a/include/warthog/io/posthoc_trace.h +++ b/include/warthog/io/posthoc_trace.h @@ -17,52 +17,66 @@ namespace warthog::io /// @brief base posthoc observer class. /// -/// Set the search_id to set which search the trace is printed for, by default will not trace. -/// event begin_search and end_search will setup the trace to print only a specified. -/// Inherit to create new trace format by overriding print_posthoc_header for custom header. -/// Add own events to print posthoc event to stream() if (*this) holds true, -/// (*this) holds true iff id == search_id for event begin_search once, and ends at end_search. +/// Set the search_id to set which search the trace is printed for, by default +/// will not trace. event begin_search and end_search will setup the trace to +/// print only a specified. Inherit to create new trace format by overriding +/// print_posthoc_header for custom header. Add own events to print posthoc +/// event to stream() if (*this) holds true, +/// (*this) holds true iff id == search_id for event begin_search once, and +/// ends at end_search. class posthoc_trace : public stream_observer { public: using stream_observer::stream_observer; // override print the header if not already printed - virtual void print_posthoc_header(); + virtual void + print_posthoc_header(); - int search_id() const noexcept { return search_id_; } - void search_id(int sid) noexcept { search_id_ = sid; } + int + search_id() const noexcept + { + return search_id_; + } + void + search_id(int sid) noexcept + { + search_id_ = sid; + } - /// @brief begin_search event, if overridden then must still be called first (only id argument is needed), + /// @brief begin_search event, if overridden then must still be called + /// first (only id argument is needed), /// then checked if can write to stream. - /// @param id the search id, (*this) will become true if id == search_id_ && done_trace_ == false - template - void begin_search(int id, Args&&...) + /// @param id the search id, (*this) will become true if id == search_id_ + /// && done_trace_ == false + template + void + begin_search(int id, Args&&...) { do_trace_ = false; - if (done_trace_) return; // do not repeat a trace - if (stream_observer::operator bool() && id == search_id_) { - do_trace_ = true; + if(done_trace_) return; // do not repeat a trace + if(stream_observer::operator bool() && id == search_id_) + { + do_trace_ = true; done_trace_ = true; print_posthoc_header(); } } - /// @brief end_search event, if overridden then must still be called first (no args needed). - template - void end_search(Args&&...) + /// @brief end_search event, if overridden then must still be called first + /// (no args needed). + template + void + end_search(Args&&...) { do_trace_ = false; } /// @brief returns true if trace can be outputted, false otherwise. - operator bool() const noexcept - { - return do_trace_; - } + operator bool() const noexcept { return do_trace_; } protected: - int search_id_ = -1; - bool do_trace_ = false; + int search_id_ = -1; + bool do_trace_ = false; bool done_trace_ = false; }; diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index afb8216..10471bc 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -3,22 +3,23 @@ // io/stream_observer.h // -// The stream observer is a base class for observers that can open and own a filestream, -// or pass another filestream. +// The stream observer is a base class for observers that can open and own a +// filestream, or pass another filestream. // // Is designed to be used with observer tuples. // Inherited class will call stream() to get the current stream for output. -// Using the observer methodology, event functions will be given that will write to output in certain ways. +// Using the observer methodology, event functions will be given that will +// write to output in certain ways. // // @author: Ryan Hechenberger // @created: 2025-08-01 // -#include -#include -#include #include +#include #include +#include +#include namespace warthog::io { @@ -26,37 +27,58 @@ namespace warthog::io /// @brief A base-stream observer. /// /// Inherit and give event functions for observers to call. -/// shared_stream_t is a shared_ptr to an std::ostream, and can be passed around to other stream_observer. +/// shared_stream_t is a shared_ptr to an std::ostream, and can be passed +/// around to other stream_observer. class stream_observer { public: using shared_stream_t = std::shared_ptr; - stream_observer() = default; - stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + stream_observer() = default; + stream_observer( + const std::filesystem::path& filename, + std::ios_base::openmode mode = std::ios_base::out); stream_observer(std::ostream& stream); stream_observer(const shared_stream_t& stream); ~stream_observer(); /// @brief open a file stream, store in a shared_stream_t - void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + void + stream_open( + const std::filesystem::path& filename, + std::ios_base::openmode mode = std::ios_base::out); /// @brief pass an existing stream, must remain in - void stream(std::ostream& stream); + void + stream(std::ostream& stream); /// @brief pass a shared stream - void stream_share(const shared_stream_t& stream); + void + stream_share(const shared_stream_t& stream); /// @brief copy stream from another stream_observer - void stream_share(const stream_observer& stream); + void + stream_share(const stream_observer& stream); /// @brief sets to std::cout - void stream_stdout(); + void + stream_stdout(); /// @brief sets to std::cerr - void stream_stderr(); + void + stream_stderr(); /// @brief unsets the stream - void clear_stream(); + void + clear_stream(); operator bool() const noexcept { return stream_ != nullptr; } /// @brief undefined behaviour if no stream is open (asserts on debug) - std::ostream& stream() const noexcept { assert(stream_ != nullptr); return *stream_; } - const shared_stream_t& shared_stream() const noexcept { return shared_stream_; } + std::ostream& + stream() const noexcept + { + assert(stream_ != nullptr); + return *stream_; + } + const shared_stream_t& + shared_stream() const noexcept + { + return shared_stream_; + } private: std::ostream* stream_ = nullptr; diff --git a/include/warthog/search/problem_instance.h b/include/warthog/search/problem_instance.h index eb16556..9f66875 100644 --- a/include/warthog/search/problem_instance.h +++ b/include/warthog/search/problem_instance.h @@ -88,25 +88,30 @@ convert_problem_instance_to_search(const problem_instance& pi, Domain& d) std::ostream& operator<<(std::ostream& str, const problem_instance& pi); std::ostream& -operator<<( - std::ostream& str, const search_problem_instance& pi); +operator<<(std::ostream& str, const search_problem_instance& pi); } // namespace warthog::search template<::warthog::Identity STATE> struct std::formatter<::warthog::search::problem_instance_base, char> { - template - constexpr auto parse(ParseContext& ctx) const + template + constexpr auto + parse(ParseContext& ctx) const { return ctx.begin(); } template - FmtContext::iterator format(const ::warthog::search::problem_instance_base& pi, FmtContext& ctx) const + FmtContext::iterator + format( + const ::warthog::search::problem_instance_base& pi, + FmtContext& ctx) const { - return std::format_to(ctx.out(), "problem instance[{}]; start:{} target:{} search_id:{}", - typeid(typename STATE::tag).name(), pi.start_.id, pi.target_.id, pi.instance_id_); + return std::format_to( + ctx.out(), "problem instance[{}]; start:{} target:{} search_id:{}", + typeid(typename STATE::tag).name(), pi.start_.id, pi.target_.id, + pi.instance_id_); } }; diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 7ac705f..90b9eba 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -8,8 +8,8 @@ // #include -#include #include +#include #include @@ -274,22 +274,26 @@ struct cmp_less_search_node_f_only std::ostream& operator<<(std::ostream& str, const warthog::search::search_node& sn); -template <> +template<> struct std::formatter<::warthog::search::search_node, char> { - template - constexpr auto parse(ParseContext& ctx) const + template + constexpr auto + parse(ParseContext& ctx) const { return ctx.begin(); } template - FmtContext::iterator format(const ::warthog::search::search_node& s, FmtContext& ctx) const - { - return std::format_to(ctx.out(), "search_node id:{} p_id:{} g:{} f:{} ub:{} expanded:{} search_number:{}", - s.get_id().id, s.get_parent().id, - s.get_g(), s.get_f(), s.get_ub(), - s.get_expanded(), s.get_search_number()); + FmtContext::iterator + format(const ::warthog::search::search_node& s, FmtContext& ctx) const + { + return std::format_to( + ctx.out(), + "search_node id:{} p_id:{} g:{} f:{} ub:{} expanded:{} " + "search_number:{}", + s.get_id().id, s.get_parent().id, s.get_g(), s.get_f(), s.get_ub(), + s.get_expanded(), s.get_search_number()); } }; diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index b22378e..bfaac52 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -99,19 +99,25 @@ feasible( if(next->get_f() > par->get_max_cost_cutoff()) { - WARTHOG_GINFO_FMT_IF(par->verbose_, "cost cutoff {} > {}", next->get_f(), par->get_max_cost_cutoff()); + WARTHOG_GINFO_FMT_IF( + par->verbose_, "cost cutoff {} > {}", next->get_f(), + par->get_max_cost_cutoff()); return false; } if(met->nodes_expanded_ >= par->get_max_expansions_cutoff()) { - WARTHOG_GINFO_FMT_IF(par->verbose_, "expansions cutoff {} > {}", met->nodes_expanded_, par->get_max_expansions_cutoff()); + WARTHOG_GINFO_FMT_IF( + par->verbose_, "expansions cutoff {} > {}", met->nodes_expanded_, + par->get_max_expansions_cutoff()); return false; } if(met->time_elapsed_nano_ > par->get_max_time_cutoff()) { - WARTHOG_GINFO_FMT_IF(par->verbose_, "time cutoff {} > {}", met->time_elapsed_nano_, par->get_max_time_cutoff()); + WARTHOG_GINFO_FMT_IF( + par->verbose_, "time cutoff {} > {}", met->time_elapsed_nano_, + par->get_max_time_cutoff()); return false; } diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index cb41aaa..6b75ced 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -15,9 +15,9 @@ #include "search_parameters.h" #include "solution.h" #include "uds_traits.h" -#include #include #include +#include #include #include #include @@ -40,7 +40,8 @@ namespace warthog::search // used determine if a search should continue or terminate. // (default: search for any solution, until OPEN is exhausted) template< - typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>, + typename H, typename E, typename Q = util::pqueue_min, + typename L = std::tuple<>, admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> @@ -49,7 +50,8 @@ class unidirectional_search public: unidirectional_search( H* heuristic, E* expander, Q* queue, L listeners = L{}) - : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) + : heuristic_(heuristic), expander_(expander), open_(queue), + listeners_(listeners) { } ~unidirectional_search() { } @@ -182,7 +184,9 @@ class unidirectional_search if(n->get_ub() < sol->met_.ub_) { sol->met_.ub_ = n->get_ub(); - WARTHOG_GDEBUG_FMT_IF(pi->verbose_, "NEW UB: Incumbent Cost {}", sol->sum_of_edge_costs_); + WARTHOG_GDEBUG_FMT_IF( + pi->verbose_, "NEW UB: Incumbent Cost {}", + sol->sum_of_edge_costs_); } } @@ -193,7 +197,8 @@ class unidirectional_search mytimer.start(); open_->clear(); - io::observer_begin_search(listeners_, static_cast(pi->instance_id_), *pi); + io::observer_begin_search( + listeners_, static_cast(pi->instance_id_), *pi); // initialise the start node and push to OPEN { @@ -206,7 +211,8 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); + io::observer_generate_node( + listeners_, nullptr, *start, 0, UINT32_MAX); WARTHOG_GINFO_FMT_IF(pi->verbose_, "{}", *pi); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Start node: {}", *start); update_ub(start, sol, pi); @@ -252,7 +258,8 @@ class unidirectional_search open_->push(n); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Generate: {}", *n); update_ub(current, sol, pi); - io::observer_generate_node(listeners_, current, *n, gval, i); + io::observer_generate_node( + listeners_, current, *n, gval, i); continue; } } @@ -270,7 +277,8 @@ class unidirectional_search if(open_->contains(n)) { open_->decrease_key(n); - WARTHOG_GINFO_FMT_IF(pi->verbose_, "Updating: {}", *n); + WARTHOG_GINFO_FMT_IF( + pi->verbose_, "Updating: {}", *n); update_ub(current, sol, pi); continue; } @@ -278,7 +286,8 @@ class unidirectional_search if(reopen()) { open_->push(n); - WARTHOG_GINFO_FMT_IF(pi->verbose_, "Reopen: {}", *n); + WARTHOG_GINFO_FMT_IF( + pi->verbose_, "Reopen: {}", *n); update_ub(current, sol, pi); sol->met_.nodes_reopen_++; continue; @@ -300,15 +309,17 @@ class unidirectional_search sol->met_.nodes_surplus_ = open_->size(); sol->met_.heap_ops_ = open_->get_heap_ops(); - WARTHOG_GINFO_IF(pi->verbose_ && sol->sum_of_edge_costs_ == warthog::COST_MAX, "Search failed; no solution exists."); + WARTHOG_GINFO_IF( + pi->verbose_ && sol->sum_of_edge_costs_ == warthog::COST_MAX, + "Search failed; no solution exists."); } }; template< - typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>> -unidirectional_search( - H* heuristic, E* expander, Q* queue, - L listeners = L{}) -> unidirectional_search; + typename H, typename E, typename Q = util::pqueue_min, + typename L = std::tuple<>> +unidirectional_search(H* heuristic, E* expander, Q* queue, L listeners = L{}) + -> unidirectional_search; } // namespace warthog::search diff --git a/include/warthog/util/vec_io.h b/include/warthog/util/vec_io.h index 8f0e24c..8383c8a 100644 --- a/include/warthog/util/vec_io.h +++ b/include/warthog/util/vec_io.h @@ -1,7 +1,6 @@ #ifndef WARTHOG_UTIL_VEC_IO_H #define WARTHOG_UTIL_VEC_IO_H -#include #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include template [[deprecated("TDB")]] @@ -95,7 +95,9 @@ load_vector(std::FILE* file) size_t stuffRead = std::fread(&v[0], sizeof(T), s, file); if((int)stuffRead != s) { - WARTHOG_GERROR_FMT("we were expecting to read {} but we read {} elements instead", s, stuffRead); + WARTHOG_GERROR_FMT( + "we were expecting to read {} but we read {} elements instead", s, + stuffRead); throw std::runtime_error("std::fread failed"); } diff --git a/src/io/log.cpp b/src/io/log.cpp index d265548..423cc74 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -1,42 +1,51 @@ -#include -#include #include +#include #include +#include namespace warthog::io { -log_sink_stream::log_sink_stream() : log_sink{nullptr, - {{&log_sink_stream::write_trace,&log_sink_stream::write_debug, - &log_sink_stream::write_information,&log_sink_stream::write_warning, - &log_sink_stream::write_error,&log_sink_stream::write_critical}}} +log_sink_stream::log_sink_stream() + : log_sink{ + nullptr, + {{&log_sink_stream::write_trace, &log_sink_stream::write_debug, + &log_sink_stream::write_information, + &log_sink_stream::write_warning, &log_sink_stream::write_error, + &log_sink_stream::write_critical}}} { } -log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) : log_sink_stream() +log_sink_stream::log_sink_stream( + const std::filesystem::path& filename, std::ios_base::openmode mode) + : log_sink_stream() { - data = this; + data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } log_sink_stream::log_sink_stream(std::ostream* stream) : log_sink_stream() { - data = this; + data = this; log_stream_ = stream; } -void log_sink_stream::open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void +log_sink_stream::open( + const std::filesystem::path& filename, std::ios_base::openmode mode) { - data = this; + data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -void log_sink_stream::open(std::ostream& stream) +void +log_sink_stream::open(std::ostream& stream) { data = this; owned_file_.release(); log_stream_ = &stream; } -void log_sink_stream::open_stderr() +void +log_sink_stream::open_stderr() { data = this; owned_file_.release(); @@ -45,92 +54,118 @@ void log_sink_stream::open_stderr() // log_sink_stream -log_sink_std::log_sink_std() : log_sink{ - nullptr, - {{&log_sink_std::write_trace,&log_sink_std::write_debug, - &log_sink_std::write_information,&log_sink_std::write_warning, - &log_sink_std::write_error,&log_sink_std::write_critical}}} +log_sink_std::log_sink_std() + : log_sink{ + nullptr, + {{&log_sink_std::write_trace, &log_sink_std::write_debug, + &log_sink_std::write_information, &log_sink_std::write_warning, + &log_sink_std::write_error, &log_sink_std::write_critical}}} { } -log_sink_std::log_sink_std(bool cerr) : log_sink{ - cerr ? &std::cerr : &std::cout, - {{&log_sink_std::write_trace,&log_sink_std::write_debug, - &log_sink_std::write_information,&log_sink_std::write_warning, - &log_sink_std::write_error,&log_sink_std::write_critical}}} +log_sink_std::log_sink_std(bool cerr) + : log_sink{ + cerr ? &std::cerr : &std::cout, + {{&log_sink_std::write_trace, &log_sink_std::write_debug, + &log_sink_std::write_information, &log_sink_std::write_warning, + &log_sink_std::write_error, &log_sink_std::write_critical}}} { } -void log_sink_std::write_trace(void* logger, std::string_view msg) +void +log_sink_std::write_trace(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_debug(void* logger, std::string_view msg) +void +log_sink_std::write_debug(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_information(void* logger, std::string_view msg) +void +log_sink_std::write_information(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_warning(void* logger, std::string_view msg) +void +log_sink_std::write_warning(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_error(void* logger, std::string_view msg) +void +log_sink_std::write_error(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_critical(void* logger, std::string_view msg) +void +log_sink_std::write_critical(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_stream::write_trace(void* logger, std::string_view msg) +void +log_sink_stream::write_trace(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_trace(log_stream->log_stream_, msg); } -void log_sink_stream::write_debug(void* logger, std::string_view msg) +void +log_sink_stream::write_debug(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_debug(log_stream->log_stream_, msg); } -void log_sink_stream::write_information(void* logger, std::string_view msg) +void +log_sink_stream::write_information(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_information(log_stream->log_stream_, msg); } -void log_sink_stream::write_warning(void* logger, std::string_view msg) +void +log_sink_stream::write_warning(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_warning(log_stream->log_stream_, msg); } -void log_sink_stream::write_error(void* logger, std::string_view msg) +void +log_sink_stream::write_error(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_error(log_stream->log_stream_, msg); } -void log_sink_stream::write_critical(void* logger, std::string_view msg) +void +log_sink_stream::write_critical(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); @@ -138,18 +173,22 @@ void log_sink_stream::write_critical(void* logger, std::string_view msg) log_sink_std::write_critical(log_stream->log_stream_, msg); } -namespace { +namespace +{ global_logger_type global_logger_(log_sink_std(true).sink()); } -global_logger_type& glog() +global_logger_type& +glog() { return global_logger_; } -const log_sink& glogs() +const log_sink& +glogs() { return global_logger_.sink(); } -void set_glog(log_sink log) +void +set_glog(log_sink log) { global_logger_ = log; } diff --git a/src/io/observer.cpp b/src/io/observer.cpp index fd8567f..92e1316 100644 --- a/src/io/observer.cpp +++ b/src/io/observer.cpp @@ -1,16 +1,17 @@ -#include -#include #include #include #include +#include +#include namespace warthog::io { -stream_observer::stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode) +stream_observer::stream_observer( + const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } stream_observer::stream_observer(std::ostream& stream) { @@ -19,49 +20,59 @@ stream_observer::stream_observer(std::ostream& stream) stream_observer::stream_observer(const shared_stream_t& stream) { shared_stream_ = stream; - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } stream_observer::~stream_observer() = default; -void stream_observer::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void +stream_observer::stream_open( + const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } -void stream_observer::stream(std::ostream& stream) +void +stream_observer::stream(std::ostream& stream) { - stream_ = &stream; + stream_ = &stream; shared_stream_ = nullptr; } -void stream_observer::stream_share(const shared_stream_t& stream) +void +stream_observer::stream_share(const shared_stream_t& stream) { shared_stream_ = stream; - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } -void stream_observer::stream_share(const stream_observer& stream) +void +stream_observer::stream_share(const stream_observer& stream) { shared_stream_ = stream.shared_stream_; - stream_ = stream.stream_; + stream_ = stream.stream_; } -void stream_observer::stream_stdout() +void +stream_observer::stream_stdout() { shared_stream_ = nullptr; - stream_ = &std::cout; + stream_ = &std::cout; } -void stream_observer::stream_stderr() +void +stream_observer::stream_stderr() { shared_stream_ = nullptr; - stream_ = &std::cerr; + stream_ = &std::cerr; } -void stream_observer::clear_stream() +void +stream_observer::clear_stream() { shared_stream_ = nullptr; - stream_ = nullptr; + stream_ = nullptr; } -void posthoc_trace::print_posthoc_header() +void +posthoc_trace::print_posthoc_header() { - if (*this) { + if(*this) + { stream() << R"posthoc(version: 1.4.0 events: )posthoc"; diff --git a/src/io/traces.cpp b/src/io/traces.cpp index 0a78304..47e8e21 100644 --- a/src/io/traces.cpp +++ b/src/io/traces.cpp @@ -1,14 +1,15 @@ -#include #include #include +#include namespace warthog::io { - -void grid_trace::print_posthoc_header() +void +grid_trace::print_posthoc_header() { - if (*this) { + if(*this) + { stream() << R"posthoc(version: 1.4.0 views: cell: @@ -53,92 +54,102 @@ void grid_trace::print_posthoc_header() } } -void grid_trace::begin_search(int id, const search::search_problem_instance& pi) +void +grid_trace::begin_search(int id, const search::search_problem_instance& pi) { posthoc_trace::begin_search(id); - if (*this) { - if (grid_ == nullptr) { + if(*this) + { + if(grid_ == nullptr) + { throw std::logic_error("grid_trace::grid_ is null"); } uint32_t x, y; grid_->to_unpadded_xy(pi.start_, x, y); - stream() << std::format(" - {{ type: source, id: {}, x: {}, y: {} }}\n", - pi.start_.id, x, y - ); + stream() << std::format( + " - {{ type: source, id: {}, x: {}, y: {} }}\n", pi.start_.id, x, + y); grid_->to_unpadded_xy(pi.target_, x, y); - stream() << std::format(" - {{ type: destination, id: {}, x: {}, y: {} }}\n", - pi.target_.id, x, y - ); + stream() << std::format( + " - {{ type: destination, id: {}, x: {}, y: {} }}\n", + pi.target_.id, x, y); } } void grid_trace::expand_node(const node& current) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (auto cpid = current.get_parent(); !cpid.is_none()) { + if(auto cpid = current.get_parent(); !cpid.is_none()) + { pid = std::format(", pId: {}", cpid.id); } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: expand, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g() - ); + stream() << std::format( + " - {{ type: expand, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g()); } } void grid_trace::relax_node(const node& current) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (auto cpid = current.get_parent(); !cpid.is_none()) { + if(auto cpid = current.get_parent(); !cpid.is_none()) + { pid = std::format(", pId: {}", cpid.id); } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: relax, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g() - ); + stream() << std::format( + " - {{ type: relax, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g()); } } - void grid_trace::close_node(const node& current) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (auto cpid = current.get_parent(); !cpid.is_none()) { + if(auto cpid = current.get_parent(); !cpid.is_none()) + { pid = std::format(", pId: {}", cpid.id); } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: close, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g() - ); + stream() << std::format( + " - {{ type: close, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g()); } } - void -grid_trace::generate_node(const node* parent, const node& child, cost_t, uint32_t) const +grid_trace::generate_node( + const node* parent, const node& child, cost_t, uint32_t) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (parent != nullptr) { + if(parent != nullptr) + { pid = std::format(", pId: {}", parent->get_id().id); } uint32_t x, y; grid_->to_unpadded_xy(child.get_id(), x, y); - stream() << std::format(" - {{ type: generate, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - child.get_id().id, pid, x, y, child.get_f(), child.get_g() - ); + stream() << std::format( + " - {{ type: generate, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + child.get_id().id, pid, x, y, child.get_f(), child.get_g()); } } diff --git a/src/search/problem_instance.cpp b/src/search/problem_instance.cpp index 7022a1a..602f767 100644 --- a/src/search/problem_instance.cpp +++ b/src/search/problem_instance.cpp @@ -12,8 +12,7 @@ operator<<(std::ostream& str, const problem_instance& pi) } std::ostream& -operator<<( - std::ostream& str, const search_problem_instance& pi) +operator<<(std::ostream& str, const search_problem_instance& pi) { pi.print(str); diff --git a/src/search/search_metrics.cpp b/src/search/search_metrics.cpp index 910ac13..64ed2d2 100644 --- a/src/search/search_metrics.cpp +++ b/src/search/search_metrics.cpp @@ -1,6 +1,7 @@ #include -namespace warthog::search { +namespace warthog::search +{ std::ostream& operator<<(std::ostream& str, const warthog::search::search_metrics& met) From 9622516a2a12415498b5ba5252488939ef8b8025 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Tue, 21 Oct 2025 15:38:35 +1100 Subject: [PATCH 20/61] added template node_pool --- include/warthog/memory/node_pool.h | 8 +- include/warthog/memory/typed_pool.h | 169 ++++++++++++++++++++++++++++ src/memory/node_pool.cpp | 6 +- 3 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 include/warthog/memory/typed_pool.h diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index cc5f5e8..12ffa90 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -25,8 +25,8 @@ #include "cpool.h" #include - -#include +#include +#include namespace warthog::memory { @@ -63,8 +63,8 @@ class node_pool init(size_t nblocks); size_t num_blocks_; - search::search_node** blocks_; - cpool* blockspool_; + std::unique_ptr blocks_; + std::unique_ptr blockspool_; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/memory/typed_pool.h b/include/warthog/memory/typed_pool.h new file mode 100644 index 0000000..5dbf247 --- /dev/null +++ b/include/warthog/memory/typed_pool.h @@ -0,0 +1,169 @@ +#ifndef WARTHOG_MEMORY_TYPED_POOL_H +#define WARTHOG_MEMORY_TYPED_POOL_H + +// memory/typed_pool.h +// +// A memory pool for nodes of a templated type. +// +// This in intended as a temperary pool before an allocation +// framework is added. +// +// @author: Ryan Hechenberger +// @created: 2025-10-25 +// + +#include "node_pool.h" + +namespace warthog::memory +{ + +template +class typed_pool +{ +public: + using value_type = T; + + typed_pool(size_t num_nodes); + ~typed_pool(); + + // return a warthog::search_node object corresponding to the given id. + // if the node has already been generated, return a pointer to the + // previous instance; otherwise allocate memory for a new object. + value_type* + generate(pad_id node_id); + + // return a pre-allocated pointer. if the corresponding node has not + // been allocated yet, return null + value_type* + get_ptr(pad_id node_id); + + size_t + mem(); + +private: + void + init(size_t nblocks); + + size_t num_blocks_ = 0; + std::unique_ptr blocks_; + std::unique_ptr blockspool_; + // uint64_t* node_init_; + // uint64_t node_init_sz_; +}; + +template +typed_pool::typed_pool(size_t num_nodes) +{ + init(num_nodes); +} + +template +void +typed_pool::init(size_t num_nodes) +{ + num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; + blocks_ = std::make_unique(num_blocks_); + for(size_t i = 0; i < num_blocks_; i++) + { + blocks_[i] = 0; + } + + // by default: + // allocate one chunk of memory of size + // DEFAULT_CHUNK_SIZE and assign addresses + // from that pool in order to generate blocks of nodes. when the pool is + // full, cpool pre-allocates more, one chunk at a time. + size_t block_sz = node_pool_ns::NBS * sizeof(value_type); + blockspool_ = std::make_unique(block_sz, 1); +} + +template +typed_pool::~typed_pool() +{ + // delete [] node_init_; + + blockspool_->reclaim(); + delete blockspool_; + + for(size_t i = 0; i < num_blocks_; i++) + { + if(blocks_[i] != 0) + { + // std::cerr << "deleting block: "< +value_type* +typed_pool::generate(pad_id node_id) +{ + sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; + + // id outside the pool address range + if(block_id > num_blocks_) { return 0; } + + // add a new block of nodes if necessary + if(!blocks_[block_id]) + { + // std::cerr << "generating block: "<allocate()) + value_type[node_pool_ns::NBS]; + + // initialise memory + sn_id_t current_id = sn_id_t{node_id} - list_id; + for(uint32_t i = 0; i < node_pool_ns::NBS; i += 8) + { + new(&blocks_[block_id][i]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 1]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 2]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 3]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 4]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 5]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 6]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 7]) + value_type(pad_id{current_id++}); + } + } + + // return the node from its position in the assocated block + return &(blocks_[block_id][list_id]); +} + +template +value_type* +typed_pool::get_ptr(pad_id node_id) +{ + sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; + + // id outside the pool address range + if(block_id > num_blocks_) { return 0; } + + if(!blocks_[block_id]) { return 0; } + return &(blocks_[block_id][list_id]); +} + +template +size_t +typed_pool::mem() +{ + size_t bytes + = sizeof(*this) + blockspool_->mem() + num_blocks_ * sizeof(void*); + + return bytes; +} + +} // namespace warthog::memory + +#endif // WARTHOG_MEMORY_NODE_POOL_H diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index d2df7a1..f81232a 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -14,7 +14,7 @@ void node_pool::init(size_t num_nodes) { num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = new search::search_node*[num_blocks_]; + blocks_ = std::make_unique(num_blocks_); for(size_t i = 0; i < num_blocks_; i++) { blocks_[i] = 0; @@ -26,7 +26,7 @@ node_pool::init(size_t num_nodes) // from that pool in order to generate blocks of nodes. when the pool is // full, cpool pre-allocates more, one chunk at a time. size_t block_sz = node_pool_ns::NBS * sizeof(search::search_node); - blockspool_ = new cpool(block_sz, 1); + blockspool_ = std::make_unique(block_sz, 1); } node_pool::~node_pool() @@ -34,7 +34,6 @@ node_pool::~node_pool() // delete [] node_init_; blockspool_->reclaim(); - delete blockspool_; for(size_t i = 0; i < num_blocks_; i++) { @@ -44,7 +43,6 @@ node_pool::~node_pool() blocks_[i] = 0; } } - delete[] blocks_; } search::search_node* From 01f6f2c78027506aeb13a2e6b7e796ba534bef37 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 22 Oct 2025 15:54:10 +1100 Subject: [PATCH 21/61] added traits to unidirectional_search --- include/warthog/search/uds_traits.h | 90 ++++++++++++++++++- .../warthog/search/unidirectional_search.h | 48 ++++++---- 2 files changed, 122 insertions(+), 16 deletions(-) diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index bfaac52..34f339a 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -133,7 +133,7 @@ enum class reopen_policy // decide whether to renopen nodes already expanded (when their g-value // can be improved). we handle the positive case via specialisation. -template +template inline bool reopen() { @@ -147,6 +147,94 @@ reopen() return true; } +struct uds_default_traits +{ + // using node = std::tuple<>; + // using observer = std::tuple<>; + static constexpr admissibility_criteria ac = admissibility_criteria::any; + static constexpr feasibility_criteria fc = feasibility_criteria::until_exhaustion; + static constexpr reopen_policy rp = reopen_policy::no; +}; + +template < + typename N = search_node, + typename L = std::tuple<>, + admissibility_criteria AC = admissibility_criteria::any, + feasibility_criteria FC = feasibility_criteria::until_exhaustion, + reopen_policy RP = reopen_policy::no> +struct uds_traits +{ + using node = N; + using observer = L; + static constexpr admissibility_criteria ac = AC; + static constexpr feasibility_criteria fc = FC; + static constexpr reopen_policy rp = RP; +}; + +namespace details { + +template +struct uds_trait_node +{ + using type = search_node; +}; +template + requires requires { typename Traits::node; } +struct uds_trait_node +{ + using type = typename Traits::node; +}; + +template +struct uds_trait_observer +{ + using type = search_node; +}; +template + requires requires { typename Traits::observer; } +struct uds_trait_observer +{ + using type = typename Traits::observer; +}; + +} // namespace details + +template +using uds_trait_node = typename details::uds_trait_node::type; + +template +using uds_trait_observer = typename details::uds_trait_observer::type; + +template +inline consteval admissibility_criteria uds_trait_ac() noexcept +{ + if constexpr (requires { { Traits::ac } -> std::same_as; }) { + return Traits::ac; + } else { + return admissibility_criteria::any; + } +} + +template +inline consteval feasibility_criteria uds_trait_fc() noexcept +{ + if constexpr (requires { { Traits::fc } -> std::same_as; }) { + return Traits::fc; + } else { + return feasibility_criteria::until_exhaustion; + } +} + +template +inline consteval reopen_policy uds_trait_rp() noexcept +{ + if constexpr (requires { { Traits::rp } -> std::same_as; }) { + return Traits::rp; + } else { + return reopen_policy::no; + } +} + } // namespace warthog::search #endif // WARTHOG_SEARCH_UDS_TRAITS_H diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 6b75ced..ec9edf2 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -41,20 +41,28 @@ namespace warthog::search // (default: search for any solution, until OPEN is exhausted) template< typename H, typename E, typename Q = util::pqueue_min, - typename L = std::tuple<>, - admissibility_criteria AC = admissibility_criteria::any, - feasibility_criteria FC = feasibility_criteria::until_exhaustion, - reopen_policy RP = reopen_policy::no> -class unidirectional_search + typename Traits = uds_default_traits> +class unidirectional_search_full { public: - unidirectional_search( + using traits = Traits; + using search_node = uds_trait_node; + using L = uds_trait_observer; + + static constexpr admissibility_criteria AC = uds_trait_ac(); + static constexpr feasibility_criteria FC = uds_trait_fc(); + static constexpr reopen_policy RP = uds_trait_rp(); + + unidirectional_search_full( H* heuristic, E* expander, Q* queue, L listeners = L{}) : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) { } + unidirectional_search_full(const unidirectional_search_full& other) = delete; + ~unidirectional_search_full() = default; - ~unidirectional_search() { } + unidirectional_search_full& + operator=(const unidirectional_search_full& other) = delete; void get_pathcost(problem_instance* pi, search_parameters* par, solution* sol) @@ -138,14 +146,6 @@ class unidirectional_search Q* open_; [[no_unique_address]] L listeners_; - // no copy ctor - unidirectional_search(const unidirectional_search& other) { } - unidirectional_search& - operator=(const unidirectional_search& other) - { - return *this; - } - /** * Initialise a new 'search_node' for the ongoing search given the parent * node (@param current). @@ -315,6 +315,24 @@ class unidirectional_search } }; +// Keep for backward compatibility. +// Done as class instead of using to support user-defined deduction +template< + typename H, typename E, typename Q = util::pqueue_min, + typename L = std::tuple<>, + admissibility_criteria AC = admissibility_criteria::any, + feasibility_criteria FC = feasibility_criteria::until_exhaustion, + reopen_policy RP = reopen_policy::no> +class unidirectional_search : public unidirectional_search_full > +{ +public: + using unidirectional_search_full = typename unidirectional_search_full >::unidirectional_search_full; + + using unidirectional_search_full::unidirectional_search_full; +}; + template< typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>> From 75863a43f5dabc351b15e685a5cb1e421525ae7b Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 6 Nov 2025 15:17:10 +1100 Subject: [PATCH 22/61] added custom search_node to node_pool --- include/warthog/memory/node_pool.h | 41 +++++++++++++-- include/warthog/search/search_node.h | 36 ++++++------- src/memory/node_pool.cpp | 76 ++++++++-------------------- 3 files changed, 73 insertions(+), 80 deletions(-) diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index 12ffa90..ef1433d 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -27,23 +27,51 @@ #include #include #include +#include namespace warthog::memory { namespace node_pool_ns { -static const uint64_t NBS = 8; // node block size; set this >= 8 -static const uint64_t LOG2_NBS = 3; -static const uint64_t NBS_MASK = 7; +constexpr uint64_t LOG2_NBS = 6; // node block size = 2^n, n >= 3 +constexpr uint64_t NBS = 1 << LOG2_NBS; +constexpr uint64_t NBS_MASK = NBS - 1; +static_assert(LOG2_NBS >= 3, "must be at least 3 for size of 8"); } class node_pool { public: + node_pool(); node_pool(size_t num_nodes); ~node_pool(); + template T = search::search_node> + void set_type() noexcept + { + size_t block_sz = node_pool_ns::NBS * sizeof(T); + blockspool_ = std::make_unique(block_sz, 1); + create_block_ = [](void* block_p, sn_id_t block_id) noexcept + { + assert(block_p != nullptr); + T* block = reinterpret_cast(block_p); + pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; + for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) + { + std::construct_at(block + i, current_id); + } + }; + get_ptr_ = [](void** blocks, pad_id node_id) noexcept -> search::search_node* + { + sn_id_t block_id = static_cast(node_id) >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; + assert(blocks[block_id] != nullptr); + T* block = reinterpret_cast(blocks[block_id]); + return static_cast(block + list_id); + }; + } + // return a warthog::search_node object corresponding to the given id. // if the node has already been generated, return a pointer to the // previous instance; otherwise allocate memory for a new object. @@ -61,10 +89,13 @@ class node_pool private: void init(size_t nblocks); + void release(); - size_t num_blocks_; - std::unique_ptr blocks_; + size_t num_blocks_ = 0; + std::unique_ptr blocks_; std::unique_ptr blockspool_; + void (*create_block_)(void* block, sn_id_t bock_id) noexcept = nullptr; + search::search_node* (*get_ptr_)(void** blocks, pad_id start_id) noexcept = nullptr; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 90b9eba..c91e2ff 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -16,13 +16,10 @@ namespace warthog::search { -class search_node +struct search_node { -public: search_node(pad_id id = pad_id::max()) - : id_(id), parent_id_(warthog::SN_ID_MAX), g_(warthog::COST_MAX), - f_(warthog::COST_MAX), ub_(warthog::COST_MAX), status_(0), - priority_(warthog::INF32), search_number_(UINT32_MAX) + : id_(id) { refcount_++; } @@ -32,12 +29,12 @@ class search_node inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, - cost_t ub = warthog::COST_MAX) + cost_t h = warthog::COST_MAX) { parent_id_ = parent_id; f_ = f; g_ = g; - ub_ = ub; + h_ = h; search_number_ = search_number; status_ = false; } @@ -129,13 +126,13 @@ class search_node inline cost_t get_ub() const { - return ub_; + return h_; } inline void set_ub(cost_t ub) { - ub_ = ub; + h_ = ub; } inline void @@ -144,7 +141,7 @@ class search_node assert(g < g_); f_ = (f_ - g_) + g; g_ = g; - if(ub_ < warthog::COST_MAX) { ub_ = (ub_ - g_) + g; } + if(h_ < warthog::COST_MAX) { h_ = (h_ - g_) + g; } parent_id_ = parent_id; } @@ -209,7 +206,7 @@ class search_node out << "search_node id:" << get_id().id; out << " p_id: "; out << parent_id_.id; - out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ + out << " g: " << g_ << " f: " << this->get_f() << " ub: " << h_ << " expanded: " << get_expanded() << " " << " search_number_: " << search_number_; } @@ -226,19 +223,18 @@ class search_node return refcount_; } -private: - pad_id id_; - pad_id parent_id_; + pad_id id_ = pad_id(warthog::SN_ID_MAX); + pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); - cost_t g_; - cost_t f_; - cost_t ub_; + cost_t g_ = warthog::COST_MAX; + cost_t f_ = warthog::COST_MAX; + cost_t h_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? - uint8_t status_; // open or closed - uint32_t priority_; // expansion priority + uint8_t status_ = 0; // open or closed + uint32_t priority_ = warthog::INF32; // expansion priority - uint32_t search_number_; + uint32_t search_number_ = UINT32_MAX; static uint32_t refcount_; }; diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index f81232a..2011bb5 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -5,16 +5,19 @@ namespace warthog::memory { -node_pool::node_pool(size_t num_nodes) : blocks_(0) +node_pool::node_pool() +{ } +node_pool::node_pool(size_t num_nodes) { init(num_nodes); + set_type(); } void node_pool::init(size_t num_nodes) { num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = std::make_unique(num_blocks_); + blocks_ = std::make_unique(num_blocks_); for(size_t i = 0; i < num_blocks_; i++) { blocks_[i] = 0; @@ -25,80 +28,43 @@ node_pool::init(size_t num_nodes) // DEFAULT_CHUNK_SIZE and assign addresses // from that pool in order to generate blocks of nodes. when the pool is // full, cpool pre-allocates more, one chunk at a time. - size_t block_sz = node_pool_ns::NBS * sizeof(search::search_node); - blockspool_ = std::make_unique(block_sz, 1); } -node_pool::~node_pool() -{ - // delete [] node_init_; - - blockspool_->reclaim(); - - for(size_t i = 0; i < num_blocks_; i++) - { - if(blocks_[i] != 0) - { - // std::cerr << "deleting block: "<> node_pool_ns::LOG2_NBS; sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } + assert(block_id < num_blocks_); // add a new block of nodes if necessary if(!blocks_[block_id]) { // std::cerr << "generating block: "<allocate()) - search::search_node[node_pool_ns::NBS]; - - // initialise memory - sn_id_t current_id = sn_id_t{node_id} - list_id; - for(uint32_t i = 0; i < node_pool_ns::NBS; i += 8) - { - new(&blocks_[block_id][i]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 1]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 2]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 3]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 4]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 5]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 6]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 7]) - search::search_node(pad_id{current_id++}); - } + blocks_[block_id] = blockspool_->allocate(); + create_block_(blocks_[block_id], block_id); } // return the node from its position in the assocated block - return &(blocks_[block_id][list_id]); + return get_ptr_(blocks_.get(), node_id); +} + +void node_pool::release() +{ + num_blocks_ = 0; + blocks_ = nullptr; + blockspool_ = nullptr; + create_block_ = nullptr; + get_ptr_ = nullptr; } search::search_node* node_pool::get_ptr(pad_id node_id) { - sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } - - if(!blocks_[block_id]) { return 0; } - return &(blocks_[block_id][list_id]); + assert((sn_id_t{node_id} >> node_pool_ns::LOG2_NBS) < num_blocks_); + return get_ptr_(blocks_.get(), node_id); } size_t From 92616fec1fac49bd331c0b42d9c990efae67be77 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 6 Nov 2025 15:26:10 +1100 Subject: [PATCH 23/61] improve node_pool get_ptr efficency --- include/warthog/memory/node_pool.h | 13 +++---------- src/memory/node_pool.cpp | 13 ++++++++----- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index ef1433d..ddf1297 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -50,6 +50,7 @@ class node_pool template T = search::search_node> void set_type() noexcept { + block_type_sizes_ = sizeof(search::search_node); size_t block_sz = node_pool_ns::NBS * sizeof(T); blockspool_ = std::make_unique(block_sz, 1); create_block_ = [](void* block_p, sn_id_t block_id) noexcept @@ -62,14 +63,6 @@ class node_pool std::construct_at(block + i, current_id); } }; - get_ptr_ = [](void** blocks, pad_id node_id) noexcept -> search::search_node* - { - sn_id_t block_id = static_cast(node_id) >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; - assert(blocks[block_id] != nullptr); - T* block = reinterpret_cast(blocks[block_id]); - return static_cast(block + list_id); - }; } // return a warthog::search_node object corresponding to the given id. @@ -92,10 +85,10 @@ class node_pool void release(); size_t num_blocks_ = 0; - std::unique_ptr blocks_; + std::unique_ptr blocks_; + size_t block_type_sizes_ = 0; std::unique_ptr blockspool_; void (*create_block_)(void* block, sn_id_t bock_id) noexcept = nullptr; - search::search_node* (*get_ptr_)(void** blocks, pad_id start_id) noexcept = nullptr; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index 2011bb5..05545ea 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -17,7 +17,7 @@ void node_pool::init(size_t num_nodes) { num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = std::make_unique(num_blocks_); + blocks_ = std::make_unique(num_blocks_); for(size_t i = 0; i < num_blocks_; i++) { blocks_[i] = 0; @@ -43,12 +43,12 @@ node_pool::generate(pad_id node_id) if(!blocks_[block_id]) { // std::cerr << "generating block: "<allocate(); + blocks_[block_id] = reinterpret_cast(blockspool_->allocate()); create_block_(blocks_[block_id], block_id); } // return the node from its position in the assocated block - return get_ptr_(blocks_.get(), node_id); + return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); } void node_pool::release() @@ -57,14 +57,17 @@ void node_pool::release() blocks_ = nullptr; blockspool_ = nullptr; create_block_ = nullptr; - get_ptr_ = nullptr; } search::search_node* node_pool::get_ptr(pad_id node_id) { assert((sn_id_t{node_id} >> node_pool_ns::LOG2_NBS) < num_blocks_); - return get_ptr_(blocks_.get(), node_id); + sn_id_t block_id = static_cast(node_id) >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; + assert(block_id < num_blocks_); + assert(blocks_[block_id] != nullptr); + return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); } size_t From dac0e918eb2c61dc987d0428fd6547396a5d9efb Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 4 Dec 2025 15:56:20 +1100 Subject: [PATCH 24/61] scenario serializer in progress --- include/warthog/io/grid.h | 10 +++ include/warthog/io/scenario.h | 142 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 include/warthog/io/scenario.h diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index 401688a..f3d1fd6 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -1,6 +1,16 @@ #ifndef WARTHOG_IO_GRID_H #define WARTHOG_IO_GRID_H +// io/grid.h +// +// Read utility for gridmap. +// +// Supported MovingAI map format. +// +// @author: Ryan Hechenberger +// @created: 2025-06-01 +// + #include #include #include diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h new file mode 100644 index 0000000..c7d9f8c --- /dev/null +++ b/include/warthog/io/scenario.h @@ -0,0 +1,142 @@ +#ifndef WARTHOG_UTIL_SCENARIO_MANAGER_H +#define WARTHOG_UTIL_SCENARIO_MANAGER_H + +// io/scenario.h +// +// Read/write utilities for scenario files. +// +// Supported formats for read: +// - GPPC 1.0 format (as at 2012 Grid-based Path Planning Competition) +// (fields: bucket,map,mapwidth,mapheight,sx,sy,gx,gy,distance) +// - DIMACS format (as at the 9th DIMACS Implementation Challenge) +// (fields: q [source-id] [target-id]) +// +// Supported formats for generate/write: +// - GPPC 1.0 format (as at 2012 Grid-based Path Planning Competition) +// - Dynamic format (tbd link) +// +// @author: dharabor & Ryan Hechenberger +// @created: 2025-12-04 +// + +#include +#include +#include +#include +#include +#include +#include + +namespace warthog::util +{ + +enum class scenario_version : uint8_t +{ + version1, + version2, +}; +enum class dist_type : uint8_t +{ + octile_ncc, + octile_cc, + manhatten, + anyangle_ncc, + anyangle_cc, + dist_count, +}; + +struct scenario_query +{ + int64_t bucket; + std::string map; + double start_x; + double start_y; + double goal_x; + double goal_y; + std::array dist; + + void reset() + { + (*this) = {}; + } +}; + +class scenario_serialize +{ +public: + /// @brief reads in file version information, and sets version accessable via get_version() + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{} (0) + std::errc read_version(std::istream* in = nullptr); + /// @brief with header (without version) information. + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{} (0) + /// + /// With version1: peeks first query to gain map name + /// With version2: gets map width/height, available costs and patch filename + std::errc read_header(std::istream* in = nullptr); + /// @brief calls read_version then read_header for full header + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{} (0) + std::errc read_version_header(std::istream* in = nullptr); + + void set_scenario_filename(std::filesystem::path&& filename) + { + m_scenario_filename = std::move(filename); + } + const std::filesystem::path& get_scenario_filename() const noexcept + { + return m_scenario_filename; + } + + void set_map_filename(std::filesystem::path&& filename) + { + m_map_filename = std::move(filename); + } + const std::filesystem::path& get_map_filename() const noexcept + { + return m_map_filename; + } + + void set_version(scenario_version version) noexcept + { + m_version = version; + m_dist.reset(); + if (version == scenario_version::version1) + m_dist.set(0, true); + } + scenario_version get_version() const noexcept + { + return m_version; + } + + /// @brief the types of dist used, only relivent for version2, otherwise octile_ncc(0) + /// @return bitset + auto get_dist_types() const noexcept + { + return m_dist; + } + +protected: + std::istream* get_instream(std::istream* in) noexcept + { + return in != nullptr ? in : m_scenario_in; + } + +protected: + scenario_version m_version = scenario_version::version1; + std::filesystem::path m_scenario_filename; + std::filesystem::path m_map_filename; + std::istream* m_scenario_in = nullptr; + std::bitset<(size_t)dist_type::dist_count> m_dist; + + // dynamic data + std::pmr::monotonic_buffer_resource m_dyn_res; + std::pmr::string m_line; + std::pmr::vector m_dist_strings; + std::pmr::vector m_dist_id; +}; + +} // namespace warthog::util + +#endif // WARTHOG_UTIL_SCENARIO_MANAGER_H From 82aba8b8003ab1849b3d7ece1be659d101bbe0b1 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 12 Dec 2025 16:45:54 +1100 Subject: [PATCH 25/61] update include order, more scenario seriallizer --- include/warthog/io/scenario.h | 30 +++++++++++++++---- src/CMakeLists.txt | 1 + src/io/grid.cpp | 3 +- src/io/scenario.cpp | 35 ++++++++++++++++++++++ src/memory/node_pool.cpp | 1 + src/search/gridmap_expansion_policy.cpp | 1 + src/search/vl_gridmap_expansion_policy.cpp | 3 +- src/util/dimacs_parser.cpp | 3 +- src/util/helpers.cpp | 3 +- src/util/scenario_manager.cpp | 3 +- src/util/timer.cpp | 3 +- 11 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 src/io/scenario.cpp diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index c7d9f8c..4a36313 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -1,5 +1,5 @@ -#ifndef WARTHOG_UTIL_SCENARIO_MANAGER_H -#define WARTHOG_UTIL_SCENARIO_MANAGER_H +#ifndef WARTHOG_IO_SCENARIO_H +#define WARTHOG_IO_SCENARIO_H // io/scenario.h // @@ -27,7 +27,7 @@ #include #include -namespace warthog::util +namespace warthog::io { enum class scenario_version : uint8_t @@ -64,6 +64,10 @@ struct scenario_query class scenario_serialize { public: + static constexpr size_t max_line_length = 2000; + scenario_serialize(); + ~scenario_serialize(); + /// @brief reads in file version information, and sets version accessable via get_version() /// @param in optional stream to use, otherwise uses internal-set stream /// @return success std::errc{} (0) @@ -118,16 +122,30 @@ class scenario_serialize } protected: - std::istream* get_instream(std::istream* in) noexcept + std::pair get_instream(std::istream* in) noexcept + { + if (in == nullptr) + in = m_scenario_in; + if (in == nullptr || !in->good()) + return {nullptr, std::errc::io_error}; + return {in, {}}; + } + std::pair get_outstream(std::ostream* out) noexcept { - return in != nullptr ? in : m_scenario_in; + if (out == nullptr) + out = m_scenario_out; + if (out == nullptr || !out->good()) + return {nullptr, std::errc::io_error}; + return {out, {}}; } + std::pair readline(std::istream* in); protected: scenario_version m_version = scenario_version::version1; std::filesystem::path m_scenario_filename; std::filesystem::path m_map_filename; std::istream* m_scenario_in = nullptr; + std::ostream* m_scenario_out = nullptr; std::bitset<(size_t)dist_type::dist_count> m_dist; // dynamic data @@ -139,4 +157,4 @@ class scenario_serialize } // namespace warthog::util -#endif // WARTHOG_UTIL_SCENARIO_MANAGER_H +#endif // WARTHOG_IO_SCENARIO_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f53bdd..7626e77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ geometry/geography.cpp geometry/geom.cpp io/grid.cpp +io/scenario.cpp memory/node_pool.cpp diff --git a/src/io/grid.cpp b/src/io/grid.cpp index 0219240..cbcbf88 100644 --- a/src/io/grid.cpp +++ b/src/io/grid.cpp @@ -1,6 +1,7 @@ +#include + #include #include -#include namespace warthog::io { diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp new file mode 100644 index 0000000..a11e40e --- /dev/null +++ b/src/io/scenario.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include + +namespace warthog::io +{ + +scenario_serialize::scenario_serialize() : m_line(max_line_length + 2, '\0', &m_dyn_res), + m_dist_strings(&m_dyn_res), + m_dist_id(&m_dyn_res) +{ } +scenario_serialize::~scenario_serialize() = default; + +std::pair scenario_serialize::readline(std::istream* in) +{ + auto [s, err] = get_instream(in); + if (err != std::errc{}) + { + return {{}, err}; + } + s->getline(m_line.data(), max_line_length); +} + +std::errc scenario_serialize::read_version(std::istream* in) +{ + auto [s, err] = get_instream(in); + if (err != std::errc{}) + { + return err; + } + +} + +} // namespace warthog::io diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index d2df7a1..b7a3576 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -1,4 +1,5 @@ #include + #include #include diff --git a/src/search/gridmap_expansion_policy.cpp b/src/search/gridmap_expansion_policy.cpp index f60470b..676e2ed 100644 --- a/src/search/gridmap_expansion_policy.cpp +++ b/src/search/gridmap_expansion_policy.cpp @@ -1,4 +1,5 @@ #include + #include #include diff --git a/src/search/vl_gridmap_expansion_policy.cpp b/src/search/vl_gridmap_expansion_policy.cpp index e8750c4..0a7945d 100644 --- a/src/search/vl_gridmap_expansion_policy.cpp +++ b/src/search/vl_gridmap_expansion_policy.cpp @@ -1,5 +1,6 @@ -#include #include + +#include #include namespace warthog::search diff --git a/src/util/dimacs_parser.cpp b/src/util/dimacs_parser.cpp index 51eb8b5..75b0f64 100644 --- a/src/util/dimacs_parser.cpp +++ b/src/util/dimacs_parser.cpp @@ -1,6 +1,7 @@ -#include #include +#include + #include #include #include diff --git a/src/util/helpers.cpp b/src/util/helpers.cpp index f3c78b2..518176b 100644 --- a/src/util/helpers.cpp +++ b/src/util/helpers.cpp @@ -1,6 +1,7 @@ +#include + #include #include -#include #include #include diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 7bbaa13..8eb0ca5 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -1,6 +1,7 @@ +#include + #include #include -#include #include #include diff --git a/src/util/timer.cpp b/src/util/timer.cpp index 702b7a3..0029b6b 100644 --- a/src/util/timer.cpp +++ b/src/util/timer.cpp @@ -1,6 +1,7 @@ -#include #include +#include + namespace warthog::util { From 2d8fb8224111d1cb723f9cfc40fd05a7bd3570c8 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 18 Dec 2025 15:13:37 +1100 Subject: [PATCH 26/61] work on scenario loading --- include/warthog/io/scenario.h | 88 ++++++++++++++++++---- src/io/scenario.cpp | 138 +++++++++++++++++++++++++++++++++- 2 files changed, 210 insertions(+), 16 deletions(-) diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 4a36313..92d03a2 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -22,10 +22,13 @@ #include #include #include +#include #include #include #include #include +#include +#include namespace warthog::io { @@ -61,28 +64,31 @@ struct scenario_query } }; +struct scenario_patch +{ + int64_t bucket; + uint32_t patch_id; + uint16_t loc_x; + uint16_t loc_y; +}; + class scenario_serialize { public: + enum class CurrentState + { + Init, + Version, + Header, + Query, + Error, + }; static constexpr size_t max_line_length = 2000; scenario_serialize(); ~scenario_serialize(); - /// @brief reads in file version information, and sets version accessable via get_version() - /// @param in optional stream to use, otherwise uses internal-set stream - /// @return success std::errc{} (0) - std::errc read_version(std::istream* in = nullptr); - /// @brief with header (without version) information. - /// @param in optional stream to use, otherwise uses internal-set stream - /// @return success std::errc{} (0) - /// - /// With version1: peeks first query to gain map name - /// With version2: gets map width/height, available costs and patch filename - std::errc read_header(std::istream* in = nullptr); - /// @brief calls read_version then read_header for full header - /// @param in optional stream to use, otherwise uses internal-set stream - /// @return success std::errc{} (0) - std::errc read_version_header(std::istream* in = nullptr); + /// @brief Resets class, including memory. Must use between seperate read/writes, needed for memory managment. + void reset(); void set_scenario_filename(std::filesystem::path&& filename) { @@ -120,6 +126,44 @@ class scenario_serialize { return m_dist; } + bool has_dist_type(dist_type d) const noexcept + { + assert((uint32_t)d < (uint32_t)dist_type::dist_count); + return m_dist.test(d); + } + + /// @brief opens scenario file get_scenario_filename() for reading + /// @param scenario use a user provided instead of get_scenario_filename() + /// @return error on operation + std::errc open_read(std::istream* scenario = nullptr); + /// @brief opens scenario file get_scenario_filename() for writing + /// @param scenario use a user provided instead of get_scenario_filename() + /// @return error on operation + std::errc open_write(std::ostream* scenario = nullptr); + + /// @return if scenario is open for reading + bool can_read(); + /// @return if scenario is open for writing + bool can_write(); + + /// @brief reads in file version information, and sets version accessable via get_version() + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{} (0) + std::errc read_version(std::istream* in = nullptr); + /// @brief with header (without version) information. + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{} (0) + /// + /// With version1: peeks first query to gain map name + /// With version2: gets map width/height, available costs and patch filename + std::errc read_header(std::istream* in = nullptr); + /// @brief calls read_version then read_header for full header + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{} (0) + std::errc read_version_header(std::istream* in = nullptr); + + std::errc read_header_v1(std::istream* in = nullptr); + std::errc read_header_v2(std::istream* in = nullptr); protected: std::pair get_instream(std::istream* in) noexcept @@ -140,19 +184,33 @@ class scenario_serialize } std::pair readline(std::istream* in); + std::errc read_query_line_v1(scenario_query& query); + std::errc read_query_line_v2(scenario_query& query); + std::errc read_patch_line_v2(scenario_patch& query); + protected: + CurrentState m_state = CurrentState::Init; scenario_version m_version = scenario_version::version1; std::filesystem::path m_scenario_filename; std::filesystem::path m_map_filename; std::istream* m_scenario_in = nullptr; std::ostream* m_scenario_out = nullptr; + std::unique_ptr m_scenario_file; std::bitset<(size_t)dist_type::dist_count> m_dist; + uint32_t m_map_width; + uint32_t m_map_height; + int32_t m_query_at = 0; // dynamic data std::pmr::monotonic_buffer_resource m_dyn_res; std::pmr::string m_line; std::pmr::vector m_dist_strings; std::pmr::vector m_dist_id; + + // shared temp parameter + // TODO: replace with a custom string stream that does not allocate memory + std::istringstream m_iss; + std::string m_token; }; } // namespace warthog::util diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index a11e40e..1a5f4ff 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace warthog::io { @@ -24,12 +25,147 @@ std::pair scenario_serialize::readline(std::istream std::errc scenario_serialize::read_version(std::istream* in) { - auto [s, err] = get_instream(in); + if (m_state != CurrentState::Init) + { + return std::errc::state_not_recoverable; + } + std::errc err; + std::tie(in, err) = get_instream(in); + if (err != std::errc{}) + { + m_state = CurrentState::Error; + return err; + } + std::string_view line; + std::tie(line, err) = readline(in); if (err != std::errc{}) { + m_state = CurrentState::Error; + return err; + } + m_iss.str(std::string(line)); + int version; + if (!(*in >> m_token >> version)) { + m_state = CurrentState::Error; + return std::errc::io_error; + } + if (version < 1 || version > 2) { + m_state = CurrentState::Error; + return std::errc::invalid_argument; + } + m_version = version == 1 ? scenario_version::version1 : scenario_version::version2; + m_state = CurrentState::Header; + return std::errc{}; +} + +std::errc scenario_serialize::read_header_v1(std::istream* in) +{ + if (m_state != CurrentState::Header) + { + return std::errc::state_not_recoverable; + } + std::errc err; + std::tie(in, err) = get_instream(in); + if (err != std::errc{}) + { + m_state = CurrentState::Error; + return err; + } + std::string_view line; + std::tie(line, err) = readline(in); + if (err != std::errc{}) + { + m_state = CurrentState::Error; return err; } + if (!(*in >> m_token >> version)) + return std::errc::io_error; + if (version < 1 || version > 2) + return std::errc::invalid_argument; + m_version = version == 1 ? scenario_version::version1 : scenario_version::version2; + m_state = CurrentState::Header; + return std::errc{}; +} + +std::errc scenario_serialize::read_query_line_v1(scenario_query& query) +{ + int width, height; + if (!(m_iss >> query.bucket >> query.map >> width >> height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y >> query.dist[0])) + { + return std::errc::io_error; + } + if (!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) || !std::isfinite(query.dist[0])) + { + return std::errc::invalid_argument; + } + m_iss >> std::ws; + if (m_iss.eof()) + { + return std::errc::invalid_argument; + } + return std::errc{}; +} + +std::errc scenario_serialize::read_query_line_v2(scenario_query& query) +{ + int width, height; + double d; + query.dist.fill(-1.0); + if (!(m_iss >> query.bucket >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y)) + { + return std::errc::io_error; + } + for (auto i : m_dist_id) + { + if (!(m_iss >> d)) + { + return std::errc::io_error; + } + if (!std::isfinite(d)) + { + return std::errc::invalid_argument; + } + if (i >= 0) { + query.dist[i] = d; + } + } + m_iss >> std::ws; + if (m_iss.eof()) + { + return std::errc::invalid_argument; + } + return std::errc{}; +} + +std::errc scenario_serialize::read_patch_line_v2(scenario_patch& patch) +{ + int width, height; + double d; + if (!(m_iss >> patch.bucket >> patch.patch_id >> patch.loc_x >> patch.loc_y)) + { + return std::errc::io_error; + } + for (auto i : m_dist_id) + { + if (!(m_iss >> d)) + { + return std::errc::io_error; + } + if (!std::isfinite(d)) + { + return std::errc::invalid_argument; + } + if (i >= 0) { + query.dist[i] = d; + } + } + m_iss >> std::ws; + if (m_iss.eof()) + { + return std::errc::invalid_argument; + } + return std::errc{}; } } // namespace warthog::io From 439fa3c2d6ec28c93faf4bd56cb074b879c196f2 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 12 Mar 2026 14:36:38 +1100 Subject: [PATCH 27/61] continue on dynaimc format reader --- include/warthog/io/scenario.h | 4 ++ src/io/scenario.cpp | 78 +++++++++++++++++++++++++++-------- src/util/scenario_manager.cpp | 58 ++++++++++++++++++++++++-- 3 files changed, 120 insertions(+), 20 deletions(-) diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 92d03a2..bed09a1 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -84,6 +84,7 @@ class scenario_serialize Error, }; static constexpr size_t max_line_length = 2000; + static constexpr size_t max_dimension = 15'000; scenario_serialize(); ~scenario_serialize(); @@ -108,6 +109,8 @@ class scenario_serialize return m_map_filename; } + void set_relative_map_filename(std::filesystem::path&& filename); + void set_version(scenario_version version) noexcept { m_version = version; @@ -211,6 +214,7 @@ class scenario_serialize // TODO: replace with a custom string stream that does not allocate memory std::istringstream m_iss; std::string m_token; + scenario_query m_query; }; } // namespace warthog::util diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 1a5f4ff..09f4145 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace warthog::io { @@ -71,20 +72,72 @@ std::errc scenario_serialize::read_header_v1(std::istream* in) m_state = CurrentState::Error; return err; } + // read first query line to get map + std::tie(std::ignore, err) = readline(in); + if (err != std::errc{}) + { + m_state = CurrentState::Error; + return err; + } + read_query_line_v1(m_query); + set_relative_map_filename(m_query.map); + m_state = CurrentState::Query; + return std::errc{}; +} + +std::errc scenario_serialize::read_header_v2(std::istream* in) +{ + if (m_state != CurrentState::Header) + { + return std::errc::state_not_recoverable; + } + std::errc err; + std::tie(in, err) = get_instream(in); + if (err != std::errc{}) + { + m_state = CurrentState::Error; + return err; + } + // read first query line to get map std::string_view line; + // height std::tie(line, err) = readline(in); if (err != std::errc{}) { m_state = CurrentState::Error; return err; } - - if (!(*in >> m_token >> version)) + m_iss.str(std::string(line)); + if (!(m_iss >> m_token >> m_map_height)) + { + m_state = CurrentState::Error; return std::errc::io_error; - if (version < 1 || version > 2) + } + if (m_map_height < 1 || m_map_height > max_dimension) + { + m_state = CurrentState::Error; return std::errc::invalid_argument; - m_version = version == 1 ? scenario_version::version1 : scenario_version::version2; - m_state = CurrentState::Header; + } + // width + std::tie(line, err) = readline(in); + if (err != std::errc{}) + { + m_state = CurrentState::Error; + return err; + } + m_iss.str(std::string(line)); + if (!(m_iss >> m_token >> m_map_width)) + { + m_state = CurrentState::Error; + return std::errc::io_error; + } + if (m_map_width < 1 || m_map_width > max_dimension) + { + m_state = CurrentState::Error; + return std::errc::invalid_argument; + } + set_relative_map_filename(m_query.map); + m_state = CurrentState::Query; return std::errc{}; } @@ -146,20 +199,11 @@ std::errc scenario_serialize::read_patch_line_v2(scenario_patch& patch) { return std::errc::io_error; } - for (auto i : m_dist_id) + if (patch.loc_x >= m_map_width || patch.loc_y > m_map_height) { - if (!(m_iss >> d)) - { - return std::errc::io_error; - } - if (!std::isfinite(d)) - { - return std::errc::invalid_argument; - } - if (i >= 0) { - query.dist[i] = d; - } + return std::errc::invalid_argument; } + m_iss >> std::ws; if (m_iss.eof()) { diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 8eb0ca5..374382a 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -129,7 +129,7 @@ scenario_manager::sort() */ std::filesystem::path find_map_filename( - const scenario_manager& scenmgr, std::filesystem::path sfilename) + const scenario_manager& scenmgr, const std::filesystem::path& sfilename) { namespace fs = std::filesystem; const auto& mapname = scenmgr.get_experiment(0)->map(); @@ -159,10 +159,62 @@ find_map_filename( if(!sfilename.empty()) { // replace extenion with .map - auto mapfile = sfilename.replace_extension(".map"); + auto mapfile = sfilename; + mapfile.replace_extension(".map"); if(fs::is_regular_file(mapfile)) return mapfile; // remove extension and check it is now .map (test for .map.scen) - mapfile = sfilename.replace_extension(""); + mapfile.replace_extension(""); + if(mapfile.extension() == ".map" && fs::is_regular_file(mapfile)) + return mapfile; + } + // no clear way to deduce map, return empty path for no success + return {}; +} +/** + * Finds a matching map file to a scenario. + * Take mappath as scenmgr map name. scendir as partent(sfilename), or + * current_working_dir. Returns path in order below: If mappath is absolute + * path: if exists return mappath, else return no path. If scendir/mappath + * exists: return scendir/mappath. If sfilename != '' and replace sfilename ext + * to '.map': if exists return that. If sfilename != '' and remove sfilename + * ext: if new extension is '.map' and exists return that. Return empty path. + */ +std::filesystem::path +find_map_filename( + const std::filesystem::path& mapname, const std::filesystem::path& sfilename) +{ + namespace fs = std::filesystem; + // scen file has a map name designated. + if(!mapname.empty()) + { + auto mappath = fs::path(mapname); + // absolute path, try to use that only. + if(mappath.is_absolute()) + { + if(fs::is_regular_file(mappath)) { return mappath; } + else { return {}; } + } + // path is relative path + auto spath = !sfilename.empty() ? sfilename.parent_path() + : fs::current_path(); + // check relative path from either sfilename directory or + // current_working_directory + if(auto append_path = spath / mapname; + fs::is_regular_file(append_path)) + { + return append_path; + } + } + // if a scenario filename was presented, try to deduce map from scenario + // filename + if(!sfilename.empty()) + { + // replace extenion with .map + auto mapfile = sfilename; + mapfile.replace_extension(".map"); + if(fs::is_regular_file(mapfile)) return mapfile; + // remove extension and check it is now .map (test for .map.scen) + mapfile.replace_extension(""); if(mapfile.extension() == ".map" && fs::is_regular_file(mapfile)) return mapfile; } From 18950eba5e4bd3a0f554bcaf71ebaa8b50683c2d Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 12:11:03 +1100 Subject: [PATCH 28/61] fix bugs to allow dynamic typed node_pool --- include/warthog/memory/node_pool.h | 63 ++++++++++- include/warthog/memory/typed_pool.h | 169 ---------------------------- src/memory/node_pool.cpp | 21 +++- 3 files changed, 75 insertions(+), 178 deletions(-) delete mode 100644 include/warthog/memory/typed_pool.h diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index ddf1297..1a2be5e 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace warthog::memory { @@ -43,24 +44,69 @@ static_assert(LOG2_NBS >= 3, "must be at least 3 for size of 8"); class node_pool { public: + struct data_deleter_ptr + { + void (*del)(void*) = nullptr; + + constexpr data_deleter_ptr() noexcept = default; + constexpr data_deleter_ptr(void (*p)(void*)) noexcept : del(p) + { } + + void operator()(void *data) const noexcept + { + (*del)(data); + } + }; node_pool(); node_pool(size_t num_nodes); ~node_pool(); - template T = search::search_node> - void set_type() noexcept + template T = search::search_node, typename... NodeArgs> + void set_type(NodeArgs... node_args) noexcept { - block_type_sizes_ = sizeof(search::search_node); + clear(); + using arg_type = std::tuple; + if constexpr (sizeof...(NodeArgs) != 0) { + create_block_data_ = static_cast(new arg_type(std::forward(node_args)...)); + } + block_type_sizes_ = sizeof(T); size_t block_sz = node_pool_ns::NBS * sizeof(T); blockspool_ = std::make_unique(block_sz, 1); - create_block_ = [](void* block_p, sn_id_t block_id) noexcept + create_block_ = [](void* block_p, sn_id_t block_id, void* data) noexcept { assert(block_p != nullptr); T* block = reinterpret_cast(block_p); pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) { - std::construct_at(block + i, current_id); + if constexpr (sizeof...(NodeArgs) == 0) { + std::construct_at(block + i, current_id); + } else { + assert(data != nullptr); + std::apply([block_i=block + i, current_id](NodeX&&... args) { + std::construct_at(block_i, current_id, args...); + }, *static_cast(data) + ); + } + } + }; + clear_ = [](node_pool& np) noexcept + { + if (np.blocks_) { + for (size_t i = 0; i < np.num_blocks_; ++i) { + T* nodes = reinterpret_cast(np.blocks_[i]); + if (nodes != nullptr) { + for(uint32_t j = 0; j < node_pool_ns::NBS; ++j) { + std::destroy_at(nodes + j); + } + } + } + } + if constexpr (sizeof...(NodeArgs) == 0) { + if (!np.create_block_data_) { + arg_type* args = static_cast(np.create_block_data_); + delete args; + } } }; } @@ -79,6 +125,9 @@ class node_pool size_t mem(); + // reset nodes + void clear(); + private: void init(size_t nblocks); @@ -88,7 +137,9 @@ class node_pool std::unique_ptr blocks_; size_t block_type_sizes_ = 0; std::unique_ptr blockspool_; - void (*create_block_)(void* block, sn_id_t bock_id) noexcept = nullptr; + void (*create_block_)(void* block, sn_id_t bock_id, void* data) noexcept = nullptr; + void (*clear_)(node_pool& np) = nullptr; + void *create_block_data_; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/memory/typed_pool.h b/include/warthog/memory/typed_pool.h deleted file mode 100644 index 5dbf247..0000000 --- a/include/warthog/memory/typed_pool.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef WARTHOG_MEMORY_TYPED_POOL_H -#define WARTHOG_MEMORY_TYPED_POOL_H - -// memory/typed_pool.h -// -// A memory pool for nodes of a templated type. -// -// This in intended as a temperary pool before an allocation -// framework is added. -// -// @author: Ryan Hechenberger -// @created: 2025-10-25 -// - -#include "node_pool.h" - -namespace warthog::memory -{ - -template -class typed_pool -{ -public: - using value_type = T; - - typed_pool(size_t num_nodes); - ~typed_pool(); - - // return a warthog::search_node object corresponding to the given id. - // if the node has already been generated, return a pointer to the - // previous instance; otherwise allocate memory for a new object. - value_type* - generate(pad_id node_id); - - // return a pre-allocated pointer. if the corresponding node has not - // been allocated yet, return null - value_type* - get_ptr(pad_id node_id); - - size_t - mem(); - -private: - void - init(size_t nblocks); - - size_t num_blocks_ = 0; - std::unique_ptr blocks_; - std::unique_ptr blockspool_; - // uint64_t* node_init_; - // uint64_t node_init_sz_; -}; - -template -typed_pool::typed_pool(size_t num_nodes) -{ - init(num_nodes); -} - -template -void -typed_pool::init(size_t num_nodes) -{ - num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = std::make_unique(num_blocks_); - for(size_t i = 0; i < num_blocks_; i++) - { - blocks_[i] = 0; - } - - // by default: - // allocate one chunk of memory of size - // DEFAULT_CHUNK_SIZE and assign addresses - // from that pool in order to generate blocks of nodes. when the pool is - // full, cpool pre-allocates more, one chunk at a time. - size_t block_sz = node_pool_ns::NBS * sizeof(value_type); - blockspool_ = std::make_unique(block_sz, 1); -} - -template -typed_pool::~typed_pool() -{ - // delete [] node_init_; - - blockspool_->reclaim(); - delete blockspool_; - - for(size_t i = 0; i < num_blocks_; i++) - { - if(blocks_[i] != 0) - { - // std::cerr << "deleting block: "< -value_type* -typed_pool::generate(pad_id node_id) -{ - sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } - - // add a new block of nodes if necessary - if(!blocks_[block_id]) - { - // std::cerr << "generating block: "<allocate()) - value_type[node_pool_ns::NBS]; - - // initialise memory - sn_id_t current_id = sn_id_t{node_id} - list_id; - for(uint32_t i = 0; i < node_pool_ns::NBS; i += 8) - { - new(&blocks_[block_id][i]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 1]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 2]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 3]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 4]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 5]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 6]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 7]) - value_type(pad_id{current_id++}); - } - } - - // return the node from its position in the assocated block - return &(blocks_[block_id][list_id]); -} - -template -value_type* -typed_pool::get_ptr(pad_id node_id) -{ - sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } - - if(!blocks_[block_id]) { return 0; } - return &(blocks_[block_id][list_id]); -} - -template -size_t -typed_pool::mem() -{ - size_t bytes - = sizeof(*this) + blockspool_->mem() + num_blocks_ * sizeof(void*); - - return bytes; -} - -} // namespace warthog::memory - -#endif // WARTHOG_MEMORY_NODE_POOL_H diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index 05545ea..9f176c1 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -13,6 +13,11 @@ node_pool::node_pool(size_t num_nodes) set_type(); } +node_pool::~node_pool() +{ + clear(); +} + void node_pool::init(size_t num_nodes) { @@ -30,8 +35,6 @@ node_pool::init(size_t num_nodes) // full, cpool pre-allocates more, one chunk at a time. } -node_pool::~node_pool() = default; - search::search_node* node_pool::generate(pad_id node_id) { @@ -44,7 +47,7 @@ node_pool::generate(pad_id node_id) { // std::cerr << "generating block: "<(blockspool_->allocate()); - create_block_(blocks_[block_id], block_id); + create_block_(blocks_[block_id], block_id, create_block_data_); } // return the node from its position in the assocated block @@ -79,4 +82,16 @@ node_pool::mem() return bytes; } + +void node_pool::clear() +{ + if (clear_) { + (*clear_)(*this); + } + blockspool_ = nullptr; + create_block_ = nullptr; + clear_ = nullptr; + create_block_data_ = nullptr; +} + } // namespace warthog::memory From 645317408456bfc17b7086b096999633a218e466 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 12:11:17 +1100 Subject: [PATCH 29/61] udi bugfix for template type --- include/warthog/search/uds_traits.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 34f339a..3f2b466 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -208,7 +208,7 @@ using uds_trait_observer = typename details::uds_trait_observer::type; template inline consteval admissibility_criteria uds_trait_ac() noexcept { - if constexpr (requires { { Traits::ac } -> std::same_as; }) { + if constexpr (requires { { Traits::ac } -> std::convertible_to; }) { return Traits::ac; } else { return admissibility_criteria::any; @@ -218,7 +218,7 @@ inline consteval admissibility_criteria uds_trait_ac() noexcept template inline consteval feasibility_criteria uds_trait_fc() noexcept { - if constexpr (requires { { Traits::fc } -> std::same_as; }) { + if constexpr (requires { { Traits::fc } -> std::convertible_to; }) { return Traits::fc; } else { return feasibility_criteria::until_exhaustion; @@ -228,7 +228,7 @@ inline consteval feasibility_criteria uds_trait_fc() noexcept template inline consteval reopen_policy uds_trait_rp() noexcept { - if constexpr (requires { { Traits::rp } -> std::same_as; }) { + if constexpr (requires { { Traits::rp } -> std::convertible_to; }) { return Traits::rp; } else { return reopen_policy::no; From ac432fffea6dfe53276a33c47cf62fb3cd1a95d8 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 12:14:48 +1100 Subject: [PATCH 30/61] udi search traits must be same type but do not check reference --- include/warthog/search/uds_traits.h | 7 ++++--- include/warthog/util/template.h | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 3f2b466..0c93a5c 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -14,6 +14,7 @@ #include "search_metrics.h" #include +#include namespace warthog::search { @@ -208,7 +209,7 @@ using uds_trait_observer = typename details::uds_trait_observer::type; template inline consteval admissibility_criteria uds_trait_ac() noexcept { - if constexpr (requires { { Traits::ac } -> std::convertible_to; }) { + if constexpr (requires { { Traits::ac } -> util::same_as_rmref; }) { return Traits::ac; } else { return admissibility_criteria::any; @@ -218,7 +219,7 @@ inline consteval admissibility_criteria uds_trait_ac() noexcept template inline consteval feasibility_criteria uds_trait_fc() noexcept { - if constexpr (requires { { Traits::fc } -> std::convertible_to; }) { + if constexpr (requires { { Traits::fc } -> util::same_as_rmref; }) { return Traits::fc; } else { return feasibility_criteria::until_exhaustion; @@ -228,7 +229,7 @@ inline consteval feasibility_criteria uds_trait_fc() noexcept template inline consteval reopen_policy uds_trait_rp() noexcept { - if constexpr (requires { { Traits::rp } -> std::convertible_to; }) { + if constexpr (requires { { Traits::rp } -> util::same_as_rmref; }) { return Traits::rp; } else { return reopen_policy::no; diff --git a/include/warthog/util/template.h b/include/warthog/util/template.h index ce94e3d..d0a0b0b 100644 --- a/include/warthog/util/template.h +++ b/include/warthog/util/template.h @@ -108,6 +108,11 @@ choose_integer_sequence(auto value, TemplateFunc&& tfunc) value, std::forward(tfunc)); } +template +concept same_as_rmref = std::same_as, std::remove_reference_t>; +template +concept same_as_rmcvref = std::same_as, std::remove_cvref_t>; + } // namespace warthog::util #endif // WARTHOG_UTIL_CAST_H From b196854a192679b2b6c3707c78493d4fb86df66f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 03:51:45 +0000 Subject: [PATCH 31/61] auto clang-format action --- include/warthog/memory/node_pool.h | 105 +++++++++++------- include/warthog/search/search_node.h | 10 +- include/warthog/search/uds_traits.h | 76 +++++++------ .../warthog/search/unidirectional_search.h | 22 ++-- include/warthog/util/template.h | 10 +- src/memory/node_pool.cpp | 35 +++--- 6 files changed, 147 insertions(+), 111 deletions(-) diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index 1a2be5e..4d780ff 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -24,11 +24,11 @@ // #include "cpool.h" -#include -#include -#include #include +#include +#include #include +#include namespace warthog::memory { @@ -36,7 +36,7 @@ namespace warthog::memory namespace node_pool_ns { constexpr uint64_t LOG2_NBS = 6; // node block size = 2^n, n >= 3 -constexpr uint64_t NBS = 1 << LOG2_NBS; +constexpr uint64_t NBS = 1 << LOG2_NBS; constexpr uint64_t NBS_MASK = NBS - 1; static_assert(LOG2_NBS >= 3, "must be at least 3 for size of 8"); } @@ -49,10 +49,10 @@ class node_pool void (*del)(void*) = nullptr; constexpr data_deleter_ptr() noexcept = default; - constexpr data_deleter_ptr(void (*p)(void*)) noexcept : del(p) - { } + constexpr data_deleter_ptr(void (*p)(void*)) noexcept : del(p) { } - void operator()(void *data) const noexcept + void + operator()(void* data) const noexcept { (*del)(data); } @@ -61,50 +61,66 @@ class node_pool node_pool(size_t num_nodes); ~node_pool(); - template T = search::search_node, typename... NodeArgs> - void set_type(NodeArgs... node_args) noexcept + template< + std::derived_from T = search::search_node, + typename... NodeArgs> + void + set_type(NodeArgs... node_args) noexcept { clear(); using arg_type = std::tuple; - if constexpr (sizeof...(NodeArgs) != 0) { - create_block_data_ = static_cast(new arg_type(std::forward(node_args)...)); + if constexpr(sizeof...(NodeArgs) != 0) + { + create_block_data_ = static_cast( + new arg_type(std::forward(node_args)...)); } block_type_sizes_ = sizeof(T); - size_t block_sz = node_pool_ns::NBS * sizeof(T); - blockspool_ = std::make_unique(block_sz, 1); - create_block_ = [](void* block_p, sn_id_t block_id, void* data) noexcept - { - assert(block_p != nullptr); - T* block = reinterpret_cast(block_p); - pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; - for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) - { - if constexpr (sizeof...(NodeArgs) == 0) { - std::construct_at(block + i, current_id); - } else { - assert(data != nullptr); - std::apply([block_i=block + i, current_id](NodeX&&... args) { - std::construct_at(block_i, current_id, args...); - }, *static_cast(data) - ); - } - } + size_t block_sz = node_pool_ns::NBS * sizeof(T); + blockspool_ = std::make_unique(block_sz, 1); + create_block_ = [](void* block_p, sn_id_t block_id, + void* data) noexcept { + assert(block_p != nullptr); + T* block = reinterpret_cast(block_p); + pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; + for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) + { + if constexpr(sizeof...(NodeArgs) == 0) + { + std::construct_at(block + i, current_id); + } + else + { + assert(data != nullptr); + std::apply( + [block_i = block + i, + current_id](NodeX&&... args) { + std::construct_at(block_i, current_id, args...); + }, + *static_cast(data)); + } + } }; - clear_ = [](node_pool& np) noexcept - { - if (np.blocks_) { - for (size_t i = 0; i < np.num_blocks_; ++i) { + clear_ = [](node_pool& np) noexcept { + if(np.blocks_) + { + for(size_t i = 0; i < np.num_blocks_; ++i) + { T* nodes = reinterpret_cast(np.blocks_[i]); - if (nodes != nullptr) { - for(uint32_t j = 0; j < node_pool_ns::NBS; ++j) { + if(nodes != nullptr) + { + for(uint32_t j = 0; j < node_pool_ns::NBS; ++j) + { std::destroy_at(nodes + j); } } } } - if constexpr (sizeof...(NodeArgs) == 0) { - if (!np.create_block_data_) { - arg_type* args = static_cast(np.create_block_data_); + if constexpr(sizeof...(NodeArgs) == 0) + { + if(!np.create_block_data_) + { + arg_type* args + = static_cast(np.create_block_data_); delete args; } } @@ -126,20 +142,23 @@ class node_pool mem(); // reset nodes - void clear(); + void + clear(); private: void init(size_t nblocks); - void release(); + void + release(); size_t num_blocks_ = 0; std::unique_ptr blocks_; size_t block_type_sizes_ = 0; std::unique_ptr blockspool_; - void (*create_block_)(void* block, sn_id_t bock_id, void* data) noexcept = nullptr; + void (*create_block_)(void* block, sn_id_t bock_id, void* data) noexcept + = nullptr; void (*clear_)(node_pool& np) = nullptr; - void *create_block_data_; + void* create_block_data_; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index c91e2ff..505f809 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -18,11 +18,7 @@ namespace warthog::search struct search_node { - search_node(pad_id id = pad_id::max()) - : id_(id) - { - refcount_++; - } + search_node(pad_id id = pad_id::max()) : id_(id) { refcount_++; } ~search_node() { refcount_--; } @@ -223,7 +219,7 @@ struct search_node return refcount_; } - pad_id id_ = pad_id(warthog::SN_ID_MAX); + pad_id id_ = pad_id(warthog::SN_ID_MAX); pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); cost_t g_ = warthog::COST_MAX; @@ -231,7 +227,7 @@ struct search_node cost_t h_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? - uint8_t status_ = 0; // open or closed + uint8_t status_ = 0; // open or closed uint32_t priority_ = warthog::INF32; // expansion priority uint32_t search_number_ = UINT32_MAX; diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 0c93a5c..9d65c8b 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -153,46 +153,47 @@ struct uds_default_traits // using node = std::tuple<>; // using observer = std::tuple<>; static constexpr admissibility_criteria ac = admissibility_criteria::any; - static constexpr feasibility_criteria fc = feasibility_criteria::until_exhaustion; - static constexpr reopen_policy rp = reopen_policy::no; + static constexpr feasibility_criteria fc + = feasibility_criteria::until_exhaustion; + static constexpr reopen_policy rp = reopen_policy::no; }; -template < - typename N = search_node, - typename L = std::tuple<>, +template< + typename N = search_node, typename L = std::tuple<>, admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> struct uds_traits { - using node = N; - using observer = L; + using node = N; + using observer = L; static constexpr admissibility_criteria ac = AC; static constexpr feasibility_criteria fc = FC; static constexpr reopen_policy rp = RP; }; -namespace details { +namespace details +{ -template +template struct uds_trait_node { using type = search_node; }; -template - requires requires { typename Traits::node; } +template + requires requires { typename Traits::node; } struct uds_trait_node { using type = typename Traits::node; }; -template +template struct uds_trait_observer { using type = search_node; }; -template - requires requires { typename Traits::observer; } +template + requires requires { typename Traits::observer; } struct uds_trait_observer { using type = typename Traits::observer; @@ -200,40 +201,53 @@ struct uds_trait_observer } // namespace details -template +template using uds_trait_node = typename details::uds_trait_node::type; -template +template using uds_trait_observer = typename details::uds_trait_observer::type; -template -inline consteval admissibility_criteria uds_trait_ac() noexcept +template +inline consteval admissibility_criteria +uds_trait_ac() noexcept { - if constexpr (requires { { Traits::ac } -> util::same_as_rmref; }) { + if constexpr(requires { + { + Traits::ac + } -> util::same_as_rmref; + }) + { return Traits::ac; - } else { - return admissibility_criteria::any; } + else { return admissibility_criteria::any; } } -template -inline consteval feasibility_criteria uds_trait_fc() noexcept +template +inline consteval feasibility_criteria +uds_trait_fc() noexcept { - if constexpr (requires { { Traits::fc } -> util::same_as_rmref; }) { + if constexpr(requires { + { + Traits::fc + } -> util::same_as_rmref; + }) + { return Traits::fc; - } else { - return feasibility_criteria::until_exhaustion; } + else { return feasibility_criteria::until_exhaustion; } } -template -inline consteval reopen_policy uds_trait_rp() noexcept +template +inline consteval reopen_policy +uds_trait_rp() noexcept { - if constexpr (requires { { Traits::rp } -> util::same_as_rmref; }) { + if constexpr(requires { + { Traits::rp } -> util::same_as_rmref; + }) + { return Traits::rp; - } else { - return reopen_policy::no; } + else { return reopen_policy::no; } } } // namespace warthog::search diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index ec9edf2..7bb0d5c 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -45,24 +45,26 @@ template< class unidirectional_search_full { public: - using traits = Traits; + using traits = Traits; using search_node = uds_trait_node; - using L = uds_trait_observer; + using L = uds_trait_observer; static constexpr admissibility_criteria AC = uds_trait_ac(); static constexpr feasibility_criteria FC = uds_trait_fc(); static constexpr reopen_policy RP = uds_trait_rp(); - + unidirectional_search_full( H* heuristic, E* expander, Q* queue, L listeners = L{}) : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) { } - unidirectional_search_full(const unidirectional_search_full& other) = delete; + unidirectional_search_full(const unidirectional_search_full& other) + = delete; ~unidirectional_search_full() = default; unidirectional_search_full& - operator=(const unidirectional_search_full& other) = delete; + operator=(const unidirectional_search_full& other) + = delete; void get_pathcost(problem_instance* pi, search_parameters* par, solution* sol) @@ -323,12 +325,14 @@ template< admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> -class unidirectional_search : public unidirectional_search_full > +class unidirectional_search + : public unidirectional_search_full< + H, E, Q, uds_traits> { public: - using unidirectional_search_full = typename unidirectional_search_full >::unidirectional_search_full; + using unidirectional_search_full = typename unidirectional_search_full< + H, E, Q, + uds_traits>::unidirectional_search_full; using unidirectional_search_full::unidirectional_search_full; }; diff --git a/include/warthog/util/template.h b/include/warthog/util/template.h index d0a0b0b..47213c2 100644 --- a/include/warthog/util/template.h +++ b/include/warthog/util/template.h @@ -108,10 +108,12 @@ choose_integer_sequence(auto value, TemplateFunc&& tfunc) value, std::forward(tfunc)); } -template -concept same_as_rmref = std::same_as, std::remove_reference_t>; -template -concept same_as_rmcvref = std::same_as, std::remove_cvref_t>; +template +concept same_as_rmref + = std::same_as, std::remove_reference_t>; +template +concept same_as_rmcvref + = std::same_as, std::remove_cvref_t>; } // namespace warthog::util diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index 9f176c1..92c0d58 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -5,8 +5,7 @@ namespace warthog::memory { -node_pool::node_pool() -{ } +node_pool::node_pool() { } node_pool::node_pool(size_t num_nodes) { init(num_nodes); @@ -46,19 +45,22 @@ node_pool::generate(pad_id node_id) if(!blocks_[block_id]) { // std::cerr << "generating block: "<(blockspool_->allocate()); + blocks_[block_id] + = reinterpret_cast(blockspool_->allocate()); create_block_(blocks_[block_id], block_id, create_block_data_); } // return the node from its position in the assocated block - return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); + return reinterpret_cast( + blocks_[block_id] + list_id * block_type_sizes_); } -void node_pool::release() +void +node_pool::release() { - num_blocks_ = 0; - blocks_ = nullptr; - blockspool_ = nullptr; + num_blocks_ = 0; + blocks_ = nullptr; + blockspool_ = nullptr; create_block_ = nullptr; } @@ -70,7 +72,8 @@ node_pool::get_ptr(pad_id node_id) sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; assert(block_id < num_blocks_); assert(blocks_[block_id] != nullptr); - return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); + return reinterpret_cast( + blocks_[block_id] + list_id * block_type_sizes_); } size_t @@ -82,15 +85,13 @@ node_pool::mem() return bytes; } - -void node_pool::clear() +void +node_pool::clear() { - if (clear_) { - (*clear_)(*this); - } - blockspool_ = nullptr; - create_block_ = nullptr; - clear_ = nullptr; + if(clear_) { (*clear_)(*this); } + blockspool_ = nullptr; + create_block_ = nullptr; + clear_ = nullptr; create_block_data_ = nullptr; } From 8cd576a373b4ee702205027bc5c4357d4f22da9b Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 14:58:16 +1100 Subject: [PATCH 32/61] revert search_node h_ -> ub_ change --- include/warthog/search/search_node.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 505f809..b1dca2f 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -25,12 +25,12 @@ struct search_node inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, - cost_t h = warthog::COST_MAX) + cost_t ub = warthog::COST_MAX) { parent_id_ = parent_id; f_ = f; g_ = g; - h_ = h; + ub_ = ub; search_number_ = search_number; status_ = false; } @@ -122,13 +122,13 @@ struct search_node inline cost_t get_ub() const { - return h_; + return ub_; } inline void set_ub(cost_t ub) { - h_ = ub; + ub_ = ub; } inline void @@ -137,7 +137,7 @@ struct search_node assert(g < g_); f_ = (f_ - g_) + g; g_ = g; - if(h_ < warthog::COST_MAX) { h_ = (h_ - g_) + g; } + if(ub_ < warthog::COST_MAX) { ub_ = (ub_ - g_) + g; } parent_id_ = parent_id; } @@ -202,7 +202,7 @@ struct search_node out << "search_node id:" << get_id().id; out << " p_id: "; out << parent_id_.id; - out << " g: " << g_ << " f: " << this->get_f() << " ub: " << h_ + out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ << " expanded: " << get_expanded() << " " << " search_number_: " << search_number_; } @@ -224,7 +224,7 @@ struct search_node cost_t g_ = warthog::COST_MAX; cost_t f_ = warthog::COST_MAX; - cost_t h_ = warthog::COST_MAX; + cost_t ub_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? uint8_t status_ = 0; // open or closed From 080b9fbe927a143da0e95e03f4b8ded6586a9e27 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 14:59:53 +1100 Subject: [PATCH 33/61] manual format --- include/warthog/search/search_node.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index b1dca2f..0c5279b 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -222,8 +222,8 @@ struct search_node pad_id id_ = pad_id(warthog::SN_ID_MAX); pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); - cost_t g_ = warthog::COST_MAX; - cost_t f_ = warthog::COST_MAX; + cost_t g_ = warthog::COST_MAX; + cost_t f_ = warthog::COST_MAX; cost_t ub_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? From bca97acdf11a554246bc8e7fd055085e35afe5bc Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Tue, 17 Mar 2026 13:54:28 +1100 Subject: [PATCH 34/61] update serach_node with noexcept and removed static refcount --- include/warthog/search/search_node.h | 71 +++++++++++----------------- src/search/search_node.cpp | 15 +++++- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 0c5279b..1ebb9fd 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -18,14 +18,14 @@ namespace warthog::search struct search_node { - search_node(pad_id id = pad_id::max()) : id_(id) { refcount_++; } + search_node() noexcept = default; + search_node(pad_id id = pad_id::max()) noexcept : id_(id) { } - ~search_node() { refcount_--; } inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, - cost_t ub = warthog::COST_MAX) + cost_t ub = warthog::COST_MAX) noexcept { parent_id_ = parent_id; f_ = f; @@ -36,103 +36,103 @@ struct search_node } inline uint32_t - get_search_number() const + get_search_number() const noexcept { return search_number_; } inline void - set_search_number(uint32_t search_number) + set_search_number(uint32_t search_number) noexcept { search_number_ = search_number; } inline pad_id - get_id() const + get_id() const noexcept { return id_; } inline void - set_id(pad_id id) + set_id(pad_id id) noexcept { id_ = id; } inline bool - get_expanded() const + get_expanded() const noexcept { return status_; } inline void - set_expanded(bool expanded) + set_expanded(bool expanded) noexcept { status_ = expanded; } inline pad_id - get_parent() const + get_parent() const noexcept { return parent_id_; } inline void - set_parent(pad_id parent_id) + set_parent(pad_id parent_id) noexcept { parent_id_ = parent_id; } inline uint32_t - get_priority() const + get_priority() const noexcept { return priority_; } inline void - set_priority(uint32_t priority) + set_priority(uint32_t priority) noexcept { priority_ = priority; } inline cost_t - get_g() const + get_g() const noexcept { return g_; } inline void - set_g(cost_t g) + set_g(cost_t g) noexcept { g_ = g; } inline cost_t - get_f() const + get_f() const noexcept { return f_; } inline void - set_f(cost_t f) + set_f(cost_t f) noexcept { f_ = f; } inline cost_t - get_ub() const + get_ub() const noexcept { return ub_; } inline void - set_ub(cost_t ub) + set_ub(cost_t ub) noexcept { ub_ = ub; } inline void - relax(cost_t g, pad_id parent_id) + relax(cost_t g, pad_id parent_id) noexcept { assert(g < g_); f_ = (f_ - g_) + g; @@ -142,7 +142,7 @@ struct search_node } inline bool - operator<(const search_node& other) const + operator<(const search_node& other) const noexcept { // static uint64_t SIGN_MASK = UINT64_MAX & (1ULL<<63); // cost_t result = this->f_ - other.f_; @@ -163,7 +163,7 @@ struct search_node } inline bool - operator>(const search_node& other) const + operator>(const search_node& other) const noexcept { if(f_ > other.f_) { return true; } if(f_ < other.f_) { return false; } @@ -174,14 +174,14 @@ struct search_node } inline bool - operator==(const search_node& other) const + operator==(const search_node& other) const noexcept { if(!(*this < other) && !(*this > other)) { return true; } return false; } inline bool - operator<=(const search_node& other) const + operator<=(const search_node& other) const noexcept { if(*this < other) { return true; } if(!(*this > other)) { return true; } @@ -189,36 +189,22 @@ struct search_node } inline bool - operator>=(const search_node& other) const + operator>=(const search_node& other) const noexcept { if(*this > other) { return true; } if(!(*this < other)) { return true; } return false; } - inline void - print(std::ostream& out) const - { - out << "search_node id:" << get_id().id; - out << " p_id: "; - out << parent_id_.id; - out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ - << " expanded: " << get_expanded() << " " - << " search_number_: " << search_number_; - } + void + print(std::ostream& out) const; uint32_t - mem() + mem() noexcept { return sizeof(*this); } - static uint32_t - get_refcount() - { - return refcount_; - } - pad_id id_ = pad_id(warthog::SN_ID_MAX); pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); @@ -231,7 +217,6 @@ struct search_node uint32_t priority_ = warthog::INF32; // expansion priority uint32_t search_number_ = UINT32_MAX; - static uint32_t refcount_; }; struct cmp_less_search_node diff --git a/src/search/search_node.cpp b/src/search/search_node.cpp index 4e90feb..6ce1187 100644 --- a/src/search/search_node.cpp +++ b/src/search/search_node.cpp @@ -1,6 +1,17 @@ #include -unsigned int warthog::search::search_node::refcount_ = 0; +namespace warthog::search { + +void +search_node::print(std::ostream& out) const +{ + out << "search_node id:" << get_id().id; + out << " p_id: "; + out << parent_id_.id; + out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ + << " expanded: " << get_expanded() << " " + << " search_number_: " << search_number_; +} std::ostream& operator<<(std::ostream& str, const warthog::search::search_node& sn) @@ -9,3 +20,5 @@ operator<<(std::ostream& str, const warthog::search::search_node& sn) return str; } + +} // namespace warthog::search From 56ff38f23a69cd537cde82067de2301a93ee4df5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:29:10 +0000 Subject: [PATCH 35/61] auto clang-format action --- include/warthog/search/search_node.h | 1 - src/search/search_node.cpp | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 1ebb9fd..b091c9a 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -21,7 +21,6 @@ struct search_node search_node() noexcept = default; search_node(pad_id id = pad_id::max()) noexcept : id_(id) { } - inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, diff --git a/src/search/search_node.cpp b/src/search/search_node.cpp index 6ce1187..cae2985 100644 --- a/src/search/search_node.cpp +++ b/src/search/search_node.cpp @@ -1,6 +1,7 @@ #include -namespace warthog::search { +namespace warthog::search +{ void search_node::print(std::ostream& out) const @@ -9,8 +10,8 @@ search_node::print(std::ostream& out) const out << " p_id: "; out << parent_id_.id; out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ - << " expanded: " << get_expanded() << " " - << " search_number_: " << search_number_; + << " expanded: " << get_expanded() << " " + << " search_number_: " << search_number_; } std::ostream& From 45e9547b0cb9dc51af8889ac09a515ec5a8a635a Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 19 Mar 2026 17:15:52 +1100 Subject: [PATCH 36/61] scenario seralizer work in progress to scenario_manager --- include/warthog/io/scenario.h | 54 +++++----- include/warthog/util/scenario_manager.h | 22 ++-- src/io/scenario.cpp | 137 ++++++++++++++---------- src/util/scenario_manager.cpp | 73 +++++-------- 4 files changed, 151 insertions(+), 135 deletions(-) diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index bed09a1..8ca7bc2 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -75,13 +75,13 @@ struct scenario_patch class scenario_serialize { public: - enum class CurrentState + enum class serialize_state { - Init, - Version, - Header, - Query, - Error, + init, + version, + header, + query, + error, }; static constexpr size_t max_line_length = 2000; static constexpr size_t max_dimension = 15'000; @@ -132,7 +132,7 @@ class scenario_serialize bool has_dist_type(dist_type d) const noexcept { assert((uint32_t)d < (uint32_t)dist_type::dist_count); - return m_dist.test(d); + return m_dist.test((uint32_t)d); } /// @brief opens scenario file get_scenario_filename() for reading @@ -145,9 +145,19 @@ class scenario_serialize std::errc open_write(std::ostream* scenario = nullptr); /// @return if scenario is open for reading - bool can_read(); + bool can_read(std::istream* in = nullptr) + { + if (in == nullptr) + in = m_scenario_in; + return in != nullptr && in->good(); + } /// @return if scenario is open for writing - bool can_write(); + bool can_write(std::ostream* out = nullptr) + { + if (out == nullptr) + out = m_scenario_out; + return out != nullptr && out->good(); + } /// @brief reads in file version information, and sets version accessable via get_version() /// @param in optional stream to use, otherwise uses internal-set stream @@ -160,16 +170,9 @@ class scenario_serialize /// With version1: peeks first query to gain map name /// With version2: gets map width/height, available costs and patch filename std::errc read_header(std::istream* in = nullptr); - /// @brief calls read_version then read_header for full header - /// @param in optional stream to use, otherwise uses internal-set stream - /// @return success std::errc{} (0) - std::errc read_version_header(std::istream* in = nullptr); - - std::errc read_header_v1(std::istream* in = nullptr); - std::errc read_header_v2(std::istream* in = nullptr); protected: - std::pair get_instream(std::istream* in) noexcept + std::pair get_istream(std::istream* in = nullptr) noexcept { if (in == nullptr) in = m_scenario_in; @@ -177,7 +180,7 @@ class scenario_serialize return {nullptr, std::errc::io_error}; return {in, {}}; } - std::pair get_outstream(std::ostream* out) noexcept + std::pair get_ostream(std::ostream* out = nullptr) noexcept { if (out == nullptr) out = m_scenario_out; @@ -187,12 +190,15 @@ class scenario_serialize } std::pair readline(std::istream* in); - std::errc read_query_line_v1(scenario_query& query); - std::errc read_query_line_v2(scenario_query& query); - std::errc read_patch_line_v2(scenario_patch& query); + std::errc read_header_v1(std::istream* in = nullptr); + std::errc read_header_v2(std::istream* in = nullptr); + + std::pair read_query_line_v1(scenario_query& query); + std::pair read_query_line_v2(scenario_query& query); + std::pair read_patch_line_v2(scenario_patch& query); protected: - CurrentState m_state = CurrentState::Init; + serialize_state m_state = serialize_state::init; scenario_version m_version = scenario_version::version1; std::filesystem::path m_scenario_filename; std::filesystem::path m_map_filename; @@ -200,8 +206,8 @@ class scenario_serialize std::ostream* m_scenario_out = nullptr; std::unique_ptr m_scenario_file; std::bitset<(size_t)dist_type::dist_count> m_dist; - uint32_t m_map_width; - uint32_t m_map_height; + uint32_t m_map_width = 0; + uint32_t m_map_height = 0; int32_t m_query_at = 0; // dynamic data diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index 33ba408..1126410 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -20,8 +20,8 @@ #include "experiment.h" #include -#include -#include +#include +#include #include #include @@ -82,23 +82,29 @@ class scenario_manager void generate_experiments(domain::gridmap*, int num); void - load_scenario(const char* filelocation); + load_scenario(const std::filesystem::path& filelocation); + void + load_scenario(std::istream& file, std::istream* map_override = nullptr, const std::filesystem::path& mapfile_override = {}); void write_scenario(std::ostream& out); void sort(); // organise by increasing solution length -private: - void - load_gppc_scenario(std::ifstream& infile); +protected: + std::errc + load_gppc_scenario(std::istream& scenfile, std::istream* mapfile); std::vector experiments_; - std::string sfile_; + std::filesystem::path sfile_; }; std::filesystem::path find_map_filename( - const scenario_manager& scenmgr, std::filesystem::path sfilename = {}); + const scenario_manager& scenmgr, const std::filesystem::path& sfilename = {}); + +std::filesystem::path +find_map_filename( + const std::filesystem::path& scenmgr, const std::filesystem::path& sfilename = {}); } // namespace warthog::util diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 09f4145..eeda567 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -16,86 +16,102 @@ scenario_serialize::~scenario_serialize() = default; std::pair scenario_serialize::readline(std::istream* in) { - auto [s, err] = get_instream(in); + auto [s, err] = get_istream(in); if (err != std::errc{}) { return {{}, err}; } s->getline(m_line.data(), max_line_length); + return {m_line, {}}; } std::errc scenario_serialize::read_version(std::istream* in) { - if (m_state != CurrentState::Init) + if (m_state != serialize_state::init) { return std::errc::state_not_recoverable; } std::errc err; - std::tie(in, err) = get_instream(in); + std::tie(in, err) = get_istream(in); if (err != std::errc{}) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return err; } std::string_view line; std::tie(line, err) = readline(in); if (err != std::errc{}) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return err; } m_iss.str(std::string(line)); int version; if (!(*in >> m_token >> version)) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return std::errc::io_error; } if (version < 1 || version > 2) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return std::errc::invalid_argument; } m_version = version == 1 ? scenario_version::version1 : scenario_version::version2; - m_state = CurrentState::Header; + m_state = serialize_state::header; return std::errc{}; } -std::errc scenario_serialize::read_header_v1(std::istream* in) +std::errc scenario_serialize::read_header(std::istream* in) { - if (m_state != CurrentState::Header) + if (!can_read(in) || m_state != serialize_state::header) { return std::errc::state_not_recoverable; } - std::errc err; - std::tie(in, err) = get_instream(in); - if (err != std::errc{}) + + switch (m_version) { - m_state = CurrentState::Error; - return err; + case scenario_version::version1: + return read_header_v1(get_istream().first); + case scenario_version::version2: + return read_header_v2(get_istream().first); + default: + return std::errc::state_not_recoverable; + } +} + +std::errc scenario_serialize::read_header_v1(std::istream* in) +{ + std::errc ec; + bool s; + if (in == nullptr) { + std::tie(in, ec) = get_istream(in); + if (ec != std::errc{}) + { + m_state = serialize_state::error; + return ec; + } } // read first query line to get map - std::tie(std::ignore, err) = readline(in); - if (err != std::errc{}) + std::tie(s, ec) = read_query_line_v1(m_query); + if (ec != std::errc{}) { - m_state = CurrentState::Error; - return err; + m_state = serialize_state::error; + return ec; } - read_query_line_v1(m_query); - set_relative_map_filename(m_query.map); - m_state = CurrentState::Query; + if (s) + { + set_relative_map_filename(m_query.map); + } + m_state = serialize_state::query; return std::errc{}; } std::errc scenario_serialize::read_header_v2(std::istream* in) { - if (m_state != CurrentState::Header) - { - return std::errc::state_not_recoverable; - } std::errc err; - std::tie(in, err) = get_instream(in); + std::tie(in, err) = get_istream(in); if (err != std::errc{}) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return err; } // read first query line to get map @@ -104,80 +120,92 @@ std::errc scenario_serialize::read_header_v2(std::istream* in) std::tie(line, err) = readline(in); if (err != std::errc{}) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return err; } m_iss.str(std::string(line)); if (!(m_iss >> m_token >> m_map_height)) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return std::errc::io_error; } if (m_map_height < 1 || m_map_height > max_dimension) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return std::errc::invalid_argument; } // width std::tie(line, err) = readline(in); if (err != std::errc{}) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return err; } m_iss.str(std::string(line)); if (!(m_iss >> m_token >> m_map_width)) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return std::errc::io_error; } if (m_map_width < 1 || m_map_width > max_dimension) { - m_state = CurrentState::Error; + m_state = serialize_state::error; return std::errc::invalid_argument; } set_relative_map_filename(m_query.map); - m_state = CurrentState::Query; + m_state = serialize_state::query; return std::errc{}; } -std::errc scenario_serialize::read_query_line_v1(scenario_query& query) +std::pair scenario_serialize::read_query_line_v1(scenario_query& query) { + assert(can_read()); int width, height; - if (!(m_iss >> query.bucket >> query.map >> width >> height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y >> query.dist[0])) + // move to start of read + auto [iss,ec] = get_istream(); + if (ec != std::errc{}) { - return std::errc::io_error; + return {false, std::errc::io_error}; } - if (!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) || !std::isfinite(query.dist[0])) + auto [line,ec] = readline(); + if (!(m_iss >> std::ws)) { - return std::errc::invalid_argument; + return {false, std::errc::io_error}; } - m_iss >> std::ws; if (m_iss.eof()) { - return std::errc::invalid_argument; + return {false, std::errc{}}; } - return std::errc{}; + if (!(m_iss >> query.bucket >> query.map >> width >> height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y >> query.dist[0])) + { + return {false, std::errc::io_error}; + } + if (!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) || !std::isfinite(query.dist[0])) + { + return {false, std::errc::invalid_argument}; + } + return {false, std::errc{}}; } -std::errc scenario_serialize::read_query_line_v2(scenario_query& query) +std::pair scenario_serialize::read_query_line_v2(scenario_query& query) { + assert(can_read()); int width, height; double d; query.dist.fill(-1.0); if (!(m_iss >> query.bucket >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y)) { - return std::errc::io_error; + return {false, std::errc::io_error}; } for (auto i : m_dist_id) { if (!(m_iss >> d)) { - return std::errc::io_error; + return {false, std::errc::io_error}; } if (!std::isfinite(d)) { - return std::errc::invalid_argument; + return {false, std::errc::invalid_argument}; } if (i >= 0) { query.dist[i] = d; @@ -186,30 +214,31 @@ std::errc scenario_serialize::read_query_line_v2(scenario_query& query) m_iss >> std::ws; if (m_iss.eof()) { - return std::errc::invalid_argument; + return {false, std::errc::invalid_argument}; } - return std::errc{}; + return {false, std::errc{}}; } -std::errc scenario_serialize::read_patch_line_v2(scenario_patch& patch) +std::pair scenario_serialize::read_patch_line_v2(scenario_patch& patch) { + assert(can_read()); int width, height; double d; if (!(m_iss >> patch.bucket >> patch.patch_id >> patch.loc_x >> patch.loc_y)) { - return std::errc::io_error; + return {false, std::errc::io_error}; } if (patch.loc_x >= m_map_width || patch.loc_y > m_map_height) { - return std::errc::invalid_argument; + return {false, std::errc::invalid_argument}; } m_iss >> std::ws; if (m_iss.eof()) { - return std::errc::invalid_argument; + return {false, std::errc::invalid_argument}; } - return std::errc{}; + return {false, std::errc{}}; } } // namespace warthog::io diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 374382a..8df9a6c 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -21,62 +21,37 @@ scenario_manager::~scenario_manager() } void -scenario_manager::load_scenario(const char* filelocation) +scenario_manager::load_scenario(const std::filesystem::path& filelocation) { - std::ifstream infile; - infile.open(filelocation, std::ios::in); - - if(!infile.good()) - { - std::cerr << "err; scenario_manager::load_scenario " - << "Invalid scenario file: " << filelocation << std::endl; - infile.close(); - exit(1); - } +} - sfile_ = filelocation; +void +scenario_manager::load_scenario(std::istream& file, std::istream* map_override, const std::filesystem::path& mapfile_override) +{ - char buf[1024]; - infile.getline(buf, 1024); - if(strstr(buf, "version 1")) - { - // GPPC format scenarios - load_gppc_scenario(infile); - } - else - { - std::cerr << "err; scenario_manager::load_scenario " - << " scenario file not in GPPC format\n"; - infile.close(); - exit(1); - } - infile.close(); + si.set_scenario_filename() } // V1.0 is the version officially supported by HOG -void -scenario_manager::load_gppc_scenario(std::ifstream& infile) +std::errc +scenario_manager::load_gppc_scenario(std::istream& scenfile, std::istream* mapfile) { - uint32_t sizeX = 0, sizeY = 0; - uint32_t bucket; - std::string map; - uint32_t xs, ys, xg, yg; - std::string dist; - - while(infile >> bucket >> map >> sizeX >> sizeY >> xs >> ys >> xg >> yg - >> dist) - { - double dbl_dist = strtod(dist.c_str(), 0); - experiments_.push_back( - new experiment(xs, ys, xg, yg, sizeX, sizeY, dbl_dist, map)); - - int32_t precision = 0; - if(dist.find(".") != std::string::npos) - { - precision = ((int32_t)dist.size() - (int32_t)(dist.find(".") + 1)); - } - experiments_.back()->set_precision(precision); - } + io::scenario_serialize si; + io::bittable_serialize bi; + + // open stream and read header + if (auto ec = si.open_read(&scenfile); ec != std::errc{}) + return ec; + if (auto ec = si.read_version(); ec != std::errc{}) + return std::errc::io_error; + if (si.get_version() != io::scenario_version::version1) + return std::errc::invalid_argument; + if (auto ec = si.read_header(); ec != std::errc{}) + return std::errc::io_error; + + sfile_ = si.get_map_filename(); + experiments_.clear(); + experiments_.reserve(1024); } void From 9a85099af1e597892abe1177d48c2fadb6ba3489 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 20 Mar 2026 13:35:04 +1100 Subject: [PATCH 37/61] convert scenario_manger to scenario_serialize for read-in in static --- include/warthog/io/grid.h | 2 + include/warthog/io/scenario.h | 74 ++++++-- include/warthog/util/scenario_manager.h | 20 ++- src/io/scenario.cpp | 214 +++++++++++++++++++----- src/util/scenario_manager.cpp | 84 ++++++++-- 5 files changed, 314 insertions(+), 80 deletions(-) diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index f3d1fd6..f80ef50 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -35,6 +35,8 @@ enum class bittable_cell : uint8_t UNKNOWN }; +inline constexpr uint32_t GRID_MAX_SIZE = 15'000; + class bittable_serialize { public: diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 8ca7bc2..7c74724 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -52,6 +52,8 @@ struct scenario_query { int64_t bucket; std::string map; + int32_t width; + int32_t height; double start_x; double start_y; double goal_x; @@ -75,7 +77,7 @@ struct scenario_patch class scenario_serialize { public: - enum class serialize_state + enum class serialize_state : uint8_t { init, version, @@ -83,8 +85,15 @@ class scenario_serialize query, error, }; + enum query_res : uint8_t + { + valid, + invalid, + final, + next_query, + next_patch + }; static constexpr size_t max_line_length = 2000; - static constexpr size_t max_dimension = 15'000; scenario_serialize(); ~scenario_serialize(); @@ -109,7 +118,7 @@ class scenario_serialize return m_map_filename; } - void set_relative_map_filename(std::filesystem::path&& filename); + void set_relative_map_filename(const std::filesystem::path& filename); void set_version(scenario_version version) noexcept { @@ -135,6 +144,32 @@ class scenario_serialize return m_dist.test((uint32_t)d); } + int32_t get_line_num() const noexcept + { + return m_line_num; + } + uint32_t get_map_width() const noexcept + { + return m_map_width; + } + int32_t get_map_height() const noexcept + { + return m_map_height; + } + std::string_view get_last_line() const noexcept + { + return m_line; + } + + void set_force_int(bool v) noexcept + { + m_force_int = v; + } + bool get_force_int() noexcept + { + return m_force_int; + } + /// @brief opens scenario file get_scenario_filename() for reading /// @param scenario use a user provided instead of get_scenario_filename() /// @return error on operation @@ -144,19 +179,21 @@ class scenario_serialize /// @return error on operation std::errc open_write(std::ostream* scenario = nullptr); + void close(); + /// @return if scenario is open for reading bool can_read(std::istream* in = nullptr) { if (in == nullptr) in = m_scenario_in; - return in != nullptr && in->good(); + return in != nullptr && !in->bad(); } /// @return if scenario is open for writing bool can_write(std::ostream* out = nullptr) { if (out == nullptr) out = m_scenario_out; - return out != nullptr && out->good(); + return out != nullptr && !out->bad(); } /// @brief reads in file version information, and sets version accessable via get_version() @@ -171,6 +208,15 @@ class scenario_serialize /// With version2: gets map width/height, available costs and patch filename std::errc read_header(std::istream* in = nullptr); + std::errc read_header_v1(std::istream* in = nullptr); + std::errc read_header_v2(std::istream* in = nullptr); + + std::pair read_query_line(scenario_query& query, std::istream* in = nullptr); + + std::pair read_query_line_v1(scenario_query& query, std::istream* in = nullptr); + std::pair read_query_line_v2(scenario_query& query, std::istream* in = nullptr); + std::pair read_patch_line_v2(scenario_patch& query, std::istream* in = nullptr); + protected: std::pair get_istream(std::istream* in = nullptr) noexcept { @@ -188,31 +234,30 @@ class scenario_serialize return {nullptr, std::errc::io_error}; return {out, {}}; } + std::istream& line_stream(std::string_view line); std::pair readline(std::istream* in); - - std::errc read_header_v1(std::istream* in = nullptr); - std::errc read_header_v2(std::istream* in = nullptr); - - std::pair read_query_line_v1(scenario_query& query); - std::pair read_query_line_v2(scenario_query& query); - std::pair read_patch_line_v2(scenario_patch& query); + void unreadline(std::string_view line); protected: serialize_state m_state = serialize_state::init; scenario_version m_version = scenario_version::version1; + bool m_force_int = false; std::filesystem::path m_scenario_filename; std::filesystem::path m_map_filename; + std::unique_ptr m_scenario_stream; std::istream* m_scenario_in = nullptr; std::ostream* m_scenario_out = nullptr; - std::unique_ptr m_scenario_file; std::bitset<(size_t)dist_type::dist_count> m_dist; uint32_t m_map_width = 0; uint32_t m_map_height = 0; int32_t m_query_at = 0; + int32_t m_line_num = -1; // dynamic data std::pmr::monotonic_buffer_resource m_dyn_res; - std::pmr::string m_line; + std::pmr::monotonic_buffer_resource m_string_res; + char* m_line = nullptr; ///< sets from m_dyn_res + std::pmr::string m_unget_line; std::pmr::vector m_dist_strings; std::pmr::vector m_dist_id; @@ -220,7 +265,6 @@ class scenario_serialize // TODO: replace with a custom string stream that does not allocate memory std::istringstream m_iss; std::string m_token; - scenario_query m_query; }; } // namespace warthog::util diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index 1126410..a20c6c1 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace warthog::util { @@ -68,15 +69,26 @@ class scenario_manager return sizeof(*this) + sizeof(experiment) * experiments_.size(); } - const std::string& + std::string last_file_loaded() noexcept + { + return sfile_.string(); + } + const std::filesystem::path& + scenario_filename() noexcept { return sfile_; } + const std::filesystem::path& + map_filename() noexcept + { + return mfile_; + } void clear() { experiments_.clear(); + experiments_res_.release(); } void @@ -84,7 +96,7 @@ class scenario_manager void load_scenario(const std::filesystem::path& filelocation); void - load_scenario(std::istream& file, std::istream* map_override = nullptr, const std::filesystem::path& mapfile_override = {}); + load_scenario(std::istream& file, std::filesystem::path&& mapfile_override = {}); void write_scenario(std::ostream& out); void @@ -92,10 +104,12 @@ class scenario_manager protected: std::errc - load_gppc_scenario(std::istream& scenfile, std::istream* mapfile); + load_gppc_scenario(std::istream& scenfile); + std::pmr::monotonic_buffer_resource experiments_res_; std::vector experiments_; std::filesystem::path sfile_; + std::filesystem::path mfile_; }; std::filesystem::path diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index eeda567..e45fe2f 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -1,28 +1,110 @@ #include +#include +#include + #include #include #include -#include +#include namespace warthog::io { -scenario_serialize::scenario_serialize() : m_line(max_line_length + 2, '\0', &m_dyn_res), +scenario_serialize::scenario_serialize() : m_line(static_cast(m_dyn_res.allocate(max_line_length+2))), + m_unget_line(&m_dyn_res), m_dist_strings(&m_dyn_res), m_dist_id(&m_dyn_res) -{ } +{ + m_line[0] = '\0'; +} scenario_serialize::~scenario_serialize() = default; +std::errc scenario_serialize::open_read(std::istream* scenario) +{ + close(); + if (scenario != nullptr) + { + m_scenario_in = scenario; + } else { + auto stream = std::make_unique(m_scenario_filename); + m_scenario_in = stream.get(); + m_scenario_stream = std::move(stream); + } + if (!*m_scenario_in) + { + // bad stream + m_scenario_in = nullptr; + m_scenario_stream = nullptr; + return std::errc::io_error; + } + return std::errc{}; +} + +void scenario_serialize::close() +{ + m_state = serialize_state::init; + m_version = scenario_version::version1; + m_scenario_in = nullptr; + m_scenario_out = nullptr; + m_dist.reset(); + m_map_width = 0; + m_map_height = 0; + m_query_at = 0; + m_line_num = -1; + m_line[0] = '\0'; + m_unget_line.clear(); + m_dist_strings.clear(); + m_dist_id.clear(); + m_scenario_stream = nullptr; +} + std::pair scenario_serialize::readline(std::istream* in) { - auto [s, err] = get_istream(in); - if (err != std::errc{}) + size_t len; + if (!m_unget_line.empty()) { - return {{}, err}; + // return last unreadline + len = m_unget_line.length(); + if (len > max_line_length-1) { + return {{}, std::errc::invalid_argument}; + } + std::memcpy(m_line, m_unget_line.c_str(), len+1); + m_unget_line.clear(); + } else { + auto [s, err] = get_istream(in); + if (err != std::errc{}) + { + return {{}, err}; + } + if (!s->getline(m_line, max_line_length)) + { + if (s->eof()) + return {{}, {}}; + else + return {{}, std::errc::io_error}; + } + len = strlen(m_line); } - s->getline(m_line.data(), max_line_length); - return {m_line, {}}; + m_line_num += 1; + return {std::string_view(m_line, len), {}}; +} +void scenario_serialize::unreadline(std::string_view line) +{ + m_unget_line = line; +} + +void scenario_serialize::set_relative_map_filename(const std::filesystem::path& filename) +{ + m_map_filename = std::filesystem::proximate(filename, m_scenario_filename); +} + +std::istream& scenario_serialize::line_stream(std::string_view line) +{ + m_iss.clear(); + m_iss.str(std::string(line)); + m_iss.seekg(0); + return m_iss; } std::errc scenario_serialize::read_version(std::istream* in) @@ -45,9 +127,9 @@ std::errc scenario_serialize::read_version(std::istream* in) m_state = serialize_state::error; return err; } - m_iss.str(std::string(line)); + auto& iss = line_stream(line); int version; - if (!(*in >> m_token >> version)) { + if (!(iss >> m_token >> version)) { m_state = serialize_state::error; return std::errc::io_error; } @@ -91,16 +173,25 @@ std::errc scenario_serialize::read_header_v1(std::istream* in) } } // read first query line to get map - std::tie(s, ec) = read_query_line_v1(m_query); + scenario_query Q; + std::tie(s, ec) = read_query_line_v1(Q); if (ec != std::errc{}) { m_state = serialize_state::error; return ec; } - if (s) + if (s != final) { - set_relative_map_filename(m_query.map); + if (Q.width <= 0 || Q.width > GRID_MAX_SIZE || Q.height <= 0 || Q.height > GRID_MAX_SIZE) + { + m_state = serialize_state::error; + return std::errc::invalid_argument; + } + set_relative_map_filename(Q.map); + m_map_width = Q.width; + m_map_height = Q.height; } + unreadline(m_line); m_state = serialize_state::query; return std::errc{}; } @@ -123,13 +214,15 @@ std::errc scenario_serialize::read_header_v2(std::istream* in) m_state = serialize_state::error; return err; } - m_iss.str(std::string(line)); - if (!(m_iss >> m_token >> m_map_height)) + { + auto& iss = line_stream(line); + if (!(iss >> m_token >> m_map_height)) { m_state = serialize_state::error; return std::errc::io_error; } - if (m_map_height < 1 || m_map_height > max_dimension) + } + if (m_map_height < 1 || m_map_height > GRID_MAX_SIZE) { m_state = serialize_state::error; return std::errc::invalid_argument; @@ -141,71 +234,100 @@ std::errc scenario_serialize::read_header_v2(std::istream* in) m_state = serialize_state::error; return err; } - m_iss.str(std::string(line)); - if (!(m_iss >> m_token >> m_map_width)) + { + auto& iss = line_stream(line); + if (!(iss >> m_token >> m_map_width)) { m_state = serialize_state::error; return std::errc::io_error; } - if (m_map_width < 1 || m_map_width > max_dimension) + } + if (m_map_width < 1 || m_map_width > GRID_MAX_SIZE) { m_state = serialize_state::error; return std::errc::invalid_argument; } - set_relative_map_filename(m_query.map); + // set_relative_map_filename(m_query.map); m_state = serialize_state::query; return std::errc{}; } -std::pair scenario_serialize::read_query_line_v1(scenario_query& query) +auto scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) -> std::pair { - assert(can_read()); - int width, height; + assert(can_read(in)); // move to start of read - auto [iss,ec] = get_istream(); + std::errc ec; + std::string_view line; + std::tie(in, ec) = get_istream(in); if (ec != std::errc{}) { - return {false, std::errc::io_error}; + return {invalid, std::errc::io_error}; } - auto [line,ec] = readline(); - if (!(m_iss >> std::ws)) + std::tie(line, ec) = readline(in); + if (ec != std::errc{}) { - return {false, std::errc::io_error}; + return {invalid, std::errc::io_error}; } - if (m_iss.eof()) + if (in->eof()) { - return {false, std::errc{}}; + return {final, std::errc{}}; } - if (!(m_iss >> query.bucket >> query.map >> width >> height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y >> query.dist[0])) + assert(!std::all_of(line.begin(), line.end(), [](char a) { return std::isspace((unsigned char)a); })); + auto& iss = line_stream(line); + if (!(iss >> query.bucket >> query.map >> query.width >> query.height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y >> query.dist[0])) { - return {false, std::errc::io_error}; + return {invalid, std::errc::io_error}; } if (!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) || !std::isfinite(query.dist[0])) { - return {false, std::errc::invalid_argument}; + return {invalid, std::errc::invalid_argument}; + } + // perform checking of values, but do not return as error if fail + if (query.width != m_map_width || query.height != m_map_height) + { + return {invalid, std::errc{}}; + } + if (m_force_int) { + // check integer bounds + if (rint(query.start_x) != query.start_x || rint(query.start_y) != query.start_y + || rint(query.goal_x) != query.goal_x || rint(query.goal_y) != query.goal_y) + { + return {invalid, std::errc{}}; + } + if (query.start_x < 0 || query.start_y >= m_map_width || + query.goal_x < 0 || query.goal_y >= m_map_height) + { + return {invalid, std::errc{}}; + } + } else { + if (query.start_x < 0 || query.start_y > m_map_width || + query.goal_x < 0 || query.goal_y > m_map_height) + { + return {invalid, std::errc{}}; + } } - return {false, std::errc{}}; + return {valid, std::errc{}}; } -std::pair scenario_serialize::read_query_line_v2(scenario_query& query) +auto scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) -> std::pair { - assert(can_read()); + assert(can_read(in)); int width, height; double d; query.dist.fill(-1.0); if (!(m_iss >> query.bucket >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y)) { - return {false, std::errc::io_error}; + return {invalid, std::errc::io_error}; } for (auto i : m_dist_id) { if (!(m_iss >> d)) { - return {false, std::errc::io_error}; + return {invalid, std::errc::io_error}; } if (!std::isfinite(d)) { - return {false, std::errc::invalid_argument}; + return {invalid, std::errc::invalid_argument}; } if (i >= 0) { query.dist[i] = d; @@ -214,31 +336,31 @@ std::pair scenario_serialize::read_query_line_v2(scenario_query& m_iss >> std::ws; if (m_iss.eof()) { - return {false, std::errc::invalid_argument}; + return {invalid, std::errc::invalid_argument}; } - return {false, std::errc{}}; + return {valid, std::errc{}}; } -std::pair scenario_serialize::read_patch_line_v2(scenario_patch& patch) +auto scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) -> std::pair { assert(can_read()); int width, height; double d; if (!(m_iss >> patch.bucket >> patch.patch_id >> patch.loc_x >> patch.loc_y)) { - return {false, std::errc::io_error}; + return {invalid, std::errc::io_error}; } if (patch.loc_x >= m_map_width || patch.loc_y > m_map_height) { - return {false, std::errc::invalid_argument}; + return {invalid, std::errc::invalid_argument}; } m_iss >> std::ws; if (m_iss.eof()) { - return {false, std::errc::invalid_argument}; + return {invalid, std::errc::invalid_argument}; } - return {false, std::errc{}}; + return {valid, std::errc{}}; } } // namespace warthog::io diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 8df9a6c..0a53106 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -11,47 +12,98 @@ namespace warthog::util scenario_manager::scenario_manager() { } -scenario_manager::~scenario_manager() -{ - for(unsigned int i = 0; i < experiments_.size(); i++) - { - delete experiments_[i]; - } - experiments_.clear(); -} +scenario_manager::~scenario_manager() = default; void scenario_manager::load_scenario(const std::filesystem::path& filelocation) { + sfile_ = filelocation; + std::ifstream in(filelocation); + if (load_gppc_scenario(in) != std::errc{}) + { + WARTHOG_GERROR_FMT("Failed to load scenario file \"{}\"", std::string(sfile_)); + throw std::runtime_error("scenario_manager failed to load scenario"); + } } void -scenario_manager::load_scenario(std::istream& file, std::istream* map_override, const std::filesystem::path& mapfile_override) +scenario_manager::load_scenario(std::istream& file, std::filesystem::path&& scenfile) { - - si.set_scenario_filename() + sfile_ = std::move(scenfile); + load_gppc_scenario(file); + if (load_gppc_scenario(file) != std::errc{}) + { + WARTHOG_GERROR_FMT("Failed to load scenario file \"{}\"", std::string(sfile_)); + throw std::runtime_error("scenario_manager failed to load scenario"); + } } // V1.0 is the version officially supported by HOG std::errc -scenario_manager::load_gppc_scenario(std::istream& scenfile, std::istream* mapfile) +scenario_manager::load_gppc_scenario(std::istream& scenfile) { + clear(); + io::scenario_serialize si; - io::bittable_serialize bi; + si.set_scenario_filename(std::filesystem::path(sfile_)); + si.set_force_int(true); // open stream and read header if (auto ec = si.open_read(&scenfile); ec != std::errc{}) + { + WARTHOG_GERROR("scenario_manager failed to open for read"); return ec; - if (auto ec = si.read_version(); ec != std::errc{}) + } + if (auto ec = si.read_version(); ec != std::errc{}){ + WARTHOG_GERROR_FMT("scenario_manager failed to read version on line: {}", si.get_line_num()); return std::errc::io_error; + } if (si.get_version() != io::scenario_version::version1) + { + WARTHOG_GERROR("scenario_manager reading unsupported version"); return std::errc::invalid_argument; - if (auto ec = si.read_header(); ec != std::errc{}) + } + if (auto ec = si.read_header_v1(); ec != std::errc{}) + { + WARTHOG_GERROR_FMT("scenario_manager failed to read scenario header on line: {}", si.get_line_num()); return std::errc::io_error; + } - sfile_ = si.get_map_filename(); + mfile_ = si.get_map_filename(); experiments_.clear(); experiments_.reserve(1024); + uint32_t width = si.get_map_width(); + uint32_t height = si.get_map_height(); + // read queries until done + bool first = true; + while (true) + { + io::scenario_query Q; + auto [con,ec] = si.read_query_line_v1(Q); + if (ec != std::errc{}) + { + WARTHOG_GERROR_FMT("scenario_manager failed to read query on line: {}", si.get_line_num()); + return std::errc::io_error; + } + if (con == io::scenario_serialize::valid) + { + // no need to check for + experiment* ex = std::construct_at( + static_cast(experiments_res_.allocate(sizeof(experiment), alignof(experiment))), + (uint32_t)Q.start_x, (uint32_t)Q.start_y, + (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, + si.get_map_width(), si.get_map_height(), + Q.dist[(int)io::dist_type::octile_ncc], + (first ? std::string(Q.map) : std::string()) + ); + experiments_.push_back(ex); + } else if (con == io::scenario_serialize::final) + { + break; + } + first = false; // store map in first query for backward compatability + } + return std::errc{}; } void From 37eff90427f3fe5f7791df732ca13646173fde73 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 25 Mar 2026 14:16:46 +1100 Subject: [PATCH 38/61] update scenario query syntax --- include/warthog/util/experiment.h | 4 ++-- src/util/scenario_manager.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/warthog/util/experiment.h b/include/warthog/util/experiment.h index bfa76e6..315b183 100644 --- a/include/warthog/util/experiment.h +++ b/include/warthog/util/experiment.h @@ -33,9 +33,9 @@ class experiment public: experiment( uint32_t sx, uint32_t sy, uint32_t gx, uint32_t gy, uint32_t mapwidth, - uint32_t mapheight, double d, std::string m) + uint32_t mapheight, double d, std::string&& m) : startx_(sx), starty_(sy), goalx_(gx), goaly_(gy), - mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), map_(m), + mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), map_(std::move(m)), precision_(4) { } diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 0a53106..82148bb 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -76,9 +76,10 @@ scenario_manager::load_gppc_scenario(std::istream& scenfile) uint32_t height = si.get_map_height(); // read queries until done bool first = true; + io::scenario_query Q; // keep out of loop to reuse std::string while (true) { - io::scenario_query Q; + Q.reset(); auto [con,ec] = si.read_query_line_v1(Q); if (ec != std::errc{}) { From 9c046205c4df0d3dbc792b175d5d88c094f373f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:17:43 +0000 Subject: [PATCH 39/61] auto clang-format action --- include/warthog/io/scenario.h | 159 +++++++++------ include/warthog/util/experiment.h | 4 +- include/warthog/util/scenario_manager.h | 11 +- src/io/scenario.cpp | 250 +++++++++++++----------- src/util/scenario_manager.cpp | 72 ++++--- 5 files changed, 277 insertions(+), 219 deletions(-) diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 7c74724..2a06d15 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -19,16 +19,16 @@ // @created: 2025-12-04 // +#include +#include +#include #include -#include #include #include -#include -#include -#include +#include #include -#include #include +#include namespace warthog::io { @@ -60,7 +60,8 @@ struct scenario_query double goal_y; std::array dist; - void reset() + void + reset() { (*this) = {}; } @@ -97,75 +98,92 @@ class scenario_serialize scenario_serialize(); ~scenario_serialize(); - /// @brief Resets class, including memory. Must use between seperate read/writes, needed for memory managment. - void reset(); + /// @brief Resets class, including memory. Must use between seperate + /// read/writes, needed for memory managment. + void + reset(); - void set_scenario_filename(std::filesystem::path&& filename) + void + set_scenario_filename(std::filesystem::path&& filename) { m_scenario_filename = std::move(filename); } - const std::filesystem::path& get_scenario_filename() const noexcept + const std::filesystem::path& + get_scenario_filename() const noexcept { return m_scenario_filename; } - void set_map_filename(std::filesystem::path&& filename) + void + set_map_filename(std::filesystem::path&& filename) { m_map_filename = std::move(filename); } - const std::filesystem::path& get_map_filename() const noexcept + const std::filesystem::path& + get_map_filename() const noexcept { return m_map_filename; } - void set_relative_map_filename(const std::filesystem::path& filename); + void + set_relative_map_filename(const std::filesystem::path& filename); - void set_version(scenario_version version) noexcept + void + set_version(scenario_version version) noexcept { m_version = version; m_dist.reset(); - if (version == scenario_version::version1) - m_dist.set(0, true); + if(version == scenario_version::version1) m_dist.set(0, true); } - scenario_version get_version() const noexcept + scenario_version + get_version() const noexcept { return m_version; } - /// @brief the types of dist used, only relivent for version2, otherwise octile_ncc(0) + /// @brief the types of dist used, only relivent for version2, otherwise + /// octile_ncc(0) /// @return bitset - auto get_dist_types() const noexcept + auto + get_dist_types() const noexcept { return m_dist; } - bool has_dist_type(dist_type d) const noexcept + bool + has_dist_type(dist_type d) const noexcept { assert((uint32_t)d < (uint32_t)dist_type::dist_count); return m_dist.test((uint32_t)d); } - int32_t get_line_num() const noexcept + int32_t + get_line_num() const noexcept { return m_line_num; } - uint32_t get_map_width() const noexcept + uint32_t + get_map_width() const noexcept { return m_map_width; } - int32_t get_map_height() const noexcept + int32_t + get_map_height() const noexcept { return m_map_height; } - std::string_view get_last_line() const noexcept + std::string_view + get_last_line() const noexcept { return m_line; } - void set_force_int(bool v) noexcept + void + set_force_int(bool v) noexcept { m_force_int = v; } - bool get_force_int() noexcept + bool + get_force_int() noexcept { return m_force_int; } @@ -173,85 +191,100 @@ class scenario_serialize /// @brief opens scenario file get_scenario_filename() for reading /// @param scenario use a user provided instead of get_scenario_filename() /// @return error on operation - std::errc open_read(std::istream* scenario = nullptr); + std::errc + open_read(std::istream* scenario = nullptr); /// @brief opens scenario file get_scenario_filename() for writing /// @param scenario use a user provided instead of get_scenario_filename() /// @return error on operation - std::errc open_write(std::ostream* scenario = nullptr); + std::errc + open_write(std::ostream* scenario = nullptr); - void close(); + void + close(); /// @return if scenario is open for reading - bool can_read(std::istream* in = nullptr) + bool + can_read(std::istream* in = nullptr) { - if (in == nullptr) - in = m_scenario_in; + if(in == nullptr) in = m_scenario_in; return in != nullptr && !in->bad(); } /// @return if scenario is open for writing - bool can_write(std::ostream* out = nullptr) + bool + can_write(std::ostream* out = nullptr) { - if (out == nullptr) - out = m_scenario_out; + if(out == nullptr) out = m_scenario_out; return out != nullptr && !out->bad(); } - /// @brief reads in file version information, and sets version accessable via get_version() + /// @brief reads in file version information, and sets version accessable + /// via get_version() /// @param in optional stream to use, otherwise uses internal-set stream /// @return success std::errc{} (0) - std::errc read_version(std::istream* in = nullptr); + std::errc + read_version(std::istream* in = nullptr); /// @brief with header (without version) information. /// @param in optional stream to use, otherwise uses internal-set stream /// @return success std::errc{} (0) /// /// With version1: peeks first query to gain map name - /// With version2: gets map width/height, available costs and patch filename - std::errc read_header(std::istream* in = nullptr); + /// With version2: gets map width/height, available costs and patch + /// filename + std::errc + read_header(std::istream* in = nullptr); - std::errc read_header_v1(std::istream* in = nullptr); - std::errc read_header_v2(std::istream* in = nullptr); + std::errc + read_header_v1(std::istream* in = nullptr); + std::errc + read_header_v2(std::istream* in = nullptr); - std::pair read_query_line(scenario_query& query, std::istream* in = nullptr); + std::pair + read_query_line(scenario_query& query, std::istream* in = nullptr); - std::pair read_query_line_v1(scenario_query& query, std::istream* in = nullptr); - std::pair read_query_line_v2(scenario_query& query, std::istream* in = nullptr); - std::pair read_patch_line_v2(scenario_patch& query, std::istream* in = nullptr); + std::pair + read_query_line_v1(scenario_query& query, std::istream* in = nullptr); + std::pair + read_query_line_v2(scenario_query& query, std::istream* in = nullptr); + std::pair + read_patch_line_v2(scenario_patch& query, std::istream* in = nullptr); protected: - std::pair get_istream(std::istream* in = nullptr) noexcept + std::pair + get_istream(std::istream* in = nullptr) noexcept { - if (in == nullptr) - in = m_scenario_in; - if (in == nullptr || !in->good()) - return {nullptr, std::errc::io_error}; + if(in == nullptr) in = m_scenario_in; + if(in == nullptr || !in->good()) return {nullptr, std::errc::io_error}; return {in, {}}; } - std::pair get_ostream(std::ostream* out = nullptr) noexcept + std::pair + get_ostream(std::ostream* out = nullptr) noexcept { - if (out == nullptr) - out = m_scenario_out; - if (out == nullptr || !out->good()) + if(out == nullptr) out = m_scenario_out; + if(out == nullptr || !out->good()) return {nullptr, std::errc::io_error}; return {out, {}}; } - std::istream& line_stream(std::string_view line); - std::pair readline(std::istream* in); - void unreadline(std::string_view line); + std::istream& + line_stream(std::string_view line); + std::pair + readline(std::istream* in); + void + unreadline(std::string_view line); protected: - serialize_state m_state = serialize_state::init; + serialize_state m_state = serialize_state::init; scenario_version m_version = scenario_version::version1; - bool m_force_int = false; + bool m_force_int = false; std::filesystem::path m_scenario_filename; std::filesystem::path m_map_filename; std::unique_ptr m_scenario_stream; - std::istream* m_scenario_in = nullptr; + std::istream* m_scenario_in = nullptr; std::ostream* m_scenario_out = nullptr; std::bitset<(size_t)dist_type::dist_count> m_dist; - uint32_t m_map_width = 0; + uint32_t m_map_width = 0; uint32_t m_map_height = 0; - int32_t m_query_at = 0; - int32_t m_line_num = -1; + int32_t m_query_at = 0; + int32_t m_line_num = -1; // dynamic data std::pmr::monotonic_buffer_resource m_dyn_res; diff --git a/include/warthog/util/experiment.h b/include/warthog/util/experiment.h index 315b183..b8f5214 100644 --- a/include/warthog/util/experiment.h +++ b/include/warthog/util/experiment.h @@ -35,8 +35,8 @@ class experiment uint32_t sx, uint32_t sy, uint32_t gx, uint32_t gy, uint32_t mapwidth, uint32_t mapheight, double d, std::string&& m) : startx_(sx), starty_(sy), goalx_(gx), goaly_(gy), - mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), map_(std::move(m)), - precision_(4) + mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), + map_(std::move(m)), precision_(4) { } // no copy diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index a20c6c1..a46bf92 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include namespace warthog::util { @@ -96,7 +96,8 @@ class scenario_manager void load_scenario(const std::filesystem::path& filelocation); void - load_scenario(std::istream& file, std::filesystem::path&& mapfile_override = {}); + load_scenario( + std::istream& file, std::filesystem::path&& mapfile_override = {}); void write_scenario(std::ostream& out); void @@ -114,11 +115,13 @@ class scenario_manager std::filesystem::path find_map_filename( - const scenario_manager& scenmgr, const std::filesystem::path& sfilename = {}); + const scenario_manager& scenmgr, + const std::filesystem::path& sfilename = {}); std::filesystem::path find_map_filename( - const std::filesystem::path& scenmgr, const std::filesystem::path& sfilename = {}); + const std::filesystem::path& scenmgr, + const std::filesystem::path& sfilename = {}); } // namespace warthog::util diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index e45fe2f..845df61 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -3,83 +3,85 @@ #include #include -#include -#include #include +#include #include +#include namespace warthog::io { -scenario_serialize::scenario_serialize() : m_line(static_cast(m_dyn_res.allocate(max_line_length+2))), - m_unget_line(&m_dyn_res), - m_dist_strings(&m_dyn_res), - m_dist_id(&m_dyn_res) +scenario_serialize::scenario_serialize() + : m_line(static_cast(m_dyn_res.allocate(max_line_length + 2))), + m_unget_line(&m_dyn_res), m_dist_strings(&m_dyn_res), + m_dist_id(&m_dyn_res) { m_line[0] = '\0'; } scenario_serialize::~scenario_serialize() = default; -std::errc scenario_serialize::open_read(std::istream* scenario) +std::errc +scenario_serialize::open_read(std::istream* scenario) { close(); - if (scenario != nullptr) + if(scenario != nullptr) { m_scenario_in = scenario; } + else { - m_scenario_in = scenario; - } else { - auto stream = std::make_unique(m_scenario_filename); + auto stream = std::make_unique(m_scenario_filename); m_scenario_in = stream.get(); m_scenario_stream = std::move(stream); } - if (!*m_scenario_in) + if(!*m_scenario_in) { // bad stream - m_scenario_in = nullptr; + m_scenario_in = nullptr; m_scenario_stream = nullptr; return std::errc::io_error; } return std::errc{}; } -void scenario_serialize::close() +void +scenario_serialize::close() { - m_state = serialize_state::init; - m_version = scenario_version::version1; - m_scenario_in = nullptr; + m_state = serialize_state::init; + m_version = scenario_version::version1; + m_scenario_in = nullptr; m_scenario_out = nullptr; m_dist.reset(); - m_map_width = 0; + m_map_width = 0; m_map_height = 0; - m_query_at = 0; - m_line_num = -1; - m_line[0] = '\0'; + m_query_at = 0; + m_line_num = -1; + m_line[0] = '\0'; m_unget_line.clear(); m_dist_strings.clear(); m_dist_id.clear(); m_scenario_stream = nullptr; } -std::pair scenario_serialize::readline(std::istream* in) +std::pair +scenario_serialize::readline(std::istream* in) { size_t len; - if (!m_unget_line.empty()) + if(!m_unget_line.empty()) { // return last unreadline len = m_unget_line.length(); - if (len > max_line_length-1) { + if(len > max_line_length - 1) + { return {{}, std::errc::invalid_argument}; } - std::memcpy(m_line, m_unget_line.c_str(), len+1); + std::memcpy(m_line, m_unget_line.c_str(), len + 1); m_unget_line.clear(); - } else { + } + else + { auto [s, err] = get_istream(in); - if (err != std::errc{}) - { - return {{}, err}; - } - if (!s->getline(m_line, max_line_length)) + if(err != std::errc{}) { return {{}, err}; } + if(!s->getline(m_line, max_line_length)) { - if (s->eof()) + if(s->eof()) return {{}, {}}; else return {{}, std::errc::io_error}; @@ -89,17 +91,21 @@ std::pair scenario_serialize::readline(std::istream m_line_num += 1; return {std::string_view(m_line, len), {}}; } -void scenario_serialize::unreadline(std::string_view line) +void +scenario_serialize::unreadline(std::string_view line) { m_unget_line = line; } -void scenario_serialize::set_relative_map_filename(const std::filesystem::path& filename) +void +scenario_serialize::set_relative_map_filename( + const std::filesystem::path& filename) { m_map_filename = std::filesystem::proximate(filename, m_scenario_filename); } -std::istream& scenario_serialize::line_stream(std::string_view line) +std::istream& +scenario_serialize::line_stream(std::string_view line) { m_iss.clear(); m_iss.str(std::string(line)); @@ -107,49 +113,54 @@ std::istream& scenario_serialize::line_stream(std::string_view line) return m_iss; } -std::errc scenario_serialize::read_version(std::istream* in) +std::errc +scenario_serialize::read_version(std::istream* in) { - if (m_state != serialize_state::init) + if(m_state != serialize_state::init) { return std::errc::state_not_recoverable; } std::errc err; std::tie(in, err) = get_istream(in); - if (err != std::errc{}) + if(err != std::errc{}) { m_state = serialize_state::error; return err; } std::string_view line; std::tie(line, err) = readline(in); - if (err != std::errc{}) + if(err != std::errc{}) { m_state = serialize_state::error; return err; } auto& iss = line_stream(line); int version; - if (!(iss >> m_token >> version)) { + if(!(iss >> m_token >> version)) + { m_state = serialize_state::error; return std::errc::io_error; } - if (version < 1 || version > 2) { + if(version < 1 || version > 2) + { m_state = serialize_state::error; return std::errc::invalid_argument; } - m_version = version == 1 ? scenario_version::version1 : scenario_version::version2; - m_state = serialize_state::header; + m_version = version == 1 ? scenario_version::version1 + : scenario_version::version2; + m_state = serialize_state::header; return std::errc{}; } -std::errc scenario_serialize::read_header(std::istream* in) +std::errc +scenario_serialize::read_header(std::istream* in) { - if (!can_read(in) || m_state != serialize_state::header) + if(!can_read(in) || m_state != serialize_state::header) { return std::errc::state_not_recoverable; } - switch (m_version) + switch(m_version) { case scenario_version::version1: return read_header_v1(get_istream().first); @@ -160,13 +171,15 @@ std::errc scenario_serialize::read_header(std::istream* in) } } -std::errc scenario_serialize::read_header_v1(std::istream* in) +std::errc +scenario_serialize::read_header_v1(std::istream* in) { std::errc ec; bool s; - if (in == nullptr) { + if(in == nullptr) + { std::tie(in, ec) = get_istream(in); - if (ec != std::errc{}) + if(ec != std::errc{}) { m_state = serialize_state::error; return ec; @@ -175,20 +188,21 @@ std::errc scenario_serialize::read_header_v1(std::istream* in) // read first query line to get map scenario_query Q; std::tie(s, ec) = read_query_line_v1(Q); - if (ec != std::errc{}) + if(ec != std::errc{}) { m_state = serialize_state::error; return ec; } - if (s != final) + if(s != final) { - if (Q.width <= 0 || Q.width > GRID_MAX_SIZE || Q.height <= 0 || Q.height > GRID_MAX_SIZE) + if(Q.width <= 0 || Q.width > GRID_MAX_SIZE || Q.height <= 0 + || Q.height > GRID_MAX_SIZE) { m_state = serialize_state::error; return std::errc::invalid_argument; } set_relative_map_filename(Q.map); - m_map_width = Q.width; + m_map_width = Q.width; m_map_height = Q.height; } unreadline(m_line); @@ -196,11 +210,12 @@ std::errc scenario_serialize::read_header_v1(std::istream* in) return std::errc{}; } -std::errc scenario_serialize::read_header_v2(std::istream* in) +std::errc +scenario_serialize::read_header_v2(std::istream* in) { std::errc err; std::tie(in, err) = get_istream(in); - if (err != std::errc{}) + if(err != std::errc{}) { m_state = serialize_state::error; return err; @@ -209,40 +224,40 @@ std::errc scenario_serialize::read_header_v2(std::istream* in) std::string_view line; // height std::tie(line, err) = readline(in); - if (err != std::errc{}) + if(err != std::errc{}) { m_state = serialize_state::error; return err; } { - auto& iss = line_stream(line); - if (!(iss >> m_token >> m_map_height)) - { - m_state = serialize_state::error; - return std::errc::io_error; - } + auto& iss = line_stream(line); + if(!(iss >> m_token >> m_map_height)) + { + m_state = serialize_state::error; + return std::errc::io_error; + } } - if (m_map_height < 1 || m_map_height > GRID_MAX_SIZE) + if(m_map_height < 1 || m_map_height > GRID_MAX_SIZE) { m_state = serialize_state::error; return std::errc::invalid_argument; } // width std::tie(line, err) = readline(in); - if (err != std::errc{}) + if(err != std::errc{}) { m_state = serialize_state::error; return err; } { - auto& iss = line_stream(line); - if (!(iss >> m_token >> m_map_width)) - { - m_state = serialize_state::error; - return std::errc::io_error; - } + auto& iss = line_stream(line); + if(!(iss >> m_token >> m_map_width)) + { + m_state = serialize_state::error; + return std::errc::io_error; + } } - if (m_map_width < 1 || m_map_width > GRID_MAX_SIZE) + if(m_map_width < 1 || m_map_width > GRID_MAX_SIZE) { m_state = serialize_state::error; return std::errc::invalid_argument; @@ -252,56 +267,60 @@ std::errc scenario_serialize::read_header_v2(std::istream* in) return std::errc{}; } -auto scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) -> std::pair +auto +scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) + -> std::pair { assert(can_read(in)); // move to start of read std::errc ec; std::string_view line; std::tie(in, ec) = get_istream(in); - if (ec != std::errc{}) - { - return {invalid, std::errc::io_error}; - } + if(ec != std::errc{}) { return {invalid, std::errc::io_error}; } std::tie(line, ec) = readline(in); - if (ec != std::errc{}) - { - return {invalid, std::errc::io_error}; - } - if (in->eof()) - { - return {final, std::errc{}}; - } - assert(!std::all_of(line.begin(), line.end(), [](char a) { return std::isspace((unsigned char)a); })); + if(ec != std::errc{}) { return {invalid, std::errc::io_error}; } + if(in->eof()) { return {final, std::errc{}}; } + assert(!std::all_of(line.begin(), line.end(), [](char a) { + return std::isspace((unsigned char)a); + })); auto& iss = line_stream(line); - if (!(iss >> query.bucket >> query.map >> query.width >> query.height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y >> query.dist[0])) + if(!(iss >> query.bucket >> query.map >> query.width >> query.height + >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y + >> query.dist[0])) { return {invalid, std::errc::io_error}; } - if (!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) || !std::isfinite(query.dist[0])) + if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) + || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) + || !std::isfinite(query.dist[0])) { return {invalid, std::errc::invalid_argument}; } // perform checking of values, but do not return as error if fail - if (query.width != m_map_width || query.height != m_map_height) + if(query.width != m_map_width || query.height != m_map_height) { return {invalid, std::errc{}}; } - if (m_force_int) { + if(m_force_int) + { // check integer bounds - if (rint(query.start_x) != query.start_x || rint(query.start_y) != query.start_y - || rint(query.goal_x) != query.goal_x || rint(query.goal_y) != query.goal_y) + if(rint(query.start_x) != query.start_x + || rint(query.start_y) != query.start_y + || rint(query.goal_x) != query.goal_x + || rint(query.goal_y) != query.goal_y) { return {invalid, std::errc{}}; } - if (query.start_x < 0 || query.start_y >= m_map_width || - query.goal_x < 0 || query.goal_y >= m_map_height) + if(query.start_x < 0 || query.start_y >= m_map_width + || query.goal_x < 0 || query.goal_y >= m_map_height) { return {invalid, std::errc{}}; } - } else { - if (query.start_x < 0 || query.start_y > m_map_width || - query.goal_x < 0 || query.goal_y > m_map_height) + } + else + { + if(query.start_x < 0 || query.start_y > m_map_width || query.goal_x < 0 + || query.goal_y > m_map_height) { return {invalid, std::errc{}}; } @@ -309,57 +328,52 @@ auto scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* return {valid, std::errc{}}; } -auto scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) -> std::pair +auto +scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) + -> std::pair { assert(can_read(in)); int width, height; double d; query.dist.fill(-1.0); - if (!(m_iss >> query.bucket >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y)) + if(!(m_iss >> query.bucket >> query.start_x >> query.start_y + >> query.goal_x >> query.goal_y)) { return {invalid, std::errc::io_error}; } - for (auto i : m_dist_id) + for(auto i : m_dist_id) { - if (!(m_iss >> d)) - { - return {invalid, std::errc::io_error}; - } - if (!std::isfinite(d)) + if(!(m_iss >> d)) { return {invalid, std::errc::io_error}; } + if(!std::isfinite(d)) { return {invalid, std::errc::invalid_argument}; } - if (i >= 0) { - query.dist[i] = d; - } + if(i >= 0) { query.dist[i] = d; } } m_iss >> std::ws; - if (m_iss.eof()) - { - return {invalid, std::errc::invalid_argument}; - } + if(m_iss.eof()) { return {invalid, std::errc::invalid_argument}; } return {valid, std::errc{}}; } -auto scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) -> std::pair +auto +scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) + -> std::pair { assert(can_read()); int width, height; double d; - if (!(m_iss >> patch.bucket >> patch.patch_id >> patch.loc_x >> patch.loc_y)) + if(!(m_iss >> patch.bucket >> patch.patch_id >> patch.loc_x + >> patch.loc_y)) { return {invalid, std::errc::io_error}; } - if (patch.loc_x >= m_map_width || patch.loc_y > m_map_height) + if(patch.loc_x >= m_map_width || patch.loc_y > m_map_height) { return {invalid, std::errc::invalid_argument}; } m_iss >> std::ws; - if (m_iss.eof()) - { - return {invalid, std::errc::invalid_argument}; - } + if(m_iss.eof()) { return {invalid, std::errc::invalid_argument}; } return {valid, std::errc{}}; } diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 82148bb..205e4d4 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -1,8 +1,8 @@ #include +#include #include #include -#include #include #include @@ -19,21 +19,24 @@ scenario_manager::load_scenario(const std::filesystem::path& filelocation) { sfile_ = filelocation; std::ifstream in(filelocation); - if (load_gppc_scenario(in) != std::errc{}) + if(load_gppc_scenario(in) != std::errc{}) { - WARTHOG_GERROR_FMT("Failed to load scenario file \"{}\"", std::string(sfile_)); + WARTHOG_GERROR_FMT( + "Failed to load scenario file \"{}\"", std::string(sfile_)); throw std::runtime_error("scenario_manager failed to load scenario"); } } void -scenario_manager::load_scenario(std::istream& file, std::filesystem::path&& scenfile) +scenario_manager::load_scenario( + std::istream& file, std::filesystem::path&& scenfile) { sfile_ = std::move(scenfile); load_gppc_scenario(file); - if (load_gppc_scenario(file) != std::errc{}) + if(load_gppc_scenario(file) != std::errc{}) { - WARTHOG_GERROR_FMT("Failed to load scenario file \"{}\"", std::string(sfile_)); + WARTHOG_GERROR_FMT( + "Failed to load scenario file \"{}\"", std::string(sfile_)); throw std::runtime_error("scenario_manager failed to load scenario"); } } @@ -49,59 +52,63 @@ scenario_manager::load_gppc_scenario(std::istream& scenfile) si.set_force_int(true); // open stream and read header - if (auto ec = si.open_read(&scenfile); ec != std::errc{}) + if(auto ec = si.open_read(&scenfile); ec != std::errc{}) { WARTHOG_GERROR("scenario_manager failed to open for read"); return ec; } - if (auto ec = si.read_version(); ec != std::errc{}){ - WARTHOG_GERROR_FMT("scenario_manager failed to read version on line: {}", si.get_line_num()); + if(auto ec = si.read_version(); ec != std::errc{}) + { + WARTHOG_GERROR_FMT( + "scenario_manager failed to read version on line: {}", + si.get_line_num()); return std::errc::io_error; } - if (si.get_version() != io::scenario_version::version1) + if(si.get_version() != io::scenario_version::version1) { WARTHOG_GERROR("scenario_manager reading unsupported version"); return std::errc::invalid_argument; } - if (auto ec = si.read_header_v1(); ec != std::errc{}) + if(auto ec = si.read_header_v1(); ec != std::errc{}) { - WARTHOG_GERROR_FMT("scenario_manager failed to read scenario header on line: {}", si.get_line_num()); + WARTHOG_GERROR_FMT( + "scenario_manager failed to read scenario header on line: {}", + si.get_line_num()); return std::errc::io_error; } - + mfile_ = si.get_map_filename(); experiments_.clear(); experiments_.reserve(1024); - uint32_t width = si.get_map_width(); + uint32_t width = si.get_map_width(); uint32_t height = si.get_map_height(); // read queries until done bool first = true; io::scenario_query Q; // keep out of loop to reuse std::string - while (true) + while(true) { Q.reset(); - auto [con,ec] = si.read_query_line_v1(Q); - if (ec != std::errc{}) + auto [con, ec] = si.read_query_line_v1(Q); + if(ec != std::errc{}) { - WARTHOG_GERROR_FMT("scenario_manager failed to read query on line: {}", si.get_line_num()); + WARTHOG_GERROR_FMT( + "scenario_manager failed to read query on line: {}", + si.get_line_num()); return std::errc::io_error; } - if (con == io::scenario_serialize::valid) + if(con == io::scenario_serialize::valid) { - // no need to check for + // no need to check for experiment* ex = std::construct_at( - static_cast(experiments_res_.allocate(sizeof(experiment), alignof(experiment))), - (uint32_t)Q.start_x, (uint32_t)Q.start_y, - (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, - si.get_map_width(), si.get_map_height(), - Q.dist[(int)io::dist_type::octile_ncc], - (first ? std::string(Q.map) : std::string()) - ); + static_cast(experiments_res_.allocate( + sizeof(experiment), alignof(experiment))), + (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, + (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), + Q.dist[(int)io::dist_type::octile_ncc], + (first ? std::string(Q.map) : std::string())); experiments_.push_back(ex); - } else if (con == io::scenario_serialize::final) - { - break; } + else if(con == io::scenario_serialize::final) { break; } first = false; // store map in first query for backward compatability } return std::errc{}; @@ -209,9 +216,10 @@ find_map_filename( */ std::filesystem::path find_map_filename( - const std::filesystem::path& mapname, const std::filesystem::path& sfilename) + const std::filesystem::path& mapname, + const std::filesystem::path& sfilename) { - namespace fs = std::filesystem; + namespace fs = std::filesystem; // scen file has a map name designated. if(!mapname.empty()) { From 3d6451fc4b8717e82b384100e955560a78f7dbbc Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 1 Apr 2026 14:53:20 +1100 Subject: [PATCH 40/61] update scenario_manager to support dynamic scenario (still to update reading) --- apps/warthog.cpp | 9 - include/warthog/io/log.h | 10 + include/warthog/io/scenario.h | 29 +-- include/warthog/util/experiment.h | 43 ++++- include/warthog/util/scenario_manager.h | 123 ++++++++++-- src/io/scenario.cpp | 34 +++- src/util/scenario_manager.cpp | 241 +++++++++++++++++++++--- 7 files changed, 412 insertions(+), 77 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index c8a420a..1fa4322 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -340,15 +340,6 @@ main(int argc, char** argv) trace_file = cfg.get_param_value("trace"); #endif - // if(gen != "") - // { - // warthog::util::scenario_manager sm; - // warthog::domain::gridmap gm(gen.c_str()); - // sm.generate_experiments(&gm, 1000) ; - // sm.write_scenario(std::cout); - // exit(0); - // } - // running experiments if(alg == "" || sfile == "") { diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 8c61726..3751070 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -56,6 +56,16 @@ #include #include +// define utility for help with log messages +#define WARTHOG_STRING_(x) #x +#define WARTHOG_STRING(x) WARTHOG_STRING_(x) +#define WARTHOG_STRING2(x) WARTHOG_STRING(x) +#define WARTHOG_STRING3(x) WARTHOG_STRING(x) +#define WARTHOG_STRING4(x) WARTHOG_STRING(x) + +#define WARTHOG_LINE WARTHOG_STRING2(__LINE__) +#define WARTHOG_FILENAME_LINE __FILE__ "@" WARTHOG_LINE + // default log levels, also for global logger #ifdef WARTHOG_LOG #define WARTHOG_DEFAULT_LOG_LEVEL WARTHOG_LOG diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 2a06d15..4e2d2b9 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -35,17 +35,18 @@ namespace warthog::io enum class scenario_version : uint8_t { - version1, - version2, + UNKNOWN, + VERSION_1, + VERSION_2, }; enum class dist_type : uint8_t { - octile_ncc, - octile_cc, - manhatten, - anyangle_ncc, - anyangle_cc, - dist_count, + N_8C_NCC, + N_8C_CC, + N_4C, + AA_NCC, + AA_CC, + DISC_COUNT, }; struct scenario_query @@ -58,7 +59,7 @@ struct scenario_query double start_y; double goal_x; double goal_y; - std::array dist; + std::array dist; void reset() @@ -133,7 +134,7 @@ class scenario_serialize { m_version = version; m_dist.reset(); - if(version == scenario_version::version1) m_dist.set(0, true); + if(version == scenario_version::VERSION_1) m_dist.set(0, true); } scenario_version get_version() const noexcept @@ -152,7 +153,7 @@ class scenario_serialize bool has_dist_type(dist_type d) const noexcept { - assert((uint32_t)d < (uint32_t)dist_type::dist_count); + assert((uint32_t)d < (uint32_t)dist_type::DISC_COUNT); return m_dist.test((uint32_t)d); } @@ -227,7 +228,7 @@ class scenario_serialize /// @param in optional stream to use, otherwise uses internal-set stream /// @return success std::errc{} (0) /// - /// With version1: peeks first query to gain map name + /// With VERSION1: peeks first query to gain map name /// With version2: gets map width/height, available costs and patch /// filename std::errc @@ -273,14 +274,14 @@ class scenario_serialize protected: serialize_state m_state = serialize_state::init; - scenario_version m_version = scenario_version::version1; + scenario_version m_version = scenario_version::UNKNOWN; bool m_force_int = false; std::filesystem::path m_scenario_filename; std::filesystem::path m_map_filename; std::unique_ptr m_scenario_stream; std::istream* m_scenario_in = nullptr; std::ostream* m_scenario_out = nullptr; - std::bitset<(size_t)dist_type::dist_count> m_dist; + std::bitset<(size_t)dist_type::DISC_COUNT> m_dist; uint32_t m_map_width = 0; uint32_t m_map_height = 0; int32_t m_query_at = 0; diff --git a/include/warthog/util/experiment.h b/include/warthog/util/experiment.h index b8f5214..a0bf04a 100644 --- a/include/warthog/util/experiment.h +++ b/include/warthog/util/experiment.h @@ -23,7 +23,7 @@ #include #include -#include +#include namespace warthog::util { @@ -33,10 +33,17 @@ class experiment public: experiment( uint32_t sx, uint32_t sy, uint32_t gx, uint32_t gy, uint32_t mapwidth, - uint32_t mapheight, double d, std::string&& m) + uint32_t mapheight, double d, std::string_view m) : startx_(sx), starty_(sy), goalx_(gx), goaly_(gy), mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), - map_(std::move(m)), precision_(4) + map_(m), precision_(4) + { } + experiment( + double sx, double sy, double gx, double gy, uint32_t mapwidth, + uint32_t mapheight, double d, std::string_view m) + : startx_(sx), starty_(sy), goalx_(gx), goaly_(gy), + mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), + map_(m), precision_(4) { } // no copy @@ -49,24 +56,44 @@ class experiment uint32_t startx() const noexcept + { + return static_cast(startx_); + } + double + startx_f() const noexcept { return startx_; } uint32_t starty() const noexcept + { + return static_cast(starty_); + } + double + starty_f() const noexcept { return starty_; } uint32_t goalx() const noexcept + { + return static_cast(goalx_); + } + double + goalx_f() const noexcept { return goalx_; } uint32_t goaly() const noexcept + { + return static_cast(goaly_); + } + double + goaly_f() const noexcept { return goaly_; } @@ -77,7 +104,7 @@ class experiment return distance_; } - const std::string& + std::string_view map() const noexcept { return map_; @@ -114,15 +141,15 @@ class experiment get_instance() const noexcept { return search::problem_instance( - pack_id{starty_ * mapwidth_ + startx_}, - pack_id{goaly_ * mapwidth_ + goalx_}); + pack_id{starty() * mapwidth_ + startx()}, + pack_id{goaly() * mapwidth_ + goalx()}); } private: - uint32_t startx_, starty_, goalx_, goaly_; + double startx_, starty_, goalx_, goaly_; uint32_t mapwidth_, mapheight_; double distance_; - std::string map_; + std::string_view map_; int32_t precision_; }; diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index a46bf92..2f20f44 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -32,6 +32,30 @@ namespace warthog::util { +struct scenario_command +{ + enum type_ : uint8_t { + SNAPSHOT, + PATCH, + QUERY + }; + int type; ///< command type + int32_t bucket; ///< bucket id number (meta), snapshot id for dynamic + int32_t id; ///< SNAPSHOT: snapshot num, PATCH: patch to apply, QUERY: experiment num + union cmd_ { + struct snapshot_ { + uint32_t count; ///< number of commands until next SNAPSHOT + } snapshot; + struct patch_ { + uint16_t topleft_x; + uint16_t topleft_y; + } patch; + struct query_ { + uint32_t query_id; ///< experiment number + } query; + } cmd; ///< command union based on type +}; + class scenario_manager { public: @@ -39,16 +63,77 @@ class scenario_manager ~scenario_manager(); experiment* - get_experiment(unsigned int which) + get_experiment(uint32_t which) { - if(which < experiments_.size()) { return experiments_[which]; } - return 0; + if(which >= experiments_.size()) + return nullptr; + if (static_scenario_start_ >= 0) { + return experiments_[which]; + } + // handle dynamic scenario + if (experiment_at_ == which) { + return experiments_[which]; + } else if (experiment_at_ < (int32_t)which) { + return experiment_next((int32_t)which - experiment_at_).first; + } else { + throw std::logic_error("scenario_manager::get_experiment can only progress forwards in dynamic scenarios."); + } } const experiment* - get_experiment(unsigned int which) const + get_experiment(uint32_t which) const + { + if (static_scenario_start_ >= 0) { + if(which < experiments_.size()) { return experiments_[which]; } + return nullptr; + } + throw std::logic_error("scenario_manager::get_experiment can only be const for static scenarios."); + } + + /// @brief progress from current to count experiment away and return it + /// @param count + /// @return pair of the reached query and snapshot id. + /// + /// Will progress through commands until count queries are encounted, returning the final query. + /// When count == 1, is exactly the next query. + /// All patches required + std::pair + experiment_next(uint32_t count = 1); + + /// @brief reset scenario to first command + void + restart(); + /// @brief goto the start of the next snapshot (SNAPSHOT command) + /// @return snapshot id reached, or -1 if at end of commands (no more snapshots) + /// + /// If current command is SNAPSHOT, if id != current_snapshot() then already at next snapshot, + /// otherwise progress until next SNAPSHOT command is reached (or end of commands). + /// Clears all patches from get_patches() and replaces with any PATCH command to next snapshot. + int + snapshot_next(bool clear_patch = true); + /// @brief starting at SNAPSHOT or current PATCH, apply all patches until reaching SNAPSHOT or QUERY + /// @param clear_patch clears get_patches() + /// @return the number of patches, patch id are retrivable from get_patches() + /// + /// Requires to be on SNAPSHOT or PATCH, otherwise returns 0 and does nothing. + /// If @clear_patch is set, clears get_patches(). + /// Appends all processed PATCH commands to get_patches(). + int + snapshot_patches(bool clear_patch = true); + /// @brief returns the current query experiment if at query and progress to next command + /// @return the current query experiment if command is query, otherwise nullptr + /// + /// Requires to be on QUERY, otherwise return nullptr and do nothing. + /// If QUERY, returns corrisponding experiment and goto next command. + /// Does not affect get_patches(). + experiment* + snapshot_query(); + + bool complete() const noexcept { return command_at_ >= commands_.size(); } + + std::span + get_patches() const noexcept { - if(which < experiments_.size()) { return experiments_[which]; } - return 0; + return patches_; } void @@ -85,14 +170,8 @@ class scenario_manager return mfile_; } void - clear() - { - experiments_.clear(); - experiments_res_.release(); - } + clear(); - void - generate_experiments(domain::gridmap*, int num); void load_scenario(const std::filesystem::path& filelocation); void @@ -100,17 +179,31 @@ class scenario_manager std::istream& file, std::filesystem::path&& mapfile_override = {}); void write_scenario(std::ostream& out); - void - sort(); // organise by increasing solution length + + bool is_static_scenario() const noexcept { return static_scenario_start_ >= 0; } protected: std::errc load_gppc_scenario(std::istream& scenfile); + std::errc + load_gppc_scenario_body_v1(io::scenario_serialize& si); + std::errc + load_gppc_scenario_body_v2(io::scenario_serialize& si); + + std::string_view copy_string(std::string_view str); + std::pmr::monotonic_buffer_resource experiments_res_; std::vector experiments_; + std::vector commands_; + std::vector patches_; std::filesystem::path sfile_; std::filesystem::path mfile_; + io::scenario_version version_ = io::scenario_version::UNKNOWN; + int32_t static_scenario_start_ = -1; ///< >=0: is static scenario where query commands start at pos, else is dynamic scenario + uint32_t command_at_ = 0; ///< command at, used for dynamic scenario + int32_t experiment_at_ = 0; + int32_t snapshot_at_ = -1; }; std::filesystem::path diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 845df61..333dbb2 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -45,7 +45,7 @@ void scenario_serialize::close() { m_state = serialize_state::init; - m_version = scenario_version::version1; + m_version = scenario_version::UNKNOWN; m_scenario_in = nullptr; m_scenario_out = nullptr; m_dist.reset(); @@ -146,8 +146,8 @@ scenario_serialize::read_version(std::istream* in) m_state = serialize_state::error; return std::errc::invalid_argument; } - m_version = version == 1 ? scenario_version::version1 - : scenario_version::version2; + m_version = version == 1 ? scenario_version::VERSION_1 + : scenario_version::VERSION_2; m_state = serialize_state::header; return std::errc{}; } @@ -162,9 +162,9 @@ scenario_serialize::read_header(std::istream* in) switch(m_version) { - case scenario_version::version1: + case scenario_version::VERSION_1: return read_header_v1(get_istream().first); - case scenario_version::version2: + case scenario_version::VERSION_2: return read_header_v2(get_istream().first); default: return std::errc::state_not_recoverable; @@ -237,7 +237,7 @@ scenario_serialize::read_header_v2(std::istream* in) return std::errc::io_error; } } - if(m_map_height < 1 || m_map_height > GRID_MAX_SIZE) + if(m_token != "height" || m_map_height < 1 || m_map_height > GRID_MAX_SIZE) { m_state = serialize_state::error; return std::errc::invalid_argument; @@ -257,7 +257,27 @@ scenario_serialize::read_header_v2(std::istream* in) return std::errc::io_error; } } - if(m_map_width < 1 || m_map_width > GRID_MAX_SIZE) + // cost + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + m_state = serialize_state::error; + return err; + } + { + auto& iss = line_stream(line); + int cost_count; + if(!(iss >> m_token >> cost_count)) + { + m_state = serialize_state::error; + return std::errc::io_error; + } + if (cost_count < 0 || cost_count > 128) + { + // limit cost_count to be within reason + } + } + if(m_token != "width" || m_map_width < 1 || m_map_width > GRID_MAX_SIZE) { m_state = serialize_state::error; return std::errc::invalid_argument; diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 205e4d4..672d139 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -14,6 +14,128 @@ scenario_manager::scenario_manager() { } scenario_manager::~scenario_manager() = default; +void scenario_manager::clear() +{ + experiments_.clear(); + experiments_res_.release(); + sfile_.clear(); + mfile_.clear(); + restart(); +} + +std::pair scenario_manager::experiment_next(uint32_t count) +{ + patches_.clear(); // reset patches + if (count == 0) + return {nullptr, 0}; + uint32_t command_size = static_cast(commands_.size()); + int patch_count = 0; + while (command_at_ < command_size) { + auto cmd = commands_[command_at_]; + // command_at_ incremented in following fuction calls + switch (cmd.type) { + case scenario_command::SNAPSHOT: + case scenario_command::PATCH: + patch_count += snapshot_patches(); + break; + case scenario_command::QUERY: + if (experiment* query = snapshot_query(); query != nullptr) { + if (--count == 0) + return {query, patch_count}; + } + break; + default: + // should never be reached + ++command_at_; + WARTHOG_GDEBUG("scenario_manager::experiment_next invalid command type in " WARTHOG_FILENAME_LINE); + } + // exits loop + } + // no more experiments + return {nullptr, patch_count}; +} + +void +scenario_manager::restart() +{ + patches_.clear(); + version_ = io::scenario_version::UNKNOWN; + static_scenario_start_ = -1; + command_at_ = 0; + experiment_at_ = -1; + snapshot_at_ = -1; +} + +int +scenario_manager::snapshot_next(bool clear_patch) +{ + if (clear_patch) + patches_.clear(); + const uint32_t command_size = static_cast(commands_.size()); + while (command_at_ < command_size) { + auto cmd = commands_[command_at_]; + switch (cmd.type) { + case scenario_command::SNAPSHOT: + if (cmd.id != snapshot_at_) { + // at new snapshot, return + snapshot_at_ = cmd.id; + return snapshot_at_; + } + // current snapshot, goto next snapshot + [[fallthrough]]; + case scenario_command::QUERY: + ++snapshot_at_; + break; + case scenario_command::PATCH: + snapshot_patches(false); + // snapshot_patches increments snapshot_at_ + break; + default: + WARTHOG_GDEBUG("scenario_manager::snapshot_next invalid command type in " WARTHOG_FILENAME_LINE); + } + } + return false; +} + +int +scenario_manager::snapshot_patches(bool clear_patch) +{ + if (clear_patch) + patches_.clear(); + const uint32_t command_size = static_cast(commands_.size()); + int count = 0; + // if start of snapshot, apply that snapshot patches + if (command_at_ < command_size && commands_[command_at_].type == scenario_command::SNAPSHOT) + command_at_ += 1; + // while command_at_ is PATCH, add patch to applied list + while (command_at_ < command_size) { + if (auto cmd = commands_[command_at_]; cmd.bucket == scenario_command::PATCH) { + command_at_ += 1; + count += 1; + patches_.push_back(cmd.id); + } + } + // end at first non-PATCH command + return count; +} + +experiment* +scenario_manager::snapshot_query() +{ + if (command_at_ >= commands_.size()) + return nullptr; + auto cmd = commands_[command_at_]; + if (cmd.type != scenario_command::QUERY) + return nullptr; + command_at_ += 1; + experiment_at_ += 1; + if (cmd.cmd.query.query_id >= experiments_.size() || cmd.cmd.query.query_id != (uint32_t)experiment_at_) { + WARTHOG_GERROR_FMT("scenario_manager::snapshot_query invalid query_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.query_id, experiment_at_, experiments_.size(), WARTHOG_FILENAME_LINE); + return nullptr; + } + return experiments_[cmd.cmd.query.query_id]; +} + void scenario_manager::load_scenario(const std::filesystem::path& filelocation) { @@ -64,27 +186,96 @@ scenario_manager::load_gppc_scenario(std::istream& scenfile) si.get_line_num()); return std::errc::io_error; } - if(si.get_version() != io::scenario_version::version1) + auto version = si.get_version(); + if (version == io::scenario_version::VERSION_1) { + return load_gppc_scenario_body_v1(si); + } else if(version == io::scenario_version::VERSION_2) { + return load_gppc_scenario_body_v2(si); + } else { WARTHOG_GERROR("scenario_manager reading unsupported version"); return std::errc::invalid_argument; } +} + +std::errc +scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) +{ + if(auto ec = si.read_header_v1(); ec != std::errc{}) { WARTHOG_GERROR_FMT( - "scenario_manager failed to read scenario header on line: {}", + "scenario_manager failed to read scenario v1 header on line: {}", + si.get_line_num()); + return std::errc::io_error; + } + + mfile_ = si.get_map_filename(); + experiments_.reserve(1024); + uint32_t width = si.get_map_width(); + uint32_t height = si.get_map_height(); + // read queries until done + bool first = true; + io::scenario_query Q; // keep out of loop to reuse std::string + std::string_view map_string; + while(true) + { + Q.reset(); + auto [con, ec] = si.read_query_line_v1(Q); + if(ec != std::errc{}) + { + WARTHOG_GERROR_FMT( + "scenario_manager failed to read query on line: {}", + si.get_line_num()); + return std::errc::io_error; + } + if(con == io::scenario_serialize::valid) + { + std::string_view current_map(Q.map); + if (current_map.size() > 2048) // limit string size + { + WARTHOG_GERROR_FMT( + "scenario_manager v1 query line map exceeds 2048 chars on line: {}", + si.get_line_num()); + return std::errc::argument_out_of_domain; + } + if (map_string != current_map) { + map_string = copy_string(current_map); + } + experiment* ex = std::construct_at( + static_cast(experiments_res_.allocate( + sizeof(experiment), alignof(experiment))), + (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, + (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), + Q.dist[(int)io::dist_type::N_8C_NCC], + map_string); + experiments_.push_back(ex); + } + else if(con == io::scenario_serialize::final) { break; } + } + return std::errc{}; +} + +std::errc +scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) +{ + + if(auto ec = si.read_header_v2(); ec != std::errc{}) + { + WARTHOG_GERROR_FMT( + "scenario_manager failed to read scenario v2 header on line: {}", si.get_line_num()); return std::errc::io_error; } mfile_ = si.get_map_filename(); - experiments_.clear(); experiments_.reserve(1024); uint32_t width = si.get_map_width(); uint32_t height = si.get_map_height(); // read queries until done bool first = true; io::scenario_query Q; // keep out of loop to reuse std::string + std::string_view map_string; while(true) { Q.reset(); @@ -98,22 +289,41 @@ scenario_manager::load_gppc_scenario(std::istream& scenfile) } if(con == io::scenario_serialize::valid) { - // no need to check for + std::string_view current_map(Q.map); + if (current_map.size() > 2048) // limit string size + { + WARTHOG_GERROR_FMT( + "scenario_manager v1 query line map exceeds 2048 chars on line: {}", + si.get_line_num()); + return std::errc::argument_out_of_domain; + } + if (map_string != current_map) { + map_string = copy_string(current_map); + } experiment* ex = std::construct_at( static_cast(experiments_res_.allocate( sizeof(experiment), alignof(experiment))), (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), - Q.dist[(int)io::dist_type::octile_ncc], - (first ? std::string(Q.map) : std::string())); + Q.dist[(int)io::dist_type::N_8C_NCC], + map_string); experiments_.push_back(ex); } else if(con == io::scenario_serialize::final) { break; } - first = false; // store map in first query for backward compatability } return std::errc{}; } +std::string_view scenario_manager::copy_string(std::string_view str) +{ + if (str.empty()) + return std::string_view(); + char* mapchars = static_cast(experiments_res_.allocate(str.size() + 1)); + std::memcpy(mapchars, str.data(), str.size()); + mapchars[str.size()] = '\0'; + return std::string_view(mapchars, str.size()); +} + void scenario_manager::write_scenario(std::ostream& scenariofile) { @@ -136,23 +346,6 @@ scenario_manager::write_scenario(std::ostream& scenariofile) // scenariofile.close(); } -void -scenario_manager::sort() -{ - for(unsigned int i = 0; i < experiments_.size(); i++) - { - for(unsigned int j = i; j < experiments_.size(); j++) - { - if(experiments_.at(j)->distance() < experiments_.at(i)->distance()) - { - experiment* tmp = experiments_.at(i); - experiments_.at(i) = experiments_.at(j); - experiments_.at(j) = tmp; - } - } - } -} - /** * Finds a matching map file to a scenario. * Take mappath as scenmgr map name. scendir as partent(sfilename), or From d2b177e36be7722da29d76f25966f0e0273e2f72 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 9 Apr 2026 09:17:13 +1000 Subject: [PATCH 41/61] dynamic scenario progress --- include/warthog/io/scenario.h | 75 ++++++++++++++++++++----- include/warthog/util/scenario_manager.h | 23 ++++++-- src/io/scenario.cpp | 31 ++++++---- src/util/scenario_manager.cpp | 13 ++++- 4 files changed, 109 insertions(+), 33 deletions(-) diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 4e2d2b9..c5c680a 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace warthog::io { @@ -46,20 +47,20 @@ enum class dist_type : uint8_t N_4C, AA_NCC, AA_CC, - DISC_COUNT, + OTHER, }; struct scenario_query { int64_t bucket; - std::string map; + std::string_view map; int32_t width; int32_t height; double start_x; double start_y; double goal_x; double goal_y; - std::array dist; + std::span dist; void reset() @@ -99,6 +100,9 @@ class scenario_serialize scenario_serialize(); ~scenario_serialize(); + static constexpr std::string_view get_dist_str(dist_type a) noexcept; + static constexpr dist_type get_dist_type(std::string_view a) noexcept; + /// @brief Resets class, including memory. Must use between seperate /// read/writes, needed for memory managment. void @@ -133,8 +137,6 @@ class scenario_serialize set_version(scenario_version version) noexcept { m_version = version; - m_dist.reset(); - if(version == scenario_version::VERSION_1) m_dist.set(0, true); } scenario_version get_version() const noexcept @@ -145,16 +147,26 @@ class scenario_serialize /// @brief the types of dist used, only relivent for version2, otherwise /// octile_ncc(0) /// @return bitset - auto - get_dist_types() const noexcept + std::span + get_dist_strings() const noexcept { - return m_dist; + return m_dist_strings; } - bool - has_dist_type(dist_type d) const noexcept + std::span + get_dist_type() const noexcept + { + return m_dist_type; + } + std::span + get_dist_value() const noexcept + { + return m_dist_value; + } + int + get_dist_index(dist_type d) const noexcept { - assert((uint32_t)d < (uint32_t)dist_type::DISC_COUNT); - return m_dist.test((uint32_t)d); + auto it = std::find(m_dist_type.begin(), m_dist_type.end(), d); + return it != m_dist_type.end() ? static_cast(it - m_dist_type.begin()) : -1; } int32_t @@ -281,7 +293,6 @@ class scenario_serialize std::unique_ptr m_scenario_stream; std::istream* m_scenario_in = nullptr; std::ostream* m_scenario_out = nullptr; - std::bitset<(size_t)dist_type::DISC_COUNT> m_dist; uint32_t m_map_width = 0; uint32_t m_map_height = 0; int32_t m_query_at = 0; @@ -292,8 +303,10 @@ class scenario_serialize std::pmr::monotonic_buffer_resource m_string_res; char* m_line = nullptr; ///< sets from m_dyn_res std::pmr::string m_unget_line; - std::pmr::vector m_dist_strings; - std::pmr::vector m_dist_id; + // distance types + std::pmr::vector m_dist_strings; ///< names read in + std::pmr::vector m_dist_type; ///< dist_type of index (corrisponding dist_strings and dist_value) + std::pmr::vector m_dist_value; ///< distance value stored, will be placed in dynamic_scenario // shared temp parameter // TODO: replace with a custom string stream that does not allocate memory @@ -301,6 +314,38 @@ class scenario_serialize std::string m_token; }; +inline constexpr std::string_view scenario_serialize::get_dist_str(dist_type a) noexcept +{ + switch(a) { + case dist_type::N_8C_NCC: + return "8c-ncc"; + case dist_type::N_8C_CC: + return "8c-cc"; + case dist_type::N_4C: + return "4c"; + case dist_type::AA_NCC: + return "aa-ncc"; + case dist_type::AA_CC: + return "aa-cc"; + default: + return std::string_view(); + } +} +inline constexpr dist_type scenario_serialize::get_dist_type(std::string_view a) noexcept +{ + if (a == "8c-ncc") + return dist_type::N_8C_NCC; + if (a == "8c-cc") + return dist_type::N_8C_CC; + if (a == "4c") + return dist_type::N_4C; + if (a == "aa-ncc") + return dist_type::AA_NCC; + if (a == "aa-cc") + return dist_type::AA_CC; + return dist_type::OTHER; +} + } // namespace warthog::util #endif // WARTHOG_IO_SCENARIO_H diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index 2f20f44..294976a 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -41,19 +41,30 @@ struct scenario_command }; int type; ///< command type int32_t bucket; ///< bucket id number (meta), snapshot id for dynamic - int32_t id; ///< SNAPSHOT: snapshot num, PATCH: patch to apply, QUERY: experiment num + uint32_t id; ///< SNAPSHOT: snapshot num, PATCH: patch to apply, QUERY: query id union cmd_ { - struct snapshot_ { - uint32_t count; ///< number of commands until next SNAPSHOT - } snapshot; + struct snapshot_ { } snapshot; struct patch_ { uint16_t topleft_x; uint16_t topleft_y; } patch; struct query_ { - uint32_t query_id; ///< experiment number + uint32_t experiment_id; ///< experiment number } query; } cmd; ///< command union based on type + + static constexpr scenario_command make_snapshot(int32_t bucket_id, uint32_t snapshot_id) noexcept + { + return scenario_command{SNAPSHOT, bucket_id, snapshot_id, {.snapshot={}}}; + } + static constexpr scenario_command make_patch(int32_t bucket_id, uint32_t patch_id, uint16_t x, uint16_t y) noexcept + { + return scenario_command{PATCH, bucket_id, patch_id, {.patch={x,y}}}; + } + static constexpr scenario_command make_query(int32_t bucket_id, uint32_t query_id, uint32_t experiment_id) noexcept + { + return scenario_command{QUERY, bucket_id, query_id, {.query={experiment_id}}}; + } }; class scenario_manager @@ -200,6 +211,8 @@ class scenario_manager std::filesystem::path sfile_; std::filesystem::path mfile_; io::scenario_version version_ = io::scenario_version::UNKNOWN; + uint32_t query_count_ = 0; + uint32_t patch_count_ = 0; int32_t static_scenario_start_ = -1; ///< >=0: is static scenario where query commands start at pos, else is dynamic scenario uint32_t command_at_ = 0; ///< command at, used for dynamic scenario int32_t experiment_at_ = 0; diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 333dbb2..b969c45 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -12,9 +12,10 @@ namespace warthog::io { scenario_serialize::scenario_serialize() - : m_line(static_cast(m_dyn_res.allocate(max_line_length + 2))), + : m_dyn_res(1024), m_string_res(256), + m_line(static_cast(m_dyn_res.allocate(max_line_length + 2))), m_unget_line(&m_dyn_res), m_dist_strings(&m_dyn_res), - m_dist_id(&m_dyn_res) + m_dist_type(&m_dyn_res), m_dist_value(&m_dyn_res) { m_line[0] = '\0'; } @@ -48,15 +49,16 @@ scenario_serialize::close() m_version = scenario_version::UNKNOWN; m_scenario_in = nullptr; m_scenario_out = nullptr; - m_dist.reset(); m_map_width = 0; m_map_height = 0; m_query_at = 0; m_line_num = -1; - m_line[0] = '\0'; + if (m_line != nullptr) + m_line[0] = '\0'; m_unget_line.clear(); m_dist_strings.clear(); - m_dist_id.clear(); + m_dist_type.clear(); + m_dist_value.clear(); m_scenario_stream = nullptr; } @@ -87,8 +89,8 @@ scenario_serialize::readline(std::istream* in) return {{}, std::errc::io_error}; } len = strlen(m_line); + m_line_num += 1; } - m_line_num += 1; return {std::string_view(m_line, len), {}}; } void @@ -207,6 +209,13 @@ scenario_serialize::read_header_v1(std::istream* in) } unreadline(m_line); m_state = serialize_state::query; + // setup distance types + m_dist_strings.resize(1); + m_dist_type.resize(1); + m_dist_value.resize(1); + m_dist_strings[0] = get_dist_str(dist_type::N_8C_NCC); + m_dist_type[0] = dist_type::N_8C_NCC; + m_dist_value[0] = -1; return std::errc{}; } @@ -304,12 +313,14 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) return std::isspace((unsigned char)a); })); auto& iss = line_stream(line); - if(!(iss >> query.bucket >> query.map >> query.width >> query.height + if(!(iss >> query.bucket >> m_token >> query.width >> query.height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y - >> query.dist[0])) + >> m_dist_value.at(0))) { return {invalid, std::errc::io_error}; } + query.map = m_token; + query.dist = m_dist_value; if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) || !std::isfinite(query.dist[0])) @@ -355,13 +366,13 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) assert(can_read(in)); int width, height; double d; - query.dist.fill(-1.0); + std::fill(m_dist_value.begin(), m_dist_value.end(), -1.0); if(!(m_iss >> query.bucket >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y)) { return {invalid, std::errc::io_error}; } - for(auto i : m_dist_id) + for(uint32_t i = 0, ie = m_dist_type.size(); i < ie; ++i) { if(!(m_iss >> d)) { return {invalid, std::errc::io_error}; } if(!std::isfinite(d)) diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 672d139..bd486cd 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -129,11 +129,11 @@ scenario_manager::snapshot_query() return nullptr; command_at_ += 1; experiment_at_ += 1; - if (cmd.cmd.query.query_id >= experiments_.size() || cmd.cmd.query.query_id != (uint32_t)experiment_at_) { - WARTHOG_GERROR_FMT("scenario_manager::snapshot_query invalid query_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.query_id, experiment_at_, experiments_.size(), WARTHOG_FILENAME_LINE); + if (cmd.cmd.query.experiment_id >= experiments_.size() || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) { + WARTHOG_GERROR_FMT("scenario_manager::snapshot_query invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, experiments_.size(), WARTHOG_FILENAME_LINE); return nullptr; } - return experiments_[cmd.cmd.query.query_id]; + return experiments_[cmd.cmd.query.experiment_id]; } void @@ -212,8 +212,14 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) mfile_ = si.get_map_filename(); experiments_.reserve(1024); + commands_.reserve(1024); uint32_t width = si.get_map_width(); uint32_t height = si.get_map_height(); + // setup commands header for static scenario + commands_.push_back(scenario_command::make_snapshot(0, 0)); + commands_.push_back(scenario_command::make_patch(0, 0, 0, 0)); + ++patch_count_; + static_scenario_start_ = (int32_t)commands_.size(); // read queries until done bool first = true; io::scenario_query Q; // keep out of loop to reuse std::string @@ -250,6 +256,7 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) Q.dist[(int)io::dist_type::N_8C_NCC], map_string); experiments_.push_back(ex); + commands_.push_back(scenario_command::make_query(Q.bucket, query_count_++, (uint32_t)(experiments_.size()-1))); } else if(con == io::scenario_serialize::final) { break; } } From 77d8800c27ff63cafd1bea332598d9451f5f9e9f Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 9 Apr 2026 22:47:15 +1000 Subject: [PATCH 42/61] seperated scenario_manager to scenario_runner --- include/warthog/io/scenario.h | 54 ++++---- include/warthog/util/scenario_manager.h | 85 ++++-------- include/warthog/util/scenario_runner.h | 106 +++++++++++++++ src/CMakeLists.txt | 1 + src/io/scenario.cpp | 12 +- src/util/scenario_manager.cpp | 118 +---------------- src/util/scenario_runner.cpp | 164 ++++++++++++++++++++++++ 7 files changed, 332 insertions(+), 208 deletions(-) create mode 100644 include/warthog/util/scenario_runner.h create mode 100644 src/util/scenario_runner.cpp diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index c5c680a..a767162 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include namespace warthog::io { @@ -40,11 +42,11 @@ enum class scenario_version : uint8_t VERSION_1, VERSION_2, }; -enum class dist_type : uint8_t +enum class cost_type : uint8_t { - N_8C_NCC, - N_8C_CC, - N_4C, + G_8C_NCC, + G_8C_CC, + G_4C, AA_NCC, AA_CC, OTHER, @@ -100,8 +102,8 @@ class scenario_serialize scenario_serialize(); ~scenario_serialize(); - static constexpr std::string_view get_dist_str(dist_type a) noexcept; - static constexpr dist_type get_dist_type(std::string_view a) noexcept; + static constexpr std::string_view get_dist_str(cost_type a) noexcept; + static constexpr cost_type get_cost_type(std::string_view a) noexcept; /// @brief Resets class, including memory. Must use between seperate /// read/writes, needed for memory managment. @@ -152,10 +154,10 @@ class scenario_serialize { return m_dist_strings; } - std::span - get_dist_type() const noexcept + std::span + get_cost_type() const noexcept { - return m_dist_type; + return m_cost_type; } std::span get_dist_value() const noexcept @@ -163,10 +165,10 @@ class scenario_serialize return m_dist_value; } int - get_dist_index(dist_type d) const noexcept + get_dist_index(cost_type d) const noexcept { - auto it = std::find(m_dist_type.begin(), m_dist_type.end(), d); - return it != m_dist_type.end() ? static_cast(it - m_dist_type.begin()) : -1; + auto it = std::find(m_cost_type.begin(), m_cost_type.end(), d); + return it != m_cost_type.end() ? static_cast(it - m_cost_type.begin()) : -1; } int32_t @@ -305,7 +307,7 @@ class scenario_serialize std::pmr::string m_unget_line; // distance types std::pmr::vector m_dist_strings; ///< names read in - std::pmr::vector m_dist_type; ///< dist_type of index (corrisponding dist_strings and dist_value) + std::pmr::vector m_cost_type; ///< cost_type of index (corrisponding dist_strings and dist_value) std::pmr::vector m_dist_value; ///< distance value stored, will be placed in dynamic_scenario // shared temp parameter @@ -314,36 +316,36 @@ class scenario_serialize std::string m_token; }; -inline constexpr std::string_view scenario_serialize::get_dist_str(dist_type a) noexcept +inline constexpr std::string_view scenario_serialize::get_dist_str(cost_type a) noexcept { switch(a) { - case dist_type::N_8C_NCC: + case cost_type::G_8C_NCC: return "8c-ncc"; - case dist_type::N_8C_CC: + case cost_type::G_8C_CC: return "8c-cc"; - case dist_type::N_4C: + case cost_type::G_4C: return "4c"; - case dist_type::AA_NCC: + case cost_type::AA_NCC: return "aa-ncc"; - case dist_type::AA_CC: + case cost_type::AA_CC: return "aa-cc"; default: return std::string_view(); } } -inline constexpr dist_type scenario_serialize::get_dist_type(std::string_view a) noexcept +inline constexpr cost_type scenario_serialize::get_cost_type(std::string_view a) noexcept { if (a == "8c-ncc") - return dist_type::N_8C_NCC; + return cost_type::G_8C_NCC; if (a == "8c-cc") - return dist_type::N_8C_CC; + return cost_type::G_8C_CC; if (a == "4c") - return dist_type::N_4C; + return cost_type::G_4C; if (a == "aa-ncc") - return dist_type::AA_NCC; + return cost_type::AA_NCC; if (a == "aa-cc") - return dist_type::AA_CC; - return dist_type::OTHER; + return cost_type::AA_CC; + return cost_type::OTHER; } } // namespace warthog::util diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index 294976a..73502d1 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -76,75 +76,42 @@ class scenario_manager experiment* get_experiment(uint32_t which) { - if(which >= experiments_.size()) + if(which >= experiments_.size()) { + WARTHOG_GWARN_FMT("scenario has max {} experiments, cannot retrive {}", experiments_.size(), which); return nullptr; - if (static_scenario_start_ >= 0) { - return experiments_[which]; - } - // handle dynamic scenario - if (experiment_at_ == which) { - return experiments_[which]; - } else if (experiment_at_ < (int32_t)which) { - return experiment_next((int32_t)which - experiment_at_).first; - } else { - throw std::logic_error("scenario_manager::get_experiment can only progress forwards in dynamic scenarios."); } + return experiments_[which]; } const experiment* get_experiment(uint32_t which) const { - if (static_scenario_start_ >= 0) { - if(which < experiments_.size()) { return experiments_[which]; } + if(which >= experiments_.size()) { + WARTHOG_GWARN_FMT("scenario has max {} experiments, cannot retrive {}", experiments_.size(), which); return nullptr; } - throw std::logic_error("scenario_manager::get_experiment can only be const for static scenarios."); + return experiments_[which]; } - /// @brief progress from current to count experiment away and return it - /// @param count - /// @return pair of the reached query and snapshot id. - /// - /// Will progress through commands until count queries are encounted, returning the final query. - /// When count == 1, is exactly the next query. - /// All patches required - std::pair - experiment_next(uint32_t count = 1); - - /// @brief reset scenario to first command - void - restart(); - /// @brief goto the start of the next snapshot (SNAPSHOT command) - /// @return snapshot id reached, or -1 if at end of commands (no more snapshots) - /// - /// If current command is SNAPSHOT, if id != current_snapshot() then already at next snapshot, - /// otherwise progress until next SNAPSHOT command is reached (or end of commands). - /// Clears all patches from get_patches() and replaces with any PATCH command to next snapshot. - int - snapshot_next(bool clear_patch = true); - /// @brief starting at SNAPSHOT or current PATCH, apply all patches until reaching SNAPSHOT or QUERY - /// @param clear_patch clears get_patches() - /// @return the number of patches, patch id are retrivable from get_patches() - /// - /// Requires to be on SNAPSHOT or PATCH, otherwise returns 0 and does nothing. - /// If @clear_patch is set, clears get_patches(). - /// Appends all processed PATCH commands to get_patches(). - int - snapshot_patches(bool clear_patch = true); - /// @brief returns the current query experiment if at query and progress to next command - /// @return the current query experiment if command is query, otherwise nullptr - /// - /// Requires to be on QUERY, otherwise return nullptr and do nothing. - /// If QUERY, returns corrisponding experiment and goto next command. - /// Does not affect get_patches(). - experiment* - snapshot_query(); + std::span + get_experiments() noexcept + { + return experiments_; + } + std::span + get_experiments() const noexcept + { + return experiments_; + } - bool complete() const noexcept { return command_at_ >= commands_.size(); } - - std::span - get_patches() const noexcept + std::span + get_commands() noexcept + { + return commands_; + } + std::span + get_commands() const noexcept { - return patches_; + return commands_; } void @@ -192,6 +159,7 @@ class scenario_manager write_scenario(std::ostream& out); bool is_static_scenario() const noexcept { return static_scenario_start_ >= 0; } + int32_t get_static_scenario_start() const noexcept { return static_scenario_start_; } protected: std::errc @@ -214,9 +182,6 @@ class scenario_manager uint32_t query_count_ = 0; uint32_t patch_count_ = 0; int32_t static_scenario_start_ = -1; ///< >=0: is static scenario where query commands start at pos, else is dynamic scenario - uint32_t command_at_ = 0; ///< command at, used for dynamic scenario - int32_t experiment_at_ = 0; - int32_t snapshot_at_ = -1; }; std::filesystem::path diff --git a/include/warthog/util/scenario_runner.h b/include/warthog/util/scenario_runner.h new file mode 100644 index 0000000..023fe11 --- /dev/null +++ b/include/warthog/util/scenario_runner.h @@ -0,0 +1,106 @@ +#ifndef WARTHOG_UTIL_SCENARIO_RUNNER_H +#define WARTHOG_UTIL_SCENARIO_RUNNER_H + +// scenario_runner.h +// +// Take a scenario_manager object and be able to progress through a dynamic scenario. +// +// @author: Ryan Hechenberger +// @created: 2026-04-09 +// + +#include "scenario_manager.h" + +namespace warthog::util +{ + +class scenario_runner +{ +public: + scenario_runner(); + scenario_runner(const scenario_manager* scen); + ~scenario_runner(); + + /// @brief reset scenario to first command + void + clear(); + + /// @brief reset scenario to first command + void + restart(); + + /// @brief progress from current to count experiment away and return it + /// @param count + /// @return pair of the reached query and snapshot id. + /// + /// Will progress through commands until count queries are encounted, returning the final query. + /// When count == 1, is exactly the next query. + /// All patches required + std::pair + experiment_next(uint32_t count = 1); + + /// @brief goto the start of the next snapshot (SNAPSHOT command) + /// @return snapshot id reached, or -1 if at end of commands (no more snapshots) + /// + /// If current command is SNAPSHOT, if id != current_snapshot() then already at next snapshot, + /// otherwise progress until next SNAPSHOT command is reached (or end of commands). + /// Clears all patches from get_patches() and replaces with any PATCH command to next snapshot. + int + snapshot_next(bool clear_patch = true); + + /// @brief starting at SNAPSHOT or current PATCH, apply all patches until reaching SNAPSHOT or QUERY + /// @param clear_patch clears get_patches() + /// @return the number of patches, patch id are retrivable from get_patches() + /// + /// Requires to be on SNAPSHOT or PATCH, otherwise returns 0 and does nothing. + /// If @clear_patch is set, clears get_patches(). + /// Appends all processed PATCH commands to get_patches(). + int + snapshot_patches(bool clear_patch = true); + + /// @brief returns the current query experiment if at query and progress to next command + /// @return the current query experiment if command is query, otherwise nullptr + /// + /// Requires to be on QUERY, otherwise return nullptr and do nothing. + /// If QUERY, returns corrisponding experiment and goto next command. + /// Does not affect get_patches(). + const experiment* + snapshot_query(); + + /// @brief progress from current to count experiment away and return it + /// @param clear_patch clears get_patches() + /// @return pair of the reached query and snapshot id. + /// + /// Will progress through commands until count queries are encounted, returning the final query. + /// When count == 1, is exactly the next query. + /// All patches required + std::span + snapshot_query_all(); + + bool complete() const noexcept { return command_at_ >= command_size_; } + + std::span + get_patches() const noexcept + { + return patches_; + } + + size_t + mem() const noexcept + { + return 0; + } + +protected: + const scenario_manager* scenario_ = nullptr; + std::vector patches_; + std::vector experiments_; + uint32_t command_at_ = 0; ///< command at, used for dynamic scenario + uint32_t command_size_ = 0; + int32_t experiment_at_ = 0; + int32_t snapshot_at_ = -1; +}; + +} // namespace warthog::util + +#endif // WARTHOG_UTIL_SCENARIO_RUNNER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7275d68..f78645c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,5 +28,6 @@ util/file_utils.cpp util/gm_parser.cpp util/helpers.cpp util/scenario_manager.cpp +util/scenario_runner.cpp util/timer.cpp ) diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index b969c45..a7ab7f1 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -15,7 +15,7 @@ scenario_serialize::scenario_serialize() : m_dyn_res(1024), m_string_res(256), m_line(static_cast(m_dyn_res.allocate(max_line_length + 2))), m_unget_line(&m_dyn_res), m_dist_strings(&m_dyn_res), - m_dist_type(&m_dyn_res), m_dist_value(&m_dyn_res) + m_cost_type(&m_dyn_res), m_dist_value(&m_dyn_res) { m_line[0] = '\0'; } @@ -57,7 +57,7 @@ scenario_serialize::close() m_line[0] = '\0'; m_unget_line.clear(); m_dist_strings.clear(); - m_dist_type.clear(); + m_cost_type.clear(); m_dist_value.clear(); m_scenario_stream = nullptr; } @@ -211,10 +211,10 @@ scenario_serialize::read_header_v1(std::istream* in) m_state = serialize_state::query; // setup distance types m_dist_strings.resize(1); - m_dist_type.resize(1); + m_cost_type.resize(1); m_dist_value.resize(1); - m_dist_strings[0] = get_dist_str(dist_type::N_8C_NCC); - m_dist_type[0] = dist_type::N_8C_NCC; + m_dist_strings[0] = get_dist_str(cost_type::G_8C_NCC); + m_cost_type[0] = cost_type::G_8C_NCC; m_dist_value[0] = -1; return std::errc{}; } @@ -372,7 +372,7 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) { return {invalid, std::errc::io_error}; } - for(uint32_t i = 0, ie = m_dist_type.size(); i < ie; ++i) + for(uint32_t i = 0, ie = m_cost_type.size(); i < ie; ++i) { if(!(m_iss >> d)) { return {invalid, std::errc::io_error}; } if(!std::isfinite(d)) diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index bd486cd..f351d28 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -20,120 +20,6 @@ void scenario_manager::clear() experiments_res_.release(); sfile_.clear(); mfile_.clear(); - restart(); -} - -std::pair scenario_manager::experiment_next(uint32_t count) -{ - patches_.clear(); // reset patches - if (count == 0) - return {nullptr, 0}; - uint32_t command_size = static_cast(commands_.size()); - int patch_count = 0; - while (command_at_ < command_size) { - auto cmd = commands_[command_at_]; - // command_at_ incremented in following fuction calls - switch (cmd.type) { - case scenario_command::SNAPSHOT: - case scenario_command::PATCH: - patch_count += snapshot_patches(); - break; - case scenario_command::QUERY: - if (experiment* query = snapshot_query(); query != nullptr) { - if (--count == 0) - return {query, patch_count}; - } - break; - default: - // should never be reached - ++command_at_; - WARTHOG_GDEBUG("scenario_manager::experiment_next invalid command type in " WARTHOG_FILENAME_LINE); - } - // exits loop - } - // no more experiments - return {nullptr, patch_count}; -} - -void -scenario_manager::restart() -{ - patches_.clear(); - version_ = io::scenario_version::UNKNOWN; - static_scenario_start_ = -1; - command_at_ = 0; - experiment_at_ = -1; - snapshot_at_ = -1; -} - -int -scenario_manager::snapshot_next(bool clear_patch) -{ - if (clear_patch) - patches_.clear(); - const uint32_t command_size = static_cast(commands_.size()); - while (command_at_ < command_size) { - auto cmd = commands_[command_at_]; - switch (cmd.type) { - case scenario_command::SNAPSHOT: - if (cmd.id != snapshot_at_) { - // at new snapshot, return - snapshot_at_ = cmd.id; - return snapshot_at_; - } - // current snapshot, goto next snapshot - [[fallthrough]]; - case scenario_command::QUERY: - ++snapshot_at_; - break; - case scenario_command::PATCH: - snapshot_patches(false); - // snapshot_patches increments snapshot_at_ - break; - default: - WARTHOG_GDEBUG("scenario_manager::snapshot_next invalid command type in " WARTHOG_FILENAME_LINE); - } - } - return false; -} - -int -scenario_manager::snapshot_patches(bool clear_patch) -{ - if (clear_patch) - patches_.clear(); - const uint32_t command_size = static_cast(commands_.size()); - int count = 0; - // if start of snapshot, apply that snapshot patches - if (command_at_ < command_size && commands_[command_at_].type == scenario_command::SNAPSHOT) - command_at_ += 1; - // while command_at_ is PATCH, add patch to applied list - while (command_at_ < command_size) { - if (auto cmd = commands_[command_at_]; cmd.bucket == scenario_command::PATCH) { - command_at_ += 1; - count += 1; - patches_.push_back(cmd.id); - } - } - // end at first non-PATCH command - return count; -} - -experiment* -scenario_manager::snapshot_query() -{ - if (command_at_ >= commands_.size()) - return nullptr; - auto cmd = commands_[command_at_]; - if (cmd.type != scenario_command::QUERY) - return nullptr; - command_at_ += 1; - experiment_at_ += 1; - if (cmd.cmd.query.experiment_id >= experiments_.size() || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) { - WARTHOG_GERROR_FMT("scenario_manager::snapshot_query invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, experiments_.size(), WARTHOG_FILENAME_LINE); - return nullptr; - } - return experiments_[cmd.cmd.query.experiment_id]; } void @@ -253,7 +139,7 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) sizeof(experiment), alignof(experiment))), (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), - Q.dist[(int)io::dist_type::N_8C_NCC], + Q.dist[(int)io::cost_type::G_8C_NCC], map_string); experiments_.push_back(ex); commands_.push_back(scenario_command::make_query(Q.bucket, query_count_++, (uint32_t)(experiments_.size()-1))); @@ -312,7 +198,7 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) sizeof(experiment), alignof(experiment))), (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), - Q.dist[(int)io::dist_type::N_8C_NCC], + Q.dist[(int)io::cost_type::G_8C_NCC], map_string); experiments_.push_back(ex); } diff --git a/src/util/scenario_runner.cpp b/src/util/scenario_runner.cpp new file mode 100644 index 0000000..c0f7764 --- /dev/null +++ b/src/util/scenario_runner.cpp @@ -0,0 +1,164 @@ +#include + +#include +#include +#include + +#include +#include + +namespace warthog::util +{ + +scenario_runner::scenario_runner() { } + +scenario_runner::~scenario_runner() = default; + +std::pair scenario_runner::experiment_next(uint32_t count) +{ + assert(scenario_ != nullptr); + auto commands = scenario_->get_commands(); + patches_.clear(); // reset patches + if (count == 0) + return {nullptr, 0}; + int patch_count = 0; + while (command_at_ < command_size_) { + auto cmd = commands[command_at_]; + // command_at_ incremented in following fuction calls + switch (cmd.type) { + case scenario_command::SNAPSHOT: + case scenario_command::PATCH: + patch_count += snapshot_patches(); + break; + case scenario_command::QUERY: + if (const experiment* query = snapshot_query(); query != nullptr) { + if (--count == 0) + return {query, patch_count}; + } + break; + default: + // should never be reached + ++command_at_; + WARTHOG_GDEBUG("scenario_runner::experiment_next invalid command type in " WARTHOG_FILENAME_LINE); + } + // exits loop + } + // no more experiments + return {nullptr, patch_count}; +} + +void +scenario_runner::clear() +{ + restart(); + scenario_ = nullptr; +} + +void +scenario_runner::restart() +{ + patches_.clear(); + experiments_.clear(); + command_at_ = 0; + experiment_at_ = -1; + snapshot_at_ = -1; +} + +int +scenario_runner::snapshot_next(bool clear_patch) +{ + assert(scenario_ != nullptr); + auto commands = scenario_->get_commands(); + if (clear_patch) + patches_.clear(); + while (command_at_ < commands.size()) { + auto cmd = commands[command_at_]; + switch (cmd.type) { + case scenario_command::SNAPSHOT: + if (cmd.id != snapshot_at_) { + // at new snapshot, return + snapshot_at_ = cmd.id; + return snapshot_at_; + } + // current snapshot, goto next snapshot + [[fallthrough]]; + case scenario_command::QUERY: + ++snapshot_at_; + break; + case scenario_command::PATCH: + snapshot_patches(false); + // snapshot_patches increments snapshot_at_ + break; + default: + WARTHOG_GDEBUG("scenario_runner::snapshot_next invalid command type in " WARTHOG_FILENAME_LINE); + } + } + return false; +} + +int +scenario_runner::snapshot_patches(bool clear_patch) +{ + assert(scenario_ != nullptr); + auto commands = scenario_->get_commands(); + if (clear_patch) + patches_.clear(); + int count = 0; + // if start of snapshot, apply that snapshot patches + if (command_at_ < commands.size() && commands[command_at_].type == scenario_command::SNAPSHOT) + command_at_ += 1; + // while command_at_ is PATCH, add patch to applied list + while (command_at_ < commands.size()) { + if (auto cmd = commands[command_at_]; cmd.bucket == scenario_command::PATCH) { + command_at_ += 1; + count += 1; + patches_.push_back(cmd.id); + } + } + // end at first non-PATCH command + return count; +} + +const experiment* +scenario_runner::snapshot_query() +{ + assert(scenario_ != nullptr); + auto commands = scenario_->get_commands(); + if (command_at_ >= commands.size()) + return nullptr; + auto cmd = commands[command_at_]; + if (cmd.type != scenario_command::QUERY) + return nullptr; + command_at_ += 1; + experiment_at_ += 1; + if (cmd.cmd.query.experiment_id >= scenario_->num_experiments() || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) { + WARTHOG_GERROR_FMT("scenario_runner::snapshot_query invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, scenario_->num_experiments(), WARTHOG_FILENAME_LINE); + return nullptr; + } + return experiments_[cmd.cmd.query.experiment_id]; +} + +std::span +scenario_runner::snapshot_query_all() +{ + assert(scenario_ != nullptr); + experiments_.clear(); + auto commands = scenario_->get_commands(); + auto exp = scenario_->get_experiments(); + while (command_at_ < commands.size()) + { + auto cmd = commands[command_at_]; + if (cmd.type != scenario_command::QUERY) + break; + if (cmd.cmd.query.experiment_id >= exp.size() || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) { + WARTHOG_GERROR_FMT("scenario_runner::snapshot_query_all invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, exp.size(), WARTHOG_FILENAME_LINE); + return {}; + } + experiments_.push_back(exp[cmd.cmd.query.experiment_id]); + command_at_ += 1; + experiment_at_ += 1; + } + return experiments_; +} + +} // namespace warthog::util From 6b1f63e74aef24e4623a824645e17155b44e9642 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 10 Apr 2026 10:14:33 +1000 Subject: [PATCH 43/61] move scenario related objects to new manager namespace --- include/warthog/{util => manager}/scenario_runner.h | 0 src/{util => manager}/scenario_runner.cpp | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename include/warthog/{util => manager}/scenario_runner.h (100%) rename src/{util => manager}/scenario_runner.cpp (100%) diff --git a/include/warthog/util/scenario_runner.h b/include/warthog/manager/scenario_runner.h similarity index 100% rename from include/warthog/util/scenario_runner.h rename to include/warthog/manager/scenario_runner.h diff --git a/src/util/scenario_runner.cpp b/src/manager/scenario_runner.cpp similarity index 100% rename from src/util/scenario_runner.cpp rename to src/manager/scenario_runner.cpp From 8d29459010030e56a6ec188d244cb6aa891c6701 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 10 Apr 2026 18:19:48 +1000 Subject: [PATCH 44/61] update grid seralizer to support dynamic patch set --- include/warthog/domain/gridmap.h | 16 ++- include/warthog/io/grid.h | 137 +++++++++++----------- include/warthog/io/scenario.h | 85 ++------------ include/warthog/io/serialize_base.h | 124 ++++++++++++++++++++ src/CMakeLists.txt | 2 +- src/domain/gridmap.cpp | 40 +++++-- src/io/grid.cpp | 170 +++++++++++++++++++++++----- src/io/scenario.cpp | 85 +------------- src/io/serialize_base.cpp | 130 +++++++++++++++++++++ 9 files changed, 527 insertions(+), 262 deletions(-) create mode 100644 include/warthog/io/serialize_base.h create mode 100644 src/io/serialize_base.cpp diff --git a/include/warthog/domain/gridmap.h b/include/warthog/domain/gridmap.h index efeea4b..f8721de 100644 --- a/include/warthog/domain/gridmap.h +++ b/include/warthog/domain/gridmap.h @@ -19,6 +19,7 @@ // #include "grid.h" +#include #include #include #include @@ -32,6 +33,8 @@ #include #include #include +#include +#include namespace warthog::domain { @@ -45,6 +48,10 @@ class gridmap : public memory::bittable using bittable = gridmap::bittable; // inform of type existing using bitarray = gridmap::bitarray; // inform of type existing gridmap(uint32_t height, uint32_t width); + gridmap(std::istream& input); + gridmap(io::bittable_serialize& parser); + gridmap(std::filesystem::path&& filename); + gridmap(const std::filesystem::path& filename); gridmap(const char* filename); gridmap(const gridmap&) = delete; ~gridmap(); @@ -327,7 +334,7 @@ class gridmap : public memory::bittable const char* filename() const noexcept { - return this->filename_; + return this->filename_.c_str(); } uint32_t @@ -348,12 +355,15 @@ class gridmap : public memory::bittable return sizeof(*this) + sizeof(warthog::dbword) * db_size_; } -private: +protected: using bittable::setup; + void setup_stream_(std::istream& in); + void setup_ser_(io::bittable_serialize& parser); +private: warthog::util::gm_header header_; warthog::dbword* db_; - char filename_[256]; + std::filesystem::path filename_; uint32_t dbwidth_; uint32_t dbwidth64_; diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index f80ef50..8e195ef 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -5,17 +5,20 @@ // // Read utility for gridmap. // -// Supported MovingAI map format. +//Supported MovingAI map format. Read format spec: https://movingai.com/benchmarks/formats.html // // @author: Ryan Hechenberger // @created: 2025-06-01 // -#include -#include +#include "serialize_base.h" #include #include +#include +#include +#include + namespace warthog::io { @@ -28,16 +31,35 @@ enum class bittable_type : uint8_t NONE, }; -enum class bittable_cell : uint8_t +enum class gridmap_cell : char { - BLOCKER, - TRAVERSABLE, - UNKNOWN + TERRAIN = '.', + TERRAIN_2 = 'G', + OUT_OF_BOUNDS = '@', + OUT_OF_BOUNDS_2 = 'O', + TREES = 'T', + SWAMP = 'S', + WATER = 'W', }; +constexpr inline bool gridmap_cell_traversable(gridmap_cell c) noexcept +{ + switch (c) { + case gridmap_cell::TERRAIN: + case gridmap_cell::TERRAIN_2: + return true; + default: + return false; + } +} +constexpr inline bool gridmap_cell_traversable(char c) noexcept +{ + return gridmap_cell_traversable(static_cast(c)); +} + inline constexpr uint32_t GRID_MAX_SIZE = 15'000; -class bittable_serialize +class bittable_serialize : public serialize_base { public: memory::bittable_dimension @@ -51,37 +73,6 @@ class bittable_serialize return m_type; } - static constexpr bool - is_traversable(char c) noexcept - { - switch(c) - { - case '.': - case 'G': - return true; - default: - return false; - } - } - static constexpr bittable_cell - cell_type(char c) noexcept - { - switch(c) - { - case '.': - case 'G': - return bittable_cell::TRAVERSABLE; - case '@': - case 'O': - case 'S': - case 'T': - case 'W': - return bittable_cell::BLOCKER; - default: - return bittable_cell::UNKNOWN; - } - } - void set_dim(uint32_t width, uint32_t height) { @@ -105,14 +96,21 @@ class bittable_serialize m_type = type; } - bool - read_header(std::istream& in); + std::errc + read_header(std::istream* in = nullptr); + + std::errc + read_grid_header(std::istream* in = nullptr); template - bool - read_map( - std::istream& in, BitTable& table, uint32_t offset_x = 0, - uint32_t offset_y = 0); + std::errc + read_grid_data( + BitTable& table, uint32_t offset_x = 0, + uint32_t offset_y = 0, std::istream* in = nullptr); + + std::errc + read_grid_raw( + std::vector& raw_data, std::istream* in = nullptr); protected: memory::bittable_dimension m_dim = {}; @@ -121,40 +119,53 @@ class bittable_serialize }; template -bool -bittable_serialize::read_map( - std::istream& in, BitTable& table, uint32_t offset_x, uint32_t offset_y) +std::errc +bittable_serialize::read_grid_data( + BitTable& table, uint32_t offset_x, uint32_t offset_y, std::istream* in) { + // check table const memory::bittable_dimension dim = table.dim(); const memory::bittable_dimension read_dim = m_dim; - table.fill(0); // set whole table to 0 (blocker) // detect for overflow if(offset_x >= dim.width || read_dim.width + offset_x > dim.width) - return false; + return std::errc::argument_out_of_domain; if(offset_y >= dim.height || read_dim.height + offset_y > dim.height) - return false; - assert(read_dim.width <= GRID_DIMENSION_MAX); - if(read_dim.width > GRID_DIMENSION_MAX) - return false; // shound never happen + return std::errc::argument_out_of_domain; uint32_t bit_id = static_cast(table.xy_to_id(offset_x, offset_y)); const uint32_t bit_row_offset = dim.width - read_dim.width; - char buffer[(GRID_DIMENSION_MAX + 16) & ~7ull]; - static_assert(sizeof(buffer) / sizeof(char) >= GRID_DIMENSION_MAX); + + std::errc err; + std::tie(in, err) = get_istream(in); + if(err != std::errc{}) + { + return err; + } + std::string_view line; for(uint32_t y = 0; y < read_dim.height; ++y, bit_id += bit_row_offset) { - in >> std::ws; - in.read(buffer, read_dim.width); + // read row + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } + auto& iss = line_stream(line); + if (!(iss >> m_token) || !line_stream_eof()) + { + return std::errc::io_error; + } + if (m_token.size() != read_dim.width) + return std::errc::argument_out_of_domain; + // copy row to table for(uint32_t x = 0; x < read_dim.width; ++x, ++bit_id) { - auto cell = cell_type(buffer[x]); - if(cell == bittable_cell::UNKNOWN) - return false; - else if(cell == bittable_cell::TRAVERSABLE) + if (gridmap_cell_traversable(m_token[x])) table.bit_or(static_cast(bit_id), 1); } } - return true; + + return std::errc{}; } } // namespace warthog::memory diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index a767162..b415b3d 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -19,13 +19,13 @@ // @created: 2025-12-04 // +#include "serialize_base.h" + #include #include #include #include #include -#include -#include #include #include #include @@ -79,7 +79,7 @@ struct scenario_patch uint16_t loc_y; }; -class scenario_serialize +class scenario_serialize : public serialize_base { public: enum class serialize_state : uint8_t @@ -100,7 +100,7 @@ class scenario_serialize }; static constexpr size_t max_line_length = 2000; scenario_serialize(); - ~scenario_serialize(); + ~scenario_serialize() override; static constexpr std::string_view get_dist_str(cost_type a) noexcept; static constexpr cost_type get_cost_type(std::string_view a) noexcept; @@ -113,12 +113,12 @@ class scenario_serialize void set_scenario_filename(std::filesystem::path&& filename) { - m_scenario_filename = std::move(filename); + set_filename(std::move(filename)); } const std::filesystem::path& get_scenario_filename() const noexcept { - return m_scenario_filename; + return get_filename(); } void @@ -171,11 +171,6 @@ class scenario_serialize return it != m_cost_type.end() ? static_cast(it - m_cost_type.begin()) : -1; } - int32_t - get_line_num() const noexcept - { - return m_line_num; - } uint32_t get_map_width() const noexcept { @@ -186,11 +181,6 @@ class scenario_serialize { return m_map_height; } - std::string_view - get_last_line() const noexcept - { - return m_line; - } void set_force_int(bool v) noexcept @@ -203,34 +193,8 @@ class scenario_serialize return m_force_int; } - /// @brief opens scenario file get_scenario_filename() for reading - /// @param scenario use a user provided instead of get_scenario_filename() - /// @return error on operation - std::errc - open_read(std::istream* scenario = nullptr); - /// @brief opens scenario file get_scenario_filename() for writing - /// @param scenario use a user provided instead of get_scenario_filename() - /// @return error on operation - std::errc - open_write(std::ostream* scenario = nullptr); - void - close(); - - /// @return if scenario is open for reading - bool - can_read(std::istream* in = nullptr) - { - if(in == nullptr) in = m_scenario_in; - return in != nullptr && !in->bad(); - } - /// @return if scenario is open for writing - bool - can_write(std::ostream* out = nullptr) - { - if(out == nullptr) out = m_scenario_out; - return out != nullptr && !out->bad(); - } + close() override; /// @brief reads in file version information, and sets version accessable /// via get_version() @@ -263,57 +227,22 @@ class scenario_serialize std::pair read_patch_line_v2(scenario_patch& query, std::istream* in = nullptr); -protected: - std::pair - get_istream(std::istream* in = nullptr) noexcept - { - if(in == nullptr) in = m_scenario_in; - if(in == nullptr || !in->good()) return {nullptr, std::errc::io_error}; - return {in, {}}; - } - std::pair - get_ostream(std::ostream* out = nullptr) noexcept - { - if(out == nullptr) out = m_scenario_out; - if(out == nullptr || !out->good()) - return {nullptr, std::errc::io_error}; - return {out, {}}; - } - std::istream& - line_stream(std::string_view line); - std::pair - readline(std::istream* in); - void - unreadline(std::string_view line); - protected: serialize_state m_state = serialize_state::init; scenario_version m_version = scenario_version::UNKNOWN; bool m_force_int = false; - std::filesystem::path m_scenario_filename; std::filesystem::path m_map_filename; - std::unique_ptr m_scenario_stream; - std::istream* m_scenario_in = nullptr; - std::ostream* m_scenario_out = nullptr; uint32_t m_map_width = 0; uint32_t m_map_height = 0; int32_t m_query_at = 0; - int32_t m_line_num = -1; // dynamic data std::pmr::monotonic_buffer_resource m_dyn_res; std::pmr::monotonic_buffer_resource m_string_res; - char* m_line = nullptr; ///< sets from m_dyn_res - std::pmr::string m_unget_line; // distance types std::pmr::vector m_dist_strings; ///< names read in std::pmr::vector m_cost_type; ///< cost_type of index (corrisponding dist_strings and dist_value) std::pmr::vector m_dist_value; ///< distance value stored, will be placed in dynamic_scenario - - // shared temp parameter - // TODO: replace with a custom string stream that does not allocate memory - std::istringstream m_iss; - std::string m_token; }; inline constexpr std::string_view scenario_serialize::get_dist_str(cost_type a) noexcept diff --git a/include/warthog/io/serialize_base.h b/include/warthog/io/serialize_base.h new file mode 100644 index 0000000..60147ef --- /dev/null +++ b/include/warthog/io/serialize_base.h @@ -0,0 +1,124 @@ +#ifndef WARTHOG_IO_SERIALIZE_BASE_H +#define WARTHOG_IO_SERIALIZE_BASE_H + +// io/serialize_base.h +// +// Read/write base class for serialize classes. +// Adds a uniform low-level interface for reading from a file, one-line at a time. +// Support for both file or just from a istream/ostream object. +// Tracking of line number for user-level error reporting and debugging. +// +// @author: Ryan Hechenberger +// @created: 2026-04-10 +// + +#include +#include +#include +#include +#include + +namespace warthog::io +{ + +class serialize_base +{ +public: + static constexpr size_t max_line_length = 16 << 10; // 16kB + serialize_base(); + virtual ~serialize_base(); + + void + set_filename(std::filesystem::path&& filename) + { + m_filename = std::move(filename); + } + const std::filesystem::path& + get_filename() const noexcept + { + return m_filename; + } + + int32_t + get_line_num() const noexcept + { + return m_line_num; + } + std::string_view + get_last_line() const noexcept + { + return m_line; + } + + /// @brief opens scenario file get_scenario_filename() for reading + /// @param scenario use a user provided instead of get_scenario_filename() + /// @return error on operation + virtual std::errc + open_read(std::istream* scenario = nullptr); + /// @brief opens scenario file get_scenario_filename() for writing + /// @param scenario use a user provided instead of get_scenario_filename() + /// @return error on operation + virtual std::errc + open_write(std::ostream* scenario = nullptr); + + virtual void + close(); + + /// @return if scenario is open for reading + bool + can_read(std::istream* in = nullptr) + { + if(in == nullptr) in = m_stream_in; + return in != nullptr && !in->bad(); + } + /// @return if scenario is open for writing + bool + can_write(std::ostream* out = nullptr) + { + if(out == nullptr) out = m_stream_out; + return out != nullptr && !out->bad(); + } + +protected: + std::pair + get_istream(std::istream* in = nullptr) noexcept + { + if(in == nullptr) in = m_stream_in; + if(in == nullptr || !in->good()) return {nullptr, std::errc::io_error}; + return {in, {}}; + } + std::pair + get_ostream(std::ostream* out = nullptr) noexcept + { + if(out == nullptr) out = m_stream_out; + if(out == nullptr || !out->good()) + return {nullptr, std::errc::io_error}; + return {out, {}}; + } + std::istream& + line_stream(std::string_view line); + bool + line_stream_eof(); + std::pair + readline(std::istream* in); + void + unreadline(std::string_view line); + +protected: + std::filesystem::path m_filename; + std::unique_ptr m_stream; + std::istream* m_stream_in = nullptr; + std::ostream* m_stream_out = nullptr; + int32_t m_line_num = -1; + std::unique_ptr m_line_data; + std::string_view m_line; + std::string m_unget_line; + + // shared temp parameter + std::istringstream m_iss; + std::string m_token; +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_SERIALIZE_BASE_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f78645c..fe46f48 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,7 @@ io/grid.cpp io/grid_trace.cpp io/log.cpp io/scenario.cpp +io/serialize_base.cpp memory/node_pool.cpp @@ -28,6 +29,5 @@ util/file_utils.cpp util/gm_parser.cpp util/helpers.cpp util/scenario_manager.cpp -util/scenario_runner.cpp util/timer.cpp ) diff --git a/src/domain/gridmap.cpp b/src/domain/gridmap.cpp index 9f8ed5d..707b027 100644 --- a/src/domain/gridmap.cpp +++ b/src/domain/gridmap.cpp @@ -15,21 +15,45 @@ gridmap::gridmap(unsigned int h, unsigned int w) : header_(h, w, "octile") this->init_db(); } -gridmap::gridmap(const char* filename) +gridmap::gridmap(std::istream& input) { - strcpy(filename_, filename); - io::bittable_serialize parser; + setup_stream_(input); +} + +gridmap::gridmap(std::filesystem::path&& filename) +{ + filename_ = std::move(filename); std::ifstream in(filename_); - if(!parser.read_header(in)) + setup_stream_(in); +} + +gridmap::gridmap(const std::filesystem::path& filename) : gridmap(std::filesystem::path(filename)) +{ } + +gridmap::gridmap(const char* filename) : gridmap(std::filesystem::path(filename)) +{ } + +void +gridmap::setup_stream_(std::istream& in) +{ + io::bittable_serialize parser; + parser.open_read(&in); + if(parser.read_header() != std::errc{}) + throw std::runtime_error("invalid grid format"); + setup_ser_(parser); +} + +void +gridmap::setup_ser_(io::bittable_serialize& parser) +{ + if (parser.read_grid_header() != std::errc{}) throw std::runtime_error("invalid grid format"); - if(parser.get_type() != io::bittable_type::OCTILE) - throw std::runtime_error("gridmap::gridmap must be OCTILE"); this->header_.type_ = "octile"; this->header_.width_ = parser.get_dim().width; this->header_.height_ = parser.get_dim().height; init_db(); - if(!parser.read_map(in, *this, 0, PADDED_ROWS)) + if(parser.read_grid_data(*this, 0, PADDED_ROWS) != std::errc{}) throw std::runtime_error("invalid grid format"); // calculate traversable num_traversable_ = static_cast(std::transform_reduce( @@ -44,7 +68,7 @@ gridmap::init_db() // zeroes. this eliminates the need for bounds checking when // fetching the neighbours of a node. uint32_t store_width, store_height; - store_height = this->header_.height_ + PADDED_ROWS + PADDED_ROWS; + store_height = this->header_.height_ + 2 * PADDED_ROWS; // calculate # of extra/redundant padding bits required, // per row, to align map width with dbword size diff --git a/src/io/grid.cpp b/src/io/grid.cpp index cbcbf88..6f9f4c2 100644 --- a/src/io/grid.cpp +++ b/src/io/grid.cpp @@ -6,44 +6,158 @@ namespace warthog::io { -bool -bittable_serialize::read_header(std::istream& in) +std::errc +bittable_serialize::read_header(std::istream* in) { - char buffer[32]; - auto read_key - = [&]() { return static_cast(in >> std::setw(32) >> buffer); }; - - if(!read_key()) return false; + std::errc err; + std::tie(in, err) = get_istream(in); + if(err != std::errc{}) + { + return err; + } + std::string_view line; + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } bittable_type detected_type = bittable_type::NONE; - if(std::strcmp(buffer, "type") == 0) { - if(!read_key()) return false; - if(std::strcmp(buffer, "octile") == 0) - detected_type = bittable_type::OCTILE; - else if(std::strcmp(buffer, "patch") == 0) - detected_type = bittable_type::PATCH; - else - detected_type = bittable_type::OTHER; - - if(!read_key()) return false; + auto& iss = line_stream(line); + if (!(iss >> m_token) || m_token != "type") { + return std::errc::io_error; + } + if (!(iss >> m_token) || !line_stream_eof()) { + return std::errc::io_error; + } } + if(m_token == "octile") + detected_type = bittable_type::OCTILE; + else if(m_token == "patch") + detected_type = bittable_type::PATCH; + else + detected_type = bittable_type::OTHER; + m_type = detected_type; + // read patch header before map header if(detected_type == bittable_type::PATCH) { - if(std::strcmp(buffer, "patches") != 0) return false; - in >> m_patch_count; - if(!read_key()) return false; + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } + auto& iss = line_stream(line); + if (!(iss >> m_token >> m_patch_count) || !line_stream_eof()) { + return std::errc::io_error; + } + if (m_token != "patches") { + return std::errc::argument_out_of_domain; + } } - if(std::strcmp(buffer, "height") != 0) return false; - if(!(in >> m_dim.height)) return false; - if(!read_key()) return false; - if(std::strcmp(buffer, "width") != 0) return false; - if(!(in >> m_dim.width)) return false; - m_type = detected_type; - if(!read_key() || std::strcmp(buffer, "map") != 0) return false; - return true; + return std::errc{}; +} + +std::errc +bittable_serialize::read_grid_header(std::istream* in) +{ + std::errc err; + std::tie(in, err) = get_istream(in); + if(err != std::errc{}) + { + return err; + } + std::string_view line; + + // read height + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } + { + auto& iss = line_stream(line); + if (!(iss >> m_token >> m_dim.height) || !line_stream_eof()) + { + return std::errc::io_error; + } + if (m_token != "height" || m_dim.height > GRID_DIMENSION_MAX) { + return std::errc::argument_out_of_domain; + } + } + // read width + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } + { + auto& iss = line_stream(line); + if (!(iss >> m_token >> m_dim.width) || !line_stream_eof()) + { + return std::errc::io_error; + } + if (m_token != "width" || m_dim.width > GRID_DIMENSION_MAX) { + return std::errc::argument_out_of_domain; + } + } + // read "map" + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } + { + auto& iss = line_stream(line); + if (!(iss >> m_token) || !line_stream_eof()) + { + return std::errc::io_error; + } + if (m_token != "map") { + return std::errc::argument_out_of_domain; + } + } + + return std::errc{}; +} + +std::errc +bittable_serialize::read_grid_raw( + std::vector& raw_data, std::istream* in) +{ + const memory::bittable_dimension read_dim = m_dim; + raw_data.resize(read_dim.width * read_dim.height); + char* data_at = raw_data.data(); + + std::errc err; + std::tie(in, err) = get_istream(in); + if(err != std::errc{}) + { + return err; + } + std::string_view line; + for(uint32_t y = 0; y < read_dim.height; ++y) + { + // read row + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } + auto& iss = line_stream(line); + if (!(iss >> m_token) || !line_stream_eof()) + { + return std::errc::io_error; + } + if (m_token.size() != read_dim.width) + return std::errc::argument_out_of_domain; + // copy row to table + std::memcpy(data_at, m_token.data(), read_dim.width); + data_at += read_dim.width; + } + return std::errc{}; } } // namespace warthog::io diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index a7ab7f1..d051117 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -12,107 +12,30 @@ namespace warthog::io { scenario_serialize::scenario_serialize() - : m_dyn_res(1024), m_string_res(256), - m_line(static_cast(m_dyn_res.allocate(max_line_length + 2))), - m_unget_line(&m_dyn_res), m_dist_strings(&m_dyn_res), + : m_dyn_res(1024), m_string_res(256), m_dist_strings(&m_dyn_res), m_cost_type(&m_dyn_res), m_dist_value(&m_dyn_res) -{ - m_line[0] = '\0'; -} +{ } scenario_serialize::~scenario_serialize() = default; -std::errc -scenario_serialize::open_read(std::istream* scenario) -{ - close(); - if(scenario != nullptr) { m_scenario_in = scenario; } - else - { - auto stream = std::make_unique(m_scenario_filename); - m_scenario_in = stream.get(); - m_scenario_stream = std::move(stream); - } - if(!*m_scenario_in) - { - // bad stream - m_scenario_in = nullptr; - m_scenario_stream = nullptr; - return std::errc::io_error; - } - return std::errc{}; -} - void scenario_serialize::close() { + serialize_base::close(); m_state = serialize_state::init; m_version = scenario_version::UNKNOWN; - m_scenario_in = nullptr; - m_scenario_out = nullptr; m_map_width = 0; m_map_height = 0; m_query_at = 0; - m_line_num = -1; - if (m_line != nullptr) - m_line[0] = '\0'; - m_unget_line.clear(); m_dist_strings.clear(); m_cost_type.clear(); m_dist_value.clear(); - m_scenario_stream = nullptr; -} - -std::pair -scenario_serialize::readline(std::istream* in) -{ - size_t len; - if(!m_unget_line.empty()) - { - // return last unreadline - len = m_unget_line.length(); - if(len > max_line_length - 1) - { - return {{}, std::errc::invalid_argument}; - } - std::memcpy(m_line, m_unget_line.c_str(), len + 1); - m_unget_line.clear(); - } - else - { - auto [s, err] = get_istream(in); - if(err != std::errc{}) { return {{}, err}; } - if(!s->getline(m_line, max_line_length)) - { - if(s->eof()) - return {{}, {}}; - else - return {{}, std::errc::io_error}; - } - len = strlen(m_line); - m_line_num += 1; - } - return {std::string_view(m_line, len), {}}; -} -void -scenario_serialize::unreadline(std::string_view line) -{ - m_unget_line = line; } void scenario_serialize::set_relative_map_filename( const std::filesystem::path& filename) { - m_map_filename = std::filesystem::proximate(filename, m_scenario_filename); -} - -std::istream& -scenario_serialize::line_stream(std::string_view line) -{ - m_iss.clear(); - m_iss.str(std::string(line)); - m_iss.seekg(0); - return m_iss; + m_map_filename = std::filesystem::proximate(filename, get_scenario_filename()); } std::errc diff --git a/src/io/serialize_base.cpp b/src/io/serialize_base.cpp new file mode 100644 index 0000000..56992a3 --- /dev/null +++ b/src/io/serialize_base.cpp @@ -0,0 +1,130 @@ +#include + +#include +#include +#include +#include + +namespace warthog::io +{ + +serialize_base::serialize_base() : m_line_data(std::make_unique(max_line_length)) +{ } +serialize_base::~serialize_base() = default; + +std::errc +serialize_base::open_read(std::istream* is) +{ + close(); + if(is != nullptr) { m_stream_in = is; } + else + { + auto stream = std::make_unique(get_filename()); + m_stream_in = stream.get(); + m_stream = std::move(stream); + } + if(!*m_stream_in) + { + // bad stream + m_stream_in = nullptr; + m_stream = nullptr; + return std::errc::io_error; + } + return std::errc{}; +} + +std::errc +serialize_base::open_write(std::ostream* os) +{ + close(); + if(os != nullptr) { m_stream_out = os; } + else + { + auto stream = std::make_unique(get_filename()); + m_stream_out = stream.get(); + m_stream = std::move(stream); + } + if(!*m_stream_out) + { + // bad stream + m_stream_out = nullptr; + m_stream = nullptr; + return std::errc::io_error; + } + return std::errc{}; +} + +void +serialize_base::close() +{ + m_line_num = -1; + m_line = std::string_view(); + m_unget_line.clear(); + m_stream = nullptr; + m_stream_in = nullptr; + m_stream_out = nullptr; +} + +std::pair +serialize_base::readline(std::istream* in) +{ + size_t len; + if(len = m_unget_line.size(); len != 0) + { + // return last unreadline + if(len > max_line_length - 1) + { + return {{}, std::errc::invalid_argument}; + } + if (len == 1 && m_unget_line[0] == '\0') { + // empty line + m_line = std::string_view(); + } else { + // copy line + std::memcpy(m_line_data.get(), m_unget_line.c_str(), len+1); + m_line = std::string_view(m_line_data.get(), len); + } + m_unget_line.clear(); + } + else + { + auto [s, err] = get_istream(in); + if(err != std::errc{}) { return {{}, err}; } + if(!s->getline(m_line_data.get(), max_line_length)) + { + return {{}, std::errc::io_error}; + } + // get length, if eof is set, is last line and no delimiter was extracted + len = static_cast(s->gcount()) - static_cast(s->eof()); + m_line_num += 1; + } + return {std::string_view(m_line_data.get(), len), {}}; +} +void +serialize_base::unreadline(std::string_view line) +{ + if (!line.empty()) [[likely]] { + m_unget_line = line; + } else { + m_unget_line = '\0'; + } +} + +std::istream& +serialize_base::line_stream(std::string_view line) +{ + m_iss.clear(); + m_iss.str(std::string(line)); + m_iss.seekg(0); + return m_iss; +} +bool +serialize_base::line_stream_eof() +{ + if (m_iss && !m_iss.eof()) { + m_iss >> std::ws; + } + return m_iss && m_iss.eof(); +} + +} // namespace warthog::io From 1e42ddd365f0407403c915ddd7a020fd8916ab29 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Tue, 14 Apr 2026 15:35:21 +1000 Subject: [PATCH 45/61] added string utility and parsers --- include/warthog/domain/grid_patches.h | 32 ++ include/warthog/io/scenario.h | 81 ++-- include/warthog/io/serialize_base.h | 18 +- include/warthog/io/string_parser.h | 21 + include/warthog/manager/experiment.h | 19 + include/warthog/manager/grid_patch_set.h | 30 ++ include/warthog/manager/scenario_manager.h | 20 + include/warthog/manager/scenario_runner.h | 8 +- include/warthog/util/scenario_manager.h | 18 +- include/warthog/util/string.h | 99 ++++ src/io/scenario.cpp | 506 +++++++++++++++++---- src/io/serialize_base.cpp | 90 ++-- src/manager/scenario_runner.cpp | 24 +- src/util/scenario_manager.cpp | 120 +++-- 14 files changed, 884 insertions(+), 202 deletions(-) create mode 100644 include/warthog/domain/grid_patches.h create mode 100644 include/warthog/io/string_parser.h create mode 100644 include/warthog/manager/experiment.h create mode 100644 include/warthog/manager/grid_patch_set.h create mode 100644 include/warthog/manager/scenario_manager.h create mode 100644 include/warthog/util/string.h diff --git a/include/warthog/domain/grid_patches.h b/include/warthog/domain/grid_patches.h new file mode 100644 index 0000000..4cf0910 --- /dev/null +++ b/include/warthog/domain/grid_patches.h @@ -0,0 +1,32 @@ +#ifndef WARTHOG_DOMAIN_GRIDMAP_H +#define WARTHOG_DOMAIN_GRIDMAP_H + +// Scenario v2 grid-based patch set. +// grid_patches primailry holds an array of bittable, handles reading from file +// and efficent memory storage. +// +// Look at utilities in manager/dynamic_gridmap.h for automatic translation to gridmap +// in a dynamic scenario. +// +// @author: Ryan Hechenberger +// @created: 2026-04-10 +// + +#include "grid.h" +#include +#include + +namespace warthog::domain +{ + +class grid_patches +{ + grid_patches(); + grid_patches(std::istream& input); + grid_patches(); + grid_patches(); +}; + +} // namespace warthog::domain + +#endif // WARTHOG_DOMAIN_GRIDMAP_H diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index b415b3d..cb3801d 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -84,21 +84,21 @@ class scenario_serialize : public serialize_base public: enum class serialize_state : uint8_t { - init, - version, - header, - query, - error, + INIT, + VERSION, + COMMAND, + END, + ERROR, }; - enum query_res : uint8_t + enum command_res : uint8_t { - valid, - invalid, - final, - next_query, - next_patch + INVALID, + VALID, + FINAL, + CMD_QUERY, + CMD_PATCH, + CMD_UNKNOWN, }; - static constexpr size_t max_line_length = 2000; scenario_serialize(); ~scenario_serialize() override; @@ -146,13 +146,11 @@ class scenario_serialize : public serialize_base return m_version; } - /// @brief the types of dist used, only relivent for version2, otherwise - /// octile_ncc(0) - /// @return bitset + /// @brief the types of dist used, only relivent for version2 std::span - get_dist_strings() const noexcept + get_cost_strings() const noexcept { - return m_dist_strings; + return m_cost_strings; } std::span get_cost_type() const noexcept @@ -160,16 +158,22 @@ class scenario_serialize : public serialize_base return m_cost_type; } std::span - get_dist_value() const noexcept + get_cost_value() const noexcept { - return m_dist_value; + return m_cost_value; } int - get_dist_index(cost_type d) const noexcept + find_cost_index(cost_type c) const noexcept { - auto it = std::find(m_cost_type.begin(), m_cost_type.end(), d); + auto it = std::find(m_cost_type.begin(), m_cost_type.end(), c); return it != m_cost_type.end() ? static_cast(it - m_cost_type.begin()) : -1; } + int + find_cost_index(std::string_view c) const noexcept + { + auto it = std::find(m_cost_strings.begin(), m_cost_strings.end(), c); + return it != m_cost_strings.end() ? static_cast(it - m_cost_strings.begin()) : -1; + } uint32_t get_map_width() const noexcept @@ -196,11 +200,14 @@ class scenario_serialize : public serialize_base void close() override; + virtual int + last_command_type() const; + /// @brief reads in file version information, and sets version accessable /// via get_version() /// @param in optional stream to use, otherwise uses internal-set stream /// @return success std::errc{} (0) - std::errc + virtual std::errc read_version(std::istream* in = nullptr); /// @brief with header (without version) information. /// @param in optional stream to use, otherwise uses internal-set stream @@ -209,7 +216,7 @@ class scenario_serialize : public serialize_base /// With VERSION1: peeks first query to gain map name /// With version2: gets map width/height, available costs and patch /// filename - std::errc + virtual std::errc read_header(std::istream* in = nullptr); std::errc @@ -217,18 +224,30 @@ class scenario_serialize : public serialize_base std::errc read_header_v2(std::istream* in = nullptr); - std::pair + std::pair + next_command_type(std::istream* in = nullptr); + std::errc + skip_commands(int count = 1, std::istream* in = nullptr); + + virtual std::pair read_query_line(scenario_query& query, std::istream* in = nullptr); - std::pair + std::pair read_query_line_v1(scenario_query& query, std::istream* in = nullptr); - std::pair + std::pair read_query_line_v2(scenario_query& query, std::istream* in = nullptr); - std::pair + + virtual std::pair + read_patch_line(scenario_patch& query, std::istream* in = nullptr); + + std::pair read_patch_line_v2(scenario_patch& query, std::istream* in = nullptr); + std::string_view + copy_string(std::string_view str); + protected: - serialize_state m_state = serialize_state::init; + serialize_state m_state = serialize_state::INIT; scenario_version m_version = scenario_version::UNKNOWN; bool m_force_int = false; std::filesystem::path m_map_filename; @@ -240,9 +259,9 @@ class scenario_serialize : public serialize_base std::pmr::monotonic_buffer_resource m_dyn_res; std::pmr::monotonic_buffer_resource m_string_res; // distance types - std::pmr::vector m_dist_strings; ///< names read in + std::pmr::vector m_cost_strings; ///< names read in std::pmr::vector m_cost_type; ///< cost_type of index (corrisponding dist_strings and dist_value) - std::pmr::vector m_dist_value; ///< distance value stored, will be placed in dynamic_scenario + std::pmr::vector m_cost_value; ///< distance value stored, will be placed in dynamic_scenario }; inline constexpr std::string_view scenario_serialize::get_dist_str(cost_type a) noexcept @@ -277,6 +296,6 @@ inline constexpr cost_type scenario_serialize::get_cost_type(std::string_view a) return cost_type::OTHER; } -} // namespace warthog::util +} // namespace warthog::io #endif // WARTHOG_IO_SCENARIO_H diff --git a/include/warthog/io/serialize_base.h b/include/warthog/io/serialize_base.h index 60147ef..95284a1 100644 --- a/include/warthog/io/serialize_base.h +++ b/include/warthog/io/serialize_base.h @@ -79,6 +79,12 @@ class serialize_base return out != nullptr && !out->bad(); } + std::string_view + get_last_token() const noexcept + { + return m_token; + } + protected: std::pair get_istream(std::istream* in = nullptr) noexcept @@ -95,21 +101,23 @@ class serialize_base return {nullptr, std::errc::io_error}; return {out, {}}; } + bool istream_eof(std::istream* in = nullptr); + std::pair + readline(std::istream* in, bool skip_blanks = false); + void + unreadline(std::string_view line); + bool is_line_blank(std::string_view line); std::istream& line_stream(std::string_view line); bool line_stream_eof(); - std::pair - readline(std::istream* in); - void - unreadline(std::string_view line); protected: std::filesystem::path m_filename; std::unique_ptr m_stream; std::istream* m_stream_in = nullptr; std::ostream* m_stream_out = nullptr; - int32_t m_line_num = -1; + int32_t m_line_num = 0; std::unique_ptr m_line_data; std::string_view m_line; std::string m_unget_line; diff --git a/include/warthog/io/string_parser.h b/include/warthog/io/string_parser.h new file mode 100644 index 0000000..72d2b23 --- /dev/null +++ b/include/warthog/io/string_parser.h @@ -0,0 +1,21 @@ +#ifndef WARTHOG_IO_STRING_PARSER_H +#define WARTHOG_IO_STRING_PARSER_H + +// io/string_parser.h +// +// +// @author: Ryan Hechenberger +// @created: 2026-04-14 +// + +#include +#include + +namespace warthog::io +{ + + + +} // namespace warthog::io + +#endif // WARTHOG_IO_STRING_PARSER_H diff --git a/include/warthog/manager/experiment.h b/include/warthog/manager/experiment.h new file mode 100644 index 0000000..699504b --- /dev/null +++ b/include/warthog/manager/experiment.h @@ -0,0 +1,19 @@ +#ifndef WARTHOG_MANAGER_EXPERIMENT_H +#define WARTHOG_MANAGER_EXPERIMENT_H + +// scenario_runner.h +// +// Alias for util::experiemnt. +// +// @author: Ryan Hechenberger +// @created: 2026-04-10 +// + +#include + +namespace warthog::manager +{ +using warthog::util::experiment; +} // namespace warthog::manager + +#endif // WARTHOG_MANAGER_SCENARIO_MANAGER_H diff --git a/include/warthog/manager/grid_patch_set.h b/include/warthog/manager/grid_patch_set.h new file mode 100644 index 0000000..ee81d88 --- /dev/null +++ b/include/warthog/manager/grid_patch_set.h @@ -0,0 +1,30 @@ +#ifndef WARTHOG_MANAGER_GRID_PATCH_SET_H +#define WARTHOG_MANAGER_GRID_PATCH_SET_H + +// scenario_runner.h +// +// Take a scenario_manager object and be able to progress through a dynamic scenario. +// +// @author: Ryan Hechenberger +// @created: 2026-04-10 +// + +#include "experiment.h" +#include +#include +#include + +namespace warthog::manager +{ + +/// @brief a class for managing a set of patches +/// +/// Stores a list of patches at +class grid_patch_set +{ + +}; + +} // namespace warthog::manager + +#endif // WARTHOG_MANAGER_PATCH_SET_H diff --git a/include/warthog/manager/scenario_manager.h b/include/warthog/manager/scenario_manager.h new file mode 100644 index 0000000..c805131 --- /dev/null +++ b/include/warthog/manager/scenario_manager.h @@ -0,0 +1,20 @@ +#ifndef WARTHOG_MANAGER_SCENARIO_MANAGER_H +#define WARTHOG_MANAGER_SCENARIO_MANAGER_H + +// scenario_runner.h +// +// Alias for util::scenario_manager. +// +// @author: Ryan Hechenberger +// @created: 2026-04-10 +// + +#include +#include "experiment.h" + +namespace warthog::manager +{ +using warthog::util::scenario_manager; +} // namespace warthog::manager + +#endif // WARTHOG_MANAGER_SCENARIO_MANAGER_H diff --git a/include/warthog/manager/scenario_runner.h b/include/warthog/manager/scenario_runner.h index 023fe11..d284ebe 100644 --- a/include/warthog/manager/scenario_runner.h +++ b/include/warthog/manager/scenario_runner.h @@ -1,5 +1,5 @@ -#ifndef WARTHOG_UTIL_SCENARIO_RUNNER_H -#define WARTHOG_UTIL_SCENARIO_RUNNER_H +#ifndef WARTHOG_MANAGER_SCENARIO_RUNNER_H +#define WARTHOG_MANAGER_SCENARIO_RUNNER_H // scenario_runner.h // @@ -11,7 +11,7 @@ #include "scenario_manager.h" -namespace warthog::util +namespace warthog::manager { class scenario_runner @@ -103,4 +103,4 @@ class scenario_runner } // namespace warthog::util -#endif // WARTHOG_UTIL_SCENARIO_RUNNER_H +#endif // WARTHOG_MANAGER_SCENARIO_RUNNER_H diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index 73502d1..e7cc77d 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -92,7 +92,7 @@ class scenario_manager return experiments_[which]; } - std::span + std::span get_experiments() noexcept { return experiments_; @@ -158,6 +158,19 @@ class scenario_manager void write_scenario(std::ostream& out); + std::string_view get_cost_type() const noexcept + { + return cost_type_; + } + void set_cost_type(std::string_view v) noexcept + { + cost_type_ = v; + } + void set_cost_type(io::cost_type c) noexcept + { + cost_type_ = io::scenario_serialize::get_dist_str(c); + } + bool is_static_scenario() const noexcept { return static_scenario_start_ >= 0; } int32_t get_static_scenario_start() const noexcept { return static_scenario_start_; } @@ -178,7 +191,10 @@ class scenario_manager std::vector patches_; std::filesystem::path sfile_; std::filesystem::path mfile_; + std::string cost_type_; io::scenario_version version_ = io::scenario_version::UNKNOWN; + uint32_t scenario_width_ = 0; + uint32_t scenario_height_ = 0; uint32_t query_count_ = 0; uint32_t patch_count_ = 0; int32_t static_scenario_start_ = -1; ///< >=0: is static scenario where query commands start at pos, else is dynamic scenario diff --git a/include/warthog/util/string.h b/include/warthog/util/string.h new file mode 100644 index 0000000..2653bfa --- /dev/null +++ b/include/warthog/util/string.h @@ -0,0 +1,99 @@ +#ifndef WARTHOG_UTIL_STRING_H +#define WARTHOG_UTIL_STRING_H + +// template.h +// +// Utility string functions. +// Includes handling of space and conversion of string to other values, +// mainly int/double. +// +// @author: Ryan Hechenberger +// @created: 2026-04-14 +// + +#include +#include +#include +#include +#include + +namespace warthog::util +{ + +/// @brief remove white spaces (as std::isspace) from beginning of string_view +std::string_view ltrim(std::string_view str) +{ + str.remove_prefix(std::find_if(str.begin(), str.end(), [](char c) { return !isspace((unsigned char)c); }) - str.begin()); +} + +/// @brief remove white spaces (as std::isspace) from end of string_view +std::string_view rtrim(std::string_view str) +{ + str.remove_suffix(std::find_if(str.rbegin(), str.rend(), [](char c) { return !isspace((unsigned char)c); }) - str.rbegin()); +} + +/// @brief string_view is either empty or only contains spaces (as std::isspace) +bool is_blank(std::string_view str) +{ + return std::all_of(str.begin(), str.end(), [](char c) { return isspace((unsigned char)c); }); +} + +struct token_return +{ + std::string_view token; ///< token extracted + size_t trim; ///< amount of space discarded from front of string +}; + +token_return get_token(std::string_view str) +{ + auto fstr = ltrim(str); + if (fstr.empty()) { + return {std::string_view(), str.size()}; + } + auto endsp = std::find_if(str.begin(), str.end(), [](char c) { return isspace((unsigned char)c); }) - str.begin(); + token_return ret; + ret.token = fstr.substr(0, endsp); + ret.trim = str.size() - fstr.size(); + return ret; +} + +token_return get_token_quoted(std::string_view str, char quote = '"', char escape = '\\') +{ + if (quote == escape) { + return {std::string_view(), str.size()}; + } + auto fstr = ltrim(str); + if (fstr.empty()) { + return {std::string_view(), str.size()}; + } + token_return ret; + ret.trim = str.size() - fstr.size(); + if (fstr[0] == quote) { + // quoted + size_t len = 0; + while(true){ + len = fstr.find(quote, len+1); + if (len == std::string_view::npos) { + // no quote close + return {std::string_view(), ret.trim}; + } + auto epos = fstr.find_last_not_of(escape, len-1) - len; + if ((epos & 1) != 0) { + // not delimited, return + ret.token = fstr.substr(0, len+1); + break; + } + // delimited, continue + } + } else { + // no delimiter, read as normal + auto endsp = std::find_if(str.begin(), str.end(), [](char c) { return isspace((unsigned char)c); }) - str.begin(); + ret.token = fstr.substr(0, endsp); + } + return ret; +} + + +} // namespace warthog::util + +#endif // WARTHOG_UTIL_STRING_H diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index d051117..2b141f1 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -12,8 +12,8 @@ namespace warthog::io { scenario_serialize::scenario_serialize() - : m_dyn_res(1024), m_string_res(256), m_dist_strings(&m_dyn_res), - m_cost_type(&m_dyn_res), m_dist_value(&m_dyn_res) + : m_dyn_res(1024), m_string_res(256), m_cost_strings(&m_dyn_res), + m_cost_type(&m_dyn_res), m_cost_value(&m_dyn_res) { } scenario_serialize::~scenario_serialize() = default; @@ -21,14 +21,14 @@ void scenario_serialize::close() { serialize_base::close(); - m_state = serialize_state::init; + m_state = serialize_state::INIT; m_version = scenario_version::UNKNOWN; m_map_width = 0; m_map_height = 0; m_query_at = 0; - m_dist_strings.clear(); + m_cost_strings.clear(); m_cost_type.clear(); - m_dist_value.clear(); + m_cost_value.clear(); } void @@ -38,10 +38,20 @@ scenario_serialize::set_relative_map_filename( m_map_filename = std::filesystem::proximate(filename, get_scenario_filename()); } +int +scenario_serialize::last_command_type() const +{ + if (m_token == "Q") + return CMD_QUERY; + if (m_token == "P") + return CMD_PATCH; + return CMD_UNKNOWN; +} + std::errc scenario_serialize::read_version(std::istream* in) { - if(m_state != serialize_state::init) + if(m_state != serialize_state::INIT) { return std::errc::state_not_recoverable; } @@ -49,38 +59,38 @@ scenario_serialize::read_version(std::istream* in) std::tie(in, err) = get_istream(in); if(err != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return err; } std::string_view line; std::tie(line, err) = readline(in); if(err != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return err; } auto& iss = line_stream(line); int version; if(!(iss >> m_token >> version)) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::io_error; } if(version < 1 || version > 2) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::invalid_argument; } m_version = version == 1 ? scenario_version::VERSION_1 : scenario_version::VERSION_2; - m_state = serialize_state::header; + m_state = serialize_state::VERSION; return std::errc{}; } std::errc scenario_serialize::read_header(std::istream* in) { - if(!can_read(in) || m_state != serialize_state::header) + if(!can_read(in) || m_state != serialize_state::VERSION) { return std::errc::state_not_recoverable; } @@ -88,9 +98,9 @@ scenario_serialize::read_header(std::istream* in) switch(m_version) { case scenario_version::VERSION_1: - return read_header_v1(get_istream().first); + return read_header_v1(in); case scenario_version::VERSION_2: - return read_header_v2(get_istream().first); + return read_header_v2(in); default: return std::errc::state_not_recoverable; } @@ -106,24 +116,31 @@ scenario_serialize::read_header_v1(std::istream* in) std::tie(in, ec) = get_istream(in); if(ec != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return ec; } } + // setup distance types + m_cost_strings.resize(1); + m_cost_type.resize(1); + m_cost_value.resize(1); + m_cost_strings[0] = get_dist_str(cost_type::G_8C_NCC); + m_cost_type[0] = cost_type::G_8C_NCC; + m_cost_value[0] = -1; + // read first query line to get map scenario_query Q; std::tie(s, ec) = read_query_line_v1(Q); if(ec != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return ec; } - if(s != final) - { + if(s != FINAL){ if(Q.width <= 0 || Q.width > GRID_MAX_SIZE || Q.height <= 0 || Q.height > GRID_MAX_SIZE) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::invalid_argument; } set_relative_map_filename(Q.map); @@ -131,14 +148,7 @@ scenario_serialize::read_header_v1(std::istream* in) m_map_height = Q.height; } unreadline(m_line); - m_state = serialize_state::query; - // setup distance types - m_dist_strings.resize(1); - m_cost_type.resize(1); - m_dist_value.resize(1); - m_dist_strings[0] = get_dist_str(cost_type::G_8C_NCC); - m_cost_type[0] = cost_type::G_8C_NCC; - m_dist_value[0] = -1; + m_state = serialize_state::COMMAND; return std::errc{}; } @@ -149,111 +159,328 @@ scenario_serialize::read_header_v2(std::istream* in) std::tie(in, err) = get_istream(in); if(err != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return err; } // read first query line to get map std::string_view line; + // height std::tie(line, err) = readline(in); if(err != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return err; } { auto& iss = line_stream(line); - if(!(iss >> m_token >> m_map_height)) + if(!(iss >> m_token >> m_map_height) || !line_stream_eof()) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::io_error; } } if(m_token != "height" || m_map_height < 1 || m_map_height > GRID_MAX_SIZE) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::invalid_argument; } + // width std::tie(line, err) = readline(in); if(err != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return err; } { auto& iss = line_stream(line); - if(!(iss >> m_token >> m_map_width)) + if(!(iss >> m_token >> m_map_width) || !line_stream_eof()) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::io_error; } } + if(m_token != "width" || m_map_width < 1 || m_map_width > GRID_MAX_SIZE) + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; + } + // cost std::tie(line, err) = readline(in); if(err != std::errc{}) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return err; } { auto& iss = line_stream(line); int cost_count; - if(!(iss >> m_token >> cost_count)) + if(!(iss >> m_token >> cost_count) || !line_stream_eof()) { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::io_error; } - if (cost_count < 0 || cost_count > 128) + if (m_token != "cost" || cost_count < 0 || cost_count > 128) { // limit cost_count to be within reason + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; + } + m_cost_strings.resize(cost_count); + m_cost_type.resize(cost_count); + m_cost_value.resize(cost_count); + for (int i = 0; i < cost_count; ++i) { + if (!(iss >> m_token)) { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; + } + std::string_view ctype = copy_string(m_token); + m_cost_strings[i] = copy_string(m_token); + m_cost_type[i] = get_cost_type(ctype); + m_cost_value[i] = -1; + } + // end of line + if (!line_stream_eof()) { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; } } - if(m_token != "width" || m_map_width < 1 || m_map_width > GRID_MAX_SIZE) + + // get map (patch) filename + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + m_state = serialize_state::ERROR; + return err; + } + { + auto& iss = line_stream(line); + if(!(iss >> m_token >> m_map_filename) || !line_stream_eof()) + { + m_state = serialize_state::ERROR; + return std::errc::io_error; + } + } + if(m_token != "patch") { - m_state = serialize_state::error; + m_state = serialize_state::ERROR; return std::errc::invalid_argument; } + + // read in "commands" + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + m_state = serialize_state::ERROR; + return err; + } + { + auto& iss = line_stream(line); + if(!(iss >> m_token) || !line_stream_eof()) + { + m_state = serialize_state::ERROR; + return std::errc::io_error; + } + } + if(m_token != "patch") + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; + } + // set_relative_map_filename(m_query.map); - m_state = serialize_state::query; + m_state = serialize_state::COMMAND; return std::errc{}; } +std::pair +scenario_serialize::next_command_type(std::istream* in) +{ + if(!can_read(in) || m_state != serialize_state::COMMAND) + { + return {INVALID, std::errc::state_not_recoverable}; + } + std::errc err; + std::tie(in, err) = get_istream(in); + if(err != std::errc{}) + { + m_state = serialize_state::ERROR; + return {INVALID, err}; + } + + switch(m_version) + { + case scenario_version::VERSION_1: { + auto [line,ec] = readline(in, true); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + if(istream_eof(in)) { + m_state = serialize_state::END; + return {FINAL, {}}; + } + unreadline(line); + return {CMD_QUERY, {}}; + } + case scenario_version::VERSION_2: { + auto [line,ec] = readline(in, true); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + if(istream_eof(in)) { + m_state = serialize_state::END; + return {FINAL, {}}; + } + auto& iss = line_stream(line); + if (!(iss >> m_token)) { + return {INVALID, std::errc::io_error}; + } + auto cmd_type = last_command_type(); + unreadline(line); + return {cmd_type, {}}; + } + default: + return {INVALID, std::errc::state_not_recoverable}; + } +} + +std::pair +scenario_serialize::read_query_line(scenario_query& query, std::istream* in) +{ + if (m_state == serialize_state::END) + { + return {FINAL, {}}; + } + if(!can_read(in) || m_state != serialize_state::COMMAND) + { + return {0, std::errc::state_not_recoverable}; + } + + switch (m_version) { + case scenario_version::VERSION_1: + return {CMD_QUERY, {}}; + case scenario_version::VERSION_2: { + std::errc ec; + std::string_view line; + std::tie(in, ec) = get_istream(in); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + std::tie(line, ec) = readline(in, true); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + if(istream_eof(in)) { + m_state = serialize_state::END; + return {FINAL, {}}; + } + + auto& iss = line_stream(line); + if (!(iss >> m_token)) { + return {INVALID, std::errc::io_error}; + } + auto cmd_type = last_command_type(); + unreadline(line); + return {cmd_type, {}}; + } + default: + return {INVALID, std::errc::state_not_recoverable}; + } +} + +std::errc +scenario_serialize::skip_commands(int count, std::istream* in) +{ + if (m_state == serialize_state::END) + { + return {}; + } + if(!can_read(in) || m_state != serialize_state::COMMAND) + { + return std::errc::state_not_recoverable; + } + + std::errc ec; + std::string_view line; + std::tie(in, ec) = get_istream(in); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return ec; + } + while (count > 0) { + --count; + std::tie(line, ec) = readline(in, true); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return ec; + } + if(istream_eof(in)) { + m_state = serialize_state::END; + break; + } + } + return {}; +} + auto scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) - -> std::pair + -> std::pair { assert(can_read(in)); // move to start of read std::errc ec; std::string_view line; std::tie(in, ec) = get_istream(in); - if(ec != std::errc{}) { return {invalid, std::errc::io_error}; } - std::tie(line, ec) = readline(in); - if(ec != std::errc{}) { return {invalid, std::errc::io_error}; } - if(in->eof()) { return {final, std::errc{}}; } - assert(!std::all_of(line.begin(), line.end(), [](char a) { - return std::isspace((unsigned char)a); - })); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + std::tie(line, ec) = readline(in, true); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + if(istream_eof(in)) { + m_state = serialize_state::END; + return {FINAL, std::errc{}}; + } auto& iss = line_stream(line); if(!(iss >> query.bucket >> m_token >> query.width >> query.height >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y - >> m_dist_value.at(0))) + >> m_cost_value.at(0))) { - return {invalid, std::errc::io_error}; + std::cout << std::quoted(line) << std::endl; + // state not set to error, up to user + return {INVALID, std::errc::io_error}; } + std::cout << std::quoted(line) << std::endl; + std::cout << iss.tellg() << std::endl; + std::cout << iss.good() << iss.eof() << std::endl; + iss.clear(); + iss >> std::ws; + std::cout << iss.good() << iss.eof() << std::endl; + std::string ttt; + iss >> ttt; + std::cout << std::quoted(ttt) << std::endl; + std::cout << iss.good() << iss.eof() << std::endl; query.map = m_token; - query.dist = m_dist_value; + query.dist = m_cost_value; if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) || !std::isfinite(query.dist[0])) { - return {invalid, std::errc::invalid_argument}; + return {INVALID, std::errc::invalid_argument}; } // perform checking of values, but do not return as error if fail if(query.width != m_map_width || query.height != m_map_height) { - return {invalid, std::errc{}}; + return {INVALID, std::errc{}}; } if(m_force_int) { @@ -263,12 +490,12 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) || rint(query.goal_x) != query.goal_x || rint(query.goal_y) != query.goal_y) { - return {invalid, std::errc{}}; + return {INVALID, std::errc{}}; } if(query.start_x < 0 || query.start_y >= m_map_width || query.goal_x < 0 || query.goal_y >= m_map_height) { - return {invalid, std::errc{}}; + return {INVALID, std::errc{}}; } } else @@ -276,59 +503,168 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) if(query.start_x < 0 || query.start_y > m_map_width || query.goal_x < 0 || query.goal_y > m_map_height) { - return {invalid, std::errc{}}; + return {INVALID, std::errc{}}; } } - return {valid, std::errc{}}; + return {VALID, std::errc{}}; } auto scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) - -> std::pair + -> std::pair { assert(can_read(in)); - int width, height; - double d; - std::fill(m_dist_value.begin(), m_dist_value.end(), -1.0); - if(!(m_iss >> query.bucket >> query.start_x >> query.start_y - >> query.goal_x >> query.goal_y)) + // move to start of read + std::errc ec; + std::string_view line; + std::tie(in, ec) = get_istream(in); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + std::tie(line, ec) = readline(in, true); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + if(istream_eof(in)) { + m_state = serialize_state::END; + return {FINAL, std::errc{}}; + } + + auto& iss = line_stream(line); + if (!(iss >> m_token)) { + return {INVALID, std::errc::io_error}; + } + auto cmd_type = last_command_type(); + if (cmd_type != CMD_QUERY) { + unreadline(line); + return {cmd_type, std::errc{}}; + } + if(!(iss >> query.bucket >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y)) { - return {invalid, std::errc::io_error}; + return {INVALID, std::errc::io_error}; } - for(uint32_t i = 0, ie = m_cost_type.size(); i < ie; ++i) + if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) + || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y)) { - if(!(m_iss >> d)) { return {invalid, std::errc::io_error}; } - if(!std::isfinite(d)) + return {INVALID, std::errc::invalid_argument}; + } + query.width = query.height = 0; + for (auto& cost : m_cost_value) { + if (!(iss >> cost)) { + return {INVALID, std::errc::io_error}; + } + if (!std::isfinite(cost)) { - return {invalid, std::errc::invalid_argument}; + return {INVALID, std::errc::invalid_argument}; } - if(i >= 0) { query.dist[i] = d; } } - m_iss >> std::ws; - if(m_iss.eof()) { return {invalid, std::errc::invalid_argument}; } - return {valid, std::errc{}}; + query.dist = m_cost_value; + if (!line_stream_eof()) { + // unexpected number of paramters + return {INVALID, std::errc::io_error}; + } + if(m_force_int) + { + // check integer bounds + if(rint(query.start_x) != query.start_x + || rint(query.start_y) != query.start_y + || rint(query.goal_x) != query.goal_x + || rint(query.goal_y) != query.goal_y) + { + return {INVALID, std::errc{}}; + } + if(query.start_x < 0 || query.start_y >= m_map_width + || query.goal_x < 0 || query.goal_y >= m_map_height) + { + return {INVALID, std::errc{}}; + } + } + else + { + if(query.start_x < 0 || query.start_y > m_map_width || query.goal_x < 0 + || query.goal_y > m_map_height) + { + return {INVALID, std::errc{}}; + } + } + return {VALID, std::errc{}}; +} + +std::pair +scenario_serialize::read_patch_line(scenario_patch& patch, std::istream* in) +{ + if(!can_read(in) || m_state != serialize_state::VERSION) + { + return {0, std::errc::state_not_recoverable}; + } + + switch(m_version) + { + case scenario_version::VERSION_2: + return read_patch_line_v2(patch, in); + default: + return {0, std::errc::state_not_recoverable}; + } } auto scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) - -> std::pair + -> std::pair { - assert(can_read()); - int width, height; - double d; - if(!(m_iss >> patch.bucket >> patch.patch_id >> patch.loc_x - >> patch.loc_y)) + assert(can_read(in)); + // move to start of read + std::errc ec; + std::string_view line; + std::tie(in, ec) = get_istream(in); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + std::tie(line, ec) = readline(in, true); + if(ec != std::errc{}) { + m_state = serialize_state::ERROR; + return {INVALID, ec}; + } + if(istream_eof(in)) { + m_state = serialize_state::END; + return {FINAL, std::errc{}}; + } + + auto& iss = line_stream(line); + if (!(iss >> m_token)) { + return {INVALID, std::errc::io_error}; + } + auto cmd_type = last_command_type(); + if (cmd_type != CMD_PATCH) { + unreadline(line); + return {cmd_type, std::errc{}}; + } + + if(!(iss >> patch.bucket >> patch.patch_id >> patch.loc_x + >> patch.loc_y) || !line_stream_eof()) { - return {invalid, std::errc::io_error}; + return {INVALID, std::errc::io_error}; } if(patch.loc_x >= m_map_width || patch.loc_y > m_map_height) { - return {invalid, std::errc::invalid_argument}; + return {INVALID, {}}; } - m_iss >> std::ws; - if(m_iss.eof()) { return {invalid, std::errc::invalid_argument}; } - return {valid, std::errc{}}; + return {VALID, std::errc{}}; +} + +std::string_view +scenario_serialize::copy_string(std::string_view str) +{ + size_t len = str.size(); + if (len == 0) + return std::string_view(); + char* data = static_cast(m_string_res.allocate(len+1, 8)); + std::memcpy(data, str.data(), len); + data[len] = '\0'; + return std::string_view(data, len); } } // namespace warthog::io diff --git a/src/io/serialize_base.cpp b/src/io/serialize_base.cpp index 56992a3..e47d3f5 100644 --- a/src/io/serialize_base.cpp +++ b/src/io/serialize_base.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace warthog::io { @@ -57,7 +58,7 @@ serialize_base::open_write(std::ostream* os) void serialize_base::close() { - m_line_num = -1; + m_line_num = 0; m_line = std::string_view(); m_unget_line.clear(); m_stream = nullptr; @@ -65,40 +66,64 @@ serialize_base::close() m_stream_out = nullptr; } +bool +serialize_base::istream_eof(std::istream* in) +{ + auto [s, err] = get_istream(in); + if (err != std::errc{}) + return false; + return s->eof(); +} + std::pair -serialize_base::readline(std::istream* in) +serialize_base::readline(std::istream* in, bool skip_blanks) { size_t len; - if(len = m_unget_line.size(); len != 0) - { - // return last unreadline - if(len > max_line_length - 1) + auto [s, err] = get_istream(in); + while (true) { + if(len = m_unget_line.size(); len != 0) { - return {{}, std::errc::invalid_argument}; - } - if (len == 1 && m_unget_line[0] == '\0') { - // empty line - m_line = std::string_view(); - } else { - // copy line - std::memcpy(m_line_data.get(), m_unget_line.c_str(), len+1); - m_line = std::string_view(m_line_data.get(), len); + // return last unreadline + if(len > max_line_length - 1) + { + return {{}, std::errc::invalid_argument}; + } + if (len == 1 && m_unget_line[0] == '\0') { + // empty line + m_line = std::string_view(); + } else { + // copy line + std::memcpy(m_line_data.get(), m_unget_line.c_str(), len+1); + m_line = std::string_view(m_line_data.get(), len); + } + m_unget_line.clear(); } - m_unget_line.clear(); - } - else - { - auto [s, err] = get_istream(in); - if(err != std::errc{}) { return {{}, err}; } - if(!s->getline(m_line_data.get(), max_line_length)) + else { - return {{}, std::errc::io_error}; + if(err != std::errc{}) { return {{}, err}; } + if (s->eof()) { + return {{}, std::errc::io_error}; + } + if(!s->getline(m_line_data.get(), max_line_length)) + { + if (s->eof() && s->gcount() == 0) { + // extracted no characters and reached end of file, return no error + return {{}, {}}; + } else { + // error + return {{}, std::errc::io_error}; + } + } + // get length, if eof is set, is last line and no delimiter was extracted + len = static_cast(s->gcount()) - static_cast(s->eof()); + m_line_num += 1; + } + std::string_view line(m_line_data.get(), len); + if (skip_blanks && is_line_blank(line)) { + continue; // blank line, repeat } - // get length, if eof is set, is last line and no delimiter was extracted - len = static_cast(s->gcount()) - static_cast(s->eof()); - m_line_num += 1; + return {line, {}}; } - return {std::string_view(m_line_data.get(), len), {}}; } void serialize_base::unreadline(std::string_view line) @@ -110,12 +135,19 @@ serialize_base::unreadline(std::string_view line) } } +bool serialize_base::is_line_blank(std::string_view line) +{ + return std::all_of(line.begin(), line.end(), [](char c) { + return std::isspace((unsigned char)c); + }); +} + std::istream& serialize_base::line_stream(std::string_view line) { - m_iss.clear(); m_iss.str(std::string(line)); m_iss.seekg(0); + m_iss.clear(); return m_iss; } bool @@ -124,7 +156,7 @@ serialize_base::line_stream_eof() if (m_iss && !m_iss.eof()) { m_iss >> std::ws; } - return m_iss && m_iss.eof(); + return m_iss.eof(); } } // namespace warthog::io diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index c0f7764..8690153 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -7,7 +7,7 @@ #include #include -namespace warthog::util +namespace warthog::manager { scenario_runner::scenario_runner() { } @@ -26,11 +26,11 @@ std::pair scenario_runner::experiment_next(uint32_t coun auto cmd = commands[command_at_]; // command_at_ incremented in following fuction calls switch (cmd.type) { - case scenario_command::SNAPSHOT: - case scenario_command::PATCH: + case util::scenario_command::SNAPSHOT: + case util::scenario_command::PATCH: patch_count += snapshot_patches(); break; - case scenario_command::QUERY: + case util::scenario_command::QUERY: if (const experiment* query = snapshot_query(); query != nullptr) { if (--count == 0) return {query, patch_count}; @@ -74,7 +74,7 @@ scenario_runner::snapshot_next(bool clear_patch) while (command_at_ < commands.size()) { auto cmd = commands[command_at_]; switch (cmd.type) { - case scenario_command::SNAPSHOT: + case util::scenario_command::SNAPSHOT: if (cmd.id != snapshot_at_) { // at new snapshot, return snapshot_at_ = cmd.id; @@ -82,10 +82,10 @@ scenario_runner::snapshot_next(bool clear_patch) } // current snapshot, goto next snapshot [[fallthrough]]; - case scenario_command::QUERY: + case util::scenario_command::QUERY: ++snapshot_at_; break; - case scenario_command::PATCH: + case util::scenario_command::PATCH: snapshot_patches(false); // snapshot_patches increments snapshot_at_ break; @@ -105,11 +105,11 @@ scenario_runner::snapshot_patches(bool clear_patch) patches_.clear(); int count = 0; // if start of snapshot, apply that snapshot patches - if (command_at_ < commands.size() && commands[command_at_].type == scenario_command::SNAPSHOT) + if (command_at_ < commands.size() && commands[command_at_].type == util::scenario_command::SNAPSHOT) command_at_ += 1; // while command_at_ is PATCH, add patch to applied list while (command_at_ < commands.size()) { - if (auto cmd = commands[command_at_]; cmd.bucket == scenario_command::PATCH) { + if (auto cmd = commands[command_at_]; cmd.bucket == util::scenario_command::PATCH) { command_at_ += 1; count += 1; patches_.push_back(cmd.id); @@ -127,7 +127,7 @@ scenario_runner::snapshot_query() if (command_at_ >= commands.size()) return nullptr; auto cmd = commands[command_at_]; - if (cmd.type != scenario_command::QUERY) + if (cmd.type != util::scenario_command::QUERY) return nullptr; command_at_ += 1; experiment_at_ += 1; @@ -148,7 +148,7 @@ scenario_runner::snapshot_query_all() while (command_at_ < commands.size()) { auto cmd = commands[command_at_]; - if (cmd.type != scenario_command::QUERY) + if (cmd.type != util::scenario_command::QUERY) break; if (cmd.cmd.query.experiment_id >= exp.size() || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) { WARTHOG_GERROR_FMT("scenario_runner::snapshot_query_all invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, exp.size(), WARTHOG_FILENAME_LINE); diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index f351d28..57cc000 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -18,7 +18,6 @@ void scenario_manager::clear() { experiments_.clear(); experiments_res_.release(); - sfile_.clear(); mfile_.clear(); } @@ -30,7 +29,7 @@ scenario_manager::load_scenario(const std::filesystem::path& filelocation) if(load_gppc_scenario(in) != std::errc{}) { WARTHOG_GERROR_FMT( - "Failed to load scenario file \"{}\"", std::string(sfile_)); + "Failed to load scenario file \"{}\"", sfile_.string()); throw std::runtime_error("scenario_manager failed to load scenario"); } } @@ -87,8 +86,7 @@ scenario_manager::load_gppc_scenario(std::istream& scenfile) std::errc scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) { - - if(auto ec = si.read_header_v1(); ec != std::errc{}) + if(auto ec = si.read_header(); ec != std::errc{}) { WARTHOG_GERROR_FMT( "scenario_manager failed to read scenario v1 header on line: {}", @@ -99,8 +97,8 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) mfile_ = si.get_map_filename(); experiments_.reserve(1024); commands_.reserve(1024); - uint32_t width = si.get_map_width(); - uint32_t height = si.get_map_height(); + scenario_width_ = si.get_map_width(); + scenario_height_ = si.get_map_height(); // setup commands header for static scenario commands_.push_back(scenario_command::make_snapshot(0, 0)); commands_.push_back(scenario_command::make_patch(0, 0, 0, 0)); @@ -108,12 +106,12 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) static_scenario_start_ = (int32_t)commands_.size(); // read queries until done bool first = true; - io::scenario_query Q; // keep out of loop to reuse std::string + io::scenario_query Q; std::string_view map_string; while(true) { Q.reset(); - auto [con, ec] = si.read_query_line_v1(Q); + auto [con, ec] = si.read_query_line(Q); if(ec != std::errc{}) { WARTHOG_GERROR_FMT( @@ -121,7 +119,7 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) si.get_line_num()); return std::errc::io_error; } - if(con == io::scenario_serialize::valid) + if(con == io::scenario_serialize::VALID) { std::string_view current_map(Q.map); if (current_map.size() > 2048) // limit string size @@ -139,12 +137,12 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) sizeof(experiment), alignof(experiment))), (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), - Q.dist[(int)io::cost_type::G_8C_NCC], + Q.dist[(int)0], map_string); experiments_.push_back(ex); commands_.push_back(scenario_command::make_query(Q.bucket, query_count_++, (uint32_t)(experiments_.size()-1))); } - else if(con == io::scenario_serialize::final) { break; } + else if(con == io::scenario_serialize::FINAL) { break; } } return std::errc{}; } @@ -152,8 +150,7 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) std::errc scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) { - - if(auto ec = si.read_header_v2(); ec != std::errc{}) + if(auto ec = si.read_header(); ec != std::errc{}) { WARTHOG_GERROR_FMT( "scenario_manager failed to read scenario v2 header on line: {}", @@ -163,46 +160,99 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) mfile_ = si.get_map_filename(); experiments_.reserve(1024); - uint32_t width = si.get_map_width(); - uint32_t height = si.get_map_height(); + commands_.reserve(1024); + scenario_width_ = si.get_map_width(); + scenario_height_ = si.get_map_height(); + static_scenario_start_ = -1; // init dynamic scenario + + // set map filename + std::string_view map_string = copy_string(si.get_map_filename().string()); + if (map_string.size() > 2048) // limit string size + { + WARTHOG_GERROR( + "scenario_manager v2 map exceeds 2048 chars"); + return std::errc::argument_out_of_domain; + } + + // get cost index + int cost_index = si.get_cost_type().size() != 0 ? 0 : -1; + if (!cost_type_.empty()) { + // user-provided cost index + cost_index = si.find_cost_index(cost_type_); + if (cost_index < 0) { + WARTHOG_GWARN_FMT("scenario_manager v2 failed to find user-provided cost: {}", cost_type_); + } + } else { + // try to use grid if exist + if (int ci = si.find_cost_index(io::cost_type::G_8C_NCC); ci >= 0) { + cost_index = ci; + } + } + // read queries until done - bool first = true; - io::scenario_query Q; // keep out of loop to reuse std::string - std::string_view map_string; + io::scenario_query Q; + io::scenario_patch P; + int last_type = -1; + int last_bucket = -1; + int snapshot_count = -1; while(true) { - Q.reset(); - auto [con, ec] = si.read_query_line_v1(Q); + // try reading a query line + int con; + std::errc ec; + std::tie(con, ec) = si.read_query_line(Q); if(ec != std::errc{}) { WARTHOG_GERROR_FMT( - "scenario_manager failed to read query on line: {}", + "scenario_manager failed to read command on line: {}", si.get_line_num()); return std::errc::io_error; } - if(con == io::scenario_serialize::valid) + if(con == io::scenario_serialize::VALID) { - std::string_view current_map(Q.map); - if (current_map.size() > 2048) // limit string size - { - WARTHOG_GERROR_FMT( - "scenario_manager v1 query line map exceeds 2048 chars on line: {}", - si.get_line_num()); - return std::errc::argument_out_of_domain; - } - if (map_string != current_map) { - map_string = copy_string(current_map); + if (last_type == -1) { + // only used if first command is a query + commands_.push_back(scenario_command::make_snapshot(Q.bucket, ++snapshot_count)); } + last_type = io::scenario_serialize::CMD_QUERY; + last_bucket = Q.bucket; + experiment* ex = std::construct_at( static_cast(experiments_res_.allocate( sizeof(experiment), alignof(experiment))), - (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, - (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), + Q.start_x, Q.start_y, Q.goal_x, + Q.goal_y, si.get_map_width(), si.get_map_height(), Q.dist[(int)io::cost_type::G_8C_NCC], map_string); experiments_.push_back(ex); + commands_.push_back(scenario_command::make_query(Q.bucket, query_count_++, (uint32_t)(experiments_.size()-1))); + } else if (con == io::scenario_serialize::CMD_PATCH) { + std::tie(con, ec) = si.read_patch_line(P); + if(ec != std::errc{} || con != io::scenario_serialize::VALID) + { + WARTHOG_GERROR_FMT( + "scenario_manager failed to read command on line: {}", + si.get_line_num()); + return std::errc::io_error; + } + if (last_type != io::scenario_serialize::CMD_PATCH || last_bucket != P.bucket) { + commands_.push_back(scenario_command::make_snapshot(Q.bucket, ++snapshot_count)); + } + last_type = io::scenario_serialize::CMD_PATCH; + last_bucket = Q.bucket; + + commands_.push_back(scenario_command::make_patch(P.bucket, P.patch_id, P.loc_x, P.loc_y)); + } else if(con == io::scenario_serialize::FINAL) { break; } + else if(con == io::scenario_serialize::CMD_UNKNOWN) { + // ignore + si.skip_commands(); + } else { + // error, invalid query + WARTHOG_GERROR_FMT( + "scenario_manager failed to read command on line: {}", + si.get_line_num()); + return std::errc::io_error; } - else if(con == io::scenario_serialize::final) { break; } } return std::errc{}; } From d85fef9591699f554c8bf2132b8bd63eeec5a960 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 16 Apr 2026 23:22:37 +1000 Subject: [PATCH 46/61] ported istringstream to custom parser --- cmake/headers.cmake | 13 + include/warthog/io/grid.h | 12 +- include/warthog/io/scenario.h | 2 + include/warthog/io/serialize_base.h | 24 +- include/warthog/io/string_parser.h | 21 -- include/warthog/util/string.h | 352 +++++++++++++++++++++++++++- src/io/grid.cpp | 46 ++-- src/io/scenario.cpp | 277 +++++++++++----------- src/io/serialize_base.cpp | 34 +-- 9 files changed, 543 insertions(+), 238 deletions(-) delete mode 100644 include/warthog/io/string_parser.h diff --git a/cmake/headers.cmake b/cmake/headers.cmake index 46e1ba6..6d90038 100644 --- a/cmake/headers.cmake +++ b/cmake/headers.cmake @@ -16,6 +16,7 @@ include/warthog/limits.h include/warthog/domain/grid.h include/warthog/domain/gridmap.h +include/warthog/domain/grid_patches.h include/warthog/domain/labelled_gridmap.h include/warthog/geometry/geography.h @@ -27,7 +28,18 @@ include/warthog/heuristic/octile_heuristic.h include/warthog/heuristic/zero_heuristic.h include/warthog/io/grid.h +include/warthog/io/grid_trace.h include/warthog/io/log.h +include/warthog/io/observer.h +include/warthog/io/posthoc_trace.h +include/warthog/io/scenario.h +include/warthog/io/serialize_base.h +include/warthog/io/stream_observer.h + +include/warthog/manager/experiment.h +include/warthog/manager/grid_patch_set.h +include/warthog/manager/scenario_manager.h +include/warthog/manager/scenario_runner.h include/warthog/memory/arraylist.h include/warthog/memory/bittable.h @@ -60,6 +72,7 @@ include/warthog/util/intrin.h include/warthog/util/macros.h include/warthog/util/pqueue.h include/warthog/util/scenario_manager.h +include/warthog/util/string.h include/warthog/util/template.h include/warthog/util/timer.h include/warthog/util/vec_io.h diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index 8e195ef..016ff67 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -142,6 +142,8 @@ bittable_serialize::read_grid_data( return err; } std::string_view line; + std::string_view token; + for(uint32_t y = 0; y < read_dim.height; ++y, bit_id += bit_row_offset) { // read row @@ -150,17 +152,17 @@ bittable_serialize::read_grid_data( { return err; } - auto& iss = line_stream(line); - if (!(iss >> m_token) || !line_stream_eof()) + parser par(line); + if (!par.next(token).eof()) { - return std::errc::io_error; + return par.error(); } - if (m_token.size() != read_dim.width) + if (token.size() != read_dim.width) return std::errc::argument_out_of_domain; // copy row to table for(uint32_t x = 0; x < read_dim.width; ++x, ++bit_id) { - if (gridmap_cell_traversable(m_token[x])) + if (gridmap_cell_traversable(token[x])) table.bit_or(static_cast(bit_id), 1); } } diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index cb3801d..96f8780 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -262,6 +262,8 @@ class scenario_serialize : public serialize_base std::pmr::vector m_cost_strings; ///< names read in std::pmr::vector m_cost_type; ///< cost_type of index (corrisponding dist_strings and dist_value) std::pmr::vector m_cost_value; ///< distance value stored, will be placed in dynamic_scenario + + std::string m_command_type; ///< last cost type }; inline constexpr std::string_view scenario_serialize::get_dist_str(cost_type a) noexcept diff --git a/include/warthog/io/serialize_base.h b/include/warthog/io/serialize_base.h index 95284a1..def0bb3 100644 --- a/include/warthog/io/serialize_base.h +++ b/include/warthog/io/serialize_base.h @@ -12,6 +12,8 @@ // @created: 2026-04-10 // +#include + #include #include #include @@ -24,6 +26,8 @@ namespace warthog::io class serialize_base { public: + using parser = util::string_parser; + static constexpr size_t max_line_length = 16 << 10; // 16kB serialize_base(); virtual ~serialize_base(); @@ -69,20 +73,14 @@ class serialize_base can_read(std::istream* in = nullptr) { if(in == nullptr) in = m_stream_in; - return in != nullptr && !in->bad(); + return in != nullptr; } /// @return if scenario is open for writing bool can_write(std::ostream* out = nullptr) { if(out == nullptr) out = m_stream_out; - return out != nullptr && !out->bad(); - } - - std::string_view - get_last_token() const noexcept - { - return m_token; + return out != nullptr; } protected: @@ -90,7 +88,7 @@ class serialize_base get_istream(std::istream* in = nullptr) noexcept { if(in == nullptr) in = m_stream_in; - if(in == nullptr || !in->good()) return {nullptr, std::errc::io_error}; + if(in == nullptr || !in) return {nullptr, std::errc::io_error}; return {in, {}}; } std::pair @@ -107,10 +105,6 @@ class serialize_base void unreadline(std::string_view line); bool is_line_blank(std::string_view line); - std::istream& - line_stream(std::string_view line); - bool - line_stream_eof(); protected: std::filesystem::path m_filename; @@ -121,10 +115,6 @@ class serialize_base std::unique_ptr m_line_data; std::string_view m_line; std::string m_unget_line; - - // shared temp parameter - std::istringstream m_iss; - std::string m_token; }; } // namespace warthog::io diff --git a/include/warthog/io/string_parser.h b/include/warthog/io/string_parser.h deleted file mode 100644 index 72d2b23..0000000 --- a/include/warthog/io/string_parser.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef WARTHOG_IO_STRING_PARSER_H -#define WARTHOG_IO_STRING_PARSER_H - -// io/string_parser.h -// -// -// @author: Ryan Hechenberger -// @created: 2026-04-14 -// - -#include -#include - -namespace warthog::io -{ - - - -} // namespace warthog::io - -#endif // WARTHOG_IO_STRING_PARSER_H diff --git a/include/warthog/util/string.h b/include/warthog/util/string.h index 2653bfa..a0cde10 100644 --- a/include/warthog/util/string.h +++ b/include/warthog/util/string.h @@ -14,26 +14,32 @@ #include #include #include +#include #include #include +#include +#include +#include namespace warthog::util { /// @brief remove white spaces (as std::isspace) from beginning of string_view -std::string_view ltrim(std::string_view str) +inline std::string_view ltrim(std::string_view str) { str.remove_prefix(std::find_if(str.begin(), str.end(), [](char c) { return !isspace((unsigned char)c); }) - str.begin()); + return str; } /// @brief remove white spaces (as std::isspace) from end of string_view -std::string_view rtrim(std::string_view str) +inline std::string_view rtrim(std::string_view str) { str.remove_suffix(std::find_if(str.rbegin(), str.rend(), [](char c) { return !isspace((unsigned char)c); }) - str.rbegin()); + return str; } /// @brief string_view is either empty or only contains spaces (as std::isspace) -bool is_blank(std::string_view str) +inline bool is_blank(std::string_view str) { return std::all_of(str.begin(), str.end(), [](char c) { return isspace((unsigned char)c); }); } @@ -44,23 +50,23 @@ struct token_return size_t trim; ///< amount of space discarded from front of string }; -token_return get_token(std::string_view str) +inline token_return get_token(std::string_view str) { auto fstr = ltrim(str); if (fstr.empty()) { return {std::string_view(), str.size()}; } - auto endsp = std::find_if(str.begin(), str.end(), [](char c) { return isspace((unsigned char)c); }) - str.begin(); + auto endsp = std::find_if(fstr.begin(), fstr.end(), [](char c) { return isspace((unsigned char)c); }) - fstr.begin(); token_return ret; ret.token = fstr.substr(0, endsp); ret.trim = str.size() - fstr.size(); return ret; } -token_return get_token_quoted(std::string_view str, char quote = '"', char escape = '\\') +inline token_return get_token_quoted(std::string_view str, char quote = '"', char escape = '\\') { if (quote == escape) { - return {std::string_view(), str.size()}; + return {std::string_view(), 0}; } auto fstr = ltrim(str); if (fstr.empty()) { @@ -93,6 +99,338 @@ token_return get_token_quoted(std::string_view str, char quote = '"', char escap return ret; } +inline std::errc parse_token(std::string_view token, std::integral auto& out, int base = 10) +{ + const char*const f = token.data(); + const char*const ed = token.data() + token.size(); + auto res = std::from_chars(token.data(), ed, out, base); + if (res.ec != std::errc{}) + return res.ec; + // value set + if (res.ptr != ed) { + return std::errc::invalid_argument; + } + return std::errc{}; +} +inline std::errc parse_token(std::string_view token, std::floating_point auto& out, std::chars_format fmt = std::chars_format::general) +{ + const char*const ed = token.data() + token.size(); + auto res = std::from_chars(token.data(), ed, out, fmt); + if (res.ec != std::errc{}) + return res.ec; + // value set + if (res.ptr != ed) { + return std::errc::invalid_argument; + } + return std::errc{}; +} + +inline std::pair parse_token_part(std::string_view token, std::integral auto& out, int base = 10) +{ + std::from_chars_result res = std::from_chars(token.data(), token.data() + token.size(), out, base); + if (res.ec != std::errc{}) + return {{}, res.ec}; + // value set + token.remove_prefix(res.ptr - token.data()); + return {token, {}}; +} +inline std::pair parse_token_part(std::string_view token, std::floating_point auto& out, std::chars_format fmt = std::chars_format::general) +{ + std::from_chars_result res = std::from_chars(token.data(), token.data() + token.size(), out, fmt); + if (res.ec != std::errc{}) + return {{}, res.ec}; + // value set + token.remove_prefix(res.ptr - token.data()); + return {token, {}}; +} + +inline std::pair parse_token_quoted(std::string_view token, char* buffer, size_t size, char quote = '"', char escape = '\\') +{ + if (quote == escape) { + return {nullptr, std::errc::invalid_argument}; + } + if (token.empty() || size == 0) { + return {nullptr, std::errc::invalid_argument}; + } + const char*const ed = token.end() + token.size(); + if (token[0] != '"') { + // not quoted, take as whole token + if (std::any_of(token.data(), ed, [](char c) { return std::isspace((unsigned char)c); })) { + // space detected, error + return {nullptr, std::errc::invalid_argument}; + } + if (token.size()+1 > size) { + // too large to hold token + return {nullptr, std::errc::value_too_large}; + } + token.copy(buffer, token.size()); + buffer[token.size()] = '\0'; + return {buffer + token.size() + 1, {}}; + } + // quoted text, process + const char* t_at = token.data() + 1; + while (true) { + assert(size > 0); + if (t_at >= ed) { + // reached end of token with no end-quote + return {buffer, std::errc::invalid_argument}; + } + if (*t_at == '\\') [[unlikely]] { + if (++t_at >= ed) { + // unexpected end of string + return {buffer, std::errc::invalid_argument}; + } + } else if (*t_at == '"') [[unlikely]] { + // found end quote, append null-terminator + *(buffer++) = '\0'; + // check is end of token + return {buffer, t_at+1 == ed ? std::errc{} : std::errc::invalid_argument}; + } + *(buffer++) = *(t_at++); + if (--size == 0) { + // out of space for null-terminator + return {buffer, std::errc::value_too_large}; + } + } +} +inline std::errc parse_token_quoted(std::string_view token, auto& str_out, char quote = '"', char escape = '\\') +{ + str_out.resize(token.size()+1); // plus 1 to hold \0 placed in buffer + auto [ed, ec] = parse_token_quoted(token, str_out.data(), str_out.size(), quote, escape); + if (ec != std::errc{}) { + // failed to read, clear str_out and return error code + str_out.resize(0); + return ec; + } else { + // successor, adjust str_out length and return + assert(ed - str_out.data() > 0); + str_out.resize((ed - str_out.data()) - 1); + return std::errc{}; + } +} + + +class string_parser +{ +public: + string_parser(); + string_parser(std::string_view str) : m_str(str) + { } + // error codes + void clear_error() noexcept + { + m_error = std::errc{}; + } + std::errc error() const noexcept + { + return m_error; + } + bool is_error() const noexcept + { + return m_error != std::errc{}; + } + operator bool() const noexcept + { + return m_error == std::errc{}; + } + + // set string + std::string_view str() const noexcept + { + return m_str; + } + void str(std::string_view s) noexcept + { + m_str = s; + m_token = std::string_view{}; + clear_error(); + } + + std::string_view last_token() const noexcept + { + return m_token; + } + + string_parser& next(std::integral auto& int_out, int base = 10) + { + if (is_error()) // do nothing if in error state + return *this; + if (!next_token_()) + return *this; + std::errc ec = util::parse_token(m_token, int_out, base); + if (ec != std::errc{}) + m_error = ec; + return *this; + } + string_parser& next(std::floating_point auto& float_out, std::chars_format fmt = std::chars_format::general) + { + if (is_error()) // do nothing if in error state + return *this; + if (!next_token_()) + return *this; + std::errc ec = util::parse_token(m_token, float_out, fmt); + if (ec != std::errc{}) + m_error = ec; + return *this; + } + string_parser& next(std::span& buffer) + { + if (is_error()) // do nothing if in error state + return *this; + if (buffer.empty()) { + m_error = std::errc::invalid_argument; + return *this; + } + if (!next_token_()) + return *this; + if (m_token.size() > buffer.size()) { + m_error = std::errc::value_too_large; + return *this; + } + m_token.copy(buffer.data(), m_token.size()); + buffer = buffer.subspan(0, m_token.size()); + return *this; + } + string_parser& next(std::string_view& out_token) + { + if (is_error()) // do nothing if in error state + return *this; + if (!next_token_()) + return *this; + out_token = m_token; + return *this; + } + string_parser& next(std::string& out_token) + { + if (is_error()) // do nothing if in error state + return *this; + if (!next_token_()) + return *this; + out_token = m_token; + return *this; + } + string_parser& next(std::pmr::string& out_token) + { + if (is_error()) // do nothing if in error state + return *this; + if (!next_token_()) + return *this; + out_token = m_token; + return *this; + } + + string_parser& next_q(std::span& buffer, char quote = '"', char escape = '\\') + { + if (is_error()) // do nothing if in error state + return *this; + if (buffer.empty()) { + m_error = std::errc::invalid_argument; + return *this; + } + if (!next_token_quoted_(quote, escape)) + return *this; + auto [ed, ec] = util::parse_token_quoted(m_token, buffer.data(), buffer.size(), quote, escape); + if (ec != std::errc{}) { + m_error = ec; + return *this; + } + if (ed == buffer.data()) { + assert(false); // should never occur + m_error = std::errc::state_not_recoverable; + return *this; + } + buffer = buffer.subspan(0, (ed - buffer.data()) - 1); + return *this; + } + string_parser& next_q(std::string& out_token, char quote = '"', char escape = '\\') + { + if (is_error()) // do nothing if in error state + return *this; + if (!next_token_quoted_(quote, escape)) + return *this; + std::errc ec = util::parse_token_quoted(m_token, out_token, quote, escape); + if (ec != std::errc{}) { + m_error = ec; + return *this; + } + return *this; + } + string_parser& next_q(std::pmr::string& out_token, char quote = '"', char escape = '\\') + { + if (is_error()) // do nothing if in error state + return *this; + if (!next_token_quoted_(quote, escape)) + return *this; + std::errc ec = util::parse_token_quoted(m_token, out_token, quote, escape); + if (ec != std::errc{}) { + m_error = ec; + return *this; + } + return *this; + } + string_parser& ignore() + { + if (is_error()) // do nothing if in error state + return *this; + next_token_(); + return *this; + } + string_parser& ignore_q(char quote = '"', char escape = '\\') + { + if (is_error()) // do nothing if in error state + return *this; + next_token_quoted_(quote, escape); + return *this; + } + + bool eof() + { + if (is_error()) + return false; + if (!util::is_blank(m_str)) + { + m_error = std::errc::invalid_argument; + return false; + } + return true; + } + +protected: + bool next_token_() + { + auto ret = util::get_token(m_str); + if (ret.token.empty()) { + // failed to read a token, error + m_error = std::errc::invalid_argument; + return false; + } else { + m_token = ret.token; + assert(ret.trim <= m_str.size()); + m_str.remove_prefix(ret.token.size() + ret.trim); + return true; + } + } + bool next_token_quoted_(char quote, char escape) + { + auto ret = util::get_token_quoted(m_str, quote, escape); + if (ret.token.empty()) { + // failed to read a token, error + m_error = std::errc::invalid_argument; + return false; + } else { + m_token = ret.token; + assert(ret.trim <= m_str.size()); + m_str.remove_prefix(ret.token.size() + ret.trim); + return true; + } + } + +protected: + std::string_view m_str; + std::string_view m_token; + std::errc m_error = std::errc{}; +}; + } // namespace warthog::util diff --git a/src/io/grid.cpp b/src/io/grid.cpp index 6f9f4c2..55ec733 100644 --- a/src/io/grid.cpp +++ b/src/io/grid.cpp @@ -16,6 +16,8 @@ bittable_serialize::read_header(std::istream* in) return err; } std::string_view line; + std::string_view token; + std::tie(line, err) = readline(in); if(err != std::errc{}) { @@ -23,18 +25,18 @@ bittable_serialize::read_header(std::istream* in) } bittable_type detected_type = bittable_type::NONE; { - auto& iss = line_stream(line); - if (!(iss >> m_token) || m_token != "type") { + parser par(line); + if (!par.next(token) || token != "type") { return std::errc::io_error; } - if (!(iss >> m_token) || !line_stream_eof()) { + if (!par.next(token).eof()) { return std::errc::io_error; } } - if(m_token == "octile") + if(token == "octile") detected_type = bittable_type::OCTILE; - else if(m_token == "patch") + else if(token == "patch") detected_type = bittable_type::PATCH; else detected_type = bittable_type::OTHER; @@ -48,11 +50,11 @@ bittable_serialize::read_header(std::istream* in) { return err; } - auto& iss = line_stream(line); - if (!(iss >> m_token >> m_patch_count) || !line_stream_eof()) { + parser par(line); + if (!par.next(token).next(m_patch_count).eof()) { return std::errc::io_error; } - if (m_token != "patches") { + if (token != "patches") { return std::errc::argument_out_of_domain; } } @@ -70,6 +72,7 @@ bittable_serialize::read_grid_header(std::istream* in) return err; } std::string_view line; + std::string_view token; // read height std::tie(line, err) = readline(in); @@ -78,12 +81,12 @@ bittable_serialize::read_grid_header(std::istream* in) return err; } { - auto& iss = line_stream(line); - if (!(iss >> m_token >> m_dim.height) || !line_stream_eof()) + parser par(line); + if (!par.next(token).next(m_dim.height).eof()) { return std::errc::io_error; } - if (m_token != "height" || m_dim.height > GRID_DIMENSION_MAX) { + if (token != "height" || m_dim.height > GRID_DIMENSION_MAX) { return std::errc::argument_out_of_domain; } } @@ -94,12 +97,12 @@ bittable_serialize::read_grid_header(std::istream* in) return err; } { - auto& iss = line_stream(line); - if (!(iss >> m_token >> m_dim.width) || !line_stream_eof()) + parser par(line); + if (!par.next(token).next(m_dim.width).eof()) { return std::errc::io_error; } - if (m_token != "width" || m_dim.width > GRID_DIMENSION_MAX) { + if (token != "width" || m_dim.width > GRID_DIMENSION_MAX) { return std::errc::argument_out_of_domain; } } @@ -110,12 +113,12 @@ bittable_serialize::read_grid_header(std::istream* in) return err; } { - auto& iss = line_stream(line); - if (!(iss >> m_token) || !line_stream_eof()) + parser par(line); + if (!par.next(token).eof()) { return std::errc::io_error; } - if (m_token != "map") { + if (token != "map") { return std::errc::argument_out_of_domain; } } @@ -130,6 +133,7 @@ bittable_serialize::read_grid_raw( const memory::bittable_dimension read_dim = m_dim; raw_data.resize(read_dim.width * read_dim.height); char* data_at = raw_data.data(); + std::string_view token; std::errc err; std::tie(in, err) = get_istream(in); @@ -146,15 +150,15 @@ bittable_serialize::read_grid_raw( { return err; } - auto& iss = line_stream(line); - if (!(iss >> m_token) || !line_stream_eof()) + parser par(line); + if (!par.next(token).eof()) { return std::errc::io_error; } - if (m_token.size() != read_dim.width) + if (token.size() != read_dim.width) return std::errc::argument_out_of_domain; // copy row to table - std::memcpy(data_at, m_token.data(), read_dim.width); + token.copy(data_at, token.size()); data_at += read_dim.width; } return std::errc{}; diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 2b141f1..80f6a8b 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -41,9 +41,9 @@ scenario_serialize::set_relative_map_filename( int scenario_serialize::last_command_type() const { - if (m_token == "Q") + if (m_command_type == "Q") return CMD_QUERY; - if (m_token == "P") + if (m_command_type == "P") return CMD_PATCH; return CMD_UNKNOWN; } @@ -63,18 +63,20 @@ scenario_serialize::read_version(std::istream* in) return err; } std::string_view line; + std::string_view token; + std::tie(line, err) = readline(in); if(err != std::errc{}) { m_state = serialize_state::ERROR; return err; } - auto& iss = line_stream(line); + parser par(line); int version; - if(!(iss >> m_token >> version)) + if(!par.next(token).next(version).eof()) { m_state = serialize_state::ERROR; - return std::errc::io_error; + return par.error(); } if(version < 1 || version > 2) { @@ -129,25 +131,37 @@ scenario_serialize::read_header_v1(std::istream* in) m_cost_value[0] = -1; // read first query line to get map - scenario_query Q; - std::tie(s, ec) = read_query_line_v1(Q); + auto [line, err] = readline(in); + if(err != std::errc{}) + { + m_state = serialize_state::ERROR; + return err; + } + parser par(line); + std::string_view map; + if(!par.ignore().next(map).next(m_map_width).next(m_map_height)) + { + m_state = serialize_state::ERROR; + return par.error(); + } + if(m_map_height < 1 || m_map_height > GRID_MAX_SIZE) + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; + } if(ec != std::errc{}) { m_state = serialize_state::ERROR; return ec; } - if(s != FINAL){ - if(Q.width <= 0 || Q.width > GRID_MAX_SIZE || Q.height <= 0 - || Q.height > GRID_MAX_SIZE) - { - m_state = serialize_state::ERROR; - return std::errc::invalid_argument; - } - set_relative_map_filename(Q.map); - m_map_width = Q.width; - m_map_height = Q.height; + if(m_map_width <= 0 || m_map_width > GRID_MAX_SIZE || m_map_height <= 0 + || m_map_height > GRID_MAX_SIZE) + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; } - unreadline(m_line); + set_relative_map_filename(map); + unreadline(line); m_state = serialize_state::COMMAND; return std::errc{}; } @@ -164,6 +178,7 @@ scenario_serialize::read_header_v2(std::istream* in) } // read first query line to get map std::string_view line; + std::string_view token; // height std::tie(line, err) = readline(in); @@ -173,17 +188,17 @@ scenario_serialize::read_header_v2(std::istream* in) return err; } { - auto& iss = line_stream(line); - if(!(iss >> m_token >> m_map_height) || !line_stream_eof()) + parser par(line); + if(!par.next(token).next(m_map_height).eof()) { m_state = serialize_state::ERROR; - return std::errc::io_error; + return par.error(); + } + if(token != "height" || m_map_height < 1 || m_map_height > GRID_MAX_SIZE) + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; } - } - if(m_token != "height" || m_map_height < 1 || m_map_height > GRID_MAX_SIZE) - { - m_state = serialize_state::ERROR; - return std::errc::invalid_argument; } // width @@ -194,17 +209,17 @@ scenario_serialize::read_header_v2(std::istream* in) return err; } { - auto& iss = line_stream(line); - if(!(iss >> m_token >> m_map_width) || !line_stream_eof()) + parser par(line); + if(!par.next(token).next(m_map_width).eof()) { m_state = serialize_state::ERROR; - return std::errc::io_error; + return par.error(); + } + if(token != "width" || m_map_width < 1 || m_map_width > GRID_MAX_SIZE) + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; } - } - if(m_token != "width" || m_map_width < 1 || m_map_width > GRID_MAX_SIZE) - { - m_state = serialize_state::ERROR; - return std::errc::invalid_argument; } // cost @@ -215,14 +230,14 @@ scenario_serialize::read_header_v2(std::istream* in) return err; } { - auto& iss = line_stream(line); + parser par(line); int cost_count; - if(!(iss >> m_token >> cost_count) || !line_stream_eof()) + if(!par.next(token).next(cost_count)) { m_state = serialize_state::ERROR; - return std::errc::io_error; + return par.error(); } - if (m_token != "cost" || cost_count < 0 || cost_count > 128) + if (token != "cost" || cost_count < 0 || cost_count > 128) { // limit cost_count to be within reason m_state = serialize_state::ERROR; @@ -232,19 +247,19 @@ scenario_serialize::read_header_v2(std::istream* in) m_cost_type.resize(cost_count); m_cost_value.resize(cost_count); for (int i = 0; i < cost_count; ++i) { - if (!(iss >> m_token)) { + if (!par.next(token)) { m_state = serialize_state::ERROR; - return std::errc::invalid_argument; + return par.error(); } - std::string_view ctype = copy_string(m_token); - m_cost_strings[i] = copy_string(m_token); + std::string_view ctype = copy_string(token); + m_cost_strings[i] = ctype; m_cost_type[i] = get_cost_type(ctype); m_cost_value[i] = -1; } // end of line - if (!line_stream_eof()) { + if (!par.eof()) { m_state = serialize_state::ERROR; - return std::errc::invalid_argument; + return par.error(); } } @@ -256,17 +271,19 @@ scenario_serialize::read_header_v2(std::istream* in) return err; } { - auto& iss = line_stream(line); - if(!(iss >> m_token >> m_map_filename) || !line_stream_eof()) + parser par(line); + std::string fname; + if(!par.next(token).next_q(fname).eof()) { m_state = serialize_state::ERROR; - return std::errc::io_error; + return par.error(); + } + m_map_filename = std::move(fname); + if(token != "patch") + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; } - } - if(m_token != "patch") - { - m_state = serialize_state::ERROR; - return std::errc::invalid_argument; } // read in "commands" @@ -277,17 +294,17 @@ scenario_serialize::read_header_v2(std::istream* in) return err; } { - auto& iss = line_stream(line); - if(!(iss >> m_token) || !line_stream_eof()) + parser par(line); + if(!par.next(token).eof()) { m_state = serialize_state::ERROR; - return std::errc::io_error; + return par.error(); + } + if(token != "commands") + { + m_state = serialize_state::ERROR; + return std::errc::invalid_argument; } - } - if(m_token != "patch") - { - m_state = serialize_state::ERROR; - return std::errc::invalid_argument; } // set_relative_map_filename(m_query.map); @@ -335,9 +352,9 @@ scenario_serialize::next_command_type(std::istream* in) m_state = serialize_state::END; return {FINAL, {}}; } - auto& iss = line_stream(line); - if (!(iss >> m_token)) { - return {INVALID, std::errc::io_error}; + parser par(line); + if (!par.next(m_command_type)) { + return {INVALID, par.error()}; } auto cmd_type = last_command_type(); unreadline(line); @@ -348,52 +365,6 @@ scenario_serialize::next_command_type(std::istream* in) } } -std::pair -scenario_serialize::read_query_line(scenario_query& query, std::istream* in) -{ - if (m_state == serialize_state::END) - { - return {FINAL, {}}; - } - if(!can_read(in) || m_state != serialize_state::COMMAND) - { - return {0, std::errc::state_not_recoverable}; - } - - switch (m_version) { - case scenario_version::VERSION_1: - return {CMD_QUERY, {}}; - case scenario_version::VERSION_2: { - std::errc ec; - std::string_view line; - std::tie(in, ec) = get_istream(in); - if(ec != std::errc{}) { - m_state = serialize_state::ERROR; - return {INVALID, ec}; - } - std::tie(line, ec) = readline(in, true); - if(ec != std::errc{}) { - m_state = serialize_state::ERROR; - return {INVALID, ec}; - } - if(istream_eof(in)) { - m_state = serialize_state::END; - return {FINAL, {}}; - } - - auto& iss = line_stream(line); - if (!(iss >> m_token)) { - return {INVALID, std::errc::io_error}; - } - auto cmd_type = last_command_type(); - unreadline(line); - return {cmd_type, {}}; - } - default: - return {INVALID, std::errc::state_not_recoverable}; - } -} - std::errc scenario_serialize::skip_commands(int count, std::istream* in) { @@ -428,14 +399,37 @@ scenario_serialize::skip_commands(int count, std::istream* in) return {}; } -auto +std::pair +scenario_serialize::read_query_line(scenario_query& query, std::istream* in) +{ + if (m_state == serialize_state::END) + { + return {FINAL, {}}; + } + if(!can_read(in) || m_state != serialize_state::COMMAND) + { + return {0, std::errc::state_not_recoverable}; + } + + switch (m_version) { + case scenario_version::VERSION_1: + return read_query_line_v1(query, in); + case scenario_version::VERSION_2: + return read_query_line_v2(query, in); + default: + return {INVALID, std::errc::state_not_recoverable}; + } +} + +std::pair scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) - -> std::pair { assert(can_read(in)); // move to start of read std::errc ec; std::string_view line; + std::string_view token; + std::tie(in, ec) = get_istream(in); if(ec != std::errc{}) { m_state = serialize_state::ERROR; @@ -450,26 +444,15 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) m_state = serialize_state::END; return {FINAL, std::errc{}}; } - auto& iss = line_stream(line); - if(!(iss >> query.bucket >> m_token >> query.width >> query.height - >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y - >> m_cost_value.at(0))) + parser par(line); + if(!par.next(query.bucket).next(token).next(query.width).next(query.height) + .next(query.start_x).next(query.start_y).next(query.goal_x).next(query.goal_y) + .next(m_cost_value.at(0)).eof()) { - std::cout << std::quoted(line) << std::endl; // state not set to error, up to user - return {INVALID, std::errc::io_error}; - } - std::cout << std::quoted(line) << std::endl; - std::cout << iss.tellg() << std::endl; - std::cout << iss.good() << iss.eof() << std::endl; - iss.clear(); - iss >> std::ws; - std::cout << iss.good() << iss.eof() << std::endl; - std::string ttt; - iss >> ttt; - std::cout << std::quoted(ttt) << std::endl; - std::cout << iss.good() << iss.eof() << std::endl; - query.map = m_token; + return {INVALID, par.error()}; + } + query.map = token; query.dist = m_cost_value; if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) @@ -509,14 +492,15 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) return {VALID, std::errc{}}; } -auto +std::pair scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) - -> std::pair { assert(can_read(in)); // move to start of read std::errc ec; std::string_view line; + std::string_view token; + std::tie(in, ec) = get_istream(in); if(ec != std::errc{}) { m_state = serialize_state::ERROR; @@ -532,18 +516,18 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) return {FINAL, std::errc{}}; } - auto& iss = line_stream(line); - if (!(iss >> m_token)) { - return {INVALID, std::errc::io_error}; + parser par(line); + if (!par.next(m_command_type)) { + return {INVALID, par.error()}; } auto cmd_type = last_command_type(); if (cmd_type != CMD_QUERY) { unreadline(line); return {cmd_type, std::errc{}}; } - if(!(iss >> query.bucket >> query.start_x >> query.start_y >> query.goal_x >> query.goal_y)) + if(!par.next(query.bucket).next(query.start_x).next(query.start_y).next(query.goal_x).next(query.goal_y)) { - return {INVALID, std::errc::io_error}; + return {INVALID, par.error()}; } if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y)) @@ -552,8 +536,8 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) } query.width = query.height = 0; for (auto& cost : m_cost_value) { - if (!(iss >> cost)) { - return {INVALID, std::errc::io_error}; + if (!par.next(cost)) { + return {INVALID, par.error()}; } if (!std::isfinite(cost)) { @@ -561,9 +545,9 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) } } query.dist = m_cost_value; - if (!line_stream_eof()) { + if (!par.eof()) { // unexpected number of paramters - return {INVALID, std::errc::io_error}; + return {INVALID, par.error()}; } if(m_force_int) { @@ -617,6 +601,8 @@ scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) // move to start of read std::errc ec; std::string_view line; + std::string_view token; + std::tie(in, ec) = get_istream(in); if(ec != std::errc{}) { m_state = serialize_state::ERROR; @@ -632,9 +618,9 @@ scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) return {FINAL, std::errc{}}; } - auto& iss = line_stream(line); - if (!(iss >> m_token)) { - return {INVALID, std::errc::io_error}; + parser par(line); + if (!par.next(token)) { + return {INVALID, par.error()}; } auto cmd_type = last_command_type(); if (cmd_type != CMD_PATCH) { @@ -642,10 +628,9 @@ scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) return {cmd_type, std::errc{}}; } - if(!(iss >> patch.bucket >> patch.patch_id >> patch.loc_x - >> patch.loc_y) || !line_stream_eof()) + if(!par.next(patch.bucket).next(patch.patch_id).next(patch.loc_x).next(patch.loc_y).eof()) { - return {INVALID, std::errc::io_error}; + return {INVALID, par.error()}; } if(patch.loc_x >= m_map_width || patch.loc_y > m_map_height) { diff --git a/src/io/serialize_base.cpp b/src/io/serialize_base.cpp index e47d3f5..e38e48c 100644 --- a/src/io/serialize_base.cpp +++ b/src/io/serialize_base.cpp @@ -107,6 +107,7 @@ serialize_base::readline(std::istream* in, bool skip_blanks) if(!s->getline(m_line_data.get(), max_line_length)) { if (s->eof() && s->gcount() == 0) { + s->clear(std::ios::eofbit); // extracted no characters and reached end of file, return no error return {{}, {}}; } else { @@ -115,14 +116,22 @@ serialize_base::readline(std::istream* in, bool skip_blanks) } } // get length, if eof is set, is last line and no delimiter was extracted - len = static_cast(s->gcount()) - static_cast(s->eof()); + len = static_cast(s->gcount()) - (s->eof() ? 0 : 1); + if (len == 0) { + m_line = std::string_view(); + } else { + if (std::iscntrl(m_line_data[len-1])) { + // mainly \r reading windows files in linux + m_line_data[--len] = '\0'; + } + m_line = std::string_view(m_line_data.get(), len); + } m_line_num += 1; } - std::string_view line(m_line_data.get(), len); - if (skip_blanks && is_line_blank(line)) { + if (skip_blanks && is_line_blank(m_line)) { continue; // blank line, repeat } - return {line, {}}; + return {m_line, {}}; } } void @@ -142,21 +151,4 @@ bool serialize_base::is_line_blank(std::string_view line) }); } -std::istream& -serialize_base::line_stream(std::string_view line) -{ - m_iss.str(std::string(line)); - m_iss.seekg(0); - m_iss.clear(); - return m_iss; -} -bool -serialize_base::line_stream_eof() -{ - if (m_iss && !m_iss.eof()) { - m_iss >> std::ws; - } - return m_iss.eof(); -} - } // namespace warthog::io From 7e6b92236e9644413474719979062ae412b34154 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 30 Apr 2026 10:52:08 +1000 Subject: [PATCH 47/61] WIP table copy --- include/warthog/io/grid.h | 79 ++++++++++++++++++++---- include/warthog/manager/grid_patch_set.h | 40 +++++++++++- include/warthog/memory/bittable.h | 20 +++++- src/io/grid.cpp | 29 +++++++-- 4 files changed, 149 insertions(+), 19 deletions(-) diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index 016ff67..19c02b3 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -5,7 +5,7 @@ // // Read utility for gridmap. // -//Supported MovingAI map format. Read format spec: https://movingai.com/benchmarks/formats.html +// Supported MovingAI map format. Read format spec: https://movingai.com/benchmarks/formats.html // // @author: Ryan Hechenberger // @created: 2025-06-01 @@ -24,7 +24,6 @@ namespace warthog::io enum class bittable_type : uint8_t { - AUTO, OCTILE, PATCH, OTHER, @@ -42,6 +41,9 @@ enum class gridmap_cell : char WATER = 'W', }; +/// @brief Standard traversable terrain type from gridmap_cell +/// @param c +/// @return ".G" return true, false otherwise constexpr inline bool gridmap_cell_traversable(gridmap_cell c) noexcept { switch (c) { @@ -52,27 +54,30 @@ constexpr inline bool gridmap_cell_traversable(gridmap_cell c) noexcept return false; } } +/// @return char c is traversable, as gridmap_cell_traversable((gridmap_cell)c) constexpr inline bool gridmap_cell_traversable(char c) noexcept { return gridmap_cell_traversable(static_cast(c)); } +/// @brief Max grid size inline constexpr uint32_t GRID_MAX_SIZE = 15'000; +/// @brief Limit on max number of patches +inline constexpr uint32_t PATCH_COUNT_LIMIT = 10'000'000; + +/// @brief The bittable serialize class, flexable read/write of bittable/gridmap or +/// similiar datatypes class bittable_serialize : public serialize_base { public: + /// @return the grid dimension, either as last read grid from file or set by user for writing memory::bittable_dimension get_dim() const noexcept { return m_dim; } - bittable_type - get_type() const noexcept - { - return m_type; - } - + /// @brief sets the grid dimension, throws if out of range void set_dim(uint32_t width, uint32_t height) { @@ -85,37 +90,87 @@ class bittable_serialize : public serialize_base m_dim.width = width; m_dim.height = height; } + + /// @return the type/version of the file, default OCTILE + bittable_type + get_type() const noexcept + { + return m_type; + } + /// @brief Sets the type/version to write to the file header, supported is octile/patch. + /// Throws on unsupported type. void set_type(bittable_type type) { - if(static_cast(type) - > static_cast(bittable_type::NONE)) + if(type != bittable_type::OCTILE && type != bittable_type::PATCH) { throw std::out_of_range("type"); } m_type = type; } + uint32_t get_patch_count() const noexcept + { + return m_patch_count; + } + void set_patch_count(uint32_t count) + { + if(count > PATCH_COUNT_LIMIT) + { + throw std::out_of_range("count"); + } + m_patch_count = count; + } + + /// @brief Reads the map/patch file header, getting the type + /// @param in alternative filestream to read from + /// @return value init on success, error code on failure + /// + /// Reads the header line, `type octile` for bittable_type::OCTILE or + /// `type patch` for bittable_type::PATCH, retrivable by get_type(). + /// For PATCH type, also reads following line for number of patches in file. std::errc read_header(std::istream* in = nullptr); + /// @brief Reads the grids header, getting width/height up to the map data. + /// @param in alternative filestream to read from + /// @return value init on success, error code on failure + /// @pre get_type() matches the format of file. std::errc read_grid_header(std::istream* in = nullptr); + /// @brief Reads the grids data and stores it into a bittable, expects size from get_dim() + /// @param table bittable derived type to store, must be init + /// @param offset_x offset of top-left in table to copy grid to + /// @param offset_y offset of top-left in table to copy grid to + /// @param in alternative filestream to read from + /// @return value init on success, error code on failure + /// @pre table must be init and large enough to store whole grid (including from offset) template std::errc read_grid_data( BitTable& table, uint32_t offset_x = 0, uint32_t offset_y = 0, std::istream* in = nullptr); + /// @brief Reads the raw rows (char) from map into a 1D array, expects size from get_dim() + /// @param buffer the buffer + /// @param in alternative filestream to read from + /// @return value init on success, error code on failure + /// @pre buffer must be large enough to store width x height characters from get_dim() + /// + /// Reads row by row from the top left, writing into buffer. + /// Data is tightly packed, with no delimited between rows of size width. + /// Characters are as defined by the MovingAI spec, use gridmap_cell_traversable(c) + /// to determine traversability if applicable. std::errc read_grid_raw( - std::vector& raw_data, std::istream* in = nullptr); + std::span buffer, std::istream* in = nullptr); protected: memory::bittable_dimension m_dim = {}; - bittable_type m_type = bittable_type::AUTO; + bittable_type m_type = bittable_type::OCTILE; uint32_t m_patch_count = 0; + uint32_t m_patch_num = 0; }; template diff --git a/include/warthog/manager/grid_patch_set.h b/include/warthog/manager/grid_patch_set.h index ee81d88..12de239 100644 --- a/include/warthog/manager/grid_patch_set.h +++ b/include/warthog/manager/grid_patch_set.h @@ -9,11 +9,12 @@ // @created: 2026-04-10 // -#include "experiment.h" -#include #include #include +#include +#include + namespace warthog::manager { @@ -22,7 +23,42 @@ namespace warthog::manager /// Stores a list of patches at class grid_patch_set { +public: + using bittable = domain::gridmap::bittable; + static constexpr uint32_t npos = (uint32_t)-1u; + grid_patch_set(std::pmr::memory_resource* upstream = nullptr) : + grid_res_(1024*4, upstream) + { } + + bool push_copy(bittable table, uint32_t offset_x = 0, uint32_t offset_y, + uint32_t width = npos, uint32_t height = npos) + { + if (table.size() == 0) { + return false; + } + bittable patch; + if (offset_x == 0 && offset_y == 0 && width == npos && height == npos) { + // copy as is + auto size = table.size_bytes(); + auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); + std::memcpy(grid_data, table.data(), size); + patch = bittable(grid_data, table.width(), table.height()); + } else { + // crop + if (offset_x >= table.width() || width > table.width() || offset_x + width > table.width()) + { + return false; + } + if (offset_y >= table.height() || height > table.height() || offset_y + height > table.height()) + { + return false; + } + table.setup() + } +protected: + std::pmr::monotonic_buffer_resource grid_res_; + std::vector patches_; }; } // namespace warthog::manager diff --git a/include/warthog/memory/bittable.h b/include/warthog/memory/bittable.h index b361898..ece4a20 100644 --- a/include/warthog/memory/bittable.h +++ b/include/warthog/memory/bittable.h @@ -238,6 +238,19 @@ struct bitarray } } + void copy(void* dest, uint8_t dest_bit, id_type pos = id_type(0), size_t count) + { + assert(dest_bit < CHAR_BIT); + // primative implemtation + bitarray + id_value_type p = pos; + while (count > 0) { + bool c = get(id_type(pos++)); + pos + std::byte + } + } + /// /// struct data /// @@ -332,7 +345,7 @@ struct bittable : bitarray { return m_dim.width; } - /// @return width in bytes, rounded down + /// @return width in bytes constexpr uint32_t width_bytes() const noexcept { @@ -350,6 +363,11 @@ struct bittable : bitarray { return static_cast(m_dim.width) * m_dim.height; } + constexpr size_t + size_bytes() const noexcept + { + return sizeof(value_type) * data_elements(); + } constexpr bittable_dimension dim() const noexcept { diff --git a/src/io/grid.cpp b/src/io/grid.cpp index 55ec733..638c5c9 100644 --- a/src/io/grid.cpp +++ b/src/io/grid.cpp @@ -54,7 +54,8 @@ bittable_serialize::read_header(std::istream* in) if (!par.next(token).next(m_patch_count).eof()) { return std::errc::io_error; } - if (token != "patches") { + if (token != "patches" || m_patch_count > PATCH_COUNT_LIMIT) { + m_patch_count = 0; return std::errc::argument_out_of_domain; } } @@ -80,6 +81,23 @@ bittable_serialize::read_grid_header(std::istream* in) { return err; } + if (m_type == bittable_type::PATCH) { + // get patch number + parser par(line); + if (!par.next(token).next(m_patch_num).eof()) + { + return std::errc::io_error; + } + if (token != "patch") { + return std::errc::argument_out_of_domain; + } + + std::tie(line, err) = readline(in); + if(err != std::errc{}) + { + return err; + } + } { parser par(line); if (!par.next(token).next(m_dim.height).eof()) @@ -128,11 +146,14 @@ bittable_serialize::read_grid_header(std::istream* in) std::errc bittable_serialize::read_grid_raw( - std::vector& raw_data, std::istream* in) + std::span buffer, std::istream* in) { const memory::bittable_dimension read_dim = m_dim; - raw_data.resize(read_dim.width * read_dim.height); - char* data_at = raw_data.data(); + if (buffer.size() < (uint64_t)read_dim.width * (uint64_t)read_dim.height) + { + return std::errc::result_out_of_range; + } + char* data_at = buffer.data(); std::string_view token; std::errc err; From 841f284a2380a5a36f80d9e541eb89da542847ea Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 30 Apr 2026 23:47:15 +1000 Subject: [PATCH 48/61] dynamic grid progress --- include/warthog/domain/gridmap.h | 25 ++++++++++- include/warthog/manager/grid_patch_set.h | 37 ++++++++++++++-- include/warthog/manager/scenario_runner.h | 34 ++++++++++++++- include/warthog/memory/bittable.h | 51 ++++++++++++++++------- include/warthog/util/gm_parser.h | 24 ++++------- include/warthog/util/scenario_manager.h | 18 ++++++++ src/CMakeLists.txt | 2 + src/domain/gridmap.cpp | 39 ++++++++++++++++- src/manager/scenario_runner.cpp | 36 +++++++++++++++- 9 files changed, 226 insertions(+), 40 deletions(-) diff --git a/include/warthog/domain/gridmap.h b/include/warthog/domain/gridmap.h index f8721de..dcdd5e3 100644 --- a/include/warthog/domain/gridmap.h +++ b/include/warthog/domain/gridmap.h @@ -47,6 +47,7 @@ class gridmap : public memory::bittable public: using bittable = gridmap::bittable; // inform of type existing using bitarray = gridmap::bitarray; // inform of type existing + gridmap(); gridmap(uint32_t height, uint32_t width); gridmap(std::istream& input); gridmap(io::bittable_serialize& parser); @@ -63,6 +64,13 @@ class gridmap : public memory::bittable operator=(const gridmap&) = delete; + void setup(uint32_t height, uint32_t width); + void load(std::istream& input); + void load(io::bittable_serialize& parser); + void load(std::filesystem::path&& filename); + void load(const std::filesystem::path& filename); + void load(const char* filename); + // here we convert from the coordinate space of // the original grid to the coordinate space of db_. pad_id @@ -106,6 +114,14 @@ class gridmap : public memory::bittable assert(x < header_.width_ && y < header_.height_); } + void + to_unpadded_xy_from_padded(uint32_t padded_x, uint32_t padded_y, uint32_t& x, uint32_t& y) const noexcept + { + y = padded_y - PADDED_ROWS; + x = padded_x; + assert(x < header_.width_ && y < header_.height_); + } + void to_padded_xy(pad_id grid_id, uint32_t& x, uint32_t& y) const noexcept { @@ -114,6 +130,14 @@ class gridmap : public memory::bittable assert(x < width() && y < height()); } + void + to_padded_xy_from_unpadded(uint32_t unpadded_x, uint32_t unpadded_y, uint32_t& x, uint32_t& y) const noexcept + { + y = unpadded_y + PADDED_ROWS; + x = unpadded_x; + assert(x < width() && y < height()); + } + pack_id to_unpadded_id(pad_id grid_id) const noexcept { @@ -356,7 +380,6 @@ class gridmap : public memory::bittable } protected: - using bittable::setup; void setup_stream_(std::istream& in); void setup_ser_(io::bittable_serialize& parser); diff --git a/include/warthog/manager/grid_patch_set.h b/include/warthog/manager/grid_patch_set.h index 12de239..c6b211b 100644 --- a/include/warthog/manager/grid_patch_set.h +++ b/include/warthog/manager/grid_patch_set.h @@ -25,13 +25,13 @@ class grid_patch_set { public: using bittable = domain::gridmap::bittable; - static constexpr uint32_t npos = (uint32_t)-1u; + static constexpr uint16_t npos = (uint16_t)-1u; grid_patch_set(std::pmr::memory_resource* upstream = nullptr) : grid_res_(1024*4, upstream) { } - bool push_copy(bittable table, uint32_t offset_x = 0, uint32_t offset_y, - uint32_t width = npos, uint32_t height = npos) + bool push_copy(bittable table, uint16_t offset_x = 0, uint16_t offset_y = 0, + uint16_t width = npos, uint16_t height = npos) { if (table.size() == 0) { return false; @@ -53,7 +53,36 @@ class grid_patch_set { return false; } - table.setup() + auto size = bittable::calc_array_size(width, height); + auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); + patch = bittable(grid_data, table.width(), table.height()); + table.copy(patch, pad_id::zero(), table.xy_to_id(offset_x, offset_y), width, height); + } + patches_.push_back(patch); + } + bool push_ref(bittable patch) + { + patches_.push_back(patch); + return true; + } + + void reset() + { + patches_.clear(); + grid_res_.release(); + } + + size_t size() const noexcept + { + return patches_.size(); + } + bittable get_patch(size_t i) const + { + return patches_.at(i); + } + std::span get_patches() const noexcept + { + return patches_; } protected: diff --git a/include/warthog/manager/scenario_runner.h b/include/warthog/manager/scenario_runner.h index d284ebe..f450581 100644 --- a/include/warthog/manager/scenario_runner.h +++ b/include/warthog/manager/scenario_runner.h @@ -10,10 +10,19 @@ // #include "scenario_manager.h" +#include "grid_patch_set.h" +#include namespace warthog::manager { +struct patch_loc +{ + uint32_t patch_id; + uint16_t topleft_x; + uint16_t topleft_y; +}; + class scenario_runner { public: @@ -79,7 +88,7 @@ class scenario_runner bool complete() const noexcept { return command_at_ >= command_size_; } - std::span + std::span get_patches() const noexcept { return patches_; @@ -91,9 +100,30 @@ class scenario_runner return 0; } + /// @brief setup grid and contain snapshot 0 + /// @param grid the gridmap to setup + /// @param patch_set the patch set to + /// @param setup_grid + /// @return true if operation is successful + bool gridmap_init(domain::gridmap& grid, const grid_patch_set& patch_set, bool setup_grid = true); + + /// @brief apply patches from patch_set in order by get_patches() + /// @param grid grid to apply to + /// @param patch_set patch_set to pull patch data from + /// @return -1 if successful, the failed patch application otherwise + int gridmap_apply_patches(domain::gridmap& grid, const grid_patch_set& patch_set); + + /// @brief apply a single patch to a gridmap + /// @param grid grid to apply to + /// @param patch patch bittable to copy + /// @param padded_x the padded topleft on grid + /// @param padded_y the padded topleft on grid + /// @return true if apply is successful, false otherwise + bool girdmap_apply_patch(domain::gridmap& grid, domain::gridmap::bittable patch, uint32_t padded_x, uint32_t padded_y); + protected: const scenario_manager* scenario_ = nullptr; - std::vector patches_; + std::vector patches_; std::vector experiments_; uint32_t command_at_ = 0; ///< command at, used for dynamic scenario uint32_t command_size_ = 0; diff --git a/include/warthog/memory/bittable.h b/include/warthog/memory/bittable.h index ece4a20..9673a1c 100644 --- a/include/warthog/memory/bittable.h +++ b/include/warthog/memory/bittable.h @@ -1,12 +1,13 @@ #ifndef WARTHOG_MEMORY_BITTABLE_H #define WARTHOG_MEMORY_BITTABLE_H +#include + #include #include #include #include #include -#include namespace warthog::memory { @@ -53,8 +54,7 @@ struct bittable_span_type /** * IdType: should be Identity, although integer is allowed. - * ValueBits: size of bits stored for each value. Must be power of 2 and - * smaller than BaseType. + * ValueBits: size of bits stored for each value. Must be power of 2 and <=8 * * bittable does not own its own data. * copy results in sharing underlying table data. @@ -65,7 +65,7 @@ struct bitarray { public: static_assert( - ValueBits <= sizeof(BaseType) * CHAR_BIT + ValueBits <= CHAR_BIT && std::popcount(ValueBits) == 1, "ValueBits must be to power of 2 and fit inside BaseType bits."); constexpr static size_t value_bits = ValueBits; @@ -191,8 +191,8 @@ struct bitarray // return id position split // `m_data[first] >> second` will return the value as position id - constexpr std::pair - id_split(id_type id) const noexcept + constexpr static std::pair + id_split(id_type id) noexcept { id_value_type idval = id_value_type{id} << value_bit_width; return { @@ -238,18 +238,23 @@ struct bitarray } } - void copy(void* dest, uint8_t dest_bit, id_type pos = id_type(0), size_t count) + void copy_p(void* dest, uint8_t dest_bit, size_t count, id_type pos = id_type(0)) { - assert(dest_bit < CHAR_BIT); + // TODO: faster copy + assert(dest_bit < CHAR_BIT && (dest_bit & (ValueBits-1)) == 0); // primative implemtation - bitarray - id_value_type p = pos; + bitarray dest_t(static_cast(dest)); + id_value_type p(pos); + id_value_type d = dest_bit; while (count > 0) { - bool c = get(id_type(pos++)); - pos - std::byte + dest_t.set(typename decltype(dest_t)::id_type(d++), get(id_type(p++))); } } + void copy(bitarray dest, id_type dest_pos, size_t count, id_type pos = id_type(0)) + { + auto [byte, bit] = id_split(dest_pos); + copy_p(dest.data() + byte, bit, count, pos); + } /// /// struct data @@ -272,6 +277,7 @@ struct bittable : bitarray public: using typename bittable::bitarray::id_type; using typename bittable::bitarray::value_type; + using typename bittable::bitarray::id_value_type; static constexpr size_t calc_array_size(uint32_t width, uint32_t height) noexcept @@ -321,7 +327,7 @@ struct bittable : bitarray { assert(m_dim.width != 0); const auto value - = static_cast(id); + = static_cast(id); return { static_cast(value % m_dim.width), static_cast(value / m_dim.width)}; @@ -438,6 +444,23 @@ struct bittable : bitarray return bittable::bitarray::id_split(id); } + void copy(bittable dest, id_type dest_pos, id_type pos, uint32_t width, uint32_t height) + { + // TODO: faster copy + if (width == 0 || height == 0) + return; + + id_value_type d(dest_pos); + id_value_type dd = dest.m_dim.width; + id_value_type p(pos); + id_value_type pd = m_dim.width; + do { + bittable::bitarray::copy(dest, id_type(d), width, id_type(p)); + d += dd; + p += pd; + } while (--height != 0); + } + /// /// struct data /// diff --git a/include/warthog/util/gm_parser.h b/include/warthog/util/gm_parser.h index f96207b..613fdc6 100644 --- a/include/warthog/util/gm_parser.h +++ b/include/warthog/util/gm_parser.h @@ -9,6 +9,7 @@ // @created: 08/08/2012 // +#include #include #include #include @@ -20,31 +21,20 @@ namespace warthog::util class gm_header { public: - gm_header(unsigned int height, unsigned int width, const char* type) + gm_header(uint32_t height, uint32_t width, const char* type) : height_(height), width_(width), type_(type) { } - gm_header() : height_(0), width_(0), type_("") { } + gm_header() = default; - gm_header(const gm_header& other) { (*this) = other; } + ~gm_header() { } - virtual ~gm_header() { } - - gm_header& - operator=(const gm_header& other) - { - this->height_ = other.height_; - this->width_ = other.width_; - this->type_ = other.type_; - return *this; - } - - unsigned int height_; - unsigned int width_; + uint32_t height_; + uint32_t width_; std::string type_; }; -class gm_parser +class [[deprecated]] gm_parser { public: gm_parser(const char* filename); diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index e7cc77d..2b05613 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -174,6 +174,24 @@ class scenario_manager bool is_static_scenario() const noexcept { return static_scenario_start_ >= 0; } int32_t get_static_scenario_start() const noexcept { return static_scenario_start_; } + uint32_t get_scenario_width() const noexcept + { + return scenario_width_; + } + void set_scenario_width(uint32_t width) noexcept + { + scenario_width_ = width; + } + + uint32_t get_scenario_height() const noexcept + { + return scenario_height_; + } + void set_scenario_height(uint32_t height) noexcept + { + scenario_height_ = height; + } + protected: std::errc load_gppc_scenario(std::istream& scenfile); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fe46f48..d9ddb56 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,8 @@ io/log.cpp io/scenario.cpp io/serialize_base.cpp +manager/scenario_runner.cpp + memory/node_pool.cpp search/expansion_policy.cpp diff --git a/src/domain/gridmap.cpp b/src/domain/gridmap.cpp index 707b027..0d0ed3d 100644 --- a/src/domain/gridmap.cpp +++ b/src/domain/gridmap.cpp @@ -10,7 +10,7 @@ namespace warthog::domain { -gridmap::gridmap(unsigned int h, unsigned int w) : header_(h, w, "octile") +gridmap::gridmap(uint32_t h, uint32_t w) : header_(h, w, "octile") { this->init_db(); } @@ -20,6 +20,11 @@ gridmap::gridmap(std::istream& input) setup_stream_(input); } +gridmap::gridmap(io::bittable_serialize& parser) +{ + setup_ser_(parser); +} + gridmap::gridmap(std::filesystem::path&& filename) { filename_ = std::move(filename); @@ -33,6 +38,38 @@ gridmap::gridmap(const std::filesystem::path& filename) : gridmap(std::filesyste gridmap::gridmap(const char* filename) : gridmap(std::filesystem::path(filename)) { } +void gridmap::setup(uint32_t h, uint32_t w) +{ + header_.height_ = h; + header_.width_ = w; + this->init_db(); +} + +void gridmap::load(std::istream& input) +{ + setup_stream_(input); +} +void gridmap::load(io::bittable_serialize& parser) +{ + setup_ser_(parser); +} +void gridmap::load(std::filesystem::path&& filename) +{ + filename_ = std::move(filename); + std::ifstream in(filename_); + setup_stream_(in); +} + +void gridmap::load(const std::filesystem::path& filename) +{ + load(std::filesystem::path(filename)); +} + +void gridmap::load(const char* filename) +{ + load(std::filesystem::path(filename)); +} + void gridmap::setup_stream_(std::istream& in) { diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index 8690153..aee2c8b 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -112,7 +112,7 @@ scenario_runner::snapshot_patches(bool clear_patch) if (auto cmd = commands[command_at_]; cmd.bucket == util::scenario_command::PATCH) { command_at_ += 1; count += 1; - patches_.push_back(cmd.id); + patches_.push_back({cmd.id, cmd.cmd.patch.topleft_x, cmd.cmd.patch.topleft_y}); } } // end at first non-PATCH command @@ -161,4 +161,38 @@ scenario_runner::snapshot_query_all() return experiments_; } +bool scenario_runner::gridmap_init(domain::gridmap& grid, const grid_patch_set& patch_set, bool setup_grid) +{ + if (setup_grid) { + grid.setup(scenario_->get_scenario_height(), scenario_->get_scenario_width()); + } + restart(); + snapshot_patches(); + return gridmap_apply_patches(grid, patch_set) < 0; +} +int scenario_runner::gridmap_apply_patches(domain::gridmap& grid, const grid_patch_set& patch_set) +{ + int count = 0; + for (auto& P : patches_) { + uint32_t x, y; + grid.to_padded_xy_from_unpadded(P.topleft_x, P.topleft_y, x, y); + if (girdmap_apply_patch(grid, patch_set.get_patch(P.patch_id), x, y)) { + count++; + } else { + return count; + } + } + return -1; +} +bool scenario_runner::girdmap_apply_patch(domain::gridmap& grid, domain::gridmap::bittable patch, uint32_t padded_x, uint32_t padded_y) +{ + if ((uint64_t)padded_x + patch.width() >= (uint64_t)grid.width() || + (uint64_t)padded_y + patch.height() >= (uint64_t)grid.height()) + return false; + + // apply patch + patch.copy(grid, grid.to_padded_id_from_padded(padded_x, padded_y), pad_id::zero(), patch.width(), patch.height()); + return true; +} + } // namespace warthog::util From 409b2367c88d2cd72abda614e6dc97121b7b61ea Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 1 May 2026 15:57:56 +1000 Subject: [PATCH 49/61] new scenario runner, static working, dynamic not running correctly --- apps/warthog.cpp | 121 ++++++++++++------ include/warthog/io/fwd.h | 43 +++++++ include/warthog/io/grid.h | 31 ++++- include/warthog/io/grid_trace.h | 1 + include/warthog/io/scenario.h | 16 --- include/warthog/io/serialize_base.h | 2 + include/warthog/io/stream_observer.h | 2 + include/warthog/manager/grid_patch_set.h | 71 +++++------ include/warthog/manager/scenario_runner.h | 7 +- include/warthog/memory/bittable.h | 2 +- include/warthog/util/scenario_manager.h | 6 +- include/warthog/util/string.h | 2 +- src/CMakeLists.txt | 1 + src/domain/gridmap.cpp | 2 + src/io/grid.cpp | 15 ++- src/io/scenario.cpp | 2 +- src/manager/grid_patch_set.cpp | 145 ++++++++++++++++++++++ src/manager/scenario_runner.cpp | 27 ++-- 18 files changed, 375 insertions(+), 121 deletions(-) create mode 100644 include/warthog/io/fwd.h create mode 100644 src/manager/grid_patch_set.cpp diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 1fa4322..f30f0c6 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -93,7 +95,7 @@ help(std::ostream& out) bool check_optimality( - warthog::search::solution& sol, warthog::util::experiment* exp) + const warthog::search::solution& sol, const warthog::util::experiment* exp) { uint32_t precision = 2; double epsilon = (1.0 / (int)pow(10, precision)) / 2; @@ -129,11 +131,32 @@ check_optimality( #define WARTHOG_POSTHOC_DO(f) #endif +struct gridmap_scenario +{ + const warthog::manager::scenario_manager* mgr; + warthog::manager::scenario_runner run; + warthog::domain::gridmap grid; + warthog::manager::grid_patch_set patches; + + gridmap_scenario(const warthog::manager::scenario_manager& scen) + : mgr(&scen), run(&scen) + { } + + bool load_map(const std::filesystem::path map) + { + if (!patches.load(map)) { + return false; + } + return run.gridmap_init(grid, patches); + } +}; + template int run_experiments( Search& algo, std::string alg_name, - warthog::util::scenario_manager& scenmgr, bool verbose, bool checkopt, + gridmap_scenario& scen, + bool verbose, bool checkopt, std::ostream& out) { WARTHOG_GINFO_FMT("start search with algorithm {}", alg_name); @@ -144,29 +167,43 @@ run_experiments( out << "id\talg\texpanded\tgenerated\treopen\tsurplus\theapops" << "\tnanos\tplen\tpcost\tscost\tmap\n"; - for(uint32_t i = filter_id >= 0 ? static_cast(filter_id) : 0, - ie = filter_id >= 0 - ? i + 1 - : static_cast(scenmgr.num_experiments()); - i < ie; i++) + + for (uint32_t i = 0; ; ++i) { #ifdef WARTHOG_POSTHOC std::optional trace_stream; // open and pass to trace if used - if constexpr(std::same_as< - listener_type, - std::remove_cvref_t>) - { - if(i == filter_id && !trace_file.empty()) + +#endif + auto [exp, patch_count] = scen.run.experiment_next(); + if (exp == nullptr) { + break; + } + if (patch_count != 0) { + if (scen.run.gridmap_apply_patches(scen.grid, scen.patches) < 0) { + // failed to apply patches, exit + WARTHOG_GCRIT("dynamic patch error: failed to apply patches"); + return 5; + } + } + + if (filter_id >= 0 && i == filter_id) { + // trace + if constexpr(std::same_as< + listener_type, + std::remove_cvref_t>) { - listener_grid& l - = std::get(algo.get_listeners()); - trace_stream.emplace(trace_file); - l.open(*trace_stream); + if(!trace_file.empty()) + { + listener_grid& l + = std::get(algo.get_listeners()); + trace_stream.emplace(trace_file); + l.open(*trace_stream); + } } + } else if (filter_id >= 0) { + continue; } -#endif - warthog::util::experiment* exp = scenmgr.get_experiment(i); warthog::pack_id startid = expander->get_pack(exp->startx(), exp->starty()); @@ -197,7 +234,7 @@ run_experiments( << sol.met_.time_elapsed_nano_.count() << "\t" << (!sol.path_.empty() ? sol.path_.size() - 1 : 0) << "\t" << sol.sum_of_edge_costs_ << "\t" << exp->distance() << "\t" - << scenmgr.last_file_loaded() << std::endl; + << scen.mgr->last_file_loaded() << std::endl; if(checkopt) { @@ -210,7 +247,7 @@ run_experiments( } WARTHOG_GINFO_FMT( - "search complete; total memory: {}", algo.mem() + scenmgr.mem()); + "search complete; total memory: {}", algo.mem() + scen.mgr->mem()); return 0; } @@ -219,16 +256,20 @@ run_astar( warthog::util::scenario_manager& scenmgr, std::string mapname, std::string alg_name) { - warthog::domain::gridmap map(mapname.c_str()); - warthog::search::gridmap_expansion_policy expander(&map); - warthog::heuristic::octile_heuristic heuristic(map.width(), map.height()); + gridmap_scenario scen(scenmgr); + if (!scen.load_map(std::filesystem::path(mapname))) { + WARTHOG_GCRIT("failed to load map"); + return 3; + } + warthog::search::gridmap_expansion_policy expander(&scen.grid); + warthog::heuristic::octile_heuristic heuristic(scen.grid.width(), scen.grid.height()); warthog::util::pqueue_min open; warthog::search::unidirectional_search astar( - &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&map))); + &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); int ret = run_experiments( - astar, alg_name, scenmgr, verbose, checkopt, std::cout); + astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } @@ -237,17 +278,21 @@ run_astar4c( warthog::util::scenario_manager& scenmgr, std::string mapname, std::string alg_name) { - warthog::domain::gridmap map(mapname.c_str()); - warthog::search::gridmap_expansion_policy expander(&map, true); + gridmap_scenario scen(scenmgr); + if (!scen.load_map(std::filesystem::path(mapname))) { + WARTHOG_GCRIT("failed to load map"); + return 3; + } + warthog::search::gridmap_expansion_policy expander(&scen.grid, true); warthog::heuristic::manhattan_heuristic heuristic( - map.width(), map.height()); + scen.grid.width(), scen.grid.height()); warthog::util::pqueue_min open; warthog::search::unidirectional_search astar( - &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&map))); + &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); int ret = run_experiments( - astar, alg_name, scenmgr, verbose, checkopt, std::cout); + astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } @@ -256,16 +301,20 @@ run_dijkstra( warthog::util::scenario_manager& scenmgr, std::string mapname, std::string alg_name) { - warthog::domain::gridmap map(mapname.c_str()); - warthog::search::gridmap_expansion_policy expander(&map); + gridmap_scenario scen(scenmgr); + if (!scen.load_map(std::filesystem::path(mapname))) { + WARTHOG_GCRIT("failed to load map"); + return 3; + } + warthog::search::gridmap_expansion_policy expander(&scen.grid); warthog::heuristic::zero_heuristic heuristic; warthog::util::pqueue_min open; warthog::search::unidirectional_search astar( - &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&map))); + &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); int ret = run_experiments( - astar, alg_name, scenmgr, verbose, checkopt, std::cout); + astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } @@ -274,6 +323,8 @@ run_wgm_astar( warthog::util::scenario_manager& scenmgr, std::string mapname, std::string alg_name, std::string costfile) { + gridmap_scenario scen(scenmgr); + // do not load map here warthog::util::cost_table costs(costfile.c_str()); warthog::domain::vl_gridmap map(mapname.c_str()); warthog::search::vl_gridmap_expansion_policy expander(&map, costs); @@ -292,7 +343,7 @@ run_wgm_astar( warthog::search::unidirectional_search astar(&heuristic, &expander, &open); int ret = run_experiments( - astar, alg_name, scenmgr, verbose, checkopt, std::cout); + astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } diff --git a/include/warthog/io/fwd.h b/include/warthog/io/fwd.h new file mode 100644 index 0000000..4e396d5 --- /dev/null +++ b/include/warthog/io/fwd.h @@ -0,0 +1,43 @@ +#ifndef WARTHOG_IO_FWD_H +#define WARTHOG_IO_FWD_H + +// io/fwd.h +// +// Forward class definitions and store global enums. +// +// @author: Ryan Hechenberger +// @created: 2026-05-01 +// + +#include + +namespace warthog::io +{ + +// serializers +class serialize_base; +class scenario_serialize; + +// observers +class stream_observer; +class posthoc_trace; + +enum class scenario_version : uint8_t +{ + UNKNOWN, + VERSION_1, + VERSION_2, +}; +enum class cost_type : uint8_t +{ + G_8C_NCC, + G_8C_CC, + G_4C, + AA_NCC, + AA_CC, + OTHER, +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_FWD_H diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index 19c02b3..4541142 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -12,6 +12,7 @@ // #include "serialize_base.h" + #include #include @@ -109,17 +110,34 @@ class bittable_serialize : public serialize_base m_type = type; } - uint32_t get_patch_count() const noexcept + /// @brief get the number of patches in file + uint32_t get_patch_amount() const noexcept { - return m_patch_count; + return m_patch_amount; } - void set_patch_count(uint32_t count) + /// @brief set the number of patches (for writing) + void set_patch_amount(uint32_t count) { if(count > PATCH_COUNT_LIMIT) { throw std::out_of_range("count"); } - m_patch_count = count; + m_patch_amount = count; + } + /// @brief gets the number of patches that have been read/write + uint32_t get_patch_count() const noexcept + { + return m_patch_count; + } + /// @brief gets the id of last patch (usually get_patch_count()) + uint32_t get_patch_id() const noexcept + { + return m_patch_id; + } + /// @brief gets the number of patches that have been read + void set_patch_id(uint32_t id) noexcept + { + m_patch_id = id; } /// @brief Reads the map/patch file header, getting the type @@ -168,9 +186,10 @@ class bittable_serialize : public serialize_base protected: memory::bittable_dimension m_dim = {}; - bittable_type m_type = bittable_type::OCTILE; + bittable_type m_type = bittable_type::NONE; + uint32_t m_patch_amount = 0; uint32_t m_patch_count = 0; - uint32_t m_patch_num = 0; + uint32_t m_patch_id = 0; }; template diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index 6a76ff0..15cc1b6 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -10,6 +10,7 @@ // #include "posthoc_trace.h" + #include #include #include diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 96f8780..717e349 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -36,22 +36,6 @@ namespace warthog::io { -enum class scenario_version : uint8_t -{ - UNKNOWN, - VERSION_1, - VERSION_2, -}; -enum class cost_type : uint8_t -{ - G_8C_NCC, - G_8C_CC, - G_4C, - AA_NCC, - AA_CC, - OTHER, -}; - struct scenario_query { int64_t bucket; diff --git a/include/warthog/io/serialize_base.h b/include/warthog/io/serialize_base.h index def0bb3..3cb7263 100644 --- a/include/warthog/io/serialize_base.h +++ b/include/warthog/io/serialize_base.h @@ -12,6 +12,8 @@ // @created: 2026-04-10 // +#include "fwd.h" + #include #include diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index 33678e8..2f0fdb5 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -17,6 +17,8 @@ // @created: 2025-08-01 // +#include "fwd.h" + #include "log.h" #include #include diff --git a/include/warthog/manager/grid_patch_set.h b/include/warthog/manager/grid_patch_set.h index c6b211b..f5fab22 100644 --- a/include/warthog/manager/grid_patch_set.h +++ b/include/warthog/manager/grid_patch_set.h @@ -11,9 +11,11 @@ #include #include +#include #include #include +#include namespace warthog::manager { @@ -24,53 +26,42 @@ namespace warthog::manager class grid_patch_set { public: + enum flags : uint32_t { + DEFAULT = 0u, ///< default options + SKIP_HEADER = 1u << 0, ///< will not read header + FORCE_HEADER = 1u << 1, ///< must read header + IGNORE_INDEX = 1u << 2, ///< ignores patch read index + }; using bittable = domain::gridmap::bittable; static constexpr uint16_t npos = (uint16_t)-1u; grid_patch_set(std::pmr::memory_resource* upstream = nullptr) : - grid_res_(1024*4, upstream) + grid_res_(upstream != nullptr ? upstream : std::pmr::get_default_resource()) { } + bool load(std::istream& file); + bool load(const std::filesystem::path& maps); + + /// @brief reads from a serialize, checking for errors. Can read both type octile and patch files. + /// @param S the bittable_serialize, must be open + /// @param max_grids the max grids to read, or -1 for no limit + /// @param flags flags that control how to read, check enum flags + /// @return the number of grids read successfully, or <0 for negative index of patch that failed to be read successfully + /// + /// This function will load from an established bittable_serialize, reading all grids and appending to existing patches. + /// If type octile, only one grid is read as the first patch, otherwise up to max_grids (default all) are read. + /// + /// By DEFAULT, it will read the header if not already read and append all remaining grids to existing patches, but will error if the index in the file does not match the index in this class. + /// SKIP_HEADER requires that S have already read the header else errors. + /// FORCE_HEADER requires that S have not read the header else errors. + /// IGNORE_INDEX will not error if index in patch file does not match. + int deserialize(io::bittable_serialize& S, int max_grids = -1, uint32_t flags = DEFAULT); + bool push_copy(bittable table, uint16_t offset_x = 0, uint16_t offset_y = 0, - uint16_t width = npos, uint16_t height = npos) - { - if (table.size() == 0) { - return false; - } - bittable patch; - if (offset_x == 0 && offset_y == 0 && width == npos && height == npos) { - // copy as is - auto size = table.size_bytes(); - auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); - std::memcpy(grid_data, table.data(), size); - patch = bittable(grid_data, table.width(), table.height()); - } else { - // crop - if (offset_x >= table.width() || width > table.width() || offset_x + width > table.width()) - { - return false; - } - if (offset_y >= table.height() || height > table.height() || offset_y + height > table.height()) - { - return false; - } - auto size = bittable::calc_array_size(width, height); - auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); - patch = bittable(grid_data, table.width(), table.height()); - table.copy(patch, pad_id::zero(), table.xy_to_id(offset_x, offset_y), width, height); - } - patches_.push_back(patch); - } - bool push_ref(bittable patch) - { - patches_.push_back(patch); - return true; - } + uint16_t width = npos, uint16_t height = npos); + + bool push_ref(bittable patch); - void reset() - { - patches_.clear(); - grid_res_.release(); - } + void reset(); size_t size() const noexcept { diff --git a/include/warthog/manager/scenario_runner.h b/include/warthog/manager/scenario_runner.h index f450581..9d123e9 100644 --- a/include/warthog/manager/scenario_runner.h +++ b/include/warthog/manager/scenario_runner.h @@ -86,7 +86,7 @@ class scenario_runner std::span snapshot_query_all(); - bool complete() const noexcept { return command_at_ >= command_size_; } + bool complete() const noexcept { return command_at_ >= scenario_->get_commands().size(); } std::span get_patches() const noexcept @@ -110,7 +110,7 @@ class scenario_runner /// @brief apply patches from patch_set in order by get_patches() /// @param grid grid to apply to /// @param patch_set patch_set to pull patch data from - /// @return -1 if successful, the failed patch application otherwise + /// @return >=0 successful patches applied, <0 failed to apply negative id (from 1) int gridmap_apply_patches(domain::gridmap& grid, const grid_patch_set& patch_set); /// @brief apply a single patch to a gridmap @@ -126,8 +126,7 @@ class scenario_runner std::vector patches_; std::vector experiments_; uint32_t command_at_ = 0; ///< command at, used for dynamic scenario - uint32_t command_size_ = 0; - int32_t experiment_at_ = 0; + int32_t experiment_at_ = -1; int32_t snapshot_at_ = -1; }; diff --git a/include/warthog/memory/bittable.h b/include/warthog/memory/bittable.h index 9673a1c..2cbe33a 100644 --- a/include/warthog/memory/bittable.h +++ b/include/warthog/memory/bittable.h @@ -246,7 +246,7 @@ struct bitarray bitarray dest_t(static_cast(dest)); id_value_type p(pos); id_value_type d = dest_bit; - while (count > 0) { + while (count-- > 0) { dest_t.set(typename decltype(dest_t)::id_type(d++), get(id_type(p++))); } } diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index 2b05613..bfddc52 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -133,17 +133,17 @@ class scenario_manager } std::string - last_file_loaded() noexcept + last_file_loaded() const noexcept { return sfile_.string(); } const std::filesystem::path& - scenario_filename() noexcept + scenario_filename() const noexcept { return sfile_; } const std::filesystem::path& - map_filename() noexcept + map_filename() const noexcept { return mfile_; } diff --git a/include/warthog/util/string.h b/include/warthog/util/string.h index a0cde10..3a60200 100644 --- a/include/warthog/util/string.h +++ b/include/warthog/util/string.h @@ -93,7 +93,7 @@ inline token_return get_token_quoted(std::string_view str, char quote = '"', cha } } else { // no delimiter, read as normal - auto endsp = std::find_if(str.begin(), str.end(), [](char c) { return isspace((unsigned char)c); }) - str.begin(); + auto endsp = std::find_if(fstr.begin(), fstr.end(), [](char c) { return isspace((unsigned char)c); }) - fstr.begin(); ret.token = fstr.substr(0, endsp); } return ret; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d9ddb56..a5f752a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,7 @@ io/log.cpp io/scenario.cpp io/serialize_base.cpp +manager/grid_patch_set.cpp manager/scenario_runner.cpp memory/node_pool.cpp diff --git a/src/domain/gridmap.cpp b/src/domain/gridmap.cpp index 0d0ed3d..2a16e9d 100644 --- a/src/domain/gridmap.cpp +++ b/src/domain/gridmap.cpp @@ -10,6 +10,8 @@ namespace warthog::domain { +gridmap::gridmap() = default; + gridmap::gridmap(uint32_t h, uint32_t w) : header_(h, w, "octile") { this->init_db(); diff --git a/src/io/grid.cpp b/src/io/grid.cpp index 638c5c9..24e5c55 100644 --- a/src/io/grid.cpp +++ b/src/io/grid.cpp @@ -43,6 +43,9 @@ bittable_serialize::read_header(std::istream* in) m_type = detected_type; // read patch header before map header + if (detected_type == bittable_type::OCTILE) { + m_patch_amount = 1; + } else if(detected_type == bittable_type::PATCH) { std::tie(line, err) = readline(in); @@ -51,14 +54,16 @@ bittable_serialize::read_header(std::istream* in) return err; } parser par(line); - if (!par.next(token).next(m_patch_count).eof()) { + if (!par.next(token).next(m_patch_amount).eof()) { return std::errc::io_error; } - if (token != "patches" || m_patch_count > PATCH_COUNT_LIMIT) { - m_patch_count = 0; + if (token != "patches" || m_patch_amount > PATCH_COUNT_LIMIT) { + m_patch_amount = 0; return std::errc::argument_out_of_domain; } } + m_patch_count = 0; + m_patch_id = 0; return std::errc{}; } @@ -75,6 +80,8 @@ bittable_serialize::read_grid_header(std::istream* in) std::string_view line; std::string_view token; + m_patch_count += 1; + // read height std::tie(line, err) = readline(in); if(err != std::errc{}) @@ -84,7 +91,7 @@ bittable_serialize::read_grid_header(std::istream* in) if (m_type == bittable_type::PATCH) { // get patch number parser par(line); - if (!par.next(token).next(m_patch_num).eof()) + if (!par.next(token).next(m_patch_id).eof()) { return std::errc::io_error; } diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 80f6a8b..487b209 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -579,7 +579,7 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) std::pair scenario_serialize::read_patch_line(scenario_patch& patch, std::istream* in) { - if(!can_read(in) || m_state != serialize_state::VERSION) + if(!can_read(in) || m_state != serialize_state::COMMAND) { return {0, std::errc::state_not_recoverable}; } diff --git a/src/manager/grid_patch_set.cpp b/src/manager/grid_patch_set.cpp new file mode 100644 index 0000000..0ec3563 --- /dev/null +++ b/src/manager/grid_patch_set.cpp @@ -0,0 +1,145 @@ +#include + +#include +#include + +#include + +namespace warthog::manager +{ + +bool grid_patch_set::load(std::istream& file) +{ + io::bittable_serialize S; + if (auto ec = S.open_read(&file); ec != std::errc{}) { + WARTHOG_GWARN_FMT("grid patch failed to open file code={}", (int)ec); + return false; + } + if (int r = deserialize(S); r < 0) { + WARTHOG_GWARN_FMT("grid patch failed to read patch {}", -r - 1); + return false; + } + return true; +} + +bool grid_patch_set::load(const std::filesystem::path& maps) +{ + io::bittable_serialize S; + S.set_filename(std::filesystem::path(maps)); + if (auto ec = S.open_read(); ec != std::errc{}) { + WARTHOG_GWARN_FMT("grid patch failed to open file code={}", (int)ec); + return false; + } + if (int r = deserialize(S); r < 0) { + WARTHOG_GWARN_FMT("grid patch failed to read patch {}", -r - 1); + return false; + } + return true; +} + +int grid_patch_set::deserialize(io::bittable_serialize& S, int max_grids, uint32_t flags) +{ + // read header + if (S.get_type() == io::bittable_type::NONE) { + // header not read + if ((flags & SKIP_HEADER) != 0) { + WARTHOG_GWARN("grid patch SKIP_HEADER for serializer when header has not been read."); + return -1; + } + if (auto ec = S.read_header(); ec != std::errc{}) { + WARTHOG_GWARN_FMT("grid patch failed to read header code={}", (int)ec); + return -1; + } + } else { + // header has been read, check + if ((flags & FORCE_HEADER) != 0) { + WARTHOG_GWARN("grid patch FORCE_HEADER for serializer when header has already been read."); + return -1; + } + } + auto type = S.get_type(); + if (type != io::bittable_type::OCTILE && type != io::bittable_type::PATCH) { + WARTHOG_GWARN("grid patch only supports reading type octile or patch."); + return -1; + } + + // start reading grids + int count = 0; + if (max_grids < 0) { + max_grids = std::numeric_limits::max(); + } + while (count < max_grids && S.get_patch_count() < S.get_patch_amount()) { + // read new grid + ++count; + if (auto ec = S.read_grid_header(); ec != std::errc{}) { + WARTHOG_GWARN_FMT("grid patch failed to read patch {} header code={}", count, (int)ec); + return -count; + } + if ((flags & IGNORE_INDEX) == 0) { + // check index + if (S.get_patch_id() != patches_.size()) { + // index mismatch + WARTHOG_GWARN_FMT("grid patch failed patch {} index mismatch of {} expected {}", count, S.get_patch_id(), patches_.size()); + return -count; + } + } + // setup patch data + auto dim = S.get_dim(); + auto bytes = bittable::calc_array_size(dim.width, dim.height); + auto* grid_data = static_cast(grid_res_.allocate(bytes, alignof(bittable::value_type))); + bittable patch(grid_data, dim.width, dim.height); + if (auto ec = S.read_grid_data(patch); ec != std::errc{}) { + WARTHOG_GWARN_FMT("grid patch failed to read patch {} data code={}", count, (int)ec); + return -count; + } + patches_.push_back(patch); + } + + return count; +} + +bool grid_patch_set::push_copy(bittable table, uint16_t offset_x, uint16_t offset_y, + uint16_t width, uint16_t height) +{ + if (table.size() == 0) { + return false; + } + bittable patch; + if (offset_x == 0 && offset_y == 0 && width == npos && height == npos) { + // copy as is + auto size = table.size_bytes(); + auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); + std::memcpy(grid_data, table.data(), size); + patch = bittable(grid_data, table.width(), table.height()); + } else { + // crop + if (offset_x >= table.width() || width > table.width() || offset_x + width > table.width()) + { + return false; + } + if (offset_y >= table.height() || height > table.height() || offset_y + height > table.height()) + { + return false; + } + auto size = bittable::calc_array_size(width, height); + auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); + patch = bittable(grid_data, table.width(), table.height()); + table.copy(patch, pad_id::zero(), table.xy_to_id(offset_x, offset_y), width, height); + } + patches_.push_back(patch); + return true; +} + +bool grid_patch_set::push_ref(bittable patch) +{ + patches_.push_back(patch); + return true; +} + +void grid_patch_set::reset() +{ + patches_.clear(); + grid_res_.release(); +} + +} // namespace warthog::manager diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index aee2c8b..e959c22 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -10,7 +10,11 @@ namespace warthog::manager { -scenario_runner::scenario_runner() { } +scenario_runner::scenario_runner() = default; + +scenario_runner::scenario_runner(const scenario_manager* scen) + : scenario_(scen) +{ } scenario_runner::~scenario_runner() = default; @@ -22,7 +26,7 @@ std::pair scenario_runner::experiment_next(uint32_t coun if (count == 0) return {nullptr, 0}; int patch_count = 0; - while (command_at_ < command_size_) { + while (command_at_ < commands.size()) { auto cmd = commands[command_at_]; // command_at_ incremented in following fuction calls switch (cmd.type) { @@ -109,10 +113,12 @@ scenario_runner::snapshot_patches(bool clear_patch) command_at_ += 1; // while command_at_ is PATCH, add patch to applied list while (command_at_ < commands.size()) { - if (auto cmd = commands[command_at_]; cmd.bucket == util::scenario_command::PATCH) { + if (auto cmd = commands[command_at_]; cmd.type == util::scenario_command::PATCH) { command_at_ += 1; count += 1; patches_.push_back({cmd.id, cmd.cmd.patch.topleft_x, cmd.cmd.patch.topleft_y}); + } else { + break; } } // end at first non-PATCH command @@ -135,7 +141,7 @@ scenario_runner::snapshot_query() WARTHOG_GERROR_FMT("scenario_runner::snapshot_query invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, scenario_->num_experiments(), WARTHOG_FILENAME_LINE); return nullptr; } - return experiments_[cmd.cmd.query.experiment_id]; + return scenario_->get_experiment(cmd.cmd.query.experiment_id); } std::span @@ -168,21 +174,22 @@ bool scenario_runner::gridmap_init(domain::gridmap& grid, const grid_patch_set& } restart(); snapshot_patches(); - return gridmap_apply_patches(grid, patch_set) < 0; + return gridmap_apply_patches(grid, patch_set) >= 0; } int scenario_runner::gridmap_apply_patches(domain::gridmap& grid, const grid_patch_set& patch_set) { int count = 0; for (auto& P : patches_) { + ++count; + if (P.patch_id >= patch_set.size()) + return -count; uint32_t x, y; grid.to_padded_xy_from_unpadded(P.topleft_x, P.topleft_y, x, y); - if (girdmap_apply_patch(grid, patch_set.get_patch(P.patch_id), x, y)) { - count++; - } else { - return count; + if (!girdmap_apply_patch(grid, patch_set.get_patch(P.patch_id), x, y)) { + return -count; } } - return -1; + return count; } bool scenario_runner::girdmap_apply_patch(domain::gridmap& grid, domain::gridmap::bittable patch, uint32_t padded_x, uint32_t padded_y) { From 0cb493d9de7bc8fec49b862cbf3dfc0431822a13 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Tue, 5 May 2026 12:55:17 +1000 Subject: [PATCH 50/61] bugfixes, dynamic scenario works --- apps/warthog.cpp | 72 +++++++++++++++++++++++++------ include/warthog/io/grid.h | 3 +- include/warthog/memory/bittable.h | 3 +- src/manager/scenario_runner.cpp | 7 +++ src/util/scenario_manager.cpp | 4 +- 5 files changed, 72 insertions(+), 17 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index f30f0c6..7a14b0b 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,9 @@ int verbose = 0; int print_help = 0; // run only this query, or -1 for all int filter_id = -1; +// dump map at id if set +int dump_map_id = -1; +std::string dump_map_file; #ifdef WARTHOG_POSTHOC // write trace to file, empty string to disable std::string trace_file; @@ -78,11 +82,14 @@ help(std::ostream& out) "values in scen file) \n" << "\t--costs [costs file] (required if using a weighted " "terrain algorithm)\n" + << "\t--v2-cost [type] (optional; change used cost type for v2 scen file dynamic)" << "\t--checkopt (optional; compare solution costs against " "values in the scen file)\n" << "\t--verbose (optional; prints debugging info when compiled " "with debug symbols)\n" << "\t--filter [id] (optional; run only query [id])\n" + << "\t--dump-map [id] (optional; dump map at id to stderr)" + << "\t--dump-map-file [filename] (optional; file to dump map to, default /dev/stderr)" #ifdef WARTHOG_POSTHOC << "\t--trace [.trace.yaml file] (optional; write posthoc trace for " "first query to [file])\n" @@ -90,7 +97,10 @@ help(std::ostream& out) << "Invoking the program this way solves all instances in [scen " "file] with algorithm [alg]\n" << "Currently recognised values for [alg]:\n" - << "\tastar, astar_wgm, astar4c, dijkstra\n"; + << "\tastar, astar_wgm, astar4c, dijkstra\n" + << "Currently recognised values for [v2-cost]:\n" + << "8c-ncc (default), 8c-cc, 4c, aa-ncc, aa-cc\n" + << "8c = 8-connected, 4c = 4-connected, aa = anyangle, ncc = no-corner-cut, cc = corner-cut\n"; } bool @@ -103,20 +113,12 @@ check_optimality( if(fabs(delta - epsilon) > epsilon) { - std::stringstream strpathlen; - strpathlen << std::fixed << std::setprecision(exp->precision()); - strpathlen << sol.sum_of_edge_costs_; - - std::stringstream stroptlen; - stroptlen << std::fixed << std::setprecision(exp->precision()); - stroptlen << exp->distance(); - std::cerr << std::setprecision(exp->precision()); std::cerr << "optimality check failed!" << std::endl; std::cerr << std::endl; - std::cerr << "optimal path length: " << stroptlen.str() + std::cerr << "optimal path length: " << sol.sum_of_edge_costs_ << " computed length: "; - std::cerr << strpathlen.str() << std::endl; + std::cerr << exp->distance() << std::endl; std::cerr << "precision: " << precision << " epsilon: " << epsilon << std::endl; std::cerr << "delta: " << delta << std::endl; @@ -187,8 +189,34 @@ run_experiments( } } + if (i == dump_map_id) { + // print map + std::optional outstream; + std::ostream* out = nullptr; + // convert dev to stream for cross-platform support + if (dump_map_file == "/dev/stderr") { + out = &std::cerr; + } else if (dump_map_file == "/dev/stdout") { + out = &std::cout; + } else { + out = &outstream.emplace(dump_map_file); + WARTHOG_GERROR_FMT_IF(!*out, "failed to open file to dump map {}\n", dump_map_file); + } + if (*out) { + // out is valid, print + std::string line(scen.grid.width()+1, '\n'); + for (uint32_t y = 0, ye = scen.grid.height(), xe = scen.grid.width(); y < ye; ++y) { + for (uint32_t x = 0; x < xe; ++x) { + line[x] = "@."[(int)(scen.grid.get_label(scen.grid.to_padded_id_from_padded(x, y)) != 0)]; + } + *out << line; + } + } + } + if (filter_id >= 0 && i == filter_id) { // trace +#ifdef WARTHOG_POSTHOC if constexpr(std::same_as< listener_type, std::remove_cvref_t>) @@ -201,6 +229,7 @@ run_experiments( l.open(*trace_stream); } } +#endif } else if (filter_id >= 0) { continue; } @@ -362,10 +391,13 @@ main(int argc, char** argv) {"checkopt", no_argument, &checkopt, 1}, {"verbose", no_argument, &verbose, 1}, {"filter", required_argument, &filter_id, 1}, + {"dump-map", required_argument, &dump_map_id, 1}, + {"dump-map-file", required_argument, 0, 0}, #ifdef WARTHOG_POSTHOC {"trace", required_argument, 0, 0}, #endif - {"costs", required_argument, 0, 1}, + {"costs", required_argument, 0, 0}, + {"v2-cost", required_argument, 0, 0}, {0, 0, 0, 0}}; warthog::util::cfg cfg; @@ -382,6 +414,8 @@ main(int argc, char** argv) // std::string gen = cfg.get_param_value("gen"); std::string mapfile = cfg.get_param_value("map"); std::string costfile = cfg.get_param_value("costs"); + std::string v2cost = cfg.get_param_value("v2-cost"); + dump_map_file = cfg.get_param_value("dump-map-file"); if(filter_id == 1) { @@ -398,8 +432,22 @@ main(int argc, char** argv) return 0; } + // check v2cost + if (v2cost.empty()) { + v2cost = "8c-ncc"; + } + + if(dump_map_id == 1) + { + dump_map_id = std::stoi(cfg.get_param_value("dump-map")); + } + if (dump_map_file.empty()) { + dump_map_file = "/dev/stderr"; + } + // load up the instances warthog::util::scenario_manager scenmgr; + scenmgr.set_cost_type(v2cost); scenmgr.load_scenario(sfile.c_str()); if(scenmgr.num_experiments() == 0) diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index 4541142..d534e08 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -236,8 +236,7 @@ bittable_serialize::read_grid_data( // copy row to table for(uint32_t x = 0; x < read_dim.width; ++x, ++bit_id) { - if (gridmap_cell_traversable(token[x])) - table.bit_or(static_cast(bit_id), 1); + table.set(static_cast(bit_id), gridmap_cell_traversable(token[x]) ? 1 : 0); } } diff --git a/include/warthog/memory/bittable.h b/include/warthog/memory/bittable.h index 2cbe33a..b567e21 100644 --- a/include/warthog/memory/bittable.h +++ b/include/warthog/memory/bittable.h @@ -247,7 +247,8 @@ struct bitarray id_value_type p(pos); id_value_type d = dest_bit; while (count-- > 0) { - dest_t.set(typename decltype(dest_t)::id_type(d++), get(id_type(p++))); + auto v = get(id_type(p++)); + dest_t.set(typename decltype(dest_t)::id_type(d++), v); } } void copy(bitarray dest, id_type dest_pos, size_t count, id_type pos = id_type(0)) diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index e959c22..cb388ab 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -171,6 +171,13 @@ bool scenario_runner::gridmap_init(domain::gridmap& grid, const grid_patch_set& { if (setup_grid) { grid.setup(scenario_->get_scenario_height(), scenario_->get_scenario_width()); + // grid is 0 (non-traversable), make map area traversable but keep padding non-traversable + for (uint32_t y = 0, ye = grid.header_height(), xe = grid.header_width(); y < ye; ++y) { + pad_id row_id = grid.to_padded_id_from_unpadded(0, y); + for (uint32_t x = 0; x < xe; ++x, ++row_id.id) { + grid.set_label(row_id, true); + } + } } restart(); snapshot_patches(); diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 57cc000..8f1a5a9 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -236,10 +236,10 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) return std::errc::io_error; } if (last_type != io::scenario_serialize::CMD_PATCH || last_bucket != P.bucket) { - commands_.push_back(scenario_command::make_snapshot(Q.bucket, ++snapshot_count)); + commands_.push_back(scenario_command::make_snapshot(P.bucket, ++snapshot_count)); } last_type = io::scenario_serialize::CMD_PATCH; - last_bucket = Q.bucket; + last_bucket = P.bucket; commands_.push_back(scenario_command::make_patch(P.bucket, P.patch_id, P.loc_x, P.loc_y)); } else if(con == io::scenario_serialize::FINAL) { break; } From 333055254c3a756494bd8002a27affead610a7ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 02:56:21 +0000 Subject: [PATCH 51/61] auto clang-format action --- apps/warthog.cpp | 150 ++++---- include/warthog/domain/grid_patches.h | 6 +- include/warthog/domain/gridmap.h | 36 +- include/warthog/io/grid.h | 104 +++--- include/warthog/io/scenario.h | 53 +-- include/warthog/io/serialize_base.h | 16 +- include/warthog/manager/grid_patch_set.h | 82 +++-- include/warthog/manager/scenario_manager.h | 2 +- include/warthog/manager/scenario_runner.h | 80 +++-- include/warthog/memory/bittable.h | 40 ++- include/warthog/util/experiment.h | 8 +- include/warthog/util/scenario_manager.h | 102 ++++-- include/warthog/util/string.h | 390 ++++++++++++--------- src/domain/gridmap.cpp | 28 +- src/io/grid.cpp | 114 ++---- src/io/scenario.cpp | 175 +++++---- src/io/serialize_base.cpp | 87 ++--- src/manager/grid_patch_set.cpp | 139 +++++--- src/manager/scenario_runner.cpp | 153 ++++---- src/util/scenario_manager.cpp | 122 ++++--- 20 files changed, 1094 insertions(+), 793 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 7a14b0b..201d106 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -10,12 +10,12 @@ #include #include #include -#include -#include #include #include #include #include +#include +#include #include #include #include @@ -82,14 +82,16 @@ help(std::ostream& out) "values in scen file) \n" << "\t--costs [costs file] (required if using a weighted " "terrain algorithm)\n" - << "\t--v2-cost [type] (optional; change used cost type for v2 scen file dynamic)" + << "\t--v2-cost [type] (optional; change used cost type for v2 scen " + "file dynamic)" << "\t--checkopt (optional; compare solution costs against " "values in the scen file)\n" << "\t--verbose (optional; prints debugging info when compiled " "with debug symbols)\n" << "\t--filter [id] (optional; run only query [id])\n" - << "\t--dump-map [id] (optional; dump map at id to stderr)" - << "\t--dump-map-file [filename] (optional; file to dump map to, default /dev/stderr)" + << "\t--dump-map [id] (optional; dump map at id to stderr)" + << "\t--dump-map-file [filename] (optional; file to dump map to, " + "default /dev/stderr)" #ifdef WARTHOG_POSTHOC << "\t--trace [.trace.yaml file] (optional; write posthoc trace for " "first query to [file])\n" @@ -98,9 +100,10 @@ help(std::ostream& out) "file] with algorithm [alg]\n" << "Currently recognised values for [alg]:\n" << "\tastar, astar_wgm, astar4c, dijkstra\n" - << "Currently recognised values for [v2-cost]:\n" - << "8c-ncc (default), 8c-cc, 4c, aa-ncc, aa-cc\n" - << "8c = 8-connected, 4c = 4-connected, aa = anyangle, ncc = no-corner-cut, cc = corner-cut\n"; + << "Currently recognised values for [v2-cost]:\n" + << "8c-ncc (default), 8c-cc, 4c, aa-ncc, aa-cc\n" + << "8c = 8-connected, 4c = 4-connected, aa = anyangle, ncc = " + "no-corner-cut, cc = corner-cut\n"; } bool @@ -141,14 +144,13 @@ struct gridmap_scenario warthog::manager::grid_patch_set patches; gridmap_scenario(const warthog::manager::scenario_manager& scen) - : mgr(&scen), run(&scen) + : mgr(&scen), run(&scen) { } - bool load_map(const std::filesystem::path map) + bool + load_map(const std::filesystem::path map) { - if (!patches.load(map)) { - return false; - } + if(!patches.load(map)) { return false; } return run.gridmap_init(grid, patches); } }; @@ -156,10 +158,8 @@ struct gridmap_scenario template int run_experiments( - Search& algo, std::string alg_name, - gridmap_scenario& scen, - bool verbose, bool checkopt, - std::ostream& out) + Search& algo, std::string alg_name, gridmap_scenario& scen, bool verbose, + bool checkopt, std::ostream& out) { WARTHOG_GINFO_FMT("start search with algorithm {}", alg_name); warthog::search::search_parameters par; @@ -169,8 +169,8 @@ run_experiments( out << "id\talg\texpanded\tgenerated\treopen\tsurplus\theapops" << "\tnanos\tplen\tpcost\tscost\tmap\n"; - - for (uint32_t i = 0; ; ++i) + + for(uint32_t i = 0;; ++i) { #ifdef WARTHOG_POSTHOC std::optional @@ -178,61 +178,72 @@ run_experiments( #endif auto [exp, patch_count] = scen.run.experiment_next(); - if (exp == nullptr) { - break; - } - if (patch_count != 0) { - if (scen.run.gridmap_apply_patches(scen.grid, scen.patches) < 0) { + if(exp == nullptr) { break; } + if(patch_count != 0) + { + if(scen.run.gridmap_apply_patches(scen.grid, scen.patches) < 0) + { // failed to apply patches, exit WARTHOG_GCRIT("dynamic patch error: failed to apply patches"); return 5; } } - if (i == dump_map_id) { + if(i == dump_map_id) + { // print map std::optional outstream; std::ostream* out = nullptr; // convert dev to stream for cross-platform support - if (dump_map_file == "/dev/stderr") { - out = &std::cerr; - } else if (dump_map_file == "/dev/stdout") { - out = &std::cout; - } else { + if(dump_map_file == "/dev/stderr") { out = &std::cerr; } + else if(dump_map_file == "/dev/stdout") { out = &std::cout; } + else + { out = &outstream.emplace(dump_map_file); - WARTHOG_GERROR_FMT_IF(!*out, "failed to open file to dump map {}\n", dump_map_file); + WARTHOG_GERROR_FMT_IF( + !*out, "failed to open file to dump map {}\n", + dump_map_file); } - if (*out) { + if(*out) + { // out is valid, print - std::string line(scen.grid.width()+1, '\n'); - for (uint32_t y = 0, ye = scen.grid.height(), xe = scen.grid.width(); y < ye; ++y) { - for (uint32_t x = 0; x < xe; ++x) { - line[x] = "@."[(int)(scen.grid.get_label(scen.grid.to_padded_id_from_padded(x, y)) != 0)]; + std::string line(scen.grid.width() + 1, '\n'); + for(uint32_t y = 0, ye = scen.grid.height(), + xe = scen.grid.width(); + y < ye; ++y) + { + for(uint32_t x = 0; x < xe; ++x) + { + line[x] = "@."[( + int)(scen.grid.get_label( + scen.grid.to_padded_id_from_padded(x, y)) + != 0)]; } *out << line; } } } - if (filter_id >= 0 && i == filter_id) { + if(filter_id >= 0 && i == filter_id) + { // trace #ifdef WARTHOG_POSTHOC if constexpr(std::same_as< - listener_type, - std::remove_cvref_t>) + listener_type, + std::remove_cvref_t< + decltype(algo.get_listeners())>>) { if(!trace_file.empty()) { listener_grid& l - = std::get(algo.get_listeners()); + = std::get(algo.get_listeners()); trace_stream.emplace(trace_file); l.open(*trace_stream); } } #endif - } else if (filter_id >= 0) { - continue; } + else if(filter_id >= 0) { continue; } warthog::pack_id startid = expander->get_pack(exp->startx(), exp->starty()); @@ -286,19 +297,22 @@ run_astar( std::string alg_name) { gridmap_scenario scen(scenmgr); - if (!scen.load_map(std::filesystem::path(mapname))) { + if(!scen.load_map(std::filesystem::path(mapname))) + { WARTHOG_GCRIT("failed to load map"); return 3; } warthog::search::gridmap_expansion_policy expander(&scen.grid); - warthog::heuristic::octile_heuristic heuristic(scen.grid.width(), scen.grid.height()); + warthog::heuristic::octile_heuristic heuristic( + scen.grid.width(), scen.grid.height()); warthog::util::pqueue_min open; warthog::search::unidirectional_search astar( - &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); + &heuristic, &expander, &open, + listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); - int ret = run_experiments( - astar, alg_name, scen, verbose, checkopt, std::cout); + int ret + = run_experiments(astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } @@ -308,7 +322,8 @@ run_astar4c( std::string alg_name) { gridmap_scenario scen(scenmgr); - if (!scen.load_map(std::filesystem::path(mapname))) { + if(!scen.load_map(std::filesystem::path(mapname))) + { WARTHOG_GCRIT("failed to load map"); return 3; } @@ -318,10 +333,11 @@ run_astar4c( warthog::util::pqueue_min open; warthog::search::unidirectional_search astar( - &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); + &heuristic, &expander, &open, + listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); - int ret = run_experiments( - astar, alg_name, scen, verbose, checkopt, std::cout); + int ret + = run_experiments(astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } @@ -331,7 +347,8 @@ run_dijkstra( std::string alg_name) { gridmap_scenario scen(scenmgr); - if (!scen.load_map(std::filesystem::path(mapname))) { + if(!scen.load_map(std::filesystem::path(mapname))) + { WARTHOG_GCRIT("failed to load map"); return 3; } @@ -340,10 +357,11 @@ run_dijkstra( warthog::util::pqueue_min open; warthog::search::unidirectional_search astar( - &heuristic, &expander, &open, listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); + &heuristic, &expander, &open, + listener_type(WARTHOG_POSTHOC_DO(&scen.grid))); - int ret = run_experiments( - astar, alg_name, scen, verbose, checkopt, std::cout); + int ret + = run_experiments(astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } @@ -371,8 +389,8 @@ run_wgm_astar( warthog::search::unidirectional_search astar(&heuristic, &expander, &open); - int ret = run_experiments( - astar, alg_name, scen, verbose, checkopt, std::cout); + int ret + = run_experiments(astar, alg_name, scen, verbose, checkopt, std::cout); return ret; } @@ -391,13 +409,13 @@ main(int argc, char** argv) {"checkopt", no_argument, &checkopt, 1}, {"verbose", no_argument, &verbose, 1}, {"filter", required_argument, &filter_id, 1}, - {"dump-map", required_argument, &dump_map_id, 1}, - {"dump-map-file", required_argument, 0, 0}, + {"dump-map", required_argument, &dump_map_id, 1}, + {"dump-map-file", required_argument, 0, 0}, #ifdef WARTHOG_POSTHOC {"trace", required_argument, 0, 0}, #endif {"costs", required_argument, 0, 0}, - {"v2-cost", required_argument, 0, 0}, + {"v2-cost", required_argument, 0, 0}, {0, 0, 0, 0}}; warthog::util::cfg cfg; @@ -414,8 +432,8 @@ main(int argc, char** argv) // std::string gen = cfg.get_param_value("gen"); std::string mapfile = cfg.get_param_value("map"); std::string costfile = cfg.get_param_value("costs"); - std::string v2cost = cfg.get_param_value("v2-cost"); - dump_map_file = cfg.get_param_value("dump-map-file"); + std::string v2cost = cfg.get_param_value("v2-cost"); + dump_map_file = cfg.get_param_value("dump-map-file"); if(filter_id == 1) { @@ -433,17 +451,13 @@ main(int argc, char** argv) } // check v2cost - if (v2cost.empty()) { - v2cost = "8c-ncc"; - } + if(v2cost.empty()) { v2cost = "8c-ncc"; } if(dump_map_id == 1) { dump_map_id = std::stoi(cfg.get_param_value("dump-map")); } - if (dump_map_file.empty()) { - dump_map_file = "/dev/stderr"; - } + if(dump_map_file.empty()) { dump_map_file = "/dev/stderr"; } // load up the instances warthog::util::scenario_manager scenmgr; diff --git a/include/warthog/domain/grid_patches.h b/include/warthog/domain/grid_patches.h index 4cf0910..8b6fe65 100644 --- a/include/warthog/domain/grid_patches.h +++ b/include/warthog/domain/grid_patches.h @@ -5,16 +5,16 @@ // grid_patches primailry holds an array of bittable, handles reading from file // and efficent memory storage. // -// Look at utilities in manager/dynamic_gridmap.h for automatic translation to gridmap -// in a dynamic scenario. +// Look at utilities in manager/dynamic_gridmap.h for automatic translation to +// gridmap in a dynamic scenario. // // @author: Ryan Hechenberger // @created: 2026-04-10 // #include "grid.h" -#include #include +#include namespace warthog::domain { diff --git a/include/warthog/domain/gridmap.h b/include/warthog/domain/gridmap.h index dcdd5e3..33ccfda 100644 --- a/include/warthog/domain/gridmap.h +++ b/include/warthog/domain/gridmap.h @@ -19,8 +19,8 @@ // #include "grid.h" -#include #include +#include #include #include #include @@ -32,9 +32,9 @@ #include #include #include -#include #include #include +#include namespace warthog::domain { @@ -64,12 +64,18 @@ class gridmap : public memory::bittable operator=(const gridmap&) = delete; - void setup(uint32_t height, uint32_t width); - void load(std::istream& input); - void load(io::bittable_serialize& parser); - void load(std::filesystem::path&& filename); - void load(const std::filesystem::path& filename); - void load(const char* filename); + void + setup(uint32_t height, uint32_t width); + void + load(std::istream& input); + void + load(io::bittable_serialize& parser); + void + load(std::filesystem::path&& filename); + void + load(const std::filesystem::path& filename); + void + load(const char* filename); // here we convert from the coordinate space of // the original grid to the coordinate space of db_. @@ -115,7 +121,9 @@ class gridmap : public memory::bittable } void - to_unpadded_xy_from_padded(uint32_t padded_x, uint32_t padded_y, uint32_t& x, uint32_t& y) const noexcept + to_unpadded_xy_from_padded( + uint32_t padded_x, uint32_t padded_y, uint32_t& x, + uint32_t& y) const noexcept { y = padded_y - PADDED_ROWS; x = padded_x; @@ -131,7 +139,9 @@ class gridmap : public memory::bittable } void - to_padded_xy_from_unpadded(uint32_t unpadded_x, uint32_t unpadded_y, uint32_t& x, uint32_t& y) const noexcept + to_padded_xy_from_unpadded( + uint32_t unpadded_x, uint32_t unpadded_y, uint32_t& x, + uint32_t& y) const noexcept { y = unpadded_y + PADDED_ROWS; x = unpadded_x; @@ -380,8 +390,10 @@ class gridmap : public memory::bittable } protected: - void setup_stream_(std::istream& in); - void setup_ser_(io::bittable_serialize& parser); + void + setup_stream_(std::istream& in); + void + setup_ser_(io::bittable_serialize& parser); private: warthog::util::gm_header header_; diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index d534e08..127c6a8 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -5,7 +5,8 @@ // // Read utility for gridmap. // -// Supported MovingAI map format. Read format spec: https://movingai.com/benchmarks/formats.html +// Supported MovingAI map format. Read format spec: +// https://movingai.com/benchmarks/formats.html // // @author: Ryan Hechenberger // @created: 2025-06-01 @@ -33,21 +34,23 @@ enum class bittable_type : uint8_t enum class gridmap_cell : char { - TERRAIN = '.', - TERRAIN_2 = 'G', - OUT_OF_BOUNDS = '@', + TERRAIN = '.', + TERRAIN_2 = 'G', + OUT_OF_BOUNDS = '@', OUT_OF_BOUNDS_2 = 'O', - TREES = 'T', - SWAMP = 'S', - WATER = 'W', + TREES = 'T', + SWAMP = 'S', + WATER = 'W', }; /// @brief Standard traversable terrain type from gridmap_cell -/// @param c +/// @param c /// @return ".G" return true, false otherwise -constexpr inline bool gridmap_cell_traversable(gridmap_cell c) noexcept +constexpr inline bool +gridmap_cell_traversable(gridmap_cell c) noexcept { - switch (c) { + switch(c) + { case gridmap_cell::TERRAIN: case gridmap_cell::TERRAIN_2: return true; @@ -56,7 +59,8 @@ constexpr inline bool gridmap_cell_traversable(gridmap_cell c) noexcept } } /// @return char c is traversable, as gridmap_cell_traversable((gridmap_cell)c) -constexpr inline bool gridmap_cell_traversable(char c) noexcept +constexpr inline bool +gridmap_cell_traversable(char c) noexcept { return gridmap_cell_traversable(static_cast(c)); } @@ -67,12 +71,14 @@ inline constexpr uint32_t GRID_MAX_SIZE = 15'000; /// @brief Limit on max number of patches inline constexpr uint32_t PATCH_COUNT_LIMIT = 10'000'000; -/// @brief The bittable serialize class, flexable read/write of bittable/gridmap or +/// @brief The bittable serialize class, flexable read/write of +/// bittable/gridmap or /// similiar datatypes class bittable_serialize : public serialize_base { public: - /// @return the grid dimension, either as last read grid from file or set by user for writing + /// @return the grid dimension, either as last read grid from file or set + /// by user for writing memory::bittable_dimension get_dim() const noexcept { @@ -98,7 +104,8 @@ class bittable_serialize : public serialize_base { return m_type; } - /// @brief Sets the type/version to write to the file header, supported is octile/patch. + /// @brief Sets the type/version to write to the file header, supported is + /// octile/patch. /// Throws on unsupported type. void set_type(bittable_type type) @@ -111,31 +118,33 @@ class bittable_serialize : public serialize_base } /// @brief get the number of patches in file - uint32_t get_patch_amount() const noexcept + uint32_t + get_patch_amount() const noexcept { return m_patch_amount; } /// @brief set the number of patches (for writing) - void set_patch_amount(uint32_t count) + void + set_patch_amount(uint32_t count) { - if(count > PATCH_COUNT_LIMIT) - { - throw std::out_of_range("count"); - } + if(count > PATCH_COUNT_LIMIT) { throw std::out_of_range("count"); } m_patch_amount = count; } /// @brief gets the number of patches that have been read/write - uint32_t get_patch_count() const noexcept + uint32_t + get_patch_count() const noexcept { return m_patch_count; } /// @brief gets the id of last patch (usually get_patch_count()) - uint32_t get_patch_id() const noexcept + uint32_t + get_patch_id() const noexcept { return m_patch_id; } /// @brief gets the number of patches that have been read - void set_patch_id(uint32_t id) noexcept + void + set_patch_id(uint32_t id) noexcept { m_patch_id = id; } @@ -146,7 +155,8 @@ class bittable_serialize : public serialize_base /// /// Reads the header line, `type octile` for bittable_type::OCTILE or /// `type patch` for bittable_type::PATCH, retrivable by get_type(). - /// For PATCH type, also reads following line for number of patches in file. + /// For PATCH type, also reads following line for number of patches in + /// file. std::errc read_header(std::istream* in = nullptr); @@ -157,39 +167,42 @@ class bittable_serialize : public serialize_base std::errc read_grid_header(std::istream* in = nullptr); - /// @brief Reads the grids data and stores it into a bittable, expects size from get_dim() + /// @brief Reads the grids data and stores it into a bittable, expects size + /// from get_dim() /// @param table bittable derived type to store, must be init /// @param offset_x offset of top-left in table to copy grid to /// @param offset_y offset of top-left in table to copy grid to /// @param in alternative filestream to read from /// @return value init on success, error code on failure - /// @pre table must be init and large enough to store whole grid (including from offset) + /// @pre table must be init and large enough to store whole grid (including + /// from offset) template std::errc read_grid_data( - BitTable& table, uint32_t offset_x = 0, - uint32_t offset_y = 0, std::istream* in = nullptr); + BitTable& table, uint32_t offset_x = 0, uint32_t offset_y = 0, + std::istream* in = nullptr); - /// @brief Reads the raw rows (char) from map into a 1D array, expects size from get_dim() + /// @brief Reads the raw rows (char) from map into a 1D array, expects size + /// from get_dim() /// @param buffer the buffer /// @param in alternative filestream to read from /// @return value init on success, error code on failure - /// @pre buffer must be large enough to store width x height characters from get_dim() + /// @pre buffer must be large enough to store width x height characters + /// from get_dim() /// /// Reads row by row from the top left, writing into buffer. /// Data is tightly packed, with no delimited between rows of size width. - /// Characters are as defined by the MovingAI spec, use gridmap_cell_traversable(c) - /// to determine traversability if applicable. + /// Characters are as defined by the MovingAI spec, use + /// gridmap_cell_traversable(c) to determine traversability if applicable. std::errc - read_grid_raw( - std::span buffer, std::istream* in = nullptr); + read_grid_raw(std::span buffer, std::istream* in = nullptr); protected: memory::bittable_dimension m_dim = {}; bittable_type m_type = bittable_type::NONE; uint32_t m_patch_amount = 0; uint32_t m_patch_count = 0; - uint32_t m_patch_id = 0; + uint32_t m_patch_id = 0; }; template @@ -211,10 +224,7 @@ bittable_serialize::read_grid_data( std::errc err; std::tie(in, err) = get_istream(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } std::string_view line; std::string_view token; @@ -222,21 +232,17 @@ bittable_serialize::read_grid_data( { // read row std::tie(line, err) = readline(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } parser par(line); - if (!par.next(token).eof()) - { - return par.error(); - } - if (token.size() != read_dim.width) + if(!par.next(token).eof()) { return par.error(); } + if(token.size() != read_dim.width) return std::errc::argument_out_of_domain; // copy row to table for(uint32_t x = 0; x < read_dim.width; ++x, ++bit_id) { - table.set(static_cast(bit_id), gridmap_cell_traversable(token[x]) ? 1 : 0); + table.set( + static_cast(bit_id), + gridmap_cell_traversable(token[x]) ? 1 : 0); } } diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 717e349..b1f9239 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -21,17 +21,17 @@ #include "serialize_base.h" +#include #include #include #include +#include #include #include #include +#include #include #include -#include -#include -#include namespace warthog::io { @@ -86,8 +86,10 @@ class scenario_serialize : public serialize_base scenario_serialize(); ~scenario_serialize() override; - static constexpr std::string_view get_dist_str(cost_type a) noexcept; - static constexpr cost_type get_cost_type(std::string_view a) noexcept; + static constexpr std::string_view + get_dist_str(cost_type a) noexcept; + static constexpr cost_type + get_cost_type(std::string_view a) noexcept; /// @brief Resets class, including memory. Must use between seperate /// read/writes, needed for memory managment. @@ -150,13 +152,17 @@ class scenario_serialize : public serialize_base find_cost_index(cost_type c) const noexcept { auto it = std::find(m_cost_type.begin(), m_cost_type.end(), c); - return it != m_cost_type.end() ? static_cast(it - m_cost_type.begin()) : -1; + return it != m_cost_type.end() + ? static_cast(it - m_cost_type.begin()) + : -1; } int find_cost_index(std::string_view c) const noexcept { auto it = std::find(m_cost_strings.begin(), m_cost_strings.end(), c); - return it != m_cost_strings.end() ? static_cast(it - m_cost_strings.begin()) : -1; + return it != m_cost_strings.end() + ? static_cast(it - m_cost_strings.begin()) + : -1; } uint32_t @@ -244,15 +250,20 @@ class scenario_serialize : public serialize_base std::pmr::monotonic_buffer_resource m_string_res; // distance types std::pmr::vector m_cost_strings; ///< names read in - std::pmr::vector m_cost_type; ///< cost_type of index (corrisponding dist_strings and dist_value) - std::pmr::vector m_cost_value; ///< distance value stored, will be placed in dynamic_scenario - + std::pmr::vector + m_cost_type; ///< cost_type of index (corrisponding dist_strings and + ///< dist_value) + std::pmr::vector m_cost_value; ///< distance value stored, will be + ///< placed in dynamic_scenario + std::string m_command_type; ///< last cost type }; -inline constexpr std::string_view scenario_serialize::get_dist_str(cost_type a) noexcept +inline constexpr std::string_view +scenario_serialize::get_dist_str(cost_type a) noexcept { - switch(a) { + switch(a) + { case cost_type::G_8C_NCC: return "8c-ncc"; case cost_type::G_8C_CC: @@ -267,18 +278,14 @@ inline constexpr std::string_view scenario_serialize::get_dist_str(cost_type a) return std::string_view(); } } -inline constexpr cost_type scenario_serialize::get_cost_type(std::string_view a) noexcept +inline constexpr cost_type +scenario_serialize::get_cost_type(std::string_view a) noexcept { - if (a == "8c-ncc") - return cost_type::G_8C_NCC; - if (a == "8c-cc") - return cost_type::G_8C_CC; - if (a == "4c") - return cost_type::G_4C; - if (a == "aa-ncc") - return cost_type::AA_NCC; - if (a == "aa-cc") - return cost_type::AA_CC; + if(a == "8c-ncc") return cost_type::G_8C_NCC; + if(a == "8c-cc") return cost_type::G_8C_CC; + if(a == "4c") return cost_type::G_4C; + if(a == "aa-ncc") return cost_type::AA_NCC; + if(a == "aa-cc") return cost_type::AA_CC; return cost_type::OTHER; } diff --git a/include/warthog/io/serialize_base.h b/include/warthog/io/serialize_base.h index 3cb7263..0c3f26f 100644 --- a/include/warthog/io/serialize_base.h +++ b/include/warthog/io/serialize_base.h @@ -4,9 +4,9 @@ // io/serialize_base.h // // Read/write base class for serialize classes. -// Adds a uniform low-level interface for reading from a file, one-line at a time. -// Support for both file or just from a istream/ostream object. -// Tracking of line number for user-level error reporting and debugging. +// Adds a uniform low-level interface for reading from a file, one-line at a +// time. Support for both file or just from a istream/ostream object. Tracking +// of line number for user-level error reporting and debugging. // // @author: Ryan Hechenberger // @created: 2026-04-10 @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include namespace warthog::io { @@ -101,19 +101,21 @@ class serialize_base return {nullptr, std::errc::io_error}; return {out, {}}; } - bool istream_eof(std::istream* in = nullptr); + bool + istream_eof(std::istream* in = nullptr); std::pair readline(std::istream* in, bool skip_blanks = false); void unreadline(std::string_view line); - bool is_line_blank(std::string_view line); + bool + is_line_blank(std::string_view line); protected: std::filesystem::path m_filename; std::unique_ptr m_stream; std::istream* m_stream_in = nullptr; std::ostream* m_stream_out = nullptr; - int32_t m_line_num = 0; + int32_t m_line_num = 0; std::unique_ptr m_line_data; std::string_view m_line; std::string m_unget_line; diff --git a/include/warthog/manager/grid_patch_set.h b/include/warthog/manager/grid_patch_set.h index f5fab22..73fcf39 100644 --- a/include/warthog/manager/grid_patch_set.h +++ b/include/warthog/manager/grid_patch_set.h @@ -3,75 +3,97 @@ // scenario_runner.h // -// Take a scenario_manager object and be able to progress through a dynamic scenario. +// Take a scenario_manager object and be able to progress through a dynamic +// scenario. // // @author: Ryan Hechenberger // @created: 2026-04-10 // -#include #include #include +#include -#include #include #include +#include namespace warthog::manager { /// @brief a class for managing a set of patches /// -/// Stores a list of patches at +/// Stores a list of patches at class grid_patch_set { public: - enum flags : uint32_t { - DEFAULT = 0u, ///< default options - SKIP_HEADER = 1u << 0, ///< will not read header + enum flags : uint32_t + { + DEFAULT = 0u, ///< default options + SKIP_HEADER = 1u << 0, ///< will not read header FORCE_HEADER = 1u << 1, ///< must read header IGNORE_INDEX = 1u << 2, ///< ignores patch read index }; - using bittable = domain::gridmap::bittable; + using bittable = domain::gridmap::bittable; static constexpr uint16_t npos = (uint16_t)-1u; - grid_patch_set(std::pmr::memory_resource* upstream = nullptr) : - grid_res_(upstream != nullptr ? upstream : std::pmr::get_default_resource()) + grid_patch_set(std::pmr::memory_resource* upstream = nullptr) + : grid_res_( + upstream != nullptr ? upstream + : std::pmr::get_default_resource()) { } - bool load(std::istream& file); - bool load(const std::filesystem::path& maps); + bool + load(std::istream& file); + bool + load(const std::filesystem::path& maps); - /// @brief reads from a serialize, checking for errors. Can read both type octile and patch files. + /// @brief reads from a serialize, checking for errors. Can read both type + /// octile and patch files. /// @param S the bittable_serialize, must be open /// @param max_grids the max grids to read, or -1 for no limit /// @param flags flags that control how to read, check enum flags - /// @return the number of grids read successfully, or <0 for negative index of patch that failed to be read successfully + /// @return the number of grids read successfully, or <0 for negative index + /// of patch that failed to be read successfully + /// + /// This function will load from an established bittable_serialize, reading + /// all grids and appending to existing patches. If type octile, only one + /// grid is read as the first patch, otherwise up to max_grids (default + /// all) are read. /// - /// This function will load from an established bittable_serialize, reading all grids and appending to existing patches. - /// If type octile, only one grid is read as the first patch, otherwise up to max_grids (default all) are read. - /// - /// By DEFAULT, it will read the header if not already read and append all remaining grids to existing patches, but will error if the index in the file does not match the index in this class. - /// SKIP_HEADER requires that S have already read the header else errors. - /// FORCE_HEADER requires that S have not read the header else errors. - /// IGNORE_INDEX will not error if index in patch file does not match. - int deserialize(io::bittable_serialize& S, int max_grids = -1, uint32_t flags = DEFAULT); + /// By DEFAULT, it will read the header if not already read and append all + /// remaining grids to existing patches, but will error if the index in the + /// file does not match the index in this class. SKIP_HEADER requires that + /// S have already read the header else errors. FORCE_HEADER requires that + /// S have not read the header else errors. IGNORE_INDEX will not error if + /// index in patch file does not match. + int + deserialize( + io::bittable_serialize& S, int max_grids = -1, + uint32_t flags = DEFAULT); + + bool + push_copy( + bittable table, uint16_t offset_x = 0, uint16_t offset_y = 0, + uint16_t width = npos, uint16_t height = npos); - bool push_copy(bittable table, uint16_t offset_x = 0, uint16_t offset_y = 0, - uint16_t width = npos, uint16_t height = npos); - - bool push_ref(bittable patch); + bool + push_ref(bittable patch); - void reset(); + void + reset(); - size_t size() const noexcept + size_t + size() const noexcept { return patches_.size(); } - bittable get_patch(size_t i) const + bittable + get_patch(size_t i) const { return patches_.at(i); } - std::span get_patches() const noexcept + std::span + get_patches() const noexcept { return patches_; } diff --git a/include/warthog/manager/scenario_manager.h b/include/warthog/manager/scenario_manager.h index c805131..8817a80 100644 --- a/include/warthog/manager/scenario_manager.h +++ b/include/warthog/manager/scenario_manager.h @@ -9,8 +9,8 @@ // @created: 2026-04-10 // -#include #include "experiment.h" +#include namespace warthog::manager { diff --git a/include/warthog/manager/scenario_runner.h b/include/warthog/manager/scenario_runner.h index 9d123e9..8cc04d0 100644 --- a/include/warthog/manager/scenario_runner.h +++ b/include/warthog/manager/scenario_runner.h @@ -3,14 +3,15 @@ // scenario_runner.h // -// Take a scenario_manager object and be able to progress through a dynamic scenario. +// Take a scenario_manager object and be able to progress through a dynamic +// scenario. // // @author: Ryan Hechenberger // @created: 2026-04-09 // -#include "scenario_manager.h" #include "grid_patch_set.h" +#include "scenario_manager.h" #include namespace warthog::manager @@ -39,36 +40,42 @@ class scenario_runner restart(); /// @brief progress from current to count experiment away and return it - /// @param count + /// @param count /// @return pair of the reached query and snapshot id. /// - /// Will progress through commands until count queries are encounted, returning the final query. - /// When count == 1, is exactly the next query. - /// All patches required + /// Will progress through commands until count queries are encounted, + /// returning the final query. When count == 1, is exactly the next query. + /// All patches required std::pair experiment_next(uint32_t count = 1); /// @brief goto the start of the next snapshot (SNAPSHOT command) - /// @return snapshot id reached, or -1 if at end of commands (no more snapshots) + /// @return snapshot id reached, or -1 if at end of commands (no more + /// snapshots) /// - /// If current command is SNAPSHOT, if id != current_snapshot() then already at next snapshot, - /// otherwise progress until next SNAPSHOT command is reached (or end of commands). - /// Clears all patches from get_patches() and replaces with any PATCH command to next snapshot. + /// If current command is SNAPSHOT, if id != current_snapshot() then + /// already at next snapshot, otherwise progress until next SNAPSHOT + /// command is reached (or end of commands). Clears all patches from + /// get_patches() and replaces with any PATCH command to next snapshot. int snapshot_next(bool clear_patch = true); - /// @brief starting at SNAPSHOT or current PATCH, apply all patches until reaching SNAPSHOT or QUERY + /// @brief starting at SNAPSHOT or current PATCH, apply all patches until + /// reaching SNAPSHOT or QUERY /// @param clear_patch clears get_patches() - /// @return the number of patches, patch id are retrivable from get_patches() + /// @return the number of patches, patch id are retrivable from + /// get_patches() /// - /// Requires to be on SNAPSHOT or PATCH, otherwise returns 0 and does nothing. - /// If @clear_patch is set, clears get_patches(). - /// Appends all processed PATCH commands to get_patches(). + /// Requires to be on SNAPSHOT or PATCH, otherwise returns 0 and does + /// nothing. If @clear_patch is set, clears get_patches(). Appends all + /// processed PATCH commands to get_patches(). int snapshot_patches(bool clear_patch = true); - /// @brief returns the current query experiment if at query and progress to next command - /// @return the current query experiment if command is query, otherwise nullptr + /// @brief returns the current query experiment if at query and progress to + /// next command + /// @return the current query experiment if command is query, otherwise + /// nullptr /// /// Requires to be on QUERY, otherwise return nullptr and do nothing. /// If QUERY, returns corrisponding experiment and goto next command. @@ -80,14 +87,18 @@ class scenario_runner /// @param clear_patch clears get_patches() /// @return pair of the reached query and snapshot id. /// - /// Will progress through commands until count queries are encounted, returning the final query. - /// When count == 1, is exactly the next query. - /// All patches required + /// Will progress through commands until count queries are encounted, + /// returning the final query. When count == 1, is exactly the next query. + /// All patches required std::span snapshot_query_all(); - bool complete() const noexcept { return command_at_ >= scenario_->get_commands().size(); } - + bool + complete() const noexcept + { + return command_at_ >= scenario_->get_commands().size(); + } + std::span get_patches() const noexcept { @@ -102,16 +113,22 @@ class scenario_runner /// @brief setup grid and contain snapshot 0 /// @param grid the gridmap to setup - /// @param patch_set the patch set to - /// @param setup_grid + /// @param patch_set the patch set to + /// @param setup_grid /// @return true if operation is successful - bool gridmap_init(domain::gridmap& grid, const grid_patch_set& patch_set, bool setup_grid = true); + bool + gridmap_init( + domain::gridmap& grid, const grid_patch_set& patch_set, + bool setup_grid = true); /// @brief apply patches from patch_set in order by get_patches() /// @param grid grid to apply to /// @param patch_set patch_set to pull patch data from - /// @return >=0 successful patches applied, <0 failed to apply negative id (from 1) - int gridmap_apply_patches(domain::gridmap& grid, const grid_patch_set& patch_set); + /// @return >=0 successful patches applied, <0 failed to apply negative id + /// (from 1) + int + gridmap_apply_patches( + domain::gridmap& grid, const grid_patch_set& patch_set); /// @brief apply a single patch to a gridmap /// @param grid grid to apply to @@ -119,15 +136,18 @@ class scenario_runner /// @param padded_x the padded topleft on grid /// @param padded_y the padded topleft on grid /// @return true if apply is successful, false otherwise - bool girdmap_apply_patch(domain::gridmap& grid, domain::gridmap::bittable patch, uint32_t padded_x, uint32_t padded_y); + bool + girdmap_apply_patch( + domain::gridmap& grid, domain::gridmap::bittable patch, + uint32_t padded_x, uint32_t padded_y); protected: const scenario_manager* scenario_ = nullptr; std::vector patches_; std::vector experiments_; - uint32_t command_at_ = 0; ///< command at, used for dynamic scenario + uint32_t command_at_ = 0; ///< command at, used for dynamic scenario int32_t experiment_at_ = -1; - int32_t snapshot_at_ = -1; + int32_t snapshot_at_ = -1; }; } // namespace warthog::util diff --git a/include/warthog/memory/bittable.h b/include/warthog/memory/bittable.h index b567e21..23e1cc3 100644 --- a/include/warthog/memory/bittable.h +++ b/include/warthog/memory/bittable.h @@ -65,8 +65,7 @@ struct bitarray { public: static_assert( - ValueBits <= CHAR_BIT - && std::popcount(ValueBits) == 1, + ValueBits <= CHAR_BIT && std::popcount(ValueBits) == 1, "ValueBits must be to power of 2 and fit inside BaseType bits."); constexpr static size_t value_bits = ValueBits; using id_type = IdType; @@ -238,20 +237,27 @@ struct bitarray } } - void copy_p(void* dest, uint8_t dest_bit, size_t count, id_type pos = id_type(0)) + void + copy_p( + void* dest, uint8_t dest_bit, size_t count, id_type pos = id_type(0)) { // TODO: faster copy - assert(dest_bit < CHAR_BIT && (dest_bit & (ValueBits-1)) == 0); + assert(dest_bit < CHAR_BIT && (dest_bit & (ValueBits - 1)) == 0); // primative implemtation - bitarray dest_t(static_cast(dest)); + bitarray dest_t( + static_cast(dest)); id_value_type p(pos); id_value_type d = dest_bit; - while (count-- > 0) { + while(count-- > 0) + { auto v = get(id_type(p++)); dest_t.set(typename decltype(dest_t)::id_type(d++), v); } } - void copy(bitarray dest, id_type dest_pos, size_t count, id_type pos = id_type(0)) + void + copy( + bitarray dest, id_type dest_pos, size_t count, + id_type pos = id_type(0)) { auto [byte, bit] = id_split(dest_pos); copy_p(dest.data() + byte, bit, count, pos); @@ -277,8 +283,8 @@ struct bittable : bitarray { public: using typename bittable::bitarray::id_type; - using typename bittable::bitarray::value_type; using typename bittable::bitarray::id_value_type; + using typename bittable::bitarray::value_type; static constexpr size_t calc_array_size(uint32_t width, uint32_t height) noexcept @@ -327,8 +333,7 @@ struct bittable : bitarray id_to_xy(id_type id) const noexcept { assert(m_dim.width != 0); - const auto value - = static_cast(id); + const auto value = static_cast(id); return { static_cast(value % m_dim.width), static_cast(value / m_dim.width)}; @@ -445,21 +450,24 @@ struct bittable : bitarray return bittable::bitarray::id_split(id); } - void copy(bittable dest, id_type dest_pos, id_type pos, uint32_t width, uint32_t height) + void + copy( + bittable dest, id_type dest_pos, id_type pos, uint32_t width, + uint32_t height) { // TODO: faster copy - if (width == 0 || height == 0) - return; - + if(width == 0 || height == 0) return; + id_value_type d(dest_pos); id_value_type dd = dest.m_dim.width; id_value_type p(pos); id_value_type pd = m_dim.width; - do { + do + { bittable::bitarray::copy(dest, id_type(d), width, id_type(p)); d += dd; p += pd; - } while (--height != 0); + } while(--height != 0); } /// diff --git a/include/warthog/util/experiment.h b/include/warthog/util/experiment.h index a0bf04a..db02007 100644 --- a/include/warthog/util/experiment.h +++ b/include/warthog/util/experiment.h @@ -35,15 +35,15 @@ class experiment uint32_t sx, uint32_t sy, uint32_t gx, uint32_t gy, uint32_t mapwidth, uint32_t mapheight, double d, std::string_view m) : startx_(sx), starty_(sy), goalx_(gx), goaly_(gy), - mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), - map_(m), precision_(4) + mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), map_(m), + precision_(4) { } experiment( double sx, double sy, double gx, double gy, uint32_t mapwidth, uint32_t mapheight, double d, std::string_view m) : startx_(sx), starty_(sy), goalx_(gx), goaly_(gy), - mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), - map_(m), precision_(4) + mapwidth_(mapwidth), mapheight_(mapheight), distance_(d), map_(m), + precision_(4) { } // no copy diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index bfddc52..e8ac099 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -34,36 +34,50 @@ namespace warthog::util struct scenario_command { - enum type_ : uint8_t { + enum type_ : uint8_t + { SNAPSHOT, PATCH, QUERY }; - int type; ///< command type + int type; ///< command type int32_t bucket; ///< bucket id number (meta), snapshot id for dynamic - uint32_t id; ///< SNAPSHOT: snapshot num, PATCH: patch to apply, QUERY: query id - union cmd_ { - struct snapshot_ { } snapshot; - struct patch_ { + uint32_t + id; ///< SNAPSHOT: snapshot num, PATCH: patch to apply, QUERY: query id + union cmd_ + { + struct snapshot_ + { + } snapshot; + struct patch_ + { uint16_t topleft_x; uint16_t topleft_y; } patch; - struct query_ { + struct query_ + { uint32_t experiment_id; ///< experiment number } query; } cmd; ///< command union based on type - static constexpr scenario_command make_snapshot(int32_t bucket_id, uint32_t snapshot_id) noexcept + static constexpr scenario_command + make_snapshot(int32_t bucket_id, uint32_t snapshot_id) noexcept { - return scenario_command{SNAPSHOT, bucket_id, snapshot_id, {.snapshot={}}}; + return scenario_command{ + SNAPSHOT, bucket_id, snapshot_id, {.snapshot = {}}}; } - static constexpr scenario_command make_patch(int32_t bucket_id, uint32_t patch_id, uint16_t x, uint16_t y) noexcept + static constexpr scenario_command + make_patch( + int32_t bucket_id, uint32_t patch_id, uint16_t x, uint16_t y) noexcept { - return scenario_command{PATCH, bucket_id, patch_id, {.patch={x,y}}}; + return scenario_command{PATCH, bucket_id, patch_id, {.patch = {x, y}}}; } - static constexpr scenario_command make_query(int32_t bucket_id, uint32_t query_id, uint32_t experiment_id) noexcept + static constexpr scenario_command + make_query( + int32_t bucket_id, uint32_t query_id, uint32_t experiment_id) noexcept { - return scenario_command{QUERY, bucket_id, query_id, {.query={experiment_id}}}; + return scenario_command{ + QUERY, bucket_id, query_id, {.query = {experiment_id}}}; } }; @@ -76,8 +90,11 @@ class scenario_manager experiment* get_experiment(uint32_t which) { - if(which >= experiments_.size()) { - WARTHOG_GWARN_FMT("scenario has max {} experiments, cannot retrive {}", experiments_.size(), which); + if(which >= experiments_.size()) + { + WARTHOG_GWARN_FMT( + "scenario has max {} experiments, cannot retrive {}", + experiments_.size(), which); return nullptr; } return experiments_[which]; @@ -85,8 +102,11 @@ class scenario_manager const experiment* get_experiment(uint32_t which) const { - if(which >= experiments_.size()) { - WARTHOG_GWARN_FMT("scenario has max {} experiments, cannot retrive {}", experiments_.size(), which); + if(which >= experiments_.size()) + { + WARTHOG_GWARN_FMT( + "scenario has max {} experiments, cannot retrive {}", + experiments_.size(), which); return nullptr; } return experiments_[which]; @@ -158,36 +178,51 @@ class scenario_manager void write_scenario(std::ostream& out); - std::string_view get_cost_type() const noexcept + std::string_view + get_cost_type() const noexcept { return cost_type_; } - void set_cost_type(std::string_view v) noexcept + void + set_cost_type(std::string_view v) noexcept { cost_type_ = v; } - void set_cost_type(io::cost_type c) noexcept + void + set_cost_type(io::cost_type c) noexcept { cost_type_ = io::scenario_serialize::get_dist_str(c); } - bool is_static_scenario() const noexcept { return static_scenario_start_ >= 0; } - int32_t get_static_scenario_start() const noexcept { return static_scenario_start_; } + bool + is_static_scenario() const noexcept + { + return static_scenario_start_ >= 0; + } + int32_t + get_static_scenario_start() const noexcept + { + return static_scenario_start_; + } - uint32_t get_scenario_width() const noexcept + uint32_t + get_scenario_width() const noexcept { return scenario_width_; } - void set_scenario_width(uint32_t width) noexcept + void + set_scenario_width(uint32_t width) noexcept { scenario_width_ = width; } - uint32_t get_scenario_height() const noexcept + uint32_t + get_scenario_height() const noexcept { return scenario_height_; } - void set_scenario_height(uint32_t height) noexcept + void + set_scenario_height(uint32_t height) noexcept { scenario_height_ = height; } @@ -201,7 +236,8 @@ class scenario_manager std::errc load_gppc_scenario_body_v2(io::scenario_serialize& si); - std::string_view copy_string(std::string_view str); + std::string_view + copy_string(std::string_view str); std::pmr::monotonic_buffer_resource experiments_res_; std::vector experiments_; @@ -211,11 +247,13 @@ class scenario_manager std::filesystem::path mfile_; std::string cost_type_; io::scenario_version version_ = io::scenario_version::UNKNOWN; - uint32_t scenario_width_ = 0; - uint32_t scenario_height_ = 0; - uint32_t query_count_ = 0; - uint32_t patch_count_ = 0; - int32_t static_scenario_start_ = -1; ///< >=0: is static scenario where query commands start at pos, else is dynamic scenario + uint32_t scenario_width_ = 0; + uint32_t scenario_height_ = 0; + uint32_t query_count_ = 0; + uint32_t patch_count_ = 0; + int32_t static_scenario_start_ + = -1; ///< >=0: is static scenario where query commands start at pos, + ///< else is dynamic scenario }; std::filesystem::path diff --git a/include/warthog/util/string.h b/include/warthog/util/string.h index 3a60200..2419e4d 100644 --- a/include/warthog/util/string.h +++ b/include/warthog/util/string.h @@ -11,155 +11,187 @@ // @created: 2026-04-14 // -#include -#include #include -#include +#include #include +#include #include -#include +#include #include #include +#include namespace warthog::util { /// @brief remove white spaces (as std::isspace) from beginning of string_view -inline std::string_view ltrim(std::string_view str) +inline std::string_view +ltrim(std::string_view str) { - str.remove_prefix(std::find_if(str.begin(), str.end(), [](char c) { return !isspace((unsigned char)c); }) - str.begin()); + str.remove_prefix( + std::find_if( + str.begin(), str.end(), + [](char c) { return !isspace((unsigned char)c); }) + - str.begin()); return str; } /// @brief remove white spaces (as std::isspace) from end of string_view -inline std::string_view rtrim(std::string_view str) +inline std::string_view +rtrim(std::string_view str) { - str.remove_suffix(std::find_if(str.rbegin(), str.rend(), [](char c) { return !isspace((unsigned char)c); }) - str.rbegin()); + str.remove_suffix( + std::find_if( + str.rbegin(), str.rend(), + [](char c) { return !isspace((unsigned char)c); }) + - str.rbegin()); return str; } -/// @brief string_view is either empty or only contains spaces (as std::isspace) -inline bool is_blank(std::string_view str) +/// @brief string_view is either empty or only contains spaces (as +/// std::isspace) +inline bool +is_blank(std::string_view str) { - return std::all_of(str.begin(), str.end(), [](char c) { return isspace((unsigned char)c); }); + return std::all_of(str.begin(), str.end(), [](char c) { + return isspace((unsigned char)c); + }); } struct token_return { std::string_view token; ///< token extracted - size_t trim; ///< amount of space discarded from front of string + size_t trim; ///< amount of space discarded from front of string }; -inline token_return get_token(std::string_view str) +inline token_return +get_token(std::string_view str) { auto fstr = ltrim(str); - if (fstr.empty()) { - return {std::string_view(), str.size()}; - } - auto endsp = std::find_if(fstr.begin(), fstr.end(), [](char c) { return isspace((unsigned char)c); }) - fstr.begin(); + if(fstr.empty()) { return {std::string_view(), str.size()}; } + auto endsp = std::find_if( + fstr.begin(), fstr.end(), + [](char c) { return isspace((unsigned char)c); }) + - fstr.begin(); token_return ret; ret.token = fstr.substr(0, endsp); - ret.trim = str.size() - fstr.size(); + ret.trim = str.size() - fstr.size(); return ret; } -inline token_return get_token_quoted(std::string_view str, char quote = '"', char escape = '\\') +inline token_return +get_token_quoted(std::string_view str, char quote = '"', char escape = '\\') { - if (quote == escape) { - return {std::string_view(), 0}; - } + if(quote == escape) { return {std::string_view(), 0}; } auto fstr = ltrim(str); - if (fstr.empty()) { - return {std::string_view(), str.size()}; - } + if(fstr.empty()) { return {std::string_view(), str.size()}; } token_return ret; ret.trim = str.size() - fstr.size(); - if (fstr[0] == quote) { + if(fstr[0] == quote) + { // quoted size_t len = 0; - while(true){ - len = fstr.find(quote, len+1); - if (len == std::string_view::npos) { + while(true) + { + len = fstr.find(quote, len + 1); + if(len == std::string_view::npos) + { // no quote close return {std::string_view(), ret.trim}; } - auto epos = fstr.find_last_not_of(escape, len-1) - len; - if ((epos & 1) != 0) { + auto epos = fstr.find_last_not_of(escape, len - 1) - len; + if((epos & 1) != 0) + { // not delimited, return - ret.token = fstr.substr(0, len+1); + ret.token = fstr.substr(0, len + 1); break; } // delimited, continue } - } else { + } + else + { // no delimiter, read as normal - auto endsp = std::find_if(fstr.begin(), fstr.end(), [](char c) { return isspace((unsigned char)c); }) - fstr.begin(); + auto endsp = std::find_if( + fstr.begin(), fstr.end(), + [](char c) { return isspace((unsigned char)c); }) + - fstr.begin(); ret.token = fstr.substr(0, endsp); } return ret; } -inline std::errc parse_token(std::string_view token, std::integral auto& out, int base = 10) +inline std::errc +parse_token(std::string_view token, std::integral auto& out, int base = 10) { - const char*const f = token.data(); - const char*const ed = token.data() + token.size(); - auto res = std::from_chars(token.data(), ed, out, base); - if (res.ec != std::errc{}) - return res.ec; + const char* const f = token.data(); + const char* const ed = token.data() + token.size(); + auto res = std::from_chars(token.data(), ed, out, base); + if(res.ec != std::errc{}) return res.ec; // value set - if (res.ptr != ed) { - return std::errc::invalid_argument; - } + if(res.ptr != ed) { return std::errc::invalid_argument; } return std::errc{}; } -inline std::errc parse_token(std::string_view token, std::floating_point auto& out, std::chars_format fmt = std::chars_format::general) +inline std::errc +parse_token( + std::string_view token, std::floating_point auto& out, + std::chars_format fmt = std::chars_format::general) { - const char*const ed = token.data() + token.size(); - auto res = std::from_chars(token.data(), ed, out, fmt); - if (res.ec != std::errc{}) - return res.ec; + const char* const ed = token.data() + token.size(); + auto res = std::from_chars(token.data(), ed, out, fmt); + if(res.ec != std::errc{}) return res.ec; // value set - if (res.ptr != ed) { - return std::errc::invalid_argument; - } + if(res.ptr != ed) { return std::errc::invalid_argument; } return std::errc{}; } -inline std::pair parse_token_part(std::string_view token, std::integral auto& out, int base = 10) +inline std::pair +parse_token_part( + std::string_view token, std::integral auto& out, int base = 10) { - std::from_chars_result res = std::from_chars(token.data(), token.data() + token.size(), out, base); - if (res.ec != std::errc{}) - return {{}, res.ec}; + std::from_chars_result res = std::from_chars( + token.data(), token.data() + token.size(), out, base); + if(res.ec != std::errc{}) return {{}, res.ec}; // value set token.remove_prefix(res.ptr - token.data()); return {token, {}}; } -inline std::pair parse_token_part(std::string_view token, std::floating_point auto& out, std::chars_format fmt = std::chars_format::general) +inline std::pair +parse_token_part( + std::string_view token, std::floating_point auto& out, + std::chars_format fmt = std::chars_format::general) { - std::from_chars_result res = std::from_chars(token.data(), token.data() + token.size(), out, fmt); - if (res.ec != std::errc{}) - return {{}, res.ec}; + std::from_chars_result res + = std::from_chars(token.data(), token.data() + token.size(), out, fmt); + if(res.ec != std::errc{}) return {{}, res.ec}; // value set token.remove_prefix(res.ptr - token.data()); return {token, {}}; } -inline std::pair parse_token_quoted(std::string_view token, char* buffer, size_t size, char quote = '"', char escape = '\\') +inline std::pair +parse_token_quoted( + std::string_view token, char* buffer, size_t size, char quote = '"', + char escape = '\\') { - if (quote == escape) { - return {nullptr, std::errc::invalid_argument}; - } - if (token.empty() || size == 0) { + if(quote == escape) { return {nullptr, std::errc::invalid_argument}; } + if(token.empty() || size == 0) + { return {nullptr, std::errc::invalid_argument}; } - const char*const ed = token.end() + token.size(); - if (token[0] != '"') { + const char* const ed = token.end() + token.size(); + if(token[0] != '"') + { // not quoted, take as whole token - if (std::any_of(token.data(), ed, [](char c) { return std::isspace((unsigned char)c); })) { + if(std::any_of(token.data(), ed, [](char c) { + return std::isspace((unsigned char)c); + })) + { // space detected, error return {nullptr, std::errc::invalid_argument}; } - if (token.size()+1 > size) { + if(token.size() + 1 > size) + { // too large to hold token return {nullptr, std::errc::value_too_large}; } @@ -169,39 +201,55 @@ inline std::pair parse_token_quoted(std::string_view token, cha } // quoted text, process const char* t_at = token.data() + 1; - while (true) { + while(true) + { assert(size > 0); - if (t_at >= ed) { + if(t_at >= ed) + { // reached end of token with no end-quote return {buffer, std::errc::invalid_argument}; } - if (*t_at == '\\') [[unlikely]] { - if (++t_at >= ed) { + if(*t_at == '\\') [[unlikely]] + { + if(++t_at >= ed) + { // unexpected end of string return {buffer, std::errc::invalid_argument}; } - } else if (*t_at == '"') [[unlikely]] { + } + else if(*t_at == '"') [[unlikely]] + { // found end quote, append null-terminator *(buffer++) = '\0'; // check is end of token - return {buffer, t_at+1 == ed ? std::errc{} : std::errc::invalid_argument}; + return { + buffer, + t_at + 1 == ed ? std::errc{} : std::errc::invalid_argument}; } *(buffer++) = *(t_at++); - if (--size == 0) { + if(--size == 0) + { // out of space for null-terminator return {buffer, std::errc::value_too_large}; } } } -inline std::errc parse_token_quoted(std::string_view token, auto& str_out, char quote = '"', char escape = '\\') +inline std::errc +parse_token_quoted( + std::string_view token, auto& str_out, char quote = '"', + char escape = '\\') { - str_out.resize(token.size()+1); // plus 1 to hold \0 placed in buffer - auto [ed, ec] = parse_token_quoted(token, str_out.data(), str_out.size(), quote, escape); - if (ec != std::errc{}) { + str_out.resize(token.size() + 1); // plus 1 to hold \0 placed in buffer + auto [ed, ec] = parse_token_quoted( + token, str_out.data(), str_out.size(), quote, escape); + if(ec != std::errc{}) + { // failed to read, clear str_out and return error code str_out.resize(0); return ec; - } else { + } + else + { // successor, adjust str_out length and return assert(ed - str_out.data() > 0); str_out.resize((ed - str_out.data()) - 1); @@ -209,81 +257,84 @@ inline std::errc parse_token_quoted(std::string_view token, auto& str_out, char } } - class string_parser { public: string_parser(); - string_parser(std::string_view str) : m_str(str) - { } + string_parser(std::string_view str) : m_str(str) { } // error codes - void clear_error() noexcept + void + clear_error() noexcept { m_error = std::errc{}; } - std::errc error() const noexcept + std::errc + error() const noexcept { - return m_error; + return m_error; } - bool is_error() const noexcept + bool + is_error() const noexcept { return m_error != std::errc{}; } - operator bool() const noexcept - { - return m_error == std::errc{}; - } - + operator bool() const noexcept { return m_error == std::errc{}; } + // set string - std::string_view str() const noexcept + std::string_view + str() const noexcept { return m_str; } - void str(std::string_view s) noexcept + void + str(std::string_view s) noexcept { - m_str = s; + m_str = s; m_token = std::string_view{}; clear_error(); } - std::string_view last_token() const noexcept + std::string_view + last_token() const noexcept { return m_token; } - string_parser& next(std::integral auto& int_out, int base = 10) + string_parser& + next(std::integral auto& int_out, int base = 10) { - if (is_error()) // do nothing if in error state - return *this; - if (!next_token_()) + if(is_error()) // do nothing if in error state return *this; + if(!next_token_()) return *this; std::errc ec = util::parse_token(m_token, int_out, base); - if (ec != std::errc{}) - m_error = ec; + if(ec != std::errc{}) m_error = ec; return *this; } - string_parser& next(std::floating_point auto& float_out, std::chars_format fmt = std::chars_format::general) + string_parser& + next( + std::floating_point auto& float_out, + std::chars_format fmt = std::chars_format::general) { - if (is_error()) // do nothing if in error state - return *this; - if (!next_token_()) + if(is_error()) // do nothing if in error state return *this; + if(!next_token_()) return *this; std::errc ec = util::parse_token(m_token, float_out, fmt); - if (ec != std::errc{}) - m_error = ec; + if(ec != std::errc{}) m_error = ec; return *this; } - string_parser& next(std::span& buffer) + string_parser& + next(std::span& buffer) { - if (is_error()) // do nothing if in error state + if(is_error()) // do nothing if in error state return *this; - if (buffer.empty()) { + if(buffer.empty()) + { m_error = std::errc::invalid_argument; return *this; } - if (!next_token_()) - return *this; - if (m_token.size() > buffer.size()) { + if(!next_token_()) return *this; + if(m_token.size() > buffer.size()) + { m_error = std::errc::value_too_large; return *this; } @@ -291,50 +342,54 @@ class string_parser buffer = buffer.subspan(0, m_token.size()); return *this; } - string_parser& next(std::string_view& out_token) + string_parser& + next(std::string_view& out_token) { - if (is_error()) // do nothing if in error state - return *this; - if (!next_token_()) + if(is_error()) // do nothing if in error state return *this; + if(!next_token_()) return *this; out_token = m_token; return *this; } - string_parser& next(std::string& out_token) + string_parser& + next(std::string& out_token) { - if (is_error()) // do nothing if in error state - return *this; - if (!next_token_()) + if(is_error()) // do nothing if in error state return *this; + if(!next_token_()) return *this; out_token = m_token; return *this; } - string_parser& next(std::pmr::string& out_token) + string_parser& + next(std::pmr::string& out_token) { - if (is_error()) // do nothing if in error state - return *this; - if (!next_token_()) + if(is_error()) // do nothing if in error state return *this; + if(!next_token_()) return *this; out_token = m_token; return *this; } - string_parser& next_q(std::span& buffer, char quote = '"', char escape = '\\') + string_parser& + next_q(std::span& buffer, char quote = '"', char escape = '\\') { - if (is_error()) // do nothing if in error state + if(is_error()) // do nothing if in error state return *this; - if (buffer.empty()) { + if(buffer.empty()) + { m_error = std::errc::invalid_argument; return *this; } - if (!next_token_quoted_(quote, escape)) - return *this; - auto [ed, ec] = util::parse_token_quoted(m_token, buffer.data(), buffer.size(), quote, escape); - if (ec != std::errc{}) { + if(!next_token_quoted_(quote, escape)) return *this; + auto [ed, ec] = util::parse_token_quoted( + m_token, buffer.data(), buffer.size(), quote, escape); + if(ec != std::errc{}) + { m_error = ec; return *this; } - if (ed == buffer.data()) { + if(ed == buffer.data()) + { assert(false); // should never occur m_error = std::errc::state_not_recoverable; return *this; @@ -342,52 +397,58 @@ class string_parser buffer = buffer.subspan(0, (ed - buffer.data()) - 1); return *this; } - string_parser& next_q(std::string& out_token, char quote = '"', char escape = '\\') + string_parser& + next_q(std::string& out_token, char quote = '"', char escape = '\\') { - if (is_error()) // do nothing if in error state - return *this; - if (!next_token_quoted_(quote, escape)) + if(is_error()) // do nothing if in error state return *this; - std::errc ec = util::parse_token_quoted(m_token, out_token, quote, escape); - if (ec != std::errc{}) { + if(!next_token_quoted_(quote, escape)) return *this; + std::errc ec + = util::parse_token_quoted(m_token, out_token, quote, escape); + if(ec != std::errc{}) + { m_error = ec; return *this; } return *this; } - string_parser& next_q(std::pmr::string& out_token, char quote = '"', char escape = '\\') + string_parser& + next_q(std::pmr::string& out_token, char quote = '"', char escape = '\\') { - if (is_error()) // do nothing if in error state - return *this; - if (!next_token_quoted_(quote, escape)) + if(is_error()) // do nothing if in error state return *this; - std::errc ec = util::parse_token_quoted(m_token, out_token, quote, escape); - if (ec != std::errc{}) { + if(!next_token_quoted_(quote, escape)) return *this; + std::errc ec + = util::parse_token_quoted(m_token, out_token, quote, escape); + if(ec != std::errc{}) + { m_error = ec; return *this; } return *this; } - string_parser& ignore() + string_parser& + ignore() { - if (is_error()) // do nothing if in error state + if(is_error()) // do nothing if in error state return *this; next_token_(); return *this; } - string_parser& ignore_q(char quote = '"', char escape = '\\') + string_parser& + ignore_q(char quote = '"', char escape = '\\') { - if (is_error()) // do nothing if in error state + if(is_error()) // do nothing if in error state return *this; next_token_quoted_(quote, escape); return *this; } - bool eof() + bool + eof() { - if (is_error()) - return false; - if (!util::is_blank(m_str)) + if(is_error()) return false; + if(!util::is_blank(m_str)) { m_error = std::errc::invalid_argument; return false; @@ -396,28 +457,36 @@ class string_parser } protected: - bool next_token_() + bool + next_token_() { auto ret = util::get_token(m_str); - if (ret.token.empty()) { + if(ret.token.empty()) + { // failed to read a token, error m_error = std::errc::invalid_argument; return false; - } else { + } + else + { m_token = ret.token; assert(ret.trim <= m_str.size()); m_str.remove_prefix(ret.token.size() + ret.trim); return true; } } - bool next_token_quoted_(char quote, char escape) + bool + next_token_quoted_(char quote, char escape) { auto ret = util::get_token_quoted(m_str, quote, escape); - if (ret.token.empty()) { + if(ret.token.empty()) + { // failed to read a token, error m_error = std::errc::invalid_argument; return false; - } else { + } + else + { m_token = ret.token; assert(ret.trim <= m_str.size()); m_str.remove_prefix(ret.token.size() + ret.trim); @@ -431,7 +500,6 @@ class string_parser std::errc m_error = std::errc{}; }; - } // namespace warthog::util #endif // WARTHOG_UTIL_STRING_H diff --git a/src/domain/gridmap.cpp b/src/domain/gridmap.cpp index 2a16e9d..48fd5fd 100644 --- a/src/domain/gridmap.cpp +++ b/src/domain/gridmap.cpp @@ -34,40 +34,48 @@ gridmap::gridmap(std::filesystem::path&& filename) setup_stream_(in); } -gridmap::gridmap(const std::filesystem::path& filename) : gridmap(std::filesystem::path(filename)) +gridmap::gridmap(const std::filesystem::path& filename) + : gridmap(std::filesystem::path(filename)) { } -gridmap::gridmap(const char* filename) : gridmap(std::filesystem::path(filename)) +gridmap::gridmap(const char* filename) + : gridmap(std::filesystem::path(filename)) { } -void gridmap::setup(uint32_t h, uint32_t w) +void +gridmap::setup(uint32_t h, uint32_t w) { header_.height_ = h; - header_.width_ = w; + header_.width_ = w; this->init_db(); } -void gridmap::load(std::istream& input) +void +gridmap::load(std::istream& input) { setup_stream_(input); } -void gridmap::load(io::bittable_serialize& parser) +void +gridmap::load(io::bittable_serialize& parser) { setup_ser_(parser); } -void gridmap::load(std::filesystem::path&& filename) +void +gridmap::load(std::filesystem::path&& filename) { filename_ = std::move(filename); std::ifstream in(filename_); setup_stream_(in); } -void gridmap::load(const std::filesystem::path& filename) +void +gridmap::load(const std::filesystem::path& filename) { load(std::filesystem::path(filename)); } -void gridmap::load(const char* filename) +void +gridmap::load(const char* filename) { load(std::filesystem::path(filename)); } @@ -85,7 +93,7 @@ gridmap::setup_stream_(std::istream& in) void gridmap::setup_ser_(io::bittable_serialize& parser) { - if (parser.read_grid_header() != std::errc{}) + if(parser.read_grid_header() != std::errc{}) throw std::runtime_error("invalid grid format"); this->header_.type_ = "octile"; this->header_.width_ = parser.get_dim().width; diff --git a/src/io/grid.cpp b/src/io/grid.cpp index 24e5c55..8cdb474 100644 --- a/src/io/grid.cpp +++ b/src/io/grid.cpp @@ -11,27 +11,17 @@ bittable_serialize::read_header(std::istream* in) { std::errc err; std::tie(in, err) = get_istream(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } std::string_view line; std::string_view token; std::tie(line, err) = readline(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } bittable_type detected_type = bittable_type::NONE; { - parser par(line); - if (!par.next(token) || token != "type") { - return std::errc::io_error; - } - if (!par.next(token).eof()) { - return std::errc::io_error; - } + parser par(line); + if(!par.next(token) || token != "type") { return std::errc::io_error; } + if(!par.next(token).eof()) { return std::errc::io_error; } } if(token == "octile") @@ -43,27 +33,24 @@ bittable_serialize::read_header(std::istream* in) m_type = detected_type; // read patch header before map header - if (detected_type == bittable_type::OCTILE) { - m_patch_amount = 1; - } else - if(detected_type == bittable_type::PATCH) + if(detected_type == bittable_type::OCTILE) { m_patch_amount = 1; } + else if(detected_type == bittable_type::PATCH) { std::tie(line, err) = readline(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } parser par(line); - if (!par.next(token).next(m_patch_amount).eof()) { + if(!par.next(token).next(m_patch_amount).eof()) + { return std::errc::io_error; } - if (token != "patches" || m_patch_amount > PATCH_COUNT_LIMIT) { + if(token != "patches" || m_patch_amount > PATCH_COUNT_LIMIT) + { m_patch_amount = 0; return std::errc::argument_out_of_domain; } } m_patch_count = 0; - m_patch_id = 0; + m_patch_id = 0; return std::errc{}; } @@ -73,10 +60,7 @@ bittable_serialize::read_grid_header(std::istream* in) { std::errc err; std::tie(in, err) = get_istream(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } std::string_view line; std::string_view token; @@ -84,79 +68,62 @@ bittable_serialize::read_grid_header(std::istream* in) // read height std::tie(line, err) = readline(in); - if(err != std::errc{}) + if(err != std::errc{}) { return err; } + if(m_type == bittable_type::PATCH) { - return err; - } - if (m_type == bittable_type::PATCH) { // get patch number parser par(line); - if (!par.next(token).next(m_patch_id).eof()) + if(!par.next(token).next(m_patch_id).eof()) { return std::errc::io_error; } - if (token != "patch") { - return std::errc::argument_out_of_domain; - } - + if(token != "patch") { return std::errc::argument_out_of_domain; } + std::tie(line, err) = readline(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } } { parser par(line); - if (!par.next(token).next(m_dim.height).eof()) + if(!par.next(token).next(m_dim.height).eof()) { return std::errc::io_error; } - if (token != "height" || m_dim.height > GRID_DIMENSION_MAX) { + if(token != "height" || m_dim.height > GRID_DIMENSION_MAX) + { return std::errc::argument_out_of_domain; } } // read width std::tie(line, err) = readline(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } { parser par(line); - if (!par.next(token).next(m_dim.width).eof()) + if(!par.next(token).next(m_dim.width).eof()) { return std::errc::io_error; } - if (token != "width" || m_dim.width > GRID_DIMENSION_MAX) { + if(token != "width" || m_dim.width > GRID_DIMENSION_MAX) + { return std::errc::argument_out_of_domain; } } // read "map" std::tie(line, err) = readline(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } { parser par(line); - if (!par.next(token).eof()) - { - return std::errc::io_error; - } - if (token != "map") { - return std::errc::argument_out_of_domain; - } + if(!par.next(token).eof()) { return std::errc::io_error; } + if(token != "map") { return std::errc::argument_out_of_domain; } } return std::errc{}; } std::errc -bittable_serialize::read_grid_raw( - std::span buffer, std::istream* in) +bittable_serialize::read_grid_raw(std::span buffer, std::istream* in) { const memory::bittable_dimension read_dim = m_dim; - if (buffer.size() < (uint64_t)read_dim.width * (uint64_t)read_dim.height) + if(buffer.size() < (uint64_t)read_dim.width * (uint64_t)read_dim.height) { return std::errc::result_out_of_range; } @@ -165,25 +132,16 @@ bittable_serialize::read_grid_raw( std::errc err; std::tie(in, err) = get_istream(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } std::string_view line; for(uint32_t y = 0; y < read_dim.height; ++y) { // read row std::tie(line, err) = readline(in); - if(err != std::errc{}) - { - return err; - } + if(err != std::errc{}) { return err; } parser par(line); - if (!par.next(token).eof()) - { - return std::errc::io_error; - } - if (token.size() != read_dim.width) + if(!par.next(token).eof()) { return std::errc::io_error; } + if(token.size() != read_dim.width) return std::errc::argument_out_of_domain; // copy row to table token.copy(data_at, token.size()); diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 487b209..1a68ac5 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -21,8 +21,8 @@ void scenario_serialize::close() { serialize_base::close(); - m_state = serialize_state::INIT; - m_version = scenario_version::UNKNOWN; + m_state = serialize_state::INIT; + m_version = scenario_version::UNKNOWN; m_map_width = 0; m_map_height = 0; m_query_at = 0; @@ -35,16 +35,15 @@ void scenario_serialize::set_relative_map_filename( const std::filesystem::path& filename) { - m_map_filename = std::filesystem::proximate(filename, get_scenario_filename()); + m_map_filename + = std::filesystem::proximate(filename, get_scenario_filename()); } int scenario_serialize::last_command_type() const { - if (m_command_type == "Q") - return CMD_QUERY; - if (m_command_type == "P") - return CMD_PATCH; + if(m_command_type == "Q") return CMD_QUERY; + if(m_command_type == "P") return CMD_PATCH; return CMD_UNKNOWN; } @@ -127,8 +126,8 @@ scenario_serialize::read_header_v1(std::istream* in) m_cost_type.resize(1); m_cost_value.resize(1); m_cost_strings[0] = get_dist_str(cost_type::G_8C_NCC); - m_cost_type[0] = cost_type::G_8C_NCC; - m_cost_value[0] = -1; + m_cost_type[0] = cost_type::G_8C_NCC; + m_cost_value[0] = -1; // read first query line to get map auto [line, err] = readline(in); @@ -155,7 +154,7 @@ scenario_serialize::read_header_v1(std::istream* in) return ec; } if(m_map_width <= 0 || m_map_width > GRID_MAX_SIZE || m_map_height <= 0 - || m_map_height > GRID_MAX_SIZE) + || m_map_height > GRID_MAX_SIZE) { m_state = serialize_state::ERROR; return std::errc::invalid_argument; @@ -194,7 +193,8 @@ scenario_serialize::read_header_v2(std::istream* in) m_state = serialize_state::ERROR; return par.error(); } - if(token != "height" || m_map_height < 1 || m_map_height > GRID_MAX_SIZE) + if(token != "height" || m_map_height < 1 + || m_map_height > GRID_MAX_SIZE) { m_state = serialize_state::ERROR; return std::errc::invalid_argument; @@ -237,7 +237,7 @@ scenario_serialize::read_header_v2(std::istream* in) m_state = serialize_state::ERROR; return par.error(); } - if (token != "cost" || cost_count < 0 || cost_count > 128) + if(token != "cost" || cost_count < 0 || cost_count > 128) { // limit cost_count to be within reason m_state = serialize_state::ERROR; @@ -246,18 +246,21 @@ scenario_serialize::read_header_v2(std::istream* in) m_cost_strings.resize(cost_count); m_cost_type.resize(cost_count); m_cost_value.resize(cost_count); - for (int i = 0; i < cost_count; ++i) { - if (!par.next(token)) { + for(int i = 0; i < cost_count; ++i) + { + if(!par.next(token)) + { m_state = serialize_state::ERROR; return par.error(); } std::string_view ctype = copy_string(token); - m_cost_strings[i] = ctype; - m_cost_type[i] = get_cost_type(ctype); - m_cost_value[i] = -1; + m_cost_strings[i] = ctype; + m_cost_type[i] = get_cost_type(ctype); + m_cost_value[i] = -1; } // end of line - if (!par.eof()) { + if(!par.eof()) + { m_state = serialize_state::ERROR; return par.error(); } @@ -329,33 +332,37 @@ scenario_serialize::next_command_type(std::istream* in) switch(m_version) { - case scenario_version::VERSION_1: { - auto [line,ec] = readline(in, true); - if(ec != std::errc{}) { + case scenario_version::VERSION_1: + { + auto [line, ec] = readline(in, true); + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } - if(istream_eof(in)) { + if(istream_eof(in)) + { m_state = serialize_state::END; return {FINAL, {}}; } unreadline(line); return {CMD_QUERY, {}}; } - case scenario_version::VERSION_2: { - auto [line,ec] = readline(in, true); - if(ec != std::errc{}) { + case scenario_version::VERSION_2: + { + auto [line, ec] = readline(in, true); + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } - if(istream_eof(in)) { + if(istream_eof(in)) + { m_state = serialize_state::END; return {FINAL, {}}; } parser par(line); - if (!par.next(m_command_type)) { - return {INVALID, par.error()}; - } + if(!par.next(m_command_type)) { return {INVALID, par.error()}; } auto cmd_type = last_command_type(); unreadline(line); return {cmd_type, {}}; @@ -368,10 +375,7 @@ scenario_serialize::next_command_type(std::istream* in) std::errc scenario_serialize::skip_commands(int count, std::istream* in) { - if (m_state == serialize_state::END) - { - return {}; - } + if(m_state == serialize_state::END) { return {}; } if(!can_read(in) || m_state != serialize_state::COMMAND) { return std::errc::state_not_recoverable; @@ -380,18 +384,22 @@ scenario_serialize::skip_commands(int count, std::istream* in) std::errc ec; std::string_view line; std::tie(in, ec) = get_istream(in); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return ec; } - while (count > 0) { + while(count > 0) + { --count; std::tie(line, ec) = readline(in, true); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return ec; } - if(istream_eof(in)) { + if(istream_eof(in)) + { m_state = serialize_state::END; break; } @@ -402,16 +410,14 @@ scenario_serialize::skip_commands(int count, std::istream* in) std::pair scenario_serialize::read_query_line(scenario_query& query, std::istream* in) { - if (m_state == serialize_state::END) - { - return {FINAL, {}}; - } + if(m_state == serialize_state::END) { return {FINAL, {}}; } if(!can_read(in) || m_state != serialize_state::COMMAND) { return {0, std::errc::state_not_recoverable}; } - switch (m_version) { + switch(m_version) + { case scenario_version::VERSION_1: return read_query_line_v1(query, in); case scenario_version::VERSION_2: @@ -431,28 +437,38 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) std::string_view token; std::tie(in, ec) = get_istream(in); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } std::tie(line, ec) = readline(in, true); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } - if(istream_eof(in)) { + if(istream_eof(in)) + { m_state = serialize_state::END; return {FINAL, std::errc{}}; } parser par(line); - if(!par.next(query.bucket).next(token).next(query.width).next(query.height) - .next(query.start_x).next(query.start_y).next(query.goal_x).next(query.goal_y) - .next(m_cost_value.at(0)).eof()) + if(!par.next(query.bucket) + .next(token) + .next(query.width) + .next(query.height) + .next(query.start_x) + .next(query.start_y) + .next(query.goal_x) + .next(query.goal_y) + .next(m_cost_value.at(0)) + .eof()) { // state not set to error, up to user return {INVALID, par.error()}; } - query.map = token; + query.map = token; query.dist = m_cost_value; if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) @@ -502,30 +518,36 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) std::string_view token; std::tie(in, ec) = get_istream(in); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } std::tie(line, ec) = readline(in, true); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } - if(istream_eof(in)) { + if(istream_eof(in)) + { m_state = serialize_state::END; return {FINAL, std::errc{}}; } parser par(line); - if (!par.next(m_command_type)) { - return {INVALID, par.error()}; - } + if(!par.next(m_command_type)) { return {INVALID, par.error()}; } auto cmd_type = last_command_type(); - if (cmd_type != CMD_QUERY) { + if(cmd_type != CMD_QUERY) + { unreadline(line); return {cmd_type, std::errc{}}; } - if(!par.next(query.bucket).next(query.start_x).next(query.start_y).next(query.goal_x).next(query.goal_y)) + if(!par.next(query.bucket) + .next(query.start_x) + .next(query.start_y) + .next(query.goal_x) + .next(query.goal_y)) { return {INVALID, par.error()}; } @@ -535,17 +557,17 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) return {INVALID, std::errc::invalid_argument}; } query.width = query.height = 0; - for (auto& cost : m_cost_value) { - if (!par.next(cost)) { - return {INVALID, par.error()}; - } - if (!std::isfinite(cost)) + for(auto& cost : m_cost_value) + { + if(!par.next(cost)) { return {INVALID, par.error()}; } + if(!std::isfinite(cost)) { return {INVALID, std::errc::invalid_argument}; } } query.dist = m_cost_value; - if (!par.eof()) { + if(!par.eof()) + { // unexpected number of paramters return {INVALID, par.error()}; } @@ -604,31 +626,37 @@ scenario_serialize::read_patch_line_v2(scenario_patch& patch, std::istream* in) std::string_view token; std::tie(in, ec) = get_istream(in); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } std::tie(line, ec) = readline(in, true); - if(ec != std::errc{}) { + if(ec != std::errc{}) + { m_state = serialize_state::ERROR; return {INVALID, ec}; } - if(istream_eof(in)) { + if(istream_eof(in)) + { m_state = serialize_state::END; return {FINAL, std::errc{}}; } parser par(line); - if (!par.next(token)) { - return {INVALID, par.error()}; - } + if(!par.next(token)) { return {INVALID, par.error()}; } auto cmd_type = last_command_type(); - if (cmd_type != CMD_PATCH) { + if(cmd_type != CMD_PATCH) + { unreadline(line); return {cmd_type, std::errc{}}; } - if(!par.next(patch.bucket).next(patch.patch_id).next(patch.loc_x).next(patch.loc_y).eof()) + if(!par.next(patch.bucket) + .next(patch.patch_id) + .next(patch.loc_x) + .next(patch.loc_y) + .eof()) { return {INVALID, par.error()}; } @@ -644,9 +672,8 @@ std::string_view scenario_serialize::copy_string(std::string_view str) { size_t len = str.size(); - if (len == 0) - return std::string_view(); - char* data = static_cast(m_string_res.allocate(len+1, 8)); + if(len == 0) return std::string_view(); + char* data = static_cast(m_string_res.allocate(len + 1, 8)); std::memcpy(data, str.data(), len); data[len] = '\0'; return std::string_view(data, len); diff --git a/src/io/serialize_base.cpp b/src/io/serialize_base.cpp index e38e48c..677569d 100644 --- a/src/io/serialize_base.cpp +++ b/src/io/serialize_base.cpp @@ -1,15 +1,16 @@ #include -#include +#include +#include #include #include -#include -#include +#include namespace warthog::io { -serialize_base::serialize_base() : m_line_data(std::make_unique(max_line_length)) +serialize_base::serialize_base() + : m_line_data(std::make_unique(max_line_length)) { } serialize_base::~serialize_base() = default; @@ -20,15 +21,15 @@ serialize_base::open_read(std::istream* is) if(is != nullptr) { m_stream_in = is; } else { - auto stream = std::make_unique(get_filename()); + auto stream = std::make_unique(get_filename()); m_stream_in = stream.get(); - m_stream = std::move(stream); + m_stream = std::move(stream); } if(!*m_stream_in) { // bad stream - m_stream_in = nullptr; - m_stream = nullptr; + m_stream_in = nullptr; + m_stream = nullptr; return std::errc::io_error; } return std::errc{}; @@ -41,15 +42,15 @@ serialize_base::open_write(std::ostream* os) if(os != nullptr) { m_stream_out = os; } else { - auto stream = std::make_unique(get_filename()); + auto stream = std::make_unique(get_filename()); m_stream_out = stream.get(); - m_stream = std::move(stream); + m_stream = std::move(stream); } if(!*m_stream_out) { // bad stream - m_stream_out = nullptr; - m_stream = nullptr; + m_stream_out = nullptr; + m_stream = nullptr; return std::errc::io_error; } return std::errc{}; @@ -58,11 +59,11 @@ serialize_base::open_write(std::ostream* os) void serialize_base::close() { - m_line_num = 0; - m_line = std::string_view(); + m_line_num = 0; + m_line = std::string_view(); m_unget_line.clear(); - m_stream = nullptr; - m_stream_in = nullptr; + m_stream = nullptr; + m_stream_in = nullptr; m_stream_out = nullptr; } @@ -70,8 +71,7 @@ bool serialize_base::istream_eof(std::istream* in) { auto [s, err] = get_istream(in); - if (err != std::errc{}) - return false; + if(err != std::errc{}) return false; return s->eof(); } @@ -80,7 +80,8 @@ serialize_base::readline(std::istream* in, bool skip_blanks) { size_t len; auto [s, err] = get_istream(in); - while (true) { + while(true) + { if(len = m_unget_line.size(); len != 0) { // return last unreadline @@ -88,12 +89,15 @@ serialize_base::readline(std::istream* in, bool skip_blanks) { return {{}, std::errc::invalid_argument}; } - if (len == 1 && m_unget_line[0] == '\0') { + if(len == 1 && m_unget_line[0] == '\0') + { // empty line m_line = std::string_view(); - } else { + } + else + { // copy line - std::memcpy(m_line_data.get(), m_unget_line.c_str(), len+1); + std::memcpy(m_line_data.get(), m_unget_line.c_str(), len + 1); m_line = std::string_view(m_line_data.get(), len); } m_unget_line.clear(); @@ -101,26 +105,30 @@ serialize_base::readline(std::istream* in, bool skip_blanks) else { if(err != std::errc{}) { return {{}, err}; } - if (s->eof()) { - return {{}, std::errc::io_error}; - } + if(s->eof()) { return {{}, std::errc::io_error}; } if(!s->getline(m_line_data.get(), max_line_length)) { - if (s->eof() && s->gcount() == 0) { + if(s->eof() && s->gcount() == 0) + { s->clear(std::ios::eofbit); - // extracted no characters and reached end of file, return no error + // extracted no characters and reached end of file, return + // no error return {{}, {}}; - } else { + } + else + { // error return {{}, std::errc::io_error}; } } - // get length, if eof is set, is last line and no delimiter was extracted + // get length, if eof is set, is last line and no delimiter was + // extracted len = static_cast(s->gcount()) - (s->eof() ? 0 : 1); - if (len == 0) { - m_line = std::string_view(); - } else { - if (std::iscntrl(m_line_data[len-1])) { + if(len == 0) { m_line = std::string_view(); } + else + { + if(std::iscntrl(m_line_data[len - 1])) + { // mainly \r reading windows files in linux m_line_data[--len] = '\0'; } @@ -128,7 +136,8 @@ serialize_base::readline(std::istream* in, bool skip_blanks) } m_line_num += 1; } - if (skip_blanks && is_line_blank(m_line)) { + if(skip_blanks && is_line_blank(m_line)) + { continue; // blank line, repeat } return {m_line, {}}; @@ -137,14 +146,12 @@ serialize_base::readline(std::istream* in, bool skip_blanks) void serialize_base::unreadline(std::string_view line) { - if (!line.empty()) [[likely]] { - m_unget_line = line; - } else { - m_unget_line = '\0'; - } + if(!line.empty()) [[likely]] { m_unget_line = line; } + else { m_unget_line = '\0'; } } -bool serialize_base::is_line_blank(std::string_view line) +bool +serialize_base::is_line_blank(std::string_view line) { return std::all_of(line.begin(), line.end(), [](char c) { return std::isspace((unsigned char)c); diff --git a/src/manager/grid_patch_set.cpp b/src/manager/grid_patch_set.cpp index 0ec3563..789530c 100644 --- a/src/manager/grid_patch_set.cpp +++ b/src/manager/grid_patch_set.cpp @@ -8,88 +8,118 @@ namespace warthog::manager { -bool grid_patch_set::load(std::istream& file) +bool +grid_patch_set::load(std::istream& file) { io::bittable_serialize S; - if (auto ec = S.open_read(&file); ec != std::errc{}) { + if(auto ec = S.open_read(&file); ec != std::errc{}) + { WARTHOG_GWARN_FMT("grid patch failed to open file code={}", (int)ec); return false; } - if (int r = deserialize(S); r < 0) { + if(int r = deserialize(S); r < 0) + { WARTHOG_GWARN_FMT("grid patch failed to read patch {}", -r - 1); return false; } return true; } -bool grid_patch_set::load(const std::filesystem::path& maps) +bool +grid_patch_set::load(const std::filesystem::path& maps) { io::bittable_serialize S; S.set_filename(std::filesystem::path(maps)); - if (auto ec = S.open_read(); ec != std::errc{}) { + if(auto ec = S.open_read(); ec != std::errc{}) + { WARTHOG_GWARN_FMT("grid patch failed to open file code={}", (int)ec); return false; } - if (int r = deserialize(S); r < 0) { + if(int r = deserialize(S); r < 0) + { WARTHOG_GWARN_FMT("grid patch failed to read patch {}", -r - 1); return false; } return true; } -int grid_patch_set::deserialize(io::bittable_serialize& S, int max_grids, uint32_t flags) +int +grid_patch_set::deserialize( + io::bittable_serialize& S, int max_grids, uint32_t flags) { // read header - if (S.get_type() == io::bittable_type::NONE) { + if(S.get_type() == io::bittable_type::NONE) + { // header not read - if ((flags & SKIP_HEADER) != 0) { - WARTHOG_GWARN("grid patch SKIP_HEADER for serializer when header has not been read."); + if((flags & SKIP_HEADER) != 0) + { + WARTHOG_GWARN("grid patch SKIP_HEADER for serializer when header " + "has not been read."); return -1; } - if (auto ec = S.read_header(); ec != std::errc{}) { - WARTHOG_GWARN_FMT("grid patch failed to read header code={}", (int)ec); + if(auto ec = S.read_header(); ec != std::errc{}) + { + WARTHOG_GWARN_FMT( + "grid patch failed to read header code={}", (int)ec); return -1; } - } else { + } + else + { // header has been read, check - if ((flags & FORCE_HEADER) != 0) { - WARTHOG_GWARN("grid patch FORCE_HEADER for serializer when header has already been read."); + if((flags & FORCE_HEADER) != 0) + { + WARTHOG_GWARN("grid patch FORCE_HEADER for serializer when header " + "has already been read."); return -1; } } auto type = S.get_type(); - if (type != io::bittable_type::OCTILE && type != io::bittable_type::PATCH) { - WARTHOG_GWARN("grid patch only supports reading type octile or patch."); - return -1; + if(type != io::bittable_type::OCTILE && type != io::bittable_type::PATCH) + { + WARTHOG_GWARN( + "grid patch only supports reading type octile or patch."); + return -1; } // start reading grids int count = 0; - if (max_grids < 0) { - max_grids = std::numeric_limits::max(); - } - while (count < max_grids && S.get_patch_count() < S.get_patch_amount()) { + if(max_grids < 0) { max_grids = std::numeric_limits::max(); } + while(count < max_grids && S.get_patch_count() < S.get_patch_amount()) + { // read new grid ++count; - if (auto ec = S.read_grid_header(); ec != std::errc{}) { - WARTHOG_GWARN_FMT("grid patch failed to read patch {} header code={}", count, (int)ec); + if(auto ec = S.read_grid_header(); ec != std::errc{}) + { + WARTHOG_GWARN_FMT( + "grid patch failed to read patch {} header code={}", count, + (int)ec); return -count; } - if ((flags & IGNORE_INDEX) == 0) { + if((flags & IGNORE_INDEX) == 0) + { // check index - if (S.get_patch_id() != patches_.size()) { + if(S.get_patch_id() != patches_.size()) + { // index mismatch - WARTHOG_GWARN_FMT("grid patch failed patch {} index mismatch of {} expected {}", count, S.get_patch_id(), patches_.size()); + WARTHOG_GWARN_FMT( + "grid patch failed patch {} index mismatch of {} expected " + "{}", + count, S.get_patch_id(), patches_.size()); return -count; } } // setup patch data - auto dim = S.get_dim(); - auto bytes = bittable::calc_array_size(dim.width, dim.height); - auto* grid_data = static_cast(grid_res_.allocate(bytes, alignof(bittable::value_type))); + auto dim = S.get_dim(); + auto bytes = bittable::calc_array_size(dim.width, dim.height); + auto* grid_data = static_cast( + grid_res_.allocate(bytes, alignof(bittable::value_type))); bittable patch(grid_data, dim.width, dim.height); - if (auto ec = S.read_grid_data(patch); ec != std::errc{}) { - WARTHOG_GWARN_FMT("grid patch failed to read patch {} data code={}", count, (int)ec); + if(auto ec = S.read_grid_data(patch); ec != std::errc{}) + { + WARTHOG_GWARN_FMT( + "grid patch failed to read patch {} data code={}", count, + (int)ec); return -count; } patches_.push_back(patch); @@ -98,45 +128,56 @@ int grid_patch_set::deserialize(io::bittable_serialize& S, int max_grids, uint32 return count; } -bool grid_patch_set::push_copy(bittable table, uint16_t offset_x, uint16_t offset_y, - uint16_t width, uint16_t height) +bool +grid_patch_set::push_copy( + bittable table, uint16_t offset_x, uint16_t offset_y, uint16_t width, + uint16_t height) { - if (table.size() == 0) { - return false; - } + if(table.size() == 0) { return false; } bittable patch; - if (offset_x == 0 && offset_y == 0 && width == npos && height == npos) { + if(offset_x == 0 && offset_y == 0 && width == npos && height == npos) + { // copy as is - auto size = table.size_bytes(); - auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); + auto size = table.size_bytes(); + auto* grid_data = static_cast( + grid_res_.allocate(size, alignof(bittable::value_type))); std::memcpy(grid_data, table.data(), size); patch = bittable(grid_data, table.width(), table.height()); - } else { + } + else + { // crop - if (offset_x >= table.width() || width > table.width() || offset_x + width > table.width()) + if(offset_x >= table.width() || width > table.width() + || offset_x + width > table.width()) { return false; } - if (offset_y >= table.height() || height > table.height() || offset_y + height > table.height()) + if(offset_y >= table.height() || height > table.height() + || offset_y + height > table.height()) { return false; } - auto size = bittable::calc_array_size(width, height); - auto* grid_data = static_cast(grid_res_.allocate(size, alignof(bittable::value_type))); + auto size = bittable::calc_array_size(width, height); + auto* grid_data = static_cast( + grid_res_.allocate(size, alignof(bittable::value_type))); patch = bittable(grid_data, table.width(), table.height()); - table.copy(patch, pad_id::zero(), table.xy_to_id(offset_x, offset_y), width, height); + table.copy( + patch, pad_id::zero(), table.xy_to_id(offset_x, offset_y), width, + height); } patches_.push_back(patch); return true; } - -bool grid_patch_set::push_ref(bittable patch) + +bool +grid_patch_set::push_ref(bittable patch) { patches_.push_back(patch); return true; } -void grid_patch_set::reset() +void +grid_patch_set::reset() { patches_.clear(); grid_res_.release(); diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index cb388ab..7ca9eba 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -13,39 +13,42 @@ namespace warthog::manager scenario_runner::scenario_runner() = default; scenario_runner::scenario_runner(const scenario_manager* scen) - : scenario_(scen) + : scenario_(scen) { } scenario_runner::~scenario_runner() = default; -std::pair scenario_runner::experiment_next(uint32_t count) +std::pair +scenario_runner::experiment_next(uint32_t count) { assert(scenario_ != nullptr); auto commands = scenario_->get_commands(); patches_.clear(); // reset patches - if (count == 0) - return {nullptr, 0}; + if(count == 0) return {nullptr, 0}; int patch_count = 0; - while (command_at_ < commands.size()) { + while(command_at_ < commands.size()) + { auto cmd = commands[command_at_]; // command_at_ incremented in following fuction calls - switch (cmd.type) { + switch(cmd.type) + { case util::scenario_command::SNAPSHOT: case util::scenario_command::PATCH: patch_count += snapshot_patches(); break; case util::scenario_command::QUERY: - if (const experiment* query = snapshot_query(); query != nullptr) { - if (--count == 0) - return {query, patch_count}; + if(const experiment* query = snapshot_query(); query != nullptr) + { + if(--count == 0) return {query, patch_count}; } break; default: // should never be reached ++command_at_; - WARTHOG_GDEBUG("scenario_runner::experiment_next invalid command type in " WARTHOG_FILENAME_LINE); + WARTHOG_GDEBUG("scenario_runner::experiment_next invalid command " + "type in " WARTHOG_FILENAME_LINE); } - // exits loop + // exits loop } // no more experiments return {nullptr, patch_count}; @@ -63,9 +66,9 @@ scenario_runner::restart() { patches_.clear(); experiments_.clear(); - command_at_ = 0; + command_at_ = 0; experiment_at_ = -1; - snapshot_at_ = -1; + snapshot_at_ = -1; } int @@ -73,19 +76,21 @@ scenario_runner::snapshot_next(bool clear_patch) { assert(scenario_ != nullptr); auto commands = scenario_->get_commands(); - if (clear_patch) - patches_.clear(); - while (command_at_ < commands.size()) { + if(clear_patch) patches_.clear(); + while(command_at_ < commands.size()) + { auto cmd = commands[command_at_]; - switch (cmd.type) { + switch(cmd.type) + { case util::scenario_command::SNAPSHOT: - if (cmd.id != snapshot_at_) { + if(cmd.id != snapshot_at_) + { // at new snapshot, return snapshot_at_ = cmd.id; return snapshot_at_; } // current snapshot, goto next snapshot - [[fallthrough]]; + [[fallthrough]]; case util::scenario_command::QUERY: ++snapshot_at_; break; @@ -94,7 +99,8 @@ scenario_runner::snapshot_next(bool clear_patch) // snapshot_patches increments snapshot_at_ break; default: - WARTHOG_GDEBUG("scenario_runner::snapshot_next invalid command type in " WARTHOG_FILENAME_LINE); + WARTHOG_GDEBUG("scenario_runner::snapshot_next invalid command " + "type in " WARTHOG_FILENAME_LINE); } } return false; @@ -105,21 +111,24 @@ scenario_runner::snapshot_patches(bool clear_patch) { assert(scenario_ != nullptr); auto commands = scenario_->get_commands(); - if (clear_patch) - patches_.clear(); + if(clear_patch) patches_.clear(); int count = 0; // if start of snapshot, apply that snapshot patches - if (command_at_ < commands.size() && commands[command_at_].type == util::scenario_command::SNAPSHOT) + if(command_at_ < commands.size() + && commands[command_at_].type == util::scenario_command::SNAPSHOT) command_at_ += 1; // while command_at_ is PATCH, add patch to applied list - while (command_at_ < commands.size()) { - if (auto cmd = commands[command_at_]; cmd.type == util::scenario_command::PATCH) { + while(command_at_ < commands.size()) + { + if(auto cmd = commands[command_at_]; + cmd.type == util::scenario_command::PATCH) + { command_at_ += 1; - count += 1; - patches_.push_back({cmd.id, cmd.cmd.patch.topleft_x, cmd.cmd.patch.topleft_y}); - } else { - break; + count += 1; + patches_.push_back( + {cmd.id, cmd.cmd.patch.topleft_x, cmd.cmd.patch.topleft_y}); } + else { break; } } // end at first non-PATCH command return count; @@ -130,15 +139,19 @@ scenario_runner::snapshot_query() { assert(scenario_ != nullptr); auto commands = scenario_->get_commands(); - if (command_at_ >= commands.size()) - return nullptr; + if(command_at_ >= commands.size()) return nullptr; auto cmd = commands[command_at_]; - if (cmd.type != util::scenario_command::QUERY) - return nullptr; - command_at_ += 1; + if(cmd.type != util::scenario_command::QUERY) return nullptr; + command_at_ += 1; experiment_at_ += 1; - if (cmd.cmd.query.experiment_id >= scenario_->num_experiments() || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) { - WARTHOG_GERROR_FMT("scenario_runner::snapshot_query invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, scenario_->num_experiments(), WARTHOG_FILENAME_LINE); + if(cmd.cmd.query.experiment_id >= scenario_->num_experiments() + || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) + { + WARTHOG_GERROR_FMT( + "scenario_runner::snapshot_query invalid experiment_id {} to " + "experiment, expected {} (max {}) in {}", + cmd.cmd.query.experiment_id, experiment_at_, + scenario_->num_experiments(), WARTHOG_FILENAME_LINE); return nullptr; } return scenario_->get_experiment(cmd.cmd.query.experiment_id); @@ -150,31 +163,45 @@ scenario_runner::snapshot_query_all() assert(scenario_ != nullptr); experiments_.clear(); auto commands = scenario_->get_commands(); - auto exp = scenario_->get_experiments(); - while (command_at_ < commands.size()) + auto exp = scenario_->get_experiments(); + while(command_at_ < commands.size()) { auto cmd = commands[command_at_]; - if (cmd.type != util::scenario_command::QUERY) - break; - if (cmd.cmd.query.experiment_id >= exp.size() || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) { - WARTHOG_GERROR_FMT("scenario_runner::snapshot_query_all invalid experiment_id {} to experiment, expected {} (max {}) in {}", cmd.cmd.query.experiment_id, experiment_at_, exp.size(), WARTHOG_FILENAME_LINE); + if(cmd.type != util::scenario_command::QUERY) break; + if(cmd.cmd.query.experiment_id >= exp.size() + || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) + { + WARTHOG_GERROR_FMT( + "scenario_runner::snapshot_query_all invalid experiment_id {} " + "to experiment, expected {} (max {}) in {}", + cmd.cmd.query.experiment_id, experiment_at_, exp.size(), + WARTHOG_FILENAME_LINE); return {}; } experiments_.push_back(exp[cmd.cmd.query.experiment_id]); - command_at_ += 1; + command_at_ += 1; experiment_at_ += 1; } return experiments_; } -bool scenario_runner::gridmap_init(domain::gridmap& grid, const grid_patch_set& patch_set, bool setup_grid) +bool +scenario_runner::gridmap_init( + domain::gridmap& grid, const grid_patch_set& patch_set, bool setup_grid) { - if (setup_grid) { - grid.setup(scenario_->get_scenario_height(), scenario_->get_scenario_width()); - // grid is 0 (non-traversable), make map area traversable but keep padding non-traversable - for (uint32_t y = 0, ye = grid.header_height(), xe = grid.header_width(); y < ye; ++y) { + if(setup_grid) + { + grid.setup( + scenario_->get_scenario_height(), scenario_->get_scenario_width()); + // grid is 0 (non-traversable), make map area traversable but keep + // padding non-traversable + for(uint32_t y = 0, ye = grid.header_height(), + xe = grid.header_width(); + y < ye; ++y) + { pad_id row_id = grid.to_padded_id_from_unpadded(0, y); - for (uint32_t x = 0; x < xe; ++x, ++row_id.id) { + for(uint32_t x = 0; x < xe; ++x, ++row_id.id) + { grid.set_label(row_id, true); } } @@ -183,29 +210,37 @@ bool scenario_runner::gridmap_init(domain::gridmap& grid, const grid_patch_set& snapshot_patches(); return gridmap_apply_patches(grid, patch_set) >= 0; } -int scenario_runner::gridmap_apply_patches(domain::gridmap& grid, const grid_patch_set& patch_set) +int +scenario_runner::gridmap_apply_patches( + domain::gridmap& grid, const grid_patch_set& patch_set) { int count = 0; - for (auto& P : patches_) { + for(auto& P : patches_) + { ++count; - if (P.patch_id >= patch_set.size()) - return -count; + if(P.patch_id >= patch_set.size()) return -count; uint32_t x, y; grid.to_padded_xy_from_unpadded(P.topleft_x, P.topleft_y, x, y); - if (!girdmap_apply_patch(grid, patch_set.get_patch(P.patch_id), x, y)) { + if(!girdmap_apply_patch(grid, patch_set.get_patch(P.patch_id), x, y)) + { return -count; } } return count; } -bool scenario_runner::girdmap_apply_patch(domain::gridmap& grid, domain::gridmap::bittable patch, uint32_t padded_x, uint32_t padded_y) +bool +scenario_runner::girdmap_apply_patch( + domain::gridmap& grid, domain::gridmap::bittable patch, uint32_t padded_x, + uint32_t padded_y) { - if ((uint64_t)padded_x + patch.width() >= (uint64_t)grid.width() || - (uint64_t)padded_y + patch.height() >= (uint64_t)grid.height()) + if((uint64_t)padded_x + patch.width() >= (uint64_t)grid.width() + || (uint64_t)padded_y + patch.height() >= (uint64_t)grid.height()) return false; - + // apply patch - patch.copy(grid, grid.to_padded_id_from_padded(padded_x, padded_y), pad_id::zero(), patch.width(), patch.height()); + patch.copy( + grid, grid.to_padded_id_from_padded(padded_x, padded_y), + pad_id::zero(), patch.width(), patch.height()); return true; } diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 8f1a5a9..a79d89b 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -14,7 +14,8 @@ scenario_manager::scenario_manager() { } scenario_manager::~scenario_manager() = default; -void scenario_manager::clear() +void +scenario_manager::clear() { experiments_.clear(); experiments_res_.release(); @@ -72,12 +73,16 @@ scenario_manager::load_gppc_scenario(std::istream& scenfile) return std::errc::io_error; } auto version = si.get_version(); - if (version == io::scenario_version::VERSION_1) { + if(version == io::scenario_version::VERSION_1) + { return load_gppc_scenario_body_v1(si); - } else if(version == io::scenario_version::VERSION_2) + } + else if(version == io::scenario_version::VERSION_2) { return load_gppc_scenario_body_v2(si); - } else { + } + else + { WARTHOG_GERROR("scenario_manager reading unsupported version"); return std::errc::invalid_argument; } @@ -122,14 +127,16 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) if(con == io::scenario_serialize::VALID) { std::string_view current_map(Q.map); - if (current_map.size() > 2048) // limit string size + if(current_map.size() > 2048) // limit string size { WARTHOG_GERROR_FMT( - "scenario_manager v1 query line map exceeds 2048 chars on line: {}", - si.get_line_num()); + "scenario_manager v1 query line map exceeds 2048 chars on " + "line: {}", + si.get_line_num()); return std::errc::argument_out_of_domain; } - if (map_string != current_map) { + if(map_string != current_map) + { map_string = copy_string(current_map); } experiment* ex = std::construct_at( @@ -137,10 +144,11 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) sizeof(experiment), alignof(experiment))), (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), - Q.dist[(int)0], - map_string); + Q.dist[(int)0], map_string); experiments_.push_back(ex); - commands_.push_back(scenario_command::make_query(Q.bucket, query_count_++, (uint32_t)(experiments_.size()-1))); + commands_.push_back(scenario_command::make_query( + Q.bucket, query_count_++, + (uint32_t)(experiments_.size() - 1))); } else if(con == io::scenario_serialize::FINAL) { break; } } @@ -161,30 +169,36 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) mfile_ = si.get_map_filename(); experiments_.reserve(1024); commands_.reserve(1024); - scenario_width_ = si.get_map_width(); - scenario_height_ = si.get_map_height(); + scenario_width_ = si.get_map_width(); + scenario_height_ = si.get_map_height(); static_scenario_start_ = -1; // init dynamic scenario // set map filename std::string_view map_string = copy_string(si.get_map_filename().string()); - if (map_string.size() > 2048) // limit string size + if(map_string.size() > 2048) // limit string size { - WARTHOG_GERROR( - "scenario_manager v2 map exceeds 2048 chars"); + WARTHOG_GERROR("scenario_manager v2 map exceeds 2048 chars"); return std::errc::argument_out_of_domain; } // get cost index int cost_index = si.get_cost_type().size() != 0 ? 0 : -1; - if (!cost_type_.empty()) { + if(!cost_type_.empty()) + { // user-provided cost index cost_index = si.find_cost_index(cost_type_); - if (cost_index < 0) { - WARTHOG_GWARN_FMT("scenario_manager v2 failed to find user-provided cost: {}", cost_type_); + if(cost_index < 0) + { + WARTHOG_GWARN_FMT( + "scenario_manager v2 failed to find user-provided cost: {}", + cost_type_); } - } else { + } + else + { // try to use grid if exist - if (int ci = si.find_cost_index(io::cost_type::G_8C_NCC); ci >= 0) { + if(int ci = si.find_cost_index(io::cost_type::G_8C_NCC); ci >= 0) + { cost_index = ci; } } @@ -192,8 +206,8 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) // read queries until done io::scenario_query Q; io::scenario_patch P; - int last_type = -1; - int last_bucket = -1; + int last_type = -1; + int last_bucket = -1; int snapshot_count = -1; while(true) { @@ -210,58 +224,72 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) } if(con == io::scenario_serialize::VALID) { - if (last_type == -1) { + if(last_type == -1) + { // only used if first command is a query - commands_.push_back(scenario_command::make_snapshot(Q.bucket, ++snapshot_count)); + commands_.push_back(scenario_command::make_snapshot( + Q.bucket, ++snapshot_count)); } - last_type = io::scenario_serialize::CMD_QUERY; + last_type = io::scenario_serialize::CMD_QUERY; last_bucket = Q.bucket; experiment* ex = std::construct_at( static_cast(experiments_res_.allocate( sizeof(experiment), alignof(experiment))), - Q.start_x, Q.start_y, Q.goal_x, - Q.goal_y, si.get_map_width(), si.get_map_height(), - Q.dist[(int)io::cost_type::G_8C_NCC], - map_string); + Q.start_x, Q.start_y, Q.goal_x, Q.goal_y, si.get_map_width(), + si.get_map_height(), Q.dist[(int)io::cost_type::G_8C_NCC], + map_string); experiments_.push_back(ex); - commands_.push_back(scenario_command::make_query(Q.bucket, query_count_++, (uint32_t)(experiments_.size()-1))); - } else if (con == io::scenario_serialize::CMD_PATCH) { + commands_.push_back(scenario_command::make_query( + Q.bucket, query_count_++, + (uint32_t)(experiments_.size() - 1))); + } + else if(con == io::scenario_serialize::CMD_PATCH) + { std::tie(con, ec) = si.read_patch_line(P); if(ec != std::errc{} || con != io::scenario_serialize::VALID) { WARTHOG_GERROR_FMT( - "scenario_manager failed to read command on line: {}", - si.get_line_num()); + "scenario_manager failed to read command on line: {}", + si.get_line_num()); return std::errc::io_error; } - if (last_type != io::scenario_serialize::CMD_PATCH || last_bucket != P.bucket) { - commands_.push_back(scenario_command::make_snapshot(P.bucket, ++snapshot_count)); + if(last_type != io::scenario_serialize::CMD_PATCH + || last_bucket != P.bucket) + { + commands_.push_back(scenario_command::make_snapshot( + P.bucket, ++snapshot_count)); } - last_type = io::scenario_serialize::CMD_PATCH; + last_type = io::scenario_serialize::CMD_PATCH; last_bucket = P.bucket; - commands_.push_back(scenario_command::make_patch(P.bucket, P.patch_id, P.loc_x, P.loc_y)); - } else if(con == io::scenario_serialize::FINAL) { break; } - else if(con == io::scenario_serialize::CMD_UNKNOWN) { + commands_.push_back(scenario_command::make_patch( + P.bucket, P.patch_id, P.loc_x, P.loc_y)); + } + else if(con == io::scenario_serialize::FINAL) { break; } + else if(con == io::scenario_serialize::CMD_UNKNOWN) + { // ignore si.skip_commands(); - } else { + } + else + { // error, invalid query WARTHOG_GERROR_FMT( - "scenario_manager failed to read command on line: {}", - si.get_line_num()); + "scenario_manager failed to read command on line: {}", + si.get_line_num()); return std::errc::io_error; } } return std::errc{}; } -std::string_view scenario_manager::copy_string(std::string_view str) +std::string_view +scenario_manager::copy_string(std::string_view str) { - if (str.empty()) - return std::string_view(); - char* mapchars = static_cast(experiments_res_.allocate(str.size() + 1)); + if(str.empty()) return std::string_view(); + char* mapchars + = static_cast(experiments_res_.allocate(str.size() + 1)); std::memcpy(mapchars, str.data(), str.size()); mapchars[str.size()] = '\0'; return std::string_view(mapchars, str.size()); From bf893c2e982f0afd5fd776cf9ca39f034d70e301 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 6 May 2026 23:24:43 +1000 Subject: [PATCH 52/61] reports no-solution correctly --- apps/warthog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 201d106..0a9cbbd 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -253,6 +253,10 @@ run_experiments( sol.reset(); algo.get_path(&pi, &par, &sol); + // check for no solution + if (sol.sum_of_edge_costs_ >= warthog::COST_MAX) { + sol.sum_of_edge_costs_ = -1; + } #ifdef WARTHOG_POSTHOC if constexpr(std::same_as< From 02f08867437e6cf9428a8c5ef479c521ce8292b6 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 May 2026 11:30:11 +1000 Subject: [PATCH 53/61] rewrote labelled_gridmap to conform to gridmap --- cmake/headers.cmake | 2 +- include/warthog/domain/grid_patches.h | 32 --- include/warthog/domain/gridmap.h | 61 +++--- include/warthog/domain/labelled_gridmap.h | 255 ++++++++++++++++------ src/domain/gridmap.cpp | 13 +- 5 files changed, 227 insertions(+), 136 deletions(-) delete mode 100644 include/warthog/domain/grid_patches.h diff --git a/cmake/headers.cmake b/cmake/headers.cmake index 6d90038..863f2cb 100644 --- a/cmake/headers.cmake +++ b/cmake/headers.cmake @@ -16,7 +16,6 @@ include/warthog/limits.h include/warthog/domain/grid.h include/warthog/domain/gridmap.h -include/warthog/domain/grid_patches.h include/warthog/domain/labelled_gridmap.h include/warthog/geometry/geography.h @@ -27,6 +26,7 @@ include/warthog/heuristic/manhattan_heuristic.h include/warthog/heuristic/octile_heuristic.h include/warthog/heuristic/zero_heuristic.h +include/warthog/io/fwd.h include/warthog/io/grid.h include/warthog/io/grid_trace.h include/warthog/io/log.h diff --git a/include/warthog/domain/grid_patches.h b/include/warthog/domain/grid_patches.h deleted file mode 100644 index 8b6fe65..0000000 --- a/include/warthog/domain/grid_patches.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef WARTHOG_DOMAIN_GRIDMAP_H -#define WARTHOG_DOMAIN_GRIDMAP_H - -// Scenario v2 grid-based patch set. -// grid_patches primailry holds an array of bittable, handles reading from file -// and efficent memory storage. -// -// Look at utilities in manager/dynamic_gridmap.h for automatic translation to -// gridmap in a dynamic scenario. -// -// @author: Ryan Hechenberger -// @created: 2026-04-10 -// - -#include "grid.h" -#include -#include - -namespace warthog::domain -{ - -class grid_patches -{ - grid_patches(); - grid_patches(std::istream& input); - grid_patches(); - grid_patches(); -}; - -} // namespace warthog::domain - -#endif // WARTHOG_DOMAIN_GRIDMAP_H diff --git a/include/warthog/domain/gridmap.h b/include/warthog/domain/gridmap.h index 33ccfda..265030f 100644 --- a/include/warthog/domain/gridmap.h +++ b/include/warthog/domain/gridmap.h @@ -14,7 +14,7 @@ // in a one dimensional array and also to avoid range checks when trying to // identify invalid neighbours of tiles on the edge of the map. // -// @author: dharabor +// @author: dharabor & Ryan Hechenberger // @created: 08/08/2012 // @@ -77,8 +77,7 @@ class gridmap : public memory::bittable void load(const char* filename); - // here we convert from the coordinate space of - // the original grid to the coordinate space of db_. + /// @brief convert unpadded id to padded id pad_id to_padded_id(pack_id node_id) const noexcept { @@ -91,19 +90,20 @@ class gridmap : public memory::bittable (uint32_t{node_id} / header_.width_) * padding_per_row_}; } - // here we convert from the coordinate space of - // the original grid to the coordinate space of db_. + /// @brief convert unpadded (x,y) to a padded id pad_id to_padded_id_from_unpadded(uint32_t x, uint32_t y) const noexcept { return pad_id{(y + PADDED_ROWS) * width() + x}; } + /// @brief convert padded (x,y) to a padded id pad_id to_padded_id_from_padded(uint32_t x, uint32_t y) const noexcept { return pad_id{y * width() + x}; } + /// @brief convert unpadded id to unpadded (x,y) void to_unpadded_xy(pack_id grid_id, uint32_t& x, uint32_t& y) const noexcept { @@ -112,6 +112,7 @@ class gridmap : public memory::bittable assert(x < header_.width_ && y < header_.height_); } + /// @brief convert padded id to unpadded (x,y) void to_unpadded_xy(pad_id grid_id, uint32_t& x, uint32_t& y) const noexcept { @@ -120,6 +121,7 @@ class gridmap : public memory::bittable assert(x < header_.width_ && y < header_.height_); } + /// @brief convert padded (x,y) to unpadded (x,y) void to_unpadded_xy_from_padded( uint32_t padded_x, uint32_t padded_y, uint32_t& x, @@ -130,6 +132,7 @@ class gridmap : public memory::bittable assert(x < header_.width_ && y < header_.height_); } + /// @brief convert padded id to padded (x,y) void to_padded_xy(pad_id grid_id, uint32_t& x, uint32_t& y) const noexcept { @@ -138,6 +141,7 @@ class gridmap : public memory::bittable assert(x < width() && y < height()); } + /// @brief convert unpadded (x,y) to padded (x,y) void to_padded_xy_from_unpadded( uint32_t unpadded_x, uint32_t unpadded_y, uint32_t& x, @@ -148,6 +152,7 @@ class gridmap : public memory::bittable assert(x < width() && y < height()); } + /// @brief convert padded id to unpadded id pack_id to_unpadded_id(pad_id grid_id) const noexcept { @@ -160,6 +165,7 @@ class gridmap : public memory::bittable // as the padded width is already removed PADDED_ROWS * header_.width_}; } + /// @brief convert unpadded (x,y) to unpadded id pack_id to_unpadded_id_from_unpadded(uint32_t x, uint32_t y) const noexcept { @@ -192,11 +198,11 @@ class gridmap : public memory::bittable // read from the byte just before node_id and shift down until the // nei adjacent to node_id is in the lowest position tiles[0] - = (uint8_t)(*((uint32_t*)(db_ + (pos1 - 1))) >> (bit_offset + 7)); + = (uint8_t)(*((uint32_t*)(db_.get() + (pos1 - 1))) >> (bit_offset + 7)); tiles[1] - = (uint8_t)(*((uint32_t*)(db_ + (pos2 - 1))) >> (bit_offset + 7)); + = (uint8_t)(*((uint32_t*)(db_.get() + (pos2 - 1))) >> (bit_offset + 7)); tiles[2] - = (uint8_t)(*((uint32_t*)(db_ + (pos3 - 1))) >> (bit_offset + 7)); + = (uint8_t)(*((uint32_t*)(db_.get() + (pos3 - 1))) >> (bit_offset + 7)); } // takes the tiles from get_neighbours and tightly packs them into 8-bits @@ -248,9 +254,9 @@ class gridmap : public memory::bittable // read 32bits of memory; grid_id_p is in the // lowest bit position of tiles[1] - tiles[0] = (uint32_t)(*((uint64_t*)(db_ + pos1)) >> (bit_offset)); - tiles[1] = (uint32_t)(*((uint64_t*)(db_ + pos2)) >> (bit_offset)); - tiles[2] = (uint32_t)(*((uint64_t*)(db_ + pos3)) >> (bit_offset)); + tiles[0] = (uint32_t)(*((uint64_t*)(db_.get() + pos1)) >> (bit_offset)); + tiles[1] = (uint32_t)(*((uint64_t*)(db_.get() + pos2)) >> (bit_offset)); + tiles[2] = (uint32_t)(*((uint64_t*)(db_.get() + pos3)) >> (bit_offset)); } // similar to get_neighbours_32bit but grid_id_p is placed into the @@ -279,9 +285,9 @@ class gridmap : public memory::bittable // read 32bits of memory; grid_id_p is in the // highest bit position of tiles[1] - tiles[0] = (uint32_t)(*((uint64_t*)(db_ + pos1)) >> (bit_offset + 1)); - tiles[1] = (uint32_t)(*((uint64_t*)(db_ + pos2)) >> (bit_offset + 1)); - tiles[2] = (uint64_t)(*((uint64_t*)(db_ + pos3)) >> (bit_offset + 1)); + tiles[0] = (uint32_t)(*((uint64_t*)(db_.get() + pos1)) >> (bit_offset + 1)); + tiles[1] = (uint32_t)(*((uint64_t*)(db_.get() + pos2)) >> (bit_offset + 1)); + tiles[2] = (uint64_t)(*((uint64_t*)(db_.get() + pos3)) >> (bit_offset + 1)); } // fetches a contiguous set of tiles from three adjacent rows. @@ -300,9 +306,9 @@ class gridmap : public memory::bittable uint32_t pos3 = dbindex + dbwidth64_; // read 64bits of tile data from each of the three rows - tiles[0] = *((uint64_t*)(db_) + pos1); - tiles[1] = *((uint64_t*)(db_) + pos2); - tiles[2] = *((uint64_t*)(db_) + pos3); + tiles[0] = *((uint64_t*)(db_.get()) + pos1); + tiles[1] = *((uint64_t*)(db_.get()) + pos2); + tiles[2] = *((uint64_t*)(db_.get()) + pos3); } // fetches a contiguous set of tiles from three adjacent rows. @@ -395,19 +401,18 @@ class gridmap : public memory::bittable void setup_ser_(io::bittable_serialize& parser); -private: - warthog::util::gm_header header_; - warthog::dbword* db_; + warthog::util::gm_header header_ = {}; + std::unique_ptr db_; std::filesystem::path filename_; - uint32_t dbwidth_; - uint32_t dbwidth64_; - uint32_t dbheight_; - uint32_t db_size_; - uint32_t padding_per_row_; - uint32_t padding_column_above_; - uint32_t max_id_; - uint32_t num_traversable_; + uint32_t dbwidth_ = 0; + uint32_t dbwidth64_ = 0; + uint32_t dbheight_ = 0; + uint32_t db_size_ = 0; + uint32_t padding_per_row_ = 0; + uint32_t padding_column_above_ = 0; + uint32_t max_id_ = 0; + uint32_t num_traversable_ = 0; void init_db(); diff --git a/include/warthog/domain/labelled_gridmap.h b/include/warthog/domain/labelled_gridmap.h index c689997..f628fa6 100644 --- a/include/warthog/domain/labelled_gridmap.h +++ b/include/warthog/domain/labelled_gridmap.h @@ -12,18 +12,21 @@ // - a line of terminator characters are added before the first row. // - a line of terminator characters are added after the last row. // -// @author: dharabor +// @author: dharabor & Ryan Hechenberger // @created: 2018-11-08 // +#include "grid.h" #include #include #include +#include #include #include #include -#include +#include +#include namespace warthog::domain { @@ -32,35 +35,77 @@ template class labelled_gridmap { public: - labelled_gridmap(unsigned int h, unsigned int w) : header_(h, w, "octile") + labelled_gridmap() = default; + labelled_gridmap(uint32_t h, uint32_t w) : header_(h, w, "octile") { this->init_db(); } - + labelled_gridmap(std::istream& input) +{ + setup_stream_(input); +} + labelled_gridmap(io::bittable_serialize& parser) +{ + setup_ser_(parser); +} + labelled_gridmap(std::filesystem::path&& filename) +{ + filename_ = std::move(filename); + std::ifstream in(filename_); + setup_stream_(in); +} + labelled_gridmap(const std::filesystem::path& filename) + : labelled_gridmap(std::filesystem::path(filename)) +{ } labelled_gridmap(const char* filename) - { - util::gm_parser parser(filename); - header_ = parser.get_header(); - strcpy(filename_, filename); - init_db(); - - for(unsigned int i = 0; i < parser.get_num_tiles(); i++) - { - CELL cell = parser.get_tile_at(i); - ; - set_label(uint32_t{to_padded_id(pack_id{i})}, cell); - assert(get_label(uint32_t{to_padded_id(pack_id{i})}) == cell); - } - } + : labelled_gridmap(std::filesystem::path(filename)) +{ } labelled_gridmap(const labelled_gridmap&) = delete; + ~labelled_gridmap() = default; + + /// The number of padded rows before and after + static constexpr uint32_t PADDED_ROWS = 3; + labelled_gridmap& operator=(const labelled_gridmap&) = delete; - ~labelled_gridmap() { delete[] db_; } + void + setup(uint32_t h, uint32_t w) + { + header_.height_ = h; + header_.width_ = w; + this->init_db(); + } + void + load(std::istream& input) + { + setup_stream_(input); + } + void + load(io::bittable_serialize& parser) + { + setup_ser_(parser); + } + void + load(std::filesystem::path&& filename) + { + filename_ = std::move(filename); + std::ifstream in(filename_); + setup_stream_(in); + } + void + load(const std::filesystem::path& filename) + { + load(std::filesystem::path(filename)); + } + void + load(const char* filename) + { + load(std::filesystem::path(filename)); + } - // here we convert from the coordinate space of - // the original grid to the coordinate space of db_. + /// @brief convert unpadded id to padded id pad_id to_padded_id(pack_id node_id) const noexcept { @@ -68,54 +113,87 @@ class labelled_gridmap return pad_id{ uint32_t{node_id} + // padded rows before the actual map data starts - padded_rows_before_first_row_ * padded_width_ + + PADDED_ROWS * width() + // padding from each row of data before this one - (node_id.id / header_.width_) * padding_per_row_}; + (uint32_t{node_id} / header_.width_) * padding_per_row_}; } - // here we convert from the coordinate space of - // the original grid to the coordinate space of db_. + /// @brief convert unpadded (x,y) to a padded id pad_id to_padded_id_from_unpadded(uint32_t x, uint32_t y) const noexcept { - return pad_id{(y + padded_rows_before_first_row_) * padded_width_ + x}; + return pad_id{(y + PADDED_ROWS) * width() + x}; } + /// @brief convert padded (x,y) to a padded id pad_id to_padded_id_from_padded(uint32_t x, uint32_t y) const noexcept { - return pad_id{y * padded_width_ + x}; + return pad_id{y * width() + x}; } + /// @brief convert unpadded id to unpadded (x,y) void to_unpadded_xy(pack_id grid_id, uint32_t& x, uint32_t& y) const noexcept { y = uint32_t{grid_id} / header_.width_; x = uint32_t{grid_id} % header_.width_; + assert(x < header_.width_ && y < header_.height_); } + /// @brief convert padded id to unpadded (x,y) void to_unpadded_xy(pad_id grid_id, uint32_t& x, uint32_t& y) const noexcept { to_padded_xy(grid_id, x, y); - y -= padded_rows_before_first_row_; + y -= PADDED_ROWS; assert(x < header_.width_ && y < header_.height_); } + /// @brief convert padded (x,y) to unpadded (x,y) + void + to_unpadded_xy_from_padded( + uint32_t padded_x, uint32_t padded_y, uint32_t& x, + uint32_t& y) const noexcept + { + y = padded_y - PADDED_ROWS; + x = padded_x; + assert(x < header_.width_ && y < header_.height_); + } + + /// @brief convert padded id to padded (x,y) void to_padded_xy(pad_id grid_id, uint32_t& x, uint32_t& y) const noexcept { - y = uint32_t{grid_id} / padded_width_; - x = uint32_t{grid_id} % padded_width_; - assert(x < padded_width_ && y < padded_height_); + y = uint32_t{grid_id} / width(); + x = uint32_t{grid_id} % width(); + assert(x < width() && y < height()); + } + + /// @brief convert unpadded (x,y) to padded (x,y) + void + to_padded_xy_from_unpadded( + uint32_t unpadded_x, uint32_t unpadded_y, uint32_t& x, + uint32_t& y) const noexcept + { + y = unpadded_y + PADDED_ROWS; + x = unpadded_x; + assert(x < width() && y < height()); } + /// @brief convert padded id to unpadded id pack_id to_unpadded_id(pad_id grid_id) const noexcept { - uint32_t x, y; - to_unpadded_xy(grid_id, x, y); - return pack_id{y * header_.width_ + x}; + assert(width() != 0); + return pack_id{ + uint32_t{grid_id} - + // padding from each row of data + (uint32_t{grid_id} / width()) * padding_per_row_ - + // padded rows before the actual map data starts, use header_width + // as the padded width is already removed + PADDED_ROWS * header_.width_}; } + /// @brief convert unpadded (x,y) to unpadded id pack_id to_unpadded_id_from_unpadded(uint32_t x, uint32_t y) const noexcept { @@ -165,7 +243,7 @@ class labelled_gridmap return this->header_.width_; } - const char* + const std::filesystem::path filename() const noexcept { return this->filename_; @@ -177,44 +255,87 @@ class labelled_gridmap return sizeof(*this) + sizeof(CELL) * db_size_; } -private: - char filename_[256]; - util::gm_header header_; - CELL* db_; - - uint32_t db_size_; - uint32_t padding_per_row_; - uint32_t padded_rows_before_first_row_; - uint32_t padded_rows_after_last_row_; - uint32_t padded_width_; - uint32_t padded_height_; - +protected: void - init_db() - { - // when storing the grid we pad the edges of the map. - // this eliminates the need for bounds checking when - // fetching the neighbours of a node. - this->padded_rows_before_first_row_ = 3; - this->padded_rows_after_last_row_ = 3; - this->padding_per_row_ = 1; + setup_stream_(std::istream& in); + void + setup_ser_(io::bittable_serialize& parser); - this->padded_width_ = this->header_.width_ + this->padding_per_row_; - this->padded_height_ = this->header_.height_ - + this->padded_rows_after_last_row_ - + this->padded_rows_before_first_row_; + util::gm_header header_ = {}; + std::unique_ptr db_; + std::filesystem::path filename_; - this->db_size_ = this->padded_height_ * padded_width_; + uint32_t db_size_ = 0; + uint32_t padding_per_row_ = 0; + uint32_t padded_rows_before_first_row_ = 0; + uint32_t padded_rows_after_last_row_ = 0; + uint32_t padded_width_ = 0; + uint32_t padded_height_ = 0; - // create a one dimensional dbword array to store the grid - this->db_ = new CELL[db_size_]; + void + init_db(); +}; - for(uint32_t i = 0; i < this->db_size_; i++) - { - this->db_[i] = 0; - } +template +inline void +labelled_gridmap::setup_stream_(std::istream& in) +{ + io::bittable_serialize parser; + parser.open_read(&in); + if(parser.read_header() != std::errc{}) + throw std::runtime_error("invalid grid format"); + setup_ser_(parser); +} + +template +inline void +labelled_gridmap::setup_ser_(io::bittable_serialize& parser) +{ + if(parser.read_grid_header() != std::errc{}) + throw std::runtime_error("invalid grid format"); + this->header_.type_ = "octile"; + this->header_.width_ = parser.get_dim().width; + this->header_.height_ = parser.get_dim().height; + + init_db(); + // read raw data to buffer + std::unique_ptr buffer_v = std::make_unique(this->db_size_); + std::span buffer(buffer_v.get(), this->db_size_); + if(auto ec = parser.read_grid_raw(buffer); ec != std::errc{}) + throw std::runtime_error("invalid grid format"); + // copy buffet to db + std::copy_n(buffer.data(), buffer.size(), this->db_.get()); +} + +template +inline void +labelled_gridmap::init_db() +{ + // when storing the grid we pad the edges of the map with + // zeroes. this eliminates the need for bounds checking when + // fetching the neighbours of a node. + uint32_t store_width, store_height; + store_height = this->header_.height_ + 2 * PADDED_ROWS; + + // calculate # of extra/redundant padding bits required, + // per row, to align map width with dbword size + store_width = this->header_.width_ + 1; + if((store_width % 8) != 0) + { + store_width = (this->header_.width_ / 8 + 1) * 8; } -}; + this->padded_width_ = store_width; + this->padded_height_ = store_height; + this->padded_rows_before_first_row_ = PADDED_ROWS; + this->padded_rows_after_last_row_ = PADDED_ROWS; + this->padding_per_row_ = store_width - this->header_.width_; + + this->db_size_ = store_width * store_height; + + // create a one dimensional dbword array to store the grid + this->db_ = std::make_unique(db_size_); + std::memset(this->db_.get(), 0, this->db_size_); +} // vertex-labelled gridmap using vl_gridmap = labelled_gridmap; diff --git a/src/domain/gridmap.cpp b/src/domain/gridmap.cpp index 48fd5fd..6e9ce5a 100644 --- a/src/domain/gridmap.cpp +++ b/src/domain/gridmap.cpp @@ -104,7 +104,7 @@ gridmap::setup_ser_(io::bittable_serialize& parser) throw std::runtime_error("invalid grid format"); // calculate traversable num_traversable_ = static_cast(std::transform_reduce( - db_, db_ + db_size_, static_cast(0), std::plus(), + db_.get(), db_.get() + db_size_, static_cast(0), std::plus(), &std::popcount)); } @@ -133,17 +133,14 @@ gridmap::init_db() this->db_size_ = bittable::calc_array_size(store_width, store_height) + 8; // create a one dimensional dbword array to store the grid - this->db_ = new warthog::dbword[db_size_]; - bittable::setup(this->db_, store_width, store_height); + this->db_ = std::make_unique(db_size_); + bittable::setup(this->db_.get(), store_width, store_height); fill(0); - max_id_ = this->dbheight_ * this->dbwidth_ - 1; + this->max_id_ = this->dbheight_ * this->dbwidth_ - 1; } -gridmap::~gridmap() -{ - delete[] db_; -} +gridmap::~gridmap() = default; void gridmap::print(std::ostream& out) From c6d0a88d1cd34261ae9f047337f0644aa06f265e Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 May 2026 14:35:32 +1000 Subject: [PATCH 54/61] updated documention, rename query->inst and dist->cost --- apps/warthog.cpp | 6 +- include/warthog/domain/grid.h | 45 +++---- include/warthog/io/fwd.h | 13 +- include/warthog/io/grid.h | 86 +++++++------ include/warthog/io/grid_trace.h | 13 +- include/warthog/io/log.h | 85 +++++++------ include/warthog/io/observer.h | 43 ++++--- include/warthog/io/posthoc_trace.h | 15 ++- include/warthog/io/scenario.h | 137 +++++++++++++++------ include/warthog/io/serialize_base.h | 91 ++++++++++++-- include/warthog/io/stream_observer.h | 28 ++--- include/warthog/manager/experiment.h | 14 +-- include/warthog/manager/grid_patch_set.h | 46 +++++-- include/warthog/manager/scenario_manager.h | 14 +-- include/warthog/manager/scenario_runner.h | 38 +++--- include/warthog/util/scenario_manager.h | 26 ++-- src/io/grid.cpp | 6 + src/io/scenario.cpp | 106 ++++++++-------- src/io/serialize_base.cpp | 9 +- src/manager/grid_patch_set.cpp | 6 +- src/manager/scenario_runner.cpp | 36 +++--- src/util/scenario_manager.cpp | 32 ++--- 22 files changed, 532 insertions(+), 363 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 0a9cbbd..91f2720 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -51,7 +51,7 @@ int checkopt = 0; int verbose = 0; // display program help on startup int print_help = 0; -// run only this query, or -1 for all +// run only this inst, or -1 for all int filter_id = -1; // dump map at id if set int dump_map_id = -1; @@ -88,13 +88,13 @@ help(std::ostream& out) "values in the scen file)\n" << "\t--verbose (optional; prints debugging info when compiled " "with debug symbols)\n" - << "\t--filter [id] (optional; run only query [id])\n" + << "\t--filter [id] (optional; run only inst [id])\n" << "\t--dump-map [id] (optional; dump map at id to stderr)" << "\t--dump-map-file [filename] (optional; file to dump map to, " "default /dev/stderr)" #ifdef WARTHOG_POSTHOC << "\t--trace [.trace.yaml file] (optional; write posthoc trace for " - "first query to [file])\n" + "first instance to [file])\n" #endif << "Invoking the program this way solves all instances in [scen " "file] with algorithm [alg]\n" diff --git a/include/warthog/domain/grid.h b/include/warthog/domain/grid.h index 558b670..012ea34 100644 --- a/include/warthog/domain/grid.h +++ b/include/warthog/domain/grid.h @@ -1,13 +1,18 @@ #ifndef WARTHOG_DOMAIN_GRID_H #define WARTHOG_DOMAIN_GRID_H -// domains/grid.h -// -// A place for grid-related curios -// -// @author: dharabor -// @created: 2018-11-03 -// +/// @file domains/grid.h +/// +/// A place for grid-related features. +/// Includes the grid identifier (id), direction (and direction_id), +/// point (coordinate) and utilities. +/// +/// These utilities include conversion between direction/direction_id, rotation, +/// direction to point and inverse, helpers for grid traversal, etc. +/// +/// @author: dharabor & Ryan Hechenberger +/// @created: 2018-11-03 +/// #include #include @@ -20,8 +25,6 @@ namespace warthog::grid { -// TODO: document - using grid_id = pad32_id; typedef enum : uint8_t @@ -71,25 +74,25 @@ template concept SecInterCardinalID = static_cast(D) >= 8; /// @return is NORTH_ID/SOUTH_ID/EAST_ID/WEST_ID -constexpr inline bool +constexpr bool is_cardinal_id(direction_id d) noexcept { return static_cast(d) < 4; } /// @return is NORTHEAST_ID/NORTHWEST_ID/SOUTHEAST_ID/SOUTHWEST_ID -constexpr inline bool +constexpr bool is_intercardinal_id(direction_id d) noexcept { return static_cast(d - 4) < 4; } /// @return is a intercardinal+cardinal combined direction -constexpr inline bool +constexpr bool is_secic_id(direction_id d) noexcept { return static_cast(d) >= 8; } /// @return get the cardinal id from a combined direction -constexpr inline direction_id +constexpr direction_id secic_cardinal(direction_id d) noexcept { direction_id c @@ -98,7 +101,7 @@ secic_cardinal(direction_id d) noexcept return c; } /// @return get the intercardinal id from a combined direction -constexpr inline direction_id +constexpr direction_id secic_intercardinal(direction_id d) noexcept { direction_id c = static_cast(static_cast(d) >> 3); @@ -519,7 +522,7 @@ operator==(spoint a, spoint b) return std::bit_cast(a) == std::bit_cast(b); } -constexpr inline std::pair +constexpr std::pair point_signed_diff(point a, point b) noexcept { return { @@ -529,20 +532,20 @@ point_signed_diff(point a, point b) noexcept static_cast(b.y) - static_cast(a.y))}; } -constexpr inline point +constexpr point operator+(point a, spoint b) noexcept { return point{ static_cast(a.x + static_cast(b.x)), static_cast(a.y + static_cast(b.y))}; } -constexpr inline spoint +constexpr spoint operator+(spoint a, spoint b) noexcept { return spoint{ static_cast(a.x + b.x), static_cast(a.y + b.y)}; } -constexpr inline spoint +constexpr spoint operator*(int16_t a, spoint b) noexcept { return spoint{ @@ -552,7 +555,7 @@ operator*(int16_t a, spoint b) noexcept /// @brief gets a unit signed-point in direction /// @param d the direction for the unit-distance /// @return the unit spoint -constexpr inline spoint +constexpr spoint dir_unit_point(direction_id d) noexcept { assert(static_cast(d) < 8); @@ -582,7 +585,7 @@ dir_unit_point(direction_id d) noexcept /// @param d the direction for unit-point, if a secic will use the /// intercardinal component /// @return the unit spoint -constexpr inline spoint +constexpr spoint dir_unit_point_secic(direction_id d) noexcept { assert(static_cast(d) < 8); @@ -614,7 +617,7 @@ dir_unit_point_secic(direction_id d) noexcept /// @return the direction from p1 to p2. If diff x or diff y is zero, will be /// cardinal, otherwise is intercardinal direction. -constexpr inline direction_id +constexpr direction_id point_to_direction_id(point p1, point p2) noexcept { union diff --git a/include/warthog/io/fwd.h b/include/warthog/io/fwd.h index 4e396d5..21840d1 100644 --- a/include/warthog/io/fwd.h +++ b/include/warthog/io/fwd.h @@ -1,13 +1,12 @@ #ifndef WARTHOG_IO_FWD_H #define WARTHOG_IO_FWD_H -// io/fwd.h -// -// Forward class definitions and store global enums. -// -// @author: Ryan Hechenberger -// @created: 2026-05-01 -// +/// @file fwd.h +/// +/// Forward class definitions and store global enums. +/// +/// @author: Ryan Hechenberger +/// @created: 2026-05-01 #include diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index 127c6a8..ca2c0c2 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -1,16 +1,15 @@ #ifndef WARTHOG_IO_GRID_H #define WARTHOG_IO_GRID_H -// io/grid.h -// -// Read utility for gridmap. -// -// Supported MovingAI map format. Read format spec: -// https://movingai.com/benchmarks/formats.html -// -// @author: Ryan Hechenberger -// @created: 2025-06-01 -// +/// @file io/grid.h +/// +/// Read utility for gridmap. +/// +/// Supported MovingAI map format. Read format spec: +/// https://movingai.com/benchmarks/formats.html +/// +/// @author: Ryan Hechenberger +/// @created: 2025-06-01 #include "serialize_base.h" @@ -24,14 +23,16 @@ namespace warthog::io { +/// @brief the type of bittable to (de)serialize enum class bittable_type : uint8_t { - OCTILE, - PATCH, - OTHER, - NONE, + OCTILE, ///< original MovingAI format + PATCH, ///< patch format, grouping multiple octiles + OTHER, ///< unknown format + NONE, ///< no format specified }; +/// @brief the cell character, as specified by MovingAI enum class gridmap_cell : char { TERRAIN = '.', @@ -65,18 +66,19 @@ gridmap_cell_traversable(char c) noexcept return gridmap_cell_traversable(static_cast(c)); } -/// @brief Max grid size +/// @brief max grid size inline constexpr uint32_t GRID_MAX_SIZE = 15'000; -/// @brief Limit on max number of patches +/// @brief limit on max number of patches inline constexpr uint32_t PATCH_COUNT_LIMIT = 10'000'000; -/// @brief The bittable serialize class, flexable read/write of -/// bittable/gridmap or -/// similiar datatypes +/// @brief the bittable serialize class, flexable read/write of +/// bittable/gridmap or similiar datatypes, see serialize_base for +/// how files are read. class bittable_serialize : public serialize_base { public: + bittable_serialize(); /// @return the grid dimension, either as last read grid from file or set /// by user for writing memory::bittable_dimension @@ -104,17 +106,18 @@ class bittable_serialize : public serialize_base { return m_type; } - /// @brief Sets the type/version to write to the file header, supported is + /// @brief sets the type/version to write to the file header, supported is /// octile/patch. - /// Throws on unsupported type. - void + /// @return std::errc{} for success, otherwise failure + std::errc set_type(bittable_type type) { if(type != bittable_type::OCTILE && type != bittable_type::PATCH) { - throw std::out_of_range("type"); + return std::errc::argument_out_of_domain; } m_type = type; + return std::errc{}; } /// @brief get the number of patches in file @@ -124,11 +127,16 @@ class bittable_serialize : public serialize_base return m_patch_amount; } /// @brief set the number of patches (for writing) - void + /// @return std::errc{} for success, otherwise failure + std::errc set_patch_amount(uint32_t count) { - if(count > PATCH_COUNT_LIMIT) { throw std::out_of_range("count"); } + if(count > PATCH_COUNT_LIMIT) + { + return std::errc::argument_out_of_domain; + } m_patch_amount = count; + return std::errc{}; } /// @brief gets the number of patches that have been read/write uint32_t @@ -143,27 +151,29 @@ class bittable_serialize : public serialize_base return m_patch_id; } /// @brief gets the number of patches that have been read - void + /// @return std::errc{} for success, otherwise failure + std::errc set_patch_id(uint32_t id) noexcept { m_patch_id = id; + return std::errc{}; } - /// @brief Reads the map/patch file header, getting the type - /// @param in alternative filestream to read from - /// @return value init on success, error code on failure - /// - /// Reads the header line, `type octile` for bittable_type::OCTILE or - /// `type patch` for bittable_type::PATCH, retrivable by get_type(). - /// For PATCH type, also reads following line for number of patches in - /// file. + /// @brief reads the map/patch file header, getting the type + /// @param in alternative file stream to read from + /// @return value init on success, error code on failure + /// + /// Reads the header line, `type octile` for bittable_type::OCTILE or + /// `type patch` for bittable_type::PATCH, retrievable by get_type(). + /// For PATCH type, also reads following line for number of patches in + /// file. std::errc read_header(std::istream* in = nullptr); - /// @brief Reads the grids header, getting width/height up to the map data. - /// @param in alternative filestream to read from - /// @return value init on success, error code on failure - /// @pre get_type() matches the format of file. + /// @brief Reads the grids' header, getting width/height up to the map data. + /// @param in alternative filestream to read from + /// @return value init on success, error code on failure + /// @pre get_type() matches the format of file. std::errc read_grid_header(std::istream* in = nullptr); diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index 15cc1b6..28ce37d 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -1,13 +1,12 @@ #ifndef WARTHOG_IO_GRID_TRACE_H #define WARTHOG_IO_GRID_TRACE_H -// io/grid_trace.h -// -// Basic posthoc_trace for use with gridmap. -// -// @author: Ryan Hechenberger -// @created: 2025-08-07 -// +/// @file io/grid_trace.h +/// +/// Basic posthoc_trace for use with gridmap. +/// +/// @author: Ryan Hechenberger +/// @created: 2025-08-07 #include "posthoc_trace.h" diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 3751070..1d36142 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -1,49 +1,48 @@ #ifndef WARTHOG_IO_LOG_H #define WARTHOG_IO_LOG_H -// io/log.h -// -// Logging utility framework, where a user can provide at a high-level -// data structure with function pointers to log messages as a single string -// with a specific log level. -// This will call a user-defined function (if able) that will output this -// message as the user desires. -// Default functions (output to stderr or file) are defined here. -// -// The log_sink is a non-owning copyable struct that points to the data and -// logging function calls. All logging is performed through log_sink. All -// classes here are thread safe, follow comments for outlying cases. -// -// Special classes inherit log_sink to provide default functionality. -// log_sink_std should be used to write to std::cout and std::cerr. -// log_sink_stream pipe to a stream or open a file stream. -// -// The logs are made to be logged to a certain log_level. -// The WARTHOG_TRACE and others in this header provide interfaces to log to a -// special logger<> class. This logger<> class knows at compile time the -// minimum level to log, and with the use of a macro will be compiled out if -// that level was not set at compile time when used with the logging macros. -// -// The global logger can be acquired with the glog() (logger<>) or glogs() -// (log_sink), and set with set_glog(log_sink). The log level of the global -// logger is set though the WARTHOG_LOG definition, with a default of 1(debug) -// for debug builds, and 2(information) for release (NDEBUG defined). Macros -// like WARTHOG_GTRACE will automatically write to the global logger. -// -// The _IF will only log if a runtime if true. -// The _FMT uses the std::format from C++20 to format the output messages. -// The logging utilities uses dynamic memory std::strings to produce the final -// log message strings, thus is better to be disabled for release builds -// through the log level. -// -// Log messages produce a messaged as "[TIME LEVEL] msg". -// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be -// overridden with define cstring WARTHOG_LOG_TIME. Clock is in local system -// time, but can be set to UTC by definiong WARTHOG_LOG_CLOCK_UTC. -// -// @author: Ryan Hechenberger -// @created: 2025-09-09 -// +/// @file log.h +/// +/// Logging utility framework, where a user can provide at a high-level +/// data structure with function pointers to log messages as a single string +/// with a specific log level. +/// This will call a user-defined function (if able) that will output this +/// message as the user desires. +/// Default functions (output to stderr or file) are defined here. +/// +/// The log_sink is a non-owning copyable struct that points to the data and +/// logging function calls. All logging is performed through log_sink. All +/// classes here are thread safe, follow comments for outlying cases. +/// +/// Special classes inherit log_sink to provide default functionality. +/// log_sink_std should be used to write to std::cout and std::cerr. +/// log_sink_stream pipe to a stream or open a file stream. +/// +/// The logs are made to be logged to a certain log_level. +/// The WARTHOG_TRACE and others in this header provide interfaces to log to a +/// special logger<> class. This logger<> class knows at compile time the +/// minimum level to log, and with the use of a macro will be compiled out if +/// that level was not set at compile time when used with the logging macros. +/// +/// The global logger can be acquired with the glog() (logger<>) or glogs() +/// (log_sink), and set with set_glog(log_sink). The log level of the global +/// logger is set though the WARTHOG_LOG definition, with a default of 1(debug) +/// for debug builds, and 2(information) for release (NDEBUG defined). Macros +/// like WARTHOG_GTRACE will automatically write to the global logger. +/// +/// The _IF will only log if a runtime if true. +/// The _FMT uses the std::format from C++20 to format the output messages. +/// The logging utilities uses dynamic memory std::strings to produce the final +/// log message strings, thus is better to be disabled for release builds +/// through the log level. +/// +/// Log messages produce a messaged as "[TIME LEVEL] msg". +/// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be +/// overridden with define cstring WARTHOG_LOG_TIME. Clock is in local system +/// time, but can be set to UTC by defining WARTHOG_LOG_CLOCK_UTC. +/// +/// @author: Ryan Hechenberger +/// @created: 2025-09-09 #include #include diff --git a/include/warthog/io/observer.h b/include/warthog/io/observer.h index d617bd1..d4aa096 100644 --- a/include/warthog/io/observer.h +++ b/include/warthog/io/observer.h @@ -1,28 +1,27 @@ #ifndef WARTHOG_IO_OBSERVER_H #define WARTHOG_IO_OBSERVER_H -// io/observer.h -// -// Defines use of an observer pattern, in which observer object is registered -// to an observable, and the observable will trigger events to all relevant -// observers. -// The observer is passed to an observable as a list of tuples, -// and when triggering an event will notify all observers by function call of -// the event name that is callable to the observer. -// Observer are either stored as value in the tuple or pointer to an observer. -// -// These function names must be registered before use, common ones registered -// here. -// -// To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). -// Invoke event with observer_[function](listeners, args...) where listeners -// are tuple of observers. This will run through each element in tuple (i) and -// call i.[function](args...) if able. If i.event([function],args...) is a -// valid callable, calls this function first, also tries i.event([function]). -// -// @author: Ryan Hechenberger -// @created: 2025-08-06 -// +/// @file io/observer.h +/// +/// Defines use of an observer pattern, in which observer object is registered +/// to an observable, and the observable will trigger events to all relevant +/// observers. +/// The observer is` passed to an observable as a list of tuples, +/// and when triggering an event will notify all observers by function call of +/// the event name that is callable to the observer. +/// Observer are either stored as value in the tuple or pointer to an observer. +/// +/// These function names must be registered before use, common ones registered +/// here. +/// +/// To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). +/// Invoke event with observer_[function](listeners, args...) where listeners +/// are tuple of observers. This will run through each element in tuple (i) and +/// call i.[function](args...) if able. If i.event([function],args...) is a +/// valid callable, calls this function first, also tries i.event([function]). +/// +/// @author: Ryan Hechenberger +/// @created: 2025-08-06 #include diff --git a/include/warthog/io/posthoc_trace.h b/include/warthog/io/posthoc_trace.h index fcdf35a..926329f 100644 --- a/include/warthog/io/posthoc_trace.h +++ b/include/warthog/io/posthoc_trace.h @@ -1,14 +1,13 @@ #ifndef WARTHOG_IO_POSTHOC_TRACE_H #define WARTHOG_IO_POSTHOC_TRACE_H -// io/posthoc_trace.h -// -// stream_observer that outputs a trace for use with posthoc visuliser. -// See https://posthoc-app.pathfinding.ai/ -// -// @author: Ryan Hechenberger -// @created: 2025-08-07 -// +/// @file posthoc_trace.h +/// +/// stream_observer that outputs a trace for use with posthoc visuliser. +/// See https:///posthoc-app.pathfinding.ai/ +/// +/// @author: Ryan Hechenberger +/// @created: 2025-08-07 #include "stream_observer.h" diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index b1f9239..637b347 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -1,23 +1,24 @@ #ifndef WARTHOG_IO_SCENARIO_H #define WARTHOG_IO_SCENARIO_H -// io/scenario.h -// -// Read/write utilities for scenario files. -// -// Supported formats for read: -// - GPPC 1.0 format (as at 2012 Grid-based Path Planning Competition) -// (fields: bucket,map,mapwidth,mapheight,sx,sy,gx,gy,distance) -// - DIMACS format (as at the 9th DIMACS Implementation Challenge) -// (fields: q [source-id] [target-id]) -// -// Supported formats for generate/write: -// - GPPC 1.0 format (as at 2012 Grid-based Path Planning Competition) -// - Dynamic format (tbd link) -// -// @author: dharabor & Ryan Hechenberger -// @created: 2025-12-04 -// +/// @file scenario.h +/// +/// Read/write utilities for scenario files. +/// +/// Supported formats for read: +/// - GPPC 1.0 format (as at 2012 Grid-based Path Planning Competition) +/// (fields: bucket,map,mapwidth,mapheight,sx,sy,gx,gy,distance) +/// - DIMACS format (as at the 9th DIMACS Implementation Challenge) +/// (fields: q [source-id] [target-id]) +/// - Dynamic format (tbd link) +/// +/// Supported formats for generate/write: +/// - GPPC 1.0 format (as at 2012 Grid-based Path Planning Competition) +/// - Dynamic format (tbd link) +/// +/// @author: dharabor & Ryan Hechenberger +/// @created: 2025-12-04 +/// #include "serialize_base.h" @@ -36,7 +37,8 @@ namespace warthog::io { -struct scenario_query +/// @brief scenario instance struct, reusable with fields set by scenario_serialize +struct scenario_instance { int64_t bucket; std::string_view map; @@ -46,7 +48,8 @@ struct scenario_query double start_y; double goal_x; double goal_y; - std::span dist; + std::span cost; + void* extra_data; ///< holds extra data that a user may require (not used by default) void reset() @@ -79,7 +82,7 @@ class scenario_serialize : public serialize_base INVALID, VALID, FINAL, - CMD_QUERY, + CMD_INST, CMD_PATCH, CMD_UNKNOWN, }; @@ -87,7 +90,7 @@ class scenario_serialize : public serialize_base ~scenario_serialize() override; static constexpr std::string_view - get_dist_str(cost_type a) noexcept; + get_cost_str(cost_type a) noexcept; static constexpr cost_type get_cost_type(std::string_view a) noexcept; @@ -96,6 +99,12 @@ class scenario_serialize : public serialize_base void reset(); + /// @return the current state of scenario read/write + serialize_state state() const noexcept + { + return m_state; + } + void set_scenario_filename(std::filesystem::path&& filename) { @@ -193,46 +202,98 @@ class scenario_serialize : public serialize_base virtual int last_command_type() const; - /// @brief reads in file version information, and sets version accessable - /// via get_version() - /// @param in optional stream to use, otherwise uses internal-set stream - /// @return success std::errc{} (0) + /// @brief reads in file version information, and sets version (accessible via get_version()) + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{}, else failure + /// @pre state() == serialize_state::INIT (returns errc otherwise) virtual std::errc read_version(std::istream* in = nullptr); - /// @brief with header (without version) information. + /// @brief read header (without version) information. /// @param in optional stream to use, otherwise uses internal-set stream - /// @return success std::errc{} (0) + /// @return success std::errc{}, else failure + /// @pre state() == serialize_state::VERSION (returns errc otherwise) /// - /// With VERSION1: peeks first query to gain map name - /// With version2: gets map width/height, available costs and patch + /// With VERSION1: peeks first instance to gain map name + /// With VERSION2: gets map width/height, available costs and patch /// filename virtual std::errc read_header(std::istream* in = nullptr); + /// @brief read header as VERSION_1, does not consider the state or version. use read_header for checks instead. std::errc read_header_v1(std::istream* in = nullptr); + /// @brief read header as VERSION_2, does not consider the state or version. use read_header for checks instead. std::errc read_header_v2(std::istream* in = nullptr); + /// @brief gets the next command type + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return a pair with a value from command_res and std::errc for success (else error) + /// @pre state() == serialize_state::COMMAND (returns error otherwise) + /// + /// With VERSION_1: + /// Returns CMD_INST or FINAL if no more commands are present + /// With VERSION_2: + /// Returns command based on last_command_type(), or FINAL if not present. + /// Default expects CMD_INST or CMD_PATCH. std::pair next_command_type(std::istream* in = nullptr); + /// @brief skips the next count number of commands + /// @param count number of commands to skip + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{}, else failure + /// @pre state() == serialize_state::COMMAND (returns errc otherwise) std::errc skip_commands(int count = 1, std::istream* in = nullptr); + /// @brief reads an instance line and stores results in scenario_instance + /// @param inst where to store the instance data read in + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return a pair with a value from command_res and std::errc for success (else error) + /// @pre state() == serialize_state::COMMAND (returns error otherwise) + /// + /// if next_command_type().first == CMD_INST, then reads the instance, otherwise + /// returns CMD_? dependent on the type of command, or FINAL. + /// With get_version() == VERSION_1: + /// Returns VALID for success, FINAL for no more commands, and INVALID for invalid instance. + /// With get_version() == VERSION_2: + /// Returns VALID for success, FINAL for no more commands, and INVALID for invalid instance, + /// or last_command_type() (only CMD_PATCH for standard v2 scenario) of type of command. + /// + /// INVALID return without error code means success in reading command, but command has invalid parameters. + /// Main checks are non-finite floats (start/goal/cost), mismatch width/height, or out of bounds + /// start/goal. If get_force_int() == true, also checks start/goal are integers within the grid, + /// otherwise allows float also within the grid (allows x == width() or y == height()). virtual std::pair - read_query_line(scenario_query& query, std::istream* in = nullptr); + read_instance_line(scenario_instance& inst, std::istream* in = nullptr); + /// @brief as VERSION_1 with read_instance_line, does not check pre-conditions std::pair - read_query_line_v1(scenario_query& query, std::istream* in = nullptr); + read_instance_line_v1(scenario_instance& inst, std::istream* in = nullptr); + /// @brief as VERSION_2 with read_instance_line, does not check pre-conditions std::pair - read_query_line_v2(scenario_query& query, std::istream* in = nullptr); + read_instance_line_v2(scenario_instance& inst, std::istream* in = nullptr); + /// @brief reads a patch line and stores results in scenario_patch + /// @param patch where to store the patch data read in (not grid) + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return a pair with a value from command_res and std::errc for success (else error) + /// @pre state() == serialize_state::COMMAND (returns error otherwise) + /// + /// if next_command_type().first == CMD_PATCH, then reads the instance, otherwise + /// returns CMD_? dependent on the type of command, or FINAL. + /// With get_version() == VERSION_2: + /// Returns VALID for success, FINAL for no more commands, INVALID if location is out of grid bounds, + /// or last_command_type() (only CMD_INST for standard v2 scenario) of type of command. virtual std::pair - read_patch_line(scenario_patch& query, std::istream* in = nullptr); + read_patch_line(scenario_patch& patch, std::istream* in = nullptr); + /// @brief as VERSION_2 with read_patch_line, does not check pre-conditions std::pair - read_patch_line_v2(scenario_patch& query, std::istream* in = nullptr); + read_patch_line_v2(scenario_patch& patch, std::istream* in = nullptr); +protected: + /// @return a owned version of str std::string_view copy_string(std::string_view str); @@ -243,7 +304,7 @@ class scenario_serialize : public serialize_base std::filesystem::path m_map_filename; uint32_t m_map_width = 0; uint32_t m_map_height = 0; - int32_t m_query_at = 0; + int32_t m_inst_at = 0; // dynamic data std::pmr::monotonic_buffer_resource m_dyn_res; @@ -259,8 +320,8 @@ class scenario_serialize : public serialize_base std::string m_command_type; ///< last cost type }; -inline constexpr std::string_view -scenario_serialize::get_dist_str(cost_type a) noexcept +constexpr std::string_view +scenario_serialize::get_cost_str(cost_type a) noexcept { switch(a) { @@ -278,7 +339,7 @@ scenario_serialize::get_dist_str(cost_type a) noexcept return std::string_view(); } } -inline constexpr cost_type +constexpr cost_type scenario_serialize::get_cost_type(std::string_view a) noexcept { if(a == "8c-ncc") return cost_type::G_8C_NCC; diff --git a/include/warthog/io/serialize_base.h b/include/warthog/io/serialize_base.h index 0c3f26f..35272b9 100644 --- a/include/warthog/io/serialize_base.h +++ b/include/warthog/io/serialize_base.h @@ -1,16 +1,18 @@ #ifndef WARTHOG_IO_SERIALIZE_BASE_H #define WARTHOG_IO_SERIALIZE_BASE_H -// io/serialize_base.h -// -// Read/write base class for serialize classes. -// Adds a uniform low-level interface for reading from a file, one-line at a -// time. Support for both file or just from a istream/ostream object. Tracking -// of line number for user-level error reporting and debugging. -// -// @author: Ryan Hechenberger -// @created: 2026-04-10 -// +/// @file serialize_base.h +/// +/// Read/write base class for serialize classes. +/// Adds a uniform low-level interface for reading from a file, one-line at a +/// time. Support for both file or just from a istream/ostream object. Tracking +/// of line number for user-level error reporting and debugging. +/// +/// Ideal for use with serialize_base::parser (util::string_parser), +/// which supports fast reading of tokens through string_view and error checking. +/// +/// @author: Ryan Hechenberger +/// @created: 2026-04-10 #include "fwd.h" @@ -25,12 +27,26 @@ namespace warthog::io { +/// @brief serialize base class to support line-by-line reading with strict +/// error checking, line-number tracking and other features. +/// +/// The base class for serialize, low-level with strict error checking. +/// The ideal use case is to ensure data is read correctly, and error on ill-formed +/// without raising exceptions. +/// +/// To support low-level, each read/write takes istream|ostream pointer to override the +/// class default-defined, use get_istream|get_ostream to handle the selection manually. +/// Most functions return std::errc of non-value init to state that procedure has failed. +/// +/// Use readline() to raed next line (or next blank line with true), limits length to max_line_length (1k def) and +/// strips \r?\n from end. +/// Use unreadline() to put a line back to be read by next readline (can be changed). class serialize_base { public: using parser = util::string_parser; - static constexpr size_t max_line_length = 16 << 10; // 16kB + static constexpr size_t max_line_length = 1 << 10; ///< 1kB default maximum line length serialize_base(); virtual ~serialize_base(); @@ -85,7 +101,14 @@ class serialize_base return out != nullptr; } + /// @return the maximum line length (null terminator included), 0 means unallocated and will default to max_line_length on readline() call + uint32_t get_max_line_length() noexcept + { + return m_max_line_length; + } + protected: + /// @return the internal istream or provided, or error std::pair get_istream(std::istream* in = nullptr) noexcept { @@ -93,6 +116,7 @@ class serialize_base if(in == nullptr || !in) return {nullptr, std::errc::io_error}; return {in, {}}; } + /// @return the internal istream or provided, or error std::pair get_ostream(std::ostream* out = nullptr) noexcept { @@ -101,26 +125,71 @@ class serialize_base return {nullptr, std::errc::io_error}; return {out, {}}; } + + /// @return true if istream is at eof, false if not or in error bool istream_eof(std::istream* in = nullptr); + + /// @brief reads lines and return 1 line, checking for errors + /// @param in optional stream to use, otherwise uses internal-set stream + /// @param skip_blanks if true, the first line where is_line_blank(line) is false is returned + /// @return the first valid line (can be blank), or error, the final empty line is returned empty and eof is set + /// + /// Reads lines into a buffer (max size get_max_line_length()), removing the line ends \r\n. + /// If unreadline was called before, returns that line, otherwise reads from istream. + /// Change the max buffer size with set_max_line_length, although it clears the buffer and current line. + /// + /// If line does not fit, return an error. + /// If an empty line is discovered, it will return the same result as the eof condition, + /// use istream_eof to distinguish. + /// The file ending on an empty line is handled the same way as the last line having no trailing \n. + /// + /// get_line_num() will return the read number read, reading from an unreadline does not change the line number. std::pair readline(std::istream* in, bool skip_blanks = false); + + /// @brief unreads a line, keeps only a single line + /// @param line the line to return and read later, does not have to match a line read from readline. void unreadline(std::string_view line); + + /// @brief check if while line is either blank or empty, as per std::isspace bool is_line_blank(std::string_view line); + /// @brief sets the max line length, clears current line buffer and line + void set_max_line_length(uint32_t len) + { + m_max_line_length = len; + m_line_data = nullptr; + m_line = std::string_view(); + } + protected: std::filesystem::path m_filename; std::unique_ptr m_stream; std::istream* m_stream_in = nullptr; std::ostream* m_stream_out = nullptr; int32_t m_line_num = 0; + uint32_t m_max_line_length = 0; std::unique_ptr m_line_data; std::string_view m_line; std::string m_unget_line; }; +/// @brief a serialize_base used solely for reading line-by-line +class line_serialize final : public serialize_base +{ +public: + using serialize_base::serialize_base; + + using serialize_base::readline; + using serialize_base::unreadline; + using serialize_base::is_line_blank; + using serialize_base::istream_eof; + using serialize_base::set_max_line_length; +}; + } // namespace warthog::io #endif // WARTHOG_IO_SERIALIZE_BASE_H diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index 2f0fdb5..09373f9 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -1,20 +1,20 @@ #ifndef WARTHOG_IO_STEAM_OBSERVER_H #define WARTHOG_IO_STEAM_OBSERVER_H -// io/stream_observer.h -// -// The stream observer is a base class for observers that can open and own a -// filestream, or pass another filestream. -// This is designed as a many-to-one observers to stream. -// No inbuilt support for multi-threading, use locks in the observer function. -// -// Is designed to be used with observer tuples. -// Inherited class will call stream() to get the current stream for output. -// Using the observer methodology, event functions will be given that will -// write to output in certain ways. -// -// @author: Ryan Hechenberger -// @created: 2025-08-01 +/// @file stream_observer.h +/// +/// The stream observer is a base class for observers that can open and own a +/// filestream, or pass another filestream. +/// This is designed as a many-to-one observers to stream. +/// No inbuilt support for multi-threading, use locks in the observer function. +/// +/// Is designed to be used with observer tuples. +/// Inherited class will call stream() to get the current stream for output. +/// Using the observer methodology, event functions will be given that will +/// write to output in certain ways. +/// +/// @author: Ryan Hechenberger +/// @created: 2025-08-01 // #include "fwd.h" diff --git a/include/warthog/manager/experiment.h b/include/warthog/manager/experiment.h index 699504b..adcd703 100644 --- a/include/warthog/manager/experiment.h +++ b/include/warthog/manager/experiment.h @@ -1,13 +1,13 @@ #ifndef WARTHOG_MANAGER_EXPERIMENT_H #define WARTHOG_MANAGER_EXPERIMENT_H -// scenario_runner.h -// -// Alias for util::experiemnt. -// -// @author: Ryan Hechenberger -// @created: 2026-04-10 -// +/// @file experiment.h +/// +/// Alias for util::experiemnt. +/// +/// @author: Ryan Hechenberger +/// @created: 2026-04-10 +/// #include diff --git a/include/warthog/manager/grid_patch_set.h b/include/warthog/manager/grid_patch_set.h index 73fcf39..9d51c92 100644 --- a/include/warthog/manager/grid_patch_set.h +++ b/include/warthog/manager/grid_patch_set.h @@ -1,14 +1,14 @@ #ifndef WARTHOG_MANAGER_GRID_PATCH_SET_H #define WARTHOG_MANAGER_GRID_PATCH_SET_H -// scenario_runner.h -// -// Take a scenario_manager object and be able to progress through a dynamic -// scenario. -// -// @author: Ryan Hechenberger -// @created: 2026-04-10 -// +/// @file grid_patch_set.h +/// +/// Utility to store grid map/patches. +/// Can read with some flexibility and be used with scenario_runner to update a gridmap. +/// +/// @author: Ryan Hechenberger +/// @created: 2026-04-10 +/// #include #include @@ -23,7 +23,9 @@ namespace warthog::manager /// @brief a class for managing a set of patches /// -/// Stores a list of patches at +/// Stores a list of patches in a vector. +/// Supports loading of patches from a file/stream with flags to control this. +/// User can also add their own patches. class grid_patch_set { public: @@ -36,16 +38,26 @@ class grid_patch_set }; using bittable = domain::gridmap::bittable; static constexpr uint16_t npos = (uint16_t)-1u; + grid_patch_set(std::pmr::memory_resource* upstream = nullptr) : grid_res_( upstream != nullptr ? upstream : std::pmr::get_default_resource()) { } + /// @brief read istream as whole patch set + /// @param file open text istream + /// @param max_grids maximum number of grids to read + /// @return true if success, false otherwise bool - load(std::istream& file); + load(std::istream& file, int max_grids = -1); + + /// @brief opens file and read as whole patch set + /// @param maps the filename to open + /// @param max_grids maximum number of grids to read + /// @return true if success, false otherwise bool - load(const std::filesystem::path& maps); + load(const std::filesystem::path& maps, int max_grids = -1); /// @brief reads from a serialize, checking for errors. Can read both type /// octile and patch files. @@ -71,11 +83,23 @@ class grid_patch_set io::bittable_serialize& S, int max_grids = -1, uint32_t flags = DEFAULT); + /// @brief copies a user-provided bittable, subregion from offset with width/height + /// @param table the base table to push + /// @return true on success, false otherwise + /// @pre (offset_x == 0 && offset_y == 0 && width == npos && height == npos) || (width != npos && height != npos) + /// + /// Will copy from (offset_x,offset_y) table of width by height. + /// Subtable must fully fit within table or fail. + /// Defaults will copy whole bittable, if any arguments are changed then width/height + /// must be specified (i.e. cannot be npos). bool push_copy( bittable table, uint16_t offset_x = 0, uint16_t offset_y = 0, uint16_t width = npos, uint16_t height = npos); + /// @brief pushes a bittable to set, not copying contents, does not own table memory + /// @param patch the patch to push + /// @return true on success, false otherwise bool push_ref(bittable patch); diff --git a/include/warthog/manager/scenario_manager.h b/include/warthog/manager/scenario_manager.h index 8817a80..633622d 100644 --- a/include/warthog/manager/scenario_manager.h +++ b/include/warthog/manager/scenario_manager.h @@ -1,13 +1,13 @@ #ifndef WARTHOG_MANAGER_SCENARIO_MANAGER_H #define WARTHOG_MANAGER_SCENARIO_MANAGER_H -// scenario_runner.h -// -// Alias for util::scenario_manager. -// -// @author: Ryan Hechenberger -// @created: 2026-04-10 -// +/// @file scenario_runner.h +/// +/// Alias for util::scenario_manager. +/// +/// @author: Ryan Hechenberger +/// @created: 2026-04-10 +/// #include "experiment.h" #include diff --git a/include/warthog/manager/scenario_runner.h b/include/warthog/manager/scenario_runner.h index 8cc04d0..f6d890c 100644 --- a/include/warthog/manager/scenario_runner.h +++ b/include/warthog/manager/scenario_runner.h @@ -1,14 +1,14 @@ #ifndef WARTHOG_MANAGER_SCENARIO_RUNNER_H #define WARTHOG_MANAGER_SCENARIO_RUNNER_H -// scenario_runner.h -// -// Take a scenario_manager object and be able to progress through a dynamic -// scenario. -// -// @author: Ryan Hechenberger -// @created: 2026-04-09 -// +/// @file scenario_runner.h +/// +/// Take a scenario_manager object and be able to progress through a dynamic +/// scenario. +/// +/// @author: Ryan Hechenberger +/// @created: 2026-04-09 +/// #include "grid_patch_set.h" #include "scenario_manager.h" @@ -41,10 +41,10 @@ class scenario_runner /// @brief progress from current to count experiment away and return it /// @param count - /// @return pair of the reached query and snapshot id. + /// @return pair of the reached inst and snapshot id. /// /// Will progress through commands until count queries are encounted, - /// returning the final query. When count == 1, is exactly the next query. + /// returning the final inst. When count == 1, is exactly the next inst. /// All patches required std::pair experiment_next(uint32_t count = 1); @@ -61,7 +61,7 @@ class scenario_runner snapshot_next(bool clear_patch = true); /// @brief starting at SNAPSHOT or current PATCH, apply all patches until - /// reaching SNAPSHOT or QUERY + /// reaching SNAPSHOT or INST /// @param clear_patch clears get_patches() /// @return the number of patches, patch id are retrivable from /// get_patches() @@ -72,26 +72,26 @@ class scenario_runner int snapshot_patches(bool clear_patch = true); - /// @brief returns the current query experiment if at query and progress to + /// @brief returns the current inst experiment if at inst and progress to /// next command - /// @return the current query experiment if command is query, otherwise + /// @return the current inst experiment if command is inst, otherwise /// nullptr /// - /// Requires to be on QUERY, otherwise return nullptr and do nothing. - /// If QUERY, returns corrisponding experiment and goto next command. + /// Requires to be on INST, otherwise return nullptr and do nothing. + /// If INST, returns corrisponding experiment and goto next command. /// Does not affect get_patches(). const experiment* - snapshot_query(); + snapshot_inst(); /// @brief progress from current to count experiment away and return it /// @param clear_patch clears get_patches() - /// @return pair of the reached query and snapshot id. + /// @return pair of the reached inst and snapshot id. /// /// Will progress through commands until count queries are encounted, - /// returning the final query. When count == 1, is exactly the next query. + /// returning the final inst. When count == 1, is exactly the next inst. /// All patches required std::span - snapshot_query_all(); + snapshot_inst_all(); bool complete() const noexcept diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index e8ac099..cb830a1 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -10,11 +10,9 @@ // (fields: bucket,map,mapwidth,mapheight,sx,sy,gx,gy,distance) // - DIMACS format (as at the 9th DIMACS Implementation Challenge) // (fields: q [source-id] [target-id]) +// - Dynamic format (tbd) // -// Supported formats for generate/write: -// - GPPC 1.0 format (as at 2012 Grid-based Path Planning Competition) -// -// @author: dharabor +// @author: dharabor & Ryan Hechenberger // @created: 21/08/2012 // @@ -38,12 +36,12 @@ struct scenario_command { SNAPSHOT, PATCH, - QUERY + INST }; int type; ///< command type int32_t bucket; ///< bucket id number (meta), snapshot id for dynamic uint32_t - id; ///< SNAPSHOT: snapshot num, PATCH: patch to apply, QUERY: query id + id; ///< SNAPSHOT: snapshot num, PATCH: patch to apply, INST: inst id union cmd_ { struct snapshot_ @@ -54,10 +52,10 @@ struct scenario_command uint16_t topleft_x; uint16_t topleft_y; } patch; - struct query_ + struct inst_ { uint32_t experiment_id; ///< experiment number - } query; + } inst; } cmd; ///< command union based on type static constexpr scenario_command @@ -73,11 +71,11 @@ struct scenario_command return scenario_command{PATCH, bucket_id, patch_id, {.patch = {x, y}}}; } static constexpr scenario_command - make_query( - int32_t bucket_id, uint32_t query_id, uint32_t experiment_id) noexcept + make_inst( + int32_t bucket_id, uint32_t inst_id, uint32_t experiment_id) noexcept { return scenario_command{ - QUERY, bucket_id, query_id, {.query = {experiment_id}}}; + INST, bucket_id, inst_id, {.inst = {experiment_id}}}; } }; @@ -191,7 +189,7 @@ class scenario_manager void set_cost_type(io::cost_type c) noexcept { - cost_type_ = io::scenario_serialize::get_dist_str(c); + cost_type_ = io::scenario_serialize::get_cost_str(c); } bool @@ -249,10 +247,10 @@ class scenario_manager io::scenario_version version_ = io::scenario_version::UNKNOWN; uint32_t scenario_width_ = 0; uint32_t scenario_height_ = 0; - uint32_t query_count_ = 0; + uint32_t inst_count_ = 0; uint32_t patch_count_ = 0; int32_t static_scenario_start_ - = -1; ///< >=0: is static scenario where query commands start at pos, + = -1; ///< >=0: is static scenario where inst commands start at pos, ///< else is dynamic scenario }; diff --git a/src/io/grid.cpp b/src/io/grid.cpp index 8cdb474..f35f641 100644 --- a/src/io/grid.cpp +++ b/src/io/grid.cpp @@ -6,6 +6,12 @@ namespace warthog::io { +bittable_serialize::bittable_serialize() +{ + // increase max line length for larger grids + set_max_line_length(20 << 10); +} + std::errc bittable_serialize::read_header(std::istream* in) { diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 1a68ac5..3681907 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -25,7 +25,7 @@ scenario_serialize::close() m_version = scenario_version::UNKNOWN; m_map_width = 0; m_map_height = 0; - m_query_at = 0; + m_inst_at = 0; m_cost_strings.clear(); m_cost_type.clear(); m_cost_value.clear(); @@ -42,7 +42,7 @@ scenario_serialize::set_relative_map_filename( int scenario_serialize::last_command_type() const { - if(m_command_type == "Q") return CMD_QUERY; + if(m_command_type == "Q") return CMD_INST; if(m_command_type == "P") return CMD_PATCH; return CMD_UNKNOWN; } @@ -121,15 +121,15 @@ scenario_serialize::read_header_v1(std::istream* in) return ec; } } - // setup distance types + // setup cost types m_cost_strings.resize(1); m_cost_type.resize(1); m_cost_value.resize(1); - m_cost_strings[0] = get_dist_str(cost_type::G_8C_NCC); + m_cost_strings[0] = get_cost_str(cost_type::G_8C_NCC); m_cost_type[0] = cost_type::G_8C_NCC; m_cost_value[0] = -1; - // read first query line to get map + // read first inst line to get map auto [line, err] = readline(in); if(err != std::errc{}) { @@ -175,7 +175,7 @@ scenario_serialize::read_header_v2(std::istream* in) m_state = serialize_state::ERROR; return err; } - // read first query line to get map + // read first inst line to get map std::string_view line; std::string_view token; @@ -310,7 +310,7 @@ scenario_serialize::read_header_v2(std::istream* in) } } - // set_relative_map_filename(m_query.map); + // set_relative_map_filename(m_inst.map); m_state = serialize_state::COMMAND; return std::errc{}; } @@ -346,7 +346,7 @@ scenario_serialize::next_command_type(std::istream* in) return {FINAL, {}}; } unreadline(line); - return {CMD_QUERY, {}}; + return {CMD_INST, {}}; } case scenario_version::VERSION_2: { @@ -408,7 +408,7 @@ scenario_serialize::skip_commands(int count, std::istream* in) } std::pair -scenario_serialize::read_query_line(scenario_query& query, std::istream* in) +scenario_serialize::read_instance_line(scenario_instance& inst, std::istream* in) { if(m_state == serialize_state::END) { return {FINAL, {}}; } if(!can_read(in) || m_state != serialize_state::COMMAND) @@ -419,16 +419,16 @@ scenario_serialize::read_query_line(scenario_query& query, std::istream* in) switch(m_version) { case scenario_version::VERSION_1: - return read_query_line_v1(query, in); + return read_instance_line_v1(inst, in); case scenario_version::VERSION_2: - return read_query_line_v2(query, in); + return read_instance_line_v2(inst, in); default: return {INVALID, std::errc::state_not_recoverable}; } } std::pair -scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) +scenario_serialize::read_instance_line_v1(scenario_instance& inst, std::istream* in) { assert(can_read(in)); // move to start of read @@ -454,53 +454,53 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) return {FINAL, std::errc{}}; } parser par(line); - if(!par.next(query.bucket) + if(!par.next(inst.bucket) .next(token) - .next(query.width) - .next(query.height) - .next(query.start_x) - .next(query.start_y) - .next(query.goal_x) - .next(query.goal_y) + .next(inst.width) + .next(inst.height) + .next(inst.start_x) + .next(inst.start_y) + .next(inst.goal_x) + .next(inst.goal_y) .next(m_cost_value.at(0)) .eof()) { // state not set to error, up to user return {INVALID, par.error()}; } - query.map = token; - query.dist = m_cost_value; - if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) - || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y) - || !std::isfinite(query.dist[0])) + inst.map = token; + inst.cost = m_cost_value; + if(!std::isfinite(inst.start_x) || !std::isfinite(inst.start_y) + || !std::isfinite(inst.goal_x) || !std::isfinite(inst.goal_y) + || !std::isfinite(inst.cost[0])) { - return {INVALID, std::errc::invalid_argument}; + return {INVALID, std::errc{}}; } // perform checking of values, but do not return as error if fail - if(query.width != m_map_width || query.height != m_map_height) + if(inst.width != m_map_width || inst.height != m_map_height) { return {INVALID, std::errc{}}; } if(m_force_int) { // check integer bounds - if(rint(query.start_x) != query.start_x - || rint(query.start_y) != query.start_y - || rint(query.goal_x) != query.goal_x - || rint(query.goal_y) != query.goal_y) + if(rint(inst.start_x) != inst.start_x + || rint(inst.start_y) != inst.start_y + || rint(inst.goal_x) != inst.goal_x + || rint(inst.goal_y) != inst.goal_y) { return {INVALID, std::errc{}}; } - if(query.start_x < 0 || query.start_y >= m_map_width - || query.goal_x < 0 || query.goal_y >= m_map_height) + if(inst.start_x < 0 || inst.start_y >= m_map_width + || inst.goal_x < 0 || inst.goal_y >= m_map_height) { return {INVALID, std::errc{}}; } } else { - if(query.start_x < 0 || query.start_y > m_map_width || query.goal_x < 0 - || query.goal_y > m_map_height) + if(inst.start_x < 0 || inst.start_y > m_map_width || inst.goal_x < 0 + || inst.goal_y > m_map_height) { return {INVALID, std::errc{}}; } @@ -509,7 +509,7 @@ scenario_serialize::read_query_line_v1(scenario_query& query, std::istream* in) } std::pair -scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) +scenario_serialize::read_instance_line_v2(scenario_instance& inst, std::istream* in) { assert(can_read(in)); // move to start of read @@ -538,25 +538,25 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) parser par(line); if(!par.next(m_command_type)) { return {INVALID, par.error()}; } auto cmd_type = last_command_type(); - if(cmd_type != CMD_QUERY) + if(cmd_type != CMD_INST) { unreadline(line); return {cmd_type, std::errc{}}; } - if(!par.next(query.bucket) - .next(query.start_x) - .next(query.start_y) - .next(query.goal_x) - .next(query.goal_y)) + if(!par.next(inst.bucket) + .next(inst.start_x) + .next(inst.start_y) + .next(inst.goal_x) + .next(inst.goal_y)) { return {INVALID, par.error()}; } - if(!std::isfinite(query.start_x) || !std::isfinite(query.start_y) - || !std::isfinite(query.goal_x) || !std::isfinite(query.goal_y)) + if(!std::isfinite(inst.start_x) || !std::isfinite(inst.start_y) + || !std::isfinite(inst.goal_x) || !std::isfinite(inst.goal_y)) { return {INVALID, std::errc::invalid_argument}; } - query.width = query.height = 0; + inst.width = inst.height = 0; for(auto& cost : m_cost_value) { if(!par.next(cost)) { return {INVALID, par.error()}; } @@ -565,7 +565,7 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) return {INVALID, std::errc::invalid_argument}; } } - query.dist = m_cost_value; + inst.cost = m_cost_value; if(!par.eof()) { // unexpected number of paramters @@ -574,23 +574,23 @@ scenario_serialize::read_query_line_v2(scenario_query& query, std::istream* in) if(m_force_int) { // check integer bounds - if(rint(query.start_x) != query.start_x - || rint(query.start_y) != query.start_y - || rint(query.goal_x) != query.goal_x - || rint(query.goal_y) != query.goal_y) + if(rint(inst.start_x) != inst.start_x + || rint(inst.start_y) != inst.start_y + || rint(inst.goal_x) != inst.goal_x + || rint(inst.goal_y) != inst.goal_y) { return {INVALID, std::errc{}}; } - if(query.start_x < 0 || query.start_y >= m_map_width - || query.goal_x < 0 || query.goal_y >= m_map_height) + if(inst.start_x < 0 || inst.start_y >= m_map_width + || inst.goal_x < 0 || inst.goal_y >= m_map_height) { return {INVALID, std::errc{}}; } } else { - if(query.start_x < 0 || query.start_y > m_map_width || query.goal_x < 0 - || query.goal_y > m_map_height) + if(inst.start_x < 0 || inst.start_y > m_map_width || inst.goal_x < 0 + || inst.goal_y > m_map_height) { return {INVALID, std::errc{}}; } diff --git a/src/io/serialize_base.cpp b/src/io/serialize_base.cpp index 677569d..7cff3ea 100644 --- a/src/io/serialize_base.cpp +++ b/src/io/serialize_base.cpp @@ -10,7 +10,6 @@ namespace warthog::io { serialize_base::serialize_base() - : m_line_data(std::make_unique(max_line_length)) { } serialize_base::~serialize_base() = default; @@ -80,12 +79,16 @@ serialize_base::readline(std::istream* in, bool skip_blanks) { size_t len; auto [s, err] = get_istream(in); + if (!m_line_data) { + m_max_line_length = m_max_line_length > 0 ? m_max_line_length : max_line_length; + m_line_data = std::make_unique(m_max_line_length); + } while(true) { if(len = m_unget_line.size(); len != 0) { // return last unreadline - if(len > max_line_length - 1) + if(len > m_max_line_length - 1) { return {{}, std::errc::invalid_argument}; } @@ -106,7 +109,7 @@ serialize_base::readline(std::istream* in, bool skip_blanks) { if(err != std::errc{}) { return {{}, err}; } if(s->eof()) { return {{}, std::errc::io_error}; } - if(!s->getline(m_line_data.get(), max_line_length)) + if(!s->getline(m_line_data.get(), m_max_line_length)) { if(s->eof() && s->gcount() == 0) { diff --git a/src/manager/grid_patch_set.cpp b/src/manager/grid_patch_set.cpp index 789530c..111bfe8 100644 --- a/src/manager/grid_patch_set.cpp +++ b/src/manager/grid_patch_set.cpp @@ -9,7 +9,7 @@ namespace warthog::manager { bool -grid_patch_set::load(std::istream& file) +grid_patch_set::load(std::istream& file, int max_grids) { io::bittable_serialize S; if(auto ec = S.open_read(&file); ec != std::errc{}) @@ -17,7 +17,7 @@ grid_patch_set::load(std::istream& file) WARTHOG_GWARN_FMT("grid patch failed to open file code={}", (int)ec); return false; } - if(int r = deserialize(S); r < 0) + if(int r = deserialize(S, max_grids); r < 0) { WARTHOG_GWARN_FMT("grid patch failed to read patch {}", -r - 1); return false; @@ -26,7 +26,7 @@ grid_patch_set::load(std::istream& file) } bool -grid_patch_set::load(const std::filesystem::path& maps) +grid_patch_set::load(const std::filesystem::path& maps, int max_grids) { io::bittable_serialize S; S.set_filename(std::filesystem::path(maps)); diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index 7ca9eba..8dc7e06 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -36,10 +36,10 @@ scenario_runner::experiment_next(uint32_t count) case util::scenario_command::PATCH: patch_count += snapshot_patches(); break; - case util::scenario_command::QUERY: - if(const experiment* query = snapshot_query(); query != nullptr) + case util::scenario_command::INST: + if(const experiment* inst = snapshot_inst(); inst != nullptr) { - if(--count == 0) return {query, patch_count}; + if(--count == 0) return {inst, patch_count}; } break; default: @@ -91,7 +91,7 @@ scenario_runner::snapshot_next(bool clear_patch) } // current snapshot, goto next snapshot [[fallthrough]]; - case util::scenario_command::QUERY: + case util::scenario_command::INST: ++snapshot_at_; break; case util::scenario_command::PATCH: @@ -135,30 +135,30 @@ scenario_runner::snapshot_patches(bool clear_patch) } const experiment* -scenario_runner::snapshot_query() +scenario_runner::snapshot_inst() { assert(scenario_ != nullptr); auto commands = scenario_->get_commands(); if(command_at_ >= commands.size()) return nullptr; auto cmd = commands[command_at_]; - if(cmd.type != util::scenario_command::QUERY) return nullptr; + if(cmd.type != util::scenario_command::INST) return nullptr; command_at_ += 1; experiment_at_ += 1; - if(cmd.cmd.query.experiment_id >= scenario_->num_experiments() - || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) + if(cmd.cmd.inst.experiment_id >= scenario_->num_experiments() + || cmd.cmd.inst.experiment_id != (uint32_t)experiment_at_) { WARTHOG_GERROR_FMT( - "scenario_runner::snapshot_query invalid experiment_id {} to " + "scenario_runner::snapshot_inst invalid experiment_id {} to " "experiment, expected {} (max {}) in {}", - cmd.cmd.query.experiment_id, experiment_at_, + cmd.cmd.inst.experiment_id, experiment_at_, scenario_->num_experiments(), WARTHOG_FILENAME_LINE); return nullptr; } - return scenario_->get_experiment(cmd.cmd.query.experiment_id); + return scenario_->get_experiment(cmd.cmd.inst.experiment_id); } std::span -scenario_runner::snapshot_query_all() +scenario_runner::snapshot_inst_all() { assert(scenario_ != nullptr); experiments_.clear(); @@ -167,18 +167,18 @@ scenario_runner::snapshot_query_all() while(command_at_ < commands.size()) { auto cmd = commands[command_at_]; - if(cmd.type != util::scenario_command::QUERY) break; - if(cmd.cmd.query.experiment_id >= exp.size() - || cmd.cmd.query.experiment_id != (uint32_t)experiment_at_) + if(cmd.type != util::scenario_command::INST) break; + if(cmd.cmd.inst.experiment_id >= exp.size() + || cmd.cmd.inst.experiment_id != (uint32_t)experiment_at_) { WARTHOG_GERROR_FMT( - "scenario_runner::snapshot_query_all invalid experiment_id {} " + "scenario_runner::snapshot_inst_all invalid experiment_id {} " "to experiment, expected {} (max {}) in {}", - cmd.cmd.query.experiment_id, experiment_at_, exp.size(), + cmd.cmd.inst.experiment_id, experiment_at_, exp.size(), WARTHOG_FILENAME_LINE); return {}; } - experiments_.push_back(exp[cmd.cmd.query.experiment_id]); + experiments_.push_back(exp[cmd.cmd.inst.experiment_id]); command_at_ += 1; experiment_at_ += 1; } diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index a79d89b..42ea984 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -111,16 +111,16 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) static_scenario_start_ = (int32_t)commands_.size(); // read queries until done bool first = true; - io::scenario_query Q; + io::scenario_instance Q; std::string_view map_string; while(true) { Q.reset(); - auto [con, ec] = si.read_query_line(Q); + auto [con, ec] = si.read_instance_line(Q); if(ec != std::errc{}) { WARTHOG_GERROR_FMT( - "scenario_manager failed to read query on line: {}", + "scenario_manager failed to read inst on line: {}", si.get_line_num()); return std::errc::io_error; } @@ -130,7 +130,7 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) if(current_map.size() > 2048) // limit string size { WARTHOG_GERROR_FMT( - "scenario_manager v1 query line map exceeds 2048 chars on " + "scenario_manager v1 inst line map exceeds 2048 chars on " "line: {}", si.get_line_num()); return std::errc::argument_out_of_domain; @@ -144,10 +144,10 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) sizeof(experiment), alignof(experiment))), (uint32_t)Q.start_x, (uint32_t)Q.start_y, (uint32_t)Q.goal_x, (uint32_t)Q.goal_y, si.get_map_width(), si.get_map_height(), - Q.dist[(int)0], map_string); + Q.cost[(int)0], map_string); experiments_.push_back(ex); - commands_.push_back(scenario_command::make_query( - Q.bucket, query_count_++, + commands_.push_back(scenario_command::make_inst( + Q.bucket, inst_count_++, (uint32_t)(experiments_.size() - 1))); } else if(con == io::scenario_serialize::FINAL) { break; } @@ -204,17 +204,17 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) } // read queries until done - io::scenario_query Q; + io::scenario_instance Q; io::scenario_patch P; int last_type = -1; int last_bucket = -1; int snapshot_count = -1; while(true) { - // try reading a query line + // try reading a inst line int con; std::errc ec; - std::tie(con, ec) = si.read_query_line(Q); + std::tie(con, ec) = si.read_instance_line(Q); if(ec != std::errc{}) { WARTHOG_GERROR_FMT( @@ -226,22 +226,22 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) { if(last_type == -1) { - // only used if first command is a query + // only used if first command is a inst commands_.push_back(scenario_command::make_snapshot( Q.bucket, ++snapshot_count)); } - last_type = io::scenario_serialize::CMD_QUERY; + last_type = io::scenario_serialize::CMD_INST; last_bucket = Q.bucket; experiment* ex = std::construct_at( static_cast(experiments_res_.allocate( sizeof(experiment), alignof(experiment))), Q.start_x, Q.start_y, Q.goal_x, Q.goal_y, si.get_map_width(), - si.get_map_height(), Q.dist[(int)io::cost_type::G_8C_NCC], + si.get_map_height(), Q.cost[(int)io::cost_type::G_8C_NCC], map_string); experiments_.push_back(ex); - commands_.push_back(scenario_command::make_query( - Q.bucket, query_count_++, + commands_.push_back(scenario_command::make_inst( + Q.bucket, inst_count_++, (uint32_t)(experiments_.size() - 1))); } else if(con == io::scenario_serialize::CMD_PATCH) @@ -274,7 +274,7 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) } else { - // error, invalid query + // error, invalid inst WARTHOG_GERROR_FMT( "scenario_manager failed to read command on line: {}", si.get_line_num()); From 8029a3dbb12126eb30631b04030d93bb03ae469d Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 May 2026 14:40:37 +1000 Subject: [PATCH 55/61] bugfix uds typename --- include/warthog/search/unidirectional_search.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 7bb0d5c..a4ab920 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -330,10 +330,10 @@ class unidirectional_search H, E, Q, uds_traits> { public: - using unidirectional_search_full = typename unidirectional_search_full< + using unidirectional_search_full = unidirectional_search_full< H, E, Q, - uds_traits>::unidirectional_search_full; - + uds_traits>; + using unidirectional_search_full::unidirectional_search_full; }; From 482ac3197cd2dce7b0e163cf51def99f4a1d9566 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 04:42:00 +0000 Subject: [PATCH 56/61] auto clang-format action --- apps/warthog.cpp | 3 +- include/warthog/domain/grid.h | 4 +- include/warthog/domain/gridmap.h | 44 ++++++----- include/warthog/domain/labelled_gridmap.h | 61 +++++++-------- include/warthog/io/grid.h | 33 ++++---- include/warthog/io/scenario.h | 74 +++++++++++------- include/warthog/io/serialize_base.h | 78 +++++++++++-------- include/warthog/manager/grid_patch_set.h | 16 ++-- .../warthog/search/unidirectional_search.h | 5 +- include/warthog/util/scenario_manager.h | 2 +- src/domain/gridmap.cpp | 4 +- src/io/scenario.cpp | 19 +++-- src/io/serialize_base.cpp | 9 ++- src/util/scenario_manager.cpp | 6 +- 14 files changed, 198 insertions(+), 160 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 91f2720..513758f 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -254,7 +254,8 @@ run_experiments( algo.get_path(&pi, &par, &sol); // check for no solution - if (sol.sum_of_edge_costs_ >= warthog::COST_MAX) { + if(sol.sum_of_edge_costs_ >= warthog::COST_MAX) + { sol.sum_of_edge_costs_ = -1; } diff --git a/include/warthog/domain/grid.h b/include/warthog/domain/grid.h index 012ea34..455edac 100644 --- a/include/warthog/domain/grid.h +++ b/include/warthog/domain/grid.h @@ -7,8 +7,8 @@ /// Includes the grid identifier (id), direction (and direction_id), /// point (coordinate) and utilities. /// -/// These utilities include conversion between direction/direction_id, rotation, -/// direction to point and inverse, helpers for grid traversal, etc. +/// These utilities include conversion between direction/direction_id, +/// rotation, direction to point and inverse, helpers for grid traversal, etc. /// /// @author: dharabor & Ryan Hechenberger /// @created: 2018-11-03 diff --git a/include/warthog/domain/gridmap.h b/include/warthog/domain/gridmap.h index 265030f..a986609 100644 --- a/include/warthog/domain/gridmap.h +++ b/include/warthog/domain/gridmap.h @@ -197,12 +197,12 @@ class gridmap : public memory::bittable // read from the byte just before node_id and shift down until the // nei adjacent to node_id is in the lowest position - tiles[0] - = (uint8_t)(*((uint32_t*)(db_.get() + (pos1 - 1))) >> (bit_offset + 7)); - tiles[1] - = (uint8_t)(*((uint32_t*)(db_.get() + (pos2 - 1))) >> (bit_offset + 7)); - tiles[2] - = (uint8_t)(*((uint32_t*)(db_.get() + (pos3 - 1))) >> (bit_offset + 7)); + tiles[0] = (uint8_t)(*((uint32_t*)(db_.get() + (pos1 - 1))) + >> (bit_offset + 7)); + tiles[1] = (uint8_t)(*((uint32_t*)(db_.get() + (pos2 - 1))) + >> (bit_offset + 7)); + tiles[2] = (uint8_t)(*((uint32_t*)(db_.get() + (pos3 - 1))) + >> (bit_offset + 7)); } // takes the tiles from get_neighbours and tightly packs them into 8-bits @@ -254,9 +254,12 @@ class gridmap : public memory::bittable // read 32bits of memory; grid_id_p is in the // lowest bit position of tiles[1] - tiles[0] = (uint32_t)(*((uint64_t*)(db_.get() + pos1)) >> (bit_offset)); - tiles[1] = (uint32_t)(*((uint64_t*)(db_.get() + pos2)) >> (bit_offset)); - tiles[2] = (uint32_t)(*((uint64_t*)(db_.get() + pos3)) >> (bit_offset)); + tiles[0] + = (uint32_t)(*((uint64_t*)(db_.get() + pos1)) >> (bit_offset)); + tiles[1] + = (uint32_t)(*((uint64_t*)(db_.get() + pos2)) >> (bit_offset)); + tiles[2] + = (uint32_t)(*((uint64_t*)(db_.get() + pos3)) >> (bit_offset)); } // similar to get_neighbours_32bit but grid_id_p is placed into the @@ -285,9 +288,12 @@ class gridmap : public memory::bittable // read 32bits of memory; grid_id_p is in the // highest bit position of tiles[1] - tiles[0] = (uint32_t)(*((uint64_t*)(db_.get() + pos1)) >> (bit_offset + 1)); - tiles[1] = (uint32_t)(*((uint64_t*)(db_.get() + pos2)) >> (bit_offset + 1)); - tiles[2] = (uint64_t)(*((uint64_t*)(db_.get() + pos3)) >> (bit_offset + 1)); + tiles[0] + = (uint32_t)(*((uint64_t*)(db_.get() + pos1)) >> (bit_offset + 1)); + tiles[1] + = (uint32_t)(*((uint64_t*)(db_.get() + pos2)) >> (bit_offset + 1)); + tiles[2] + = (uint64_t)(*((uint64_t*)(db_.get() + pos3)) >> (bit_offset + 1)); } // fetches a contiguous set of tiles from three adjacent rows. @@ -405,14 +411,14 @@ class gridmap : public memory::bittable std::unique_ptr db_; std::filesystem::path filename_; - uint32_t dbwidth_ = 0; - uint32_t dbwidth64_ = 0; - uint32_t dbheight_ = 0; - uint32_t db_size_ = 0; - uint32_t padding_per_row_ = 0; + uint32_t dbwidth_ = 0; + uint32_t dbwidth64_ = 0; + uint32_t dbheight_ = 0; + uint32_t db_size_ = 0; + uint32_t padding_per_row_ = 0; uint32_t padding_column_above_ = 0; - uint32_t max_id_ = 0; - uint32_t num_traversable_ = 0; + uint32_t max_id_ = 0; + uint32_t num_traversable_ = 0; void init_db(); diff --git a/include/warthog/domain/labelled_gridmap.h b/include/warthog/domain/labelled_gridmap.h index f628fa6..c5b2b14 100644 --- a/include/warthog/domain/labelled_gridmap.h +++ b/include/warthog/domain/labelled_gridmap.h @@ -18,14 +18,14 @@ #include "grid.h" #include +#include #include #include -#include #include #include -#include #include +#include #include namespace warthog::domain @@ -40,28 +40,22 @@ class labelled_gridmap { this->init_db(); } - labelled_gridmap(std::istream& input) -{ - setup_stream_(input); -} - labelled_gridmap(io::bittable_serialize& parser) -{ - setup_ser_(parser); -} + labelled_gridmap(std::istream& input) { setup_stream_(input); } + labelled_gridmap(io::bittable_serialize& parser) { setup_ser_(parser); } labelled_gridmap(std::filesystem::path&& filename) -{ - filename_ = std::move(filename); - std::ifstream in(filename_); - setup_stream_(in); -} + { + filename_ = std::move(filename); + std::ifstream in(filename_); + setup_stream_(in); + } labelled_gridmap(const std::filesystem::path& filename) - : labelled_gridmap(std::filesystem::path(filename)) -{ } + : labelled_gridmap(std::filesystem::path(filename)) + { } labelled_gridmap(const char* filename) - : labelled_gridmap(std::filesystem::path(filename)) -{ } + : labelled_gridmap(std::filesystem::path(filename)) + { } labelled_gridmap(const labelled_gridmap&) = delete; - ~labelled_gridmap() = default; + ~labelled_gridmap() = default; /// The number of padded rows before and after static constexpr uint32_t PADDED_ROWS = 3; @@ -265,18 +259,18 @@ class labelled_gridmap std::unique_ptr db_; std::filesystem::path filename_; - uint32_t db_size_ = 0; - uint32_t padding_per_row_ = 0; + uint32_t db_size_ = 0; + uint32_t padding_per_row_ = 0; uint32_t padded_rows_before_first_row_ = 0; - uint32_t padded_rows_after_last_row_ = 0; - uint32_t padded_width_ = 0; - uint32_t padded_height_ = 0; + uint32_t padded_rows_after_last_row_ = 0; + uint32_t padded_width_ = 0; + uint32_t padded_height_ = 0; void init_db(); }; -template +template inline void labelled_gridmap::setup_stream_(std::istream& in) { @@ -287,7 +281,7 @@ labelled_gridmap::setup_stream_(std::istream& in) setup_ser_(parser); } -template +template inline void labelled_gridmap::setup_ser_(io::bittable_serialize& parser) { @@ -299,7 +293,8 @@ labelled_gridmap::setup_ser_(io::bittable_serialize& parser) init_db(); // read raw data to buffer - std::unique_ptr buffer_v = std::make_unique(this->db_size_); + std::unique_ptr buffer_v + = std::make_unique(this->db_size_); std::span buffer(buffer_v.get(), this->db_size_); if(auto ec = parser.read_grid_raw(buffer); ec != std::errc{}) throw std::runtime_error("invalid grid format"); @@ -307,7 +302,7 @@ labelled_gridmap::setup_ser_(io::bittable_serialize& parser) std::copy_n(buffer.data(), buffer.size(), this->db_.get()); } -template +template inline void labelled_gridmap::init_db() { @@ -324,13 +319,13 @@ labelled_gridmap::init_db() { store_width = (this->header_.width_ / 8 + 1) * 8; } - this->padded_width_ = store_width; - this->padded_height_ = store_height; + this->padded_width_ = store_width; + this->padded_height_ = store_height; this->padded_rows_before_first_row_ = PADDED_ROWS; this->padded_rows_after_last_row_ = PADDED_ROWS; - this->padding_per_row_ = store_width - this->header_.width_; + this->padding_per_row_ = store_width - this->header_.width_; - this->db_size_ = store_width * store_height; + this->db_size_ = store_width * store_height; // create a one dimensional dbword array to store the grid this->db_ = std::make_unique(db_size_); diff --git a/include/warthog/io/grid.h b/include/warthog/io/grid.h index ca2c0c2..3a65ae8 100644 --- a/include/warthog/io/grid.h +++ b/include/warthog/io/grid.h @@ -26,10 +26,10 @@ namespace warthog::io /// @brief the type of bittable to (de)serialize enum class bittable_type : uint8_t { - OCTILE, ///< original MovingAI format - PATCH, ///< patch format, grouping multiple octiles - OTHER, ///< unknown format - NONE, ///< no format specified + OCTILE, ///< original MovingAI format + PATCH, ///< patch format, grouping multiple octiles + OTHER, ///< unknown format + NONE, ///< no format specified }; /// @brief the cell character, as specified by MovingAI @@ -159,21 +159,22 @@ class bittable_serialize : public serialize_base return std::errc{}; } - /// @brief reads the map/patch file header, getting the type - /// @param in alternative file stream to read from - /// @return value init on success, error code on failure - /// - /// Reads the header line, `type octile` for bittable_type::OCTILE or - /// `type patch` for bittable_type::PATCH, retrievable by get_type(). - /// For PATCH type, also reads following line for number of patches in - /// file. + /// @brief reads the map/patch file header, getting the type + /// @param in alternative file stream to read from + /// @return value init on success, error code on failure + /// + /// Reads the header line, `type octile` for bittable_type::OCTILE or + /// `type patch` for bittable_type::PATCH, retrievable by get_type(). + /// For PATCH type, also reads following line for number of patches in + /// file. std::errc read_header(std::istream* in = nullptr); - /// @brief Reads the grids' header, getting width/height up to the map data. - /// @param in alternative filestream to read from - /// @return value init on success, error code on failure - /// @pre get_type() matches the format of file. + /// @brief Reads the grids' header, getting width/height up to the map + /// data. + /// @param in alternative filestream to read from + /// @return value init on success, error code on failure + /// @pre get_type() matches the format of file. std::errc read_grid_header(std::istream* in = nullptr); diff --git a/include/warthog/io/scenario.h b/include/warthog/io/scenario.h index 637b347..12e6de1 100644 --- a/include/warthog/io/scenario.h +++ b/include/warthog/io/scenario.h @@ -37,7 +37,8 @@ namespace warthog::io { -/// @brief scenario instance struct, reusable with fields set by scenario_serialize +/// @brief scenario instance struct, reusable with fields set by +/// scenario_serialize struct scenario_instance { int64_t bucket; @@ -49,7 +50,8 @@ struct scenario_instance double goal_x; double goal_y; std::span cost; - void* extra_data; ///< holds extra data that a user may require (not used by default) + void* extra_data; ///< holds extra data that a user may require (not used + ///< by default) void reset() @@ -100,7 +102,8 @@ class scenario_serialize : public serialize_base reset(); /// @return the current state of scenario read/write - serialize_state state() const noexcept + serialize_state + state() const noexcept { return m_state; } @@ -202,9 +205,10 @@ class scenario_serialize : public serialize_base virtual int last_command_type() const; - /// @brief reads in file version information, and sets version (accessible via get_version()) - /// @param in optional stream to use, otherwise uses internal-set stream - /// @return success std::errc{}, else failure + /// @brief reads in file version information, and sets version (accessible + /// via get_version()) + /// @param in optional stream to use, otherwise uses internal-set stream + /// @return success std::errc{}, else failure /// @pre state() == serialize_state::INIT (returns errc otherwise) virtual std::errc read_version(std::istream* in = nullptr); @@ -219,23 +223,26 @@ class scenario_serialize : public serialize_base virtual std::errc read_header(std::istream* in = nullptr); - /// @brief read header as VERSION_1, does not consider the state or version. use read_header for checks instead. + /// @brief read header as VERSION_1, does not consider the state or + /// version. use read_header for checks instead. std::errc read_header_v1(std::istream* in = nullptr); - /// @brief read header as VERSION_2, does not consider the state or version. use read_header for checks instead. + /// @brief read header as VERSION_2, does not consider the state or + /// version. use read_header for checks instead. std::errc read_header_v2(std::istream* in = nullptr); /// @brief gets the next command type /// @param in optional stream to use, otherwise uses internal-set stream - /// @return a pair with a value from command_res and std::errc for success (else error) + /// @return a pair with a value from command_res and std::errc for success + /// (else error) /// @pre state() == serialize_state::COMMAND (returns error otherwise) /// /// With VERSION_1: /// Returns CMD_INST or FINAL if no more commands are present /// With VERSION_2: - /// Returns command based on last_command_type(), or FINAL if not present. - /// Default expects CMD_INST or CMD_PATCH. + /// Returns command based on last_command_type(), or FINAL if not + /// present. Default expects CMD_INST or CMD_PATCH. std::pair next_command_type(std::istream* in = nullptr); /// @brief skips the next count number of commands @@ -249,42 +256,51 @@ class scenario_serialize : public serialize_base /// @brief reads an instance line and stores results in scenario_instance /// @param inst where to store the instance data read in /// @param in optional stream to use, otherwise uses internal-set stream - /// @return a pair with a value from command_res and std::errc for success (else error) + /// @return a pair with a value from command_res and std::errc for success + /// (else error) /// @pre state() == serialize_state::COMMAND (returns error otherwise) /// - /// if next_command_type().first == CMD_INST, then reads the instance, otherwise - /// returns CMD_? dependent on the type of command, or FINAL. + /// if next_command_type().first == CMD_INST, then reads the instance, + /// otherwise returns CMD_? dependent on the type of command, or FINAL. /// With get_version() == VERSION_1: - /// Returns VALID for success, FINAL for no more commands, and INVALID for invalid instance. + /// Returns VALID for success, FINAL for no more commands, and INVALID + /// for invalid instance. /// With get_version() == VERSION_2: - /// Returns VALID for success, FINAL for no more commands, and INVALID for invalid instance, - /// or last_command_type() (only CMD_PATCH for standard v2 scenario) of type of command. + /// Returns VALID for success, FINAL for no more commands, and INVALID + /// for invalid instance, or last_command_type() (only CMD_PATCH for + /// standard v2 scenario) of type of command. /// - /// INVALID return without error code means success in reading command, but command has invalid parameters. - /// Main checks are non-finite floats (start/goal/cost), mismatch width/height, or out of bounds - /// start/goal. If get_force_int() == true, also checks start/goal are integers within the grid, - /// otherwise allows float also within the grid (allows x == width() or y == height()). + /// INVALID return without error code means success in reading command, but + /// command has invalid parameters. Main checks are non-finite floats + /// (start/goal/cost), mismatch width/height, or out of bounds start/goal. + /// If get_force_int() == true, also checks start/goal are integers within + /// the grid, otherwise allows float also within the grid (allows x == + /// width() or y == height()). virtual std::pair read_instance_line(scenario_instance& inst, std::istream* in = nullptr); - /// @brief as VERSION_1 with read_instance_line, does not check pre-conditions + /// @brief as VERSION_1 with read_instance_line, does not check + /// pre-conditions std::pair read_instance_line_v1(scenario_instance& inst, std::istream* in = nullptr); - /// @brief as VERSION_2 with read_instance_line, does not check pre-conditions + /// @brief as VERSION_2 with read_instance_line, does not check + /// pre-conditions std::pair read_instance_line_v2(scenario_instance& inst, std::istream* in = nullptr); /// @brief reads a patch line and stores results in scenario_patch /// @param patch where to store the patch data read in (not grid) /// @param in optional stream to use, otherwise uses internal-set stream - /// @return a pair with a value from command_res and std::errc for success (else error) + /// @return a pair with a value from command_res and std::errc for success + /// (else error) /// @pre state() == serialize_state::COMMAND (returns error otherwise) /// - /// if next_command_type().first == CMD_PATCH, then reads the instance, otherwise - /// returns CMD_? dependent on the type of command, or FINAL. + /// if next_command_type().first == CMD_PATCH, then reads the instance, + /// otherwise returns CMD_? dependent on the type of command, or FINAL. /// With get_version() == VERSION_2: - /// Returns VALID for success, FINAL for no more commands, INVALID if location is out of grid bounds, - /// or last_command_type() (only CMD_INST for standard v2 scenario) of type of command. + /// Returns VALID for success, FINAL for no more commands, INVALID if + /// location is out of grid bounds, or last_command_type() (only CMD_INST + /// for standard v2 scenario) of type of command. virtual std::pair read_patch_line(scenario_patch& patch, std::istream* in = nullptr); @@ -304,7 +320,7 @@ class scenario_serialize : public serialize_base std::filesystem::path m_map_filename; uint32_t m_map_width = 0; uint32_t m_map_height = 0; - int32_t m_inst_at = 0; + int32_t m_inst_at = 0; // dynamic data std::pmr::monotonic_buffer_resource m_dyn_res; diff --git a/include/warthog/io/serialize_base.h b/include/warthog/io/serialize_base.h index 35272b9..89ffc1b 100644 --- a/include/warthog/io/serialize_base.h +++ b/include/warthog/io/serialize_base.h @@ -9,7 +9,8 @@ /// of line number for user-level error reporting and debugging. /// /// Ideal for use with serialize_base::parser (util::string_parser), -/// which supports fast reading of tokens through string_view and error checking. +/// which supports fast reading of tokens through string_view and error +/// checking. /// /// @author: Ryan Hechenberger /// @created: 2026-04-10 @@ -31,22 +32,25 @@ namespace warthog::io /// error checking, line-number tracking and other features. /// /// The base class for serialize, low-level with strict error checking. -/// The ideal use case is to ensure data is read correctly, and error on ill-formed -/// without raising exceptions. +/// The ideal use case is to ensure data is read correctly, and error on +/// ill-formed without raising exceptions. /// -/// To support low-level, each read/write takes istream|ostream pointer to override the -/// class default-defined, use get_istream|get_ostream to handle the selection manually. -/// Most functions return std::errc of non-value init to state that procedure has failed. +/// To support low-level, each read/write takes istream|ostream pointer to +/// override the class default-defined, use get_istream|get_ostream to handle +/// the selection manually. Most functions return std::errc of non-value init +/// to state that procedure has failed. /// -/// Use readline() to raed next line (or next blank line with true), limits length to max_line_length (1k def) and -/// strips \r?\n from end. -/// Use unreadline() to put a line back to be read by next readline (can be changed). +/// Use readline() to raed next line (or next blank line with true), limits +/// length to max_line_length (1k def) and strips \r?\n from end. Use +/// unreadline() to put a line back to be read by next readline (can be +/// changed). class serialize_base { public: using parser = util::string_parser; - static constexpr size_t max_line_length = 1 << 10; ///< 1kB default maximum line length + static constexpr size_t max_line_length = 1 + << 10; ///< 1kB default maximum line length serialize_base(); virtual ~serialize_base(); @@ -101,8 +105,10 @@ class serialize_base return out != nullptr; } - /// @return the maximum line length (null terminator included), 0 means unallocated and will default to max_line_length on readline() call - uint32_t get_max_line_length() noexcept + /// @return the maximum line length (null terminator included), 0 means + /// unallocated and will default to max_line_length on readline() call + uint32_t + get_max_line_length() noexcept { return m_max_line_length; } @@ -130,39 +136,47 @@ class serialize_base bool istream_eof(std::istream* in = nullptr); - /// @brief reads lines and return 1 line, checking for errors - /// @param in optional stream to use, otherwise uses internal-set stream - /// @param skip_blanks if true, the first line where is_line_blank(line) is false is returned - /// @return the first valid line (can be blank), or error, the final empty line is returned empty and eof is set - /// - /// Reads lines into a buffer (max size get_max_line_length()), removing the line ends \r\n. - /// If unreadline was called before, returns that line, otherwise reads from istream. - /// Change the max buffer size with set_max_line_length, although it clears the buffer and current line. + /// @brief reads lines and return 1 line, checking for errors + /// @param in optional stream to use, otherwise uses internal-set stream + /// @param skip_blanks if true, the first line where is_line_blank(line) is + /// false is returned + /// @return the first valid line (can be blank), or error, the final empty + /// line is returned empty and eof is set /// - /// If line does not fit, return an error. - /// If an empty line is discovered, it will return the same result as the eof condition, - /// use istream_eof to distinguish. - /// The file ending on an empty line is handled the same way as the last line having no trailing \n. + /// Reads lines into a buffer (max size get_max_line_length()), removing + /// the line ends \r\n. If unreadline was called before, returns that line, + /// otherwise reads from istream. Change the max buffer size with + /// set_max_line_length, although it clears the buffer and current line. /// - /// get_line_num() will return the read number read, reading from an unreadline does not change the line number. + /// If line does not fit, return an error. + /// If an empty line is discovered, it will return the same result as the + /// eof condition, use istream_eof to distinguish. The file ending on an + /// empty line is handled the same way as the last line having no trailing + /// \n. + /// + /// get_line_num() will return the read number read, reading from an + /// unreadline does not change the line number. std::pair readline(std::istream* in, bool skip_blanks = false); /// @brief unreads a line, keeps only a single line - /// @param line the line to return and read later, does not have to match a line read from readline. + /// @param line the line to return and read later, does not have to match a + /// line read from readline. void unreadline(std::string_view line); - /// @brief check if while line is either blank or empty, as per std::isspace + /// @brief check if while line is either blank or empty, as per + /// std::isspace bool is_line_blank(std::string_view line); /// @brief sets the max line length, clears current line buffer and line - void set_max_line_length(uint32_t len) + void + set_max_line_length(uint32_t len) { m_max_line_length = len; - m_line_data = nullptr; - m_line = std::string_view(); + m_line_data = nullptr; + m_line = std::string_view(); } protected: @@ -183,11 +197,11 @@ class line_serialize final : public serialize_base public: using serialize_base::serialize_base; - using serialize_base::readline; - using serialize_base::unreadline; using serialize_base::is_line_blank; using serialize_base::istream_eof; + using serialize_base::readline; using serialize_base::set_max_line_length; + using serialize_base::unreadline; }; } // namespace warthog::io diff --git a/include/warthog/manager/grid_patch_set.h b/include/warthog/manager/grid_patch_set.h index 9d51c92..b9eb999 100644 --- a/include/warthog/manager/grid_patch_set.h +++ b/include/warthog/manager/grid_patch_set.h @@ -4,7 +4,8 @@ /// @file grid_patch_set.h /// /// Utility to store grid map/patches. -/// Can read with some flexibility and be used with scenario_runner to update a gridmap. +/// Can read with some flexibility and be used with scenario_runner to update a +/// gridmap. /// /// @author: Ryan Hechenberger /// @created: 2026-04-10 @@ -83,21 +84,24 @@ class grid_patch_set io::bittable_serialize& S, int max_grids = -1, uint32_t flags = DEFAULT); - /// @brief copies a user-provided bittable, subregion from offset with width/height + /// @brief copies a user-provided bittable, subregion from offset with + /// width/height /// @param table the base table to push /// @return true on success, false otherwise - /// @pre (offset_x == 0 && offset_y == 0 && width == npos && height == npos) || (width != npos && height != npos) + /// @pre (offset_x == 0 && offset_y == 0 && width == npos && height == + /// npos) || (width != npos && height != npos) /// /// Will copy from (offset_x,offset_y) table of width by height. /// Subtable must fully fit within table or fail. - /// Defaults will copy whole bittable, if any arguments are changed then width/height - /// must be specified (i.e. cannot be npos). + /// Defaults will copy whole bittable, if any arguments are changed then + /// width/height must be specified (i.e. cannot be npos). bool push_copy( bittable table, uint16_t offset_x = 0, uint16_t offset_y = 0, uint16_t width = npos, uint16_t height = npos); - /// @brief pushes a bittable to set, not copying contents, does not own table memory + /// @brief pushes a bittable to set, not copying contents, does not own + /// table memory /// @param patch the patch to push /// @return true on success, false otherwise bool diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index a4ab920..4f18aec 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -331,9 +331,8 @@ class unidirectional_search { public: using unidirectional_search_full = unidirectional_search_full< - H, E, Q, - uds_traits>; - + H, E, Q, uds_traits>; + using unidirectional_search_full::unidirectional_search_full; }; diff --git a/include/warthog/util/scenario_manager.h b/include/warthog/util/scenario_manager.h index cb830a1..d388f17 100644 --- a/include/warthog/util/scenario_manager.h +++ b/include/warthog/util/scenario_manager.h @@ -247,7 +247,7 @@ class scenario_manager io::scenario_version version_ = io::scenario_version::UNKNOWN; uint32_t scenario_width_ = 0; uint32_t scenario_height_ = 0; - uint32_t inst_count_ = 0; + uint32_t inst_count_ = 0; uint32_t patch_count_ = 0; int32_t static_scenario_start_ = -1; ///< >=0: is static scenario where inst commands start at pos, diff --git a/src/domain/gridmap.cpp b/src/domain/gridmap.cpp index 6e9ce5a..bdddcef 100644 --- a/src/domain/gridmap.cpp +++ b/src/domain/gridmap.cpp @@ -104,8 +104,8 @@ gridmap::setup_ser_(io::bittable_serialize& parser) throw std::runtime_error("invalid grid format"); // calculate traversable num_traversable_ = static_cast(std::transform_reduce( - db_.get(), db_.get() + db_size_, static_cast(0), std::plus(), - &std::popcount)); + db_.get(), db_.get() + db_size_, static_cast(0), + std::plus(), &std::popcount)); } void diff --git a/src/io/scenario.cpp b/src/io/scenario.cpp index 3681907..b6c0d53 100644 --- a/src/io/scenario.cpp +++ b/src/io/scenario.cpp @@ -25,7 +25,7 @@ scenario_serialize::close() m_version = scenario_version::UNKNOWN; m_map_width = 0; m_map_height = 0; - m_inst_at = 0; + m_inst_at = 0; m_cost_strings.clear(); m_cost_type.clear(); m_cost_value.clear(); @@ -408,7 +408,8 @@ scenario_serialize::skip_commands(int count, std::istream* in) } std::pair -scenario_serialize::read_instance_line(scenario_instance& inst, std::istream* in) +scenario_serialize::read_instance_line( + scenario_instance& inst, std::istream* in) { if(m_state == serialize_state::END) { return {FINAL, {}}; } if(!can_read(in) || m_state != serialize_state::COMMAND) @@ -428,7 +429,8 @@ scenario_serialize::read_instance_line(scenario_instance& inst, std::istream* in } std::pair -scenario_serialize::read_instance_line_v1(scenario_instance& inst, std::istream* in) +scenario_serialize::read_instance_line_v1( + scenario_instance& inst, std::istream* in) { assert(can_read(in)); // move to start of read @@ -491,8 +493,8 @@ scenario_serialize::read_instance_line_v1(scenario_instance& inst, std::istream* { return {INVALID, std::errc{}}; } - if(inst.start_x < 0 || inst.start_y >= m_map_width - || inst.goal_x < 0 || inst.goal_y >= m_map_height) + if(inst.start_x < 0 || inst.start_y >= m_map_width || inst.goal_x < 0 + || inst.goal_y >= m_map_height) { return {INVALID, std::errc{}}; } @@ -509,7 +511,8 @@ scenario_serialize::read_instance_line_v1(scenario_instance& inst, std::istream* } std::pair -scenario_serialize::read_instance_line_v2(scenario_instance& inst, std::istream* in) +scenario_serialize::read_instance_line_v2( + scenario_instance& inst, std::istream* in) { assert(can_read(in)); // move to start of read @@ -581,8 +584,8 @@ scenario_serialize::read_instance_line_v2(scenario_instance& inst, std::istream* { return {INVALID, std::errc{}}; } - if(inst.start_x < 0 || inst.start_y >= m_map_width - || inst.goal_x < 0 || inst.goal_y >= m_map_height) + if(inst.start_x < 0 || inst.start_y >= m_map_width || inst.goal_x < 0 + || inst.goal_y >= m_map_height) { return {INVALID, std::errc{}}; } diff --git a/src/io/serialize_base.cpp b/src/io/serialize_base.cpp index 7cff3ea..7e59900 100644 --- a/src/io/serialize_base.cpp +++ b/src/io/serialize_base.cpp @@ -9,8 +9,7 @@ namespace warthog::io { -serialize_base::serialize_base() -{ } +serialize_base::serialize_base() { } serialize_base::~serialize_base() = default; std::errc @@ -79,8 +78,10 @@ serialize_base::readline(std::istream* in, bool skip_blanks) { size_t len; auto [s, err] = get_istream(in); - if (!m_line_data) { - m_max_line_length = m_max_line_length > 0 ? m_max_line_length : max_line_length; + if(!m_line_data) + { + m_max_line_length + = m_max_line_length > 0 ? m_max_line_length : max_line_length; m_line_data = std::make_unique(m_max_line_length); } while(true) diff --git a/src/util/scenario_manager.cpp b/src/util/scenario_manager.cpp index 42ea984..29a7f64 100644 --- a/src/util/scenario_manager.cpp +++ b/src/util/scenario_manager.cpp @@ -147,8 +147,7 @@ scenario_manager::load_gppc_scenario_body_v1(io::scenario_serialize& si) Q.cost[(int)0], map_string); experiments_.push_back(ex); commands_.push_back(scenario_command::make_inst( - Q.bucket, inst_count_++, - (uint32_t)(experiments_.size() - 1))); + Q.bucket, inst_count_++, (uint32_t)(experiments_.size() - 1))); } else if(con == io::scenario_serialize::FINAL) { break; } } @@ -241,8 +240,7 @@ scenario_manager::load_gppc_scenario_body_v2(io::scenario_serialize& si) map_string); experiments_.push_back(ex); commands_.push_back(scenario_command::make_inst( - Q.bucket, inst_count_++, - (uint32_t)(experiments_.size() - 1))); + Q.bucket, inst_count_++, (uint32_t)(experiments_.size() - 1))); } else if(con == io::scenario_serialize::CMD_PATCH) { From dc4f9369787691cf64f7c0c2dc67bc42031589cf Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 May 2026 15:35:05 +1000 Subject: [PATCH 57/61] jetteson changes belonging to other branches --- include/warthog/memory/node_pool.h | 110 ++---------------- include/warthog/search/uds_traits.h | 105 +---------------- .../warthog/search/unidirectional_search.h | 53 +++------ include/warthog/util/template.h | 7 -- src/memory/node_pool.cpp | 100 +++++++++------- 5 files changed, 83 insertions(+), 292 deletions(-) diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index 4d780ff..cc5f5e8 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -24,109 +24,26 @@ // #include "cpool.h" -#include -#include -#include -#include #include +#include + namespace warthog::memory { namespace node_pool_ns { -constexpr uint64_t LOG2_NBS = 6; // node block size = 2^n, n >= 3 -constexpr uint64_t NBS = 1 << LOG2_NBS; -constexpr uint64_t NBS_MASK = NBS - 1; -static_assert(LOG2_NBS >= 3, "must be at least 3 for size of 8"); +static const uint64_t NBS = 8; // node block size; set this >= 8 +static const uint64_t LOG2_NBS = 3; +static const uint64_t NBS_MASK = 7; } class node_pool { public: - struct data_deleter_ptr - { - void (*del)(void*) = nullptr; - - constexpr data_deleter_ptr() noexcept = default; - constexpr data_deleter_ptr(void (*p)(void*)) noexcept : del(p) { } - - void - operator()(void* data) const noexcept - { - (*del)(data); - } - }; - node_pool(); node_pool(size_t num_nodes); ~node_pool(); - template< - std::derived_from T = search::search_node, - typename... NodeArgs> - void - set_type(NodeArgs... node_args) noexcept - { - clear(); - using arg_type = std::tuple; - if constexpr(sizeof...(NodeArgs) != 0) - { - create_block_data_ = static_cast( - new arg_type(std::forward(node_args)...)); - } - block_type_sizes_ = sizeof(T); - size_t block_sz = node_pool_ns::NBS * sizeof(T); - blockspool_ = std::make_unique(block_sz, 1); - create_block_ = [](void* block_p, sn_id_t block_id, - void* data) noexcept { - assert(block_p != nullptr); - T* block = reinterpret_cast(block_p); - pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; - for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) - { - if constexpr(sizeof...(NodeArgs) == 0) - { - std::construct_at(block + i, current_id); - } - else - { - assert(data != nullptr); - std::apply( - [block_i = block + i, - current_id](NodeX&&... args) { - std::construct_at(block_i, current_id, args...); - }, - *static_cast(data)); - } - } - }; - clear_ = [](node_pool& np) noexcept { - if(np.blocks_) - { - for(size_t i = 0; i < np.num_blocks_; ++i) - { - T* nodes = reinterpret_cast(np.blocks_[i]); - if(nodes != nullptr) - { - for(uint32_t j = 0; j < node_pool_ns::NBS; ++j) - { - std::destroy_at(nodes + j); - } - } - } - } - if constexpr(sizeof...(NodeArgs) == 0) - { - if(!np.create_block_data_) - { - arg_type* args - = static_cast(np.create_block_data_); - delete args; - } - } - }; - } - // return a warthog::search_node object corresponding to the given id. // if the node has already been generated, return a pointer to the // previous instance; otherwise allocate memory for a new object. @@ -141,24 +58,13 @@ class node_pool size_t mem(); - // reset nodes - void - clear(); - private: void init(size_t nblocks); - void - release(); - size_t num_blocks_ = 0; - std::unique_ptr blocks_; - size_t block_type_sizes_ = 0; - std::unique_ptr blockspool_; - void (*create_block_)(void* block, sn_id_t bock_id, void* data) noexcept - = nullptr; - void (*clear_)(node_pool& np) = nullptr; - void* create_block_data_; + size_t num_blocks_; + search::search_node** blocks_; + cpool* blockspool_; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 9d65c8b..bfaac52 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -14,7 +14,6 @@ #include "search_metrics.h" #include -#include namespace warthog::search { @@ -134,7 +133,7 @@ enum class reopen_policy // decide whether to renopen nodes already expanded (when their g-value // can be improved). we handle the positive case via specialisation. -template +template inline bool reopen() { @@ -148,108 +147,6 @@ reopen() return true; } -struct uds_default_traits -{ - // using node = std::tuple<>; - // using observer = std::tuple<>; - static constexpr admissibility_criteria ac = admissibility_criteria::any; - static constexpr feasibility_criteria fc - = feasibility_criteria::until_exhaustion; - static constexpr reopen_policy rp = reopen_policy::no; -}; - -template< - typename N = search_node, typename L = std::tuple<>, - admissibility_criteria AC = admissibility_criteria::any, - feasibility_criteria FC = feasibility_criteria::until_exhaustion, - reopen_policy RP = reopen_policy::no> -struct uds_traits -{ - using node = N; - using observer = L; - static constexpr admissibility_criteria ac = AC; - static constexpr feasibility_criteria fc = FC; - static constexpr reopen_policy rp = RP; -}; - -namespace details -{ - -template -struct uds_trait_node -{ - using type = search_node; -}; -template - requires requires { typename Traits::node; } -struct uds_trait_node -{ - using type = typename Traits::node; -}; - -template -struct uds_trait_observer -{ - using type = search_node; -}; -template - requires requires { typename Traits::observer; } -struct uds_trait_observer -{ - using type = typename Traits::observer; -}; - -} // namespace details - -template -using uds_trait_node = typename details::uds_trait_node::type; - -template -using uds_trait_observer = typename details::uds_trait_observer::type; - -template -inline consteval admissibility_criteria -uds_trait_ac() noexcept -{ - if constexpr(requires { - { - Traits::ac - } -> util::same_as_rmref; - }) - { - return Traits::ac; - } - else { return admissibility_criteria::any; } -} - -template -inline consteval feasibility_criteria -uds_trait_fc() noexcept -{ - if constexpr(requires { - { - Traits::fc - } -> util::same_as_rmref; - }) - { - return Traits::fc; - } - else { return feasibility_criteria::until_exhaustion; } -} - -template -inline consteval reopen_policy -uds_trait_rp() noexcept -{ - if constexpr(requires { - { Traits::rp } -> util::same_as_rmref; - }) - { - return Traits::rp; - } - else { return reopen_policy::no; } -} - } // namespace warthog::search #endif // WARTHOG_SEARCH_UDS_TRAITS_H diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 4f18aec..2a04e05 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -41,30 +41,20 @@ namespace warthog::search // (default: search for any solution, until OPEN is exhausted) template< typename H, typename E, typename Q = util::pqueue_min, - typename Traits = uds_default_traits> -class unidirectional_search_full + typename L = std::tuple<>, + admissibility_criteria AC = admissibility_criteria::any, + feasibility_criteria FC = feasibility_criteria::until_exhaustion, + reopen_policy RP = reopen_policy::no> +class unidirectional_search { public: - using traits = Traits; - using search_node = uds_trait_node; - using L = uds_trait_observer; - - static constexpr admissibility_criteria AC = uds_trait_ac(); - static constexpr feasibility_criteria FC = uds_trait_fc(); - static constexpr reopen_policy RP = uds_trait_rp(); - - unidirectional_search_full( + unidirectional_search( H* heuristic, E* expander, Q* queue, L listeners = L{}) : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) { } - unidirectional_search_full(const unidirectional_search_full& other) - = delete; - ~unidirectional_search_full() = default; - unidirectional_search_full& - operator=(const unidirectional_search_full& other) - = delete; + ~unidirectional_search() { } void get_pathcost(problem_instance* pi, search_parameters* par, solution* sol) @@ -148,6 +138,14 @@ class unidirectional_search_full Q* open_; [[no_unique_address]] L listeners_; + // no copy ctor + unidirectional_search(const unidirectional_search& other) { } + unidirectional_search& + operator=(const unidirectional_search& other) + { + return *this; + } + /** * Initialise a new 'search_node' for the ongoing search given the parent * node (@param current). @@ -255,7 +253,7 @@ class unidirectional_search_full if(n->get_search_number() != current->get_search_number()) { initialise_node_(n, current->get_id(), gval, pi, par, sol); - if(n->get_f() <= sol->sum_of_edge_costs_) + if(n->get_f() < sol->sum_of_edge_costs_) { open_->push(n); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Generate: {}", *n); @@ -317,25 +315,6 @@ class unidirectional_search_full } }; -// Keep for backward compatibility. -// Done as class instead of using to support user-defined deduction -template< - typename H, typename E, typename Q = util::pqueue_min, - typename L = std::tuple<>, - admissibility_criteria AC = admissibility_criteria::any, - feasibility_criteria FC = feasibility_criteria::until_exhaustion, - reopen_policy RP = reopen_policy::no> -class unidirectional_search - : public unidirectional_search_full< - H, E, Q, uds_traits> -{ -public: - using unidirectional_search_full = unidirectional_search_full< - H, E, Q, uds_traits>; - - using unidirectional_search_full::unidirectional_search_full; -}; - template< typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>> diff --git a/include/warthog/util/template.h b/include/warthog/util/template.h index 47213c2..ce94e3d 100644 --- a/include/warthog/util/template.h +++ b/include/warthog/util/template.h @@ -108,13 +108,6 @@ choose_integer_sequence(auto value, TemplateFunc&& tfunc) value, std::forward(tfunc)); } -template -concept same_as_rmref - = std::same_as, std::remove_reference_t>; -template -concept same_as_rmcvref - = std::same_as, std::remove_cvref_t>; - } // namespace warthog::util #endif // WARTHOG_UTIL_CAST_H diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index a649c30..d2df7a1 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -1,28 +1,20 @@ #include - #include #include namespace warthog::memory { -node_pool::node_pool() { } -node_pool::node_pool(size_t num_nodes) +node_pool::node_pool(size_t num_nodes) : blocks_(0) { init(num_nodes); - set_type(); -} - -node_pool::~node_pool() -{ - clear(); } void node_pool::init(size_t num_nodes) { num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = std::make_unique(num_blocks_); + blocks_ = new search::search_node*[num_blocks_]; for(size_t i = 0; i < num_blocks_; i++) { blocks_[i] = 0; @@ -33,6 +25,26 @@ node_pool::init(size_t num_nodes) // DEFAULT_CHUNK_SIZE and assign addresses // from that pool in order to generate blocks of nodes. when the pool is // full, cpool pre-allocates more, one chunk at a time. + size_t block_sz = node_pool_ns::NBS * sizeof(search::search_node); + blockspool_ = new cpool(block_sz, 1); +} + +node_pool::~node_pool() +{ + // delete [] node_init_; + + blockspool_->reclaim(); + delete blockspool_; + + for(size_t i = 0; i < num_blocks_; i++) + { + if(blocks_[i] != 0) + { + // std::cerr << "deleting block: "<> node_pool_ns::LOG2_NBS; sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - assert(block_id < num_blocks_); + + // id outside the pool address range + if(block_id > num_blocks_) { return 0; } // add a new block of nodes if necessary if(!blocks_[block_id]) { // std::cerr << "generating block: "<(blockspool_->allocate()); - create_block_(blocks_[block_id], block_id, create_block_data_); + blocks_[block_id] = new(blockspool_->allocate()) + search::search_node[node_pool_ns::NBS]; + + // initialise memory + sn_id_t current_id = sn_id_t{node_id} - list_id; + for(uint32_t i = 0; i < node_pool_ns::NBS; i += 8) + { + new(&blocks_[block_id][i]) + search::search_node(pad_id{current_id++}); + new(&blocks_[block_id][i + 1]) + search::search_node(pad_id{current_id++}); + new(&blocks_[block_id][i + 2]) + search::search_node(pad_id{current_id++}); + new(&blocks_[block_id][i + 3]) + search::search_node(pad_id{current_id++}); + new(&blocks_[block_id][i + 4]) + search::search_node(pad_id{current_id++}); + new(&blocks_[block_id][i + 5]) + search::search_node(pad_id{current_id++}); + new(&blocks_[block_id][i + 6]) + search::search_node(pad_id{current_id++}); + new(&blocks_[block_id][i + 7]) + search::search_node(pad_id{current_id++}); + } } // return the node from its position in the assocated block - return reinterpret_cast( - blocks_[block_id] + list_id * block_type_sizes_); -} - -void -node_pool::release() -{ - num_blocks_ = 0; - blocks_ = nullptr; - blockspool_ = nullptr; - create_block_ = nullptr; + return &(blocks_[block_id][list_id]); } search::search_node* node_pool::get_ptr(pad_id node_id) { - assert((sn_id_t{node_id} >> node_pool_ns::LOG2_NBS) < num_blocks_); - sn_id_t block_id = static_cast(node_id) >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; - assert(block_id < num_blocks_); - assert(blocks_[block_id] != nullptr); - return reinterpret_cast( - blocks_[block_id] + list_id * block_type_sizes_); + sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; + + // id outside the pool address range + if(block_id > num_blocks_) { return 0; } + + if(!blocks_[block_id]) { return 0; } + return &(blocks_[block_id][list_id]); } size_t @@ -86,14 +112,4 @@ node_pool::mem() return bytes; } -void -node_pool::clear() -{ - if(clear_) { (*clear_)(*this); } - blockspool_ = nullptr; - create_block_ = nullptr; - clear_ = nullptr; - create_block_data_ = nullptr; -} - } // namespace warthog::memory From 992503c8707be194c20c87415fa4ca90c151f474 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 May 2026 15:37:28 +1000 Subject: [PATCH 58/61] jetteson stale files --- src/io/observer.cpp | 82 ----------------------- src/io/traces.cpp | 156 -------------------------------------------- 2 files changed, 238 deletions(-) delete mode 100644 src/io/observer.cpp delete mode 100644 src/io/traces.cpp diff --git a/src/io/observer.cpp b/src/io/observer.cpp deleted file mode 100644 index 92e1316..0000000 --- a/src/io/observer.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include -#include - -namespace warthog::io -{ - -stream_observer::stream_observer( - const std::filesystem::path& filename, std::ios_base::openmode mode) -{ - shared_stream_ = std::make_shared(filename, mode); - stream_ = shared_stream_.get(); -} -stream_observer::stream_observer(std::ostream& stream) -{ - stream_ = &stream; -} -stream_observer::stream_observer(const shared_stream_t& stream) -{ - shared_stream_ = stream; - stream_ = shared_stream_.get(); -} -stream_observer::~stream_observer() = default; - -void -stream_observer::stream_open( - const std::filesystem::path& filename, std::ios_base::openmode mode) -{ - shared_stream_ = std::make_shared(filename, mode); - stream_ = shared_stream_.get(); -} -void -stream_observer::stream(std::ostream& stream) -{ - stream_ = &stream; - shared_stream_ = nullptr; -} -void -stream_observer::stream_share(const shared_stream_t& stream) -{ - shared_stream_ = stream; - stream_ = shared_stream_.get(); -} -void -stream_observer::stream_share(const stream_observer& stream) -{ - shared_stream_ = stream.shared_stream_; - stream_ = stream.stream_; -} -void -stream_observer::stream_stdout() -{ - shared_stream_ = nullptr; - stream_ = &std::cout; -} -void -stream_observer::stream_stderr() -{ - shared_stream_ = nullptr; - stream_ = &std::cerr; -} -void -stream_observer::clear_stream() -{ - shared_stream_ = nullptr; - stream_ = nullptr; -} - -void -posthoc_trace::print_posthoc_header() -{ - if(*this) - { - stream() << R"posthoc(version: 1.4.0 -events: -)posthoc"; - } -} - -} // namespace warthog::io diff --git a/src/io/traces.cpp b/src/io/traces.cpp deleted file mode 100644 index 47e8e21..0000000 --- a/src/io/traces.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include -#include - -namespace warthog::io -{ - -void -grid_trace::print_posthoc_header() -{ - if(*this) - { - stream() << R"posthoc(version: 1.4.0 -views: - cell: - - $: rect - width: 1 - height: 1 - x: ${{$.x}} - y: ${{$.y}} - fill: ${{$.fill}} - clear: ${{ $.clear }} - main: - - $: cell - $if: ${{ $.type == 'source' }} - fill: green - clear: false - - $: cell - $if: ${{ $.type == 'destination' }} - fill: red - clear: false - - $: cell - $if: ${{ $.type == 'expand' }} - fill: cyan - clear: false - - $: cell - $if: ${{ $.type == 'expand' }} - fill: blue - clear: close - - $: cell - $if: ${{ $.type == 'generate' }} - fill: purple - clear: false - - $: cell - $if: ${{ $.type == 'generate' }} - fill: orange - clear: true -pivot: - x: ${{ $.x + 0.5 }} - y: ${{ $.y + 0.5 }} - scale: 1 -events: -)posthoc"; - } -} - -void -grid_trace::begin_search(int id, const search::search_problem_instance& pi) -{ - posthoc_trace::begin_search(id); - if(*this) - { - if(grid_ == nullptr) - { - throw std::logic_error("grid_trace::grid_ is null"); - } - uint32_t x, y; - grid_->to_unpadded_xy(pi.start_, x, y); - stream() << std::format( - " - {{ type: source, id: {}, x: {}, y: {} }}\n", pi.start_.id, x, - y); - grid_->to_unpadded_xy(pi.target_, x, y); - stream() << std::format( - " - {{ type: destination, id: {}, x: {}, y: {} }}\n", - pi.target_.id, x, y); - } -} - -void -grid_trace::expand_node(const node& current) const -{ - if(*this) - { - assert(grid_ != nullptr); - std::string pid; - if(auto cpid = current.get_parent(); !cpid.is_none()) - { - pid = std::format(", pId: {}", cpid.id); - } - uint32_t x, y; - grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format( - " - {{ type: expand, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g()); - } -} - -void -grid_trace::relax_node(const node& current) const -{ - if(*this) - { - assert(grid_ != nullptr); - std::string pid; - if(auto cpid = current.get_parent(); !cpid.is_none()) - { - pid = std::format(", pId: {}", cpid.id); - } - uint32_t x, y; - grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format( - " - {{ type: relax, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g()); - } -} - -void -grid_trace::close_node(const node& current) const -{ - if(*this) - { - assert(grid_ != nullptr); - std::string pid; - if(auto cpid = current.get_parent(); !cpid.is_none()) - { - pid = std::format(", pId: {}", cpid.id); - } - uint32_t x, y; - grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format( - " - {{ type: close, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g()); - } -} - -void -grid_trace::generate_node( - const node* parent, const node& child, cost_t, uint32_t) const -{ - if(*this) - { - assert(grid_ != nullptr); - std::string pid; - if(parent != nullptr) - { - pid = std::format(", pId: {}", parent->get_id().id); - } - uint32_t x, y; - grid_->to_unpadded_xy(child.get_id(), x, y); - stream() << std::format( - " - {{ type: generate, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - child.get_id().id, pid, x, y, child.get_f(), child.get_g()); - } -} - -} // namespace warthog::io From 04b923664c325eb2e37c036bf025b033571bdd59 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 May 2026 15:41:23 +1000 Subject: [PATCH 59/61] bugfix --- include/warthog/search/search_node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index b091c9a..bdfa9e4 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -19,7 +19,7 @@ namespace warthog::search struct search_node { search_node() noexcept = default; - search_node(pad_id id = pad_id::max()) noexcept : id_(id) { } + search_node(pad_id id) noexcept : id_(id) { } inline void init( From c5442cbc7eaf467bd24c2349227703902ff24924 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 May 2026 16:36:07 +1000 Subject: [PATCH 60/61] added snapshot to warthog output --- apps/warthog.cpp | 5 +++-- include/warthog/manager/scenario_runner.h | 15 +++++++++++++++ src/manager/scenario_runner.cpp | 6 ++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 513758f..8948f5c 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -167,7 +167,7 @@ run_experiments( auto* expander = algo.get_expander(); if(expander == nullptr) return 1; - out << "id\talg\texpanded\tgenerated\treopen\tsurplus\theapops" + out << "id\tsnapshot\talg\texpanded\tgenerated\treopen\tsurplus\theapops" << "\tnanos\tplen\tpcost\tscost\tmap\n"; for(uint32_t i = 0;; ++i) @@ -272,7 +272,8 @@ run_experiments( } #endif - out << i << "\t" << alg_name << "\t" << sol.met_.nodes_expanded_ + out << i << "\t" << scen.run.get_snapshot_at() << "\t" + << alg_name << "\t" << sol.met_.nodes_expanded_ << "\t" << sol.met_.nodes_generated_ << "\t" << sol.met_.nodes_reopen_ << "\t" << sol.met_.nodes_surplus_ << "\t" << sol.met_.heap_ops_ << "\t" diff --git a/include/warthog/manager/scenario_runner.h b/include/warthog/manager/scenario_runner.h index f6d890c..3c9cef8 100644 --- a/include/warthog/manager/scenario_runner.h +++ b/include/warthog/manager/scenario_runner.h @@ -92,6 +92,21 @@ class scenario_runner /// All patches required std::span snapshot_inst_all(); + + uint32_t get_command_at() const noexcept + { + return command_at_; + } + + int32_t get_experiment_at() const noexcept + { + return experiment_at_; + } + + int32_t get_snapshot_at() const noexcept + { + return snapshot_at_; + } bool complete() const noexcept diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index 8dc7e06..37981b6 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -92,7 +92,7 @@ scenario_runner::snapshot_next(bool clear_patch) // current snapshot, goto next snapshot [[fallthrough]]; case util::scenario_command::INST: - ++snapshot_at_; + ++experiment_at_; break; case util::scenario_command::PATCH: snapshot_patches(false); @@ -115,8 +115,10 @@ scenario_runner::snapshot_patches(bool clear_patch) int count = 0; // if start of snapshot, apply that snapshot patches if(command_at_ < commands.size() - && commands[command_at_].type == util::scenario_command::SNAPSHOT) + && commands[command_at_].type == util::scenario_command::SNAPSHOT) { + snapshot_at_ = commands[command_at_].id; command_at_ += 1; + } // while command_at_ is PATCH, add patch to applied list while(command_at_ < commands.size()) { From 86d8687c6dcc0d749e1d27c12c34f780e54c7588 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 06:37:47 +0000 Subject: [PATCH 61/61] auto clang-format action --- apps/warthog.cpp | 11 +++++------ include/warthog/manager/scenario_runner.h | 15 +++++++++------ src/manager/scenario_runner.cpp | 3 ++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 8948f5c..50ab3ca 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -272,12 +272,11 @@ run_experiments( } #endif - out << i << "\t" << scen.run.get_snapshot_at() << "\t" - << alg_name << "\t" << sol.met_.nodes_expanded_ - << "\t" << sol.met_.nodes_generated_ << "\t" - << sol.met_.nodes_reopen_ << "\t" << sol.met_.nodes_surplus_ - << "\t" << sol.met_.heap_ops_ << "\t" - << sol.met_.time_elapsed_nano_.count() << "\t" + out << i << "\t" << scen.run.get_snapshot_at() << "\t" << alg_name + << "\t" << sol.met_.nodes_expanded_ << "\t" + << sol.met_.nodes_generated_ << "\t" << sol.met_.nodes_reopen_ + << "\t" << sol.met_.nodes_surplus_ << "\t" << sol.met_.heap_ops_ + << "\t" << sol.met_.time_elapsed_nano_.count() << "\t" << (!sol.path_.empty() ? sol.path_.size() - 1 : 0) << "\t" << sol.sum_of_edge_costs_ << "\t" << exp->distance() << "\t" << scen.mgr->last_file_loaded() << std::endl; diff --git a/include/warthog/manager/scenario_runner.h b/include/warthog/manager/scenario_runner.h index 3c9cef8..8fbd0b9 100644 --- a/include/warthog/manager/scenario_runner.h +++ b/include/warthog/manager/scenario_runner.h @@ -92,18 +92,21 @@ class scenario_runner /// All patches required std::span snapshot_inst_all(); - - uint32_t get_command_at() const noexcept + + uint32_t + get_command_at() const noexcept { return command_at_; } - - int32_t get_experiment_at() const noexcept + + int32_t + get_experiment_at() const noexcept { return experiment_at_; } - - int32_t get_snapshot_at() const noexcept + + int32_t + get_snapshot_at() const noexcept { return snapshot_at_; } diff --git a/src/manager/scenario_runner.cpp b/src/manager/scenario_runner.cpp index 37981b6..724dd3d 100644 --- a/src/manager/scenario_runner.cpp +++ b/src/manager/scenario_runner.cpp @@ -115,7 +115,8 @@ scenario_runner::snapshot_patches(bool clear_patch) int count = 0; // if start of snapshot, apply that snapshot patches if(command_at_ < commands.size() - && commands[command_at_].type == util::scenario_command::SNAPSHOT) { + && commands[command_at_].type == util::scenario_command::SNAPSHOT) + { snapshot_at_ = commands[command_at_].id; command_at_ += 1; }