The Labs \ Source Viewer \ SSCLI \ System.Text \ InternalEncoderBestFitFallback

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. // EncoderBestFitFallback.cs
  16. //
  17. // This is used internally to create best fit behavior as per the original windows best fit behavior.
  18. //
  19. namespace System.Text
  20. {
  21.     using System;
  22.     using System.Globalization;
  23.     using System.Text;
  24.     using System.Threading;
  25.    
  26.     [Serializable()]
  27.     internal class InternalEncoderBestFitFallback : EncoderFallback
  28.     {
  29.         // Our variables
  30.         internal Encoding encoding = null;
  31.         internal char[] arrayBestFit = null;
  32.        
  33.         internal InternalEncoderBestFitFallback(Encoding encoding)
  34.         {
  35.             // Need to load our replacement characters table.
  36.             this.encoding = encoding;
  37.             this.bIsMicrosoftBestFitFallback = true;
  38.         }
  39.        
  40.         public override EncoderFallbackBuffer CreateFallbackBuffer()
  41.         {
  42.             return new InternalEncoderBestFitFallbackBuffer(this);
  43.         }
  44.        
  45.         // Maximum number of characters that this instance of this fallback could return
  46.         public override int MaxCharCount {
  47.             get { return 1; }
  48.         }
  49.        
  50.         public override bool Equals(object value)
  51.         {
  52.             InternalEncoderBestFitFallback that = value as InternalEncoderBestFitFallback;
  53.             if (that != null) {
  54.                 return (this.encoding.CodePage == that.encoding.CodePage);
  55.             }
  56.             return (false);
  57.         }
  58.        
  59.         public override int GetHashCode()
  60.         {
  61.             return this.encoding.CodePage;
  62.         }
  63.     }
  64.    
  65.     internal sealed class InternalEncoderBestFitFallbackBuffer : EncoderFallbackBuffer
  66.     {
  67.         // Our variables
  68.         private char cBestFit = '\0';
  69.         private InternalEncoderBestFitFallback oFallback;
  70.         private int iCount = -1;
  71.         private int iSize;
  72.        
  73.         // Private object for locking instead of locking on a public type for SQL reliability work.
  74.         private static object s_InternalSyncObject;
  75.         private static object InternalSyncObject {
  76.             get {
  77.                 if (s_InternalSyncObject == null) {
  78.                     object o = new object();
  79.                     Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
  80.                 }
  81.                 return s_InternalSyncObject;
  82.             }
  83.         }
  84.        
  85.         // Constructor
  86.         public InternalEncoderBestFitFallbackBuffer(InternalEncoderBestFitFallback fallback)
  87.         {
  88.             this.oFallback = fallback;
  89.            
  90.             if (oFallback.arrayBestFit == null) {
  91.                 // Lock so we don't confuse ourselves.
  92.                 lock (InternalSyncObject) {
  93.                     // Double check before we do it again.
  94.                     if (oFallback.arrayBestFit == null)
  95.                         oFallback.arrayBestFit = fallback.encoding.GetBestFitUnicodeToBytesData();
  96.                 }
  97.             }
  98.         }
  99.        
  100.         // Fallback methods
  101.         public override bool Fallback(char charUnknown, int index)
  102.         {
  103.             // If we had a buffer already we're being recursive, throw, it's probably at the suspect
  104.             // character in our array.
  105.             // Shouldn't be able to get here for all of our code pages, table would have to be messed up.
  106.             BCLDebug.Assert(iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(non surrogate)] Fallback char " + ((int)cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback");
  107.            
  108.             iCount = iSize = 1;
  109.             cBestFit = TryBestFit(charUnknown);
  110.             if (cBestFit == '\0')
  111.                 cBestFit = '?';
  112.            
  113.             return true;
  114.         }
  115.        
  116.         public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
  117.         {
  118.             // Double check input surrogate pair
  119.             if (!Char.IsHighSurrogate(charUnknownHigh))
  120.                 throw new ArgumentOutOfRangeException("charUnknownHigh", Environment.GetResourceString("ArgumentOutOfRange_Range", 55296, 56319));
  121.            
  122.             if (!Char.IsLowSurrogate(charUnknownLow))
  123.                 throw new ArgumentOutOfRangeException("CharUnknownLow", Environment.GetResourceString("ArgumentOutOfRange_Range", 56320, 57343));
  124.            
  125.             // If we had a buffer already we're being recursive, throw, it's probably at the suspect
  126.             // character in our array. 0 is processing last character, < 0 is not falling back
  127.             // Shouldn't be able to get here, table would have to be messed up.
  128.             BCLDebug.Assert(iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(surrogate)] Fallback char " + ((int)cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback");
  129.            
  130.             // Go ahead and get our fallback, surrogates don't have best fit
  131.             cBestFit = '?';
  132.             iCount = iSize = 2;
  133.            
  134.             return true;
  135.         }
  136.        
  137.         // Default version is overridden in EncoderReplacementFallback.cs
  138.         public override char GetNextChar()
  139.         {
  140.             // Just return cReturn, which is 0 if there's no best fit for it.
  141.             return (iCount-- > 0) ? cBestFit : '\0';
  142.         }
  143.        
  144.         public override bool MovePrevious()
  145.         {
  146.             // Exception fallback doesn't have anywhere to back up to.
  147.             if (iCount >= 0)
  148.                 iCount++;
  149.            
  150.             // Return true if we could do it.
  151.             return (iCount >= 0 && iCount <= iSize);
  152.         }
  153.        
  154.        
  155.         // How many characters left to output?
  156.         public override int Remaining {
  157.             get { return (iCount > 0) ? iCount : 0; }
  158.         }
  159.        
  160.         // Clear the buffer
  161.         unsafe public override void Reset()
  162.         {
  163.             iCount = -1;
  164.             charStart = null;
  165.             bFallingBack = false;
  166.         }
  167.        
  168.         // private helper methods
  169.         private char TryBestFit(char cUnknown)
  170.         {
  171.             // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array
  172.             int lowBound = 0;
  173.             int highBound = oFallback.arrayBestFit.Length;
  174.             int index;
  175.            
  176.             // Binary search the array
  177.             int iDiff;
  178.             while ((iDiff = (highBound - lowBound)) > 6) {
  179.                 // Look in the middle, which is complicated by the fact that we have 2 #s for each pair,
  180.                 // so we don't want index to be odd because we want to be on word boundaries.
  181.                 // Also note that index can never == highBound (because diff is rounded down)
  182.                 index = ((iDiff / 2) + lowBound) & 65534;
  183.                
  184.                 char cTest = oFallback.arrayBestFit[index];
  185.                 if (cTest == cUnknown) {
  186.                     // We found it
  187.                     BCLDebug.Assert(index + 1 < oFallback.arrayBestFit.Length, "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
  188.                     return oFallback.arrayBestFit[index + 1];
  189.                 }
  190.                 else if (cTest < cUnknown) {
  191.                     // We weren't high enough
  192.                     lowBound = index;
  193.                 }
  194.                 else {
  195.                     // We weren't low enough
  196.                     highBound = index;
  197.                 }
  198.             }
  199.            
  200.             for (index = lowBound; index < highBound; index += 2) {
  201.                 if (oFallback.arrayBestFit[index] == cUnknown) {
  202.                     // We found it
  203.                     BCLDebug.Assert(index + 1 < oFallback.arrayBestFit.Length, "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
  204.                     return oFallback.arrayBestFit[index + 1];
  205.                 }
  206.             }
  207.            
  208.             // Char wasn't in our table
  209.             return '\0';
  210.         }
  211.     }
  212. }

Developer Fusion