//
// 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 );
}
}
}