From c163ebcb59fc1a21afc9e18a2cc832ad0b60514f Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Wed, 2 Sep 2015 19:44:24 +1000 Subject: [PATCH] Proper fix of UnsafeString on windows platforms - should fix input text and block info string. --- .../2D/Screens/BlockSelectScreen.cs | 6 +- ClassicalSharp/2D/Screens/FpsScreen.cs | 8 +- ClassicalSharp/2D/Utils2D.cs | 4 - ClassicalSharp/2D/Widgets/TextInputWidget.cs | 15 +- ClassicalSharp/ClassicalSharp.csproj | 2 +- ClassicalSharp/Utils/StringBuffer.cs | 180 ++++++++++++++++++ ClassicalSharp/Utils/UnsafeString.cs | 122 ------------ OpenTK/SharpDX.Direct3D/Resources.cs | 2 +- 8 files changed, 195 insertions(+), 144 deletions(-) create mode 100644 ClassicalSharp/Utils/StringBuffer.cs delete mode 100644 ClassicalSharp/Utils/UnsafeString.cs diff --git a/ClassicalSharp/2D/Screens/BlockSelectScreen.cs b/ClassicalSharp/2D/Screens/BlockSelectScreen.cs index a0535c4f4..4f748071e 100644 --- a/ClassicalSharp/2D/Screens/BlockSelectScreen.cs +++ b/ClassicalSharp/2D/Screens/BlockSelectScreen.cs @@ -28,7 +28,7 @@ namespace ClassicalSharp { int rows; int startX, startY; readonly Font font; - UnsafeString buffer = new UnsafeString( 96 ); + StringBuffer buffer = new StringBuffer( 96 ); public override void Render( double delta ) { graphicsApi.Texturing = true; @@ -130,9 +130,7 @@ namespace ClassicalSharp { Block block = blocksTable[selectedIndex].BlockId; UpdateBlockInfoString( block ); - string value = buffer.value; - if( Utils2D.needWinXpFix ) - value = value.TrimEnd( Utils2D.trimChars ); + string value = buffer.UpdateCachedString(); Size size = Utils2D.MeasureSize( value, font, true ); int x = startX + ( blockSize * blocksPerRow ) / 2 - size.Width / 2; diff --git a/ClassicalSharp/2D/Screens/FpsScreen.cs b/ClassicalSharp/2D/Screens/FpsScreen.cs index 0546ec72f..d5de28130 100644 --- a/ClassicalSharp/2D/Screens/FpsScreen.cs +++ b/ClassicalSharp/2D/Screens/FpsScreen.cs @@ -6,10 +6,10 @@ namespace ClassicalSharp { public class FpsScreen : Screen { readonly Font font; - UnsafeString text; + StringBuffer text; public FpsScreen( Game window ) : base( window ) { font = new Font( "Arial", 13 ); - text = new UnsafeString( 96 ); + text = new StringBuffer( 96 ); } TextWidget fpsTextWidget; @@ -37,10 +37,8 @@ namespace ClassicalSharp { .Append( ref ptr2, "), chunks/s: " ).AppendNum( ref ptr2, game.ChunkUpdates ) .Append( ref ptr2, ", vertices: " ).AppendNum( ref ptr2, game.Vertices ); } - string textString = text.value; - if( Utils2D.needWinXpFix ) - textString = textString.TrimEnd( Utils2D.trimChars ); + string textString = text.UpdateCachedString(); fpsTextWidget.SetText( textString ); maxDelta = 0; accumulator = 0; diff --git a/ClassicalSharp/2D/Utils2D.cs b/ClassicalSharp/2D/Utils2D.cs index 5054bd963..b3a126307 100644 --- a/ClassicalSharp/2D/Utils2D.cs +++ b/ClassicalSharp/2D/Utils2D.cs @@ -12,8 +12,6 @@ namespace ClassicalSharp { static Bitmap measuringBmp; static Graphics measuringGraphics; static Dictionary brushCache = new Dictionary( 16 ); - internal static bool needWinXpFix; - internal static char[] trimChars = { '\0' }; static Utils2D() { format = StringFormat.GenericTypographic; @@ -24,8 +22,6 @@ namespace ClassicalSharp { measuringBmp = new Bitmap( 1, 1 ); measuringGraphics = Graphics.FromImage( measuringBmp ); measuringGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; - OperatingSystem os = Environment.OSVersion; - needWinXpFix = os.Platform == PlatformID.Win32NT && os.Version.Major < 6; } static SolidBrush GetOrCreateBrush( Color color ) { diff --git a/ClassicalSharp/2D/Widgets/TextInputWidget.cs b/ClassicalSharp/2D/Widgets/TextInputWidget.cs index 0e6c36172..498de49b2 100644 --- a/ClassicalSharp/2D/Widgets/TextInputWidget.cs +++ b/ClassicalSharp/2D/Widgets/TextInputWidget.cs @@ -19,7 +19,7 @@ namespace ClassicalSharp { typingLogPos = game.ChatInputLog.Count; // Index of newest entry + 1. this.font = font; this.boldFont = boldFont; - chatInputText = new UnsafeString( 64 ); + chatInputText = new StringBuffer( 64 ); } Texture chatInputTexture, chatCaretTexture; @@ -27,7 +27,7 @@ namespace ClassicalSharp { int caretPos = -1; int typingLogPos = 0; public int ChatInputYOffset; - internal UnsafeString chatInputText; + internal StringBuffer chatInputText; readonly Font font, boldFont; public override void Render( double delta ) { @@ -40,9 +40,7 @@ namespace ClassicalSharp { X = 10; DrawTextArgs caretArgs = new DrawTextArgs( graphicsApi, "_", Color.White, false ); chatCaretTexture = Utils2D.MakeTextTexture( boldFont, 0, 0, ref caretArgs ); - string value = chatInputText.value; - if( Utils2D.needWinXpFix ) - value = value.TrimEnd( Utils2D.trimChars ); + string value = chatInputText.UpdateCachedString(); if( chatInputText.Empty ) { caretPos = -1; @@ -53,9 +51,12 @@ namespace ClassicalSharp { chatCaretTexture.X1 = 10 + size.Width; size.Width += chatCaretTexture.Width; } else { - Size trimmedSize = Utils2D.MeasureSize( value.Substring( 0, caretPos ), font, false ); + string subString = chatInputText.GetSubstring( caretPos ); + Size trimmedSize = Utils2D.MeasureSize( subString, font, false ); + chatInputText.RestoreLength(); + chatCaretTexture.X1 = 10 + trimmedSize.Width; - Size charSize = Utils2D.MeasureSize( value.Substring( caretPos, 1 ), font, false ); + Size charSize = Utils2D.MeasureSize( new String( value[caretPos], 1 ), font, false ); chatCaretTexture.Width = charSize.Width; } size.Height = Math.Max( size.Height, chatCaretTexture.Height ); diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index 31a9ed0e9..11ad0451e 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -164,7 +164,7 @@ - + diff --git a/ClassicalSharp/Utils/StringBuffer.cs b/ClassicalSharp/Utils/StringBuffer.cs new file mode 100644 index 000000000..8ded0008f --- /dev/null +++ b/ClassicalSharp/Utils/StringBuffer.cs @@ -0,0 +1,180 @@ +using System; +using System.Reflection; + +namespace ClassicalSharp { + + // Class used to minimise memory allocations of strings. + // Really, only useful for FpsScreen and TextInputWidget as they allocate lots of very small strings. + // Seriously, you should *not* use this anywhere else, as Empty and Length are O(N). + internal sealed unsafe class StringBuffer { + + internal string value; + internal int capacity; + + static readonly FieldInfo arrayField, lengthField; + static bool supportsLengthSetting; + + static StringBuffer() { + if( OpenTK.Configuration.RunningOnMono ) + return; + + arrayField = typeof( String ).GetField( "m_arrayLength", BindingFlags.NonPublic | BindingFlags.Instance ); + lengthField = typeof( String ).GetField( "m_stringLength", BindingFlags.NonPublic | BindingFlags.Instance ); + + // Make sure we are running on a framework that has both methods - + // otherwise we play it safe and just use TrimEnd(). + if( arrayField != null && lengthField != null ) { + supportsLengthSetting = true; + } else { + arrayField = null; + lengthField = null; + } + } + + public StringBuffer( int capacity ) { + this.capacity = capacity; + value = new String( '\0', capacity ); + } + + public StringBuffer Append( int index, char c ) { + fixed( char* ptr = value ) { + ptr[index] = c; + return this; + } + } + + public StringBuffer Append( int index, string s ) { + fixed( char* ptr = value ) { + char* offsetPtr = ptr + index; + return Append( ref offsetPtr, s ); + } + } + + public StringBuffer Append( ref char* ptr, string s ) { + for( int i = 0; i < s.Length; i++ ) { + *ptr++ = s[i]; + } + return this; + } + + public StringBuffer Append( ref char* ptr, char c) { + *ptr++ = c; + return this; + } + + static char[] numBuffer = new char[20]; + public StringBuffer AppendNum( ref char* ptr, long num ) { + int index = 0; + numBuffer[index++] = (char)( '0' + ( num % 10 ) ); + num /= 10; + + while( num > 0 ) { + numBuffer[index++] = (char)( '0' + ( num % 10 ) ); + num /= 10; + } + for( int i = index - 1; i >= 0; i-- ) { + *ptr++ = numBuffer[i]; + } + return this; + } + + public StringBuffer Clear() { + fixed( char* ptr = value ) { + return Clear( ptr ); + } + } + + public StringBuffer Clear( char* ptr ) { + for( int i = 0; i < capacity; i++ ) { + *ptr++ = '\0'; + } + return this; + } + + public void DeleteAt( int index ) { + fixed( char* ptr = value ) { + char* offsetPtr = ptr + index; + for( int i = index; i < capacity - 1; i++ ) { + *offsetPtr = *( offsetPtr + 1 ); + offsetPtr++; + } + *offsetPtr = '\0'; + } + } + + public void InsertAt( int index, char c ) { + fixed( char* ptr = value ) { + char* offsetPtr = ptr + capacity - 1; + for( int i = capacity - 1; i > index; i-- ) { + *offsetPtr = *( offsetPtr - 1 ); + offsetPtr--; + } + *offsetPtr = c; + } + } + + + public bool Empty { + get { + fixed( char* ptr = value ) { + for( int i = 0; i < capacity; i++ ) { + if( ptr[i] != '\0' ) + return false; + } + } + return true; + } + } + + public int Length { + get { + int len = capacity; + fixed( char* ptr = value ) { + for( int i = capacity - 1; i >= 0; i-- ) { + if( ptr[i] != '\0' ) + break; + len--; + } + } + return len; + } + } + + /// Hack that modifies the underlying string's length to avoid memory allocations. + /// The underlying string - ***do not*** store this because it is mutable! + /// You should only use this string for temporary measuring and drawing. + public string UpdateCachedString() { + return supportsLengthSetting ? value : value.TrimEnd( trimEnd ); + } + static char[] trimEnd = { '\0' }; + + public override string ToString() { + return GetCopy( Length ); + } + + public string GetSubstring( int length ) { + return supportsLengthSetting ? SetLength( length ) : GetCopy( length ); + } + + public void RestoreLength() { + if( supportsLengthSetting ) + SetLength( Length ); + } + + string SetLength( int len ) { + arrayField.SetValue( value, len + 1 ); + lengthField.SetValue( value, len ); + return value; + } + + string GetCopy( int len ) { + string copiedString = new String( '\0', len ); + fixed( char* src = value, dst = copiedString ) { + for( int i = 0; i < len; i++ ) { + dst[i] = src[i]; + } + } + return copiedString; + } + } +} diff --git a/ClassicalSharp/Utils/UnsafeString.cs b/ClassicalSharp/Utils/UnsafeString.cs deleted file mode 100644 index f747041c6..000000000 --- a/ClassicalSharp/Utils/UnsafeString.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; - -namespace ClassicalSharp { - - // Class used to minimise memory allocations of strings. - // Really, only useful for FpsScreen and TextInputWidget as they allocate lots of very small strings. - // Seriously, you should *not* use this anywhere else, as Empty and Length are O(N). - internal sealed unsafe class UnsafeString { - - internal string value; - internal int capacity; - - public UnsafeString( int capacity ) { - this.capacity = capacity; - value = new String( '\0', capacity ); - } - - public UnsafeString Append( int index, char c ) { - fixed( char* ptr = value ) { - ptr[index] = c; - return this; - } - } - - public UnsafeString Append( int index, string s ) { - fixed( char* ptr = value ) { - char* offsetPtr = ptr + index; - return Append( ref offsetPtr, s ); - } - } - - public UnsafeString Append( ref char* ptr, string s ) { - for( int i = 0; i < s.Length; i++ ) { - *ptr++ = s[i]; - } - return this; - } - - public UnsafeString Append( ref char* ptr, char c) { - *ptr++ = c; - return this; - } - - static char[] numBuffer = new char[20]; - public UnsafeString AppendNum( ref char* ptr, long num ) { - int index = 0; - numBuffer[index++] = (char)( '0' + ( num % 10 ) ); - num /= 10; - - while( num > 0 ) { - numBuffer[index++] = (char)( '0' + ( num % 10 ) ); - num /= 10; - } - for( int i = index - 1; i >= 0; i-- ) { - *ptr++ = numBuffer[i]; - } - return this; - } - - public UnsafeString Clear() { - fixed( char* ptr = value ) { - return Clear( ptr ); - } - } - - public UnsafeString Clear( char* ptr ) { - for( int i = 0; i < capacity; i++ ) { - *ptr++ = '\0'; - } - return this; - } - - public void DeleteAt( int index ) { - fixed( char* ptr = value ) { - char* offsetPtr = ptr + index; - for( int i = index; i < capacity - 1; i++ ) { - *offsetPtr = *( offsetPtr + 1 ); - offsetPtr++; - } - *offsetPtr = '\0'; - } - } - - public void InsertAt( int index, char c ) { - fixed( char* ptr = value ) { - char* offsetPtr = ptr + capacity - 1; - for( int i = capacity - 1; i > index; i-- ) { - *offsetPtr = *( offsetPtr - 1 ); - offsetPtr--; - } - *offsetPtr = c; - } - } - - - public bool Empty { - get { - for( int i = 0; i < capacity; i++ ) { - if( value[i] != '\0' ) - return false; - } - return true; - } - } - - public int Length { - get { - int len = capacity; - for( int i = capacity - 1; i >= 0; i-- ) { - if( value[i] != '\0' ) - break; - len--; - } - return len; - } - } - - public override string ToString() { - return value.TrimEnd( '\0' ); - } - } -} diff --git a/OpenTK/SharpDX.Direct3D/Resources.cs b/OpenTK/SharpDX.Direct3D/Resources.cs index 830b61003..399ac7663 100644 --- a/OpenTK/SharpDX.Direct3D/Resources.cs +++ b/OpenTK/SharpDX.Direct3D/Resources.cs @@ -113,7 +113,7 @@ namespace SharpDX.Direct3D9 { return lockedRect; } - public void SetData( int level, LockFlags flags, IntPtr data,int bytes ) { + public void SetData( int level, LockFlags flags, IntPtr data, int bytes ) { LockedRectangle rect = LockRectangle( level, flags ); MemUtils.memcpy( data, rect.DataPointer, bytes ); UnlockRectangle( level );