refactor: modernize standard library usage across six Sonar rules (#5930)

This commit is contained in:
Dave Lane authored and GitHub committed 2026-10-14 15:33:54 -04:00
1 parent 81a677c1ee
commit 80588defde
37 files changed
+464 -121

No files matched your search

+33 -33
View File
@@ -742,20 +742,20 @@ namespace config {
}, // qsv }, // qsv
{ {
(int) amd::usage_h264_e::ultralowlatency, // usage (h264) std::to_underlying(amd::usage_h264_e::ultralowlatency), // usage (h264)
(int) amd::usage_hevc_e::ultralowlatency, // usage (hevc) std::to_underlying(amd::usage_hevc_e::ultralowlatency), // usage (hevc)
(int) amd::usage_av1_e::ultralowlatency, // usage (av1) std::to_underlying(amd::usage_av1_e::ultralowlatency), // usage (av1)
(int) amd::rc_h264_e::vbr_latency, // rate control (h264) std::to_underlying(amd::rc_h264_e::vbr_latency), // rate control (h264)
(int) amd::rc_hevc_e::vbr_latency, // rate control (hevc) std::to_underlying(amd::rc_hevc_e::vbr_latency), // rate control (hevc)
(int) amd::rc_av1_e::vbr_latency, // rate control (av1) std::to_underlying(amd::rc_av1_e::vbr_latency), // rate control (av1)
0, // enforce_hrd 0, // enforce_hrd
(int) amd::quality_h264_e::balanced, // quality (h264) std::to_underlying(amd::quality_h264_e::balanced), // quality (h264)
(int) amd::quality_hevc_e::balanced, // quality (hevc) std::to_underlying(amd::quality_hevc_e::balanced), // quality (hevc)
(int) amd::quality_av1_e::balanced, // quality (av1) std::to_underlying(amd::quality_av1_e::balanced), // quality (av1)
0, // preanalysis 0, // preanalysis
1, // vbaq 1, // vbaq
{}, // max_au_size (disabled by default) {}, // max_au_size (disabled by default)
(int) amd::coder_e::_auto, // coder std::to_underlying(amd::coder_e::_auto), // coder
}, // amd }, // amd
{ {
@@ -1021,8 +1021,8 @@ namespace config {
/** /**
* @brief Parse Sunshine configuration text into key-value entries. * @brief Parse Sunshine configuration text into key-value entries.
*/ */
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content) { config_map_t parse_config(const std::string_view &file_content) {
std::unordered_map<std::string, std::string> vars; config_map_t vars;
auto pos = std::begin(file_content); auto pos = std::begin(file_content);
auto end = std::end(file_content); auto end = std::end(file_content);
@@ -1081,7 +1081,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &destination) { void string_f(config_map_t &vars, const std::string &name, std::string &destination) {
auto it = vars.find(name); auto it = vars.find(name);
if (it == std::end(vars)) { if (it == std::end(vars)) {
return; return;
@@ -1101,7 +1101,7 @@ namespace config {
* @param f Converter applied to the raw configuration string. * @param f Converter applied to the raw configuration string.
*/ */
template<typename T, typename F> template<typename T, typename F>
void generic_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, T &destination, F &&f) { void generic_f(config_map_t &vars, const std::string &name, T &destination, F &&f) {
std::string tmp; std::string tmp;
string_f(vars, name, tmp); string_f(vars, name, tmp);
if (!tmp.empty()) { if (!tmp.empty()) {
@@ -1117,7 +1117,7 @@ namespace config {
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
* @param allowed_vals Accepted string values for this setting. * @param allowed_vals Accepted string values for this setting.
*/ */
void string_restricted_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &destination, const std::vector<std::string_view> &allowed_vals) { void string_restricted_f(config_map_t &vars, const std::string &name, std::string &destination, const std::vector<std::string_view> &allowed_vals) {
std::string temp; std::string temp;
string_f(vars, name, temp); string_f(vars, name, temp);
@@ -1136,7 +1136,7 @@ namespace config {
* @param name Setting name. * @param name Setting name.
* @param output Parsed string list. * @param output Parsed string list.
*/ */
void string_list_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<std::string> &output) { // NOSONAR(cpp:S6045): transparent hasher not available for unordered_map in this codebase void string_list_f(config_map_t &vars, const std::string &name, std::vector<std::string> &output) {
std::string temp; std::string temp;
string_f(vars, name, temp); string_f(vars, name, temp);
@@ -1164,7 +1164,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void path_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, fs::path &destination) { void path_f(config_map_t &vars, const std::string &name, fs::path &destination) {
// appdata needs to be retrieved once only // appdata needs to be retrieved once only
static auto appdata = platf::appdata(); static auto appdata = platf::appdata();
@@ -1195,7 +1195,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void path_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &destination) { void path_f(config_map_t &vars, const std::string &name, std::string &destination) {
fs::path temp = destination; fs::path temp = destination;
path_f(vars, name, temp); path_f(vars, name, temp);
@@ -1227,7 +1227,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &destination) { void int_f(config_map_t &vars, const std::string &name, int &destination) {
auto it = vars.find(name); auto it = vars.find(name);
if (it == std::end(vars)) { if (it == std::end(vars)) {
@@ -1245,7 +1245,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<int> &destination) { void int_f(config_map_t &vars, const std::string &name, std::optional<int> &destination) {
auto it = vars.find(name); auto it = vars.find(name);
if (it == std::end(vars)) { if (it == std::end(vars)) {
@@ -1265,7 +1265,7 @@ namespace config {
* @param f Converter applied to the raw configuration string. * @param f Converter applied to the raw configuration string.
*/ */
template<class F> template<class F>
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &destination, F &&f) { void int_f(config_map_t &vars, const std::string &name, int &destination, F &&f) {
std::string tmp; std::string tmp;
string_f(vars, name, tmp); string_f(vars, name, tmp);
if (!tmp.empty()) { if (!tmp.empty()) {
@@ -1282,7 +1282,7 @@ namespace config {
* @param f Converter applied to the raw configuration string. * @param f Converter applied to the raw configuration string.
*/ */
template<class F> template<class F>
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<int> &destination, F &&f) { void int_f(config_map_t &vars, const std::string &name, std::optional<int> &destination, F &&f) {
std::string tmp; std::string tmp;
string_f(vars, name, tmp); string_f(vars, name, tmp);
if (!tmp.empty()) { if (!tmp.empty()) {
@@ -1298,7 +1298,7 @@ namespace config {
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
* @param range Inclusive range accepted for the parsed value. * @param range Inclusive range accepted for the parsed value.
*/ */
void int_between_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &destination, const std::pair<int, int> &range) { void int_between_f(config_map_t &vars, const std::string &name, int &destination, const std::pair<int, int> &range) {
int temp = destination; int temp = destination;
int_f(vars, name, temp); int_f(vars, name, temp);
@@ -1335,7 +1335,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void bool_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, bool &destination) { void bool_f(config_map_t &vars, const std::string &name, bool &destination) {
std::string tmp; std::string tmp;
string_f(vars, name, tmp); string_f(vars, name, tmp);
@@ -1353,7 +1353,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void double_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &destination) { void double_f(config_map_t &vars, const std::string &name, double &destination) {
std::string tmp; std::string tmp;
string_f(vars, name, tmp); string_f(vars, name, tmp);
@@ -1379,7 +1379,7 @@ namespace config {
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
* @param range Inclusive range accepted for the parsed value. * @param range Inclusive range accepted for the parsed value.
*/ */
void double_between_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &destination, const std::pair<double, double> &range) { void double_between_f(config_map_t &vars, const std::string &name, double &destination, const std::pair<double, double> &range) {
double temp = destination; double temp = destination;
double_f(vars, name, temp); double_f(vars, name, temp);
@@ -1397,7 +1397,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void list_string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<std::string> &destination) { void list_string_f(config_map_t &vars, const std::string &name, std::vector<std::string> &destination) {
std::string string; std::string string;
string_f(vars, name, string); string_f(vars, name, string);
@@ -1443,7 +1443,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void list_prep_cmd_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<prep_cmd_t> &destination) { void list_prep_cmd_f(config_map_t &vars, const std::string &name, std::vector<prep_cmd_t> &destination) {
std::string string; std::string string;
string_f(vars, name, string); string_f(vars, name, string);
@@ -1476,7 +1476,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void list_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<int> &destination) { void list_int_f(config_map_t &vars, const std::string &name, std::vector<int> &destination) {
std::vector<std::string> list; std::vector<std::string> list;
list_string_f(vars, name, list); list_string_f(vars, name, list);
@@ -1516,7 +1516,7 @@ namespace config {
* @param name Configuration key to consume. * @param name Configuration key to consume.
* @param destination Destination field updated when the setting exists and parses successfully. * @param destination Destination field updated when the setting exists and parses successfully.
*/ */
void map_int_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::unordered_map<int, int> &destination) { void map_int_int_f(config_map_t &vars, const std::string &name, std::unordered_map<int, int> &destination) {
std::vector<int> list; std::vector<int> list;
list_int_f(vars, name, list); list_int_f(vars, name, list);
@@ -1589,7 +1589,7 @@ namespace config {
/** /**
* @brief Log parsed configuration entries and optionally record them as modified. * @brief Log parsed configuration entries and optionally record them as modified.
*/ */
void log_config_settings(const std::unordered_map<std::string, std::string> &vars, bool save) { void log_config_settings(const config_map_t &vars, bool save) {
for (auto &[name, val] : vars) { for (auto &[name, val] : vars) {
bool is_redacted = std::ranges::find(config::redacted_config, name) != config::redacted_config.end(); bool is_redacted = std::ranges::find(config::redacted_config, name) != config::redacted_config.end();
@@ -1606,7 +1606,7 @@ namespace config {
* *
* @param vars Parsed configuration entries; consumed keys are erased. * @param vars Parsed configuration entries; consumed keys are erased.
*/ */
void apply_config(std::unordered_map<std::string, std::string> &&vars) { void apply_config(config_map_t &&vars) {
log_config_settings(vars, true); log_config_settings(vars, true);
int_f(vars, "qp", video.qp); int_f(vars, "qp", video.qp);
@@ -1973,7 +1973,7 @@ namespace config {
* @brief Parse serialized text into the corresponding runtime representation. * @brief Parse serialized text into the corresponding runtime representation.
*/ */
int parse(int argc, char *argv[]) { int parse(int argc, char *argv[]) {
std::unordered_map<std::string, std::string> cmd_vars; config_map_t cmd_vars;
#ifdef _WIN32 #ifdef _WIN32
bool shortcut_launch = false; bool shortcut_launch = false;
bool service_admin_launch = false; bool service_admin_launch = false;
+9 -3
View File
@@ -15,8 +15,14 @@
// local includes // local includes
#include "nvenc/nvenc_config.h" #include "nvenc/nvenc_config.h"
#include "string_hash.h"
namespace config { namespace config {
/**
* @brief Owning configuration entries supporting lookup by string view or C string.
*/
using config_map_t = std::unordered_map<std::string, std::string, util::transparent_string_hash, std::equal_to<>>;
namespace nv { namespace nv {
/** /**
* @brief Convert Sunshine's NVENC quality preset number to FFmpeg's stable symbolic name. * @brief Convert Sunshine's NVENC quality preset number to FFmpeg's stable symbolic name.
@@ -39,7 +45,7 @@ namespace config {
inline constexpr std::string_view GAMEPAD_DRIVER_NONE = "none"; ///< Disable virtual gamepads on Windows and macOS. inline constexpr std::string_view GAMEPAD_DRIVER_NONE = "none"; ///< Disable virtual gamepads on Windows and macOS.
// track modified config options // track modified config options
inline std::unordered_map<std::string, std::string> modified_config_settings; ///< Configuration keys changed during the current parse or UI update. inline config_map_t modified_config_settings; ///< Configuration keys changed during the current parse or UI update.
// sensitive values that should be redacted from logging // sensitive values that should be redacted from logging
/** /**
@@ -55,7 +61,7 @@ namespace config {
* @param vars Parsed configuration entries to log. * @param vars Parsed configuration entries to log.
* @param save Whether modified configuration values should be written back to disk. * @param save Whether modified configuration values should be written back to disk.
*/ */
void log_config_settings(const std::unordered_map<std::string, std::string> &vars, bool save); void log_config_settings(const config_map_t &vars, bool save);
/** /**
* @brief Video encoder, capture, and color settings loaded from configuration. * @brief Video encoder, capture, and color settings loaded from configuration.
@@ -427,7 +433,7 @@ namespace config {
* @param file_content Raw configuration file contents to parse. * @param file_content Raw configuration file contents to parse.
* @return Parsed configuration key-value entries. * @return Parsed configuration key-value entries.
*/ */
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content); config_map_t parse_config(const std::string_view &file_content);
/** /**
* @brief Persist a configuration option when the active file does not already define it. * @brief Persist a configuration option when the active file does not already define it.
+1 -1
View File
@@ -274,7 +274,7 @@ namespace confighttp {
/** /**
* @brief File-extension to MIME-type mapping used when serving the Web UI. * @brief File-extension to MIME-type mapping used when serving the Web UI.
*/ */
const std::map<std::string, std::string> mime_types = { const std::map<std::string, std::string, std::less<>> mime_types = {
{"css", "text/css"}, {"css", "text/css"},
{"gif", "image/gif"}, {"gif", "image/gif"},
{"htm", "text/html"}, {"htm", "text/html"},
+1 -1
View File
@@ -2109,7 +2109,7 @@ namespace input {
// the input to the OS. This avoids potentially lengthy lock contention // the input to the OS. This avoids potentially lengthy lock contention
// in the control stream thread while input is being processed by the OS. // in the control stream thread while input is being processed by the OS.
{ {
std::lock_guard<std::mutex> lg(input->input_queue_lock); std::scoped_lock lg(input->input_queue_lock);
// If all entries have already been processed, nothing to do // If all entries have already been processed, nothing to do
if (input->input_queue.empty()) { if (input->input_queue.empty()) {
+3 -2
View File
@@ -7,6 +7,7 @@
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <utility>
// lib includes // lib includes
#include <boost/core/null_deleter.hpp> #include <boost/core/null_deleter.hpp>
@@ -226,8 +227,8 @@ namespace logging {
} }
void setup_libdisplaydevice_logging(int min_log_level) { void setup_libdisplaydevice_logging(int min_log_level) {
constexpr int min_level {static_cast<int>(display_device::Logger::LogLevel::verbose)}; constexpr int min_level {std::to_underlying(display_device::Logger::LogLevel::verbose)};
constexpr int max_level {static_cast<int>(display_device::Logger::LogLevel::fatal)}; constexpr int max_level {std::to_underlying(display_device::Logger::LogLevel::fatal)};
const auto log_level {static_cast<display_device::Logger::LogLevel>(std::min(std::max(min_level, min_log_level), max_level))}; const auto log_level {static_cast<display_device::Logger::LogLevel>(std::min(std::max(min_level, min_log_level), max_level))};
display_device::Logger::get().setLogLevel(log_level); display_device::Logger::get().setLogLevel(log_level);
+3 -2
View File
@@ -123,8 +123,9 @@ namespace logging {
void setup_av_logging(int min_log_level); void setup_av_logging(int min_log_level);
/** /**
* @brief Setup logging for libdisplaydevice. * @brief Set up libdisplaydevice logging, clamping to its supported enum values.
* @param min_log_level The log level. *
* @param min_log_level Numeric minimum severity, clamped from verbose through fatal.
*/ */
void setup_libdisplaydevice_logging(int min_log_level); void setup_libdisplaydevice_logging(int min_log_level);
+8 -8
View File
@@ -18,15 +18,15 @@ namespace ip = boost::asio::ip;
namespace net { namespace net {
/** /**
* @brief Pc ips v4. * @brief IPv4 loopback networks used for local-client classification.
*/ */
std::vector<ip::network_v4> pc_ips_v4 { std::vector pc_ips_v4 {
ip::make_network_v4("127.0.0.0/8"sv), ip::make_network_v4("127.0.0.0/8"sv),
}; };
/** /**
* @brief Lan ips v4. * @brief IPv4 private and link-local networks used for LAN classification.
*/ */
std::vector<ip::network_v4> lan_ips_v4 { std::vector lan_ips_v4 {
ip::make_network_v4("192.168.0.0/16"sv), ip::make_network_v4("192.168.0.0/16"sv),
ip::make_network_v4("172.16.0.0/12"sv), ip::make_network_v4("172.16.0.0/12"sv),
ip::make_network_v4("10.0.0.0/8"sv), ip::make_network_v4("10.0.0.0/8"sv),
@@ -35,15 +35,15 @@ namespace net {
}; };
/** /**
* @brief Pc ips v6. * @brief IPv6 loopback networks used for local-client classification.
*/ */
std::vector<ip::network_v6> pc_ips_v6 { std::vector pc_ips_v6 {
ip::make_network_v6("::1/128"sv), ip::make_network_v6("::1/128"sv),
}; };
/** /**
* @brief Lan ips v6. * @brief IPv6 private and link-local networks used for LAN classification.
*/ */
std::vector<ip::network_v6> lan_ips_v6 { std::vector lan_ips_v6 {
ip::make_network_v6("fc00::/7"sv), ip::make_network_v6("fc00::/7"sv),
ip::make_network_v6("fe80::/64"sv), ip::make_network_v6("fe80::/64"sv),
}; };
+2 -1
View File
@@ -35,6 +35,7 @@
#include "platform/common.h" #include "platform/common.h"
#include "process.h" #include "process.h"
#include "rtsp.h" #include "rtsp.h"
#include "string_hash.h"
#include "system_tray.h" #include "system_tray.h"
#include "utility.h" #include "utility.h"
#include "uuid.h" #include "uuid.h"
@@ -191,7 +192,7 @@ namespace nvhttp {
}; };
// uniqueID, session // uniqueID, session
std::unordered_map<std::string, pair_session_t> map_id_sess; ///< Pairing sessions keyed by temporary unique ID. std::unordered_map<std::string, pair_session_t, util::transparent_string_hash, std::equal_to<>> map_id_sess; ///< Pairing sessions keyed by temporary unique ID.
/** /**
* @brief Get the mutex protecting pairing-session state. * @brief Get the mutex protecting pairing-session state.
+3 -2
View File
@@ -8,6 +8,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <filesystem> #include <filesystem>
#include <thread> #include <thread>
#include <utility>
// lib includes // lib includes
#include <ffnvcodec/dynlink_loader.h> #include <ffnvcodec/dynlink_loader.h>
@@ -363,7 +364,7 @@ namespace cuda {
BOOST_LOG(debug) << "Found CUDA device with PCI bus ID: "sv << pci_bus_id.data(); BOOST_LOG(debug) << "Found CUDA device with PCI bus ID: "sv << pci_bus_id.data();
// Linux uses lowercase hexadecimal while CUDA uses uppercase // Linux uses lowercase hexadecimal while CUDA uses uppercase
std::transform(pci_bus_id.begin(), pci_bus_id.end(), pci_bus_id.begin(), [](char c) { std::ranges::transform(pci_bus_id, pci_bus_id.begin(), [](char c) {
return std::tolower(c); return std::tolower(c);
}); });
@@ -1063,7 +1064,7 @@ namespace cuda {
} }
break; break;
default: default:
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; BOOST_LOG(error) << "Unrecognized capture status ["sv << std::to_underlying(status) << ']';
return status; return status;
} }
} }
+2 -1
View File
@@ -7,6 +7,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <future> #include <future>
#include <stdexcept> #include <stdexcept>
#include <string_view>
// local includes // local includes
#include "graphics.h" #include "graphics.h"
@@ -557,7 +558,7 @@ namespace egl {
}; };
for (auto ext : extensions) { for (auto ext : extensions) {
if (!std::strstr(extension_st, ext)) { if (!std::string_view {extension_st}.contains(ext)) {
BOOST_LOG(error) << "Missing extension: ["sv << ext << ']'; BOOST_LOG(error) << "Missing extension: ["sv << ext << ']';
return nullptr; return nullptr;
} }
+3 -2
View File
@@ -12,6 +12,7 @@
#include <stdexcept> #include <stdexcept>
#include <thread> #include <thread>
#include <unistd.h> #include <unistd.h>
#include <utility>
// platform includes // platform includes
#include <drm_fourcc.h> #include <drm_fourcc.h>
@@ -1831,7 +1832,7 @@ namespace platf {
} }
break; break;
default: default:
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; BOOST_LOG(error) << "Unrecognized capture status ["sv << std::to_underlying(status) << ']';
return status; return status;
} }
} }
@@ -2107,7 +2108,7 @@ namespace platf {
} }
break; break;
default: default:
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; BOOST_LOG(error) << "Unrecognized capture status ["sv << std::to_underlying(status) << ']';
return status; return status;
} }
} }
+3 -2
View File
@@ -20,6 +20,7 @@
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <utility>
// platform includes // platform includes
#include <arpa/inet.h> #include <arpa/inet.h>
@@ -1020,7 +1021,7 @@ namespace platf {
// We can't track QoS state separately for each destination on this OS, // We can't track QoS state separately for each destination on this OS,
// so we keep a ref count to only disable QoS options when all clients // so we keep a ref count to only disable QoS options when all clients
// are disconnected. // are disconnected.
static std::atomic<int> qos_ref_count = 0; static std::atomic qos_ref_count = 0;
/** /**
* @brief Linux QoS state used to tune socket priority while streaming. * @brief Linux QoS state used to tune socket priority while streaming.
@@ -1086,7 +1087,7 @@ namespace platf {
dscp = 48; dscp = 48;
break; break;
default: default:
BOOST_LOG(error) << "Unknown traffic type: "sv << (int) data_type; BOOST_LOG(error) << "Unknown traffic type: "sv << std::to_underlying(data_type);
break; break;
} }
+1 -1
View File
@@ -1081,7 +1081,7 @@ namespace portal {
dbus_t dbus; ///< DBus connection used for portal screencast requests. dbus_t dbus; ///< DBus connection used for portal screencast requests.
// Class variable to store runtime state of maxFramerate negotiation // Class variable to store runtime state of maxFramerate negotiation
static inline std::atomic<bool> negotiate_maxframerate {true}; ///< Whether portal negotiation should request the maximum frame rate. static inline std::atomic negotiate_maxframerate {true}; ///< Whether portal negotiation should request the maximum frame rate.
}; };
} // namespace portal } // namespace portal
+3 -2
View File
@@ -9,6 +9,7 @@
#include <fstream> #include <fstream>
#include <ranges> #include <ranges>
#include <thread> #include <thread>
#include <utility>
#ifdef SUNSHINE_TESTS #ifdef SUNSHINE_TESTS
#include <gtest/gtest.h> #include <gtest/gtest.h>
@@ -630,7 +631,7 @@ namespace platf {
} }
break; break;
default: default:
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; BOOST_LOG(error) << "Unrecognized capture status ["sv << std::to_underlying(status) << ']';
return status; return status;
} }
} }
@@ -804,7 +805,7 @@ namespace platf {
} }
break; break;
default: default:
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; BOOST_LOG(error) << "Unrecognized capture status ["sv << std::to_underlying(status) << ']';
return status; return status;
} }
} }
+1 -1
View File
@@ -120,7 +120,7 @@ namespace {
break; break;
} }
static_assert((int) sample_format_e::_size == 5, "Unrecognized sample_format_e"); static_assert(std::to_underlying(sample_format_e::_size) == 5, "Unrecognized sample_format_e");
waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
waveformat.Format.nChannels = channel_count; waveformat.Format.nChannels = channel_count;
+3 -2
View File
@@ -6,6 +6,7 @@
#include <array> #include <array>
#include <cmath> #include <cmath>
#include <thread> #include <thread>
#include <utility>
// platform includes // platform includes
#include <initguid.h> #include <initguid.h>
@@ -353,7 +354,7 @@ namespace platf::dxgi {
} }
break; break;
default: default:
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; BOOST_LOG(error) << "Unrecognized capture status ["sv << std::to_underlying(status) << ']';
return status; return status;
} }
@@ -1137,7 +1138,7 @@ namespace platf {
bool needs_encoder_reenumeration() { bool needs_encoder_reenumeration() {
// Serialize access to the static DXGI factory // Serialize access to the static DXGI factory
static std::mutex reenumeration_state_lock; static std::mutex reenumeration_state_lock;
auto lg = std::lock_guard(reenumeration_state_lock); auto lg = std::scoped_lock(reenumeration_state_lock);
// Keep a reference to the DXGI factory, which will keep track of changes internally. // Keep a reference to the DXGI factory, which will keep track of changes internally.
static dxgi::factory1_t factory; static dxgi::factory1_t factory;
+4 -2
View File
@@ -3,8 +3,10 @@
* @brief Definitions for handling video ram. * @brief Definitions for handling video ram.
*/ */
// standard includes // standard includes
#include <algorithm>
#include <array> #include <array>
#include <cmath> #include <cmath>
#include <span>
// platform includes // platform includes
#include <d3dcompiler.h> #include <d3dcompiler.h>
@@ -286,7 +288,7 @@ namespace platf::dxgi {
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
{ {
util::buffer_t<std::uint8_t> cursor_img = img_data; util::buffer_t<std::uint8_t> cursor_img = img_data;
std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { std::ranges::for_each(std::span {(std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img)}, [](auto &pixel) {
auto alpha = (std::uint8_t) ((pixel >> 24) & 0xFF); auto alpha = (std::uint8_t) ((pixel >> 24) & 0xFF);
if (alpha == 0xFF) { if (alpha == 0xFF) {
// Pixels with 0xFF alpha will be XOR-blended as is. // Pixels with 0xFF alpha will be XOR-blended as is.
@@ -360,7 +362,7 @@ namespace platf::dxgi {
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
{ {
util::buffer_t<std::uint8_t> cursor_img = img_data; util::buffer_t<std::uint8_t> cursor_img = img_data;
std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { std::ranges::for_each(std::span {(std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img)}, [](auto &pixel) {
auto alpha = (std::uint8_t) ((pixel >> 24) & 0xFF); auto alpha = (std::uint8_t) ((pixel >> 24) & 0xFF);
if (alpha == 0xFF) { if (alpha == 0xFF) {
// Pixels with 0xFF alpha will be XOR-blended by make_cursor_xor_image(). // Pixels with 0xFF alpha will be XOR-blended by make_cursor_xor_image().
+10 -6
View File
@@ -10,12 +10,14 @@
#include <iterator> #include <iterator>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <utility>
#include <vector> #include <vector>
// lib includes // lib includes
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/asio/ip/address.hpp> #include <boost/asio/ip/address.hpp>
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <boost/range/algorithm/find_if.hpp>
// local includes required before platform includes // local includes required before platform includes
#include "src/boost_process_compat.h" #include "src/boost_process_compat.h"
@@ -100,7 +102,7 @@ extern "C" {
namespace { namespace {
std::atomic<bool> used_nt_set_timer_resolution = false; std::atomic used_nt_set_timer_resolution = false;
bool nt_set_timer_resolution_max() { bool nt_set_timer_resolution_max() {
ULONG maximum; ULONG maximum;
@@ -375,6 +377,8 @@ namespace platf {
/** /**
* @brief Merge user environment variables into a Windows environment block. * @brief Merge user environment variables into a Windows environment block.
* *
* Existing variable names retain their spelling when matched case-insensitively.
*
* @param env Environment variables for the child process. * @param env Environment variables for the child process.
* @param shell_token Shell token. * @param shell_token Shell token.
* @return True when the user environment block was merged into `env`. * @return True when the user environment block was merged into `env`.
@@ -394,7 +398,7 @@ namespace platf {
std::string env_val = env_tuple.substr(env_tuple.find('=') + 1); std::string env_val = env_tuple.substr(env_tuple.find('=') + 1);
// Perform a case-insensitive search to see if this variable name already exists // Perform a case-insensitive search to see if this variable name already exists
if (auto itr = std::find_if(env.begin(), env.end(), [&](const auto &e) { if (auto itr = boost::range::find_if(env, [&env_name](const auto &e) {
return boost::iequals(e.get_name(), env_name); return boost::iequals(e.get_name(), env_name);
}); });
itr != env.end()) { itr != env.end()) {
@@ -829,7 +833,7 @@ namespace platf {
// Overriding these predefined keys affects process-wide state, so serialize all calls // Overriding these predefined keys affects process-wide state, so serialize all calls
// to ensure the handle state is consistent while we perform the command query. // to ensure the handle state is consistent while we perform the command query.
static std::mutex per_user_key_mutex; static std::mutex per_user_key_mutex;
auto lg = std::lock_guard(per_user_key_mutex); auto lg = std::scoped_lock(per_user_key_mutex);
// Override HKEY_CLASSES_ROOT and HKEY_CURRENT_USER to ensure we query the correct class info // Override HKEY_CLASSES_ROOT and HKEY_CURRENT_USER to ensure we query the correct class info
if (!override_per_user_predefined_keys(token)) { if (!override_per_user_predefined_keys(token)) {
@@ -1027,7 +1031,7 @@ namespace platf {
auto sunshine_wenv = boost::this_process::wenvironment(); auto sunshine_wenv = boost::this_process::wenvironment();
std::wstring path_var_name {L"PATH"}; std::wstring path_var_name {L"PATH"};
std::wstring old_path_val; std::wstring old_path_val;
auto itr = std::find_if(sunshine_wenv.cbegin(), sunshine_wenv.cend(), [&](const auto &e) { auto itr = boost::range::find_if(std::as_const(sunshine_wenv), [&path_var_name](const auto &e) {
return boost::iequals(e.get_name(), path_var_name); return boost::iequals(e.get_name(), path_var_name);
}); });
if (itr != sunshine_wenv.cend()) { if (itr != sunshine_wenv.cend()) {
@@ -1144,7 +1148,7 @@ namespace platf {
win32_priority = THREAD_PRIORITY_HIGHEST; win32_priority = THREAD_PRIORITY_HIGHEST;
break; break;
default: default:
BOOST_LOG(error) << "Unknown thread priority: "sv << (int) priority; BOOST_LOG(error) << "Unknown thread priority: "sv << std::to_underlying(priority);
return; return;
} }
@@ -1749,7 +1753,7 @@ namespace platf {
traffic_type = QOSTrafficTypeAudioVideo; traffic_type = QOSTrafficTypeAudioVideo;
break; break;
default: default:
BOOST_LOG(error) << "Unknown traffic type: "sv << (int) data_type; BOOST_LOG(error) << "Unknown traffic type: "sv << std::to_underlying(data_type);
return nullptr; return nullptr;
} }
+6 -3
View File
@@ -5,10 +5,12 @@
#define BOOST_BIND_GLOBAL_PLACEHOLDERS #define BOOST_BIND_GLOBAL_PLACEHOLDERS
// standard includes // standard includes
#include <algorithm>
#include <array> #include <array>
#include <filesystem> #include <filesystem>
#include <string> #include <string>
#include <thread> #include <thread>
#include <utility>
#include <vector> #include <vector>
// lib includes // lib includes
@@ -18,6 +20,7 @@
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/token_functions.hpp> #include <boost/token_functions.hpp>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/sha.h> #include <openssl/sha.h>
@@ -130,7 +133,7 @@ namespace proc {
BOOST_LOG(debug) << "Parsed target ["sv << parts.at(0) << "] from command ["sv << cmd << ']'; BOOST_LOG(debug) << "Parsed target ["sv << parts.at(0) << "] from command ["sv << cmd << ']';
// If the target is a URL, don't parse any further here // If the target is a URL, don't parse any further here
if (parts.at(0).find("://") != std::string::npos) { if (parts.at(0).contains("://")) {
return boost::filesystem::path(); return boost::filesystem::path();
} }
@@ -495,7 +498,7 @@ namespace proc {
// Windows treats environment variable names in a case-insensitive manner, // Windows treats environment variable names in a case-insensitive manner,
// so we look for a case-insensitive match here. This is critical for // so we look for a case-insensitive match here. This is critical for
// correctly appending to PATH on Windows. // correctly appending to PATH on Windows.
auto itr = std::find_if(env.cbegin(), env.cend(), [&](const auto &e) { auto itr = boost::range::find_if(std::as_const(env), [&var_name](const auto &e) {
return boost::iequals(e.get_name(), var_name); return boost::iequals(e.get_name(), var_name);
}); });
if (itr != env.cend()) { if (itr != env.cend()) {
@@ -715,7 +718,7 @@ namespace proc {
this_env[name] = parse_env_val(this_env, val.get_value<std::string>()); this_env[name] = parse_env_val(this_env, val.get_value<std::string>());
} }
std::set<std::string> ids; std::set<std::string, std::less<>> ids;
std::vector<proc::ctx_t> apps; std::vector<proc::ctx_t> apps;
int i = 0; int i = 0;
for (auto &[_, app_node] : apps_node) { for (auto &[_, app_node] : apps_node) {
+3 -3
View File
@@ -375,7 +375,7 @@ namespace rtsp_stream {
// If content_length > bytes read, then we need to store current data read, // If content_length > bytes read, then we need to store current data read,
// to be appended by the next read. // to be appended by the next read.
std::string_view content {option->content}; std::string_view content {option->content};
auto begin = std::find_if(std::begin(content), std::end(content), [](auto ch) { auto begin = std::ranges::find_if(content, [](auto ch) {
return (bool) std::isdigit(ch); return (bool) std::isdigit(ch);
}); });
@@ -1019,7 +1019,7 @@ namespace rtsp_stream {
seqn.content = const_cast<char *>(seqn_str.c_str()); seqn.content = const_cast<char *>(seqn_str.c_str());
std::string_view target {req->message.request.target}; std::string_view target {req->message.request.target};
auto begin = std::find(std::begin(target), std::end(target), '=') + 1; auto begin = std::ranges::find(target, '=') + 1;
auto end = std::find(begin, std::end(target), '/'); auto end = std::find(begin, std::end(target), '/');
std::string_view type {begin, (size_t) std::distance(begin, end)}; std::string_view type {begin, (size_t) std::distance(begin, end)};
@@ -1220,7 +1220,7 @@ namespace rtsp_stream {
if ("Host"sv == request_option->option) { if ("Host"sv == request_option->option) {
std::string_view content {request_option->content}; std::string_view content {request_option->content};
BOOST_LOG(debug) << "Found Host: "sv << content; BOOST_LOG(debug) << "Found Host: "sv << content;
config.audio.flags[audio::config_t::HIGH_QUALITY] = (content.find("0.0.0.0"sv) == std::string::npos); config.audio.flags[audio::config_t::HIGH_QUALITY] = !content.contains("0.0.0.0"sv);
} }
} }
} else if (session.surround_params.length() > 3) { } else if (session.surround_params.length() > 3) {
+3 -3
View File
@@ -367,7 +367,7 @@ namespace stream {
static inline int encode_audio(bool encrypted, const audio::buffer_t &plaintext, uint8_t *destination, crypto::aes_t &iv, crypto::cipher::cbc_t &cbc) { static inline int encode_audio(bool encrypted, const audio::buffer_t &plaintext, uint8_t *destination, crypto::aes_t &iv, crypto::cipher::cbc_t &cbc) {
// If encryption isn't enabled // If encryption isn't enabled
if (!encrypted) { if (!encrypted) {
std::copy(std::begin(plaintext), std::end(plaintext), destination); std::ranges::copy(plaintext, destination);
return (int) plaintext.size(); return (int) plaintext.size();
} }
@@ -1000,7 +1000,7 @@ namespace stream {
std::copy(begin, next, std::back_inserter(replaced)); std::copy(begin, next, std::back_inserter(replaced));
if (next != end) { if (next != end) {
std::copy(std::begin(_new), std::end(_new), std::back_inserter(replaced)); std::ranges::copy(_new, std::back_inserter(replaced));
std::copy(next + old.size(), end, std::back_inserter(replaced)); std::copy(next + old.size(), end, std::back_inserter(replaced));
} }
@@ -2116,7 +2116,7 @@ namespace stream {
} }
TUPLE_2D_REF(recv_peer, msg, *msg_opt); TUPLE_2D_REF(recv_peer, msg, *msg_opt);
if (msg.find(expected_payload) != std::string::npos) { if (msg.contains(expected_payload)) {
// Match the new PING payload format // Match the new PING payload format
BOOST_LOG(debug) << "Received ping [v2] from "sv << recv_peer.address() << ':' << recv_peer.port() << " ["sv << util::hex_vec(msg) << ']'; BOOST_LOG(debug) << "Received ping [v2] from "sv << recv_peer.address() << ':' << recv_peer.port() << " ["sv << util::hex_vec(msg) << ']';
} else if (!(session->config.mlFeatureFlags & ML_FF_SESSION_ID_V1) && msg == "PING"sv) { } else if (!(session->config.mlFeatureFlags & ML_FF_SESSION_ID_V1) && msg == "PING"sv) {
+29
View File
@@ -0,0 +1,29 @@
/**
* @file src/string_hash.h
* @brief Transparent hashing for containers with owning string keys.
*/
#pragma once
// standard includes
#include <cstddef>
#include <functional>
#include <string_view>
namespace util {
/**
* @brief Hash strings, string views, and C strings without temporary allocations.
*/
struct transparent_string_hash {
using is_transparent = void; ///< Enable heterogeneous unordered-container lookup.
/**
* @brief Hash all characters in a string-like key, including embedded nulls in views.
*
* @param value Text to hash.
* @return Hash shared by equivalent owning and non-owning string keys.
*/
std::size_t operator()(std::string_view value) const noexcept {
return std::hash<std::string_view> {}(value);
}
};
} // namespace util
+3 -3
View File
@@ -29,10 +29,10 @@ namespace sync_util {
/** /**
* @brief Acquire the underlying lock or keyed mutex. * @brief Acquire the underlying lock or keyed mutex.
* *
* @return Lock guard owning the synchronized object until destruction. * @return Scoped lock owning the synchronized object until destruction.
*/ */
std::lock_guard<mutex_t> lock() { std::scoped_lock<mutex_t> lock() {
return std::lock_guard {_lock}; return std::scoped_lock {_lock};
} }
/** /**
+2 -18
View File
@@ -77,6 +77,7 @@
#include "platform/common.h" #include "platform/common.h"
#include "process.h" #include "process.h"
#include "src/entry_handler.h" #include "src/entry_handler.h"
#include "string_hash.h"
#include "system_tray.h" #include "system_tray.h"
#include "thread_safe.h" #include "thread_safe.h"
#ifdef _WIN32 #ifdef _WIN32
@@ -118,23 +119,6 @@ namespace system_tray {
return worker; return worker;
} }
/**
* @brief Hash string-like resource paths without allocating temporary strings.
*/
struct transparent_string_hash_t {
using is_transparent = void; ///< Enable heterogeneous unordered-map lookup.
/**
* @brief Hash a string view.
*
* @param value String view to hash.
* @return Hash value for the supplied text.
*/
std::size_t operator()(const std::string_view value) const noexcept {
return std::hash<std::string_view> {}(value);
}
};
#if defined(_WIN32) || defined(__APPLE__) #if defined(_WIN32) || defined(__APPLE__)
/** /**
* @brief Access storage for dynamic Virtual HID Broker license menu labels. * @brief Access storage for dynamic Virtual HID Broker license menu labels.
@@ -629,7 +613,7 @@ namespace system_tray {
#if defined(_WIN32) || defined(__APPLE__) #if defined(_WIN32) || defined(__APPLE__)
// Simple cache ensures our string pointers live forever. // Simple cache ensures our string pointers live forever.
static std::unordered_map<std::string, std::string, transparent_string_hash_t, std::equal_to<>> g_cache; static std::unordered_map<std::string, std::string, util::transparent_string_hash, std::equal_to<>> g_cache;
auto search = g_cache.find(std::string_view {relativePath}); auto search = g_cache.find(std::string_view {relativePath});
if (search != g_cache.end()) { if (search != g_cache.end()) {
return search->second.c_str(); return search->second.c_str();
+9 -9
View File
@@ -154,7 +154,7 @@ namespace task_pool_util {
auto future = task.get_future(); auto future = task.get_future();
std::lock_guard<std::mutex> lg(_task_mutex); std::scoped_lock lg(_task_mutex);
_tasks.emplace_back(toRunnable(std::move(task))); _tasks.emplace_back(toRunnable(std::move(task)));
return future; return future;
@@ -166,7 +166,7 @@ namespace task_pool_util {
* @param task Task object to enqueue or execute. * @param task Task object to enqueue or execute.
*/ */
void pushDelayed(std::pair<__time_point, __task> &&task) { void pushDelayed(std::pair<__time_point, __task> &&task) {
std::lock_guard lg(_task_mutex); std::scoped_lock lg(_task_mutex);
auto it = _timer_tasks.cbegin(); auto it = _timer_tasks.cbegin();
for (; it < _timer_tasks.cend(); ++it) { for (; it < _timer_tasks.cend(); ++it) {
@@ -221,7 +221,7 @@ namespace task_pool_util {
*/ */
template<class X, class Y> template<class X, class Y>
void delay(task_id_t task_id, std::chrono::duration<X, Y> duration) { void delay(task_id_t task_id, std::chrono::duration<X, Y> duration) {
std::lock_guard<std::mutex> lg(_task_mutex); std::scoped_lock lg(_task_mutex);
auto it = _timer_tasks.begin(); auto it = _timer_tasks.begin();
for (; it < _timer_tasks.cend(); ++it) { for (; it < _timer_tasks.cend(); ++it) {
@@ -257,7 +257,7 @@ namespace task_pool_util {
* @return True when the task was found and cancelled. * @return True when the task was found and cancelled.
*/ */
bool cancel(task_id_t task_id) { bool cancel(task_id_t task_id) {
std::lock_guard lg(_task_mutex); std::scoped_lock lg(_task_mutex);
auto it = _timer_tasks.begin(); auto it = _timer_tasks.begin();
for (; it < _timer_tasks.cend(); ++it) { for (; it < _timer_tasks.cend(); ++it) {
@@ -280,9 +280,9 @@ namespace task_pool_util {
* @return Removed queue item, or empty result when the queue is stopped or empty. * @return Removed queue item, or empty result when the queue is stopped or empty.
*/ */
std::optional<std::pair<__time_point, __task>> pop(task_id_t task_id) { std::optional<std::pair<__time_point, __task>> pop(task_id_t task_id) {
std::lock_guard lg(_task_mutex); std::scoped_lock lg(_task_mutex);
auto pos = std::find_if(std::begin(_timer_tasks), std::end(_timer_tasks), [&task_id](const auto &t) { auto pos = std::ranges::find_if(_timer_tasks, [&task_id](const auto &t) {
return t.second.get() == task_id; return t.second.get() == task_id;
}); });
@@ -299,7 +299,7 @@ namespace task_pool_util {
* @return Removed queue item, or empty result when the queue is stopped or empty. * @return Removed queue item, or empty result when the queue is stopped or empty.
*/ */
std::optional<__task> pop() { std::optional<__task> pop() {
std::lock_guard lg(_task_mutex); std::scoped_lock lg(_task_mutex);
if (!_tasks.empty()) { if (!_tasks.empty()) {
__task task = std::move(_tasks.front()); __task task = std::move(_tasks.front());
@@ -322,7 +322,7 @@ namespace task_pool_util {
* @return True when the task was found and cancelled. * @return True when the task was found and cancelled.
*/ */
bool ready() { bool ready() {
std::lock_guard<std::mutex> lg(_task_mutex); std::scoped_lock lg(_task_mutex);
return !_tasks.empty() || (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()); return !_tasks.empty() || (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now());
} }
@@ -333,7 +333,7 @@ namespace task_pool_util {
* @return Time point for the next queued timer task, or std::nullopt when none exist. * @return Time point for the next queued timer task, or std::nullopt when none exist.
*/ */
std::optional<__time_point> next() { std::optional<__time_point> next() {
std::lock_guard<std::mutex> lg(_task_mutex); std::scoped_lock lg(_task_mutex);
if (_timer_tasks.empty()) { if (_timer_tasks.empty()) {
return std::nullopt; return std::nullopt;
+6 -6
View File
@@ -640,7 +640,7 @@ namespace util {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'); return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');
}; };
auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2; auto buf_size = std::ranges::count_if(hex, is_convertable) / 2;
auto padding = sizeof(T) - buf_size; auto padding = sizeof(T) - buf_size;
const char *data = hex.data() + hex.size() - 1; const char *data = hex.data() + hex.size() - 1;
@@ -671,7 +671,7 @@ namespace util {
}); });
if (rev) { if (rev) {
std::reverse(std::begin(buf), std::end(buf)); std::ranges::reverse(buf);
} }
return std::bit_cast<T>(buf); return std::bit_cast<T>(buf);
@@ -695,7 +695,7 @@ namespace util {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'); return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');
}; };
auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2; auto buf_size = std::ranges::count_if(hex, is_convertable) / 2;
buf.resize(buf_size); buf.resize(buf_size);
const char *data = hex.data() + hex.size() - 1; const char *data = hex.data() + hex.size() - 1;
@@ -724,7 +724,7 @@ namespace util {
} }
if (rev) { if (rev) {
std::reverse(std::begin(buf), std::end(buf)); std::ranges::reverse(buf);
} }
return buf; return buf;
@@ -1477,14 +1477,14 @@ namespace util {
} }
/** /**
* @brief Construct an owning contiguous buffer. * @brief Copy the source buffer's logical range into an independent allocation.
* *
* @param o Source object used for comparison or assignment. * @param o Source object used for comparison or assignment.
*/ */
buffer_t(const buffer_t &o): buffer_t(const buffer_t &o):
_els {o._els}, _els {o._els},
_buf(_els) { _buf(_els) {
std::copy(o.begin(), o.end(), begin()); std::ranges::copy(o, begin());
} }
/** /**
+2 -2
View File
@@ -77,12 +77,12 @@ namespace uuid_util {
auto last_slice = hex_view.substr(20, 12); auto last_slice = hex_view.substr(20, 12);
for (auto &slice : slices) { for (auto &slice : slices) {
std::copy(std::begin(slice), std::end(slice), std::back_inserter(result)); std::ranges::copy(slice, std::back_inserter(result));
result.push_back('-'); result.push_back('-');
} }
std::copy(std::begin(last_slice), std::end(last_slice), std::back_inserter(result)); std::ranges::copy(last_slice, std::back_inserter(result));
return result; return result;
} }
+2 -2
View File
@@ -96,7 +96,7 @@ namespace log_checker {
} }
/** /**
* @brief Check if a log file contains a line that contains the given substring. * @brief Check whether any log line contains the substring, including an empty substring.
* @param log_file Path to the log file. * @param log_file Path to the log file.
* @param substr The substring to search for. * @param substr The substring to search for.
* @param case_insensitive Whether the search should be case-insensitive. * @param case_insensitive Whether the search should be case-insensitive.
@@ -122,7 +122,7 @@ namespace log_checker {
// sonarcloud complains about this, but the solution doesn't work for macOS-12 // sonarcloud complains about this, but the solution doesn't work for macOS-12
std::transform(line.begin(), line.end(), line.begin(), ::tolower); std::transform(line.begin(), line.end(), line.begin(), ::tolower);
} }
if (line.find(search_str) != std::string::npos) { if (line.contains(search_str)) {
return true; return true;
} }
} }
@@ -8,6 +8,7 @@
#ifdef _WIN32 #ifdef _WIN32
// standard includes // standard includes
#include <algorithm>
#include <array> #include <array>
#include <bit> #include <bit>
#include <cstdint> #include <cstdint>
@@ -87,6 +88,27 @@ TEST(WindowsCursorTest, StopsAtTheLastPixelInAPartialMaskByte) {
})); }));
} }
TEST(WindowsCursorTest, ProcessesEveryMaskedColorPixelAndPreservesTheInput) {
const std::array<std::uint32_t, 3> pixels {0xFF112233, 0x00123456, 0x7F445566};
const auto bytes = std::bit_cast<std::array<std::uint8_t, 12>>(pixels);
util::buffer_t<std::uint8_t> input {bytes.size()};
std::ranges::copy(bytes, input.begin());
const DXGI_OUTDUPL_POINTER_SHAPE_INFO shape {
.Type = DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR,
.Width = 3,
.Height = 1,
.Pitch = 12,
};
const auto alpha = platf::dxgi::make_cursor_alpha_image(input, shape);
const auto xor_image = platf::dxgi::make_cursor_xor_image(input, shape);
const std::array<std::uint32_t, 3> expected_alpha {0, 0xFF123456, 0x7F445566};
const std::array<std::uint32_t, 3> expected_xor {0xFF112233, 0, 0x7F445566};
EXPECT_TRUE((std::ranges::equal(alpha, std::bit_cast<std::array<std::uint8_t, 12>>(expected_alpha))));
EXPECT_TRUE((std::ranges::equal(xor_image, std::bit_cast<std::array<std::uint8_t, 12>>(expected_xor))));
EXPECT_TRUE(std::ranges::equal(input, bytes));
}
using platf::dxgi::fp16_transfer_e; using platf::dxgi::fp16_transfer_e;
using platf::dxgi::select_fp16_transfer; using platf::dxgi::select_fp16_transfer;
+27
View File
@@ -20,6 +20,20 @@
using namespace std::literals; using namespace std::literals;
TEST(ConfigParsingTest, ParsedEntriesOwnKeysAndSupportStringViewLookup) {
std::string input = "encoder = software\nencoder = nvenc\n";
const auto entries = config::parse_config(input);
input.assign(input.size(), 'x');
const std::string padded = "__encoder__";
const auto key = std::string_view {padded}.substr(2, 7);
const auto found = entries.find(key);
ASSERT_NE(found, entries.end());
EXPECT_EQ(found->second, "software");
EXPECT_FALSE(entries.contains("missing"sv));
EXPECT_EQ(entries.size(), 1);
}
TEST(ConfigDefaultsTest, UsesAutomaticGamepadSelection) { TEST(ConfigDefaultsTest, UsesAutomaticGamepadSelection) {
EXPECT_EQ(config::input.gamepad, "auto"); EXPECT_EQ(config::input.gamepad, "auto");
} }
@@ -28,6 +42,19 @@ TEST(ConfigDefaultsTest, TreatsFp16SdrCapturesAsLinear) {
EXPECT_FALSE(config::video.fp16_sdr_gamma_encoded); EXPECT_FALSE(config::video.fp16_sdr_gamma_encoded);
} }
TEST(ConfigDefaultsTest, PreservesAmdEncoderNumericDefaults) {
EXPECT_EQ(config::video.amd.amd_usage_h264, 1);
EXPECT_EQ(config::video.amd.amd_usage_hevc, 1);
EXPECT_EQ(config::video.amd.amd_usage_av1, 2);
EXPECT_EQ(config::video.amd.amd_rc_h264, 3);
EXPECT_EQ(config::video.amd.amd_rc_hevc, 1);
EXPECT_EQ(config::video.amd.amd_rc_av1, 1);
EXPECT_EQ(config::video.amd.amd_quality_h264, 0);
EXPECT_EQ(config::video.amd.amd_quality_hevc, 5);
EXPECT_EQ(config::video.amd.amd_quality_av1, 70);
EXPECT_EQ(config::video.amd.amd_coder, 0);
}
using NvencPresetNameParam = std::pair<int, std::string_view>; using NvencPresetNameParam = std::pair<int, std::string_view>;
/** /**
+12
View File
@@ -127,6 +127,18 @@ TEST_F(LogRotationTest, ReportsFilesystemErrors) {
EXPECT_EQ(read_log_file(log_path), "current"); EXPECT_EQ(read_log_file(log_path), "current");
} }
TEST_F(LogRotationTest, FindsSubstringsAtBoundariesAndWithCaseFolding) {
write_log_file(log_path, "prefix middle suffix\nMiXeD\n\n");
const auto path = log_path.string();
EXPECT_TRUE(log_checker::line_contains(path, "prefix"));
EXPECT_TRUE(log_checker::line_contains(path, "middle"));
EXPECT_TRUE(log_checker::line_contains(path, "suffix"));
EXPECT_TRUE(log_checker::line_contains(path, ""));
EXPECT_TRUE(log_checker::line_contains(path, "mixed", true));
EXPECT_FALSE(log_checker::line_contains(path, "mixed"));
EXPECT_FALSE(log_checker::line_contains(path, "absent"));
}
struct LogLevelsTest: BaseTest, testing::WithParamInterface<decltype(log_levels)::value_type> {}; struct LogLevelsTest: BaseTest, testing::WithParamInterface<decltype(log_levels)::value_type> {};
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
+12
View File
@@ -9,6 +9,18 @@
// local includes // local includes
#include <src/network.h> #include <src/network.h>
TEST(NetworkClassificationTest, DeducedNetworkListsPreserveAddressScopes) {
EXPECT_EQ(net::from_address("127.0.0.1"), net::PC);
EXPECT_EQ(net::from_address("192.168.1.2"), net::LAN);
EXPECT_EQ(net::from_address("100.64.0.1"), net::LAN);
EXPECT_EQ(net::from_address("169.254.1.2"), net::LAN);
EXPECT_EQ(net::from_address("8.8.8.8"), net::WAN);
EXPECT_EQ(net::from_address("::1"), net::PC);
EXPECT_EQ(net::from_address("fc00::1"), net::LAN);
EXPECT_EQ(net::from_address("fe80::1"), net::LAN);
EXPECT_EQ(net::from_address("2001:4860:4860::8888"), net::WAN);
}
struct MdnsInstanceNameTest: BaseTest, testing::WithParamInterface<std::tuple<std::string, std::string>> {}; struct MdnsInstanceNameTest: BaseTest, testing::WithParamInterface<std::tuple<std::string, std::string>> {};
TEST_P(MdnsInstanceNameTest, Run) { TEST_P(MdnsInstanceNameTest, Run) {
+48
View File
@@ -15,9 +15,57 @@
// local includes // local includes
#include <src/process.h> #include <src/process.h>
#include <src/utility.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace proc {
/**
* @brief Resolve a command's working directory, returning an empty path for URLs.
*
* @param cmd Command line to inspect.
* @param env Environment used for executable lookup.
* @return Working directory, or an empty path when the command is a URL.
*/
boost::filesystem::path find_working_directory(const std::string &cmd, boost::process::v1::environment &env);
/**
* @brief Expand environment references while preserving Windows variable-name casing.
*
* @param env Environment whose variables are expanded.
* @param val_raw Text containing variable references.
* @return Text with environment references expanded.
*/
std::string parse_env_val(boost::process::v1::native_environment &env, const std::string_view &val_raw);
} // namespace proc
#ifdef _WIN32
TEST(ProcessTest, ExpandsEnvironmentNamesWithoutChangingTheirCase) {
auto env = boost::this_process::environment();
const std::string key = "SUNSHINE_RANGE_SEARCH_REGRESSION";
const auto original = env.find(key);
const std::optional<std::string> saved = original == env.end() ? std::nullopt : std::optional {original->to_string()};
const auto restore = util::fail_guard([&env, &key, &saved]() {
if (saved.has_value()) {
env[key] = *saved;
} else {
env[key].clear();
}
});
env[key] = "case-preserved";
EXPECT_EQ(proc::parse_env_val(env, "before-$(sunshine_range_search_regression)-after"), "before-case-preserved-after");
EXPECT_EQ(proc::parse_env_val(env, "$(SUNSHINE_RANGE_SEARCH_REGRESSION)"), "case-preserved");
}
#endif
TEST(ProcessTest, UrlCommandsDoNotResolveAnExecutableWorkingDirectory) {
boost::process::v1::environment env;
EXPECT_TRUE(proc::find_working_directory("https://example.com", env).empty());
EXPECT_TRUE(proc::find_working_directory("steam://run/123", env).empty());
EXPECT_TRUE(proc::find_working_directory("custom+scheme://host/path", env).empty());
}
TEST(ProcessTest, PrepareCommand) { TEST(ProcessTest, PrepareCommand) {
#ifdef SUNSHINE_BUILD_FLATPAK #ifdef SUNSHINE_BUILD_FLATPAK
EXPECT_EQ(proc::prepare_command("steam"), "flatpak-spawn --host steam"); EXPECT_EQ(proc::prepare_command("steam"), "flatpak-spawn --host steam");
+42
View File
@@ -0,0 +1,42 @@
/**
* @file tests/unit/test_string_hash.cpp
* @brief Regression coverage for heterogeneous owning-string containers.
*/
// test includes
#include "../tests_common.h"
// standard includes
#include <string>
#include <string_view>
#include <unordered_map>
// local includes
#include <src/string_hash.h>
TEST(TransparentStringHashTest, EquivalentKeyRepresentationsFindTheSameEntry) {
const std::string key(128, 'k');
const std::string padded = "prefix" + key + "suffix";
const auto view = std::string_view {padded}.substr(6, key.size());
std::unordered_map<std::string, int, util::transparent_string_hash, std::equal_to<>> entries {{key, 42}};
EXPECT_EQ(entries.find(key)->second, 42);
EXPECT_EQ(entries.find(key.c_str())->second, 42);
EXPECT_EQ(entries.find(view)->second, 42);
EXPECT_FALSE(entries.contains(std::string_view {padded}));
EXPECT_EQ(entries.size(), 1);
}
TEST(TransparentStringHashTest, EmptyAndEmbeddedNullKeysRetainTheirLengths) {
using namespace std::literals;
std::unordered_map<std::string, int, util::transparent_string_hash, std::equal_to<>> entries {
{"", 1},
{"key\0suffix"s, 2},
{"key", 3},
};
EXPECT_EQ(entries.find(""sv)->second, 1);
EXPECT_EQ(entries.find("key\0suffix"sv)->second, 2);
EXPECT_EQ(entries.find("key")->second, 3);
EXPECT_FALSE(entries.contains("key\0other"sv));
}
+92
View File
@@ -0,0 +1,92 @@
/**
* @file tests/unit/test_sync.cpp
* @brief Regression coverage for synchronized-value lock lifetimes.
*/
// standard includes
#include <array>
#include <stdexcept>
#include <system_error>
#include <thread>
// lib includes
#include <gtest/gtest.h>
// local includes
#include <src/sync.h>
namespace {
/**
* @brief BasicLockable mutex that detects an unreleased lock without requiring try_lock.
*/
class basic_mutex {
public:
/**
* @brief Acquire the mutex, rejecting recursive locking.
*
* @throws std::system_error If the mutex is already locked.
*/
void lock() {
if (held_) {
throw std::system_error {std::make_error_code(std::errc::resource_deadlock_would_occur)};
}
held_ = true;
}
/**
* @brief Release the mutex.
*/
void unlock() noexcept {
held_ = false;
}
private:
bool held_ {false}; ///< Whether a guard currently owns this mutex.
};
} // namespace
TEST(SyncLockTest, RejectsRecursiveLockingAndReleasesTheOriginalGuard) {
sync_util::sync_t<int, basic_mutex> value {0};
{
const auto guard = value.lock();
EXPECT_THROW(value.lock(), std::system_error);
}
EXPECT_NO_THROW(value.lock());
}
TEST(SyncLockTest, SupportsBasicLockableAndReleasesOnException) {
sync_util::sync_t<int, basic_mutex> value {0};
EXPECT_THROW(
[&value]() {
const auto guard = value.lock();
++*value;
throw std::runtime_error {"unwind the guard"};
}(),
std::runtime_error
);
EXPECT_NO_THROW([&value]() {
const auto guard = value.lock();
EXPECT_EQ(*value, 1);
}());
EXPECT_NO_THROW([&value]() {
const auto guard = value.lock();
EXPECT_EQ(*value, 1);
}());
}
TEST(SyncLockTest, SerializesConcurrentUpdates) {
sync_util::sync_t<int> value {0};
{
std::array<std::jthread, 4> workers;
for (auto &worker : workers) {
worker = std::jthread {[&value]() {
for (int i = 0; i < 250; ++i) {
const auto guard = value.lock();
++*value;
}
}};
}
}
EXPECT_EQ(*value, 1000);
}
+38
View File
@@ -0,0 +1,38 @@
/**
* @file tests/unit/test_task_pool.cpp
* @brief Regression coverage for delayed task lookup over the complete queue.
*/
// lib includes
#include <gtest/gtest.h>
// local includes
#include <src/task_pool.h>
TEST(TaskPoolTest, FindsRequestedDelayedTaskAndRejectsMissingIdentifiers) {
using namespace std::chrono_literals;
task_pool_util::TaskPool pool;
EXPECT_FALSE(pool.pop(nullptr).has_value());
auto first = pool.pushDelayed([]() {
return 11;
},
1h);
auto second = pool.pushDelayed([]() {
return 22;
},
2h);
EXPECT_FALSE(pool.pop(nullptr).has_value());
auto removed = pool.pop(first.task_id);
ASSERT_TRUE(removed.has_value());
ASSERT_NE(removed->second, nullptr);
removed->second->run();
EXPECT_EQ(first.future.get(), 11);
auto other = pool.pop(second.task_id);
ASSERT_TRUE(other.has_value());
ASSERT_NE(other->second, nullptr);
other->second->run();
EXPECT_EQ(second.future.get(), 22);
}
+13
View File
@@ -267,13 +267,26 @@ TEST(UtilityBufferTests, MoveConstruction) {
TEST(UtilityBufferTests, CopyConstruction) { TEST(UtilityBufferTests, CopyConstruction) {
util::buffer_t buf1(3, 55); util::buffer_t buf1(3, 55);
buf1[1] = 66;
buf1[2] = 77;
util::buffer_t<int> buf2(buf1); util::buffer_t<int> buf2(buf1);
EXPECT_EQ(buf2.size(), 3U); EXPECT_EQ(buf2.size(), 3U);
EXPECT_EQ(buf2[0], 55); EXPECT_EQ(buf2[0], 55);
EXPECT_EQ(buf2[1], 66);
EXPECT_EQ(buf2[2], 77);
EXPECT_EQ(buf1.size(), 3U); EXPECT_EQ(buf1.size(), 3U);
EXPECT_EQ(buf1[0], 55); EXPECT_EQ(buf1[0], 55);
} }
TEST(UtilityBufferTests, CopiesAnEmptyLogicalRange) {
util::buffer_t buf(3, 42);
buf.fake_resize(0);
const util::buffer_t<int> copy(buf);
EXPECT_EQ(copy.size(), 0U);
EXPECT_EQ(copy.begin(), copy.end());
EXPECT_EQ(buf[0], 42);
}
TEST(UtilityBufferTests, SizeConstructionValueInitializesEveryElement) { TEST(UtilityBufferTests, SizeConstructionValueInitializesEveryElement) {
const util::buffer_t<int> buf(4); const util::buffer_t<int> buf(4);
EXPECT_EQ(buf.end() - buf.begin(), 4); EXPECT_EQ(buf.end() - buf.begin(), 4);