Files
udpscope/src/search.hpp
T

98 lines
4.2 KiB
C++

// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2026 Martino Ferrari
#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
#include "condition.hpp"
#include "model.hpp"
namespace udpscope {
/// A scan of the whole retained buffer for every sample satisfying an `EventCondition`.
///
/// It runs an `EventDetector` -- the *same* evaluator the live trigger runs -- over the ring
/// in stream order, so a hit is by construction an instant the trigger would have fired on.
/// That is the claim the probe's armed-vs-scanned cross-check checks, and it is the reason
/// the detector was separated from the engine at all.
///
/// Everything here is in **ring-stamp space**, which is stream seconds relative to the
/// model's epoch: the clock `TriggerEngine::process()` is fed, the clock
/// `ScopeModel::live_window_locked()` searches, and the clock `publish_pause_view()` cuts in.
/// No conversion to display x happens in this class.
///
/// It runs on the GTK thread in chunks rather than on a thread of its own: the samples live
/// behind `ScopeModel::mutex()`, which the producer holds for each packet, and a scan that
/// took the lock once would stall acquisition for the whole buffer. One chunk per call holds
/// it for `kChunk` samples and no longer.
class BufferSearch {
public:
/// The hit list's ceiling. A condition matching a large share of the samples -- an edge
/// trigger on a noisy channel -- would otherwise fill memory with a list nobody can
/// navigate; the scan stops and says `Capped`, which is information rather than a hang.
static constexpr std::size_t kHitsMax = 10000;
/// Samples per `pump()`, and therefore the only latency knob: the producer waits on the
/// lock for exactly one chunk's scan.
static constexpr u64 kChunk = 8192;
enum class State {
Idle, ///< nothing has been searched.
Running, ///< `pump()` has more to do.
Done, ///< the whole retained buffer was scanned.
Truncated, ///< the producer overwrote samples before they were reached.
Capped, ///< `kHitsMax` was reached.
Cancelled, ///< the user stopped it, or the channel went away.
};
/// Begins a scan of the channel for the condition. Discards any previous result.
void start(const EventCondition& c, int channel, double dt);
void cancel() noexcept;
void clear() noexcept;
/// Scans one chunk. Returns true while there is more to do. Takes `model.mutex()` itself,
/// so the caller must *not* hold it.
bool pump(ScopeModel& model);
State state() const noexcept { return state_; }
const std::vector<double>& hits() const noexcept { return hits_; }
/// Which hit the view is on, or -1 for none.
int cursor() const noexcept { return cursor_; }
void select(int i) noexcept;
/// Moves the cursor and returns whether it moved; the ends do not wrap, because a wrap
/// from the last hit to the first looks exactly like the view not having moved at all.
bool next() noexcept;
bool prev() noexcept;
/// How much of the buffer has been scanned, and how much there was, in stream seconds.
double scanned_s() const noexcept { return scanned_s_; }
double total_s() const noexcept { return total_s_; }
/// Cost, for the probe: chunks scanned, the worst single chunk, and the whole scan.
std::uint64_t chunks() const noexcept { return chunks_; }
double worst_chunk_ms() const noexcept { return worst_chunk_ms_; }
double total_ms() const noexcept { return total_ms_; }
private:
EventDetector detector_{};
int channel_ = -1;
State state_ = State::Idle;
bool started_ = false; ///< the index range has been established.
bool truncated_ = false; ///< the producer overtook the scan at least once; sticky for the whole scan.
u64 at_ = 0; ///< next absolute index to scan.
u64 end_ = 0; ///< one past the last absolute index to scan.
double first_s_ = 0.0; ///< stamp of `at_` when the scan began.
double scanned_s_ = 0.0;
double total_s_ = 0.0;
std::vector<double> hits_;
int cursor_ = -1;
std::uint64_t chunks_ = 0;
double worst_chunk_ms_ = 0.0;
double total_ms_ = 0.0;
std::vector<f64> t_scratch_;
std::vector<f32> v_scratch_;
};
} // namespace udpscope