//===-- 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< char[] > 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: vrFormat - (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 & vrFormating, ... ) { va_list args; va_start( args, vrFormating ); CMIUtilString strResult = CMIUtilString::FormatPriv( vrFormating, 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< CMIUtilString & >( *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 != ULONG_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< MIchar * >( &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; }