//////////////////////////////////////////////////////////////////////////////////////// // // Nestopia - NES/Famicom emulator written in C++ // // Copyright (C) 2003-2008 Martin Freij // // This file is part of Nestopia. // // Nestopia is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Nestopia 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. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Nestopia; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //////////////////////////////////////////////////////////////////////////////////////// #ifndef NST_STRING_H #define NST_STRING_H #pragma once #include #include #include #include #include "NstMain.hpp" namespace Nestopia { namespace String { class Base { template static bool ToUnsigned(const T* NST_RESTRICT,uint,uint&); template static void AppendSigned(T&,int); template static void AppendUnsigned(T&,uint); static int Compare(const char*,int,const char*,int); static int Compare(const wchar_t*,int,const wchar_t*,int); static int CompareCase(const char*,int,const char*,int); static int CompareCase(const wchar_t*,int,const wchar_t*,int); protected: enum { MAX_INT_LENGTH = 12, MAX_FLT_LENGTH = 24 }; template static T* Remove(T*,T); static bool Trim(char*); static bool Trim(wchar_t*); static uint GetAbsolutePath(const char*,char*,uint); static uint GetAbsolutePath(const wchar_t*,wchar_t*,uint); template static T* FromUnsigned(T (&)[MAX_INT_LENGTH],uint); template static T* FromSigned(T (&)[MAX_INT_LENGTH],int); static uint FromDouble(char (&)[MAX_FLT_LENGTH],double,uint); static uint FromDouble(wchar_t (&)[MAX_FLT_LENGTH],double,uint); template static void AssignSigned(T&,int); template static void AssignUnsigned(T&,uint); template static bool ConvertToUnsigned(const T&,U&); static uint Length(const char* str) { return std::strlen( str ); } static uint Length(const wchar_t* str) { return std::wcslen( str ); } static void Copy(char* NST_RESTRICT a,const char* NST_RESTRICT b,uint n) { std::memcpy( a, b, n ); } static void Copy(wchar_t* NST_RESTRICT a,const wchar_t* NST_RESTRICT b,uint n) { std::wmemcpy( a, b, n ); } static void Copy(wchar_t* NST_RESTRICT a,const char* NST_RESTRICT b,uint n) { for (uint i=0; i < n; ++i) a[i] = b[i]; } static void Copy(char* NST_RESTRICT a,const wchar_t* NST_RESTRICT b,uint n) { for (uint i=0; i < n; ++i) a[i] = b[i]; } static void Move(char* a,const char* b,uint n) { std::memmove( a, b, n ); } static void Move(wchar_t* a,const wchar_t* b,uint n) { std::wmemmove( a, b, n ); } template static const typename T::Type* GetPtr(const T& t) { return t.Ptr(); } template static const T* GetPtr(T* const& t) { return t; } template static const T* GetPtr(const T* const& t) { return t; } template static const T* GetPtr(const T (&t)[N]) { return t; } template static uint GetLength(const T& t) { return t.Length(); } template static uint GetLength(T* const& t) { return Length(t); } template static uint GetLength(const T* const& t) { return Length(t); } template static uint GetLength(const T (&t)[N]) { return Length(t); } template static bool Equal(const T& t,const U& u) { return t.Length() == u.Length() && !Compare( t.Ptr(), t.Length(), u.Ptr(), u.Length() ); } template static bool Equal(const T& t,const U*& u) { return !Compare( t.Ptr(), t.Length(), u, -1 ); } template static bool Equal(const T& t,const U* const& u) { return !Compare( t.Ptr(), t.Length(), u, -1 ); } template static bool Equal(const T& t,const U (&u)[N]) { return !Compare( t.Ptr(), t.Length(), u, -1 ); } template static bool CaseEqual(const T& t,const U& u) { return t.Length() == u.Length() && !CompareCase( t.Ptr(), t.Length(), u.Ptr(), u.Length() ); } template static bool CaseEqual(const T& t,const U*& u) { return !CompareCase( t.Ptr(), t.Length(), u, -1 ); } template static bool CaseEqual(const T& t,const U* const& u) { return !CompareCase( t.Ptr(), t.Length(), u, -1 ); } template static bool CaseEqual(const T& t,const U (&u)[N]) { return !CompareCase( t.Ptr(), t.Length(), u, -1 ); } template static int Compare(const T& t,const U& u) { return Compare( t.Ptr(), t.Length(), u.Ptr(), u.Length() ); } template static int Compare(const T& t,const U*& u) { return Compare( t.Ptr(), t.Length(), u, -1 ); } template static int Compare(const T& t,const U* const& u) { return Compare( t.Ptr(), t.Length(), u, -1 ); } template static int Compare(const T& t,const U (&u)[N]) { return Compare( t.Ptr(), t.Length(), u, -1 ); } static bool Wide(const char*,uint) { return false; } static bool Wide(const wchar_t* t,uint n) { for (uint i=0; i < n; ++i) { if (t[i] > 0xFF) return true; } return false; } template static void Append(T& t,const U& u) { t.Append( GetPtr(u), GetLength(u) ); } template static void Append(T& t,char c) { const typename T::Type v = c; t.Append( &v, 1 ); } template static void Append(T& t,wchar_t c) { t.Append( &c, 1 ); } template static void Append(T& t,int i) { AppendSigned ( t, i ); } template static void Append(T& t,uint i) { AppendUnsigned ( t, i ); } template static void Append(T& t,schar i) { AppendSigned ( t, i ); } template static void Append(T& t,uchar i) { AppendUnsigned ( t, i ); } template static void Append(T& t,short i) { AppendSigned ( t, i ); } template static void Append(T& t,ushort i) { AppendUnsigned ( t, i ); } template static void Append(T& t,long i) { AppendSigned ( t, i ); } template static void Append(T& t,ulong i) { AppendUnsigned ( t, i ); } }; template T* Base::FromUnsigned(T (&buffer)[MAX_INT_LENGTH],uint number) { number &= 0xFFFFFFFF; T* it = buffer + MAX_INT_LENGTH-1; *it = '\0'; do { *--it = '0' + number % 10; } while (number /= 10); return it; } template T* Base::FromSigned(T (&buffer)[MAX_INT_LENGTH],const int number) { uint value = (number >= 0 ? number : -number) & 0x7FFFFFFFU; T* it = buffer + MAX_INT_LENGTH-1; *it = '\0'; do { *--it = '0' + value % 10; } while (value /= 10); if (number < 0) *--it = '-'; return it; } template void Base::AssignUnsigned(T& t,const uint i) { typename T::Type buffer[MAX_INT_LENGTH]; const typename T::Type* const offset = FromUnsigned( buffer, i ); t.Assign( offset, buffer + MAX_INT_LENGTH-1 - offset ); } template void Base::AssignSigned(T& t,const int i) { typename T::Type buffer[MAX_INT_LENGTH]; const typename T::Type* const offset = FromSigned( buffer, i ); t.Assign( offset, buffer + MAX_INT_LENGTH-1 - offset ); } template void Base::AppendUnsigned(T& t,const uint i) { typename T::Type buffer[MAX_INT_LENGTH]; const typename T::Type* const offset = FromUnsigned( buffer, i ); t.Append( offset, buffer + MAX_INT_LENGTH-1 - offset ); } template void Base::AppendSigned(T& t,const int i) { typename T::Type buffer[MAX_INT_LENGTH]; const typename T::Type* const offset = FromSigned( buffer, i ); t.Append( offset, buffer + MAX_INT_LENGTH-1 - offset ); } template bool Base::ToUnsigned(const T* NST_RESTRICT string,const uint length,uint& result) { result = 0; if (length) { bool neg = false; uint base = 10; uint maxvalue = 0xFFFFFFFF / 10; const T* end = string + length; switch (*string) { case '-': neg = true; case '+': ++string; break; case '0': if (++string != end) { base = 16; maxvalue = 0xFFFFFFFF / 16; const T digit = *string++; if (digit == 'x' || digit == 'X') break; else return false; } else { return true; } } if (string != end) { uint value = 0; do { const T digit = *string++; uint num; if (digit >= '0' && digit <= '9') { num = digit - '0'; } else if (digit >= 'A' && digit <= 'F' && base == 16) { num = digit - 'A' + 10; } else if (digit >= 'a' && digit <= 'f' && base == 16) { num = digit - 'a' + 10; } else { return false; } if (value < maxvalue || (value == maxvalue && num <= 0xFFFFFFFFU % base)) value = value * base + num; else return false; } while (string != end); result = neg ? uint(-int(value)) : value; return true; } } return false; } template bool Base::ConvertToUnsigned(const T& string,U& value) { uint u; const bool r = ToUnsigned( string.Ptr(), string.Length(), u ); value = U(u); return r; } template T* Base::Remove(T* string,const T c) { T* p; for (p=string; *string; ++string) { if (*string != c) *p++ = *string; } *p = '\0'; return p; } template class Generic : protected Base { public: typedef T Type; enum { TYPE_SIZE = sizeof(Type) }; protected: const Type* string; uint length; static const Type empty; enum DoNothing {NOP}; Generic(DoNothing) {} public: Generic() : string(&empty), length(0) {} Generic(const Type* t,uint l) : string(t), length(l) { NST_ASSERT( t ); } template Generic(const U& t) : string(Base::GetPtr(t)), length(Base::GetLength(t)) {} template Generic& operator = (const U& t) { string = Base::GetPtr(t); length = Base::GetLength(t); return *this; } template bool operator >> (U& t) const { return Base::ConvertToUnsigned( *this, t ); } uint Length() const { return length; } bool Empty() const { return !length; } const Type& Front() const { NST_ASSERT( length ); return string[0]; } const Type& Back() const { NST_ASSERT( length ); return string[length-1]; } bool NullTerminated() const { return string[length] == '\0'; } const Type* Ptr() const { return string; } const Type& operator [] (uint i) const { return string[i]; } bool Wide() const { return Base::Wide( Ptr(), Length() ); } Generic operator () (uint p) const { NST_ASSERT( p <= length ); return Generic( string + p, length - p ); } Generic operator () (uint p,uint l) const { NST_ASSERT( p + l <= length ); return Generic( string + p, l ); } template bool Equal(const U& t) const { return Base::CaseEqual( *this, t ); } template bool operator == (const U& t) const { return Base::Equal( *this, t ); } template bool operator != (const U& t) const { return !Base::Equal( *this, t ); } template bool operator < (const U& t) const { return Base::Compare( *this, t ) < 0; } template bool operator <= (const U& t) const { return Base::Compare( *this, t ) <= 0; } template bool operator > (const U& t) const { return Base::Compare( *this, t ) > 0; } template bool operator >= (const U& t) const { return Base::Compare( *this, t ) >= 0; } }; template const T Generic::empty = '\0'; template class Generic : public Generic { typedef Generic Parent; void Copy(const Generic&); T buffer[Parent::MAX_INT_LENGTH]; void SetValue(int number) { Parent::string = Base::FromSigned( buffer, number ); Parent::length = buffer + Parent::MAX_INT_LENGTH-1 - Parent::string; } void SetValue(uint number) { Parent::string = Base::FromUnsigned( buffer, number ); Parent::length = buffer + Parent::MAX_INT_LENGTH-1 - Parent::string; } public: Generic(const Generic& g) : Generic(Parent::NOP) { Copy( g ); } template Generic(const U& t) : Generic(t) {} Generic(T c) : Generic(Parent::NOP) { Parent::string = buffer; Parent::length = 1; buffer[0] = c; buffer[1] = '\0'; } Generic( schar i ) : Generic(Parent::NOP) { SetValue( int (i) ); } Generic( uchar i ) : Generic(Parent::NOP) { SetValue( uint (i) ); } Generic( short i ) : Generic(Parent::NOP) { SetValue( int (i) ); } Generic( ushort i ) : Generic(Parent::NOP) { SetValue( uint (i) ); } Generic( int i ) : Generic(Parent::NOP) { SetValue( int (i) ); } Generic( uint i ) : Generic(Parent::NOP) { SetValue( uint (i) ); } Generic( long i ) : Generic(Parent::NOP) { SetValue( int (i) ); } Generic( ulong i ) : Generic(Parent::NOP) { SetValue( uint (i) ); } Generic& operator = (const Generic& g) { Copy( g ); return *this; } template Generic& operator = (const U& t) { Parent::operator = (t); return *this; } Generic& operator = (T c) { Parent::string = buffer; Parent::length = 1; buffer[0] = c; buffer[1] = '\0'; return *this; } Generic& operator = ( schar i ) { SetValue( int (i) ); return *this; } Generic& operator = ( uchar i ) { SetValue( uint (i) ); return *this; } Generic& operator = ( short i ) { SetValue( int (i) ); return *this; } Generic& operator = ( ushort i ) { SetValue( uint (i) ); return *this; } Generic& operator = ( int i ) { SetValue( int (i) ); return *this; } Generic& operator = ( uint i ) { SetValue( uint (i) ); return *this; } Generic& operator = ( long i ) { SetValue( int (i) ); return *this; } Generic& operator = ( ulong i ) { SetValue( uint (i) ); return *this; } }; template class Num : public Generic { public: Num(uint v) : Generic(v) {} Num(int v) : Generic(v) {} Num& operator = (uint v) { Generic::operator = (v); return *this; } Num& operator = (int v) { Generic::operator = (v); return *this; } }; template void Generic::Copy(const Generic& g) { Generic::length = g.length; if (g.string < g.buffer || g.string >= g.buffer + Generic::MAX_INT_LENGTH) { Generic::string = g.string; } else { Generic::string = buffer + (g.string - g.buffer); Base::Copy( buffer, g.buffer, Generic::MAX_INT_LENGTH ); } } template class Sub : protected Base { public: typedef typename T::Type Type; enum { TYPE_SIZE = sizeof(Type) }; template void Assign(const U* NST_RESTRICT,uint); protected: T& string; uint offset; uint length; public: Sub(T& t,uint o) : string(t), offset(o), length(t.Length() - offset) { NST_ASSERT( offset + length <= string.Length() ); } Sub(T& t,uint o,uint l) : string(t), offset(o), length(l) { NST_ASSERT( offset + length <= string.Length() ); } template Sub& operator = (const U& t) { Assign( Base::GetPtr(t), Base::GetLength(t) ); return *this; } Sub& operator = (Type c) { Assign( &c, 1 ); return *this; } Sub& operator = (int i) { Base::AssignSigned ( *this, i ); return *this; } Sub& operator = (uint i) { Base::AssignUnsigned ( *this, i ); return *this; } Sub& operator = (schar i) { Base::AssignSigned ( *this, i ); return *this; } Sub& operator = (uchar i) { Base::AssignUnsigned ( *this, i ); return *this; } Sub& operator = (short i) { Base::AssignSigned ( *this, i ); return *this; } Sub& operator = (ushort i) { Base::AssignUnsigned ( *this, i ); return *this; } Sub& operator = (long i) { Base::AssignSigned ( *this, i ); return *this; } Sub& operator = (ulong i) { Base::AssignUnsigned ( *this, i ); return *this; } template bool operator >> (U& t) const { return Base::ConvertToUnsigned( *this, t ); } void Clear() { if (const uint n = length) { length = 0; string.Erase( offset, n ); } } uint Length() const { return length; } bool Empty() const { return !length; } Type* Ptr() { return string.Ptr() + offset; } const Type* Ptr() const { return string.Ptr() + offset; } Type& operator [] (uint i) { NST_ASSERT( i < length ); return string[offset+i]; } const Type& operator [] (uint i) const { NST_ASSERT( i < length ); return string[offset+i]; } Type& Front() { NST_ASSERT( length ); return string[offset]; } const Type& Front() const { NST_ASSERT( length ); return string[offset]; } Type& Back() { NST_ASSERT( length ); return string[offset+length-1]; } const Type& Back() const { NST_ASSERT( length ); return string[offset+length-1]; } bool Wide() const { return Base::Wide( Ptr(), Length() ); } Generic operator () (uint p) const { NST_ASSERT( p <= length ); return Generic( string.Ptr() + offset + p, length - p ); } Generic operator () (uint p,uint l) const { NST_ASSERT( p + l <= length ); return Generic( string.Ptr() + offset + p, l ); } template bool Equal(const U& t) const { return Base::CaseEqual( *this, t ); } template bool operator == (const U& t) const { return Base::Equal( *this, t ); } template bool operator != (const U& t) const { return !Base::Equal( *this, t ); } template bool operator < (const U& t) const { return Base::Compare( *this, t ) < 0; } template bool operator <= (const U& t) const { return Base::Compare( *this, t ) <= 0; } template bool operator > (const U& t) const { return Base::Compare( *this, t ) > 0; } template bool operator >= (const U& t) const { return Base::Compare( *this, t ) >= 0; } }; template template void Sub::Assign(const U* const NST_RESTRICT t,const uint n) { const uint l = length; length = n; string.Erase( offset, l ); string.Insert( offset, t, n ); } template class Stack : Base { public: typedef T Type; typedef Sub< Stack > SubString; enum { TYPE_SIZE = sizeof(Type), MAX_LENGTH = N }; template void Assign(const U* NST_RESTRICT,uint); template void Append(const U* NST_RESTRICT,uint); template void Insert(const uint,const U* NST_RESTRICT,uint); void Erase(uint,uint); private: template void Assign(const U* NST_RESTRICT); template void Append(const U* NST_RESTRICT); enum { STACK_LENGTH = (N + 4U) & ~3U }; uint length; Type string[STACK_LENGTH]; public: Stack() : length(0) { *string = '\0'; } Stack(const Stack& t) { Assign( t.Ptr(), t.Length() ); } template Stack(const U* u,uint n) { Assign( u, n ); } template Stack(const U& t) { Assign( t.Ptr(), t.Length() ); } template Stack(const U* const& t) { Assign( t ); } template Stack(U* const& t) { Assign( t ); } template Stack(const U (&t)[C]) { Assign( t ); } Stack& operator = (const Stack& t) { Assign( t.Ptr(), t.Length() ); return *this; } template Stack& operator = (const U& t) { Assign( t.Ptr(), t.Length() ); return *this; } template Stack& operator = (const U* const& t) { Assign( t ); return *this; } template Stack& operator = (U* const& t) { Assign( t ); return *this; } template Stack& operator = (const U (&t)[C]) { Assign( t ); return *this; } template Stack& operator << (const U& t) { Base::Append( *this, t ); return *this; } template bool operator >> (U& t) const { return Base::ConvertToUnsigned( *this, t ); } uint Length() const { return length; } bool Empty() const { return !length; } Type* Ptr() { return string; } const Type* Ptr() const { return string; } Type& operator [] (uint i) { NST_ASSERT( i < MAX_LENGTH ); return string[i]; } const Type& operator [] (uint i) const { NST_ASSERT( i < MAX_LENGTH ); return string[i]; } Type& At(uint i) { NST_ASSERT( i < MAX_LENGTH ); return string[i]; } const Type& At(uint i) const { NST_ASSERT( i < MAX_LENGTH ); return string[i]; } Type& Front() { NST_ASSERT( length ); return string[0]; } const Type& Front() const { NST_ASSERT( length ); return string[0]; } Type& Back() { NST_ASSERT( length ); return string[length-1]; } const Type& Back() const { NST_ASSERT( length ); return string[length-1]; } void Clear() { length = 0; *string = 0; } bool Wide() const { return Base::Wide( Ptr(), Length() ); } uint Validate() { length = Base::Length(string); return length; } void Resize(uint n) { NST_ASSERT( n <= MAX_LENGTH ); string[length=n] = '\0'; } void ShrinkTo(uint n) { Resize( n ); } void Remove(const Type c) { length = Base::Remove( string, c ) - string; } void Trim() { if (Base::Trim( string )) Validate(); } Generic operator () (uint p) const { NST_ASSERT( p <= length ); return Generic( string + p, length - p ); } Generic operator () (uint p,uint l) const { NST_ASSERT( p + l <= length ); return Generic( string + p, l ); } SubString operator () (uint p) { NST_ASSERT( p <= length ); return SubString( *this, p, length - p ); } SubString operator () (uint p,uint l) { NST_ASSERT( p + l <= length ); return SubString( *this, p, l ); } template bool Equal(const U& t) const { return Base::CaseEqual( *this, t ); } template bool operator == (const U& t) const { return Base::Equal( *this, t ); } template bool operator != (const U& t) const { return !Base::Equal( *this, t ); } template bool operator < (const U& t) const { return Base::Compare( *this, t ) < 0; } template bool operator <= (const U& t) const { return Base::Compare( *this, t ) <= 0; } template bool operator > (const U& t) const { return Base::Compare( *this, t ) > 0; } template bool operator >= (const U& t) const { return Base::Compare( *this, t ) >= 0; } }; template template void Stack::Assign(const U* const NST_RESTRICT t) { NST_ASSERT( Base::Length(t) <= MAX_LENGTH ); uint i = ~0U; Type c; do { ++i; string[i] = c = t[i]; } while (c); length = i; } template template void Stack::Assign(const U* const NST_RESTRICT t,const uint n) { NST_ASSERT( t && n <= MAX_LENGTH ); length = n; Base::Copy( string, t, n ); string[n] = '\0'; } template template void Stack::Append(const U* const NST_RESTRICT t) { NST_ASSERT( length + Base::Length(t) <= MAX_LENGTH ); uint i = length - 1; Type c; do { ++i; string[i] = c = t[i]; } while (c); length = i; } template template void Stack::Append(const U* const NST_RESTRICT t,const uint n) { NST_ASSERT( t && length + n <= MAX_LENGTH ); Type* const offset = string + length; length += n; Base::Copy( offset, t, n ); offset[n] = '\0'; } template void Stack::Erase(const uint p,const uint n) { NST_ASSERT( p + n <= length ); const uint l = length; length -= n; Base::Move( string + p, string + p + n, (l + 1) - (p + n) ); } template template void Stack::Insert(const uint pos,const U* const NST_RESTRICT t,const uint n) { NST_ASSERT( length + n <= MAX_LENGTH ); if (n) { const uint p = length; length += n; Base::Move( string + pos + n, string + pos, p + 1 - pos ); Base::Copy( string + pos, t, n ); } } template class Heap : public Base { public: typedef T Type; typedef Sub< Heap > SubString; enum { TYPE_SIZE = sizeof(Type) }; template void Assign(const U* NST_RESTRICT,uint); template void Append(const U* NST_RESTRICT,uint); template void Insert(uint,const U* NST_RESTRICT,uint); void Reserve(uint); void Resize(uint); void Defrag(); void Destroy(); void Erase(uint,uint); Heap& Import(cstring,bool=false); Heap& Import(wcstring,bool=false); private: template void Init(const U* NST_RESTRICT,uint); template void Assign(const U* NST_RESTRICT); template void Append(const U* NST_RESTRICT); Type* string; uint length; uint capacity; static Type empty[4]; template void Copy(const U* const NST_RESTRICT t,const uint n) { NST_ASSERT( t ); Base::Copy( string, t, n ); string[n] = '\0'; } public: Heap() : string(empty), length(0), capacity(0) { NST_ASSERT( !*empty ); } Heap(const Heap& t) { Init( t.Ptr(), t.Length() ); } template Heap(const U& t) { Init( Base::GetPtr(t), Base::GetLength(t) ); } template Heap(const U* t,uint n) { Init( t, n ); } ~Heap() { if (capacity) std::free( string ); } Heap& operator = (const Heap& t) { Assign( t.Ptr(), t.Length() ); return *this; } template Heap& operator = (const U& t) { Assign( Base::GetPtr(t), Base::GetLength(t) ); return *this; } template Heap& operator << (const U& t) { Base::Append( *this, t ); return *this; } template bool operator >> (U& t) const { return Base::ConvertToUnsigned( *this, t ); } template void Insert(const uint pos,const U& t) { Insert( pos, Base::GetPtr(t), Base::GetLength(t) ); } void ShrinkTo(uint n) { NST_ASSERT( capacity >= n ); string[length = n] = '\0'; } void Clear() { string[length = 0] = '\0'; } uint Validate() { length = Base::Length(string); return length; } void Remove(const Type c) { length = Base::Remove( string, c ) - string; } void Trim() { if (Base::Trim( string )) Validate(); } uint Length() const { return length; } uint Capacity() const { return capacity; } bool Empty() const { return !length; } Type* Ptr() { return string; } const Type* Ptr() const { return string; } Type& operator [] (uint i) { return string[i]; } const Type& operator [] (uint i) const { return string[i]; } Type& At(uint i) { return string[i]; } const Type& At(uint i) const { return string[i]; } Type& Front() { NST_ASSERT( length ); return string[0]; } const Type& Front() const { NST_ASSERT( length ); return string[0]; } Type& Back() { NST_ASSERT( length ); return string[length-1]; } const Type& Back() const { NST_ASSERT( length ); return string[length-1]; } bool Wide() const { return Base::Wide( Ptr(), Length() ); } Generic operator () (uint p) const { NST_ASSERT( p <= length ); return Generic( string + p, length - p ); } Generic operator () (uint p,uint l) const { NST_ASSERT( p + l <= length ); return Generic( string + p, l ); } SubString operator () (uint p) { NST_ASSERT( p <= length ); return SubString( *this, p, length - p ); } SubString operator () (uint p,uint l) { NST_ASSERT( p + l <= length ); return SubString( *this, p, l ); } uint FindFirstOf(const Type c,const uint length) { for (uint i=0; i < length; ++i) if (string[i] == c) return i; return length; } uint FindFirstOf(const Type c) { return FindFirstOf( c, length ); } uint FindFirstNotOf(const Type c,const uint length) { for (uint i=0; i < length; ++i) if (string[i] != c) return i; return length; } uint FindLastOf(const Type c,const uint length) { for (uint i=length; i; ) if (string[--i] == c) return i; return length; } uint FindLastNotOf(const Type c,const uint length) { for (uint i=length; i; ) if (string[--i] != c) return i; return length; } SubString FirstOf(Type c) { uint first = FindFirstOf(c,length); return SubString( *this, first, length - first ); } template bool operator == (const U& t) const { return Base::Equal( *this, t ); } template bool operator != (const U& t) const { return !Base::Equal( *this, t ); } template bool operator < (const U& t) const { return Base::Compare( *this, t ) < 0; } template bool operator <= (const U& t) const { return Base::Compare( *this, t ) <= 0; } template bool operator > (const U& t) const { return Base::Compare( *this, t ) > 0; } template bool operator >= (const U& t) const { return Base::Compare( *this, t ) >= 0; } }; template typename Heap::Type Heap::empty[4] = {'\0','\0','\0','\0'}; template template void Heap::Init(const U* const NST_RESTRICT t,const uint n) { NST_ASSERT( t && !*empty ); if (0 != (capacity = length = n)) { string = static_cast(std::malloc( (n+1) * TYPE_SIZE )); Copy( t, n ); } else { string = empty; } } template void Heap::Reserve(const uint n) { if (capacity < n) { uint prev = capacity; capacity = n; string = static_cast(std::realloc( prev ? string : NULL, (n+1) * TYPE_SIZE )); string[length] = '\0'; } } template void Heap::Resize(uint n) { Reserve( n ); string[length = n] = '\0'; } template void Heap::Destroy() { if (capacity) { length = capacity = 0; Type* tmp = string; string = empty; std::free( tmp ); } } template template void Heap::Assign(const U* NST_RESTRICT t,const uint n) { length = n; if (capacity < n) { uint prev = capacity; capacity = n; string = static_cast(std::realloc( prev ? string : NULL, (capacity+1) * TYPE_SIZE )); } Copy( t, n ); } template template void Heap::Assign(const U* NST_RESTRICT t) { Assign( t, Base::Length(t) ); } template template void Heap::Append(const U* NST_RESTRICT t,const uint n) { length += n; if (capacity < length) { uint prev = capacity; capacity = length * 2; string = static_cast(std::realloc( prev ? string : NULL, (capacity+1) * TYPE_SIZE )); } Base::Copy( string + length - n, t, n ); string[length] = '\0'; } template template void Heap::Append(const U* NST_RESTRICT t) { Append( t, Base::Length(t) ); } template template void Heap::Insert(const uint pos,const U* const NST_RESTRICT t,const uint n) { if (n) { if (capacity >= length + n) { Base::Move( string + pos + n, string + pos, length + 1 - pos ); } else { capacity = length + n * 2; Type* const next = static_cast(std::malloc( (capacity+1) * TYPE_SIZE )); Base::Copy( next, string, pos ); Base::Copy( next + pos + n, string + pos, length + 1 - pos ); if (string != empty) std::free( string ); string = next; } length += n; Base::Copy( string + pos, t, n ); } } template void Heap::Erase(const uint p,const uint n) { NST_ASSERT( p + n <= length ); const uint l = length; length -= n; Base::Move( string + p, string + p + n, (l + 1) - (p + n) ); } template void Heap::Defrag() { if (capacity != length) { capacity = length; if (capacity) { string = static_cast(std::realloc( string, (capacity+1) * TYPE_SIZE )); } else { Type* tmp = string; string = empty; std::free( tmp ); } } } template class Path : public Heap { typedef Heap Parent; typedef Generic Any; typedef typename Heap::SubString Sub; public: Path() {} Path(Any,Any,Any=Any()); Path(const Path& p) : Parent(static_cast(p)) {} template Path(const U& t) : Parent(t) {} Path& operator = (const Path& t) { Parent::operator = (static_cast(t)); return *this; } template Path& operator = (const U& t) { Parent::operator = (t); return *this; } template Path& operator << (const U& t) { Parent::operator << (t); return *this; } void Set(Any,Any,Any=Any()); void MakePretty(bool=false); Path& MakeAbsolute(bool=false); bool FileExists() const; bool FileProtected() const; bool DirectoryExists() const; static const Path Compact(Any,uint); private: static bool Compact(T*,const T*,uint); uint FindDirectory() const; void FindExtension(uint (&)[2]) const; uint FindArchive() const; void FindFileInArchive(uint (&)[2]) const; static uint ExtensionId(const T* NST_RESTRICT,uint); class DirectorySub : public Sub { public: DirectorySub(Path& path,uint length) : Sub( path, 0, length ) {} void operator = (Any); void operator += (Any); void operator -= (uint); }; class ExtensionSub : public Sub { public: ExtensionSub(Path& path,uint pos,uint length) : Sub( path, pos, length ) {} void Assign(const T* NST_RESTRICT,uint); void Clear(); template void operator = (const U& t) { Assign( Base::GetPtr(t), Base::GetLength(t) ); } uint Id() const { return ExtensionId( Sub::Ptr(), Sub::Length() ); } }; class ConstExtensionSub : public Any { public: ConstExtensionSub(const T* string,uint length) : Any( string, length ) {} uint Id() const { return ExtensionId( Any::Ptr(), Any::Length() ); } }; class ArchiveFileSub : public Any { public: ArchiveFileSub(const T* string,uint length) : Any( string, length ) {} const Any File() const { for (uint i=Any::length; i; ) { switch (Any::string[--i]) { case '\\': case '/': return Any( Any::string + i + 1, Any::length - (i + 1) ); } } return *this; } }; public: DirectorySub Directory() { return DirectorySub( *this, FindDirectory() ); } const Any Directory() const { return Any( Parent::Ptr(), FindDirectory() ); } Sub File() { return Sub( *this, FindDirectory() ); } const Any File() const { uint p = FindDirectory(); return Any( Parent::Ptr() + p, Parent::Length() - p ); } ExtensionSub Extension() { uint p[2]; FindExtension( p ); return ExtensionSub( *this, p[0], p[1] ); } const ConstExtensionSub Extension() const { uint p[2]; FindExtension( p ); return ConstExtensionSub( Parent::Ptr() + p[0], p[1] ); } const Any Archive() const { return Any( Parent::Ptr(), FindArchive() ); } ArchiveFileSub FileInArchive() const { uint p[2]; FindFileInArchive( p ); return ArchiveFileSub( Parent::Ptr() + p[0], p[1] ); } ArchiveFileSub Target() const { const ArchiveFileSub archive( FileInArchive() ); if (archive.Length()) return archive; return ArchiveFileSub( Parent::Ptr(), Parent::Length() ); } }; template Path::Path(const Any dir,const Any file,const Any ext) { Set( dir, file, ext ); } template void Path::Set(const Any dir,const Any file,const Any ext) { Parent::Clear(); Reserve( dir.Length() + file.Length() + ext.Length() ); Directory() = dir; File() = file; if (ext.Length()) Extension() = ext; } template void Path::MakePretty(const bool checkSlash) { if (Parent::Length()) { if ( Parent::Length() >= 3 && Parent::At(0) >= 'a' && Parent::At(0) <= 'z' && Parent::At(1) == ':' && (Parent::At(2) == '\\' || Parent::At(2) == '/') ) (*this)[0] -= ('a'-'A'); if (checkSlash && Parent::Back() != '\\' && Parent::Back() != '/') (*this) << '\\'; } } template Path& Path::MakeAbsolute(const bool checkSlash) { Heap buffer; buffer.Reserve( 255 ); buffer.Resize( Parent::GetAbsolutePath( Parent::Ptr(), buffer.Ptr(), 255+1 ) ); if (buffer.Length() > 255+1) { Parent::GetAbsolutePath( Parent::Ptr(), buffer.Ptr(), buffer.Length() ); buffer.ShrinkTo( buffer.Length() - 1 ); } if (checkSlash && Parent::Length() && Parent::Back() != '\\' && Parent::Back() != '/') buffer << '\\'; *this = buffer; return *this; } template uint Path::FindDirectory() const { for (uint i=Parent::Length(); i; ) { switch (Parent::At(--i)) { case '\\': case '/': return i + 1; case '>': while (i && Parent::At(--i) != '<'); break; } } return 0; } template void Path::FindExtension(uint (&p)[2]) const { p[1] = Parent::Length(); for (uint i=Parent::Length(); i; ) { switch (Parent::At(--i)) { case '.': p[0] = i + 1; p[1] -= p[0]; return; case '\\': case '/': i = 0; break; case '>': while (i && Parent::At(--i) != '<'); if (i && Parent::At(i-1) == ' ') --i; p[1] = i; break; } } p[0] = p[1]; p[1] = 0; } template uint Path::FindArchive() const { for (uint i=Parent::Length(); i; ) { if (Parent::At(--i) == '>') { while (i) { if (Parent::At(--i) == '<') { if (i && Parent::At(i-1) == ' ') --i; return i; } } break; } } return 0; } template void Path::FindFileInArchive(uint (&p)[2]) const { for (uint i=Parent::Length(); i; ) { if (Parent::At(--i) == '>') { p[1] = i; while (i) { if (Parent::At(--i) == '<') { p[0] = i + 1; p[1] -= p[0]; return; } } break; } } p[0] = Parent::Length(); p[1] = 0; } template uint Path::ExtensionId(const T* const NST_RESTRICT string,uint length) { uint id = 0; if (length) { if (length > 4) length = 4; for (uint i=0; i < length; ++i) { const T c = string[i]; uint v; if (c >= 'a' && c <= 'z') { v = c; } else if (c >= 'A' && c <= 'Z') { v = c - 'A' + 'a'; } else if (c >= '0' && c <= '9') { v = c; } else { return 0; } id |= v << (8 * i); } } return id; } template void Path::DirectorySub::operator = (const Any t) { Sub::Clear(); if (t.Length()) { Sub::string.Insert( 0, t.Ptr(), t.Length() ); Sub::length = t.Length(); if (t.Back() != '\\' && t.Back() != '/') { const T c = '\\'; Sub::string.Insert( Sub::length++, &c, 1 ); } } } template void Path::DirectorySub::operator += (const Any t) { if (t.Length()) { Sub::string.Insert( Sub::length, t.Ptr(), t.Length() ); Sub::length += t.Length(); if (t.Back() != '\\' && t.Back() != '/') { const T c = '\\'; Sub::string.Insert( Sub::length++, &c, 1 ); } } } template void Path::DirectorySub::operator -= (uint subs) { if (subs && Sub::length && (Sub::Back() == '\\' || Sub::Back() == '/')) { uint i = Sub::length - 1; uint p = ~0U; do { if (Sub::string.At(--i) == '\\' || Sub::string.At(i) == '/') { p = i; if (!--subs) break; } } while (i); if (p != ~0U) { const uint n = Sub::length; Sub::length = ++p; Sub::string.Erase( p, n - p ); } } } template void Path::ExtensionSub::Clear() { const uint dot = (Sub::offset && Sub::string[Sub::offset-1] == '.'); const uint n = Sub::length; Sub::length = 0; Sub::string.Erase( Sub::offset -= dot, n + dot ); } template void Path::ExtensionSub::Assign(const T* NST_RESTRICT t,uint n) { Clear(); if (n) { if (*t == '.') { ++t; --n; } if (n) { Sub::length = n; const T c = '.'; Sub::string.Insert( Sub::offset++, &c, 1 ); Sub::string.Insert( Sub::offset, t, n ); } } } template const Path Path::Compact(const Any t,const uint maxLength) { Path path; if (t.Length()) { path.Reserve( maxLength ); if (Compact( path.Ptr(), Path<>(t).Ptr(), maxLength )) path.Validate(); else path.Clear(); } return path; } template class Hex { public: typedef T Type; enum { TYPE_SIZE = sizeof(Type) }; private: Type buffer[12]; const Type* const string; const uint length; void Convert(uint,uint); public: Hex(const uint b,const uint v,const bool n0x=false) : string(n0x ? buffer + 2 : buffer), length(n0x ? b/4 : 2+b/4) { NST_ASSERT( b == 8 || b == 16 || b == 32 ); Convert( v, 2+b/4 ); } const Type* Ptr() const { return string; } uint Length() const { return length; } }; template void Hex::Convert(uint number,const uint length) { Type* it = buffer + length; *it = '\0'; static const Type lut[] = { '0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F' }; do { *--it = lut[number % 16]; number /= 16; } while (it != buffer + 2); *--it = 'x'; *--it = '0'; } template class Real : Base { public: typedef T Type; enum { TYPE_SIZE = sizeof(Type) }; Real(double,uint=1,bool=false); private: static bool Fraction(double v) { return std::modf( v, &v ); } Type buffer[24]; const uint length; public: const Type* Ptr() const { return buffer; } uint Length() const { return length; } }; template Real::Real(double v,uint d,bool p) : length( Base::FromDouble( buffer, v, p || (d > 1 && Fraction(v)) ? d : 1 )) {} } typedef String::Heap HeapString; typedef String::Generic GenericString; typedef String::Generic ValueString; typedef String::Hex HexString; typedef String::Real RealString; typedef String::Path Path; } #endif