Commit f45c31d8fd630f8158e28e544d89c0d81ffa4022

Authored by Peter M. Groen
1 parent 427cbf54

MOve headers to include directory

include/bimap.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_BIMAP_H
  23 +#define OSDEV_COMPONENTS_MQTT_BIMAP_H
  24 +
  25 +// boost
  26 +#include <boost/bimap.hpp>
  27 +
  28 +namespace osdev {
  29 +namespace components {
  30 +namespace mqtt {
  31 +
  32 +/**
  33 + * @brief Factory function to create boost::bimap from an initializer list
  34 + *
  35 + * Usage:
  36 + * @code
  37 + * auto myBimap = makeBimap<int, int>( { { 1, 2 }, { 2, 3 } } );
  38 + * @endcode
  39 + */
  40 +template <typename L, typename R>
  41 +boost::bimap<L, R> makeBimap(std::initializer_list<typename boost::bimap<L, R>::value_type> list)
  42 +{
  43 + return boost::bimap<L, R>(list.begin(), list.end());
  44 +}
  45 +
  46 +} // End namespace mqtt
  47 +} // End namespace components
  48 +} // End namespace osdev
  49 +
  50 +#endif // OSDEV_COMPONENTS_MQTT_BIMAP_H
... ...
include/clientpaho.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_CLIENTPAHO_H
  23 +#define OSDEV_COMPONENTS_MQTT_CLIENTPAHO_H
  24 +
  25 +// std
  26 +#include <atomic>
  27 +#include <condition_variable>
  28 +#include <deque>
  29 +#include <functional>
  30 +#include <future>
  31 +#include <map>
  32 +#include <memory>
  33 +#include <mutex>
  34 +#include <string>
  35 +#include <vector>
  36 +
  37 +// boost
  38 +#include <boost/regex.hpp>
  39 +
  40 +// paho
  41 +#include <MQTTAsync.h>
  42 +
  43 +// osdev::components::mqtt
  44 +#include "synchronizedqueue.h"
  45 +#include "imqttclientimpl.h"
  46 +#include "mqttfailure.h"
  47 +#include "mqttsuccess.h"
  48 +
  49 +namespace osdev {
  50 +namespace components {
  51 +namespace mqtt {
  52 +
  53 +/**
  54 + * @brief Wrapper class for the paho-c library.
  55 + * This implementation uses the clean session flag and recreates subscriptions on reconnect.
  56 + *
  57 + * The implementation allows multiple subscriptions as long as the subscriptions do not have overlap. For mqtt 3 it is not
  58 + * possible to track down the subscription based on the incoming message meta information. By matching the topic against
  59 + * the various topic filters a subscription can be identified (but only when there is only one subscription that matches).
  60 + */
  61 +class ClientPaho : public IMqttClientImpl
  62 +{
  63 +public:
  64 + /**
  65 + * @brief Construct a ClientPaho instance.
  66 + * @param endpoint The endpoint to connect to
  67 + * @param id The clientId that is used in the connection.
  68 + * @param connectionStatusCallback The callback on which connection status information is communicated.
  69 + * @param deliveryCompleteCallback Callback that is called with the publish message tokens for messages that are delivered.
  70 + * Being delivered has different meanings depending on the quality of service.
  71 + */
  72 + ClientPaho(const std::string& endpoint,
  73 + const std::string& id,
  74 + const std::function<void(const std::string&, ConnectionStatus)>& connectionStatusCallback,
  75 + const std::function<void(const std::string&, std::int32_t)>& deliveryCompleteCallback);
  76 + virtual ~ClientPaho() override;
  77 +
  78 + // Non copyable, non movable.
  79 + ClientPaho(const ClientPaho&) = delete;
  80 + ClientPaho& operator=(const ClientPaho&) = delete;
  81 + ClientPaho(ClientPaho&&) = delete;
  82 + ClientPaho& operator=(ClientPaho&&) = delete;
  83 +
  84 + /**
  85 + * @see IMqttClientImpl
  86 + */
  87 + virtual std::string clientId() const override;
  88 +
  89 + /**
  90 + * @see IMqttClientImpl
  91 + */
  92 + virtual ConnectionStatus connectionStatus() const override;
  93 +
  94 + /**
  95 + * @see IMqttClientImpl
  96 + */
  97 + virtual std::int32_t connect(bool wait) override;
  98 +
  99 + /**
  100 + * @see IMqttClientImpl
  101 + */
  102 + virtual std::int32_t disconnect(bool wait, int timeoutMs) override;
  103 +
  104 + /**
  105 + * @see IMqttClientImpl
  106 + */
  107 + virtual std::int32_t publish(const MqttMessage& message, int qos) override;
  108 +
  109 + /**
  110 + * @see IMqttClientImpl
  111 + */
  112 + virtual void publishPending() override;
  113 +
  114 + /**
  115 + * @see IMqttClientImpl
  116 + */
  117 + virtual std::int32_t subscribe(const std::string& topic, int qos, const std::function<void(MqttMessage msg)>& cb) override;
  118 +
  119 + /**
  120 + * @see IMqttClientImpl
  121 + */
  122 + virtual void resubscribe() override;
  123 +
  124 + /**
  125 + * @see IMqttClientImpl
  126 + */
  127 + virtual std::int32_t unsubscribe(const std::string& topic, int qos) override;
  128 +
  129 + /**
  130 + * @see IMqttClientImpl
  131 + */
  132 + virtual void unsubscribeAll() override;
  133 +
  134 + /**
  135 + * @see IMqttClientImpl
  136 + */
  137 + virtual std::chrono::milliseconds waitForCompletion(std::chrono::milliseconds waitFor, const std::set<std::int32_t>& tokens) const override;
  138 +
  139 + /**
  140 + * @see IMqttClientImpl
  141 + */
  142 + virtual bool isOverlapping(const std::string& topic) const override;
  143 +
  144 + /**
  145 + * @see IMqttClientImpl
  146 + */
  147 + virtual bool isOverlapping(const std::string& topic, std::string& existingTopic) const override;
  148 +
  149 + /**
  150 + * @see IMqttClientImpl
  151 + */
  152 + virtual std::vector<std::int32_t> pendingOperations() const override;
  153 +
  154 + /**
  155 + * @see IMqttClientImpl
  156 + */
  157 + virtual bool hasPendingSubscriptions() const override;
  158 +
  159 + /**
  160 + * @see IMqttClientImpl
  161 + */
  162 + virtual boost::optional<bool> operationResult(std::int32_t token) const override;
  163 +
  164 +private:
  165 + void parseEndpoint(const std::string& endpoint);
  166 +
  167 + std::int32_t publishInternal(const MqttMessage& message, int qos);
  168 + std::int32_t subscribeInternal(const std::string& topic, int qos);
  169 +
  170 + void setConnectionStatus(ConnectionStatus status);
  171 + bool isOverlappingInternal(const std::string& topic, std::string& existingTopic) const;
  172 +
  173 + /**
  174 + * @brief Internal struct for subscriber information.
  175 + * Used to store subscriptions.
  176 + */
  177 + struct Subscription
  178 + {
  179 + int qos;
  180 + boost::regex topicRegex;
  181 + std::function<void(MqttMessage)> callback;
  182 + };
  183 +
  184 + /**
  185 + * @brief Internal struct for publisher information.
  186 + * Used to store pending publishes during reconnection.
  187 + */
  188 + struct Publish
  189 + {
  190 + int qos;
  191 + MqttMessage data;
  192 + };
  193 +
  194 + /**
  195 + * @brief Add an incoming callback event to the synchronized queue.
  196 + * @param ev A function object that calls one of the event handlers on a ClientPaho instance, but other types of actions are also possible.
  197 + */
  198 + void pushIncomingEvent(std::function<void()> ev);
  199 +
  200 + /**
  201 + * @brief Worker method that executes the events.
  202 + */
  203 + void callbackEventHandler();
  204 +
  205 + /**
  206 + * @brief Callback method that is called when a reconnect succeeds.
  207 + * @param cause The cause of the original disconnect.
  208 + */
  209 + void onConnectOnInstance(const std::string& cause);
  210 +
  211 + /**
  212 + * @brief Callback that is called when a connect call succeeds.
  213 + * This callback is also called when a reconnect succeeds because the paho library reuses the initial connect command!
  214 + * The connection status is set to Connected.
  215 + * @param response A success response with connection data.
  216 + */
  217 + void onConnectSuccessOnInstance(const MqttSuccess& response);
  218 +
  219 + /**
  220 + * @brief Callback that is called when a connect call fails after being sent to the endpoint.
  221 + * This callback is also called when a reconnect fails because the paho library reuses the initial connect command!
  222 + * The connection status is set to Disconnected when the connection state is ConnectInProgress, othwerwise the connection status is left as is.
  223 + * @param response A failure response.
  224 + */
  225 + void onConnectFailureOnInstance(const MqttFailure& response);
  226 +
  227 + //void onDisconnectOnInstance(enum MQTTReasonCodes reasonCode); for MQTT V5 which is not supported by centos7 paho-c
  228 +
  229 + /**
  230 + * @brief Callback that is called when a disconnect call succeeds.
  231 + * The connection status is set to Disconnected.
  232 + * @param response A success response with no specific data.
  233 + */
  234 + void onDisconnectSuccessOnInstance(const MqttSuccess& response);
  235 +
  236 + /**
  237 + * @brief Callback that is called when a disconnect call fails after being sent to the endpoint.
  238 + * Based on the result returned by the paho library The connection status is set to Disconnected or Connected.
  239 + * @param response A failure response.
  240 + */
  241 + void onDisconnectFailureOnInstance(const MqttFailure& response);
  242 +
  243 + /**
  244 + * @brief Callback that is called when a publish call succeeds.
  245 + * This callback is called before the delivery complete callback.
  246 + * @param response A success response with the published message.
  247 + */
  248 + void onPublishSuccessOnInstance(const MqttSuccess& response);
  249 +
  250 + /**
  251 + * @brief Callback that is called when a publish call fails after being sent to the endpoint.
  252 + * @param response A failure response.
  253 + */
  254 + void onPublishFailureOnInstance(const MqttFailure& response);
  255 +
  256 + /**
  257 + * @brief Callback that is called when a subscribe call succeeds.
  258 + * @param response A success response with the subscription information. The actual used qos is conveyed in this response.
  259 + */
  260 + void onSubscribeSuccessOnInstance(const MqttSuccess& response);
  261 +
  262 + /**
  263 + * @brief Callback that is called when a subscribe call fails after being sent to the endpoint.
  264 + * @param response A failure response.
  265 + */
  266 + void onSubscribeFailureOnInstance(const MqttFailure& response);
  267 +
  268 + /**
  269 + * @brief Callback that is called when an unsubscribe call succeeds.
  270 + * @param response A success response with no specific data.
  271 + */
  272 + void onUnsubscribeSuccessOnInstance(const MqttSuccess& response);
  273 +
  274 + /**
  275 + * @brief Callback that is called when an unsubscribe call fails after being sent to the endpoint.
  276 + * @param response A failure response.
  277 + */
  278 + void onUnsubscribeFailureOnInstance(const MqttFailure& response);
  279 +
  280 + /**
  281 + * @brief Callback that is called when a message is received.
  282 + * @param message The message payload and meta data.
  283 + */
  284 + int onMessageArrivedOnInstance(const MqttMessage& message);
  285 +
  286 + /**
  287 + * @brief Callback that is called when the delivery of a publish message is considered complete.
  288 + * The definition of complete depends on the quality of service used in the publish command.
  289 + * @param token The token with the publish command is sent.
  290 + */
  291 + void onDeliveryCompleteOnInstance(MQTTAsync_token token);
  292 +
  293 + /**
  294 + * @brief Callback that is called when the connection is broken.
  295 + * @param cause The reason string. Always "cause unknown" for mqtt3 endpoints.
  296 + */
  297 + void onConnectionLostOnInstance(const std::string& cause);
  298 +
  299 + // Static callback functions that are registered on the paho library. Functions call their *OnInstance() counterparts.
  300 + static void onConnect(void* context, char* cause);
  301 + static void onConnectSuccess(void* context, MQTTAsync_successData* response);
  302 + static void onConnectFailure(void* context, MQTTAsync_failureData* response);
  303 + //static void onDisconnect(void* context, MQTTProperties* properties, enum MQTTReasonCodes reasonCode); for MQTT V5 which is not supported by centos7 paho-c
  304 + static void onDisconnectSuccess(void* context, MQTTAsync_successData* response);
  305 + static void onDisconnectFailure(void* context, MQTTAsync_failureData* response);
  306 + static void onPublishSuccess(void* context, MQTTAsync_successData* response);
  307 + static void onPublishFailure(void* context, MQTTAsync_failureData* response);
  308 + static void onSubscribeSuccess(void* context, MQTTAsync_successData* response);
  309 + static void onSubscribeFailure(void* context, MQTTAsync_failureData* response);
  310 + static void onUnsubscribeSuccess(void* context, MQTTAsync_successData* response);
  311 + static void onUnsubscribeFailure(void* context, MQTTAsync_failureData* response);
  312 + static int onMessageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* message);
  313 + static void onDeliveryComplete(void* context, MQTTAsync_token token);
  314 + static void onConnectionLost(void* context, char* cause);
  315 +
  316 + /**
  317 + * @brief Connects the paho logging to the mlogic logging system.
  318 + * This callback is registered the first time a ClientPaho instance is constructed.
  319 + */
  320 + static void onLogPaho(enum MQTTASYNC_TRACE_LEVELS level, char* message);
  321 +
  322 + mutable std::mutex m_mutex;
  323 + std::string m_endpoint;
  324 + std::string m_username;
  325 + std::string m_password;
  326 + std::string m_clientId;
  327 + std::set<MQTTAsync_token> m_pendingOperations;
  328 + std::map<MQTTAsync_token, bool> m_operationResult;
  329 + mutable std::condition_variable m_operationsCompleteCV;
  330 + std::map<std::string, Subscription> m_subscriptions;
  331 + std::map<std::string, Subscription> m_pendingSubscriptions;
  332 + std::map<MQTTAsync_token, std::string> m_subscribeTokenToTopic;
  333 + std::map<MQTTAsync_token, std::string> m_unsubscribeTokenToTopic;
  334 + std::deque<Publish> m_pendingPublishes;
  335 + bool m_processPendingPublishes;
  336 + mutable std::condition_variable m_pendingPublishesReadyCV;
  337 + ::MQTTAsync m_client;
  338 + std::atomic<ConnectionStatus> m_connectionStatus;
  339 + std::function<void(const std::string&, ConnectionStatus)> m_connectionStatusCallback;
  340 + std::function<void(const std::string&, std::int32_t)> m_deliveryCompleteCallback;
  341 + MQTTAsync_token m_lastUnsubscribe; ///< centos7 workaround
  342 + std::unique_ptr<std::promise<void>> m_connectPromise;
  343 + std::unique_ptr<std::promise<void>> m_disconnectPromise;
  344 +
  345 + SynchronizedQueue<std::function<void()>> m_callbackEventQueue;
  346 + std::thread m_workerThread;
  347 +
  348 + static std::atomic_int s_numberOfInstances;
  349 +};
  350 +
  351 +} // End namespace mqtt
  352 +} // End namespace components
  353 +} // End namespace osdev
  354 +
  355 +#endif // OSDEV_COMPONENTS_MQTT_CLIENTPAHO_H
... ...
include/commondefs.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_COMMONDEFS_H
  23 +#define OSDEV_COMPONENTS_MQTT_COMMONDEFS_H
  24 +
  25 +// std
  26 +#include <chrono>
  27 +#include <cstdint>
  28 +#include <map>
  29 +#include <ostream>
  30 +#include <set>
  31 +#include <string>
  32 +#include <vector>
  33 +
  34 +// boost
  35 +#include <boost/lexical_cast.hpp>
  36 +#include <boost/optional.hpp>
  37 +#include <boost/uuid/uuid.hpp>
  38 +#include <boost/uuid/uuid_io.hpp>
  39 +
  40 +#include "utils.h"
  41 +
  42 +
  43 +/// @brief Check if an id is valid
  44 +/// @throws InvalidArgumentException if id is invalid
  45 +#define OSDEV_COMPONENTS_CHECKMQTTID(id) \
  46 + [&] { \
  47 + if (id.is_nil()) { \
  48 + } \
  49 + }()
  50 +
  51 +
  52 +namespace osdev {
  53 +namespace components {
  54 +namespace mqtt {
  55 +
  56 +using MqttId = boost::uuids::uuid;
  57 +using OptionalId = MqttId;
  58 +using MqttIdSet = std::set<MqttId>;
  59 +using MqttIdSetIterator = MqttIdSet::const_iterator;
  60 +using MqttIdSetDelta = std::pair<MqttIdSet, MqttIdSet>;
  61 +using StdTime = std::chrono::system_clock::time_point;
  62 +using OptionalTime = boost::optional<StdTime>;
  63 +using StdTimeVec = std::vector<StdTime>;
  64 +using SequenceNumber = std::uint32_t;
  65 +using OptionalSequenceNumber = boost::optional<SequenceNumber>;
  66 +using CustomField = std::string;
  67 +using CustomFieldCollection = std::vector<CustomField>;
  68 +
  69 +using CountryCodeEnum = std::int32_t;
  70 +
  71 +/**
  72 + * @brief Defines a parsed uri.
  73 + */
  74 +using ParsedUri = std::map<std::string, std::string>;
  75 +
  76 +/**
  77 + * @brief Type for the parsed query part of a uri.
  78 + */
  79 +using ParsedQuery = std::map<std::string, std::string>;
  80 +
  81 +/**
  82 + * @brief Type for the parsed path part of a uri.
  83 + */
  84 +using ParsedPath = std::vector<std::string>;
  85 +
  86 +/**
  87 + * @brief Defines a duration with the granularity of a day in seconds (24 * 60 * 60 = 86400).
  88 + * This duration can be used to create a time_point at midnight of a given DateTime amongst others.
  89 + *
  90 + * The representation is a signed type so that negative durations are also possible.
  91 + */
  92 +using days = std::chrono::duration<std::int32_t, std::ratio<86400>>;
  93 +
  94 +/**
  95 + * @brief Defines a duration with the granularity of a year in seconds. A year is a bit longer than 365 days (365.2425). If a year is
  96 + * subtracted from a date the time part of the new date will therefore differ from the time part of the subtracted from date.
  97 + *
  98 + * The representation is a signed type so that negative durations are also possible.
  99 + */
  100 +using years = std::chrono::duration<std::int32_t, std::ratio<31556952>>; // excactly 365 days would give 31536000
  101 +
  102 +/**
  103 + * A timepoint type that is printed with millisecond resolution.
  104 + */
  105 +struct StdTimeMs
  106 +{
  107 + StdTimeMs(const StdTime& tp)
  108 + : timePoint(tp)
  109 + {
  110 + }
  111 +
  112 + operator StdTime() const { return timePoint; }
  113 +
  114 + StdTime timePoint;
  115 +};
  116 +
  117 +std::ostream& operator<<(std::ostream& os, const StdTimeMs& rhs);
  118 +
  119 +} // End namespace mqtt
  120 +} // End namespace components
  121 +} // End namespace osdev
  122 +
  123 +namespace std {
  124 +
  125 +std::ostream& operator<<(std::ostream& os, const osdev::components::mqtt::StdTime& rhs);
  126 +
  127 +} // End namespace std
  128 +
  129 +#endif // OSDEV_COMPONENTS_MQTT_COMMONDEFS_H
... ...
include/compat-c++14.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_COMPATCXX14
  23 +#define OSDEV_COMPONENTS_COMPATCXX14
  24 +
  25 +#include <memory>
  26 +
  27 +// The below code must be skipped if we use a C++14 or newer compiler
  28 +#if __cplusplus == 201103L
  29 +
  30 +namespace std {
  31 +/// Copied from libstdc++ 4.9.2 bits/unique_ptr.h
  32 +
  33 +template <typename _Tp>
  34 +struct _MakeUniq
  35 +{
  36 + typedef unique_ptr<_Tp> __single_object;
  37 +};
  38 +
  39 +template <typename _Tp>
  40 +struct _MakeUniq<_Tp[]>
  41 +{
  42 + typedef unique_ptr<_Tp[]> __array;
  43 +};
  44 +
  45 +template <typename _Tp, size_t _Bound>
  46 +struct _MakeUniq<_Tp[_Bound]>
  47 +{
  48 + struct __invalid_type
  49 + {
  50 + };
  51 +};
  52 +
  53 +/// std::make_unique for single objects
  54 +template <typename _Tp, typename... _Args>
  55 +inline typename _MakeUniq<_Tp>::__single_object make_unique(_Args&&... __args)
  56 +{
  57 + return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...));
  58 +}
  59 +
  60 +/// std::make_unique for arrays of unknown bound
  61 +template <typename _Tp>
  62 +inline typename _MakeUniq<_Tp>::__array make_unique(size_t __num)
  63 +{
  64 + return unique_ptr<_Tp>(new typename remove_extent<_Tp>::type[__num]());
  65 +}
  66 +
  67 +/// Disable std::make_unique for arrays of known bound
  68 +template <typename _Tp, typename... _Args>
  69 +inline typename _MakeUniq<_Tp>::__invalid_type make_unique(_Args&&...) = delete;
  70 +
  71 +} // End namespace std
  72 +
  73 +#endif // Check for C++14
  74 +
  75 +#endif
... ...
include/compat-chrono.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#include <chrono>
  23 +#include <type_traits>
  24 +
  25 +// function std::chrono::ceil is added in the c++17 standard.
  26 +// Added implementation from https://en.cppreference.com/w/cpp/chrono/duration/ceil
  27 +// to this compatibility header. The header only defines this implementation when
  28 +// the c++ compiler is used with an older standard.
  29 +
  30 +#if !defined __cpp_lib_chrono || __cpp_lib_chrono < 201611
  31 +
  32 +namespace std {
  33 +namespace chrono {
  34 +
  35 +template <class T>
  36 +struct is_duration : std::false_type
  37 +{
  38 +};
  39 +template <class Rep, class Period>
  40 +struct is_duration<
  41 + std::chrono::duration<Rep, Period>> : std::true_type
  42 +{
  43 +};
  44 +
  45 +template <class To, class Rep, class Period,
  46 + class = typename enable_if<is_duration<To>{}>::type>
  47 +To ceil(const std::chrono::duration<Rep, Period>& d)
  48 +{
  49 + To t = std::chrono::duration_cast<To>(d);
  50 + if (t < d)
  51 + {
  52 + return t + To{ 1 };
  53 + }
  54 +
  55 + // or else...
  56 + return t;
  57 +}
  58 +
  59 +} // End namespace chrono
  60 +} // End namespace std
  61 +
  62 +#endif
... ...
include/compiletimedigits.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_COMPILETIMEDIGITS_H
  23 +#define OSDEV_COMPONENTS_MQTT_COMPILETIMEDIGITS_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +#include "compiletimestring.h"
  29 +
  30 +namespace osdev {
  31 +namespace components {
  32 +namespace mqtt {
  33 +
  34 +/**
  35 + * @brief Calculate the number of digits needed to represent a given value in a given base.
  36 + * @tparam B The base to use.
  37 + * @param value The value to represent.
  38 + * @return The number of digits.
  39 + */
  40 +template <unsigned B>
  41 +constexpr std::size_t numberOfDigits(unsigned value) noexcept
  42 +{
  43 + static_assert(B > 0, "base must be larger than zero");
  44 + return (value / B) == 0 ? 1 : (1 + numberOfDigits<B>(value / B));
  45 +}
  46 +
  47 +/**
  48 + * @return stringified digit that represents the given single digit value.
  49 + * @note Values can range from 0 to 37 inclusive which maps on 0-9A-Z.
  50 + */
  51 +template <unsigned value>
  52 +constexpr char digit() noexcept
  53 +{
  54 + static_assert(value <= 37, "Value must lie between 0 and 37 both inclusive");
  55 + return (value < 10 ? '0' + static_cast<char>(value) : 'A' + static_cast<char>(value) - 10);
  56 +}
  57 +
  58 +/**
  59 + * @brief Recursive template definition that is used to determine the value of a given number N in a given base B.
  60 + * @tparam N The number to obtain the representation for.
  61 + * @tparam B The base to use.
  62 + * @tparam Len The length of the representation including the 0 terminator.
  63 + * If not provided the length is calculated on first instantiation of this template.
  64 + * @tparam remains Empty parameter pack on first instantiation. Will be filled further by every following instantiation.
  65 + */
  66 +template <unsigned N, unsigned B, std::size_t Len = numberOfDigits<B>(N) + 1, unsigned... remains>
  67 +struct Digits
  68 +{
  69 +
  70 + static constexpr auto create() noexcept -> std::array<char, Len>
  71 + {
  72 + static_assert(B > 0, "base must be larger than zero");
  73 + return Digits<N / B, B, Len, digit<N % B>(), remains...>::create();
  74 + }
  75 +};
  76 +
  77 +/**
  78 + * @brief Termination template that will return the actual hex digit array that represents the given value.
  79 + * @tparam B The base to use.
  80 + * @tparam Len The length of the hexadecimal representation including 0 terminator.
  81 + * @tparam remains Parameter pack that contains the hexadecimal character representations.
  82 + */
  83 +template <unsigned B, std::size_t Len, unsigned... remains>
  84 +struct Digits<0, B, Len, remains...>
  85 +{
  86 +
  87 + static constexpr auto create() noexcept -> std::array<char, Len>
  88 + {
  89 + static_assert(sizeof...(remains) + 1 == Len, "Parameter 'Len' must be equal to the length of the parameter pack 'remains' including the termination zero");
  90 + return std::array<char, Len>{ { remains..., 0 } };
  91 + }
  92 +};
  93 +
  94 +/**
  95 + * @brief Digits builder can be used in combination with the compiletime_string.
  96 + * @tparam N The number to obtain the compile time string representation for.
  97 + * @tparam B The base to use (up to 37).
  98 + * This template struct defines a produce() method and the return type of that method.
  99 + */
  100 +template <unsigned N, unsigned B>
  101 +struct ProduceDigits
  102 +{
  103 + /**
  104 + * @brief Return type definition of the produce method.
  105 + */
  106 + using return_t = std::array<char, numberOfDigits<B>(N) + 1>;
  107 +
  108 + /**
  109 + * @brief Produce the actual representation.
  110 + */
  111 + static constexpr return_t produce() noexcept
  112 + {
  113 + return Digits<N, B>::create();
  114 + }
  115 +};
  116 +
  117 +} // End namespace mqtt
  118 +} // End namespace components
  119 +} // End namespace osdev
  120 +
  121 +#endif // OSDEV_COMPONENTS_MQTT_COMPILETIMEDIGITS_H
... ...
include/compiletimestring.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H
  23 +#define OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H
  24 +
  25 +/// @file
  26 +///
  27 +/// Support for compiletime string building.
  28 +///
  29 +/// In some cases it is necessary to embed string messages as read only string literals
  30 +/// so that they can be used in low memory conditions for example.
  31 +/// This header defines two macro's that can be used to build managed string literals.
  32 +/// These managed string literals can be concatenated and searched at compiletime.
  33 +/// When the compiler is done with this code only the embedded string literal that are
  34 +/// used by normal expressions remain in the binary.
  35 +/// The implementation is a mix of meta template programming and the use of constexpr
  36 +/// expressions and is based on an idea found on <a href="http://stackoverflow.com/a/15912824">stackoverflow</a>.
  37 +/// This code is meant to handle small strings (up to say 100 characters). Larger strings can trigger the maximum
  38 +/// template recursion depth of the compiler (-ftemplate-depth). This depth is set at 900. So in principle strings
  39 +/// of 900 characters can be handled. However this is very inefficient and will prolong compiletime severly.
  40 +///
  41 +/// Here follows an example of how this code can be used.
  42 +/// @code
  43 +/// auto str1 = OSDEV_COMPONENTS_CSTRING("This is a test");
  44 +/// auto str2 = OSDEV_COMPONENTS_CSTRING(" with concatenation");
  45 +/// auto str3 = str1 + str2;
  46 +/// const char* s = str3.chars;
  47 +/// @endcode
  48 +/// str3.chars contains the compiletime concatenated string.
  49 +///
  50 +/// When examing the binary one will see that it contains the null terminated string literal
  51 +/// @code
  52 +/// This is a test with concatenation^@
  53 +/// @endcode
  54 +/// The str1 and str2 literals are discarded since they are never used in a non compiletime way.
  55 +/// @note This code is not meant to handle string literals with internal \0 characters. Beware!
  56 +
  57 +#include <limits>
  58 +
  59 +#include "metaprogrammingdefs.h"
  60 +
  61 +/// @brief Build a managed substring string literal from a string literal input
  62 +/// The strings are build compile time.
  63 +/// The upper bound is one past the last character that the caller is interested in.
  64 +#define OSDEV_COMPONENTS_CSTRING_BOUNDED(string_literal, lower, upper) \
  65 + [] { \
  66 + constexpr std::size_t lb = (lower); \
  67 + constexpr std::size_t ub = (upper); \
  68 + static_assert(lb <= ub, "lower bound must be smaller than or equal to upper bound"); \
  69 + static_assert(ub < sizeof(string_literal), \
  70 + "upper bound must be smaller than or equal to the length of the string"); \
  71 + struct constexpr_string_type \
  72 + { \
  73 + const char* chars = string_literal; \
  74 + }; \
  75 + return typename osdev::components::mqtt::apply_bounded_range<lb, ub, \
  76 + osdev::components::mqtt::string_builder<constexpr_string_type>::template produce>::result{}; \
  77 + }()
  78 +
  79 +/// @brief Build a managed string literal from a string literal input
  80 +#define OSDEV_COMPONENTS_CSTRING(string_literal) \
  81 + OSDEV_COMPONENTS_CSTRING_BOUNDED(string_literal, 0, sizeof(string_literal) - 1)
  82 +
  83 +namespace osdev {
  84 +namespace components {
  85 +namespace mqtt {
  86 +
  87 +/// @brief Managed string literal.
  88 +/// This class is used to hold string literals at compile time.
  89 +template <char... str>
  90 +struct compiletime_string
  91 +{
  92 + /// @brief Declaration of the string
  93 + static constexpr const char chars[sizeof...(str) + 1] = { str..., '\0' };
  94 +};
  95 +
  96 +/// @brief Definition of the string
  97 +template <char... str>
  98 +constexpr const char compiletime_string<str...>::chars[sizeof...(str) + 1];
  99 +
  100 +/// @brief Managed string literal builder.
  101 +/// This class is used to build string literals at compile time.
  102 +template <typename lambda_str_type>
  103 +struct string_builder
  104 +{
  105 + /// @brief maps indices list on the char array
  106 + template <std::size_t... indices>
  107 + struct produce
  108 + {
  109 + typedef compiletime_string<lambda_str_type{}.chars[indices]...> result;
  110 + };
  111 +};
  112 +
  113 +/// @brief Adapter for coupling other producers to string_builder.
  114 +/// tparam T The type of producer to adapt.
  115 +/// @note The adapter must define its return type as return_t and provide a static method produce() which produces the result.
  116 +/// The return type must be a kind of array type of chars (char[len], std::array<char,len>, etc).
  117 +template <typename T>
  118 +struct adapt_for_string_builder
  119 +{
  120 + static constexpr typename T::return_t chars = T::produce();
  121 +};
  122 +
  123 +/// @brief Concatenate two managed string literals
  124 +/// The literals are packed in a variadic template.
  125 +/// These templates are formed by the MLOGIC_COMMON_CSTRING* macro's
  126 +/// The concatenation is done at compiletime.
  127 +template <char... str0, char... str1>
  128 +compiletime_string<str0..., str1...> operator+(compiletime_string<str0...>, compiletime_string<str1...>)
  129 +{
  130 + return {};
  131 +}
  132 +
  133 +/// @brief Search for a character in a string literal from the right side.
  134 +/// The character index is returned relative to the left side.
  135 +/// If the character is not found a std::size_t(-1) is returned.
  136 +/// The default search starts at the end of the string. The index
  137 +/// can be given to start the search at another point in the string.
  138 +/// The index is given as nr of characters from the right end side of
  139 +/// the string. To proceed with a search see following example.
  140 +/// @code
  141 +/// constexpr const char s[] = "This is a test";
  142 +/// constexpr std::size_t index = osdev_components::mqtt::rfind(s, 'i'); // gives 5
  143 +/// static_assert(index == 5, "");
  144 +/// constexpr std::size_t index2 = osdev::components::mqtt::rfind(s, 'i', sizeof(s) - index); // gives 2
  145 +/// static_assert(index2 == 2, "");
  146 +/// @endcode
  147 +/// This function should only be used at compile time!
  148 +template <std::size_t N>
  149 +constexpr std::size_t rfind(const char (&str)[N], char c, std::size_t index = 0)
  150 +{
  151 + return index >= N ? std::numeric_limits<std::size_t>::max() : str[N - index - 1] == c ? N - index - 1 : rfind(str, c, index + 1);
  152 +}
  153 +
  154 +} // End namespace mqtt
  155 +} // End namespace components
  156 +} // End namespace osdev
  157 +
  158 +#endif // OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H
... ...
include/connectionstatus.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_CONNECTIONSTATUS_H
  23 +#define OSDEV_COMPONENTS_MQTT_CONNECTIONSTATUS_H
  24 +
  25 +// std
  26 +#include <ostream>
  27 +
  28 +namespace osdev {
  29 +namespace components {
  30 +namespace mqtt {
  31 +
  32 +/*!
  33 + * \brief Enumeration for MQTT connection Status
  34 + */
  35 +enum class ConnectionStatus
  36 +{
  37 + Disconnected, ///< Client is disconnected.
  38 + DisconnectInProgress, ///< Client is being disconnected.
  39 + ConnectInProgress, ///< Client is being connected.
  40 + ReconnectInProgress, ///< Client tries to reconnect.
  41 + Connected, ///< Client is connected.
  42 +};
  43 +
  44 +/*!
  45 + * \brief Stream operator for the connection status
  46 + */
  47 +std::ostream& operator<<(std::ostream &os, ConnectionStatus rhs);
  48 +
  49 +} // End namespace mqtt
  50 +} // End namespace components
  51 +} // End namespace osdev
  52 +
  53 +#endif // OSDEV_COMPONENTS_MQTT_CONNECTIONSTATUS_H
... ...
include/credentials.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_CREDENTIALS_H
  23 +#define OSDEV_COMPONENTS_MQTT_CREDENTIALS_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +namespace osdev {
  29 +namespace components {
  30 +namespace mqtt {
  31 +
  32 +/*!
  33 + * \brief Class that holds user credentials
  34 + */
  35 +class Credentials
  36 +{
  37 +public:
  38 + /*!
  39 + * \brief Default CTor, empty credentials
  40 + */
  41 + Credentials();
  42 +
  43 + /*!
  44 + * \brief Constructor for username/password credentials
  45 + * \param username - The username
  46 + * \param password - The password
  47 + */
  48 + Credentials(const std::string &username, const std::string &password);
  49 +
  50 + const std::string& username() const { return m_username; }
  51 + const std::string& password() const { return m_password; }
  52 +private:
  53 + std::string m_username;
  54 + std::string m_password;
  55 +};
  56 +
  57 +} // End namespace mqtt
  58 +} // End namespace components
  59 +} // End namespace osdev
  60 +
  61 +#endif // OSDEV_COMPONENTS_MQTT_CREDENTIALS_H
... ...
include/date.h 0 → 100644
Changes suppressed. Click to show
  1 +#ifndef DATE_H
  2 +#define DATE_H
  3 +
  4 +// The MIT License (MIT)
  5 +//
  6 +// Copyright (c) 2015, 2016, 2017 Howard Hinnant
  7 +// Copyright (c) 2016 Adrian Colomitchi
  8 +// Copyright (c) 2017 Florian Dang
  9 +// Copyright (c) 2017 Paul Thompson
  10 +// Copyright (c) 2018, 2019 Tomasz Kamiński
  11 +// Copyright (c) 2019 Jiangang Zhuang
  12 +//
  13 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  14 +// of this software and associated documentation files (the "Software"), to deal
  15 +// in the Software without restriction, including without limitation the rights
  16 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  17 +// copies of the Software, and to permit persons to whom the Software is
  18 +// furnished to do so, subject to the following conditions:
  19 +//
  20 +// The above copyright notice and this permission notice shall be included in all
  21 +// copies or substantial portions of the Software.
  22 +//
  23 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  26 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  28 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  29 +// SOFTWARE.
  30 +//
  31 +// Our apologies. When the previous paragraph was written, lowercase had not yet
  32 +// been invented (that would involve another several millennia of evolution).
  33 +// We did not mean to shout.
  34 +
  35 +#ifndef HAS_STRING_VIEW
  36 +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
  37 +# define HAS_STRING_VIEW 1
  38 +# else
  39 +# define HAS_STRING_VIEW 0
  40 +# endif
  41 +#endif // HAS_STRING_VIEW
  42 +
  43 +#include <cassert>
  44 +#include <algorithm>
  45 +#include <cctype>
  46 +#include <chrono>
  47 +#include <climits>
  48 +#include <cmath>
  49 +#include <cstddef>
  50 +#include <cstdint>
  51 +#include <cstdlib>
  52 +#include <ctime>
  53 +#include <ios>
  54 +#include <istream>
  55 +#include <iterator>
  56 +#include <limits>
  57 +#include <locale>
  58 +#include <memory>
  59 +#include <ostream>
  60 +#include <ratio>
  61 +#include <sstream>
  62 +#include <stdexcept>
  63 +#include <string>
  64 +#if HAS_STRING_VIEW
  65 +# include <string_view>
  66 +#endif
  67 +#include <utility>
  68 +#include <type_traits>
  69 +
  70 +#ifdef __GNUC__
  71 +# pragma GCC diagnostic push
  72 +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7)
  73 +# pragma GCC diagnostic ignored "-Wpedantic"
  74 +# endif
  75 +# if __GNUC__ < 5
  76 + // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers
  77 +# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
  78 +# endif
  79 +#endif
  80 +
  81 +#ifdef _MSC_VER
  82 +# pragma warning(push)
  83 +// warning C4127: conditional expression is constant
  84 +# pragma warning(disable : 4127)
  85 +#endif
  86 +
  87 +namespace date
  88 +{
  89 +
  90 +//---------------+
  91 +// Configuration |
  92 +//---------------+
  93 +
  94 +#ifndef ONLY_C_LOCALE
  95 +# define ONLY_C_LOCALE 0
  96 +#endif
  97 +
  98 +#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910))
  99 +// MSVC
  100 +# ifndef _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING
  101 +# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING
  102 +# endif
  103 +# if _MSC_VER < 1910
  104 +// before VS2017
  105 +# define CONSTDATA const
  106 +# define CONSTCD11
  107 +# define CONSTCD14
  108 +# define NOEXCEPT _NOEXCEPT
  109 +# else
  110 +// VS2017 and later
  111 +# define CONSTDATA constexpr const
  112 +# define CONSTCD11 constexpr
  113 +# define CONSTCD14 constexpr
  114 +# define NOEXCEPT noexcept
  115 +# endif
  116 +
  117 +#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150
  118 +// Oracle Developer Studio 12.6 and earlier
  119 +# define CONSTDATA constexpr const
  120 +# define CONSTCD11 constexpr
  121 +# define CONSTCD14
  122 +# define NOEXCEPT noexcept
  123 +
  124 +#elif __cplusplus >= 201402
  125 +// C++14
  126 +# define CONSTDATA constexpr const
  127 +# define CONSTCD11 constexpr
  128 +# define CONSTCD14 constexpr
  129 +# define NOEXCEPT noexcept
  130 +#else
  131 +// C++11
  132 +# define CONSTDATA constexpr const
  133 +# define CONSTCD11 constexpr
  134 +# define CONSTCD14
  135 +# define NOEXCEPT noexcept
  136 +#endif
  137 +
  138 +#ifndef HAS_UNCAUGHT_EXCEPTIONS
  139 +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
  140 +# define HAS_UNCAUGHT_EXCEPTIONS 1
  141 +# else
  142 +# define HAS_UNCAUGHT_EXCEPTIONS 0
  143 +# endif
  144 +#endif // HAS_UNCAUGHT_EXCEPTIONS
  145 +
  146 +#ifndef HAS_VOID_T
  147 +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
  148 +# define HAS_VOID_T 1
  149 +# else
  150 +# define HAS_VOID_T 0
  151 +# endif
  152 +#endif // HAS_VOID_T
  153 +
  154 +// Protect from Oracle sun macro
  155 +#ifdef sun
  156 +# undef sun
  157 +#endif
  158 +
  159 +// Work around for a NVCC compiler bug which causes it to fail
  160 +// to compile std::ratio_{multiply,divide} when used directly
  161 +// in the std::chrono::duration template instantiations below
  162 +namespace detail {
  163 +template <typename R1, typename R2>
  164 +using ratio_multiply = decltype(std::ratio_multiply<R1, R2>{});
  165 +
  166 +template <typename R1, typename R2>
  167 +using ratio_divide = decltype(std::ratio_divide<R1, R2>{});
  168 +} // namespace detail
  169 +
  170 +//-----------+
  171 +// Interface |
  172 +//-----------+
  173 +
  174 +// durations
  175 +
  176 +using days = std::chrono::duration
  177 + <int, detail::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
  178 +
  179 +using weeks = std::chrono::duration
  180 + <int, detail::ratio_multiply<std::ratio<7>, days::period>>;
  181 +
  182 +using years = std::chrono::duration
  183 + <int, detail::ratio_multiply<std::ratio<146097, 400>, days::period>>;
  184 +
  185 +using months = std::chrono::duration
  186 + <int, detail::ratio_divide<years::period, std::ratio<12>>>;
  187 +
  188 +// time_point
  189 +
  190 +template <class Duration>
  191 + using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
  192 +
  193 +using sys_days = sys_time<days>;
  194 +using sys_seconds = sys_time<std::chrono::seconds>;
  195 +
  196 +struct local_t {};
  197 +
  198 +template <class Duration>
  199 + using local_time = std::chrono::time_point<local_t, Duration>;
  200 +
  201 +using local_seconds = local_time<std::chrono::seconds>;
  202 +using local_days = local_time<days>;
  203 +
  204 +// types
  205 +
  206 +struct last_spec
  207 +{
  208 + explicit last_spec() = default;
  209 +};
  210 +
  211 +class day;
  212 +class month;
  213 +class year;
  214 +
  215 +class weekday;
  216 +class weekday_indexed;
  217 +class weekday_last;
  218 +
  219 +class month_day;
  220 +class month_day_last;
  221 +class month_weekday;
  222 +class month_weekday_last;
  223 +
  224 +class year_month;
  225 +
  226 +class year_month_day;
  227 +class year_month_day_last;
  228 +class year_month_weekday;
  229 +class year_month_weekday_last;
  230 +
  231 +// date composition operators
  232 +
  233 +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT;
  234 +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT;
  235 +
  236 +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT;
  237 +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT;
  238 +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT;
  239 +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT;
  240 +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT;
  241 +
  242 +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT;
  243 +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT;
  244 +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT;
  245 +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT;
  246 +
  247 +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT;
  248 +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT;
  249 +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT;
  250 +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT;
  251 +
  252 +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT;
  253 +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT;
  254 +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT;
  255 +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT;
  256 +
  257 +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT;
  258 +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT;
  259 +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT;
  260 +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT;
  261 +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT;
  262 +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT;
  263 +
  264 +CONSTCD11
  265 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT;
  266 +CONSTCD11
  267 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT;
  268 +CONSTCD11
  269 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT;
  270 +CONSTCD11
  271 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT;
  272 +CONSTCD11
  273 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT;
  274 +
  275 +CONSTCD11
  276 +year_month_weekday
  277 +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT;
  278 +
  279 +CONSTCD11
  280 +year_month_weekday
  281 +operator/(const year& y, const month_weekday& mwd) NOEXCEPT;
  282 +
  283 +CONSTCD11
  284 +year_month_weekday
  285 +operator/(int y, const month_weekday& mwd) NOEXCEPT;
  286 +
  287 +CONSTCD11
  288 +year_month_weekday
  289 +operator/(const month_weekday& mwd, const year& y) NOEXCEPT;
  290 +
  291 +CONSTCD11
  292 +year_month_weekday
  293 +operator/(const month_weekday& mwd, int y) NOEXCEPT;
  294 +
  295 +CONSTCD11
  296 +year_month_weekday_last
  297 +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT;
  298 +
  299 +CONSTCD11
  300 +year_month_weekday_last
  301 +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT;
  302 +
  303 +CONSTCD11
  304 +year_month_weekday_last
  305 +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT;
  306 +
  307 +CONSTCD11
  308 +year_month_weekday_last
  309 +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT;
  310 +
  311 +CONSTCD11
  312 +year_month_weekday_last
  313 +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT;
  314 +
  315 +// Detailed interface
  316 +
  317 +// day
  318 +
  319 +class day
  320 +{
  321 + unsigned char d_;
  322 +
  323 +public:
  324 + day() = default;
  325 + explicit CONSTCD11 day(unsigned d) NOEXCEPT;
  326 +
  327 + CONSTCD14 day& operator++() NOEXCEPT;
  328 + CONSTCD14 day operator++(int) NOEXCEPT;
  329 + CONSTCD14 day& operator--() NOEXCEPT;
  330 + CONSTCD14 day operator--(int) NOEXCEPT;
  331 +
  332 + CONSTCD14 day& operator+=(const days& d) NOEXCEPT;
  333 + CONSTCD14 day& operator-=(const days& d) NOEXCEPT;
  334 +
  335 + CONSTCD11 explicit operator unsigned() const NOEXCEPT;
  336 + CONSTCD11 bool ok() const NOEXCEPT;
  337 +};
  338 +
  339 +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT;
  340 +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT;
  341 +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT;
  342 +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT;
  343 +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT;
  344 +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT;
  345 +
  346 +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT;
  347 +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT;
  348 +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT;
  349 +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT;
  350 +
  351 +template<class CharT, class Traits>
  352 +std::basic_ostream<CharT, Traits>&
  353 +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d);
  354 +
  355 +// month
  356 +
  357 +class month
  358 +{
  359 + unsigned char m_;
  360 +
  361 +public:
  362 + month() = default;
  363 + explicit CONSTCD11 month(unsigned m) NOEXCEPT;
  364 +
  365 + CONSTCD14 month& operator++() NOEXCEPT;
  366 + CONSTCD14 month operator++(int) NOEXCEPT;
  367 + CONSTCD14 month& operator--() NOEXCEPT;
  368 + CONSTCD14 month operator--(int) NOEXCEPT;
  369 +
  370 + CONSTCD14 month& operator+=(const months& m) NOEXCEPT;
  371 + CONSTCD14 month& operator-=(const months& m) NOEXCEPT;
  372 +
  373 + CONSTCD11 explicit operator unsigned() const NOEXCEPT;
  374 + CONSTCD11 bool ok() const NOEXCEPT;
  375 +};
  376 +
  377 +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT;
  378 +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT;
  379 +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT;
  380 +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT;
  381 +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT;
  382 +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT;
  383 +
  384 +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT;
  385 +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT;
  386 +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT;
  387 +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT;
  388 +
  389 +template<class CharT, class Traits>
  390 +std::basic_ostream<CharT, Traits>&
  391 +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m);
  392 +
  393 +// year
  394 +
  395 +class year
  396 +{
  397 + short y_;
  398 +
  399 +public:
  400 + year() = default;
  401 + explicit CONSTCD11 year(int y) NOEXCEPT;
  402 +
  403 + CONSTCD14 year& operator++() NOEXCEPT;
  404 + CONSTCD14 year operator++(int) NOEXCEPT;
  405 + CONSTCD14 year& operator--() NOEXCEPT;
  406 + CONSTCD14 year operator--(int) NOEXCEPT;
  407 +
  408 + CONSTCD14 year& operator+=(const years& y) NOEXCEPT;
  409 + CONSTCD14 year& operator-=(const years& y) NOEXCEPT;
  410 +
  411 + CONSTCD11 year operator-() const NOEXCEPT;
  412 + CONSTCD11 year operator+() const NOEXCEPT;
  413 +
  414 + CONSTCD11 bool is_leap() const NOEXCEPT;
  415 +
  416 + CONSTCD11 explicit operator int() const NOEXCEPT;
  417 + CONSTCD11 bool ok() const NOEXCEPT;
  418 +
  419 + static CONSTCD11 year min() NOEXCEPT { return year{-32767}; }
  420 + static CONSTCD11 year max() NOEXCEPT { return year{32767}; }
  421 +};
  422 +
  423 +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT;
  424 +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT;
  425 +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT;
  426 +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT;
  427 +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT;
  428 +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT;
  429 +
  430 +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT;
  431 +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT;
  432 +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT;
  433 +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT;
  434 +
  435 +template<class CharT, class Traits>
  436 +std::basic_ostream<CharT, Traits>&
  437 +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y);
  438 +
  439 +// weekday
  440 +
  441 +class weekday
  442 +{
  443 + unsigned char wd_;
  444 +public:
  445 + weekday() = default;
  446 + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT;
  447 + CONSTCD14 weekday(const sys_days& dp) NOEXCEPT;
  448 + CONSTCD14 explicit weekday(const local_days& dp) NOEXCEPT;
  449 +
  450 + CONSTCD14 weekday& operator++() NOEXCEPT;
  451 + CONSTCD14 weekday operator++(int) NOEXCEPT;
  452 + CONSTCD14 weekday& operator--() NOEXCEPT;
  453 + CONSTCD14 weekday operator--(int) NOEXCEPT;
  454 +
  455 + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT;
  456 + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT;
  457 +
  458 + CONSTCD11 bool ok() const NOEXCEPT;
  459 +
  460 + CONSTCD11 unsigned c_encoding() const NOEXCEPT;
  461 + CONSTCD11 unsigned iso_encoding() const NOEXCEPT;
  462 +
  463 + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT;
  464 + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT;
  465 +
  466 +private:
  467 + static CONSTCD14 unsigned char weekday_from_days(int z) NOEXCEPT;
  468 +
  469 + friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT;
  470 + friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT;
  471 + friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT;
  472 + template<class CharT, class Traits>
  473 + friend std::basic_ostream<CharT, Traits>&
  474 + operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd);
  475 + friend class weekday_indexed;
  476 +};
  477 +
  478 +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT;
  479 +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT;
  480 +
  481 +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT;
  482 +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT;
  483 +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT;
  484 +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT;
  485 +
  486 +template<class CharT, class Traits>
  487 +std::basic_ostream<CharT, Traits>&
  488 +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd);
  489 +
  490 +// weekday_indexed
  491 +
  492 +class weekday_indexed
  493 +{
  494 + unsigned char wd_ : 4;
  495 + unsigned char index_ : 4;
  496 +
  497 +public:
  498 + weekday_indexed() = default;
  499 + CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT;
  500 +
  501 + CONSTCD11 date::weekday weekday() const NOEXCEPT;
  502 + CONSTCD11 unsigned index() const NOEXCEPT;
  503 + CONSTCD11 bool ok() const NOEXCEPT;
  504 +};
  505 +
  506 +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT;
  507 +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT;
  508 +
  509 +template<class CharT, class Traits>
  510 +std::basic_ostream<CharT, Traits>&
  511 +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi);
  512 +
  513 +// weekday_last
  514 +
  515 +class weekday_last
  516 +{
  517 + date::weekday wd_;
  518 +
  519 +public:
  520 + explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT;
  521 +
  522 + CONSTCD11 date::weekday weekday() const NOEXCEPT;
  523 + CONSTCD11 bool ok() const NOEXCEPT;
  524 +};
  525 +
  526 +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT;
  527 +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT;
  528 +
  529 +template<class CharT, class Traits>
  530 +std::basic_ostream<CharT, Traits>&
  531 +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl);
  532 +
  533 +namespace detail
  534 +{
  535 +
  536 +struct unspecified_month_disambiguator {};
  537 +
  538 +} // namespace detail
  539 +
  540 +// year_month
  541 +
  542 +class year_month
  543 +{
  544 + date::year y_;
  545 + date::month m_;
  546 +
  547 +public:
  548 + year_month() = default;
  549 + CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT;
  550 +
  551 + CONSTCD11 date::year year() const NOEXCEPT;
  552 + CONSTCD11 date::month month() const NOEXCEPT;
  553 +
  554 + template<class = detail::unspecified_month_disambiguator>
  555 + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT;
  556 + template<class = detail::unspecified_month_disambiguator>
  557 + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT;
  558 + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT;
  559 + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT;
  560 +
  561 + CONSTCD11 bool ok() const NOEXCEPT;
  562 +};
  563 +
  564 +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT;
  565 +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT;
  566 +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT;
  567 +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT;
  568 +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT;
  569 +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT;
  570 +
  571 +template<class = detail::unspecified_month_disambiguator>
  572 +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT;
  573 +template<class = detail::unspecified_month_disambiguator>
  574 +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT;
  575 +template<class = detail::unspecified_month_disambiguator>
  576 +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT;
  577 +
  578 +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT;
  579 +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT;
  580 +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT;
  581 +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT;
  582 +
  583 +template<class CharT, class Traits>
  584 +std::basic_ostream<CharT, Traits>&
  585 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym);
  586 +
  587 +// month_day
  588 +
  589 +class month_day
  590 +{
  591 + date::month m_;
  592 + date::day d_;
  593 +
  594 +public:
  595 + month_day() = default;
  596 + CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT;
  597 +
  598 + CONSTCD11 date::month month() const NOEXCEPT;
  599 + CONSTCD11 date::day day() const NOEXCEPT;
  600 +
  601 + CONSTCD14 bool ok() const NOEXCEPT;
  602 +};
  603 +
  604 +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT;
  605 +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT;
  606 +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT;
  607 +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT;
  608 +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT;
  609 +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT;
  610 +
  611 +template<class CharT, class Traits>
  612 +std::basic_ostream<CharT, Traits>&
  613 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md);
  614 +
  615 +// month_day_last
  616 +
  617 +class month_day_last
  618 +{
  619 + date::month m_;
  620 +
  621 +public:
  622 + CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT;
  623 +
  624 + CONSTCD11 date::month month() const NOEXCEPT;
  625 + CONSTCD11 bool ok() const NOEXCEPT;
  626 +};
  627 +
  628 +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT;
  629 +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT;
  630 +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT;
  631 +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT;
  632 +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT;
  633 +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT;
  634 +
  635 +template<class CharT, class Traits>
  636 +std::basic_ostream<CharT, Traits>&
  637 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl);
  638 +
  639 +// month_weekday
  640 +
  641 +class month_weekday
  642 +{
  643 + date::month m_;
  644 + date::weekday_indexed wdi_;
  645 +public:
  646 + CONSTCD11 month_weekday(const date::month& m,
  647 + const date::weekday_indexed& wdi) NOEXCEPT;
  648 +
  649 + CONSTCD11 date::month month() const NOEXCEPT;
  650 + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT;
  651 +
  652 + CONSTCD11 bool ok() const NOEXCEPT;
  653 +};
  654 +
  655 +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT;
  656 +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT;
  657 +
  658 +template<class CharT, class Traits>
  659 +std::basic_ostream<CharT, Traits>&
  660 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd);
  661 +
  662 +// month_weekday_last
  663 +
  664 +class month_weekday_last
  665 +{
  666 + date::month m_;
  667 + date::weekday_last wdl_;
  668 +
  669 +public:
  670 + CONSTCD11 month_weekday_last(const date::month& m,
  671 + const date::weekday_last& wd) NOEXCEPT;
  672 +
  673 + CONSTCD11 date::month month() const NOEXCEPT;
  674 + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT;
  675 +
  676 + CONSTCD11 bool ok() const NOEXCEPT;
  677 +};
  678 +
  679 +CONSTCD11
  680 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT;
  681 +CONSTCD11
  682 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT;
  683 +
  684 +template<class CharT, class Traits>
  685 +std::basic_ostream<CharT, Traits>&
  686 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl);
  687 +
  688 +// class year_month_day
  689 +
  690 +class year_month_day
  691 +{
  692 + date::year y_;
  693 + date::month m_;
  694 + date::day d_;
  695 +
  696 +public:
  697 + year_month_day() = default;
  698 + CONSTCD11 year_month_day(const date::year& y, const date::month& m,
  699 + const date::day& d) NOEXCEPT;
  700 + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT;
  701 +
  702 + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT;
  703 + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT;
  704 +
  705 + template<class = detail::unspecified_month_disambiguator>
  706 + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT;
  707 + template<class = detail::unspecified_month_disambiguator>
  708 + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT;
  709 + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT;
  710 + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT;
  711 +
  712 + CONSTCD11 date::year year() const NOEXCEPT;
  713 + CONSTCD11 date::month month() const NOEXCEPT;
  714 + CONSTCD11 date::day day() const NOEXCEPT;
  715 +
  716 + CONSTCD14 operator sys_days() const NOEXCEPT;
  717 + CONSTCD14 explicit operator local_days() const NOEXCEPT;
  718 + CONSTCD14 bool ok() const NOEXCEPT;
  719 +
  720 +private:
  721 + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT;
  722 + CONSTCD14 days to_days() const NOEXCEPT;
  723 +};
  724 +
  725 +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT;
  726 +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT;
  727 +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT;
  728 +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT;
  729 +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT;
  730 +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT;
  731 +
  732 +template<class = detail::unspecified_month_disambiguator>
  733 +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT;
  734 +template<class = detail::unspecified_month_disambiguator>
  735 +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT;
  736 +template<class = detail::unspecified_month_disambiguator>
  737 +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT;
  738 +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT;
  739 +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT;
  740 +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT;
  741 +
  742 +template<class CharT, class Traits>
  743 +std::basic_ostream<CharT, Traits>&
  744 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd);
  745 +
  746 +// year_month_day_last
  747 +
  748 +class year_month_day_last
  749 +{
  750 + date::year y_;
  751 + date::month_day_last mdl_;
  752 +
  753 +public:
  754 + CONSTCD11 year_month_day_last(const date::year& y,
  755 + const date::month_day_last& mdl) NOEXCEPT;
  756 +
  757 + template<class = detail::unspecified_month_disambiguator>
  758 + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT;
  759 + template<class = detail::unspecified_month_disambiguator>
  760 + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT;
  761 + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT;
  762 + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT;
  763 +
  764 + CONSTCD11 date::year year() const NOEXCEPT;
  765 + CONSTCD11 date::month month() const NOEXCEPT;
  766 + CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT;
  767 + CONSTCD14 date::day day() const NOEXCEPT;
  768 +
  769 + CONSTCD14 operator sys_days() const NOEXCEPT;
  770 + CONSTCD14 explicit operator local_days() const NOEXCEPT;
  771 + CONSTCD11 bool ok() const NOEXCEPT;
  772 +};
  773 +
  774 +CONSTCD11
  775 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT;
  776 +CONSTCD11
  777 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT;
  778 +CONSTCD11
  779 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT;
  780 +CONSTCD11
  781 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT;
  782 +CONSTCD11
  783 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT;
  784 +CONSTCD11
  785 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT;
  786 +
  787 +template<class = detail::unspecified_month_disambiguator>
  788 +CONSTCD14
  789 +year_month_day_last
  790 +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT;
  791 +
  792 +template<class = detail::unspecified_month_disambiguator>
  793 +CONSTCD14
  794 +year_month_day_last
  795 +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT;
  796 +
  797 +CONSTCD11
  798 +year_month_day_last
  799 +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT;
  800 +
  801 +CONSTCD11
  802 +year_month_day_last
  803 +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT;
  804 +
  805 +template<class = detail::unspecified_month_disambiguator>
  806 +CONSTCD14
  807 +year_month_day_last
  808 +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT;
  809 +
  810 +CONSTCD11
  811 +year_month_day_last
  812 +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT;
  813 +
  814 +template<class CharT, class Traits>
  815 +std::basic_ostream<CharT, Traits>&
  816 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl);
  817 +
  818 +// year_month_weekday
  819 +
  820 +class year_month_weekday
  821 +{
  822 + date::year y_;
  823 + date::month m_;
  824 + date::weekday_indexed wdi_;
  825 +
  826 +public:
  827 + year_month_weekday() = default;
  828 + CONSTCD11 year_month_weekday(const date::year& y, const date::month& m,
  829 + const date::weekday_indexed& wdi) NOEXCEPT;
  830 + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT;
  831 + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT;
  832 +
  833 + template<class = detail::unspecified_month_disambiguator>
  834 + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT;
  835 + template<class = detail::unspecified_month_disambiguator>
  836 + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT;
  837 + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT;
  838 + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT;
  839 +
  840 + CONSTCD11 date::year year() const NOEXCEPT;
  841 + CONSTCD11 date::month month() const NOEXCEPT;
  842 + CONSTCD11 date::weekday weekday() const NOEXCEPT;
  843 + CONSTCD11 unsigned index() const NOEXCEPT;
  844 + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT;
  845 +
  846 + CONSTCD14 operator sys_days() const NOEXCEPT;
  847 + CONSTCD14 explicit operator local_days() const NOEXCEPT;
  848 + CONSTCD14 bool ok() const NOEXCEPT;
  849 +
  850 +private:
  851 + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT;
  852 + CONSTCD14 days to_days() const NOEXCEPT;
  853 +};
  854 +
  855 +CONSTCD11
  856 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT;
  857 +CONSTCD11
  858 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT;
  859 +
  860 +template<class = detail::unspecified_month_disambiguator>
  861 +CONSTCD14
  862 +year_month_weekday
  863 +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT;
  864 +
  865 +template<class = detail::unspecified_month_disambiguator>
  866 +CONSTCD14
  867 +year_month_weekday
  868 +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT;
  869 +
  870 +CONSTCD11
  871 +year_month_weekday
  872 +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT;
  873 +
  874 +CONSTCD11
  875 +year_month_weekday
  876 +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT;
  877 +
  878 +template<class = detail::unspecified_month_disambiguator>
  879 +CONSTCD14
  880 +year_month_weekday
  881 +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT;
  882 +
  883 +CONSTCD11
  884 +year_month_weekday
  885 +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT;
  886 +
  887 +template<class CharT, class Traits>
  888 +std::basic_ostream<CharT, Traits>&
  889 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi);
  890 +
  891 +// year_month_weekday_last
  892 +
  893 +class year_month_weekday_last
  894 +{
  895 + date::year y_;
  896 + date::month m_;
  897 + date::weekday_last wdl_;
  898 +
  899 +public:
  900 + CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m,
  901 + const date::weekday_last& wdl) NOEXCEPT;
  902 +
  903 + template<class = detail::unspecified_month_disambiguator>
  904 + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT;
  905 + template<class = detail::unspecified_month_disambiguator>
  906 + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT;
  907 + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT;
  908 + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT;
  909 +
  910 + CONSTCD11 date::year year() const NOEXCEPT;
  911 + CONSTCD11 date::month month() const NOEXCEPT;
  912 + CONSTCD11 date::weekday weekday() const NOEXCEPT;
  913 + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT;
  914 +
  915 + CONSTCD14 operator sys_days() const NOEXCEPT;
  916 + CONSTCD14 explicit operator local_days() const NOEXCEPT;
  917 + CONSTCD11 bool ok() const NOEXCEPT;
  918 +
  919 +private:
  920 + CONSTCD14 days to_days() const NOEXCEPT;
  921 +};
  922 +
  923 +CONSTCD11
  924 +bool
  925 +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT;
  926 +
  927 +CONSTCD11
  928 +bool
  929 +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT;
  930 +
  931 +template<class = detail::unspecified_month_disambiguator>
  932 +CONSTCD14
  933 +year_month_weekday_last
  934 +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT;
  935 +
  936 +template<class = detail::unspecified_month_disambiguator>
  937 +CONSTCD14
  938 +year_month_weekday_last
  939 +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT;
  940 +
  941 +CONSTCD11
  942 +year_month_weekday_last
  943 +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT;
  944 +
  945 +CONSTCD11
  946 +year_month_weekday_last
  947 +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT;
  948 +
  949 +template<class = detail::unspecified_month_disambiguator>
  950 +CONSTCD14
  951 +year_month_weekday_last
  952 +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT;
  953 +
  954 +CONSTCD11
  955 +year_month_weekday_last
  956 +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT;
  957 +
  958 +template<class CharT, class Traits>
  959 +std::basic_ostream<CharT, Traits>&
  960 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl);
  961 +
  962 +#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
  963 +inline namespace literals
  964 +{
  965 +
  966 +CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT;
  967 +CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT;
  968 +
  969 +} // inline namespace literals
  970 +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
  971 +
  972 +// CONSTDATA date::month January{1};
  973 +// CONSTDATA date::month February{2};
  974 +// CONSTDATA date::month March{3};
  975 +// CONSTDATA date::month April{4};
  976 +// CONSTDATA date::month May{5};
  977 +// CONSTDATA date::month June{6};
  978 +// CONSTDATA date::month July{7};
  979 +// CONSTDATA date::month August{8};
  980 +// CONSTDATA date::month September{9};
  981 +// CONSTDATA date::month October{10};
  982 +// CONSTDATA date::month November{11};
  983 +// CONSTDATA date::month December{12};
  984 +//
  985 +// CONSTDATA date::weekday Sunday{0u};
  986 +// CONSTDATA date::weekday Monday{1u};
  987 +// CONSTDATA date::weekday Tuesday{2u};
  988 +// CONSTDATA date::weekday Wednesday{3u};
  989 +// CONSTDATA date::weekday Thursday{4u};
  990 +// CONSTDATA date::weekday Friday{5u};
  991 +// CONSTDATA date::weekday Saturday{6u};
  992 +
  993 +#if HAS_VOID_T
  994 +
  995 +template <class T, class = std::void_t<>>
  996 +struct is_clock
  997 + : std::false_type
  998 +{};
  999 +
  1000 +template <class T>
  1001 +struct is_clock<T, std::void_t<decltype(T::now()), typename T::rep, typename T::period,
  1002 + typename T::duration, typename T::time_point,
  1003 + decltype(T::is_steady)>>
  1004 + : std::true_type
  1005 +{};
  1006 +
  1007 +template<class T> inline constexpr bool is_clock_v = is_clock<T>::value;
  1008 +
  1009 +#endif // HAS_VOID_T
  1010 +
  1011 +//----------------+
  1012 +// Implementation |
  1013 +//----------------+
  1014 +
  1015 +// utilities
  1016 +namespace detail {
  1017 +
  1018 +template<class CharT, class Traits = std::char_traits<CharT>>
  1019 +class save_istream
  1020 +{
  1021 +protected:
  1022 + std::basic_ios<CharT, Traits>& is_;
  1023 + CharT fill_;
  1024 + std::ios::fmtflags flags_;
  1025 + std::streamsize precision_;
  1026 + std::streamsize width_;
  1027 + std::basic_ostream<CharT, Traits>* tie_;
  1028 + std::locale loc_;
  1029 +
  1030 +public:
  1031 + ~save_istream()
  1032 + {
  1033 + is_.fill(fill_);
  1034 + is_.flags(flags_);
  1035 + is_.precision(precision_);
  1036 + is_.width(width_);
  1037 + is_.imbue(loc_);
  1038 + is_.tie(tie_);
  1039 + }
  1040 +
  1041 + save_istream(const save_istream&) = delete;
  1042 + save_istream& operator=(const save_istream&) = delete;
  1043 +
  1044 + explicit save_istream(std::basic_ios<CharT, Traits>& is)
  1045 + : is_(is)
  1046 + , fill_(is.fill())
  1047 + , flags_(is.flags())
  1048 + , precision_(is.precision())
  1049 + , width_(is.width(0))
  1050 + , tie_(is.tie(nullptr))
  1051 + , loc_(is.getloc())
  1052 + {
  1053 + if (tie_ != nullptr)
  1054 + tie_->flush();
  1055 + }
  1056 +};
  1057 +
  1058 +template<class CharT, class Traits = std::char_traits<CharT>>
  1059 +class save_ostream
  1060 + : private save_istream<CharT, Traits>
  1061 +{
  1062 +public:
  1063 + ~save_ostream()
  1064 + {
  1065 + if ((this->flags_ & std::ios::unitbuf) &&
  1066 +#if HAS_UNCAUGHT_EXCEPTIONS
  1067 + std::uncaught_exceptions() == 0 &&
  1068 +#else
  1069 + !std::uncaught_exception() &&
  1070 +#endif
  1071 + this->is_.good())
  1072 + this->is_.rdbuf()->pubsync();
  1073 + }
  1074 +
  1075 + save_ostream(const save_ostream&) = delete;
  1076 + save_ostream& operator=(const save_ostream&) = delete;
  1077 +
  1078 + explicit save_ostream(std::basic_ios<CharT, Traits>& os)
  1079 + : save_istream<CharT, Traits>(os)
  1080 + {
  1081 + }
  1082 +};
  1083 +
  1084 +template <class T>
  1085 +struct choose_trunc_type
  1086 +{
  1087 + static const int digits = std::numeric_limits<T>::digits;
  1088 + using type = typename std::conditional
  1089 + <
  1090 + digits < 32,
  1091 + std::int32_t,
  1092 + typename std::conditional
  1093 + <
  1094 + digits < 64,
  1095 + std::int64_t,
  1096 +#ifdef __SIZEOF_INT128__
  1097 + __int128
  1098 +#else
  1099 + std::int64_t
  1100 +#endif
  1101 + >::type
  1102 + >::type;
  1103 +};
  1104 +
  1105 +template <class T>
  1106 +CONSTCD11
  1107 +inline
  1108 +typename std::enable_if
  1109 +<
  1110 + !std::chrono::treat_as_floating_point<T>::value,
  1111 + T
  1112 +>::type
  1113 +trunc(T t) NOEXCEPT
  1114 +{
  1115 + return t;
  1116 +}
  1117 +
  1118 +template <class T>
  1119 +CONSTCD14
  1120 +inline
  1121 +typename std::enable_if
  1122 +<
  1123 + std::chrono::treat_as_floating_point<T>::value,
  1124 + T
  1125 +>::type
  1126 +trunc(T t) NOEXCEPT
  1127 +{
  1128 + using std::numeric_limits;
  1129 + using I = typename choose_trunc_type<T>::type;
  1130 + CONSTDATA auto digits = numeric_limits<T>::digits;
  1131 + static_assert(digits < numeric_limits<I>::digits, "");
  1132 + CONSTDATA auto max = I{1} << (digits-1);
  1133 + CONSTDATA auto min = -max;
  1134 + const auto negative = t < T{0};
  1135 + if (min <= t && t <= max && t != 0 && t == t)
  1136 + {
  1137 + t = static_cast<T>(static_cast<I>(t));
  1138 + if (t == 0 && negative)
  1139 + t = -t;
  1140 + }
  1141 + return t;
  1142 +}
  1143 +
  1144 +template <std::intmax_t Xp, std::intmax_t Yp>
  1145 +struct static_gcd
  1146 +{
  1147 + static const std::intmax_t value = static_gcd<Yp, Xp % Yp>::value;
  1148 +};
  1149 +
  1150 +template <std::intmax_t Xp>
  1151 +struct static_gcd<Xp, 0>
  1152 +{
  1153 + static const std::intmax_t value = Xp;
  1154 +};
  1155 +
  1156 +template <>
  1157 +struct static_gcd<0, 0>
  1158 +{
  1159 + static const std::intmax_t value = 1;
  1160 +};
  1161 +
  1162 +template <class R1, class R2>
  1163 +struct no_overflow
  1164 +{
  1165 +private:
  1166 + static const std::intmax_t gcd_n1_n2 = static_gcd<R1::num, R2::num>::value;
  1167 + static const std::intmax_t gcd_d1_d2 = static_gcd<R1::den, R2::den>::value;
  1168 + static const std::intmax_t n1 = R1::num / gcd_n1_n2;
  1169 + static const std::intmax_t d1 = R1::den / gcd_d1_d2;
  1170 + static const std::intmax_t n2 = R2::num / gcd_n1_n2;
  1171 + static const std::intmax_t d2 = R2::den / gcd_d1_d2;
  1172 +#ifdef __cpp_constexpr
  1173 + static const std::intmax_t max = std::numeric_limits<std::intmax_t>::max();
  1174 +#else
  1175 + static const std::intmax_t max = LLONG_MAX;
  1176 +#endif
  1177 +
  1178 + template <std::intmax_t Xp, std::intmax_t Yp, bool overflow>
  1179 + struct mul // overflow == false
  1180 + {
  1181 + static const std::intmax_t value = Xp * Yp;
  1182 + };
  1183 +
  1184 + template <std::intmax_t Xp, std::intmax_t Yp>
  1185 + struct mul<Xp, Yp, true>
  1186 + {
  1187 + static const std::intmax_t value = 1;
  1188 + };
  1189 +
  1190 +public:
  1191 + static const bool value = (n1 <= max / d2) && (n2 <= max / d1);
  1192 + typedef std::ratio<mul<n1, d2, !value>::value,
  1193 + mul<n2, d1, !value>::value> type;
  1194 +};
  1195 +
  1196 +} // detail
  1197 +
  1198 +// trunc towards zero
  1199 +template <class To, class Rep, class Period>
  1200 +CONSTCD11
  1201 +inline
  1202 +typename std::enable_if
  1203 +<
  1204 + detail::no_overflow<Period, typename To::period>::value,
  1205 + To
  1206 +>::type
  1207 +trunc(const std::chrono::duration<Rep, Period>& d)
  1208 +{
  1209 + return To{detail::trunc(std::chrono::duration_cast<To>(d).count())};
  1210 +}
  1211 +
  1212 +template <class To, class Rep, class Period>
  1213 +CONSTCD11
  1214 +inline
  1215 +typename std::enable_if
  1216 +<
  1217 + !detail::no_overflow<Period, typename To::period>::value,
  1218 + To
  1219 +>::type
  1220 +trunc(const std::chrono::duration<Rep, Period>& d)
  1221 +{
  1222 + using std::chrono::duration_cast;
  1223 + using std::chrono::duration;
  1224 + using rep = typename std::common_type<Rep, typename To::rep>::type;
  1225 + return To{detail::trunc(duration_cast<To>(duration_cast<duration<rep>>(d)).count())};
  1226 +}
  1227 +
  1228 +#ifndef HAS_CHRONO_ROUNDING
  1229 +# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__)))
  1230 +# define HAS_CHRONO_ROUNDING 1
  1231 +# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510
  1232 +# define HAS_CHRONO_ROUNDING 1
  1233 +# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800
  1234 +# define HAS_CHRONO_ROUNDING 1
  1235 +# else
  1236 +# define HAS_CHRONO_ROUNDING 0
  1237 +# endif
  1238 +#endif // HAS_CHRONO_ROUNDING
  1239 +
  1240 +#if HAS_CHRONO_ROUNDING == 0
  1241 +
  1242 +// round down
  1243 +template <class To, class Rep, class Period>
  1244 +CONSTCD14
  1245 +inline
  1246 +typename std::enable_if
  1247 +<
  1248 + detail::no_overflow<Period, typename To::period>::value,
  1249 + To
  1250 +>::type
  1251 +floor(const std::chrono::duration<Rep, Period>& d)
  1252 +{
  1253 + auto t = trunc<To>(d);
  1254 + if (t > d)
  1255 + return t - To{1};
  1256 + return t;
  1257 +}
  1258 +
  1259 +template <class To, class Rep, class Period>
  1260 +CONSTCD14
  1261 +inline
  1262 +typename std::enable_if
  1263 +<
  1264 + !detail::no_overflow<Period, typename To::period>::value,
  1265 + To
  1266 +>::type
  1267 +floor(const std::chrono::duration<Rep, Period>& d)
  1268 +{
  1269 + using rep = typename std::common_type<Rep, typename To::rep>::type;
  1270 + return floor<To>(floor<std::chrono::duration<rep>>(d));
  1271 +}
  1272 +
  1273 +// round to nearest, to even on tie
  1274 +template <class To, class Rep, class Period>
  1275 +CONSTCD14
  1276 +inline
  1277 +To
  1278 +round(const std::chrono::duration<Rep, Period>& d)
  1279 +{
  1280 + auto t0 = floor<To>(d);
  1281 + auto t1 = t0 + To{1};
  1282 + if (t1 == To{0} && t0 < To{0})
  1283 + t1 = -t1;
  1284 + auto diff0 = d - t0;
  1285 + auto diff1 = t1 - d;
  1286 + if (diff0 == diff1)
  1287 + {
  1288 + if (t0 - trunc<To>(t0/2)*2 == To{0})
  1289 + return t0;
  1290 + return t1;
  1291 + }
  1292 + if (diff0 < diff1)
  1293 + return t0;
  1294 + return t1;
  1295 +}
  1296 +
  1297 +// round up
  1298 +template <class To, class Rep, class Period>
  1299 +CONSTCD14
  1300 +inline
  1301 +To
  1302 +ceil(const std::chrono::duration<Rep, Period>& d)
  1303 +{
  1304 + auto t = trunc<To>(d);
  1305 + if (t < d)
  1306 + return t + To{1};
  1307 + return t;
  1308 +}
  1309 +
  1310 +template <class Rep, class Period,
  1311 + class = typename std::enable_if
  1312 + <
  1313 + std::numeric_limits<Rep>::is_signed
  1314 + >::type>
  1315 +CONSTCD11
  1316 +std::chrono::duration<Rep, Period>
  1317 +abs(std::chrono::duration<Rep, Period> d)
  1318 +{
  1319 + return d >= d.zero() ? d : -d;
  1320 +}
  1321 +
  1322 +// round down
  1323 +template <class To, class Clock, class FromDuration>
  1324 +CONSTCD11
  1325 +inline
  1326 +std::chrono::time_point<Clock, To>
  1327 +floor(const std::chrono::time_point<Clock, FromDuration>& tp)
  1328 +{
  1329 + using std::chrono::time_point;
  1330 + return time_point<Clock, To>{date::floor<To>(tp.time_since_epoch())};
  1331 +}
  1332 +
  1333 +// round to nearest, to even on tie
  1334 +template <class To, class Clock, class FromDuration>
  1335 +CONSTCD11
  1336 +inline
  1337 +std::chrono::time_point<Clock, To>
  1338 +round(const std::chrono::time_point<Clock, FromDuration>& tp)
  1339 +{
  1340 + using std::chrono::time_point;
  1341 + return time_point<Clock, To>{round<To>(tp.time_since_epoch())};
  1342 +}
  1343 +
  1344 +// round up
  1345 +template <class To, class Clock, class FromDuration>
  1346 +CONSTCD11
  1347 +inline
  1348 +std::chrono::time_point<Clock, To>
  1349 +ceil(const std::chrono::time_point<Clock, FromDuration>& tp)
  1350 +{
  1351 + using std::chrono::time_point;
  1352 + return time_point<Clock, To>{ceil<To>(tp.time_since_epoch())};
  1353 +}
  1354 +
  1355 +#else // HAS_CHRONO_ROUNDING == 1
  1356 +
  1357 +using std::chrono::floor;
  1358 +using std::chrono::ceil;
  1359 +using std::chrono::round;
  1360 +using std::chrono::abs;
  1361 +
  1362 +#endif // HAS_CHRONO_ROUNDING
  1363 +
  1364 +namespace detail
  1365 +{
  1366 +
  1367 +template <class To, class Rep, class Period>
  1368 +CONSTCD14
  1369 +inline
  1370 +typename std::enable_if
  1371 +<
  1372 + !std::chrono::treat_as_floating_point<typename To::rep>::value,
  1373 + To
  1374 +>::type
  1375 +round_i(const std::chrono::duration<Rep, Period>& d)
  1376 +{
  1377 + return round<To>(d);
  1378 +}
  1379 +
  1380 +template <class To, class Rep, class Period>
  1381 +CONSTCD14
  1382 +inline
  1383 +typename std::enable_if
  1384 +<
  1385 + std::chrono::treat_as_floating_point<typename To::rep>::value,
  1386 + To
  1387 +>::type
  1388 +round_i(const std::chrono::duration<Rep, Period>& d)
  1389 +{
  1390 + return d;
  1391 +}
  1392 +
  1393 +template <class To, class Clock, class FromDuration>
  1394 +CONSTCD11
  1395 +inline
  1396 +std::chrono::time_point<Clock, To>
  1397 +round_i(const std::chrono::time_point<Clock, FromDuration>& tp)
  1398 +{
  1399 + using std::chrono::time_point;
  1400 + return time_point<Clock, To>{round_i<To>(tp.time_since_epoch())};
  1401 +}
  1402 +
  1403 +} // detail
  1404 +
  1405 +// trunc towards zero
  1406 +template <class To, class Clock, class FromDuration>
  1407 +CONSTCD11
  1408 +inline
  1409 +std::chrono::time_point<Clock, To>
  1410 +trunc(const std::chrono::time_point<Clock, FromDuration>& tp)
  1411 +{
  1412 + using std::chrono::time_point;
  1413 + return time_point<Clock, To>{trunc<To>(tp.time_since_epoch())};
  1414 +}
  1415 +
  1416 +// day
  1417 +
  1418 +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast<decltype(d_)>(d)) {}
  1419 +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;}
  1420 +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
  1421 +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;}
  1422 +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;}
  1423 +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;}
  1424 +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;}
  1425 +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;}
  1426 +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;}
  1427 +
  1428 +CONSTCD11
  1429 +inline
  1430 +bool
  1431 +operator==(const day& x, const day& y) NOEXCEPT
  1432 +{
  1433 + return static_cast<unsigned>(x) == static_cast<unsigned>(y);
  1434 +}
  1435 +
  1436 +CONSTCD11
  1437 +inline
  1438 +bool
  1439 +operator!=(const day& x, const day& y) NOEXCEPT
  1440 +{
  1441 + return !(x == y);
  1442 +}
  1443 +
  1444 +CONSTCD11
  1445 +inline
  1446 +bool
  1447 +operator<(const day& x, const day& y) NOEXCEPT
  1448 +{
  1449 + return static_cast<unsigned>(x) < static_cast<unsigned>(y);
  1450 +}
  1451 +
  1452 +CONSTCD11
  1453 +inline
  1454 +bool
  1455 +operator>(const day& x, const day& y) NOEXCEPT
  1456 +{
  1457 + return y < x;
  1458 +}
  1459 +
  1460 +CONSTCD11
  1461 +inline
  1462 +bool
  1463 +operator<=(const day& x, const day& y) NOEXCEPT
  1464 +{
  1465 + return !(y < x);
  1466 +}
  1467 +
  1468 +CONSTCD11
  1469 +inline
  1470 +bool
  1471 +operator>=(const day& x, const day& y) NOEXCEPT
  1472 +{
  1473 + return !(x < y);
  1474 +}
  1475 +
  1476 +CONSTCD11
  1477 +inline
  1478 +days
  1479 +operator-(const day& x, const day& y) NOEXCEPT
  1480 +{
  1481 + return days{static_cast<days::rep>(static_cast<unsigned>(x)
  1482 + - static_cast<unsigned>(y))};
  1483 +}
  1484 +
  1485 +CONSTCD11
  1486 +inline
  1487 +day
  1488 +operator+(const day& x, const days& y) NOEXCEPT
  1489 +{
  1490 + return day{static_cast<unsigned>(x) + static_cast<unsigned>(y.count())};
  1491 +}
  1492 +
  1493 +CONSTCD11
  1494 +inline
  1495 +day
  1496 +operator+(const days& x, const day& y) NOEXCEPT
  1497 +{
  1498 + return y + x;
  1499 +}
  1500 +
  1501 +CONSTCD11
  1502 +inline
  1503 +day
  1504 +operator-(const day& x, const days& y) NOEXCEPT
  1505 +{
  1506 + return x + -y;
  1507 +}
  1508 +
  1509 +namespace detail
  1510 +{
  1511 +
  1512 +template<class CharT, class Traits>
  1513 +std::basic_ostream<CharT, Traits>&
  1514 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const day& d)
  1515 +{
  1516 + detail::save_ostream<CharT, Traits> _(os);
  1517 + os.fill('0');
  1518 + os.flags(std::ios::dec | std::ios::right);
  1519 + os.width(2);
  1520 + os << static_cast<unsigned>(d);
  1521 + return os;
  1522 +}
  1523 +
  1524 +} // namespace detail
  1525 +
  1526 +template<class CharT, class Traits>
  1527 +inline
  1528 +std::basic_ostream<CharT, Traits>&
  1529 +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
  1530 +{
  1531 + detail::low_level_fmt(os, d);
  1532 + if (!d.ok())
  1533 + os << " is not a valid day";
  1534 + return os;
  1535 +}
  1536 +
  1537 +// month
  1538 +
  1539 +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast<decltype(m_)>(m)) {}
  1540 +CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;}
  1541 +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
  1542 +CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; return *this;}
  1543 +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;}
  1544 +
  1545 +CONSTCD14
  1546 +inline
  1547 +month&
  1548 +month::operator+=(const months& m) NOEXCEPT
  1549 +{
  1550 + *this = *this + m;
  1551 + return *this;
  1552 +}
  1553 +
  1554 +CONSTCD14
  1555 +inline
  1556 +month&
  1557 +month::operator-=(const months& m) NOEXCEPT
  1558 +{
  1559 + *this = *this - m;
  1560 + return *this;
  1561 +}
  1562 +
  1563 +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;}
  1564 +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;}
  1565 +
  1566 +CONSTCD11
  1567 +inline
  1568 +bool
  1569 +operator==(const month& x, const month& y) NOEXCEPT
  1570 +{
  1571 + return static_cast<unsigned>(x) == static_cast<unsigned>(y);
  1572 +}
  1573 +
  1574 +CONSTCD11
  1575 +inline
  1576 +bool
  1577 +operator!=(const month& x, const month& y) NOEXCEPT
  1578 +{
  1579 + return !(x == y);
  1580 +}
  1581 +
  1582 +CONSTCD11
  1583 +inline
  1584 +bool
  1585 +operator<(const month& x, const month& y) NOEXCEPT
  1586 +{
  1587 + return static_cast<unsigned>(x) < static_cast<unsigned>(y);
  1588 +}
  1589 +
  1590 +CONSTCD11
  1591 +inline
  1592 +bool
  1593 +operator>(const month& x, const month& y) NOEXCEPT
  1594 +{
  1595 + return y < x;
  1596 +}
  1597 +
  1598 +CONSTCD11
  1599 +inline
  1600 +bool
  1601 +operator<=(const month& x, const month& y) NOEXCEPT
  1602 +{
  1603 + return !(y < x);
  1604 +}
  1605 +
  1606 +CONSTCD11
  1607 +inline
  1608 +bool
  1609 +operator>=(const month& x, const month& y) NOEXCEPT
  1610 +{
  1611 + return !(x < y);
  1612 +}
  1613 +
  1614 +CONSTCD14
  1615 +inline
  1616 +months
  1617 +operator-(const month& x, const month& y) NOEXCEPT
  1618 +{
  1619 + auto const d = static_cast<unsigned>(x) - static_cast<unsigned>(y);
  1620 + return months(d <= 11 ? d : d + 12);
  1621 +}
  1622 +
  1623 +CONSTCD14
  1624 +inline
  1625 +month
  1626 +operator+(const month& x, const months& y) NOEXCEPT
  1627 +{
  1628 + auto const mu = static_cast<long long>(static_cast<unsigned>(x)) + y.count() - 1;
  1629 + auto const yr = (mu >= 0 ? mu : mu-11) / 12;
  1630 + return month{static_cast<unsigned>(mu - yr * 12 + 1)};
  1631 +}
  1632 +
  1633 +CONSTCD14
  1634 +inline
  1635 +month
  1636 +operator+(const months& x, const month& y) NOEXCEPT
  1637 +{
  1638 + return y + x;
  1639 +}
  1640 +
  1641 +CONSTCD14
  1642 +inline
  1643 +month
  1644 +operator-(const month& x, const months& y) NOEXCEPT
  1645 +{
  1646 + return x + -y;
  1647 +}
  1648 +
  1649 +namespace detail
  1650 +{
  1651 +
  1652 +template<class CharT, class Traits>
  1653 +std::basic_ostream<CharT, Traits>&
  1654 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month& m)
  1655 +{
  1656 + if (m.ok())
  1657 + {
  1658 + CharT fmt[] = {'%', 'b', 0};
  1659 + os << format(os.getloc(), fmt, m);
  1660 + }
  1661 + else
  1662 + os << static_cast<unsigned>(m);
  1663 + return os;
  1664 +}
  1665 +
  1666 +} // namespace detail
  1667 +
  1668 +template<class CharT, class Traits>
  1669 +inline
  1670 +std::basic_ostream<CharT, Traits>&
  1671 +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
  1672 +{
  1673 + detail::low_level_fmt(os, m);
  1674 + if (!m.ok())
  1675 + os << " is not a valid month";
  1676 + return os;
  1677 +}
  1678 +
  1679 +// year
  1680 +
  1681 +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(y)) {}
  1682 +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;}
  1683 +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
  1684 +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;}
  1685 +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;}
  1686 +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;}
  1687 +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;}
  1688 +CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};}
  1689 +CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;}
  1690 +
  1691 +CONSTCD11
  1692 +inline
  1693 +bool
  1694 +year::is_leap() const NOEXCEPT
  1695 +{
  1696 + return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0);
  1697 +}
  1698 +
  1699 +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;}
  1700 +
  1701 +CONSTCD11
  1702 +inline
  1703 +bool
  1704 +year::ok() const NOEXCEPT
  1705 +{
  1706 + return y_ != std::numeric_limits<short>::min();
  1707 +}
  1708 +
  1709 +CONSTCD11
  1710 +inline
  1711 +bool
  1712 +operator==(const year& x, const year& y) NOEXCEPT
  1713 +{
  1714 + return static_cast<int>(x) == static_cast<int>(y);
  1715 +}
  1716 +
  1717 +CONSTCD11
  1718 +inline
  1719 +bool
  1720 +operator!=(const year& x, const year& y) NOEXCEPT
  1721 +{
  1722 + return !(x == y);
  1723 +}
  1724 +
  1725 +CONSTCD11
  1726 +inline
  1727 +bool
  1728 +operator<(const year& x, const year& y) NOEXCEPT
  1729 +{
  1730 + return static_cast<int>(x) < static_cast<int>(y);
  1731 +}
  1732 +
  1733 +CONSTCD11
  1734 +inline
  1735 +bool
  1736 +operator>(const year& x, const year& y) NOEXCEPT
  1737 +{
  1738 + return y < x;
  1739 +}
  1740 +
  1741 +CONSTCD11
  1742 +inline
  1743 +bool
  1744 +operator<=(const year& x, const year& y) NOEXCEPT
  1745 +{
  1746 + return !(y < x);
  1747 +}
  1748 +
  1749 +CONSTCD11
  1750 +inline
  1751 +bool
  1752 +operator>=(const year& x, const year& y) NOEXCEPT
  1753 +{
  1754 + return !(x < y);
  1755 +}
  1756 +
  1757 +CONSTCD11
  1758 +inline
  1759 +years
  1760 +operator-(const year& x, const year& y) NOEXCEPT
  1761 +{
  1762 + return years{static_cast<int>(x) - static_cast<int>(y)};
  1763 +}
  1764 +
  1765 +CONSTCD11
  1766 +inline
  1767 +year
  1768 +operator+(const year& x, const years& y) NOEXCEPT
  1769 +{
  1770 + return year{static_cast<int>(x) + y.count()};
  1771 +}
  1772 +
  1773 +CONSTCD11
  1774 +inline
  1775 +year
  1776 +operator+(const years& x, const year& y) NOEXCEPT
  1777 +{
  1778 + return y + x;
  1779 +}
  1780 +
  1781 +CONSTCD11
  1782 +inline
  1783 +year
  1784 +operator-(const year& x, const years& y) NOEXCEPT
  1785 +{
  1786 + return year{static_cast<int>(x) - y.count()};
  1787 +}
  1788 +
  1789 +namespace detail
  1790 +{
  1791 +
  1792 +template<class CharT, class Traits>
  1793 +std::basic_ostream<CharT, Traits>&
  1794 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year& y)
  1795 +{
  1796 + detail::save_ostream<CharT, Traits> _(os);
  1797 + os.fill('0');
  1798 + os.flags(std::ios::dec | std::ios::internal);
  1799 + os.width(4 + (y < year{0}));
  1800 + os.imbue(std::locale::classic());
  1801 + os << static_cast<int>(y);
  1802 + return os;
  1803 +}
  1804 +
  1805 +} // namespace detail
  1806 +
  1807 +template<class CharT, class Traits>
  1808 +inline
  1809 +std::basic_ostream<CharT, Traits>&
  1810 +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
  1811 +{
  1812 + detail::low_level_fmt(os, y);
  1813 + if (!y.ok())
  1814 + os << " is not a valid year";
  1815 + return os;
  1816 +}
  1817 +
  1818 +// weekday
  1819 +
  1820 +CONSTCD14
  1821 +inline
  1822 +unsigned char
  1823 +weekday::weekday_from_days(int z) NOEXCEPT
  1824 +{
  1825 + auto u = static_cast<unsigned>(z);
  1826 + return static_cast<unsigned char>(z >= -4 ? (u+4) % 7 : u % 7);
  1827 +}
  1828 +
  1829 +CONSTCD11
  1830 +inline
  1831 +weekday::weekday(unsigned wd) NOEXCEPT
  1832 + : wd_(static_cast<decltype(wd_)>(wd != 7 ? wd : 0))
  1833 + {}
  1834 +
  1835 +CONSTCD14
  1836 +inline
  1837 +weekday::weekday(const sys_days& dp) NOEXCEPT
  1838 + : wd_(weekday_from_days(dp.time_since_epoch().count()))
  1839 + {}
  1840 +
  1841 +CONSTCD14
  1842 +inline
  1843 +weekday::weekday(const local_days& dp) NOEXCEPT
  1844 + : wd_(weekday_from_days(dp.time_since_epoch().count()))
  1845 + {}
  1846 +
  1847 +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;}
  1848 +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;}
  1849 +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; return *this;}
  1850 +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;}
  1851 +
  1852 +CONSTCD14
  1853 +inline
  1854 +weekday&
  1855 +weekday::operator+=(const days& d) NOEXCEPT
  1856 +{
  1857 + *this = *this + d;
  1858 + return *this;
  1859 +}
  1860 +
  1861 +CONSTCD14
  1862 +inline
  1863 +weekday&
  1864 +weekday::operator-=(const days& d) NOEXCEPT
  1865 +{
  1866 + *this = *this - d;
  1867 + return *this;
  1868 +}
  1869 +
  1870 +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;}
  1871 +
  1872 +CONSTCD11
  1873 +inline
  1874 +unsigned weekday::c_encoding() const NOEXCEPT
  1875 +{
  1876 + return unsigned{wd_};
  1877 +}
  1878 +
  1879 +CONSTCD11
  1880 +inline
  1881 +unsigned weekday::iso_encoding() const NOEXCEPT
  1882 +{
  1883 + return unsigned{((wd_ == 0u) ? 7u : wd_)};
  1884 +}
  1885 +
  1886 +CONSTCD11
  1887 +inline
  1888 +bool
  1889 +operator==(const weekday& x, const weekday& y) NOEXCEPT
  1890 +{
  1891 + return x.wd_ == y.wd_;
  1892 +}
  1893 +
  1894 +CONSTCD11
  1895 +inline
  1896 +bool
  1897 +operator!=(const weekday& x, const weekday& y) NOEXCEPT
  1898 +{
  1899 + return !(x == y);
  1900 +}
  1901 +
  1902 +CONSTCD14
  1903 +inline
  1904 +days
  1905 +operator-(const weekday& x, const weekday& y) NOEXCEPT
  1906 +{
  1907 + auto const wdu = x.wd_ - y.wd_;
  1908 + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7;
  1909 + return days{wdu - wk * 7};
  1910 +}
  1911 +
  1912 +CONSTCD14
  1913 +inline
  1914 +weekday
  1915 +operator+(const weekday& x, const days& y) NOEXCEPT
  1916 +{
  1917 + auto const wdu = static_cast<long long>(static_cast<unsigned>(x.wd_)) + y.count();
  1918 + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7;
  1919 + return weekday{static_cast<unsigned>(wdu - wk * 7)};
  1920 +}
  1921 +
  1922 +CONSTCD14
  1923 +inline
  1924 +weekday
  1925 +operator+(const days& x, const weekday& y) NOEXCEPT
  1926 +{
  1927 + return y + x;
  1928 +}
  1929 +
  1930 +CONSTCD14
  1931 +inline
  1932 +weekday
  1933 +operator-(const weekday& x, const days& y) NOEXCEPT
  1934 +{
  1935 + return x + -y;
  1936 +}
  1937 +
  1938 +namespace detail
  1939 +{
  1940 +
  1941 +template<class CharT, class Traits>
  1942 +std::basic_ostream<CharT, Traits>&
  1943 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
  1944 +{
  1945 + if (wd.ok())
  1946 + {
  1947 + CharT fmt[] = {'%', 'a', 0};
  1948 + os << format(fmt, wd);
  1949 + }
  1950 + else
  1951 + os << wd.c_encoding();
  1952 + return os;
  1953 +}
  1954 +
  1955 +} // namespace detail
  1956 +
  1957 +template<class CharT, class Traits>
  1958 +inline
  1959 +std::basic_ostream<CharT, Traits>&
  1960 +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
  1961 +{
  1962 + detail::low_level_fmt(os, wd);
  1963 + if (!wd.ok())
  1964 + os << " is not a valid weekday";
  1965 + return os;
  1966 +}
  1967 +
  1968 +#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
  1969 +inline namespace literals
  1970 +{
  1971 +
  1972 +CONSTCD11
  1973 +inline
  1974 +date::day
  1975 +operator "" _d(unsigned long long d) NOEXCEPT
  1976 +{
  1977 + return date::day{static_cast<unsigned>(d)};
  1978 +}
  1979 +
  1980 +CONSTCD11
  1981 +inline
  1982 +date::year
  1983 +operator "" _y(unsigned long long y) NOEXCEPT
  1984 +{
  1985 + return date::year(static_cast<int>(y));
  1986 +}
  1987 +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
  1988 +
  1989 +CONSTDATA date::last_spec last{};
  1990 +
  1991 +CONSTDATA date::month jan{1};
  1992 +CONSTDATA date::month feb{2};
  1993 +CONSTDATA date::month mar{3};
  1994 +CONSTDATA date::month apr{4};
  1995 +CONSTDATA date::month may{5};
  1996 +CONSTDATA date::month jun{6};
  1997 +CONSTDATA date::month jul{7};
  1998 +CONSTDATA date::month aug{8};
  1999 +CONSTDATA date::month sep{9};
  2000 +CONSTDATA date::month oct{10};
  2001 +CONSTDATA date::month nov{11};
  2002 +CONSTDATA date::month dec{12};
  2003 +
  2004 +CONSTDATA date::weekday sun{0u};
  2005 +CONSTDATA date::weekday mon{1u};
  2006 +CONSTDATA date::weekday tue{2u};
  2007 +CONSTDATA date::weekday wed{3u};
  2008 +CONSTDATA date::weekday thu{4u};
  2009 +CONSTDATA date::weekday fri{5u};
  2010 +CONSTDATA date::weekday sat{6u};
  2011 +
  2012 +#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
  2013 +} // inline namespace literals
  2014 +#endif
  2015 +
  2016 +CONSTDATA date::month January{1};
  2017 +CONSTDATA date::month February{2};
  2018 +CONSTDATA date::month March{3};
  2019 +CONSTDATA date::month April{4};
  2020 +CONSTDATA date::month May{5};
  2021 +CONSTDATA date::month June{6};
  2022 +CONSTDATA date::month July{7};
  2023 +CONSTDATA date::month August{8};
  2024 +CONSTDATA date::month September{9};
  2025 +CONSTDATA date::month October{10};
  2026 +CONSTDATA date::month November{11};
  2027 +CONSTDATA date::month December{12};
  2028 +
  2029 +CONSTDATA date::weekday Monday{1};
  2030 +CONSTDATA date::weekday Tuesday{2};
  2031 +CONSTDATA date::weekday Wednesday{3};
  2032 +CONSTDATA date::weekday Thursday{4};
  2033 +CONSTDATA date::weekday Friday{5};
  2034 +CONSTDATA date::weekday Saturday{6};
  2035 +CONSTDATA date::weekday Sunday{7};
  2036 +
  2037 +// weekday_indexed
  2038 +
  2039 +CONSTCD11
  2040 +inline
  2041 +weekday
  2042 +weekday_indexed::weekday() const NOEXCEPT
  2043 +{
  2044 + return date::weekday{static_cast<unsigned>(wd_)};
  2045 +}
  2046 +
  2047 +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;}
  2048 +
  2049 +CONSTCD11
  2050 +inline
  2051 +bool
  2052 +weekday_indexed::ok() const NOEXCEPT
  2053 +{
  2054 + return weekday().ok() && 1 <= index_ && index_ <= 5;
  2055 +}
  2056 +
  2057 +#ifdef __GNUC__
  2058 +# pragma GCC diagnostic push
  2059 +# pragma GCC diagnostic ignored "-Wconversion"
  2060 +#endif // __GNUC__
  2061 +
  2062 +CONSTCD11
  2063 +inline
  2064 +weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT
  2065 + : wd_(static_cast<decltype(wd_)>(static_cast<unsigned>(wd.wd_)))
  2066 + , index_(static_cast<decltype(index_)>(index))
  2067 + {}
  2068 +
  2069 +#ifdef __GNUC__
  2070 +# pragma GCC diagnostic pop
  2071 +#endif // __GNUC__
  2072 +
  2073 +namespace detail
  2074 +{
  2075 +
  2076 +template<class CharT, class Traits>
  2077 +std::basic_ostream<CharT, Traits>&
  2078 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
  2079 +{
  2080 + return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']';
  2081 +}
  2082 +
  2083 +} // namespace detail
  2084 +
  2085 +template<class CharT, class Traits>
  2086 +inline
  2087 +std::basic_ostream<CharT, Traits>&
  2088 +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
  2089 +{
  2090 + detail::low_level_fmt(os, wdi);
  2091 + if (!wdi.ok())
  2092 + os << " is not a valid weekday_indexed";
  2093 + return os;
  2094 +}
  2095 +
  2096 +CONSTCD11
  2097 +inline
  2098 +weekday_indexed
  2099 +weekday::operator[](unsigned index) const NOEXCEPT
  2100 +{
  2101 + return {*this, index};
  2102 +}
  2103 +
  2104 +CONSTCD11
  2105 +inline
  2106 +bool
  2107 +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT
  2108 +{
  2109 + return x.weekday() == y.weekday() && x.index() == y.index();
  2110 +}
  2111 +
  2112 +CONSTCD11
  2113 +inline
  2114 +bool
  2115 +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT
  2116 +{
  2117 + return !(x == y);
  2118 +}
  2119 +
  2120 +// weekday_last
  2121 +
  2122 +CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;}
  2123 +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();}
  2124 +CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {}
  2125 +
  2126 +CONSTCD11
  2127 +inline
  2128 +bool
  2129 +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT
  2130 +{
  2131 + return x.weekday() == y.weekday();
  2132 +}
  2133 +
  2134 +CONSTCD11
  2135 +inline
  2136 +bool
  2137 +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT
  2138 +{
  2139 + return !(x == y);
  2140 +}
  2141 +
  2142 +namespace detail
  2143 +{
  2144 +
  2145 +template<class CharT, class Traits>
  2146 +std::basic_ostream<CharT, Traits>&
  2147 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
  2148 +{
  2149 + return low_level_fmt(os, wdl.weekday()) << "[last]";
  2150 +}
  2151 +
  2152 +} // namespace detail
  2153 +
  2154 +template<class CharT, class Traits>
  2155 +inline
  2156 +std::basic_ostream<CharT, Traits>&
  2157 +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
  2158 +{
  2159 + detail::low_level_fmt(os, wdl);
  2160 + if (!wdl.ok())
  2161 + os << " is not a valid weekday_last";
  2162 + return os;
  2163 +}
  2164 +
  2165 +CONSTCD11
  2166 +inline
  2167 +weekday_last
  2168 +weekday::operator[](last_spec) const NOEXCEPT
  2169 +{
  2170 + return weekday_last{*this};
  2171 +}
  2172 +
  2173 +// year_month
  2174 +
  2175 +CONSTCD11
  2176 +inline
  2177 +year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT
  2178 + : y_(y)
  2179 + , m_(m)
  2180 + {}
  2181 +
  2182 +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;}
  2183 +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;}
  2184 +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();}
  2185 +
  2186 +template<class>
  2187 +CONSTCD14
  2188 +inline
  2189 +year_month&
  2190 +year_month::operator+=(const months& dm) NOEXCEPT
  2191 +{
  2192 + *this = *this + dm;
  2193 + return *this;
  2194 +}
  2195 +
  2196 +template<class>
  2197 +CONSTCD14
  2198 +inline
  2199 +year_month&
  2200 +year_month::operator-=(const months& dm) NOEXCEPT
  2201 +{
  2202 + *this = *this - dm;
  2203 + return *this;
  2204 +}
  2205 +
  2206 +CONSTCD14
  2207 +inline
  2208 +year_month&
  2209 +year_month::operator+=(const years& dy) NOEXCEPT
  2210 +{
  2211 + *this = *this + dy;
  2212 + return *this;
  2213 +}
  2214 +
  2215 +CONSTCD14
  2216 +inline
  2217 +year_month&
  2218 +year_month::operator-=(const years& dy) NOEXCEPT
  2219 +{
  2220 + *this = *this - dy;
  2221 + return *this;
  2222 +}
  2223 +
  2224 +CONSTCD11
  2225 +inline
  2226 +bool
  2227 +operator==(const year_month& x, const year_month& y) NOEXCEPT
  2228 +{
  2229 + return x.year() == y.year() && x.month() == y.month();
  2230 +}
  2231 +
  2232 +CONSTCD11
  2233 +inline
  2234 +bool
  2235 +operator!=(const year_month& x, const year_month& y) NOEXCEPT
  2236 +{
  2237 + return !(x == y);
  2238 +}
  2239 +
  2240 +CONSTCD11
  2241 +inline
  2242 +bool
  2243 +operator<(const year_month& x, const year_month& y) NOEXCEPT
  2244 +{
  2245 + return x.year() < y.year() ? true
  2246 + : (x.year() > y.year() ? false
  2247 + : (x.month() < y.month()));
  2248 +}
  2249 +
  2250 +CONSTCD11
  2251 +inline
  2252 +bool
  2253 +operator>(const year_month& x, const year_month& y) NOEXCEPT
  2254 +{
  2255 + return y < x;
  2256 +}
  2257 +
  2258 +CONSTCD11
  2259 +inline
  2260 +bool
  2261 +operator<=(const year_month& x, const year_month& y) NOEXCEPT
  2262 +{
  2263 + return !(y < x);
  2264 +}
  2265 +
  2266 +CONSTCD11
  2267 +inline
  2268 +bool
  2269 +operator>=(const year_month& x, const year_month& y) NOEXCEPT
  2270 +{
  2271 + return !(x < y);
  2272 +}
  2273 +
  2274 +template<class>
  2275 +CONSTCD14
  2276 +inline
  2277 +year_month
  2278 +operator+(const year_month& ym, const months& dm) NOEXCEPT
  2279 +{
  2280 + auto dmi = static_cast<int>(static_cast<unsigned>(ym.month())) - 1 + dm.count();
  2281 + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12;
  2282 + dmi = dmi - dy * 12 + 1;
  2283 + return (ym.year() + years(dy)) / month(static_cast<unsigned>(dmi));
  2284 +}
  2285 +
  2286 +template<class>
  2287 +CONSTCD14
  2288 +inline
  2289 +year_month
  2290 +operator+(const months& dm, const year_month& ym) NOEXCEPT
  2291 +{
  2292 + return ym + dm;
  2293 +}
  2294 +
  2295 +template<class>
  2296 +CONSTCD14
  2297 +inline
  2298 +year_month
  2299 +operator-(const year_month& ym, const months& dm) NOEXCEPT
  2300 +{
  2301 + return ym + -dm;
  2302 +}
  2303 +
  2304 +CONSTCD11
  2305 +inline
  2306 +months
  2307 +operator-(const year_month& x, const year_month& y) NOEXCEPT
  2308 +{
  2309 + return (x.year() - y.year()) +
  2310 + months(static_cast<unsigned>(x.month()) - static_cast<unsigned>(y.month()));
  2311 +}
  2312 +
  2313 +CONSTCD11
  2314 +inline
  2315 +year_month
  2316 +operator+(const year_month& ym, const years& dy) NOEXCEPT
  2317 +{
  2318 + return (ym.year() + dy) / ym.month();
  2319 +}
  2320 +
  2321 +CONSTCD11
  2322 +inline
  2323 +year_month
  2324 +operator+(const years& dy, const year_month& ym) NOEXCEPT
  2325 +{
  2326 + return ym + dy;
  2327 +}
  2328 +
  2329 +CONSTCD11
  2330 +inline
  2331 +year_month
  2332 +operator-(const year_month& ym, const years& dy) NOEXCEPT
  2333 +{
  2334 + return ym + -dy;
  2335 +}
  2336 +
  2337 +namespace detail
  2338 +{
  2339 +
  2340 +template<class CharT, class Traits>
  2341 +std::basic_ostream<CharT, Traits>&
  2342 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
  2343 +{
  2344 + low_level_fmt(os, ym.year()) << '/';
  2345 + return low_level_fmt(os, ym.month());
  2346 +}
  2347 +
  2348 +} // namespace detail
  2349 +
  2350 +template<class CharT, class Traits>
  2351 +inline
  2352 +std::basic_ostream<CharT, Traits>&
  2353 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
  2354 +{
  2355 + detail::low_level_fmt(os, ym);
  2356 + if (!ym.ok())
  2357 + os << " is not a valid year_month";
  2358 + return os;
  2359 +}
  2360 +
  2361 +// month_day
  2362 +
  2363 +CONSTCD11
  2364 +inline
  2365 +month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT
  2366 + : m_(m)
  2367 + , d_(d)
  2368 + {}
  2369 +
  2370 +CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;}
  2371 +CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;}
  2372 +
  2373 +CONSTCD14
  2374 +inline
  2375 +bool
  2376 +month_day::ok() const NOEXCEPT
  2377 +{
  2378 + CONSTDATA date::day d[] =
  2379 + {
  2380 + date::day(31), date::day(29), date::day(31),
  2381 + date::day(30), date::day(31), date::day(30),
  2382 + date::day(31), date::day(31), date::day(30),
  2383 + date::day(31), date::day(30), date::day(31)
  2384 + };
  2385 + return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
  2386 +}
  2387 +
  2388 +CONSTCD11
  2389 +inline
  2390 +bool
  2391 +operator==(const month_day& x, const month_day& y) NOEXCEPT
  2392 +{
  2393 + return x.month() == y.month() && x.day() == y.day();
  2394 +}
  2395 +
  2396 +CONSTCD11
  2397 +inline
  2398 +bool
  2399 +operator!=(const month_day& x, const month_day& y) NOEXCEPT
  2400 +{
  2401 + return !(x == y);
  2402 +}
  2403 +
  2404 +CONSTCD11
  2405 +inline
  2406 +bool
  2407 +operator<(const month_day& x, const month_day& y) NOEXCEPT
  2408 +{
  2409 + return x.month() < y.month() ? true
  2410 + : (x.month() > y.month() ? false
  2411 + : (x.day() < y.day()));
  2412 +}
  2413 +
  2414 +CONSTCD11
  2415 +inline
  2416 +bool
  2417 +operator>(const month_day& x, const month_day& y) NOEXCEPT
  2418 +{
  2419 + return y < x;
  2420 +}
  2421 +
  2422 +CONSTCD11
  2423 +inline
  2424 +bool
  2425 +operator<=(const month_day& x, const month_day& y) NOEXCEPT
  2426 +{
  2427 + return !(y < x);
  2428 +}
  2429 +
  2430 +CONSTCD11
  2431 +inline
  2432 +bool
  2433 +operator>=(const month_day& x, const month_day& y) NOEXCEPT
  2434 +{
  2435 + return !(x < y);
  2436 +}
  2437 +
  2438 +namespace detail
  2439 +{
  2440 +
  2441 +template<class CharT, class Traits>
  2442 +std::basic_ostream<CharT, Traits>&
  2443 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day& md)
  2444 +{
  2445 + low_level_fmt(os, md.month()) << '/';
  2446 + return low_level_fmt(os, md.day());
  2447 +}
  2448 +
  2449 +} // namespace detail
  2450 +
  2451 +template<class CharT, class Traits>
  2452 +inline
  2453 +std::basic_ostream<CharT, Traits>&
  2454 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md)
  2455 +{
  2456 + detail::low_level_fmt(os, md);
  2457 + if (!md.ok())
  2458 + os << " is not a valid month_day";
  2459 + return os;
  2460 +}
  2461 +
  2462 +// month_day_last
  2463 +
  2464 +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;}
  2465 +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();}
  2466 +CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {}
  2467 +
  2468 +CONSTCD11
  2469 +inline
  2470 +bool
  2471 +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT
  2472 +{
  2473 + return x.month() == y.month();
  2474 +}
  2475 +
  2476 +CONSTCD11
  2477 +inline
  2478 +bool
  2479 +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT
  2480 +{
  2481 + return !(x == y);
  2482 +}
  2483 +
  2484 +CONSTCD11
  2485 +inline
  2486 +bool
  2487 +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT
  2488 +{
  2489 + return x.month() < y.month();
  2490 +}
  2491 +
  2492 +CONSTCD11
  2493 +inline
  2494 +bool
  2495 +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT
  2496 +{
  2497 + return y < x;
  2498 +}
  2499 +
  2500 +CONSTCD11
  2501 +inline
  2502 +bool
  2503 +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT
  2504 +{
  2505 + return !(y < x);
  2506 +}
  2507 +
  2508 +CONSTCD11
  2509 +inline
  2510 +bool
  2511 +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT
  2512 +{
  2513 + return !(x < y);
  2514 +}
  2515 +
  2516 +namespace detail
  2517 +{
  2518 +
  2519 +template<class CharT, class Traits>
  2520 +std::basic_ostream<CharT, Traits>&
  2521 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
  2522 +{
  2523 + return low_level_fmt(os, mdl.month()) << "/last";
  2524 +}
  2525 +
  2526 +} // namespace detail
  2527 +
  2528 +template<class CharT, class Traits>
  2529 +inline
  2530 +std::basic_ostream<CharT, Traits>&
  2531 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
  2532 +{
  2533 + detail::low_level_fmt(os, mdl);
  2534 + if (!mdl.ok())
  2535 + os << " is not a valid month_day_last";
  2536 + return os;
  2537 +}
  2538 +
  2539 +// month_weekday
  2540 +
  2541 +CONSTCD11
  2542 +inline
  2543 +month_weekday::month_weekday(const date::month& m,
  2544 + const date::weekday_indexed& wdi) NOEXCEPT
  2545 + : m_(m)
  2546 + , wdi_(wdi)
  2547 + {}
  2548 +
  2549 +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;}
  2550 +
  2551 +CONSTCD11
  2552 +inline
  2553 +weekday_indexed
  2554 +month_weekday::weekday_indexed() const NOEXCEPT
  2555 +{
  2556 + return wdi_;
  2557 +}
  2558 +
  2559 +CONSTCD11
  2560 +inline
  2561 +bool
  2562 +month_weekday::ok() const NOEXCEPT
  2563 +{
  2564 + return m_.ok() && wdi_.ok();
  2565 +}
  2566 +
  2567 +CONSTCD11
  2568 +inline
  2569 +bool
  2570 +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT
  2571 +{
  2572 + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed();
  2573 +}
  2574 +
  2575 +CONSTCD11
  2576 +inline
  2577 +bool
  2578 +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT
  2579 +{
  2580 + return !(x == y);
  2581 +}
  2582 +
  2583 +namespace detail
  2584 +{
  2585 +
  2586 +template<class CharT, class Traits>
  2587 +std::basic_ostream<CharT, Traits>&
  2588 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
  2589 +{
  2590 + low_level_fmt(os, mwd.month()) << '/';
  2591 + return low_level_fmt(os, mwd.weekday_indexed());
  2592 +}
  2593 +
  2594 +} // namespace detail
  2595 +
  2596 +template<class CharT, class Traits>
  2597 +inline
  2598 +std::basic_ostream<CharT, Traits>&
  2599 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
  2600 +{
  2601 + detail::low_level_fmt(os, mwd);
  2602 + if (!mwd.ok())
  2603 + os << " is not a valid month_weekday";
  2604 + return os;
  2605 +}
  2606 +
  2607 +// month_weekday_last
  2608 +
  2609 +CONSTCD11
  2610 +inline
  2611 +month_weekday_last::month_weekday_last(const date::month& m,
  2612 + const date::weekday_last& wdl) NOEXCEPT
  2613 + : m_(m)
  2614 + , wdl_(wdl)
  2615 + {}
  2616 +
  2617 +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;}
  2618 +
  2619 +CONSTCD11
  2620 +inline
  2621 +weekday_last
  2622 +month_weekday_last::weekday_last() const NOEXCEPT
  2623 +{
  2624 + return wdl_;
  2625 +}
  2626 +
  2627 +CONSTCD11
  2628 +inline
  2629 +bool
  2630 +month_weekday_last::ok() const NOEXCEPT
  2631 +{
  2632 + return m_.ok() && wdl_.ok();
  2633 +}
  2634 +
  2635 +CONSTCD11
  2636 +inline
  2637 +bool
  2638 +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT
  2639 +{
  2640 + return x.month() == y.month() && x.weekday_last() == y.weekday_last();
  2641 +}
  2642 +
  2643 +CONSTCD11
  2644 +inline
  2645 +bool
  2646 +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT
  2647 +{
  2648 + return !(x == y);
  2649 +}
  2650 +
  2651 +namespace detail
  2652 +{
  2653 +
  2654 +template<class CharT, class Traits>
  2655 +std::basic_ostream<CharT, Traits>&
  2656 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
  2657 +{
  2658 + low_level_fmt(os, mwdl.month()) << '/';
  2659 + return low_level_fmt(os, mwdl.weekday_last());
  2660 +}
  2661 +
  2662 +} // namespace detail
  2663 +
  2664 +template<class CharT, class Traits>
  2665 +inline
  2666 +std::basic_ostream<CharT, Traits>&
  2667 +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
  2668 +{
  2669 + detail::low_level_fmt(os, mwdl);
  2670 + if (!mwdl.ok())
  2671 + os << " is not a valid month_weekday_last";
  2672 + return os;
  2673 +}
  2674 +
  2675 +// year_month_day_last
  2676 +
  2677 +CONSTCD11
  2678 +inline
  2679 +year_month_day_last::year_month_day_last(const date::year& y,
  2680 + const date::month_day_last& mdl) NOEXCEPT
  2681 + : y_(y)
  2682 + , mdl_(mdl)
  2683 + {}
  2684 +
  2685 +template<class>
  2686 +CONSTCD14
  2687 +inline
  2688 +year_month_day_last&
  2689 +year_month_day_last::operator+=(const months& m) NOEXCEPT
  2690 +{
  2691 + *this = *this + m;
  2692 + return *this;
  2693 +}
  2694 +
  2695 +template<class>
  2696 +CONSTCD14
  2697 +inline
  2698 +year_month_day_last&
  2699 +year_month_day_last::operator-=(const months& m) NOEXCEPT
  2700 +{
  2701 + *this = *this - m;
  2702 + return *this;
  2703 +}
  2704 +
  2705 +CONSTCD14
  2706 +inline
  2707 +year_month_day_last&
  2708 +year_month_day_last::operator+=(const years& y) NOEXCEPT
  2709 +{
  2710 + *this = *this + y;
  2711 + return *this;
  2712 +}
  2713 +
  2714 +CONSTCD14
  2715 +inline
  2716 +year_month_day_last&
  2717 +year_month_day_last::operator-=(const years& y) NOEXCEPT
  2718 +{
  2719 + *this = *this - y;
  2720 + return *this;
  2721 +}
  2722 +
  2723 +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;}
  2724 +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();}
  2725 +
  2726 +CONSTCD11
  2727 +inline
  2728 +month_day_last
  2729 +year_month_day_last::month_day_last() const NOEXCEPT
  2730 +{
  2731 + return mdl_;
  2732 +}
  2733 +
  2734 +CONSTCD14
  2735 +inline
  2736 +day
  2737 +year_month_day_last::day() const NOEXCEPT
  2738 +{
  2739 + CONSTDATA date::day d[] =
  2740 + {
  2741 + date::day(31), date::day(28), date::day(31),
  2742 + date::day(30), date::day(31), date::day(30),
  2743 + date::day(31), date::day(31), date::day(30),
  2744 + date::day(31), date::day(30), date::day(31)
  2745 + };
  2746 + return (month() != February || !y_.is_leap()) && mdl_.ok() ?
  2747 + d[static_cast<unsigned>(month()) - 1] : date::day{29};
  2748 +}
  2749 +
  2750 +CONSTCD14
  2751 +inline
  2752 +year_month_day_last::operator sys_days() const NOEXCEPT
  2753 +{
  2754 + return sys_days(year()/month()/day());
  2755 +}
  2756 +
  2757 +CONSTCD14
  2758 +inline
  2759 +year_month_day_last::operator local_days() const NOEXCEPT
  2760 +{
  2761 + return local_days(year()/month()/day());
  2762 +}
  2763 +
  2764 +CONSTCD11
  2765 +inline
  2766 +bool
  2767 +year_month_day_last::ok() const NOEXCEPT
  2768 +{
  2769 + return y_.ok() && mdl_.ok();
  2770 +}
  2771 +
  2772 +CONSTCD11
  2773 +inline
  2774 +bool
  2775 +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
  2776 +{
  2777 + return x.year() == y.year() && x.month_day_last() == y.month_day_last();
  2778 +}
  2779 +
  2780 +CONSTCD11
  2781 +inline
  2782 +bool
  2783 +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
  2784 +{
  2785 + return !(x == y);
  2786 +}
  2787 +
  2788 +CONSTCD11
  2789 +inline
  2790 +bool
  2791 +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
  2792 +{
  2793 + return x.year() < y.year() ? true
  2794 + : (x.year() > y.year() ? false
  2795 + : (x.month_day_last() < y.month_day_last()));
  2796 +}
  2797 +
  2798 +CONSTCD11
  2799 +inline
  2800 +bool
  2801 +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
  2802 +{
  2803 + return y < x;
  2804 +}
  2805 +
  2806 +CONSTCD11
  2807 +inline
  2808 +bool
  2809 +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
  2810 +{
  2811 + return !(y < x);
  2812 +}
  2813 +
  2814 +CONSTCD11
  2815 +inline
  2816 +bool
  2817 +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
  2818 +{
  2819 + return !(x < y);
  2820 +}
  2821 +
  2822 +namespace detail
  2823 +{
  2824 +
  2825 +template<class CharT, class Traits>
  2826 +std::basic_ostream<CharT, Traits>&
  2827 +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
  2828 +{
  2829 + low_level_fmt(os, ymdl.year()) << '/';
  2830 + return low_level_fmt(os, ymdl.month_day_last());
  2831 +}
  2832 +
  2833 +} // namespace detail
  2834 +
  2835 +template<class CharT, class Traits>
  2836 +inline
  2837 +std::basic_ostream<CharT, Traits>&
  2838 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
  2839 +{
  2840 + detail::low_level_fmt(os, ymdl);
  2841 + if (!ymdl.ok())
  2842 + os << " is not a valid year_month_day_last";
  2843 + return os;
  2844 +}
  2845 +
  2846 +template<class>
  2847 +CONSTCD14
  2848 +inline
  2849 +year_month_day_last
  2850 +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT
  2851 +{
  2852 + return (ymdl.year() / ymdl.month() + dm) / last;
  2853 +}
  2854 +
  2855 +template<class>
  2856 +CONSTCD14
  2857 +inline
  2858 +year_month_day_last
  2859 +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT
  2860 +{
  2861 + return ymdl + dm;
  2862 +}
  2863 +
  2864 +template<class>
  2865 +CONSTCD14
  2866 +inline
  2867 +year_month_day_last
  2868 +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT
  2869 +{
  2870 + return ymdl + (-dm);
  2871 +}
  2872 +
  2873 +CONSTCD11
  2874 +inline
  2875 +year_month_day_last
  2876 +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT
  2877 +{
  2878 + return {ymdl.year()+dy, ymdl.month_day_last()};
  2879 +}
  2880 +
  2881 +CONSTCD11
  2882 +inline
  2883 +year_month_day_last
  2884 +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT
  2885 +{
  2886 + return ymdl + dy;
  2887 +}
  2888 +
  2889 +CONSTCD11
  2890 +inline
  2891 +year_month_day_last
  2892 +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT
  2893 +{
  2894 + return ymdl + (-dy);
  2895 +}
  2896 +
  2897 +// year_month_day
  2898 +
  2899 +CONSTCD11
  2900 +inline
  2901 +year_month_day::year_month_day(const date::year& y, const date::month& m,
  2902 + const date::day& d) NOEXCEPT
  2903 + : y_(y)
  2904 + , m_(m)
  2905 + , d_(d)
  2906 + {}
  2907 +
  2908 +CONSTCD14
  2909 +inline
  2910 +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT
  2911 + : y_(ymdl.year())
  2912 + , m_(ymdl.month())
  2913 + , d_(ymdl.day())
  2914 + {}
  2915 +
  2916 +CONSTCD14
  2917 +inline
  2918 +year_month_day::year_month_day(sys_days dp) NOEXCEPT
  2919 + : year_month_day(from_days(dp.time_since_epoch()))
  2920 + {}
  2921 +
  2922 +CONSTCD14
  2923 +inline
  2924 +year_month_day::year_month_day(local_days dp) NOEXCEPT
  2925 + : year_month_day(from_days(dp.time_since_epoch()))
  2926 + {}
  2927 +
  2928 +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;}
  2929 +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;}
  2930 +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;}
  2931 +
  2932 +template<class>
  2933 +CONSTCD14
  2934 +inline
  2935 +year_month_day&
  2936 +year_month_day::operator+=(const months& m) NOEXCEPT
  2937 +{
  2938 + *this = *this + m;
  2939 + return *this;
  2940 +}
  2941 +
  2942 +template<class>
  2943 +CONSTCD14
  2944 +inline
  2945 +year_month_day&
  2946 +year_month_day::operator-=(const months& m) NOEXCEPT
  2947 +{
  2948 + *this = *this - m;
  2949 + return *this;
  2950 +}
  2951 +
  2952 +CONSTCD14
  2953 +inline
  2954 +year_month_day&
  2955 +year_month_day::operator+=(const years& y) NOEXCEPT
  2956 +{
  2957 + *this = *this + y;
  2958 + return *this;
  2959 +}
  2960 +
  2961 +CONSTCD14
  2962 +inline
  2963 +year_month_day&
  2964 +year_month_day::operator-=(const years& y) NOEXCEPT
  2965 +{
  2966 + *this = *this - y;
  2967 + return *this;
  2968 +}
  2969 +
  2970 +CONSTCD14
  2971 +inline
  2972 +days
  2973 +year_month_day::to_days() const NOEXCEPT
  2974 +{
  2975 + static_assert(std::numeric_limits<unsigned>::digits >= 18,
  2976 + "This algorithm has not been ported to a 16 bit unsigned integer");
  2977 + static_assert(std::numeric_limits<int>::digits >= 20,
  2978 + "This algorithm has not been ported to a 16 bit signed integer");
  2979 + auto const y = static_cast<int>(y_) - (m_ <= February);
  2980 + auto const m = static_cast<unsigned>(m_);
  2981 + auto const d = static_cast<unsigned>(d_);
  2982 + auto const era = (y >= 0 ? y : y-399) / 400;
  2983 + auto const yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
  2984 + auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365]
  2985 + auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
  2986 + return days{era * 146097 + static_cast<int>(doe) - 719468};
  2987 +}
  2988 +
  2989 +CONSTCD14
  2990 +inline
  2991 +year_month_day::operator sys_days() const NOEXCEPT
  2992 +{
  2993 + return sys_days{to_days()};
  2994 +}
  2995 +
  2996 +CONSTCD14
  2997 +inline
  2998 +year_month_day::operator local_days() const NOEXCEPT
  2999 +{
  3000 + return local_days{to_days()};
  3001 +}
  3002 +
  3003 +CONSTCD14
  3004 +inline
  3005 +bool
  3006 +year_month_day::ok() const NOEXCEPT
  3007 +{
  3008 + if (!(y_.ok() && m_.ok()))
  3009 + return false;
  3010 + return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day();
  3011 +}
  3012 +
  3013 +CONSTCD11
  3014 +inline
  3015 +bool
  3016 +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT
  3017 +{
  3018 + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day();
  3019 +}
  3020 +
  3021 +CONSTCD11
  3022 +inline
  3023 +bool
  3024 +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT
  3025 +{
  3026 + return !(x == y);
  3027 +}
  3028 +
  3029 +CONSTCD11
  3030 +inline
  3031 +bool
  3032 +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT
  3033 +{
  3034 + return x.year() < y.year() ? true
  3035 + : (x.year() > y.year() ? false
  3036 + : (x.month() < y.month() ? true
  3037 + : (x.month() > y.month() ? false
  3038 + : (x.day() < y.day()))));
  3039 +}
  3040 +
  3041 +CONSTCD11
  3042 +inline
  3043 +bool
  3044 +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT
  3045 +{
  3046 + return y < x;
  3047 +}
  3048 +
  3049 +CONSTCD11
  3050 +inline
  3051 +bool
  3052 +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT
  3053 +{
  3054 + return !(y < x);
  3055 +}
  3056 +
  3057 +CONSTCD11
  3058 +inline
  3059 +bool
  3060 +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT
  3061 +{
  3062 + return !(x < y);
  3063 +}
  3064 +
  3065 +template<class CharT, class Traits>
  3066 +inline
  3067 +std::basic_ostream<CharT, Traits>&
  3068 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd)
  3069 +{
  3070 + detail::save_ostream<CharT, Traits> _(os);
  3071 + os.fill('0');
  3072 + os.flags(std::ios::dec | std::ios::right);
  3073 + os.imbue(std::locale::classic());
  3074 + os << static_cast<int>(ymd.year()) << '-';
  3075 + os.width(2);
  3076 + os << static_cast<unsigned>(ymd.month()) << '-';
  3077 + os.width(2);
  3078 + os << static_cast<unsigned>(ymd.day());
  3079 + if (!ymd.ok())
  3080 + os << " is not a valid year_month_day";
  3081 + return os;
  3082 +}
  3083 +
  3084 +CONSTCD14
  3085 +inline
  3086 +year_month_day
  3087 +year_month_day::from_days(days dp) NOEXCEPT
  3088 +{
  3089 + static_assert(std::numeric_limits<unsigned>::digits >= 18,
  3090 + "This algorithm has not been ported to a 16 bit unsigned integer");
  3091 + static_assert(std::numeric_limits<int>::digits >= 20,
  3092 + "This algorithm has not been ported to a 16 bit signed integer");
  3093 + auto const z = dp.count() + 719468;
  3094 + auto const era = (z >= 0 ? z : z - 146096) / 146097;
  3095 + auto const doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
  3096 + auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
  3097 + auto const y = static_cast<days::rep>(yoe) + era * 400;
  3098 + auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
  3099 + auto const mp = (5*doy + 2)/153; // [0, 11]
  3100 + auto const d = doy - (153*mp+2)/5 + 1; // [1, 31]
  3101 + auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12]
  3102 + return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)};
  3103 +}
  3104 +
  3105 +template<class>
  3106 +CONSTCD14
  3107 +inline
  3108 +year_month_day
  3109 +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT
  3110 +{
  3111 + return (ymd.year() / ymd.month() + dm) / ymd.day();
  3112 +}
  3113 +
  3114 +template<class>
  3115 +CONSTCD14
  3116 +inline
  3117 +year_month_day
  3118 +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT
  3119 +{
  3120 + return ymd + dm;
  3121 +}
  3122 +
  3123 +template<class>
  3124 +CONSTCD14
  3125 +inline
  3126 +year_month_day
  3127 +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT
  3128 +{
  3129 + return ymd + (-dm);
  3130 +}
  3131 +
  3132 +CONSTCD11
  3133 +inline
  3134 +year_month_day
  3135 +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT
  3136 +{
  3137 + return (ymd.year() + dy) / ymd.month() / ymd.day();
  3138 +}
  3139 +
  3140 +CONSTCD11
  3141 +inline
  3142 +year_month_day
  3143 +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT
  3144 +{
  3145 + return ymd + dy;
  3146 +}
  3147 +
  3148 +CONSTCD11
  3149 +inline
  3150 +year_month_day
  3151 +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT
  3152 +{
  3153 + return ymd + (-dy);
  3154 +}
  3155 +
  3156 +// year_month_weekday
  3157 +
  3158 +CONSTCD11
  3159 +inline
  3160 +year_month_weekday::year_month_weekday(const date::year& y, const date::month& m,
  3161 + const date::weekday_indexed& wdi)
  3162 + NOEXCEPT
  3163 + : y_(y)
  3164 + , m_(m)
  3165 + , wdi_(wdi)
  3166 + {}
  3167 +
  3168 +CONSTCD14
  3169 +inline
  3170 +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT
  3171 + : year_month_weekday(from_days(dp.time_since_epoch()))
  3172 + {}
  3173 +
  3174 +CONSTCD14
  3175 +inline
  3176 +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT
  3177 + : year_month_weekday(from_days(dp.time_since_epoch()))
  3178 + {}
  3179 +
  3180 +template<class>
  3181 +CONSTCD14
  3182 +inline
  3183 +year_month_weekday&
  3184 +year_month_weekday::operator+=(const months& m) NOEXCEPT
  3185 +{
  3186 + *this = *this + m;
  3187 + return *this;
  3188 +}
  3189 +
  3190 +template<class>
  3191 +CONSTCD14
  3192 +inline
  3193 +year_month_weekday&
  3194 +year_month_weekday::operator-=(const months& m) NOEXCEPT
  3195 +{
  3196 + *this = *this - m;
  3197 + return *this;
  3198 +}
  3199 +
  3200 +CONSTCD14
  3201 +inline
  3202 +year_month_weekday&
  3203 +year_month_weekday::operator+=(const years& y) NOEXCEPT
  3204 +{
  3205 + *this = *this + y;
  3206 + return *this;
  3207 +}
  3208 +
  3209 +CONSTCD14
  3210 +inline
  3211 +year_month_weekday&
  3212 +year_month_weekday::operator-=(const years& y) NOEXCEPT
  3213 +{
  3214 + *this = *this - y;
  3215 + return *this;
  3216 +}
  3217 +
  3218 +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;}
  3219 +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;}
  3220 +
  3221 +CONSTCD11
  3222 +inline
  3223 +weekday
  3224 +year_month_weekday::weekday() const NOEXCEPT
  3225 +{
  3226 + return wdi_.weekday();
  3227 +}
  3228 +
  3229 +CONSTCD11
  3230 +inline
  3231 +unsigned
  3232 +year_month_weekday::index() const NOEXCEPT
  3233 +{
  3234 + return wdi_.index();
  3235 +}
  3236 +
  3237 +CONSTCD11
  3238 +inline
  3239 +weekday_indexed
  3240 +year_month_weekday::weekday_indexed() const NOEXCEPT
  3241 +{
  3242 + return wdi_;
  3243 +}
  3244 +
  3245 +CONSTCD14
  3246 +inline
  3247 +year_month_weekday::operator sys_days() const NOEXCEPT
  3248 +{
  3249 + return sys_days{to_days()};
  3250 +}
  3251 +
  3252 +CONSTCD14
  3253 +inline
  3254 +year_month_weekday::operator local_days() const NOEXCEPT
  3255 +{
  3256 + return local_days{to_days()};
  3257 +}
  3258 +
  3259 +CONSTCD14
  3260 +inline
  3261 +bool
  3262 +year_month_weekday::ok() const NOEXCEPT
  3263 +{
  3264 + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1)
  3265 + return false;
  3266 + if (wdi_.index() <= 4)
  3267 + return true;
  3268 + auto d2 = wdi_.weekday() - date::weekday(static_cast<sys_days>(y_/m_/1)) +
  3269 + days((wdi_.index()-1)*7 + 1);
  3270 + return static_cast<unsigned>(d2.count()) <= static_cast<unsigned>((y_/m_/last).day());
  3271 +}
  3272 +
  3273 +CONSTCD14
  3274 +inline
  3275 +year_month_weekday
  3276 +year_month_weekday::from_days(days d) NOEXCEPT
  3277 +{
  3278 + sys_days dp{d};
  3279 + auto const wd = date::weekday(dp);
  3280 + auto const ymd = year_month_day(dp);
  3281 + return {ymd.year(), ymd.month(), wd[(static_cast<unsigned>(ymd.day())-1)/7+1]};
  3282 +}
  3283 +
  3284 +CONSTCD14
  3285 +inline
  3286 +days
  3287 +year_month_weekday::to_days() const NOEXCEPT
  3288 +{
  3289 + auto d = sys_days(y_/m_/1);
  3290 + return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7})
  3291 + ).time_since_epoch();
  3292 +}
  3293 +
  3294 +CONSTCD11
  3295 +inline
  3296 +bool
  3297 +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT
  3298 +{
  3299 + return x.year() == y.year() && x.month() == y.month() &&
  3300 + x.weekday_indexed() == y.weekday_indexed();
  3301 +}
  3302 +
  3303 +CONSTCD11
  3304 +inline
  3305 +bool
  3306 +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT
  3307 +{
  3308 + return !(x == y);
  3309 +}
  3310 +
  3311 +template<class CharT, class Traits>
  3312 +inline
  3313 +std::basic_ostream<CharT, Traits>&
  3314 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi)
  3315 +{
  3316 + detail::low_level_fmt(os, ymwdi.year()) << '/';
  3317 + detail::low_level_fmt(os, ymwdi.month()) << '/';
  3318 + detail::low_level_fmt(os, ymwdi.weekday_indexed());
  3319 + if (!ymwdi.ok())
  3320 + os << " is not a valid year_month_weekday";
  3321 + return os;
  3322 +}
  3323 +
  3324 +template<class>
  3325 +CONSTCD14
  3326 +inline
  3327 +year_month_weekday
  3328 +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT
  3329 +{
  3330 + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed();
  3331 +}
  3332 +
  3333 +template<class>
  3334 +CONSTCD14
  3335 +inline
  3336 +year_month_weekday
  3337 +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT
  3338 +{
  3339 + return ymwd + dm;
  3340 +}
  3341 +
  3342 +template<class>
  3343 +CONSTCD14
  3344 +inline
  3345 +year_month_weekday
  3346 +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT
  3347 +{
  3348 + return ymwd + (-dm);
  3349 +}
  3350 +
  3351 +CONSTCD11
  3352 +inline
  3353 +year_month_weekday
  3354 +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT
  3355 +{
  3356 + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()};
  3357 +}
  3358 +
  3359 +CONSTCD11
  3360 +inline
  3361 +year_month_weekday
  3362 +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT
  3363 +{
  3364 + return ymwd + dy;
  3365 +}
  3366 +
  3367 +CONSTCD11
  3368 +inline
  3369 +year_month_weekday
  3370 +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT
  3371 +{
  3372 + return ymwd + (-dy);
  3373 +}
  3374 +
  3375 +// year_month_weekday_last
  3376 +
  3377 +CONSTCD11
  3378 +inline
  3379 +year_month_weekday_last::year_month_weekday_last(const date::year& y,
  3380 + const date::month& m,
  3381 + const date::weekday_last& wdl) NOEXCEPT
  3382 + : y_(y)
  3383 + , m_(m)
  3384 + , wdl_(wdl)
  3385 + {}
  3386 +
  3387 +template<class>
  3388 +CONSTCD14
  3389 +inline
  3390 +year_month_weekday_last&
  3391 +year_month_weekday_last::operator+=(const months& m) NOEXCEPT
  3392 +{
  3393 + *this = *this + m;
  3394 + return *this;
  3395 +}
  3396 +
  3397 +template<class>
  3398 +CONSTCD14
  3399 +inline
  3400 +year_month_weekday_last&
  3401 +year_month_weekday_last::operator-=(const months& m) NOEXCEPT
  3402 +{
  3403 + *this = *this - m;
  3404 + return *this;
  3405 +}
  3406 +
  3407 +CONSTCD14
  3408 +inline
  3409 +year_month_weekday_last&
  3410 +year_month_weekday_last::operator+=(const years& y) NOEXCEPT
  3411 +{
  3412 + *this = *this + y;
  3413 + return *this;
  3414 +}
  3415 +
  3416 +CONSTCD14
  3417 +inline
  3418 +year_month_weekday_last&
  3419 +year_month_weekday_last::operator-=(const years& y) NOEXCEPT
  3420 +{
  3421 + *this = *this - y;
  3422 + return *this;
  3423 +}
  3424 +
  3425 +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;}
  3426 +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;}
  3427 +
  3428 +CONSTCD11
  3429 +inline
  3430 +weekday
  3431 +year_month_weekday_last::weekday() const NOEXCEPT
  3432 +{
  3433 + return wdl_.weekday();
  3434 +}
  3435 +
  3436 +CONSTCD11
  3437 +inline
  3438 +weekday_last
  3439 +year_month_weekday_last::weekday_last() const NOEXCEPT
  3440 +{
  3441 + return wdl_;
  3442 +}
  3443 +
  3444 +CONSTCD14
  3445 +inline
  3446 +year_month_weekday_last::operator sys_days() const NOEXCEPT
  3447 +{
  3448 + return sys_days{to_days()};
  3449 +}
  3450 +
  3451 +CONSTCD14
  3452 +inline
  3453 +year_month_weekday_last::operator local_days() const NOEXCEPT
  3454 +{
  3455 + return local_days{to_days()};
  3456 +}
  3457 +
  3458 +CONSTCD11
  3459 +inline
  3460 +bool
  3461 +year_month_weekday_last::ok() const NOEXCEPT
  3462 +{
  3463 + return y_.ok() && m_.ok() && wdl_.ok();
  3464 +}
  3465 +
  3466 +CONSTCD14
  3467 +inline
  3468 +days
  3469 +year_month_weekday_last::to_days() const NOEXCEPT
  3470 +{
  3471 + auto const d = sys_days(y_/m_/last);
  3472 + return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch();
  3473 +}
  3474 +
  3475 +CONSTCD11
  3476 +inline
  3477 +bool
  3478 +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT
  3479 +{
  3480 + return x.year() == y.year() && x.month() == y.month() &&
  3481 + x.weekday_last() == y.weekday_last();
  3482 +}
  3483 +
  3484 +CONSTCD11
  3485 +inline
  3486 +bool
  3487 +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT
  3488 +{
  3489 + return !(x == y);
  3490 +}
  3491 +
  3492 +template<class CharT, class Traits>
  3493 +inline
  3494 +std::basic_ostream<CharT, Traits>&
  3495 +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl)
  3496 +{
  3497 + detail::low_level_fmt(os, ymwdl.year()) << '/';
  3498 + detail::low_level_fmt(os, ymwdl.month()) << '/';
  3499 + detail::low_level_fmt(os, ymwdl.weekday_last());
  3500 + if (!ymwdl.ok())
  3501 + os << " is not a valid year_month_weekday_last";
  3502 + return os;
  3503 +}
  3504 +
  3505 +template<class>
  3506 +CONSTCD14
  3507 +inline
  3508 +year_month_weekday_last
  3509 +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT
  3510 +{
  3511 + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last();
  3512 +}
  3513 +
  3514 +template<class>
  3515 +CONSTCD14
  3516 +inline
  3517 +year_month_weekday_last
  3518 +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT
  3519 +{
  3520 + return ymwdl + dm;
  3521 +}
  3522 +
  3523 +template<class>
  3524 +CONSTCD14
  3525 +inline
  3526 +year_month_weekday_last
  3527 +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT
  3528 +{
  3529 + return ymwdl + (-dm);
  3530 +}
  3531 +
  3532 +CONSTCD11
  3533 +inline
  3534 +year_month_weekday_last
  3535 +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT
  3536 +{
  3537 + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()};
  3538 +}
  3539 +
  3540 +CONSTCD11
  3541 +inline
  3542 +year_month_weekday_last
  3543 +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT
  3544 +{
  3545 + return ymwdl + dy;
  3546 +}
  3547 +
  3548 +CONSTCD11
  3549 +inline
  3550 +year_month_weekday_last
  3551 +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT
  3552 +{
  3553 + return ymwdl + (-dy);
  3554 +}
  3555 +
  3556 +// year_month from operator/()
  3557 +
  3558 +CONSTCD11
  3559 +inline
  3560 +year_month
  3561 +operator/(const year& y, const month& m) NOEXCEPT
  3562 +{
  3563 + return {y, m};
  3564 +}
  3565 +
  3566 +CONSTCD11
  3567 +inline
  3568 +year_month
  3569 +operator/(const year& y, int m) NOEXCEPT
  3570 +{
  3571 + return y / month(static_cast<unsigned>(m));
  3572 +}
  3573 +
  3574 +// month_day from operator/()
  3575 +
  3576 +CONSTCD11
  3577 +inline
  3578 +month_day
  3579 +operator/(const month& m, const day& d) NOEXCEPT
  3580 +{
  3581 + return {m, d};
  3582 +}
  3583 +
  3584 +CONSTCD11
  3585 +inline
  3586 +month_day
  3587 +operator/(const day& d, const month& m) NOEXCEPT
  3588 +{
  3589 + return m / d;
  3590 +}
  3591 +
  3592 +CONSTCD11
  3593 +inline
  3594 +month_day
  3595 +operator/(const month& m, int d) NOEXCEPT
  3596 +{
  3597 + return m / day(static_cast<unsigned>(d));
  3598 +}
  3599 +
  3600 +CONSTCD11
  3601 +inline
  3602 +month_day
  3603 +operator/(int m, const day& d) NOEXCEPT
  3604 +{
  3605 + return month(static_cast<unsigned>(m)) / d;
  3606 +}
  3607 +
  3608 +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;}
  3609 +
  3610 +// month_day_last from operator/()
  3611 +
  3612 +CONSTCD11
  3613 +inline
  3614 +month_day_last
  3615 +operator/(const month& m, last_spec) NOEXCEPT
  3616 +{
  3617 + return month_day_last{m};
  3618 +}
  3619 +
  3620 +CONSTCD11
  3621 +inline
  3622 +month_day_last
  3623 +operator/(last_spec, const month& m) NOEXCEPT
  3624 +{
  3625 + return m/last;
  3626 +}
  3627 +
  3628 +CONSTCD11
  3629 +inline
  3630 +month_day_last
  3631 +operator/(int m, last_spec) NOEXCEPT
  3632 +{
  3633 + return month(static_cast<unsigned>(m))/last;
  3634 +}
  3635 +
  3636 +CONSTCD11
  3637 +inline
  3638 +month_day_last
  3639 +operator/(last_spec, int m) NOEXCEPT
  3640 +{
  3641 + return m/last;
  3642 +}
  3643 +
  3644 +// month_weekday from operator/()
  3645 +
  3646 +CONSTCD11
  3647 +inline
  3648 +month_weekday
  3649 +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT
  3650 +{
  3651 + return {m, wdi};
  3652 +}
  3653 +
  3654 +CONSTCD11
  3655 +inline
  3656 +month_weekday
  3657 +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT
  3658 +{
  3659 + return m / wdi;
  3660 +}
  3661 +
  3662 +CONSTCD11
  3663 +inline
  3664 +month_weekday
  3665 +operator/(int m, const weekday_indexed& wdi) NOEXCEPT
  3666 +{
  3667 + return month(static_cast<unsigned>(m)) / wdi;
  3668 +}
  3669 +
  3670 +CONSTCD11
  3671 +inline
  3672 +month_weekday
  3673 +operator/(const weekday_indexed& wdi, int m) NOEXCEPT
  3674 +{
  3675 + return m / wdi;
  3676 +}
  3677 +
  3678 +// month_weekday_last from operator/()
  3679 +
  3680 +CONSTCD11
  3681 +inline
  3682 +month_weekday_last
  3683 +operator/(const month& m, const weekday_last& wdl) NOEXCEPT
  3684 +{
  3685 + return {m, wdl};
  3686 +}
  3687 +
  3688 +CONSTCD11
  3689 +inline
  3690 +month_weekday_last
  3691 +operator/(const weekday_last& wdl, const month& m) NOEXCEPT
  3692 +{
  3693 + return m / wdl;
  3694 +}
  3695 +
  3696 +CONSTCD11
  3697 +inline
  3698 +month_weekday_last
  3699 +operator/(int m, const weekday_last& wdl) NOEXCEPT
  3700 +{
  3701 + return month(static_cast<unsigned>(m)) / wdl;
  3702 +}
  3703 +
  3704 +CONSTCD11
  3705 +inline
  3706 +month_weekday_last
  3707 +operator/(const weekday_last& wdl, int m) NOEXCEPT
  3708 +{
  3709 + return m / wdl;
  3710 +}
  3711 +
  3712 +// year_month_day from operator/()
  3713 +
  3714 +CONSTCD11
  3715 +inline
  3716 +year_month_day
  3717 +operator/(const year_month& ym, const day& d) NOEXCEPT
  3718 +{
  3719 + return {ym.year(), ym.month(), d};
  3720 +}
  3721 +
  3722 +CONSTCD11
  3723 +inline
  3724 +year_month_day
  3725 +operator/(const year_month& ym, int d) NOEXCEPT
  3726 +{
  3727 + return ym / day(static_cast<unsigned>(d));
  3728 +}
  3729 +
  3730 +CONSTCD11
  3731 +inline
  3732 +year_month_day
  3733 +operator/(const year& y, const month_day& md) NOEXCEPT
  3734 +{
  3735 + return y / md.month() / md.day();
  3736 +}
  3737 +
  3738 +CONSTCD11
  3739 +inline
  3740 +year_month_day
  3741 +operator/(int y, const month_day& md) NOEXCEPT
  3742 +{
  3743 + return year(y) / md;
  3744 +}
  3745 +
  3746 +CONSTCD11
  3747 +inline
  3748 +year_month_day
  3749 +operator/(const month_day& md, const year& y) NOEXCEPT
  3750 +{
  3751 + return y / md;
  3752 +}
  3753 +
  3754 +CONSTCD11
  3755 +inline
  3756 +year_month_day
  3757 +operator/(const month_day& md, int y) NOEXCEPT
  3758 +{
  3759 + return year(y) / md;
  3760 +}
  3761 +
  3762 +// year_month_day_last from operator/()
  3763 +
  3764 +CONSTCD11
  3765 +inline
  3766 +year_month_day_last
  3767 +operator/(const year_month& ym, last_spec) NOEXCEPT
  3768 +{
  3769 + return {ym.year(), month_day_last{ym.month()}};
  3770 +}
  3771 +
  3772 +CONSTCD11
  3773 +inline
  3774 +year_month_day_last
  3775 +operator/(const year& y, const month_day_last& mdl) NOEXCEPT
  3776 +{
  3777 + return {y, mdl};
  3778 +}
  3779 +
  3780 +CONSTCD11
  3781 +inline
  3782 +year_month_day_last
  3783 +operator/(int y, const month_day_last& mdl) NOEXCEPT
  3784 +{
  3785 + return year(y) / mdl;
  3786 +}
  3787 +
  3788 +CONSTCD11
  3789 +inline
  3790 +year_month_day_last
  3791 +operator/(const month_day_last& mdl, const year& y) NOEXCEPT
  3792 +{
  3793 + return y / mdl;
  3794 +}
  3795 +
  3796 +CONSTCD11
  3797 +inline
  3798 +year_month_day_last
  3799 +operator/(const month_day_last& mdl, int y) NOEXCEPT
  3800 +{
  3801 + return year(y) / mdl;
  3802 +}
  3803 +
  3804 +// year_month_weekday from operator/()
  3805 +
  3806 +CONSTCD11
  3807 +inline
  3808 +year_month_weekday
  3809 +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT
  3810 +{
  3811 + return {ym.year(), ym.month(), wdi};
  3812 +}
  3813 +
  3814 +CONSTCD11
  3815 +inline
  3816 +year_month_weekday
  3817 +operator/(const year& y, const month_weekday& mwd) NOEXCEPT
  3818 +{
  3819 + return {y, mwd.month(), mwd.weekday_indexed()};
  3820 +}
  3821 +
  3822 +CONSTCD11
  3823 +inline
  3824 +year_month_weekday
  3825 +operator/(int y, const month_weekday& mwd) NOEXCEPT
  3826 +{
  3827 + return year(y) / mwd;
  3828 +}
  3829 +
  3830 +CONSTCD11
  3831 +inline
  3832 +year_month_weekday
  3833 +operator/(const month_weekday& mwd, const year& y) NOEXCEPT
  3834 +{
  3835 + return y / mwd;
  3836 +}
  3837 +
  3838 +CONSTCD11
  3839 +inline
  3840 +year_month_weekday
  3841 +operator/(const month_weekday& mwd, int y) NOEXCEPT
  3842 +{
  3843 + return year(y) / mwd;
  3844 +}
  3845 +
  3846 +// year_month_weekday_last from operator/()
  3847 +
  3848 +CONSTCD11
  3849 +inline
  3850 +year_month_weekday_last
  3851 +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT
  3852 +{
  3853 + return {ym.year(), ym.month(), wdl};
  3854 +}
  3855 +
  3856 +CONSTCD11
  3857 +inline
  3858 +year_month_weekday_last
  3859 +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT
  3860 +{
  3861 + return {y, mwdl.month(), mwdl.weekday_last()};
  3862 +}
  3863 +
  3864 +CONSTCD11
  3865 +inline
  3866 +year_month_weekday_last
  3867 +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT
  3868 +{
  3869 + return year(y) / mwdl;
  3870 +}
  3871 +
  3872 +CONSTCD11
  3873 +inline
  3874 +year_month_weekday_last
  3875 +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT
  3876 +{
  3877 + return y / mwdl;
  3878 +}
  3879 +
  3880 +CONSTCD11
  3881 +inline
  3882 +year_month_weekday_last
  3883 +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT
  3884 +{
  3885 + return year(y) / mwdl;
  3886 +}
  3887 +
  3888 +template <class Duration>
  3889 +struct fields;
  3890 +
  3891 +template <class CharT, class Traits, class Duration>
  3892 +std::basic_ostream<CharT, Traits>&
  3893 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
  3894 + const fields<Duration>& fds, const std::string* abbrev = nullptr,
  3895 + const std::chrono::seconds* offset_sec = nullptr);
  3896 +
  3897 +template <class CharT, class Traits, class Duration, class Alloc>
  3898 +std::basic_istream<CharT, Traits>&
  3899 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
  3900 + fields<Duration>& fds, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  3901 + std::chrono::minutes* offset = nullptr);
  3902 +
  3903 +// hh_mm_ss
  3904 +
  3905 +namespace detail
  3906 +{
  3907 +
  3908 +struct undocumented {explicit undocumented() = default;};
  3909 +
  3910 +// width<n>::value is the number of fractional decimal digits in 1/n
  3911 +// width<0>::value and width<1>::value are defined to be 0
  3912 +// If 1/n takes more than 18 fractional decimal digits,
  3913 +// the result is truncated to 19.
  3914 +// Example: width<2>::value == 1
  3915 +// Example: width<3>::value == 19
  3916 +// Example: width<4>::value == 2
  3917 +// Example: width<10>::value == 1
  3918 +// Example: width<1000>::value == 3
  3919 +template <std::uint64_t n, std::uint64_t d, unsigned w = 0,
  3920 + bool should_continue = n%d != 0 && (w < 19)>
  3921 +struct width
  3922 +{
  3923 + static_assert(d > 0, "width called with zero denominator");
  3924 + static CONSTDATA unsigned value = 1 + width<n%d*10, d, w+1>::value;
  3925 +};
  3926 +
  3927 +template <std::uint64_t n, std::uint64_t d, unsigned w>
  3928 +struct width<n, d, w, false>
  3929 +{
  3930 + static CONSTDATA unsigned value = 0;
  3931 +};
  3932 +
  3933 +template <unsigned exp>
  3934 +struct static_pow10
  3935 +{
  3936 +private:
  3937 + static CONSTDATA std::uint64_t h = static_pow10<exp/2>::value;
  3938 +public:
  3939 + static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1);
  3940 +};
  3941 +
  3942 +template <>
  3943 +struct static_pow10<0>
  3944 +{
  3945 + static CONSTDATA std::uint64_t value = 1;
  3946 +};
  3947 +
  3948 +template <class Duration>
  3949 +class decimal_format_seconds
  3950 +{
  3951 + using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
  3952 + using rep = typename CT::rep;
  3953 + static unsigned CONSTDATA trial_width =
  3954 + detail::width<CT::period::num, CT::period::den>::value;
  3955 +public:
  3956 + static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u;
  3957 + using precision = std::chrono::duration<rep,
  3958 + std::ratio<1, static_pow10<width>::value>>;
  3959 +
  3960 +private:
  3961 + std::chrono::seconds s_;
  3962 + precision sub_s_;
  3963 +
  3964 +public:
  3965 + CONSTCD11 decimal_format_seconds()
  3966 + : s_()
  3967 + , sub_s_()
  3968 + {}
  3969 +
  3970 + CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT
  3971 + : s_(std::chrono::duration_cast<std::chrono::seconds>(d))
  3972 + , sub_s_(std::chrono::duration_cast<precision>(d - s_))
  3973 + {}
  3974 +
  3975 + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;}
  3976 + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;}
  3977 + CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;}
  3978 +
  3979 + CONSTCD14 precision to_duration() const NOEXCEPT
  3980 + {
  3981 + return s_ + sub_s_;
  3982 + }
  3983 +
  3984 + CONSTCD11 bool in_conventional_range() const NOEXCEPT
  3985 + {
  3986 + return sub_s_ < std::chrono::seconds{1} && s_ < std::chrono::minutes{1};
  3987 + }
  3988 +
  3989 + template <class CharT, class Traits>
  3990 + friend
  3991 + std::basic_ostream<CharT, Traits>&
  3992 + operator<<(std::basic_ostream<CharT, Traits>& os, const decimal_format_seconds& x)
  3993 + {
  3994 + return x.print(os, std::chrono::treat_as_floating_point<rep>{});
  3995 + }
  3996 +
  3997 + template <class CharT, class Traits>
  3998 + std::basic_ostream<CharT, Traits>&
  3999 + print(std::basic_ostream<CharT, Traits>& os, std::true_type) const
  4000 + {
  4001 + date::detail::save_ostream<CharT, Traits> _(os);
  4002 + std::chrono::duration<rep> d = s_ + sub_s_;
  4003 + if (d < std::chrono::seconds{10})
  4004 + os << '0';
  4005 + os.precision(width+6);
  4006 + os << std::fixed << d.count();
  4007 + return os;
  4008 + }
  4009 +
  4010 + template <class CharT, class Traits>
  4011 + std::basic_ostream<CharT, Traits>&
  4012 + print(std::basic_ostream<CharT, Traits>& os, std::false_type) const
  4013 + {
  4014 + date::detail::save_ostream<CharT, Traits> _(os);
  4015 + os.fill('0');
  4016 + os.flags(std::ios::dec | std::ios::right);
  4017 + os.width(2);
  4018 + os << s_.count();
  4019 + if (width > 0)
  4020 + {
  4021 +#if !ONLY_C_LOCALE
  4022 + os << std::use_facet<std::numpunct<CharT>>(os.getloc()).decimal_point();
  4023 +#else
  4024 + os << '.';
  4025 +#endif
  4026 + date::detail::save_ostream<CharT, Traits> _s(os);
  4027 + os.imbue(std::locale::classic());
  4028 + os.width(width);
  4029 + os << sub_s_.count();
  4030 + }
  4031 + return os;
  4032 + }
  4033 +};
  4034 +
  4035 +template <class Rep, class Period>
  4036 +inline
  4037 +CONSTCD11
  4038 +typename std::enable_if
  4039 + <
  4040 + std::numeric_limits<Rep>::is_signed,
  4041 + std::chrono::duration<Rep, Period>
  4042 + >::type
  4043 +abs(std::chrono::duration<Rep, Period> d)
  4044 +{
  4045 + return d >= d.zero() ? +d : -d;
  4046 +}
  4047 +
  4048 +template <class Rep, class Period>
  4049 +inline
  4050 +CONSTCD11
  4051 +typename std::enable_if
  4052 + <
  4053 + !std::numeric_limits<Rep>::is_signed,
  4054 + std::chrono::duration<Rep, Period>
  4055 + >::type
  4056 +abs(std::chrono::duration<Rep, Period> d)
  4057 +{
  4058 + return d;
  4059 +}
  4060 +
  4061 +} // namespace detail
  4062 +
  4063 +template <class Duration>
  4064 +class hh_mm_ss
  4065 +{
  4066 + using dfs = detail::decimal_format_seconds<typename std::common_type<Duration,
  4067 + std::chrono::seconds>::type>;
  4068 +
  4069 + std::chrono::hours h_;
  4070 + std::chrono::minutes m_;
  4071 + dfs s_;
  4072 + bool neg_;
  4073 +
  4074 +public:
  4075 + static unsigned CONSTDATA fractional_width = dfs::width;
  4076 + using precision = typename dfs::precision;
  4077 +
  4078 + CONSTCD11 hh_mm_ss() NOEXCEPT
  4079 + : hh_mm_ss(Duration::zero())
  4080 + {}
  4081 +
  4082 + CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT
  4083 + : h_(std::chrono::duration_cast<std::chrono::hours>(detail::abs(d)))
  4084 + , m_(std::chrono::duration_cast<std::chrono::minutes>(detail::abs(d)) - h_)
  4085 + , s_(detail::abs(d) - h_ - m_)
  4086 + , neg_(d < Duration::zero())
  4087 + {}
  4088 +
  4089 + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;}
  4090 + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;}
  4091 + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();}
  4092 + CONSTCD14 std::chrono::seconds&
  4093 + seconds(detail::undocumented) NOEXCEPT {return s_.seconds();}
  4094 + CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();}
  4095 + CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;}
  4096 +
  4097 + CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();}
  4098 + CONSTCD11 precision to_duration() const NOEXCEPT
  4099 + {return (s_.to_duration() + m_ + h_) * (1-2*neg_);}
  4100 +
  4101 + CONSTCD11 bool in_conventional_range() const NOEXCEPT
  4102 + {
  4103 + return !neg_ && h_ < days{1} && m_ < std::chrono::hours{1} &&
  4104 + s_.in_conventional_range();
  4105 + }
  4106 +
  4107 +private:
  4108 +
  4109 + template <class charT, class traits>
  4110 + friend
  4111 + std::basic_ostream<charT, traits>&
  4112 + operator<<(std::basic_ostream<charT, traits>& os, hh_mm_ss const& tod)
  4113 + {
  4114 + if (tod.is_negative())
  4115 + os << '-';
  4116 + if (tod.h_ < std::chrono::hours{10})
  4117 + os << '0';
  4118 + os << tod.h_.count() << ':';
  4119 + if (tod.m_ < std::chrono::minutes{10})
  4120 + os << '0';
  4121 + os << tod.m_.count() << ':' << tod.s_;
  4122 + return os;
  4123 + }
  4124 +
  4125 + template <class CharT, class Traits, class Duration2>
  4126 + friend
  4127 + std::basic_ostream<CharT, Traits>&
  4128 + date::to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
  4129 + const fields<Duration2>& fds, const std::string* abbrev,
  4130 + const std::chrono::seconds* offset_sec);
  4131 +
  4132 + template <class CharT, class Traits, class Duration2, class Alloc>
  4133 + friend
  4134 + std::basic_istream<CharT, Traits>&
  4135 + date::from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
  4136 + fields<Duration2>& fds,
  4137 + std::basic_string<CharT, Traits, Alloc>* abbrev, std::chrono::minutes* offset);
  4138 +};
  4139 +
  4140 +inline
  4141 +CONSTCD14
  4142 +bool
  4143 +is_am(std::chrono::hours const& h) NOEXCEPT
  4144 +{
  4145 + using std::chrono::hours;
  4146 + return hours{0} <= h && h < hours{12};
  4147 +}
  4148 +
  4149 +inline
  4150 +CONSTCD14
  4151 +bool
  4152 +is_pm(std::chrono::hours const& h) NOEXCEPT
  4153 +{
  4154 + using std::chrono::hours;
  4155 + return hours{12} <= h && h < hours{24};
  4156 +}
  4157 +
  4158 +inline
  4159 +CONSTCD14
  4160 +std::chrono::hours
  4161 +make12(std::chrono::hours h) NOEXCEPT
  4162 +{
  4163 + using std::chrono::hours;
  4164 + if (h < hours{12})
  4165 + {
  4166 + if (h == hours{0})
  4167 + h = hours{12};
  4168 + }
  4169 + else
  4170 + {
  4171 + if (h != hours{12})
  4172 + h = h - hours{12};
  4173 + }
  4174 + return h;
  4175 +}
  4176 +
  4177 +inline
  4178 +CONSTCD14
  4179 +std::chrono::hours
  4180 +make24(std::chrono::hours h, bool is_pm) NOEXCEPT
  4181 +{
  4182 + using std::chrono::hours;
  4183 + if (is_pm)
  4184 + {
  4185 + if (h != hours{12})
  4186 + h = h + hours{12};
  4187 + }
  4188 + else if (h == hours{12})
  4189 + h = hours{0};
  4190 + return h;
  4191 +}
  4192 +
  4193 +template <class Duration>
  4194 +using time_of_day = hh_mm_ss<Duration>;
  4195 +
  4196 +template <class Rep, class Period>
  4197 +CONSTCD11
  4198 +inline
  4199 +hh_mm_ss<std::chrono::duration<Rep, Period>>
  4200 +make_time(const std::chrono::duration<Rep, Period>& d)
  4201 +{
  4202 + return hh_mm_ss<std::chrono::duration<Rep, Period>>(d);
  4203 +}
  4204 +
  4205 +template <class CharT, class Traits, class Duration>
  4206 +inline
  4207 +typename std::enable_if
  4208 +<
  4209 + std::ratio_less<typename Duration::period, days::period>::value
  4210 + , std::basic_ostream<CharT, Traits>&
  4211 +>::type
  4212 +operator<<(std::basic_ostream<CharT, Traits>& os, const sys_time<Duration>& tp)
  4213 +{
  4214 + auto const dp = date::floor<days>(tp);
  4215 + return os << year_month_day(dp) << ' ' << make_time(tp-dp);
  4216 +}
  4217 +
  4218 +template <class CharT, class Traits>
  4219 +inline
  4220 +std::basic_ostream<CharT, Traits>&
  4221 +operator<<(std::basic_ostream<CharT, Traits>& os, const sys_days& dp)
  4222 +{
  4223 + return os << year_month_day(dp);
  4224 +}
  4225 +
  4226 +template <class CharT, class Traits, class Duration>
  4227 +inline
  4228 +std::basic_ostream<CharT, Traits>&
  4229 +operator<<(std::basic_ostream<CharT, Traits>& os, const local_time<Duration>& ut)
  4230 +{
  4231 + return (os << sys_time<Duration>{ut.time_since_epoch()});
  4232 +}
  4233 +
  4234 +namespace detail
  4235 +{
  4236 +
  4237 +template <class CharT, std::size_t N>
  4238 +class string_literal;
  4239 +
  4240 +template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
  4241 +inline
  4242 +CONSTCD14
  4243 +string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type,
  4244 + N1 + N2 - 1>
  4245 +operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT;
  4246 +
  4247 +template <class CharT, std::size_t N>
  4248 +class string_literal
  4249 +{
  4250 + CharT p_[N];
  4251 +
  4252 + CONSTCD11 string_literal() NOEXCEPT
  4253 + : p_{}
  4254 + {}
  4255 +
  4256 +public:
  4257 + using const_iterator = const CharT*;
  4258 +
  4259 + string_literal(string_literal const&) = default;
  4260 + string_literal& operator=(string_literal const&) = delete;
  4261 +
  4262 + template <std::size_t N1 = 2,
  4263 + class = typename std::enable_if<N1 == N>::type>
  4264 + CONSTCD11 string_literal(CharT c) NOEXCEPT
  4265 + : p_{c}
  4266 + {
  4267 + }
  4268 +
  4269 + template <std::size_t N1 = 3,
  4270 + class = typename std::enable_if<N1 == N>::type>
  4271 + CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT
  4272 + : p_{c1, c2}
  4273 + {
  4274 + }
  4275 +
  4276 + template <std::size_t N1 = 4,
  4277 + class = typename std::enable_if<N1 == N>::type>
  4278 + CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT
  4279 + : p_{c1, c2, c3}
  4280 + {
  4281 + }
  4282 +
  4283 + CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT
  4284 + : p_{}
  4285 + {
  4286 + for (std::size_t i = 0; i < N; ++i)
  4287 + p_[i] = a[i];
  4288 + }
  4289 +
  4290 + template <class U = CharT,
  4291 + class = typename std::enable_if<(1 < sizeof(U))>::type>
  4292 + CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT
  4293 + : p_{}
  4294 + {
  4295 + for (std::size_t i = 0; i < N; ++i)
  4296 + p_[i] = a[i];
  4297 + }
  4298 +
  4299 + template <class CharT2,
  4300 + class = typename std::enable_if<!std::is_same<CharT2, CharT>::value>::type>
  4301 + CONSTCD14 string_literal(string_literal<CharT2, N> const& a) NOEXCEPT
  4302 + : p_{}
  4303 + {
  4304 + for (std::size_t i = 0; i < N; ++i)
  4305 + p_[i] = a[i];
  4306 + }
  4307 +
  4308 + CONSTCD11 const CharT* data() const NOEXCEPT {return p_;}
  4309 + CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;}
  4310 +
  4311 + CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;}
  4312 + CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;}
  4313 +
  4314 + CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT
  4315 + {
  4316 + return p_[n];
  4317 + }
  4318 +
  4319 + template <class Traits>
  4320 + friend
  4321 + std::basic_ostream<CharT, Traits>&
  4322 + operator<<(std::basic_ostream<CharT, Traits>& os, const string_literal& s)
  4323 + {
  4324 + return os << s.p_;
  4325 + }
  4326 +
  4327 + template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
  4328 + friend
  4329 + CONSTCD14
  4330 + string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type,
  4331 + N1 + N2 - 1>
  4332 + operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT;
  4333 +};
  4334 +
  4335 +template <class CharT>
  4336 +CONSTCD11
  4337 +inline
  4338 +string_literal<CharT, 3>
  4339 +operator+(const string_literal<CharT, 2>& x, const string_literal<CharT, 2>& y) NOEXCEPT
  4340 +{
  4341 + return string_literal<CharT, 3>(x[0], y[0]);
  4342 +}
  4343 +
  4344 +template <class CharT>
  4345 +CONSTCD11
  4346 +inline
  4347 +string_literal<CharT, 4>
  4348 +operator+(const string_literal<CharT, 3>& x, const string_literal<CharT, 2>& y) NOEXCEPT
  4349 +{
  4350 + return string_literal<CharT, 4>(x[0], x[1], y[0]);
  4351 +}
  4352 +
  4353 +template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
  4354 +CONSTCD14
  4355 +inline
  4356 +string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type,
  4357 + N1 + N2 - 1>
  4358 +operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT
  4359 +{
  4360 + using CT = typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type;
  4361 +
  4362 + string_literal<CT, N1 + N2 - 1> r;
  4363 + std::size_t i = 0;
  4364 + for (; i < N1-1; ++i)
  4365 + r.p_[i] = CT(x.p_[i]);
  4366 + for (std::size_t j = 0; j < N2; ++j, ++i)
  4367 + r.p_[i] = CT(y.p_[j]);
  4368 +
  4369 + return r;
  4370 +}
  4371 +
  4372 +
  4373 +template <class CharT, class Traits, class Alloc, std::size_t N>
  4374 +inline
  4375 +std::basic_string<CharT, Traits, Alloc>
  4376 +operator+(std::basic_string<CharT, Traits, Alloc> x, const string_literal<CharT, N>& y)
  4377 +{
  4378 + x.append(y.data(), y.size());
  4379 + return x;
  4380 +}
  4381 +
  4382 +#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \
  4383 + && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150)
  4384 +
  4385 +template <class CharT,
  4386 + class = std::enable_if_t<std::is_same<CharT, char>::value ||
  4387 + std::is_same<CharT, wchar_t>::value ||
  4388 + std::is_same<CharT, char16_t>::value ||
  4389 + std::is_same<CharT, char32_t>::value>>
  4390 +CONSTCD14
  4391 +inline
  4392 +string_literal<CharT, 2>
  4393 +msl(CharT c) NOEXCEPT
  4394 +{
  4395 + return string_literal<CharT, 2>{c};
  4396 +}
  4397 +
  4398 +CONSTCD14
  4399 +inline
  4400 +std::size_t
  4401 +to_string_len(std::intmax_t i)
  4402 +{
  4403 + std::size_t r = 0;
  4404 + do
  4405 + {
  4406 + i /= 10;
  4407 + ++r;
  4408 + } while (i > 0);
  4409 + return r;
  4410 +}
  4411 +
  4412 +template <std::intmax_t N>
  4413 +CONSTCD14
  4414 +inline
  4415 +std::enable_if_t
  4416 +<
  4417 + N < 10,
  4418 + string_literal<char, to_string_len(N)+1>
  4419 +>
  4420 +msl() NOEXCEPT
  4421 +{
  4422 + return msl(char(N % 10 + '0'));
  4423 +}
  4424 +
  4425 +template <std::intmax_t N>
  4426 +CONSTCD14
  4427 +inline
  4428 +std::enable_if_t
  4429 +<
  4430 + 10 <= N,
  4431 + string_literal<char, to_string_len(N)+1>
  4432 +>
  4433 +msl() NOEXCEPT
  4434 +{
  4435 + return msl<N/10>() + msl(char(N % 10 + '0'));
  4436 +}
  4437 +
  4438 +template <class CharT, std::intmax_t N, std::intmax_t D>
  4439 +CONSTCD14
  4440 +inline
  4441 +std::enable_if_t
  4442 +<
  4443 + std::ratio<N, D>::type::den != 1,
  4444 + string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) +
  4445 + to_string_len(std::ratio<N, D>::type::den) + 4>
  4446 +>
  4447 +msl(std::ratio<N, D>) NOEXCEPT
  4448 +{
  4449 + using R = typename std::ratio<N, D>::type;
  4450 + return msl(CharT{'['}) + msl<R::num>() + msl(CharT{'/'}) +
  4451 + msl<R::den>() + msl(CharT{']'});
  4452 +}
  4453 +
  4454 +template <class CharT, std::intmax_t N, std::intmax_t D>
  4455 +CONSTCD14
  4456 +inline
  4457 +std::enable_if_t
  4458 +<
  4459 + std::ratio<N, D>::type::den == 1,
  4460 + string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) + 3>
  4461 +>
  4462 +msl(std::ratio<N, D>) NOEXCEPT
  4463 +{
  4464 + using R = typename std::ratio<N, D>::type;
  4465 + return msl(CharT{'['}) + msl<R::num>() + msl(CharT{']'});
  4466 +}
  4467 +
  4468 +
  4469 +#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411)
  4470 +
  4471 +inline
  4472 +std::string
  4473 +to_string(std::uint64_t x)
  4474 +{
  4475 + return std::to_string(x);
  4476 +}
  4477 +
  4478 +template <class CharT>
  4479 +inline
  4480 +std::basic_string<CharT>
  4481 +to_string(std::uint64_t x)
  4482 +{
  4483 + auto y = std::to_string(x);
  4484 + return std::basic_string<CharT>(y.begin(), y.end());
  4485 +}
  4486 +
  4487 +template <class CharT, std::intmax_t N, std::intmax_t D>
  4488 +inline
  4489 +typename std::enable_if
  4490 +<
  4491 + std::ratio<N, D>::type::den != 1,
  4492 + std::basic_string<CharT>
  4493 +>::type
  4494 +msl(std::ratio<N, D>)
  4495 +{
  4496 + using R = typename std::ratio<N, D>::type;
  4497 + return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{'/'} +
  4498 + to_string<CharT>(R::den) + CharT{']'};
  4499 +}
  4500 +
  4501 +template <class CharT, std::intmax_t N, std::intmax_t D>
  4502 +inline
  4503 +typename std::enable_if
  4504 +<
  4505 + std::ratio<N, D>::type::den == 1,
  4506 + std::basic_string<CharT>
  4507 +>::type
  4508 +msl(std::ratio<N, D>)
  4509 +{
  4510 + using R = typename std::ratio<N, D>::type;
  4511 + return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{']'};
  4512 +}
  4513 +
  4514 +#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411)
  4515 +
  4516 +template <class CharT>
  4517 +CONSTCD11
  4518 +inline
  4519 +string_literal<CharT, 2>
  4520 +msl(std::atto) NOEXCEPT
  4521 +{
  4522 + return string_literal<CharT, 2>{'a'};
  4523 +}
  4524 +
  4525 +template <class CharT>
  4526 +CONSTCD11
  4527 +inline
  4528 +string_literal<CharT, 2>
  4529 +msl(std::femto) NOEXCEPT
  4530 +{
  4531 + return string_literal<CharT, 2>{'f'};
  4532 +}
  4533 +
  4534 +template <class CharT>
  4535 +CONSTCD11
  4536 +inline
  4537 +string_literal<CharT, 2>
  4538 +msl(std::pico) NOEXCEPT
  4539 +{
  4540 + return string_literal<CharT, 2>{'p'};
  4541 +}
  4542 +
  4543 +template <class CharT>
  4544 +CONSTCD11
  4545 +inline
  4546 +string_literal<CharT, 2>
  4547 +msl(std::nano) NOEXCEPT
  4548 +{
  4549 + return string_literal<CharT, 2>{'n'};
  4550 +}
  4551 +
  4552 +template <class CharT>
  4553 +CONSTCD11
  4554 +inline
  4555 +typename std::enable_if
  4556 +<
  4557 + std::is_same<CharT, char>::value,
  4558 + string_literal<char, 3>
  4559 +>::type
  4560 +msl(std::micro) NOEXCEPT
  4561 +{
  4562 + return string_literal<char, 3>{'\xC2', '\xB5'};
  4563 +}
  4564 +
  4565 +template <class CharT>
  4566 +CONSTCD11
  4567 +inline
  4568 +typename std::enable_if
  4569 +<
  4570 + !std::is_same<CharT, char>::value,
  4571 + string_literal<CharT, 2>
  4572 +>::type
  4573 +msl(std::micro) NOEXCEPT
  4574 +{
  4575 + return string_literal<CharT, 2>{CharT{static_cast<unsigned char>('\xB5')}};
  4576 +}
  4577 +
  4578 +template <class CharT>
  4579 +CONSTCD11
  4580 +inline
  4581 +string_literal<CharT, 2>
  4582 +msl(std::milli) NOEXCEPT
  4583 +{
  4584 + return string_literal<CharT, 2>{'m'};
  4585 +}
  4586 +
  4587 +template <class CharT>
  4588 +CONSTCD11
  4589 +inline
  4590 +string_literal<CharT, 2>
  4591 +msl(std::centi) NOEXCEPT
  4592 +{
  4593 + return string_literal<CharT, 2>{'c'};
  4594 +}
  4595 +
  4596 +template <class CharT>
  4597 +CONSTCD11
  4598 +inline
  4599 +string_literal<CharT, 3>
  4600 +msl(std::deca) NOEXCEPT
  4601 +{
  4602 + return string_literal<CharT, 3>{'d', 'a'};
  4603 +}
  4604 +
  4605 +template <class CharT>
  4606 +CONSTCD11
  4607 +inline
  4608 +string_literal<CharT, 2>
  4609 +msl(std::deci) NOEXCEPT
  4610 +{
  4611 + return string_literal<CharT, 2>{'d'};
  4612 +}
  4613 +
  4614 +template <class CharT>
  4615 +CONSTCD11
  4616 +inline
  4617 +string_literal<CharT, 2>
  4618 +msl(std::hecto) NOEXCEPT
  4619 +{
  4620 + return string_literal<CharT, 2>{'h'};
  4621 +}
  4622 +
  4623 +template <class CharT>
  4624 +CONSTCD11
  4625 +inline
  4626 +string_literal<CharT, 2>
  4627 +msl(std::kilo) NOEXCEPT
  4628 +{
  4629 + return string_literal<CharT, 2>{'k'};
  4630 +}
  4631 +
  4632 +template <class CharT>
  4633 +CONSTCD11
  4634 +inline
  4635 +string_literal<CharT, 2>
  4636 +msl(std::mega) NOEXCEPT
  4637 +{
  4638 + return string_literal<CharT, 2>{'M'};
  4639 +}
  4640 +
  4641 +template <class CharT>
  4642 +CONSTCD11
  4643 +inline
  4644 +string_literal<CharT, 2>
  4645 +msl(std::giga) NOEXCEPT
  4646 +{
  4647 + return string_literal<CharT, 2>{'G'};
  4648 +}
  4649 +
  4650 +template <class CharT>
  4651 +CONSTCD11
  4652 +inline
  4653 +string_literal<CharT, 2>
  4654 +msl(std::tera) NOEXCEPT
  4655 +{
  4656 + return string_literal<CharT, 2>{'T'};
  4657 +}
  4658 +
  4659 +template <class CharT>
  4660 +CONSTCD11
  4661 +inline
  4662 +string_literal<CharT, 2>
  4663 +msl(std::peta) NOEXCEPT
  4664 +{
  4665 + return string_literal<CharT, 2>{'P'};
  4666 +}
  4667 +
  4668 +template <class CharT>
  4669 +CONSTCD11
  4670 +inline
  4671 +string_literal<CharT, 2>
  4672 +msl(std::exa) NOEXCEPT
  4673 +{
  4674 + return string_literal<CharT, 2>{'E'};
  4675 +}
  4676 +
  4677 +template <class CharT, class Period>
  4678 +CONSTCD11
  4679 +inline
  4680 +auto
  4681 +get_units(Period p)
  4682 + -> decltype(msl<CharT>(p) + string_literal<CharT, 2>{'s'})
  4683 +{
  4684 + return msl<CharT>(p) + string_literal<CharT, 2>{'s'};
  4685 +}
  4686 +
  4687 +template <class CharT>
  4688 +CONSTCD11
  4689 +inline
  4690 +string_literal<CharT, 2>
  4691 +get_units(std::ratio<1>)
  4692 +{
  4693 + return string_literal<CharT, 2>{'s'};
  4694 +}
  4695 +
  4696 +template <class CharT>
  4697 +CONSTCD11
  4698 +inline
  4699 +string_literal<CharT, 2>
  4700 +get_units(std::ratio<3600>)
  4701 +{
  4702 + return string_literal<CharT, 2>{'h'};
  4703 +}
  4704 +
  4705 +template <class CharT>
  4706 +CONSTCD11
  4707 +inline
  4708 +string_literal<CharT, 4>
  4709 +get_units(std::ratio<60>)
  4710 +{
  4711 + return string_literal<CharT, 4>{'m', 'i', 'n'};
  4712 +}
  4713 +
  4714 +template <class CharT>
  4715 +CONSTCD11
  4716 +inline
  4717 +string_literal<CharT, 2>
  4718 +get_units(std::ratio<86400>)
  4719 +{
  4720 + return string_literal<CharT, 2>{'d'};
  4721 +}
  4722 +
  4723 +template <class CharT, class Traits = std::char_traits<CharT>>
  4724 +struct make_string;
  4725 +
  4726 +template <>
  4727 +struct make_string<char>
  4728 +{
  4729 + template <class Rep>
  4730 + static
  4731 + std::string
  4732 + from(Rep n)
  4733 + {
  4734 + return std::to_string(n);
  4735 + }
  4736 +};
  4737 +
  4738 +template <class Traits>
  4739 +struct make_string<char, Traits>
  4740 +{
  4741 + template <class Rep>
  4742 + static
  4743 + std::basic_string<char, Traits>
  4744 + from(Rep n)
  4745 + {
  4746 + auto s = std::to_string(n);
  4747 + return std::basic_string<char, Traits>(s.begin(), s.end());
  4748 + }
  4749 +};
  4750 +
  4751 +template <>
  4752 +struct make_string<wchar_t>
  4753 +{
  4754 + template <class Rep>
  4755 + static
  4756 + std::wstring
  4757 + from(Rep n)
  4758 + {
  4759 + return std::to_wstring(n);
  4760 + }
  4761 +};
  4762 +
  4763 +template <class Traits>
  4764 +struct make_string<wchar_t, Traits>
  4765 +{
  4766 + template <class Rep>
  4767 + static
  4768 + std::basic_string<wchar_t, Traits>
  4769 + from(Rep n)
  4770 + {
  4771 + auto s = std::to_wstring(n);
  4772 + return std::basic_string<wchar_t, Traits>(s.begin(), s.end());
  4773 + }
  4774 +};
  4775 +
  4776 +} // namespace detail
  4777 +
  4778 +// to_stream
  4779 +
  4780 +CONSTDATA year nanyear{-32768};
  4781 +
  4782 +template <class Duration>
  4783 +struct fields
  4784 +{
  4785 + year_month_day ymd{nanyear/0/0};
  4786 + weekday wd{8u};
  4787 + hh_mm_ss<Duration> tod{};
  4788 + bool has_tod = false;
  4789 +
  4790 +#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ <= 409)
  4791 + fields() : ymd{nanyear/0/0}, wd{8u}, tod{}, has_tod{false} {}
  4792 +#else
  4793 + fields() = default;
  4794 +#endif
  4795 +
  4796 + fields(year_month_day ymd_) : ymd(ymd_) {}
  4797 + fields(weekday wd_) : wd(wd_) {}
  4798 + fields(hh_mm_ss<Duration> tod_) : tod(tod_), has_tod(true) {}
  4799 +
  4800 + fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {}
  4801 + fields(year_month_day ymd_, hh_mm_ss<Duration> tod_) : ymd(ymd_), tod(tod_),
  4802 + has_tod(true) {}
  4803 +
  4804 + fields(weekday wd_, hh_mm_ss<Duration> tod_) : wd(wd_), tod(tod_), has_tod(true) {}
  4805 +
  4806 + fields(year_month_day ymd_, weekday wd_, hh_mm_ss<Duration> tod_)
  4807 + : ymd(ymd_)
  4808 + , wd(wd_)
  4809 + , tod(tod_)
  4810 + , has_tod(true)
  4811 + {}
  4812 +};
  4813 +
  4814 +namespace detail
  4815 +{
  4816 +
  4817 +template <class CharT, class Traits, class Duration>
  4818 +unsigned
  4819 +extract_weekday(std::basic_ostream<CharT, Traits>& os, const fields<Duration>& fds)
  4820 +{
  4821 + if (!fds.ymd.ok() && !fds.wd.ok())
  4822 + {
  4823 + // fds does not contain a valid weekday
  4824 + os.setstate(std::ios::failbit);
  4825 + return 8;
  4826 + }
  4827 + weekday wd;
  4828 + if (fds.ymd.ok())
  4829 + {
  4830 + wd = weekday{sys_days(fds.ymd)};
  4831 + if (fds.wd.ok() && wd != fds.wd)
  4832 + {
  4833 + // fds.ymd and fds.wd are inconsistent
  4834 + os.setstate(std::ios::failbit);
  4835 + return 8;
  4836 + }
  4837 + }
  4838 + else
  4839 + wd = fds.wd;
  4840 + return static_cast<unsigned>((wd - Sunday).count());
  4841 +}
  4842 +
  4843 +template <class CharT, class Traits, class Duration>
  4844 +unsigned
  4845 +extract_month(std::basic_ostream<CharT, Traits>& os, const fields<Duration>& fds)
  4846 +{
  4847 + if (!fds.ymd.month().ok())
  4848 + {
  4849 + // fds does not contain a valid month
  4850 + os.setstate(std::ios::failbit);
  4851 + return 0;
  4852 + }
  4853 + return static_cast<unsigned>(fds.ymd.month());
  4854 +}
  4855 +
  4856 +} // namespace detail
  4857 +
  4858 +#if ONLY_C_LOCALE
  4859 +
  4860 +namespace detail
  4861 +{
  4862 +
  4863 +inline
  4864 +std::pair<const std::string*, const std::string*>
  4865 +weekday_names()
  4866 +{
  4867 + static const std::string nm[] =
  4868 + {
  4869 + "Sunday",
  4870 + "Monday",
  4871 + "Tuesday",
  4872 + "Wednesday",
  4873 + "Thursday",
  4874 + "Friday",
  4875 + "Saturday",
  4876 + "Sun",
  4877 + "Mon",
  4878 + "Tue",
  4879 + "Wed",
  4880 + "Thu",
  4881 + "Fri",
  4882 + "Sat"
  4883 + };
  4884 + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0]));
  4885 +}
  4886 +
  4887 +inline
  4888 +std::pair<const std::string*, const std::string*>
  4889 +month_names()
  4890 +{
  4891 + static const std::string nm[] =
  4892 + {
  4893 + "January",
  4894 + "February",
  4895 + "March",
  4896 + "April",
  4897 + "May",
  4898 + "June",
  4899 + "July",
  4900 + "August",
  4901 + "September",
  4902 + "October",
  4903 + "November",
  4904 + "December",
  4905 + "Jan",
  4906 + "Feb",
  4907 + "Mar",
  4908 + "Apr",
  4909 + "May",
  4910 + "Jun",
  4911 + "Jul",
  4912 + "Aug",
  4913 + "Sep",
  4914 + "Oct",
  4915 + "Nov",
  4916 + "Dec"
  4917 + };
  4918 + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0]));
  4919 +}
  4920 +
  4921 +inline
  4922 +std::pair<const std::string*, const std::string*>
  4923 +ampm_names()
  4924 +{
  4925 + static const std::string nm[] =
  4926 + {
  4927 + "AM",
  4928 + "PM"
  4929 + };
  4930 + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0]));
  4931 +}
  4932 +
  4933 +template <class CharT, class Traits, class FwdIter>
  4934 +FwdIter
  4935 +scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
  4936 +{
  4937 + size_t nkw = static_cast<size_t>(std::distance(kb, ke));
  4938 + const unsigned char doesnt_match = '\0';
  4939 + const unsigned char might_match = '\1';
  4940 + const unsigned char does_match = '\2';
  4941 + unsigned char statbuf[100];
  4942 + unsigned char* status = statbuf;
  4943 + std::unique_ptr<unsigned char, void(*)(void*)> stat_hold(0, free);
  4944 + if (nkw > sizeof(statbuf))
  4945 + {
  4946 + status = (unsigned char*)std::malloc(nkw);
  4947 + if (status == nullptr)
  4948 + throw std::bad_alloc();
  4949 + stat_hold.reset(status);
  4950 + }
  4951 + size_t n_might_match = nkw; // At this point, any keyword might match
  4952 + size_t n_does_match = 0; // but none of them definitely do
  4953 + // Initialize all statuses to might_match, except for "" keywords are does_match
  4954 + unsigned char* st = status;
  4955 + for (auto ky = kb; ky != ke; ++ky, ++st)
  4956 + {
  4957 + if (!ky->empty())
  4958 + *st = might_match;
  4959 + else
  4960 + {
  4961 + *st = does_match;
  4962 + --n_might_match;
  4963 + ++n_does_match;
  4964 + }
  4965 + }
  4966 + // While there might be a match, test keywords against the next CharT
  4967 + for (size_t indx = 0; is && n_might_match > 0; ++indx)
  4968 + {
  4969 + // Peek at the next CharT but don't consume it
  4970 + auto ic = is.peek();
  4971 + if (ic == EOF)
  4972 + {
  4973 + is.setstate(std::ios::eofbit);
  4974 + break;
  4975 + }
  4976 + auto c = static_cast<char>(toupper(static_cast<unsigned char>(ic)));
  4977 + bool consume = false;
  4978 + // For each keyword which might match, see if the indx character is c
  4979 + // If a match if found, consume c
  4980 + // If a match is found, and that is the last character in the keyword,
  4981 + // then that keyword matches.
  4982 + // If the keyword doesn't match this character, then change the keyword
  4983 + // to doesn't match
  4984 + st = status;
  4985 + for (auto ky = kb; ky != ke; ++ky, ++st)
  4986 + {
  4987 + if (*st == might_match)
  4988 + {
  4989 + if (c == static_cast<char>(toupper(static_cast<unsigned char>((*ky)[indx]))))
  4990 + {
  4991 + consume = true;
  4992 + if (ky->size() == indx+1)
  4993 + {
  4994 + *st = does_match;
  4995 + --n_might_match;
  4996 + ++n_does_match;
  4997 + }
  4998 + }
  4999 + else
  5000 + {
  5001 + *st = doesnt_match;
  5002 + --n_might_match;
  5003 + }
  5004 + }
  5005 + }
  5006 + // consume if we matched a character
  5007 + if (consume)
  5008 + {
  5009 + (void)is.get();
  5010 + // If we consumed a character and there might be a matched keyword that
  5011 + // was marked matched on a previous iteration, then such keywords
  5012 + // are now marked as not matching.
  5013 + if (n_might_match + n_does_match > 1)
  5014 + {
  5015 + st = status;
  5016 + for (auto ky = kb; ky != ke; ++ky, ++st)
  5017 + {
  5018 + if (*st == does_match && ky->size() != indx+1)
  5019 + {
  5020 + *st = doesnt_match;
  5021 + --n_does_match;
  5022 + }
  5023 + }
  5024 + }
  5025 + }
  5026 + }
  5027 + // We've exited the loop because we hit eof and/or we have no more "might matches".
  5028 + // Return the first matching result
  5029 + for (st = status; kb != ke; ++kb, ++st)
  5030 + if (*st == does_match)
  5031 + break;
  5032 + if (kb == ke)
  5033 + is.setstate(std::ios::failbit);
  5034 + return kb;
  5035 +}
  5036 +
  5037 +} // namespace detail
  5038 +
  5039 +#endif // ONLY_C_LOCALE
  5040 +
  5041 +template <class CharT, class Traits, class Duration>
  5042 +std::basic_ostream<CharT, Traits>&
  5043 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
  5044 + const fields<Duration>& fds, const std::string* abbrev,
  5045 + const std::chrono::seconds* offset_sec)
  5046 +{
  5047 +#if ONLY_C_LOCALE
  5048 + using detail::weekday_names;
  5049 + using detail::month_names;
  5050 + using detail::ampm_names;
  5051 +#endif
  5052 + using detail::save_ostream;
  5053 + using detail::get_units;
  5054 + using detail::extract_weekday;
  5055 + using detail::extract_month;
  5056 + using std::ios;
  5057 + using std::chrono::duration_cast;
  5058 + using std::chrono::seconds;
  5059 + using std::chrono::minutes;
  5060 + using std::chrono::hours;
  5061 + date::detail::save_ostream<CharT, Traits> ss(os);
  5062 + os.fill(' ');
  5063 + os.flags(std::ios::skipws | std::ios::dec);
  5064 + os.width(0);
  5065 + tm tm{};
  5066 + bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero();
  5067 +#if !ONLY_C_LOCALE
  5068 + auto& facet = std::use_facet<std::time_put<CharT>>(os.getloc());
  5069 +#endif
  5070 + const CharT* command = nullptr;
  5071 + CharT modified = CharT{};
  5072 + for (; *fmt; ++fmt)
  5073 + {
  5074 + switch (*fmt)
  5075 + {
  5076 + case 'a':
  5077 + case 'A':
  5078 + if (command)
  5079 + {
  5080 + if (modified == CharT{})
  5081 + {
  5082 + tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
  5083 + if (os.fail())
  5084 + return os;
  5085 +#if !ONLY_C_LOCALE
  5086 + const CharT f[] = {'%', *fmt};
  5087 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5088 +#else // ONLY_C_LOCALE
  5089 + os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')];
  5090 +#endif // ONLY_C_LOCALE
  5091 + }
  5092 + else
  5093 + {
  5094 + os << CharT{'%'} << modified << *fmt;
  5095 + modified = CharT{};
  5096 + }
  5097 + command = nullptr;
  5098 + }
  5099 + else
  5100 + os << *fmt;
  5101 + break;
  5102 + case 'b':
  5103 + case 'B':
  5104 + case 'h':
  5105 + if (command)
  5106 + {
  5107 + if (modified == CharT{})
  5108 + {
  5109 + tm.tm_mon = static_cast<int>(extract_month(os, fds)) - 1;
  5110 +#if !ONLY_C_LOCALE
  5111 + const CharT f[] = {'%', *fmt};
  5112 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5113 +#else // ONLY_C_LOCALE
  5114 + os << month_names().first[tm.tm_mon+12*(*fmt != 'B')];
  5115 +#endif // ONLY_C_LOCALE
  5116 + }
  5117 + else
  5118 + {
  5119 + os << CharT{'%'} << modified << *fmt;
  5120 + modified = CharT{};
  5121 + }
  5122 + command = nullptr;
  5123 + }
  5124 + else
  5125 + os << *fmt;
  5126 + break;
  5127 + case 'c':
  5128 + case 'x':
  5129 + if (command)
  5130 + {
  5131 + if (modified == CharT{'O'})
  5132 + os << CharT{'%'} << modified << *fmt;
  5133 + else
  5134 + {
  5135 + if (!fds.ymd.ok())
  5136 + os.setstate(std::ios::failbit);
  5137 + if (*fmt == 'c' && !fds.has_tod)
  5138 + os.setstate(std::ios::failbit);
  5139 +#if !ONLY_C_LOCALE
  5140 + tm = std::tm{};
  5141 + auto const& ymd = fds.ymd;
  5142 + auto ld = local_days(ymd);
  5143 + if (*fmt == 'c')
  5144 + {
  5145 + tm.tm_sec = static_cast<int>(fds.tod.seconds().count());
  5146 + tm.tm_min = static_cast<int>(fds.tod.minutes().count());
  5147 + tm.tm_hour = static_cast<int>(fds.tod.hours().count());
  5148 + }
  5149 + tm.tm_mday = static_cast<int>(static_cast<unsigned>(ymd.day()));
  5150 + tm.tm_mon = static_cast<int>(extract_month(os, fds) - 1);
  5151 + tm.tm_year = static_cast<int>(ymd.year()) - 1900;
  5152 + tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
  5153 + if (os.fail())
  5154 + return os;
  5155 + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
  5156 + CharT f[3] = {'%'};
  5157 + auto fe = std::begin(f) + 1;
  5158 + if (modified == CharT{'E'})
  5159 + *fe++ = modified;
  5160 + *fe++ = *fmt;
  5161 + facet.put(os, os, os.fill(), &tm, std::begin(f), fe);
  5162 +#else // ONLY_C_LOCALE
  5163 + if (*fmt == 'c')
  5164 + {
  5165 + auto wd = static_cast<int>(extract_weekday(os, fds));
  5166 + os << weekday_names().first[static_cast<unsigned>(wd)+7]
  5167 + << ' ';
  5168 + os << month_names().first[extract_month(os, fds)-1+12] << ' ';
  5169 + auto d = static_cast<int>(static_cast<unsigned>(fds.ymd.day()));
  5170 + if (d < 10)
  5171 + os << ' ';
  5172 + os << d << ' '
  5173 + << make_time(duration_cast<seconds>(fds.tod.to_duration()))
  5174 + << ' ' << fds.ymd.year();
  5175 +
  5176 + }
  5177 + else // *fmt == 'x'
  5178 + {
  5179 + auto const& ymd = fds.ymd;
  5180 + save_ostream<CharT, Traits> _(os);
  5181 + os.fill('0');
  5182 + os.flags(std::ios::dec | std::ios::right);
  5183 + os.width(2);
  5184 + os << static_cast<unsigned>(ymd.month()) << CharT{'/'};
  5185 + os.width(2);
  5186 + os << static_cast<unsigned>(ymd.day()) << CharT{'/'};
  5187 + os.width(2);
  5188 + os << static_cast<int>(ymd.year()) % 100;
  5189 + }
  5190 +#endif // ONLY_C_LOCALE
  5191 + }
  5192 + command = nullptr;
  5193 + modified = CharT{};
  5194 + }
  5195 + else
  5196 + os << *fmt;
  5197 + break;
  5198 + case 'C':
  5199 + if (command)
  5200 + {
  5201 + if (modified == CharT{'O'})
  5202 + os << CharT{'%'} << modified << *fmt;
  5203 + else
  5204 + {
  5205 + if (!fds.ymd.year().ok())
  5206 + os.setstate(std::ios::failbit);
  5207 + auto y = static_cast<int>(fds.ymd.year());
  5208 +#if !ONLY_C_LOCALE
  5209 + if (modified == CharT{})
  5210 +#endif
  5211 + {
  5212 + save_ostream<CharT, Traits> _(os);
  5213 + os.fill('0');
  5214 + os.flags(std::ios::dec | std::ios::right);
  5215 + if (y >= 0)
  5216 + {
  5217 + os.width(2);
  5218 + os << y/100;
  5219 + }
  5220 + else
  5221 + {
  5222 + os << CharT{'-'};
  5223 + os.width(2);
  5224 + os << -(y-99)/100;
  5225 + }
  5226 + }
  5227 +#if !ONLY_C_LOCALE
  5228 + else if (modified == CharT{'E'})
  5229 + {
  5230 + tm.tm_year = y - 1900;
  5231 + CharT f[3] = {'%', 'E', 'C'};
  5232 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5233 + }
  5234 +#endif
  5235 + }
  5236 + command = nullptr;
  5237 + modified = CharT{};
  5238 + }
  5239 + else
  5240 + os << *fmt;
  5241 + break;
  5242 + case 'd':
  5243 + case 'e':
  5244 + if (command)
  5245 + {
  5246 + if (modified == CharT{'E'})
  5247 + os << CharT{'%'} << modified << *fmt;
  5248 + else
  5249 + {
  5250 + if (!fds.ymd.day().ok())
  5251 + os.setstate(std::ios::failbit);
  5252 + auto d = static_cast<int>(static_cast<unsigned>(fds.ymd.day()));
  5253 +#if !ONLY_C_LOCALE
  5254 + if (modified == CharT{})
  5255 +#endif
  5256 + {
  5257 + save_ostream<CharT, Traits> _(os);
  5258 + if (*fmt == CharT{'d'})
  5259 + os.fill('0');
  5260 + else
  5261 + os.fill(' ');
  5262 + os.flags(std::ios::dec | std::ios::right);
  5263 + os.width(2);
  5264 + os << d;
  5265 + }
  5266 +#if !ONLY_C_LOCALE
  5267 + else if (modified == CharT{'O'})
  5268 + {
  5269 + tm.tm_mday = d;
  5270 + CharT f[3] = {'%', 'O', *fmt};
  5271 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5272 + }
  5273 +#endif
  5274 + }
  5275 + command = nullptr;
  5276 + modified = CharT{};
  5277 + }
  5278 + else
  5279 + os << *fmt;
  5280 + break;
  5281 + case 'D':
  5282 + if (command)
  5283 + {
  5284 + if (modified == CharT{})
  5285 + {
  5286 + if (!fds.ymd.ok())
  5287 + os.setstate(std::ios::failbit);
  5288 + auto const& ymd = fds.ymd;
  5289 + save_ostream<CharT, Traits> _(os);
  5290 + os.fill('0');
  5291 + os.flags(std::ios::dec | std::ios::right);
  5292 + os.width(2);
  5293 + os << static_cast<unsigned>(ymd.month()) << CharT{'/'};
  5294 + os.width(2);
  5295 + os << static_cast<unsigned>(ymd.day()) << CharT{'/'};
  5296 + os.width(2);
  5297 + os << static_cast<int>(ymd.year()) % 100;
  5298 + }
  5299 + else
  5300 + {
  5301 + os << CharT{'%'} << modified << *fmt;
  5302 + modified = CharT{};
  5303 + }
  5304 + command = nullptr;
  5305 + }
  5306 + else
  5307 + os << *fmt;
  5308 + break;
  5309 + case 'F':
  5310 + if (command)
  5311 + {
  5312 + if (modified == CharT{})
  5313 + {
  5314 + if (!fds.ymd.ok())
  5315 + os.setstate(std::ios::failbit);
  5316 + auto const& ymd = fds.ymd;
  5317 + save_ostream<CharT, Traits> _(os);
  5318 + os.imbue(std::locale::classic());
  5319 + os.fill('0');
  5320 + os.flags(std::ios::dec | std::ios::right);
  5321 + os.width(4);
  5322 + os << static_cast<int>(ymd.year()) << CharT{'-'};
  5323 + os.width(2);
  5324 + os << static_cast<unsigned>(ymd.month()) << CharT{'-'};
  5325 + os.width(2);
  5326 + os << static_cast<unsigned>(ymd.day());
  5327 + }
  5328 + else
  5329 + {
  5330 + os << CharT{'%'} << modified << *fmt;
  5331 + modified = CharT{};
  5332 + }
  5333 + command = nullptr;
  5334 + }
  5335 + else
  5336 + os << *fmt;
  5337 + break;
  5338 + case 'g':
  5339 + case 'G':
  5340 + if (command)
  5341 + {
  5342 + if (modified == CharT{})
  5343 + {
  5344 + if (!fds.ymd.ok())
  5345 + os.setstate(std::ios::failbit);
  5346 + auto ld = local_days(fds.ymd);
  5347 + auto y = year_month_day{ld + days{3}}.year();
  5348 + auto start = local_days((y-years{1})/December/Thursday[last]) +
  5349 + (Monday-Thursday);
  5350 + if (ld < start)
  5351 + --y;
  5352 + if (*fmt == CharT{'G'})
  5353 + os << y;
  5354 + else
  5355 + {
  5356 + save_ostream<CharT, Traits> _(os);
  5357 + os.fill('0');
  5358 + os.flags(std::ios::dec | std::ios::right);
  5359 + os.width(2);
  5360 + os << std::abs(static_cast<int>(y)) % 100;
  5361 + }
  5362 + }
  5363 + else
  5364 + {
  5365 + os << CharT{'%'} << modified << *fmt;
  5366 + modified = CharT{};
  5367 + }
  5368 + command = nullptr;
  5369 + }
  5370 + else
  5371 + os << *fmt;
  5372 + break;
  5373 + case 'H':
  5374 + case 'I':
  5375 + if (command)
  5376 + {
  5377 + if (modified == CharT{'E'})
  5378 + os << CharT{'%'} << modified << *fmt;
  5379 + else
  5380 + {
  5381 + if (!fds.has_tod)
  5382 + os.setstate(std::ios::failbit);
  5383 + if (insert_negative)
  5384 + {
  5385 + os << '-';
  5386 + insert_negative = false;
  5387 + }
  5388 + auto hms = fds.tod;
  5389 +#if !ONLY_C_LOCALE
  5390 + if (modified == CharT{})
  5391 +#endif
  5392 + {
  5393 + auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours();
  5394 + if (h < hours{10})
  5395 + os << CharT{'0'};
  5396 + os << h.count();
  5397 + }
  5398 +#if !ONLY_C_LOCALE
  5399 + else if (modified == CharT{'O'})
  5400 + {
  5401 + const CharT f[] = {'%', modified, *fmt};
  5402 + tm.tm_hour = static_cast<int>(hms.hours().count());
  5403 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5404 + }
  5405 +#endif
  5406 + }
  5407 + modified = CharT{};
  5408 + command = nullptr;
  5409 + }
  5410 + else
  5411 + os << *fmt;
  5412 + break;
  5413 + case 'j':
  5414 + if (command)
  5415 + {
  5416 + if (modified == CharT{})
  5417 + {
  5418 + if (fds.ymd.ok() || fds.has_tod)
  5419 + {
  5420 + days doy;
  5421 + if (fds.ymd.ok())
  5422 + {
  5423 + auto ld = local_days(fds.ymd);
  5424 + auto y = fds.ymd.year();
  5425 + doy = ld - local_days(y/January/1) + days{1};
  5426 + }
  5427 + else
  5428 + {
  5429 + doy = duration_cast<days>(fds.tod.to_duration());
  5430 + }
  5431 + save_ostream<CharT, Traits> _(os);
  5432 + os.fill('0');
  5433 + os.flags(std::ios::dec | std::ios::right);
  5434 + os.width(3);
  5435 + os << doy.count();
  5436 + }
  5437 + else
  5438 + {
  5439 + os.setstate(std::ios::failbit);
  5440 + }
  5441 + }
  5442 + else
  5443 + {
  5444 + os << CharT{'%'} << modified << *fmt;
  5445 + modified = CharT{};
  5446 + }
  5447 + command = nullptr;
  5448 + }
  5449 + else
  5450 + os << *fmt;
  5451 + break;
  5452 + case 'm':
  5453 + if (command)
  5454 + {
  5455 + if (modified == CharT{'E'})
  5456 + os << CharT{'%'} << modified << *fmt;
  5457 + else
  5458 + {
  5459 + if (!fds.ymd.month().ok())
  5460 + os.setstate(std::ios::failbit);
  5461 + auto m = static_cast<unsigned>(fds.ymd.month());
  5462 +#if !ONLY_C_LOCALE
  5463 + if (modified == CharT{})
  5464 +#endif
  5465 + {
  5466 + if (m < 10)
  5467 + os << CharT{'0'};
  5468 + os << m;
  5469 + }
  5470 +#if !ONLY_C_LOCALE
  5471 + else if (modified == CharT{'O'})
  5472 + {
  5473 + const CharT f[] = {'%', modified, *fmt};
  5474 + tm.tm_mon = static_cast<int>(m-1);
  5475 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5476 + }
  5477 +#endif
  5478 + }
  5479 + modified = CharT{};
  5480 + command = nullptr;
  5481 + }
  5482 + else
  5483 + os << *fmt;
  5484 + break;
  5485 + case 'M':
  5486 + if (command)
  5487 + {
  5488 + if (modified == CharT{'E'})
  5489 + os << CharT{'%'} << modified << *fmt;
  5490 + else
  5491 + {
  5492 + if (!fds.has_tod)
  5493 + os.setstate(std::ios::failbit);
  5494 + if (insert_negative)
  5495 + {
  5496 + os << '-';
  5497 + insert_negative = false;
  5498 + }
  5499 +#if !ONLY_C_LOCALE
  5500 + if (modified == CharT{})
  5501 +#endif
  5502 + {
  5503 + if (fds.tod.minutes() < minutes{10})
  5504 + os << CharT{'0'};
  5505 + os << fds.tod.minutes().count();
  5506 + }
  5507 +#if !ONLY_C_LOCALE
  5508 + else if (modified == CharT{'O'})
  5509 + {
  5510 + const CharT f[] = {'%', modified, *fmt};
  5511 + tm.tm_min = static_cast<int>(fds.tod.minutes().count());
  5512 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5513 + }
  5514 +#endif
  5515 + }
  5516 + modified = CharT{};
  5517 + command = nullptr;
  5518 + }
  5519 + else
  5520 + os << *fmt;
  5521 + break;
  5522 + case 'n':
  5523 + if (command)
  5524 + {
  5525 + if (modified == CharT{})
  5526 + os << CharT{'\n'};
  5527 + else
  5528 + {
  5529 + os << CharT{'%'} << modified << *fmt;
  5530 + modified = CharT{};
  5531 + }
  5532 + command = nullptr;
  5533 + }
  5534 + else
  5535 + os << *fmt;
  5536 + break;
  5537 + case 'p':
  5538 + if (command)
  5539 + {
  5540 + if (modified == CharT{})
  5541 + {
  5542 + if (!fds.has_tod)
  5543 + os.setstate(std::ios::failbit);
  5544 +#if !ONLY_C_LOCALE
  5545 + const CharT f[] = {'%', *fmt};
  5546 + tm.tm_hour = static_cast<int>(fds.tod.hours().count());
  5547 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5548 +#else
  5549 + if (date::is_am(fds.tod.hours()))
  5550 + os << ampm_names().first[0];
  5551 + else
  5552 + os << ampm_names().first[1];
  5553 +#endif
  5554 + }
  5555 + else
  5556 + {
  5557 + os << CharT{'%'} << modified << *fmt;
  5558 + }
  5559 + modified = CharT{};
  5560 + command = nullptr;
  5561 + }
  5562 + else
  5563 + os << *fmt;
  5564 + break;
  5565 + case 'Q':
  5566 + case 'q':
  5567 + if (command)
  5568 + {
  5569 + if (modified == CharT{})
  5570 + {
  5571 + if (!fds.has_tod)
  5572 + os.setstate(std::ios::failbit);
  5573 + auto d = fds.tod.to_duration();
  5574 + if (*fmt == 'q')
  5575 + os << get_units<CharT>(typename decltype(d)::period::type{});
  5576 + else
  5577 + os << d.count();
  5578 + }
  5579 + else
  5580 + {
  5581 + os << CharT{'%'} << modified << *fmt;
  5582 + }
  5583 + modified = CharT{};
  5584 + command = nullptr;
  5585 + }
  5586 + else
  5587 + os << *fmt;
  5588 + break;
  5589 + case 'r':
  5590 + if (command)
  5591 + {
  5592 + if (modified == CharT{})
  5593 + {
  5594 + if (!fds.has_tod)
  5595 + os.setstate(std::ios::failbit);
  5596 +#if !ONLY_C_LOCALE
  5597 + const CharT f[] = {'%', *fmt};
  5598 + tm.tm_hour = static_cast<int>(fds.tod.hours().count());
  5599 + tm.tm_min = static_cast<int>(fds.tod.minutes().count());
  5600 + tm.tm_sec = static_cast<int>(fds.tod.seconds().count());
  5601 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5602 +#else
  5603 + hh_mm_ss<seconds> tod(duration_cast<seconds>(fds.tod.to_duration()));
  5604 + save_ostream<CharT, Traits> _(os);
  5605 + os.fill('0');
  5606 + os.width(2);
  5607 + os << date::make12(tod.hours()).count() << CharT{':'};
  5608 + os.width(2);
  5609 + os << tod.minutes().count() << CharT{':'};
  5610 + os.width(2);
  5611 + os << tod.seconds().count() << CharT{' '};
  5612 + if (date::is_am(tod.hours()))
  5613 + os << ampm_names().first[0];
  5614 + else
  5615 + os << ampm_names().first[1];
  5616 +#endif
  5617 + }
  5618 + else
  5619 + {
  5620 + os << CharT{'%'} << modified << *fmt;
  5621 + }
  5622 + modified = CharT{};
  5623 + command = nullptr;
  5624 + }
  5625 + else
  5626 + os << *fmt;
  5627 + break;
  5628 + case 'R':
  5629 + if (command)
  5630 + {
  5631 + if (modified == CharT{})
  5632 + {
  5633 + if (!fds.has_tod)
  5634 + os.setstate(std::ios::failbit);
  5635 + if (fds.tod.hours() < hours{10})
  5636 + os << CharT{'0'};
  5637 + os << fds.tod.hours().count() << CharT{':'};
  5638 + if (fds.tod.minutes() < minutes{10})
  5639 + os << CharT{'0'};
  5640 + os << fds.tod.minutes().count();
  5641 + }
  5642 + else
  5643 + {
  5644 + os << CharT{'%'} << modified << *fmt;
  5645 + modified = CharT{};
  5646 + }
  5647 + command = nullptr;
  5648 + }
  5649 + else
  5650 + os << *fmt;
  5651 + break;
  5652 + case 'S':
  5653 + if (command)
  5654 + {
  5655 + if (modified == CharT{'E'})
  5656 + os << CharT{'%'} << modified << *fmt;
  5657 + else
  5658 + {
  5659 + if (!fds.has_tod)
  5660 + os.setstate(std::ios::failbit);
  5661 + if (insert_negative)
  5662 + {
  5663 + os << '-';
  5664 + insert_negative = false;
  5665 + }
  5666 +#if !ONLY_C_LOCALE
  5667 + if (modified == CharT{})
  5668 +#endif
  5669 + {
  5670 + os << fds.tod.s_;
  5671 + }
  5672 +#if !ONLY_C_LOCALE
  5673 + else if (modified == CharT{'O'})
  5674 + {
  5675 + const CharT f[] = {'%', modified, *fmt};
  5676 + tm.tm_sec = static_cast<int>(fds.tod.s_.seconds().count());
  5677 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5678 + }
  5679 +#endif
  5680 + }
  5681 + modified = CharT{};
  5682 + command = nullptr;
  5683 + }
  5684 + else
  5685 + os << *fmt;
  5686 + break;
  5687 + case 't':
  5688 + if (command)
  5689 + {
  5690 + if (modified == CharT{})
  5691 + os << CharT{'\t'};
  5692 + else
  5693 + {
  5694 + os << CharT{'%'} << modified << *fmt;
  5695 + modified = CharT{};
  5696 + }
  5697 + command = nullptr;
  5698 + }
  5699 + else
  5700 + os << *fmt;
  5701 + break;
  5702 + case 'T':
  5703 + if (command)
  5704 + {
  5705 + if (modified == CharT{})
  5706 + {
  5707 + if (!fds.has_tod)
  5708 + os.setstate(std::ios::failbit);
  5709 + os << fds.tod;
  5710 + }
  5711 + else
  5712 + {
  5713 + os << CharT{'%'} << modified << *fmt;
  5714 + modified = CharT{};
  5715 + }
  5716 + command = nullptr;
  5717 + }
  5718 + else
  5719 + os << *fmt;
  5720 + break;
  5721 + case 'u':
  5722 + if (command)
  5723 + {
  5724 + if (modified == CharT{'E'})
  5725 + os << CharT{'%'} << modified << *fmt;
  5726 + else
  5727 + {
  5728 + auto wd = extract_weekday(os, fds);
  5729 +#if !ONLY_C_LOCALE
  5730 + if (modified == CharT{})
  5731 +#endif
  5732 + {
  5733 + os << (wd != 0 ? wd : 7u);
  5734 + }
  5735 +#if !ONLY_C_LOCALE
  5736 + else if (modified == CharT{'O'})
  5737 + {
  5738 + const CharT f[] = {'%', modified, *fmt};
  5739 + tm.tm_wday = static_cast<int>(wd);
  5740 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5741 + }
  5742 +#endif
  5743 + }
  5744 + modified = CharT{};
  5745 + command = nullptr;
  5746 + }
  5747 + else
  5748 + os << *fmt;
  5749 + break;
  5750 + case 'U':
  5751 + if (command)
  5752 + {
  5753 + if (modified == CharT{'E'})
  5754 + os << CharT{'%'} << modified << *fmt;
  5755 + else
  5756 + {
  5757 + auto const& ymd = fds.ymd;
  5758 + if (!ymd.ok())
  5759 + os.setstate(std::ios::failbit);
  5760 + auto ld = local_days(ymd);
  5761 +#if !ONLY_C_LOCALE
  5762 + if (modified == CharT{})
  5763 +#endif
  5764 + {
  5765 + auto st = local_days(Sunday[1]/January/ymd.year());
  5766 + if (ld < st)
  5767 + os << CharT{'0'} << CharT{'0'};
  5768 + else
  5769 + {
  5770 + auto wn = duration_cast<weeks>(ld - st).count() + 1;
  5771 + if (wn < 10)
  5772 + os << CharT{'0'};
  5773 + os << wn;
  5774 + }
  5775 + }
  5776 + #if !ONLY_C_LOCALE
  5777 + else if (modified == CharT{'O'})
  5778 + {
  5779 + const CharT f[] = {'%', modified, *fmt};
  5780 + tm.tm_year = static_cast<int>(ymd.year()) - 1900;
  5781 + tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
  5782 + if (os.fail())
  5783 + return os;
  5784 + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
  5785 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5786 + }
  5787 +#endif
  5788 + }
  5789 + modified = CharT{};
  5790 + command = nullptr;
  5791 + }
  5792 + else
  5793 + os << *fmt;
  5794 + break;
  5795 + case 'V':
  5796 + if (command)
  5797 + {
  5798 + if (modified == CharT{'E'})
  5799 + os << CharT{'%'} << modified << *fmt;
  5800 + else
  5801 + {
  5802 + if (!fds.ymd.ok())
  5803 + os.setstate(std::ios::failbit);
  5804 + auto ld = local_days(fds.ymd);
  5805 +#if !ONLY_C_LOCALE
  5806 + if (modified == CharT{})
  5807 +#endif
  5808 + {
  5809 + auto y = year_month_day{ld + days{3}}.year();
  5810 + auto st = local_days((y-years{1})/12/Thursday[last]) +
  5811 + (Monday-Thursday);
  5812 + if (ld < st)
  5813 + {
  5814 + --y;
  5815 + st = local_days((y - years{1})/12/Thursday[last]) +
  5816 + (Monday-Thursday);
  5817 + }
  5818 + auto wn = duration_cast<weeks>(ld - st).count() + 1;
  5819 + if (wn < 10)
  5820 + os << CharT{'0'};
  5821 + os << wn;
  5822 + }
  5823 +#if !ONLY_C_LOCALE
  5824 + else if (modified == CharT{'O'})
  5825 + {
  5826 + const CharT f[] = {'%', modified, *fmt};
  5827 + auto const& ymd = fds.ymd;
  5828 + tm.tm_year = static_cast<int>(ymd.year()) - 1900;
  5829 + tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
  5830 + if (os.fail())
  5831 + return os;
  5832 + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
  5833 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5834 + }
  5835 +#endif
  5836 + }
  5837 + modified = CharT{};
  5838 + command = nullptr;
  5839 + }
  5840 + else
  5841 + os << *fmt;
  5842 + break;
  5843 + case 'w':
  5844 + if (command)
  5845 + {
  5846 + auto wd = extract_weekday(os, fds);
  5847 + if (os.fail())
  5848 + return os;
  5849 +#if !ONLY_C_LOCALE
  5850 + if (modified == CharT{})
  5851 +#else
  5852 + if (modified != CharT{'E'})
  5853 +#endif
  5854 + {
  5855 + os << wd;
  5856 + }
  5857 +#if !ONLY_C_LOCALE
  5858 + else if (modified == CharT{'O'})
  5859 + {
  5860 + const CharT f[] = {'%', modified, *fmt};
  5861 + tm.tm_wday = static_cast<int>(wd);
  5862 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5863 + }
  5864 +#endif
  5865 + else
  5866 + {
  5867 + os << CharT{'%'} << modified << *fmt;
  5868 + }
  5869 + modified = CharT{};
  5870 + command = nullptr;
  5871 + }
  5872 + else
  5873 + os << *fmt;
  5874 + break;
  5875 + case 'W':
  5876 + if (command)
  5877 + {
  5878 + if (modified == CharT{'E'})
  5879 + os << CharT{'%'} << modified << *fmt;
  5880 + else
  5881 + {
  5882 + auto const& ymd = fds.ymd;
  5883 + if (!ymd.ok())
  5884 + os.setstate(std::ios::failbit);
  5885 + auto ld = local_days(ymd);
  5886 +#if !ONLY_C_LOCALE
  5887 + if (modified == CharT{})
  5888 +#endif
  5889 + {
  5890 + auto st = local_days(Monday[1]/January/ymd.year());
  5891 + if (ld < st)
  5892 + os << CharT{'0'} << CharT{'0'};
  5893 + else
  5894 + {
  5895 + auto wn = duration_cast<weeks>(ld - st).count() + 1;
  5896 + if (wn < 10)
  5897 + os << CharT{'0'};
  5898 + os << wn;
  5899 + }
  5900 + }
  5901 +#if !ONLY_C_LOCALE
  5902 + else if (modified == CharT{'O'})
  5903 + {
  5904 + const CharT f[] = {'%', modified, *fmt};
  5905 + tm.tm_year = static_cast<int>(ymd.year()) - 1900;
  5906 + tm.tm_wday = static_cast<int>(extract_weekday(os, fds));
  5907 + if (os.fail())
  5908 + return os;
  5909 + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count());
  5910 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5911 + }
  5912 +#endif
  5913 + }
  5914 + modified = CharT{};
  5915 + command = nullptr;
  5916 + }
  5917 + else
  5918 + os << *fmt;
  5919 + break;
  5920 + case 'X':
  5921 + if (command)
  5922 + {
  5923 + if (modified == CharT{'O'})
  5924 + os << CharT{'%'} << modified << *fmt;
  5925 + else
  5926 + {
  5927 + if (!fds.has_tod)
  5928 + os.setstate(std::ios::failbit);
  5929 +#if !ONLY_C_LOCALE
  5930 + tm = std::tm{};
  5931 + tm.tm_sec = static_cast<int>(fds.tod.seconds().count());
  5932 + tm.tm_min = static_cast<int>(fds.tod.minutes().count());
  5933 + tm.tm_hour = static_cast<int>(fds.tod.hours().count());
  5934 + CharT f[3] = {'%'};
  5935 + auto fe = std::begin(f) + 1;
  5936 + if (modified == CharT{'E'})
  5937 + *fe++ = modified;
  5938 + *fe++ = *fmt;
  5939 + facet.put(os, os, os.fill(), &tm, std::begin(f), fe);
  5940 +#else
  5941 + os << fds.tod;
  5942 +#endif
  5943 + }
  5944 + command = nullptr;
  5945 + modified = CharT{};
  5946 + }
  5947 + else
  5948 + os << *fmt;
  5949 + break;
  5950 + case 'y':
  5951 + if (command)
  5952 + {
  5953 + if (!fds.ymd.year().ok())
  5954 + os.setstate(std::ios::failbit);
  5955 + auto y = static_cast<int>(fds.ymd.year());
  5956 +#if !ONLY_C_LOCALE
  5957 + if (modified == CharT{})
  5958 + {
  5959 +#endif
  5960 + y = std::abs(y) % 100;
  5961 + if (y < 10)
  5962 + os << CharT{'0'};
  5963 + os << y;
  5964 +#if !ONLY_C_LOCALE
  5965 + }
  5966 + else
  5967 + {
  5968 + const CharT f[] = {'%', modified, *fmt};
  5969 + tm.tm_year = y - 1900;
  5970 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  5971 + }
  5972 +#endif
  5973 + modified = CharT{};
  5974 + command = nullptr;
  5975 + }
  5976 + else
  5977 + os << *fmt;
  5978 + break;
  5979 + case 'Y':
  5980 + if (command)
  5981 + {
  5982 + if (modified == CharT{'O'})
  5983 + os << CharT{'%'} << modified << *fmt;
  5984 + else
  5985 + {
  5986 + if (!fds.ymd.year().ok())
  5987 + os.setstate(std::ios::failbit);
  5988 + auto y = fds.ymd.year();
  5989 +#if !ONLY_C_LOCALE
  5990 + if (modified == CharT{})
  5991 +#endif
  5992 + {
  5993 + save_ostream<CharT, Traits> _(os);
  5994 + os.imbue(std::locale::classic());
  5995 + os << y;
  5996 + }
  5997 +#if !ONLY_C_LOCALE
  5998 + else if (modified == CharT{'E'})
  5999 + {
  6000 + const CharT f[] = {'%', modified, *fmt};
  6001 + tm.tm_year = static_cast<int>(y) - 1900;
  6002 + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f));
  6003 + }
  6004 +#endif
  6005 + }
  6006 + modified = CharT{};
  6007 + command = nullptr;
  6008 + }
  6009 + else
  6010 + os << *fmt;
  6011 + break;
  6012 + case 'z':
  6013 + if (command)
  6014 + {
  6015 + if (offset_sec == nullptr)
  6016 + {
  6017 + // Can not format %z with unknown offset
  6018 + os.setstate(ios::failbit);
  6019 + return os;
  6020 + }
  6021 + auto m = duration_cast<minutes>(*offset_sec);
  6022 + auto neg = m < minutes{0};
  6023 + m = date::abs(m);
  6024 + auto h = duration_cast<hours>(m);
  6025 + m -= h;
  6026 + if (neg)
  6027 + os << CharT{'-'};
  6028 + else
  6029 + os << CharT{'+'};
  6030 + if (h < hours{10})
  6031 + os << CharT{'0'};
  6032 + os << h.count();
  6033 + if (modified != CharT{})
  6034 + os << CharT{':'};
  6035 + if (m < minutes{10})
  6036 + os << CharT{'0'};
  6037 + os << m.count();
  6038 + command = nullptr;
  6039 + modified = CharT{};
  6040 + }
  6041 + else
  6042 + os << *fmt;
  6043 + break;
  6044 + case 'Z':
  6045 + if (command)
  6046 + {
  6047 + if (modified == CharT{})
  6048 + {
  6049 + if (abbrev == nullptr)
  6050 + {
  6051 + // Can not format %Z with unknown time_zone
  6052 + os.setstate(ios::failbit);
  6053 + return os;
  6054 + }
  6055 + for (auto c : *abbrev)
  6056 + os << CharT(c);
  6057 + }
  6058 + else
  6059 + {
  6060 + os << CharT{'%'} << modified << *fmt;
  6061 + modified = CharT{};
  6062 + }
  6063 + command = nullptr;
  6064 + }
  6065 + else
  6066 + os << *fmt;
  6067 + break;
  6068 + case 'E':
  6069 + case 'O':
  6070 + if (command)
  6071 + {
  6072 + if (modified == CharT{})
  6073 + {
  6074 + modified = *fmt;
  6075 + }
  6076 + else
  6077 + {
  6078 + os << CharT{'%'} << modified << *fmt;
  6079 + command = nullptr;
  6080 + modified = CharT{};
  6081 + }
  6082 + }
  6083 + else
  6084 + os << *fmt;
  6085 + break;
  6086 + case '%':
  6087 + if (command)
  6088 + {
  6089 + if (modified == CharT{})
  6090 + {
  6091 + os << CharT{'%'};
  6092 + command = nullptr;
  6093 + }
  6094 + else
  6095 + {
  6096 + os << CharT{'%'} << modified << CharT{'%'};
  6097 + command = nullptr;
  6098 + modified = CharT{};
  6099 + }
  6100 + }
  6101 + else
  6102 + command = fmt;
  6103 + break;
  6104 + default:
  6105 + if (command)
  6106 + {
  6107 + os << CharT{'%'};
  6108 + command = nullptr;
  6109 + }
  6110 + if (modified != CharT{})
  6111 + {
  6112 + os << modified;
  6113 + modified = CharT{};
  6114 + }
  6115 + os << *fmt;
  6116 + break;
  6117 + }
  6118 + }
  6119 + if (command)
  6120 + os << CharT{'%'};
  6121 + if (modified != CharT{})
  6122 + os << modified;
  6123 + return os;
  6124 +}
  6125 +
  6126 +template <class CharT, class Traits>
  6127 +inline
  6128 +std::basic_ostream<CharT, Traits>&
  6129 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const year& y)
  6130 +{
  6131 + using CT = std::chrono::seconds;
  6132 + fields<CT> fds{y/0/0};
  6133 + return to_stream(os, fmt, fds);
  6134 +}
  6135 +
  6136 +template <class CharT, class Traits>
  6137 +inline
  6138 +std::basic_ostream<CharT, Traits>&
  6139 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const month& m)
  6140 +{
  6141 + using CT = std::chrono::seconds;
  6142 + fields<CT> fds{m/0/nanyear};
  6143 + return to_stream(os, fmt, fds);
  6144 +}
  6145 +
  6146 +template <class CharT, class Traits>
  6147 +inline
  6148 +std::basic_ostream<CharT, Traits>&
  6149 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const day& d)
  6150 +{
  6151 + using CT = std::chrono::seconds;
  6152 + fields<CT> fds{d/0/nanyear};
  6153 + return to_stream(os, fmt, fds);
  6154 +}
  6155 +
  6156 +template <class CharT, class Traits>
  6157 +inline
  6158 +std::basic_ostream<CharT, Traits>&
  6159 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const weekday& wd)
  6160 +{
  6161 + using CT = std::chrono::seconds;
  6162 + fields<CT> fds{wd};
  6163 + return to_stream(os, fmt, fds);
  6164 +}
  6165 +
  6166 +template <class CharT, class Traits>
  6167 +inline
  6168 +std::basic_ostream<CharT, Traits>&
  6169 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const year_month& ym)
  6170 +{
  6171 + using CT = std::chrono::seconds;
  6172 + fields<CT> fds{ym/0};
  6173 + return to_stream(os, fmt, fds);
  6174 +}
  6175 +
  6176 +template <class CharT, class Traits>
  6177 +inline
  6178 +std::basic_ostream<CharT, Traits>&
  6179 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const month_day& md)
  6180 +{
  6181 + using CT = std::chrono::seconds;
  6182 + fields<CT> fds{md/nanyear};
  6183 + return to_stream(os, fmt, fds);
  6184 +}
  6185 +
  6186 +template <class CharT, class Traits>
  6187 +inline
  6188 +std::basic_ostream<CharT, Traits>&
  6189 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
  6190 + const year_month_day& ymd)
  6191 +{
  6192 + using CT = std::chrono::seconds;
  6193 + fields<CT> fds{ymd};
  6194 + return to_stream(os, fmt, fds);
  6195 +}
  6196 +
  6197 +template <class CharT, class Traits, class Rep, class Period>
  6198 +inline
  6199 +std::basic_ostream<CharT, Traits>&
  6200 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
  6201 + const std::chrono::duration<Rep, Period>& d)
  6202 +{
  6203 + using Duration = std::chrono::duration<Rep, Period>;
  6204 + using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
  6205 + fields<CT> fds{hh_mm_ss<CT>{d}};
  6206 + return to_stream(os, fmt, fds);
  6207 +}
  6208 +
  6209 +template <class CharT, class Traits, class Duration>
  6210 +std::basic_ostream<CharT, Traits>&
  6211 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
  6212 + const local_time<Duration>& tp, const std::string* abbrev = nullptr,
  6213 + const std::chrono::seconds* offset_sec = nullptr)
  6214 +{
  6215 + using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
  6216 + auto ld = std::chrono::time_point_cast<days>(tp);
  6217 + fields<CT> fds;
  6218 + if (ld <= tp)
  6219 + fds = fields<CT>{year_month_day{ld}, hh_mm_ss<CT>{tp-local_seconds{ld}}};
  6220 + else
  6221 + fds = fields<CT>{year_month_day{ld - days{1}},
  6222 + hh_mm_ss<CT>{days{1} - (local_seconds{ld} - tp)}};
  6223 + return to_stream(os, fmt, fds, abbrev, offset_sec);
  6224 +}
  6225 +
  6226 +template <class CharT, class Traits, class Duration>
  6227 +std::basic_ostream<CharT, Traits>&
  6228 +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
  6229 + const sys_time<Duration>& tp)
  6230 +{
  6231 + using std::chrono::seconds;
  6232 + using CT = typename std::common_type<Duration, seconds>::type;
  6233 + const std::string abbrev("UTC");
  6234 + CONSTDATA seconds offset{0};
  6235 + auto sd = std::chrono::time_point_cast<days>(tp);
  6236 + fields<CT> fds;
  6237 + if (sd <= tp)
  6238 + fds = fields<CT>{year_month_day{sd}, hh_mm_ss<CT>{tp-sys_seconds{sd}}};
  6239 + else
  6240 + fds = fields<CT>{year_month_day{sd - days{1}},
  6241 + hh_mm_ss<CT>{days{1} - (sys_seconds{sd} - tp)}};
  6242 + return to_stream(os, fmt, fds, &abbrev, &offset);
  6243 +}
  6244 +
  6245 +// format
  6246 +
  6247 +template <class CharT, class Streamable>
  6248 +auto
  6249 +format(const std::locale& loc, const CharT* fmt, const Streamable& tp)
  6250 + -> decltype(to_stream(std::declval<std::basic_ostream<CharT>&>(), fmt, tp),
  6251 + std::basic_string<CharT>{})
  6252 +{
  6253 + std::basic_ostringstream<CharT> os;
  6254 + os.exceptions(std::ios::failbit | std::ios::badbit);
  6255 + os.imbue(loc);
  6256 + to_stream(os, fmt, tp);
  6257 + return os.str();
  6258 +}
  6259 +
  6260 +template <class CharT, class Streamable>
  6261 +auto
  6262 +format(const CharT* fmt, const Streamable& tp)
  6263 + -> decltype(to_stream(std::declval<std::basic_ostream<CharT>&>(), fmt, tp),
  6264 + std::basic_string<CharT>{})
  6265 +{
  6266 + std::basic_ostringstream<CharT> os;
  6267 + os.exceptions(std::ios::failbit | std::ios::badbit);
  6268 + to_stream(os, fmt, tp);
  6269 + return os.str();
  6270 +}
  6271 +
  6272 +template <class CharT, class Traits, class Alloc, class Streamable>
  6273 +auto
  6274 +format(const std::locale& loc, const std::basic_string<CharT, Traits, Alloc>& fmt,
  6275 + const Streamable& tp)
  6276 + -> decltype(to_stream(std::declval<std::basic_ostream<CharT, Traits>&>(), fmt.c_str(), tp),
  6277 + std::basic_string<CharT, Traits, Alloc>{})
  6278 +{
  6279 + std::basic_ostringstream<CharT, Traits, Alloc> os;
  6280 + os.exceptions(std::ios::failbit | std::ios::badbit);
  6281 + os.imbue(loc);
  6282 + to_stream(os, fmt.c_str(), tp);
  6283 + return os.str();
  6284 +}
  6285 +
  6286 +template <class CharT, class Traits, class Alloc, class Streamable>
  6287 +auto
  6288 +format(const std::basic_string<CharT, Traits, Alloc>& fmt, const Streamable& tp)
  6289 + -> decltype(to_stream(std::declval<std::basic_ostream<CharT, Traits>&>(), fmt.c_str(), tp),
  6290 + std::basic_string<CharT, Traits, Alloc>{})
  6291 +{
  6292 + std::basic_ostringstream<CharT, Traits, Alloc> os;
  6293 + os.exceptions(std::ios::failbit | std::ios::badbit);
  6294 + to_stream(os, fmt.c_str(), tp);
  6295 + return os.str();
  6296 +}
  6297 +
  6298 +// parse
  6299 +
  6300 +namespace detail
  6301 +{
  6302 +
  6303 +template <class CharT, class Traits>
  6304 +bool
  6305 +read_char(std::basic_istream<CharT, Traits>& is, CharT fmt, std::ios::iostate& err)
  6306 +{
  6307 + auto ic = is.get();
  6308 + if (Traits::eq_int_type(ic, Traits::eof()) ||
  6309 + !Traits::eq(Traits::to_char_type(ic), fmt))
  6310 + {
  6311 + err |= std::ios::failbit;
  6312 + is.setstate(std::ios::failbit);
  6313 + return false;
  6314 + }
  6315 + return true;
  6316 +}
  6317 +
  6318 +template <class CharT, class Traits>
  6319 +unsigned
  6320 +read_unsigned(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10)
  6321 +{
  6322 + unsigned x = 0;
  6323 + unsigned count = 0;
  6324 + while (true)
  6325 + {
  6326 + auto ic = is.peek();
  6327 + if (Traits::eq_int_type(ic, Traits::eof()))
  6328 + break;
  6329 + auto c = static_cast<char>(Traits::to_char_type(ic));
  6330 + if (!('0' <= c && c <= '9'))
  6331 + break;
  6332 + (void)is.get();
  6333 + ++count;
  6334 + x = 10*x + static_cast<unsigned>(c - '0');
  6335 + if (count == M)
  6336 + break;
  6337 + }
  6338 + if (count < m)
  6339 + is.setstate(std::ios::failbit);
  6340 + return x;
  6341 +}
  6342 +
  6343 +template <class CharT, class Traits>
  6344 +int
  6345 +read_signed(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10)
  6346 +{
  6347 + auto ic = is.peek();
  6348 + if (!Traits::eq_int_type(ic, Traits::eof()))
  6349 + {
  6350 + auto c = static_cast<char>(Traits::to_char_type(ic));
  6351 + if (('0' <= c && c <= '9') || c == '-' || c == '+')
  6352 + {
  6353 + if (c == '-' || c == '+')
  6354 + (void)is.get();
  6355 + auto x = static_cast<int>(read_unsigned(is, std::max(m, 1u), M));
  6356 + if (!is.fail())
  6357 + {
  6358 + if (c == '-')
  6359 + x = -x;
  6360 + return x;
  6361 + }
  6362 + }
  6363 + }
  6364 + if (m > 0)
  6365 + is.setstate(std::ios::failbit);
  6366 + return 0;
  6367 +}
  6368 +
  6369 +template <class CharT, class Traits>
  6370 +long double
  6371 +read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10)
  6372 +{
  6373 + unsigned count = 0;
  6374 + unsigned fcount = 0;
  6375 + unsigned long long i = 0;
  6376 + unsigned long long f = 0;
  6377 + bool parsing_fraction = false;
  6378 +#if ONLY_C_LOCALE
  6379 + typename Traits::int_type decimal_point = '.';
  6380 +#else
  6381 + auto decimal_point = Traits::to_int_type(
  6382 + std::use_facet<std::numpunct<CharT>>(is.getloc()).decimal_point());
  6383 +#endif
  6384 + while (true)
  6385 + {
  6386 + auto ic = is.peek();
  6387 + if (Traits::eq_int_type(ic, Traits::eof()))
  6388 + break;
  6389 + if (Traits::eq_int_type(ic, decimal_point))
  6390 + {
  6391 + decimal_point = Traits::eof();
  6392 + parsing_fraction = true;
  6393 + }
  6394 + else
  6395 + {
  6396 + auto c = static_cast<char>(Traits::to_char_type(ic));
  6397 + if (!('0' <= c && c <= '9'))
  6398 + break;
  6399 + if (!parsing_fraction)
  6400 + {
  6401 + i = 10*i + static_cast<unsigned>(c - '0');
  6402 + }
  6403 + else
  6404 + {
  6405 + f = 10*f + static_cast<unsigned>(c - '0');
  6406 + ++fcount;
  6407 + }
  6408 + }
  6409 + (void)is.get();
  6410 + if (++count == M)
  6411 + break;
  6412 + }
  6413 + if (count < m)
  6414 + {
  6415 + is.setstate(std::ios::failbit);
  6416 + return 0;
  6417 + }
  6418 + return static_cast<long double>(i) + static_cast<long double>(f)/std::pow(10.L, fcount);
  6419 +}
  6420 +
  6421 +struct rs
  6422 +{
  6423 + int& i;
  6424 + unsigned m;
  6425 + unsigned M;
  6426 +};
  6427 +
  6428 +struct ru
  6429 +{
  6430 + int& i;
  6431 + unsigned m;
  6432 + unsigned M;
  6433 +};
  6434 +
  6435 +struct rld
  6436 +{
  6437 + long double& i;
  6438 + unsigned m;
  6439 + unsigned M;
  6440 +};
  6441 +
  6442 +template <class CharT, class Traits>
  6443 +void
  6444 +read(std::basic_istream<CharT, Traits>&)
  6445 +{
  6446 +}
  6447 +
  6448 +template <class CharT, class Traits, class ...Args>
  6449 +void
  6450 +read(std::basic_istream<CharT, Traits>& is, CharT a0, Args&& ...args);
  6451 +
  6452 +template <class CharT, class Traits, class ...Args>
  6453 +void
  6454 +read(std::basic_istream<CharT, Traits>& is, rs a0, Args&& ...args);
  6455 +
  6456 +template <class CharT, class Traits, class ...Args>
  6457 +void
  6458 +read(std::basic_istream<CharT, Traits>& is, ru a0, Args&& ...args);
  6459 +
  6460 +template <class CharT, class Traits, class ...Args>
  6461 +void
  6462 +read(std::basic_istream<CharT, Traits>& is, int a0, Args&& ...args);
  6463 +
  6464 +template <class CharT, class Traits, class ...Args>
  6465 +void
  6466 +read(std::basic_istream<CharT, Traits>& is, rld a0, Args&& ...args);
  6467 +
  6468 +template <class CharT, class Traits, class ...Args>
  6469 +void
  6470 +read(std::basic_istream<CharT, Traits>& is, CharT a0, Args&& ...args)
  6471 +{
  6472 + // No-op if a0 == CharT{}
  6473 + if (a0 != CharT{})
  6474 + {
  6475 + auto ic = is.peek();
  6476 + if (Traits::eq_int_type(ic, Traits::eof()))
  6477 + {
  6478 + is.setstate(std::ios::failbit | std::ios::eofbit);
  6479 + return;
  6480 + }
  6481 + if (!Traits::eq(Traits::to_char_type(ic), a0))
  6482 + {
  6483 + is.setstate(std::ios::failbit);
  6484 + return;
  6485 + }
  6486 + (void)is.get();
  6487 + }
  6488 + read(is, std::forward<Args>(args)...);
  6489 +}
  6490 +
  6491 +template <class CharT, class Traits, class ...Args>
  6492 +void
  6493 +read(std::basic_istream<CharT, Traits>& is, rs a0, Args&& ...args)
  6494 +{
  6495 + auto x = read_signed(is, a0.m, a0.M);
  6496 + if (is.fail())
  6497 + return;
  6498 + a0.i = x;
  6499 + read(is, std::forward<Args>(args)...);
  6500 +}
  6501 +
  6502 +template <class CharT, class Traits, class ...Args>
  6503 +void
  6504 +read(std::basic_istream<CharT, Traits>& is, ru a0, Args&& ...args)
  6505 +{
  6506 + auto x = read_unsigned(is, a0.m, a0.M);
  6507 + if (is.fail())
  6508 + return;
  6509 + a0.i = static_cast<int>(x);
  6510 + read(is, std::forward<Args>(args)...);
  6511 +}
  6512 +
  6513 +template <class CharT, class Traits, class ...Args>
  6514 +void
  6515 +read(std::basic_istream<CharT, Traits>& is, int a0, Args&& ...args)
  6516 +{
  6517 + if (a0 != -1)
  6518 + {
  6519 + auto u = static_cast<unsigned>(a0);
  6520 + CharT buf[std::numeric_limits<unsigned>::digits10+2u] = {};
  6521 + auto e = buf;
  6522 + do
  6523 + {
  6524 + *e++ = static_cast<CharT>(CharT(u % 10) + CharT{'0'});
  6525 + u /= 10;
  6526 + } while (u > 0);
  6527 + std::reverse(buf, e);
  6528 + for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p)
  6529 + read(is, *p);
  6530 + }
  6531 + if (is.rdstate() == std::ios::goodbit)
  6532 + read(is, std::forward<Args>(args)...);
  6533 +}
  6534 +
  6535 +template <class CharT, class Traits, class ...Args>
  6536 +void
  6537 +read(std::basic_istream<CharT, Traits>& is, rld a0, Args&& ...args)
  6538 +{
  6539 + auto x = read_long_double(is, a0.m, a0.M);
  6540 + if (is.fail())
  6541 + return;
  6542 + a0.i = x;
  6543 + read(is, std::forward<Args>(args)...);
  6544 +}
  6545 +
  6546 +template <class T, class CharT, class Traits>
  6547 +inline
  6548 +void
  6549 +checked_set(T& value, T from, T not_a_value, std::basic_ios<CharT, Traits>& is)
  6550 +{
  6551 + if (!is.fail())
  6552 + {
  6553 + if (value == not_a_value)
  6554 + value = std::move(from);
  6555 + else if (value != from)
  6556 + is.setstate(std::ios::failbit);
  6557 + }
  6558 +}
  6559 +
  6560 +} // namespace detail;
  6561 +
  6562 +template <class CharT, class Traits, class Duration, class Alloc = std::allocator<CharT>>
  6563 +std::basic_istream<CharT, Traits>&
  6564 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
  6565 + fields<Duration>& fds, std::basic_string<CharT, Traits, Alloc>* abbrev,
  6566 + std::chrono::minutes* offset)
  6567 +{
  6568 + using std::numeric_limits;
  6569 + using std::ios;
  6570 + using std::chrono::duration;
  6571 + using std::chrono::duration_cast;
  6572 + using std::chrono::seconds;
  6573 + using std::chrono::minutes;
  6574 + using std::chrono::hours;
  6575 + using detail::round_i;
  6576 + typename std::basic_istream<CharT, Traits>::sentry ok{is, true};
  6577 + if (ok)
  6578 + {
  6579 + date::detail::save_istream<CharT, Traits> ss(is);
  6580 + is.fill(' ');
  6581 + is.flags(std::ios::skipws | std::ios::dec);
  6582 + is.width(0);
  6583 +#if !ONLY_C_LOCALE
  6584 + auto& f = std::use_facet<std::time_get<CharT>>(is.getloc());
  6585 + std::tm tm{};
  6586 +#endif
  6587 + const CharT* command = nullptr;
  6588 + auto modified = CharT{};
  6589 + auto width = -1;
  6590 +
  6591 + CONSTDATA int not_a_year = numeric_limits<short>::min();
  6592 + CONSTDATA int not_a_2digit_year = 100;
  6593 + CONSTDATA int not_a_century = not_a_year / 100;
  6594 + CONSTDATA int not_a_month = 0;
  6595 + CONSTDATA int not_a_day = 0;
  6596 + CONSTDATA int not_a_hour = numeric_limits<int>::min();
  6597 + CONSTDATA int not_a_hour_12_value = 0;
  6598 + CONSTDATA int not_a_minute = not_a_hour;
  6599 + CONSTDATA Duration not_a_second = Duration::min();
  6600 + CONSTDATA int not_a_doy = -1;
  6601 + CONSTDATA int not_a_weekday = 8;
  6602 + CONSTDATA int not_a_week_num = 100;
  6603 + CONSTDATA int not_a_ampm = -1;
  6604 + CONSTDATA minutes not_a_offset = minutes::min();
  6605 +
  6606 + int Y = not_a_year; // c, F, Y *
  6607 + int y = not_a_2digit_year; // D, x, y *
  6608 + int g = not_a_2digit_year; // g *
  6609 + int G = not_a_year; // G *
  6610 + int C = not_a_century; // C *
  6611 + int m = not_a_month; // b, B, h, m, c, D, F, x *
  6612 + int d = not_a_day; // c, d, D, e, F, x *
  6613 + int j = not_a_doy; // j *
  6614 + int wd = not_a_weekday; // a, A, u, w *
  6615 + int H = not_a_hour; // c, H, R, T, X *
  6616 + int I = not_a_hour_12_value; // I, r *
  6617 + int p = not_a_ampm; // p, r *
  6618 + int M = not_a_minute; // c, M, r, R, T, X *
  6619 + Duration s = not_a_second; // c, r, S, T, X *
  6620 + int U = not_a_week_num; // U *
  6621 + int V = not_a_week_num; // V *
  6622 + int W = not_a_week_num; // W *
  6623 + std::basic_string<CharT, Traits, Alloc> temp_abbrev; // Z *
  6624 + minutes temp_offset = not_a_offset; // z *
  6625 +
  6626 + using detail::read;
  6627 + using detail::rs;
  6628 + using detail::ru;
  6629 + using detail::rld;
  6630 + using detail::checked_set;
  6631 + for (; *fmt != CharT{} && !is.fail(); ++fmt)
  6632 + {
  6633 + switch (*fmt)
  6634 + {
  6635 + case 'a':
  6636 + case 'A':
  6637 + case 'u':
  6638 + case 'w': // wd: a, A, u, w
  6639 + if (command)
  6640 + {
  6641 + int trial_wd = not_a_weekday;
  6642 + if (*fmt == 'a' || *fmt == 'A')
  6643 + {
  6644 + if (modified == CharT{})
  6645 + {
  6646 +#if !ONLY_C_LOCALE
  6647 + ios::iostate err = ios::goodbit;
  6648 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6649 + is.setstate(err);
  6650 + if (!is.fail())
  6651 + trial_wd = tm.tm_wday;
  6652 +#else
  6653 + auto nm = detail::weekday_names();
  6654 + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
  6655 + if (!is.fail())
  6656 + trial_wd = i % 7;
  6657 +#endif
  6658 + }
  6659 + else
  6660 + read(is, CharT{'%'}, width, modified, *fmt);
  6661 + }
  6662 + else // *fmt == 'u' || *fmt == 'w'
  6663 + {
  6664 +#if !ONLY_C_LOCALE
  6665 + if (modified == CharT{})
  6666 +#else
  6667 + if (modified != CharT{'E'})
  6668 +#endif
  6669 + {
  6670 + read(is, ru{trial_wd, 1, width == -1 ?
  6671 + 1u : static_cast<unsigned>(width)});
  6672 + if (!is.fail())
  6673 + {
  6674 + if (*fmt == 'u')
  6675 + {
  6676 + if (!(1 <= trial_wd && trial_wd <= 7))
  6677 + {
  6678 + trial_wd = not_a_weekday;
  6679 + is.setstate(ios::failbit);
  6680 + }
  6681 + else if (trial_wd == 7)
  6682 + trial_wd = 0;
  6683 + }
  6684 + else // *fmt == 'w'
  6685 + {
  6686 + if (!(0 <= trial_wd && trial_wd <= 6))
  6687 + {
  6688 + trial_wd = not_a_weekday;
  6689 + is.setstate(ios::failbit);
  6690 + }
  6691 + }
  6692 + }
  6693 + }
  6694 +#if !ONLY_C_LOCALE
  6695 + else if (modified == CharT{'O'})
  6696 + {
  6697 + ios::iostate err = ios::goodbit;
  6698 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6699 + is.setstate(err);
  6700 + if (!is.fail())
  6701 + trial_wd = tm.tm_wday;
  6702 + }
  6703 +#endif
  6704 + else
  6705 + read(is, CharT{'%'}, width, modified, *fmt);
  6706 + }
  6707 + if (trial_wd != not_a_weekday)
  6708 + checked_set(wd, trial_wd, not_a_weekday, is);
  6709 + }
  6710 + else // !command
  6711 + read(is, *fmt);
  6712 + command = nullptr;
  6713 + width = -1;
  6714 + modified = CharT{};
  6715 + break;
  6716 + case 'b':
  6717 + case 'B':
  6718 + case 'h':
  6719 + if (command)
  6720 + {
  6721 + if (modified == CharT{})
  6722 + {
  6723 + int ttm = not_a_month;
  6724 +#if !ONLY_C_LOCALE
  6725 + ios::iostate err = ios::goodbit;
  6726 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6727 + if ((err & ios::failbit) == 0)
  6728 + ttm = tm.tm_mon + 1;
  6729 + is.setstate(err);
  6730 +#else
  6731 + auto nm = detail::month_names();
  6732 + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
  6733 + if (!is.fail())
  6734 + ttm = i % 12 + 1;
  6735 +#endif
  6736 + checked_set(m, ttm, not_a_month, is);
  6737 + }
  6738 + else
  6739 + read(is, CharT{'%'}, width, modified, *fmt);
  6740 + command = nullptr;
  6741 + width = -1;
  6742 + modified = CharT{};
  6743 + }
  6744 + else
  6745 + read(is, *fmt);
  6746 + break;
  6747 + case 'c':
  6748 + if (command)
  6749 + {
  6750 + if (modified != CharT{'O'})
  6751 + {
  6752 +#if !ONLY_C_LOCALE
  6753 + ios::iostate err = ios::goodbit;
  6754 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6755 + if ((err & ios::failbit) == 0)
  6756 + {
  6757 + checked_set(Y, tm.tm_year + 1900, not_a_year, is);
  6758 + checked_set(m, tm.tm_mon + 1, not_a_month, is);
  6759 + checked_set(d, tm.tm_mday, not_a_day, is);
  6760 + checked_set(H, tm.tm_hour, not_a_hour, is);
  6761 + checked_set(M, tm.tm_min, not_a_minute, is);
  6762 + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
  6763 + not_a_second, is);
  6764 + }
  6765 + is.setstate(err);
  6766 +#else
  6767 + // "%a %b %e %T %Y"
  6768 + auto nm = detail::weekday_names();
  6769 + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
  6770 + checked_set(wd, static_cast<int>(i % 7), not_a_weekday, is);
  6771 + ws(is);
  6772 + nm = detail::month_names();
  6773 + i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
  6774 + checked_set(m, static_cast<int>(i % 12 + 1), not_a_month, is);
  6775 + ws(is);
  6776 + int td = not_a_day;
  6777 + read(is, rs{td, 1, 2});
  6778 + checked_set(d, td, not_a_day, is);
  6779 + ws(is);
  6780 + using dfs = detail::decimal_format_seconds<Duration>;
  6781 + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
  6782 + int tH;
  6783 + int tM;
  6784 + long double S{};
  6785 + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
  6786 + CharT{':'}, rld{S, 1, w});
  6787 + checked_set(H, tH, not_a_hour, is);
  6788 + checked_set(M, tM, not_a_minute, is);
  6789 + checked_set(s, round_i<Duration>(duration<long double>{S}),
  6790 + not_a_second, is);
  6791 + ws(is);
  6792 + int tY = not_a_year;
  6793 + read(is, rs{tY, 1, 4u});
  6794 + checked_set(Y, tY, not_a_year, is);
  6795 +#endif
  6796 + }
  6797 + else
  6798 + read(is, CharT{'%'}, width, modified, *fmt);
  6799 + command = nullptr;
  6800 + width = -1;
  6801 + modified = CharT{};
  6802 + }
  6803 + else
  6804 + read(is, *fmt);
  6805 + break;
  6806 + case 'x':
  6807 + if (command)
  6808 + {
  6809 + if (modified != CharT{'O'})
  6810 + {
  6811 +#if !ONLY_C_LOCALE
  6812 + ios::iostate err = ios::goodbit;
  6813 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6814 + if ((err & ios::failbit) == 0)
  6815 + {
  6816 + checked_set(Y, tm.tm_year + 1900, not_a_year, is);
  6817 + checked_set(m, tm.tm_mon + 1, not_a_month, is);
  6818 + checked_set(d, tm.tm_mday, not_a_day, is);
  6819 + }
  6820 + is.setstate(err);
  6821 +#else
  6822 + // "%m/%d/%y"
  6823 + int ty = not_a_2digit_year;
  6824 + int tm = not_a_month;
  6825 + int td = not_a_day;
  6826 + read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'},
  6827 + rs{ty, 1, 2});
  6828 + checked_set(y, ty, not_a_2digit_year, is);
  6829 + checked_set(m, tm, not_a_month, is);
  6830 + checked_set(d, td, not_a_day, is);
  6831 +#endif
  6832 + }
  6833 + else
  6834 + read(is, CharT{'%'}, width, modified, *fmt);
  6835 + command = nullptr;
  6836 + width = -1;
  6837 + modified = CharT{};
  6838 + }
  6839 + else
  6840 + read(is, *fmt);
  6841 + break;
  6842 + case 'X':
  6843 + if (command)
  6844 + {
  6845 + if (modified != CharT{'O'})
  6846 + {
  6847 +#if !ONLY_C_LOCALE
  6848 + ios::iostate err = ios::goodbit;
  6849 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6850 + if ((err & ios::failbit) == 0)
  6851 + {
  6852 + checked_set(H, tm.tm_hour, not_a_hour, is);
  6853 + checked_set(M, tm.tm_min, not_a_minute, is);
  6854 + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
  6855 + not_a_second, is);
  6856 + }
  6857 + is.setstate(err);
  6858 +#else
  6859 + // "%T"
  6860 + using dfs = detail::decimal_format_seconds<Duration>;
  6861 + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
  6862 + int tH = not_a_hour;
  6863 + int tM = not_a_minute;
  6864 + long double S{};
  6865 + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
  6866 + CharT{':'}, rld{S, 1, w});
  6867 + checked_set(H, tH, not_a_hour, is);
  6868 + checked_set(M, tM, not_a_minute, is);
  6869 + checked_set(s, round_i<Duration>(duration<long double>{S}),
  6870 + not_a_second, is);
  6871 +#endif
  6872 + }
  6873 + else
  6874 + read(is, CharT{'%'}, width, modified, *fmt);
  6875 + command = nullptr;
  6876 + width = -1;
  6877 + modified = CharT{};
  6878 + }
  6879 + else
  6880 + read(is, *fmt);
  6881 + break;
  6882 + case 'C':
  6883 + if (command)
  6884 + {
  6885 + int tC = not_a_century;
  6886 +#if !ONLY_C_LOCALE
  6887 + if (modified == CharT{})
  6888 + {
  6889 +#endif
  6890 + read(is, rs{tC, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  6891 +#if !ONLY_C_LOCALE
  6892 + }
  6893 + else
  6894 + {
  6895 + ios::iostate err = ios::goodbit;
  6896 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6897 + if ((err & ios::failbit) == 0)
  6898 + {
  6899 + auto tY = tm.tm_year + 1900;
  6900 + tC = (tY >= 0 ? tY : tY-99) / 100;
  6901 + }
  6902 + is.setstate(err);
  6903 + }
  6904 +#endif
  6905 + checked_set(C, tC, not_a_century, is);
  6906 + command = nullptr;
  6907 + width = -1;
  6908 + modified = CharT{};
  6909 + }
  6910 + else
  6911 + read(is, *fmt);
  6912 + break;
  6913 + case 'D':
  6914 + if (command)
  6915 + {
  6916 + if (modified == CharT{})
  6917 + {
  6918 + int tn = not_a_month;
  6919 + int td = not_a_day;
  6920 + int ty = not_a_2digit_year;
  6921 + read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'},
  6922 + ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'},
  6923 + rs{ty, 1, 2});
  6924 + checked_set(y, ty, not_a_2digit_year, is);
  6925 + checked_set(m, tn, not_a_month, is);
  6926 + checked_set(d, td, not_a_day, is);
  6927 + }
  6928 + else
  6929 + read(is, CharT{'%'}, width, modified, *fmt);
  6930 + command = nullptr;
  6931 + width = -1;
  6932 + modified = CharT{};
  6933 + }
  6934 + else
  6935 + read(is, *fmt);
  6936 + break;
  6937 + case 'F':
  6938 + if (command)
  6939 + {
  6940 + if (modified == CharT{})
  6941 + {
  6942 + int tY = not_a_year;
  6943 + int tn = not_a_month;
  6944 + int td = not_a_day;
  6945 + read(is, rs{tY, 1, width == -1 ? 4u : static_cast<unsigned>(width)},
  6946 + CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2});
  6947 + checked_set(Y, tY, not_a_year, is);
  6948 + checked_set(m, tn, not_a_month, is);
  6949 + checked_set(d, td, not_a_day, is);
  6950 + }
  6951 + else
  6952 + read(is, CharT{'%'}, width, modified, *fmt);
  6953 + command = nullptr;
  6954 + width = -1;
  6955 + modified = CharT{};
  6956 + }
  6957 + else
  6958 + read(is, *fmt);
  6959 + break;
  6960 + case 'd':
  6961 + case 'e':
  6962 + if (command)
  6963 + {
  6964 +#if !ONLY_C_LOCALE
  6965 + if (modified == CharT{})
  6966 +#else
  6967 + if (modified != CharT{'E'})
  6968 +#endif
  6969 + {
  6970 + int td = not_a_day;
  6971 + read(is, rs{td, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  6972 + checked_set(d, td, not_a_day, is);
  6973 + }
  6974 +#if !ONLY_C_LOCALE
  6975 + else if (modified == CharT{'O'})
  6976 + {
  6977 + ios::iostate err = ios::goodbit;
  6978 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  6979 + command = nullptr;
  6980 + width = -1;
  6981 + modified = CharT{};
  6982 + if ((err & ios::failbit) == 0)
  6983 + checked_set(d, tm.tm_mday, not_a_day, is);
  6984 + is.setstate(err);
  6985 + }
  6986 +#endif
  6987 + else
  6988 + read(is, CharT{'%'}, width, modified, *fmt);
  6989 + command = nullptr;
  6990 + width = -1;
  6991 + modified = CharT{};
  6992 + }
  6993 + else
  6994 + read(is, *fmt);
  6995 + break;
  6996 + case 'H':
  6997 + if (command)
  6998 + {
  6999 +#if !ONLY_C_LOCALE
  7000 + if (modified == CharT{})
  7001 +#else
  7002 + if (modified != CharT{'E'})
  7003 +#endif
  7004 + {
  7005 + int tH = not_a_hour;
  7006 + read(is, ru{tH, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7007 + checked_set(H, tH, not_a_hour, is);
  7008 + }
  7009 +#if !ONLY_C_LOCALE
  7010 + else if (modified == CharT{'O'})
  7011 + {
  7012 + ios::iostate err = ios::goodbit;
  7013 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7014 + if ((err & ios::failbit) == 0)
  7015 + checked_set(H, tm.tm_hour, not_a_hour, is);
  7016 + is.setstate(err);
  7017 + }
  7018 +#endif
  7019 + else
  7020 + read(is, CharT{'%'}, width, modified, *fmt);
  7021 + command = nullptr;
  7022 + width = -1;
  7023 + modified = CharT{};
  7024 + }
  7025 + else
  7026 + read(is, *fmt);
  7027 + break;
  7028 + case 'I':
  7029 + if (command)
  7030 + {
  7031 + if (modified == CharT{})
  7032 + {
  7033 + int tI = not_a_hour_12_value;
  7034 + // reads in an hour into I, but most be in [1, 12]
  7035 + read(is, rs{tI, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7036 + if (!(1 <= tI && tI <= 12))
  7037 + is.setstate(ios::failbit);
  7038 + checked_set(I, tI, not_a_hour_12_value, is);
  7039 + }
  7040 + else
  7041 + read(is, CharT{'%'}, width, modified, *fmt);
  7042 + command = nullptr;
  7043 + width = -1;
  7044 + modified = CharT{};
  7045 + }
  7046 + else
  7047 + read(is, *fmt);
  7048 + break;
  7049 + case 'j':
  7050 + if (command)
  7051 + {
  7052 + if (modified == CharT{})
  7053 + {
  7054 + int tj = not_a_doy;
  7055 + read(is, ru{tj, 1, width == -1 ? 3u : static_cast<unsigned>(width)});
  7056 + checked_set(j, tj, not_a_doy, is);
  7057 + }
  7058 + else
  7059 + read(is, CharT{'%'}, width, modified, *fmt);
  7060 + command = nullptr;
  7061 + width = -1;
  7062 + modified = CharT{};
  7063 + }
  7064 + else
  7065 + read(is, *fmt);
  7066 + break;
  7067 + case 'M':
  7068 + if (command)
  7069 + {
  7070 +#if !ONLY_C_LOCALE
  7071 + if (modified == CharT{})
  7072 +#else
  7073 + if (modified != CharT{'E'})
  7074 +#endif
  7075 + {
  7076 + int tM = not_a_minute;
  7077 + read(is, ru{tM, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7078 + checked_set(M, tM, not_a_minute, is);
  7079 + }
  7080 +#if !ONLY_C_LOCALE
  7081 + else if (modified == CharT{'O'})
  7082 + {
  7083 + ios::iostate err = ios::goodbit;
  7084 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7085 + if ((err & ios::failbit) == 0)
  7086 + checked_set(M, tm.tm_min, not_a_minute, is);
  7087 + is.setstate(err);
  7088 + }
  7089 +#endif
  7090 + else
  7091 + read(is, CharT{'%'}, width, modified, *fmt);
  7092 + command = nullptr;
  7093 + width = -1;
  7094 + modified = CharT{};
  7095 + }
  7096 + else
  7097 + read(is, *fmt);
  7098 + break;
  7099 + case 'm':
  7100 + if (command)
  7101 + {
  7102 +#if !ONLY_C_LOCALE
  7103 + if (modified == CharT{})
  7104 +#else
  7105 + if (modified != CharT{'E'})
  7106 +#endif
  7107 + {
  7108 + int tn = not_a_month;
  7109 + read(is, rs{tn, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7110 + checked_set(m, tn, not_a_month, is);
  7111 + }
  7112 +#if !ONLY_C_LOCALE
  7113 + else if (modified == CharT{'O'})
  7114 + {
  7115 + ios::iostate err = ios::goodbit;
  7116 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7117 + if ((err & ios::failbit) == 0)
  7118 + checked_set(m, tm.tm_mon + 1, not_a_month, is);
  7119 + is.setstate(err);
  7120 + }
  7121 +#endif
  7122 + else
  7123 + read(is, CharT{'%'}, width, modified, *fmt);
  7124 + command = nullptr;
  7125 + width = -1;
  7126 + modified = CharT{};
  7127 + }
  7128 + else
  7129 + read(is, *fmt);
  7130 + break;
  7131 + case 'n':
  7132 + case 't':
  7133 + if (command)
  7134 + {
  7135 + if (modified == CharT{})
  7136 + {
  7137 + // %n matches a single white space character
  7138 + // %t matches 0 or 1 white space characters
  7139 + auto ic = is.peek();
  7140 + if (Traits::eq_int_type(ic, Traits::eof()))
  7141 + {
  7142 + ios::iostate err = ios::eofbit;
  7143 + if (*fmt == 'n')
  7144 + err |= ios::failbit;
  7145 + is.setstate(err);
  7146 + break;
  7147 + }
  7148 + if (isspace(ic))
  7149 + {
  7150 + (void)is.get();
  7151 + }
  7152 + else if (*fmt == 'n')
  7153 + is.setstate(ios::failbit);
  7154 + }
  7155 + else
  7156 + read(is, CharT{'%'}, width, modified, *fmt);
  7157 + command = nullptr;
  7158 + width = -1;
  7159 + modified = CharT{};
  7160 + }
  7161 + else
  7162 + read(is, *fmt);
  7163 + break;
  7164 + case 'p':
  7165 + if (command)
  7166 + {
  7167 + if (modified == CharT{})
  7168 + {
  7169 + int tp = not_a_ampm;
  7170 +#if !ONLY_C_LOCALE
  7171 + tm = std::tm{};
  7172 + tm.tm_hour = 1;
  7173 + ios::iostate err = ios::goodbit;
  7174 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7175 + is.setstate(err);
  7176 + if (tm.tm_hour == 1)
  7177 + tp = 0;
  7178 + else if (tm.tm_hour == 13)
  7179 + tp = 1;
  7180 + else
  7181 + is.setstate(err);
  7182 +#else
  7183 + auto nm = detail::ampm_names();
  7184 + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
  7185 + tp = static_cast<decltype(tp)>(i);
  7186 +#endif
  7187 + checked_set(p, tp, not_a_ampm, is);
  7188 + }
  7189 + else
  7190 + read(is, CharT{'%'}, width, modified, *fmt);
  7191 + command = nullptr;
  7192 + width = -1;
  7193 + modified = CharT{};
  7194 + }
  7195 + else
  7196 + read(is, *fmt);
  7197 +
  7198 + break;
  7199 + case 'r':
  7200 + if (command)
  7201 + {
  7202 + if (modified == CharT{})
  7203 + {
  7204 +#if !ONLY_C_LOCALE
  7205 + ios::iostate err = ios::goodbit;
  7206 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7207 + if ((err & ios::failbit) == 0)
  7208 + {
  7209 + checked_set(H, tm.tm_hour, not_a_hour, is);
  7210 + checked_set(M, tm.tm_min, not_a_hour, is);
  7211 + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
  7212 + not_a_second, is);
  7213 + }
  7214 + is.setstate(err);
  7215 +#else
  7216 + // "%I:%M:%S %p"
  7217 + using dfs = detail::decimal_format_seconds<Duration>;
  7218 + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
  7219 + long double S{};
  7220 + int tI = not_a_hour_12_value;
  7221 + int tM = not_a_minute;
  7222 + read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2},
  7223 + CharT{':'}, rld{S, 1, w});
  7224 + checked_set(I, tI, not_a_hour_12_value, is);
  7225 + checked_set(M, tM, not_a_minute, is);
  7226 + checked_set(s, round_i<Duration>(duration<long double>{S}),
  7227 + not_a_second, is);
  7228 + ws(is);
  7229 + auto nm = detail::ampm_names();
  7230 + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first;
  7231 + checked_set(p, static_cast<int>(i), not_a_ampm, is);
  7232 +#endif
  7233 + }
  7234 + else
  7235 + read(is, CharT{'%'}, width, modified, *fmt);
  7236 + command = nullptr;
  7237 + width = -1;
  7238 + modified = CharT{};
  7239 + }
  7240 + else
  7241 + read(is, *fmt);
  7242 + break;
  7243 + case 'R':
  7244 + if (command)
  7245 + {
  7246 + if (modified == CharT{})
  7247 + {
  7248 + int tH = not_a_hour;
  7249 + int tM = not_a_minute;
  7250 + read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'},
  7251 + ru{tM, 1, 2}, CharT{'\0'});
  7252 + checked_set(H, tH, not_a_hour, is);
  7253 + checked_set(M, tM, not_a_minute, is);
  7254 + }
  7255 + else
  7256 + read(is, CharT{'%'}, width, modified, *fmt);
  7257 + command = nullptr;
  7258 + width = -1;
  7259 + modified = CharT{};
  7260 + }
  7261 + else
  7262 + read(is, *fmt);
  7263 + break;
  7264 + case 'S':
  7265 + if (command)
  7266 + {
  7267 + #if !ONLY_C_LOCALE
  7268 + if (modified == CharT{})
  7269 +#else
  7270 + if (modified != CharT{'E'})
  7271 +#endif
  7272 + {
  7273 + using dfs = detail::decimal_format_seconds<Duration>;
  7274 + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
  7275 + long double S{};
  7276 + read(is, rld{S, 1, width == -1 ? w : static_cast<unsigned>(width)});
  7277 + checked_set(s, round_i<Duration>(duration<long double>{S}),
  7278 + not_a_second, is);
  7279 + }
  7280 +#if !ONLY_C_LOCALE
  7281 + else if (modified == CharT{'O'})
  7282 + {
  7283 + ios::iostate err = ios::goodbit;
  7284 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7285 + if ((err & ios::failbit) == 0)
  7286 + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}),
  7287 + not_a_second, is);
  7288 + is.setstate(err);
  7289 + }
  7290 +#endif
  7291 + else
  7292 + read(is, CharT{'%'}, width, modified, *fmt);
  7293 + command = nullptr;
  7294 + width = -1;
  7295 + modified = CharT{};
  7296 + }
  7297 + else
  7298 + read(is, *fmt);
  7299 + break;
  7300 + case 'T':
  7301 + if (command)
  7302 + {
  7303 + if (modified == CharT{})
  7304 + {
  7305 + using dfs = detail::decimal_format_seconds<Duration>;
  7306 + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
  7307 + int tH = not_a_hour;
  7308 + int tM = not_a_minute;
  7309 + long double S{};
  7310 + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
  7311 + CharT{':'}, rld{S, 1, w});
  7312 + checked_set(H, tH, not_a_hour, is);
  7313 + checked_set(M, tM, not_a_minute, is);
  7314 + checked_set(s, round_i<Duration>(duration<long double>{S}),
  7315 + not_a_second, is);
  7316 + }
  7317 + else
  7318 + read(is, CharT{'%'}, width, modified, *fmt);
  7319 + command = nullptr;
  7320 + width = -1;
  7321 + modified = CharT{};
  7322 + }
  7323 + else
  7324 + read(is, *fmt);
  7325 + break;
  7326 + case 'Y':
  7327 + if (command)
  7328 + {
  7329 +#if !ONLY_C_LOCALE
  7330 + if (modified == CharT{})
  7331 +#else
  7332 + if (modified != CharT{'O'})
  7333 +#endif
  7334 + {
  7335 + int tY = not_a_year;
  7336 + read(is, rs{tY, 1, width == -1 ? 4u : static_cast<unsigned>(width)});
  7337 + checked_set(Y, tY, not_a_year, is);
  7338 + }
  7339 +#if !ONLY_C_LOCALE
  7340 + else if (modified == CharT{'E'})
  7341 + {
  7342 + ios::iostate err = ios::goodbit;
  7343 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7344 + if ((err & ios::failbit) == 0)
  7345 + checked_set(Y, tm.tm_year + 1900, not_a_year, is);
  7346 + is.setstate(err);
  7347 + }
  7348 +#endif
  7349 + else
  7350 + read(is, CharT{'%'}, width, modified, *fmt);
  7351 + command = nullptr;
  7352 + width = -1;
  7353 + modified = CharT{};
  7354 + }
  7355 + else
  7356 + read(is, *fmt);
  7357 + break;
  7358 + case 'y':
  7359 + if (command)
  7360 + {
  7361 +#if !ONLY_C_LOCALE
  7362 + if (modified == CharT{})
  7363 +#endif
  7364 + {
  7365 + int ty = not_a_2digit_year;
  7366 + read(is, ru{ty, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7367 + checked_set(y, ty, not_a_2digit_year, is);
  7368 + }
  7369 +#if !ONLY_C_LOCALE
  7370 + else
  7371 + {
  7372 + ios::iostate err = ios::goodbit;
  7373 + f.get(is, nullptr, is, err, &tm, command, fmt+1);
  7374 + if ((err & ios::failbit) == 0)
  7375 + checked_set(Y, tm.tm_year + 1900, not_a_year, is);
  7376 + is.setstate(err);
  7377 + }
  7378 +#endif
  7379 + command = nullptr;
  7380 + width = -1;
  7381 + modified = CharT{};
  7382 + }
  7383 + else
  7384 + read(is, *fmt);
  7385 + break;
  7386 + case 'g':
  7387 + if (command)
  7388 + {
  7389 + if (modified == CharT{})
  7390 + {
  7391 + int tg = not_a_2digit_year;
  7392 + read(is, ru{tg, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7393 + checked_set(g, tg, not_a_2digit_year, is);
  7394 + }
  7395 + else
  7396 + read(is, CharT{'%'}, width, modified, *fmt);
  7397 + command = nullptr;
  7398 + width = -1;
  7399 + modified = CharT{};
  7400 + }
  7401 + else
  7402 + read(is, *fmt);
  7403 + break;
  7404 + case 'G':
  7405 + if (command)
  7406 + {
  7407 + if (modified == CharT{})
  7408 + {
  7409 + int tG = not_a_year;
  7410 + read(is, rs{tG, 1, width == -1 ? 4u : static_cast<unsigned>(width)});
  7411 + checked_set(G, tG, not_a_year, is);
  7412 + }
  7413 + else
  7414 + read(is, CharT{'%'}, width, modified, *fmt);
  7415 + command = nullptr;
  7416 + width = -1;
  7417 + modified = CharT{};
  7418 + }
  7419 + else
  7420 + read(is, *fmt);
  7421 + break;
  7422 + case 'U':
  7423 + if (command)
  7424 + {
  7425 + if (modified == CharT{})
  7426 + {
  7427 + int tU = not_a_week_num;
  7428 + read(is, ru{tU, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7429 + checked_set(U, tU, not_a_week_num, is);
  7430 + }
  7431 + else
  7432 + read(is, CharT{'%'}, width, modified, *fmt);
  7433 + command = nullptr;
  7434 + width = -1;
  7435 + modified = CharT{};
  7436 + }
  7437 + else
  7438 + read(is, *fmt);
  7439 + break;
  7440 + case 'V':
  7441 + if (command)
  7442 + {
  7443 + if (modified == CharT{})
  7444 + {
  7445 + int tV = not_a_week_num;
  7446 + read(is, ru{tV, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7447 + checked_set(V, tV, not_a_week_num, is);
  7448 + }
  7449 + else
  7450 + read(is, CharT{'%'}, width, modified, *fmt);
  7451 + command = nullptr;
  7452 + width = -1;
  7453 + modified = CharT{};
  7454 + }
  7455 + else
  7456 + read(is, *fmt);
  7457 + break;
  7458 + case 'W':
  7459 + if (command)
  7460 + {
  7461 + if (modified == CharT{})
  7462 + {
  7463 + int tW = not_a_week_num;
  7464 + read(is, ru{tW, 1, width == -1 ? 2u : static_cast<unsigned>(width)});
  7465 + checked_set(W, tW, not_a_week_num, is);
  7466 + }
  7467 + else
  7468 + read(is, CharT{'%'}, width, modified, *fmt);
  7469 + command = nullptr;
  7470 + width = -1;
  7471 + modified = CharT{};
  7472 + }
  7473 + else
  7474 + read(is, *fmt);
  7475 + break;
  7476 + case 'E':
  7477 + case 'O':
  7478 + if (command)
  7479 + {
  7480 + if (modified == CharT{})
  7481 + {
  7482 + modified = *fmt;
  7483 + }
  7484 + else
  7485 + {
  7486 + read(is, CharT{'%'}, width, modified, *fmt);
  7487 + command = nullptr;
  7488 + width = -1;
  7489 + modified = CharT{};
  7490 + }
  7491 + }
  7492 + else
  7493 + read(is, *fmt);
  7494 + break;
  7495 + case '%':
  7496 + if (command)
  7497 + {
  7498 + if (modified == CharT{})
  7499 + read(is, *fmt);
  7500 + else
  7501 + read(is, CharT{'%'}, width, modified, *fmt);
  7502 + command = nullptr;
  7503 + width = -1;
  7504 + modified = CharT{};
  7505 + }
  7506 + else
  7507 + command = fmt;
  7508 + break;
  7509 + case 'z':
  7510 + if (command)
  7511 + {
  7512 + int tH, tM;
  7513 + minutes toff = not_a_offset;
  7514 + bool neg = false;
  7515 + auto ic = is.peek();
  7516 + if (!Traits::eq_int_type(ic, Traits::eof()))
  7517 + {
  7518 + auto c = static_cast<char>(Traits::to_char_type(ic));
  7519 + if (c == '-')
  7520 + neg = true;
  7521 + }
  7522 + if (modified == CharT{})
  7523 + {
  7524 + read(is, rs{tH, 2, 2});
  7525 + if (!is.fail())
  7526 + toff = hours{std::abs(tH)};
  7527 + if (is.good())
  7528 + {
  7529 + ic = is.peek();
  7530 + if (!Traits::eq_int_type(ic, Traits::eof()))
  7531 + {
  7532 + auto c = static_cast<char>(Traits::to_char_type(ic));
  7533 + if ('0' <= c && c <= '9')
  7534 + {
  7535 + read(is, ru{tM, 2, 2});
  7536 + if (!is.fail())
  7537 + toff += minutes{tM};
  7538 + }
  7539 + }
  7540 + }
  7541 + }
  7542 + else
  7543 + {
  7544 + read(is, rs{tH, 1, 2});
  7545 + if (!is.fail())
  7546 + toff = hours{std::abs(tH)};
  7547 + if (is.good())
  7548 + {
  7549 + ic = is.peek();
  7550 + if (!Traits::eq_int_type(ic, Traits::eof()))
  7551 + {
  7552 + auto c = static_cast<char>(Traits::to_char_type(ic));
  7553 + if (c == ':')
  7554 + {
  7555 + (void)is.get();
  7556 + read(is, ru{tM, 2, 2});
  7557 + if (!is.fail())
  7558 + toff += minutes{tM};
  7559 + }
  7560 + }
  7561 + }
  7562 + }
  7563 + if (neg)
  7564 + toff = -toff;
  7565 + checked_set(temp_offset, toff, not_a_offset, is);
  7566 + command = nullptr;
  7567 + width = -1;
  7568 + modified = CharT{};
  7569 + }
  7570 + else
  7571 + read(is, *fmt);
  7572 + break;
  7573 + case 'Z':
  7574 + if (command)
  7575 + {
  7576 + if (modified == CharT{})
  7577 + {
  7578 + std::basic_string<CharT, Traits, Alloc> buf;
  7579 + while (is.rdstate() == std::ios::goodbit)
  7580 + {
  7581 + auto i = is.rdbuf()->sgetc();
  7582 + if (Traits::eq_int_type(i, Traits::eof()))
  7583 + {
  7584 + is.setstate(ios::eofbit);
  7585 + break;
  7586 + }
  7587 + auto wc = Traits::to_char_type(i);
  7588 + auto c = static_cast<char>(wc);
  7589 + // is c a valid time zone name or abbreviation character?
  7590 + if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) ||
  7591 + c == '_' || c == '/' || c == '-' || c == '+'))
  7592 + break;
  7593 + buf.push_back(c);
  7594 + is.rdbuf()->sbumpc();
  7595 + }
  7596 + if (buf.empty())
  7597 + is.setstate(ios::failbit);
  7598 + checked_set(temp_abbrev, buf, {}, is);
  7599 + }
  7600 + else
  7601 + read(is, CharT{'%'}, width, modified, *fmt);
  7602 + command = nullptr;
  7603 + width = -1;
  7604 + modified = CharT{};
  7605 + }
  7606 + else
  7607 + read(is, *fmt);
  7608 + break;
  7609 + default:
  7610 + if (command)
  7611 + {
  7612 + if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9')
  7613 + {
  7614 + width = static_cast<char>(*fmt) - '0';
  7615 + while ('0' <= fmt[1] && fmt[1] <= '9')
  7616 + width = 10*width + static_cast<char>(*++fmt) - '0';
  7617 + }
  7618 + else
  7619 + {
  7620 + if (modified == CharT{})
  7621 + read(is, CharT{'%'}, width, *fmt);
  7622 + else
  7623 + read(is, CharT{'%'}, width, modified, *fmt);
  7624 + command = nullptr;
  7625 + width = -1;
  7626 + modified = CharT{};
  7627 + }
  7628 + }
  7629 + else // !command
  7630 + {
  7631 + if (isspace(static_cast<unsigned char>(*fmt)))
  7632 + {
  7633 + // space matches 0 or more white space characters
  7634 + if (is.good())
  7635 + ws(is);
  7636 + }
  7637 + else
  7638 + read(is, *fmt);
  7639 + }
  7640 + break;
  7641 + }
  7642 + }
  7643 + // is.fail() || *fmt == CharT{}
  7644 + if (is.rdstate() == ios::goodbit && command)
  7645 + {
  7646 + if (modified == CharT{})
  7647 + read(is, CharT{'%'}, width);
  7648 + else
  7649 + read(is, CharT{'%'}, width, modified);
  7650 + }
  7651 + if (!is.fail())
  7652 + {
  7653 + if (y != not_a_2digit_year)
  7654 + {
  7655 + // Convert y and an optional C to Y
  7656 + if (!(0 <= y && y <= 99))
  7657 + goto broken;
  7658 + if (C == not_a_century)
  7659 + {
  7660 + if (Y == not_a_year)
  7661 + {
  7662 + if (y >= 69)
  7663 + C = 19;
  7664 + else
  7665 + C = 20;
  7666 + }
  7667 + else
  7668 + {
  7669 + C = (Y >= 0 ? Y : Y-100) / 100;
  7670 + }
  7671 + }
  7672 + int tY;
  7673 + if (C >= 0)
  7674 + tY = 100*C + y;
  7675 + else
  7676 + tY = 100*(C+1) - (y == 0 ? 100 : y);
  7677 + if (Y != not_a_year && Y != tY)
  7678 + goto broken;
  7679 + Y = tY;
  7680 + }
  7681 + if (g != not_a_2digit_year)
  7682 + {
  7683 + // Convert g and an optional C to G
  7684 + if (!(0 <= g && g <= 99))
  7685 + goto broken;
  7686 + if (C == not_a_century)
  7687 + {
  7688 + if (G == not_a_year)
  7689 + {
  7690 + if (g >= 69)
  7691 + C = 19;
  7692 + else
  7693 + C = 20;
  7694 + }
  7695 + else
  7696 + {
  7697 + C = (G >= 0 ? G : G-100) / 100;
  7698 + }
  7699 + }
  7700 + int tG;
  7701 + if (C >= 0)
  7702 + tG = 100*C + g;
  7703 + else
  7704 + tG = 100*(C+1) - (g == 0 ? 100 : g);
  7705 + if (G != not_a_year && G != tG)
  7706 + goto broken;
  7707 + G = tG;
  7708 + }
  7709 + if (Y < static_cast<int>(year::min()) || Y > static_cast<int>(year::max()))
  7710 + Y = not_a_year;
  7711 + bool computed = false;
  7712 + if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday)
  7713 + {
  7714 + year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) +
  7715 + (Monday-Thursday) + weeks{V-1} +
  7716 + (weekday{static_cast<unsigned>(wd)}-Monday);
  7717 + if (Y == not_a_year)
  7718 + Y = static_cast<int>(ymd_trial.year());
  7719 + else if (year{Y} != ymd_trial.year())
  7720 + goto broken;
  7721 + if (m == not_a_month)
  7722 + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
  7723 + else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
  7724 + goto broken;
  7725 + if (d == not_a_day)
  7726 + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
  7727 + else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
  7728 + goto broken;
  7729 + computed = true;
  7730 + }
  7731 + if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday)
  7732 + {
  7733 + year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) +
  7734 + weeks{U-1} +
  7735 + (weekday{static_cast<unsigned>(wd)} - Sunday);
  7736 + if (Y == not_a_year)
  7737 + Y = static_cast<int>(ymd_trial.year());
  7738 + else if (year{Y} != ymd_trial.year())
  7739 + goto broken;
  7740 + if (m == not_a_month)
  7741 + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
  7742 + else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
  7743 + goto broken;
  7744 + if (d == not_a_day)
  7745 + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
  7746 + else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
  7747 + goto broken;
  7748 + computed = true;
  7749 + }
  7750 + if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday)
  7751 + {
  7752 + year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) +
  7753 + weeks{W-1} +
  7754 + (weekday{static_cast<unsigned>(wd)} - Monday);
  7755 + if (Y == not_a_year)
  7756 + Y = static_cast<int>(ymd_trial.year());
  7757 + else if (year{Y} != ymd_trial.year())
  7758 + goto broken;
  7759 + if (m == not_a_month)
  7760 + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
  7761 + else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
  7762 + goto broken;
  7763 + if (d == not_a_day)
  7764 + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
  7765 + else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
  7766 + goto broken;
  7767 + computed = true;
  7768 + }
  7769 + if (j != not_a_doy && Y != not_a_year)
  7770 + {
  7771 + auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}};
  7772 + if (m == not_a_month)
  7773 + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
  7774 + else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
  7775 + goto broken;
  7776 + if (d == not_a_day)
  7777 + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
  7778 + else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
  7779 + goto broken;
  7780 + j = not_a_doy;
  7781 + }
  7782 + auto ymd = year{Y}/m/d;
  7783 + if (ymd.ok())
  7784 + {
  7785 + if (wd == not_a_weekday)
  7786 + wd = static_cast<int>((weekday(sys_days(ymd)) - Sunday).count());
  7787 + else if (wd != static_cast<int>((weekday(sys_days(ymd)) - Sunday).count()))
  7788 + goto broken;
  7789 + if (!computed)
  7790 + {
  7791 + if (G != not_a_year || V != not_a_week_num)
  7792 + {
  7793 + sys_days sd = ymd;
  7794 + auto G_trial = year_month_day{sd + days{3}}.year();
  7795 + auto start = sys_days((G_trial - years{1})/December/Thursday[last]) +
  7796 + (Monday - Thursday);
  7797 + if (sd < start)
  7798 + {
  7799 + --G_trial;
  7800 + if (V != not_a_week_num)
  7801 + start = sys_days((G_trial - years{1})/December/Thursday[last])
  7802 + + (Monday - Thursday);
  7803 + }
  7804 + if (G != not_a_year && G != static_cast<int>(G_trial))
  7805 + goto broken;
  7806 + if (V != not_a_week_num)
  7807 + {
  7808 + auto V_trial = duration_cast<weeks>(sd - start).count() + 1;
  7809 + if (V != V_trial)
  7810 + goto broken;
  7811 + }
  7812 + }
  7813 + if (U != not_a_week_num)
  7814 + {
  7815 + auto start = sys_days(Sunday[1]/January/ymd.year());
  7816 + auto U_trial = floor<weeks>(sys_days(ymd) - start).count() + 1;
  7817 + if (U != U_trial)
  7818 + goto broken;
  7819 + }
  7820 + if (W != not_a_week_num)
  7821 + {
  7822 + auto start = sys_days(Monday[1]/January/ymd.year());
  7823 + auto W_trial = floor<weeks>(sys_days(ymd) - start).count() + 1;
  7824 + if (W != W_trial)
  7825 + goto broken;
  7826 + }
  7827 + }
  7828 + }
  7829 + fds.ymd = ymd;
  7830 + if (I != not_a_hour_12_value)
  7831 + {
  7832 + if (!(1 <= I && I <= 12))
  7833 + goto broken;
  7834 + if (p != not_a_ampm)
  7835 + {
  7836 + // p is in [0, 1] == [AM, PM]
  7837 + // Store trial H in I
  7838 + if (I == 12)
  7839 + --p;
  7840 + I += p*12;
  7841 + // Either set H from I or make sure H and I are consistent
  7842 + if (H == not_a_hour)
  7843 + H = I;
  7844 + else if (I != H)
  7845 + goto broken;
  7846 + }
  7847 + else // p == not_a_ampm
  7848 + {
  7849 + // if H, make sure H and I could be consistent
  7850 + if (H != not_a_hour)
  7851 + {
  7852 + if (I == 12)
  7853 + {
  7854 + if (H != 0 && H != 12)
  7855 + goto broken;
  7856 + }
  7857 + else if (!(I == H || I == H+12))
  7858 + {
  7859 + goto broken;
  7860 + }
  7861 + }
  7862 + else // I is ambiguous, AM or PM?
  7863 + goto broken;
  7864 + }
  7865 + }
  7866 + if (H != not_a_hour)
  7867 + {
  7868 + fds.has_tod = true;
  7869 + fds.tod = hh_mm_ss<Duration>{hours{H}};
  7870 + }
  7871 + if (M != not_a_minute)
  7872 + {
  7873 + fds.has_tod = true;
  7874 + fds.tod.m_ = minutes{M};
  7875 + }
  7876 + if (s != not_a_second)
  7877 + {
  7878 + fds.has_tod = true;
  7879 + fds.tod.s_ = detail::decimal_format_seconds<Duration>{s};
  7880 + }
  7881 + if (j != not_a_doy)
  7882 + {
  7883 + fds.has_tod = true;
  7884 + fds.tod.h_ += hours{days{j}};
  7885 + }
  7886 + if (wd != not_a_weekday)
  7887 + fds.wd = weekday{static_cast<unsigned>(wd)};
  7888 + if (abbrev != nullptr)
  7889 + *abbrev = std::move(temp_abbrev);
  7890 + if (offset != nullptr && temp_offset != not_a_offset)
  7891 + *offset = temp_offset;
  7892 + }
  7893 + return is;
  7894 + }
  7895 +broken:
  7896 + is.setstate(ios::failbit);
  7897 + return is;
  7898 +}
  7899 +
  7900 +template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
  7901 +std::basic_istream<CharT, Traits>&
  7902 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year& y,
  7903 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  7904 + std::chrono::minutes* offset = nullptr)
  7905 +{
  7906 + using CT = std::chrono::seconds;
  7907 + fields<CT> fds{};
  7908 + date::from_stream(is, fmt, fds, abbrev, offset);
  7909 + if (!fds.ymd.year().ok())
  7910 + is.setstate(std::ios::failbit);
  7911 + if (!is.fail())
  7912 + y = fds.ymd.year();
  7913 + return is;
  7914 +}
  7915 +
  7916 +template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
  7917 +std::basic_istream<CharT, Traits>&
  7918 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month& m,
  7919 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  7920 + std::chrono::minutes* offset = nullptr)
  7921 +{
  7922 + using CT = std::chrono::seconds;
  7923 + fields<CT> fds{};
  7924 + date::from_stream(is, fmt, fds, abbrev, offset);
  7925 + if (!fds.ymd.month().ok())
  7926 + is.setstate(std::ios::failbit);
  7927 + if (!is.fail())
  7928 + m = fds.ymd.month();
  7929 + return is;
  7930 +}
  7931 +
  7932 +template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
  7933 +std::basic_istream<CharT, Traits>&
  7934 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, day& d,
  7935 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  7936 + std::chrono::minutes* offset = nullptr)
  7937 +{
  7938 + using CT = std::chrono::seconds;
  7939 + fields<CT> fds{};
  7940 + date::from_stream(is, fmt, fds, abbrev, offset);
  7941 + if (!fds.ymd.day().ok())
  7942 + is.setstate(std::ios::failbit);
  7943 + if (!is.fail())
  7944 + d = fds.ymd.day();
  7945 + return is;
  7946 +}
  7947 +
  7948 +template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
  7949 +std::basic_istream<CharT, Traits>&
  7950 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, weekday& wd,
  7951 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  7952 + std::chrono::minutes* offset = nullptr)
  7953 +{
  7954 + using CT = std::chrono::seconds;
  7955 + fields<CT> fds{};
  7956 + date::from_stream(is, fmt, fds, abbrev, offset);
  7957 + if (!fds.wd.ok())
  7958 + is.setstate(std::ios::failbit);
  7959 + if (!is.fail())
  7960 + wd = fds.wd;
  7961 + return is;
  7962 +}
  7963 +
  7964 +template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
  7965 +std::basic_istream<CharT, Traits>&
  7966 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year_month& ym,
  7967 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  7968 + std::chrono::minutes* offset = nullptr)
  7969 +{
  7970 + using CT = std::chrono::seconds;
  7971 + fields<CT> fds{};
  7972 + date::from_stream(is, fmt, fds, abbrev, offset);
  7973 + if (!fds.ymd.month().ok())
  7974 + is.setstate(std::ios::failbit);
  7975 + if (!is.fail())
  7976 + ym = fds.ymd.year()/fds.ymd.month();
  7977 + return is;
  7978 +}
  7979 +
  7980 +template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
  7981 +std::basic_istream<CharT, Traits>&
  7982 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month_day& md,
  7983 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  7984 + std::chrono::minutes* offset = nullptr)
  7985 +{
  7986 + using CT = std::chrono::seconds;
  7987 + fields<CT> fds{};
  7988 + date::from_stream(is, fmt, fds, abbrev, offset);
  7989 + if (!fds.ymd.month().ok() || !fds.ymd.day().ok())
  7990 + is.setstate(std::ios::failbit);
  7991 + if (!is.fail())
  7992 + md = fds.ymd.month()/fds.ymd.day();
  7993 + return is;
  7994 +}
  7995 +
  7996 +template <class CharT, class Traits, class Alloc = std::allocator<CharT>>
  7997 +std::basic_istream<CharT, Traits>&
  7998 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
  7999 + year_month_day& ymd, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  8000 + std::chrono::minutes* offset = nullptr)
  8001 +{
  8002 + using CT = std::chrono::seconds;
  8003 + fields<CT> fds{};
  8004 + date::from_stream(is, fmt, fds, abbrev, offset);
  8005 + if (!fds.ymd.ok())
  8006 + is.setstate(std::ios::failbit);
  8007 + if (!is.fail())
  8008 + ymd = fds.ymd;
  8009 + return is;
  8010 +}
  8011 +
  8012 +template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>>
  8013 +std::basic_istream<CharT, Traits>&
  8014 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
  8015 + sys_time<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  8016 + std::chrono::minutes* offset = nullptr)
  8017 +{
  8018 + using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
  8019 + using detail::round_i;
  8020 + std::chrono::minutes offset_local{};
  8021 + auto offptr = offset ? offset : &offset_local;
  8022 + fields<CT> fds{};
  8023 + fds.has_tod = true;
  8024 + date::from_stream(is, fmt, fds, abbrev, offptr);
  8025 + if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
  8026 + is.setstate(std::ios::failbit);
  8027 + if (!is.fail())
  8028 + tp = round_i<Duration>(sys_days(fds.ymd) - *offptr + fds.tod.to_duration());
  8029 + return is;
  8030 +}
  8031 +
  8032 +template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>>
  8033 +std::basic_istream<CharT, Traits>&
  8034 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
  8035 + local_time<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  8036 + std::chrono::minutes* offset = nullptr)
  8037 +{
  8038 + using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
  8039 + using detail::round_i;
  8040 + fields<CT> fds{};
  8041 + fds.has_tod = true;
  8042 + date::from_stream(is, fmt, fds, abbrev, offset);
  8043 + if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
  8044 + is.setstate(std::ios::failbit);
  8045 + if (!is.fail())
  8046 + tp = round_i<Duration>(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration());
  8047 + return is;
  8048 +}
  8049 +
  8050 +template <class Rep, class Period, class CharT, class Traits, class Alloc = std::allocator<CharT>>
  8051 +std::basic_istream<CharT, Traits>&
  8052 +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
  8053 + std::chrono::duration<Rep, Period>& d,
  8054 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  8055 + std::chrono::minutes* offset = nullptr)
  8056 +{
  8057 + using Duration = std::chrono::duration<Rep, Period>;
  8058 + using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
  8059 + using detail::round_i;
  8060 + fields<CT> fds{};
  8061 + date::from_stream(is, fmt, fds, abbrev, offset);
  8062 + if (!fds.has_tod)
  8063 + is.setstate(std::ios::failbit);
  8064 + if (!is.fail())
  8065 + d = round_i<Duration>(fds.tod.to_duration());
  8066 + return is;
  8067 +}
  8068 +
  8069 +template <class Parsable, class CharT, class Traits = std::char_traits<CharT>,
  8070 + class Alloc = std::allocator<CharT>>
  8071 +struct parse_manip
  8072 +{
  8073 + const std::basic_string<CharT, Traits, Alloc> format_;
  8074 + Parsable& tp_;
  8075 + std::basic_string<CharT, Traits, Alloc>* abbrev_;
  8076 + std::chrono::minutes* offset_;
  8077 +
  8078 +public:
  8079 + parse_manip(std::basic_string<CharT, Traits, Alloc> format, Parsable& tp,
  8080 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  8081 + std::chrono::minutes* offset = nullptr)
  8082 + : format_(std::move(format))
  8083 + , tp_(tp)
  8084 + , abbrev_(abbrev)
  8085 + , offset_(offset)
  8086 + {}
  8087 +
  8088 +#if HAS_STRING_VIEW
  8089 + parse_manip(const CharT* format, Parsable& tp,
  8090 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  8091 + std::chrono::minutes* offset = nullptr)
  8092 + : format_(format)
  8093 + , tp_(tp)
  8094 + , abbrev_(abbrev)
  8095 + , offset_(offset)
  8096 + {}
  8097 +
  8098 + parse_manip(std::basic_string_view<CharT, Traits> format, Parsable& tp,
  8099 + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
  8100 + std::chrono::minutes* offset = nullptr)
  8101 + : format_(format)
  8102 + , tp_(tp)
  8103 + , abbrev_(abbrev)
  8104 + , offset_(offset)
  8105 + {}
  8106 +#endif // HAS_STRING_VIEW
  8107 +};
  8108 +
  8109 +template <class Parsable, class CharT, class Traits, class Alloc>
  8110 +std::basic_istream<CharT, Traits>&
  8111 +operator>>(std::basic_istream<CharT, Traits>& is,
  8112 + const parse_manip<Parsable, CharT, Traits, Alloc>& x)
  8113 +{
  8114 + return date::from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_);
  8115 +}
  8116 +
  8117 +template <class Parsable, class CharT, class Traits, class Alloc>
  8118 +inline
  8119 +auto
  8120 +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp)
  8121 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
  8122 + format.c_str(), tp),
  8123 + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp})
  8124 +{
  8125 + return {format, tp};
  8126 +}
  8127 +
  8128 +template <class Parsable, class CharT, class Traits, class Alloc>
  8129 +inline
  8130 +auto
  8131 +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
  8132 + std::basic_string<CharT, Traits, Alloc>& abbrev)
  8133 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
  8134 + format.c_str(), tp, &abbrev),
  8135 + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
  8136 +{
  8137 + return {format, tp, &abbrev};
  8138 +}
  8139 +
  8140 +template <class Parsable, class CharT, class Traits, class Alloc>
  8141 +inline
  8142 +auto
  8143 +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
  8144 + std::chrono::minutes& offset)
  8145 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
  8146 + format.c_str(), tp,
  8147 + std::declval<std::basic_string<CharT, Traits, Alloc>*>(),
  8148 + &offset),
  8149 + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, nullptr, &offset})
  8150 +{
  8151 + return {format, tp, nullptr, &offset};
  8152 +}
  8153 +
  8154 +template <class Parsable, class CharT, class Traits, class Alloc>
  8155 +inline
  8156 +auto
  8157 +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
  8158 + std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
  8159 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
  8160 + format.c_str(), tp, &abbrev, &offset),
  8161 + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
  8162 +{
  8163 + return {format, tp, &abbrev, &offset};
  8164 +}
  8165 +
  8166 +// const CharT* formats
  8167 +
  8168 +template <class Parsable, class CharT>
  8169 +inline
  8170 +auto
  8171 +parse(const CharT* format, Parsable& tp)
  8172 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format, tp),
  8173 + parse_manip<Parsable, CharT>{format, tp})
  8174 +{
  8175 + return {format, tp};
  8176 +}
  8177 +
  8178 +template <class Parsable, class CharT, class Traits, class Alloc>
  8179 +inline
  8180 +auto
  8181 +parse(const CharT* format, Parsable& tp, std::basic_string<CharT, Traits, Alloc>& abbrev)
  8182 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
  8183 + tp, &abbrev),
  8184 + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
  8185 +{
  8186 + return {format, tp, &abbrev};
  8187 +}
  8188 +
  8189 +template <class Parsable, class CharT>
  8190 +inline
  8191 +auto
  8192 +parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset)
  8193 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format,
  8194 + tp, std::declval<std::basic_string<CharT>*>(), &offset),
  8195 + parse_manip<Parsable, CharT>{format, tp, nullptr, &offset})
  8196 +{
  8197 + return {format, tp, nullptr, &offset};
  8198 +}
  8199 +
  8200 +template <class Parsable, class CharT, class Traits, class Alloc>
  8201 +inline
  8202 +auto
  8203 +parse(const CharT* format, Parsable& tp,
  8204 + std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
  8205 + -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
  8206 + tp, &abbrev, &offset),
  8207 + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
  8208 +{
  8209 + return {format, tp, &abbrev, &offset};
  8210 +}
  8211 +
  8212 +// duration streaming
  8213 +
  8214 +template <class CharT, class Traits, class Rep, class Period>
  8215 +inline
  8216 +std::basic_ostream<CharT, Traits>&
  8217 +operator<<(std::basic_ostream<CharT, Traits>& os,
  8218 + const std::chrono::duration<Rep, Period>& d)
  8219 +{
  8220 + return os << detail::make_string<CharT, Traits>::from(d.count()) +
  8221 + detail::get_units<CharT>(typename Period::type{});
  8222 +}
  8223 +
  8224 +} // namespace date
  8225 +
  8226 +#ifdef _MSC_VER
  8227 +# pragma warning(pop)
  8228 +#endif
  8229 +
  8230 +#ifdef __GNUC__
  8231 +# pragma GCC diagnostic pop
  8232 +#endif
  8233 +
  8234 +#endif // DATE_H
  8235 +
... ...
include/errorcode.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_ERRORCODE_H
  23 +#define OSDEV_COMPONENTS_MQTT_ERRORCODE_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +namespace osdev {
  29 +namespace components {
  30 +namespace mqtt {
  31 +
  32 +/*!
  33 + * \return A stringified paho error code...
  34 + */
  35 +std::string pahoAsyncErrorCodeToString( int errorCode );
  36 +
  37 +
  38 +} // End namespace mqtt
  39 +} // End nnamespace components
  40 +} // End namesapce osdev
  41 +
  42 +#endif // OSDEV_COMPONENTS_MQTT_ERRORCODE_H
... ...
include/histogram.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H
  23 +#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H
  24 +
  25 +#include <atomic>
  26 +#include <cmath>
  27 +#include <limits>
  28 +#include <mutex>
  29 +#include <string>
  30 +#include <vector>
  31 +
  32 +#include "utils.h"
  33 +
  34 +#include "ihistogram.h"
  35 +
  36 +namespace osdev {
  37 +namespace components {
  38 +namespace mqtt {
  39 +namespace measurement {
  40 +
  41 +template <typename T>
  42 +double fabs(T val)
  43 +{
  44 + return std::fabs(val);
  45 +}
  46 +
  47 +template <typename Rep, typename Dur>
  48 +double fabs(const std::chrono::duration<Rep, Dur>& dur)
  49 +{
  50 + return std::fabs(dur.count());
  51 +}
  52 +
  53 +template <typename T>
  54 +double to_double(T val)
  55 +{
  56 + return static_cast<double>(val);
  57 +}
  58 +
  59 +template <typename Rep, typename Dur>
  60 +double to_double(const std::chrono::duration<Rep, Dur>& dur)
  61 +{
  62 + return static_cast<double>(dur.count());
  63 +}
  64 +
  65 +template <typename T>
  66 +std::string to_string(T val)
  67 +{
  68 + return std::to_string(val);
  69 +}
  70 +
  71 +template <typename Rep, typename Dur>
  72 +std::string to_string(const std::chrono::duration<Rep, Dur>& dur)
  73 +{
  74 + return std::to_string(dur.count());
  75 +}
  76 +
  77 +template <typename T>
  78 +T min(T)
  79 +{
  80 + return std::numeric_limits<T>::min();
  81 +}
  82 +
  83 +template <typename Rep, typename Dur>
  84 +std::chrono::duration<Rep, Dur> min(const std::chrono::duration<Rep, Dur>&)
  85 +{
  86 + return std::chrono::duration<Rep, Dur>::min();
  87 +}
  88 +
  89 +template <typename T>
  90 +T max(T)
  91 +{
  92 + return std::numeric_limits<T>::max();
  93 +}
  94 +
  95 +template <typename Rep, typename Dur>
  96 +std::chrono::duration<Rep, Dur> max(const std::chrono::duration<Rep, Dur>&)
  97 +{
  98 + return std::chrono::duration<Rep, Dur>::max();
  99 +}
  100 +
  101 +/**
  102 + * @brief This class implements a histogram for typed values.
  103 + * Outlier values to the left are counted on the first bin and to the right on the last bin.
  104 + * @tparam T The value type. T must be default constructable.
  105 + * @tparam Buckets The number of buckets to use for this histogram. Default is 10 buckets.
  106 + */
  107 +template <typename T, std::size_t Buckets = 10>
  108 +class Histogram : public IHistogram
  109 +{
  110 +public:
  111 + /**
  112 + * @brief Construct a histogram.
  113 + * @param id The histogram identification
  114 + * @param minValue The minimum value of the histogram.
  115 + * @param maxValue The maxmim value of the histogram.
  116 + * @param unit The unit of the values that are collected in this histogram.
  117 + */
  118 + Histogram(const std::string& id, T minValue, T maxValue, const char* unit);
  119 +
  120 + /**
  121 + * @brief Set a value in the histogram.
  122 + */
  123 + void setValue(T value);
  124 +
  125 + /**
  126 + * @return true when values are added since the last time a snapshot was taken of the histogram or a reset was performed, false otherwise.
  127 + */
  128 + bool isUpdated() const;
  129 +
  130 + /**
  131 + * @brief Reset the histogram.
  132 + * All counts are made zero and the smallest and largest recorded values are set to default.
  133 + */
  134 + void reset();
  135 +
  136 + // IHistogram implementation
  137 + virtual const std::string& id() const override;
  138 + virtual std::size_t numBuckets() const override;
  139 + virtual double bucketWidth() const override;
  140 + virtual std::string minValueString() const override;
  141 + virtual std::string maxValueString() const override;
  142 + virtual const std::string& unit() const override;
  143 + virtual HistogramData histogramData() const override;
  144 + virtual std::size_t numberOfValuesSinceLastClear() const override;
  145 + virtual std::string smallestValueSinceLastClear() const override;
  146 + virtual std::string largestValueSinceLastClear() const override;
  147 + virtual std::string averageValueSinceLastClear() const override;
  148 + virtual void clearRunningValues() override;
  149 + virtual std::string toString() const override;
  150 +
  151 +private:
  152 + /**
  153 + * @brief Get the index on which to count the given value.
  154 + * The outliers to the left are counted on index 0 and to the right on index size() - 1.
  155 + * @param value The value to get the index for.
  156 + * @return the index in the histogram on which to count the given value.
  157 + */
  158 + inline std::size_t index(T value)
  159 + {
  160 + if (value > m_maxValue) {
  161 + return m_histogram.size() - 1;
  162 + }
  163 + if (value < m_minValue) {
  164 + return 0;
  165 + }
  166 +
  167 + return 1 + static_cast<std::size_t>(to_double((value - m_minValue) / m_bucketWidth));
  168 + }
  169 +
  170 + const std::string m_id;
  171 + const std::string m_unit;
  172 + const T m_minValue;
  173 + const T m_maxValue;
  174 + const double m_bucketWidth;
  175 + T m_smallestValue;
  176 + T m_largestValue;
  177 + T m_smallestValueSinceLastClear;
  178 + T m_largestValueSinceLastClear;
  179 + T m_summedValueSinceLastClear;
  180 + std::size_t m_numberOfValuesSinceLastClear;
  181 + mutable std::mutex m_mutex;
  182 + std::vector<std::size_t> m_histogram;
  183 + mutable std::atomic_bool m_isUpdated;
  184 +};
  185 +
  186 +template <typename T, std::size_t Buckets>
  187 +Histogram<T, Buckets>::Histogram(const std::string& _id, T minValue, T maxValue, const char* _unit)
  188 + : m_id(_id)
  189 + , m_unit(_unit)
  190 + , m_minValue(minValue)
  191 + , m_maxValue(maxValue)
  192 + , m_bucketWidth(fabs((m_maxValue - m_minValue)) / Buckets)
  193 + , m_smallestValue{ max(maxValue) }
  194 + , m_largestValue{ min(maxValue) }
  195 + , m_smallestValueSinceLastClear{ max(maxValue) }
  196 + , m_largestValueSinceLastClear{ min(maxValue) }
  197 + , m_summedValueSinceLastClear{}
  198 + , m_numberOfValuesSinceLastClear{}
  199 + , m_mutex()
  200 + , m_histogram(Buckets + 2) // + 2 for the out of bound buckets
  201 + , m_isUpdated(false)
  202 +{
  203 +}
  204 +
  205 +template <typename T, std::size_t Buckets>
  206 +void Histogram<T, Buckets>::setValue(T value)
  207 +{
  208 + std::lock_guard<std::mutex> lg(m_mutex);
  209 + apply_unused_parameters(lg);
  210 +
  211 + if (value > m_largestValueSinceLastClear) {
  212 + m_largestValueSinceLastClear = value;
  213 + }
  214 +
  215 + if (value < m_smallestValueSinceLastClear) {
  216 + m_smallestValueSinceLastClear = value;
  217 + }
  218 +
  219 + if (value > m_largestValue) {
  220 + m_largestValue = value;
  221 + }
  222 +
  223 + if (value < m_smallestValue) {
  224 + m_smallestValue = value;
  225 + }
  226 +
  227 + m_summedValueSinceLastClear += value;
  228 + ++m_numberOfValuesSinceLastClear;
  229 +
  230 + m_histogram[this->index(value)]++;
  231 + m_isUpdated = true;
  232 +}
  233 +
  234 +template <typename T, std::size_t Buckets>
  235 +bool Histogram<T, Buckets>::isUpdated() const
  236 +{
  237 + return m_isUpdated;
  238 +}
  239 +
  240 +template <typename T, std::size_t Buckets>
  241 +void Histogram<T, Buckets>::reset()
  242 +{
  243 + std::lock_guard<std::mutex> lg(m_mutex);
  244 + apply_unused_parameters(lg);
  245 +
  246 + m_smallestValue = T{ max(m_maxValue) };
  247 + m_largestValue = T{ min(m_maxValue) };
  248 + m_smallestValueSinceLastClear = T{ max(m_maxValue) };
  249 + m_largestValueSinceLastClear = T{ min(m_maxValue) };
  250 + m_summedValueSinceLastClear = T{};
  251 + m_numberOfValuesSinceLastClear = 0;
  252 +
  253 + std::fill(m_histogram.begin(), m_histogram.end(), 0);
  254 + m_isUpdated = false;
  255 +}
  256 +
  257 +template <typename T, std::size_t Buckets>
  258 +const std::string& Histogram<T, Buckets>::id() const
  259 +{
  260 + return m_id;
  261 +}
  262 +
  263 +template <typename T, std::size_t Buckets>
  264 +std::size_t Histogram<T, Buckets>::numBuckets() const
  265 +{
  266 + return Buckets;
  267 +}
  268 +
  269 +template <typename T, std::size_t Buckets>
  270 +double Histogram<T, Buckets>::bucketWidth() const
  271 +{
  272 + return m_bucketWidth;
  273 +}
  274 +
  275 +template <typename T, std::size_t Buckets>
  276 +std::string Histogram<T, Buckets>::minValueString() const
  277 +{
  278 + return to_string(m_minValue);
  279 +}
  280 +
  281 +template <typename T, std::size_t Buckets>
  282 +std::string Histogram<T, Buckets>::maxValueString() const
  283 +{
  284 + return to_string(m_maxValue);
  285 +}
  286 +
  287 +template <typename T, std::size_t Buckets>
  288 +const std::string& Histogram<T, Buckets>::unit() const
  289 +{
  290 + return m_unit;
  291 +}
  292 +
  293 +template <typename T, std::size_t Buckets>
  294 +HistogramData Histogram<T, Buckets>::histogramData() const
  295 +{
  296 + std::lock_guard<std::mutex> lg(m_mutex);
  297 + apply_unused_parameters(lg);
  298 +
  299 + m_isUpdated = false;
  300 + return HistogramData(to_string(m_smallestValue), to_string(m_largestValue),
  301 + to_string(m_smallestValueSinceLastClear), to_string(m_largestValueSinceLastClear), averageValueSinceLastClear(),
  302 + m_numberOfValuesSinceLastClear, m_histogram);
  303 +}
  304 +
  305 +template <typename T, std::size_t Buckets>
  306 +std::size_t Histogram<T, Buckets>::numberOfValuesSinceLastClear() const
  307 +{
  308 + return m_numberOfValuesSinceLastClear;
  309 +}
  310 +
  311 +template <typename T, std::size_t Buckets>
  312 +std::string Histogram<T, Buckets>::smallestValueSinceLastClear() const
  313 +{
  314 + return to_string(m_smallestValueSinceLastClear);
  315 +}
  316 +
  317 +template <typename T, std::size_t Buckets>
  318 +std::string Histogram<T, Buckets>::largestValueSinceLastClear() const
  319 +{
  320 + return to_string(m_largestValueSinceLastClear);
  321 +}
  322 +
  323 +template <typename T, std::size_t Buckets>
  324 +std::string Histogram<T, Buckets>::averageValueSinceLastClear() const
  325 +{
  326 + if (0 == m_numberOfValuesSinceLastClear) {
  327 + return "undefined";
  328 + }
  329 + return to_string(to_double(m_summedValueSinceLastClear) / m_numberOfValuesSinceLastClear);
  330 +}
  331 +
  332 +template <typename T, std::size_t Buckets>
  333 +void Histogram<T, Buckets>::clearRunningValues()
  334 +{
  335 + std::lock_guard<std::mutex> lg(m_mutex);
  336 + apply_unused_parameters(lg);
  337 +
  338 + m_smallestValueSinceLastClear = T{ max(m_maxValue) };
  339 + m_largestValueSinceLastClear = T{ min(m_maxValue) };
  340 + m_summedValueSinceLastClear = T{};
  341 + m_numberOfValuesSinceLastClear = 0;
  342 +}
  343 +
  344 +template <typename T, std::size_t Buckets>
  345 +std::string Histogram<T, Buckets>::toString() const
  346 +{
  347 + return visualizeHistogram(*this);
  348 +}
  349 +
  350 +} // End namespace measurement
  351 +} // End namespace mqtt
  352 +} // End namespace components
  353 +} // End namespace osdev
  354 +
  355 +#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H
... ...
include/histogramprovider.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H
  23 +#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H
  24 +
  25 +// std
  26 +#include <iostream>
  27 +#include <memory>
  28 +#include <string>
  29 +#include <unordered_map>
  30 +
  31 +#include "compat-c++14.h"
  32 +
  33 +// mlogic::common
  34 +#include "sharedreaderlock.h"
  35 +
  36 +#include "histogram.h"
  37 +
  38 +namespace osdev {
  39 +namespace components {
  40 +namespace mqtt {
  41 +namespace measurement {
  42 +
  43 +/**
  44 + * @brief This class provides the user with Entry class for putting measurements in a histogram.
  45 + * @tparam TContext A tag type that is used to give the HistogramProvider a user defined context.
  46 + * @tparam TValue Defines the value type that the histograms of this provider use.
  47 + * @tparam Buckets The number of buckets to use on creation of a Histogram. The default number of buckets is 10.
  48 + *
  49 + * The TValue type must define the following publically visible members.
  50 + *
  51 + * value_type - The underlying value type used in the histogram
  52 + * const value_type minValue - The minimum value of the histogram
  53 + * const value_type maxValue - The maximum value of the histogram
  54 + * const char* unit - The value unit.
  55 + */
  56 +template <typename TContext, typename TValue, std::size_t Buckets = 10>
  57 +class HistogramProvider
  58 +{
  59 +public:
  60 + using HistogramType = Histogram<typename TValue::value_type, Buckets>;
  61 +
  62 + /**
  63 + * @brief Get the HistogramProvider instance.
  64 + */
  65 + static HistogramProvider& instance();
  66 +
  67 + /**
  68 + * @brief Add a value to the histogram identified by id.
  69 + * @param id Identifies the histogram to add the value to.
  70 + * @param value The value to add.
  71 + */
  72 + void addValue(const std::string& id, typename TValue::value_type value);
  73 +
  74 + /**
  75 + * @brief Log the histogram identified by id via the ILogger framework.
  76 + * @param id The histogram to log. If no histogram is found no work is done.
  77 + */
  78 + void log(const std::string& id);
  79 +
  80 + /**
  81 + * @brief Log the histograms via the ILogger framework.
  82 + * @param onlyChanged When set to true only log histograms that have changed. Default is false.
  83 + */
  84 + void logAll(bool onlyChanged = false);
  85 +
  86 + /**
  87 + * @brief Reset all the histograms.
  88 + * @param logBeforeReset Flag that indicates whether the histogram needs to be logged before reset. Default is true.
  89 + * @note Only changed histograms are logged.
  90 + */
  91 + void resetAll(bool logBeforeReset = true);
  92 +
  93 + /**
  94 + * @return The logOnDestroy flag value.
  95 + */
  96 + bool logOnDestroy() const { return m_logOnDestroy; }
  97 +
  98 + /**
  99 + * @brief Set the logOnDestroy flag.
  100 + * @param logOnDest Log on destruction when true, do not log when set to false.
  101 + */
  102 + void setLogOnDestroy(bool logOnDest) { m_logOnDestroy = logOnDest; }
  103 +
  104 +private:
  105 + HistogramProvider();
  106 +
  107 + /**
  108 + * @brief On destruction the provider will log all its histogram information to stdout.
  109 + * Since the destruction will happen as one of the last things in the process, stdout is
  110 + * used for logging.
  111 + */
  112 + ~HistogramProvider();
  113 +
  114 + void log(const HistogramType& hist, bool onlyChanged);
  115 +
  116 + SharedReaderLock m_sharedLock;
  117 + std::unordered_map<std::string, std::unique_ptr<HistogramType>> m_histograms;
  118 + bool m_logOnDestroy; ///< Default is true.
  119 +};
  120 +
  121 +// static
  122 +template <typename TContext, typename TValue, std::size_t Buckets>
  123 +HistogramProvider<TContext, TValue, Buckets>& HistogramProvider<TContext, TValue, Buckets>::instance()
  124 +{
  125 + static HistogramProvider<TContext, TValue, Buckets> s_provider;
  126 + return s_provider;
  127 +}
  128 +
  129 +template <typename TContext, typename TValue, std::size_t Buckets>
  130 +void HistogramProvider<TContext, TValue, Buckets>::addValue(const std::string& id, typename TValue::value_type value)
  131 +{
  132 + OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
  133 + auto it = m_histograms.find(id);
  134 + if (m_histograms.end() == it) {
  135 + OSDEV_COMPONENTS_EXCLUSIVELOCK_SCOPE(m_sharedLock);
  136 + constexpr TValue val;
  137 + it = (m_histograms.emplace(std::make_pair(id, std::make_unique<HistogramType>(id, val.minValue, val.maxValue, val.unit)))).first;
  138 + }
  139 + it->second->setValue(value);
  140 +}
  141 +
  142 +template <typename TContext, typename TValue, std::size_t Buckets>
  143 +void HistogramProvider<TContext, TValue, Buckets>::log(const std::string& id)
  144 +{
  145 + OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
  146 + auto it = m_histograms.find(id);
  147 + if (m_histograms.end() != it) {
  148 + log(*(it->second), false);
  149 + }
  150 +}
  151 +
  152 +template <typename TContext, typename TValue, std::size_t Buckets>
  153 +void HistogramProvider<TContext, TValue, Buckets>::logAll(bool onlyChanged)
  154 +{
  155 + OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
  156 + for (const auto& h : m_histograms) {
  157 + log(*h.second, onlyChanged);
  158 + }
  159 +}
  160 +
  161 +template <typename TContext, typename TValue, std::size_t Buckets>
  162 +void HistogramProvider<TContext, TValue, Buckets>::resetAll(bool logBeforeReset)
  163 +{
  164 + OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
  165 + for (const auto& h : m_histograms) {
  166 + if (logBeforeReset) {
  167 + log(*h.second, true); // only log the histograms that have changed
  168 + }
  169 + h.second->reset();
  170 + }
  171 +}
  172 +
  173 +template <typename TContext, typename TValue, std::size_t Buckets>
  174 +HistogramProvider<TContext, TValue, Buckets>::HistogramProvider()
  175 + : m_sharedLock()
  176 + , m_histograms()
  177 + , m_logOnDestroy(true)
  178 +{
  179 +}
  180 +
  181 +template <typename TContext, typename TValue, std::size_t Buckets>
  182 +HistogramProvider<TContext, TValue, Buckets>::~HistogramProvider()
  183 +{
  184 + if (m_logOnDestroy) {
  185 + for (const auto& h : m_histograms) {
  186 + std::cout << *h.second << std::endl;
  187 + }
  188 + }
  189 +}
  190 +
  191 +template <typename TContext, typename TValue, std::size_t Buckets>
  192 +void HistogramProvider<TContext, TValue, Buckets>::log(const HistogramType& hist, bool onlyChanged)
  193 +{
  194 + if ((onlyChanged && hist.isUpdated()) || !onlyChanged) {
  195 + MLOGIC_COMMON_INFO("HistogramProvider", "%1", hist);
  196 + }
  197 +}
  198 +
  199 +} // End namespace measurement
  200 +} // End namespace mqtt
  201 +} // End namespace components
  202 +} // End namespace osdev
  203 +
  204 +#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H
... ...
include/ihistogram.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_IHISTOGRAM_H
  23 +#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_IHISTOGRAM_H
  24 +
  25 +// std
  26 +#include <ostream>
  27 +#include <string>
  28 +#include <vector>
  29 +
  30 +namespace osdev {
  31 +namespace components {
  32 +namespace mqtt {
  33 +namespace measurement {
  34 +
  35 +/**
  36 + * @brief Class that holds the dynamic part of a histogram.
  37 + * Used to take a snapshot of the histogram.
  38 + */
  39 +class HistogramData
  40 +{
  41 +public:
  42 + /**
  43 + * @brief Construct dynamic histrogram data.
  44 + * @param smallestValue A stringified version of the smallest recorded value since the last update.
  45 + * @param largestValue A stringified version of the largest recorded value since the last update.
  46 + * @param smallestValueSinceLastClear A stringified version of the smallest value as string since last clear.
  47 + * @param largestValueSinceLastClear A stringified version of the largest value as string since last clear.
  48 + * @param averageValueSinceLastClear A stringified version of the average value as string since last clear.
  49 + * @param nrOfValuesSinceLastClear Number of values since last clear.
  50 + * @param histCounts The histogram counts.
  51 + */
  52 + HistogramData(const std::string& smallestValue, const std::string& largestValue,
  53 + const std::string& smallestValueSinceLastClear,
  54 + const std::string& largestValueSinceLastClear,
  55 + const std::string& averageValueSinceLastClear,
  56 + std::size_t nrOfValuesSinceLastClear,
  57 + const std::vector<std::size_t>& histCounts)
  58 + : m_smallestValueString(smallestValue)
  59 + , m_largestValueString(largestValue)
  60 + , m_smallestValueSinceLastClearString(smallestValueSinceLastClear)
  61 + , m_largestValueSinceLastClearString(largestValueSinceLastClear)
  62 + , m_averageValueSinceLastClearString(averageValueSinceLastClear)
  63 + , m_numberOfValuesSinceLastClear(nrOfValuesSinceLastClear)
  64 + , m_data(histCounts)
  65 + {
  66 + }
  67 +
  68 + /**
  69 + * @return The smallest recorded value as a string.
  70 + */
  71 + const std::string& smallestValueString() const
  72 + {
  73 + return m_smallestValueString;
  74 + }
  75 +
  76 + /**
  77 + * @return The largest recorded value as a string.
  78 + */
  79 + const std::string& largestValueString() const
  80 + {
  81 + return m_largestValueString;
  82 + }
  83 +
  84 + /**
  85 + * @return The number of value since last clear.
  86 + */
  87 + std::size_t numberOfValuesSinceLastClear() const
  88 + {
  89 + return m_numberOfValuesSinceLastClear;
  90 + }
  91 +
  92 + /**
  93 + * @return The smallest value since last clear as a string.
  94 + */
  95 + const std::string& smallestValueSinceLastClearString() const
  96 + {
  97 + return m_smallestValueSinceLastClearString;
  98 + }
  99 +
  100 + /**
  101 + * @return The largest value since last clear as a string.
  102 + */
  103 + const std::string& largestValueSinceLastClearString() const
  104 + {
  105 + return m_largestValueSinceLastClearString;
  106 + }
  107 +
  108 + /**
  109 + * @return The average value since last clear as a string.
  110 + */
  111 + const std::string& averageValueSinceLastClearString() const
  112 + {
  113 + return m_averageValueSinceLastClearString;
  114 + }
  115 +
  116 + /**
  117 + * @return The histogram counts.
  118 + */
  119 + const std::vector<std::size_t>& data() const
  120 + {
  121 + return m_data;
  122 + }
  123 +
  124 +private:
  125 + std::string m_smallestValueString;
  126 + std::string m_largestValueString;
  127 + std::string m_smallestValueSinceLastClearString;
  128 + std::string m_largestValueSinceLastClearString;
  129 + std::string m_averageValueSinceLastClearString;
  130 + std::size_t m_numberOfValuesSinceLastClear;
  131 + std::vector<std::size_t> m_data;
  132 +};
  133 +
  134 +/**
  135 + * @brief Interface for histogram classes
  136 + * Can be used to store histograms in a container and visualize the histogram.
  137 + */
  138 +class IHistogram
  139 +{
  140 +public:
  141 + virtual ~IHistogram();
  142 +
  143 + /**
  144 + * @return The histogram identification (title).
  145 + */
  146 + virtual const std::string& id() const = 0;
  147 +
  148 + /**
  149 + * @return The number of buckets.
  150 + */
  151 + virtual std::size_t numBuckets() const = 0;
  152 +
  153 + /**
  154 + * @return The bucket width as a floating point value.
  155 + */
  156 + virtual double bucketWidth() const = 0;
  157 +
  158 + /**
  159 + * @return The minimum value of the histogram definition as a string.
  160 + */
  161 + virtual std::string minValueString() const = 0;
  162 +
  163 + /**
  164 + * @return The maximum value of the histogram definition as a string.
  165 + */
  166 + virtual std::string maxValueString() const = 0;
  167 +
  168 + /**
  169 + * @return The value unit as a a string.
  170 + */
  171 + virtual const std::string& unit() const = 0;
  172 +
  173 + /**
  174 + * @return The histogram data.
  175 + * This takes a snapshot of the histogram data.
  176 + */
  177 + virtual HistogramData histogramData() const = 0;
  178 +
  179 + /**
  180 + * @return The number of value since last clear.
  181 + */
  182 + virtual std::size_t numberOfValuesSinceLastClear() const = 0;
  183 +
  184 + /**
  185 + * @return The smallest value since last clear as a string.
  186 + */
  187 + virtual std::string smallestValueSinceLastClear() const = 0;
  188 +
  189 + /**
  190 + * @return The largest value since last clear as a string.
  191 + */
  192 + virtual std::string largestValueSinceLastClear() const = 0;
  193 +
  194 + /**
  195 + * @return The average value since last clear as a string.
  196 + */
  197 + virtual std::string averageValueSinceLastClear() const = 0;
  198 +
  199 + /**
  200 + * @brief Clears the values that are kept between successive clearRunningValues calls.
  201 + * These are:
  202 + * smallestValueSinceLastClear
  203 + * largestValueSinceLastClear
  204 + * averageValueSinceLastClear
  205 + */
  206 + virtual void clearRunningValues() = 0;
  207 +
  208 + /**
  209 + * @return The ascii representation of the histogram.
  210 + */
  211 + virtual std::string toString() const = 0;
  212 +};
  213 +
  214 +/**
  215 + * @brief Make an ascii visualisation of the given histogram data.
  216 + * @param histogram The histogram to visualize.
  217 + */
  218 +std::string visualizeHistogram(const IHistogram& histogram);
  219 +
  220 +/**
  221 + * @brief Stream operator for IHistogram instances.
  222 + */
  223 +std::ostream& operator<<(std::ostream& os, const IHistogram& rhs);
  224 +
  225 +} // End namespace measurement
  226 +} // End namespace mqtt
  227 +} // End namespace components
  228 +} // End namespace osdev
  229 +
  230 +#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_IHISTOGRAM_H
... ...
include/imqttclient.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_IMQTTCLIENT_H
  23 +#define OSDEV_COMPONENTS_MQTT_IMQTTCLIENT_H
  24 +
  25 +// std
  26 +#include <chrono>
  27 +#include <functional>
  28 +#include <set>
  29 +#include <string>
  30 +
  31 +// boost
  32 +#include <boost/optional.hpp>
  33 +
  34 +// mlogic::client
  35 +#include "istatecallback.h"
  36 +
  37 +// mlogic::mqtt
  38 +#include "connectionstatus.h"
  39 +#include "credentials.h"
  40 +#include "mqttmessage.h"
  41 +#include "token.h"
  42 +
  43 +namespace osdev {
  44 +namespace components {
  45 +namespace mqtt {
  46 +
  47 +/**
  48 + * @brief Interface that describes the minimal interface that a wrapper implementation needs to have in
  49 + * order to coorporate with the MqttClient class.
  50 + */
  51 +class IMqttClient : public virtual IStateCallback
  52 +{
  53 +public:
  54 + virtual ~IMqttClient() {}
  55 +
  56 + /**
  57 + * @brief Connect to the endpoint
  58 + * @param host The host name or ip address.
  59 + * @param port The port to use.
  60 + * @param credentials The credentials to use.
  61 + */
  62 + virtual void connect(const std::string& host, int port, const Credentials& credentials) = 0;
  63 +
  64 + /**
  65 + * @brief Connect to the endpoint
  66 + * @param endpoint an uri endpoint description.
  67 + */
  68 + virtual void connect(const std::string& endpoint) = 0;
  69 +
  70 + /**
  71 + * @brief Disconnect the client from the broker
  72 + */
  73 + virtual void disconnect() = 0;
  74 +
  75 + /**
  76 + * @brief Publish a message with a given quality of service (0, 1 or 2).
  77 + * @param message The message to publish.
  78 + * @param qos The quality of service to use.
  79 + * @return The token that identifies the publication (used in the deliveryCompleteCallback).
  80 + */
  81 + virtual Token publish(const MqttMessage& message, int qos) = 0;
  82 +
  83 + /**
  84 + * @brief Subscribe to a topic(filter).
  85 + * The combination of topic and qos makes a subscription unique on a wrapper client. When a topic has overlap
  86 + * with an existing subscription it is guaranteed that the given callback is only called once.
  87 + * @param topic The topic to subscribe to (can have wildcards). The topic can have overlap with existing topic subscriptions.
  88 + * @param qos The quality of service to use (0, 1 or 2).
  89 + * @param cb The callback that is called when messages are received for this subscription.
  90 + * @return A token that identifies the subscribe command.
  91 + */
  92 + virtual Token subscribe(const std::string& topic, int qos, const std::function<void(MqttMessage)>& cb) = 0;
  93 +
  94 + /**
  95 + * @brief Unsubscribe an existing subscription.
  96 + * The combination of topic and qos can occur on multiple wrapper clients. All subscriptions that match are unsubscribed.
  97 + * @param topic The topic to unsubscribe.
  98 + * @param qos The quality of service of the subscription (0, 1 or 2).
  99 + * @return A set of unsubscribe tokens identifying the unsubscribe commands. Usually the set will contain only one item.
  100 + */
  101 + virtual std::set<Token> unsubscribe(const std::string& topic, int qos) = 0;
  102 +
  103 + /**
  104 + * @brief Wait for all commands to complete.
  105 + * @param waitFor The number of milliseconds to wait for completetion of all commands.
  106 + * @return True when all commands have completed in time or false if not.
  107 + */
  108 + virtual bool waitForCompletion(std::chrono::milliseconds waitFor) const = 0;
  109 +
  110 + /**
  111 + * @brief Wait for a single command to complete.
  112 + * @param waitFor The number of milliseconds to wait for completetion of the command.
  113 + * @param token The token to wait for.
  114 + * @return True when the command has completed in time or false if not.
  115 + * @note Non existent tokens are also reported as completed.
  116 + */
  117 + virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const Token& token) const = 0;
  118 +
  119 + /**
  120 + * @brief Wait for commands to complete.
  121 + * This method enables the user to wait for a set of commands. An empty set means to wait for all commands to complete.
  122 + * @param waitFor The number of milliseconds to wait for completetion of all commands.
  123 + * @param tokens The tokens to wait for. An empty set means to wait for all commands to complete.
  124 + * @return True when the commands have completed in time or false if not.
  125 + * @note Non existent tokens are also reported as completed.
  126 + */
  127 + virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const std::set<Token>& tokens) const = 0;
  128 +
  129 + /**
  130 + * @brief Get the result of a command.
  131 + * @param token The token identifying the result.
  132 + * @return The command result when available.
  133 + */
  134 + virtual boost::optional<bool> commandResult(const Token& token) const = 0;
  135 +
  136 + /**
  137 + * @return The endpoint uri.
  138 + */
  139 + virtual std::string endpoint() const = 0;
  140 +};
  141 +
  142 +} // End namespace mqtt
  143 +} // End namespace components
  144 +} // End namespace osdev
  145 +
  146 +#endif // OSDEV_COMPONENTS_MQTT_IMQTTCLIENT_H
... ...
include/imqttclientimpl.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_IMQTTCLIENTIMPL_H
  23 +#define OSDEV_COMPONENTS_MQTT_IMQTTCLIENTIMPL_H
  24 +
  25 +// std
  26 +#include <chrono>
  27 +#include <functional>
  28 +#include <ostream>
  29 +#include <set>
  30 +#include <string>
  31 +#include <vector>
  32 +
  33 +// boost
  34 +#include <boost/optional.hpp>
  35 +
  36 +// mlogic::mqtt
  37 +#include "connectionstatus.h"
  38 +#include "mqttmessage.h"
  39 +
  40 +namespace osdev {
  41 +namespace components {
  42 +namespace mqtt {
  43 +
  44 +/**
  45 + * @brief Interface that describes the minimal interface that a wrapper implementation needs to have in
  46 + * order to coorporate with the MqttClient class.
  47 + */
  48 +class IMqttClientImpl
  49 +{
  50 +public:
  51 + virtual ~IMqttClientImpl();
  52 +
  53 + /**
  54 + * @return id of this client.
  55 + */
  56 + virtual std::string clientId() const = 0;
  57 +
  58 + /**
  59 + * @return The connection status of the wrapper.
  60 + */
  61 + virtual ConnectionStatus connectionStatus() const = 0;
  62 +
  63 + /**
  64 + * @brief Connect the wrapper to the endpoint.
  65 + * @param wait A flag that indicates if the method should wait for a succesful connection.
  66 + * @return the operation token.
  67 + */
  68 + virtual std::int32_t connect(bool wait) = 0;
  69 +
  70 + /**
  71 + * @brief Disconnect the wrapper.
  72 + * @param wait A flag that indicates if the method should wait for a succesful disconnect. When true the method will wait for the timeoutMs value plus some additional time.
  73 + * @param timeoutMs A timeout in milliseconds. The timeout is used to give in flight messages a chance to get delivered. After the timeout the disconnect command is sent.
  74 + * @return the operation token.
  75 + */
  76 + virtual std::int32_t disconnect(bool wait, int timeoutMs) = 0;
  77 +
  78 + /**
  79 + * @brief Publish a message to an mqtt endpoint with a given quality of service.
  80 + * When the connection is in state reconnect the publish is saved so that it can be published later.
  81 + * @param message The message to send.
  82 + * @param qos The quality of service to use (0, 1 or 2).
  83 + * @return The message token. This token identifies the publication.
  84 + */
  85 + virtual std::int32_t publish(const MqttMessage& message, int qos) = 0;
  86 +
  87 + /**
  88 + * @brief Publish messages when client is reconnected.
  89 + */
  90 + virtual void publishPending() = 0;
  91 +
  92 + /**
  93 + * @brief Subscribe to a topic(filter).
  94 + * The combination of topic and qos makes a subscription unique. Subscriptions with the same topic filter
  95 + * but different qos are considered different, but they will overlap and cannot exist in the same wrapper.
  96 + * @param topic The topic to subscribe to (can have wildcards). The topic cannot have overlap with existing topic subscriptions.
  97 + * @param qos The quality of service to use (0, 1 or 2).
  98 + * @param cb The callback that is called when messages are received for this subscription.
  99 + * @return the operation token.
  100 + */
  101 + virtual std::int32_t subscribe(const std::string& topic, int qos, const std::function<void(MqttMessage msg)>& cb) = 0;
  102 +
  103 + /**
  104 + * @brief Resubscribe existing topicfilters.
  105 + * This method should be called only after a reconnect when subscriptions need to be reinstated.
  106 + */
  107 + virtual void resubscribe() = 0;
  108 +
  109 + /**
  110 + * @brief Unsubscribe an existing subscription.
  111 + * The combination of topic and qos make a subscription unique.
  112 + * @param topic The topic to unsubscribe.
  113 + * @param qos The quality of service of the subscription (0, 1 or 2).
  114 + * @return the operation token.
  115 + */
  116 + virtual std::int32_t unsubscribe(const std::string& topic, int qos) = 0;
  117 +
  118 + /**
  119 + * @brief Unsubscribe all subscriptions.
  120 + */
  121 + virtual void unsubscribeAll() = 0;
  122 +
  123 + /**
  124 + * @brief Wait for commands to complete.
  125 + * This method enables the user to wait for a set of tokens. An empty set means wait for all operations to complete.
  126 + * @param waitFor The number of milliseconds to wait for completetion of all commands.
  127 + * @param tokens The set of tokens to wait for.
  128 + * @return The number of milliseconds left.
  129 + */
  130 + virtual std::chrono::milliseconds waitForCompletion(std::chrono::milliseconds waitFor, const std::set<std::int32_t>& tokens) const = 0;
  131 +
  132 + /**
  133 + * @brief Check if a topic overlaps with existing topic subscriptions.
  134 + * @param topic The topic to test.
  135 + * @return true when overlap is detected, false otherwise.
  136 + */
  137 + virtual bool isOverlapping(const std::string& topic) const = 0;
  138 +
  139 + /**
  140 + * @brief Check if a topic overlaps with existing topic subscriptions.
  141 + * @param topic The topic to test.
  142 + * @param[out] existingTopic Contains the topic on which overlap is detected.
  143 + * @return true when overlap is detected. The existingTopic contains the topic on which overlap is detected, false otherwise.
  144 + */
  145 + virtual bool isOverlapping(const std::string& topic, std::string& existingTopic) const = 0;
  146 +
  147 + /**
  148 + * @return A vector with the pending operation tokens.
  149 + */
  150 + virtual std::vector<std::int32_t> pendingOperations() const = 0;
  151 +
  152 + /**
  153 + * @return true when client wrapper has pending subscriptions, false otherwise.
  154 + */
  155 + virtual bool hasPendingSubscriptions() const = 0;
  156 +
  157 + /**
  158 + * @return The operation result.
  159 + * @retval true operation has succeeded.
  160 + * @retval false operation has failed.
  161 + */
  162 + virtual boost::optional<bool> operationResult(std::int32_t token) const = 0;
  163 +};
  164 +
  165 +} // End namespace mqtt
  166 +} // End namespace components
  167 +} // End namespace osdev
  168 +
  169 +#endif // OSDEV_COMPONENTS_MQTT_IMQTTCLIENTIMPL_H
... ...
include/istatecallback.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_ISTATECALLBACK_H
  23 +#define OSDEV_COMPONENTS_MQTT_ISTATECALLBACK_H
  24 +
  25 +// std
  26 +#include <ostream>
  27 +
  28 +// boost
  29 +#include <boost/signals2.hpp>
  30 +
  31 +namespace osdev {
  32 +namespace components {
  33 +namespace mqtt {
  34 +
  35 +/*!
  36 + * \brief Struct introduces a typed client identifier.
  37 + */
  38 +struct ClientIdentifier
  39 +{
  40 + /*!
  41 + * \brief Construct a ClientIdentifier
  42 + * \param id The client id. Default is empty.
  43 + */
  44 + explicit ClientIdentifier(const std::string& id = "")
  45 + : m_id(id)
  46 + {
  47 + }
  48 +
  49 + const std::string m_id; ///< Client identifier
  50 +};
  51 +
  52 +/*!
  53 + * \brief Enumeration of state codes.
  54 + * The states Unknown, CommunicationFailure, GeneralFailure, Good
  55 + * and Shutdown are used to communicate state about the server
  56 + * that is connected to. The states ConnectionFailure and Unregister
  57 + * communicate state about the client.
  58 + */
  59 +enum class StateEnum
  60 +{
  61 + Unknown, ///< State of underlying data source is unknown.
  62 + CommunicationFailure, ///< No communication with underlying data source.
  63 + GeneralFailure, ///< Some failure in the underlying data source.
  64 + Good, ///< Underlying data source is available.
  65 + Shutdown, ///< Underlying data source is shutting down.
  66 + ConnectionFailure, ///< Client connection has failed.
  67 + Unregister ///< Client is being unregistered.
  68 +};
  69 +
  70 +/*!
  71 + * \brief Stream a StateEnum value
  72 + */
  73 +std::ostream& operator<<(std::ostream& os, StateEnum rhs);
  74 +
  75 +/*!
  76 + * \brief Type for identifying state change callbacks.
  77 + */
  78 +using StateChangeCallbackHandle = std::uint32_t;
  79 +
  80 +class IStateCallback;
  81 +
  82 +/*!
  83 + * \brief Create an id for an IStateCallback object.
  84 + * \param idStatic - Static part of the id (name of the class).
  85 + * \param clientId - Client identifier. Can contain information on how
  86 + * the client is used for instance. If the id is empty
  87 + * it is not used.
  88 + * \param idDynamic - Dynamic part (object address).
  89 + * \return identifier as string
  90 + */
  91 +std::string createIdentifier(const std::string& idStatic, const ClientIdentifier& clientId, const IStateCallback* idDynamic);
  92 +
  93 +/*!
  94 + * \brief State callback interface.
  95 + * When a client implements this interface it can be registered on a
  96 + * server in order to handle state changes from the underlying data source.
  97 + * \note When the client is destroyed it is expected that it unregisters itself by calling
  98 + * clearAllStateChangeCallbacks().
  99 + */
  100 +class IStateCallback
  101 +{
  102 +public:
  103 + using SigStateChange = boost::signals2::signal<void(const IStateCallback*, StateEnum)>;
  104 + using SlotStateChange = SigStateChange::slot_type;
  105 +
  106 + virtual ~IStateCallback();
  107 +
  108 + /*!
  109 + * \brief Get a string that identifies this interface.
  110 + */
  111 + virtual std::string clientId() const = 0;
  112 +
  113 + /*!
  114 + * \brief Register a callback function that is called when the status changes.
  115 + * \param cb - The callback function to register.
  116 + * \return handle to the registered callback.
  117 + * \note Make sure that the callback function lives longer than the IStateCallback object.
  118 + * Otherwise unregister callback function before it is destroyed.
  119 + */
  120 + virtual StateChangeCallbackHandle registerStateChangeCallback(const SlotStateChange& cb) = 0;
  121 +
  122 + /*!
  123 + * \brief Unregister a previously registered callback function.
  124 + * If the callback function was not registered nothing happens.
  125 + * \param handle - Identifies the callback function to to unregister.
  126 + */
  127 + virtual void unregisterStateChangeCallback(StateChangeCallbackHandle handle) = 0;
  128 +
  129 + /*!
  130 + * \brief Remove all the registered callback functions.
  131 + */
  132 + virtual void clearAllStateChangeCallbacks() = 0;
  133 +
  134 + /*!
  135 + * \brief retuns the latest state received.
  136 + */
  137 + virtual StateEnum state() const = 0;
  138 +};
  139 +
  140 +} // End namespace mqtt
  141 +} // End namespace components
  142 +} // End namespace osdev
  143 +
  144 +#endif // OSDEV_COMPONENTS_MQTT_ISTATECALLBACK_H
... ...
include/lockguard.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_LOCKGUARD_H
  23 +#define OSDEV_COMPONENTS_MQTT_LOCKGUARD_H
  24 +
  25 +// std
  26 +#include <mutex>
  27 +
  28 +// osdev::components::mqtt::measurement
  29 +#include "measure.h"
  30 +#include "utils.h"
  31 +
  32 +/**
  33 + * @brief Enable the lock measurements when macro MEASURE_LOCKS is defined.
  34 + * If the macro is not defined the NOP versions of the measure macros are used.
  35 + */
  36 +#ifdef MEASURE_LOCKS
  37 +#define OSDEV_COMPONENTS_MEASURELOCK OSDEV_COMPONENTS_MEASUREMENT_MEASURE
  38 +#define OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC
  39 +#else
  40 +#define OSDEV_COMPONENTS_MEASURELOCK OSDEV_COMPONENTS_MEASUREMENT_MEASURE_NOP
  41 +#define OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_NOP
  42 +#endif
  43 +
  44 +/**
  45 + * @brief Create a lockguard for a given mutex in a specific context.
  46 + * @param mutexVariableName The name of the mutex.
  47 + * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex.
  48 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  49 + */
  50 +#define OSDEV_COMPONENTS_LOCKGUARD_OBTAINER(mutexVariableName, mutexObtainer) \
  51 + OSDEV_COMPONENTS_MEASURELOCK(LOCKGUARD, std::lock_guard<std::mutex> Lock__Guard__##mutexVariableName##__(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \
  52 + osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__);
  53 +
  54 +/**
  55 + * @brief Create a lockguard for a given mutex in a specific context.
  56 + * @param mutexVariableName The name of the mutex.
  57 + * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex.
  58 + * @param id The id that identifies this specific lock guard. Used for measuring timings.
  59 + * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock.
  60 + */
  61 +#define OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexObtainer, id) \
  62 + OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, LOCKGUARD, std::lock_guard<std::mutex> Lock__Guard__##mutexVariableName##__(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \
  63 + osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__);
  64 +
  65 +/**
  66 + * @brief Create a lockguard for a given recursive mutex in a specific context.
  67 + * @param mutexVariableName The name of the mutex.
  68 + * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex.
  69 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  70 + */
  71 +#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD_OBTAINER(mutexVariableName, mutexObtainer) \
  72 + OSDEV_COMPONENTS_MEASURELOCK(RECURSIVELOCKGUARD, std::lock_guard<std::recursive_mutex> Lock__Guard__##mutexVariableName##__(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \
  73 + osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__);
  74 +
  75 +/**
  76 + * @brief Create a lockguard for a given recursive mutex in a specific context.
  77 + * @param mutexVariableName The name of the mutex.
  78 + * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex.
  79 + * @param id The id that identifies this specific lock guard. Used for measuring timings.
  80 + * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock.
  81 + */
  82 +#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexObtainer, id) \
  83 + OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, RECURSIVELOCKGUARD, std::lock_guard<std::recursive_mutex> Lock__Guard__##mutexVariableName##__(mutexVariableName), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \
  84 + osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__);
  85 +
  86 +/**
  87 + * @brief Create a lockguard for a given bare mutex in a specific context.
  88 + * @param mutexVariableName The name of the mutex.
  89 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  90 + */
  91 +#define OSDEV_COMPONENTS_LOCKGUARD(mutexVariableName) \
  92 + OSDEV_COMPONENTS_LOCKGUARD_OBTAINER(mutexVariableName, mutexVariableName)
  93 +
  94 +/**
  95 + * @brief Create a lockguard for a given bare mutex in a specific context.
  96 + * @param mutexVariableName The name of the mutex.
  97 + * @param id The id that identifies this specific lock guard. Used for measuring timings.
  98 + * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock.
  99 + */
  100 +#define OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC(mutexVariableName, id) \
  101 + OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexVariableName, id)
  102 +
  103 +/**
  104 + * @brief Create a lockguard for a given bare recursive mutex in a specific context.
  105 + * @param mutexVariableName The name of the mutex.
  106 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  107 + */
  108 +#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD(mutexVariableName) \
  109 + OSDEV_COMPONENTS_RECURSIVELOCKGUARD_OBTAINER(mutexVariableName, mutexVariableName)
  110 +
  111 +/**
  112 + * @brief Create a lockguard for a given bare recursive mutex in a specific context.
  113 + * @param mutexVariableName The name of the mutex.
  114 + * @param id The id that identifies this specific lock guard. Used for measuring timings.
  115 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  116 + */
  117 +#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD_SPECIFIC(mutexVariableName, id) \
  118 + OSDEV_COMPONENTS_RECURSIVELOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexVariableName, id)
  119 +
  120 +/**
  121 + * @brief Defines the lock name that is used by the OSDEV_COMPONENTS_UNIQUELOCK_* macros
  122 + */
  123 +#define OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName) Unique__Lock__##mutexVariableName##__
  124 +
  125 +/**
  126 + * @brief Create a uniqeue lock for a given mutex.
  127 + * @param mutexVariableName The name of the mutex.
  128 + * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex.
  129 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  130 + */
  131 +#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE_OBTAINER(mutexVariableName, mutexObtainer) \
  132 + OSDEV_COMPONENTS_MEASURELOCK(UNIQUELOCK_CREATE, std::unique_lock<std::mutex> OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName)(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \
  133 + osdev::components::mqtt::apply_unused_parameters(OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName));
  134 +
  135 +/**
  136 + * @brief Create a uniqeue lock for a given mutex.
  137 + * @param mutexVariableName The name of the mutex.
  138 + * @param mutexObtainer The mutex to lock. This can also be a function that returns mutex.
  139 + * @param id The id that identifies this specific lock guard. Used for measuring timings.
  140 + * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock.
  141 + */
  142 +#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC_OBTAINER(mutexVariableName, mutexObtainer, id) \
  143 + OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, UNIQUELOCK_CREATE, std::unique_lock<std::mutex> OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName)(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \
  144 + osdev::components::mqtt::apply_unused_parameters(OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName));
  145 +
  146 +/**
  147 + * @brief Create a uniqeue lock for a given bare mutex.
  148 + * @param mutexVariableName The name of the mutex.
  149 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  150 + */
  151 +#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE(mutexVariableName) \
  152 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_OBTAINER(mutexVariableName, mutexVariableName)
  153 +
  154 +/**
  155 + * @brief Create a uniqeue lock for a given bare mutex.
  156 + * @param mutexVariableName The name of the mutex.
  157 + * @param id The id that identifies this specific unique lock. Used for measuring timings.
  158 + * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock.
  159 + */
  160 +#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(mutexVariableName, id) \
  161 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC_OBTAINER(mutexVariableName, mutexVariableName, id)
  162 +
  163 +/**
  164 + * @brief Lock a given uniqeue lock.
  165 + * @param mutexVariableName The name of the mutex from which the lockname is derived.
  166 + * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances.
  167 + */
  168 +#define OSDEV_COMPONENTS_UNIQUELOCK_LOCK(mutexVariableName) \
  169 + OSDEV_COMPONENTS_MEASURELOCK(UNIQUELOCK_LOCK, OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).lock(), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100)
  170 +
  171 +/**
  172 + * @brief Lock a given uniqeue lock.
  173 + * @param mutexVariableName The name of the mutex from which the lockname is derived.
  174 + * @param id The id that identifies this specific unique lock guard. Used for measuring timings.
  175 + * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock.
  176 + */
  177 +#define OSDEV_COMPONENTS_UNIQUELOCK_LOCK_SPECIFIC(mutexVariableName, id) \
  178 + OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, UNIQUELOCK_LOCK, OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).lock(), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100)
  179 +
  180 +/**
  181 + * @brief Unlock a given uniqeue lock.
  182 + * @param mutexVariableName The name of the mutex from which the lockname is derived.
  183 + */
  184 +#define OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK(mutexVariableName) \
  185 + OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).unlock();
  186 +
  187 +/**
  188 + * @brief Unlock a given uniqeue lock.
  189 + * @param mutexVariableName The name of the mutex from which the lockname is derived.
  190 + * @param id The id that identifies this specific unique lock guard. Can be used for measuring timings.
  191 + */
  192 +#define OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(mutexVariableName, id) \
  193 + OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).unlock();
  194 +
  195 +namespace osdev {
  196 +namespace components {
  197 +namespace mqtt {
  198 +
  199 +/**
  200 + * @brief Context tag type.
  201 + */
  202 +struct measure_locking_tag
  203 +{
  204 +};
  205 +
  206 +/**
  207 + * @brief Type for measuring lock timings
  208 + * The unit is microseconds and the values are expected between 0 and 100 microseconds.
  209 + * This type is used to construct a timing histogram.
  210 + */
  211 +struct MeasureLockingValue
  212 +{
  213 + /**
  214 + * @brief The value type of the timing value.
  215 + */
  216 + using value_type = std::chrono::microseconds;
  217 +
  218 + const value_type minValue = value_type(0); ///< Constant mininum value.
  219 + const value_type maxValue = value_type(100); ///< Constant maximum value.
  220 +
  221 + const char* unit = "us"; ///< The value unit.
  222 +};
  223 +
  224 +} // End namespace mqtt
  225 +} // End namespace components
  226 +} // End namespace osdev
  227 +
  228 +#endif // OSDEV_COMPONENTS_MQTT_LOCKGUARD_H
... ...
include/macrodefs.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MACRODEFS_H
  23 +#define OSDEV_COMPONENTS_MQTT_MACRODEFS_H
  24 +
  25 +#include <type_traits>
  26 +
  27 +#include "compiletimestring.h"
  28 +
  29 +/// @brief Helper macro to stringify a symbol
  30 +#define OSDEV_COMPONENTS_STRINGIFY(x) #x
  31 +
  32 +/// @brief Use this macro to safely stringify a symbol
  33 +/// This will also work for nested macro's
  34 +#define OSDEV_COMPONENTS_TOSTRING(x) OSDEV_COMPONENTS_STRINGIFY(x)
  35 +
  36 +/// @brief Helper macro to combine two symbols
  37 +#define OSDEV_COMPONENTS_COMBINER(x, y) x##y
  38 +
  39 +/// @brief Use this macro to safely combine two symbols
  40 +/// This will also work for nested macro's
  41 +#define OSDEV_COMPONENTS_COMBINE(x, y) OSDEV_COMPONENTS_COMBINER(x, y)
  42 +
  43 +/// @brief Macro that reduces a path to the basename
  44 +#define OSDEV_COMPONENTS_MANAGEDBASEFILENAME \
  45 + OSDEV_COMPONENTS_CSTRING_BOUNDED(__FILE__, osdev::components::rfind(__FILE__, '/') + 1, sizeof(__FILE__) - 1)
  46 +
  47 +/// @brief Compiletime test if an instance derives from a certain base class
  48 +#define OSDEV_COMPONENTS_DERIVESFROM(derived, baseType) \
  49 + static_assert(std::is_base_of<baseType, \
  50 + typename std::remove_pointer<typename std::remove_reference<decltype(derived)>::type>::type>::value, \
  51 + OSDEV_COMPONENTS_TOSTRING(derived) " must be derived from " OSDEV_COMPONENTS_TOSTRING(baseType))
  52 +
  53 +#endif //OSDEV_COMPONENTS_MQTT_MACRODEFS_H
... ...
include/measure.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_MEASURE_H
  23 +#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_MEASURE_H
  24 +
  25 +#include "macrodefs.h"
  26 +#include "histogramprovider.h"
  27 +#include "timemeasurement.h"
  28 +
  29 +namespace osdev {
  30 +namespace components {
  31 +namespace mqtt {
  32 +namespace measurement {
  33 +
  34 +inline std::string createId(const std::string& operationName, const std::string& dynamicId)
  35 +{
  36 + return operationName + (dynamicId.empty() ? std::string{} : std::string(" ") + dynamicId);
  37 +}
  38 +
  39 +} // End namespace measurement
  40 +} // End namespace mqtt
  41 +} // End namespace components
  42 +} // End namespace osdev
  43 +
  44 +#define OSDEV_COMPONENTS_MEASUREMENT_LOCATION \
  45 + (OSDEV_COMPONENTS_MANAGEDBASEFILENAME + OSDEV_COMPONENTS_CSTRING(":" OSDEV_COMPONENTS_TOSTRING(__LINE__))).chars
  46 +
  47 +/**
  48 + * @brief Macro helper that sets up a TimeMeasurement on an operation.
  49 + * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
  50 + * @param operationName The name of the operation. This is part of the id under which the measurements are registered.
  51 + * @param dynamicId The dynamic identification of this measurement. This id makes different instances of the same operation unique.
  52 + * The dynamicId must be an expression that yields a string like object. The dynamic id is part of the id under which the
  53 + * measurements are registered.
  54 + * @param operation The operation to perform.
  55 + * @param context The context tag used to identify the HistogramProvider.
  56 + * @param valueType A datatype that can be used by HistogramProvider.
  57 + * @param numBuckets The number of buckets to use for the histogram.
  58 + * @param boolOnDestroy Boolean value that makes the measurement measure on destruction.
  59 + */
  60 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, dynamicId, operation, context, valueType, numBuckets, boolOnDestroy) \
  61 + static const std::string OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) = \
  62 + std::string(OSDEV_COMPONENTS_CSTRING(#operationName).chars); \
  63 + osdev::components::mqtt::measurement::TimeMeasurement OSDEV_COMPONENTS_COMBINE( \
  64 + OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __)( \
  65 + osdev::components::mqtt::measurement::createId(OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __), dynamicId), \
  66 + [](const std::string& id, std::chrono::steady_clock::time_point, std::chrono::microseconds, std::chrono::microseconds sinceLast) { \
  67 + osdev::components::mqtt::measurement::HistogramProvider<context, valueType, numBuckets>::instance().addValue(id, sinceLast); \
  68 + }, \
  69 + boolOnDestroy); \
  70 + operation;
  71 +
  72 +/**
  73 + * @brief Make a measurement before and after an operation.
  74 + * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
  75 + * @param operationName The name under which the measurements are registered.
  76 + * @param operation The operation to perform.
  77 + * @param context The context tag used to identify the HistogramProvider.
  78 + * @param valueType A datatype that can be used by HistogramProvider.
  79 + * @param numBuckets The number of buckets to use for the histogram.
  80 + */
  81 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE(operationName, operation, context, valueType, numBuckets) \
  82 + OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, OSDEV_COMPONENTS_MEASUREMENT_LOCATION, operation, context, valueType, numBuckets, false) \
  83 + OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __).measure();
  84 +
  85 +/**
  86 + * @brief Make a measurement on a return operation.
  87 + * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
  88 + * @param operationName The name under which the measurements are registered.
  89 + * @param operation The operation to perform.
  90 + * @param context The context tag used to identify the HistogramProvider.
  91 + * @param valueType A datatype that can be used by HistogramProvider.
  92 + * @param numBuckets The number of buckets to use for the histogram.
  93 + */
  94 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_ONDESTROY(operationName, operation, context, valueType, numBuckets) \
  95 + OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, OSDEV_COMPONENTS_MEASUREMENT_LOCATION, operation, context, valueType, numBuckets, true)
  96 +
  97 +/**
  98 + * @brief Make a measurement on a return operation.
  99 + * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
  100 + * @param operationName The name under which the measurements are registered.
  101 + * @param dynamicId An extra identification to discern between instances of the same operation. The dynamicId must be an expression that
  102 + * yields a string like object.
  103 + * @param operation The operation to perform.
  104 + * @param context The context tag used to identify the HistogramProvider.
  105 + * @param valueType A datatype that can be used by HistogramProvider.
  106 + * @param numBuckets The number of buckets to use for the histogram.
  107 + */
  108 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_ONDESTROY(operationName, dynamicId, operation, context, valueType, numBuckets) \
  109 + OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, dynamicId, operation, context, valueType, numBuckets, true)
  110 +
  111 +/**
  112 + * @brief Nop version that only performs the operation.
  113 + */
  114 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_ONDESTROY_NOP(operationName, operation, context, valueType, numBuckets) operation;
  115 +
  116 +/**
  117 + * @brief Nop version that only performs the operation.
  118 + */
  119 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_NOP(operationName, operation, context, valueType, numBuckets) operation;
  120 +
  121 +/**
  122 + * @brief Nop version that only performs the operation.
  123 + */
  124 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_ONDESTROY_NOP(operationName, dynamicId, operation, context, valueType, numBuckets) operation;
  125 +
  126 +/**
  127 + * @brief Make a measurement before and after an operation for a specific instance of the operation.
  128 + * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
  129 + * @param dynamicId An extra identification to discern between instances of the same operation.
  130 + * @param operationName The name under which the measurements are registered.
  131 + * @param operation The operation to perform.
  132 + * @param context The context tag used to identify the HistogramProvider.
  133 + * @param valueType A datatype that can be used by HistogramProvider.
  134 + * @param numBuckets The number of buckets to use for the histogram.
  135 + */
  136 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC(dynamicId, operationName, operation, context, valueType, numBuckets) \
  137 + auto OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPREFIX__, __LINE__), __) = \
  138 + OSDEV_COMPONENTS_MANAGEDBASEFILENAME + OSDEV_COMPONENTS_CSTRING(":" OSDEV_COMPONENTS_TOSTRING(__LINE__) ", function "); \
  139 + auto OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPOSTFIX__, __LINE__), __) = OSDEV_COMPONENTS_CSTRING(", " #operationName " "); \
  140 + static const std::string OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) = \
  141 + std::string(OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPREFIX__, __LINE__), __).chars) + \
  142 + std::string(__func__) + \
  143 + OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPOSTFIX__, __LINE__), __).chars; \
  144 + osdev::components::mqtt::measurement::TimeMeasurement OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __)( \
  145 + OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) + dynamicId, \
  146 + [](const std::string& id, std::chrono::steady_clock::time_point, std::chrono::microseconds, std::chrono::microseconds sinceLast) { \
  147 + osdev::components::mqtt::measurement::HistogramProvider<context, valueType, numBuckets>::instance().addValue(id, sinceLast); \
  148 + }, \
  149 + false); \
  150 + operation; \
  151 + OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __).measure();
  152 +
  153 +/**
  154 + * @brief Nop version that only performs the operation.
  155 + */
  156 +#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_NOP(dynamicId, operationName, operation, context, valueType, numBuckets) operation;
  157 +
  158 +#endif // OSDEV_COMPONENTS_MEASUREMENT_MEASUREMENT_H
... ...
include/metaprogrammingdefs.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_METAPROGRAMMINGDEFS_H
  23 +#define OSDEV_COMPONENTS_MQTT_METAPROGRAMMINGDEFS_H
  24 +
  25 +#include <cstddef> // for std::size_t
  26 +#include <sstream>
  27 +#include <string>
  28 +
  29 +#include "utils.h"
  30 +
  31 +/**
  32 + * @brief Create a type trait that checks for the existence of a member in a struct.
  33 + * @param memberName The member name
  34 + *
  35 + * A call such as OSDEV_COMPONENTS_HASMEMBER_TRAIT(onSuccess) leads to a type trait has_onSuccess.
  36 + */
  37 +#define OSDEV_COMPONENTS_HASMEMBER_TRAIT(memberName) \
  38 + template <typename T, typename = void> \
  39 + struct has_##memberName : public std::false_type \
  40 + { \
  41 + }; \
  42 + \
  43 + template <typename T> \
  44 + struct has_##memberName<T, osdev::components::mqtt::void_t<decltype(std::declval<T>().memberName)>> : public std::true_type \
  45 + { \
  46 + };
  47 +
  48 +/**
  49 + * @brief Create a type trait that checks for the existence of a member method with no parameters.
  50 + * @param methodName The method name
  51 + * @param postfix An extra postfix that is added to the type trait struct.
  52 + * This makes it possible create type traits that check for
  53 + * overloaded methods.
  54 + *
  55 + * A call such as OSDEV_COMPONENTS_HASMETHOD_TRAIT(capacity,1) leads to a type trait has_capacity1.
  56 + */
  57 +#define OSDEV_COMPONENTS_HASMETHOD_TRAIT(methodName, postfix) \
  58 + template <typename T, typename = void> \
  59 + struct has_##methodName##postfix : public std::false_type \
  60 + { \
  61 + }; \
  62 + \
  63 + template <typename T> \
  64 + struct has_##methodName##postfix<T, osdev::components::mqtt::void_t<decltype(std::declval<T>().methodName())>> : public std::true_type \
  65 + { \
  66 + };
  67 +
  68 +/**
  69 + * @brief Create a type trait that checks for the existence of a member method with 1 or more parameters.
  70 + * @param methodName The method name
  71 + * @param postfix An extra postfix that is added to the type trait struct.
  72 + * This makes it possible create type traits that check for
  73 + * overloaded methods.
  74 + * @param ... Variable number of arguments. These can be values, but also declval constructs such as std::declval<MyClass>().
  75 + *
  76 + * A call such as OSDEV_COMPONENTS_HASMETHODWP_TRAIT(capacity,,"string") leads to a type trait has_capacity that
  77 + * checks for the existence of a method capacity that accepts a const char pointer.
  78 + */
  79 +#define OSDEV_COMPONENTS_HASMETHODWP_TRAIT(methodName, postfix, ...) \
  80 + template <typename T, typename = void> \
  81 + struct has_##methodName##postfix : public std::false_type \
  82 + { \
  83 + }; \
  84 + \
  85 + template <typename T> \
  86 + struct has_##methodName##postfix<T, osdev::components::mqtt::void_t<decltype(std::declval<T>().methodName(__VA_ARGS__))>> : public std::true_type \
  87 + { \
  88 + };
  89 +
  90 +namespace osdev {
  91 +namespace components {
  92 +namespace mqtt {
  93 +
  94 +/**
  95 + * @brief Used to detect ill formed types in a SFINAE context.
  96 + * If the parameter pack Ts contains an invalid type, struct make_void cannot be expanded.
  97 + */
  98 +template <typename... Ts>
  99 +struct make_void
  100 +{
  101 + using type = void;
  102 +};
  103 +
  104 +/**
  105 + * @brief Introduced in c++17 (but will also work in c++11)
  106 + */
  107 +template <typename... Ts>
  108 +using void_t = typename make_void<Ts...>::type;
  109 +
  110 +/// @brief Build an index list that can be used to traverse another list (f.i. a char array).
  111 +/// Recursive definition.
  112 +/// Start with an empty indices list. Build the indices list recursively.
  113 +/// upper-1, indices... becomes the indices... in the next recursive call.
  114 +template <std::size_t lower, std::size_t upper,
  115 + template <std::size_t...> class meta_functor, std::size_t... Is>
  116 +struct apply_bounded_range
  117 +{
  118 + typedef typename apply_bounded_range<lower, upper - 1, meta_functor, upper - 1, Is...>::result result;
  119 +};
  120 +
  121 +/// @brief Terminator of apply_bounded_range.
  122 +/// Terminates when the upper bound (which runs) is equal to the lower bound.
  123 +template <std::size_t lower, template <std::size_t...> class meta_functor, std::size_t... Is>
  124 +struct apply_bounded_range<lower, lower, meta_functor, Is...>
  125 +{
  126 + typedef typename meta_functor<Is...>::result result;
  127 +};
  128 +
  129 +/**
  130 + * @brief Helper method to perform static_assert on the expected count of a parameter pack.
  131 + */
  132 +template <std::size_t expectedCount, typename... Args>
  133 +void static_assert_count()
  134 +{
  135 + constexpr std::size_t actualCount = sizeof...(Args);
  136 + static_assert(expectedCount == actualCount, "Unexpected parameter count.");
  137 +}
  138 +
  139 +/**
  140 + * @brief Helper method to convert a type to std::string.
  141 + * @param d_first The output iterator to write the converted string to.
  142 + * @param arg The argument to convert to std::string.
  143 + */
  144 +template <typename OutputIt, typename T>
  145 +int apply_to_string_one(OutputIt d_first, const T& arg)
  146 +{
  147 + std::ostringstream ss;
  148 + ss << arg;
  149 + *d_first++ = ss.str();
  150 + return 0;
  151 +}
  152 +
  153 +/**
  154 + * @brief Converts all parameters to std::string.
  155 + * @param d_first The output iterator to write the converted strings to.
  156 + * @param args The arguments to convert to std::string.
  157 + */
  158 +template <typename OutputIt, typename... Args>
  159 +void apply_to_string(OutputIt d_first, Args&&... args)
  160 +{
  161 + // d_first is not used when args parameter pack is empty.
  162 + apply_unused_parameters(d_first);
  163 +
  164 + // We need to use the expand_type trick in order to be able to use an initializer list
  165 + // so that we can call the method for every variadic argument
  166 + using expand_type = int[];
  167 + // Array must be initialized with values. Add a single value so that the array can be initialized when the parameter pack is empty.
  168 + expand_type et{ 0, apply_to_string_one(d_first, std::forward<Args>(args))... };
  169 + apply_unused_parameters(et);
  170 +}
  171 +
  172 +/**
  173 + * @brief Helper that removes const, volatile and reference from a type.
  174 + * @note Defined in std C++20.
  175 + */
  176 +template <typename T>
  177 +struct remove_cvref
  178 +{
  179 + using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
  180 +};
  181 +
  182 +} // End namespace mqtt
  183 +} // End namespace components
  184 +} // End namespace osdev
  185 +
  186 +#endif // OSDEV_COMPONENTS_MQTT_METAPROGRAMMINGDEFS_H
... ...
include/mqttclient.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MQTTCLIENT_H
  23 +#define OSDEV_COMPONENTS_MQTT_MQTTCLIENT_H
  24 +
  25 +// std
  26 +#include <condition_variable>
  27 +#include <memory>
  28 +#include <mutex>
  29 +#include <set>
  30 +#include <thread>
  31 +#include <vector>
  32 +
  33 +// osdev::components::mqtt
  34 +#include "synchronizedqueue.h"
  35 +#include "istatecallback.h"
  36 +#include "serverstate.h"
  37 +
  38 +#include "imqttclient.h"
  39 +
  40 +namespace osdev {
  41 +namespace components {
  42 +namespace mqtt {
  43 +
  44 +// Forward definition
  45 +class IMqttClientImpl;
  46 +
  47 +class MqttClient : public virtual IMqttClient
  48 +{
  49 +public:
  50 + /*!
  51 + * \brief Construct an instance of the MqttClient.
  52 + * \param clientId The client identification used in the connection to the mqtt broker.
  53 + * \param deliveryCompleteCallback Optional callback used to signal completion of a publication.
  54 + */
  55 + MqttClient( const std::string& clientId, const std::function<void(const Token& token)>& deliveryCompleteCallback = std::function<void(const Token& token)>{});
  56 + virtual ~MqttClient() override;
  57 +
  58 + // Non copyable, non movable
  59 + MqttClient(const MqttClient&) = delete;
  60 + MqttClient& operator=(const MqttClient&) = delete;
  61 + MqttClient(MqttClient&&) = delete;
  62 + MqttClient& operator=(MqttClient&&) = delete;
  63 +
  64 + /**
  65 + * @see IStateCallback
  66 + */
  67 + virtual std::string clientId() const override;
  68 +
  69 + /**
  70 + * @see IStateCallback
  71 + */
  72 + virtual StateChangeCallbackHandle registerStateChangeCallback(const SlotStateChange& cb) override;
  73 +
  74 + /**
  75 + * @see IStateCallback
  76 + */
  77 + virtual void unregisterStateChangeCallback(StateChangeCallbackHandle handle) override;
  78 +
  79 + /**
  80 + * @see IStateCallback
  81 + */
  82 + virtual void clearAllStateChangeCallbacks() override;
  83 +
  84 + /**
  85 + * @see IStateCallback
  86 + */
  87 + virtual StateEnum state() const override;
  88 +
  89 + // MqttClient interface
  90 +
  91 + /**
  92 + * @see IMqttClient
  93 + */
  94 + virtual void connect(const std::string& host, int port, const Credentials& credentials) override;
  95 +
  96 + /**
  97 + * @see IMqttClient
  98 + */
  99 + virtual void connect(const std::string& endpoint) override;
  100 +
  101 + /**
  102 + * @see IMqttClient
  103 + */
  104 + virtual void disconnect() override;
  105 +
  106 + /**
  107 + * @see IMqttClient
  108 + */
  109 + virtual Token publish(const MqttMessage& message, int qos) override;
  110 +
  111 + /**
  112 + * @see IMqttClient
  113 + * When an overlapping subscription is detected a new connection has to be created. This is a synchronous action
  114 + * and this method will wait for the connection to be up.
  115 + */
  116 + virtual Token subscribe(const std::string& topic, int qos, const std::function<void(MqttMessage)>& cb) override;
  117 +
  118 + /**
  119 + * @see IMqttClient
  120 + */
  121 + virtual std::set<Token> unsubscribe(const std::string& topic, int qos) override;
  122 +
  123 + /**
  124 + * @see IMqttClient
  125 + */
  126 + virtual bool waitForCompletion(std::chrono::milliseconds waitFor) const override;
  127 +
  128 + /**
  129 + * @see IMqttClient
  130 + */
  131 + virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const Token& token) const override;
  132 +
  133 + /**
  134 + * @see IMqttClient
  135 + */
  136 + virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const std::set<Token>& tokens) const override;
  137 +
  138 + /**
  139 + * @see IMqttClient
  140 + */
  141 + virtual boost::optional<bool> commandResult(const Token& token) const override;
  142 +
  143 + /**
  144 + * @see IMqttClient
  145 + */
  146 + virtual std::string endpoint() const override;
  147 +
  148 +private:
  149 + /*!
  150 + * \brief Callback used to pick up the connection status of the wrappers.
  151 + * \param id The client id.
  152 + * \param cs The connection status.
  153 + */
  154 + void connectionStatusChanged( const std::string& id, ConnectionStatus cs );
  155 +
  156 + /*!
  157 + * \brief Callback for handling delivery complete.
  158 + * \param clientId The identifier of the client on which the publish command is executed.
  159 + * \param token The token identifies the publish command.
  160 + */
  161 + void deliveryComplete( const std::string& clientId, std::int32_t token );
  162 +
  163 + /**
  164 + * \brief Wait for commands to complete including active tokens in this client.
  165 + * The interface mutex is not locked by this method.
  166 + * First wait for client commands to complete (use method waitForCompletionInternalClients)
  167 + * and then wait for publish delivery callbacks to complete.
  168 + * \param clients - Vector with client wrapper pointers that need to be waited on.
  169 + * \param[in,out] waitFor - The number of milliseconds to wait for completetion of all commands and delivery callbacks.
  170 + * \param tokens - The tokens to wait for. An empty set means to wait for all commands on all clients to complete
  171 + * including all publish delivery callbacks.
  172 + * \return True when commands have completed in time including delivery callbacks or false if not.
  173 + */
  174 + bool waitForCompletionInternal(const std::vector<IMqttClientImpl*>& clients, std::chrono::milliseconds waitFor, const std::set<Token>& tokens) const;
  175 +
  176 + /**
  177 + * \brief Wait for commands on the wrapper clients to complete.
  178 + * The interface mutex is not locked by this method.
  179 + * \param clients - Vector with client wrapper pointers that need to be waited on.
  180 + * \param[in,out] waitFor - The number of milliseconds to wait for completetion of all commands.
  181 + * On return waitFor contains the time left.
  182 + * \param tokens - The tokens to wait for. An empty set means to wait for all commands
  183 + * on all clients to complete.
  184 + * \return True when all commands have completed in time or false if not.
  185 + */
  186 + bool waitForCompletionInternalClients(const std::vector<IMqttClientImpl*>& clients, std::chrono::milliseconds& waitFor, const std::set<Token>& tokens) const;
  187 +
  188 + /**
  189 + * @brief Determine the state of this client based on the connection statusses of its client wrappers.
  190 + * The states this client can communicate are:
  191 + * Unknown : When at least one wrapper is in a different state then Connected or ReconnectInProgress or when no wrappers are available.
  192 + * Good : When all wrappers are connected.
  193 + * ConnectionFailure : When at least one wrapper attempts reconnection.
  194 + * Unregister : When the serverstate instance is destroyed.
  195 + *
  196 + * The other states are about the information providers to the mqtt broker (the publishers) and we cannot say anything about them here.
  197 + * The state "Good" is the exception. This state means in this case that this clients connection is ok and not that the underlying data
  198 + * source (publisher) is ok.
  199 + *
  200 + * @param connectionStates A vector with the connection statusses of all client wrappers.
  201 + */
  202 + StateEnum determineState(const std::vector<ConnectionStatus>& connectionStates);
  203 +
  204 + /**
  205 + * @brief Add an event to the synchronized queue.
  206 + * @param ev A function object that performs work in the context of this class.
  207 + */
  208 + void pushEvent(std::function<void()> ev);
  209 +
  210 + /**
  211 + * @brief Worker method that executes the events.
  212 + */
  213 + void eventHandler();
  214 +
  215 + mutable std::mutex m_interfaceMutex; ///< Makes the interface mutual exclusive
  216 + mutable std::mutex m_internalMutex; ///< Protect the internal state.
  217 + std::string m_endpoint; ///< The endpoint uri.
  218 + std::string m_clientId; ///< The main client identification.
  219 + std::set<Token> m_activeTokens; ///< Set with active command tokens. Callbacks still need to be made for these tokens.
  220 + mutable std::condition_variable m_activeTokensCV; ///< Wait on a condition to become true w.r.t. the active token set.
  221 + std::function<void(const Token&)> m_deliveryCompleteCallback; ///< Optional callback for publish completion.
  222 + ServerState m_serverState; ///< Records the state of the connection to the broker that this client is connected to.
  223 + std::unique_ptr<IMqttClientImpl> m_principalClient; ///< The main wrapper client.
  224 + std::vector<std::unique_ptr<IMqttClientImpl>> m_additionalClients; ///< A vector of additional wrapper clients.
  225 + SynchronizedQueue<std::function<void()>> m_eventQueue; ///< Synchronized queue for scheduling additional work.
  226 + std::thread m_workerThread; ///< A worker thread that is used to perform actions that cannot be done on the callback threads.
  227 +};
  228 +
  229 +} // End namespace mqtt
  230 +} // End namespace components
  231 +} // End namespace osdev
  232 +
  233 +#endif // OSDEV_COMPONENTS_MQTT_MQTTCLIENT_H
... ...
include/mqttfailure.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MQTTFAILURE_H
  23 +#define OSDEV_COMPONENTS_MQTT_MQTTFAILURE_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +// paho
  29 +#include <MQTTAsync.h>
  30 +
  31 +namespace osdev {
  32 +namespace components {
  33 +namespace mqtt {
  34 +
  35 +/*!
  36 + * \brief Class for paho mqtt failure response data.
  37 + */
  38 +class MqttFailure
  39 +{
  40 +public:
  41 + /*!
  42 + * \brief Construct MqttFailure instance by copying information from the paho failure struct.
  43 + * \param data Paho response failure data.
  44 + */
  45 + explicit MqttFailure(const MQTTAsync_failureData* data);
  46 +
  47 + /*!
  48 + * \return The command token
  49 + */
  50 + MQTTAsync_token token() const { return m_token; }
  51 +
  52 + /*!
  53 + * \return The failure code.
  54 + */
  55 + int code() const { return m_code; }
  56 +
  57 + /*!
  58 + * \return The failure message.
  59 + * \retval "no message" when no message is available.
  60 + */
  61 + const std::string& message() const { return m_message; }
  62 +
  63 + /*!
  64 + * \return string interpretation of the code.
  65 + * \note negative codes are interpreted as paho error codes.
  66 + */
  67 + std::string codeToString() const;
  68 +
  69 +private:
  70 + MQTTAsync_token m_token; ///< Command token.
  71 + int m_code; ///< Failure code.
  72 + std::string m_message; ///< Optional message. Equal to "no message" when message is not available.
  73 +};
  74 +
  75 +} // End namespace mqtt
  76 +} // End namespace components
  77 +} // End namespace osdev
  78 +
  79 +#endif // OSDEV_COMPONENTS_MQTT_MQTTFAILURE_H
... ...
include/mqttidgenerator.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MLOGICIDGENERATOR_H
  23 +#define OSDEV_COMPONENTS_MQTT_MLOGICIDGENERATOR_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +// osdev::components::mqtt
  29 +#include "commondefs.h"
  30 +
  31 +namespace osdev {
  32 +namespace components {
  33 +namespace mqtt {
  34 +
  35 +class MqttIdGenerator
  36 +{
  37 +public:
  38 + /**
  39 + * @brief Generates a new MqttId, which is guaranteed to be unique.
  40 + * @return A new unique MqttId.
  41 + */
  42 + static MqttId generate();
  43 +
  44 + /**
  45 + * @brief Returns an MqttId that represents null.
  46 + * @return An MqttId that represents null.
  47 + */
  48 + static MqttId nullId();
  49 +
  50 + /**
  51 + * @brief Returns an MqttId based on a namespace uuid and a given string.
  52 + * @param namespaceUuid The namespace in which the MqttId is generated.
  53 + * @param name The name for which an MqttId is generated.
  54 + */
  55 + static MqttId nameId(MqttId namespaceUuid, const std::string& name);
  56 +
  57 + /**
  58 + * @brief Returns an MqttId in the MQTT namespace for a given string.
  59 + * @param name The name for which an MqttId is generated.
  60 + */
  61 + static MqttId nameId(const std::string& name);
  62 +};
  63 +
  64 +} // End namespace mqtt
  65 +} // End namespace components
  66 +} // End namespace osdev
  67 +
  68 +#endif // OSDEV_COMPONENTS_MQTT_MLOGICIDGENERATOR_H
... ...
include/mqttmessage.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MQTTMESSAGE_H
  23 +#define OSDEV_COMPONENTS_MQTT_MQTTMESSAGE_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +// paho-c
  29 +#include <MQTTAsync.h>
  30 +
  31 +namespace osdev {
  32 +namespace components {
  33 +namespace mqtt {
  34 +
  35 +/*!
  36 + * @brief Class for paho mqtt message data
  37 + */
  38 +class MqttMessage
  39 +{
  40 +public:
  41 + /*!
  42 + * @brief Construct empty MqttMessage instance
  43 + */
  44 + MqttMessage();
  45 +
  46 + /*!
  47 + * @brief Construct MqttMessage instance by copying information gfrom the paho message struct
  48 + * @param topic - Paho topic data (copied)
  49 + * @param msg - Paho message data (copied)
  50 + */
  51 + MqttMessage( const std::string &topic, const MQTTAsync_message &msg );
  52 +
  53 + /*!
  54 + * @brief Construct MqttMessage instance.
  55 + * @param topic - Topic String
  56 + * @param retainedFlag - Flag that indicates if message is retained
  57 + * @param duplicateFlag - Flag that indicates if message is duplicate.
  58 + * @param thePayload - The message itself.
  59 + */
  60 + MqttMessage( const std::string &topic, bool retainedFlag, bool duplicateFlag, std::string thePayload );
  61 +
  62 + /*! @return The retained flag value. */
  63 + bool retained() const { return m_retained; }
  64 +
  65 + /*! @return The duplicate flag value */
  66 + bool duplicate() const { return m_duplicate; }
  67 +
  68 + /*! @return The topic on which the message is received. */
  69 + const std::string& topic() const { return m_topic; }
  70 +
  71 + /*! @return The message payload. */
  72 + const std::string& payload() const { return m_payload; }
  73 +
  74 + /*! @return This instance as a paho message */
  75 + MQTTAsync_message toAsyncMessage() const;
  76 +
  77 +private:
  78 + bool m_retained; ///< Retained flag. Not all brokers communicate this flag correctly. (emqx does not, mosquitto does.)
  79 + bool m_duplicate; ///< Duplicate flag ( for qos 1? )
  80 + std::string m_topic; ///< The topic on which the message is recieved.
  81 + std::string m_payload; ///< The actual message data.
  82 +};
  83 +
  84 +
  85 +} // End namespace mqtt
  86 +} // End namespace components
  87 +} // End namespace osdev
  88 +
  89 +#endif // OSDEV_COMPONENTS_MQTT_MQTTMESSAGE_H
... ...
include/mqttstream.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MQTTSTREAM_H
  23 +#define OSDEV_COMPONENTS_MQTT_MQTTSTREAM_H
  24 +
  25 +// This header is used in conjunction with mlogic/commmon/stringify.h to get output streaming of stl container types.
  26 +// The streaming operators are not suitable for marshalling because type information is lost!
  27 +
  28 +// std
  29 +#include <array>
  30 +#include <list>
  31 +#include <map>
  32 +#include <ostream>
  33 +#include <set>
  34 +#include <string>
  35 +#include <vector>
  36 +
  37 +namespace osdev {
  38 +namespace components {
  39 +namespace mqtt {
  40 +
  41 +/**
  42 + * @brief Streams a container with content for which stream operators are available.
  43 + * @note This function is meant for printing and not for marshalling!
  44 + * @tparam Open The container opening character.
  45 + * @tparam Close The container closing character.
  46 + * @tparam T The container type.
  47 + * @param os The stream to use.
  48 + * @param rhs The container that is to be streamed.
  49 + * @param sep The field separator. Default is ", "
  50 + * @return reference to the stream object.
  51 + */
  52 +template <char Open, char Close, typename T>
  53 +std::ostream& streamContainer(std::ostream& os, const T& rhs, const std::string& sep = ", ")
  54 +{
  55 + os << Open;
  56 + for (auto cit = rhs.cbegin(); rhs.cend() != cit; ++cit) {
  57 + os << *cit;
  58 + if (std::next(cit) != rhs.end()) {
  59 + os << sep;
  60 + }
  61 + }
  62 + os << Close;
  63 +
  64 + return os;
  65 +}
  66 +
  67 +} // End namespace mqtt
  68 +} // End namespace components
  69 +} // End namespace osdev
  70 +
  71 +namespace std {
  72 +
  73 +/**
  74 + * @brief Streams a list that contains values for which an output stream operator is available.
  75 + */
  76 +template <typename T>
  77 +std::ostream& operator<<(std::ostream& os, const std::list<T>& rhs)
  78 +{
  79 + return osdev::components::mqtt::streamContainer<'<', '>'>(os, rhs);
  80 +}
  81 +
  82 +/**
  83 + * @brief Streams an array that contains values for which an output stream operator is available.
  84 + */
  85 +template <typename T, std::size_t N>
  86 +std::ostream& operator<<(std::ostream& os, const std::array<T, N>& rhs)
  87 +{
  88 + return osdev::components::mqtt::streamContainer<'[', ']'>(os, rhs);
  89 +}
  90 +
  91 +/**
  92 + * @brief Streams a vector that contains values for which an output stream operator is available.
  93 + */
  94 +template <typename T>
  95 +std::ostream& operator<<(std::ostream& os, const std::vector<T>& rhs)
  96 +{
  97 + return osdev::components::mqtt::streamContainer<'[', ']'>(os, rhs);
  98 +}
  99 +
  100 +/**
  101 + * @brief Streams a set that contains values for which an output stream operator is available.
  102 + */
  103 +template <typename T>
  104 +std::ostream& operator<<(std::ostream& os, const std::set<T>& rhs)
  105 +{
  106 + return osdev::components::mqtt::streamContainer<'{', '}'>(os, rhs);
  107 +}
  108 +
  109 +/**
  110 + * @brief Streams a map that contains keys and values for which an output stream operator is available.
  111 + */
  112 +template <typename TKey, typename TValue>
  113 +std::ostream& operator<<(std::ostream& os, const std::map<TKey, TValue>& rhs)
  114 +{
  115 + return osdev::components::mqtt::streamContainer<'{', '}'>(os, rhs);
  116 +}
  117 +
  118 +/**
  119 + * @brief Streams a pair that contains values for which an output stream operator is available.
  120 + */
  121 +template <typename TFirst, typename TSecond>
  122 +std::ostream& operator<<(std::ostream& os, const std::pair<TFirst, TSecond>& rhs)
  123 +{
  124 + os << "{" << rhs.first << " : " << rhs.second << "}";
  125 + return os;
  126 +}
  127 +
  128 +} // End namespace std
  129 +
  130 +#endif // OSDEV_COMPONENTS_MQTT_MQTTSTREAM_H
... ...
include/mqttsuccess.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MQTTSUCCESS_H
  23 +#define OSDEV_COMPONENTS_MQTT_MQTTSUCCESS_H
  24 +
  25 +// std
  26 +#include <string>
  27 +#include <vector>
  28 +
  29 +// boost
  30 +#include <boost/variant.hpp>
  31 +
  32 +// paho
  33 +#include <MQTTAsync.h>
  34 +
  35 +// osdev::components::mqtt
  36 +#include "mqttmessage.h"
  37 +
  38 +namespace osdev {
  39 +namespace components {
  40 +namespace mqtt {
  41 +
  42 +/**
  43 + * @brief Class that holds paho connection data which is returned in the connect response.
  44 + */
  45 +class ConnectionData
  46 +{
  47 +public:
  48 + /*!
  49 + * \brief Construct an empty ConnectData instance.
  50 + */
  51 + ConnectionData();
  52 +
  53 + /*!
  54 + * \brief Construct ConnectData based on incoming values from paho.
  55 + * \param serverUri - The serverUri to which the connection is made (needs to be copied).
  56 + * \param mqttVersion - The mqtt version used by the broker.
  57 + * \param sessionPresent - Flag that indicates if a session was present for the given clientId.
  58 + */
  59 + ConnectionData(char* serverUri, int mqttVersion, int sessionPresent);
  60 +
  61 + /*!
  62 + * \return The server uri.
  63 + */
  64 + const std::string& serverUri() const { return m_serverUri; }
  65 +
  66 + /*!
  67 + * \return The mqtt version.
  68 + */
  69 + int mqttVersion() const { return m_mqttVersion; }
  70 +
  71 + /*!
  72 + * \return if a session was present for the given clientId.
  73 + */
  74 + bool sessionPresent() const { return m_sessionPresent; }
  75 +
  76 +private:
  77 + std::string m_serverUri; ///< The broker server uri.
  78 + int m_mqttVersion; ///< The mqtt version used by the broker.
  79 + bool m_sessionPresent; ///< Flag that indicates whether a session was present for the client id used in the connect.
  80 +};
  81 +
  82 +struct Unspecified
  83 +{
  84 +};
  85 +
  86 +/*!
  87 + * \brief Class for paho mqtt success response data.
  88 + * The paho success response data uses a union and can have different information depending on the command.
  89 + */
  90 +class MqttSuccess
  91 +{
  92 +public:
  93 + /*!
  94 + * \brief Response data for commands without specific data.
  95 + * \param token The token that identifies to which command this response belongs.
  96 + */
  97 + explicit MqttSuccess(MQTTAsync_token token);
  98 +
  99 + /*!
  100 + * \brief Response data for a subscribe command.
  101 + * \param token The token that identifies to which command this response belongs.
  102 + * \param qos Actual quality of service of the subscription.
  103 + */
  104 + MqttSuccess(MQTTAsync_token token, int qos);
  105 +
  106 + /*!
  107 + * \brief Response data for a subscribe many command.
  108 + * \param token The token that identifies to which command this response belongs.
  109 + * \param qosMany Actual quality of service of the subscription for each topic filter.
  110 + */
  111 + MqttSuccess(MQTTAsync_token token, const std::vector<int>& qosMany);
  112 +
  113 + /*!
  114 + * \brief Response data for a publish command.
  115 + * \param token The token that identifies to which command this response belongs.
  116 + * \param pubMsg The message that was published.
  117 + */
  118 + MqttSuccess(MQTTAsync_token token, const MqttMessage& pubMsg);
  119 +
  120 + /*!
  121 + * \brief Response data for a connect command.
  122 + * \param token The token that identifies to which command this response belongs.
  123 + * \param connData The connection data.
  124 + */
  125 + MqttSuccess(MQTTAsync_token token, const ConnectionData& connData);
  126 +
  127 + /*!
  128 + * \return the command token.
  129 + */
  130 + MQTTAsync_token token() const { return m_token; }
  131 +
  132 + /*!
  133 + * \return the qos
  134 + * \throw exception when command is not a subscribe command.
  135 + */
  136 + int qos() const;
  137 +
  138 + /*!
  139 + * \return a vector of qos values (matching the topics in the subscribe many command).
  140 + * \throw exception when command is not a subscribe many command.
  141 + */
  142 + std::vector<int> qosMany() const;
  143 +
  144 + /*!
  145 + * \return Message that has been published.
  146 + * \throw exception when command is not a publish command.
  147 + */
  148 + MqttMessage publishData() const;
  149 +
  150 + /*!
  151 + * return Connection data.
  152 + * throw exception when command is not a connect command.
  153 + */
  154 + ConnectionData connectionData() const;
  155 +
  156 +private:
  157 + MQTTAsync_token m_token; ///< Command token.
  158 + boost::variant<int, std::vector<int>, MqttMessage, ConnectionData, Unspecified> m_data; ///< Data for the various commands.
  159 +};
  160 +
  161 +} // End namespace mqtt
  162 +} // End namespace components
  163 +} // End namespace osdev
  164 +
  165 +#endif // OSDEV_COMPONENTS_MQTT_MQTTSUCCESS_H
... ...
include/mqtttypeconverter.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MLOGICTYPECONVERTER_H
  23 +#define OSDEV_COMPONENTS_MQTT_MLOGICTYPECONVERTER_H
  24 +
  25 +// std
  26 +#include <chrono>
  27 +#include <ctime>
  28 +#include "date.h"
  29 +#include <string>
  30 +
  31 +// boost
  32 +#include <boost/uuid/uuid.hpp>
  33 +
  34 +#include "commondefs.h"
  35 +
  36 +namespace osdev {
  37 +namespace components {
  38 +namespace mqtt {
  39 +
  40 +/**
  41 + * @brief Utility namespace to convert between mqtt common types and other frequently used types.
  42 + */
  43 +namespace MqttTypeConverter {
  44 +
  45 +/**
  46 + * @brief Converts from MqttId to std::string.
  47 + * @param mqttId The mqttId to convert.
  48 + * @return The std::string with contents of the provided mqttId. Format is 12345678-9abc-def0-1234-56789abcdef0.
  49 + */
  50 +std::string toStdString(const MqttId& mqttId);
  51 +
  52 +/**
  53 + * @brief Converts from system clock timepoint to std::string.
  54 + * @tparam Duration std::chrono::duration instance.
  55 + * Duration::Period is used to determine the precision
  56 + * of the subsecond part of the returned ISO8601 string.
  57 + * Uses the duration of the StdTime type by default.
  58 + * @param tp The timepoint to converter.
  59 + * @return ISO8601 string representation of stdTime.
  60 + */
  61 +template <typename Duration>
  62 +std::string toStdString(const std::chrono::time_point<std::chrono::system_clock, Duration>& tp)
  63 +{
  64 + return date::format("%FT%T%Ez", tp);
  65 +}
  66 +
  67 +/**
  68 + * @brief Converts from std::string to MqttId.
  69 + * @param mqttId The MqttId string to convert.
  70 + * @return the converted string to MqttId.
  71 + */
  72 +MqttId toMqttId(const std::string& mqttId);
  73 +
  74 +/**
  75 + * @brief Creates a descriptive string based on the specified input parameters.
  76 + * @param str The prefix of the string.
  77 + * @param mqttId The id of which to use the first 4 characters.
  78 + * @return str + "-" + <first 4 characters of mqttId>
  79 + * Example: "Unassigned-a2c4".
  80 + */
  81 +std::string toShortGuidAppendedString(const std::string& str, const MqttId& mqttId);
  82 +
  83 +/**
  84 + * @brief Converts from PosixTime (time_t) to StdTime.
  85 + * @param posixTime The Posix Time (time_t) to convert.
  86 + * @return The StdTime with same value as the provided posixTime.
  87 + */
  88 +StdTime toStdTime(const std::time_t& posixTime);
  89 +
  90 +/**
  91 + * @brief Converts from StdTime to PosixTime (time_t).
  92 + * @param stdTime The StdTime to convert.
  93 + * @return The PosixTime with the same value as the provided stdTime.
  94 + */
  95 +time_t toPosixTime(const osdev::components::mqtt::StdTime& stdTime);
  96 +
  97 +/**
  98 + * @brief Converts the specified posixTimeString to an OptionalTime.
  99 + * @param posixTimeString A posix time as string.
  100 + * @return The converted posixTimeString.
  101 + * @retval boost::none if the specified posixTimeString could not be converted to a StdTime.
  102 + */
  103 +osdev::components::mqtt::OptionalTime toOptionalTime(const std::string& posixTimeString);
  104 +
  105 +} // End namespace MqttTypeConverter
  106 +} // End namespace mqtt
  107 +} // End namespace components
  108 +} // End namespace osdev
  109 +
  110 +#endif // OSDEV_COMPONENTS_MQTT_MQTTTYPECONVERTER_H
... ...
include/mqttutil.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MQTTUTIL_H
  23 +#define OSDEV_COMPONENTS_MQTT_MQTTUTIL_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +namespace osdev {
  29 +namespace components {
  30 +namespace mqtt {
  31 +
  32 +/*!
  33 + * \brief Determine if topic is a valid mqtt topic filter.
  34 + * \param topic - The topic to test.
  35 + * \return True when topic is valid, false otherwise.
  36 + */
  37 +bool isValidTopic( const std::string &topic );
  38 +
  39 +/*!
  40 + * \brief Test a topic against another topicfilter for overlap.
  41 + * \param existingTopic - The topic to test against
  42 + * \param newTopic - The topic to test.
  43 + * \return True when topics overlap, false otherwise
  44 + */
  45 +bool testForOverlap( const std::string & existingTopic, const std::string &newTopic );
  46 +
  47 +/*!
  48 + * \brief Test a topic for occurence of wildcards
  49 + * \param topic - The topic to test
  50 + * \return True if topics contains wildcards, false otherwise
  51 + */
  52 +bool hasWildcard( const std::string &topic );
  53 +
  54 +/*!
  55 + * \brief Create a regular expression string based on a topicfilter that can be used
  56 + * to match topic strings against topics with no wildcards.
  57 + * \pre The topic filter is valid.
  58 + * \return The regular expression string. If the topic filter is not valid then the
  59 + * returned string is also not valid.
  60 + */
  61 +std::string convertTopicToRegex( const std::string &topic );
  62 +
  63 +} // End namespace mqtt
  64 +} // End namespace components
  65 +} // osdev
  66 +
  67 +#endif // OSDEV_COMPONENTS_MQTT_MQTTUTIL_H
... ...
include/scopeguard.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_SCOPEGUARD_H
  23 +#define OSDEV_COMPONENTS_MQTT_SCOPEGUARD_H
  24 +
  25 +// std
  26 +#include <functional>
  27 +
  28 +#include "macrodefs.h"
  29 +#include "utils.h"
  30 +
  31 +#define OSDEV_COMPONENTS_SCOPEGUARD(variableName, ...) \
  32 + osdev::components::mqtt::ScopeGuard OSDEV_COMPONENTS_COMBINE(Scope__Guard__##variableName##__, __LINE__)(__VA_ARGS__); \
  33 + osdev::components::mqtt::apply_unused_parameters(OSDEV_COMPONENTS_COMBINE(Scope__Guard__##variableName##__, __LINE__));
  34 +
  35 +namespace osdev {
  36 +namespace components {
  37 +namespace mqtt {
  38 +
  39 +using CleanUpFunction = std::function<void() noexcept>;
  40 +
  41 +/**
  42 + * @brief Ensures that a cleanup function is called at the end of the current scope.
  43 + */
  44 +class ScopeGuard
  45 +{
  46 +public:
  47 + /**
  48 + * @brief Constructs an empty scopeguard.
  49 + * The scopeguard can be set by moving another ScopeGuard into this one.
  50 + */
  51 + ScopeGuard();
  52 +
  53 + /**
  54 + * @brief Constructs a RAII instance that will call cleanupFunc in it's destructor.
  55 + * @param cleanupFunc The cleanup function to call at the end of the current scope.
  56 + * This cleanup function must not throw exceptions. If it does, the behavior is undefined.
  57 + */
  58 + ScopeGuard(const CleanUpFunction& cleanupFunc);
  59 +
  60 + // Movable, not copyable
  61 + ScopeGuard(const ScopeGuard&) = delete;
  62 + ScopeGuard& operator=(const ScopeGuard&) = delete;
  63 + ScopeGuard(ScopeGuard&&) = default;
  64 + ScopeGuard& operator=(ScopeGuard&&) = default;
  65 +
  66 + ~ScopeGuard() noexcept;
  67 +
  68 +private:
  69 + CleanUpFunction m_cleanupFunc;
  70 +};
  71 +
  72 +} // End namespace mqtt
  73 +} // End namespace components
  74 +} // End namespace osdev
  75 +
  76 +#endif // OSDEV_COMPONENTS_MQTT_SCOPEGUARD_H
... ...
include/serverstate.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_SERVERSTATE_H
  23 +#define OSDEV_COMPONENTS_MQTT_SERVERSTATE_H
  24 +
  25 +// std
  26 +#include <atomic>
  27 +#include <map>
  28 +
  29 +// boost
  30 +#include <boost/signals2/connection.hpp>
  31 +
  32 +// osdev::components::mqtt
  33 +#include "istatecallback.h"
  34 +
  35 +namespace osdev {
  36 +namespace components {
  37 +namespace mqtt {
  38 +
  39 +/*!
  40 + * \brief Class for administrating ServerState callbacks.
  41 + * ServiceClientBase uses this object to notify the server state listeners
  42 + * of changes in the serverstate of the server that the client is connected to.
  43 + */
  44 +class ServerState
  45 +{
  46 +public:
  47 + /*!
  48 + * \brief Constructs a ServerState object. This object has a one to one relation with an IStateCallback object.
  49 + * \param stateCallbackIf identification of the interface that generates the signal.
  50 + */
  51 + explicit ServerState(const IStateCallback* stateCallbackIf);
  52 +
  53 + /*!
  54 + * \brief Destroy the ServerState.
  55 + * Calls clearAllStateChangeCallbacks() to unregister itself from the listeners.
  56 + */
  57 + virtual ~ServerState();
  58 +
  59 + // non copyable, non movable
  60 + ServerState(const ServerState&) = delete;
  61 + ServerState& operator=(ServerState&) = delete;
  62 + ServerState(ServerState&&) = delete;
  63 + ServerState& operator=(ServerState&&) = delete;
  64 +
  65 + /*!
  66 + * \brief Registers a statechange callback method.
  67 + * \param cb - The callback method.
  68 + * \return handle that identifies the callback method.
  69 + */
  70 + StateChangeCallbackHandle registerStateChangeCallback(const IStateCallback::SlotStateChange& cb);
  71 +
  72 + /*!
  73 + * \brief Unregisters a state change callback method.
  74 + * \param handle Handle that identifies the callback method.
  75 + */
  76 + void unregisterStateChangeCallback(StateChangeCallbackHandle handle);
  77 +
  78 + /*!
  79 + * \brief Removes all callback methods.
  80 + * An Unregister state is signalled to the listeners.
  81 + */
  82 + void clearAllStateChangeCallbacks();
  83 +
  84 + /*!
  85 + * \brief Emit the State changed signal.
  86 + * \param newState - The new state.
  87 + */
  88 + void emitStateChanged(StateEnum newState);
  89 +
  90 + /*!
  91 + * \brief Return the last state received from server.
  92 + * \return state of server
  93 + */
  94 + StateEnum state() const;
  95 +
  96 + /*!
  97 + * \brief Returns the handle that will be given to the next callback that is registered.
  98 + */
  99 + static StateChangeCallbackHandle nextHandle()
  100 + {
  101 + return s_nextServerStateCallbackHandle;
  102 + }
  103 +
  104 +private:
  105 + /*!
  106 + * Type for holding connections to server state callback functions.
  107 + */
  108 + using ServerStateCallbackMap = std::map<StateChangeCallbackHandle, boost::signals2::scoped_connection>;
  109 +
  110 + IStateCallback::SigStateChange sig_serverStateChanged; ///< Signal emitted when server state has changed.
  111 +
  112 + const IStateCallback* m_stateCallbackIf; ///< Identification of the the interface that generates the signal (not owned)
  113 + ServerStateCallbackMap m_serverStateCallbackMap; ///< Map with serverstate callback connections.
  114 +
  115 + static std::atomic<StateChangeCallbackHandle> s_nextServerStateCallbackHandle; ///< Handle given to next serverstate callback registration (0 is not valid).
  116 +
  117 + StateEnum m_state; ///< Store the last state received from the server.
  118 +
  119 + /*!
  120 + * \return The clientId of the IStateCallback interface, or "null" if it is nullptr.
  121 + */
  122 + std::string stateCallbackClientId() const;
  123 +
  124 + static const std::string s_identifier;
  125 +};
  126 +
  127 +} // End namespace mqtt
  128 +} // End namespace components
  129 +} // End namespace osdev
  130 +
  131 +#endif // OSDEV_COMPONENTS_MQTT_SERVERSTATE_H
... ...
include/sharedreaderlock.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_SHAREDREADERLOCK_H
  23 +#define OSDEV_COMPONENTS_MQTT_SHAREDREADERLOCK_H
  24 +
  25 +// std
  26 +#include <condition_variable>
  27 +#include <map>
  28 +#include <mutex>
  29 +#include <thread>
  30 +
  31 +// mlogic::common
  32 +#include "scopeguard.h"
  33 +
  34 +namespace osdev {
  35 +namespace components {
  36 +namespace mqtt {
  37 +
  38 +#define OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(lockvar) \
  39 + lockvar.lockShared(); \
  40 + OSDEV_COMPONENTS_SCOPEGUARD(lockvar, [&]() { lockvar.unlock(); });
  41 +
  42 +#define OSDEV_COMPONENTS_EXCLUSIVELOCK_SCOPE(lockvar) \
  43 + lockvar.lockExclusive(); \
  44 + OSDEV_COMPONENTS_SCOPEGUARD(lockvar, [&]() { lockvar.unlock(); });
  45 +
  46 +/**
  47 + * @brief Class is used to administrate the lock data.
  48 + */
  49 +class LockData
  50 +{
  51 +public:
  52 + /**
  53 + * @brief Default constructable. Lock is not active and the count is 0.
  54 + */
  55 + LockData()
  56 + : m_count(0)
  57 + , m_active(false)
  58 + {
  59 + }
  60 +
  61 + // Copyable, movable
  62 + LockData(const LockData&) = default;
  63 + LockData& operator=(const LockData&) = default;
  64 + LockData(LockData&&) = default;
  65 + LockData& operator=(LockData&&) = default;
  66 +
  67 + /**
  68 + * @return true when the lock is active, false otherwise.
  69 + * @note A lock becomes active the first time that increase() is called.
  70 + */
  71 + inline bool active() const
  72 + {
  73 + return m_active;
  74 + }
  75 +
  76 + /**
  77 + * @brief Increases the lock count by one.
  78 + * An inactive lock becomes active by this call.
  79 + */
  80 + inline void increase()
  81 + {
  82 + m_active = true;
  83 + ++m_count;
  84 + }
  85 +
  86 + /**
  87 + * @brief Decreases the lock count by one.
  88 + * The count is only decreased for active locks. When the lock count becomes 0 the lock
  89 + * is deactivated.
  90 + * @return true when the lock is still active after decrease and false when it is deactivated.
  91 + */
  92 + inline bool decrease()
  93 + {
  94 + if (m_active) {
  95 + --m_count;
  96 + m_active = (0 != m_count);
  97 + }
  98 + return m_active;
  99 + }
  100 +
  101 + /**
  102 + * @brief Conversion operator that returns the lock count.
  103 + */
  104 + inline operator std::size_t() const
  105 + {
  106 + return m_count;
  107 + }
  108 +
  109 + /**
  110 + * @brief Static method for initializing a lock data based on already existing lock data.
  111 + * The new lock data is not active.
  112 + * @note This is used to promote a shared lock to an exclusive lock.
  113 + */
  114 + inline static LockData initialize(const LockData& other)
  115 + {
  116 + auto newLockData(other);
  117 + newLockData.m_active = false;
  118 + return newLockData;
  119 + }
  120 +
  121 +private:
  122 + std::size_t m_count; ///< The lock count.
  123 +
  124 + /**
  125 + * @brief Flag to indicate whether the lock is active.
  126 + * This flag is necessary because when the lock is promoted
  127 + * the lock count is not zero but the lock still should be activated again.
  128 + */
  129 + bool m_active;
  130 +};
  131 +
  132 +/**
  133 + * @brief Lock class that allows multiple readers to own the lock in a shared way.
  134 + * A writer will want exclusive ownership so that it can mutate the content that
  135 + * is protected by this lock.
  136 + *
  137 + * Reader and writer should be interpreted as to how threads interact with the content that this lock protects. It is up
  138 + * to the caller to enforce the correct behaviour. In other words don't take a shared lock and change the content!
  139 + *
  140 + * The administration of this class uses the std::thread::id to register which thread holds what kind of lock.
  141 + * This id is reused, so be really careful to pair each lock with an unlock, otherwise newly spawned threads might
  142 + * end up having a lock without taking one.
  143 + */
  144 +class SharedReaderLock
  145 +{
  146 +public:
  147 + /**
  148 + * Default constructable.
  149 + * The lock is not locked.
  150 + */
  151 + SharedReaderLock();
  152 +
  153 + /**
  154 + * Destructor will throw when there are threads registered.
  155 + */
  156 + ~SharedReaderLock();
  157 +
  158 + // Non copyable, non movable
  159 + SharedReaderLock(const SharedReaderLock&) = delete;
  160 + SharedReaderLock& operator=(const SharedReaderLock&) = delete;
  161 + SharedReaderLock(SharedReaderLock&&) = delete;
  162 + SharedReaderLock& operator=(SharedReaderLock&&) = delete;
  163 +
  164 + /**
  165 + * @brief Lock in a shared way. For read only operations.
  166 + * Multiple threads can have shared ownership on this lock.
  167 + * It is guaranteed that a call to lockExclusive will wait until all read locks are unlocked.
  168 + * When a call to lockExclusive is made and is waiting, no new reader locks are accepted.
  169 + * A thread that owns a shared lock can lock again. The lock will be unlocked for this thread when as many unlock calls are made.
  170 + * A thread that owns a shared lock can upgrade the lock to an exclusive lock by calling lockExclusive. The thread has to wait
  171 + * for exclusive ownership and has the exclusive lock until all unlocks are made (if it had done multiple shared locks before an exclusive lock).
  172 + */
  173 + void lockShared();
  174 +
  175 + /**
  176 + * @brief Lock in an exclusive way. For write operations.
  177 + * Only one thread can have exclusive ownership of this lock.
  178 + * While a thread waits for exlusive ownership shared locks are denied. This lock is unfair in the
  179 + * sense that it favours write locks.
  180 + * A thread that owns exclusive ownership can make another exclusive lock or a even a shared lock. Both are
  181 + * treated as an exclusive lock that updates the lock count. As many unlocks need to be called to unlock the exclusive lock.
  182 + */
  183 + void lockExclusive();
  184 +
  185 + /**
  186 + * @brief Unlock the lock. The thread id is used to determine which lock needs to be unlocked.
  187 + * If a thread does not own this lock at all then nothing happens.
  188 + */
  189 + void unlock();
  190 +
  191 +private:
  192 + std::mutex m_mutex; ///< Mutex that protects the lock administration.
  193 + std::map<std::thread::id, LockData> m_readLockMap; ///< Map with read lock data.
  194 + std::map<std::thread::id, LockData> m_writeLockMap; ///< Map with write lock data.
  195 +
  196 + std::condition_variable m_readersCV; ///< lockShared waits on this condition variable.
  197 + std::condition_variable m_writersCV; ///< lockExclusive waits on this condition variable.
  198 +};
  199 +
  200 +} // End namespace mqtt
  201 +} // End namespace components
  202 +} // End namespace osdev
  203 +
  204 +#endif // OSDEV_COMPONENTS_MQTT_SHAREDREADERLOCK_H
... ...
include/stringify.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDSEV_COMPONENTS_MQTT_STRINGIFY_H
  23 +#define OSDSEV_COMPONENTS_MQTT_STRINGIFY_H
  24 +
  25 +// std
  26 +#include <sstream>
  27 +
  28 +// osdev::components::mqtt
  29 +#include "mqttstream.h"
  30 +
  31 +namespace osdev {
  32 +namespace components {
  33 +namespace mqtt {
  34 +
  35 +/**
  36 + * @brief Stringifies all objects for which an output stream operator is available.
  37 + * @note This method is meant to be used for printing and not for marshalling!
  38 + * @tparam T The object type.
  39 + * @param value The value to stringify.
  40 + * @return stringified value.
  41 + */
  42 +template <typename T>
  43 +std::string toString(const T& value)
  44 +{
  45 + std::ostringstream oss;
  46 + oss << value;
  47 + return oss.str();
  48 +}
  49 +
  50 +} // End namespace mqtt
  51 +} // End namespace components
  52 +} // End namespace osdev
  53 +
  54 +#endif // OSDEV_COMPONENTS_MQTT_STRINGIFY_H
... ...
include/stringutils.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_STRINGUTILS_H
  23 +#define OSDEV_COMPONENTS_MQTT_STRINGUTILS_H
  24 +
  25 +// See boost/algorithm/string.hpp for more string utility functions
  26 +
  27 +// std
  28 +#include <sstream>
  29 +#include <string>
  30 +#include <vector>
  31 +
  32 +namespace osdev {
  33 +namespace components {
  34 +namespace mqtt {
  35 +
  36 +/**
  37 + * @brief Removes characters from the specified string. Modifies the specified string in place.
  38 + * @param str The string to modify.
  39 + * @param charsToRemove The characters to remove from str.
  40 + */
  41 +void removeCharsFromStringInPlace(std::string& str, const std::string& charsToRemove);
  42 +
  43 +/**
  44 + * @brief Determines whether the specified string is all digits.
  45 + * @param str The string for which to determine if it's numeric.
  46 + * @return True if the specified string is numeric; otherwise, false.
  47 + */
  48 +bool is_numeric(const std::string& str);
  49 +
  50 +} // End namespace mqtt
  51 +} // End namespace components
  52 +} // End namespace osdev
  53 +
  54 +#endif // OSDEV_COMPONENTS_MQTT_STRINGUTILS_H
... ...
include/synchronizedqueue.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_SYNCHRONIZEDQUEUE_H
  23 +#define OSDEV_COMPONENTS_MQTT_SYNCHRONIZEDQUEUE_H
  24 +
  25 +// std
  26 +#include <atomic>
  27 +#include <condition_variable>
  28 +#include <memory>
  29 +#include <mutex>
  30 +#include <queue>
  31 +#include <string>
  32 +#include <type_traits>
  33 +
  34 +// osdev::components::mqtt
  35 +#include "lockguard.h"
  36 +#include "metaprogrammingdefs.h"
  37 +
  38 +namespace osdev {
  39 +namespace components {
  40 +namespace mqtt {
  41 +
  42 +OSDEV_COMPONENTS_HASMETHOD_TRAIT(capacity, )
  43 +
  44 +/*!
  45 + * \brief Generic Queue template for defining a thin
  46 + * wrapper around std::queue to make overflow detection possible.
  47 + * This template has no definition and will lead to a compile
  48 + * time error when it is chosen.
  49 + */
  50 +template <typename T, typename C, typename enable = void>
  51 +class Queue;
  52 +
  53 +/*!
  54 + * \brief A specialization when the underlying container has a capacity method.
  55 + * To detect overflow the capacity of the underlying container is needed.
  56 + * Not all containers have a capacity.
  57 + * When the capacity method is not available SFINAE will discard this template.
  58 + */
  59 +template <typename T, typename C>
  60 +class Queue<T, C, typename std::enable_if<has_capacity<C>::value>::type> : public std::queue<T, C>
  61 +{
  62 +public:
  63 + using size_type = typename std::queue<T, C>::size_type;
  64 +
  65 + typename C::size_type capacity() const
  66 + {
  67 + return this->c.capacity();
  68 + }
  69 +};
  70 +
  71 +/*!
  72 + * \brief A specialization for when the underlying container does not support a capacity
  73 + * In this case max_size is returned which results in overflow not being detected.
  74 + */
  75 +template <typename T, typename C>
  76 +class Queue<T, C, typename std::enable_if<!has_capacity<C>::value>::type> : public std::queue<T, C>
  77 +{
  78 +public:
  79 + using size_type = typename std::queue<T, C>::size_type;
  80 + typename C::size_type capacity() const
  81 + {
  82 + return this->c.max_size();
  83 + }
  84 +};
  85 +
  86 +/*!
  87 + * \brief Represents a synchronized queue
  88 + * @tparam T The type of the items in the queue
  89 + * @tparam C The underlying character. The container must satisfy the
  90 + * requirements of a SequenceContainer.
  91 + * Addittionally container must supply a pop_front and a max_size method.
  92 + *
  93 + * The underlying container determines the overflow behaviour.
  94 + * A circular buffer leads to a lossy queue that drops items when the
  95 + * queue is full while a std::deque will never overflow.
  96 + *
  97 + * The queue has the following states: started, paused, stopped.
  98 + * In the started state the queue acceptsincoming items and it allows items
  99 + * to be popped when data is available.
  100 + * In the paused state incoming items are allowed. The pop method will
  101 + * block until the queue is unpaused or stopped.
  102 + * In the stopped state incoming items are not allowed and dropped.
  103 + * The pop method will return false.
  104 + */
  105 +template <typename T, typename C = std::deque<T>>
  106 +class SynchronizedQueue
  107 +{
  108 +public:
  109 + using QueueType = Queue<T, C>;
  110 +
  111 + /*!
  112 + * \brief Constructs an empty queue
  113 + * \param id - Identification string for this queue ( used in logging ).
  114 + * \param paused - The state in which to setup the queue ( pause or active),
  115 + * ( Default is active )
  116 + */
  117 + explicit SynchronizedQueue( const std::string &id, bool paused = false )
  118 + : m_id( id )
  119 + , m_queueMutex()
  120 + , m_dataAvailableOrStopCV()
  121 + , m_queue()
  122 + , m_stop( false )
  123 + , m_pause( paused )
  124 + , m_numOverflows( 0 )
  125 + {}
  126 +
  127 + /*!
  128 + * \brief Stops the queue on destruction
  129 + */
  130 + ~SynchronizedQueue()
  131 + {
  132 + this->stop();
  133 + }
  134 +
  135 + /*!
  136 + * @brief Pushes the item in the queue
  137 + * @tparam TItem - The cv qualified type of the value to push.
  138 + * In a stopped state the queue drops incoming data.
  139 + * In a paused / active state the queue accepts incoming data.
  140 + * @tparam item - The item to push to the queue.
  141 + */
  142 + template <typename TItem>
  143 + void push(TItem &&item)
  144 + {
  145 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC( m_queueMutex, m_id );
  146 + if( m_stop )
  147 + {
  148 + return;
  149 + }
  150 +
  151 + if( m_queue.capacity() == m_queue.size() )
  152 + {
  153 + if( m_numOverflows++ % 100 == 0 )
  154 + {
  155 + // Log a warning that there is a number of overflows.
  156 + }
  157 + }
  158 + m_queue.push( std::forward<TItem>(item) );
  159 + OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id);
  160 + m_dataAvailableOrStopCV.notify_one();
  161 + }
  162 +
  163 + /*!
  164 + * @brief pop - Pops an item from the queue. This method blocks when te state is paused or when there is no data available.
  165 + * @param item - The item to which to copy the popped item.
  166 + * @return True if an item was popped: otherwise false
  167 + */
  168 + bool pop(T& item)
  169 + {
  170 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC( m_queueMutex, m_id );
  171 + m_dataAvailableOrStopCV.wait(OSDEV_COMPONENTS_UNIQUELOCK(m_queueMutex), [this]()
  172 + { return ((this->m_queue.size() > 0 && !m_pause) || this->m_stop); });
  173 + if( m_stop )
  174 + {
  175 + return false;
  176 + }
  177 + item = std::move(m_queue.front());
  178 + m_queue.pop();
  179 + return true;
  180 + }
  181 +
  182 + bool pop(std::vector<T> &items)
  183 + {
  184 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC( m_queueMutex, m_id );
  185 + m_dataAvailableOrStopCV.wait(OSDEV_COMPONENTS_UNIQUELOCK(m_queueMutex), [this]()
  186 + { return ((this->m_queue.size() > 0 && !m_pause) || this->m_stop); });
  187 + if( m_stop )
  188 + {
  189 + return false;
  190 + }
  191 + items.clear();
  192 + items.reserve(m_queue.size());
  193 + while( m_queue.size() > 0 )
  194 + {
  195 + items.emplace_back(std::move(m_queue.front() ) );
  196 + m_queue.pop();
  197 + }
  198 + return true;
  199 + }
  200 +
  201 + /*!
  202 + * \return The current size of the queue
  203 + */
  204 + typename QueueType::size_type size() const
  205 + {
  206 + OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC(m_queueMutex, m_id);
  207 + return m_queue.size();
  208 + }
  209 +
  210 +
  211 + /*!
  212 + * \brief Start the Queue
  213 + * The queue is only started when it is in a stopped state.
  214 + * \param paused - If true, the queue will be started in a paused
  215 + * state which means that no items will be popped.
  216 + */
  217 + void start(bool paused)
  218 + {
  219 + // Reason that a lock is used: See documentation of std::condition_variable
  220 + //
  221 + // Even is the shared variable is atomic (m_stop in this case), it must be modified under the mutex
  222 + // in order to correctly publish the modification to the waiting thread.
  223 + //
  224 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(m_queueMutex, m_id);
  225 + if( !m_stop )
  226 + {
  227 + // already started
  228 + return;
  229 + }
  230 + m_stop = false;
  231 + m_pause = paused;
  232 + OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id);
  233 + if( !paused )
  234 + {
  235 + m_dataAvailableOrStopCV.notify_all();
  236 + }
  237 + }
  238 +
  239 + /*!
  240 + * \brief Pause or unpause the queue.
  241 + * When the queue is paused no items will be popped.
  242 + * The state is not altered when the queue is stopped.
  243 + * \param value - Flag that indicates whether the queue is paused or unpaused.
  244 + */
  245 + void pause(bool value)
  246 + {
  247 + // Reason that a lock is used: see documentation of std::condition_variable
  248 + //
  249 + // Even if the shared variable is atomic (m_stop in this case), it must be modified under the mutex
  250 + // in order to correctly publish the modification to the waiting thread.
  251 + //
  252 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(m_queueMutex, m_id);
  253 + if (m_stop) {
  254 + return;
  255 + }
  256 + m_pause = value;
  257 + OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id);
  258 + if (!value) {
  259 + m_dataAvailableOrStopCV.notify_all();
  260 + }
  261 + }
  262 +
  263 + /*!
  264 + * \brief Stop the queue.
  265 + * The pop method will return a false after calling this method.
  266 + * The queue can be restarted with the start method.
  267 + */
  268 + void stop()
  269 + {
  270 + // Reason that a lock is used: see documentation of std::condition_variable
  271 + //
  272 + // Even if the shared variable is atomic (m_stop in this case), it must be modified under the mutex
  273 + // in order to correctly publish the modification to the waiting thread.
  274 + //
  275 + OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(m_queueMutex, m_id);
  276 + m_stop = true;
  277 + m_pause = false;
  278 + OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id);
  279 + m_dataAvailableOrStopCV.notify_all();
  280 + }
  281 +
  282 + /*!
  283 + * \brief Clears the queue.
  284 + * This method also resets the overflow counter.
  285 + */
  286 + void clear()
  287 + {
  288 + OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC(m_queueMutex, m_id);
  289 + QueueType emptyQueue;
  290 + std::swap(m_queue, emptyQueue);
  291 + m_numOverflows = 0;
  292 + }
  293 +
  294 +private:
  295 + const std::string m_id; ///< Queue identification string
  296 + mutable std::mutex m_queueMutex; ///< Protects access to the queue
  297 + std::condition_variable m_dataAvailableOrStopCV; ///< Provides wait functionality for the queue becoming empty
  298 + QueueType m_queue; ///< Holds the items
  299 + std::atomic_bool m_stop; ///< Flag that indicates whether the queue needs to stop.
  300 + std::atomic_bool m_pause; ///< Flag that indicates whether the queue is paused.
  301 +
  302 + /*!
  303 + * \brief Counts the number of items that the buffer overflows.
  304 + * If the underlying buffer is a ring buffer an overflow
  305 + * means that an item will be overwritten. For a normal
  306 + * sequence container it means that the it is enlarged.
  307 + */
  308 + std::uint32_t m_numOverflows;
  309 +};
  310 +
  311 +
  312 +
  313 +
  314 +
  315 +} // End namespace mqtt
  316 +} // End namespace components
  317 +} // End namespace osdev
  318 +
  319 +#endif // OSDEV_COMPONENTS_MQTT_SYNCHRONIZEDQUEUE_H
... ...
include/timemeasurement.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_TIMEMEASUREMENT_H
  23 +#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_TIMEMEASUREMENT_H
  24 +
  25 +#include <chrono>
  26 +#include <functional>
  27 +#include <ostream>
  28 +#include <string>
  29 +
  30 +namespace osdev {
  31 +namespace components {
  32 +namespace mqtt {
  33 +namespace measurement {
  34 +
  35 +using TimeMeasurementCallback = std::function<void(const std::string& id, std::chrono::steady_clock::time_point start, std::chrono::microseconds sinceStart, std::chrono::microseconds sinceLast)>;
  36 +
  37 +class TimeMeasurement
  38 +{
  39 +public:
  40 + TimeMeasurement(const std::string& id, const TimeMeasurementCallback& callback, bool measureOnDestruction = true);
  41 + ~TimeMeasurement();
  42 +
  43 + TimeMeasurement(const TimeMeasurement&) = delete;
  44 + TimeMeasurement& operator=(const TimeMeasurement&) = delete;
  45 + TimeMeasurement(TimeMeasurement&&) = default;
  46 + TimeMeasurement& operator=(TimeMeasurement&&) = default;
  47 +
  48 + void set();
  49 + void measure();
  50 +
  51 +private:
  52 + std::string m_id;
  53 +
  54 + std::chrono::steady_clock::time_point m_start;
  55 + std::chrono::steady_clock::time_point m_last;
  56 + TimeMeasurementCallback m_callback;
  57 + bool m_measureOnDestruction;
  58 +};
  59 +
  60 +template <typename Rep, typename Dur>
  61 +std::ostream& operator<<(std::ostream& os, const std::chrono::duration<Rep, Dur>& rhs)
  62 +{
  63 + os << rhs.count();
  64 + return os;
  65 +}
  66 +
  67 +} // End namespace measurement
  68 +} // End namespace mqtt
  69 +} // End namespace components
  70 +} // End namespace osdev
  71 +
  72 +#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_TIMEMEASUREMENT_H
... ...
include/token.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_TOKEN_H
  23 +#define OSDEV_COMPONENTS_MQTT_TOKEN_H
  24 +
  25 +// std
  26 +#include <ostream>
  27 +#include <string>
  28 +
  29 +// paho
  30 +#include <MQTTAsync.h>
  31 +
  32 +namespace osdev {
  33 +namespace components {
  34 +namespace mqtt {
  35 +
  36 +/*!
  37 + * \brief The Token class defines an operation token
  38 + */
  39 +class Token
  40 +{
  41 +public:
  42 + /*! @brief Construct an invalid token.
  43 + * The token number is -1 in that case. The client is undefined, in this case empty.
  44 + */
  45 + Token();
  46 +
  47 + /*! @brief Constructs token for an operation originating from specific client wrapper.
  48 + * @param clientId - Identifies the client wrapper
  49 + * @param tokenNr - Identifies the operation done on that client.
  50 + */
  51 + Token( const std::string &clientId, std::int32_t tokenNr );
  52 +
  53 + /*! @return True when token has a valid token number, false otherwise. */
  54 + bool isValid() const { return -1 == m_token; }
  55 +
  56 + /*! @return The operation token */
  57 + const std::string& clientId() const { return m_clientId; }
  58 +
  59 + /*! @return The operation token */
  60 + std::int32_t token() const { return m_token; }
  61 +
  62 + /*! @return True if Tokens have the same clientId and token number, false otherwise. */
  63 + bool equals( const Token &rhs ) const;
  64 +
  65 + /*!
  66 + * @brief Token is ordered.
  67 + * First on lexical test of clientId and with same clientId on token number.
  68 + */
  69 + bool smallerThan( const Token &rhs ) const;
  70 +
  71 +private:
  72 + std::string m_clientId; ///< Identified the client
  73 + std::int32_t m_token; ///< Identifies the operation on that client.
  74 +};
  75 +
  76 +/**
  77 + * @return True if Tokens have the same clientId and token number, false otherwise.
  78 + */
  79 +inline bool operator==( const Token &lhs, const Token &rhs )
  80 +{
  81 + return lhs.equals( rhs );
  82 +}
  83 +
  84 +inline bool operator==( const Token &lhs, std::int32_t rhs )
  85 +{
  86 + return lhs.token() == rhs;
  87 +}
  88 +
  89 +inline bool operator==( std::int32_t lhs, const Token &rhs )
  90 +{
  91 + return lhs == rhs;
  92 +}
  93 +
  94 +template <typename TLeft, typename TRight>
  95 +inline bool operator!=( const TLeft &lhs, const TRight &rhs )
  96 +{
  97 + return !( lhs == rhs );
  98 +}
  99 +
  100 +/*!
  101 + * @return True if Token lhs is smaller than token rhs
  102 + */
  103 +inline bool operator<( const Token &lhs, std::int32_t rhs )
  104 +{
  105 + return lhs.token() < rhs;
  106 +}
  107 +
  108 +inline bool operator<( std::int32_t lhs, const Token &rhs )
  109 +{
  110 + return lhs < rhs.token();
  111 +}
  112 +
  113 +inline bool operator<( const Token &lhs, const Token &rhs )
  114 +{
  115 + return lhs.smallerThan( rhs );
  116 +}
  117 +
  118 +/*!
  119 + * @brief Stream operator for a Token
  120 + */
  121 +std::ostream& operator<<( std::ostream &os, const Token &rhs );
  122 +
  123 +} // End namespace mqtt
  124 +} // End namespace components
  125 +} // End namespace osdev
  126 +
  127 +#endif // OSDEV_COMPONENTS_MQTT_TOKEN_H
... ...
include/uriparser.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_URIPARSER_H
  23 +#define OSDEV_COMPONENTS_MQTT_URIPARSER_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +#include "commondefs.h"
  29 +
  30 +namespace osdev {
  31 +namespace components {
  32 +namespace mqtt {
  33 +
  34 +/**
  35 + * @brief Helper class to parse, normalize, and replace the port service name by portnumber in a uri.
  36 + * example:
  37 + * opc.tcp://user:secret\@processmodel:processmodel/path1/path2?query3=value4#fragment5
  38 + * will result in
  39 + * opc.tcp://user:secret\@processmodel:12345/path1/path2?query3=value4#fragment5
  40 + * @note This assumes that the port service name was registered in the etc/services file under protocol tcp.
  41 + * Lookup of a portnumber for protocols other than tcp are not supported.
  42 + *
  43 + * IPv6 addresses in a uri are only parsable when they are wrapped in brackets ([]) (RFC 3986, section 3.2.2)
  44 + *
  45 + * @note This class is designed to handle a subset of all possible uris. The scheme and the authority part are
  46 + * expected not to be empty. The authority part can contain user and password information.
  47 + *
  48 + * @note Only scheme, hostname and port are converted to lowercase (see RFC 3986 6.2.2.1. Case Normalization)
  49 + *
  50 + * @note Special characters are escaped with percent encoding. This applies to user, password, path, query and fragment parts.
  51 + * The path and query part is escaped on a per element basis. This means that ParsedUri contains the raw query and path strings.
  52 + * The decoding is done when the methods parseQuery and parsePath are called.
  53 + */
  54 +class UriParser
  55 +{
  56 +public:
  57 + /**
  58 + * @brief Parses the specified uri string.
  59 + * @param uri The uri string to parse.
  60 + * @return A map containing the parsed uri.
  61 + * @note The path and query parts can still contain percent encoded special characters.
  62 + */
  63 + static ParsedUri parse(const std::string& uri);
  64 +
  65 + /**
  66 + * @brief Parse the query part of a parsed uri. Percent encoded special characters are decoded.
  67 + * @param parsedUri A ParsedUri object.
  68 + * @return key/value map.
  69 + */
  70 + static ParsedQuery parseQuery(const ParsedUri& parsedUri);
  71 +
  72 + /**
  73 + * @brief Parse the path part of a parsed uri. Percent encoded special characters are decoded.
  74 + * @param parsedUri A ParsedUri object.
  75 + * @return vector of path elements.
  76 + */
  77 + static ParsedPath parsePath(const ParsedUri& parsedUri);
  78 +
  79 + /**
  80 + * @brief Parses, normalizes, and replaces the port service name by portnumber in the specified uri string.
  81 + * @param uri The uri string to parse and normalize.
  82 + * @return The normalized uri string, with the port service name replaced by the portnumber.
  83 + */
  84 + static std::string normalize(const std::string& uri);
  85 +
  86 + /**
  87 + * @brief Converts a parsed uri back to a string.
  88 + * @param parsedUri The parsed uri to convert to string.
  89 + * @return The uri as string.
  90 + */
  91 + static std::string toString(const ParsedUri& parsedUri);
  92 +
  93 + /**
  94 + * @brief Get portnumber associated with a service.
  95 + * @param serviceName Name of the service for which a portnumber is searched.
  96 + * @param protocolName Name of the protocol for which the portname was registered.
  97 + * @return portnumber.
  98 + * @throws InvalidArgumentException when service is unknown.
  99 + * @throws SystemException when underlying systemcall fails.
  100 + */
  101 + static int getPortnumber(const std::string& serviceName, const std::string& protocolName = "tcp");
  102 +};
  103 +
  104 +} // End namespace mqtt
  105 +} // End namespace components
  106 +} // End namespace osdev
  107 +
  108 +#endif // OSDEV_COMPONENTS_MQTT_URIPARSER_H
... ...
include/uriutils.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_URIUTILS_H
  23 +#define OSDEV_COMPONENTS_MQTT_URIUTILS_H
  24 +
  25 +// std
  26 +#include <string>
  27 +
  28 +#include "compiletimedigits.h"
  29 +
  30 +namespace osdev {
  31 +namespace components {
  32 +namespace mqtt {
  33 +
  34 +/**
  35 + * @brief Get the percent encoded string for a given character.
  36 + * @tparam N The character to encode.
  37 + * @return pointer to the encoded character string.
  38 + */
  39 +template <unsigned N>
  40 +inline const char* percentEncode()
  41 +{
  42 + static const auto* s_code =
  43 + (compiletime_string<'%'>{} + typename apply_bounded_range< //
  44 + 0, //
  45 + numberOfDigits<16>(N), //
  46 + string_builder< //
  47 + adapt_for_string_builder< //
  48 + ProduceDigits<N, 16>>>::template produce>::result{})
  49 + .chars;
  50 + return s_code;
  51 +}
  52 +
  53 +} // End namespace mqtt
  54 +} // End namespace components
  55 +} // End namespace osdev
  56 +
  57 +#endif // OSDEV_COMPONENTS_MQTT_URIUTILS_H
... ...
include/utils.h 0 → 100644
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +#ifndef OSDEV_COMPONENTS_MQTT_UTILS_H
  23 +#define OSDEV_COMPONENTS_MQTT_UTILS_H
  24 +
  25 +// std
  26 +#include <algorithm>
  27 +#include <functional>
  28 +#include <memory>
  29 +#include <vector>
  30 +
  31 +namespace osdev {
  32 +namespace components {
  33 +namespace mqtt {
  34 +
  35 +/**
  36 + * @brief Does nothing.
  37 + * Utility template function to explicitly use parameters, that would otherwise be unused.
  38 + * This helps to prevent the -Wunused-parameter warning.
  39 + */
  40 +template <typename... Args>
  41 +void apply_unused_parameters(const Args&...)
  42 +{
  43 +}
  44 +
  45 +/**
  46 + * @brief Converts (dynamic_cast) a std::unique_ptr from TFrom to TTo.
  47 + * @param from The std::unique_ptr to convert from TFrom to TTo.
  48 + * @return The converted std::unique_ptr.
  49 + */
  50 +template <typename TTo, typename TFrom>
  51 +std::unique_ptr<TTo> dynamic_unique_ptr_cast(std::unique_ptr<TFrom>&& from)
  52 +{
  53 + auto to = dynamic_cast<TTo*>(from.get());
  54 + if (nullptr == to) {
  55 + return std::unique_ptr<TTo>(nullptr);
  56 + }
  57 +
  58 + from.release();
  59 + return std::unique_ptr<TTo>(to);
  60 +}
  61 +
  62 +/**
  63 + * @brief Converts (dynamic_cast) a std::unique_ptr from TFrom to TTo.
  64 + * @param from The std::unique_ptr to convert from TFrom to TTo.
  65 + * @return The converted std::unique_ptr.
  66 + */
  67 +template <typename TTo, typename TFrom, typename TDeleter>
  68 +std::unique_ptr<TTo, TDeleter> dynamic_unique_ptr_cast(std::unique_ptr<TFrom, TDeleter>&& from)
  69 +{
  70 + auto to = dynamic_cast<TTo*>(from.get());
  71 + if (nullptr == to) {
  72 + return std::unique_ptr<TTo, TDeleter>(nullptr, from.get_deleter());
  73 + }
  74 +
  75 + from.release();
  76 + return std::unique_ptr<TTo, TDeleter>(to, std::move(from.get_deleter()));
  77 +}
  78 +
  79 +/**
  80 + * @brief Helper class for iteration of keys of a map.
  81 + */
  82 +template <typename TMap>
  83 +class KeyIterator : public TMap::iterator
  84 +{
  85 +public:
  86 + typedef typename TMap::iterator MapIterator;
  87 + typedef typename MapIterator::value_type::first_type KeyType;
  88 +
  89 + KeyIterator(const MapIterator& other)
  90 + : TMap::iterator(other)
  91 + {
  92 + }
  93 +
  94 + KeyType& operator*()
  95 + {
  96 + return TMap::iterator::operator*().first;
  97 + }
  98 +};
  99 +
  100 +/**
  101 + * @brief Helper function to get the begin KeyIterator from a map.
  102 + */
  103 +template <typename MapType>
  104 +KeyIterator<MapType> KeyBegin(MapType& map)
  105 +{
  106 + return KeyIterator<MapType>(map.begin());
  107 +}
  108 +
  109 +/**
  110 + * @brief Helper function to get the end KeyIterator from a map.
  111 + */
  112 +template <typename MapType>
  113 +KeyIterator<MapType> KeyEnd(MapType& map)
  114 +{
  115 + return KeyIterator<MapType>(map.end());
  116 +}
  117 +
  118 +/**
  119 + * @brief Helper class for iteration of keys of a const map.
  120 + */
  121 +template <typename TMap>
  122 +class KeyConstIterator : public TMap::const_iterator
  123 +{
  124 + typedef typename TMap::const_iterator TMapIterator;
  125 + typedef typename TMapIterator::value_type::first_type TKeyType;
  126 +
  127 +public:
  128 + KeyConstIterator(const TMapIterator& other)
  129 + : TMapIterator(other)
  130 + {
  131 + }
  132 +
  133 + const TKeyType& operator*()
  134 + {
  135 + return TMapIterator::operator*().first;
  136 + }
  137 +};
  138 +
  139 +/**
  140 + * @brief Helper function to get the cbegin KeyConstIterator from a const map.
  141 + */
  142 +template <typename TMap>
  143 +KeyConstIterator<TMap> KeyBegin(const TMap& map)
  144 +{
  145 + return KeyConstIterator<TMap>(map.cbegin());
  146 +}
  147 +
  148 +/**
  149 + * @brief Helper function to get the cend KeyConstIterator from a const map.
  150 + */
  151 +template <typename TMap>
  152 +KeyConstIterator<TMap> KeyEnd(const TMap& map)
  153 +{
  154 + return KeyConstIterator<TMap>(map.cend());
  155 +}
  156 +
  157 +/**
  158 + * @brief Helper function to get the difference of the keys in two maps.
  159 + * @param map1 The first map for which to examine the keys.
  160 + * @param map2 The second map for which to examine the keys.
  161 + * @return Collection of keys present in map1, but not in map2.
  162 + * @note The types of the keys in the maps must be identical.
  163 + */
  164 +template <typename TMap1, typename TMap2>
  165 +std::vector<typename TMap1::key_type> keyDifference(
  166 + const TMap1& map1,
  167 + const TMap2& map2,
  168 + const std::function<bool(const typename TMap1::key_type& key1, const typename TMap1::key_type& key2)>& keyCompare = std::less<typename TMap1::key_type>())
  169 +{
  170 + static_assert(std::is_same<
  171 + typename TMap1::key_type,
  172 + typename TMap2::key_type>::value,
  173 + "Inconsistent key types.");
  174 +
  175 + typedef typename TMap1::key_type Key;
  176 + std::vector<Key> onlyInMap1;
  177 + std::set_difference(
  178 + KeyBegin(map1),
  179 + KeyEnd(map1),
  180 + KeyBegin(map2),
  181 + KeyEnd(map2),
  182 + std::inserter(onlyInMap1, onlyInMap1.begin()),
  183 + keyCompare);
  184 + return onlyInMap1;
  185 +}
  186 +
  187 +/**
  188 + * @brief Helper function to get the intersection of the keys in two maps.
  189 + * @param map1 The first map for which to examine the keys.
  190 + * @param map2 The second map for which to examine the keys.
  191 + * @return Collection of keys present in both maps.
  192 + * @note The types of the keys in the maps must be identical.
  193 + */
  194 +template <typename TMap1, typename TMap2>
  195 +std::vector<typename TMap1::key_type> keyIntersection(
  196 + const TMap1& map1,
  197 + const TMap2& map2,
  198 + const std::function<bool(const typename TMap1::key_type& key1, const typename TMap1::key_type& key2)>& keyCompare = std::less<typename TMap1::key_type>())
  199 +{
  200 + static_assert(std::is_same<
  201 + typename TMap1::key_type,
  202 + typename TMap2::key_type>::value,
  203 + "Inconsistent key types.");
  204 +
  205 + typedef typename TMap1::key_type Key;
  206 + std::vector<Key> inBothMaps;
  207 + std::set_intersection(
  208 + KeyBegin(map1),
  209 + KeyEnd(map1),
  210 + KeyBegin(map2),
  211 + KeyEnd(map2),
  212 + std::inserter(inBothMaps, inBothMaps.begin()),
  213 + keyCompare);
  214 + return inBothMaps;
  215 +}
  216 +
  217 +/**
  218 + * @brief Determine the absolute path of the binary that belongs to a process.
  219 + * @param pid Process Id ifor which to determine the path to the binary. If pid equals -1 then the pid of the current process is used.
  220 + * @return Absolute path to the binary.
  221 + * @throw SystemException if call to readlink fails.
  222 + * @throw PathException if path is to long.
  223 + * @note Works only for processes owned by the user that is calling this function.
  224 + */
  225 +std::string getPathToBinary(int pid = -1);
  226 +
  227 +} // End namespace mqtt
  228 +} // End namespace components
  229 +} // End namespace osdev
  230 +
  231 +#endif // OSDEV_COMPONENTS_MQTT_UTILS_H
... ...