//===-- MIUtilString.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// //++ // File: MIUtilString.h // // Overview: CMIUtilString implementation. // // Environment: Compilers: Visual C++ 12. // gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1 // Libraries: See MIReadmetxt. // // Copyright: None. //-- // Third party headers #include // std::unique_ptr #include // va_list, va_start, var_end #include // std::stringstream #include // for strcpy #include // for ULONG_MAX // In-house headers: #include "MIUtilString.h" //++ ------------------------------------------------------------------------------------ // Details: CMIUtilString constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMIUtilString::CMIUtilString(void) : std::string() { } //++ ------------------------------------------------------------------------------------ // Details: CMIUtilString constructor. // Type: Method. // Args: vpData - Pointer to UTF8 text data. // Return: None. // Throws: None. //-- CMIUtilString::CMIUtilString(const MIchar *vpData) : std::string(vpData) { } //++ ------------------------------------------------------------------------------------ // Details: CMIUtilString constructor. // Type: Method. // Args: vpData - Pointer to UTF8 text data. // Return: None. // Throws: None. //-- CMIUtilString::CMIUtilString(const MIchar *const *vpData) : std::string((const char *)vpData) { } //++ ------------------------------------------------------------------------------------ // Details: CMIUtilString assigment operator. // Type: Method. // Args: vpRhs - Pointer to UTF8 text data. // Return: CMIUtilString & - *this string. // Throws: None. //-- CMIUtilString &CMIUtilString::operator=(const MIchar *vpRhs) { if (*this == vpRhs) return *this; if (vpRhs != nullptr) { assign(vpRhs); } return *this; } //++ ------------------------------------------------------------------------------------ // Details: CMIUtilString assigment operator. // Type: Method. // Args: vrRhs - The other string to copy from. // Return: CMIUtilString & - *this string. // Throws: None. //-- CMIUtilString &CMIUtilString::operator=(const std::string &vrRhs) { if (*this == vrRhs) return *this; assign(vrRhs); return *this; } //++ ------------------------------------------------------------------------------------ // Details: CMIUtilString destructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMIUtilString::~CMIUtilString(void) { } //++ ------------------------------------------------------------------------------------ // Details: Perform a snprintf format style on a string data. A new string object is // created and returned. // Type: Static method. // Args: vrFormat - (R) Format string data instruction. // vArgs - (R) Var list args of any type. // Return: CMIUtilString - Number of splits found in the string data. // Throws: None. //-- CMIUtilString CMIUtilString::FormatPriv(const CMIUtilString &vrFormat, va_list vArgs) { CMIUtilString strResult; MIint nFinal = 0; MIint n = vrFormat.size(); // IOR: mysterious crash in this function on some windows builds not able to duplicate // but found article which may be related. Crash occurs in vsnprintf() or va_copy() // Duplicate vArgs va_list argument pointer to ensure that it can be safely used in // a new frame // http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html va_list argsDup; va_copy(argsDup, vArgs); // Create a copy va_list to reset when we spin va_list argsCpy; va_copy(argsCpy, argsDup); if (n == 0) return strResult; n = n << 4; // Reserve 16 times as much the length of the vrFormat std::unique_ptr pFormatted; while (1) { pFormatted.reset(new char[n + 1]); // +1 for safety margin ::strncpy(&pFormatted[0], vrFormat.c_str(), n); // We need to restore the variable argument list pointer to the start again // before running vsnprintf() more then once va_copy(argsDup, argsCpy); nFinal = ::vsnprintf(&pFormatted[0], n, vrFormat.c_str(), argsDup); if ((nFinal < 0) || (nFinal >= n)) n += abs(nFinal - n + 1); else break; } va_end(argsCpy); va_end(argsDup); strResult = pFormatted.get(); return strResult; } //++ ------------------------------------------------------------------------------------ // Details: Perform a snprintf format style on a string data. A new string object is // created and returned. // Type: Static method. // Args: vFormat - (R) Format string data instruction. // ... - (R) Var list args of any type. // Return: CMIUtilString - Number of splits found in the string data. // Throws: None. //-- CMIUtilString CMIUtilString::Format(const CMIUtilString vFormating, ...) { va_list args; va_start(args, vFormating); CMIUtilString strResult = CMIUtilString::FormatPriv(vFormating, args); va_end(args); return strResult; } //++ ------------------------------------------------------------------------------------ // Details: Perform a snprintf format style on a string data. A new string object is // created and returned. // Type: Static method. // Args: vrFormat - (R) Format string data instruction. // vArgs - (R) Var list args of any type. // Return: CMIUtilString - Number of splits found in the string data. // Throws: None. //-- CMIUtilString CMIUtilString::FormatValist(const CMIUtilString &vrFormating, va_list vArgs) { return CMIUtilString::FormatPriv(vrFormating, vArgs); } //++ ------------------------------------------------------------------------------------ // Details: Splits string into array of strings using delimiter. If multiple delimiter // are found in sequence then they are not added to the list of splits. // Type: Method. // Args: vData - (R) String data to be split up. // vDelimiter - (R) Delimiter char or text. // vwVecSplits - (W) Container of splits found in string data. // Return: MIuint - Number of splits found in the string data. // Throws: None. //-- MIuint CMIUtilString::Split(const CMIUtilString &vDelimiter, VecString_t &vwVecSplits) const { vwVecSplits.clear(); if (this->empty() || vDelimiter.empty()) return 0; MIint nPos = find(vDelimiter); if (nPos == (MIint)std::string::npos) { vwVecSplits.push_back(*this); return 1; } const MIint strLen(length()); if (nPos == strLen) { vwVecSplits.push_back(*this); return 1; } MIuint nAdd1(1); if ((nPos > 0) && (substr(0, nPos) != vDelimiter)) { nPos = 0; nAdd1 = 0; } MIint nPos2 = find(vDelimiter, nPos + 1); while (nPos2 != (MIint)std::string::npos) { const MIuint len(nPos2 - nPos - nAdd1); const std::string strSection(substr(nPos + nAdd1, len)); if (strSection != vDelimiter) vwVecSplits.push_back(strSection.c_str()); nPos += len + 1; nPos2 = find(vDelimiter, nPos + 1); nAdd1 = 0; } const std::string strSection(substr(nPos, strLen - nPos)); if ((strSection.length() != 0) && (strSection != vDelimiter)) vwVecSplits.push_back(strSection.c_str()); return vwVecSplits.size(); } //++ ------------------------------------------------------------------------------------ // Details: Splits string into array of strings using delimiter. However the string is // also considered for text surrounded by quotes. Text with quotes including the // delimiter is treated as a whole. If multiple delimiter are found in sequence // then they are not added to the list of splits. Quotes that are embedded in the // the string as string formatted quotes are ignored (proceeded by a '\\') i.e. // "\"MI GDB local C++.cpp\":88". // Type: Method. // Args: vData - (R) String data to be split up. // vDelimiter - (R) Delimiter char or text. // vwVecSplits - (W) Container of splits found in string data. // Return: MIuint - Number of splits found in the string data. // Throws: None. //-- MIuint CMIUtilString::SplitConsiderQuotes(const CMIUtilString &vDelimiter, VecString_t &vwVecSplits) const { vwVecSplits.clear(); if (this->empty() || vDelimiter.empty()) return 0; MIint nPos = find(vDelimiter); if (nPos == (MIint)std::string::npos) { vwVecSplits.push_back(*this); return 1; } const MIint strLen(length()); if (nPos == strLen) { vwVecSplits.push_back(*this); return 1; } // Look for more quotes bool bHaveQuotes = false; const MIchar cBckSlash = '\\'; const MIchar cQuote = '"'; MIint nPosQ = find(cQuote); MIint nPosQ2 = (MIint)std::string::npos; if (nPosQ != (MIint)std::string::npos) { nPosQ2 = nPosQ + 1; while (nPosQ2 < strLen) { nPosQ2 = find(cQuote, nPosQ2); if ((nPosQ2 == (MIint)std::string::npos) || (at(nPosQ2 - 1) != cBckSlash)) break; nPosQ2++; } bHaveQuotes = (nPosQ2 != (MIint)std::string::npos); } MIuint nAdd1(1); if ((nPos > 0) && (substr(0, nPos) != vDelimiter)) { nPos = 0; nAdd1 = 0; } MIint nPos2 = find(vDelimiter, nPos + 1); while (nPos2 != (MIint)std::string::npos) { if (!bHaveQuotes || (bHaveQuotes && ((nPos2 > nPosQ2) || (nPos2 < nPosQ)))) { // Extract text or quoted text const MIuint len(nPos2 - nPos - nAdd1); const std::string strSection(substr(nPos + nAdd1, len)); if (strSection != vDelimiter) vwVecSplits.push_back(strSection.c_str()); nPos += len + 1; nPos2 = find(vDelimiter, nPos + 1); nAdd1 = 0; if (bHaveQuotes && (nPos2 > nPosQ2)) { // Reset, look for more quotes bHaveQuotes = false; nPosQ = find(cQuote, nPos); nPosQ2 = (MIint)std::string::npos; if (nPosQ != (MIint)std::string::npos) { nPosQ2 = find(cQuote, nPosQ + 1); bHaveQuotes = (nPosQ2 != (MIint)std::string::npos); } } } else { // Skip passed text in quotes nPos2 = find(vDelimiter, nPosQ2 + 1); } } const std::string strSection(substr(nPos, strLen - nPos)); if ((strSection.length() != 0) && (strSection != vDelimiter)) vwVecSplits.push_back(strSection.c_str()); return vwVecSplits.size(); } //++ ------------------------------------------------------------------------------------ // Details: Remove '\n' from the end of string if found. It does not alter // *this string. // Type: Method. // Args: None. // Return: CMIUtilString - New version of the string. // Throws: None. //-- CMIUtilString CMIUtilString::StripCREndOfLine(void) const { const MIint nPos = rfind('\n'); if (nPos == (MIint)std::string::npos) return *this; const CMIUtilString strNew(substr(0, nPos).c_str()); return strNew; } //++ ------------------------------------------------------------------------------------ // Details: Remove all '\n' from the string and replace with a space. It does not alter // *this string. // Type: Method. // Args: None. // Return: CMIUtilString - New version of the string. // Throws: None. //-- CMIUtilString CMIUtilString::StripCRAll(void) const { return FindAndReplace("\n", " "); } //++ ------------------------------------------------------------------------------------ // Details: Find and replace all matches of a sub string with another string. It does not // alter *this string. // Type: Method. // Args: vFind - (R) The string to look for. // vReplaceWith - (R) The string to replace the vFind match. // Return: CMIUtilString - New version of the string. // Throws: None. //-- CMIUtilString CMIUtilString::FindAndReplace(const CMIUtilString &vFind, const CMIUtilString &vReplaceWith) const { if (vFind.empty() || this->empty()) return *this; MIint nPos = find(vFind); if (nPos == (MIint)std::string::npos) return *this; CMIUtilString strNew(*this); while (nPos != (MIint)std::string::npos) { strNew.replace(nPos, vFind.length(), vReplaceWith); nPos += vReplaceWith.length(); nPos = strNew.find(vFind, nPos); } return strNew; } //++ ------------------------------------------------------------------------------------ // Details: Check if *this string is a decimal number. // Type: Method. // Args: None. // Return: bool - True = yes number, false not a number. // Throws: None. //-- bool CMIUtilString::IsNumber(void) const { if (empty()) return false; if ((at(0) == '-') && (length() == 1)) return false; const MIint nPos = find_first_not_of("-.0123456789"); if (nPos != (MIint)std::string::npos) return false; return true; } //++ ------------------------------------------------------------------------------------ // Details: Extract the number from the string. The number can be either a hexadecimal or // natural number. It cannot contain other non-numeric characters. // Type: Method. // Args: vwrNumber - (W) Number exracted from the string. // Return: bool - True = yes number, false not a number. // Throws: None. //-- bool CMIUtilString::ExtractNumber(MIint64 &vwrNumber) const { vwrNumber = 0; if (!IsNumber()) { if (ExtractNumberFromHexadecimal(vwrNumber)) return true; return false; } std::stringstream ss(const_cast(*this)); ss >> vwrNumber; return true; } //++ ------------------------------------------------------------------------------------ // Details: Extract the number from the hexadecimal string.. // Type: Method. // Args: vwrNumber - (W) Number exracted from the string. // Return: bool - True = yes number, false not a number. // Throws: None. //-- bool CMIUtilString::ExtractNumberFromHexadecimal(MIint64 &vwrNumber) const { vwrNumber = 0; const MIint nPos = find_first_not_of("x01234567890ABCDEFabcedf"); if (nPos != (MIint)std::string::npos) return false; const MIint64 nNum = ::strtoul(this->c_str(), nullptr, 16); if (nNum != LONG_MAX) { vwrNumber = nNum; return true; } return true; } //++ ------------------------------------------------------------------------------------ // Details: Determine if the text is all valid alpha numeric characters. Letters can be // either upper or lower case. // Type: Static method. // Args: vrText - (R) The text data to examine. // Return: bool - True = yes all alpha, false = one or more chars is non alpha. // Throws: None. //-- bool CMIUtilString::IsAllValidAlphaAndNumeric(const MIchar &vrText) { const MIuint len = ::strlen(&vrText); if (len == 0) return false; MIchar *pPtr = const_cast(&vrText); for (MIuint i = 0; i < len; i++, pPtr++) { const MIchar c = *pPtr; if (::isalnum((int)c) == 0) return false; } return true; } //++ ------------------------------------------------------------------------------------ // Details: Check if two strings share equal contents. // Type: Method. // Args: vrLhs - (R) String A. // vrRhs - (R) String B. // Return: bool - True = yes equal, false - different. // Throws: None. //-- bool CMIUtilString::Compare(const CMIUtilString &vrLhs, const CMIUtilString &vrRhs) { // Check the sizes match if (vrLhs.size() != vrRhs.size()) return false; return (::strncmp(vrLhs.c_str(), vrRhs.c_str(), vrLhs.size()) == 0); } //++ ------------------------------------------------------------------------------------ // Details: Remove from either end of *this string the following: " \t\n\v\f\r". // Type: Method. // Args: None. // Return: CMIUtilString - Trimmed string. // Throws: None. //-- CMIUtilString CMIUtilString::Trim(void) const { CMIUtilString strNew(*this); const MIchar *pWhiteSpace = " \t\n\v\f\r"; const MIint nPos = find_last_not_of(pWhiteSpace); if (nPos != (MIint)std::string::npos) { strNew = substr(0, nPos + 1).c_str(); } const MIint nPos2 = strNew.find_first_not_of(pWhiteSpace); if (nPos2 != (MIint)std::string::npos) { strNew = strNew.substr(nPos2).c_str(); } return strNew; } //++ ------------------------------------------------------------------------------------ // Details: Remove from either end of *this string the specified character. // Type: Method. // Args: None. // Return: CMIUtilString - Trimmed string. // Throws: None. //-- CMIUtilString CMIUtilString::Trim(const MIchar vChar) const { CMIUtilString strNew(*this); const MIint nLen = strNew.length(); if (nLen > 1) { if ((strNew[0] == vChar) && (strNew[nLen - 1] == vChar)) strNew = strNew.substr(1, nLen - 2).c_str(); } return strNew; } //++ ------------------------------------------------------------------------------------ // Details: Do a printf equivalent for printing a number in binary i.e. "b%llB". // Type: Static method. // Args: vnDecimal - (R) The number to represent in binary. // Return: CMIUtilString - Binary number in text. // Throws: None. //-- CMIUtilString CMIUtilString::FormatBinary(const MIuint64 vnDecimal) { CMIUtilString strBinaryNumber; const MIuint nConstBits = 64; MIuint nRem[nConstBits + 1]; MIint i = 0; MIuint nLen = 0; MIuint64 nNum = vnDecimal; while ((nNum > 0) && (nLen < nConstBits)) { nRem[i++] = nNum % 2; nNum = nNum >> 1; nLen++; } MIchar pN[nConstBits + 1]; MIuint j = 0; for (i = nLen; i > 0; --i, j++) { pN[j] = '0' + nRem[i - 1]; } pN[j] = 0; // String NUL termination strBinaryNumber = CMIUtilString::Format("0b%s", &pN[0]); return strBinaryNumber; } //++ ------------------------------------------------------------------------------------ // Details: Remove from a string doubled up characters so only one set left. Characters // are only removed if the previous character is already a same character. // Type: Method. // Args: vChar - (R) The character to search for and remove adjacent duplicates. // Return: CMIUtilString - New version of the string. // Throws: None. //-- CMIUtilString CMIUtilString::RemoveRepeatedCharacters(const MIchar vChar) { return RemoveRepeatedCharacters(0, vChar); } //++ ------------------------------------------------------------------------------------ // Details: Recursively remove from a string doubled up characters so only one set left. // Characters are only removed if the previous character is already a same // character. // Type: Method. // Args: vChar - (R) The character to search for and remove adjacent duplicates. // vnPos - (R) Character position in the string. // Return: CMIUtilString - New version of the string. // Throws: None. //-- CMIUtilString CMIUtilString::RemoveRepeatedCharacters(const MIint vnPos, const MIchar vChar) { const MIchar cQuote = '"'; // Look for first quote of two MIint nPos = find(cQuote, vnPos); if (nPos == (MIint)std::string::npos) return *this; const MIint nPosNext = nPos + 1; if (nPosNext > (MIint)length()) return *this; if (at(nPosNext) == cQuote) { *this = substr(0, nPos) + substr(nPosNext, length()); RemoveRepeatedCharacters(nPosNext, vChar); } return *this; } //++ ------------------------------------------------------------------------------------ // Details: Is the text in *this string surrounded by quotes. // Type: Method. // Args: None. // Return: bool - True = Yes string is quoted, false = no quoted. // Throws: None. //-- bool CMIUtilString::IsQuoted(void) const { const MIchar cQuote = '"'; if (at(0) != cQuote) return false; const MIint nLen = length(); if ((nLen > 0) && (at(nLen - 1) != cQuote)) return false; return true; }