// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Martino Ferrari #pragma once #include #include #include #include namespace udpscope::flatudp { /// The numeric value is the `SignalInfo::type_code` (the order of the client library's `UDPS_T_*`). enum class Type : std::uint8_t { U8 = 0, I8, U16, I16, U32, I32, U64, I64, F32, F64 }; constexpr std::uint32_t kMaxCount = 4096; constexpr std::size_t kMaxDatagram = 65507; ///< the largest UDP payload over IPv4 constexpr int kMaxChannels = 256; ///< the most channels one list may make /// One signal of the sender's datagram: `count` consecutive values of `type`. struct Row { std::string name; Type type = Type::F32; std::uint32_t count = 1; /// A vector row (count > 1) only: true is ONE channel of `count` consecutive samples per /// datagram (a waveform); false is `count` channels `name[0]`.. of one sample each. bool wave = false; bool operator==(const Row&) const = default; }; std::size_t type_size(Type t); const char* type_name(Type t); ///< "uint8" ... "float64" bool parse_type(const std::string& s, Type& out); /// The bytes one datagram must have: the sum of `type_size * count`. /// True when no row is a vector. (Batching depends on `any_wave`, not on this.) /// True when no row is a vector. Such a list can be batched, several datagrams to a frame. bool all_scalar(const std::vector& rows); /// True when some row is a waveform (`wave`). bool any_wave(const std::vector& rows); /// The channels the list makes: a scalar 1, a non-wave vector `count`, a waveform 1. int channel_count(const std::vector& rows); /// `name:type[,name:type[count][w]...]`; a count of 1 is written without brackets, and the `w` /// of a waveform row follows its count. std::string format(const std::vector& rows); /// The inverse of `format`. Whitespace around names, types and counts is ignored. An empty text /// is an empty list (and not an error: `problem` reports it). Returns an error in words, empty /// on success; `rows` is empty after a failure. std::string parse(const std::string& text, std::vector& rows); /// The first reason this list cannot describe a datagram, in words for the user; empty when valid. std::string problem(const std::vector& rows); /// Decodes one datagram of `datagram_size(rows)` bytes to one column per *channel* (see /// `channel_count`): a non-wave vector row feeds `count` columns of one value each, a waveform row /// one column of `count` values. Appends; the caller clears the columns between uses. void decode_channels(const std::vector& rows, const std::uint8_t* data, bool big_endian, std::vector>& cols); /// Decodes one datagram of `datagram_size(rows)` bytes to doubles, row by row, byte-swapping when /// `big_endian` differs from the host. Appends to `cols[s]`; the caller clears them between uses. void decode_datagram(const std::vector& rows, const std::uint8_t* data, bool big_endian, std::vector>& cols); } // namespace udpscope::flatudp