/* * SRT - Secure, Reliable, Transport * Copyright (c) 2018 Haivision Systems Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ /***************************************************************************** written by Haivision Systems Inc. *****************************************************************************/ #ifndef INC_SRT_UTILITIES_H #define INC_SRT_UTILITIES_H // Windows warning disabler #define _CRT_SECURE_NO_WARNINGS 1 #include "platform_sys.h" #include "srt_attr_defs.h" // defines HAVE_CXX11 // Happens that these are defined, undefine them in advance #undef min #undef max #include #include #include #include #include #include #include #include #include #if HAVE_CXX11 #include #endif #include #include #include #include // -------------- UTILITIES ------------------------ // --- ENDIAN --- // Copied from: https://gist.github.com/panzi/6856583 // License: Public Domain. #if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) # define __WINDOWS__ #endif #if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) # include // GLIBC-2.8 and earlier does not provide these macros. // See http://linux.die.net/man/3/endian // From https://gist.github.com/panzi/6856583 # if defined(__GLIBC__) \ && ( !defined(__GLIBC_MINOR__) \ || ((__GLIBC__ < 2) \ || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 9))) ) # include # if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN) # define htole32(x) (x) # define le32toh(x) (x) # elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) # define htole16(x) ((((((uint16_t)(x)) >> 8))|((((uint16_t)(x)) << 8))) # define le16toh(x) ((((((uint16_t)(x)) >> 8))|((((uint16_t)(x)) << 8))) # define htole32(x) (((uint32_t)htole16(((uint16_t)(((uint32_t)(x)) >> 16)))) | (((uint32_t)htole16(((uint16_t)(x)))) << 16)) # define le32toh(x) (((uint32_t)le16toh(((uint16_t)(((uint32_t)(x)) >> 16)))) | (((uint32_t)le16toh(((uint16_t)(x)))) << 16)) # else # error Byte Order not supported or not defined. # endif # endif #elif defined(__APPLE__) # include # define htobe16(x) OSSwapHostToBigInt16(x) # define htole16(x) OSSwapHostToLittleInt16(x) # define be16toh(x) OSSwapBigToHostInt16(x) # define le16toh(x) OSSwapLittleToHostInt16(x) # define htobe32(x) OSSwapHostToBigInt32(x) # define htole32(x) OSSwapHostToLittleInt32(x) # define be32toh(x) OSSwapBigToHostInt32(x) # define le32toh(x) OSSwapLittleToHostInt32(x) # define htobe64(x) OSSwapHostToBigInt64(x) # define htole64(x) OSSwapHostToLittleInt64(x) # define be64toh(x) OSSwapBigToHostInt64(x) # define le64toh(x) OSSwapLittleToHostInt64(x) # define __BYTE_ORDER BYTE_ORDER # define __BIG_ENDIAN BIG_ENDIAN # define __LITTLE_ENDIAN LITTLE_ENDIAN # define __PDP_ENDIAN PDP_ENDIAN #elif defined(__OpenBSD__) # include #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) # include #ifndef be16toh # define be16toh(x) betoh16(x) #endif #ifndef le16toh # define le16toh(x) letoh16(x) #endif #ifndef be32toh # define be32toh(x) betoh32(x) #endif #ifndef le32toh # define le32toh(x) letoh32(x) #endif #ifndef be64toh # define be64toh(x) betoh64(x) #endif #ifndef le64toh # define le64toh(x) letoh64(x) #endif #elif defined(SUNOS) // SunOS/Solaris #include #include #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 # if defined(_BIG_ENDIAN) #define __BYTE_ORDER __BIG_ENDIAN #define be64toh(x) (x) #define be32toh(x) (x) #define be16toh(x) (x) #define le16toh(x) ((uint16_t)BSWAP_16(x)) #define le32toh(x) BSWAP_32(x) #define le64toh(x) BSWAP_64(x) #define htobe16(x) (x) #define htole16(x) ((uint16_t)BSWAP_16(x)) #define htobe32(x) (x) #define htole32(x) BSWAP_32(x) #define htobe64(x) (x) #define htole64(x) BSWAP_64(x) # else #define __BYTE_ORDER __LITTLE_ENDIAN #define be64toh(x) BSWAP_64(x) #define be32toh(x) ntohl(x) #define be16toh(x) ntohs(x) #define le16toh(x) (x) #define le32toh(x) (x) #define le64toh(x) (x) #define htobe16(x) htons(x) #define htole16(x) (x) #define htobe32(x) htonl(x) #define htole32(x) (x) #define htobe64(x) BSWAP_64(x) #define htole64(x) (x) # endif #elif defined(__WINDOWS__) # include # if BYTE_ORDER == LITTLE_ENDIAN # define htobe16(x) htons(x) # define htole16(x) (x) # define be16toh(x) ntohs(x) # define le16toh(x) (x) # define htobe32(x) htonl(x) # define htole32(x) (x) # define be32toh(x) ntohl(x) # define le32toh(x) (x) # define htobe64(x) htonll(x) # define htole64(x) (x) # define be64toh(x) ntohll(x) # define le64toh(x) (x) # elif BYTE_ORDER == BIG_ENDIAN /* that would be xbox 360 */ # define htobe16(x) (x) # define htole16(x) __builtin_bswap16(x) # define be16toh(x) (x) # define le16toh(x) __builtin_bswap16(x) # define htobe32(x) (x) # define htole32(x) __builtin_bswap32(x) # define be32toh(x) (x) # define le32toh(x) __builtin_bswap32(x) # define htobe64(x) (x) # define htole64(x) __builtin_bswap64(x) # define be64toh(x) (x) # define le64toh(x) __builtin_bswap64(x) # else # error byte order not supported # endif # define __BYTE_ORDER BYTE_ORDER # define __BIG_ENDIAN BIG_ENDIAN # define __LITTLE_ENDIAN LITTLE_ENDIAN # define __PDP_ENDIAN PDP_ENDIAN #else # error Endian: platform not supported #endif // Hardware <--> Network (big endian) convention inline void HtoNLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) dst[i] = htonl(src[i]); } inline void NtoHLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) dst[i] = ntohl(src[i]); } // Hardware <--> Intel (little endian) convention inline void HtoILA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) dst[i] = htole32(src[i]); } inline void ItoHLA(uint32_t* dst, const uint32_t* src, size_t size) { for (size_t i = 0; i < size; ++ i) dst[i] = le32toh(src[i]); } // Bit numbering utility. // // This is something that allows you to turn 32-bit integers into bit fields. // Although bitfields are part of C++ language, they are not designed to be // interchanged with 32-bit numbers, and any attempt to doing it (by placing // inside a union, for example) is nonportable (order of bitfields inside // same-covering 32-bit integer number is dependent on the endian), so they are // popularly disregarded as useless. Instead the 32-bit numbers with bits // individually selected is preferred, with usually manual playing around with // & and | operators, as well as << and >>. This tool is designed to simplify // the use of them. This can be used to qualify a range of bits inside a 32-bit // number to be a separate number, you can "wrap" it by placing the integer // value in the range of these bits, as well as "unwrap" (extract) it from // the given place. For your own safety, use one prefix to all constants that // concern bit ranges intended to be inside the same "bit container". // // Usage: typedef Bits MASKTYPE; // MASKTYPE is a name of your choice. // // With this defined, you can use the following members: // - MASKTYPE::mask - to get the int32_t value with bimask (used bits set to 1, others to 0) // - MASKTYPE::offset - to get the lowermost bit number, or number of bits to shift // - MASKTYPE::wrap(int value) - to create a bitset where given value is encoded in given bits // - MASKTYPE::unwrap(int bitset) - to extract an integer value from the bitset basing on mask definition // (rightmost defaults to leftmost) // REMEMBER: leftmost > rightmost because bit 0 is the LEAST significant one! template struct BitsetMask { static const bool correct = L >= R; static const uint32_t value = (1u << L) | BitsetMask::value; }; // This is kind-of functional programming. This describes a special case that is // a "terminal case" in case when decreased L-1 (see above) reached == R. template struct BitsetMask { static const bool correct = true; static const uint32_t value = 1u << R; }; // This is a trap for a case that BitsetMask::correct in the master template definition // evaluates to false. This trap causes compile error and prevents from continuing // recursive unwinding in wrong direction (and challenging the compiler's resistiveness // for infinite loops). template struct BitsetMask { }; template struct Bits { // DID YOU GET a kind-of error: 'mask' is not a member of 'Bits<3u, 5u, false>'? // See the the above declaration of 'correct'! static const uint32_t mask = BitsetMask::value; static const uint32_t offset = R; static const size_t size = L - R + 1; // Example: if our bitset mask is 00111100, this checks if given value fits in // 00001111 mask (that is, does not exceed <0, 15>. static bool fit(uint32_t value) { return (BitsetMask::value & value) == value; } /// 'wrap' gets some given value that should be placed in appropriate bit range and /// returns a whole 32-bit word that has the value already at specified place. /// To create a 32-bit container that contains already all values destined for different /// bit ranges, simply use wrap() for each of them and bind them with | operator. static uint32_t wrap(uint32_t baseval) { return (baseval << offset) & mask; } /// Extracts appropriate bit range and returns them as normal integer value. static uint32_t unwrap(uint32_t bitset) { return (bitset & mask) >> offset; } template static T unwrapt(uint32_t bitset) { return static_cast(unwrap(bitset)); } }; //inline int32_t Bit(size_t b) { return 1 << b; } // XXX This would work only with 'constexpr', but this is // available only in C++11. In C++03 this can be only done // using a macro. // // Actually this can be expressed in C++11 using a better technique, // such as user-defined literals: // 2_bit --> 1 >> 2 #ifdef BIT #undef BIT #endif #define BIT(x) (1 << (x)) // ------------------------------------------------------------ // This is something that reminds a structure consisting of fields // of the same type, implemented as an array. It's parametrized // by the type of fields and the type, which's values should be // used for indexing (preferably an enum type). Whatever type is // used for indexing, it is converted to size_t for indexing the // actual array. // // The user should use it as an array: ds[DS_NAME], stating // that DS_NAME is of enum type passed as 3rd parameter. // However trying to do ds[0] would cause a compile error. template struct DynamicStruct { FieldType inarray[NoOfFields]; void clear() { // As a standard library, it can be believed that this call // can be optimized when FieldType is some integer. std::fill(inarray, inarray + NoOfFields, FieldType()); } FieldType operator[](IndexerType ix) const { return inarray[size_t(ix)]; } FieldType& operator[](IndexerType ix) { return inarray[size_t(ix)]; } template FieldType operator[](AnyOther ix) const { // If you can see a compile error here ('int' is not a class or struct, or // that there's no definition of 'type' in given type), it means that you // have used invalid data type passed to [] operator. See the definition // of this type as DynamicStruct and see which type is required for indexing. typename AnyOther::type wrong_usage_of_operator_index = AnyOther::type; return inarray[size_t(ix)]; } template FieldType& operator[](AnyOther ix) { // If you can see a compile error here ('int' is not a class or struct, or // that there's no definition of 'type' in given type), it means that you // have used invalid data type passed to [] operator. See the definition // of this type as DynamicStruct and see which type is required for indexing. typename AnyOther::type wrong_usage_of_operator_index = AnyOther::type; return inarray[size_t(ix)]; } operator FieldType* () { return inarray; } operator const FieldType* () const { return inarray; } char* raw() { return (char*)inarray; } }; /// Fixed-size array template class. namespace srt { template class FixedArray { public: FixedArray(size_t size) : m_size(size) , m_entries(new T[size]) { } ~FixedArray() { delete [] m_entries; } public: const T& operator[](size_t index) const { if (index >= m_size) raise_expection(index); return m_entries[index]; } T& operator[](size_t index) { if (index >= m_size) raise_expection(index); return m_entries[index]; } const T& operator[](int index) const { if (index < 0 || static_cast(index) >= m_size) raise_expection(index); return m_entries[index]; } T& operator[](int index) { if (index < 0 || static_cast(index) >= m_size) raise_expection(index); return m_entries[index]; } size_t size() const { return m_size; } typedef T* iterator; typedef const T* const_iterator; iterator begin() { return m_entries; } iterator end() { return m_entries + m_size; } const_iterator cbegin() const { return m_entries; } const_iterator cend() const { return m_entries + m_size; } T* data() { return m_entries; } private: FixedArray(const FixedArray& ); FixedArray& operator=(const FixedArray&); void raise_expection(int i) const { std::stringstream ss; ss << "Index " << i << "out of range"; throw std::runtime_error(ss.str()); } private: size_t m_size; T* const m_entries; }; } // namespace srt // ------------------------------------------------------------ inline bool IsSet(int32_t bitset, int32_t flagset) { return (bitset & flagset) == flagset; } // std::addressof in C++11, // needs to be provided for C++03 template inline RefType* AddressOf(RefType& r) { return (RefType*)(&(unsigned char&)(r)); } template struct explicit_t { T inobject; explicit_t(const T& uo): inobject(uo) {} operator T() const { return inobject; } private: template explicit_t(const X& another); }; // This is required for Printable function if you have a container of pairs, // but this function has a different definition for C++11 and C++03. namespace srt_pair_op { template std::ostream& operator<<(std::ostream& s, const std::pair& v) { s << "{" << v.first << " " << v.second << "}"; return s; } } #if HAVE_CXX11 template inline auto Move(In& i) -> decltype(std::move(i)) { return std::move(i); } // Gluing string of any type, wrapper for operator << template inline Stream& Print(Stream& in) { return in;} template inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args) { sout << arg1; return Print(sout, args...); } template inline std::string Sprint(Args&&... args) { std::ostringstream sout; Print(sout, args...); return sout.str(); } // We need to use UniquePtr, in the form of C++03 it will be a #define. // Naturally will be used std::move() so that it can later painlessly // switch to C++11. template using UniquePtr = std::unique_ptr; template inline std::string Printable(const Container& in, Value /*pseudoargument*/, Args&&... args) { using namespace srt_pair_op; std::ostringstream os; Print(os, args...); os << "[ "; for (auto i: in) os << Value(i) << " "; os << "]"; return os.str(); } template inline std::string Printable(const Container& in) { using namespace srt_pair_op; using Value = typename Container::value_type; return Printable(in, Value()); } template auto map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) -> typename Map::mapped_type { auto it = m.find(key); return it == m.end() ? def : it->second; } template auto map_getp(Map& m, const Key& key) -> typename Map::mapped_type* { auto it = m.find(key); return it == m.end() ? nullptr : std::addressof(it->second); } template auto map_getp(const Map& m, const Key& key) -> typename Map::mapped_type const* { auto it = m.find(key); return it == m.end() ? nullptr : std::addressof(it->second); } #else // The unique_ptr requires C++11, and the rvalue-reference feature, // so here we're simulate the behavior using the old std::auto_ptr. // This is only to make a "move" call transparent and look ok towards // the C++11 code. template std::auto_ptr_ref Move(const std::auto_ptr_ref& in) { return in; } // We need to provide also some fixes for this type that were not present in auto_ptr, // but they are present in unique_ptr. // C++03 doesn't have a templated typedef, but still we need some things // that can only function as a class. template class UniquePtr: public std::auto_ptr { typedef std::auto_ptr Base; public: // This is a template - so method names must be declared explicitly typedef typename Base::element_type element_type; using Base::get; using Base::reset; // All constructor declarations must be repeated. // "Constructor delegation" is also only C++11 feature. explicit UniquePtr(element_type* p = 0) throw() : Base(p) {} UniquePtr(UniquePtr& a) throw() : Base(a) { } template UniquePtr(UniquePtr& a) throw() : Base(a) {} UniquePtr& operator=(UniquePtr& a) throw() { return Base::operator=(a); } template UniquePtr& operator=(UniquePtr& a) throw() { return Base::operator=(a); } // Good, now we need to add some parts of the API of unique_ptr. bool operator==(const UniquePtr& two) const { return get() == two.get(); } bool operator!=(const UniquePtr& two) const { return get() != two.get(); } bool operator==(const element_type* two) const { return get() == two; } bool operator!=(const element_type* two) const { return get() != two; } operator bool () { return 0!= get(); } }; // A primitive one-argument versions of Sprint and Printable template inline std::string Sprint(const Arg1& arg) { std::ostringstream sout; sout << arg; return sout.str(); } template inline std::string Printable(const Container& in) { using namespace srt_pair_op; typedef typename Container::value_type Value; std::ostringstream os; os << "[ "; for (typename Container::const_iterator i = in.begin(); i != in.end(); ++i) os << Value(*i) << " "; os << "]"; return os.str(); } template typename Map::mapped_type map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) { typename Map::iterator it = m.find(key); return it == m.end() ? def : it->second; } template typename Map::mapped_type map_get(const Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) { typename Map::const_iterator it = m.find(key); return it == m.end() ? def : it->second; } template typename Map::mapped_type* map_getp(Map& m, const Key& key) { typename Map::iterator it = m.find(key); return it == m.end() ? (typename Map::mapped_type*)0 : &(it->second); } template typename Map::mapped_type const* map_getp(const Map& m, const Key& key) { typename Map::const_iterator it = m.find(key); return it == m.end() ? (typename Map::mapped_type*)0 : &(it->second); } #endif // Printable with prefix added for every element. // Useful when printing a container of sockets or sequence numbers. template inline std::string PrintableMod(const Container& in, const std::string& prefix) { using namespace srt_pair_op; typedef typename Container::value_type Value; std::ostringstream os; os << "[ "; for (typename Container::const_iterator y = in.begin(); y != in.end(); ++y) os << prefix << Value(*y) << " "; os << "]"; return os.str(); } template inline void FilterIf(InputIterator bg, InputIterator nd, OutputIterator out, TransFunction fn) { for (InputIterator i = bg; i != nd; ++i) { std::pair result = fn(*i); if (!result.second) continue; *out++ = result.first; } } template inline void insert_uniq(std::vector& v, const ArgValue& val) { typename std::vector::iterator i = std::find(v.begin(), v.end(), val); if (i != v.end()) return; v.push_back(val); } template struct CallbackHolder { void* opaque; Signature* fn; CallbackHolder(): opaque(NULL), fn(NULL) {} void set(void* o, Signature* f) { // Test if the pointer is a pointer to function. Don't let // other type of pointers here. #if HAVE_CXX11 static_assert(std::is_function::value, "CallbackHolder is for functions only!"); #else // This is a poor-man's replacement, which should in most compilers // generate a warning, if `Signature` resolves to a value type. // This would make an illegal pointer cast from a value to a function type. // Casting function-to-function, however, should not. Unfortunately // newer compilers disallow that, too (when a signature differs), but // then they should better use the C++11 way, much more reliable and safer. void* (*testfn)(void*) = (void*(*)(void*))f; (void)(testfn); #endif opaque = o; fn = f; } operator bool() { return fn != NULL; } }; #define CALLBACK_CALL(holder,...) (*holder.fn)(holder.opaque, __VA_ARGS__) inline std::string FormatBinaryString(const uint8_t* bytes, size_t size) { if ( size == 0 ) return ""; //char buf[256]; using namespace std; ostringstream os; // I know, it's funny to use sprintf and ostringstream simultaneously, // but " %02X" in iostream is: << " " << hex << uppercase << setw(2) << setfill('0') << VALUE << setw(1) // Too noisy. OTOH ostringstream solves the problem of memory allocation // for a string of unpredictable size. //sprintf(buf, "%02X", int(bytes[0])); os.fill('0'); os.width(2); os.setf(ios::basefield, ios::hex); os.setf(ios::uppercase); //os << buf; os << int(bytes[0]); for (size_t i = 1; i < size; ++i) { //sprintf(buf, " %02X", int(bytes[i])); //os << buf; os << int(bytes[i]); } return os.str(); } /// This class is useful in every place where /// the time drift should be traced. It's currently in use in every /// solution that implements any kind of TSBPD. template class DriftTracer { int64_t m_qDrift; int64_t m_qOverdrift; int64_t m_qDriftSum; unsigned m_uDriftSpan; public: DriftTracer() : m_qDrift(0) , m_qOverdrift(0) , m_qDriftSum(0) , m_uDriftSpan(0) {} bool update(int64_t driftval) { m_qDriftSum += driftval; ++m_uDriftSpan; // I moved it here to calculate accumulated overdrift. if (CLEAR_ON_UPDATE) m_qOverdrift = 0; if (m_uDriftSpan < MAX_SPAN) return false; // Calculate the median of all drift values. // In most cases, the divisor should be == MAX_SPAN. m_qDrift = m_qDriftSum / m_uDriftSpan; // And clear the collection m_qDriftSum = 0; m_uDriftSpan = 0; // In case of "overdrift", save the overdriven value in 'm_qOverdrift'. // In clear mode, you should add this value to the time base when update() // returns true. The drift value will be since now measured with the // overdrift assumed to be added to the base. if (std::abs(m_qDrift) > MAX_DRIFT) { m_qOverdrift = m_qDrift < 0 ? -MAX_DRIFT : MAX_DRIFT; m_qDrift -= m_qOverdrift; } // printDriftOffset(m_qOverdrift, m_qDrift); // Timebase is separate // m_qTimeBase += m_qOverdrift; return true; } // For group overrides void forceDrift(int64_t driftval) { m_qDrift = driftval; } // These values can be read at any time, however if you want // to depend on the fact that they have been changed lately, // you have to check the return value from update(). // // IMPORTANT: drift() can be called at any time, just remember // that this value may look different than before only if the // last update() returned true, which need not be important for you. // // CASE: CLEAR_ON_UPDATE = true // overdrift() should be read only immediately after update() returned // true. It will stay available with this value until the next time when // update() returns true, in which case the value will be cleared. // Therefore, after calling update() if it retuns true, you should read // overdrift() immediately an make some use of it. Next valid overdrift // will be then relative to every previous overdrift. // // CASE: CLEAR_ON_UPDATE = false // overdrift() will start from 0, but it will always keep track on // any changes in overdrift. By manipulating the MAX_DRIFT parameter // you can decide how high the drift can go relatively to stay below // overdrift. int64_t drift() const { return m_qDrift; } int64_t overdrift() const { return m_qOverdrift; } }; template struct MapProxy { std::map& mp; const KeyType& key; MapProxy(std::map& m, const KeyType& k): mp(m), key(k) {} void operator=(const ValueType& val) { mp[key] = val; } typename std::map::iterator find() { return mp.find(key); } typename std::map::const_iterator find() const { return mp.find(key); } operator ValueType() const { typename std::map::const_iterator p = find(); if (p == mp.end()) return ""; return p->second; } ValueType deflt(const ValueType& defval) const { typename std::map::const_iterator p = find(); if (p == mp.end()) return defval; return p->second; } bool exists() const { return find() != mp.end(); } }; /// Print some hash-based stamp of the first 16 bytes in the buffer inline std::string BufferStamp(const char* mem, size_t size) { using namespace std; char spread[16]; if (size < 16) memset((spread + size), 0, 16 - size); memcpy((spread), mem, min(size_t(16), size)); // Now prepare 4 cells for uint32_t. union { uint32_t sum; char cells[4]; }; memset((cells), 0, 4); for (size_t x = 0; x < 4; ++x) for (size_t y = 0; y < 4; ++y) { cells[x] += spread[x+4*y]; } // Convert to hex string ostringstream os; os << hex << uppercase << setfill('0') << setw(8) << sum; return os.str(); } template inline void Split(const std::string & str, char delimiter, OutputIterator tokens) { if ( str.empty() ) return; // May cause crash and won't extract anything anyway std::size_t start; std::size_t end = -1; do { start = end + 1; end = str.find(delimiter, start); *tokens = str.substr( start, (end == std::string::npos) ? std::string::npos : end - start); ++tokens; } while (end != std::string::npos); } inline std::string SelectNot(const std::string& unwanted, const std::string& s1, const std::string& s2) { if (s1 == unwanted) return s2; // might be unwanted, too, but then, there's nothing you can do anyway if (s2 == unwanted) return s1; // Both have wanted values, so now compare if they are same if (s1 == s2) return s1; // occasionally there's a winner // Irresolvable situation. return std::string(); } inline std::string SelectDefault(const std::string& checked, const std::string& def) { if (checked == "") return def; return checked; } template inline size_t safe_advance(It& it, size_t num, It end) { while ( it != end && num ) { --num; ++it; } return num; // will be effectively 0, if reached the required point, or >0, if end was by that number earlier } // This is available only in C++17, dunno why not C++11 as it's pretty useful. template inline ATR_CONSTEXPR size_t Size(const V (&)[N]) ATR_NOEXCEPT { return N; } template inline ValueType avg_iir(ValueType old_value, ValueType new_value) { return (old_value * (DEPRLEN - 1) + new_value) / DEPRLEN; } template inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_val_weight) { return (old_value * (DEPRLEN - new_val_weight) + new_value * new_val_weight) / DEPRLEN; } // Property accessor definitions // // "Property" is a special method that accesses given field. // This relies only on a convention, which is the following: // // V x = object.prop(); <-- get the property's value // object.prop(x); <-- set the property a value // // Properties might be also chained when setting: // // object.prop1(v1).prop2(v2).prop3(v3); // // Properties may be defined various even very complicated // ways, which is simply providing a method with body. In order // to define a property simplest possible way, that is, refer // directly to the field that keeps it, here are the following macros: // // Prefix: SRTU_PROPERTY_ // Followed by: // - access type: RO, WO, RW, RR, RRW // - chain flag: optional _CHAIN // Where access type is: // - RO - read only. Defines reader accessor. The accessor method will be const. // - RR - read reference. The accessor isn't const to allow reference passthrough. // - WO - write only. Defines writer accessor. // - RW - combines RO and WO. // - RRW - combines RR and WO. // // The _CHAIN marker is optional for macros providing writable accessors // for properties. The difference is that while simple write accessors return // void, the chaining accessors return the reference to the object for which // the write accessor was called so that you can call the next accessor (or // any other method as well) for the result. #define SRTU_PROPERTY_RR(type, name, field) type name() { return field; } #define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; } #define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; } #define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; } #define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field) #define SRTU_PROPERTY_RW_CHAIN(otype, type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) #define SRTU_PROPERTY_RRW_CHAIN(otype, type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) #endif