SubString.h

// SubString.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2005

#pragma once
#ifndef Ceda_cxUtils_SubString_H
#define Ceda_cxUtils_SubString_H

#include "Hex.h"
#include "xstring.h"
#include "BasicSubString.h"

#ifdef _MSC_VER
    // struct 'X' needs to have dll-interface to be used by clients of class 'Y'
    #pragma warning(disable:4251)
#endif

namespace ceda
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// SubString8, SubString16

typedef BasicSubString<char8> SubString8;
typedef BasicSubString<char16> SubString16;

typedef BasicReversedSubString<char8> ReversedSubString8;
typedef BasicReversedSubString<char16> ReversedSubString16;

// Compare two substrings lexicographically.  Similar in functionality to strcmp(const char*, const char*)
cxUtils_API int strcmp(SubString8 s1, SubString8 s2);
cxUtils_API int strcmp(SubString8 s1, const char8* s2);
cxUtils_API int strcmp(SubString16 s1, SubString16 s2);
cxUtils_API int strcmp(SubString16 s1, const char16* s2);

// string comparison
cxUtils_API bool operator==(SubString8 s1, SubString8 s2);
inline bool operator!=(SubString8 s1, SubString8 s2) { return !(s1 == s2); }
inline bool operator<(SubString8 s1, SubString8 s2) { return strcmp(s1,s2) < 0; }
inline bool operator<=(SubString8 s1, SubString8 s2) { return strcmp(s1,s2) <= 0; }
inline bool operator>(SubString8 s1, SubString8 s2) { return strcmp(s1,s2) > 0; }
inline bool operator>=(SubString8 s1, SubString8 s2) { return strcmp(s1,s2) >= 0; }

inline bool operator==(SubString8 s1, const char8* s2) { return strcmp(s1,s2) == 0; }
inline bool operator!=(SubString8 s1, const char8* s2) { return strcmp(s1,s2) != 0; }

cxUtils_API bool operator==(SubString16 s1, SubString16 s2);
inline bool operator!=(SubString16 s1, SubString16 s2) { return !(s1 == s2); }
inline bool operator<(SubString16 s1, SubString16 s2) { return strcmp(s1,s2) < 0; }
inline bool operator<=(SubString16 s1, SubString16 s2) { return strcmp(s1,s2) <= 0; }
inline bool operator>(SubString16 s1, SubString16 s2) { return strcmp(s1,s2) > 0; }
inline bool operator>=(SubString16 s1, SubString16 s2) { return strcmp(s1,s2) >= 0; }

inline bool operator==(SubString16 s1, const char16* s2) { return strcmp(s1,s2) == 0; }
inline bool operator!=(SubString16 s1, const char16* s2) { return strcmp(s1,s2) != 0; }


// Count the number of lines of text by scanning the sub string for line feeds.  The last line 
// doesn't need to end with a line feed
cxUtils_API ssize_t CountNumLinesOfText(SubString8 s);
cxUtils_API ssize_t CountNumLinesOfText(SubString16 s);

inline void Convert(string8& dst, SubString16 src) { Convert(dst, src.begin(), src.size()); }
inline void Convert(string16& dst, SubString8 src) { Convert(dst, src.begin(), src.size()); }

cxUtils_API xostream& operator<<(xostream& os, SubString8 s);
cxUtils_API xostream& operator<<(xostream& os, SubString16 s);

///////////////////////////////////////////////////////////////////////////////////////////////////
// SubString

typedef BasicSubString<xchar> SubString;
typedef BasicReversedSubString<xchar> ReversedSubString;


cxUtils_API void ScanIndentation(SubString& s, ssize_t indent, ssize_t tabSize = 4);

cxUtils_API void WriteSubStringWithAdjustedIndent(xostream& os, SubString s, ssize_t srcIndent);

// Returns pointer to start of given line (zero based), or nullptr if s doesn't contain enough lines
cxUtils_API const xchar* GetLineNumberPosition(SubString s, ssize_t lineNumber);

// Returns true if s begins with the given prefix.  The comparison is case sensitive.  Always returns true
// if the prefix is empty.
cxUtils_API bool CheckPrefix(SubString s, SubString prefix);

// If s starts with the given prefix then advance s past the prefix and return true
cxUtils_API bool EatPrefix(SubString& s, SubString prefix);

