diff --git a/src/MqttStreaming.h b/src/MqttStreaming.h new file mode 100644 index 0000000..4751de3 --- /dev/null +++ b/src/MqttStreaming.h @@ -0,0 +1,412 @@ +/* MqttStreaming.h - Fork of Streaming.h adding std::string and with some minor fixes + * (I have to speek to the author in order to include my changes to his library if possible) + **/ + +/* + Streaming.h - Arduino library for supporting the << streaming operator + Copyright (c) 2010-2012 Mikal Hart. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Version 6 library changes + Copyright (c) 2019 Gazoodle. All rights reserved. + + 1. _BASED moved to template to remove type conversion to long and + sign changes which break int8_t and int16_t negative numbers. + The print implementation still upscales to long for it's internal + print routine. + + 2. _PAD added to allow padding & filling of characters to the stream + + 3. _WIDTH & _WIDTHZ added to allow width printing with space padding + and zero padding for numerics + + 4. Simple _FMT mechanism ala printf, but without the typeunsafetyness + and no internal buffers for replaceable stream printing +*/ + +#ifndef ARDUINO_STREAMING +#define ARDUINO_STREAMING + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#ifndef STREAMING_CONSOLE +#include "WProgram.h" +#endif +#endif + +#include + +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) +// No stl library, so need trivial version of std::is_signed ... +namespace std { +template + struct is_signed { static const bool value = false; }; + template<> + struct is_signed { static const bool value = true; }; + template<> + struct is_signed { static const bool value = true; }; + template<> + struct is_signed { static const bool value = true; }; +}; +#else +#include +#endif + +#define STREAMING_LIBRARY_VERSION 6 + +#if !defined(typeof) +#define typeof(x) __typeof__(x) +#endif + +// PrintBuffer implementation of Print, a small buffer to print in +// see its use with pad_float() +template +class PrintBuffer : public Print +{ + size_t pos = 0; + char str[N] {}; +public: + inline const char *operator() () + { return str; }; + + // inline void clear() + // { pos = 0; str[0] = '\0'; }; + + inline size_t write(uint8_t c) + { return write(&c, 1); }; + + inline size_t write(const uint8_t *buffer, size_t size) + { + size_t s = std::min(size, N-1 - pos); // need a /0 left + if (s) + { + memcpy(&str[pos], buffer, s); + pos += s; + } + return s; + }; +}; + +// Generic template +template +inline Print &operator <<(Print &stream, const T &arg) +{ stream.print(arg); return stream; } + +// TODO sfinae maybe could do the trick ? +inline Print &operator <<(Print &stream, const std::string &str) +{ stream.print(str.c_str()); return stream; } + +template +struct _BASED +{ + T val; + int base; + _BASED(T v, int b): val(v), base(b) + {} +}; + +#if ARDUINO >= 100 + +struct _BYTE_CODE +{ + byte val; + _BYTE_CODE(byte v) : val(v) + {} +}; +#define _BYTE(a) _BYTE_CODE(a) + +inline Print &operator <<(Print &obj, const _BYTE_CODE &arg) +{ obj.write(arg.val); return obj; } + +#else + +#define _BYTE(a) _BASED(a, BYTE) + +#endif + +#define _HEX(a) _BASED(a, HEX) +#define _DEC(a) _BASED(a, DEC) +#define _OCT(a) _BASED(a, OCT) +#define _BIN(a) _BASED(a, BIN) + +// Specialization for class _BASED +// Thanks to Arduino forum user Ben Combee who suggested this +// clever technique to allow for expressions like +// Serial << _HEX(a); + +template +inline Print &operator <<(Print &obj, const _BASED &arg) +{ obj.print(arg.val, arg.base); return obj; } + +#if ARDUINO >= 18 +// Specialization for class _FLOAT +// Thanks to Michael Margolis for suggesting a way +// to accommodate Arduino 0018's floating point precision +// feature like this: +// Serial << _FLOAT(gps_latitude, 6); // 6 digits of precision + +struct _FLOAT +{ + double val; // only Print::print(double) + int digits; + _FLOAT(double v, int d): val(v), digits(d) + {} +}; + +inline Print &operator <<(Print &obj, const _FLOAT &arg) +{ obj.print(arg.val, arg.digits); return obj; } +#endif + +// Specialization for enum _EndLineCode +// Thanks to Arduino forum user Paul V. who suggested this +// clever technique to allow for expressions like +// Serial << "Hello!" << endl; + +enum _EndLineCode { endl }; + +inline Print &operator <<(Print &obj, _EndLineCode) +{ obj.println(); return obj; } + +// Specialization for padding & filling, mainly utilized +// by the width printers +// +// Use like +// Serial << _PAD(10,' '); // Will output 10 spaces +// Serial << _PAD(4, '0'); // Will output 4 zeros +struct _PAD +{ + int8_t width; + char chr; + _PAD(int8_t w, char c) : width(w), chr(c) {} +}; + +inline Print &operator <<(Print& stm, const _PAD &arg) +{ + for(int8_t i = 0; i < arg.width; i++) + stm.print(arg.chr); + return stm; +} + +// Specialization for width printing +// +// Use like Result +// -------- ------ +// Serial << _WIDTH(1,5) " 1" +// Serial << _WIDTH(10,5) " 10" +// Serial << _WIDTH(100,5) " 100" +// Serial << _WIDTHZ(1,5) "00001" +// +// Great for times & dates, or hex dumps +// +// Serial << _WIDTHZ(hour,2) << ':' << _WIDTHZ(min,2) << ':' << _WIDTHZ(sec,2) +// +// for(int index=0; index +struct __WIDTH +{ + const T val; + int8_t width; + char pad; + __WIDTH(const T& v, int8_t w, char p) : val(v), width(w), pad(p) {} +}; + +// Count digits in an integer of specific base +template +inline uint8_t digits(T v, int8_t base = 10) +{ + uint8_t digits = 0; + if ( std::is_signed::value ) + { + if ( v < 0 ) + { + digits++; + v = -v; // v needs to be postive for the digits counter to work + } + } + do + { + v /= base; + digits++; + } while( v > 0 ); + return digits; +} + +// Generic get the width of a value in base 10 +template +inline uint8_t get_value_width(T val) +{ return digits(val); } + +inline uint8_t get_value_width(const char * val) +{ return strlen(val); } + +#ifdef ARDUINO +inline uint8_t get_value_width(const __FlashStringHelper * val) +{ return strlen_P(reinterpret_cast(val)); } +#endif + +// _BASED get the width of a value +template +inline uint8_t get_value_width(_BASED b) +{ return digits(b.val, b.base); } + +// Constructor wrapper to allow automatic template parameter deduction +template +__WIDTH _WIDTH(T val, int8_t width) { return __WIDTH(val, width, ' '); } +template +__WIDTH _WIDTHZ(T val, int8_t width) { return __WIDTH(val, width, '0'); } + + +// Operator overload to handle width printing. +template +inline Print &operator <<(Print &stm, const __WIDTH &arg) +{ stm << _PAD(arg.width - get_value_width(arg.val), arg.pad) << arg.val; return stm; } + +// explicit Operator overload to handle width printing of _FLOAT, double and float +template +inline Print &pad_float(Print &stm, const __WIDTH &arg, const double val, const int digits = 2) // see Print::print(double, int = 2) +{ + PrintBuffer<32> buf; // it's only ~45B on the stack, no allocation, leak or fragmentation + size_t size = buf.print(val, digits); // print in buf + return stm << _PAD(arg.width - size, arg.pad) << buf(); // pad and concat what's in buf +} + +inline Print &operator <<(Print &stm, const __WIDTH &arg) +{ return pad_float(stm, arg, arg.val); } + +inline Print &operator <<(Print &stm, const __WIDTH &arg) +{ return pad_float(stm, arg, arg.val); } + +inline Print &operator <<(Print &stm, const __WIDTH<_FLOAT> &arg) +{ auto& f = arg.val; return pad_float(stm, arg, f.val, f.digits); } + +// a less verbose _FLOATW for _WIDTH(_FLOAT) +#define _FLOATW(val, digits, width) _WIDTH<_FLOAT>(_FLOAT((val), (digits)), (width)) + +// Specialization for replacement formatting +// +// Designed to be similar to printf that everyone knows and loves/hates. But without +// the internal buffers and type agnosticism. This version only has placeholders in +// the format string, the actual values are supplied using the stream safe operators +// defined in this library. +// +// Use like this: +// +// Serial << FMT(F("Replace % with %"), 1, 2 ) +// Serial << FMT("Time is %:%:%", _WIDTHZ(hours,2), _WIDTHZ(minutes,2), _WIDTHZ(seconds,2)) +// Serial << FMT("Your score is %\\%", score); // Note the \\ to escape the % sign + +// Ok, hold your hats. This is a foray into C++11's variadic template engine ... + +inline char get_next_format_char(const char *& format_string) +{ + char format_char = *format_string; + if ( format_char > 0 ) format_string++; + return format_char; +} + +#ifdef ARDUINO +inline char get_next_format_char(const __FlashStringHelper*& format_string) +{ + char format_char = pgm_read_byte(format_string); + if ( format_char > 0 ) format_string = reinterpret_cast(reinterpret_cast(format_string)+1); + return format_char; +} +#endif + +template +inline bool check_backslash(char& format_char, Ft& format_string) +{ + if ( format_char == '\\') + { + format_char = get_next_format_char(format_string); + return true; + } + return false; +} + +// The template tail printer helper +template +struct __FMT +{ + Ft format_string; + __FMT(Ft f, Ts ... args) : format_string(f) {} + inline void tstreamf(Print& stm, Ft format) const + { + while(char c = get_next_format_char(format)) + { + check_backslash(c, format); + if ( c ) + stm.print(c); + } + } +}; + +// The variadic template helper +template +struct __FMT : __FMT +{ + T val; + __FMT(Ft f, T t, Ts... ts) : __FMT(f, ts...), val(t) {} + inline void tstreamf(Print& stm, Ft format) const + { + while(char c = get_next_format_char(format)) + { + if (!check_backslash(c, format)) + { + if ( c == '%') + { + stm << val; + // Variadic recursion ... compiler rolls this out during + // template argument pack expansion + __FMT::tstreamf(stm, format); + return; + } + } + if (c) + stm.print(c); + } + } +}; + +// The actual operator should you only instanciate the FMT +// helper with a format string and no parameters +template +inline Print& operator <<(Print &stm, const __FMT &args) +{ + args.tstreamf(stm, args.format_string); + return stm; +} + +// The variadic stream helper +template +inline Print& operator <<(Print &stm, const __FMT &args) +{ + args.tstreamf(stm, args.format_string); + return stm; +} + +// As we don't have C++17, we can't get a constructor to use +// automatic argument deduction, but ... this little trick gets +// around that ... +template +__FMT _FMT(Ft format, Ts ... args) { return __FMT(format, args...); } + +#endif