364 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| #ifndef AIS_STR_UTIL_H
 | |
| #define AIS_STR_UTIL_H
 | |
| 
 | |
| 
 | |
| #include <cstring>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <stdarg.h>
 | |
| #include <string>
 | |
| #include <algorithm>
 | |
| #include <vector>
 | |
| #include <memory>
 | |
| 
 | |
| 
 | |
| 
 | |
| /*
 | |
| 
 | |
| namespace
 | |
| {
 | |
| //#if !defined(_GNU_SOURCE)
 | |
|     // from 'https://github.com/freebsd/freebsd/blob/master/lib/libc/string/memrchr.c'
 | |
|     inline void *memrchr(const void *s, int c, size_t n)
 | |
|     {
 | |
|         const unsigned char *cp;
 | |
|         
 | |
|         if (n != 0) {
 | |
|             cp = (unsigned char *)s + n;
 | |
|             do {
 | |
|                 if (*(--cp) == (unsigned char)c) {
 | |
|                     return((void *)cp);
 | |
|                 }
 | |
|             } while (--n != 0);
 | |
|         }
 | |
|         
 | |
|         return nullptr;
 | |
|     }
 | |
| //#endif
 | |
| };
 | |
| 
 | |
| */
 | |
| 
 | |
| 
 | |
| namespace AIS
 | |