cxUtils_API bool CheckCaseInsensitivePrefix(SubString s, SubString prefix);
cxUtils_API bool EatCaseInsensitivePrefix(SubString& s, SubString prefix);

// Find position of the first occurence of sFind within s.  Returns nullptr if not found
cxUtils_API const xchar* ForwardsFind(SubString s, SubString sFind);

// Find position of the first occurence of character c within s.  Returns nullptr if not found
cxUtils_API const xchar* ForwardsFind(SubString s, xchar c);

// Find position of the last occurence of sFind within s.  Returns nullptr if not found
cxUtils_API const xchar* ReverseFind(SubString s, SubString sFind);

// Find position of the last occurence of character c within s.  Returns nullptr if not found
cxUtils_API const xchar* ReverseFind(SubString s, xchar c);


cxUtils_API xstring ToUpper(SubString s);
cxUtils_API xstring ToLower(SubString s);

cxUtils_API SubString GetPrefixBeforeLastDelimiter(SubString s, xchar c);
cxUtils_API SubString GetSuffixAfterLastDelimiter(SubString s, xchar c);

cxUtils_API bool CaseInsensitiveCompare(SubString s1, SubString s2);

cxUtils_API bool RemoveQuotesFromString(SubString& s);

cxUtils_API bool MapEscapeCharacter(xchar c1, xchar& c2);
cxUtils_API bool UnmapEscapeCharacter(xchar c1, xchar& c2);

cxUtils_API void WriteCharForCStyleLiteralConstants(xostream& os, xchar c);

cxUtils_API void MakeSingleQuotedLiteralChar(xchar c,xstring& t);
cxUtils_API void WriteDoubleQuotedString(xostream& os, SubString s);
cxUtils_API void MakeDoubleQuotedString(SubString s, xstring& t);

cxUtils_API ssize_t CountNumChars(SubString s, xchar c);

// Get the line number corresponding to the given position, or -1 if [s.p1, s.p2] doesn't contain pos
cxUtils_API ssize_t GetLineNumber(SubString s, const xchar* pos);

///////////////////////////////////////////////////////////////////////////////////////////////////
// HumanReadableChar

struct HumanReadableChar
{
    HumanReadableChar(xchar _x = 0) : x(_x) {}
    operator xchar() const { return x; }
    xchar x;
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// HumanReadableCharInSubString

struct HumanReadableCharInSubString
{
    HumanReadableCharInSubString(SubString s) : m_s(s) {}

    SubString m_s;
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// Scanning and tokenising

/*
Allow for iteration through whitespace delimited tokens in SubString s using the loop

    while(s)
    {
        SubString token = ScanNextToken(s);
    }
*/
cxUtils_API SubString ScanNextToken(SubString& s);

cxUtils_API void ScanWhiteSpace(SubString& s);
cxUtils_API bool ScanDigits(SubString& s);
cxUtils_API bool ScanIdentifier(SubString& s);

///////////////////////////////////////////////////////////////////////////////////////////////////
// StringX

class cxUtils_API StringX
{
    cxNotCloneable(StringX)

public:
    StringX() : m_capacity(0) {}
    ~StringX()
    {
        if (m_capacity)
        {
            delete [] (xchar*) m_subString.begin();
        }
    }

    /*
    StringX(const StringX& other) :
        m_capacity(0),
        m_subString(other.m_subString)  // Make an alias
    {
    }

    StringX& operator=(const StringX& other)
    {
        if (this != &other)
        {
            // Make an alias
            Set(other.m_subString,false);
        }
        return *this;
    }
    */

    StringX(SubString other, bool makeIndependentCopy) :
        m_capacity(0)
    {
        Set(other,makeIndependentCopy);
    }

    void Set(SubString other, bool makeIndependentCopy);

    operator SubString() const { return m_subString; }

private:
    SubString m_subString;

    // Zero indicates that m_subString is an alias
    ssize_t m_capacity;    
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// Must be inside ceda namespace !!!
cxUtils_API xostream& operator<<(xostream& os, HumanReadableChar c);
cxUtils_API xostream& operator<<(xostream& os, HumanReadableCharInSubString s);

} // namespace ceda

#endif // include guard