Optimize configuration loading: cache JSON

- Moved environment variable parsing to a dedicated loadConfig() function, called only once during initialization of the global g_jsonConfig.
- Updated GetJson() to return the cached JSON object, preventing repetitive parsing of environment variables on each access.
- Improved performance and simplified configuration data access.

Replace <stdio.h> with <cstdlib> and <cstdio>

- Updated include directives to use modern C++ headers.
- The new headers place standard library functions in the std namespace, enhancing compatibility and type safety as recommended by modern C++ standards.
This commit is contained in:
Serhii Maltsev 2025-02-28 13:35:11 +01:00
parent cc852b424a
commit f42604c527

View file

@ -4,7 +4,6 @@ Written by daijro.
*/ */
#pragma once #pragma once
#include "json.hpp" #include "json.hpp"
#include <memory> #include <memory>
#include <string> #include <string>
@ -12,10 +11,12 @@ Written by daijro.
#include <optional> #include <optional>
#include <codecvt> #include <codecvt>
#include "mozilla/glue/Debug.h" #include "mozilla/glue/Debug.h"
#include <stdlib.h> #include <cstdlib>
#include <stdio.h> #include <cstdio>
#include <variant> #include <variant>
#include <cstddef> #include <cstddef>
#include <vector>
#include <algorithm>
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
@ -23,6 +24,7 @@ Written by daijro.
namespace MaskConfig { namespace MaskConfig {
// Function to get the value of an environment variable as a UTF-8 string.
inline std::optional<std::string> get_env_utf8(const std::string& name) { inline std::optional<std::string> get_env_utf8(const std::string& name) {
#ifdef _WIN32 #ifdef _WIN32
std::wstring wName(name.begin(), name.end()); std::wstring wName(name.begin(), name.end());
@ -37,65 +39,72 @@ inline std::optional<std::string> get_env_utf8(const std::string& name) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(wValue); return converter.to_bytes(wValue);
#else #else
const char* value = getenv(name.c_str()); const char* value = std::getenv(name.c_str());
if (!value) return std::nullopt; if (!value) return std::nullopt;
return std::string(value); return std::string(value);
#endif #endif
} }
inline const nlohmann::json& GetJson() { // Helper function to load and parse configuration from environment variables.
static const nlohmann::json jsonConfig = []() { namespace {
nlohmann::json loadConfig() {
std::string jsonString; std::string jsonString;
int index = 1; int index = 1;
// Read environment variables CAMOU_CONFIG_1, CAMOU_CONFIG_2, ...
while (true) { while (true) {
std::string envName = "CAMOU_CONFIG_" + std::to_string(index); std::string envName = "CAMOU_CONFIG_" + std::to_string(index);
auto partialConfig = get_env_utf8(envName); auto partialConfig = get_env_utf8(envName);
if (!partialConfig) break; if (!partialConfig)
break;
jsonString += *partialConfig; jsonString += *partialConfig;
index++; index++;
} }
// If the concatenated string is empty, try reading the original CAMOU_CONFIG variable.
if (jsonString.empty()) { if (jsonString.empty()) {
// Check for the original CAMOU_CONFIG as fallback
auto originalConfig = get_env_utf8("CAMOU_CONFIG"); auto originalConfig = get_env_utf8("CAMOU_CONFIG");
if (originalConfig) jsonString = *originalConfig; if (originalConfig)
jsonString = *originalConfig;
} }
if (jsonString.empty()) return nlohmann::json{}; // If no configuration is provided, return an empty JSON.
if (jsonString.empty()) {
return nlohmann::json{};
}
// Validate // Validate JSON correctness.
if (!nlohmann::json::accept(jsonString)) { if (!nlohmann::json::accept(jsonString)) {
printf_stderr("ERROR: Invalid JSON passed to CAMOU_CONFIG!\n"); printf_stderr("ERROR: Invalid JSON passed to CAMOU_CONFIG!\n");
return nlohmann::json{}; return nlohmann::json{};
} }
nlohmann::json result = nlohmann::json::parse(jsonString); return nlohmann::json::parse(jsonString);
return result; }
}(); } // namespace
return jsonConfig;
// Global configuration cache, initialized only once.
static const nlohmann::json g_jsonConfig = loadConfig();
// Function returns a reference to the already parsed JSON.
inline const nlohmann::json& GetJson() {
return g_jsonConfig;
} }
inline bool HasKey(const std::string& key, nlohmann::json& data) { inline bool HasKey(const std::string& key, const nlohmann::json& data) {
// printf_stderr("Property: %s\n", key.c_str());
// printf_stderr("WARNING: Key not found: %s\n", key.c_str());
return data.contains(key); return data.contains(key);
} }
inline std::optional<std::string> GetString(const std::string& key) { inline std::optional<std::string> GetString(const std::string& key) {
// printf_stderr("GetString: %s\n", key.c_str()); const auto& data = GetJson();
auto data = GetJson();
if (!HasKey(key, data)) return std::nullopt; if (!HasKey(key, data)) return std::nullopt;
return std::make_optional(data[key].get<std::string>()); return data[key].get<std::string>();
} }
inline std::vector<std::string> GetStringList(const std::string& key) { inline std::vector<std::string> GetStringList(const std::string& key) {
std::vector<std::string> result; std::vector<std::string> result;
const auto& data = GetJson();
auto data = GetJson();
if (!HasKey(key, data)) return {}; if (!HasKey(key, data)) return {};
// Build vector
for (const auto& item : data[key]) { for (const auto& item : data[key]) {
result.push_back(item.get<std::string>()); result.push_back(item.get<std::string>());
} }
@ -113,12 +122,11 @@ inline std::vector<std::string> GetStringListLower(const std::string& key) {
template <typename T> template <typename T>
inline std::optional<T> GetUintImpl(const std::string& key) { inline std::optional<T> GetUintImpl(const std::string& key) {
auto data = GetJson(); const auto& data = GetJson();
if (!HasKey(key, data)) return std::nullopt; if (!HasKey(key, data)) return std::nullopt;
if (data[key].is_number_unsigned()) if (data[key].is_number_unsigned())
return std::make_optional(data[key].get<T>()); return data[key].get<T>();
printf_stderr("ERROR: Value for key '%s' is not an unsigned integer\n", printf_stderr("ERROR: Value for key '%s' is not an unsigned integer\n", key.c_str());
key.c_str());
return std::nullopt; return std::nullopt;
} }
@ -131,29 +139,29 @@ inline std::optional<uint32_t> GetUint32(const std::string& key) {
} }
inline std::optional<int32_t> GetInt32(const std::string& key) { inline std::optional<int32_t> GetInt32(const std::string& key) {
auto data = GetJson(); const auto& data = GetJson();
if (!HasKey(key, data)) return std::nullopt; if (!HasKey(key, data)) return std::nullopt;
if (data[key].is_number_integer()) if (data[key].is_number_integer())
return std::make_optional(data[key].get<int32_t>()); return data[key].get<int32_t>();
printf_stderr("ERROR: Value for key '%s' is not an integer\n", key.c_str()); printf_stderr("ERROR: Value for key '%s' is not an integer\n", key.c_str());
return std::nullopt; return std::nullopt;
} }
inline std::optional<double> GetDouble(const std::string& key) { inline std::optional<double> GetDouble(const std::string& key) {
auto data = GetJson(); const auto& data = GetJson();
if (!HasKey(key, data)) return std::nullopt; if (!HasKey(key, data)) return std::nullopt;
if (data[key].is_number_float()) if (data[key].is_number_float())
return std::make_optional(data[key].get<double>()); return data[key].get<double>();
if (data[key].is_number_unsigned() || data[key].is_number_integer()) if (data[key].is_number_unsigned() || data[key].is_number_integer())
return std::make_optional(static_cast<double>(data[key].get<int64_t>())); return static_cast<double>(data[key].get<int64_t>());
printf_stderr("ERROR: Value for key '%s' is not a double\n", key.c_str()); printf_stderr("ERROR: Value for key '%s' is not a double\n", key.c_str());
return std::nullopt; return std::nullopt;
} }
inline std::optional<bool> GetBool(const std::string& key) { inline std::optional<bool> GetBool(const std::string& key) {
auto data = GetJson(); const auto& data = GetJson();
if (!HasKey(key, data)) return std::nullopt; if (!HasKey(key, data)) return std::nullopt;
if (data[key].is_boolean()) return std::make_optional(data[key].get<bool>()); if (data[key].is_boolean()) return data[key].get<bool>();
printf_stderr("ERROR: Value for key '%s' is not a boolean\n", key.c_str()); printf_stderr("ERROR: Value for key '%s' is not a boolean\n", key.c_str());
return std::nullopt; return std::nullopt;
} }
@ -162,25 +170,24 @@ inline bool CheckBool(const std::string& key) {
return GetBool(key).value_or(false); return GetBool(key).value_or(false);
} }
inline std::optional<std::array<uint32_t, 4>> GetRect( inline std::optional<std::array<uint32_t, 4>> GetRect(const std::string& left,
const std::string& left, const std::string& top, const std::string& width, const std::string& top,
const std::string& width,
const std::string& height) { const std::string& height) {
// Make top and left default to 0
std::array<std::optional<uint32_t>, 4> values = { std::array<std::optional<uint32_t>, 4> values = {
GetUint32(left).value_or(0), GetUint32(top).value_or(0), GetUint32(width), GetUint32(left).value_or(0),
GetUint32(height)}; GetUint32(top).value_or(0),
GetUint32(width),
GetUint32(height)
};
// If height or width is std::nullopt, return std::nullopt
if (!values[2].has_value() || !values[3].has_value()) { if (!values[2].has_value() || !values[3].has_value()) {
if (values[2].has_value() ^ values[3].has_value()) if (values[2].has_value() ^ values[3].has_value())
printf_stderr( printf_stderr("Both %s and %s must be provided. Using default behavior.\n",
"Both %s and %s must be provided. Using default "
"behavior.\n",
height.c_str(), width.c_str()); height.c_str(), width.c_str());
return std::nullopt; return std::nullopt;
} }
// Convert std::optional<uint32_t> to uint32_t
std::array<uint32_t, 4> result; std::array<uint32_t, 4> result;
std::transform(values.begin(), values.end(), result.begin(), std::transform(values.begin(), values.end(), result.begin(),
[](const auto& value) { return value.value(); }); [](const auto& value) { return value.value(); });
@ -188,10 +195,10 @@ inline std::optional<std::array<uint32_t, 4>> GetRect(
return result; return result;
} }
inline std::optional<std::array<int32_t, 4>> GetInt32Rect( inline std::optional<std::array<int32_t, 4>> GetInt32Rect(const std::string& left,
const std::string& left, const std::string& top, const std::string& width, const std::string& top,
const std::string& width,
const std::string& height) { const std::string& height) {
// Calls GetRect but casts to int32_t
if (auto optValue = GetRect(left, top, width, height)) { if (auto optValue = GetRect(left, top, width, height)) {
std::array<int32_t, 4> result; std::array<int32_t, 4> result;
std::transform(optValue->begin(), optValue->end(), result.begin(), std::transform(optValue->begin(), optValue->end(), result.begin(),