| {	
 | |
|     inline char ascii_toupper(char _ch)
 | |
|     {
 | |
|         if (_ch <= 'z' && _ch >= 'a') return _ch - 32;
 | |
|         else return _ch;
 | |
|     }
 | |
|     
 | |
|     inline int ascii_stricmp(const std::string &_a, const std::string &_b)
 | |
|     {
 | |
|         const char *pChA = _a.c_str();
 | |
|         const char *pChB = _b.c_str();
 | |
|         
 | |
|         for (;;)
 | |
|         {
 | |
|             char chA = *pChA++;
 | |
|             char chB = *pChB++;
 | |
|             
 | |
|             if ((chA == '\0') && (chB == '\0')) return 0;
 | |
|             else if (chA == '\0') return -1;
 | |
|             else if (chB == '\0') return 1;
 | |
|             
 | |
|             chA = ascii_toupper(chA);
 | |
|             chB = ascii_toupper(chB);
 | |
|             
 | |
|             if (chA < chB) return -1;
 | |
|             else if (chA > chB) return 1;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     inline bool ascii_isspace(char _ch)
 | |
|     {
 | |
|         return (_ch == ' ') || (_ch == '\t') || (_ch == '\n') || (_ch == '\r');
 | |
|     }
 | |
|     
 | |
|     /** strip trailing chars after and including '_chStrip' */
 | |
|     inline std::string &stripTrailingAll(std::string &_str, char _chStrip)
 | |
|     {
 | |
|         const char *pNext = (const char*)memchr(_str.data(), _chStrip, _str.size());
 | |
|         if (pNext != nullptr)
 | |
|         {
 | |
|             _str.resize(pNext - _str.data());
 | |
|         }
 | |
|         
 | |
|         return _str;
 | |
|     }
 | |
| 	
 | |
|     /** strip trailing chars after and including '_chStrip' */
 | |
|     inline std::string stripTrailingAll(const std::string &_str, char _chStrip)
 | |
|     {
 | |
|         std::string ret(_str);
 | |
|         stripTrailingAll((std::string&)ret, _chStrip);
 | |
|         return ret;
 | |
|     }
 | |
|     
 | |
|     /** strip trailing whitespace */
 | |
|     inline std::string &stripTrailingWhitespace(std::string &_str)
 | |
|     {
 | |
|         const char *pStrStart = (const char *)_str.data();
 | |
|         const char *pStrEnd = (const char *)_str.data() + _str.size();
 | |
|         
 | |
|         while (pStrEnd > pStrStart)
 | |
|         {
 | |
|             if (ascii_isspace(*(--pStrEnd)) == false)
 | |
|             {
 | |
|                 size_t n = pStrEnd - pStrStart + 1;
 | |
|                 if (n != _str.size())
 | |
|                 {
 | |
|                     _str.resize(n);
 | |
|                 }
 | |
|                 
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         return _str;
 | |
|     }
 | |
|     
 | |
|     /** strip trailing chars after and including '_chStrip' */
 | |
|     inline std::string stripTrailingWhitespace(const std::string &_str)
 | |
|     {
 | |
|         std::string ret;
 | |
|         stripTrailingWhitespace((std::string&)_str);
 | |
|         return ret;
 | |
|     }
 | |
|     
 | |
|     
 | |
|     
 | |
|     /**
 | |
|      String view that just references data from another buffer.
 | |
|      Minimum string view implementation to work with pre-c++17 STL.
 | |
|      */
 | |
|     class StringRef
 | |
|     {
 | |
|      public:
 | |
|         static const size_t npos = size_t(-1);
 | |
|         
 | |
|      public:
 | |
|         StringRef()
 | |
|             :m_pData(nullptr),
 | |
|              m_uSize(0)
 | |
|         {}
 | |
|         
 | |
|         StringRef(const char *_pData, size_t _uSize)
 | |
|             :m_pData(_pData),
 | |
|              m_uSize(_uSize)
 | |
|         {}
 | |
|         
 | |
|         const char *data() const {return m_pData;}
 | |
|         size_t size() const {return m_uSize;}
 | |
|         bool empty() const {return m_uSize == 0;}
 | |
|         
 | |
|         StringRef substr(size_t _pos = 0, size_t _count = npos) const {
 | |
|             StringRef ret;
 | |
|             
 | |
|             if ( (_count == npos) &&
 | |
|                  (_pos < m_uSize) ) {
 | |
|                 ret.m_pData = m_pData + _pos;
 | |
|                 ret.m_uSize = m_uSize - _pos;
 | |
|             }
 | |
|             else if (_pos + _count < m_uSize) {
 | |
|                 ret.m_pData = m_pData + _pos;
 | |
|                 ret.m_uSize = _count;
 | |
|             }
 | |
|             
 | |
|             return ret;
 | |
|         }
 | |
|         
 | |
|         const char operator [] (size_t _i) const {
 | |
|             return m_pData[_i];
 | |
|         }
 | |
|         
 | |
|         void remove_prefix(size_t _n) {
 | |
|             if (_n < m_uSize) {
 | |
|                 m_pData += _n;
 | |
|                 m_uSize -= _n;
 | |
|             }
 | |
|             else {
 | |
|                 m_pData += m_uSize;
 | |
|                 m_uSize = 0;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         void remove_suffix(size_t _n) {
 | |
|             if (_n < m_uSize) {
 | |
|                 m_uSize -= _n;
 | |
|             }
 | |
|             else {
 | |
|                 m_uSize = 0;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         operator std::string () const {
 | |
|             return std::string(m_pData, m_uSize);
 | |
|         }
 | |
|         
 | |
|      private:
 | |
|         const char  *m_pData;
 | |
|         size_t      m_uSize;
 | |
|     };
 | |
|     
 | |
|     
 | |
|     
 | |
|     /**
 | |
|      Lightweight buffer for processing data chunks.
 | |
|      Internal buffer is allowed to grow, but not shrink.
 | |
|      This avoids allocation and resize init overheads, if the buffer is reused for multiple chunks.
 | |
|      */
 | |
|     struct Buffer
 | |
|     {
 | |
|         Buffer()
 | |
|             :m_uSize(0)
 | |
|         {}
 | |
|         
 | |
|         Buffer(size_t _uReservedSize)
 | |
|             :m_data(_uReservedSize, 0),
 | |
|              m_uSize(0)
 | |
|         {}
 | |
|         
 | |
|         const char *data() const {return m_data.data();}
 | |
|         char *data() {return (char*)m_data.data();}
 | |
|         
 | |
|         size_t size() const {return m_uSize;}
 | |
|         
 | |
|         void resize(size_t _uSize) {
 | |
|             m_uSize = _uSize;
 | |
|             if (m_uSize > m_data.size()) {
 | |
|                 m_data.resize(m_uSize);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         void clear() {
 | |
|             m_uSize = 0;
 | |
|         }
 | |
|         
 | |
|         void append(const char *_pData, size_t _uSize) {
 | |
|             if ( (_uSize > 0) &&
 | |
|                  (_pData != nullptr) )
 | |
|             {
 | |
|                 size_t uOffset = size();
 | |
|                 resize(uOffset + _uSize);
 | |
|                 memcpy(data() + uOffset, _pData, _uSize);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         void pop_front(size_t _uSize) {
 | |
|             if (_uSize < m_uSize) {
 | |
|                 std::memmove((char*)m_data.data(), (char*)m_data.data() + _uSize, m_uSize - _uSize);
 | |
|                 m_uSize -= _uSize;
 | |
|             }
 | |
|             else {
 | |
|                 m_uSize = 0;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         std::vector<char>       m_data;
 | |
|         size_t                  m_uSize;
 | |
|     };
 | |
|     
 | |
|     
 | |
|     /// find the last of '_ch' in _str
 | |
|     inline size_t findLastOf(const StringRef &_str, char _ch)
 | |
|     {
 | |
|         if (_str.size() > 0)
 | |
|         {
 | |
|             const char *pNext = (const char *)memrchr(_str.data(), _ch, _str.size());
 | |
|             if (pNext != nullptr)
 | |
|             {
 | |
|                 return pNext - _str.data();
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         return (size_t)-1;	// npos
 | |
|     }
 | |
|     
 | |
|     /// Converts string to an integer. Returns 0 if conversion failed.
 | |
|     inline int strtoi(const StringRef &_str)
 | |
|     {
 | |
|         // \note: this is a bit of a hack (might scan past end of _str, since not zero terminated, but will be terminated by a comma or end-of-line)
 | |
|         return (int)std::strtol(_str.data(), nullptr, 10);
 | |
|     }
 | |
|     
 | |
|     /// Converts a single-digit string to an integer. Quick and dirty with no error checking, but guaranteed to at
 | |
|     /// least clamp the result to the range [0,9]
 | |
|     inline int single_digit_strtoi(const StringRef &_str)
 | |
|     {
 | |
|         return ((_str.data()[0] - '0') & 0x0f) % 10;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|          Appends first line of text from input, starting at _uOffset (works with <LF> "\n" and <CR><LF> "\r\n").
 | |
|          Returns the number of bytes processed (output includes CR & LF chars).
 | |
|      */
 | |
|     inline size_t getLine(StringRef &_strOutput, const char *_pInput, size_t _uInputSize, size_t _uOffset)
 | |
|     {
 | |
|         const size_t n = _uInputSize - _uOffset;
 | |
|         const char *pData = _pInput + _uOffset;
 | |
|         const char *pSentinel = pData + n;
 | |
| 
 | |
|         // find NL/LF
 | |
|         const char *next = (const char*)memchr(pData, '\n', n);
 | |
|         
 | |
|         if (next == nullptr || next >= pSentinel) {
 | |
|             return 0;
 | |
|         } else {
 | |
|             // \note getLine() output includes <CR> and <LF> chars
 | |
|             int nb = (int)(next - pData + 1);
 | |
|             
 | |
|             // scan past broken line seperator pairs
 | |
|             while (*pData == '\r') {
 | |
|                 pData++;
 | |
|             }
 | |
|             
 | |
|             // create output
 | |
|             _strOutput = StringRef(pData, nb);
 | |
|             
 | |
|             return nb;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|  	/**
 | |
|          Separate input string into words using the delimiter.
 | |
|          The output vector is not resized and this function will not return more words than the size of the output.
 | |
|          Returns the number of words added to output, starting at index 0.
 | |
|      */
 | |
|     template <char CH, typename output_t>
 | |
|     size_t seperate(output_t &_output, const StringRef &_strInput)
 | |
| 	{
 | |
|         const char *pCh = _strInput.data();
 | |
|         const char *pChEnd = pCh + _strInput.size();
 | |
|         size_t uWordCount = 0;
 | |
|         
 | |
|         while ( (pCh < pChEnd) &&
 | |
|                 (uWordCount < _output.size()) ) {
 | |
|                 
 | |
|             const char* next =  (const char*)memchr(pCh, CH, pChEnd - pCh);
 | |
|             if (next == nullptr || next > pChEnd) {
 | |
|                 // no comma found, assume we are in the last word
 | |
|                 next = pChEnd;
 | |
|             }
 | |
|             
 | |
|             _output[uWordCount] = StringRef(pCh, next - pCh);
 | |
|             uWordCount++;
 | |
| 
 | |
|             pCh = next + 1; // continue after comma
 | |
|         }
 | |
|         
 | |
|         return uWordCount;
 | |
| 	}
 | |
|     
 | |
| 
 | |
|     
 | |
| };	// namespace AIS
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #endif // #ifndef AIS_STR_UTIL_H
 |