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

  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. namespace System.Text
  16. {
  17.     using System;
  18.     using System.Text;
  19.     using System.Threading;
  20.     using System.Globalization;
  21.     using System.Runtime.Serialization;
  22.     using System.Security.Permissions;
  23.    
  24.     // SBCSCodePageEncoding
  25.     [Serializable()]
  26.     internal class SBCSCodePageEncoding : BaseCodePageEncoding, ISerializable
  27.     {
  28.         // Pointers to our memory section parts
  29.         [NonSerialized()]
  30.         unsafe char* mapBytesToUnicode = null;
  31.         // char 256
  32.         [NonSerialized()]
  33.         unsafe byte* mapUnicodeToBytes = null;
  34.         // byte 65536
  35.         [NonSerialized()]
  36.         unsafe int* mapCodePageCached = null;
  37.         // to remember which CP is cached
  38.         const char UNKNOWN_CHAR = (char)65533;
  39.         [NonSerialized()]
  40.         char[] arrayByteBestFit = null;
  41.        
  42.         // byteUnknown is used for default fallback only
  43.         [NonSerialized()]
  44.         byte byteUnknown;
  45.         [NonSerialized()]
  46.         char charUnknown;
  47.        
  48.         public SBCSCodePageEncoding(int codePage) : this(codePage, codePage)
  49.         {
  50.         }
  51.        
  52.         internal SBCSCodePageEncoding(int codePage, int dataCodePage) : base(codePage, dataCodePage)
  53.         {
  54.         }
  55.        
  56.         // Constructor called by serialization.
  57.         // Note: We use the base GetObjectData however
  58.         internal SBCSCodePageEncoding(SerializationInfo info, StreamingContext context) : base(0)
  59.         {
  60.             // Actually this can't ever get called, CodePageEncoding is our proxy
  61.             BCLDebug.Assert(false, "Didn't expect to make it to SBCSCodePageEncoding serialization constructor");
  62.             throw new ArgumentNullException("this");
  63.         }
  64.        
  65.         // We have a managed code page entry, so load our tables
  66.         // SBCS data section looks like:
  67.         //
  68.         // char[256] - what each byte maps to in unicode. No support for surrogates. 0 is undefined code point
  69.         // (except 0 for byte 0 is expected to be a real 0)
  70.         //
  71.         // byte/char* - Data for best fit (unicode->bytes), again no best fit for Unicode
  72.         // 1st WORD is Unicode // of 1st character position
  73.         // Next bytes are best fit byte for that position. Position is incremented after each byte
  74.         // byte < 0x20 means skip the next n positions. (Where n is the byte #)
  75.         // byte == 1 means that next word is another unicode code point #
  76.         // byte == 0 is unknown. (doesn't override initial WCHAR[256] table!
  77.         unsafe protected override void LoadManagedCodePage()
  78.         {
  79.             // Should be loading OUR code page
  80.             BCLDebug.Assert(pCodePage->CodePage == this.dataTableCodePage, "[SBCSCodePageEncoding.LoadManagedCodePage]Expected to load data table code page");
  81.            
  82.             // Make sure we're really a 1 byte code page
  83.             if (pCodePage->ByteCount != 1)
  84.                 throw new NotSupportedException(Environment.GetResourceString("NotSupported_NoCodepageData", CodePage));
  85.            
  86.             // Remember our unknown bytes & chars
  87.             byteUnknown = (byte)pCodePage->ByteReplace;
  88.             charUnknown = pCodePage->UnicodeReplace;
  89.            
  90.             // Get our mapped section 65536 bytes for unicode->bytes, 256 * 2 bytes for bytes->unicode
  91.             // Plus 4 byte to remember CP # when done loading it. (Don't want to get IA64 or anything out of alignment)
  92.             byte* pMemorySection = GetSharedMemory(65536 * 1 + 256 * 2 + 4 + iExtraBytes);
  93.            
  94.             mapBytesToUnicode = (char*)pMemorySection;
  95.             mapUnicodeToBytes = (byte*)(pMemorySection + 256 * 2);
  96.             mapCodePageCached = (int*)(pMemorySection + 256 * 2 + 65536 * 1 + iExtraBytes);
  97.            
  98.             // If its cached (& filled in) we don't have to do anything else
  99.             if (*mapCodePageCached != 0) {
  100.                 BCLDebug.Assert(*mapCodePageCached == this.dataTableCodePage, "[DBCSCodePageEncoding.LoadManagedCodePage]Expected mapped section cached page to be same as data table code page. Cached : " + *mapCodePageCached + " Expected:" + this.dataTableCodePage);
  101.                
  102.                 if (*mapCodePageCached != this.dataTableCodePage)
  103.                     throw new OutOfMemoryException(Environment.GetResourceString("Arg_OutOfMemoryException"));
  104.                
  105.                 // If its cached (& filled in) we don't have to do anything else
  106.                 return;
  107.             }
  108.            
  109.             // Need to read our data file and fill in our section.
  110.             // WARNING: Multiple code pieces could do this at once (so we don't have to lock machine-wide)
  111.             // so be careful here. Only stick legal values in here, don't stick temporary values.
  112.            
  113.             // Read our data file and set mapBytesToUnicode and mapUnicodeToBytes appropriately
  114.             // First table is just all 256 mappings
  115.             char* pTemp = (char*)&(pCodePage->FirstDataWord);
  116.             for (int b = 0; b < 256; b++) {
  117.                 if (pTemp[b] != 0 || b == 0) {
  118.                     mapBytesToUnicode[b] = pTemp[b];
  119.                    
  120.                     if (pTemp[b] != UNKNOWN_CHAR)
  121.                         mapUnicodeToBytes[pTemp[b]] = (byte)b;
  122.                 }
  123.                 else {
  124.                     mapBytesToUnicode[b] = UNKNOWN_CHAR;
  125.                 }
  126.             }
  127.            
  128.             // We're done with our mapped section, set our flag so others don't have to rebuild table.
  129.             *mapCodePageCached = this.dataTableCodePage;
  130.         }
  131.        
  132.         // Private object for locking instead of locking on a public type for SQL reliability work.
  133.         private static object s_InternalSyncObject;
  134.         private static object InternalSyncObject {
  135.             get {
  136.                 if (s_InternalSyncObject == null) {
  137.                     object o = new object();
  138.                     Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
  139.                 }
  140.                 return s_InternalSyncObject;
  141.             }
  142.         }
  143.        
  144.         // Read in our best fit table
  145.         unsafe protected override void ReadBestFitTable()
  146.         {
  147.             // Lock so we don't confuse ourselves.
  148.             lock (InternalSyncObject) {
  149.                 // If we got a best fit array already, then don't do this
  150.                 if (arrayUnicodeBestFit == null) {
  151.                     //
  152.                     // Read in Best Fit table.
  153.                     //
  154.                    
  155.                     // First check the SBCS->Unicode best fit table, which starts right after the
  156.                     // 256 word data table. This table looks like word, word where 1st word is byte and 2nd
  157.                     // word is replacement for that word. It ends when byte == 0.
  158.                     byte* pData = (byte*)&(pCodePage->FirstDataWord);
  159.                     pData += 512;
  160.                    
  161.                     // Need new best fit array
  162.                     char[] arrayTemp = new char[256];
  163.                     for (int i = 0; i < 256; i++)
  164.                         arrayTemp[i] = mapBytesToUnicode[i];
  165.                    
  166.                     // See if our words are zero
  167.                     ushort byteTemp;
  168.                     while ((byteTemp = *((ushort*)pData)) != 0) {
  169.                        
  170.                         BCLDebug.Assert(arrayTemp[byteTemp] == UNKNOWN_CHAR, String.Format(CultureInfo.InvariantCulture, "[SBCSCodePageEncoding::ReadBestFitTable] Expected unallocated byte (not 0x{2:X2}) for best fit byte at 0x{0:X2} for code page {1}", byteTemp, CodePage, (int)arrayTemp[byteTemp]));
  171.                         pData += 2;
  172.                        
  173.                         arrayTemp[byteTemp] = *((char*)pData);
  174.                         pData += 2;
  175.                     }
  176.                    
  177.                     // Remember our new array
  178.                     arrayByteBestFit = arrayTemp;
  179.                    
  180.                     // It was on 0, it needs to be on next byte
  181.                     pData += 2;
  182.                     byte* pUnicodeToSBCS = pData;
  183.                    
  184.                     // Now count our characters from our Unicode->SBCS best fit table,
  185.                     // which is right after our 256 byte data table
  186.                     int iBestFitCount = 0;
  187.                    
  188.                     // Now do the UnicodeToBytes Best Fit mapping (this is the one we normally think of when we say "best fit")
  189.                     // pData should be pointing at the first data point for Bytes->Unicode table
  190.                     int unicodePosition = *((ushort*)pData);
  191.                     pData += 2;
  192.                    
  193.                     while (unicodePosition < 65536) {
  194.                         // Get the next byte
  195.                         byte input = *pData;
  196.                         pData++;
  197.                        
  198.                         // build our table:
  199.                         if (input == 1) {
  200.                             // Use next 2 bytes as our byte position
  201.                             unicodePosition = *((ushort*)pData);
  202.                             pData += 2;
  203.                         }
  204.                         else if (input < 32 && input > 0 && input != 30) {
  205.                             // Advance input characters
  206.                             unicodePosition += input;
  207.                         }
  208.                         else {
  209.                             // Use this character if it isn't zero
  210.                             if (input > 0)
  211.                                 iBestFitCount++;
  212.                            
  213.                             // skip this unicode position in any case
  214.                             unicodePosition++;
  215.                         }
  216.                     }
  217.                    
  218.                     // Make an array for our best fit data
  219.                     arrayTemp = new char[iBestFitCount * 2];
  220.                    
  221.                     // Now actually read in the data
  222.                     // reset pData should be pointing at the first data point for Bytes->Unicode table
  223.                     pData = pUnicodeToSBCS;
  224.                     unicodePosition = *((ushort*)pData);
  225.                     pData += 2;
  226.                     iBestFitCount = 0;
  227.                    
  228.                     while (unicodePosition < 65536) {
  229.                         // Get the next byte
  230.                         byte input = *pData;
  231.                         pData++;
  232.                        
  233.                         // build our table:
  234.                         if (input == 1) {
  235.                             // Use next 2 bytes as our byte position
  236.                             unicodePosition = *((ushort*)pData);
  237.                             pData += 2;
  238.                         }
  239.                         else if (input < 32 && input > 0 && input != 30) {
  240.                             // Advance input characters
  241.                             unicodePosition += input;
  242.                         }
  243.                         else {
  244.                             // Check for escape for glyph range
  245.                             if (input == 30) {
  246.                                 // Its an escape, so just read next byte directly
  247.                                 input = *pData;
  248.                                 pData++;
  249.                             }
  250.                            
  251.                             // 0 means just skip me
  252.                             if (input > 0) {
  253.                                 // Use this character
  254.                                 arrayTemp[iBestFitCount++] = (char)unicodePosition;
  255.                                 // Have to map it to Unicode because best fit will need unicode value of best fit char.
  256.                                 arrayTemp[iBestFitCount++] = mapBytesToUnicode[input];
  257.                                
  258.                                 BCLDebug.Assert(arrayTemp[iBestFitCount - 1] != (char)0, String.Format(CultureInfo.InvariantCulture, "[SBCSCodePageEncoding.ReadBestFitTable] No valid Unicode value {0:X4} for round trip bytes {1:X4}, encoding {2}", (int)mapBytesToUnicode[input], (int)input, CodePage));
  259.                             }
  260.                             unicodePosition++;
  261.                         }
  262.                     }
  263.                    
  264.                     // Remember it
  265.                     arrayUnicodeBestFit = arrayTemp;
  266.                 }
  267.             }
  268.         }
  269.        
  270.         // GetByteCount
  271.         // Note: We start by assuming that the output will be the same as count. Having
  272.         // an encoder or fallback may change that assumption
  273.         unsafe internal override int GetByteCount(char* chars, int count, EncoderNLS encoder)
  274.         {
  275.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  276.             BCLDebug.Assert(count >= 0, "[SBCSCodePageEncoding.GetByteCount]count is negative");
  277.             BCLDebug.Assert(chars != null, "[SBCSCodePageEncoding.GetByteCount]chars is null");
  278.            
  279.             // Assert because we shouldn't be able to have a null encoder.
  280.             BCLDebug.Assert(encoderFallback != null, "[SBCSCodePageEncoding.GetByteCount]Attempting to use null fallback");
  281.            
  282.             CheckMemorySection();
  283.            
  284.             // Need to test fallback
  285.             EncoderReplacementFallback fallback = null;
  286.            
  287.             // Get any left over characters
  288.             char charLeftOver = (char)0;
  289.             if (encoder != null) {
  290.                 charLeftOver = encoder.charLeftOver;
  291.                 BCLDebug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), "[SBCSCodePageEncoding.GetByteCount]leftover character should be high surrogate");
  292.                 fallback = encoder.Fallback as EncoderReplacementFallback;
  293.                
  294.                 // Verify that we have no fallbackbuffer, actually for SBCS this is always empty, so just assert
  295.                 BCLDebug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer || encoder.FallbackBuffer.Remaining == 0, "[SBCSCodePageEncoding.GetByteCount]Expected empty fallback buffer at start");
  296.             }
  297.             else {
  298.                 // If we aren't using default fallback then we may have a complicated count.
  299.                 fallback = this.EncoderFallback as EncoderReplacementFallback;
  300.             }
  301.            
  302.             if ((fallback != null && fallback.MaxCharCount == 1))/* || bIsBestFit*/ {
  303.                 // Replacement fallback encodes surrogate pairs as two ?? (or two whatever), so return size is always
  304.                 // same as input size.
  305.                 // Note that no existing SBCS code pages map code points to supplimentary characters, so this is easy.
  306.                
  307.                 // We could however have 1 extra byte if the last call had an encoder and a funky fallback and
  308.                 // if we don't use the funky fallback this time.
  309.                
  310.                 // Do we have an extra char left over from last time?
  311.                 if (charLeftOver > 0)
  312.                     count++;
  313.                
  314.                 return (count);
  315.             }
  316.            
  317.             // It had a funky fallback, so its more complicated
  318.             // Need buffer maybe later
  319.             EncoderFallbackBuffer fallbackBuffer = null;
  320.            
  321.             // prepare our end
  322.             int byteCount = 0;
  323.             char* charEnd = chars + count;
  324.            
  325.             // We may have a left over character from last time, try and process it.
  326.             if (charLeftOver > 0) {
  327.                 // Since left over char was a surrogate, it'll have to be fallen back.
  328.                 // Get Fallback
  329.                 BCLDebug.Assert(encoder != null, "[SBCSCodePageEncoding.GetByteCount]Expect to have encoder if we have a charLeftOver");
  330.                 fallbackBuffer = encoder.FallbackBuffer;
  331.                 fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false);
  332.                
  333.                 // This will fallback a pair if *chars is a low surrogate
  334.                 fallbackBuffer.InternalFallback(charLeftOver, ref chars);
  335.             }
  336.            
  337.             // Now we may have fallback char[] already from the encoder
  338.            
  339.             // Go ahead and do it, including the fallback.
  340.             char ch;
  341.             while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || chars < charEnd) {
  342.                 // First unwind any fallback
  343.                 if (ch == 0) {
  344.                     // No fallback, just get next char
  345.                     ch = *chars;
  346.                     chars++;
  347.                 }
  348.                
  349.                 // get byte for this char
  350.                 byte bTemp = mapUnicodeToBytes[ch];
  351.                
  352.                 // Check for fallback, this'll catch surrogate pairs too.
  353.                 if (bTemp == 0 && ch != (char)0) {
  354.                     if (fallbackBuffer == null) {
  355.                         // Create & init fallback buffer
  356.                         if (encoder == null)
  357.                             fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
  358.                         else
  359.                             fallbackBuffer = encoder.FallbackBuffer;
  360.                        
  361.                         // chars has moved so we need to remember figure it out so Exception fallback
  362.                         // index will be correct
  363.                         fallbackBuffer.InternalInitialize(charEnd - count, charEnd, encoder, false);
  364.                     }
  365.                    
  366.                     // Get Fallback
  367.                     fallbackBuffer.InternalFallback(ch, ref chars);
  368.                     continue;
  369.                 }
  370.                
  371.                 // We'll use this one
  372.                 byteCount++;
  373.             }
  374.            
  375.             BCLDebug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[SBCSEncoding.GetByteCount]Expected Empty fallback buffer at end");
  376.            
  377.             return (int)byteCount;
  378.         }
  379.        
  380.         unsafe internal override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder)
  381.         {
  382.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  383.             BCLDebug.Assert(bytes != null, "[SBCSCodePageEncoding.GetBytes]bytes is null");
  384.             BCLDebug.Assert(byteCount >= 0, "[SBCSCodePageEncoding.GetBytes]byteCount is negative");
  385.             BCLDebug.Assert(chars != null, "[SBCSCodePageEncoding.GetBytes]chars is null");
  386.             BCLDebug.Assert(charCount >= 0, "[SBCSCodePageEncoding.GetBytes]charCount is negative");
  387.            
  388.             // Assert because we shouldn't be able to have a null encoder.
  389.             BCLDebug.Assert(encoderFallback != null, "[SBCSCodePageEncoding.GetBytes]Attempting to use null encoder fallback");
  390.            
  391.             CheckMemorySection();
  392.            
  393.             // Need to test fallback
  394.             EncoderReplacementFallback fallback = null;
  395.            
  396.             // Get any left over characters
  397.             char charLeftOver = (char)0;
  398.             if (encoder != null) {
  399.                 charLeftOver = encoder.charLeftOver;
  400.                 BCLDebug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), "[SBCSCodePageEncoding.GetBytes]leftover character should be high surrogate");
  401.                 fallback = encoder.Fallback as EncoderReplacementFallback;
  402.                
  403.                 // Verify that we have no fallbackbuffer, for SBCS its always empty, so just assert
  404.                 BCLDebug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer || encoder.FallbackBuffer.Remaining == 0, "[SBCSCodePageEncoding.GetBytes]Expected empty fallback buffer at start");
  405.                 // if (encoder.m_throwOnOverflow && encoder.InternalHasFallbackBuffer &&
  406.                 // encoder.FallbackBuffer.Remaining > 0)
  407.                 // throw new ArgumentException(Environment.GetResourceString("Argument_EncoderFallbackNotEmpty",
  408.                 // this.EncodingName, encoder.Fallback.GetType()));
  409.             }
  410.             else {
  411.                 // If we aren't using default fallback then we may have a complicated count.
  412.                 fallback = this.EncoderFallback as EncoderReplacementFallback;
  413.             }
  414.            
  415.             // prepare our end
  416.             char* charEnd = chars + charCount;
  417.             byte* byteStart = bytes;
  418.             char* charStart = chars;
  419.            
  420.             // See if we do the fast default or slightly slower fallback
  421.             if (fallback != null && fallback.MaxCharCount == 1) {
  422.                 // Make sure our fallback character is valid first
  423.                 byte bReplacement = mapUnicodeToBytes[fallback.DefaultString[0]];
  424.                
  425.                 // Check for replacements in range, otherwise fall back to slow version.
  426.                 if (bReplacement != 0) {
  427.                     // We should have exactly as many output bytes as input bytes, unless there's a left
  428.                     // over character, in which case we may need one more.
  429.                    
  430.                     // If we had a left over character will have to add a ? (This happens if they had a funky
  431.                     // fallback last time, but not this time.) (We can't spit any out though
  432.                     // because with fallback encoder each surrogate is treated as a seperate code point)
  433.                     if (charLeftOver > 0) {
  434.                         // Have to have room
  435.                         // Throw even if doing no throw version because this is just 1 char,
  436.                         // so buffer will never be big enough
  437.                         if (byteCount == 0)
  438.                             ThrowBytesOverflow(encoder, true);
  439.                        
  440.                         // This'll make sure we still have more room and also make sure our return value is correct.
  441.                         *(bytes++) = bReplacement;
  442.                         byteCount--;
  443.                         // We used one of the ones we were counting.
  444.                     }
  445.                    
  446.                     // This keeps us from overrunning our output buffer
  447.                     if (byteCount < charCount) {
  448.                         // Throw or make buffer smaller?
  449.                         ThrowBytesOverflow(encoder, byteCount < 1);
  450.                        
  451.                         // Just use what we can
  452.                         charEnd = chars + byteCount;
  453.                     }
  454.                    
  455.                     // Simple way
  456.                     while (chars < charEnd) {
  457.                         char ch2 = *chars;
  458.                         chars++;
  459.                        
  460.                         byte bTemp = mapUnicodeToBytes[ch2];
  461.                        
  462.                         // Check for fallback
  463.                         if (bTemp == 0 && ch2 != (char)0)
  464.                             *bytes = bReplacement;
  465.                         else
  466.                             *bytes = bTemp;
  467.                        
  468.                         bytes++;
  469.                     }
  470.                    
  471.                     // Clear encoder
  472.                     if (encoder != null) {
  473.                         encoder.charLeftOver = (char)0;
  474.                         encoder.m_charsUsed = (int)(chars - charStart);
  475.                     }
  476.                     return (int)(bytes - byteStart);
  477.                 }
  478.             }
  479.            
  480.             // Slower version, have to do real fallback.
  481.            
  482.             // For fallback we may need a fallback buffer, we know we aren't default fallback
  483.             EncoderFallbackBuffer fallbackBuffer = null;
  484.            
  485.             // prepare our end
  486.             byte* byteEnd = bytes + byteCount;
  487.            
  488.             // We may have a left over character from last time, try and process it.
  489.             if (charLeftOver > 0) {
  490.                 // Since left over char was a surrogate, it'll have to be fallen back.
  491.                 // Get Fallback
  492.                 BCLDebug.Assert(encoder != null, "[SBCSCodePageEncoding.GetBytes]Expect to have encoder if we have a charLeftOver");
  493.                 fallbackBuffer = encoder.FallbackBuffer;
  494.                 fallbackBuffer.InternalInitialize(chars, charEnd, encoder, true);
  495.                
  496.                 // This will fallback a pair if *chars is a low surrogate
  497.                 fallbackBuffer.InternalFallback(charLeftOver, ref chars);
  498.                 if (fallbackBuffer.Remaining > byteEnd - bytes) {
  499.                     // Throw it, if we don't have enough for this we never will
  500.                     ThrowBytesOverflow(encoder, true);
  501.                 }
  502.             }
  503.            
  504.             // Now we may have fallback char[] already from the encoder fallback above
  505.            
  506.             // Go ahead and do it, including the fallback.
  507.             char ch;
  508.             while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || chars < charEnd) {
  509.                 // First unwind any fallback
  510.                 if (ch == 0) {
  511.                     // No fallback, just get next char
  512.                     ch = *chars;
  513.                     chars++;
  514.                 }
  515.                
  516.                 // get byte for this char
  517.                 byte bTemp = mapUnicodeToBytes[ch];
  518.                
  519.                 // Check for fallback, this'll catch surrogate pairs too.
  520.                 if (bTemp == 0 && ch != (char)0) {
  521.                     // Get Fallback
  522.                     if (fallbackBuffer == null) {
  523.                         // Create & init fallback buffer
  524.                         if (encoder == null)
  525.                             fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
  526.                         else
  527.                             fallbackBuffer = encoder.FallbackBuffer;
  528.                         // chars has moved so we need to remember figure it out so Exception fallback
  529.                         // index will be correct
  530.                         fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, true);
  531.                     }
  532.                    
  533.                     // Make sure we have enough room. Each fallback char will be 1 output char
  534.                     // (or recursion exception will be thrown)
  535.                     fallbackBuffer.InternalFallback(ch, ref chars);
  536.                     if (fallbackBuffer.Remaining > byteEnd - bytes) {
  537.                         // Didn't use this char, reset it
  538.                         BCLDebug.Assert(chars > charStart, "[SBCSCodePageEncoding.GetBytes]Expected chars to have advanced (fallback)");
  539.                         chars--;
  540.                         fallbackBuffer.InternalReset();
  541.                        
  542.                         // Throw it & drop this data
  543.                         ThrowBytesOverflow(encoder, chars == charStart);
  544.                         break;
  545.                     }
  546.                     continue;
  547.                 }
  548.                
  549.                 // We'll use this one
  550.                 // Bounds check
  551.                 if (bytes >= byteEnd) {
  552.                     // didn't use this char, we'll throw or use buffer
  553.                     BCLDebug.Assert(fallbackBuffer == null || fallbackBuffer.bFallingBack == false, "[SBCSCodePageEncoding.GetBytes]Expected to NOT be falling back");
  554.                     if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false) {
  555.                         BCLDebug.Assert(chars > charStart, "[SBCSCodePageEncoding.GetBytes]Expected chars to have advanced (normal)");
  556.                         chars--;
  557.                         // don't use last char
  558.                     }
  559.                     ThrowBytesOverflow(encoder, chars == charStart);
  560.                     // throw ?
  561.                     break;
  562.                     // don't throw, stop
  563.                 }
  564.                
  565.                 // Go ahead and add it
  566.                 *bytes = bTemp;
  567.                 bytes++;
  568.             }
  569.            
  570.             // encoder stuff if we have one
  571.             if (encoder != null) {
  572.                 // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases
  573.                 if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder)
  574.                     // Clear it in case of MustFlush
  575.                     encoder.charLeftOver = (char)0;
  576.                
  577.                 // Set our chars used count
  578.                 encoder.m_charsUsed = (int)(chars - charStart);
  579.             }
  580.            
  581.             // Expect Empty fallback buffer for SBCS
  582.             BCLDebug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[SBCSEncoding.GetBytes]Expected Empty fallback buffer at end");
  583.            
  584.             return (int)(bytes - byteStart);
  585.         }
  586.        
  587.         // This is internal and called by something else,
  588.         unsafe internal override int GetCharCount(byte* bytes, int count, DecoderNLS decoder)
  589.         {
  590.             // Just assert, we're called internally so these should be safe, checked already
  591.             BCLDebug.Assert(bytes != null, "[SBCSCodePageEncoding.GetCharCount]bytes is null");
  592.             BCLDebug.Assert(count >= 0, "[SBCSCodePageEncoding.GetCharCount]byteCount is negative");
  593.            
  594.             CheckMemorySection();
  595.            
  596.             // See if we have best fit
  597.             bool bUseBestFit = false;
  598.            
  599.             // Only need decoder fallback buffer if not using default replacement fallback or best fit fallback.
  600.             DecoderReplacementFallback fallback = null;
  601.            
  602.             if (decoder == null) {
  603.                 fallback = this.DecoderFallback as DecoderReplacementFallback;
  604.                 bUseBestFit = this.DecoderFallback.IsMicrosoftBestFitFallback;
  605.             }
  606.             else {
  607.                 fallback = decoder.Fallback as DecoderReplacementFallback;
  608.                 bUseBestFit = decoder.Fallback.IsMicrosoftBestFitFallback;
  609.                 BCLDebug.Assert(!decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[SBCSCodePageEncoding.GetChars]Expected empty fallback buffer at start");
  610.             }
  611.            
  612.             if (bUseBestFit || (fallback != null && fallback.MaxCharCount == 1)) {
  613.                 // Just return length, SBCS stay the same length because they don't map to surrogate
  614.                 // pairs and we don't have a decoder fallback.
  615.                 return count;
  616.             }
  617.            
  618.             // Might need one of these later
  619.             DecoderFallbackBuffer fallbackBuffer = null;
  620.            
  621.             // Have to do it the hard way.
  622.             // Assume charCount will be == count
  623.             int charCount = count;
  624.             byte[] byteBuffer = new byte[1];
  625.            
  626.             // Do it our fast way
  627.             byte* byteEnd = bytes + count;
  628.            
  629.             // Quick loop
  630.             while (bytes < byteEnd) {
  631.                 // Faster if don't use *bytes++;
  632.                 char c;
  633.                 c = mapBytesToUnicode[*bytes];
  634.                 bytes++;
  635.                
  636.                 // If unknown we have to do fallback count
  637.                 if (c == UNKNOWN_CHAR) {
  638.                     // Must have a fallback buffer
  639.                     if (fallbackBuffer == null) {
  640.                         // Need to adjust count so we get real start
  641.                         if (decoder == null)
  642.                             fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
  643.                         else
  644.                             fallbackBuffer = decoder.FallbackBuffer;
  645.                         fallbackBuffer.InternalInitialize(byteEnd - count, null);
  646.                     }
  647.                    
  648.                     // Use fallback buffer
  649.                     byteBuffer[0] = *(bytes - 1);
  650.                     charCount--;
  651.                     // We'd already reserved one for *(bytes-1)
  652.                     charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
  653.                 }
  654.             }
  655.            
  656.             // Fallback buffer must be empty
  657.             BCLDebug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[SBCSEncoding.GetCharCount]Expected Empty fallback buffer at end");
  658.            
  659.             // Converted sequence is same length as input
  660.             return charCount;
  661.         }
  662.        
  663.         unsafe internal override int GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS decoder)
  664.         {
  665.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  666.             BCLDebug.Assert(bytes != null, "[SBCSCodePageEncoding.GetChars]bytes is null");
  667.             BCLDebug.Assert(byteCount >= 0, "[SBCSCodePageEncoding.GetChars]byteCount is negative");
  668.             BCLDebug.Assert(chars != null, "[SBCSCodePageEncoding.GetChars]chars is null");
  669.             BCLDebug.Assert(charCount >= 0, "[SBCSCodePageEncoding.GetChars]charCount is negative");
  670.            
  671.             CheckMemorySection();
  672.            
  673.             // See if we have best fit
  674.             bool bUseBestFit = false;
  675.            
  676.             // Do it fast way if using ? replacement or best fit fallbacks
  677.             byte* byteEnd = bytes + byteCount;
  678.             byte* byteStart = bytes;
  679.             char* charStart = chars;
  680.            
  681.             // Only need decoder fallback buffer if not using default replacement fallback or best fit fallback.
  682.             DecoderReplacementFallback fallback = null;
  683.            
  684.             if (decoder == null) {
  685.                 fallback = this.DecoderFallback as DecoderReplacementFallback;
  686.                 bUseBestFit = this.DecoderFallback.IsMicrosoftBestFitFallback;
  687.             }
  688.             else {
  689.                 fallback = decoder.Fallback as DecoderReplacementFallback;
  690.                 bUseBestFit = decoder.Fallback.IsMicrosoftBestFitFallback;
  691.                 BCLDebug.Assert(!decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[SBCSCodePageEncoding.GetChars]Expected empty fallback buffer at start");
  692.             }
  693.            
  694.             if (bUseBestFit || (fallback != null && fallback.MaxCharCount == 1)) {
  695.                 // Try it the fast way
  696.                 char replacementChar;
  697.                 if (fallback == null)
  698.                     replacementChar = '?';
  699.                 else
  700.                     // Best fit alwasy has ? for fallback for SBCS
  701.                     replacementChar = fallback.DefaultString[0];
  702.                
  703.                 // Need byteCount chars, otherwise too small buffer
  704.                 if (charCount < byteCount) {
  705.                     // Need at least 1 output byte, throw if must throw
  706.                     ThrowCharsOverflow(decoder, charCount < 1);
  707.                    
  708.                     // Not throwing, use what we can
  709.                     byteEnd = bytes + charCount;
  710.                 }
  711.                
  712.                 // Quick loop, just do '?' replacement because we don't have fallbacks for decodings.
  713.                 while (bytes < byteEnd) {
  714.                     char c;
  715.                     if (bUseBestFit) {
  716.                         if (arrayByteBestFit == null) {
  717.                             ReadBestFitTable();
  718.                         }
  719.                         c = arrayByteBestFit[*bytes];
  720.                     }
  721.                     else
  722.                         c = mapBytesToUnicode[*bytes];
  723.                     bytes++;
  724.                    
  725.                     if (c == UNKNOWN_CHAR)
  726.                         *chars = replacementChar;
  727.                     else
  728.                         // This is an invalid byte in the ASCII encoding.
  729.                         *chars = c;
  730.                     chars++;
  731.                 }
  732.                
  733.                 // bytes & chars used are the same
  734.                 if (decoder != null)
  735.                     decoder.m_bytesUsed = (int)(bytes - byteStart);
  736.                 return (int)(chars - charStart);
  737.             }
  738.            
  739.             // Slower way's going to need a fallback buffer
  740.             DecoderFallbackBuffer fallbackBuffer = null;
  741.             byte[] byteBuffer = new byte[1];
  742.             char* charEnd = chars + charCount;
  743.            
  744.             // Not quite so fast loop
  745.             while (bytes < byteEnd) {
  746.                 // Faster if don't use *bytes++;
  747.                 char c = mapBytesToUnicode[*bytes];
  748.                 bytes++;
  749.                
  750.                 // See if it was unknown
  751.                 if (c == UNKNOWN_CHAR) {
  752.                     // Make sure we have a fallback buffer
  753.                     if (fallbackBuffer == null) {
  754.                         if (decoder == null)
  755.                             fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
  756.                         else
  757.                             fallbackBuffer = decoder.FallbackBuffer;
  758.                         fallbackBuffer.InternalInitialize(byteEnd - byteCount, charEnd);
  759.                     }
  760.                    
  761.                     // Use fallback buffer
  762.                     BCLDebug.Assert(bytes > byteStart, "[SBCSCodePageEncoding.GetChars]Expected bytes to have advanced already (unknown byte)");
  763.                     byteBuffer[0] = *(bytes - 1);
  764.                     // Fallback adds fallback to chars, but doesn't increment chars unless the whole thing fits.
  765.                     if (!fallbackBuffer.InternalFallback(byteBuffer, bytes, ref chars)) {
  766.                         // May or may not throw, but we didn't get this byte
  767.                         bytes--;
  768.                         // unused byte
  769.                         fallbackBuffer.InternalReset();
  770.                         // Didn't fall this back
  771.                         ThrowCharsOverflow(decoder, bytes == byteStart);
  772.                         // throw?
  773.                         break;
  774.                         // don't throw, but stop loop
  775.                     }
  776.                 }
  777.                 else {
  778.                     // Make sure we have buffer space
  779.                     if (chars >= charEnd) {
  780.                         BCLDebug.Assert(bytes > byteStart, "[SBCSCodePageEncoding.GetChars]Expected bytes to have advanced already (known byte)");
  781.                         bytes--;
  782.                         // unused byte
  783.                         ThrowCharsOverflow(decoder, bytes == byteStart);
  784.                         // throw?
  785.                         break;
  786.                         // don't throw, but stop loop
  787.                     }
  788.                    
  789.                     *(chars) = c;
  790.                     chars++;
  791.                 }
  792.             }
  793.            
  794.             // Might have had decoder fallback stuff.
  795.             if (decoder != null)
  796.                 decoder.m_bytesUsed = (int)(bytes - byteStart);
  797.            
  798.             // Expect Empty fallback buffer for GetChars
  799.             BCLDebug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[SBCSEncoding.GetChars]Expected Empty fallback buffer at end");
  800.            
  801.             return (int)(chars - charStart);
  802.         }
  803.        
  804.         public override int GetMaxByteCount(int charCount)
  805.         {
  806.             if (charCount < 0)
  807.                 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  808.            
  809.             // Characters would be # of characters + 1 in case high surrogate is ? * max fallback
  810.             long byteCount = (long)charCount + 1;
  811.            
  812.             if (EncoderFallback.MaxCharCount > 1)
  813.                 byteCount *= EncoderFallback.MaxCharCount;
  814.            
  815.             // 1 to 1 for most characters. Only surrogates with fallbacks have less.
  816.            
  817.             if (byteCount > 2147483647)
  818.                 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_GetByteCountOverflow"));
  819.             return (int)byteCount;
  820.         }
  821.        
  822.         public override int GetMaxCharCount(int byteCount)
  823.         {
  824.             if (byteCount < 0)
  825.                 throw new ArgumentOutOfRangeException("byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  826.            
  827.             // Just return length, SBCS stay the same length because they don't map to surrogate
  828.             long charCount = (long)byteCount;
  829.            
  830.             // 1 to 1 for most characters. Only surrogates with fallbacks have less, unknown fallbacks could be longer.
  831.             if (DecoderFallback.MaxCharCount > 1)
  832.                 charCount *= DecoderFallback.MaxCharCount;
  833.            
  834.             if (charCount > 2147483647)
  835.                 throw new ArgumentOutOfRangeException("byteCount", Environment.GetResourceString("ArgumentOutOfRange_GetCharCountOverflow"));
  836.            
  837.             return (int)charCount;
  838.         }
  839.        
  840.         // True if and only if the encoding only uses single byte code points. (Ie, ASCII, 1252, etc)
  841.         public override bool IsSingleByte {
  842.             get { return true; }
  843.         }
  844.        
  845.         [System.Runtime.InteropServices.ComVisible(false)]
  846.         public override bool IsAlwaysNormalized(NormalizationForm form)
  847.         {
  848.             if (form == NormalizationForm.FormC) {
  849.                 // Form C is only true for some code pages. They have to have all 256 code points assigned
  850.                 // and not map to unassigned or combinable code points.
  851.                 switch (CodePage) {
  852.                     case 1252:
  853.                     case 1250:
  854.                     case 1251:
  855.                     case 1254:
  856.                     case 1256:
  857.                     case 28591:
  858.                     case 437:
  859.                     case 737:
  860.                     case 775:
  861.                     case 850:
  862.                     case 852:
  863.                     case 855:
  864.                     case 858:
  865.                     case 860:
  866.                     case 861:
  867.                     case 862:
  868.                     case 863:
  869.                     case 865:
  870.                     case 866:
  871.                     case 869:
  872.                     case 10007:
  873.                     case 10017:
  874.                     case 10029:
  875.                     case 28592:
  876.                     case 28594:
  877.                     case 28595:
  878.                     case 28599:
  879.                     case 28603:
  880.                     case 28605:
  881.                     case 37:
  882.                     case 500:
  883.                     case 870:
  884.                     case 1026:
  885.                     case 1047:
  886.                     case 1140:
  887.                     case 1141:
  888.                     case 1142:
  889.                     case 1143:
  890.                     case 1144:
  891.                     case 1145:
  892.                     case 1146:
  893.                     case 1147:
  894.                     case 1148:
  895.                     case 1149:
  896.                     case 20273:
  897.                     case 20277:
  898.                     case 20278:
  899.                     case 20280:
  900.                     case 20284:
  901.                     case 20285:
  902.                     case 20297:
  903.                     case 20871:
  904.                     case 20880:
  905.                     case 20924:
  906.                     case 21025:
  907.                     case 720:
  908.                     case 20866:
  909.                     case 21866:
  910.                         // Return true for some code pages.
  911.                         // (Latin I - ANSI)
  912.                         // (Eastern Europe - ANSI)
  913.                         // (Cyrillic - ANSI)
  914.                         // (Turkish - ANSI)
  915.                         // (Arabic - ANSI)
  916.                         // (ISO 8859-1 Latin I)
  917.                         // (United States - OEM)
  918.                         // (Greek (aka 437G) - OEM)
  919.                         // (Baltic - OEM)
  920.                         // (Multilingual (Latin I) - OEM)
  921.                         // (Slovak (Latin II) - OEM)
  922.                         // (Cyrillic - OEM)
  923.                         // (Multilingual (Latin I) - OEM + Euro)
  924.                         // (Portuguese - OEM)
  925.                         // (Icelandic - OEM)
  926.                         // (Hebrew - OEM)
  927.                         // (Canadian French - OEM)
  928.                         // (Nordic - OEM)
  929.                         // (Russian - OEM)
  930.                         // (Modern Greek - OEM)
  931.                         // (Cyrillic - MAC)
  932.                         // (Ukraine - MAC)
  933.                         // (Latin II - MAC)
  934.                         // (ISO 8859-2 Eastern Europe)
  935.                         // (ISO 8859-4 Baltic)
  936.                         // (ISO 8859-5 Cyrillic)
  937.                         // (ISO 8859-9 Latin Alphabet No.5)
  938.                         // (ISO/IEC 8859-13:1998 (Lithuanian))
  939.                         // (ISO 8859-15 Latin 9 (IBM923=IBM819+Euro))
  940.                         // (IBM EBCDIC U.S./Canada)
  941.                         // (IBM EBCDIC International)
  942.                         // (IBM EBCDIC Latin-2 Multilingual/ROECE)
  943.                         // (IBM EBCDIC Latin-5 Turkey)
  944.                         // (IBM Latin-1/Open System)
  945.                         // (IBM EBCDIC U.S./Canada (037+Euro))
  946.                         // (IBM EBCDIC Germany (20273(IBM273)+Euro))
  947.                         // (IBM EBCDIC Denmark/Norway (20277(IBM277+Euro))
  948.                         // (IBM EBCDIC Finland/Sweden (20278(IBM278)+Euro))
  949.                         // (IBM EBCDIC Italy (20280(IBM280)+Euro))
  950.                         // (IBM EBCDIC Latin America/Spain (20284(IBM284)+Euro))
  951.                         // (IBM EBCDIC United Kingdom (20285(IBM285)+Euro))
  952.                         // (IBM EBCDIC France (20297(IBM297+Euro))
  953.                         // (IBM EBCDIC International (500+Euro))
  954.                         // (IBM EBCDIC Icelandic (20871(IBM871+Euro))
  955.                         // (IBM EBCDIC Germany)
  956.                         // (IBM EBCDIC Denmark/Norway)
  957.                         // (IBM EBCDIC Finland/Sweden)
  958.                         // (IBM EBCDIC Italy)
  959.                         // (IBM EBCDIC Latin America/Spain)
  960.                         // (IBM EBCDIC United Kingdom)
  961.                         // (IBM EBCDIC France)
  962.                         // (IBM EBCDIC Icelandic)
  963.                         // (IBM EBCDIC Cyrillic)
  964.                         // (IBM Latin-1/Open System (IBM924=IBM1047+Euro))
  965.                         // (IBM EBCDIC Cyrillic (Serbian, Bulgarian))
  966.                         // (Arabic - Transparent ASMO)
  967.                         // (Russian - KOI8)
  968.                         // (Ukrainian - KOI8-U)
  969.                         return true;
  970.                 }
  971.             }
  972.            
  973.             // False for IDNA and unknown
  974.             return false;
  975.         }
  976.     }
  977. }

Developer Fusion