//
// Created by Eugeny Grishul
//
// See license at http://bamelg.com/license.txt
//
using System.Globalization;
using System.Runtime;
using System.Text;
namespace System {
public sealed partial class Utf8String {
public const Utf8String Empty = "";
public readonly uint Length; // Number of characters needed to encode this string in UTF16
public readonly uint ByteLength;
public readonly uint Hash;
public bool IsNullOrEmpty { get { return this == null || Length == 0; } }
private Utf8String() {
}
private Utf8String( uint byteLength, uint length = 0 ) {
extra {
return sizeof( byte ) * ( byteLength + 1 );
}
body {
ByteLength = byteLength;
Length = length;
}
}
public Utf8String( Utf8String value ) {
extra {
return value.ByteLength + 1;
}
body {
Length = value.Length;
ByteLength = value.ByteLength;
Hash = value.Hash;
Memory.Copy( GetChars(), value.GetChars(), ByteLength + 1 );
}
}
public override uint GetHashCode() { return Hash; }
internal byte* GetChars() { return this != null ? bitcast<byte*>( this ) + sizeof( thistype ) : null; }
public static bool operator ==( Utf8String left, Utf8String 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 || left.ByteLength != right.ByteLength ) return false;
return Memory.Compare( left.GetChars(), right.GetChars(), ( int ) left.ByteLength );
}
public static bool operator !=( Utf8String left, Utf8String right ) {
return !( left == right );
}
public static Utf8String operator +( Utf8String left, Utf8String right ) {
if( left == null ) return right;
if( right == null ) return left;
var resultLength = left.Length + right.Length;
var resultByteLength = left.ByteLength + right.ByteLength;
var result = new Utf8String( resultByteLength, resultLength );
var chars = result.GetChars();
Memory.Copy( chars, left.GetChars(), left.ByteLength );
Memory.Copy( chars + left.ByteLength, right.GetChars(), right.ByteLength + 1 );
chars[resultByteLength] = '\0';
result.UpdateHash();
return result;
}
public static Utf8String Concat( vararg TypedReference values ) {
using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
builder.Value.Concat( vararg( values, values.Length ) );
return builder.Value.ToUtf8String();
}
}
public static Utf8String operator +( Utf8String left, TypedReference right ) { return Concat( left, right ); }
public static Utf8String operator +( TypedReference left, Utf8String right ) { return Concat( left, right ); }
private void UpdateHash() { *cast<uint*>( bitcast<byte*>( this ) + memberinfo( Hash ).ByteOffset ) = CUtf8String.ComputeHashCode( GetChars(), ByteLength ); }
/// @ Conversion from SBCS
public static explicit operator Utf8String( SbcsString value ) { return FromSBCS( value ); }
public static explicit operator Utf8String( CString value ) { return FromSBCS( value ); }
public static explicit operator Utf8String( CStringSpan value ) { return FromSBCS( value ); }
public static Utf8String FromSBCS( CString value ) { return FromSBCS( Environment.DefaultCodePage, value ); }
public static Utf8String FromSBCS( CodePageID codePage, CString value ) { return FromSBCS( codePage, value.GetChars(), 0 ); }
public static Utf8String FromSBCS( CStringSpan value ) { return FromSBCS( Environment.DefaultCodePage, value ); }
public static Utf8String FromSBCS( CodePageID codePage, CStringSpan value ) { return FromSBCS( codePage, value.GetChars(), value.Length ); }
public static Utf8String FromSBCS( SbcsString value ) {
if( value == null ) return null;
if( value.Length == 0 ) return "";
return FromSBCS( value.CodePage, value.GetChars(), value.Length );
}
public static Utf8String FromSBCS( byte* sbcsText, uint sbcsTextLength = 0 ) { return FromSBCS( Environment.DefaultCodePage, sbcsText, sbcsTextLength ); }
public static Utf8String FromSBCS( CodePageID codePage, byte* sbcsText, uint sbcsTextLength = 0 ) {
if( sbcsText == null ) return null;
if( sbcsText[0] == 0 & sbcsTextLength == 0 ) return "";
uint characterCount16, bytesCount;
Unicode.GetSbcsByteCountInUtf8( codePage, sbcsText, sbcsTextLength, characterCount16, bytesCount );
var result = new Utf8String( bytesCount, characterCount16 );
var resultChars = result.GetChars();
Unicode.ConvertSbcsCharactersToUtf8( Environment.DefaultCodePage, resultChars, bytesCount, sbcsText, sbcsTextLength );
result.UpdateHash();
return result;
}
/// @}
/// @{ UTF8 Conversion
public static explicit operator Utf8String( CUtf8String value ) { return FromUtf8( value ); }
public static explicit operator Utf8String( CUtf8StringSpan value ) { return FromUtf8( value ); }
public static Utf8String FromUtf8( CUtf8String value ) { return FromUtf8( value.GetChars(), 0U ); }
public static Utf8String FromUtf8( CUtf8StringSpan value ) { return FromUtf8( value.GetChars(), value.ByteLength ); }
public static Utf8String FromUtf8( byte* chars, uint byteLength = 0 ) {
if( chars == null ) return null;
if( chars[0] == 0 & byteLength == 0 ) return "";
uint length;
Unicode.GetUtf8CodeUnitCount( chars, byteLength, length, 0U );
var result = new Utf8String( byteLength, length );
var resultChars = result.GetChars();
Memory.Copy( resultChars, chars, byteLength );
resultChars[byteLength] = 0;
result.UpdateHash();
return result;
}
/// @}
/// @{ UTF16 Conversion
public static explicit operator Utf8String( Utf16String value ) { return FromUtf16( value ); }
public static explicit operator Utf8String( CUtf16String value ) { return FromUtf16( value ); }
public static explicit operator Utf8String( CUtf16StringSpan value ) { return FromUtf16( value ); }
public static Utf8String FromUtf16( CUtf16String value ) { return FromUtf16( value.GetChars(), 0 ); }
public static Utf8String FromUtf16( CUtf16StringSpan value ) { return FromUtf16( value.GetChars(), value.Length ); }
public static Utf8String FromUtf16( Utf16String value ) {
if( value == null ) return null;
if( value.Length == 0 ) return "";
return FromUtf16( value.GetChars(), value.Length );
}
public static Utf8String FromUtf16( char* memory, uint limitChars = 0 ) {
if( memory == null ) return null;
if( memory[0] == 0 & limitChars == 0 ) return "";
uint bytesCount, charsCount;
Unicode.GetUtf16ByteCountInUtf8( memory, limitChars, charsCount, bytesCount );
var result = new Utf8String( bytesCount );
*cast<uint*>( bitcast<byte*>( result ) + memberinfo( Length ).ByteOffset ) = charsCount;
var chars = result.GetChars();
Unicode.ConvertUtf16CharactersToUtf8( chars, memory, limitChars, 0, 0 );
chars[bytesCount] = '\0';
result.UpdateHash();
return result;
}
/// @}
/// @{ UTF32 Conversion
public static explicit operator Utf8String( Utf32String value ) { return FromUtf32( value ); }
public static explicit operator Utf8String( CUtf32String value ) { return FromUtf32( value ); }
public static explicit operator Utf8String( CUtf32StringSpan value ) { return FromUtf32( value ); }
public static Utf8String FromUtf32( CUtf32String value ) { return FromUtf32( value.GetChars(), 0 ); }
public static Utf8String FromUtf32( CUtf32StringSpan value ) { return FromUtf32( value.GetChars(), value.Length ); }
public static Utf8String FromUtf32( Utf32String value ) {
if( value == null ) return null;
if( value.Length == 0 ) return "";
return FromUtf32( value.GetChars(), value.Length );
}
public static Utf8String FromUtf32( uint* memory, uint limitChars ) {
if( memory == null ) return null;
if( memory[0] == 0 & limitChars == 0 ) return null;
uint bytesCount, charsCount;
Unicode.GetUtf32ByteCountInUtf8( memory, limitChars, charsCount, bytesCount );
var result = new Utf8String( bytesCount );
*cast<uint*>( bitcast<byte*>( result ) + memberinfo( Length ).ByteOffset ) = charsCount;
var chars = result.GetChars();
Unicode.ConvertUtf32CharactersToUtf8( chars, memory, limitChars );
chars[bytesCount] = '\0';
result.UpdateHash();
return result;
}
/// @}
public byte[] ToCharArray() {
var result = new[ByteLength] byte;
Memory.Copy( &result[0], GetChars(), ByteLength );
return result;
}
public static int Compare( thistype left, thistype right, CompareOptions options ) {
if( left == null & right == null ) return 0;
if( left == null ^ right == null ) return right == null ? 1 : -1;
return string.Compare( ( string ) left, ( string ) right, options );
}
public int CompareTo( string value ) {
if( this == null & value == null ) return 0;
if( this == null ^ value == null ) return value == null ? 1 : -1;
return cast<string>( this ).CompareTo( ( string ) value );
}
public static Utf8String Format( [VerifyFormatString] Utf16String format, vararg TypedReference parameters ) {
using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
builder.Value.AppendFormat( format, vararg( parameters, parameters.Length ) );
return builder.Value.ToUtf8String();
}
}
public Utf8String ToUpper() {
if( IsNullOrEmpty ) return this;
using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
builder.Value.Append( this );
builder.Value.ToUpper();
return builder.Value.ToUtf8String();
}
}
public Utf8String ToLower() {
if( IsNullOrEmpty ) return this;
using( var builder = StringBuilder.CachedBuilders.PopScoped() ) {
builder.Value.Append( this );
builder.Value.ToLower();
return builder.Value.ToUtf8String();
}
}
public Utf8String Replace( string find, string replace ) {
if( find.IsNullOrEmpty ) return this;
return ( Utf8String ) ( ( string ) this ).Replace( find, replace );
}
public bool EndsWith( Utf8String value ) {
if( value.IsNullOrEmpty ) return false;
if( cast<RuntimeObjectBase>( this ) == cast<RuntimeObjectBase>( value ) ) return true;
if( value.ByteLength > ByteLength ) return false;
return Memory.Compare( GetChars() + ByteLength - value.ByteLength, value.GetChars(), ( int ) value.ByteLength );
}
public bool StartsWith( Utf8String value ) {
if( value.IsNullOrEmpty ) return false;
if( cast<RuntimeObjectBase>( this ) == cast<RuntimeObjectBase>( value ) ) return true;
if( value.ByteLength > ByteLength ) return false;
return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
}
// TODO
public bool Contains( Utf8String value ) {
if( value.IsNullOrEmpty ) return false;
return ( ( string ) this ).Contains( ( string ) value );
}
public override string ToString() { return ( string ) this; }
}
[PrimitiveType( Size = sizeof( uintptr ) )]
public partial struct CUtf8String {
public const thistype Null = bitcast<thistype>( ( uintptr ) 0 );
public bool IsNullOrEmpty { get { return GetChars() == null || GetChars()[0] == 0; } }
public CUtf8String( byte* location ) { this = bitcast<thistype>( location ); }
public CUtf8String( sbyte* location ) { this = bitcast<thistype>( location ); }
public uint Length {
get {
uint result;
Unicode.GetUtf8CodeUnitCount( GetChars(), 0U, result, 0U );
return result;
}
}
public uint ByteLength {
get {
return CString.ComputeLength( GetChars() );
}
}
public uint Hash {
get { return GetHashCode(); }
}
public uint GetHashCode() { return GetHashAndByteLength( GetChars(), 0U ); }
public static bool operator ==( CUtf8String left, CUtf8String right ) { return AreEqual( left, right ); }
public static bool operator !=( CUtf8String left, CUtf8String right ) { return !AreEqual( left, right ); }
public static void Concatenate( CUtf8String destinationString, CString sourceString ) {
var destination = destinationString.GetChars();
var source = sourceString.GetChars();
var length = destinationString.Length;
var length2 = sourceString.Length;
for( var i = 0U; i <= length2; ++i )
destination[i + length] = source[i];
}
public static bool AreEqual( CUtf8String leftString, CUtf8String rightString ) {
var left = leftString.GetChars();
var right = rightString.GetChars();
if( ( left == null ) & ( right == null ) ) return true;
if( ( left == null ) | ( right == null ) ) return false;
while( *left != '\0' && ( *left == *right ) ) {
++left;
++right;
}
return ( *left - *right ) == 0;
}
public static bool AreEqual( CUtf8String leftString, CUtf8String rightString, int length ) {
var left = leftString.GetChars();
var right = rightString.GetChars();
if( ( left == null ) & ( right == null ) ) 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;
}
public bool StartsWith( CUtf8String prefix ) {
var left = GetChars();
var right = prefix.GetChars();
if( ( left == null ) | ( right == null ) ) return false;
while( *left != '\0' && ( *left == *right ) ) {
++left;
++right;
}
return *right == 0;
}
static void Copy( CUtf8String leftString, CUtf8String rightString ) {
var left = leftString.GetChars();
var right = rightString.GetChars();
while( ( *left++ = *right++ ) != 0 )
continue;
}
static void Copy( CUtf8String leftString, CUtf8String rightString, int length ) {
var left = leftString.GetChars();
var right = rightString.GetChars();
while( length != 0 && ( *left++ = *right++ ) != 0 )
--length;
if( length == 0 ) *--left = 0;
}
public bool StartsWith( CUtf8StringSpan value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( ByteLength < value.ByteLength ) return false;
return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
}
public bool EndsWith( CUtf8StringSpan value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( ByteLength < value.ByteLength ) return false;
return Memory.Compare( GetChars() + ( ByteLength - value.ByteLength ), value.GetChars(), ( int ) value.ByteLength );
}
public bool StartsWith( Utf8String value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( Length < value.ByteLength ) return false;
return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
}
public bool EndsWith( Utf8String value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( Length < value.ByteLength ) return false;
return Memory.Compare( GetChars() + ( ByteLength - value.ByteLength ), value.GetChars(), ( int ) value.ByteLength );
}
/// Dangerous operator. Result will correct only while 'value' is alive.
public static implicit operator CUtf8String( Utf8String value ) { return new CUtf8String( value.GetChars() ); }
public static uint GetHashAndByteLength( byte* chars, uint& resultLength ) {
return CString.GetHashAndByteLength( chars, resultLength );
}
[ForceInline]
public byte* GetChars() { return bitcast<byte*>( this ); }
public static uint ComputeHashCode( byte* chars, uint byteLength ) {
return CString.ComputeHashCode( chars, byteLength );
}
}
public partial struct CUtf8StringSpan {
public static readonly CUtf8StringSpan Null;
public bool IsNullOrEmpty { get { return Location == null || ByteLength == 0; } }
public byte* Location;
public uint ByteLength;
public CUtf8StringSpan( byte* location, uint byteLength ) { Location = location; ByteLength = byteLength; }
public uint GetHashCode() { return CUtf8String.ComputeHashCode( Location, ByteLength ); }
public byte* GetChars() { return Location; }
public static bool AreEqual( CUtf8StringSpan& leftString, CUtf8StringSpan& rightString ) {
var left = leftString.Location;
var right = rightString.Location;
if( left == right && leftString.ByteLength == rightString.ByteLength ) return true;
if( ( left == null ) | ( right == null ) ) return false;
return Memory.Compare( left, right, ( int ) leftString.ByteLength );
}
public static bool operator ==( CUtf8StringSpan& left, CUtf8StringSpan& right ) { return AreEqual( left, right ); }
public static bool operator !=( CUtf8StringSpan& left, CUtf8StringSpan& right ) { return !AreEqual( left, right ); }
public static bool operator ==( Utf8String left, CUtf8StringSpan& right ) { return AreEqual( left, right ); }
public static bool operator !=( Utf8String left, CUtf8StringSpan& right ) { return !AreEqual( left, right ); }
public static bool operator ==( CUtf8StringSpan& left, Utf8String right ) { return AreEqual( left, right ); }
public static bool operator !=( CUtf8StringSpan& left, Utf8String right ) { return !AreEqual( left, right ); }
/// Dangerous operator. Result will correct while 'value' is alive.
public static implicit operator CUtf8StringSpan( Utf8String value ) { return value != null ? new CUtf8StringSpan( value.GetChars(), value.ByteLength ) : CUtf8StringSpan.Null; }
public static implicit operator CUtf8StringSpan( CUtf8String value ) { return new CUtf8StringSpan( value.GetChars(), value.ByteLength ); }
public bool StartsWith( CUtf8StringSpan value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( ByteLength < value.ByteLength ) return false;
return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
}
public bool EndsWith( CUtf8StringSpan value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( ByteLength < value.ByteLength ) return false;
return Memory.Compare( GetChars() + ( ByteLength - value.ByteLength ), value.GetChars(), ( int ) value.ByteLength );
}
public bool StartsWith( Utf8String value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( ByteLength < value.ByteLength ) return false;
return Memory.Compare( GetChars(), value.GetChars(), ( int ) value.ByteLength );
}
public bool EndsWith( Utf8String value ) {
if( value == null || value.ByteLength == 0 ) return false;
if( ByteLength < value.ByteLength ) return false;
return Memory.Compare( GetChars() + ( ByteLength - value.ByteLength ), value.GetChars(), ( int ) value.ByteLength );
}
}
}