fixed bug in attaching nics to hosts, changed documentation format and generator (buggy but usable)
This commit is contained in:
@@ -1,9 +1,15 @@
|
|||||||
# core/error.h
|
# core/error.h
|
||||||
|
## `dofs::log_error`
|
||||||
## Free functions
|
`void log_error(std::string_view type, std::string_view message, std::optional<uint64_t> timestamp = std::nullopt) noexcept;`
|
||||||
|
## `DOFS_ERROR`
|
||||||
### `std::mutex &error_mutex() noexcept;`
|
`#define DOFS_ERROR(TYPE, MSG)`
|
||||||
### `inline T&& show(std::string_view name, T&& value) noexcept { ... }`
|
## `DOFS_ERROR_ST`
|
||||||
### `std::lock_guard<std::mutex> lock(error_mutex());`
|
`#define DOFS_ERROR_ST(TYPE, SRC, MSG, TS)`
|
||||||
### `inline T&& eval_and_show(std::string_view expr, T&& value) noexcept { ... }`
|
## `DOFS_ERROR_T`
|
||||||
### `std::lock_guard<std::mutex> lock(error_mutex());`
|
`#define DOFS_ERROR_T(TYPE, MSG, TS)`
|
||||||
|
## `DOFS_EVAL`
|
||||||
|
`#define DOFS_EVAL(EXPR)`
|
||||||
|
## `DOFS_SHOW`
|
||||||
|
`#define DOFS_SHOW(VAR)`
|
||||||
|
## `FORMAT`
|
||||||
|
`#define FORMAT(VAR)`
|
||||||
@@ -1,10 +1,32 @@
|
|||||||
# core/host.h
|
# core/host.h
|
||||||
|
## `Host::Host`
|
||||||
|
`Host(Simulator *const sim, NodeId id) noexcept`
|
||||||
|
|
||||||
## class NetworkNic — public interface
|
Binds a host to a simulator under the `NodeId` id.
|
||||||
|
|
||||||
### `Host(Simulator *const sim, NodeId id) noexcept;`
|
## `Host::Host`
|
||||||
### `virtual ~Host() = default;`
|
`Host(const Host &) = delete`
|
||||||
### `NetworkNic *nic() const noexcept { ... }`
|
|
||||||
### `void attach_nic(NetworkNic* nic) noexcept;`
|
Binds a host to a simulator under the `NodeId` id.
|
||||||
### `void detach_nic(NetworkNic* nic) noexcept;`
|
|
||||||
### `Host(const Host &) = delete;`
|
## `Host::attach_nic`
|
||||||
|
`void attach_nic(NetworkNic *nic) noexcept`
|
||||||
|
|
||||||
|
Attaches a `NetworkNic` to the host.
|
||||||
|
|
||||||
|
## `Host::detach_nic`
|
||||||
|
`void detach_nic(NetworkNic *nic) noexcept`
|
||||||
|
|
||||||
|
Detaches a `NetworkNic` to the host.
|
||||||
|
## `Host::nic`
|
||||||
|
`NetworkNic *nic() const noexcept`
|
||||||
|
## `Host::operator=`
|
||||||
|
`Host &operator=(const Host &) = delete`
|
||||||
|
## `Host::recv_flow`
|
||||||
|
`virtual void recv_flow(NodeId src, FlowId flow, FlowPriority priority, Bytes flow_size) = 0`
|
||||||
|
## `Host::recv_frame`
|
||||||
|
`virtual void recv_frame(const Packet &frame) = 0`
|
||||||
|
## `Host::recv_mgmt_msg`
|
||||||
|
`virtual void recv_mgmt_msg(std::unique_ptr<struct MgmtMsg> msg) = 0`
|
||||||
|
## `Host::~Host`
|
||||||
|
`virtual ~Host() = default`
|
||||||
|
|||||||
@@ -1,13 +1,31 @@
|
|||||||
# core/logger.h
|
# core/logger.h
|
||||||
|
## `Logger::Logger`
|
||||||
## class Logger — public interface
|
`Logger(Logger &&) = delete`
|
||||||
|
## `Logger::Logger`
|
||||||
### `Logger(std::string_view path, bool append) noexcept;`
|
`Logger(const Logger &) = delete`
|
||||||
### `~Logger() noexcept;`
|
## `Logger::Logger`
|
||||||
### `Logger(const Logger &) = delete;`
|
`Logger(std::string_view path, bool append) noexcept`
|
||||||
### `Logger(Logger &&) = delete;`
|
## `Logger::close`
|
||||||
### `bool is_open() const noexcept { ... }`
|
`void close() noexcept`
|
||||||
### `void write_line(std::string_view line) noexcept;`
|
## `Logger::flush`
|
||||||
### `void flush() noexcept;`
|
`void flush() noexcept`
|
||||||
### `void close() noexcept;`
|
## `Logger::is_open`
|
||||||
### `std::string_view path() const noexcept { ... }`
|
`bool is_open() const noexcept`
|
||||||
|
## `Logger::operator=`
|
||||||
|
`Logger &operator=(Logger &&) = delete`
|
||||||
|
## `Logger::operator=`
|
||||||
|
`Logger &operator=(const Logger &) = delete`
|
||||||
|
## `Logger::path`
|
||||||
|
`std::string_view path() const noexcept`
|
||||||
|
## `Logger::write_line`
|
||||||
|
`void write_line(std::string_view line) noexcept`
|
||||||
|
## `Logger::~Logger`
|
||||||
|
`~Logger() noexcept`
|
||||||
|
## `dofs::close`
|
||||||
|
`void close() noexcept;`
|
||||||
|
## `dofs::flush`
|
||||||
|
`void flush() noexcept;`
|
||||||
|
## `dofs::write_line`
|
||||||
|
`void write_line(std::string_view line) noexcept;`
|
||||||
|
## `open`
|
||||||
|
`private: bool open(std::ios::openmode mode) noexcept;`
|
||||||
@@ -1,13 +1,21 @@
|
|||||||
# core/node.h
|
# core/node.h
|
||||||
|
## `Node::Node`
|
||||||
## class Node — public interface
|
`Node(Simulator *const sim, NodeId id, NodeType type) noexcept`
|
||||||
|
## `Node::Node`
|
||||||
### `Node(Simulator *const sim, NodeId id, NodeType type) noexcept;`
|
`Node(const Node &) = delete`
|
||||||
### `virtual ~Node() = default;`
|
## `Node::boot`
|
||||||
### `NodeId id() const noexcept;`
|
`void boot(Time boottime_ns)`
|
||||||
### `NodeStatus status() const noexcept;`
|
## `Node::id`
|
||||||
### `NodeType type() const noexcept;`
|
`NodeId id() const noexcept`
|
||||||
### `void set_status(NodeStatus s) noexcept;`
|
## `Node::operator=`
|
||||||
### `void boot(Time boottime_ns);`
|
`Node &operator=(const Node &) = delete`
|
||||||
### `void reboot(Time boottime_ns);`
|
## `Node::reboot`
|
||||||
### `Node(const Node &) = delete;`
|
`void reboot(Time boottime_ns)`
|
||||||
|
## `Node::set_status`
|
||||||
|
`void set_status(NodeStatus s) noexcept`
|
||||||
|
## `Node::status`
|
||||||
|
`NodeStatus status() const noexcept`
|
||||||
|
## `Node::type`
|
||||||
|
`NodeType type() const noexcept`
|
||||||
|
## `Node::~Node`
|
||||||
|
`virtual ~Node() = default`
|
||||||
@@ -1,16 +1,45 @@
|
|||||||
# core/rng.h
|
# core/rng.h
|
||||||
|
## `Rng::Rng`
|
||||||
## class Rng — public interface
|
`explicit Rng(seed_type seed = default_seed()) noexcept : _eng(seed)`
|
||||||
|
## `Rng::choose_weighted`
|
||||||
### `: _eng(seed) { ... }`
|
`template<typename T> template<typename T> T choose_weighted(const std::vector<std::pair<double, T>> &items)`
|
||||||
### `void seed(seed_type s) noexcept { ... }`
|
## `Rng::choose_weighted`
|
||||||
### `_eng.seed(s);`
|
`template<typename T> template<typename T> T choose_weighted(std::initializer_list<std::pair<double, T>> items)`
|
||||||
### `double uniform01() { ... }`
|
## `Rng::poisson`
|
||||||
### `Int uniform_range(Int lo_inclusive, Int hi_exclusive) { ... }`
|
`std::uint64_t poisson(double lambda)`
|
||||||
### `Int uniform_range(Int hi_exclusive) { ... }`
|
## `Rng::seed`
|
||||||
### `double uniform_range(double lo_inclusive, double hi_exclusive) { ... }`
|
`void seed(seed_type s) noexcept`
|
||||||
### `std::uint64_t poisson(double lambda) { ... }`
|
## `Rng::uniform01`
|
||||||
### `T choose_weighted(const std::vector<std::pair<double, T>>& items) { ... }`
|
`double uniform01()`
|
||||||
### `return choose_weighted_impl(items.begin(), items.end());`
|
## `Rng::uniform_range`
|
||||||
### `T choose_weighted(std::initializer_list<std::pair<double, T>> items) { ... }`
|
`double uniform_range(double lo_inclusive, double hi_exclusive)`
|
||||||
### `return choose_weighted_impl(items.begin(), items.end());`
|
## `Rng::uniform_range`
|
||||||
|
`template<typename Int,
|
||||||
|
typename = std::enable_if_t<std::is_integral<Int>::value>> template<typename Int, typename = std::enable_if_t<std::is_integral<Int>::value>> Int uniform_range(Int hi_exclusive)`
|
||||||
|
## `Rng::uniform_range`
|
||||||
|
`template<typename Int,
|
||||||
|
typename = std::enable_if_t<std::is_integral<Int>::value>> template<typename Int, typename = std::enable_if_t<std::is_integral<Int>::value>> Int uniform_range(Int lo_inclusive, Int hi_exclusive)`
|
||||||
|
## `choose_weighted`
|
||||||
|
`template <typename T> T choose_weighted(const std::vector<std::pair<double, T>> &items){`
|
||||||
|
## `choose_weighted`
|
||||||
|
`template <typename T> T choose_weighted(std::initializer_list<std::pair<double, T>> items){`
|
||||||
|
## `choose_weighted_impl`
|
||||||
|
`return choose_weighted_impl(items.begin(), items.end());`
|
||||||
|
## `choose_weighted_impl`
|
||||||
|
`return choose_weighted_impl(items.begin(), items.end());`
|
||||||
|
## `choose_weighted_impl`
|
||||||
|
`template <typename Iter> auto choose_weighted_impl(Iter first, Iter last) -> typename std::iterator_traits<Iter>::value_type::second_type{`
|
||||||
|
## `default_seed`
|
||||||
|
`static constexpr seed_type default_seed() noexcept{`
|
||||||
|
## `dofs::seed`
|
||||||
|
`void seed(seed_type s) noexcept{`
|
||||||
|
## `poisson`
|
||||||
|
`std::uint64_t poisson(double lambda){`
|
||||||
|
## `uniform01`
|
||||||
|
`double uniform01(){`
|
||||||
|
## `uniform_range`
|
||||||
|
`Int uniform_range(Int hi_exclusive){`
|
||||||
|
## `uniform_range`
|
||||||
|
`Int uniform_range(Int lo_inclusive, Int hi_exclusive){`
|
||||||
|
## `uniform_range`
|
||||||
|
`double uniform_range(double lo_inclusive, double hi_exclusive){`
|
||||||
@@ -1,27 +1,49 @@
|
|||||||
# core/simulator.h
|
# core/simulator.h
|
||||||
|
## `Simulator::Cmp::operator`
|
||||||
## Free functions
|
`bool operator()(const Item &a, const Item &b) const noexcept`
|
||||||
|
## `Simulator::Simulator`
|
||||||
### `std::numeric_limits<InstanceId>::max();`
|
`Simulator() = default`
|
||||||
### `std::numeric_limits<LinkId>::max();`
|
## `Simulator::cancel`
|
||||||
|
`bool cancel(EventId id)`
|
||||||
## class Rng — public interface
|
## `Simulator::create_link`
|
||||||
|
`std::pair<LinkId, Link *> create_link(NetworkNode *a, PortId a_port, NetworkNode *b, PortId b_port, Time latency, double bandwidth_gbps)`
|
||||||
### `Simulator() = default;`
|
## `Simulator::create_rng`
|
||||||
### `static std::pair<InstanceId, Simulator *> create_simulator(InstanceId id);`
|
`Rng *create_rng(std::uint64_t seed)`
|
||||||
### `static Simulator *get_simulator(InstanceId id) noexcept;`
|
## `Simulator::create_simulator`
|
||||||
### `Time now() const noexcept;`
|
`static std::pair<InstanceId, Simulator *> create_simulator(InstanceId id)`
|
||||||
### `EventId schedule_at(Time abs_time, F&& f, Args&&... args) { ... }`
|
## `Simulator::flush_after`
|
||||||
### `_event_pq.push(std::move(it));`
|
`void flush_after(Time grace) noexcept`
|
||||||
### `EventId schedule_after(Time delay, F&& f, Args&&... args) { ... }`
|
## `Simulator::get_link`
|
||||||
### `bool cancel(EventId id);`
|
`Link *get_link(LinkId id) noexcept`
|
||||||
### `bool run_next();`
|
## `Simulator::get_link`
|
||||||
### `void run_until(Time end_time);`
|
`Link const *get_link(LinkId id) const noexcept`
|
||||||
### `void lock() noexcept;`
|
## `Simulator::get_rng`
|
||||||
### `bool is_locked() const noexcept { ... }`
|
`Rng *get_rng() noexcept`
|
||||||
### `void flush_after(Time grace) noexcept;`
|
## `Simulator::get_rng`
|
||||||
### `Rng* create_rng(std::uint64_t seed);`
|
`Rng const *get_rng() const noexcept`
|
||||||
### `Rng* get_rng() noexcept;`
|
## `Simulator::get_simulator`
|
||||||
### `Rng const* get_rng() const noexcept;`
|
`static Simulator *get_simulator(InstanceId id) noexcept`
|
||||||
### `Link* get_link(LinkId id) noexcept;`
|
## `Simulator::is_locked`
|
||||||
### `Link const* get_link(LinkId id) const noexcept;`
|
`bool is_locked() const noexcept`
|
||||||
|
## `Simulator::lock`
|
||||||
|
`void lock() noexcept`
|
||||||
|
## `Simulator::now`
|
||||||
|
`Time now() const noexcept`
|
||||||
|
## `Simulator::run_next`
|
||||||
|
`bool run_next()`
|
||||||
|
## `Simulator::run_until`
|
||||||
|
`void run_until(Time end_time)`
|
||||||
|
## `Simulator::schedule_after`
|
||||||
|
`template<class F, class... Args> template<class F, class... Args> EventId schedule_after(Time delay, F&&f, Args&&... args)`
|
||||||
|
## `Simulator::schedule_at`
|
||||||
|
`template<class F, class... Args> template<class F, class... Args> EventId schedule_at(Time abs_time, F&&f, Args&&... args)`
|
||||||
|
## `cancel`
|
||||||
|
`bool cancel(EventId id);`
|
||||||
|
## `flush_after`
|
||||||
|
`void flush_after(Time grace) noexcept;`
|
||||||
|
## `lock`
|
||||||
|
`void lock() noexcept;`
|
||||||
|
## `run_next`
|
||||||
|
`bool run_next();`
|
||||||
|
## `run_until`
|
||||||
|
`void run_until(Time end_time);`
|
||||||
@@ -1,36 +1,61 @@
|
|||||||
# core/time.h
|
# core/time.h
|
||||||
|
## `Time`
|
||||||
## Free functions
|
`return Time(a._nsec * b._nsec);`
|
||||||
|
## `Time`
|
||||||
### `constexpr Time operator""_ns(unsigned long long v) noexcept { ... }`
|
`return Time(a._nsec + b._nsec);`
|
||||||
### `return Time::from_ns(static_cast<Time::rep>(v));`
|
## `Time`
|
||||||
### `constexpr Time operator""_us(unsigned long long v) noexcept { ... }`
|
`return Time(a._nsec - b._nsec);`
|
||||||
### `return Time::from_us(static_cast<Time::rep>(v));`
|
## `Time`
|
||||||
### `constexpr Time operator""_ms(unsigned long long v) noexcept { ... }`
|
`return Time(ms * 1000ULL * 1000ULL);`
|
||||||
### `return Time::from_ms(static_cast<Time::rep>(v));`
|
## `Time`
|
||||||
### `constexpr Time operator""_s (unsigned long long v) noexcept { ... }`
|
`return Time(ns);`
|
||||||
### `return Time::from_s (static_cast<Time::rep>(v));`
|
## `Time`
|
||||||
|
`return Time(s * 1000ULL * 1000ULL * 1000ULL);`
|
||||||
## class Time — public interface
|
## `Time`
|
||||||
|
`return Time(this->_nsec - t._nsec);`
|
||||||
### `constexpr Time() : _nsec(0) { ... }`
|
## `Time`
|
||||||
### `explicit constexpr Time(rep ns) : _nsec(ns) { ... }`
|
`return Time(us * 1000ULL);`
|
||||||
### `static constexpr Time from_ns(rep ns) noexcept { ... }`
|
## `Time::Time`
|
||||||
### `return Time(ns);`
|
`constexpr Time() : _nsec(0)`
|
||||||
### `static constexpr Time from_us(rep us) noexcept { ... }`
|
## `Time::Time`
|
||||||
### `return Time(us * 1000ULL);`
|
`explicit constexpr Time(rep ns) : _nsec(ns)`
|
||||||
### `static constexpr Time from_ms(rep ms) noexcept { ... }`
|
## `Time::count`
|
||||||
### `return Time(ms * 1000ULL * 1000ULL);`
|
`constexpr rep count() const noexcept`
|
||||||
### `static constexpr Time from_s (rep s ) noexcept { ... }`
|
## `Time::from_ms`
|
||||||
### `return Time(s * 1000ULL * 1000ULL * 1000ULL);`
|
`static constexpr Time from_ms(rep ms) noexcept`
|
||||||
### `constexpr rep ns() const noexcept { ... }`
|
## `Time::from_ns`
|
||||||
### `constexpr rep count() const noexcept { ... }`
|
`static constexpr Time from_ns(rep ns) noexcept`
|
||||||
### `static constexpr rep us_to_ns(rep us) noexcept { ... }`
|
## `Time::from_s`
|
||||||
### `static constexpr rep ms_to_ns(rep ms) noexcept { ... }`
|
`static constexpr Time from_s (rep s ) noexcept`
|
||||||
### `return Time(a._nsec + b._nsec);`
|
## `Time::from_us`
|
||||||
### `return Time(a._nsec * b._nsec);`
|
`static constexpr Time from_us(rep us) noexcept`
|
||||||
### `return safe_sub(a, b);`
|
## `Time::ms_to_ns`
|
||||||
### `if (a._nsec < b._nsec) { ... }`
|
`static constexpr rep ms_to_ns(rep ms) noexcept`
|
||||||
### `return Time(a._nsec - b._nsec);`
|
## `Time::ns`
|
||||||
### `constexpr Time unsafe_sub(Time t) const noexcept { ... }`
|
`constexpr rep ns() const noexcept`
|
||||||
### `return Time(this->_nsec - t._nsec);`
|
## `Time::us_to_ns`
|
||||||
|
`static constexpr rep us_to_ns(rep us) noexcept`
|
||||||
|
## `_ms`
|
||||||
|
`constexpr Time operator _ms(unsigned long long v) noexcept{`
|
||||||
|
## `_ns`
|
||||||
|
`constexpr Time operator _ns(unsigned long long v) noexcept{`
|
||||||
|
## `_s`
|
||||||
|
`constexpr Time operator _s (unsigned long long v) noexcept{`
|
||||||
|
## `_us`
|
||||||
|
`constexpr Time operator _us(unsigned long long v) noexcept{`
|
||||||
|
## `from_ms`
|
||||||
|
`static constexpr Time from_ms(rep ms) noexcept{`
|
||||||
|
## `from_ns`
|
||||||
|
`static constexpr Time from_ns(rep ns) noexcept{`
|
||||||
|
## `from_s`
|
||||||
|
`static constexpr Time from_s (rep s ) noexcept{`
|
||||||
|
## `from_us`
|
||||||
|
`static constexpr Time from_us(rep us) noexcept{`
|
||||||
|
## `ms_to_ns`
|
||||||
|
`static constexpr rep ms_to_ns(rep ms) noexcept{`
|
||||||
|
## `safe_sub`
|
||||||
|
`friend constexpr std::optional<Time> safe_sub(Time a, Time b) noexcept{`
|
||||||
|
## `safe_sub`
|
||||||
|
`return safe_sub(a, b);`
|
||||||
|
## `us_to_ns`
|
||||||
|
`static constexpr rep us_to_ns(rep us) noexcept{`
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
# core/timer.h
|
# core/timer.h
|
||||||
|
## `Timer::Timer`
|
||||||
## class Timer — public interface
|
`Timer()`
|
||||||
|
## `Timer::elapsed`
|
||||||
### `Timer() { ... }`
|
`Time elapsed() const noexcept`
|
||||||
### `init();`
|
## `Timer::init`
|
||||||
### `void init() noexcept { ... }`
|
`void init() noexcept`
|
||||||
### `_start = clock::now();`
|
## `Timer::now`
|
||||||
### `Time start() const noexcept { ... }`
|
`Time now() const noexcept`
|
||||||
### `auto tp = _start.time_since_epoch();`
|
## `Timer::start`
|
||||||
### `Time now() const noexcept { ... }`
|
`Time start() const noexcept`
|
||||||
### `auto tp = clock::now().time_since_epoch();`
|
## `dofs::init`
|
||||||
### `Time elapsed() const noexcept { ... }`
|
`void init() noexcept{`
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
# core/types.h
|
# core/types.h
|
||||||
|
## `dofs::ipv4`
|
||||||
## Free functions
|
`inline IPv4Addr ipv4(NodeId n, PortId p) noexcept{`
|
||||||
|
|
||||||
### `inline IPv4Addr ipv4(NodeId n, PortId p) noexcept { ... }`
|
|
||||||
### `return (static_cast<uint32_t>(n) << 16) | static_cast<uint32_t>(p);`
|
|
||||||
@@ -1,19 +1,15 @@
|
|||||||
# hosts/mgmt_msg.h
|
# hosts/mgmt_msg.h
|
||||||
|
## `EndSimulationMsg::kind`
|
||||||
## class MgmtMsg — public interface
|
`MgmtKind kind() const noexcept override`
|
||||||
|
## `HeartbeatMsg::HeartbeatMsg`
|
||||||
### `virtual ~MgmtMsg() = default;`
|
`explicit HeartbeatMsg(NodeId sid, NodeStatus st, Time t) noexcept : subscriber_id(sid), status(st), generated_at(t)`
|
||||||
|
## `HeartbeatMsg::kind`
|
||||||
## class HeartbeatMsg — public interface
|
`MgmtKind kind() const noexcept override`
|
||||||
|
## `JobFinishedMsg::JobFinishedMsg`
|
||||||
### `: subscriber_id(sid), status(st), generated_at(t) { ... }`
|
`explicit JobFinishedMsg(FlowId fid, Time t) noexcept : flow_id(fid), finished_at(t)`
|
||||||
### `MgmtKind kind() const noexcept override { ... }`
|
## `JobFinishedMsg::kind`
|
||||||
|
`MgmtKind kind() const noexcept override`
|
||||||
## class JobFinishedMsg — public interface
|
## `MgmtMsg::kind`
|
||||||
|
`virtual MgmtKind kind() const noexcept = 0`
|
||||||
### `: flow_id(fid), finished_at(t) { ... }`
|
## `MgmtMsg::~MgmtMsg`
|
||||||
### `MgmtKind kind() const noexcept override { ... }`
|
`virtual ~MgmtMsg() = default`
|
||||||
|
|
||||||
## class EndSimulationMsg — public interface
|
|
||||||
|
|
||||||
### `MgmtKind kind() const noexcept override { ... }`
|
|
||||||
@@ -1,20 +1,13 @@
|
|||||||
# hosts/policies.h
|
# hosts/policies.h
|
||||||
|
## `PubBasePolicy::select_multicast_groups`
|
||||||
## class PubBasePolicy — public interface
|
`virtual PacketGroups select_multicast_groups(PacketGroups update_groups_mask) = 0`
|
||||||
|
## `PubBasePolicy::~PubBasePolicy`
|
||||||
### `virtual ~PubBasePolicy() = default;`
|
`virtual ~PubBasePolicy() = default`
|
||||||
|
## `PubRRPolicy::PubRRPolicy`
|
||||||
## class PubRRPolicy — public interface
|
`explicit PubRRPolicy(std::vector<ReplicaRange> ranges) : _ranges(std::move(ranges))`
|
||||||
|
## `PubRRPolicy::select_multicast_groups`
|
||||||
### `: _ranges(std::move(ranges)) { ... }`
|
`PacketGroups select_multicast_groups(PacketGroups update_groups_mask) override`
|
||||||
### `validate_and_build();`
|
## `group_present`
|
||||||
### `PacketGroups select_multicast_groups(PacketGroups update_groups_mask) override { ... }`
|
`static bool group_present(PacketGroups mask, uint32_t gid) noexcept{`
|
||||||
### `for (auto const& r : _ranges) { ... }`
|
## `validate_and_build`
|
||||||
|
`void validate_and_build(){`
|
||||||
## class SubBasePolicy — public interface
|
|
||||||
|
|
||||||
### `virtual ~SubBasePolicy() = default;`
|
|
||||||
|
|
||||||
## class SubDummyPolicy — public interface
|
|
||||||
|
|
||||||
### `~SubDummyPolicy() override = default;`
|
|
||||||
@@ -1,10 +1,25 @@
|
|||||||
# hosts/publisher.h
|
# hosts/publisher.h
|
||||||
|
## `Publisher::Pending::arm_staging_if_needed`
|
||||||
## class Publisher — public interface
|
`void arm_staging_if_needed() noexcept`
|
||||||
|
## `Publisher::Pending::on_staging_timer`
|
||||||
### `void recv_update(Bytes size, PacketGroups update_groups_mask) noexcept;`
|
`void on_staging_timer() noexcept`
|
||||||
### `void set_status(NodeStatus s, Time new_latency = Time{}) noexcept;`
|
## `Publisher::Publisher`
|
||||||
### `virtual void recv_mgmt_msg(MgmtMsgPtr msg) noexcept override;`
|
`Publisher(Simulator *sim, NodeId id, Time update_latency_base, std::unique_ptr<PubBasePolicy> policy, Time mgmt_latency) noexcept`
|
||||||
### `virtual void recv_frame(const Packet& frame) override;`
|
## `Publisher::bytes_out`
|
||||||
### `uint64_t updates_in() const noexcept { ... }`
|
`uint64_t bytes_out() const noexcept`
|
||||||
### `uint64_t bytes_out() const noexcept { ... }`
|
## `Publisher::recv_flow`
|
||||||
|
`virtual void recv_flow(NodeId src, FlowId flow, FlowPriority prio, Bytes flow_size) override`
|
||||||
|
## `Publisher::recv_frame`
|
||||||
|
`virtual void recv_frame(const Packet &frame) override`
|
||||||
|
## `Publisher::recv_mgmt_msg`
|
||||||
|
`virtual void recv_mgmt_msg(MgmtMsgPtr msg) noexcept override`
|
||||||
|
## `Publisher::recv_update`
|
||||||
|
`void recv_update(Bytes size, PacketGroups update_groups_mask) noexcept`
|
||||||
|
## `Publisher::set_status`
|
||||||
|
`void set_status(NodeStatus s, Time new_latency = Time{}) noexcept`
|
||||||
|
## `Publisher::updates_in`
|
||||||
|
`uint64_t updates_in() const noexcept`
|
||||||
|
## `arm_staging_if_needed`
|
||||||
|
`void arm_staging_if_needed() noexcept;`
|
||||||
|
## `on_staging_timer`
|
||||||
|
`void on_staging_timer() noexcept;`
|
||||||
@@ -1,8 +1,19 @@
|
|||||||
# hosts/subscriber.h
|
# hosts/subscriber.h
|
||||||
|
## `Subscriber::Subscriber`
|
||||||
## class Publisher — public interface
|
`Subscriber(Simulator *sim, NodeId id, Publisher *publisher, std::unique_ptr<SubBasePolicy> policy, Time mgmt_latency, Time heartbeat_period) noexcept`
|
||||||
|
## `Subscriber::recv_flow`
|
||||||
### `virtual void recv_mgmt_msg(MgmtMsgPtr msg) noexcept override;`
|
`void recv_flow(NodeId src, FlowId flow, FlowPriority prio, Bytes flow_size) override`
|
||||||
### `void recv_frame(const Packet& frame) override;`
|
## `Subscriber::recv_frame`
|
||||||
### `void set_status(NodeStatus s) noexcept;`
|
`void recv_frame(const Packet &frame) override`
|
||||||
### `void set_publisher(Publisher* p) noexcept { ... }`
|
## `Subscriber::recv_mgmt_msg`
|
||||||
|
`virtual void recv_mgmt_msg(MgmtMsgPtr msg) noexcept override`
|
||||||
|
## `Subscriber::set_publisher`
|
||||||
|
`void set_publisher(Publisher *p) noexcept`
|
||||||
|
## `Subscriber::set_status`
|
||||||
|
`void set_status(NodeStatus s) noexcept`
|
||||||
|
## `dofs::on_heartbeat_timer`
|
||||||
|
`void on_heartbeat_timer() noexcept;`
|
||||||
|
## `dofs::schedule_next_heartbeat`
|
||||||
|
`void schedule_next_heartbeat(Time delay) noexcept;`
|
||||||
|
## `dofs::send_job_finished`
|
||||||
|
`void send_job_finished(FlowId flow) noexcept;`
|
||||||
3899
docs/index.json
Normal file
3899
docs/index.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,37 @@
|
|||||||
# network/link.h
|
# network/link.h
|
||||||
|
## `Link::Link`
|
||||||
## class Link — public interface
|
`Link(Simulator *const sim, LinkId id, NetworkNode *a, PortId a_port, NetworkNode *b, PortId b_port, Time latency, double bandwidth_gbps) noexcept`
|
||||||
|
## `Link::bandwidth_gbps`
|
||||||
### `void send_pkt(Packet &pkt, NodeId caller);`
|
`double bandwidth_gbps() const noexcept`
|
||||||
### `void schedule_delivery_after(Packet &pkt, NodeId caller, Time after);`
|
## `Link::dst_id`
|
||||||
### `Time next_available(NodeId sender) const noexcept;`
|
`NodeId dst_id() const noexcept`
|
||||||
### `std::optional<Reservation> reserve(Bytes bytes, NodeId sender) noexcept;`
|
## `Link::dst_port`
|
||||||
### `Time serialization_time(Bytes bytes) const noexcept { ... }`
|
`PortId dst_port() const noexcept`
|
||||||
### `return serialization_time(bytes, _bandwidth_gbps_cur);`
|
## `Link::id`
|
||||||
### `Time propagation_latency() const noexcept { ... }`
|
`LinkId id() const noexcept`
|
||||||
### `LinkId id() const noexcept { ... }`
|
## `Link::next_available`
|
||||||
### `LinkStatus status() const noexcept { ... }`
|
`Time next_available(NodeId sender) const noexcept`
|
||||||
### `double bandwidth_gbps() const noexcept { ... }`
|
## `Link::propagation_latency`
|
||||||
### `NodeId src_id() const noexcept { ... }`
|
`Time propagation_latency() const noexcept`
|
||||||
### `NodeId dst_id() const noexcept { ... }`
|
## `Link::reserve`
|
||||||
### `PortId src_port() const noexcept { ... }`
|
`std::optional<Reservation> reserve(Bytes bytes, NodeId sender) noexcept`
|
||||||
### `PortId dst_port() const noexcept { ... }`
|
## `Link::schedule_delivery_after`
|
||||||
### `static Time serialization_time(Bytes bytes, double gbps) noexcept;`
|
`void schedule_delivery_after(Packet &pkt, NodeId caller, Time after)`
|
||||||
|
## `Link::send_pkt`
|
||||||
|
`void send_pkt(Packet &pkt, NodeId caller)`
|
||||||
|
## `Link::serialization_time`
|
||||||
|
`Time serialization_time(Bytes bytes) const noexcept`
|
||||||
|
## `Link::serialization_time`
|
||||||
|
`static Time serialization_time(Bytes bytes, double gbps) noexcept`
|
||||||
|
## `Link::set_status`
|
||||||
|
`void set_status(LinkStatus s, Time new_latency = Time(0), double new_bandwidth_gbps = 0.0)`
|
||||||
|
## `Link::src_id`
|
||||||
|
`NodeId src_id() const noexcept`
|
||||||
|
## `Link::src_port`
|
||||||
|
`PortId src_port() const noexcept`
|
||||||
|
## `Link::status`
|
||||||
|
`LinkStatus status() const noexcept`
|
||||||
|
## `serialization_time`
|
||||||
|
`static Time serialization_time(Bytes bytes, double gbps) noexcept;`
|
||||||
|
## `set_status`
|
||||||
|
`void set_status(LinkStatus s, Time new_latency = Time(0), double new_bandwidth_gbps = 0.0);`
|
||||||
@@ -1,11 +1,95 @@
|
|||||||
# network/network_nic.h
|
# network/network_nic.h
|
||||||
|
## `NicSchedulingWeights::NetworkNic::NetworkNic`
|
||||||
## class NetworkNic — public interface
|
`NetworkNic(Simulator *const sim, NodeId id, uint16_t total_ports, SwitchBuffer *const buf, Time nic_latency, Bytes mice_elephant_threshold, PacketSeq ooo_threshold, CCType cc_type, LBType lb_type, Bytes cc_init_cwnd, Bytes cc_max_cwnd, const NicSchedulingWeights &schedw) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::enqueue_packet`
|
||||||
### `virtual void recv_pkt(Packet &pkt, PortId ingress) override;`
|
`void enqueue_packet(PortId port, QClass cls, Packet pkt) noexcept`
|
||||||
### `void attach_host(Host* host) noexcept;`
|
## `NicSchedulingWeights::NetworkNic::PortQueues::is_elephant`
|
||||||
### `void detach_host(Host* host) noexcept;`
|
`bool is_elephant(Bytes sz) const noexcept`
|
||||||
### `void set_status(NodeStatus s, Time new_latency = Time(0)) noexcept;`
|
## `NicSchedulingWeights::NetworkNic::PortQueues::lookup_rtt_and_erase`
|
||||||
### `const NicTelemetry &telemetry() const noexcept { ... }`
|
`bool lookup_rtt_and_erase(const Packet &ack_like, Time now, Time &out_rtt) noexcept`
|
||||||
### `void set_port_blacklisted(PortId port, bool blacklisted) noexcept;`
|
## `NicSchedulingWeights::NetworkNic::PortQueues::make_ack_packet`
|
||||||
### `bool is_port_blacklisted(PortId port) const noexcept;`
|
`Packet make_ack_packet(const Packet &rx_data, PortId ingress) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::make_data_packet`
|
||||||
|
`Packet make_data_packet(NodeId dst, PortId out_port, FlowPriority prio, FlowId fid, PacketSeq seq, Bytes payload) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::make_nack_packet`
|
||||||
|
`Packet make_nack_packet(NodeId peer, FlowId flow, PacketSeq miss, FlowPriority prio, PortId ingress) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::make_trim_back_response`
|
||||||
|
`Packet make_trim_back_response(const Packet &trim, PortId ingress) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::pick_next_qclass`
|
||||||
|
`bool pick_next_qclass(const PortQueues &pq, QClass &out_cls) const noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::pick_src_port_for_flow`
|
||||||
|
`PortId pick_src_port_for_flow(NodeId dst) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::port_drain_task`
|
||||||
|
`void port_drain_task(PortId port) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::record_tx_timestamp`
|
||||||
|
`void record_tx_timestamp(const Packet &pkt) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::schedule_ack`
|
||||||
|
`void schedule_ack(const Packet &rx_data, PortId ingress) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::schedule_nack`
|
||||||
|
`void schedule_nack(NodeId peer, FlowId flow, PacketSeq missing_seq, FlowPriority prio, PortId ingress) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::schedule_port_if_needed`
|
||||||
|
`void schedule_port_if_needed(PortId port) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::schedule_trim_back_response`
|
||||||
|
`void schedule_trim_back_response(const Packet &trim, PortId ingress) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::start_tx_multicast`
|
||||||
|
`void start_tx_multicast(PacketGroups gmask, Bytes size, FlowPriority prio)`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::start_tx_unicast`
|
||||||
|
`void start_tx_unicast(NodeId dst, Bytes size, FlowPriority prio)`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::try_send_one`
|
||||||
|
`bool try_send_one(PortId port, QClass cls) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::PortQueues::txkey`
|
||||||
|
`static inline std::uint64_t txkey(FlowId f, PacketSeq s) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::attach_host`
|
||||||
|
`void attach_host(Host *host) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::detach_host`
|
||||||
|
`void detach_host(Host *host) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::is_port_blacklisted`
|
||||||
|
`bool is_port_blacklisted(PortId port) const noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::recv_pkt`
|
||||||
|
`virtual void recv_pkt(Packet &pkt, PortId ingress) override`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::send_flow`
|
||||||
|
`void send_flow(NodeId dst, Bytes size, FlowPriority desired = FlowPriority::AUTO)`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::send_flow`
|
||||||
|
`void send_flow(PacketGroups group_mask, Bytes size, FlowPriority desired = FlowPriority::AUTO)`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::set_port_blacklisted`
|
||||||
|
`void set_port_blacklisted(PortId port, bool blacklisted) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::set_status`
|
||||||
|
`void set_status(NodeStatus s, Time new_latency = Time(0)) noexcept`
|
||||||
|
## `NicSchedulingWeights::NetworkNic::telemetry`
|
||||||
|
`const NicTelemetry &telemetry() const noexcept`
|
||||||
|
## `enqueue_packet`
|
||||||
|
`void enqueue_packet(PortId port, QClass cls, Packet pkt) noexcept;`
|
||||||
|
## `lookup_rtt_and_erase`
|
||||||
|
`bool lookup_rtt_and_erase(const Packet &ack_like, Time now, Time &out_rtt) noexcept;`
|
||||||
|
## `make_ack_packet`
|
||||||
|
`Packet make_ack_packet(const Packet &rx_data, PortId ingress) noexcept;`
|
||||||
|
## `make_data_packet`
|
||||||
|
`Packet make_data_packet(NodeId dst, PortId out_port, FlowPriority prio, FlowId fid, PacketSeq seq, Bytes payload) noexcept;`
|
||||||
|
## `make_nack_packet`
|
||||||
|
`Packet make_nack_packet(NodeId peer, FlowId flow, PacketSeq miss, FlowPriority prio, PortId ingress) noexcept;`
|
||||||
|
## `make_trim_back_response`
|
||||||
|
`Packet make_trim_back_response(const Packet &trim, PortId ingress) noexcept;`
|
||||||
|
## `pick_src_port_for_flow`
|
||||||
|
`PortId pick_src_port_for_flow(NodeId dst) noexcept;`
|
||||||
|
## `port_drain_task`
|
||||||
|
`void port_drain_task(PortId port) noexcept;`
|
||||||
|
## `record_tx_timestamp`
|
||||||
|
`void record_tx_timestamp(const Packet &pkt) noexcept;`
|
||||||
|
## `schedule_ack`
|
||||||
|
`void schedule_ack(const Packet &rx_data, PortId ingress) noexcept;`
|
||||||
|
## `schedule_nack`
|
||||||
|
`void schedule_nack(NodeId peer, FlowId flow, PacketSeq missing_seq, FlowPriority prio, PortId ingress) noexcept;`
|
||||||
|
## `schedule_port_if_needed`
|
||||||
|
`void schedule_port_if_needed(PortId port) noexcept;`
|
||||||
|
## `schedule_trim_back_response`
|
||||||
|
`void schedule_trim_back_response(const Packet &trim, PortId ingress) noexcept;`
|
||||||
|
## `set_port_blacklisted`
|
||||||
|
`void set_port_blacklisted(PortId port, bool blacklisted) noexcept;`
|
||||||
|
## `start_tx_multicast`
|
||||||
|
`void start_tx_multicast(PacketGroups gmask, Bytes size, FlowPriority prio);`
|
||||||
|
## `start_tx_unicast`
|
||||||
|
`void start_tx_unicast(NodeId dst, Bytes size, FlowPriority prio);`
|
||||||
|
## `try_send_one`
|
||||||
|
`bool try_send_one(PortId port, QClass cls) noexcept;`
|
||||||
|
## `txkey`
|
||||||
|
`static inline std::uint64_t txkey(FlowId f, PacketSeq s) noexcept;`
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# network/network_node.h
|
# network/network_node.h
|
||||||
|
## `NetworkNode::NetworkNode`
|
||||||
## class NetworkNode — public interface
|
`explicit NetworkNode(Simulator *const sim, NodeId id, NodeType type) noexcept`
|
||||||
|
## `NetworkNode::recv_pkt`
|
||||||
### `explicit NetworkNode(Simulator *const sim, NodeId id, NodeType type) noexcept;`
|
`virtual void recv_pkt(Packet &pkt, PortId ingress) = 0`
|
||||||
### `virtual ~NetworkNode() = default;`
|
## `NetworkNode::~NetworkNode`
|
||||||
|
`virtual ~NetworkNode() = default`
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
# network/network_switch.h
|
# network/network_switch.h
|
||||||
|
## `NetworkSwitch::NetworkSwitch`
|
||||||
## class NetworkSwitch — public interface
|
`NetworkSwitch(Simulator *const sim, NodeId id, uint16_t total_ports, ECNEngine *const ecn, SwitchBuffer *const buf, const RoutingTables *const rt, Time forwarding_latency, Time multicast_dup_delay) noexcept`
|
||||||
|
## `NetworkSwitch::get_status`
|
||||||
### `virtual void recv_pkt(Packet &pkt, PortId ingress) override;`
|
`NodeStatus get_status() const noexcept`
|
||||||
### `void set_status(NodeStatus s, Time new_forward_latency = Time(0)) noexcept;`
|
## `NetworkSwitch::port_cnt`
|
||||||
### `NodeStatus get_status() const noexcept { ... }`
|
`uint16_t port_cnt() const noexcept`
|
||||||
### `return status();`
|
## `NetworkSwitch::recv_pkt`
|
||||||
### `uint16_t port_cnt() const noexcept { ... }`
|
`virtual void recv_pkt(Packet &pkt, PortId ingress) override`
|
||||||
|
## `NetworkSwitch::set_status`
|
||||||
|
`void set_status(NodeStatus s, Time new_forward_latency = Time(0)) noexcept`
|
||||||
|
## `enqueue_one`
|
||||||
|
`void enqueue_one(Packet pkt, PortId egress, FlowPriority prio) noexcept;`
|
||||||
|
## `forward_after_delay`
|
||||||
|
`private: void forward_after_delay(Packet pkt, PortId ingress) noexcept;`
|
||||||
|
## `group_bit_set`
|
||||||
|
`static inline bool group_bit_set(PacketGroups mask, std::size_t gid) noexcept;`
|
||||||
|
## `merge_sorted_unique`
|
||||||
|
`static void merge_sorted_unique(std::vector<PortId> &base, const std::vector<PortId> &add);`
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
# network/nic/congestion_control.h
|
# network/nic/congestion_control.h
|
||||||
|
## `CongestionControl::CongestionControl`
|
||||||
## class CongestionControl — public interface
|
`explicit CongestionControl(Bytes init_cwnd, Bytes max_cwnd) noexcept`
|
||||||
|
## `CongestionControl::cwnd`
|
||||||
### `explicit CongestionControl(Bytes init_cwnd, Bytes max_cwnd) noexcept;`
|
`Bytes cwnd() const noexcept`
|
||||||
### `virtual ~CongestionControl() = default;`
|
## `CongestionControl::cwnd_max`
|
||||||
### `Bytes cwnd() const noexcept { ... }`
|
`Bytes cwnd_max() const noexcept`
|
||||||
### `Bytes cwnd_max() const noexcept { ... }`
|
## `CongestionControl::is_allowed_to_send`
|
||||||
|
`virtual bool is_allowed_to_send(Bytes bytes_outstanding, Bytes next_bytes) const noexcept`
|
||||||
## class DCQCN — public interface
|
## `CongestionControl::update`
|
||||||
|
`virtual void update(const Packet &pkt, Time rtt) noexcept = 0`
|
||||||
### `explicit DCQCN(Bytes init_cwnd, Bytes max_cwnd) noexcept;`
|
## `CongestionControl::~CongestionControl`
|
||||||
### `virtual void update(const Packet& pkt, Time rtt) noexcept override;`
|
`virtual ~CongestionControl() = default`
|
||||||
|
## `DCQCN::DCQCN`
|
||||||
## class NSCC — public interface
|
`explicit DCQCN(Bytes init_cwnd, Bytes max_cwnd) noexcept`
|
||||||
|
## `DCQCN::update`
|
||||||
### `explicit NSCC(Bytes init_cwnd, Bytes max_cwnd) noexcept;`
|
`virtual void update(const Packet &pkt, Time rtt) noexcept override`
|
||||||
### `virtual void update(const Packet& pkt, Time rtt) noexcept override;`
|
## `NSCC::NSCC`
|
||||||
|
`explicit NSCC(Bytes init_cwnd, Bytes max_cwnd) noexcept`
|
||||||
|
## `NSCC::update`
|
||||||
|
`virtual void update(const Packet &pkt, Time rtt) noexcept override`
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
# network/nic/load_balance.h
|
# network/nic/load_balance.h
|
||||||
|
## `LBRandomPacketSpraying::LBRandomPacketSpraying`
|
||||||
## class LoadBalance — public interface
|
`explicit LBRandomPacketSpraying(Rng *const rng) noexcept : LoadBalance(rng)`
|
||||||
|
## `LBRandomPacketSpraying::get_entropy`
|
||||||
### `explicit LoadBalance(Rng *const rng) noexcept : _rng(rng) { ... }`
|
`virtual uint16_t get_entropy(const Packet &context) noexcept override`
|
||||||
### `virtual ~LoadBalance() = default;`
|
## `LBRandomPacketSpraying::update`
|
||||||
|
`virtual void update(const Packet &pkt) noexcept override`
|
||||||
## class LBRandomPacketSpraying — public interface
|
## `LoadBalance::LoadBalance`
|
||||||
|
`explicit LoadBalance(Rng *const rng) noexcept : _rng(rng)`
|
||||||
### `explicit LBRandomPacketSpraying(Rng *const rng) noexcept : LoadBalance(rng) { ... }`
|
## `LoadBalance::get_entropy`
|
||||||
### `virtual void update(const Packet& pkt) noexcept override;`
|
`virtual uint16_t get_entropy(const Packet &context) noexcept = 0`
|
||||||
### `virtual uint16_t get_entropy(const Packet& context) noexcept override;`
|
## `LoadBalance::update`
|
||||||
|
`virtual void update(const Packet &pkt) noexcept = 0`
|
||||||
|
## `LoadBalance::~LoadBalance`
|
||||||
|
`virtual ~LoadBalance() = default`
|
||||||
2
docs/network/nic/nic_telemetry.md
Normal file
2
docs/network/nic/nic_telemetry.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# network/nic/nic_telemetry.h
|
||||||
|
_No public API symbols found in this header._
|
||||||
@@ -1,35 +1,65 @@
|
|||||||
# network/packet.h
|
# network/packet.h
|
||||||
|
## `Packet::Packet`
|
||||||
## class Packet — public interface
|
`Packet(NodeId src_node, PortId src_port, NodeId dst_node, PortId dst_port, PacketProtocol proto, FlowPriority prio, PacketSeq seq = 0, FlowId flow = 0, uint16_t entropy = 0, uint8_t notifications = 0, Bytes payload_bytes = DEFAULT_MSS_BYTES) noexcept`
|
||||||
|
## `Packet::add_groups`
|
||||||
### `NodeId src_node() const noexcept;`
|
`void add_groups(PacketGroups gmask) noexcept`
|
||||||
### `PortId src_port() const noexcept;`
|
## `Packet::dst_node`
|
||||||
### `NodeId dst_node() const noexcept;`
|
`NodeId dst_node() const noexcept`
|
||||||
### `PortId dst_port() const noexcept;`
|
## `Packet::dst_port`
|
||||||
### `PacketProtocol protocol() const noexcept;`
|
`PortId dst_port() const noexcept`
|
||||||
### `PacketSeq seq() const noexcept;`
|
## `Packet::entropy`
|
||||||
### `FlowId flow_id() const noexcept;`
|
`uint32_t entropy() const noexcept`
|
||||||
### `uint32_t entropy() const noexcept;`
|
## `Packet::flow_id`
|
||||||
### `void set_src_node(NodeId n) noexcept;`
|
`FlowId flow_id() const noexcept`
|
||||||
### `void set_src_port(PortId p) noexcept;`
|
## `Packet::groups`
|
||||||
### `void set_dst_node(NodeId n) noexcept;`
|
`PacketGroups groups() const noexcept`
|
||||||
### `void set_dst_port(PortId p) noexcept;`
|
## `Packet::header_size`
|
||||||
### `void set_seq(PacketSeq s) noexcept;`
|
`Bytes header_size() const noexcept`
|
||||||
### `void set_flow_id(FlowId f) noexcept;`
|
## `Packet::is_ecn`
|
||||||
### `void set_entropy(uint32_t e) noexcept;`
|
`bool is_ecn() const noexcept`
|
||||||
### `void set_protocol(PacketProtocol p) noexcept;`
|
## `Packet::is_ecn_enabled`
|
||||||
### `void set_payload_size(Bytes size) noexcept;`
|
`bool is_ecn_enabled() const noexcept`
|
||||||
### `void set_ecn_enabled(bool v) noexcept;`
|
## `Packet::is_eof`
|
||||||
### `void set_ecn_marked(bool v) noexcept;`
|
`bool is_eof() const noexcept`
|
||||||
### `void set_eof(bool v) noexcept;`
|
## `Packet::payload_size`
|
||||||
### `bool is_ecn_enabled() const noexcept;`
|
`Bytes payload_size() const noexcept`
|
||||||
### `bool is_ecn() const noexcept;`
|
## `Packet::priority`
|
||||||
### `bool is_eof() const noexcept;`
|
`FlowPriority priority() const noexcept`
|
||||||
### `FlowPriority priority() const noexcept;`
|
## `Packet::priority_raw`
|
||||||
### `uint8_t priority_raw() const noexcept;`
|
`uint8_t priority_raw() const noexcept`
|
||||||
### `Bytes header_size() const noexcept;`
|
## `Packet::protocol`
|
||||||
### `Bytes payload_size() const noexcept;`
|
`PacketProtocol protocol() const noexcept`
|
||||||
### `Bytes total_size() const noexcept;`
|
## `Packet::seq`
|
||||||
### `PacketGroups groups() const noexcept;`
|
`PacketSeq seq() const noexcept`
|
||||||
### `void set_groups(PacketGroups g) noexcept;`
|
## `Packet::set_dst_node`
|
||||||
### `void add_groups(PacketGroups gmask) noexcept;`
|
`void set_dst_node(NodeId n) noexcept`
|
||||||
|
## `Packet::set_dst_port`
|
||||||
|
`void set_dst_port(PortId p) noexcept`
|
||||||
|
## `Packet::set_ecn_enabled`
|
||||||
|
`void set_ecn_enabled(bool v) noexcept`
|
||||||
|
## `Packet::set_ecn_marked`
|
||||||
|
`void set_ecn_marked(bool v) noexcept`
|
||||||
|
## `Packet::set_entropy`
|
||||||
|
`void set_entropy(uint32_t e) noexcept`
|
||||||
|
## `Packet::set_eof`
|
||||||
|
`void set_eof(bool v) noexcept`
|
||||||
|
## `Packet::set_flow_id`
|
||||||
|
`void set_flow_id(FlowId f) noexcept`
|
||||||
|
## `Packet::set_groups`
|
||||||
|
`void set_groups(PacketGroups g) noexcept`
|
||||||
|
## `Packet::set_payload_size`
|
||||||
|
`void set_payload_size(Bytes size) noexcept`
|
||||||
|
## `Packet::set_protocol`
|
||||||
|
`void set_protocol(PacketProtocol p) noexcept`
|
||||||
|
## `Packet::set_seq`
|
||||||
|
`void set_seq(PacketSeq s) noexcept`
|
||||||
|
## `Packet::set_src_node`
|
||||||
|
`void set_src_node(NodeId n) noexcept`
|
||||||
|
## `Packet::set_src_port`
|
||||||
|
`void set_src_port(PortId p) noexcept`
|
||||||
|
## `Packet::src_node`
|
||||||
|
`NodeId src_node() const noexcept`
|
||||||
|
## `Packet::src_port`
|
||||||
|
`PortId src_port() const noexcept`
|
||||||
|
## `Packet::total_size`
|
||||||
|
`Bytes total_size() const noexcept`
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# network/switch/dedicated_buffer.h
|
# network/switch/dedicated_buffer.h
|
||||||
|
## `DedicatedBuffer::DedicatedBuffer`
|
||||||
## class DedicatedBuffer — public interface
|
`DedicatedBuffer(Simulator *const sim, NetworkSwitch *const owner, Bytes total_bytes, uint16_t ports)`
|
||||||
|
## `DedicatedBuffer::drain_one`
|
||||||
### `bool drain_one(PortId port) override;`
|
`bool drain_one(PortId port) override`
|
||||||
|
## `DedicatedBuffer::enqueue_packet`
|
||||||
|
`bool enqueue_packet(const Packet &pkt, PortId egress, FlowPriority prio) override`
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# network/switch/ecn_dedicated_red.h
|
# network/switch/ecn_dedicated_red.h
|
||||||
|
## `DedicatedREDEngine::DedicatedREDEngine`
|
||||||
## class DedicatedREDEngine — public interface
|
`DedicatedREDEngine(Bytes min_th, Bytes max_th, double p_max, bool back_to_sender, Rng *const rng) noexcept : _min_th(min_th), _max_th(max_th), _p_max(p_max), _back_to_sender(back_to_sender), _rng(rng)`
|
||||||
|
## `DedicatedREDEngine::process_packet`
|
||||||
### `_rng(rng) { ... }`
|
`virtual Packet &process_packet(Packet &pkt, SwitchBuffer *buf) noexcept override`
|
||||||
|
## `dofs::ensure_size`
|
||||||
|
`private: void ensure_size(uint16_t port_cnt){`
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# network/switch/ecn_engine.h
|
# network/switch/ecn_engine.h
|
||||||
|
## `ECNEngine::process_packet`
|
||||||
## class SwitchBuffer — public interface
|
`virtual Packet &process_packet(Packet &pkt, SwitchBuffer *buf) noexcept = 0`
|
||||||
|
## `ECNEngine::~ECNEngine`
|
||||||
### `virtual ~ECNEngine() = default;`
|
`virtual ~ECNEngine() = default`
|
||||||
|
## `dofs::header_trim`
|
||||||
|
`static inline void header_trim(Packet &pkt, bool back_to_sender) noexcept{`
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
# network/switch/ecn_shared_red.h
|
# network/switch/ecn_shared_red.h
|
||||||
|
## `SharedREDEngine::SharedREDEngine`
|
||||||
## class SharedREDEngine — public interface
|
`explicit SharedREDEngine(Rng *const rng = nullptr) noexcept : _rng(rng), _avg_total_bytes(0.0), _avg_port_bytes()`
|
||||||
|
## `SharedREDEngine::process_packet`
|
||||||
### `_avg_port_bytes() { ... }`
|
`virtual Packet &process_packet(Packet &pkt, SwitchBuffer *buf) noexcept override`
|
||||||
|
## `dofs::ensure_size`
|
||||||
|
`private: void ensure_size(uint16_t port_cnt){`
|
||||||
|
## `is_ctrl`
|
||||||
|
`static inline bool is_ctrl(const Packet &p) noexcept{`
|
||||||
|
## `is_ele`
|
||||||
|
`static inline bool is_ele (const Packet &p) noexcept{`
|
||||||
|
## `is_mice`
|
||||||
|
`static inline bool is_mice(const Packet &p) noexcept{`
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
# network/switch/multicast_table.h
|
# network/switch/multicast_table.h
|
||||||
|
## `McTree::MulticastTable::MulticastTable`
|
||||||
## class MulticastTable — public interface
|
`MulticastTable()`
|
||||||
|
## `McTree::MulticastTable::add_child_port`
|
||||||
### `MulticastTable();`
|
`bool add_child_port(std::size_t group_id, uint16_t tree_id, PortId out_port)`
|
||||||
### `bool add_tree(std::size_t group_id, uint16_t tree_id);`
|
## `McTree::MulticastTable::add_tree`
|
||||||
### `bool delete_tree(std::size_t group_id, uint16_t tree_id);`
|
`bool add_tree(std::size_t group_id, uint16_t tree_id)`
|
||||||
### `bool add_child_port(std::size_t group_id, uint16_t tree_id, PortId out_port);`
|
## `McTree::MulticastTable::delete_child_port`
|
||||||
### `bool delete_child_port(std::size_t group_id, uint16_t tree_id, PortId out_port);`
|
`bool delete_child_port(std::size_t group_id, uint16_t tree_id, PortId out_port)`
|
||||||
### `bool set_parent(std::size_t group_id, uint16_t tree_id, PortId parent);`
|
## `McTree::MulticastTable::delete_tree`
|
||||||
### `bool set_weight(std::size_t group_id, uint16_t tree_id, uint8_t w);`
|
`bool delete_tree(std::size_t group_id, uint16_t tree_id)`
|
||||||
### `bool set_epoch(std::size_t group_id, uint16_t tree_id, uint8_t epoch);`
|
## `McTree::MulticastTable::get_port_list`
|
||||||
### `const std::vector<McTree> *trees_of(std::size_t group_id) const;`
|
`std::vector<PortId> get_port_list(PacketGroups groups) const`
|
||||||
### `std::size_t group_count() const noexcept;`
|
## `McTree::MulticastTable::group_count`
|
||||||
### `std::vector<PortId> get_port_list(PacketGroups groups) const;`
|
`std::size_t group_count() const noexcept`
|
||||||
|
## `McTree::MulticastTable::set_epoch`
|
||||||
|
`bool set_epoch(std::size_t group_id, uint16_t tree_id, uint8_t epoch)`
|
||||||
|
## `McTree::MulticastTable::set_parent`
|
||||||
|
`bool set_parent(std::size_t group_id, uint16_t tree_id, PortId parent)`
|
||||||
|
## `McTree::MulticastTable::set_weight`
|
||||||
|
`bool set_weight(std::size_t group_id, uint16_t tree_id, uint8_t w)`
|
||||||
|
## `McTree::MulticastTable::trees_of`
|
||||||
|
`const std::vector<McTree> *trees_of(std::size_t group_id) const`
|
||||||
7
docs/network/switch/routing_alg.md
Normal file
7
docs/network/switch/routing_alg.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# network/switch/routing_alg.h
|
||||||
|
## `dofs::hash_ecmp`
|
||||||
|
`PortId hash_ecmp(NodeId src_node, PortId src_port, NodeId dst_node, PortId dst_port, uint32_t differentiator, uint16_t port_count) noexcept;`
|
||||||
|
## `dofs::hash_ecmp`
|
||||||
|
`inline PortId hash_ecmp(NodeId src_node, NodeId dst_node, uint32_t differentiator, uint16_t port_count) noexcept{`
|
||||||
|
## `dofs::hash_ecmp`
|
||||||
|
`return hash_ecmp(src_node, static_cast<PortId>(0), dst_node, static_cast<PortId>(0), differentiator, port_count);`
|
||||||
2
docs/network/switch/routing_tables.md
Normal file
2
docs/network/switch/routing_tables.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# network/switch/routing_tables.h
|
||||||
|
_No public API symbols found in this header._
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# network/switch/shared_buffer.h
|
# network/switch/shared_buffer.h
|
||||||
|
## `SharedBuffer::SharedBuffer`
|
||||||
## class SharedBuffer — public interface
|
`SharedBuffer(Simulator *const sim, NetworkSwitch *const owner, Bytes total_bytes, uint16_t ports)`
|
||||||
|
## `SharedBuffer::drain_one`
|
||||||
### `virtual bool drain_one(PortId port) override;`
|
`virtual bool drain_one(PortId port) override`
|
||||||
|
## `SharedBuffer::enqueue_packet`
|
||||||
|
`virtual bool enqueue_packet(const Packet &pkt, PortId egress, FlowPriority prio) override`
|
||||||
@@ -1,19 +1,65 @@
|
|||||||
# network/switch/switch_buffer.h
|
# network/switch/switch_buffer.h
|
||||||
|
## `SwitchBuffer::PerPortSched::drain_once`
|
||||||
## class NetworkSwitch — public interface
|
`void drain_once(PortId port)`
|
||||||
|
## `SwitchBuffer::PerPortSched::drain_one_common`
|
||||||
### `virtual ~SwitchBuffer() = default;`
|
`bool drain_one_common(PortId port)`
|
||||||
### `Bytes buffer_size() const noexcept;`
|
## `SwitchBuffer::PerPortSched::on_dequeue_commit`
|
||||||
### `SwitchBufferType type() const noexcept;`
|
`virtual void on_dequeue_commit(PortId port, Bytes sz) = 0`
|
||||||
### `uint16_t port_cnt() const noexcept;`
|
## `SwitchBuffer::PerPortSched::on_enqueue_cap_check`
|
||||||
### `Bytes port_buffered(PortId p) const noexcept;`
|
`virtual bool on_enqueue_cap_check(PortId port, Bytes sz) = 0`
|
||||||
### `const std::vector<Bytes> &ports_buffered() const noexcept;`
|
## `SwitchBuffer::PerPortSched::on_enqueue_commit`
|
||||||
### `uint8_t share_ctrl() const noexcept;`
|
`virtual void on_enqueue_commit(PortId port, Bytes sz) = 0`
|
||||||
### `uint8_t share_mice() const noexcept;`
|
## `SwitchBuffer::PerPortSched::queued_bytes_port`
|
||||||
### `uint8_t share_elephant() const noexcept;`
|
`Bytes queued_bytes_port(PortId p) const noexcept`
|
||||||
### `void set_share_ctrl(uint8_t pct) noexcept;`
|
## `SwitchBuffer::PerPortSched::queued_bytes_total`
|
||||||
### `void set_share_mice(uint8_t pct) noexcept;`
|
`Bytes queued_bytes_total() const noexcept`
|
||||||
### `void set_share_elephant(uint8_t pct) noexcept;`
|
## `SwitchBuffer::PerPortSched::queues_for`
|
||||||
### `const Simulator *simulator() const noexcept;`
|
`virtual const std::array<std::deque<Queued>, PRI_COUNT> &queues_for( PortId p) const = 0`
|
||||||
### `const NetworkSwitch *owner() const noexcept;`
|
## `SwitchBuffer::PerPortSched::queues_for`
|
||||||
### `void set_egress_links(const std::vector<Link*> &links);`
|
`virtual std::array<std::deque<Queued>, PRI_COUNT> &queues_for(PortId p) = 0`
|
||||||
|
## `SwitchBuffer::PerPortSched::schedule_drain_if_needed`
|
||||||
|
`void schedule_drain_if_needed(PortId port)`
|
||||||
|
## `SwitchBuffer::PerPortSched::try_reserve_and_send`
|
||||||
|
`std::optional<Time> try_reserve_and_send(PortId port, Queued &q)`
|
||||||
|
## `SwitchBuffer::buffer_size`
|
||||||
|
`Bytes buffer_size() const noexcept`
|
||||||
|
## `SwitchBuffer::drain_one`
|
||||||
|
`virtual bool drain_one(PortId port) = 0`
|
||||||
|
## `SwitchBuffer::enqueue_packet`
|
||||||
|
`virtual bool enqueue_packet(const Packet &pkt, PortId egress, FlowPriority prio) = 0`
|
||||||
|
## `SwitchBuffer::owner`
|
||||||
|
`const NetworkSwitch *owner() const noexcept`
|
||||||
|
## `SwitchBuffer::port_buffered`
|
||||||
|
`Bytes port_buffered(PortId p) const noexcept`
|
||||||
|
## `SwitchBuffer::port_cnt`
|
||||||
|
`uint16_t port_cnt() const noexcept`
|
||||||
|
## `SwitchBuffer::ports_buffered`
|
||||||
|
`const std::vector<Bytes> &ports_buffered() const noexcept`
|
||||||
|
## `SwitchBuffer::set_egress_links`
|
||||||
|
`void set_egress_links(const std::vector<Link*> &links)`
|
||||||
|
## `SwitchBuffer::set_share_ctrl`
|
||||||
|
`void set_share_ctrl(uint8_t pct) noexcept`
|
||||||
|
## `SwitchBuffer::set_share_elephant`
|
||||||
|
`void set_share_elephant(uint8_t pct) noexcept`
|
||||||
|
## `SwitchBuffer::set_share_mice`
|
||||||
|
`void set_share_mice(uint8_t pct) noexcept`
|
||||||
|
## `SwitchBuffer::share_ctrl`
|
||||||
|
`uint8_t share_ctrl() const noexcept`
|
||||||
|
## `SwitchBuffer::share_elephant`
|
||||||
|
`uint8_t share_elephant() const noexcept`
|
||||||
|
## `SwitchBuffer::share_mice`
|
||||||
|
`uint8_t share_mice() const noexcept`
|
||||||
|
## `SwitchBuffer::simulator`
|
||||||
|
`const Simulator *simulator() const noexcept`
|
||||||
|
## `SwitchBuffer::type`
|
||||||
|
`SwitchBufferType type() const noexcept`
|
||||||
|
## `SwitchBuffer::~SwitchBuffer`
|
||||||
|
`virtual ~SwitchBuffer() = default`
|
||||||
|
## `drain_once`
|
||||||
|
`void drain_once(PortId port);`
|
||||||
|
## `drain_one_common`
|
||||||
|
`bool drain_one_common(PortId port);`
|
||||||
|
## `schedule_drain_if_needed`
|
||||||
|
`void schedule_drain_if_needed(PortId port);`
|
||||||
|
## `try_reserve_and_send`
|
||||||
|
`std::optional<Time> try_reserve_and_send(PortId port, Queued &q);`
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
# network/switch/unicast_table.h
|
# network/switch/unicast_table.h
|
||||||
|
## `UnicastTable::UnicastTable`
|
||||||
## class UnicastTable — public interface
|
`UnicastTable() = default`
|
||||||
|
## `UnicastTable::add_entry`
|
||||||
### `UnicastTable() = default;`
|
`bool add_entry(NodeId dst_node, PortId dst_port, PortId out_port)`
|
||||||
### `std::vector<PortId> get_port_list(NodeId dst_node, PortId dst_port) const;`
|
## `UnicastTable::delete_entry`
|
||||||
### `bool add_entry(NodeId dst_node, PortId dst_port, PortId out_port);`
|
`bool delete_entry(NodeId dst_node, PortId dst_port, PortId out_port)`
|
||||||
### `bool delete_entry(NodeId dst_node, PortId dst_port, PortId out_port);`
|
## `UnicastTable::get_port_list`
|
||||||
|
`std::vector<PortId> get_port_list(NodeId dst_node, PortId dst_port) const`
|
||||||
@@ -28,13 +28,11 @@ else()
|
|||||||
logger.h
|
logger.h
|
||||||
simulator.h
|
simulator.h
|
||||||
node.h
|
node.h
|
||||||
host.h
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
error.cc
|
error.cc
|
||||||
logger.cc
|
logger.cc
|
||||||
simulator.cc
|
simulator.cc
|
||||||
node.cc
|
node.cc
|
||||||
host.cc
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
#include "core/time.h"
|
#include "core/time.h"
|
||||||
#include "core/types.h"
|
#include "core/types.h"
|
||||||
#include "core/host.h"
|
|
||||||
#include "core/simulator.h"
|
#include "core/simulator.h"
|
||||||
|
#include "network/host.h"
|
||||||
#include "hosts/mgmt_msg.h"
|
#include "hosts/mgmt_msg.h"
|
||||||
#include "hosts/policies.h"
|
#include "hosts/policies.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "core/host.h"
|
|
||||||
#include "core/simulator.h"
|
#include "core/simulator.h"
|
||||||
|
#include "network/host.h"
|
||||||
#include "hosts/mgmt_msg.h"
|
#include "hosts/mgmt_msg.h"
|
||||||
#include "hosts/policies.h"
|
#include "hosts/policies.h"
|
||||||
|
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ else()
|
|||||||
network_node.h
|
network_node.h
|
||||||
network_switch.h
|
network_switch.h
|
||||||
network_nic.h
|
network_nic.h
|
||||||
|
host.h
|
||||||
PRIVATE
|
PRIVATE
|
||||||
packet.cc
|
packet.cc
|
||||||
link.cc
|
link.cc
|
||||||
network_node.cc
|
network_node.cc
|
||||||
network_switch.cc
|
network_switch.cc
|
||||||
network_nic.cc
|
network_nic.cc
|
||||||
|
host.cc
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "core/host.h"
|
#include "network/host.h"
|
||||||
|
|
||||||
#include "core/error.h"
|
#include "core/error.h"
|
||||||
|
#include "network/network_nic.h"
|
||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ void Host::attach_nic(NetworkNic *nic) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_nic = nic;
|
_nic = nic;
|
||||||
|
_nic->attach_host(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::detach_nic(NetworkNic *nic) noexcept {
|
void Host::detach_nic(NetworkNic *nic) noexcept {
|
||||||
@@ -24,6 +26,7 @@ void Host::detach_nic(NetworkNic *nic) noexcept {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_nic->detach_host(this);
|
||||||
_nic = nullptr;
|
_nic = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef CORE_HOST_H
|
#ifndef NETWORK_HOST_H
|
||||||
#define CORE_HOST_H
|
#define NETWORK_HOST_H
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -45,4 +45,4 @@ private:
|
|||||||
|
|
||||||
} // namespace dofs
|
} // namespace dofs
|
||||||
|
|
||||||
#endif // CORE_HOST_H
|
#endif // NETWORK_HOST_H
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "core/host.h"
|
#include "network/host.h"
|
||||||
#include "core/error.h"
|
#include "core/error.h"
|
||||||
|
|
||||||
namespace dofs {
|
namespace dofs {
|
||||||
|
|||||||
@@ -1,255 +1,626 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
# dofs/tools/extract_api.py
|
||||||
extract_api.py
|
# Extract DOFS public API from headers and emit:
|
||||||
Scan C++ headers in ./src for public interfaces (public class/struct methods
|
# - Per-header Markdown files under docs/<same path>.md (mirrors src tree)
|
||||||
and free function declarations) and emit markdown snippets mirroring the src/
|
# - One JSON index at docs/index.json
|
||||||
tree into ./docs.
|
|
||||||
|
|
||||||
Each interface line is rendered as:
|
|
||||||
### `signature`
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
cd dofs
|
|
||||||
python3 tools/extract_api.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
import sys
|
from dataclasses import dataclass, asdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import List, Optional, Tuple, Dict
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
# -------- Repo roots --------
|
||||||
SRC_DIR = REPO_ROOT / "src"
|
def _detect_repo_root() -> Path:
|
||||||
DOCS_DIR = REPO_ROOT / "docs"
|
p = Path(__file__).resolve()
|
||||||
|
for anc in [p.parent, *p.parents]:
|
||||||
|
if (anc / "src").is_dir():
|
||||||
|
return anc
|
||||||
|
return p.parent
|
||||||
|
|
||||||
HEADER_EXTS = {".h", ".hh", ".hpp", ".hxx"}
|
REPO_ROOT = _detect_repo_root() # .../dofs
|
||||||
|
SRC_ROOT = REPO_ROOT / "src"
|
||||||
# ---------- Utilities ----------
|
OUT_DIR_DEFAULT = REPO_ROOT / "docs" # mirror into docs/
|
||||||
|
|
||||||
|
# -------- IO helpers --------
|
||||||
def read_text(p: Path) -> str:
|
def read_text(p: Path) -> str:
|
||||||
try:
|
return p.read_text(encoding="utf-8", errors="ignore")
|
||||||
return p.read_text(encoding="utf-8", errors="ignore")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[WARN] failed reading {p}: {e}", file=sys.stderr)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def strip_comments(code: str) -> str:
|
def iter_headers(root: Path) -> List[Path]:
|
||||||
# Remove /* ... */ (including multiline) and // ... to end of line
|
return sorted(root.rglob("*.h"))
|
||||||
no_block = re.sub(r"/\*.*?\*/", "", code, flags=re.S)
|
|
||||||
no_line = re.sub(r"//.*?$", "", no_block, flags=re.M)
|
|
||||||
return no_line
|
|
||||||
|
|
||||||
def collapse_ws(s: str) -> str:
|
def strip_comments_and_literals(code: str) -> str:
|
||||||
return re.sub(r"\s+", " ", s).strip()
|
string_re = r'("([^"\\]|\\.)*")|(\'([^\'\\]|\\.)*\')'
|
||||||
|
slc_re = r'//[^\n]*'
|
||||||
|
mlc_re = r'/\*.*?\*/'
|
||||||
|
def _keep_nls(m): # keep line count stable
|
||||||
|
return re.sub(r'[^\n]', ' ', m.group(0))
|
||||||
|
code = re.sub(mlc_re, _keep_nls, code, flags=re.S)
|
||||||
|
code = re.sub(string_re, _keep_nls, code, flags=re.S)
|
||||||
|
code = re.sub(slc_re, _keep_nls, code)
|
||||||
|
return code
|
||||||
|
|
||||||
def ensure_dir(path: Path):
|
# -------- Data model --------
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
@dataclass
|
||||||
|
class Symbol:
|
||||||
|
kind: str # "free_function" | "method" | "ctor" | "dtor" | "conversion" | "macro"
|
||||||
|
qualified: str
|
||||||
|
signature: str
|
||||||
|
file: str # e.g., "src/core/simulator.h"
|
||||||
|
line: int
|
||||||
|
static: bool = False
|
||||||
|
const: bool = False
|
||||||
|
ref_qual: str = ""
|
||||||
|
template_params: str = ""
|
||||||
|
|
||||||
# ---------- Heuristic extractors ----------
|
# -------- Parser (same core as before; trimmed comments) --------
|
||||||
|
class Parser:
|
||||||
|
def __init__(self, text: str, relpath: str):
|
||||||
|
self.text = text; self.relpath = relpath
|
||||||
|
self.i = 0; self.n = len(text); self.line = 1
|
||||||
|
self.ns_stack: List[str] = []
|
||||||
|
self.class_stack: List[dict] = []
|
||||||
|
self.depth_brace = 0
|
||||||
|
self.pending_template: Optional[str] = None
|
||||||
|
self.syms: List[Symbol] = []
|
||||||
|
|
||||||
ACCESS_RE = re.compile(r"^\s*(public|private|protected)\s*:\s*$")
|
# simple guard for bogus names when we fall into bodies
|
||||||
CLASS_START_RE = re.compile(r"^\s*(class|struct)\s+([A-Za-z_]\w*)\b")
|
self._kw_block = {
|
||||||
POSSIBLE_FUNC_DECL_RE = re.compile(
|
"if", "for", "while", "switch", "return", "case", "default",
|
||||||
r"""^[^;{}()]*\b
|
"do", "else", "break", "continue", "goto", "try", "catch"
|
||||||
(?!typedef\b)(?!using\b)(?!friend\b)
|
}
|
||||||
[A-Za-z_~]\w*\s*
|
|
||||||
\(
|
|
||||||
[^;]* # params
|
|
||||||
\)
|
|
||||||
(?:\s*(?:const|noexcept|override|final|=0|=\s*default|=\s*delete))*\s*
|
|
||||||
;
|
|
||||||
\s*$""",
|
|
||||||
re.X,
|
|
||||||
)
|
|
||||||
POSSIBLE_INLINE_DEF_RE = re.compile(
|
|
||||||
r"""^[^;{}()]*\b
|
|
||||||
(?!typedef\b)(?!using\b)(?!friend\b)
|
|
||||||
[A-Za-z_~]\w*\s*
|
|
||||||
\(
|
|
||||||
[^{;]* # params
|
|
||||||
\)
|
|
||||||
(?:\s*(?:const|noexcept|override|final))*\s*
|
|
||||||
\{""",
|
|
||||||
re.X,
|
|
||||||
)
|
|
||||||
SKIP_PREFIXES = ("#define", "#include", "static_assert", "enum ", "enum class ",
|
|
||||||
"template<", "namespace ", "using ", "typedef ", "friend ",
|
|
||||||
"struct ", "class ")
|
|
||||||
|
|
||||||
def extract_public_methods(lines, is_struct_default_public: bool):
|
def peek(self, k=0): j=self.i+k; return self.text[j] if 0<=j<self.n else ""
|
||||||
public = is_struct_default_public
|
def advance(self, k=1):
|
||||||
out = []
|
for _ in range(k):
|
||||||
depth = 0
|
if self.i>=self.n: return
|
||||||
for raw in lines:
|
ch=self.text[self.i]; self.i+=1
|
||||||
line = raw.strip()
|
if ch=="\n": self.line+=1
|
||||||
if not line:
|
def skip_ws(self):
|
||||||
continue
|
while self.i<self.n and self.text[self.i].isspace(): self.advance(1)
|
||||||
|
|
||||||
# Track nested braces to avoid confusing nested scopes
|
def run(self):
|
||||||
depth += raw.count("{")
|
while self.i < self.n:
|
||||||
depth -= raw.count("}")
|
self.skip_ws()
|
||||||
|
if self.i >= self.n: break
|
||||||
|
if self.text.startswith("namespace", self.i): self._parse_namespace(); continue
|
||||||
|
if self.text.startswith("class ", self.i) or self.text.startswith("struct ", self.i): self._parse_record(); continue
|
||||||
|
if self.text.startswith("template", self.i):
|
||||||
|
self.pending_template = self._parse_template_intro(); continue
|
||||||
|
if self.text.startswith("public:", self.i): self._set_access("public"); self.advance(len("public:")); continue
|
||||||
|
if self.text.startswith("private:", self.i): self._set_access("private"); self.advance(len("private:")); continue
|
||||||
|
if self.text.startswith("protected:", self.i): self._set_access("protected"); self.advance(len("protected:")); continue
|
||||||
|
ch=self.peek()
|
||||||
|
if ch=="{": self.depth_brace+=1; self.advance(1); continue
|
||||||
|
if ch=="}": self.depth_brace-=1; self.advance(1); self._maybe_pop(); continue
|
||||||
|
self._maybe_decl_or_def()
|
||||||
|
return self.syms
|
||||||
|
|
||||||
m = ACCESS_RE.match(line)
|
def _skip_balanced_block(self):
|
||||||
if m and depth >= 0:
|
"""
|
||||||
public = (m.group(1) == "public")
|
Consume a balanced {...} block starting at the current position
|
||||||
continue
|
(which must be at '{'). This does NOT touch self.depth_brace /
|
||||||
|
class_stack, so it won't confuse outer block tracking.
|
||||||
|
"""
|
||||||
|
if self.peek() != "{":
|
||||||
|
return
|
||||||
|
depth = 0
|
||||||
|
# consume the first '{'
|
||||||
|
self.advance(1)
|
||||||
|
depth += 1
|
||||||
|
while self.i < self.n and depth > 0:
|
||||||
|
ch = self.peek()
|
||||||
|
if ch == "{":
|
||||||
|
depth += 1
|
||||||
|
elif ch == "}":
|
||||||
|
depth -= 1
|
||||||
|
self.advance(1)
|
||||||
|
|
||||||
if not public:
|
# --- blocks ---
|
||||||
continue
|
def _parse_namespace(self):
|
||||||
|
self.advance(len("namespace")); self.skip_ws()
|
||||||
if line.startswith(SKIP_PREFIXES) or line.endswith(":"):
|
if self.text.startswith("inline", self.i):
|
||||||
continue
|
self.advance(len("inline")); self.skip_ws()
|
||||||
|
m = re.match(r'([A-Za-z_]\w*(::[A-Za-z_]\w*)*)?', self.text[self.i:])
|
||||||
if POSSIBLE_FUNC_DECL_RE.match(line):
|
name = "";
|
||||||
out.append(collapse_ws(line))
|
if m: name = m.group(0) or ""; self.advance(len(name))
|
||||||
continue
|
self.skip_ws()
|
||||||
|
if self.peek() == "{":
|
||||||
if POSSIBLE_INLINE_DEF_RE.match(line):
|
self.advance(1); self.depth_brace += 1
|
||||||
sig = line.split("{", 1)[0].rstrip()
|
self.ns_stack.append(name if name else "")
|
||||||
out.append(collapse_ws(sig) + " { ... }")
|
def _parse_record(self):
|
||||||
continue
|
kw = "class" if self.text.startswith("class ", self.i) else "struct"
|
||||||
|
self.advance(len(kw)); self.skip_ws()
|
||||||
return out
|
name = self._read_word()
|
||||||
|
if not name: return
|
||||||
def extract_free_function_decls(code: str):
|
while self.i<self.n and self.peek() not in "{;":
|
||||||
# Remove class/struct bodies to avoid capturing methods
|
if self.peek()=="<": self._read_balanced("<", ">")
|
||||||
scrubbed = []
|
else: self.advance(1)
|
||||||
toks = code.splitlines()
|
if self.peek()=="{":
|
||||||
in_class = False
|
self.advance(1); self.depth_brace += 1
|
||||||
brace_balance = 0
|
self.class_stack.append({"name": name, "access": "public" if kw=="struct" else "private", "brace_depth": self.depth_brace})
|
||||||
for line in toks:
|
|
||||||
if not in_class:
|
|
||||||
if CLASS_START_RE.match(line):
|
|
||||||
in_class = True
|
|
||||||
brace_balance = line.count("{") - line.count("}")
|
|
||||||
scrubbed.append("")
|
|
||||||
continue
|
|
||||||
else:
|
else:
|
||||||
brace_balance += line.count("{") - line.count("}")
|
self.advance(1) # forward decl
|
||||||
if brace_balance <= 0:
|
|
||||||
in_class = False
|
def _parse_template_intro(self) -> str:
|
||||||
scrubbed.append("")
|
self.advance(len("template")); self.skip_ws()
|
||||||
|
params = self._read_balanced("<", ">") if self.peek()=="<" else ""
|
||||||
|
return f"template{params}"
|
||||||
|
|
||||||
|
def _set_access(self, acc: str):
|
||||||
|
if self.class_stack: self.class_stack[-1]["access"]=acc
|
||||||
|
|
||||||
|
def _maybe_pop(self):
|
||||||
|
if self.class_stack and self.class_stack[-1]["brace_depth"] == self.depth_brace + 1:
|
||||||
|
self.class_stack.pop(); return
|
||||||
|
if self.ns_stack: self.ns_stack.pop()
|
||||||
|
|
||||||
|
# --- helpers ---
|
||||||
|
def _read_word(self) -> str:
|
||||||
|
self.skip_ws()
|
||||||
|
m = re.match(r'[A-Za-z_]\w*', self.text[self.i:])
|
||||||
|
if not m: return ""
|
||||||
|
w = m.group(0); self.advance(len(w)); return w
|
||||||
|
def _read_balanced(self, o: str, c: str) -> str:
|
||||||
|
depth=1; out=o; self.advance(1)
|
||||||
|
while self.i<self.n and depth>0:
|
||||||
|
ch=self.peek(); out+=ch; self.advance(1)
|
||||||
|
if ch==o: depth+=1
|
||||||
|
elif ch==c: depth-=1
|
||||||
|
return out
|
||||||
|
|
||||||
|
def _current_ns_is_dofs(self) -> bool:
|
||||||
|
if not self.ns_stack: return False
|
||||||
|
chain=[p for p in self.ns_stack if p]
|
||||||
|
return bool(chain) and chain[0]=="dofs"
|
||||||
|
|
||||||
|
def _read_one_head(self) -> Tuple[str, str]:
|
||||||
|
par=ang=sq=0; start=self.i
|
||||||
|
while self.i<self.n:
|
||||||
|
ch=self.peek()
|
||||||
|
if ch=="(": par+=1
|
||||||
|
elif ch==")": par=max(0,par-1)
|
||||||
|
elif ch=="<": ang+=1
|
||||||
|
elif ch==">": ang=max(0,ang-1)
|
||||||
|
elif ch=="[": sq+=1
|
||||||
|
elif ch=="]": sq=max(0,sq-1)
|
||||||
|
elif ch==";" and par==0 and ang==0 and sq==0:
|
||||||
|
end=self.i; self.advance(1)
|
||||||
|
return self.text[start:end].strip(), ";"
|
||||||
|
elif ch=="{" and par==0 and ang==0 and sq==0:
|
||||||
|
end=self.i
|
||||||
|
return self.text[start:end].strip(), "{"
|
||||||
|
self.advance(1)
|
||||||
|
return "", ""
|
||||||
|
|
||||||
|
def _skip_brace_block(self):
|
||||||
|
"""Assumes current char is '{'; skis balanced block."""
|
||||||
|
if self.peek() != "{":
|
||||||
|
return
|
||||||
|
brace = 0
|
||||||
|
while self.i < self.n:
|
||||||
|
ch = self.peek()
|
||||||
|
self.advance(1)
|
||||||
|
if ch == "{":
|
||||||
|
brace += 1
|
||||||
|
elif ch == "}":
|
||||||
|
brace -= 1
|
||||||
|
if brace == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def _consume_until_sep(self):
|
||||||
|
par=ang=sq=0
|
||||||
|
while self.i<self.n:
|
||||||
|
ch=self.peek(); self.advance(1)
|
||||||
|
if ch=="(": par+=1
|
||||||
|
elif ch==")": par=max(0,par-1)
|
||||||
|
elif ch=="<": ang+=1
|
||||||
|
elif ch==">": ang=max(0,ang-1)
|
||||||
|
elif ch=="[": sq+=1
|
||||||
|
elif ch=="]": sq=max(0,sq-1)
|
||||||
|
elif ch==";" and par==0 and ang==0 and sq==0: return
|
||||||
|
elif ch=="{" and par==0 and ang==0 and sq==0:
|
||||||
|
brace=1
|
||||||
|
while self.i<self.n and brace>0:
|
||||||
|
c2=self.peek(); self.advance(1)
|
||||||
|
if c2=="{": brace+=1
|
||||||
|
elif c2=="}": brace-=1
|
||||||
|
return
|
||||||
|
|
||||||
|
def _maybe_decl_or_def(self):
|
||||||
|
start_line = self.line
|
||||||
|
# skip obvious non-function starts
|
||||||
|
for bs in ("using ", "typedef ", "enum ", "namespace ", "static_assert"):
|
||||||
|
if self.text.startswith(bs, self.i):
|
||||||
|
self._consume_until_sep(); return
|
||||||
|
if self.text.startswith("template ", self.i):
|
||||||
|
self.pending_template = self._parse_template_intro(); return
|
||||||
|
|
||||||
|
decl, endch = self._read_one_head()
|
||||||
|
if not decl.strip(): return
|
||||||
|
|
||||||
|
tparams = self.pending_template or ""
|
||||||
|
self.pending_template = None
|
||||||
|
|
||||||
|
if "friend" in decl: return
|
||||||
|
if "(" not in decl or ")" not in decl: return
|
||||||
|
|
||||||
|
recorded = False
|
||||||
|
# classify: method vs free fn (inside dofs)
|
||||||
|
in_class = bool(self.class_stack)
|
||||||
|
if in_class:
|
||||||
|
if self.class_stack[-1]["access"] != "public": return
|
||||||
|
self._record_method(decl, start_line, tparams)
|
||||||
|
recorded = True
|
||||||
|
else:
|
||||||
|
if self._current_ns_is_dofs():
|
||||||
|
self._record_free_function(decl, start_line, tparams)
|
||||||
|
recorded = True
|
||||||
|
|
||||||
|
# If we just read a function head with a body, skip the body **after** recording
|
||||||
|
if endch == "{":
|
||||||
|
self._skip_brace_block()
|
||||||
|
return
|
||||||
|
|
||||||
|
# If it wasn't recorded (e.g., not in dofs namespace for free function),
|
||||||
|
# just continue; declarations ending with ';' need no additional skipping.
|
||||||
|
if recorded:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- symbol building ---
|
||||||
|
def _normalize(self, s: str) -> str:
|
||||||
|
return re.sub(r'\s+', ' ', s).strip()
|
||||||
|
|
||||||
|
def _name_from_decl(self, decl: str) -> str:
|
||||||
|
"""
|
||||||
|
Find the function/method name robustly:
|
||||||
|
- choose the '(' that starts the *parameter list* (angle-depth == 0)
|
||||||
|
- then take the identifier immediately to its left as the name
|
||||||
|
Avoids mistaking template args like 'std::function<void()>' for a function.
|
||||||
|
"""
|
||||||
|
# Strip trailing qualifiers after param list for stability
|
||||||
|
head = re.split(r'\b(noexcept|requires)\b', decl)[0]
|
||||||
|
|
||||||
|
# Scan to find the '(' that begins the parameter list at angle-depth 0
|
||||||
|
ang = 0
|
||||||
|
par_open_idx = -1
|
||||||
|
for idx, ch in enumerate(head):
|
||||||
|
if ch == '<':
|
||||||
|
ang += 1
|
||||||
|
elif ch == '>':
|
||||||
|
ang = max(0, ang - 1)
|
||||||
|
elif ch == '(' and ang == 0:
|
||||||
|
par_open_idx = idx
|
||||||
|
break
|
||||||
|
if par_open_idx == -1:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Walk left from par_open_idx to find the start of the name token
|
||||||
|
j = par_open_idx - 1
|
||||||
|
# Skip whitespace
|
||||||
|
while j >= 0 and head[j].isspace():
|
||||||
|
j -= 1
|
||||||
|
# Collect identifier (and allow operator forms)
|
||||||
|
# First, try operator names
|
||||||
|
m_op = re.search(r'(operator\s*""\s*_[A-Za-z_]\w*|operator\s*[^\s(]+)\s*$', head[:par_open_idx])
|
||||||
|
if m_op:
|
||||||
|
name = m_op.group(1)
|
||||||
|
else:
|
||||||
|
# Regular identifier (possibly destructor)
|
||||||
|
m_id = re.search(r'(~?[A-Za-z_]\w*)\s*$', head[:par_open_idx])
|
||||||
|
name = m_id.group(1) if m_id else ""
|
||||||
|
|
||||||
|
if not name or name in self._kw_block:
|
||||||
|
return ""
|
||||||
|
return name
|
||||||
|
|
||||||
|
def _qualify(self, name: str) -> str:
|
||||||
|
ns = [p for p in self.ns_stack if p]
|
||||||
|
q = "::".join(ns) + "::" if ns else ""
|
||||||
|
if self.class_stack:
|
||||||
|
q += "::".join([c["name"] for c in self.class_stack]) + "::"
|
||||||
|
return (q + name) if q else name
|
||||||
|
|
||||||
|
def _kind_for_method(self, name: str, cls: str) -> str:
|
||||||
|
if name == cls: return "ctor"
|
||||||
|
if name == f"~{cls}": return "dtor"
|
||||||
|
if name.startswith("operator"):
|
||||||
|
if re.match(r'operator\s+[^(\s]+', name) and "<" not in name and name != "operator()":
|
||||||
|
return "conversion"
|
||||||
|
return "method"
|
||||||
|
return "method"
|
||||||
|
|
||||||
|
def _cvref_static(self, decl: str) -> Tuple[bool,bool,str]:
|
||||||
|
is_static = bool(re.search(r'(^|\s)static\s', decl))
|
||||||
|
r = decl.rfind(")")
|
||||||
|
tail = decl[r+1:] if r!=-1 else ""
|
||||||
|
is_const = bool(re.search(r'\bconst\b', tail))
|
||||||
|
refq = "&&" if "&&" in tail else ("&" if re.search(r'(^|\s)&(\s|$)', tail) else "")
|
||||||
|
return is_static, is_const, refq
|
||||||
|
|
||||||
|
def _record_method(self, decl: str, start_line: int, tparams: str):
|
||||||
|
cls = self.class_stack[-1]["name"]
|
||||||
|
name = self._name_from_decl(decl)
|
||||||
|
if not name: return
|
||||||
|
qualified = self._qualify(name)
|
||||||
|
is_static, is_const, refq = self._cvref_static(decl)
|
||||||
|
kind = self._kind_for_method(name, cls)
|
||||||
|
sig = self._normalize((tparams + " " + decl).strip() if tparams else decl)
|
||||||
|
self.syms.append(Symbol(kind=kind, qualified=qualified, signature=sig,
|
||||||
|
file=self.relpath, line=start_line,
|
||||||
|
static=is_static, const=is_const, ref_qual=refq,
|
||||||
|
template_params=tparams or ""))
|
||||||
|
|
||||||
|
def _record_free_function(self, decl: str, start_line: int, tparams: str):
|
||||||
|
name = self._name_from_decl(decl)
|
||||||
|
if not name: return
|
||||||
|
qualified = self._qualify(name)
|
||||||
|
sig = self._normalize((tparams + " " + decl).strip() if tparams else decl)
|
||||||
|
self.syms.append(Symbol(kind="free_function", qualified=qualified, signature=sig,
|
||||||
|
file=self.relpath, line=start_line,
|
||||||
|
template_params=tparams or ""))
|
||||||
|
|
||||||
|
# -------- Rendering --------
|
||||||
|
def to_json(symbols: List[Symbol]) -> str:
|
||||||
|
items = [asdict(s) for s in symbols]
|
||||||
|
items.sort(key=lambda s: (s["file"], s["line"], s["qualified"], s["signature"]))
|
||||||
|
return json.dumps({"version": 1, "symbols": items}, indent=2)
|
||||||
|
|
||||||
|
def _markdown_for_file(rel_repo_file: str, symbols: List[Symbol]) -> str:
|
||||||
|
"""
|
||||||
|
Build per-header Markdown for exactly the symbols whose s.file == rel_repo_file.
|
||||||
|
"""
|
||||||
|
title = rel_repo_file.replace("src/", "", 1)
|
||||||
|
lines = [f"# {title}\n"]
|
||||||
|
file_syms = [s for s in symbols if s.file == rel_repo_file]
|
||||||
|
if not file_syms:
|
||||||
|
lines.append("_No public API symbols found in this header._")
|
||||||
|
lines.append("")
|
||||||
|
return "\n".join(l.rstrip() for l in lines)
|
||||||
|
|
||||||
|
# Group macros last; keep deterministic order
|
||||||
|
def _order(s: Symbol):
|
||||||
|
k = {"macro": 2}.get(s.kind, 1)
|
||||||
|
return (k, s.qualified, s.signature)
|
||||||
|
|
||||||
|
for s in sorted(file_syms, key=_order):
|
||||||
|
tprefix = (s.template_params + " ") if s.template_params else ""
|
||||||
|
if s.kind == "macro":
|
||||||
|
# H2 with macro name, then macro head; no line numbers, no bullets
|
||||||
|
lines.append(f"## `{s.qualified}`")
|
||||||
|
lines.append(f"`{s.signature}`\n")
|
||||||
|
else:
|
||||||
|
# H2 with fully qualified name (namespace::[class::]func)
|
||||||
|
# Contract/signature on the next line
|
||||||
|
fqname = s.qualified
|
||||||
|
if tprefix:
|
||||||
|
lines.append(f"## `{fqname}`")
|
||||||
|
lines.append(f"`{tprefix.strip()} {s.signature}`\n".replace(" ", " ").strip())
|
||||||
|
else:
|
||||||
|
lines.append(f"## `{fqname}`")
|
||||||
|
lines.append(f"`{s.signature}`\n")
|
||||||
|
|
||||||
|
return "\n".join(l.rstrip() for l in lines)
|
||||||
|
|
||||||
|
# -------- Robust multi-line free-function extraction --------
|
||||||
|
# Matches things like:
|
||||||
|
# inline void foo(A a,
|
||||||
|
# B b = std::nullopt) noexcept;
|
||||||
|
# std::mutex &error_mutex() noexcept;
|
||||||
|
_FREE_FN_RE = re.compile(r"""
|
||||||
|
(?P<prefix> ^ | [;\}\n] ) # anchor
|
||||||
|
(?P<head>
|
||||||
|
(?:\s*(?:inline|constexpr|consteval|constinit|static|extern)\s+)* # storage/attrs
|
||||||
|
(?:[\w:\<\>\*\&\s]+\s+)? # return type (optional for constructors, but we only accept when present)
|
||||||
|
(?P<name>[A-Za-z_]\w*)\s* # function name
|
||||||
|
\(
|
||||||
|
(?P<params>
|
||||||
|
[^()]* (?:\([^()]*\)[^()]*)* # balanced parens inside params
|
||||||
|
)
|
||||||
|
\)
|
||||||
|
(?:\s*noexcept(?:\s*\([^)]*\))?)? # optional noexcept/noexcept(expr)
|
||||||
|
(?:\s*->\s*[^;{\n]+)? # optional trailing return type
|
||||||
|
)
|
||||||
|
\s*
|
||||||
|
(?P<ender> [;{] ) # prototype or definition
|
||||||
|
""", re.VERBOSE | re.DOTALL | re.MULTILINE)
|
||||||
|
|
||||||
|
def _collapse_ws(s: str) -> str:
|
||||||
|
# Collapse all whitespace runs to a single space for clean signatures
|
||||||
|
return " ".join(s.split())
|
||||||
|
|
||||||
|
def extract_free_functions_multiline(clean_text: str, relpath: str) -> List[Symbol]:
|
||||||
|
"""
|
||||||
|
Walk the file tracking namespace blocks and pick out free-function
|
||||||
|
heads that can span multiple lines. Avoid class/struct/enum bodies.
|
||||||
|
"""
|
||||||
|
syms: List[Symbol] = []
|
||||||
|
ns_stack: List[str] = []
|
||||||
|
class_depth = 0 # crude guard: skip when inside class/struct/enum body
|
||||||
|
|
||||||
|
# Token-ish scan to maintain simple block context
|
||||||
|
i = 0
|
||||||
|
n = len(clean_text)
|
||||||
|
while i < n:
|
||||||
|
# namespace enter
|
||||||
|
if clean_text.startswith("namespace", i):
|
||||||
|
j = i + len("namespace")
|
||||||
|
while j < n and clean_text[j].isspace():
|
||||||
|
j += 1
|
||||||
|
# Parse namespace name (could be 'dofs' or anonymous)
|
||||||
|
k = j
|
||||||
|
while k < n and (clean_text[k].isalnum() or clean_text[k] in "_:"):
|
||||||
|
k += 1
|
||||||
|
ns_name = clean_text[j:k].strip()
|
||||||
|
# Find the next '{'
|
||||||
|
m = clean_text.find("{", k)
|
||||||
|
if m != -1:
|
||||||
|
if ns_name:
|
||||||
|
ns_stack.append(ns_name)
|
||||||
|
else:
|
||||||
|
ns_stack.append("") # anonymous
|
||||||
|
i = m + 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# class/struct/enum guard
|
||||||
|
if clean_text.startswith("class ", i) or clean_text.startswith("struct ", i) or clean_text.startswith("enum ", i):
|
||||||
|
# Enter body at next '{'
|
||||||
|
m = clean_text.find("{", i)
|
||||||
|
if m != -1:
|
||||||
|
class_depth += 1
|
||||||
|
i = m + 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if clean_text[i] == '}':
|
||||||
|
if class_depth > 0:
|
||||||
|
class_depth -= 1
|
||||||
|
elif ns_stack:
|
||||||
|
ns_stack.pop()
|
||||||
|
i += 1
|
||||||
continue
|
continue
|
||||||
scrubbed.append(line)
|
|
||||||
|
|
||||||
text = "\n".join(scrubbed)
|
# Try a function head only if not inside a class-like body
|
||||||
|
if class_depth == 0:
|
||||||
|
m = _FREE_FN_RE.match(clean_text, i)
|
||||||
|
if m:
|
||||||
|
name = m.group("name")
|
||||||
|
head = m.group("head")
|
||||||
|
# filter obvious false positives: require a return type before name
|
||||||
|
# (very rough: there must be at least one space before name inside head)
|
||||||
|
if re.search(r"\S\s+" + re.escape(name) + r"\s*\(", head):
|
||||||
|
qualified = "::".join([ns for ns in ns_stack if ns]) # drop anonymous
|
||||||
|
qualified = f"{qualified}::{name}" if qualified else name
|
||||||
|
# Build a tidy signature
|
||||||
|
ender = m.group("ender")
|
||||||
|
signature = _collapse_ws(head) + ender
|
||||||
|
line = clean_text.count("\n", 0, m.start("head")) + 1
|
||||||
|
syms.append(Symbol(kind="free_function",
|
||||||
|
qualified=qualified,
|
||||||
|
signature=signature,
|
||||||
|
file=relpath,
|
||||||
|
line=line))
|
||||||
|
i = m.end()
|
||||||
|
continue
|
||||||
|
|
||||||
out = []
|
i += 1
|
||||||
for raw in text.splitlines():
|
return syms
|
||||||
line = raw.strip()
|
|
||||||
if not line or line.startswith(SKIP_PREFIXES):
|
|
||||||
continue
|
|
||||||
if POSSIBLE_FUNC_DECL_RE.match(line):
|
|
||||||
out.append(collapse_ws(line))
|
|
||||||
elif POSSIBLE_INLINE_DEF_RE.match(line):
|
|
||||||
sig = line.split("{", 1)[0].rstrip()
|
|
||||||
out.append(collapse_ws(sig) + " { ... }")
|
|
||||||
return out
|
|
||||||
|
|
||||||
def split_top_level_classes(code: str):
|
# -------- Macro extraction (function-like only) --------
|
||||||
lines = code.splitlines()
|
_MACRO_HEAD_RE = re.compile(r'^\s*#\s*define\s+([A-Za-z_]\w*)\s*\((.*)$')
|
||||||
results = []
|
|
||||||
|
def extract_function_like_macros(text: str, relpath: str) -> List[Symbol]:
|
||||||
|
"""
|
||||||
|
Capture lines of the form:
|
||||||
|
#define NAME(args) <body...>
|
||||||
|
with multi-line bodies using backslash continuations.
|
||||||
|
We record: kind="macro", qualified=NAME, signature="#define NAME(args)".
|
||||||
|
"""
|
||||||
|
syms: List[Symbol] = []
|
||||||
|
lines = text.splitlines()
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(lines):
|
while i < len(lines):
|
||||||
m = CLASS_START_RE.match(lines[i])
|
line = lines[i]
|
||||||
|
m = _MACRO_HEAD_RE.match(line)
|
||||||
if not m:
|
if not m:
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
kind, name = m.group(1), m.group(2)
|
name = m.group(1)
|
||||||
# Find opening brace on same or subsequent lines
|
args_part = m.group(2) # may or may not contain closing ')'
|
||||||
j = i
|
start_line = i + 1
|
||||||
if "{" not in lines[j]:
|
# Collect continuation lines while trailing backslash exists.
|
||||||
j += 1
|
body_lines = [line]
|
||||||
while j < len(lines) and "{" not in lines[j]:
|
i += 1
|
||||||
j += 1
|
while i < len(lines) and body_lines[-1].rstrip().endswith("\\"):
|
||||||
if j >= len(lines):
|
body_lines.append(lines[i])
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
# Reconstruct just the macro head (name + (...) args text).
|
||||||
# Capture until matching close
|
head = "".join(body_lines)
|
||||||
depth = 0
|
# Try to extract the argument list reliably (balanced parens from first '(')
|
||||||
body = []
|
# without being confused by body parentheses.
|
||||||
while j < len(lines):
|
head_from_paren = head[head.find("("):] if "(" in head else ""
|
||||||
depth += lines[j].count("{")
|
# Minimal balanced scan to the first matching ')'
|
||||||
depth -= lines[j].count("}")
|
par = 0
|
||||||
body.append(lines[j])
|
arg_end = -1
|
||||||
if depth <= 0 and "}" in lines[j]:
|
for idx, ch in enumerate(head_from_paren):
|
||||||
break
|
if ch == "(":
|
||||||
j += 1
|
par += 1
|
||||||
body_inner = body[1:-1] if body else []
|
elif ch == ")":
|
||||||
results.append((name, kind == "struct", body_inner))
|
par -= 1
|
||||||
i = j + 1
|
if par == 0:
|
||||||
return results
|
arg_end = idx
|
||||||
|
break
|
||||||
# ---------- Main per-file processing ----------
|
if arg_end != -1:
|
||||||
|
arg_text = head_from_paren[1:arg_end] # inside (...)
|
||||||
def process_header(path: Path):
|
else:
|
||||||
raw = read_text(path)
|
# Fallback: whatever we saw on the first line
|
||||||
if not raw:
|
arg_text = args_part.split(")")[0]
|
||||||
return None
|
signature = f"#define {name}({arg_text.strip()})"
|
||||||
|
syms.append(Symbol(kind="macro",
|
||||||
code = strip_comments(raw)
|
qualified=name,
|
||||||
|
signature=signature,
|
||||||
# Collect classes
|
file=relpath,
|
||||||
class_entries = []
|
line=start_line))
|
||||||
for cname, is_struct, body in split_top_level_classes(code):
|
return syms
|
||||||
methods = extract_public_methods(body, is_struct_default_public=is_struct)
|
|
||||||
if methods:
|
|
||||||
class_entries.append((cname, methods))
|
|
||||||
|
|
||||||
# Collect free function decls
|
|
||||||
free_funcs = extract_free_function_decls(code)
|
|
||||||
|
|
||||||
if not class_entries and not free_funcs:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Build markdown with ### `signature` items
|
|
||||||
rel = path.relative_to(SRC_DIR)
|
|
||||||
md_lines = []
|
|
||||||
md_lines.append(f"# {rel.as_posix()}")
|
|
||||||
md_lines.append("")
|
|
||||||
|
|
||||||
if free_funcs:
|
|
||||||
md_lines.append("## Free functions")
|
|
||||||
md_lines.append("")
|
|
||||||
for sig in free_funcs:
|
|
||||||
md_lines.append(f"### `{sig}`")
|
|
||||||
md_lines.append("")
|
|
||||||
|
|
||||||
for cname, methods in class_entries:
|
|
||||||
md_lines.append(f"## class {cname} — public interface")
|
|
||||||
md_lines.append("")
|
|
||||||
for sig in methods:
|
|
||||||
md_lines.append(f"### `{sig}`")
|
|
||||||
md_lines.append("")
|
|
||||||
|
|
||||||
return "\n".join(md_lines)
|
|
||||||
|
|
||||||
def write_markdown(src_header: Path, content: str):
|
|
||||||
rel = src_header.relative_to(SRC_DIR)
|
|
||||||
out_path = DOCS_DIR / rel
|
|
||||||
out_path = out_path.with_suffix(".md")
|
|
||||||
ensure_dir(out_path)
|
|
||||||
out_path.write_text(content, encoding="utf-8")
|
|
||||||
return out_path
|
|
||||||
|
|
||||||
|
# -------- Driver --------
|
||||||
def main():
|
def main():
|
||||||
if not SRC_DIR.exists():
|
ap = argparse.ArgumentParser(description="Extract DOFS public API (per-header docs).")
|
||||||
print(f"[ERR] src/ not found at {SRC_DIR}", file=sys.stderr)
|
ap.add_argument("--src", default=str(SRC_ROOT), help="Source root (default: repo/src)")
|
||||||
sys.exit(1)
|
ap.add_argument("--out-dir", default=str(OUT_DIR_DEFAULT), help="Docs root to mirror into (default: docs)")
|
||||||
|
ap.add_argument("--stdout", action="store_true", help="Print JSON to stdout instead of writing files")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
generated = 0
|
src_root = Path(args.src).resolve()
|
||||||
for path in SRC_DIR.rglob("*"):
|
out_root = Path(args.out_dir).resolve()
|
||||||
if not path.is_file():
|
|
||||||
continue
|
|
||||||
if path.suffix.lower() not in HEADER_EXTS:
|
|
||||||
continue
|
|
||||||
result = process_header(path)
|
|
||||||
if result:
|
|
||||||
out = write_markdown(path, result)
|
|
||||||
generated += 1
|
|
||||||
print(f"[OK] {out.relative_to(REPO_ROOT)}")
|
|
||||||
|
|
||||||
if generated == 0:
|
all_symbols: List[Symbol] = []
|
||||||
print("[INFO] no public interfaces detected (heuristics may have filtered everything)]")
|
header_paths = iter_headers(src_root)
|
||||||
|
|
||||||
|
for hp in header_paths:
|
||||||
|
rel_repo = hp.relative_to(REPO_ROOT).as_posix() # e.g., src/core/simulator.h
|
||||||
|
raw = read_text(hp)
|
||||||
|
clean = strip_comments_and_literals(raw)
|
||||||
|
p = Parser(clean, rel_repo)
|
||||||
|
# C++ functions/methods (public) inside namespace dofs
|
||||||
|
parsed = p.run()
|
||||||
|
all_symbols.extend(parsed)
|
||||||
|
# Multi-line free functions (e.g., log_error in error.h)
|
||||||
|
extra_fns = extract_free_functions_multiline(clean, rel_repo)
|
||||||
|
# De-duplicate by (kind, qualified, signature, file, line)
|
||||||
|
seen = { (s.kind, s.qualified, s.signature, s.file, s.line) for s in all_symbols }
|
||||||
|
for s in extra_fns:
|
||||||
|
key = (s.kind, s.qualified, s.signature, s.file, s.line)
|
||||||
|
if key not in seen:
|
||||||
|
all_symbols.append(s)
|
||||||
|
seen.add(key)
|
||||||
|
# Function-like macros (global, regardless of namespace)
|
||||||
|
all_symbols.extend(extract_function_like_macros(raw, rel_repo))
|
||||||
|
|
||||||
|
if args.stdout:
|
||||||
|
print(to_json(all_symbols))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Write index.json under docs/
|
||||||
|
out_root.mkdir(parents=True, exist_ok=True)
|
||||||
|
(out_root / "index.json").write_text(to_json(all_symbols), encoding="utf-8")
|
||||||
|
|
||||||
|
# Emit one markdown per header, mirroring src/ -> docs/
|
||||||
|
# src/<subpath>.h => docs/<subpath>.md
|
||||||
|
for hp in header_paths:
|
||||||
|
rel_from_repo = hp.relative_to(REPO_ROOT).as_posix() # src/...
|
||||||
|
rel_from_src = hp.relative_to(src_root).with_suffix(".md") # core/simulator.md
|
||||||
|
target_path = out_root / rel_from_src
|
||||||
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
md = _markdown_for_file(rel_from_repo, all_symbols)
|
||||||
|
target_path.write_text(md, encoding="utf-8")
|
||||||
|
|
||||||
|
print(f"[extract_api] Wrote JSON index: {out_root/'index.json'}")
|
||||||
|
print(f"[extract_api] Wrote per-header Markdown under: {out_root}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user