//
// Created by Eugeny Grishul
//
// See license at http://bamelg.com/license.txt
//

using System.Runtime;
using System.Text;

namespace System {
	public sealed partial class SbcsString {
		public const SbcsString Empty = "";

		private SbcsString() {
		}

		private SbcsString( uint length ) {
			extra {
				return sizeof( byte ) * ( length + 1 );
			}
			body {
				Length = length;
			}
		}

		private SbcsString( uint length, System.Text.CodePageID codePage ) {
			extra {
				return sizeof( byte ) * ( length + 1 );
			}
			body {
				Length = length;
				CodePage = codePage;
			}
		}

		public SbcsString( char source, uint length ) {
			extra {
				return sizeof( byte ) * ( length + 1 );
			}
			body {
				var chars = GetChars();
				Length = length;

				Memory.Fill8( chars, ( byte ) source, length );
				chars[Length] = '\0';

				UpdateHash();
			}
		}

		public SbcsString( SbcsString source ) {
			extra {
				return sizeof( source[0] ) * ( source.Length + 1 );
			}
			body {
				var chars = GetChars();
				Length = source.Length;
				Hash = source.Hash;
				CodePage = source.CodePage;

				Memory.Copy( chars, source.GetChars(), ByteLength + sizeof( GetChars()[0] ) ); // copy with '\0'
			}
		}

		public static SbcsString operator +( SbcsString left, SbcsString right ) {
			if( left == null ) return right;
			if( right == null ) return left;

			var resultLength = left.Length + right.Length;
			var result = new SbcsString( left.Length + right.Length );

			var chars = result.GetChars();

			Memory.Copy( chars, left.GetChars(), left.ByteLength );
			Memory.Copy( chars + left.Length, right.GetChars(), right.ByteLength + sizeof( right.GetChars()[0] ) ); // copy with '\0'

			result.UpdateHash();
			return result;
		}

		private void UpdateHash() { *cast<uint*>( bitcast<byte*>( this ) + memberinfo( Hash ).ByteOffset ) = CString.ComputeHashCode( GetChars(), Length ); }

		public readonly uint Length;
		public readonly uint Hash;
		public readonly System.Text.CodePageID CodePage = System.Environment.DefaultCodePage;

		public uint ByteLength { get { return Length * sizeof( this[0] ); } }

		public bool IsNullOrEmpty { get { return this == null || Length == 0; } }

		public override uint GetHashCode() { return Hash; }

		internal byte* GetChars() { return this != null ? bitcast<byte*>( this ) + sizeof( thistype ) : null; }

		public byte this[int index] { get { System.Diagnostics.Debug.Assert( ( uint ) index < Length ); return GetChars()[index]; } }
		public byte this[uint index] { get { System.Diagnostics.Debug.Assert( index < Length ); return GetChars()[index]; } }

		public override string ToString() { return ( string ) this; }

		public static bool operator ==( SbcsString left, SbcsString right ) {
			if( cast<RuntimeObjectBase>( left ) == cast<RuntimeObjectBase>( right ) ) return true;
			if( ( left == null ) | ( right == null ) ) return false;

			if( left.Hash != right.Hash ) return false;
			if( left.Length != right.Length ) return false;

			return Memory.Compare( left.GetChars(), right.GetChars(), ( int ) left.ByteLength );
		}

		public static bool operator !=( SbcsString left, SbcsString right ) {
			return !( left == right );
		}

		public SbcsString Substring( int startIndex ) {
			return Substring( startIndex, ( int ) Length - startIndex );
		}

		public SbcsString Substring( int startIndex, int length ) {
			if( !Assert.IsFalse( startIndex < 0 | ( uint ) startIndex > Length ) )
				return Empty;

			if( !Assert.IsFalse( length < 0 | ( uint ) startIndex + ( uint ) length > Length ) )
				return Empty;

			if( startIndex == 0 & ( uint ) length == Length )
				return this;

			if( length == 0 )
				return Empty;

			return FromSBCS( CodePage, GetChars() + startIndex, ( uint ) length );
		}

		public bool StartsWith( char value ) {
			if( Length < 1 ) return false;

			return this[0] == value;
		}

		public bool EndsWith( char value ) {
			if( Length < 1 ) return false;

			return this[Length - 1] == value;
		}

