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

  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.Runtime.Serialization;
  21.     using System.Security.Permissions;
  22.    
  23.     // DBCSCodePageEncoding
  24.     //
  25.     [Serializable()]
  26.     internal class DBCSCodePageEncoding : BaseCodePageEncoding, ISerializable
  27.     {
  28.         // Pointers to our memory section parts
  29.         [NonSerialized()]
  30.         unsafe protected char* mapBytesToUnicode = null;
  31.         // char 65536
  32.         [NonSerialized()]
  33.         unsafe protected ushort* mapUnicodeToBytes = null;
  34.         // byte 65536
  35.         [NonSerialized()]
  36.         unsafe protected int* mapCodePageCached = null;
  37.         // to remember which CP is cached
  38.         [NonSerialized()]
  39.         protected const char UNKNOWN_CHAR_FLAG = (char)0;
  40.         [NonSerialized()]
  41.         protected const char UNICODE_REPLACEMENT_CHAR = (char)65533;
  42.         [NonSerialized()]
  43.         protected const char LEAD_BYTE_CHAR = (char)65534;
  44.         // For lead bytes
  45.         // Note that even though we provide bytesUnknown and byteCountUnknown,
  46.         // They aren't actually used because of the fallback mechanism. (char is though)
  47.         [NonSerialized()]
  48.         ushort bytesUnknown;
  49.         [NonSerialized()]
  50.         int byteCountUnknown;
  51.         [NonSerialized()]
  52.         protected char charUnknown = (char)0;
  53.        
  54.         public DBCSCodePageEncoding(int codePage) : this(codePage, codePage)
  55.         {
  56.         }
  57.        
  58.         internal DBCSCodePageEncoding(int codePage, int dataCodePage) : base(codePage, dataCodePage)
  59.         {
  60.         }
  61.        
  62.         // Constructor called by serialization.
  63.         // Note: We use the base GetObjectData however
  64.         internal DBCSCodePageEncoding(SerializationInfo info, StreamingContext context) : base(0)
  65.         {
  66.             // Actually this can't ever get called, CodePageEncoding is our proxy
  67.             BCLDebug.Assert(false, "Didn't expect to make it to DBCSCodePageEncoding serialization constructor");
  68.             throw new ArgumentNullException("this");
  69.         }
  70.        
  71.         // MBCS data section:
  72.         //
  73.         // We treat each multibyte pattern as 2 bytes in our table. If its a single byte, then the high byte
  74.         // for that position will be 0. When the table is loaded, leading bytes are flagged with 0xFFFE, so
  75.         // when reading the table look up with each byte. If the result is 0xFFFE, then use 2 bytes to read
  76.         // further data. FFFF is a special value indicating that the unicode code is the same as the
  77.         // character code (this helps us support code points < 0x20). FFFD is used as replacement character.
  78.         //
  79.         // Normal table:
  80.         // WCHAR* - Starting with MB code point 0.
  81.         // FFFF indicates we are to use the multibyte value for our code point.
  82.         // FFFE is the lead byte mark. (This should only appear in positions < 0x100)
  83.         // FFFD is the replacement (unknown character) mark.
  84.         // 2-20 means to advance the pointer 2-0x20 characters.
  85.         // 1 means that to advance to the multibyte position contained in the next char.
  86.         // 0 nothing special (I don't think its possible.)
  87.         //
  88.         // Table ends when multibyte position has advanced to 0xFFFF.
  89.         //
  90.         // Bytes->Unicode Best Fit table:
  91.         // WCHAR* - Same as normal table, except first wchar is byte position to start at.
  92.         //
  93.         // Unicode->Bytes Best Fit Table:
  94.         // WCHAR* - Same as normal table, except first wchar is char position to start at and
  95.         // we loop through unicode code points and the table has the byte points that
  96.         // corrospond to those unicode code points.
  97.         // We have a managed code page entry, so load our tables
  98.         //
  99.         unsafe protected override void LoadManagedCodePage()
  100.         {
  101.             // Should be loading OUR code page
  102.             BCLDebug.Assert(pCodePage->CodePage == this.dataTableCodePage, "[DBCSCodePageEncoding.LoadManagedCodePage]Expected to load data table code page");
  103.            
  104.             // Make sure we're really a 1 byte code page
  105.             if (pCodePage->ByteCount != 2)
  106.                 throw new NotSupportedException(Environment.GetResourceString("NotSupported_NoCodepageData", CodePage));
  107.             // Remember our unknown bytes & chars
  108.             bytesUnknown = pCodePage->ByteReplace;
  109.             charUnknown = pCodePage->UnicodeReplace;
  110.            
  111.             // Need to make sure the fallback buffer's fallback char is correct
  112.             if (this.DecoderFallback.IsMicrosoftBestFitFallback) {
  113.                 ((InternalDecoderBestFitFallback)(this.DecoderFallback)).cReplacement = charUnknown;
  114.             }
  115.            
  116.             // Is our replacement bytesUnknown a single or double byte character?
  117.             byteCountUnknown = 1;
  118.             if (bytesUnknown > 255)
  119.                 byteCountUnknown++;
  120.            
  121.             // We use fallback encoder, which uses ?, which so far all of our tables do as well
  122.             BCLDebug.Assert(bytesUnknown == 63, "[DBCSCodePageEncoding.LoadManagedCodePage]Expected 0x3f (?) as unknown byte character");
  123.            
  124.             // Get our mapped section (bytes to allocate = 2 bytes per 65536 Unicode chars + 2 bytes per 65536 DBCS chars)
  125.             // Plus 4 byte to remember CP # when done loading it. (Don't want to get IA64 or anything out of alignment)
  126.             byte* pMemorySection = GetSharedMemory(65536 * 2 * 2 + 4 + this.iExtraBytes);
  127.            
  128.             mapBytesToUnicode = (char*)pMemorySection;
  129.             mapUnicodeToBytes = (ushort*)(pMemorySection + 65536 * 2);
  130.             mapCodePageCached = (int*)(pMemorySection + 65536 * 2 * 2 + this.iExtraBytes);
  131.            
  132.             // If its cached (& filled in) we don't have to do anything else
  133.             if (*mapCodePageCached != 0) {
  134.                 BCLDebug.Assert(((*mapCodePageCached == this.dataTableCodePage && this.bFlagDataTable) || (*mapCodePageCached == this.CodePage && !this.bFlagDataTable)), "[DBCSCodePageEncoding.LoadManagedCodePage]Expected mapped section cached page flag to be set to data table or regular code page.");
  135.                
  136.                 // Special case for GB18030 because it mangles its own code page after this function
  137.                 if ((*mapCodePageCached != this.dataTableCodePage && this.bFlagDataTable) || (*mapCodePageCached != this.CodePage && !this.bFlagDataTable))
  138.                     throw new OutOfMemoryException(Environment.GetResourceString("Arg_OutOfMemoryException"));
  139.                
  140.                 // If its cached (& filled in) we don't have to do anything else
  141.                 return;
  142.             }
  143.            
  144.             // Need to read our data file and fill in our section.
  145.             // WARNING: Multiple code pieces could do this at once (so we don't have to lock machine-wide)
  146.             // so be careful here. Only stick legal values in here, don't stick temporary values.
  147.            
  148.             // Move to the beginning of the data section
  149.             char* pData = (char*)&(pCodePage->FirstDataWord);
  150.            
  151.             // We start at bytes position 0
  152.             int bytePosition = 0;
  153.             int useBytes = 0;
  154.            
  155.             while (bytePosition < 65536) {
  156.                 // Get the next byte
  157.                 char input = *pData;
  158.                 pData++;
  159.                
  160.                 // build our table:
  161.                 if (input == 1) {
  162.                     // Use next data as our byte position
  163.                     bytePosition = (int)(*pData);
  164.                     pData++;
  165.                     continue;
  166.                 }
  167.                 else if (input < 32 && input > 0) {
  168.                     // Advance input characters
  169.                     bytePosition += input;
  170.                     continue;
  171.                 }
  172.                 else if (input == 65535) {
  173.                     // Same as our bytePosition
  174.                     useBytes = bytePosition;
  175.                     input = unchecked((char)bytePosition);
  176.                 }
  177.                 // 0xfffe
  178.                 else if (input == LEAD_BYTE_CHAR) {
  179.                     // Lead byte mark
  180.                     BCLDebug.Assert(bytePosition < 256, "[DBCSCodePageEncoding.LoadManagedCodePage]expected lead byte to be < 0x100");
  181.                     useBytes = bytePosition;
  182.                     // input stays 0xFFFE
  183.                 }
  184.                 else if (input == UNICODE_REPLACEMENT_CHAR) {
  185.                     // Replacement char is already done
  186.                     bytePosition++;
  187.                     continue;
  188.                 }
  189.                 else {
  190.                     // Use this character
  191.                     useBytes = bytePosition;
  192.                     // input == input;
  193.                 }
  194.                
  195.                 // We may need to clean up the selected character & position
  196.                 if (CleanUpBytes(ref useBytes)) {
  197.                     // Use this selected character at the selected position, don't do this if not supposed to.
  198.                     if (input != LEAD_BYTE_CHAR) {
  199.                         // Don't do this for lead byte marks.
  200.                         mapUnicodeToBytes[input] = unchecked((ushort)useBytes);
  201.                     }
  202.                     mapBytesToUnicode[useBytes] = input;
  203.                 }
  204.                 bytePosition++;
  205.             }
  206.            
  207.             // See if we have any clean up junk to do
  208.             CleanUpEndBytes(mapBytesToUnicode);
  209.            
  210.             // We're done with our mapped section, set our flag so others don't have to rebuild table.
  211.             // We only do this if we're flagging(using) the data table as our primary mechanism
  212.             if (this.bFlagDataTable)
  213.                 *mapCodePageCached = this.dataTableCodePage;
  214.         }
  215.        
  216.         // Any special processing for this code page
  217.         protected virtual bool CleanUpBytes(ref int bytes)
  218.         {
  219.             return true;
  220.         }
  221.        
  222.         // Any special processing for this code page
  223.         unsafe protected virtual void CleanUpEndBytes(char* chars)
  224.         {
  225.         }
  226.        
  227.         // Private object for locking instead of locking on a public type for SQL reliability work.
  228.         private static object s_InternalSyncObject;
  229.         private static object InternalSyncObject {
  230.             get {
  231.                 if (s_InternalSyncObject == null) {
  232.                     object o = new object();
  233.                     Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
  234.                 }
  235.                 return s_InternalSyncObject;
  236.             }
  237.         }
  238.        
  239.         // Read in our best fit table
  240.         unsafe protected override void ReadBestFitTable()
  241.         {
  242.             // Lock so we don't confuse ourselves.
  243.             lock (InternalSyncObject) {
  244.                 // If we got a best fit array already then don't do this
  245.                 if (arrayUnicodeBestFit == null) {
  246.                     //
  247.                     // Read in Best Fit table.
  248.                     //
  249.                    
  250.                     // First we have to advance past original character mapping table
  251.                     // Move to the beginning of the data section
  252.                     char* pData = (char*)&(pCodePage->FirstDataWord);
  253.                    
  254.                     // We start at bytes position 0
  255.                     int bytesPosition = 0;
  256.                    
  257.                     while (bytesPosition < 65536) {
  258.                         // Get the next byte
  259.                         char input = *pData;
  260.                         pData++;
  261.                        
  262.                         // build our table:
  263.                         if (input == 1) {
  264.                             // Use next data as our byte position
  265.                             bytesPosition = (int)(*pData);
  266.                             pData++;
  267.                         }
  268.                         else if (input < 32 && input > 0) {
  269.                             // Advance input characters
  270.                             bytesPosition += input;
  271.                         }
  272.                         else {
  273.                             // All other cases add 1 to bytes position
  274.                             bytesPosition++;
  275.                         }
  276.                     }
  277.                    
  278.                     // Now bytesPosition is at start of bytes->unicode best fit table
  279.                     char* pBytes2Unicode = pData;
  280.                    
  281.                     // Now pData should be pointing to first word of bytes -> unicode best fit table
  282.                     // (which we're also not using at the moment)
  283.                     int iBestFitCount = 0;
  284.                     bytesPosition = *pData;
  285.                     pData++;
  286.                    
  287.                     while (bytesPosition < 65536) {
  288.                         // Get the next byte
  289.                         char input = *pData;
  290.                         pData++;
  291.                        
  292.                         // build our table:
  293.                         if (input == 1) {
  294.                             // Use next data as our byte position
  295.                             bytesPosition = (int)(*pData);
  296.                             pData++;
  297.                         }
  298.                         else if (input < 32 && input > 0) {
  299.                             // Advance input characters
  300.                             bytesPosition += input;
  301.                         }
  302.                         else {
  303.                             // Use this character (unless its unknown, unk just skips 1)
  304.                             if (input != UNICODE_REPLACEMENT_CHAR) {
  305.                                 int correctedChar = bytesPosition;
  306.                                 if (CleanUpBytes(ref correctedChar)) {
  307.                                     // Sometimes correction makes them same as no best fit, skip those.
  308.                                     if (mapBytesToUnicode[correctedChar] != input) {
  309.                                         iBestFitCount++;
  310.                                     }
  311.                                 }
  312.                             }
  313.                            
  314.                             // Position gets incremented in any case.
  315.                             bytesPosition++;
  316.                         }
  317.                        
  318.                     }
  319.                    
  320.                     // Now we know how big the best fit table has to be
  321.                     char[] arrayTemp = new char[iBestFitCount * 2];
  322.                    
  323.                     // Now we know how many best fits we have, so go back & read them in
  324.                     iBestFitCount = 0;
  325.                     pData = pBytes2Unicode;
  326.                     bytesPosition = *pData;
  327.                     pData++;
  328.                     bool bOutOfOrder = false;
  329.                    
  330.                     // Read it all in again
  331.                     while (bytesPosition < 65536) {
  332.                         // Get the next byte
  333.                         char input = *pData;
  334.                         pData++;
  335.                        
  336.                         // build our table:
  337.                         if (input == 1) {
  338.                             // Use next data as our byte position
  339.                             bytesPosition = (int)(*pData);
  340.                             pData++;
  341.                         }
  342.                         else if (input < 32 && input > 0) {
  343.                             // Advance input characters
  344.                             bytesPosition += input;
  345.                         }
  346.                         else {
  347.                             // Use this character (unless its unknown, unk just skips 1)
  348.                             if (input != UNICODE_REPLACEMENT_CHAR) {
  349.                                 int correctedChar = bytesPosition;
  350.                                 if (CleanUpBytes(ref correctedChar)) {
  351.                                     // Sometimes correction makes them same as no best fit, skip those.
  352.                                     if (mapBytesToUnicode[correctedChar] != input) {
  353.                                         if (correctedChar != bytesPosition)
  354.                                             bOutOfOrder = true;
  355.                                        
  356.                                         arrayTemp[iBestFitCount++] = unchecked((char)correctedChar);
  357.                                         arrayTemp[iBestFitCount++] = input;
  358.                                     }
  359.                                 }
  360.                             }
  361.                            
  362.                             // Position gets incremented in any case.
  363.                             bytesPosition++;
  364.                         }
  365.                     }
  366.                    
  367.                     // If they're out of order we need to sort them.
  368.                     if (bOutOfOrder) {
  369.                         BCLDebug.Assert((arrayTemp.Length / 2) < 20, "[DBCSCodePageEncoding.ReadBestFitTable]Expected small best fit table < 20 for code page " + CodePage + ", not " + arrayTemp.Length / 2);
  370.                        
  371.                         for (int i = 0; i < arrayTemp.Length - 2; i += 2) {
  372.                             int iSmallest = i;
  373.                             char cSmallest = arrayTemp[i];
  374.                            
  375.                             for (int j = i + 2; j < arrayTemp.Length; j += 2) {
  376.                                 // Find smallest one for front
  377.                                 if (cSmallest > arrayTemp[j]) {
  378.                                     cSmallest = arrayTemp[j];
  379.                                     iSmallest = j;
  380.                                 }
  381.                             }
  382.                            
  383.                             // If smallest one is something else, switch them
  384.                             if (iSmallest != i) {
  385.                                 char temp = arrayTemp[iSmallest];
  386.                                 arrayTemp[iSmallest] = arrayTemp[i];
  387.                                 arrayTemp[i] = temp;
  388.                                 temp = arrayTemp[iSmallest + 1];
  389.                                 arrayTemp[iSmallest + 1] = arrayTemp[i + 1];
  390.                                 arrayTemp[i + 1] = temp;
  391.                             }
  392.                         }
  393.                     }
  394.                    
  395.                     // Remember our array
  396.                     arrayBytesBestFit = arrayTemp;
  397.                    
  398.                     // Now were at beginning of Unicode -> Bytes best fit table, need to count them
  399.                     char* pUnicode2Bytes = pData;
  400.                     int unicodePosition = *(pData++);
  401.                     iBestFitCount = 0;
  402.                    
  403.                     while (unicodePosition < 65536) {
  404.                         // Get the next byte
  405.                         char input = *pData;
  406.                         pData++;
  407.                        
  408.                         // build our table:
  409.                         if (input == 1) {
  410.                             // Use next data as our byte position
  411.                             unicodePosition = (int)*pData;
  412.                             pData++;
  413.                         }
  414.                         else if (input < 32 && input > 0) {
  415.                             // Advance input characters
  416.                             unicodePosition += input;
  417.                         }
  418.                         else {
  419.                             // Same as our unicodePosition or use this character
  420.                             if (input > 0)
  421.                                 iBestFitCount++;
  422.                             unicodePosition++;
  423.                         }
  424.                     }
  425.                    
  426.                     // Allocate our table
  427.                     arrayTemp = new char[iBestFitCount * 2];
  428.                    
  429.                     // Now do it again to fill the array with real values
  430.                     pData = pUnicode2Bytes;
  431.                     unicodePosition = *(pData++);
  432.                     iBestFitCount = 0;
  433.                    
  434.                     while (unicodePosition < 65536) {
  435.                         // Get the next byte
  436.                         char input = *pData;
  437.                         pData++;
  438.                        
  439.                         // build our table:
  440.                         if (input == 1) {
  441.                             // Use next data as our byte position
  442.                             unicodePosition = (int)*pData;
  443.                             pData++;
  444.                         }
  445.                         else if (input < 32 && input > 0) {
  446.                             // Advance input characters
  447.                             unicodePosition += input;
  448.                         }
  449.                         else {
  450.                             if (input > 0) {
  451.                                 // Use this character, may need to clean it up
  452.                                 int correctedChar = (int)input;
  453.                                 if (CleanUpBytes(ref correctedChar)) {
  454.                                     arrayTemp[iBestFitCount++] = unchecked((char)unicodePosition);
  455.                                     // Have to map it to Unicode because best fit will need unicode value of best fit char.
  456.                                     arrayTemp[iBestFitCount++] = mapBytesToUnicode[correctedChar];
  457.                                    
  458.                                 }
  459.                             }
  460.                             unicodePosition++;
  461.                         }
  462.                     }
  463.                    
  464.                     // Remember our array
  465.                     arrayUnicodeBestFit = arrayTemp;
  466.                 }
  467.                
  468.             }
  469.         }
  470.        
  471.         // GetByteCount
  472.         // Note: We start by assuming that the output will be the same as count. Having
  473.         // an encoder or fallback may change that assumption
  474.         unsafe internal override int GetByteCount(char* chars, int count, EncoderNLS encoder)
  475.         {
  476.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  477.             BCLDebug.Assert(count >= 0, "[DBCSCodePageEncoding.GetByteCount]count is negative");
  478.             BCLDebug.Assert(chars != null, "[DBCSCodePageEncoding.GetByteCount]chars is null");
  479.            
  480.             // Assert because we shouldn't be able to have a null encoder.
  481.             BCLDebug.Assert(encoderFallback != null, "[DBCSCodePageEncoding.GetByteCount]Attempting to use null fallback");
  482.            
  483.             CheckMemorySection();
  484.            
  485.             // Get any left over characters
  486.             char charLeftOver = (char)0;
  487.             if (encoder != null) {
  488.                 charLeftOver = encoder.charLeftOver;
  489.                
  490.                 // Only count if encoder.m_throwOnOverflow
  491.                 if (encoder.InternalHasFallbackBuffer && encoder.FallbackBuffer.Remaining > 0)
  492.                     throw new ArgumentException(Environment.GetResourceString("Argument_EncoderFallbackNotEmpty", this.EncodingName, encoder.Fallback.GetType()));
  493.             }
  494.            
  495.             // prepare our end
  496.             int byteCount = 0;
  497.             char* charEnd = chars + count;
  498.            
  499.             // For fallback we will need a fallback buffer
  500.             EncoderFallbackBuffer fallbackBuffer = null;
  501.            
  502.             // We may have a left over character from last time, try and process it.
  503.             if (charLeftOver > 0) {
  504.                 BCLDebug.Assert(Char.IsHighSurrogate(charLeftOver), "[DBCSCodePageEncoding.GetByteCount]leftover character should be high surrogate");
  505.                 BCLDebug.Assert(encoder != null, "[DBCSCodePageEncoding.GetByteCount]Expect to have encoder if we have a charLeftOver");
  506.                
  507.                 // Since left over char was a surrogate, it'll have to be fallen back.
  508.                 // Get Fallback
  509.                 fallbackBuffer = encoder.FallbackBuffer;
  510.                 fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false);
  511.                 // This will fallback a pair if *chars is a low surrogate
  512.                 fallbackBuffer.InternalFallback(charLeftOver, ref chars);
  513.             }
  514.            
  515.             // Now we may have fallback char[] already (from the encoder)
  516.            
  517.             // We have to use fallback method.
  518.             char ch;
  519.             while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || chars < charEnd) {
  520.                 // First unwind any fallback
  521.                 if (ch == 0) {
  522.                     // No fallback, just get next char
  523.                     ch = *chars;
  524.                     chars++;
  525.                 }
  526.                
  527.                 // get byte for this char
  528.                 ushort sTemp = mapUnicodeToBytes[ch];
  529.                
  530.                 // Check for fallback, this'll catch surrogate pairs too.
  531.                 if (sTemp == 0 && ch != (char)0) {
  532.                     if (fallbackBuffer == null) {
  533.                         // Initialize the buffer
  534.                         if (encoder == null)
  535.                             fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
  536.                         else
  537.                             fallbackBuffer = encoder.FallbackBuffer;
  538.                         fallbackBuffer.InternalInitialize(charEnd - count, charEnd, encoder, false);
  539.                     }
  540.                    
  541.                     // Get Fallback
  542.                     fallbackBuffer.InternalFallback(ch, ref chars);
  543.                     continue;
  544.                 }
  545.                
  546.                 // We'll use this one
  547.                 byteCount++;
  548.                 if (sTemp >= 256)
  549.                     byteCount++;
  550.             }
  551.            
  552.             return (int)byteCount;
  553.         }
  554.        
  555.         unsafe internal override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS encoder)
  556.         {
  557.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  558.             BCLDebug.Assert(bytes != null, "[DBCSCodePageEncoding.GetBytes]bytes is null");
  559.             BCLDebug.Assert(byteCount >= 0, "[DBCSCodePageEncoding.GetBytes]byteCount is negative");
  560.             BCLDebug.Assert(chars != null, "[DBCSCodePageEncoding.GetBytes]chars is null");
  561.             BCLDebug.Assert(charCount >= 0, "[DBCSCodePageEncoding.GetBytes]charCount is negative");
  562.            
  563.             // Assert because we shouldn't be able to have a null encoder.
  564.             BCLDebug.Assert(encoderFallback != null, "[DBCSCodePageEncoding.GetBytes]Attempting to use null encoder fallback");
  565.            
  566.             CheckMemorySection();
  567.            
  568.             // For fallback we will need a fallback buffer
  569.             EncoderFallbackBuffer fallbackBuffer = null;
  570.            
  571.             // prepare our end
  572.             char* charEnd = chars + charCount;
  573.             char* charStart = chars;
  574.             byte* byteStart = bytes;
  575.             byte* byteEnd = bytes + byteCount;
  576.            
  577.             // Get any left over characters
  578.             char charLeftOver = (char)0;
  579.             if (encoder != null) {
  580.                 charLeftOver = encoder.charLeftOver;
  581.                 BCLDebug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver), "[DBCSCodePageEncoding.GetBytes]leftover character should be high surrogate");
  582.                
  583.                 // Go ahead and get the fallback buffer (need leftover fallback if converting)
  584.                 fallbackBuffer = encoder.FallbackBuffer;
  585.                 fallbackBuffer.InternalInitialize(chars, charEnd, encoder, true);
  586.                
  587.                 // If we're not converting we must not have a fallback buffer
  588.                 if (encoder.m_throwOnOverflow && fallbackBuffer.Remaining > 0)
  589.                     throw new ArgumentException(Environment.GetResourceString("Argument_EncoderFallbackNotEmpty", this.EncodingName, encoder.Fallback.GetType()));
  590.                
  591.                 // We may have a left over character from last time, try and process it.
  592.                 if (charLeftOver > 0) {
  593.                     BCLDebug.Assert(encoder != null, "[DBCSCodePageEncoding.GetBytes]Expect to have encoder if we have a charLeftOver");
  594.                    
  595.                     // Since left over char was a surrogate, it'll have to be fallen back.
  596.                     // Get Fallback
  597.                     fallbackBuffer.InternalFallback(charLeftOver, ref chars);
  598.                 }
  599.             }
  600.            
  601.             // Now we may have fallback char[] already from the encoder
  602.            
  603.             // Go ahead and do it, including the fallback.
  604.             char ch;
  605.             while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 || chars < charEnd) {
  606.                 // First unwind any fallback
  607.                 if (ch == 0) {
  608.                     // No fallback, just get next char
  609.                     ch = *chars;
  610.                     chars++;
  611.                 }
  612.                
  613.                 // get byte for this char
  614.                 ushort sTemp = mapUnicodeToBytes[ch];
  615.                
  616.                 // Check for fallback, this'll catch surrogate pairs too.
  617.                 if (sTemp == 0 && ch != (char)0) {
  618.                     if (fallbackBuffer == null) {
  619.                         // Initialize the buffer
  620.                         BCLDebug.Assert(encoder == null, "[DBCSCodePageEncoding.GetBytes]Expected delayed create fallback only if no encoder.");
  621.                         fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
  622.                         fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, true);
  623.                     }
  624.                    
  625.                     // Get Fallback
  626.                     fallbackBuffer.InternalFallback(ch, ref chars);
  627.                     continue;
  628.                 }
  629.                
  630.                 // We'll use this one (or two)
  631.                 // Bounds check
  632.                
  633.                 // Go ahead and add it, lead byte 1st if necessary
  634.                 if (sTemp >= 256) {
  635.                     if (bytes + 1 >= byteEnd) {
  636.                         // didn't use this char, we'll throw or use buffer
  637.                         if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false) {
  638.                             BCLDebug.Assert(chars > charStart, "[DBCSCodePageEncoding.GetBytes]Expected chars to have advanced (double byte case)");
  639.                             chars--;
  640.                             // don't use last char
  641.                         }
  642.                         else
  643.                             fallbackBuffer.MovePrevious();
  644.                         // don't use last fallback
  645.                         ThrowBytesOverflow(encoder, chars == charStart);
  646.                         // throw ?
  647.                         break;
  648.                         // don't throw, stop
  649.                     }
  650.                    
  651.                     *bytes = unchecked((byte)(sTemp >> 8));
  652.                     bytes++;
  653.                 }
  654.                 // Single byte
  655.                 else if (bytes >= byteEnd) {
  656.                     // didn't use this char, we'll throw or use buffer
  657.                     if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false) {
  658.                         BCLDebug.Assert(chars > charStart, "[DBCSCodePageEncoding.GetBytes]Expected chars to have advanced (single byte case)");
  659.                         chars--;
  660.                         // don't use last char
  661.                     }
  662.                     else
  663.                         fallbackBuffer.MovePrevious();
  664.                     // don't use last fallback
  665.                     ThrowBytesOverflow(encoder, chars == charStart);
  666.                     // throw ?
  667.                     break;
  668.                     // don't throw, stop
  669.                 }
  670.                
  671.                 *bytes = unchecked((byte)(sTemp & 255));
  672.                 bytes++;
  673.             }
  674.            
  675.             // encoder stuff if we have one
  676.             if (encoder != null) {
  677.                 // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases
  678.                 if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder)
  679.                     // Clear it in case of MustFlush
  680.                     encoder.charLeftOver = (char)0;
  681.                
  682.                 // Set our chars used count
  683.                 encoder.m_charsUsed = (int)(chars - charStart);
  684.             }
  685.            
  686.             // If we're not converting we must not have a fallback buffer
  687.             // (We don't really have a way to clear none-encoder using fallbacks however)
  688.             // BCLDebug.Assert((encoder == null || encoder.m_throwOnOverflow) &&
  689.             // (fallbackBuffer == null || fallbackBuffer.Remaining == 0),
  690.             // "[DBCSEncoding.GetBytes]Expected empty fallback buffer at end if not converting");
  691.            
  692.             return (int)(bytes - byteStart);
  693.         }
  694.        
  695.         // This is internal and called by something else,
  696.         unsafe internal override int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
  697.         {
  698.             // Just assert, we're called internally so these should be safe, checked already
  699.             BCLDebug.Assert(bytes != null, "[DBCSCodePageEncoding.GetCharCount]bytes is null");
  700.             BCLDebug.Assert(count >= 0, "[DBCSCodePageEncoding.GetCharCount]byteCount is negative");
  701.            
  702.             CheckMemorySection();
  703.            
  704.             // Fix our decoder
  705.             DBCSDecoder decoder = (DBCSDecoder)baseDecoder;
  706.            
  707.             // Get our fallback
  708.             DecoderFallbackBuffer fallbackBuffer = null;
  709.            
  710.             // We'll need to know where the end is
  711.             byte* byteEnd = bytes + count;
  712.             int charCount = count;
  713.             // Assume 1 char / byte
  714.             // Shouldn't have anything in fallback buffer for GetCharCount
  715.             // (don't have to check m_throwOnOverflow for count)
  716.             BCLDebug.Assert(decoder == null || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[DBCSCodePageEncoding.GetCharCount]Expected empty fallback buffer at start");
  717.            
  718.             // If we have a left over byte, use it
  719.             if (decoder != null && decoder.bLeftOver > 0) {
  720.                 // We have a left over byte?
  721.                 if (count == 0) {
  722.                     // No input though
  723.                     if (!decoder.MustFlush) {
  724.                         // Don't have to flush
  725.                         return 0;
  726.                     }
  727.                    
  728.                    
  729.                     BCLDebug.Assert(fallbackBuffer == null, "[DBCSCodePageEncoding.GetCharCount]Expected empty fallback buffer");
  730.                     fallbackBuffer = decoder.FallbackBuffer;
  731.                     fallbackBuffer.InternalInitialize(bytes, null);
  732.                    
  733.                     byte[] byteBuffer = new byte[] {unchecked((byte)decoder.bLeftOver)};
  734.                     return fallbackBuffer.InternalFallback(byteBuffer, bytes);
  735.                 }
  736.                
  737.                 // Get our full info
  738.                 int iBytes = decoder.bLeftOver << 8;
  739.                 iBytes |= (*bytes);
  740.                 bytes++;
  741.                
  742.                 // This is either 1 known char or fallback
  743.                 // Already counted 1 char
  744.                 // Look up our bytes
  745.                 char cDecoder = mapBytesToUnicode[iBytes];
  746.                 if (cDecoder == 0 && iBytes != 0) {
  747.                     // Deallocate preallocated one
  748.                     charCount--;
  749.                    
  750.                     // We'll need a fallback
  751.                     BCLDebug.Assert(fallbackBuffer == null, "[DBCSCodePageEncoding.GetCharCount]Expected empty fallback buffer for unknown pair");
  752.                     fallbackBuffer = decoder.FallbackBuffer;
  753.                     fallbackBuffer.InternalInitialize(byteEnd - count, null);
  754.                    
  755.                     // Do fallback, we know there're 2 bytes
  756.                     byte[] byteBuffer = new byte[] {unchecked((byte)(iBytes >> 8)), unchecked((byte)iBytes)};
  757.                     charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
  758.                 }
  759.                 // else we already reserved space for this one.
  760.             }
  761.            
  762.             // Loop, watch out for fallbacks
  763.             while (bytes < byteEnd) {
  764.                 // Faster if don't use *bytes++;
  765.                 int iBytes = *bytes;
  766.                 bytes++;
  767.                 char c = mapBytesToUnicode[iBytes];
  768.                
  769.                 // See if it was a double byte character
  770.                 if (c == LEAD_BYTE_CHAR) {
  771.                     // Its a lead byte
  772.                     charCount--;
  773.                     // deallocate preallocated lead byte
  774.                     if (bytes < byteEnd) {
  775.                         // Have another to use, so use it
  776.                         iBytes <<= 8;
  777.                         iBytes |= *bytes;
  778.                         bytes++;
  779.                         c = mapBytesToUnicode[iBytes];
  780.                     }
  781.                     else {
  782.                         // No input left
  783.                         if (decoder == null || decoder.MustFlush) {
  784.                             // have to flush anyway, set to unknown so we use fallback in a 'sec
  785.                             charCount++;
  786.                             // reallocate deallocated lead byte
  787.                             c = UNKNOWN_CHAR_FLAG;
  788.                         }
  789.                         else {
  790.                             // We'll stick it in decoder
  791.                             break;
  792.                         }
  793.                     }
  794.                 }
  795.                
  796.                 // See if it was unknown.
  797.                 // Unknown and known chars already allocated, but fallbacks aren't
  798.                 if (c == UNKNOWN_CHAR_FLAG && iBytes != 0) {
  799.                     if (fallbackBuffer == null) {
  800.                         if (decoder == null)
  801.                             fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
  802.                         else
  803.                             fallbackBuffer = decoder.FallbackBuffer;
  804.                         fallbackBuffer.InternalInitialize(byteEnd - count, null);
  805.                     }
  806.                    
  807.                     // Do fallback
  808.                     charCount--;
  809.                     // Get rid of preallocated extra char
  810.                     byte[] byteBuffer = null;
  811.                     if (iBytes < 256)
  812.                         byteBuffer = new byte[] {unchecked((byte)iBytes)};
  813.                     else
  814.                         byteBuffer = new byte[] {unchecked((byte)(iBytes >> 8)), unchecked((byte)iBytes)};
  815.                     charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
  816.                 }
  817.             }
  818.            
  819.             // Shouldn't have anything in fallback buffer for GetChars
  820.             BCLDebug.Assert(decoder == null || !decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[DBCSCodePageEncoding.GetCharCount]Expected empty fallback buffer at end");
  821.            
  822.             // Return our count
  823.             return charCount;
  824.         }
  825.        
  826.         unsafe internal override int GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder)
  827.         {
  828.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  829.             BCLDebug.Assert(bytes != null, "[DBCSCodePageEncoding.GetChars]bytes is null");
  830.             BCLDebug.Assert(byteCount >= 0, "[DBCSCodePageEncoding.GetChars]byteCount is negative");
  831.             BCLDebug.Assert(chars != null, "[DBCSCodePageEncoding.GetChars]chars is null");
  832.             BCLDebug.Assert(charCount >= 0, "[DBCSCodePageEncoding.GetChars]charCount is negative");
  833.            
  834.             CheckMemorySection();
  835.            
  836.             // Fix our decoder
  837.             DBCSDecoder decoder = (DBCSDecoder)baseDecoder;
  838.            
  839.             // We'll need to know where the end is
  840.             byte* byteStart = bytes;
  841.             byte* byteEnd = bytes + byteCount;
  842.             char* charStart = chars;
  843.             char* charEnd = chars + charCount;
  844.             bool bUsedDecoder = false;
  845.            
  846.             // Get our fallback
  847.             DecoderFallbackBuffer fallbackBuffer = null;
  848.            
  849.             // Shouldn't have anything in fallback buffer for GetChars
  850.             BCLDebug.Assert(decoder == null || !decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[DBCSCodePageEncoding.GetChars]Expected empty fallback buffer at start");
  851.            
  852.             // If we have a left over byte, use it
  853.             if (decoder != null && decoder.bLeftOver > 0) {
  854.                 // We have a left over byte?
  855.                 if (byteCount == 0) {
  856.                     // No input though
  857.                     if (!decoder.MustFlush) {
  858.                         // Don't have to flush
  859.                         return 0;
  860.                     }
  861.                    
  862.                     // Well, we're flushing, so use '?' or fallback
  863.                     // fallback leftover byte
  864.                     BCLDebug.Assert(fallbackBuffer == null, "[DBCSCodePageEncoding.GetChars]Expected empty fallback");
  865.                     fallbackBuffer = decoder.FallbackBuffer;
  866.                     fallbackBuffer.InternalInitialize(bytes, charEnd);
  867.                    
  868.                     // If no room its hopeless, this was 1st fallback
  869.                     byte[] byteBuffer = new byte[] {unchecked((byte)decoder.bLeftOver)};
  870.                     if (!fallbackBuffer.InternalFallback(byteBuffer, bytes, ref chars))
  871.                         ThrowCharsOverflow(decoder, true);
  872.                    
  873.                     decoder.bLeftOver = 0;
  874.                    
  875.                     // Done, return it
  876.                     return (int)(chars - charStart);
  877.                 }
  878.                
  879.                 // Get our full info
  880.                 int iBytes = decoder.bLeftOver << 8;
  881.                 iBytes |= (*bytes);
  882.                 bytes++;
  883.                
  884.                 // Look up our bytes
  885.                 char cDecoder = mapBytesToUnicode[iBytes];
  886.                 if (cDecoder == UNKNOWN_CHAR_FLAG && iBytes != 0) {
  887.                     BCLDebug.Assert(fallbackBuffer == null, "[DBCSCodePageEncoding.GetChars]Expected empty fallback for two bytes");
  888.                     fallbackBuffer = decoder.FallbackBuffer;
  889.                     fallbackBuffer.InternalInitialize(byteEnd - byteCount, charEnd);
  890.                    
  891.                     byte[] byteBuffer = new byte[] {unchecked((byte)(iBytes >> 8)), unchecked((byte)iBytes)};
  892.                     if (!fallbackBuffer.InternalFallback(byteBuffer, bytes, ref chars))
  893.                         ThrowCharsOverflow(decoder, true);
  894.                 }
  895.                 else {
  896.                     // Do we have output room?, hopeless if not, this is first char
  897.                     if (chars >= charEnd)
  898.                         ThrowCharsOverflow(decoder, true);
  899.                    
  900.                     *(chars++) = cDecoder;
  901.                 }
  902.             }
  903.            
  904.             // Loop, paying attention to our fallbacks.
  905.             while (bytes < byteEnd) {
  906.                 // Faster if don't use *bytes++;
  907.                 int iBytes = *bytes;
  908.                 bytes++;
  909.                 char c = mapBytesToUnicode[iBytes];
  910.                
  911.                 // See if it was a double byte character
  912.                 if (c == LEAD_BYTE_CHAR) {
  913.                     // Its a lead byte
  914.                     if (bytes < byteEnd) {
  915.                         // Have another to use, so use it
  916.                         iBytes <<= 8;
  917.                         iBytes |= *bytes;
  918.                         bytes++;
  919.                         c = mapBytesToUnicode[iBytes];
  920.                     }
  921.                     else {
  922.                         // No input left
  923.                         if (decoder == null || decoder.MustFlush) {
  924.                             // have to flush anyway, set to unknown so we use fallback in a 'sec
  925.                             c = UNKNOWN_CHAR_FLAG;
  926.                         }
  927.                         else {
  928.                             // Stick it in decoder
  929.                             bUsedDecoder = true;
  930.                             decoder.bLeftOver = (byte)iBytes;
  931.                             break;
  932.                         }
  933.                     }
  934.                 }
  935.                
  936.                 // See if it was unknown
  937.                 if (c == UNKNOWN_CHAR_FLAG && iBytes != 0) {
  938.                     if (fallbackBuffer == null) {
  939.                         if (decoder == null)
  940.                             fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
  941.                         else
  942.                             fallbackBuffer = decoder.FallbackBuffer;
  943.                         fallbackBuffer.InternalInitialize(byteEnd - byteCount, charEnd);
  944.                     }
  945.                    
  946.                     // Do fallback
  947.                     byte[] byteBuffer = null;
  948.                     if (iBytes < 256)
  949.                         byteBuffer = new byte[] {unchecked((byte)iBytes)};
  950.                     else
  951.                         byteBuffer = new byte[] {unchecked((byte)(iBytes >> 8)), unchecked((byte)iBytes)};
  952.                     if (!fallbackBuffer.InternalFallback(byteBuffer, bytes, ref chars)) {
  953.                         // May or may not throw, but we didn't get these byte(s)
  954.                         BCLDebug.Assert(bytes >= byteStart + byteBuffer.Length, "[DBCSCodePageEncoding.GetChars]Expected bytes to have advanced for fallback");
  955.                         bytes -= byteBuffer.Length;
  956.                         // didn't use these byte(s)
  957.                         fallbackBuffer.InternalReset();
  958.                         // Didn't fall this back
  959.                         ThrowCharsOverflow(decoder, bytes == byteStart);
  960.                         // throw?
  961.                         break;
  962.                         // don't throw, but stop loop
  963.                     }
  964.                 }
  965.                 else {
  966.                     // Do we have buffer room?
  967.                     if (chars >= charEnd) {
  968.                         // May or may not throw, but we didn't get these byte(s)
  969.                         BCLDebug.Assert(bytes > byteStart, "[DBCSCodePageEncoding.GetChars]Expected bytes to have advanced for lead byte");
  970.                         bytes--;
  971.                         // unused byte
  972.                         if (iBytes >= 256) {
  973.                             BCLDebug.Assert(bytes > byteStart, "[DBCSCodePageEncoding.GetChars]Expected bytes to have advanced for trail byte");
  974.                             bytes--;
  975.                             // 2nd unused byte
  976.                         }
  977.                         ThrowCharsOverflow(decoder, bytes == byteStart);
  978.                         // throw?
  979.                         break;
  980.                         // don't throw, but stop loop
  981.                     }
  982.                    
  983.                     *(chars++) = c;
  984.                 }
  985.             }
  986.            
  987.             // We already stuck it in encoder if necessary, but we have to clear cases where nothing new got into decoder
  988.             if (decoder != null) {
  989.                 // Clear it in case of MustFlush
  990.                 if (bUsedDecoder == false) {
  991.                     decoder.bLeftOver = 0;
  992.                 }
  993.                
  994.                 // Remember our count
  995.                 decoder.m_bytesUsed = (int)(bytes - byteStart);
  996.             }
  997.            
  998.             // Shouldn't have anything in fallback buffer for GetChars
  999.             BCLDebug.Assert(decoder == null || !decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0, "[DBCSCodePageEncoding.GetChars]Expected empty fallback buffer at end");
  1000.            
  1001.             // Return length of our output
  1002.             return (int)(chars - charStart);
  1003.         }
  1004.        
  1005.         public override int GetMaxByteCount(int charCount)
  1006.         {
  1007.             if (charCount < 0)
  1008.                 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1009.            
  1010.             // Characters would be # of characters + 1 in case high surrogate is ? * max fallback
  1011.             long byteCount = (long)charCount + 1;
  1012.            
  1013.             if (EncoderFallback.MaxCharCount > 1)
  1014.                 byteCount *= EncoderFallback.MaxCharCount;
  1015.            
  1016.             // 2 to 1 is worst case. Already considered surrogate fallback
  1017.             byteCount *= 2;
  1018.            
  1019.             if (byteCount > 2147483647)
  1020.                 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_GetByteCountOverflow"));
  1021.            
  1022.             return (int)byteCount;
  1023.         }
  1024.        
  1025.         public override int GetMaxCharCount(int byteCount)
  1026.         {
  1027.             if (byteCount < 0)
  1028.                 throw new ArgumentOutOfRangeException("byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1029.            
  1030.             // DBCS is pretty much the same, but could have hanging high byte making extra ? and fallback for unknown
  1031.             long charCount = ((long)byteCount + 1);
  1032.            
  1033.             // 1 to 1 for most characters. Only surrogates with fallbacks have less, unknown fallbacks could be longer.
  1034.             if (DecoderFallback.MaxCharCount > 1)
  1035.                 charCount *= DecoderFallback.MaxCharCount;
  1036.            
  1037.             if (charCount > 2147483647)
  1038.                 throw new ArgumentOutOfRangeException("byteCount", Environment.GetResourceString("ArgumentOutOfRange_GetCharCountOverflow"));
  1039.            
  1040.             return (int)charCount;
  1041.         }
  1042.        
  1043.         public override Decoder GetDecoder()
  1044.         {
  1045.             return new DBCSDecoder(this);
  1046.         }
  1047.        
  1048.         [Serializable()]
  1049.         internal class DBCSDecoder : DecoderNLS
  1050.         {
  1051.             // Need a place for the last left over byte
  1052.             internal byte bLeftOver = 0;
  1053.            
  1054.             public DBCSDecoder(DBCSCodePageEncoding encoding) : base(encoding)
  1055.             {
  1056.                 // Base calls reset
  1057.             }
  1058.            
  1059.             public override void Reset()
  1060.             {
  1061.                 this.bLeftOver = 0;
  1062.                 if (m_fallbackBuffer != null)
  1063.                     m_fallbackBuffer.Reset();
  1064.             }
  1065.            
  1066.             // Anything left in our decoder?
  1067.             internal override bool HasState {
  1068.                 get { return (this.bLeftOver != 0); }
  1069.             }
  1070.         }
  1071.     }
  1072. }

Developer Fusion