diff --git a/doc/qbk/reference/properties/auth_props.qbk b/doc/qbk/reference/properties/auth_props.qbk index 72cc8c1..df0e335 100644 --- a/doc/qbk/reference/properties/auth_props.qbk +++ b/doc/qbk/reference/properties/auth_props.qbk @@ -10,7 +10,7 @@ This section lists all possible __AUTH__ Properties and describes their usage: [[authentication_method] [`std::string`] [A UTF-8 Encoded String containing the name of the authentication method used for extended authentication.]] [[authentication_data] [`std::string`] [Binary Data containing authentication data. The contents of the data are defined by the authentication method.]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] ] @@ -19,6 +19,9 @@ After obtaining an instance of `async_mqtt5::auth_props`, the subscript operator The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -27,17 +30,21 @@ The following example shows how to set a Property value: async_mqtt5::auth_props props; props[async_mqtt5::prop::authentication_method] = "SCRAM-SHA-1"; props[async_mqtt5::prop::authentication_data] = "data"; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional auth_data = props[async_mqtt5::prop::authentication_data]; if (auth_data.has_value()) // authentication data property was previously set else // authentication data property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/connack_props.qbk b/doc/qbk/reference/properties/connack_props.qbk index 876f191..6c63801 100644 --- a/doc/qbk/reference/properties/connack_props.qbk +++ b/doc/qbk/reference/properties/connack_props.qbk @@ -15,7 +15,7 @@ This section lists all possible __CONNACK__ Properties and describes their usage [[assigned_client_identifier] [`std::string`] [The Client Identifier which was assigned by the Server because a zero length Client Identifier was found in the __CONNECT__ packet]] [[topic_alias_maximum] [`uint16_t`] [The highest value that the Server will accept as a Topic Alias sent by the Client.]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. The meaning of these properties is not defined by the specification.]] [[wildcard_subscription_available] [`uint8_t`] [A value of 0 means that Wildcard Subscriptions are not supported. A value of 1 means they are supported. If not present, they are supported.]] @@ -35,6 +35,9 @@ After obtaining an instance of `async_mqtt5::connack_props`, the subscript opera The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -43,17 +46,21 @@ The following example shows how to set a Property value: async_mqtt5::connack_props props; props[async_mqtt5::prop::maximum_packet_size] = 65535; props[async_mqtt5::prop::assigned_client_identifier] = "ClientID"; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional auth_method = props[async_mqtt5::prop::authentication_method]; if (auth_method.has_value()) // authentication method property was previously set else // authentication method property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/connect_props.qbk b/doc/qbk/reference/properties/connect_props.qbk index 0b4c5d0..f750c24 100644 --- a/doc/qbk/reference/properties/connect_props.qbk +++ b/doc/qbk/reference/properties/connect_props.qbk @@ -15,7 +15,7 @@ This section lists all possible __CONNECT__ Properties and describes their usage [[request_problem_information] [`uint8_t`] [The value of 0 signals that the Server MAY return a Reason String or User Properties on a __CONNACK__ or __DISCONNECT__ packet, but MUST NOT send them on any packet other than __PUBLISH__, __CONNACK__, or __DISCONNECT__. If the value is 1, the Server MAY return a Reason String or User Properties where it is allowed.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. The meaning of these properties is not defined by the specification.]] [[authentication_method] [`std::string`] [A UTF-8 Encoded String containing the name of the authentication method used for extended authentication.]] [[authentication_data] [`std::string`] [Binary Data containing authentication data. The contents of the data are defined by the authentication method.]] @@ -26,6 +26,9 @@ After obtaining an instance of `async_mqtt5::connect_props`, the subscript opera The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -34,17 +37,21 @@ The following example shows how to set a Property value: async_mqtt5::connect_props props; props[async_mqtt5::prop::session_expiry_interval] = 1200; props[async_mqtt5::prop::receive_maximum] = uint16_t(100); + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional auth_method = props[async_mqtt5::prop::authentication_method]; if (auth_method.has_value()) // authentication method property was previously set else // authentication method property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/disconnect_props.qbk b/doc/qbk/reference/properties/disconnect_props.qbk index 2b36a63..6d93ac4 100644 --- a/doc/qbk/reference/properties/disconnect_props.qbk +++ b/doc/qbk/reference/properties/disconnect_props.qbk @@ -9,7 +9,7 @@ This section lists all possible __DISCONNECT__ Properties and describes their us [[Identifier] [Value type] [Description]] [[session_expiry_interval] [`uint32_t`] [Represents the Session Expiry Internal in seconds. Can only be sent by the Client.]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] [[server_reference] [`std::string`] [A UTF-8 Encoded String which can be used by the Client to identfy another Server to use.]] ] @@ -19,6 +19,9 @@ After obtaining an instance of `async_mqtt5::disconnect_props`, the subscript op The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -26,17 +29,21 @@ The following example shows how to set a Property value: [!c++] async_mqtt5::disconnect_props props; props[async_mqtt5::prop::reason_string] = "Lost connection!"; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional reason_string = props[async_mqtt5::prop::reason_string]; if (reason_string.has_value()) // reason string property was previously set else // reason string property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/puback_props.qbk b/doc/qbk/reference/properties/puback_props.qbk index 869c44a..55d5a74 100644 --- a/doc/qbk/reference/properties/puback_props.qbk +++ b/doc/qbk/reference/properties/puback_props.qbk @@ -8,7 +8,7 @@ This section lists all possible __PUBACK__ Properties and describes their usage: [table:puback_props PUBACK properties [[Identifier] [Value type] [Description]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] ] @@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::puback_props`, the subscript operat The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -24,17 +27,21 @@ The following example shows how to set a Property value: [!c++] async_mqtt5::puback_props props; props[async_mqtt5::prop::reason_string] = "Some reason..."; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional reason_string = props[async_mqtt5::prop::reason_string]; if (reason_string.has_value()) // reason string property was previously set else // reason string property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/pubcomp_props.qbk b/doc/qbk/reference/properties/pubcomp_props.qbk index d8fc046..372ab6f 100644 --- a/doc/qbk/reference/properties/pubcomp_props.qbk +++ b/doc/qbk/reference/properties/pubcomp_props.qbk @@ -8,7 +8,7 @@ This section lists all possible __PUBCOMP__ Properties and describes their usage [table:pubcomp_props PUBCOMP properties [[Identifier] [Value type] [Description]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] ] @@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::pubcomp_props`, the subscript opera The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -24,17 +27,21 @@ The following example shows how to set a Property value: [!c++] async_mqtt5::pubcomp_props props; props[async_mqtt5::prop::reason_string] = "Some reason..."; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional reason_string = props[async_mqtt5::prop::reason_string]; if (reason_string.has_value()) // reason string property was previously set else // reason string property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/publish_props.qbk b/doc/qbk/reference/properties/publish_props.qbk index ae19750..a385aff 100644 --- a/doc/qbk/reference/properties/publish_props.qbk +++ b/doc/qbk/reference/properties/publish_props.qbk @@ -12,9 +12,9 @@ This section lists all possible __PUBLISH__ Properties and describes their usage [[topic_alias] [`uint16_t`] [Two Byte integer representing the Topic Alias, an integer value that is used to identify the Topic instead of using the Topic Name.]] [[response_topic] [`std::string`] [A UTF-8 Encoded String which is used as the Topic Name for a response message.]] [[correlation_data] [`std::string`] [Binary Data used by the sender of the Request Message to identify which request the Response Message is for when it is received.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. The meaning of these properties is not defined by the specification.]] - [[subscription_identifier] [`int32_t`] [Identifier of the Subscription in range of 1 to 268,435,455.]] + [[subscription_identifier] [`int32_t`] [Identifier of the matching subscription. If there are multiple matching subscriptions, multiple identifiers may be included.]] [[content_type] [`std::string`] [A UTF-8 Encoded String describing the content of the Application Message.]] ] @@ -23,6 +23,11 @@ After obtaining an instance of `async_mqtt5::publish_props`, the subscript opera The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property` and `async_mqtt5::prop::subscription_identifier`, where it will return an instance of +`std::vector>` and `async_mqtt5::prop::subscription_identifiers` respectively. +`async_mqtt5::prop::subscription_identifiers` has the interface of `boost::container::small_vector`.] + [h4 Example] The following example shows how to set a Property value: @@ -32,17 +37,21 @@ The following example shows how to set a Property value: props[async_mqtt5::prop::payload_format_indicator] = uint8_t(1); props[async_mqtt5::prop::topic_alias] = uint16_t(12); props[async_mqtt5::prop::response_topic] = "response_topic"; + props[async_mqtt5::prop::subscription_identifier].push_back(40); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional topic_alias = props[async_mqtt5::prop::topic_alias]; if (topic_alias.has_value()) // topic alias property was previously set else // topic alias property was not set + + async_mqtt5::prop::subscription_identifiers& sub_ids = props[async_mqtt5::prop::subscription_identifier]; + if (!sub_ids.empty()) + // subscription identifier property was previously set + else + // subscription identifier property was not set [endsect] diff --git a/doc/qbk/reference/properties/pubrec_props.qbk b/doc/qbk/reference/properties/pubrec_props.qbk index 60b3881..5820512 100644 --- a/doc/qbk/reference/properties/pubrec_props.qbk +++ b/doc/qbk/reference/properties/pubrec_props.qbk @@ -8,7 +8,7 @@ This section lists all possible __PUBREC__ Properties and describes their usage: [table:pubrec_props PUBREC properties [[Identifier] [Value type] [Description]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] ] @@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::pubrec_props`, the subscript operat The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -24,17 +27,21 @@ The following example shows how to set a Property value: [!c++] async_mqtt5::pubrec_props props; props[async_mqtt5::prop::reason_string] = "Some reason..."; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional reason_string = props[async_mqtt5::prop::reason_string]; if (reason_string.has_value()) // reason string property was previously set else // reason string property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/pubrel_props.qbk b/doc/qbk/reference/properties/pubrel_props.qbk index 668e370..4ab73c5 100644 --- a/doc/qbk/reference/properties/pubrel_props.qbk +++ b/doc/qbk/reference/properties/pubrel_props.qbk @@ -8,7 +8,7 @@ This section lists all possible __PUBREL__ Properties and describes their usage: [table:pubrel_props PUBREL properties [[Identifier] [Value type] [Description]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] ] @@ -17,23 +17,30 @@ After obtaining an instance of `async_mqtt5::pubrel_props`, the subscript operat The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: [!c++] async_mqtt5::pubrel_props props; props[async_mqtt5::prop::reason_string] = "Some reason..."; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional reason_string = props[async_mqtt5::prop::reason_string]; if (reason_string.has_value()) // reason string property was previously set else // reason string property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/suback_props.qbk b/doc/qbk/reference/properties/suback_props.qbk index 3cf785a..56b1cb7 100644 --- a/doc/qbk/reference/properties/suback_props.qbk +++ b/doc/qbk/reference/properties/suback_props.qbk @@ -8,7 +8,7 @@ This section lists all possible __SUBACK__ Properties and describes their usage: [table:suback_props SUBACK properties [[Identifier] [Value type] [Description]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] ] @@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::suback_props`, the subscript operat The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -24,17 +27,21 @@ The following example shows how to set a Property value: [!c++] async_mqtt5::suback_props props; props[async_mqtt5::prop::reason_string] = "Some reason..."; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional reason_string = props[async_mqtt5::prop::reason_string]; if (reason_string.has_value()) // reason string property was previously set else // reason string property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/subscribe_props.qbk b/doc/qbk/reference/properties/subscribe_props.qbk index fd00286..3f8bdc4 100644 --- a/doc/qbk/reference/properties/subscribe_props.qbk +++ b/doc/qbk/reference/properties/subscribe_props.qbk @@ -8,7 +8,7 @@ This section lists all possible __SUBSCRIBE__ Properties and describes their usa [table:subscribe_props SUBSCRIBE properties [[Identifier] [Value type] [Description]] [[subscription_identifier] [`int32_t`] [Identifier of the Subscription in range of 1 to 268,435,455.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property can be used to send subscription related properties from the Client to the Server. The meaning of these properties is not defined by the specification ]] ] @@ -18,6 +18,11 @@ After obtaining an instance of `async_mqtt5::subscribe_props`, the subscript ope The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property` and `async_mqtt5::prop::subscription_identifier`, where it will return an instance of +`std::vector>` and `async_mqtt5::prop::subscription_identifiers` respectively. +`async_mqtt5::prop::subscription_identifiers` has the interface of `std::optional`.] + [h4 Example] The following example shows how to set a Property value: @@ -25,17 +30,21 @@ The following example shows how to set a Property value: [!c++] async_mqtt5::subscribe_props props; props[async_mqtt5::prop::subscription_identifier] = 1234; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] - std::optional sub_id = props[async_mqtt5::prop::subscription_identifier]; + async_mqtt5::prop::subscription_identifiers sub_id = props[async_mqtt5::prop::subscription_identifier]; if (sub_id.has_value()) // subscription identifier property was previously set else // subscription identifier property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/unsuback_props.qbk b/doc/qbk/reference/properties/unsuback_props.qbk index eced5d3..332a8aa 100644 --- a/doc/qbk/reference/properties/unsuback_props.qbk +++ b/doc/qbk/reference/properties/unsuback_props.qbk @@ -8,7 +8,7 @@ This section lists all possible __UNSUBACK__ Properties and describes their usag [table:unsuback_props UNSUBACK properties [[Identifier] [Value type] [Description]] [[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property may be used to provide additional diagnostic or other information. ]] ] @@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::unsuback_props`, the subscript oper The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -24,17 +27,21 @@ The following example shows how to set a Property value: [!c++] async_mqtt5::unsuback_props props; props[async_mqtt5::prop::reason_string] = "Some reason..."; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional reason_string = props[async_mqtt5::prop::reason_string]; if (reason_string.has_value()) // reason string property was previously set else // reason string property was not set + + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/unsubscribe_props.qbk b/doc/qbk/reference/properties/unsubscribe_props.qbk index 3e7d9c1..8685366 100644 --- a/doc/qbk/reference/properties/unsubscribe_props.qbk +++ b/doc/qbk/reference/properties/unsubscribe_props.qbk @@ -7,7 +7,7 @@ This section lists all possible __UNSUBSCRIBE__ Properties and describes their u [table:unsubscribe_props UNSUBSCRIBE properties [[Identifier] [Value type] [Description]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. This property can be used to send subscription related properties from the Client to the Server. The meaning of these properties is not defined by the specification ]] ] @@ -17,22 +17,25 @@ After obtaining an instance of `async_mqtt5::unsubscribe_props`, the subscript o The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: [!c++] async_mqtt5::unsubscribe_props props; - props[async_mqtt5::prop::user_property].push_back("key"); - props[async_mqtt5::prop::user_property].push_back("value"); + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] - std::vector user_props = props[async_mqtt5::prop::user_property]; + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set [endsect] diff --git a/doc/qbk/reference/properties/will_props.qbk b/doc/qbk/reference/properties/will_props.qbk index b71ea05..ec244b1 100644 --- a/doc/qbk/reference/properties/will_props.qbk +++ b/doc/qbk/reference/properties/will_props.qbk @@ -14,7 +14,7 @@ This section lists all possible [reflink2 will Will] Properties and describes th [[content_type] [`std::string`] [A UTF-8 Encoded String describing the content of the Will Message.]] [[response_topic] [`std::string`] [A UTF-8 Encoded String which is used as the Topic Name for a response message.]] [[correlation_data] [`std::string`] [Binary Data used by the sender of the Request Message to identify which request the Response Message is for when it is received.]] - [[user_property] [`std::vector`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties. + [[user_property] [`std::pair`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet. The meaning of these properties is not defined by the specification.]] ] @@ -23,6 +23,9 @@ After creating an instance of [reflink2 will `async_mqtt5::will`], the subscript The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access. +[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties, +except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector>`.] + [h4 Example] The following example shows how to set a Property value: @@ -31,12 +34,10 @@ The following example shows how to set a Property value: async_mqtt5::will will; will[async_mqtt5::prop::message_expiry_interval] = 90; will[async_mqtt5::prop::content_type] = "Notification"; + props[async_mqtt5::prop::user_property].emplace_back("name", "value"); The following example shows how to retrieve a Property value: -[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties, -except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector`.] - [!c++] std::optional c_type = will[async_mqtt5::prop::content_type]; if (c_type.has_value()) @@ -44,4 +45,10 @@ except for `async_mqtt5::prop::user_property`, where it will return an instance else // content type property was not set + std::vector>& user_props = props[async_mqtt5::prop::user_property]; + if (!user_props.empty()) + // user property was previously set + else + // user property was not set + [endsect] diff --git a/include/async_mqtt5/detail/utf8_mqtt.hpp b/include/async_mqtt5/detail/utf8_mqtt.hpp index edf2f34..9d0fc15 100644 --- a/include/async_mqtt5/detail/utf8_mqtt.hpp +++ b/include/async_mqtt5/detail/utf8_mqtt.hpp @@ -94,6 +94,13 @@ inline validation_result validate_mqtt_utf8(std::string_view str) { return validate_impl(str, is_valid_string_size, is_utf8); } +inline bool is_valid_string_pair( + const std::pair& str_pair +) { + return validate_mqtt_utf8(str_pair.first) == validation_result::valid && + validate_mqtt_utf8(str_pair.second) == validation_result::valid; +} + } // namespace async_mqtt5::detail #endif //ASYNC_MQTT5_UTF8_MQTT_HPP diff --git a/include/async_mqtt5/impl/codecs/base_decoders.hpp b/include/async_mqtt5/impl/codecs/base_decoders.hpp index 76b6f3a..f07c895 100644 --- a/include/async_mqtt5/impl/codecs/base_decoders.hpp +++ b/include/async_mqtt5/impl/codecs/base_decoders.hpp @@ -349,49 +349,38 @@ bool parse_to_prop( It& iter, const It last, const Ctx& ctx, RCtx& rctx, Prop& prop ) { - using prop_type = decltype(prop); + using prop_type = std::remove_reference_t; bool rv = false; - if constexpr (is_optional) { - using value_type = - typename std::remove_reference_t::value_type; - if constexpr (std::is_same_v) { - uint8_t attr; - rv = x3::byte_.parse(iter, last, ctx, rctx, attr); - prop = attr; - } - if constexpr (std::is_same_v) { - uint16_t attr; - rv = x3::big_word.parse(iter, last, ctx, rctx, attr); - prop = attr; - } - if constexpr (std::is_same_v) { - int32_t attr; - rv = basic::varint_.parse(iter, last, ctx, rctx, attr); - prop = attr; - } - if constexpr (std::is_same_v) { - uint32_t attr; - rv = x3::big_dword.parse(iter, last, ctx, rctx, attr); - prop = attr; - } - if constexpr (std::is_same_v) { - std::string attr; - rv = basic::utf8_.parse(iter, last, ctx, rctx, attr); - prop.emplace(std::move(attr)); - } + if constexpr (std::is_same_v) + rv = x3::byte_.parse(iter, last, ctx, rctx, prop); + else if constexpr (std::is_same_v) + rv = x3::big_word.parse(iter, last, ctx, rctx, prop); + else if constexpr (std::is_same_v) + rv = basic::varint_.parse(iter, last, ctx, rctx, prop); + else if constexpr (std::is_same_v) + rv = x3::big_dword.parse(iter, last, ctx, rctx, prop); + else if constexpr (std::is_same_v) + rv = basic::utf8_.parse(iter, last, ctx, rctx, prop); + + else if constexpr (is_optional) { + typename prop_type::value_type val; + rv = parse_to_prop(iter, last, ctx, rctx, val); + if (rv) prop.emplace(std::move(val)); } - if constexpr (async_mqtt5::is_vector) { - std::string value; - // key - rv = basic::utf8_.parse(iter, last, ctx, rctx, value); - if (rv) prop.push_back(std::move(value)); - // value - rv = basic::utf8_.parse(iter, last, ctx, rctx, value); + else if constexpr (is_pair) { + rv = parse_to_prop(iter, last, ctx, rctx, prop.first); + rv = parse_to_prop(iter, last, ctx, rctx, prop.second); + } + + else if constexpr (is_vector || is_small_vector) { + typename std::remove_reference_t::value_type value; + rv = parse_to_prop(iter, last, ctx, rctx, value); if (rv) prop.push_back(std::move(value)); } + return rv; } diff --git a/include/async_mqtt5/impl/codecs/base_encoders.hpp b/include/async_mqtt5/impl/codecs/base_encoders.hpp index c9b710b..437cb05 100644 --- a/include/async_mqtt5/impl/codecs/base_encoders.hpp +++ b/include/async_mqtt5/impl/codecs/base_encoders.hpp @@ -320,84 +320,26 @@ std::string& operator<<(std::string& s, T&& t) { } // end namespace basic -namespace detail { - -namespace pp = async_mqtt5::prop; - -template -constexpr bool match_v = std::is_same_v< - std::integral_constant, - typename std::tuple_element_t::key ->; - -template < - pp::property_type p, typename Tuple, - typename Idxs = std::make_index_sequence> -> -struct type_index; - -template < - pp::property_type p, template typename Tuple, - typename... Args, std::size_t... Is -> -struct type_index, std::index_sequence> : - std::integral_constant< - std::size_t, ((Is * match_v>)+... + 0) - > -{ - static_assert( - 1 == (match_v> + ... + 0), - "T doesn't appear once in tuple" - ); -}; - -} // end namespace detail - namespace prop { namespace pp = async_mqtt5::prop; -template -struct prop_encoder_type { - using key = std::integral_constant; - using value = T; -}; - -using encoder_types = std::tuple< - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type> ->; - -template -constexpr auto encoder_for_prop = typename std::tuple_element_t< - detail::type_index::value, encoder_types ->::value {}; - +template +auto encoder_for_prop_value(const T& val) { + if constexpr (std::is_same_v) + return basic::int_def{}(val); + else if constexpr (std::is_same_v) + return basic::int_def{}(val); + else if constexpr (std::is_same_v) + return basic::int_def{}(val); + else if constexpr (std::is_same_v) + return basic::int_def{}(val); + else if constexpr (std::is_same_v) + return basic::utf8_def{}(val); + else if constexpr (is_pair) + return encoder_for_prop_value(val.first) & + encoder_for_prop_value(val.second); +} template class prop_val; @@ -409,10 +351,8 @@ class prop_val< T, p, std::enable_if_t && is_optional> > : public basic::encoder { - // T is always std::optional - using opt_type = typename boost::remove_cv_ref_t::value_type; // allows T to be reference type to std::optional - static inline std::optional nulltype; + static inline boost::remove_cv_ref_t nulltype; T _val; public: prop_val(T val) : _val(val) { @@ -422,16 +362,14 @@ public: size_t byte_size() const { if (!_val) return 0; - auto sval = encoder_for_prop

