File indexing completed on 2025-09-17 08:38:19
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_MQTT5_TOPIC_VALIDATION_HPP
0009 #define BOOST_MQTT5_TOPIC_VALIDATION_HPP
0010
0011 #include <boost/mqtt5/detail/utf8_mqtt.hpp>
0012
0013 #include <cstdint>
0014 #include <string_view>
0015
0016 namespace boost::mqtt5::detail {
0017
0018 static constexpr int32_t min_subscription_identifier = 1;
0019 static constexpr int32_t max_subscription_identifier = 268'435'455;
0020
0021 static constexpr std::string_view shared_sub_prefix = "$share/";
0022
0023 inline bool is_utf8_no_wildcard(validation_result result) {
0024 return result == validation_result::valid;
0025 }
0026
0027 inline bool is_not_empty(size_t sz) {
0028 return sz != 0;
0029 }
0030
0031 inline bool is_valid_topic_size(size_t sz) {
0032 return is_not_empty(sz) && is_valid_string_size(sz);
0033 }
0034
0035 inline validation_result validate_topic_name(std::string_view str) {
0036 return validate_impl(str, is_valid_topic_size, is_utf8_no_wildcard);
0037 }
0038
0039 inline validation_result validate_topic_alias_name(std::string_view str) {
0040 return validate_impl(str, is_valid_string_size, is_utf8_no_wildcard);
0041 }
0042
0043 inline validation_result validate_shared_topic_name(std::string_view str) {
0044 return validate_impl(str, is_not_empty, is_utf8_no_wildcard);
0045 }
0046
0047 inline validation_result validate_topic_filter(std::string_view str) {
0048 if (!is_valid_topic_size(str.size()))
0049 return validation_result::invalid;
0050
0051 constexpr int multi_lvl_wildcard = '#';
0052 constexpr int single_lvl_wildcard = '+';
0053
0054
0055
0056 if (str.back() == multi_lvl_wildcard) {
0057 str.remove_suffix(1);
0058
0059 if (!str.empty() && str.back() != '/')
0060 return validation_result::invalid;
0061 }
0062
0063 int last_c = -1;
0064 validation_result result;
0065 while (!str.empty()) {
0066 int c = pop_front_unichar(str);
0067
0068
0069
0070 bool is_valid_single_lvl = (c == single_lvl_wildcard) &&
0071 (str.empty() || str.front() == '/') &&
0072 (last_c == -1 || last_c == '/');
0073
0074 result = validate_mqtt_utf8_char(c);
0075 if (
0076 result == validation_result::valid ||
0077 is_valid_single_lvl
0078 ) {
0079 last_c = c;
0080 continue;
0081 }
0082
0083 return validation_result::invalid;
0084 }
0085
0086 return validation_result::valid;
0087 }
0088
0089 inline validation_result validate_shared_topic_filter(
0090 std::string_view str, bool wildcard_allowed = true
0091 ) {
0092 if (!is_valid_topic_size(str.size()))
0093 return validation_result::invalid;
0094
0095 if (str.compare(0, shared_sub_prefix.size(), shared_sub_prefix) != 0)
0096 return validation_result::invalid;
0097
0098 str.remove_prefix(shared_sub_prefix.size());
0099
0100 size_t share_name_end = str.find_first_of('/');
0101 if (share_name_end == std::string::npos)
0102 return validation_result::invalid;
0103
0104 validation_result result;
0105 result = validate_shared_topic_name(str.substr(0, share_name_end));
0106
0107 if (result != validation_result::valid)
0108 return validation_result::invalid;
0109
0110 auto topic_filter = str.substr(share_name_end + 1);
0111 return wildcard_allowed ?
0112 validate_topic_filter(topic_filter) :
0113 validate_topic_name(topic_filter)
0114 ;
0115 }
0116
0117 }
0118
0119 #endif