		public bool StartsWith( SbcsString value ) {
			if( value == null || value.Length == 0 ) return false;
			if( Length < value.Length ) return false;

			return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
		}

		public bool EndsWith( SbcsString value ) {
			if( value == null || value.Length == 0 ) return false;
			if( Length < value.Length ) return false;

			return Memory.Compare( GetChars() + ( Length - value.Length ), value.GetChars(), ( int ) value.Length * sizeof( this[0] ) );
		}

		public thistype ToUpper() {
			if( IsNullOrEmpty ) return this;

			using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
				builder.Value.Append( this );
				builder.Value.ToUpper();
				return builder.Value.ToSbcsString( CodePage );
			}
		}

		public thistype ToLower() {
			if( IsNullOrEmpty ) return this;

			using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
				builder.Value.Append( this );
				builder.Value.ToLower();
				return builder.Value.ToSbcsString( CodePage );
			}
		}

		public SbcsString Trim() {
			if( Length == 0 ) return this;
			uint startIndex = 0, endIndex = Length - 1;

			for( uint i = 0; i < Length; ++i, ++startIndex ) {
				switch( this[i] ) {
					case ' ':
					case '\t':
					case '\n':
					case '\v':
					case '\f':
					case '\r':
						continue;

					default:
						goto trim_end;
				}
			}

		trim_end:
			for( uint i = Length - 1; i >= startIndex; --i, --endIndex ) {
				switch( this[i] ) {
					case ' ':
					case '\t':
					case '\n':
					case '\v':
					case '\f':
					case '\r':
						continue;

					default:
						goto end;
				}
			}

		end:
			if( startIndex > endIndex ) return "";
			return FromSBCS( CodePage, GetChars() + startIndex, ( uint )( endIndex - startIndex + 1 ) );
		}

		/// @ Conversion from SBCS

		public static explicit operator SbcsString( CString value ) { return FromSBCS( value ); }
		public static explicit operator SbcsString( CStringSpan value ) { return FromSBCS( value ); }

		public static SbcsString FromSBCS( CString value ) { return FromSBCS( Environment.DefaultCodePage, value ); }
		public static SbcsString FromSBCS( CodePageID codePage, CString value ) { return FromSBCS( codePage, value.GetChars(), 0 ); }

		public static SbcsString FromSBCS( CStringSpan value ) { return FromSBCS( Environment.DefaultCodePage, value ); }
		public static SbcsString FromSBCS( CodePageID codePage, CStringSpan value ) { return FromSBCS( codePage, value.GetChars(), value.ByteLength ); }

		public static SbcsString FromSBCS( CodePageID codePage, byte* sbcsText, uint sbcsTextLength = 0 ) {
			if( sbcsText == null ) return null;
			if( sbcsText[0] == 0 & sbcsTextLength == 0 ) return "";

			if( sbcsTextLength == 0 )
				sbcsTextLength = CString.ComputeLength( sbcsText );

			var result = new SbcsString( sbcsTextLength, codePage );
			var resultChars = result.GetChars();

			Memory.Copy( resultChars, sbcsText, sbcsTextLength );
			resultChars[sbcsTextLength] = 0;

			result.UpdateHash();
			return result;
		}

		/// @}

		/// @ Conversion from UTF8

		public static explicit operator SbcsString( Utf8String value ) { return FromUtf8( value ); }
		public static explicit operator SbcsString( CUtf8String value ) { return FromUtf8( value ); }
		public static explicit operator SbcsString( CUtf8StringSpan value ) { return FromUtf8( value ); }

		public static SbcsString FromUtf8( CUtf8String value ) { return FromUtf8( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf8( CodePageID codePage, CUtf8String value ) { return FromUtf8( codePage, value.GetChars(), 0 ); }

		public static SbcsString FromUtf8( CUtf8StringSpan value ) { return FromUtf8( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf8( CodePageID codePage, CUtf8StringSpan value ) { return FromUtf8( codePage, value.GetChars(), value.ByteLength ); }

		public static SbcsString FromUtf8( Utf8String value ) { return FromUtf8( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf8( CodePageID codePage, Utf8String value ) {
			if( value == null ) return null;
			if( value.Length == 0 ) return "";

			return FromUtf8( codePage, value.GetChars(), value.ByteLength );
		}

		public static SbcsString FromUtf8( byte* memory, uint limitBytes = 0 ) { return FromUtf8( Environment.DefaultCodePage, memory, limitBytes ); }
		public static SbcsString FromUtf8( CodePageID codePage, byte* memory, uint limitBytes = 0 ) {
			if( memory == null ) return null;
			if( memory[0] == 0 & limitBytes == 0 ) return "";

			using( var page = Memory.CachedPages.PopScoped() ) {
				var buffer = ( byte* ) page.Value;
				var bufferLength = ( uint ) Memory.DefaultPageSize;

				uint encodedCharsCount, charsCount;
				Unicode.GetUtf8ByteCountInUtf16( memory, limitBytes, charsCount, encodedCharsCount );

				var result = new thistype( encodedCharsCount );
				*cast<uint*>( bitcast<byte*>( result ) + memberinfo( Length ).ByteOffset ) = charsCount;
				*cast<System.Text.CodePageID*>( bitcast<byte*>( result ) + memberinfo( CodePage ).ByteOffset ) = codePage;

				var resultChars = result.GetChars();

				foreach( var segment in Unicode.StreamConvertUtf8CharactersToUtf16( ( char* ) buffer, bufferLength / 2, memory, limitBytes ) ) {
					Encoding.GetBytes( codePage, segment.Start, segment.Count, resultChars, segment.Count );
					resultChars += segment.Count;
				}

				*resultChars = '\0';
				result.UpdateHash();
				return result;
			}
		}

		/// @}

		/// @ Conversion from UTF16

		public static explicit operator SbcsString( Utf16String value ) { return FromUtf16( value ); }
		public static explicit operator SbcsString( CUtf16String value ) { return FromUtf16( value ); }
		public static explicit operator SbcsString( CUtf16StringSpan value ) { return FromUtf16( value ); }

		public static SbcsString FromUtf16( CUtf16String value ) { return FromUtf16( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf16( CodePageID codePage, CUtf16String value ) { return FromUtf16( codePage, value.GetChars(), 0 ); }

		public static SbcsString FromUtf16( CUtf16StringSpan value ) { return FromUtf16( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf16( CodePageID codePage, CUtf16StringSpan value ) { return FromUtf16( codePage, value.GetChars(), value.Length ); }

		public static SbcsString FromUtf16( Utf16String value ) { return FromUtf16( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf16( CodePageID codePage, Utf16String value ) {
			if( value == null ) return null;
			if( value.Length == 0 ) return "";

			return FromUtf16( codePage, value.GetChars(), value.Length );
		}

		public static SbcsString FromUtf16( char* memory, uint limitChars = 0 ) { return FromUtf16( Environment.DefaultCodePage, memory, limitChars ); }
		public static SbcsString FromUtf16( CodePageID codePage, char* memory, uint limitChars = 0 ) {
			if( memory == null ) return null;
			if( memory[0] == 0 & limitChars == 0 ) return "";

			if( limitChars == 0 )
				limitChars = CUtf16String.ComputeLength( memory );

			var result = new thistype( limitChars );
			*cast<uint*>( bitcast<byte*>( result ) + memberinfo( Length ).ByteOffset ) = limitChars;
			*cast<System.Text.CodePageID*>( bitcast<byte*>( result ) + memberinfo( CodePage ).ByteOffset ) = codePage;

			var chars = result.GetChars();
			Encoding.GetBytes( codePage, memory, limitChars, chars, limitChars );
			chars[limitChars] = '\0';

			result.UpdateHash();
			return result;
		}

		/// @}

		/// @ Conversion from UTF32

		public static explicit operator SbcsString( Utf32String value ) { return FromUtf32( value ); }
		public static explicit operator SbcsString( CUtf32String value ) { return FromUtf32( value ); }
		public static explicit operator SbcsString( CUtf32StringSpan value ) { return FromUtf32( value ); }

		public static SbcsString FromUtf32( CUtf32String value ) { return FromUtf32( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf32( CodePageID codePage, CUtf32String value ) { return FromUtf32( codePage, value.GetChars(), 0 ); }

		public static SbcsString FromUtf32( CUtf32StringSpan value ) { return FromUtf32( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf32( CodePageID codePage, CUtf32StringSpan value ) { return FromUtf32( codePage, value.GetChars(), value.Length ); }

		public static SbcsString FromUtf32( Utf32String value ) { return FromUtf32( Environment.DefaultCodePage, value ); }
		public static SbcsString FromUtf32( CodePageID codePage, Utf32String value ) {
			if( value == null ) return null;
			if( value.Length == 0 ) return "";

			return FromUtf32( codePage, value.GetChars(), value.Length );
		}

		public static SbcsString FromUtf32( uint* memory, uint limitChars = 0 ) { return FromUtf32( Environment.DefaultCodePage, memory, limitChars ); }
		public static SbcsString FromUtf32( CodePageID codePage, uint* memory, uint limitChars = 0 ) {
			if( memory == null ) return null;
			if( memory[0] == 0 & limitChars == 0 ) return "";

			using( var page = Memory.CachedPages.PopScoped() ) {
				var buffer = ( byte* ) page.Value;
				var bufferLength = ( uint ) Memory.DefaultPageSize;

				uint charsCount16;
				Unicode.GetUtf32ByteCountInUtf16( memory, limitChars, charsCount16 );

				var result = new thistype( charsCount16 );
				*cast<uint*>( bitcast<byte*>( result ) + memberinfo( Length ).ByteOffset ) = charsCount16;
				*cast<System.Text.CodePageID*>( bitcast<byte*>( result ) + memberinfo( CodePage ).ByteOffset ) = codePage;

				var resultChars = result.GetChars();

				foreach( var segment in Unicode.StreamConvertUtf32CharactersToUtf16( ( char* ) buffer, bufferLength / 2, memory, limitChars ) ) {
					Encoding.GetBytes( codePage, segment.Start, segment.Count, resultChars, segment.Count );
					resultChars += segment.Count;
				}

				*resultChars = '\0';
				result.UpdateHash();
				return result;
			}
		}

		/// @}

		public static SbcsString Format( [VerifyFormatString] Utf16String format, vararg TypedReference parameters ) {
			return Format( Environment.DefaultCodePage, format, vararg( parameters, parameters.Length ) );
		}

		public static SbcsString Format( CodePageID codePage, [VerifyFormatString] Utf16String format, vararg TypedReference parameters ) {
			using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
				builder.Value.AppendFormat( format, vararg( parameters, parameters.Length ) );
				return builder.Value.ToSbcsString( codePage );
			}
		}

		public static SbcsString Concat( vararg TypedReference values ) {
			return Concat( Environment.DefaultCodePage, vararg( values, values.Length ) );
		}

		public static SbcsString Concat( CodePageID codePage, vararg TypedReference values ) {
			using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
				builder.Value.Concat( vararg( values, values.Length ) );
				return builder.Value.ToSbcsString( codePage );
			}
		}

		public static SbcsString operator +( SbcsString left, TypedReference right ) { return Concat( left != null ? left.CodePage : Environment.DefaultCodePage, left, right ); }
		public static SbcsString operator +( TypedReference left, SbcsString right ) { return Concat( right != null ? right.CodePage : Environment.DefaultCodePage, left, right ); }

		public byte[] ToCharArray() {
			var result = new[Length] byte;
			Memory.Copy( &result[0], GetChars(), ByteLength );
			return result;
		}

		public int IndexOf( thistype value ) {
			return IndexOf( value, 0 );
		}

		public int IndexOf( thistype value, int startIndex ) {
			if( !Assert.IsFalse( startIndex < 0 || ( uint ) startIndex >= Length || value == "" ) )
				return -1;

			for( var i = 0U; i + value.Length < Length + 1; ++i ) {
				if( this[i] == value[0] ) {
					uint j;

					for( j = 1; j < value.Length; ++j )
						if( this[i + j] != value[j] )
							break;

					if( j == value.Length ) return ( int ) i;
				}
			}

			return -1;
		}

		public int CompareTo( CStringSpan value ) {
			if( this == null & value.Location == null ) return 0;
			if( this == null ^ value.Location == null ) return value.Location == null ? 1 : -1;

			var leftChars = GetChars();
			var rightChars = value.GetChars();
			var minLength = Math.Min( Length, value.Length );

			for( var i = 0U; i < minLength; ++i ) {
				var leftChar = leftChars[i];
				var rightChar = rightChars[i];

				if( leftChar == rightChar ) continue;

				return leftChar > rightChar ? 1 : -1;
			}

			if( Length == value.Length ) return 0;
			return Length.CompareTo( value.Length );
		}

		public int CompareTo( CUtf16StringSpan value ) {
			var leftChars = GetChars();
			var rightChars = value.GetChars();

			using( var page = Memory.CachedPages.PopScoped() ) {
				var buffer = ( byte* ) page.Value;
				var bufferLength = ( uint ) Memory.DefaultPageSize;

				var minLength = Math.Min( Length, value.Length );

				var start = 0;
				var remaining = minLength;

				for( int block = 0, blockCount = ( int )( ( minLength + Memory.DefaultPageSize - 1 ) / Memory.DefaultPageSize ); block < blockCount; block += Memory.DefaultPageSize ) {
					var end = Math.Min( remaining, start + Memory.DefaultPageSize );
					Encoding.GetBytes( CodePage, rightChars + start, ( uint )( end - start ), buffer, bufferLength );

					for( var i = start; i < end; ++i ) {
						var leftChar = leftChars[i];
						var rightChar = buffer[i - start];

						if( leftChar == rightChar ) continue;

						return leftChar > rightChar ? 1 : -1;
					}

					start += Memory.DefaultPageSize;
					remaining -= Memory.DefaultPageSize;
				}
			}

			if( Length == value.Length ) return 0;
			return Length.CompareTo( value.Length );

			// var sbcs = FromUtf16( CodePage, value );
			// return CompareTo( sbcs );
		}

		public int CompareTo( CUtf32StringSpan value ) {
			var sbcs = FromUtf32( CodePage, value );
			return CompareTo( sbcs );
		}
	}

	// Pointer to null-terminated array of single-byte characters ( char* in C string functions )
	[PrimitiveType( Size = sizeof( uintptr ) )]
	public partial struct CString {
		public const thistype Null = bitcast<thistype>( ( uintptr ) 0 );
		public bool IsNullOrEmpty { get { return GetChars() == null || GetChars()[0] == 0; } }

		public CString( byte* location ) { this = bitcast<thistype>( location ); }
		public CString( sbyte* location ) { this = bitcast<thistype>( location ); }

		public uint Length {
			get { return ComputeLength( GetChars() ); }
		}

		public uint ByteLength {
			get { return Length; }
		}

		public uint Hash {
			get { return GetHashAndByteLength( this, 0U ); }
		}

		public static implicit operator byte*( CString& @string ) { return bitcast<byte*>( @string ); }

		public static bool operator ==( CString left, CString right ) { return AreEqual( left, right ); }
		public static bool operator !=( CString left, CString right ) { return !AreEqual( left, right ); }

		public static bool AreEqual( CString leftString, CString rightString ) {
			var left = leftString.GetChars();
			var right = rightString.GetChars();

			if( left == right ) return true;
			if( ( left == null ) | ( right == null ) ) return false;

			while( *left != '\0' && ( *left == *right ) ) {
				++left;
				++right;
			}

			return ( *left - *right ) == 0;
		}

		public static bool AreEqual( CString leftString, CString rightString, int length ) {
			var left = leftString.GetChars();
			var right = rightString.GetChars();

			if( left == right ) return true;
			if( ( left == null ) | ( right == null ) ) return false;

			int n = length;

			while( --n >= 0 && *left == *right++ )
				if( *left++ == '\0' )
					return true;

			return n < 0 ? true : *left - *--right == 0;
		}

		/// Dangerous operator. Result will correct while 'value' is alive.
		public static implicit operator CString( SbcsString value ) { return value != null ? new CString( value.GetChars() ) : CString.Null; }

		public byte* GetChars() { return bitcast<byte*>( this ); }

		public byte this[int index] { get { return GetChars()[index]; } }
		public byte this[uint index] { get { return GetChars()[index]; } }

		public static uint ComputeLength( byte* chars ) {
			if( chars == null ) return 0U;

			var result = 0U;

			while( *chars++ != 0 )
				++result;

			return result;
		}

		public static uint ComputeLength( byte* chars, uint limitLength ) {
			if( chars == null ) return 0U;

			var result = 0U;

			while( *chars++ != 0 ) {
				++result;
				if( result >= limitLength ) break;
			}

			return result;
		}

		public static uint ComputeHashCode( byte* chars, uint length ) {
			if( chars == null ) return 0U;

			var result = 0U;

			for( var i = 0U; i < length; ++i )
				result = result * 41 + chars[i];

			return result;
		}

		public uint GetHashCode() { uint length; return GetHashAndByteLength( GetChars(), length ); }

		public static uint GetHashAndByteLength( byte* chars, uint& resultLength ) {
			if( chars == null ) { resultLength = 0; return 0U; }

			var result = 0U;
			var length = 0U;

			for( int i = 0; chars[i] != '\0'; ++i, ++length )
				result = result * 41 + chars[i];

			resultLength = length;
			return result;
		}
	}

	public partial struct CStringSpan {
		public static readonly CStringSpan Null;
		public bool IsNullOrEmpty { get { return Location == null || Length == 0; } }

		public byte* Location;
		public uint Length;
		public uint ByteLength { get { return Length; } }

		public CStringSpan( byte* location, uint length ) { Location = location; Length = length; }
		public CStringSpan( byte* location, byte* end ) { Assert.IsTrue( end >= location ); Location = location; Length = ( uint )( end - location ); }
		public CStringSpan( sbyte* location, uint length ) { Location = cast<byte*>( location ); Length = length; }

		public uint GetHashCode() { return CString.ComputeHashCode( Location, Length ); }

		public byte* GetChars() { return Location; }

		public static bool AreEqual( CStringSpan& leftString, CStringSpan& rightString ) {
			var left = leftString.Location;
			var right = rightString.Location;

			if( left == right && leftString.Length == rightString.Length ) return true;
			if( ( left == null ) | ( right == null ) ) return false;

			return Memory.Compare( left, right, ( int ) leftString.Length );
		}

		public static bool operator ==( CStringSpan& left, CStringSpan& right ) { return AreEqual( left, right ); }
		public static bool operator !=( CStringSpan& left, CStringSpan& right ) { return !AreEqual( left, right ); }

		public static bool operator ==( SbcsString left, CStringSpan& right ) { return AreEqual( left, right ); }
		public static bool operator !=( SbcsString left, CStringSpan& right ) { return !AreEqual( left, right ); }

		public static bool operator ==( CStringSpan& left, SbcsString right ) { return AreEqual( left, right ); }
		public static bool operator !=( CStringSpan& left, SbcsString right ) { return !AreEqual( left, right ); }

		/// Dangerous operator. Result will correct while 'value' is alive.
		public static implicit operator CStringSpan( SbcsString value ) { return value != null ? new CStringSpan( value.GetChars(), value.Length ) : CStringSpan.Null; }
		public static implicit operator CStringSpan( CString value ) { return new CStringSpan( value.GetChars(), value.ByteLength ); }
		public static explicit operator CStringSpan( CUtf8StringSpan value ) { return new CStringSpan( value.Location, value.ByteLength ); }
		public static explicit operator CStringSpan( Utf8String value ) { return new CStringSpan( value.GetChars(), value.ByteLength ); }

		public bool StartsWith( CStringSpan value ) {
			if( value == null || value.Length == 0 ) return false;
			if( Length < value.Length ) return false;

			return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
		}

		public bool EndsWith( CStringSpan value ) {
			if( value == null || value.Length == 0 ) return false;
			if( Length < value.Length ) return false;

			return Memory.Compare( GetChars() + ( Length - value.Length ), value.GetChars(), ( int ) value.ByteLength );
		}

		public bool StartsWith( SbcsString value ) {
			if( value == null || value.Length == 0 ) return false;
			if( Length < value.Length ) return false;

			return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
		}

		public bool EndsWith( SbcsString value ) {
			if( value == null || value.Length == 0 ) return false;
			if( Length < value.Length ) return false;

			return Memory.Compare( GetChars() + ( Length - value.Length ), value.GetChars(), ( int ) value.ByteLength );
		}
	}
}