(_val); - return 1 + sval.byte_size(); + return 1 + encoder_for_prop_value(*_val).byte_size(); } std::string& encode(std::string& s) const { if (!_val) return s; s.push_back(p); - auto sval = encoder_for_prop

(_val); - return sval.encode(s); + return encoder_for_prop_value(*_val).encode(s); } }; @@ -440,7 +378,7 @@ template < > class prop_val< T, p, - std::enable_if_t> + std::enable_if_t || is_small_vector> > : public basic::encoder { // allows T to be reference type to std::vector static inline boost::remove_cv_ref_t nulltype; @@ -456,16 +394,8 @@ public: if (_val.empty()) return 0; size_t total_size = 0; - for (size_t i = 0; i < _val.size() && i + 1 < _val.size(); i += 2) { - auto skey = encoder_for_prop

(_val[i]); - size_t key_size = skey.byte_size(); - - auto sval = encoder_for_prop

(_val[i + 1]); - size_t val_size = sval.byte_size(); - - if (key_size && val_size) - total_size += 1 + key_size + val_size; - } + for (const auto& elem : _val) + total_size += 1 + encoder_for_prop_value(elem).byte_size(); return total_size; } @@ -474,14 +404,9 @@ public: if (_val.empty()) return s; - for (size_t i = 0; i < _val.size() && i + 1 < _val.size(); i += 2) { + for (const auto& elem: _val) { s.push_back(p); - - auto skey = encoder_for_prop

(_val[i]); - skey.encode(s); - - auto sval = encoder_for_prop

(_val[i + 1]); - sval.encode(s); + encoder_for_prop_value(elem).encode(s); } return s; diff --git a/include/async_mqtt5/impl/codecs/traits.hpp b/include/async_mqtt5/impl/codecs/traits.hpp index 1644247..24b60b2 100644 --- a/include/async_mqtt5/impl/codecs/traits.hpp +++ b/include/async_mqtt5/impl/codecs/traits.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -30,6 +31,21 @@ constexpr bool is_vector = is_specialization< boost::remove_cv_ref_t, std::vector >; +template +constexpr std::true_type is_small_vector_impl( + boost::container::small_vector_base const & +); +constexpr std::false_type is_small_vector_impl( ... ); + +template +constexpr bool is_small_vector = + decltype(is_small_vector_impl(std::declval()))::value; + +template +constexpr bool is_pair = is_specialization< + boost::remove_cv_ref_t, std::pair +>; + template constexpr bool is_boost_iterator = is_specialization< boost::remove_cv_ref_t, boost::iterator_range diff --git a/include/async_mqtt5/impl/disconnect_op.hpp b/include/async_mqtt5/impl/disconnect_op.hpp index 4db305d..311eee0 100644 --- a/include/async_mqtt5/impl/disconnect_op.hpp +++ b/include/async_mqtt5/impl/disconnect_op.hpp @@ -131,8 +131,8 @@ private: return client::error::malformed_packet; auto user_properties = props[prop::user_property]; - for (const auto& user_prop: user_properties) - if (validate_mqtt_utf8(user_prop) != validation_result::valid) + for (const auto& user_property: user_properties) + if (!is_valid_string_pair(user_property)) return client::error::malformed_packet; return error_code {}; } diff --git a/include/async_mqtt5/impl/endpoints.hpp b/include/async_mqtt5/impl/endpoints.hpp index 25c3671..417e088 100644 --- a/include/async_mqtt5/impl/endpoints.hpp +++ b/include/async_mqtt5/impl/endpoints.hpp @@ -141,8 +141,8 @@ class endpoints { public: template - endpoints(Executor ex, asio::steady_timer& timer) - : _resolver(ex), _connect_timer(timer) + endpoints(Executor ex, asio::steady_timer& timer) : + _resolver(ex), _connect_timer(timer) {} void clone_servers(const endpoints& other) { diff --git a/include/async_mqtt5/impl/publish_send_op.hpp b/include/async_mqtt5/impl/publish_send_op.hpp index 19f32c2..fdfdbcc 100644 --- a/include/async_mqtt5/impl/publish_send_op.hpp +++ b/include/async_mqtt5/impl/publish_send_op.hpp @@ -379,16 +379,11 @@ private: return client::error::malformed_packet; auto user_properties = props[prop::user_property]; - for (const auto& user_prop: user_properties) - if (validate_mqtt_utf8(user_prop) != validation_result::valid) + for (const auto& user_property: user_properties) + if (!is_valid_string_pair(user_property)) return client::error::malformed_packet; - auto subscription_identifier = props[prop::subscription_identifier]; - if ( - subscription_identifier && - (*subscription_identifier < min_subscription_identifier || - *subscription_identifier > max_subscription_identifier) - ) + if (!props[prop::subscription_identifier].empty()) return client::error::malformed_packet; auto content_type = props[prop::content_type]; diff --git a/include/async_mqtt5/impl/subscribe_op.hpp b/include/async_mqtt5/impl/subscribe_op.hpp index 5db40f9..9db3308 100644 --- a/include/async_mqtt5/impl/subscribe_op.hpp +++ b/include/async_mqtt5/impl/subscribe_op.hpp @@ -220,8 +220,8 @@ private: error_code validate_props(const subscribe_props& props) const { auto user_properties = props[prop::user_property]; - for (const auto& user_prop: user_properties) - if (validate_mqtt_utf8(user_prop) != validation_result::valid) + for (const auto& user_property: user_properties) + if (!is_valid_string_pair(user_property)) return client::error::malformed_packet; auto sub_id = props[prop::subscription_identifier]; diff --git a/include/async_mqtt5/impl/unsubscribe_op.hpp b/include/async_mqtt5/impl/unsubscribe_op.hpp index 6d397af..0e62ed8 100644 --- a/include/async_mqtt5/impl/unsubscribe_op.hpp +++ b/include/async_mqtt5/impl/unsubscribe_op.hpp @@ -178,8 +178,8 @@ private: return client::error::invalid_topic; auto user_properties = props[prop::user_property]; - for (const auto& user_prop: user_properties) - if (validate_mqtt_utf8(user_prop) != validation_result::valid) + for (const auto& user_property: user_properties) + if (!is_valid_string_pair(user_property)) return client::error::malformed_packet; return error_code {}; } diff --git a/include/async_mqtt5/mqtt_client.hpp b/include/async_mqtt5/mqtt_client.hpp index 8936023..c8231d3 100644 --- a/include/async_mqtt5/mqtt_client.hpp +++ b/include/async_mqtt5/mqtt_client.hpp @@ -368,14 +368,15 @@ public: * * \details The return type varies according to the property requested. * For all properties, the return type will be `std::optional` of their respective value type. - * For `async_mqtt5::prop::user_property`, the return type is `std::vector`. + * For `async_mqtt5::prop::user_property`, the return type is + * `std::vector>`. * * \param prop The \__CONNACK_PROPS\__ property value to retrieve. * * \par Example * \code * std::optional auth_method = client.connack_property(async_mqtt5::prop::authentication_method); // ok - * std::optional c_type = client.connack_property(async_mqtt5::prop::content_type); // does not compile, not a CONNAK prop! + * std::optional c_type = client.connack_property(async_mqtt5::prop::content_type); // does not compile, not a CONNACK prop! * \endcode * * \see See \__CONNACK_PROPS\__ for all eligible properties. diff --git a/include/async_mqtt5/property_types.hpp b/include/async_mqtt5/property_types.hpp index 67df428..1aa3806 100644 --- a/include/async_mqtt5/property_types.hpp +++ b/include/async_mqtt5/property_types.hpp @@ -7,6 +7,8 @@ #include #include +#include + namespace async_mqtt5::prop { enum property_type : uint8_t { @@ -39,9 +41,49 @@ enum property_type : uint8_t { shared_subscription_available_t = 0x2a }; +class alignas(8) subscription_identifiers : + public boost::container::small_vector +{ + using base_type = boost::container::small_vector; + +public: + using base_type::base_type; + subscription_identifiers(int32_t val) : base_type { val } {} + + bool has_value() const noexcept { + return !empty(); + } + + int32_t& operator*() noexcept { + return front(); + } + + int32_t operator*() const noexcept { + return front(); + } + + void emplace(int32_t val = 0) { + *this = val; + } + + int32_t value() const { + return front(); + } + + int32_t value_or(int32_t default_val) const noexcept { + return empty() ? default_val : front(); + } + + void reset() noexcept { + clear(); + } +}; + template struct property_traits; +using user_property_value_t = std::vector>; + #define DEF_PROPERTY_TRAIT(Pname, Ptype) \ template <> \ struct property_traits { \ @@ -55,7 +97,7 @@ DEF_PROPERTY_TRAIT(message_expiry_interval, std::optional); DEF_PROPERTY_TRAIT(content_type, std::optional); DEF_PROPERTY_TRAIT(response_topic, std::optional); DEF_PROPERTY_TRAIT(correlation_data, std::optional); -DEF_PROPERTY_TRAIT(subscription_identifier, std::optional); +DEF_PROPERTY_TRAIT(subscription_identifier, subscription_identifiers); DEF_PROPERTY_TRAIT(session_expiry_interval, std::optional); DEF_PROPERTY_TRAIT(assigned_client_identifier, std::optional); DEF_PROPERTY_TRAIT(server_keep_alive, std::optional); @@ -72,7 +114,7 @@ DEF_PROPERTY_TRAIT(topic_alias_maximum, std::optional); DEF_PROPERTY_TRAIT(topic_alias, std::optional); DEF_PROPERTY_TRAIT(maximum_qos, std::optional); DEF_PROPERTY_TRAIT(retain_available, std::optional); -DEF_PROPERTY_TRAIT(user_property, std::vector); +DEF_PROPERTY_TRAIT(user_property, user_property_value_t); DEF_PROPERTY_TRAIT(maximum_packet_size, std::optional); DEF_PROPERTY_TRAIT(wildcard_subscription_available, std::optional); DEF_PROPERTY_TRAIT(subscription_identifier_available, std::optional); diff --git a/include/async_mqtt5/reason_codes.hpp b/include/async_mqtt5/reason_codes.hpp index 908de51..39820cc 100644 --- a/include/async_mqtt5/reason_codes.hpp +++ b/include/async_mqtt5/reason_codes.hpp @@ -41,8 +41,8 @@ public: /// \cond INTERNAL constexpr reason_code() : _code(0xff) {} - constexpr reason_code(uint8_t code, reason_codes::category cat) - : _code(code), _category(cat) + constexpr reason_code(uint8_t code, reason_codes::category cat) : + _code(code), _category(cat) {} constexpr explicit reason_code(uint8_t code) : _code(code) {} diff --git a/test/include/test_common/packet_util.hpp b/test/include/test_common/packet_util.hpp index 94b752f..4194a79 100644 --- a/test/include/test_common/packet_util.hpp +++ b/test/include/test_common/packet_util.hpp @@ -80,7 +80,14 @@ inline std::string to_readable_props(Props props) { if (v.has_value()) stream << *v << " "; if constexpr (is_vector) - stream << boost::algorithm::join(v, ","); + for (size_t i = 0; i < v.size(); i++) { + if constexpr (is_pair) + stream << "(" << v[i].first << ", " << v[i].second << ")"; + else + stream << v[i]; + if (i + 1 < v.size()) + stream << ", "; + } return true; }); return stream.str(); diff --git a/test/integration/client_functions.cpp b/test/integration/client_functions.cpp index 8d20fce..b384af4 100644 --- a/test/integration/client_functions.cpp +++ b/test/integration/client_functions.cpp @@ -191,7 +191,8 @@ struct shared_connect_prop_test_data : shared_test_data { const uint16_t topic_alias_maximum = 12345; const uint8_t request_response_information = 1; const uint8_t request_problem_information = 0; - const std::vector user_properties = std::vector{ "key", "val" }; + const std::vector> user_properties = + { { "key", "val" } }; connect_props cprops = create_connect_props(); @@ -300,7 +301,8 @@ struct shared_connack_prop_test_data { const std::string assigned_client_id = "client_id"; const uint16_t topic_alias_max = 128; const std::string reason_string = "reason string"; - const std::vector user_properties = std::vector{ "key", "val" }; + const std::vector> user_properties = + { { "key", "val" } }; const uint8_t wildcard_sub = 1; const uint8_t sub_id = 1; const uint8_t shared_sub = 0; diff --git a/test/integration/receive_publish.cpp b/test/integration/receive_publish.cpp index 77f4281..7f0d10b 100644 --- a/test/integration/receive_publish.cpp +++ b/test/integration/receive_publish.cpp @@ -125,6 +125,82 @@ BOOST_FIXTURE_TEST_CASE(receive_publish_qos2, shared_test_data) { run_test(std::move(broker_side)); } +BOOST_FIXTURE_TEST_CASE(receive_publish_properties, shared_test_data) { + constexpr int expected_handlers_called = 1; + int handlers_called = 0; + + publish_props pprops; + + pprops[prop::payload_format_indicator] = uint8_t(0); + pprops[prop::message_expiry_interval] = 102u; + pprops[prop::content_type] = "content/type"; + pprops[prop::response_topic] = "response/topic"; + pprops[prop::correlation_data] = std::string { + static_cast(0x00), static_cast(0x01), static_cast(0xFF) + }; + pprops[prop::subscription_identifier] = { 40, 41, 42 }; + pprops[prop::topic_alias] = uint16_t(103); + pprops[prop::user_property] = { { "name1", "value1" }, { "name2", "value2 "} }; + + auto publish = encoders::encode_publish( + 1, topic, payload, + qos_e::at_most_once, retain_e::no, dup_e::no, + pprops + ); + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(0ms)) + .reply_with(connack, after(0ms)) + .send(publish, after(10ms)); + + asio::io_context ioc; + auto executor = ioc.get_executor(); + auto& broker = asio::make_service( + ioc, executor, std::move(broker_side) + ); + + using client_type = mqtt_client; + client_type c(executor); + c.brokers("127.0.0.1") + .async_run(asio::detached); + + c.async_receive([&handlers_called, &pprops, &c]( + error_code ec, std::string rec_topic, std::string rec_payload, + publish_props rec_pprops + ){ + ++handlers_called; + auto data = shared_test_data(); + BOOST_TEST(!ec); + BOOST_TEST(data.topic == rec_topic); + BOOST_TEST(data.payload == rec_payload); + BOOST_TEST(*pprops[prop::payload_format_indicator] + == *rec_pprops[prop::payload_format_indicator]); + BOOST_TEST(*pprops[prop::message_expiry_interval] + == *rec_pprops[prop::message_expiry_interval]); + BOOST_TEST(*pprops[prop::content_type] + == *rec_pprops[prop::content_type]); + BOOST_TEST(*pprops[prop::response_topic] + == *rec_pprops[prop::response_topic]); + BOOST_TEST(*pprops[prop::correlation_data] + == *rec_pprops[prop::correlation_data]); + BOOST_TEST(pprops[prop::subscription_identifier] + == rec_pprops[prop::subscription_identifier]); + BOOST_TEST(*pprops[prop::topic_alias] + == *rec_pprops[prop::topic_alias]); + BOOST_TEST(pprops[prop::user_property] + == rec_pprops[prop::user_property]); + c.cancel(); + } + ); + + ioc.run(); + BOOST_TEST(handlers_called == expected_handlers_called); + BOOST_TEST(broker.received_all_expected()); +} + + BOOST_FIXTURE_TEST_CASE(receive_malformed_publish, shared_test_data) { // packets auto malformed_publish = encoders::encode_publish( @@ -322,8 +398,10 @@ BOOST_FIXTURE_TEST_CASE(receive_big_publish, shared_test_data) { cprops[prop::maximum_packet_size] = 10'000'000; publish_props big_props; - for (int i = 0; i < 100; i++) - big_props[prop::user_property].push_back(std::string(65534, 'u')); + for (int i = 0; i < 50; i++) + big_props[prop::user_property].emplace_back( + std::string(65534, 'u'), std::string(65534, 'v') + ); // packets auto connect_big_packets = encoders::encode_connect( diff --git a/test/integration/send_publish.cpp b/test/integration/send_publish.cpp index 645a3c4..ce052f5 100644 --- a/test/integration/send_publish.cpp +++ b/test/integration/send_publish.cpp @@ -545,16 +545,12 @@ BOOST_FIXTURE_TEST_CASE(send_big_publish, shared_test_data) { const std::string big_topic = std::string(65534, 't'); const std::string big_payload = std::string(65534, 'p'); - publish_props big_props; - for (int i = 0; i < 1; i++) - big_props[prop::user_property].push_back(std::string(65534, 'u')); - // packets auto allow_big_connack = encoders::encode_connack(false, uint8_t(0x00), cprops); auto big_publish = encoders::encode_publish( 1, big_topic, big_payload, qos_e::at_least_once, retain_e::no, dup_e::no, - big_props + publish_props{} ); auto pingreq = encoders::encode_pingreq(); auto pingresp = encoders::encode_pingresp(); @@ -590,7 +586,7 @@ BOOST_FIXTURE_TEST_CASE(send_big_publish, shared_test_data) { .async_run(asio::detached); c.async_publish( - big_topic, big_payload, retain_e::no, big_props, + big_topic, big_payload, retain_e::no, publish_props{}, [&handlers_called, &c](error_code ec, reason_code rc, puback_props) { ++handlers_called; diff --git a/test/unit/disconnect_op.cpp b/test/unit/disconnect_op.cpp index 087312d..6cfdfb4 100644 --- a/test/unit/disconnect_op.cpp +++ b/test/unit/disconnect_op.cpp @@ -50,9 +50,16 @@ BOOST_AUTO_TEST_CASE(malformed_reason_string) { run_malformed_props_test(dprops); } -BOOST_AUTO_TEST_CASE(malformed_user_property) { +BOOST_AUTO_TEST_CASE(malformed_user_property_key) { disconnect_props dprops; - dprops[prop::user_property].push_back(std::string { 0x01 }); + dprops[prop::user_property].emplace_back(std::string { 0x01 }, "value"); + + run_malformed_props_test(dprops); +} + +BOOST_AUTO_TEST_CASE(malformed_user_property_value) { + disconnect_props dprops; + dprops[prop::user_property].emplace_back("key", std::string { 0x01 }); run_malformed_props_test(dprops); } diff --git a/test/unit/publish_send_op.cpp b/test/unit/publish_send_op.cpp index a1eb277..9ff4e09 100644 --- a/test/unit/publish_send_op.cpp +++ b/test/unit/publish_send_op.cpp @@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(malformed_props_1) { BOOST_AUTO_TEST_CASE(malformed_props_2) { publish_props pprops; - pprops[prop::user_property].push_back(std::string { 0x01 }); + pprops[prop::user_property].emplace_back(std::string { 0x01 }, "value"); run_malformed_props_test(pprops); } @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(malformed_props_4) { BOOST_AUTO_TEST_CASE(malformed_props_5) { publish_props pprops; - pprops[prop::subscription_identifier] = 300'000'000; + pprops[prop::subscription_identifier].push_back(40); run_malformed_props_test(pprops); } diff --git a/test/unit/serialization.cpp b/test/unit/serialization.cpp index ea9940a..78cadb2 100644 --- a/test/unit/serialization.cpp +++ b/test/unit/serialization.cpp @@ -50,8 +50,7 @@ BOOST_AUTO_TEST_CASE(test_connect) { cprops[prop::topic_alias_maximum] = topic_alias_max; cprops[prop::request_response_information] = request_response_information; cprops[prop::request_problem_information] = request_problem_information; - cprops[prop::user_property].emplace_back(user_property_1); - cprops[prop::user_property].emplace_back(user_property_2); + cprops[prop::user_property].emplace_back(user_property_1, user_property_2); cprops[prop::authentication_method] = auth_method; cprops[prop::authentication_data] = auth_data; @@ -62,8 +61,7 @@ BOOST_AUTO_TEST_CASE(test_connect) { w[prop::content_type] = will_content_type; w[prop::response_topic] = will_response_topic; w[prop::correlation_data] = will_correlation_data; - w[prop::user_property].emplace_back(will_user_property_1); - w[prop::user_property].emplace_back(will_user_property_2); + w[prop::user_property].emplace_back(will_user_property_1, will_user_property_2); std::optional will_opt { std::move(w) }; auto msg = encoders::encode_connect( @@ -96,9 +94,9 @@ BOOST_AUTO_TEST_CASE(test_connect) { BOOST_CHECK_EQUAL(*cprops_[prop::topic_alias_maximum], topic_alias_max); BOOST_CHECK_EQUAL(*cprops_[prop::request_response_information], request_response_information); BOOST_CHECK_EQUAL(*cprops_[prop::request_problem_information], request_problem_information); - BOOST_ASSERT(cprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(cprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(cprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].second, user_property_2); BOOST_CHECK_EQUAL(*cprops_[prop::authentication_method], auth_method); BOOST_CHECK_EQUAL(*cprops_[prop::authentication_data], auth_data); @@ -114,9 +112,9 @@ BOOST_AUTO_TEST_CASE(test_connect) { BOOST_CHECK_EQUAL(*(*w_)[prop::content_type], will_content_type); BOOST_CHECK_EQUAL(*(*w_)[prop::response_topic], will_response_topic); BOOST_CHECK_EQUAL(*(*w_)[prop::correlation_data], will_correlation_data); - BOOST_ASSERT((*w_)[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL((*w_)[prop::user_property][0], will_user_property_1); - BOOST_CHECK_EQUAL((*w_)[prop::user_property][1], will_user_property_2); + BOOST_ASSERT((*w_)[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL((*w_)[prop::user_property][0].first, will_user_property_1); + BOOST_CHECK_EQUAL((*w_)[prop::user_property][0].second, will_user_property_2); } BOOST_AUTO_TEST_CASE(test_connack) { @@ -152,8 +150,7 @@ BOOST_AUTO_TEST_CASE(test_connack) { cprops[prop::assigned_client_identifier] = assigned_client_id; cprops[prop::topic_alias_maximum] = topic_alias_max; cprops[prop::reason_string] = reason_string; - cprops[prop::user_property].push_back(user_property_1); - cprops[prop::user_property].push_back(user_property_2); + cprops[prop::user_property].emplace_back(user_property_1, user_property_2); cprops[prop::wildcard_subscription_available] = wildcard_sub; cprops[prop::subscription_identifier_available] = sub_id; cprops[prop::shared_subscription_available] = shared_sub; @@ -186,9 +183,9 @@ BOOST_AUTO_TEST_CASE(test_connack) { BOOST_CHECK_EQUAL(*cprops_[prop::assigned_client_identifier], assigned_client_id); BOOST_CHECK_EQUAL(*cprops_[prop::topic_alias_maximum], topic_alias_max); BOOST_CHECK_EQUAL(*cprops_[prop::reason_string], reason_string); - BOOST_ASSERT(cprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(cprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(cprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].second, user_property_2); BOOST_CHECK_EQUAL(*cprops_[prop::wildcard_subscription_available], wildcard_sub); BOOST_CHECK_EQUAL(*cprops_[prop::subscription_identifier_available], sub_id); BOOST_CHECK_EQUAL(*cprops_[prop::shared_subscription_available], shared_sub); @@ -221,9 +218,8 @@ BOOST_AUTO_TEST_CASE(test_publish) { pprops[prop::topic_alias] = topic_alias; pprops[prop::response_topic] = response_topic; pprops[prop::correlation_data] = correlation_data; - pprops[prop::user_property].emplace_back(publish_prop_1); - pprops[prop::user_property].emplace_back(publish_prop_2); - pprops[prop::subscription_identifier] = subscription_identifier; + pprops[prop::user_property].emplace_back(publish_prop_1, publish_prop_2); + pprops[prop::subscription_identifier].push_back(subscription_identifier); pprops[prop::content_type] = content_type; auto msg = encoders::encode_publish( @@ -252,10 +248,11 @@ BOOST_AUTO_TEST_CASE(test_publish) { BOOST_CHECK_EQUAL(*pprops_[prop::topic_alias], topic_alias); BOOST_CHECK_EQUAL(*pprops_[prop::response_topic], response_topic); BOOST_CHECK_EQUAL(*pprops_[prop::correlation_data], correlation_data); - BOOST_ASSERT(pprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], publish_prop_1); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], publish_prop_2); - BOOST_CHECK_EQUAL(*pprops_[prop::subscription_identifier], subscription_identifier); + BOOST_ASSERT(pprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, publish_prop_1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, publish_prop_2); + BOOST_ASSERT(pprops_[prop::subscription_identifier].size() == 1); + BOOST_CHECK_EQUAL(pprops_[prop::subscription_identifier][0], subscription_identifier); BOOST_CHECK_EQUAL(*pprops_[prop::content_type], content_type); } @@ -297,8 +294,7 @@ BOOST_AUTO_TEST_CASE(test_puback) { puback_props pprops; pprops[prop::reason_string] = reason_string; - pprops[prop::user_property].emplace_back(user_property_1); - pprops[prop::user_property].emplace_back(user_property_2); + pprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_puback(packet_id, reason_code, pprops); @@ -318,9 +314,9 @@ BOOST_AUTO_TEST_CASE(test_puback) { pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(reason_code_, reason_code); BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string); - BOOST_ASSERT(pprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(pprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_pubrec) { @@ -334,8 +330,7 @@ BOOST_AUTO_TEST_CASE(test_pubrec) { pubrec_props pprops; pprops[prop::reason_string] = reason_string; - pprops[prop::user_property].emplace_back(user_property_1); - pprops[prop::user_property].emplace_back(user_property_2); + pprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_pubrec(packet_id, reason_code, pprops); @@ -355,9 +350,9 @@ BOOST_AUTO_TEST_CASE(test_pubrec) { pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(reason_code_, reason_code); BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string); - BOOST_ASSERT(pprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(pprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_pubrel) { @@ -371,8 +366,7 @@ BOOST_AUTO_TEST_CASE(test_pubrel) { pubrel_props pprops; pprops[prop::reason_string] = reason_string; - pprops[prop::user_property].emplace_back(user_property_1); - pprops[prop::user_property].emplace_back(user_property_2); + pprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_pubrel(packet_id, reason_code, pprops); @@ -392,9 +386,9 @@ BOOST_AUTO_TEST_CASE(test_pubrel) { pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(reason_code_, reason_code); BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string); - BOOST_ASSERT(pprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(pprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_pubcomp) { @@ -408,8 +402,7 @@ BOOST_AUTO_TEST_CASE(test_pubcomp) { pubcomp_props pprops; pprops[prop::reason_string] = reason_string; - pprops[prop::user_property].emplace_back(user_property_1); - pprops[prop::user_property].emplace_back(user_property_2); + pprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_pubcomp(packet_id, reason_code, pprops); @@ -429,9 +422,9 @@ BOOST_AUTO_TEST_CASE(test_pubcomp) { pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(reason_code_, reason_code); BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string); - BOOST_ASSERT(pprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(pprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_subscribe) { @@ -447,8 +440,7 @@ BOOST_AUTO_TEST_CASE(test_subscribe) { subscribe_props sprops; sprops[prop::subscription_identifier] = sub_id; - sprops[prop::user_property].push_back(user_property_1); - sprops[prop::user_property].push_back(user_property_2); + sprops[prop::user_property].emplace_back(user_property_1, user_property_2); std::vector filters { { @@ -484,9 +476,9 @@ BOOST_AUTO_TEST_CASE(test_subscribe) { sprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(*sprops_[prop::subscription_identifier], sub_id); - BOOST_ASSERT(sprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(sprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(sprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_suback) { @@ -500,8 +492,7 @@ BOOST_AUTO_TEST_CASE(test_suback) { suback_props sprops; sprops[prop::reason_string] = reason_string; - sprops[prop::user_property].push_back(user_property_1); - sprops[prop::user_property].push_back(user_property_2); + sprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_suback(packet_id, reason_codes, sprops); @@ -521,9 +512,9 @@ BOOST_AUTO_TEST_CASE(test_suback) { sprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(*sprops_[prop::reason_string], reason_string); - BOOST_ASSERT(sprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(sprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(sprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_unsubscribe) { @@ -535,8 +526,7 @@ BOOST_AUTO_TEST_CASE(test_unsubscribe) { std::string user_property_2 = "UNSUBSCRIBE user prop val"; unsubscribe_props uprops; - uprops[prop::user_property].push_back(user_property_1); - uprops[prop::user_property].push_back(user_property_2); + uprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_unsubscribe(packet_id, topics, uprops); @@ -555,9 +545,9 @@ BOOST_AUTO_TEST_CASE(test_unsubscribe) { BOOST_CHECK(topics_ == topics); uprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); - BOOST_ASSERT(uprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(uprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(uprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_unsuback) { @@ -571,8 +561,7 @@ BOOST_AUTO_TEST_CASE(test_unsuback) { unsuback_props uprops; uprops[prop::reason_string] = reason_string; - uprops[prop::user_property].push_back(user_property_1); - uprops[prop::user_property].push_back(user_property_2); + uprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_unsuback(packet_id, reason_codes, uprops); @@ -592,9 +581,9 @@ BOOST_AUTO_TEST_CASE(test_unsuback) { uprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(*uprops_[prop::reason_string], reason_string); - BOOST_ASSERT(uprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(uprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(uprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_disconnect) { @@ -610,8 +599,7 @@ BOOST_AUTO_TEST_CASE(test_disconnect) { disconnect_props dprops; dprops[prop::session_expiry_interval] = session_expiry_interval; dprops[prop::reason_string] = reason_string; - dprops[prop::user_property].emplace_back(user_property_1); - dprops[prop::user_property].emplace_back(user_property_2); + dprops[prop::user_property].emplace_back(user_property_1, user_property_2); dprops[prop::server_reference] = server_reference; auto msg = encoders::encode_disconnect(reason_code, dprops); @@ -630,9 +618,9 @@ BOOST_AUTO_TEST_CASE(test_disconnect) { dprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; }); BOOST_CHECK_EQUAL(*dprops_[prop::session_expiry_interval], session_expiry_interval); BOOST_CHECK_EQUAL(*dprops_[prop::reason_string], reason_string); - BOOST_ASSERT(dprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(dprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(dprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(dprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(dprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(dprops_[prop::user_property][0].second, user_property_2); BOOST_CHECK_EQUAL(*dprops_[prop::server_reference], server_reference); } @@ -651,8 +639,7 @@ BOOST_AUTO_TEST_CASE(test_auth) { aprops[prop::authentication_method] = authentication_method; aprops[prop::authentication_data] = authentication_data; aprops[prop::reason_string] = reason_string; - aprops[prop::user_property].emplace_back(user_property_1); - aprops[prop::user_property].emplace_back(user_property_2); + aprops[prop::user_property].emplace_back(user_property_1, user_property_2); auto msg = encoders::encode_auth(reason_code, aprops); @@ -671,9 +658,9 @@ BOOST_AUTO_TEST_CASE(test_auth) { BOOST_CHECK_EQUAL(*aprops_[prop::authentication_method], authentication_method); BOOST_CHECK_EQUAL(*aprops_[prop::authentication_data], authentication_data); BOOST_CHECK_EQUAL(*aprops_[prop::reason_string], reason_string); - BOOST_ASSERT(aprops_[prop::user_property].size() == 2); - BOOST_CHECK_EQUAL(aprops_[prop::user_property][0], user_property_1); - BOOST_CHECK_EQUAL(aprops_[prop::user_property][1], user_property_2); + BOOST_ASSERT(aprops_[prop::user_property].size() == 1); + BOOST_CHECK_EQUAL(aprops_[prop::user_property][0].first, user_property_1); + BOOST_CHECK_EQUAL(aprops_[prop::user_property][0].second, user_property_2); } BOOST_AUTO_TEST_CASE(test_pingreq) { @@ -692,8 +679,7 @@ BOOST_AUTO_TEST_CASE(test_pingresp) { BOOST_AUTO_TEST_CASE(empty_user_property) { publish_props pprops; - pprops[prop::user_property].emplace_back(""); - pprops[prop::user_property].emplace_back(""); + pprops[prop::user_property].emplace_back("", ""); auto msg = encoders::encode_publish( 1, "topic", "payload", @@ -712,69 +698,9 @@ BOOST_AUTO_TEST_CASE(empty_user_property) { const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv; auto user_props_ = pprops_[prop::user_property]; - BOOST_ASSERT(user_props_.size() == 2); - BOOST_CHECK_EQUAL(user_props_[0], ""); - BOOST_CHECK_EQUAL(user_props_[1], ""); -} - -BOOST_AUTO_TEST_CASE(omit_invalid_user_property) { - // testing variables - std::string_view user_property_1 = "key"; - std::string_view user_property_2 = "val"; - std::string_view user_property_3 = "lone key"; - - publish_props pprops; - pprops[prop::user_property].emplace_back(user_property_1); - pprops[prop::user_property].emplace_back(user_property_2); - pprops[prop::user_property].emplace_back(user_property_3); - - auto msg = encoders::encode_publish( - 1, "topic", "payload", - qos_e::at_least_once, retain_e::yes, dup_e::no, - pprops - ); - - byte_citer it = msg.cbegin(), last = msg.cend(); - auto header = decoders::decode_fixed_header(it, last); - BOOST_ASSERT(header); - - const auto& [control_byte, remain_length] = *header; - auto rv = decoders::decode_publish(control_byte, remain_length, it); - BOOST_ASSERT(rv); - - const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv; - - auto user_props_ = pprops_[prop::user_property]; - BOOST_ASSERT(user_props_.size() == 2); - BOOST_CHECK_EQUAL(user_props_[0], user_property_1); - BOOST_CHECK_EQUAL(user_props_[1], user_property_2); -} - -BOOST_AUTO_TEST_CASE(omit_all_user_property) { - // testing variables - std::string_view user_property_1 = "key"; - - publish_props pprops; - pprops[prop::user_property].emplace_back(user_property_1); - - auto msg = encoders::encode_publish( - 1, "topic", "payload", - qos_e::at_least_once, retain_e::yes, dup_e::no, - pprops - ); - - byte_citer it = msg.cbegin(), last = msg.cend(); - auto header = decoders::decode_fixed_header(it, last); - BOOST_ASSERT(header); - - const auto& [control_byte, remain_length] = *header; - auto rv = decoders::decode_publish(control_byte, remain_length, it); - BOOST_ASSERT(rv); - - const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv; - - auto user_props_ = pprops_[prop::user_property]; - BOOST_CHECK(user_props_.size() == 0); + BOOST_ASSERT(user_props_.size() == 1); + BOOST_CHECK_EQUAL(user_props_[0].first, ""); + BOOST_CHECK_EQUAL(user_props_[0].second, ""); } BOOST_AUTO_TEST_CASE(deserialize_user_property) { @@ -795,9 +721,9 @@ BOOST_AUTO_TEST_CASE(deserialize_user_property) { const auto& [reason_code_, pprops_] = *rv; auto user_props_ = pprops_[prop::user_property]; - BOOST_ASSERT(user_props_.size() == 2); - BOOST_CHECK_EQUAL(user_props_[0], "key"); - BOOST_CHECK_EQUAL(user_props_[1], "val"); + BOOST_ASSERT(user_props_.size() == 1); + BOOST_CHECK_EQUAL(user_props_[0].first, "key"); + BOOST_CHECK_EQUAL(user_props_[0].second, "val"); } BOOST_AUTO_TEST_CASE(deserialize_empty_user_property) { @@ -818,9 +744,9 @@ BOOST_AUTO_TEST_CASE(deserialize_empty_user_property) { const auto& [reason_code_, pprops_] = *rv; auto user_props_ = pprops_[prop::user_property]; - BOOST_ASSERT(user_props_.size() == 2); - BOOST_CHECK_EQUAL(user_props_[0], ""); - BOOST_CHECK_EQUAL(user_props_[1], ""); + BOOST_ASSERT(user_props_.size() == 1); + BOOST_CHECK_EQUAL(user_props_[0].first, ""); + BOOST_CHECK_EQUAL(user_props_[0].second, ""); } BOOST_AUTO_TEST_CASE(malformed_user_property) { diff --git a/test/unit/subscribe_op.cpp b/test/unit/subscribe_op.cpp index 8ea76fb..f908a5d 100644 --- a/test/unit/subscribe_op.cpp +++ b/test/unit/subscribe_op.cpp @@ -105,14 +105,14 @@ BOOST_AUTO_TEST_CASE(invalid_topic_filter_6) { BOOST_AUTO_TEST_CASE(malformed_user_property_1) { subscribe_props sprops; - sprops[prop::user_property].push_back(std::string(10, char(0x01))); + sprops[prop::user_property].emplace_back("key", std::string(10, char(0x01))); run_test(client::error::malformed_packet, "topic", sprops); } BOOST_AUTO_TEST_CASE(malformed_user_property_2) { subscribe_props sprops; - sprops[prop::user_property].push_back(std::string(75000, 'a')); + sprops[prop::user_property].emplace_back("key", std::string(75000, 'a')); run_test(client::error::malformed_packet, "topic", sprops); } diff --git a/test/unit/unsubscribe_op.cpp b/test/unit/unsubscribe_op.cpp index 499a61a..dd1311e 100644 --- a/test/unit/unsubscribe_op.cpp +++ b/test/unit/unsubscribe_op.cpp @@ -100,14 +100,14 @@ BOOST_AUTO_TEST_CASE(invalid_topic_filter_6) { BOOST_AUTO_TEST_CASE(malformed_user_property_1) { unsubscribe_props uprops; - uprops[prop::user_property].push_back(std::string(10, char(0x01))); + uprops[prop::user_property].emplace_back("key", std::string(10, char(0x01))); run_test(client::error::malformed_packet, "topic", uprops); } BOOST_AUTO_TEST_CASE(malformed_user_property_2) { unsubscribe_props uprops; - uprops[prop::user_property].push_back(std::string(75000, 'a')); + uprops[prop::user_property].emplace_back("key", std::string(75000, 'a')); run_test(client::error::malformed_packet, "topic", uprops); }