/** * yatemath.h * This file is part of the YATE Project http://YATE.null.ro * * Math data types * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2015 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * This program 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. */ #ifndef __YATEMATH_H #define __YATEMATH_H #include #include #include namespace TelEngine { #ifdef DEBUG #ifdef _WINDOWS #define YMATH_FAIL(cond,...) { \ if (!(cond)) \ Debug(DebugFail,__VA_ARGS__); \ } #else #define YMATH_FAIL(cond,args...) { \ if (!(cond)) \ Debug(DebugFail,args); \ } #endif #else #ifdef _WINDOWS #define YMATH_FAIL do { break; } while #else #define YMATH_FAIL(arg...) #endif #endif /** * This class implements a complex number * @short A Complex (float) number */ class YATE_API Complex { public: /** * Constructor */ inline Complex() : m_real(0), m_imag(0) {} /** * Constructor * @param real The real part of the complex number * @param imag The imaginary part of a complex number */ inline Complex(float real, float imag = 0) : m_real(real), m_imag(imag) {} /** * Copy constructor * @param c The source complex number */ inline Complex(const Complex& c) : m_real(c.m_real), m_imag(c.m_imag) {} /** * Obtain the real part of the complex number * @return The real part */ inline float re() const { return m_real; } /** * Set the real part of the complex number * @param r The new real part value */ inline void re(float r) { m_real = r; } /** * Obtain the imaginary part of a complex number * @return The imaginary part */ inline float im() const { return m_imag; } /** * Set the imaginary part of the complex number * @param i The new imaginary part value */ inline void im(float i) { m_imag = i; } /** * Set data * @param r The real part of the complex number * @param i The imaginary part of a complex number * @return A reference to this object */ inline Complex& set(float r = 0, float i = 0) { m_real = r; m_imag = i; return *this; } /** * Equality operator * @param c Complex number to compare with * @return True if equal, false otherwise */ inline bool operator==(const Complex& c) const { return m_real == c.m_real && m_imag == c.m_imag; } /** * Inequality operator * @param c Complex number to compare with * @return True if not equal, false otherwise */ inline bool operator!=(const Complex& c) const { return m_real != c.m_real || m_imag != c.m_imag; } /** * Assignment operator * @param c Complex number to assign * @return A reference to this object */ inline Complex& operator=(const Complex& c) { return set(c.m_real,c.m_imag); } /** * Assignment operator. Set the real part, reset the imaginary one * @param real New real part value * @return A reference to this object */ inline Complex& operator=(float real) { return set(real); } /** * Addition operator * @param c Complex number to add * @return A reference to this object */ inline Complex& operator+=(const Complex& c) { return set(m_real + c.m_real,m_imag + c.m_imag); } /** * Addition operator. Add a value to the real part * @param real Value to add to real part * @return A reference to this object */ inline Complex& operator+=(float real) { m_real += real; return *this; } /** * Substraction operator * @param c Complex number to substract from this one * @return A reference to this object */ inline Complex& operator-=(const Complex& c) { return set(m_real - c.m_real,m_imag - c.m_imag); } /** * Substraction operator. Substract a value a value from the real part * @param real Value to substract from real part * @return A reference to this object */ inline Complex& operator-=(float real) { m_real -= real; return *this; } /** * Multiplication operator * @param c Complex number to multiply with * @return A reference to this object */ inline Complex& operator*=(const Complex& c) { return set(m_real * c.m_real - m_imag * c.m_imag, m_real * c.m_imag + m_imag * c.m_real); } /** * Multiplication operator. Multiply this number with a float number * @param f Value to multiply with * @return A reference to this object */ inline Complex& operator*=(float f) { return set(m_real * f,m_imag * f); } /** * Division operator * @param c Complex number to devide with * @return A reference to this object */ inline Complex& operator/=(const Complex& c) { float tmp = c.norm2(); return set((m_real * c.m_real + m_imag * c.m_imag) / tmp, (-m_real * c.m_imag + m_imag * c.m_real) / tmp); } /** * Division operator * @param f Float number to devide with * @return A reference to this object */ inline Complex& operator/=(float f) { return set(m_real / f,m_imag / f); } /** * Compute the absolute value of this complex number * @return The result */ inline float abs() const { return ::sqrtf(norm2()); } /** * Compute the modulus value of this complex number * @return The result */ inline float mod() const { return abs(); } /** * Compute the the argument of this complex number * @return Ther result */ inline float arg() const { return ::atan(m_imag / m_real); } /** * Computes the exponential of this complex number * @return The result */ inline Complex exp() const { float r = ::expf(m_real); return Complex(r * ::cosf(m_imag),r * ::sinf(m_imag)); } /** * Compute the norm of this complex number * @return The result */ inline float norm() const { return abs(); } /** * Compute the norm2 value of this complex number * @return The result */ inline float norm2() const { return m_real * m_real + m_imag * m_imag; } private: float m_real; // The real part float m_imag; // The imaginary part }; /** * This class holds a ref counted storage * @short A fixed ref counted storage */ class YATE_API RefStorage : public RefObject { YCLASS(RefStorage,RefObject) YNOCOPY(RefStorage); // No automatic copies please public: /** * Constructor * @param value Data to assign, may be NULL to fill with zeros * @param len Length of data, may be zero (then value is ignored) */ inline RefStorage(const void* value, unsigned int len) : m_data((void*)value,len) {} /** * Get the length of the stored data * @return The length of the stored data, zero for NULL */ inline unsigned int length() const { return m_data.length(); } /** * Get a pointer to a byte range inside the stored data * @param offs Byte offset inside the stored data * @param len Number of bytes that must be valid starting at offset (must not be 0) * @return A pointer to the data or NULL if the range is not available */ inline void* data(unsigned int offs, unsigned int len) const { return len ? m_data.data(offs,len) : 0; } /** * Copy data to this storage * @param buf Buffer to copy * @param len The number of bytes to copy * @param offs The start index in this storage * @return True on success, false if there is not enough space in our storage or * the buffer pointer is NULL */ inline bool set(const void* buf, unsigned int len, unsigned int offs = 0) { return copy(data(offs,len),buf,len); } /** * Fill a buffer * @param dest Destination buffer * @param len The number of bytes to fill * @param val Value to fill with */ static inline void fill(void* dest, unsigned int len, int val = 0) { if (dest && len) ::memset(dest,val,len); } /** * Copy data * @param dest Destination buffer * @param src Source buffer * @param len The number of bytes to copy * @return True on success, false if parameters are invalid */ static inline bool copy(void* dest, const void* src, unsigned int len) { if (!(len && dest && src)) return len == 0; if (dest != src) ::memcpy(dest,src,len); return true; } /** * Compare data * @param buf1 First buffer * @param buf2 Second buffer * @param len The number of bytes to compare * @return True if equal */ static inline bool equals(const void* buf1, const void* buf2, unsigned int len) { if (len && buf1 && buf2) return (buf1 == buf2) || (::memcmp(buf1,buf2,len) == 0); return true; } /** * Split a string and append lines to another one * @param buf Destination string * @param str Input string * @param lineLen Line length, characters to copy * @param offset Offset in first line (if incomplete). No data will be * added on first line if offset is greater then line length * @param linePrefix Prefix for new lines. * Set it to empty string or 0 to use the suffix * @param suffix End of line for the last line * @return Destination string address */ static String& dumpSplit(String& buf, const String& str, unsigned int lineLen, unsigned int offset = 0, const char* linePrefix = 0, const char* suffix = "\r\n"); private: RefStorage() {}; // No default constructor please DataBlock m_data; }; /** * Base class for vector class(es). * Its purpose is to offer a common interface when processing lists * @short Base class for vector class(es) */ class YATE_API MathVectorBase : public GenObject { YCLASS(MathVectorBase,GenObject) public: /** * Destructor. Does nothing, keeps the compiler satisfied */ virtual ~MathVectorBase() {} /** * Retrieve vector size in bytes * @return Vector size in bytes */ virtual unsigned int vectorSize() const = 0; }; /** * Template for vectors holding a fixed storage and a slice in it. * This class works with objects not holding pointers: it uses memcpy to copy data * @short A slice vector */ template class SliceVector : public MathVectorBase { public: /** * Constructor */ inline SliceVector() : m_storage(0), m_data(0), m_length(0), m_maxLen(0) {} /** * Copy constructor. * Builds a slice of another vector * @param other Original vector */ inline SliceVector(const SliceVector& other) : m_storage(0), m_data(0), m_length(0), m_maxLen(0) { initSlice(false,other); } /** * Constructor. * Build the vector storage * @param len Length of data * @param buf Optional init buffer ('len' elements will be copied from it to storage) * @param maxLen Optional vector maximum length * (it will be adjusted to be at least len) */ explicit inline SliceVector(unsigned int len, const Obj* buf = 0, unsigned int maxLen = 0) : m_storage(0), m_data(0), m_length(0), m_maxLen(0) { initStorage(len,buf,maxLen); } /** * Constructor. * Build a vector by concatenating two existing ones * @param v1 The first vector * @param v2 The second vector */ explicit inline SliceVector(const SliceVector& v1, const SliceVector& v2) : m_storage(0), m_data(0), m_length(0), m_maxLen(0) { if (!initStorage(v1.length(),v1.data(),v1.length() + v2.length())) return; resizeMax(); m_storage->set(v2.data(),v2.size(),v1.size()); } /** * Constructor. * Build a vector by concatenating three existing ones * @param v1 The first vector * @param v2 The second vector * @param v3 The third vector */ explicit inline SliceVector(const SliceVector& v1, const SliceVector& v2, const SliceVector& v3) : m_storage(0), m_data(0), m_length(0), m_maxLen(0) { unsigned int n = v1.length() + v2.length() + v3.length(); if (!initStorage(v1.length(),v1.data(),n)) return; resizeMax(); m_storage->set(v2.data(),v2.size(),v1.size()); m_storage->set(v3.data(),v3.size(),v1.size() + v2.size()); } /** * Constructor. * Builds a slice of another vector * @param other Original vector * @param offs Offset in the original vector * @param len The number of elements (0 to use all available from offset) */ explicit inline SliceVector(const SliceVector& other, unsigned int offs, unsigned int len = 0) : m_storage(0), m_data(0), m_length(0), m_maxLen(0) { initSlice(false,other,offs,len); } /** * Destructor */ virtual ~SliceVector() { setData(); } /** * Get a pointer to data if 'len' elements are available from offset * @param offs The offset * @param len The number of elements to retrieve (must not be 0) * @return A pointer to data at requested offset, * NULL if there is not enough data available */ inline Obj* data(unsigned int offs, unsigned int len) { if (len && length() && offs + len <= length()) return m_data + offs; return 0; } /** * Get a pointer to data if 'len' elements are available from offset * @param offs The offset * @param len The number of elements to retrieve (must not be 0) * @return A pointer to data at requested offset, * NULL if there is not enough data available */ inline const Obj* data(unsigned int offs, unsigned int len) const { if (len && length() && offs + len <= length()) return m_data + offs; return 0; } /** * Get a pointer to data from offset to vector end * @param offs The offset * @return A pointer to data at requested offset, NULL if there is no data available */ inline Obj* data(unsigned int offs = 0) { return data(offs,available(offs)); } /** * Get a pointer to data from offset to vector end * @param offs The offset * @return A pointer to data at requested offset, NULL if there is no data available */ inline const Obj* data(unsigned int offs = 0) const { return data(offs,available(offs)); } /** * Get a pointer to data from offset to vector end * @param offs The offset * @param len The number of elements to retrieve (must not be 0) * @param eod Pointer to be filled with end of data element * (pointer to first element after requested number of elements) * @return A pointer to data data from requested offset, * NULL if there is not enough data available */ inline Obj* data(unsigned int offs, unsigned int len, Obj*& eod) { Obj* d = data(offs,len); eod = end(d,len); return d; } /** * Get a pointer to data from offset to vector end * @param offs The offset * @param len The number of elements to retrieve (must not be 0) * @param eod Pointer to be filled with end of data element * (pointer to first element after requested number of elements) * @return A pointer to data data from requested offset, * NULL if there is not enough data available */ inline const Obj* data(unsigned int offs, unsigned int len, const Obj*& eod) const { const Obj* d = data(offs,len); eod = end(d,len); return d; } /** * Get the length of the vector * @return The length of the vector */ inline unsigned int length() const { return m_length; } /** * Get the maximum length of the vector * @return The maximum length of the vector * (0 if the vector don't have a storage buffer) */ inline unsigned int maxLen() const { return m_maxLen; } /** * Get the vector size in bytes * @return Vector size in bytes */ inline unsigned int size() const { return size(length()); } /** * Retrieve the available number of elements from given offset * (not more than required number) * @param offs The offset * @param len Required number of elements (-1: all available from offset) * @return The available number of elements from given offset * (may be less than required) */ inline unsigned int available(unsigned int offs, int len = -1) const { if (len && offs < length()) { unsigned int rest = length() - offs; return (len < 0 || rest <= (unsigned int)len) ? rest : (unsigned int)len; } return 0; } /** * Retrieve the available number of elements from given offset. * Clamp the available number of elements to requested value * @param clamp Maximum number of elements to check * @param offs The offset * @param len Required number of elements (-1: all available from offset) * @return The available number of elements from given offset */ inline unsigned int availableClamp(unsigned int clamp, unsigned int offs = 0, int len = -1) const { offs = available(offs,len); return clamp <= offs ? clamp : offs; } /** * Retrieve vector size in bytes * @return Vector size in bytes */ virtual unsigned int vectorSize() const { return size(); } /** * Change the vector length without changing the contents. * If the vector length is increased the new elements' value are not reset * @param len New vector length * @return True on success, false if requested length is greater than max length */ inline bool resize(unsigned int len) { if (len <= maxLen()) { m_length = len; return true; } return false; } /** * Change the vector length to maximum allowed without changing the contents */ inline void resizeMax() { resize(maxLen()); } /** * Steal other vector's data * @param other Original vector */ inline void steal(SliceVector& other) { m_storage = other.m_storage; m_data = other.m_data; m_length = other.m_length; m_maxLen = other.m_maxLen; other.m_storage = 0; other.m_data = 0; other.m_length = other.m_maxLen = 0; } /** * Change the vector storage (re-allocate) * @param len New vector length (0 to clear vector data) * @param maxLen Optional vector maximum length * (it will be adjusted to be at least len) */ inline void resetStorage(unsigned int len, unsigned int maxLen = 0) { setData(); initStorage(len,0,maxLen); } /** * Set a slice containing another vector * @param other Original vector * @param offs Offset in the original vector * @param len The number of elements (0 to use all available from offset) * @return True on success, false on failure */ inline bool setSlice(const SliceVector& other, unsigned int offs = 0, unsigned int len = 0) { return initSlice(true,other,offs,len); } /** * Retrieve vector head * @param len The number of elements to retrieve * @return A vector containing the first elements of this vector */ inline SliceVector head(unsigned int len) const { return slice(0,len); } /** * Retrieve vector head * @param dest Destination vector * @param len The number of elements to retrieve * @return True on success, false on failure (not enough data in our vector) */ inline bool head(SliceVector& dest, unsigned int len) const { return slice(dest,0,len); } /** * Retrieve vector tail (last elements) * @param len The number of elements to retrieve * @return A vector containing the last elements of this vector */ inline SliceVector tail(unsigned int len) const { if (len < length()) return SliceVector(*this,length() - len,len); return SliceVector(); } /** * Retrieve vector tail (last elements) * @param len The number of elements to retrieve * @param dest Destination vector * @return True on success, false on failure (not enough data in our vector) */ inline bool tail(SliceVector& dest, unsigned int len) const { if (len <= length()) return dest.initSlice(true,*this,length() - len,len); dest.setData(); return false; } /** * Retrieve a vector slice * @param offs Offset in our vector * @param len The number of elements to retrieve * @return A vector containing the requested slice * (empty if offset/length are invalid) */ inline SliceVector slice(unsigned int offs, unsigned int len) const { return SliceVector(*this,offs,len); } /** * Set a slice of this vector to another one. * The destination vector will be changed * @param dest Destination vector * @param offs Offset in our vector * @param len The number of elements (0 to use all available from offset) * @return True on success, false on failure (not enough data in our vector) */ inline bool slice(SliceVector& dest, unsigned int offs, unsigned int len = 0) const { return dest.initSlice(true,*this,offs,len); } /** * Copies elements from another vector to this one. * NOTE: This method don't check for overlapping data * @param src The source vector * @param len The number of elements to copy * @param offs The start index in our vector * @param srcOffs The start index in the source vector * @return True on success, false on failure (not enough data in source vector or * not enough space in this vector) */ inline bool copy(const SliceVector& src, unsigned int len, unsigned int offs = 0, unsigned int srcOffs = 0) { return RefStorage::copy(data(offs,len),src.data(srcOffs,len),size(len)); } /** * Fill the buffer with 0 * @param offs The offset * @param len The number of elements to retrieve (must not be 0) */ inline void bzero(unsigned int offs, unsigned int len) { RefStorage::fill(data(offs,len),size(len)); } /** * Fill the buffer with 0 */ inline void bzero() { RefStorage::fill(data(),size()); } /** * Fill the vector with a given value * @param value The value to be set in this vector */ inline void fill(const Obj& value) { Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d) *d = value; } /** * Apply an unary function to all elements in this vector * @param func Function to apply */ inline void apply(void (*func)(Obj&)) { Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d) (*func)(*d); } /** * Sum vector values * @return The sum of the vector elements */ inline Obj sum() const { Obj result(0); const Obj* d = data(); for (const Obj* last = end(d,length()); d != last; ++d) result += *d; return result; } /** * Apply a function to all vector elements and take the sum of the results * @param func Function to apply * @return The result */ inline Obj sumApply(Obj (*func)(const Obj&)) const { Obj result(0); const Obj* d = data(); for (const Obj* last = end(d,length()); d != last; ++d) result += (*func)(*d); return result; } /** * Apply a function to all vector elements and take the sum of the results * @param func Function to apply * @return The result */ inline float sumApplyF(float (*func)(const Obj&)) const { float result = 0; const Obj* d = data(); for (const Obj* last = end(d,length()); d != last; ++d) result += (*func)(*d); return result; } /** * Sum this vector with another one * @param other Vector to sum with this one * @return True on sucess, false on failure (vectors don't have the same length) */ inline bool sum(const SliceVector& other) { if (length() != other.length()) return false; const Obj* od = other.m_data; Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d, ++od) *d += *od; return true; } /** * Add a value to each element of the vector * @param value Value to add */ inline void sum(const Obj& value) { Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d) *d += value; } /** * Substract another vector from this one * @param other Vector to substract * @return True on sucess, false on failure (vectors don't have the same length) */ inline bool sub(const SliceVector& other) { if (length() != other.length()) return false; const Obj* od = other.m_data; Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d, ++od) *d -= *od; return true; } /** * Substract a value from each element of the vector * @param value Value to substract */ inline void sub(const Obj& value) { Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d) *d -= value; } /** * Multiply this vector with another one * @param other Vector to multiply with * @return True on sucess, false on failure (vectors don't have the same length) */ inline bool mul(const SliceVector& other) { if (length() != other.length()) return false; const Obj* od = other.m_data; Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d, ++od) *d *= *od; return true; } /** * Multiply this vector with a value * @param value Value to multiply with */ inline void mul(const Obj& value) { Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d) *d *= value; } /** * Multiply this vector with a value * @param value Value to multiply with */ inline void mul(float value) { Obj* d = data(); for (Obj* last = end(d,length()); d != last; ++d) *d *= value; } /** * Indexing operator with unsigned int * @param index Index of element to retrieve * @return The element at requested index */ inline Obj& operator[](unsigned int index) { YMATH_FAIL(index < m_length, "SliceVector::operator[] index out of bounds [%p]",this); return m_data[index]; } /** * Indexing operator with unsigned int * @param index Index of element to retrieve * @return The element at requested index */ inline const Obj& operator[](unsigned int index) const { YMATH_FAIL(index < m_length, "SliceVector::operator[] index out of bounds [%p]",this); return m_data[index]; } /** * Indexing operator with signed int * @param index Index of element to retrieve * @return The element at requested index */ inline Obj& operator[](signed int index) { YMATH_FAIL((unsigned int)index < m_length, "SliceVector::operator[] index out of bounds [%p]",this); return m_data[index]; } /** * Indexing operator with signed int * @param index Index of element to retrieve * @return The element at requested index */ inline const Obj& operator[](signed int index) const { YMATH_FAIL((unsigned int)index < m_length, "SliceVector::operator[] index out of bounds [%p]",this); return m_data[index]; } /** * Equality operator * @param other Original vector * @return True if the vectors are equal, false otherwise */ inline bool operator==(const SliceVector& other) const { return equals(other); } /** * Inequality operator * @param other Original vector * @return True if the vectors are not equal, false otherwise */ inline bool operator!=(const SliceVector& other) const { return !equals(other); } /** * Asignment operator. Builds a slice of another vector * @param other Original vector * @return A reference to this vector */ inline SliceVector& operator=(const SliceVector& other) { setSlice(other); return *this; } /** * Sum this vector with another one * @param other Vector to sum with this one * @return A reference to this vector */ inline SliceVector& operator+=(const SliceVector& other) { YMATH_FAIL(length() == other.length(), "SliceVector(+=): invalid lengths [%p]",this); sum(other); return *this; } /** * Add a value to each element of the vector * @param value Value to add * @return A reference to this vector */ inline SliceVector& operator+=(const Obj& value) { sum(value); return *this; } /** * Substract another vector from this one * @param other Vector to substract * @return A reference to this vector */ inline SliceVector& operator-=(const SliceVector& other) { YMATH_FAIL(length() == other.length(), "SliceVector(-=): invalid lengths [%p]",this); sub(other); return *this; } /** * Substract a value from each element of the vector * @param value Value to substract * @return A reference to this vector */ inline SliceVector& operator-=(const Obj& value) { sub(value); return *this; } /** * Multiply this vector with another one * @param other Vector to multiply with * @return A reference to this vector */ inline SliceVector& operator*=(const SliceVector& other) { YMATH_FAIL(length() == other.length(), "SliceVector(*=): invalid lengths [%p]",this); mul(other); return *this; } /** * Multiply this vector with a value * @param value Value to multiply with * @return A reference to this vector */ inline SliceVector& operator*=(const Obj& value) { mul(value); return *this; } /** * Multiply this vector with a value * @param value Value to multiply with * @return A reference to this vector */ inline SliceVector& operator*=(float value) { mul(value); return *this; } /** * Compare this vector to another one (compare storage) * @param other Vector to compare with * @return True if they are equal */ inline bool equals(const SliceVector& other) const { return length() == other.length() && RefStorage::equals(data(),other.data(),size()); } /** * Dump data to a string (append) * @param buf Destination string * @param func Pointer to function who appends the object to a String * (0 to dump all available from offset) * @param sep Vector elements separator * @param fmt Optional format to use * @return Destination string address */ String& dump(String& buf, String& (*func)(String& s, const Obj& o, const char* sep, const char* fmt), const char* sep = ",", const char* fmt = 0) const { const Obj* d = data(); if (!(d && func)) return buf; String localBuf; for (const Obj* last = end(d,length()); d != last; ++d) (*func)(localBuf,*d,sep,fmt); return buf.append(localBuf); } /** * Dump this vector to string, split it and append lines to a buffer. * Line prefix length is not included when line length is calculated. * Separator length is included in line length * @param buf Destination string * @param lineLen Line length in bytes * @param func Pointer to function who append the object to a String * @param offset Offset in first line (if incomplete). No data will be * added on first line if offset is greater then line length * @param linePrefix Prefix for new lines. Empty string to use the suffix * @param suffix String to always add to final result (even if no data dumped) * @param sep Vector elements separator * @param fmt Optional format to use * @return Destination string address */ String& dump(String& buf, unsigned int lineLen, String& (*func)(String& s, const Obj& o, const char* sep, const char* fmt), unsigned int offset = 0, const char* linePrefix = 0, const char* suffix = "\r\n", const char* sep = ",", const char* fmt = 0) const { const Obj* d = data(); if (!(d && func)) return buf.append(suffix); if (TelEngine::null(linePrefix)) linePrefix = suffix; if (!lineLen || TelEngine::null(linePrefix)) return dump(buf,func,sep,fmt) << suffix; String localBuf; for (const Obj* last = end(d,length()); d != last;) { String tmp; (*func)(tmp,*d,0,fmt); if (++d != last) tmp << sep; offset += tmp.length(); if (offset > lineLen) { localBuf << linePrefix; offset = tmp.length(); } localBuf << tmp; } return buf << localBuf << suffix; } /** * Hexify data * @param buf Destination string * @param sep Optional separator * @return Destination string address */ inline String& hexify(String& buf, char sep = 0) const { return buf.hexify((void*)data(),size(),sep); } /** * Hexify data, split it and append lines to a string * @param buf Destination string * @param lineLen Line length, characters to copy * @param offset Offset in first line (if incomplete). No data will be * added on first line if offset is greater then line length * @param linePrefix Prefix for new lines. * Set it to empty string or 0 to use the suffix * @param suffix End of line for the last line * @return Destination string address */ inline String& dumpHex(String& buf, unsigned int lineLen, unsigned int offset = 0, const char* linePrefix = 0, const char* suffix = "\r\n") const { String h; return RefStorage::dumpSplit(buf,hexify(h),lineLen,offset,linePrefix,suffix); } /** * Reset storage from a hexadecimal string representation. * Clears the vector at start, i.e. the vector will be empty on failure. * The vector may be empty on success also. * Each octet must be represented in the input string with 2 hexadecimal characters. * If a separator is specified, the octets in input string must be separated using * exactly 1 separator. Only 1 leading or 1 trailing separators are allowed. * @param str Input character string * @param len Length of input string * @param sep Separator character used between octets. * [-128..127]: expected separator (0: no separator is expected). * Detect the separator if other value is given * @return 0 on success, negative if unhexify fails, * positive if the result is not a multiple of Obj size */ int unHexify(const char* str, unsigned int len, int sep = 255) { setData(); DataBlock db; bool ok = (sep < -128 || sep > 127) ? db.unHexify(str,len) : db.unHexify(str,len,(char)sep); if (ok && (db.length() % objSize() == 0)) { initStorage(db.length() / objSize(),(const Obj*)db.data(0,db.length())); return 0; } return ok ? 1 : -1; } /** * Unhexify data * @param str Input string * @param sep Separator character used between octets * @return See unHexify(const char*,unsigned int,char) */ inline int unHexify(const String& str, int sep = 255) { return unHexify(str.c_str(),str.length(),sep); } /** * Retrieve the object size * @return Obj size in bytes */ static inline unsigned int objSize() { return sizeof(Obj); } /** * Retrieve the length in bytes of a buffer containing 'count' objects * @param len Buffer length * @return Buffer length in bytes */ static inline unsigned int size(unsigned int len) { return len * objSize(); } protected: // Return end-of-data pointer from start and given length inline Obj* end(Obj* start, unsigned int len) { return start ? (start + len) : 0; } inline const Obj* end(const Obj* start, unsigned int len) const { return start ? (start + len) : 0; } // Set data. Reset storage if we don't have a valid data pointer // Return true if we have valid data inline bool setData(Obj* data = 0, unsigned int len = 0, unsigned int maxLen = 0) { m_data = data; if (m_data) { m_length = len; m_maxLen = maxLen; } else { m_length = m_maxLen = 0; TelEngine::destruct(m_storage); } return m_data != 0; } // Build storage, update data. This method assumes our data is cleared // If data is given 'len' elements will be copied from it to storage inline bool initStorage(unsigned int len, const Obj* data = 0, unsigned int maxLen = 0) { if (maxLen < len) maxLen = len; if (!maxLen) return false; if (!data || maxLen == len) m_storage = new RefStorage(data,size(maxLen)); else { m_storage = new RefStorage(0,size(maxLen)); m_storage->set(data,size(len)); } return setData((Obj*)m_storage->data(0,1),len,maxLen); } // Build storage from slice and update data. // Clear data if requested inline bool initSlice(bool del, const SliceVector& other, unsigned int offs = 0, unsigned int len = 0) { if (!len) len = other.length(); Obj* d = (Obj*)other.data(offs,len); if (!d) { if (del) setData(); return len == 0; } if (m_storage == other.m_storage) return setData(d,len,len); RefStorage* tmp = other.m_storage; if (tmp->ref()) { TelEngine::destruct(m_storage); m_storage = tmp; return setData(d,len,len); } Debug(DebugFail,"SliceVector storage ref() failed"); return del ? setData() : false; } RefStorage* m_storage; // Vector storage Obj* m_data; // Pointer to data unsigned int m_length; // Data length unsigned int m_maxLen; // Max storage }; typedef SliceVector ComplexVector; typedef SliceVector FloatVector; typedef SliceVector ByteVector; /** * This vector holds bit values using 1 byte. It implements methods operating on bits. * NOTE: The array indexing operator allows setting invalid values (not 1 or 0). * The pack/unpack methods are safe (they will handle non 0 values as bit 1). * The comparison operators may fail for vectors containing values other than 0 or 1. * @short A slice vector holding bits */ class YATE_API BitVector : public ByteVector { public: /** * Constructor */ inline BitVector() {} /** * Copy constructor. * Builds a slice of another vector * @param other Original vector */ inline BitVector(const BitVector& other) : ByteVector(other) {} /** * Constructor. * Build the vector storage * @param len Length of data * @param maxLen Optional vector maximum length * (it will be adjusted to be at least len) */ explicit inline BitVector(unsigned int len, unsigned int maxLen = 0) : ByteVector(len,0,maxLen) {} /** * Constructor. * Builds a slice of another vector * @param other Original vector * @param offs Offset in the original vector * @param len The number of elements (0 to use all available from offset) */ explicit inline BitVector(const BitVector& other, unsigned int offs, unsigned int len = 0) : ByteVector(other,offs,len) {} /** * Constructor. Build from string bits * @param str String bits ('1' -> 1, else -> 0) * @param maxLen Optional vector maximum length */ explicit BitVector(const char* str, unsigned int maxLen = 0); /** * Check if this vector contains valid values (0 or 1) * @return True on success, false if the vector contains values other than 0 or 1 */ bool valid() const; /** * Set float bit values from this vector (0 -> 0.0F, non 0 -> 1.0F). * The destination vector will be resized to this vector's length * @param dest The destination vector * @return True on success, false if destination resize failed */ bool get(FloatVector& dest) const; /** * Initializes this vector from float values (0.0F -> 0, non 0 -> 1). * The vector will be resized to input length * @param input The input vector * @return True on success, false if resize failed */ bool set(const FloatVector& input); /** * Apply XOR on vector elements using a given value's bits, MSB first. * Given v31,v30,...,v0 the value's bits in MSB order the result will be * data()[offs] ^= v31, data()[offs+1] ^= v30 ... * @param value Value to use * @param offs Start position in this BitVector * @param len The number of bits to use */ void xorMsb(uint32_t value, unsigned int offs = 0, uint8_t len = 32); /** * Apply XOR on vector elements using a given value's bits, MSB first. * Given v15,v14,...,v0 the value's bits in MSB order the result will be * data()[offs] ^= v15, data()[offs+1] ^= v14 ... * @param value Value to use * @param offs Start position in this BitVector * @param len The number of bits to use */ inline void xorMsb16(uint16_t value, unsigned int offs = 0, uint8_t len = 16) { return xorMsb((uint32_t)value << 16,offs,len <= 16 ? len : 16); } /** * Pack up to 64 bits, LSB-first (i.e. first bit goes to LSB in destination) * @param offs The start offset * @param len The number of elements to be packed (-1 to pack all available). * No more than 64 bits will be packed * @return The packed 64-bit value */ uint64_t pack(unsigned int offs = 0, int len = -1) const; /** * Unpack up to 64 bits into this vector, LSB first * @param value Value to unpack * @param offs Optional start offset * @param len The number of bits to unpack */ void unpack(uint64_t value, unsigned int offs = 0, uint8_t len = 64); /** * Unpack up to 32 bits into this vector (MSB to LSB). * MSB from value is the first unpacked bit * @param value The value to be unpacked * @param offs Optional start offset * @param len The number of bits to unpack */ void unpackMsb(uint32_t value, unsigned int offs = 0, uint8_t len = 32); /** * Unpack up to 16 bits into this vector (MSB to LSB). * MSB from value is the first unpacked bit * @param value The value to be unpacked * @param offs Optional start offset * @param len The number of bits to unpack */ inline void unpackMsb16(uint16_t value, unsigned int offs = 0, uint8_t len = 16) { unpackMsb((uint32_t)value << 16,offs,len <= 16 ? len : 16); } /** * Pack bits into a ByteVector (LSB source to MSB in destination). * MSB of first byte in destination will have the same value of the * first bit in this vector. * Remaining elements in destination are left untouched * @param dest Destination vector * @return True on success, false on failure (not enough space in destination vector) */ bool pack(ByteVector& dest) const; /** * Unpack a ByteVector into this BitVector. * MSB of the first element in source goes to first bit in this vector. * Remaining bits are left untouched * @param src Source byte vector * @return True on success, false if there is not enough space to unpack */ bool unpack(const ByteVector& src); /** * Append bits to string * @param buf Destination string * @param offs Optional starting index * @param len The number of elements to be added (negative to add all) * @return Destination string address */ String& appendTo(String& buf, unsigned int offs = 0, int len = -1) const; /** * Build a String from vector bits and return it * @param offs Optional starting index * @param len The number of elements to be added (negative to add all) * @return A newly created String containing vector bits */ inline String toString(unsigned int offs, int len = -1) const { String tmp; return appendTo(tmp,offs,len); } /** * Set a slice containing another vector * @param other Original vector * @param offs Offset in the original vector * @param len The number of elements (0 to use all available from offset) * @return True on success, false on failure */ inline bool setSlice(const BitVector& other, unsigned int offs = 0, unsigned int len = 0) { return initSlice(true,other,offs,len); } /** * Retrieve vector head * @param len The number of elements to retrieve * @return A vector containing the first elements of this vector */ inline BitVector head(unsigned int len) const { return slice(0,len); } /** * Retrieve vector head * @param dest Destination vector * @param len The number of elements to retrieve * @return True on success, false on failure (not enough data in our vector) */ inline bool head(BitVector& dest, unsigned int len) const { return slice(dest,0,len); } /** * Retrieve vector tail (last elements) * @param len The number of elements to retrieve * @return A vector containing the last elements of this vector */ inline BitVector tail(unsigned int len) const { if (len < length()) return BitVector(*this,length() - len,len); return BitVector(); } /** * Retrieve vector tail (last elements) * @param len The number of elements to retrieve * @param dest Destination vector * @return True on success, false on failure (not enough data in our vector) */ inline bool tail(BitVector& dest, unsigned int len) const { if (len <= length()) return dest.initSlice(true,*this,length() - len,len); dest.setData(); return false; } /** * Retrieve a vector slice * @param offs Offset in our vector * @param len The number of elements to retrieve * @return A vector containing the requested slice * (empty if offset/length are invalid) */ inline BitVector slice(unsigned int offs, unsigned int len) const { return BitVector(*this,offs,len); } /** * Set a slice of this vector to another one. * The destination vector will be changed * @param dest Destination vector * @param offs Offset in our vector * @param len The number of elements (0 to use all available from offset) * @return True on success, false on failure (not enough data in our vector) */ inline bool slice(BitVector& dest, unsigned int offs, unsigned int len = 0) const { return dest.initSlice(true,*this,offs,len); } }; /** * This class global Math utility methods * @short Math utilities */ class YATE_API Math { public: /** * Dump a Complex number to a String * @param buf Destination string * @param val Value to dump * @param sep Optional separator * @param fmt Format to use ("%g%+gi" if not given) * @return Destination string address */ static String& dumpComplex(String& buf, const Complex& val, const char* sep = 0, const char* fmt = 0); /** * Dump a float number to a String * @param buf Destination string * @param val Value to dump * @param sep Optional separator * @param fmt Format to use ("%g" if not given) * @return Destination string address */ static String& dumpFloat(String& buf, const float& val, const char* sep = 0, const char* fmt = 0); }; /** * Addition operator * @param c1 First number * @param c2 Second number * @return The result */ inline Complex operator+(const Complex& c1, const Complex& c2) { Complex tmp(c1); return (tmp += c2); } /** * Addition operator * @param c A Complex number * @param f A float value * @return The result */ inline Complex operator+(const Complex& c, float f) { Complex tmp(c); return (tmp += f); } /** * Addition operator * @param f The float value * @param c The Complex number * @return The result */ inline Complex operator+(float f, const Complex& c) { return operator+(c,f); } /** * Substraction operator * @param c1 First number * @param c2 Second number * @return The result */ inline Complex operator-(const Complex& c1, const Complex& c2) { Complex tmp(c1); return (tmp -= c2); } /** * Substraction operator * @param c A Complex number * @param f A float value * @return The result */ inline Complex operator-(const Complex& c, float f) { Complex tmp(c); return (tmp -= f); } /** * Multiplication operator * @param c1 First number * @param c2 Second number * @return The result */ inline Complex operator*(const Complex& c1, const Complex& c2) { Complex tmp(c1); return (tmp *= c2); } /** * Multiplication operator * @param c A Complex number * @param f A float value * @return The result */ inline Complex operator*(const Complex& c, float f) { Complex tmp(c); return (tmp *= f); } /** * Multiplication operator * @param f A float value * @param c A Complex number * @return The result */ inline Complex operator*(float f, const Complex& c) { return operator*(c,f); } /** * Division operator * @param c1 First number * @param c2 Second number * @return The result */ inline Complex operator/(const Complex& c1, const Complex& c2) { Complex tmp(c1); return (tmp /= c2); } /** * Division operator * @param c A Complex number * @param f A float value * @return The result */ inline Complex operator/(const Complex& c, float f) { Complex tmp(c); return (tmp /= f); } /** * Append operator: append a Complex number to a String * @param str Destination string * @param c Complex number to append * @return Destination string reference */ inline String& operator<<(String& str, const Complex& c) { return Math::dumpComplex(str,c); } /** * Append operator: append a BitVector to a String * @param str Destination string * @param b Vector to append * @return Destination string reference */ inline String& operator<<(String& str, const BitVector& b) { return b.appendTo(str); } }; // namespace TelEngine #endif /* __YATEMATH_H */ /* vi: set ts=8 sw=4 sts=4 noet: */