Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #pragma once
18 : :
19 : : #include <algorithm>
20 : : #include <cctype>
21 : : #include <cstdint>
22 : : #include <regex>
23 : : #include <string>
24 : : #include <string_view>
25 : : #include <vector>
26 : :
27 : : #include "wirestead/base/constants.hpp"
28 : : #include "wirestead/base/visibility.hpp"
29 : : #include "wirestead/diagnostics/exceptions.hpp"
30 : :
31 : : namespace wirestead {
32 : : namespace util {
33 : :
34 : : /**
35 : : * @brief Input validation utility class
36 : : *
37 : : * Provides comprehensive input validation for all wirestead components.
38 : : * Throws ValidationException for invalid inputs with detailed error messages.
39 : : */
40 : : class WIRESTEAD_API InputValidator {
41 : : public:
42 : : // Network validation
43 : : static void validate_host(const std::string& host);
44 : : static void validate_port(uint16_t port);
45 : : static void validate_ipv4_address(const std::string& address);
46 : : static void validate_ipv6_address(const std::string& address);
47 : : static void validate_uds_path(const std::string& path);
48 : :
49 : : // Serial validation
50 : : static void validate_device_path(const std::string& device);
51 : : static void validate_baud_rate(uint32_t baud_rate);
52 : : static void validate_data_bits(uint8_t data_bits);
53 : : static void validate_stop_bits(uint8_t stop_bits);
54 : : static void validate_parity(const std::string& parity);
55 : :
56 : : // Memory validation
57 : : static void validate_buffer_size(size_t size);
58 : : static void validate_memory_alignment(const void* ptr, size_t alignment);
59 : :
60 : : // Timeout and interval validation
61 : : static void validate_timeout(unsigned timeout_ms);
62 : : static void validate_retry_interval(unsigned interval_ms);
63 : : static void validate_retry_count(int retry_count);
64 : :
65 : : // String validation
66 : : static void validate_non_empty_string(const std::string& str, const std::string& field_name);
67 : : static void validate_string_length(const std::string& str, size_t max_length, const std::string& field_name);
68 : :
69 : : // Numeric validation
70 : : static void validate_positive_number(int64_t value, const std::string& field_name);
71 : : static void validate_range(int64_t value, int64_t min, int64_t max, const std::string& field_name);
72 : : static void validate_range(size_t value, size_t min, size_t max, const std::string& field_name);
73 : :
74 : : // Validation helpers (exposed for public use)
75 : : static bool is_valid_host(const std::string& host);
76 : : static bool is_valid_ipv4(std::string_view address);
77 : : static bool is_valid_ipv6(const std::string& address);
78 : : static bool is_valid_hostname(std::string_view hostname);
79 : : static bool is_valid_uds_path(const std::string& path);
80 : : static bool is_valid_device_path(const std::string& device);
81 : :
82 : : private:
83 : : // Constants for retry count
84 : : static constexpr int FINITE_MIN_RETRY_COUNT = 0;
85 : : static constexpr int FINITE_MAX_RETRY_COUNT = base::constants::MAX_RETRIES_LIMIT;
86 : : };
87 : :
88 : : // Inline implementations for simple validations
89 : 100 : inline void InputValidator::validate_non_empty_string(const std::string& str, const std::string& field_name) {
90 [ + + ]: 100 : if (str.empty()) {
91 : 18 : throw diagnostics::ValidationException(field_name + " cannot be empty", field_name, "non-empty string");
92 : : }
93 : 94 : }
94 : :
95 : 47 : inline void InputValidator::validate_string_length(const std::string& str, size_t max_length,
96 : : const std::string& field_name) {
97 [ + + ]: 47 : if (str.length() > max_length) {
98 : 6 : throw diagnostics::ValidationException(field_name + " length exceeds maximum allowed length", field_name,
99 : 9 : "length <= " + std::to_string(max_length));
100 : : }
101 : 44 : }
102 : :
103 : 6 : inline void InputValidator::validate_positive_number(int64_t value, const std::string& field_name) {
104 [ + + ]: 6 : if (value <= 0) {
105 : 12 : throw diagnostics::ValidationException(field_name + " must be positive", field_name, "positive number");
106 : : }
107 : 2 : }
108 : :
109 : 28 : inline void InputValidator::validate_range(int64_t value, int64_t min, int64_t max, const std::string& field_name) {
110 [ + + + + ]: 28 : if (value < min || value > max) {
111 : 28 : throw diagnostics::ValidationException(field_name + " out of range", field_name,
112 : 42 : std::to_string(min) + " <= value <= " + std::to_string(max));
113 : : }
114 : 14 : }
115 : :
116 : 8 : inline void InputValidator::validate_range(size_t value, size_t min, size_t max, const std::string& field_name) {
117 [ + + + + ]: 8 : if (value < min || value > max) {
118 : 6 : throw diagnostics::ValidationException(field_name + " out of range", field_name,
119 : 9 : std::to_string(min) + " <= value <= " + std::to_string(max));
120 : : }
121 : 5 : }
122 : :
123 : 4 : inline void InputValidator::validate_buffer_size(size_t size) {
124 : 6 : validate_range(size, static_cast<size_t>(base::constants::MIN_BUFFER_SIZE),
125 : : static_cast<size_t>(base::constants::MAX_BUFFER_SIZE), "buffer_size");
126 : 2 : }
127 : :
128 : 4 : inline void InputValidator::validate_timeout(unsigned timeout_ms) {
129 : 6 : validate_range(static_cast<int64_t>(timeout_ms), static_cast<int64_t>(base::constants::MIN_CONNECTION_TIMEOUT_MS),
130 : : static_cast<int64_t>(base::constants::MAX_CONNECTION_TIMEOUT_MS), "timeout_ms");
131 : 2 : }
132 : :
133 : 4 : inline void InputValidator::validate_retry_interval(unsigned interval_ms) {
134 : 6 : validate_range(static_cast<int64_t>(interval_ms), static_cast<int64_t>(base::constants::MIN_RETRY_INTERVAL_MS),
135 : : static_cast<int64_t>(base::constants::MAX_RETRY_INTERVAL_MS), "retry_interval_ms");
136 : 2 : }
137 : :
138 : 9 : inline void InputValidator::validate_retry_count(int retry_count) {
139 [ + + ]: 9 : if (retry_count == base::constants::DEFAULT_MAX_RETRIES) { // -1 means infinite, which is valid
140 : 2 : return;
141 : : }
142 [ + + + + ]: 7 : if (retry_count < FINITE_MIN_RETRY_COUNT || retry_count > FINITE_MAX_RETRY_COUNT) {
143 : : throw diagnostics::ValidationException("retry_count out of range", "retry_count",
144 : 6 : std::to_string(FINITE_MIN_RETRY_COUNT) + " <= retry_count <= " +
145 : 21 : std::to_string(FINITE_MAX_RETRY_COUNT) + " or -1 for infinite");
146 : : }
147 : : }
148 : :
149 : 11 : inline void InputValidator::validate_port(uint16_t port) {
150 [ + + ]: 11 : if (port == 0) {
151 : 28 : throw diagnostics::ValidationException("port cannot be zero", "port", "non-zero port number");
152 : : }
153 : : // Port numbers are already constrained by uint16_t type (0-65535)
154 : 7 : }
155 : :
156 : 4 : inline void InputValidator::validate_baud_rate(uint32_t baud_rate) {
157 : 6 : validate_range(static_cast<int64_t>(baud_rate), static_cast<int64_t>(base::constants::MIN_BAUD_RATE),
158 : : static_cast<int64_t>(base::constants::MAX_BAUD_RATE), "baud_rate");
159 : 2 : }
160 : :
161 : 4 : inline void InputValidator::validate_data_bits(uint8_t data_bits) {
162 : 6 : validate_range(static_cast<int64_t>(data_bits), static_cast<int64_t>(base::constants::MIN_DATA_BITS),
163 : : static_cast<int64_t>(base::constants::MAX_DATA_BITS), "data_bits");
164 : 2 : }
165 : :
166 : 4 : inline void InputValidator::validate_stop_bits(uint8_t stop_bits) {
167 : 6 : validate_range(static_cast<int64_t>(stop_bits), static_cast<int64_t>(base::constants::MIN_STOP_BITS),
168 : : static_cast<int64_t>(base::constants::MAX_STOP_BITS), "stop_bits");
169 : 2 : }
170 : :
171 : 3 : inline void InputValidator::validate_memory_alignment(const void* ptr, size_t alignment) {
172 [ + + ]: 3 : if (ptr == nullptr) {
173 : 7 : throw diagnostics::ValidationException("memory pointer cannot be null", "ptr", "non-null pointer");
174 : : }
175 : :
176 : 2 : uintptr_t address = reinterpret_cast<uintptr_t>(ptr);
177 [ + + ]: 2 : if (address % alignment != 0) {
178 : : throw diagnostics::ValidationException("memory pointer not properly aligned", "ptr",
179 : 5 : "aligned to " + std::to_string(alignment) + " bytes");
180 : : }
181 : 1 : }
182 : :
183 : : } // namespace util
184 : : } // namespace wirestead
|