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

  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. // ISO2022Encoding.cs
  16. //
  17. // Ported to managed code from c_is2022.c and related iso 2022 dll files from mlang
  18. //
  19. // Abstract:
  20. //
  21. // Managed implimentation of ISO 2022 code pages, ported from the implimentation in c_is2022.dll
  22. // This code should be kept in sync with the other implimentations
  23. // This encoding wraps the basic encodings in code that adds the shift in/out wrapper methods
  24. //
  25. // Notes:
  26. //
  27. // IsAlwaysNormalized ???
  28. // Regarding Normalization for ISO-2022-JP (50220, 50221, 50222), its the same rules as EUCJP
  29. // Forms KC & KD are precluded because of things like halfwidth Katakana that has compatibility mappings
  30. // Form D is precluded because of 0x00a8, which changes to space + dierises.
  31. //
  32. // Note: I think that IsAlwaysNormalized should probably return true for form C for Japanese 20932 based CPs.
  33. //
  34. // For ISO-2022-KR
  35. // Never normalized, C & D (& therefore KC & KD) are precluded because of Hangul syllables and combined characters.
  36. //
  37. // IsAlwaysNormalized ???
  38. // Regarding Normalization for ISO-2022-CN (50227, 50229) & HZ-GB2312 (52936) I think is similar to the Japanese case.
  39. // Forms KC & KD are precluded because of things like halfwidth Katakana that has compatibility mappings
  40. // Form D is precluded because of 0x00a8, which changes to space + dierises.
  41. //
  42. // Note: I think that IsAlwaysNormalized should probably return true for form C for Chinese 20936 based CPs.
  43. //
  44. namespace System.Text
  45. {
  46.     using System.Globalization;
  47.     using System.Text;
  48.     using System.Runtime.InteropServices;
  49.     using System;
  50.     using System.Security;
  51.     using System.Runtime.CompilerServices;
  52.     using System.Runtime.Serialization;
  53.    
  54.    
  55. /*=================================ISO2022Encoding============================
  56.     **
  57.     ** This is used to support ISO 2022 encodings that use shift/escape sequences.
  58.     **
  59.     ==============================================================================*/   
  60.    
  61.     [Serializable()]
  62.     internal class ISO2022Encoding : DBCSCodePageEncoding
  63.     {
  64.         const byte SHIFT_OUT = (byte)14;
  65.         const byte SHIFT_IN = (byte)15;
  66.         const byte ESCAPE = 27;
  67.         const byte LEADBYTE_HALFWIDTH = 16;
  68.        
  69.         // We have to load the 936 code page tables, so impersonate 936 as our base
  70.         // This pretends to be other code pages as far as memory sections are concerned.
  71.         internal ISO2022Encoding(int codePage) : base(codePage, tableBaseCodePages[codePage % 10])
  72.         {
  73.             this.m_bUseMlangTypeForSerialization = true;
  74.         }
  75.        
  76.         // Constructor called by serialization.
  77.         // Note: We use the base GetObjectData however
  78.         internal ISO2022Encoding(SerializationInfo info, StreamingContext context) : base(info, context)
  79.         {
  80.             // Actually this can't ever get called, CodePageEncoding is our proxy
  81.             BCLDebug.Assert(false, "Didn't expect to make it to DBCSCodePageEncoding serialization constructor");
  82.             throw new ArgumentException(Environment.GetResourceString("Arg_ExecutionEngineException"));
  83.         }
  84.        
  85.         static int[] tableBaseCodePages = {932, 932, 932, 0, 0, 949, 936, 0, 0, 0,
  86.             // 50220 ISO-2022-JP, No halfwidth Katakana, convert to full width
  87.             // 50221 ISO-2022-JP, Use escape sequence for half width Katakana
  88.             // 50222 ISO-2022-JP, Use shift-in/shift-out for half width Katakana
  89.             // 50225 ISO-2022-KR, Korean
  90.             // 52936 HZ-GB2312, 936 might be better source
  91.             // 50229 is currently unsupported, CP 20000 is currently not built in .nlp file
  92.             //20000, // 50229 ISO-2022-CN, ModeCNS11643_1
  93.             //20000, // 50229 ISO-2022-CN, ModeCNS11643_2
  94.             // ModeASCII
  95.         0, 0};
  96.        
  97.         internal enum ISO2022Modes
  98.         {
  99.             ModeHalfwidthKatakana = 0,
  100.             ModeJIS0208 = 1,
  101.             ModeKR = 5,
  102.             ModeHZ = 6,
  103.             ModeGB2312 = 7,
  104.             ModeCNS11643_1 = 9,
  105.             ModeCNS11643_2 = 10,
  106.             ModeASCII = 11,
  107.            
  108.             ModeIncompleteEscape = -1,
  109.             ModeInvalidEscape = -2,
  110.             ModeNOOP = -3
  111.         }
  112.        
  113.         unsafe protected override string GetMemorySectionName()
  114.         {
  115.             int iUseCodePage = this.bFlagDataTable ? dataTableCodePage : CodePage;
  116.            
  117.             string strFormat;
  118.            
  119.             switch (this.CodePage) {
  120.                 case 50220:
  121.                 case 50221:
  122.                 case 50222:
  123.                     strFormat = "CodePage_{0}_{1}_{2}_{3}_{4}_ISO2022JP";
  124.                     break;
  125.                 case 50225:
  126.                     strFormat = "CodePage_{0}_{1}_{2}_{3}_{4}_ISO2022KR";
  127.                     break;
  128.                 case 52936:
  129.                     strFormat = "CodePage_{0}_{1}_{2}_{3}_{4}_HZ";
  130.                     break;
  131.                 default:
  132.                     BCLDebug.Assert(false, "[ISO2022Encoding.GetMemorySectionName] Don't expect to get here for code page " + this.CodePage);
  133.                     strFormat = "CodePage_{0}_{1}_{2}_{3}_{4}";
  134.                     break;
  135.             }
  136.            
  137.             string strName = String.Format(CultureInfo.InvariantCulture, strFormat, iUseCodePage, this.pCodePage->VersionMajor, this.pCodePage->VersionMinor, this.pCodePage->VersionRevision, this.pCodePage->VersionBuild);
  138.            
  139.             return strName;
  140.         }
  141.        
  142.         // Clean up characters for ISO2022 code pages, etc.
  143.         // ISO2022 (50220, 50221, 50222)
  144.         // GB-HZ (52936)
  145.         protected override bool CleanUpBytes(ref int bytes)
  146.         {
  147.             switch (this.CodePage) {
  148.                 case 50220:
  149.                 case 50221:
  150.                 case 50222:
  151.                     // 932 based code pages
  152.                    
  153.                     {
  154.                         if (bytes >= 256) {
  155.                             // map extended char (0xfa40-0xfc4b) to a special range
  156.                             // (ported from mlang)
  157.                             if (bytes >= 64064 && bytes <= 64587) {
  158.                                 if (bytes >= 64064 && bytes <= 64091) {
  159.                                     if (bytes <= 64073)
  160.                                         bytes = bytes - 2897;
  161.                                     else if (bytes >= 64074 && bytes <= 64083)
  162.                                         bytes = bytes - 29430;
  163.                                     else if (bytes >= 64084 && bytes <= 64087)
  164.                                         bytes = bytes - 2907;
  165.                                     else if (bytes == 64088)
  166.                                         bytes = 34698;
  167.                                     else if (bytes == 64089)
  168.                                         bytes = 34690;
  169.                                     else if (bytes == 64090)
  170.                                         bytes = 34692;
  171.                                     else if (bytes == 64091)
  172.                                         bytes = 34714;
  173.                                 }
  174.                                 else if (bytes >= 64092 && bytes <= 64587) {
  175.                                     byte tc = unchecked((byte)bytes);
  176.                                     if (tc < 92)
  177.                                         bytes = bytes - 3423;
  178.                                     else if (tc >= 128 && tc <= 155)
  179.                                         bytes = bytes - 3357;
  180.                                     else
  181.                                         bytes = bytes - 3356;
  182.                                 }
  183.                             }
  184.                            
  185.                             // Convert 932 code page to 20932 like code page range
  186.                             // (also ported from mlang)
  187.                             byte bLead = unchecked((byte)(bytes >> 8));
  188.                             byte bTrail = unchecked((byte)bytes);
  189.                            
  190.                             bLead -= ((bLead > (byte)159) ? (byte)177 : (byte)113);
  191.                             bLead = (byte)((bLead << 1) + 1);
  192.                             if (bTrail > (byte)158) {
  193.                                 bTrail -= (byte)126;
  194.                                 bLead++;
  195.                             }
  196.                             else {
  197.                                 if (bTrail > (byte)126)
  198.                                     bTrail--;
  199.                                 bTrail -= (byte)31;
  200.                             }
  201.                            
  202.                             bytes = ((int)bLead) << 8 | (int)bTrail;
  203.                            
  204.                         }
  205.                         else {
  206.                             // Adjust 1/2 Katakana
  207.                             if (bytes >= 161 && bytes <= 223)
  208.                                 bytes += (LEADBYTE_HALFWIDTH << 8) - 128;
  209.                            
  210.                             // 0x81-0x9f and 0xe0-0xfc CP 932
  211.                             // 0x8e and 0xa1-0xfe CP 20932 (we don't use 8e though)
  212.                             // b0-df is 1/2 Katakana
  213.                             if (bytes >= 129 && (bytes <= 159 || (bytes >= 224 && bytes <= 252))) {
  214.                                 // Don't do lead bytes, we use escape sequences instead.
  215.                                 return false;
  216.                             }
  217.                         }
  218.                         break;
  219.                     }
  220.                     break;
  221.                 case 50225:
  222.                    
  223.                     {
  224.                         // For 50225 since we don't rely on lead byte marks, return false and don't add them,
  225.                         // esp. since we're only a 7 bit code page.
  226.                         if (bytes >= 128 && bytes <= 255)
  227.                             return false;
  228.                        
  229.                         // Ignore characters out of range (a1-7f)
  230.                         if (bytes >= 256 && ((bytes & 255) < 161 || (bytes & 255) == 255 || (bytes & 65280) < 41216 || (bytes & 65280) == 65280))
  231.                             return false;
  232.                        
  233.                         // May as well get them into our 7 bit range
  234.                         bytes &= 32639;
  235.                        
  236.                         break;
  237.                     }
  238.                     break;
  239.                 case 52936:
  240.                    
  241.                     {
  242.                         // Since we don't rely on lead byte marks for 52936, get rid of them so we
  243.                         // don't end up with extra wierd fffe mappings.
  244.                         if (bytes >= 129 && bytes <= 254)
  245.                             return false;
  246.                        
  247.                         break;
  248.                     }
  249.                     break;
  250.             }
  251.            
  252.             return true;
  253.         }
  254.        
  255.         // GetByteCount
  256.         unsafe internal override int GetByteCount(char* chars, int count, EncoderNLS baseEncoder)
  257.         {
  258.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  259.             BCLDebug.Assert(count >= 0, "[ISO2022Encoding.GetByteCount]count is negative");
  260.             BCLDebug.Assert(chars != null, "[ISO2022Encoding.GetByteCount]chars is null");
  261.            
  262.             // Just call GetBytes with null byte* to get count
  263.             return GetBytes(chars, count, null, 0, baseEncoder);
  264.         }
  265.        
  266.         unsafe internal override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS baseEncoder)
  267.         {
  268.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  269.             BCLDebug.Assert(chars != null, "[ISO2022Encoding.GetBytes]chars is null");
  270.             BCLDebug.Assert(byteCount >= 0, "[ISO2022Encoding.GetBytes]byteCount is negative");
  271.             BCLDebug.Assert(charCount >= 0, "[ISO2022Encoding.GetBytes]charCount is negative");
  272.            
  273.             // Assert because we shouldn't be able to have a null encoder.
  274.             BCLDebug.Assert(encoderFallback != null, "[ISO2022Encoding.GetBytes]Attempting to use null encoder fallback");
  275.            
  276.             // Fix our encoder
  277.             ISO2022Encoder encoder = (ISO2022Encoder)baseEncoder;
  278.            
  279.             // Our return value
  280.             int iCount = 0;
  281.            
  282.             switch (CodePage) {
  283.                 case 50220:
  284.                 case 50221:
  285.                 case 50222:
  286.                     iCount = GetBytesCP5022xJP(chars, charCount, bytes, byteCount, encoder);
  287.                     break;
  288.                 case 50225:
  289.                     iCount = GetBytesCP50225KR(chars, charCount, bytes, byteCount, encoder);
  290.                     break;
  291.                 case 52936:
  292.                     /*              case 50227:
  293.                     iCount = GetBytesCP50227CN( chars, charCount, bytes, byteCount, encoder );
  294.                     break;
  295. */                   
  296. iCount = GetBytesCP52936(chars, charCount, bytes, byteCount, encoder);
  297.                     break;
  298.             }
  299.            
  300.             return iCount;
  301.         }
  302.        
  303.         // This is internal and called by something else,
  304.         unsafe internal override int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
  305.         {
  306.             // Just assert, we're called internally so these should be safe, checked already
  307.             BCLDebug.Assert(bytes != null, "[ISO2022Encoding.GetCharCount]bytes is null");
  308.             BCLDebug.Assert(count >= 0, "[ISO2022Encoding.GetCharCount]byteCount is negative");
  309.            
  310.             // Just call getChars with null char* to get count
  311.             return GetChars(bytes, count, null, 0, baseDecoder);
  312.         }
  313.        
  314.         unsafe internal override int GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder)
  315.         {
  316.             // Just need to ASSERT, this is called by something else internal that checked parameters already
  317.             BCLDebug.Assert(bytes != null, "[ISO2022Encoding.GetChars]bytes is null");
  318.             BCLDebug.Assert(byteCount >= 0, "[ISO2022Encoding.GetChars]byteCount is negative");
  319.             BCLDebug.Assert(charCount >= 0, "[ISO2022Encoding.GetChars]charCount is negative");
  320.            
  321.             // Fix our decoder
  322.             ISO2022Decoder decoder = (ISO2022Decoder)baseDecoder;
  323.             int iCount = 0;
  324.            
  325.             switch (CodePage) {
  326.                 case 50220:
  327.                 case 50221:
  328.                 case 50222:
  329.                     iCount = GetCharsCP5022xJP(bytes, byteCount, chars, charCount, decoder);
  330.                     break;
  331.                 case 50225:
  332.                     iCount = GetCharsCP50225KR(bytes, byteCount, chars, charCount, decoder);
  333.                     break;
  334.                 case 52936:
  335.                     // Currently 50227 is the same as 936
  336.                     // case 50227:
  337.                     // iCount = GetCharsCP50227CN( bytes, byteCount, chars, charCount, decoder);
  338.                     // break;
  339.                     iCount = GetCharsCP52936(bytes, byteCount, chars, charCount, decoder);
  340.                     break;
  341.                 default:
  342.                     BCLDebug.Assert(false, "[ISO2022Encoding.GetChars] had unexpected code page");
  343.                     break;
  344.             }
  345.            
  346.             return iCount;
  347.         }
  348.        
  349.         // ISO 2022 Code pages for JP.
  350.         // 50220 - No halfwidth Katakana, convert to full width
  351.         // 50221 - Use escape sequence for half width Katakana
  352.         // 50222 - Use shift-in/shift-out for half width Katakana
  353.         //
  354.         // These are the JIS code pages, superset of ISO-2022 / ISO-2022-JP-1
  355.         // 0E Shift Out (following bytes are Katakana)
  356.         // 0F Shift In (back to "normal" behavior)
  357.         // 21-7E Byte ranges (1 or 2 bytes)
  358.         // <ESC> $ @ To Double Byte 0208 Mode (actually older code page, but subset of 0208)
  359.         // <ESC> $ B To Double Byte 0208 Mode (duplicate)
  360.         // <ESC> $ ( D To Double Byte 0212 Mode (previously we misinterpreted this)
  361.         // <ESC> $ I To half width Katakana
  362.         // <ESC> ( J To JIS-Roman
  363.         // <ESC> ( H To JIS-Roman (swedish character set)
  364.         // <ESC> ( B To ASCII
  365.         // <ESC> & @ Alternate lead in to <ESC> $ B so just ignore it.
  366.         //
  367.         // So in Katakana mode we add 0x8e as a lead byte and use CP 20932 to convert it
  368.         // In ASCII mode we just spit out the single byte.
  369.         // In Roman mode we should change 0x5c (\) -> Yen sign and 0x7e (~) to Overline, however
  370.         // we didn't in mLang, otherwise roman is like ASCII.
  371.         // In 0208 double byte mode we have to |= with 0x8080 and use CP 20932 to convert it.
  372.         // In 0212 double byte mode we have to |= with 0x8000 and use CP 20932 to convert it.
  373.         //
  374.         // Note that JIS Shift In/Shift Out is different than the other ISO2022 encodings. For JIS
  375.         // Shift out always shifts to half-width Katakana. Chinese encodings use designator sequences
  376.         // instead of escape sequences and shift out to the designated sequence or back in to ASCII.
  377.         //
  378.         // When decoding JIS 0208, MLang used a '*' (0x2a) character in JIS 0208 mode to map the trailing byte
  379.         // to halfwidth katakana. I found no description of that behavior, however that block of 0208 is
  380.         // undefined, so we maintain that behavior when decoding. We will never generate characters using
  381.         // that technique, but the decoder will process them.
  382.         //
  383.         unsafe private int GetBytesCP5022xJP(char* chars, int charCount, byte* bytes, int byteCount, ISO2022Encoder encoder)
  384.         {
  385.             // prepare our helpers
  386.             Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer(this, encoder, bytes, byteCount, chars, charCount);
  387.            
  388.             // Get our mode
  389.             ISO2022Modes currentMode = ISO2022Modes.ModeASCII;
  390.             // Mode
  391.             ISO2022Modes shiftInMode = ISO2022Modes.ModeASCII;
  392.             // Mode that shift in will go back to (only used by CP 50222)
  393.             // Check our encoder
  394.             if (encoder != null) {
  395.                 char charLeftOver = encoder.charLeftOver;
  396.                
  397.                 currentMode = encoder.currentMode;
  398.                 shiftInMode = encoder.shiftInOutMode;
  399.                
  400.                 // We may have a left over character from last time, try and process it.
  401.                 if (charLeftOver > 0) {
  402.                     BCLDebug.Assert(Char.IsHighSurrogate(charLeftOver), "[ISO2022Encoding.GetBytesCP5022xJP]leftover character should be high surrogate");
  403.                    
  404.                     // It has to be a high surrogate, which we don't support, so it has to be a fallback
  405.                     buffer.Fallback(charLeftOver);
  406.                 }
  407.             }
  408.            
  409.             while (buffer.MoreData) {
  410.                 // Get our char
  411.                 char ch = buffer.GetNextChar();
  412.                
  413.                 // Get our bytes
  414.                 ushort iBytes = mapUnicodeToBytes[ch];
  415.                 StartConvert:
  416.                
  417.                 // Check for halfwidth bytes
  418.                 byte bLeadByte = (byte)(iBytes >> 8);
  419.                 byte bTrailByte = (byte)(iBytes & 255);
  420.                
  421.                 if (bLeadByte == LEADBYTE_HALFWIDTH) {
  422.                     // Its Halfwidth Katakana
  423.                     if (CodePage == 50220) {
  424.                         // CodePage 50220 doesn't use halfwidth Katakana, convert to fullwidth
  425.                         // See if its out of range, fallback if so, throws if recursive fallback
  426.                         if (bTrailByte < 33 || bTrailByte >= 33 + HalfToFullWidthKanaTable.Length) {
  427.                             buffer.Fallback(ch);
  428.                             continue;
  429.                         }
  430.                        
  431.                         // Get the full width katakana char to use.
  432.                         iBytes = unchecked((ushort)(HalfToFullWidthKanaTable[bTrailByte - 33] & 32639));
  433.                        
  434.                         // May have to do all sorts of fun stuff for mode, go back to start convert
  435.                         goto StartConvert;
  436.                     }
  437.                    
  438.                     // Can use halfwidth Katakana, make sure we're in right mode
  439.                    
  440.                     // Make sure we're in right mode
  441.                     if (currentMode != ISO2022Modes.ModeHalfwidthKatakana) {
  442.                         // 50222 or 50221, either shift in/out or escape to get to Katakana mode
  443.                         if (CodePage == 50222) {
  444.                             // Shift Out
  445.                             if (!buffer.AddByte(SHIFT_OUT))
  446.                                 break;
  447.                             // convert out of space, stop
  448.                             // Don't change modes until after AddByte in case it fails for convert
  449.                             // We get to shift out to Katakana, make sure we'll go back to the right mode
  450.                             // (This ends up always being ASCII)
  451.                             shiftInMode = currentMode;
  452.                             currentMode = ISO2022Modes.ModeHalfwidthKatakana;
  453.                         }
  454.                         else {
  455.                             // 50221 does halfwidth katakana by escape sequence
  456.                             BCLDebug.Assert(CodePage == 50221, "[ISO2022Encoding.GetBytesCP5022xJP]Expected Code Page 50221");
  457.                            
  458.                             // Add our escape sequence
  459.                             if (!buffer.AddByte(ESCAPE, unchecked((byte)'('), unchecked((byte)'I')))
  460.                                 break;
  461.                             // convert out of space, stop
  462.                             currentMode = ISO2022Modes.ModeHalfwidthKatakana;
  463.                         }
  464.                     }
  465.                    
  466.                     // We know we're in Katakana mode now, so add it.
  467.                     // Go ahead and add the Katakana byte. Our table tail bytes are 0x80 too big.
  468.                     if (!buffer.AddByte(unchecked((byte)(bTrailByte & 127))))
  469.                         break;
  470.                     // convert out of space, stop
  471.                     // Done with this one
  472.                     continue;
  473.                 }
  474.                 else if (bLeadByte != 0) {
  475.                     //
  476.                     // It's a double byte character.
  477.                     //
  478.                    
  479.                     // If we're CP 50222 we may have to shift in from Katakana mode first
  480.                     if (CodePage == 50222 && currentMode == ISO2022Modes.ModeHalfwidthKatakana) {
  481.                         // Shift In
  482.                         if (!buffer.AddByte(SHIFT_IN))
  483.                             break;
  484.                         // convert out of space, stop
  485.                         // Need to shift in from katakana. (Still might not be right, but won't be shifted out anyway)
  486.                         currentMode = shiftInMode;
  487.                     }
  488.                    
  489.                    
  490.                     if (currentMode != ISO2022Modes.ModeJIS0208) {
  491.                         // Escape sequence, we can fail after this, mode will be correct for convert
  492.                         if (!buffer.AddByte(ESCAPE, unchecked((byte)'$'), unchecked((byte)'B')))
  493.                             break;
  494.                         // Convert out of space, stop
  495.                         currentMode = ISO2022Modes.ModeJIS0208;
  496.                     }
  497.                    
  498.                     // Add our double bytes
  499.                     if (!buffer.AddByte(unchecked((byte)(bLeadByte)), unchecked((byte)(bTrailByte))))
  500.                         break;
  501.                     // Convert out of space, stop
  502.                     continue;
  503.                 }
  504.                 else if (iBytes != 0 || ch == 0) {
  505.                     // Single byte Char
  506.                     // If we're CP 50222 we may have to shift in from Katakana mode first
  507.                     if (CodePage == 50222 && currentMode == ISO2022Modes.ModeHalfwidthKatakana) {
  508.                         // Shift IN
  509.                         if (!buffer.AddByte(SHIFT_IN))
  510.                             break;
  511.                         // convert ran out of room
  512.                         // Need to shift in from katakana. (Still might not be right, but won't be shifted out anyway)
  513.                         currentMode = shiftInMode;
  514.                     }
  515.                    
  516.                     // Its a single byte character, switch to ASCII if we have to
  517.                     if (currentMode != ISO2022Modes.ModeASCII) {
  518.                         if (!buffer.AddByte(ESCAPE, unchecked((byte)'('), unchecked((byte)'B')))
  519.                             break;
  520.                         // convert ran out of room
  521.                         currentMode = ISO2022Modes.ModeASCII;
  522.                     }
  523.                    
  524.                     // Add the ASCII char
  525.                     if (!buffer.AddByte(bTrailByte))
  526.                         break;
  527.                     // convert had no room left
  528.                     continue;
  529.                 }
  530.                
  531.                 // Its unknown, do fallback, throws if recursive (knows because we called InternalGetNextChar)
  532.                 buffer.Fallback(ch);
  533.             }
  534.            
  535.             // Switch back to ASCII if MustFlush or no encoder
  536.             if (currentMode != ISO2022Modes.ModeASCII && (encoder == null || encoder.MustFlush)) {
  537.                 // If we're CP 50222 we may have to shift in from Katakana mode first
  538.                 if (CodePage == 50222 && currentMode == ISO2022Modes.ModeHalfwidthKatakana) {
  539.                     // Shift IN, only shift mode if necessary.
  540.                     if (buffer.AddByte(SHIFT_IN))
  541.                         currentMode = shiftInMode;
  542.                     else
  543.                         // Need to shift in from katakana. (Still might not be right, but won't be shifted out anyway)
  544.                         // If not successful, convert will maintain state for next time, also
  545.                         // AddByte will have decremented our char count, however we need it to remain the same
  546.                         buffer.GetNextChar();
  547.                 }
  548.                
  549.                 // switch back to ASCII to finish neatly
  550.                 if (currentMode != ISO2022Modes.ModeASCII && (CodePage != 50222 || currentMode != ISO2022Modes.ModeHalfwidthKatakana)) {
  551.                     // only shift if it was successful
  552.                     if (buffer.AddByte(ESCAPE, unchecked((byte)'('), unchecked((byte)'B')))
  553.                         currentMode = ISO2022Modes.ModeASCII;
  554.                     else
  555.                         // If not successful, convert will maintain state for next time, also
  556.                         // AddByte will have decremented our char count, however we need it to remain the same
  557.                         buffer.GetNextChar();
  558.                 }
  559.             }
  560.            
  561.             // Remember our encoder state
  562.             if (bytes != null && encoder != null) {
  563.                 // This is ASCII if we had to flush
  564.                 encoder.currentMode = currentMode;
  565.                 encoder.shiftInOutMode = shiftInMode;
  566.                
  567.                 if (!buffer.fallbackBuffer.bUsedEncoder) {
  568.                     encoder.charLeftOver = (char)0;
  569.                 }
  570.                
  571.                 encoder.m_charsUsed = buffer.CharsUsed;
  572.             }
  573.            
  574.             // Return our length
  575.             return buffer.Count;
  576.         }
  577.        
  578.         // ISO 2022 Code pages for Korean - CP 50225
  579.         //
  580.         // CP 50225 has Shift In/Shift Out codes, and a single designator sequence that is supposed
  581.         // to appear once in the file, at the beginning of a line, before any multibyte code points.
  582.         // So we stick the designator at the beginning of the output.
  583.         //
  584.         // These are the KR code page codes for ISO-2022-KR
  585.         // 0E Shift Out (following bytes are double byte)
  586.         // 0F Shift In (back to ASCII behavior)
  587.         // 21-7E Byte ranges (1 or 2 bytes)
  588.         // <ESC> $)C Double byte ISO-2022-KR designator
  589.         //
  590.         // Note that this encoding is a little different than other encodings. The <esc>$)C sequence
  591.         // should only appear once per file. (Actually I saw another spec/rfc that said at the beginning
  592.         // of each line, but it shouldn't really matter.)
  593.         //
  594.         // During decoding Mlang accepted ' ', '\t, and '\n' as their respective characters, even if
  595.         // it was in double byte mode. We maintain that behavior, although I couldn't find a reference or
  596.         // reason for that behavior. We never generate data using that shortcut.
  597.         //
  598.         // Also Mlang always assumed KR mode, even if the designator wasn't found yet, so we do that as
  599.         // well. So basically we just ignore <ESC>$)C when decoding.
  600.         //
  601.         unsafe private int GetBytesCP50225KR(char* chars, int charCount, byte* bytes, int byteCount, ISO2022Encoder encoder)
  602.         {
  603.             // prepare our helpers
  604.             Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer(this, encoder, bytes, byteCount, chars, charCount);
  605.            
  606.             // Get our mode
  607.             ISO2022Modes currentMode = ISO2022Modes.ModeASCII;
  608.             // Mode
  609.             ISO2022Modes shiftOutMode = ISO2022Modes.ModeASCII;
  610.             // ModeKR if already stamped lead bytes
  611.             // Check our encoder
  612.             if (encoder != null) {
  613.                 // May have leftover stuff
  614.                 char charLeftOver = encoder.charLeftOver;
  615.                 currentMode = encoder.currentMode;
  616.                 shiftOutMode = encoder.shiftInOutMode;
  617.                
  618.                 // We may have a l left over character from last time, try and process it.
  619.                 if (charLeftOver > 0) {
  620.                     BCLDebug.Assert(Char.IsHighSurrogate(charLeftOver), "[ISO2022Encoding.GetBytesCP50225KR]leftover character should be high surrogate");
  621.                    
  622.                     // It has to be a high surrogate, which we don't support, so it has to be a fallback
  623.                     buffer.Fallback(charLeftOver);
  624.                 }
  625.             }
  626.            
  627.             while (buffer.MoreData) {
  628.                 // Get our data
  629.                 char ch = buffer.GetNextChar();
  630.                
  631.                 // Get our bytes
  632.                 ushort iBytes = mapUnicodeToBytes[ch];
  633.                
  634.                 // Check for double byte bytes
  635.                 byte bLeadByte = (byte)(iBytes >> 8);
  636.                 byte bTrailByte = (byte)(iBytes & 255);
  637.                
  638.                 if (bLeadByte != 0) {
  639.                     //
  640.                     // It's a double byte character.
  641.                     //
  642.                    
  643.                     // If we haven't done our Korean designator, then do so, if we have any input
  644.                     if (shiftOutMode != ISO2022Modes.ModeKR) {
  645.                         // Add our code page designator sequence
  646.                         if (!buffer.AddByte(ESCAPE, unchecked((byte)'$'), unchecked((byte)')'), unchecked((byte)'C')))
  647.                             break;
  648.                         // No room during convert.
  649.                         shiftOutMode = ISO2022Modes.ModeKR;
  650.                     }
  651.                    
  652.                     // May have to switch to ModeKR first
  653.                     if (currentMode != ISO2022Modes.ModeKR) {
  654.                         if (!buffer.AddByte(SHIFT_OUT))
  655.                             break;
  656.                         // No convert room
  657.                         currentMode = ISO2022Modes.ModeKR;
  658.                     }
  659.                    
  660.                     // Add the bytes
  661.                     if (!buffer.AddByte(bLeadByte, bTrailByte))
  662.                         break;
  663.                     // no convert room
  664.                     continue;
  665.                 }
  666.                 else if (iBytes != 0 || ch == 0) {
  667.                     // Its a single byte character, switch to ASCII if we have to
  668.                     if (currentMode != ISO2022Modes.ModeASCII) {
  669.                         if (!buffer.AddByte(SHIFT_IN))
  670.                             break;
  671.                        
  672.                         currentMode = ISO2022Modes.ModeASCII;
  673.                     }
  674.                    
  675.                     // Add the ASCII char
  676.                     if (!buffer.AddByte(bTrailByte))
  677.                         break;
  678.                     continue;
  679.                 }
  680.                
  681.                 // Its unknown, do fallback, throws if recursive (knows because we called InternalGetNextChar)
  682.                 buffer.Fallback(ch);
  683.             }
  684.            
  685.             // Switch back to ASCII if MustFlush or no encoder
  686.             if (currentMode != ISO2022Modes.ModeASCII && (encoder == null || encoder.MustFlush)) {
  687.                 // Get back to ASCII to be safe. Only do it if it success.
  688.                 if (buffer.AddByte(SHIFT_IN))
  689.                     currentMode = ISO2022Modes.ModeASCII;
  690.                 else
  691.                     // If not successful, convert will maintain state for next time, also
  692.                     // AddByte will have decremented our char count, however we need it to remain the same
  693.                     buffer.GetNextChar();
  694.             }
  695.            
  696.             // Remember our encoder state
  697.             if (bytes != null && encoder != null) {
  698.                 // If we didn't use the encoder, then there's no chars left over
  699.                 if (!buffer.fallbackBuffer.bUsedEncoder) {
  700.                     encoder.charLeftOver = (char)0;
  701.                 }
  702.                
  703.                 // This is ASCII if we had to flush
  704.                 encoder.currentMode = currentMode;
  705.                
  706.                 // We don't use shift out mode, but if we've flushed we need to reset it so it doesn't
  707.                 // get output again.
  708.                 if (!encoder.MustFlush || encoder.charLeftOver != (char)0) {
  709.                     // We should be not flushing or converting
  710.                     BCLDebug.Assert(!encoder.MustFlush || !encoder.m_throwOnOverflow, "[ISO2022Encoding.GetBytesCP50225KR]Expected no left over data or not flushing or not converting");
  711.                     encoder.shiftInOutMode = shiftOutMode;
  712.                 }
  713.                 else
  714.                     encoder.shiftInOutMode = ISO2022Modes.ModeASCII;
  715.                
  716.                 encoder.m_charsUsed = buffer.CharsUsed;
  717.             }
  718.            
  719.             // Return our length
  720.             return buffer.Count;
  721.         }
  722.        
  723.         // CP52936 is HZ Encoding
  724.         // HZ Encoding has 4 shift sequences:
  725.         // ~~ '~' (\u7e)
  726.         // ~} shift into 1 byte mode,
  727.         // ~{ shift into 2 byte GB 2312-80
  728.         // ~<NL> Maintain 2 byte mode across new lines (ignore both ~ and <NL> characters)
  729.         // (This is for mailers that restrict to 70 or 80 or whatever character lines)
  730.         //
  731.         // According to comment in mlang, lead & trail byte ranges are described in RFC 1843
  732.         // RFC 1843 => valid HZ code range: leading byte 0x21 - 0x77, 2nd byte 0x21 - 0x7e
  733.         // Our 936 code points are or'd with 0x8080, so lead byte 0xa1 - 0xf7, trail byte 0xa1 - 0xfe
  734.         //
  735.         // This encoding is designed for transmission by e-mail and news. No bytes should have high bit set.
  736.         // (all bytes <= 0x7f)
  737.         unsafe private int GetBytesCP52936(char* chars, int charCount, byte* bytes, int byteCount, ISO2022Encoder encoder)
  738.         {
  739.             // prepare our helpers
  740.             Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer(this, encoder, bytes, byteCount, chars, charCount);
  741.            
  742.             // Mode
  743.             ISO2022Modes currentMode = ISO2022Modes.ModeASCII;
  744.            
  745.             // Check our encoder
  746.             if (encoder != null) {
  747.                 char charLeftOver = encoder.charLeftOver;
  748.                 currentMode = encoder.currentMode;
  749.                
  750.                 // We may have a left over character from last time, try and process it.
  751.                 if (charLeftOver > 0) {
  752.                     BCLDebug.Assert(Char.IsHighSurrogate(charLeftOver), "[ISO2022Encoding.GetBytesCP52936]leftover character should be high surrogate");
  753.                    
  754.                     // It has to be a high surrogate, which we don't support, so it has to be a fallback
  755.                     buffer.Fallback(charLeftOver);
  756.                 }
  757.             }
  758.            
  759.             while (buffer.MoreData) {
  760.                 // Get our char
  761.                 char ch = buffer.GetNextChar();
  762.                
  763.                 // Get our bytes
  764.                 ushort sChar = mapUnicodeToBytes[ch];
  765.                 if (sChar == 0 && ch != 0) {
  766.                     // Wasn't a legal byte sequence, its a surrogate or fallback
  767.                     // Throws if recursive (knows because we called InternalGetNextChar)
  768.                     buffer.Fallback(ch);
  769.                    
  770.                     // Done with our char, now process fallback
  771.                     continue;
  772.                 }
  773.                
  774.                 // Check for halfwidth bytes
  775.                 byte bLeadByte = (byte)(sChar >> 8);
  776.                 byte bTrailByte = (byte)(sChar & 255);
  777.                
  778.                 // If its a double byte, it has to fit in the lead byte 0xa1 - 0xf7, trail byte 0xa1 - 0xfe range
  779.                 // (including the 0x8080 that our codepage or's to the value)
  780.                 if ((bLeadByte != 0 && (bLeadByte < 161 || bLeadByte > 247 || bTrailByte < 161 || bTrailByte > 254)) || (bLeadByte == 0 && bTrailByte > 128 && bTrailByte != 255)) {
  781.                     // Illegal character, in 936 code page, but not in HZ subset, get fallback for it
  782.                     buffer.Fallback(ch);
  783.                     continue;
  784.                 }
  785.                
  786.                 // sChar is now either ASCII or has an 0x8080 mask
  787.                 if (bLeadByte != 0) {
  788.                     // Its a double byte mode
  789.                     if (currentMode != ISO2022Modes.ModeHZ) {
  790.                         // Need to add the double byte mode marker
  791.                         if (!buffer.AddByte((byte)'~', (byte)'{', 2))
  792.                             break;
  793.                         // Stop if no buffer space in convert
  794.                         currentMode = ISO2022Modes.ModeHZ;
  795.                     }
  796.                    
  797.                     // Go ahead and add the 2 bytes
  798.                     if (!buffer.AddByte(unchecked((byte)(bLeadByte & 127)), unchecked((byte)(bTrailByte & 127))))
  799.                         break;
  800.                     // Stop if no buffer space in convert
  801.                 }
  802.                 else {
  803.                     // Its supposed to be ASCII
  804.                     if (currentMode != ISO2022Modes.ModeASCII) {
  805.                         // Need to add the ASCII mode marker
  806.                         // Will have 1 more byte (or 2 if ~)
  807.                         if (!buffer.AddByte((byte)'~', (byte)'}', bTrailByte == '~' ? 2 : 1))
  808.                             break;
  809.                        
  810.                         currentMode = ISO2022Modes.ModeASCII;
  811.                     }
  812.                    
  813.                     // If its a '~' we'll need an extra one
  814.                     if (bTrailByte == '~') {
  815.                         // Need to add the extra ~
  816.                         if (!buffer.AddByte((byte)'~', 1))
  817.                             break;
  818.                     }
  819.                    
  820.                     // Need to add the character
  821.                     if (!buffer.AddByte(bTrailByte))
  822.                         break;
  823.                 }
  824.             }
  825.            
  826.             // Add ASCII shift out if we're at end of decoder
  827.             if (currentMode != ISO2022Modes.ModeASCII && (encoder == null || encoder.MustFlush)) {
  828.                 // Need to add the ASCII mode marker
  829.                 // Only turn off other mode if this works
  830.                 if (buffer.AddByte((byte)'~', (byte)'}'))
  831.                     currentMode = ISO2022Modes.ModeASCII;
  832.                 else
  833.                     // If not successful, convert will maintain state for next time, also
  834.                     // AddByte will have decremented our char count, however we need it to remain the same
  835.                     buffer.GetNextChar();
  836.             }
  837.            
  838.             // Need to remember our mode
  839.             if (encoder != null && bytes != null) {
  840.                 // This is ASCII if we had to flush
  841.                 encoder.currentMode = currentMode;
  842.                
  843.                 if (!buffer.fallbackBuffer.bUsedEncoder) {
  844.                     encoder.charLeftOver = (char)0;
  845.                 }
  846.                
  847.                 encoder.m_charsUsed = buffer.CharsUsed;
  848.             }
  849.            
  850.             // Return our length
  851.             return buffer.Count;
  852.         }
  853.        
  854.         unsafe private int GetCharsCP5022xJP(byte* bytes, int byteCount, char* chars, int charCount, ISO2022Decoder decoder)
  855.         {
  856.             // Get our info.
  857.             Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer(this, decoder, chars, charCount, bytes, byteCount);
  858.            
  859.             // No mode information yet
  860.             ISO2022Modes currentMode = ISO2022Modes.ModeASCII;
  861.             // Our current Mode
  862.             ISO2022Modes shiftInMode = ISO2022Modes.ModeASCII;
  863.             // Mode that we'll shift in to
  864.             byte[] escapeBytes = new byte[4];
  865.             int escapeCount = 0;
  866.            
  867.             if (decoder != null) {
  868.                 currentMode = decoder.currentMode;
  869.                 shiftInMode = decoder.shiftInOutMode;
  870.                
  871.                 // See if we have leftover decoder buffer to use
  872.                 // Load our bytesLeftOver
  873.                 escapeCount = decoder.bytesLeftOverCount;
  874.                
  875.                 // Don't want to mess up decoder if we're counting or throw an exception
  876.                 for (int i = 0; i < escapeCount; i++)
  877.                     escapeBytes[i] = decoder.bytesLeftOver[i];
  878.             }
  879.            
  880.             // Do this until the end
  881.             while (buffer.MoreData || escapeCount > 0) {
  882.                 byte ch;
  883.                
  884.                 if (escapeCount > 0) {
  885.                     // Get more escape sequences if necessary
  886.                     if (escapeBytes[0] == ESCAPE) {
  887.                         // Stop if no more input
  888.                         if (!buffer.MoreData) {
  889.                             if (decoder != null && !decoder.MustFlush)
  890.                                 break;
  891.                         }
  892.                         else {
  893.                             // Add it to the sequence we can check
  894.                             escapeBytes[escapeCount++] = buffer.GetNextByte();
  895.                            
  896.                             // We have an escape sequence
  897.                             ISO2022Modes modeReturn = CheckEscapeSequenceJP(escapeBytes, escapeCount);
  898.                            
  899.                             if (modeReturn != ISO2022Modes.ModeInvalidEscape) {
  900.                                 if (modeReturn != ISO2022Modes.ModeIncompleteEscape) {
  901.                                     // Processed escape correctly
  902.                                     escapeCount = 0;
  903.                                    
  904.                                     // We're now this mode
  905.                                     currentMode = shiftInMode = modeReturn;
  906.                                 }
  907.                                
  908.                                 // Either way, continue to get next escape or real byte
  909.                                 continue;
  910.                             }
  911.                         }
  912.                        
  913.                         // If ModeInvalidEscape, or no input & must flush, then fall through to add escape.
  914.                     }
  915.                    
  916.                     // Read next escape byte and move them down one.
  917.                     ch = DecrementEscapeBytes(ref escapeBytes, ref escapeCount);
  918.                 }
  919.                 else {
  920.                     // Get our next byte
  921.                     ch = buffer.GetNextByte();
  922.                    
  923.                     if (ch == ESCAPE) {
  924.                         // We'll have an escape sequence, use it if we don't have one buffered already
  925.                         if (escapeCount == 0) {
  926.                             // Start this new escape sequence
  927.                             escapeBytes[0] = ch;
  928.                             escapeCount = 1;
  929.                             continue;
  930.                         }
  931.                        
  932.                         // Flush the previous escape sequence, then reuse this escape byte
  933.                         buffer.AdjustBytes(-1);
  934.                     }
  935.                 }
  936.                
  937.                 if (ch == SHIFT_OUT) {
  938.                     shiftInMode = currentMode;
  939.                     currentMode = ISO2022Modes.ModeHalfwidthKatakana;
  940.                     continue;
  941.                 }
  942.                 else if (ch == SHIFT_IN) {
  943.                     currentMode = shiftInMode;
  944.                     continue;
  945.                 }
  946.                
  947.                 // Get our full character
  948.                 ushort iBytes = ch;
  949.                 bool b2Bytes = false;
  950.                
  951.                 if (currentMode == ISO2022Modes.ModeJIS0208) {
  952.                     //
  953.                     // To handle errors, we need to check:
  954.                     // 1. if trailbyte is there
  955.                     // 2. if code is valid
  956.                     //
  957.                     if (escapeCount > 0) {
  958.                         // Let another escape fall through
  959.                         if (escapeBytes[0] != ESCAPE) {
  960.                             // Move them down one & get the next data
  961.                             iBytes <<= 8;
  962.                             iBytes |= DecrementEscapeBytes(ref escapeBytes, ref escapeCount);
  963.                             b2Bytes = true;
  964.                         }
  965.                     }
  966.                     else if (buffer.MoreData) {
  967.                         iBytes <<= 8;
  968.                         iBytes |= buffer.GetNextByte();
  969.                         b2Bytes = true;
  970.                     }
  971.                     else {
  972.                         // Not enough input, use decoder if possible
  973.                         if (decoder == null || decoder.MustFlush) {
  974.                             // No decoder, do fallback for this byte
  975.                             buffer.Fallback(ch);
  976.                             break;
  977.                         }
  978.                        
  979.                         // Stick it in the decoder if we're not counting
  980.                         if (chars != null) {
  981.                             escapeBytes[0] = ch;
  982.                             escapeCount = 1;
  983.                         }
  984.                         break;
  985.                     }
  986.                    
  987.                     // MLang treated JIS 0208 '*' lead byte like a single halfwidth katakana
  988.                     // escape, so use 0x8e00 as katakana lead byte and keep same trail byte.
  989.                     // 0x2a lead byte range is normally unused in JIS 0208, so shouldn't have
  990.                     // any wierd compatibility issues.
  991.                     if ((b2Bytes == true) && ((iBytes & 65280) == 10752)) {
  992.                         iBytes = (ushort)(iBytes & 255);
  993.                         iBytes |= (LEADBYTE_HALFWIDTH << 8);
  994.                         // Put us in the halfwidth katakana range
  995.                     }
  996.                 }
  997.                 else if (iBytes >= 161 && iBytes <= 223) {
  998.                     iBytes |= (LEADBYTE_HALFWIDTH << 8);
  999.                     // Map to halfwidth katakana range
  1000.                     iBytes &= 65407;
  1001.                     // remove extra 0x80
  1002.                 }
  1003.                 else if (currentMode == ISO2022Modes.ModeHalfwidthKatakana) {
  1004.                     // Add 0x10 lead byte that our encoding expects for Katakana:
  1005.                     iBytes |= (LEADBYTE_HALFWIDTH << 8);
  1006.                 }
  1007.                
  1008.                 // We have an iBytes to try to convert.
  1009.                 char c = mapBytesToUnicode[iBytes];
  1010.                
  1011.                 // See if it was unknown
  1012.                 if (c == UNKNOWN_CHAR_FLAG && iBytes != 0) {
  1013.                     // Have to do fallback
  1014.                     if (b2Bytes) {
  1015.                         if (!buffer.Fallback((byte)(iBytes >> 8), (byte)iBytes))
  1016.                             break;
  1017.                     }
  1018.                     else {
  1019.                         if (!buffer.Fallback(ch))
  1020.                             break;
  1021.                     }
  1022.                 }
  1023.                 else {
  1024.                     // If we were JIS 0208, then we consumed an extra byte
  1025.                     if (!buffer.AddChar(c, b2Bytes ? 2 : 1))
  1026.                         break;
  1027.                 }
  1028.             }
  1029.            
  1030.             // Make sure our decoder state matches our mode, if not counting
  1031.             if (chars != null && decoder != null) {
  1032.                 // Remember it if we don't flush
  1033.                 if (!decoder.MustFlush || escapeCount != 0) {
  1034.                     // Either not flushing or had state (from convert)
  1035.                     BCLDebug.Assert(!decoder.MustFlush || !decoder.m_throwOnOverflow, "[ISO2022Encoding.GetCharsCP5022xJP]Expected no state or not converting or not flushing");
  1036.                    
  1037.                     decoder.currentMode = currentMode;
  1038.                     decoder.shiftInOutMode = shiftInMode;
  1039.                    
  1040.                     // Remember escape buffer
  1041.                     decoder.bytesLeftOverCount = escapeCount;
  1042.                     decoder.bytesLeftOver = escapeBytes;
  1043.                 }
  1044.                 else {
  1045.                     // We flush, clear buffer
  1046.                     decoder.currentMode = ISO2022Modes.ModeASCII;
  1047.                     decoder.shiftInOutMode = ISO2022Modes.ModeASCII;
  1048.                     decoder.bytesLeftOverCount = 0;
  1049.                     // Slightly different if counting/not counting
  1050.                 }
  1051.                
  1052.                 decoder.m_bytesUsed = buffer.BytesUsed;
  1053.             }
  1054.            
  1055.             // Return # of characters we found
  1056.             return buffer.Count;
  1057.         }
  1058.        
  1059.         // We know we have an escape sequence, so check it starting with the byte after the escape
  1060.         private ISO2022Modes CheckEscapeSequenceJP(byte[] bytes, int escapeCount)
  1061.         {
  1062.             // Have an escape sequence
  1063.             if (bytes[0] != ESCAPE)
  1064.                 return ISO2022Modes.ModeInvalidEscape;
  1065.            
  1066.             if (escapeCount < 3)
  1067.                 return ISO2022Modes.ModeIncompleteEscape;
  1068.            
  1069.             if (bytes[1] == '(') {
  1070.                 // <esc>(B
  1071.                 if (bytes[2] == 'B') {
  1072.                     return ISO2022Modes.ModeASCII;
  1073.                 }
  1074.                 // <esc>(H
  1075.                 else if (bytes[2] == 'H') {
  1076.                     // Actually this is supposed to be Swedish
  1077.                     // We treat it like ASCII though.
  1078.                     return ISO2022Modes.ModeASCII;
  1079.                 }
  1080.                 // <esc>(J
  1081.                 else if (bytes[2] == 'J') {
  1082.                     // Actually this is supposed to be Roman
  1083.                     // 2 characters are different, but historically we treat it as ascii
  1084.                     return ISO2022Modes.ModeASCII;
  1085.                 }
  1086.                 // <esc>(I
  1087.                 else if (bytes[2] == 'I') {
  1088.                     return ISO2022Modes.ModeHalfwidthKatakana;
  1089.                 }
  1090.             }
  1091.             else if (bytes[1] == '$') {
  1092.                 // <esc>$@
  1093.                 // <esc>$B
  1094.                 if (bytes[2] == '@' || bytes[2] == 'B') {
  1095.                     return ISO2022Modes.ModeJIS0208;
  1096.                 }
  1097.                 else {
  1098.                     // Looking for <esc>$(D
  1099.                     if (escapeCount < 4)
  1100.                         return ISO2022Modes.ModeIncompleteEscape;
  1101.                    
  1102.                     // <esc>$(D
  1103.                     if (bytes[2] == '(' && bytes[3] == 'D') {
  1104.                         return ISO2022Modes.ModeJIS0208;
  1105.                     }
  1106.                 }
  1107.             }
  1108.             else if (bytes[1] == '&') {
  1109.                 // <esc>&@
  1110.                 if (bytes[2] == '@') {
  1111.                     // Ignore ESC & @ (prefix to <esc>$B)
  1112.                     return ISO2022Modes.ModeNOOP;
  1113.                 }
  1114.             }
  1115.            
  1116.             // If we get here we fell through and have an invalid/unknown escape sequence
  1117.             return ISO2022Modes.ModeInvalidEscape;
  1118.         }
  1119.        
  1120.         private byte DecrementEscapeBytes(ref byte[] bytes, ref int count)
  1121.         {
  1122.             BCLDebug.Assert(count > 0, "[ISO2022Encoding.DecrementEscapeBytes]count > 0");
  1123.            
  1124.             // Decrement our count
  1125.             count--;
  1126.            
  1127.             // Remember the first one
  1128.             byte returnValue = bytes[0];
  1129.            
  1130.             // Move them down one.
  1131.             for (int i = 0; i < count; i++) {
  1132.                 bytes[i] = bytes[i + 1];
  1133.             }
  1134.            
  1135.             // Clear out the last byte
  1136.             bytes[count] = 0;
  1137.            
  1138.             // Return the old 1st byte
  1139.             return returnValue;
  1140.         }
  1141.        
  1142.         // Note that in DBCS mode mlang passed through ' ', '\t' and '\n' as SBCS characters
  1143.         // probably to allow mailer formatting without too much extra work.
  1144.         unsafe private int GetCharsCP50225KR(byte* bytes, int byteCount, char* chars, int charCount, ISO2022Decoder decoder)
  1145.         {
  1146.             // Get our info.
  1147.             Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer(this, decoder, chars, charCount, bytes, byteCount);
  1148.            
  1149.             // No mode information yet
  1150.             ISO2022Modes currentMode = ISO2022Modes.ModeASCII;
  1151.             // Our current Mode
  1152.             byte[] escapeBytes = new byte[4];
  1153.             int escapeCount = 0;
  1154.            
  1155.             if (decoder != null) {
  1156.                 currentMode = decoder.currentMode;
  1157.                
  1158.                 // See if we have leftover decoder buffer to use
  1159.                 // Load our bytesLeftOver
  1160.                 escapeCount = decoder.bytesLeftOverCount;
  1161.                
  1162.                 // Don't want to mess up decoder if we're counting or throw an exception
  1163.                 for (int i = 0; i < escapeCount; i++)
  1164.                     escapeBytes[i] = decoder.bytesLeftOver[i];
  1165.             }
  1166.            
  1167.             // Do this until the end, just do '?' replacement because we don't have fallbacks for decodings.
  1168.             while (buffer.MoreData || escapeCount > 0) {
  1169.                 byte ch;
  1170.                
  1171.                 if (escapeCount > 0) {
  1172.                     // Get more escape sequences if necessary
  1173.                     if (escapeBytes[0] == ESCAPE) {
  1174.                         // Stop if no more input
  1175.                         if (!buffer.MoreData) {
  1176.                             if (decoder != null && !decoder.MustFlush)
  1177.                                 break;
  1178.                         }
  1179.                         else {
  1180.                             // Add it to the sequence we can check
  1181.                             escapeBytes[escapeCount++] = buffer.GetNextByte();
  1182.                            
  1183.                             // We have an escape sequence
  1184.                             ISO2022Modes modeReturn = CheckEscapeSequenceKR(escapeBytes, escapeCount);
  1185.                            
  1186.                             if (modeReturn != ISO2022Modes.ModeInvalidEscape) {
  1187.                                 if (modeReturn != ISO2022Modes.ModeIncompleteEscape) {
  1188.                                     // Processed escape correctly, no effect (we know about KR mode)
  1189.                                     escapeCount = 0;
  1190.                                 }
  1191.                                
  1192.                                 // Either way, continue to get next escape or real byte
  1193.                                 continue;
  1194.                             }
  1195.                         }
  1196.                        
  1197.                         // If ModeInvalidEscape, or no input & must flush, then fall through to add escape.
  1198.                     }
  1199.                    
  1200.                     // Still have something left over in escape buffer
  1201.                     // Get it and move them down one
  1202.                     ch = DecrementEscapeBytes(ref escapeBytes, ref escapeCount);
  1203.                 }
  1204.                 else {
  1205.                     // Get our next byte
  1206.                     ch = buffer.GetNextByte();
  1207.                    
  1208.                     if (ch == ESCAPE) {
  1209.                         // We'll have an escape sequence, use it if we don't have one buffered already
  1210.                         if (escapeCount == 0) {
  1211.                             // Start this new escape sequence
  1212.                             escapeBytes[0] = ch;
  1213.                             escapeCount = 1;
  1214.                             continue;
  1215.                         }
  1216.                        
  1217.                         // Flush previous escape sequence, then reuse this escape byte
  1218.                         buffer.AdjustBytes(-1);
  1219.                     }
  1220.                 }
  1221.                
  1222.                 if (ch == SHIFT_OUT) {
  1223.                     currentMode = ISO2022Modes.ModeKR;
  1224.                     continue;
  1225.                 }
  1226.                 else if (ch == SHIFT_IN) {
  1227.                     currentMode = ISO2022Modes.ModeASCII;
  1228.                     continue;
  1229.                 }
  1230.                
  1231.                 // Get our full character
  1232.                 ushort iBytes = ch;
  1233.                 bool b2Bytes = false;
  1234.                
  1235.                 // MLANG was passing through ' ', '\t' and '\n', so we do so as well, but I don't see that in the RFC.
  1236.                 if (currentMode == ISO2022Modes.ModeKR && ch != ' ' && ch != '\t' && ch != '\n') {
  1237.                     //
  1238.                     // To handle errors, we need to check:
  1239.                     // 1. if trailbyte is there
  1240.                     // 2. if code is valid
  1241.                     //
  1242.                     if (escapeCount > 0) {
  1243.                         // Let another escape fall through
  1244.                         if (escapeBytes[0] != ESCAPE) {
  1245.                             // Move them down one & get the next data
  1246.                             iBytes <<= 8;
  1247.                             iBytes |= DecrementEscapeBytes(ref escapeBytes, ref escapeCount);
  1248.                             b2Bytes = true;
  1249.                         }
  1250.                     }
  1251.                     else if (buffer.MoreData) {
  1252.                         iBytes <<= 8;
  1253.                         iBytes |= buffer.GetNextByte();
  1254.                         b2Bytes = true;
  1255.                     }
  1256.                     else {
  1257.                         // Not enough input, use decoder if possible
  1258.                         if (decoder == null || decoder.MustFlush) {
  1259.                             // No decoder, do fallback for lonely 1st byte
  1260.                             buffer.Fallback(ch);
  1261.                             break;
  1262.                         }
  1263.                        
  1264.                         // Stick it in the decoder if we're not counting
  1265.                         if (chars != null) {
  1266.                             escapeBytes[0] = ch;
  1267.                             escapeCount = 1;
  1268.                         }
  1269.                         break;
  1270.                     }
  1271.                 }
  1272.                
  1273.                 // We have a iBytes to try to convert.
  1274.                 char c = mapBytesToUnicode[iBytes];
  1275.                
  1276.                 // See if it was unknown
  1277.                 if (c == UNKNOWN_CHAR_FLAG && iBytes != 0) {
  1278.                     // Have to do fallback
  1279.                     if (b2Bytes) {
  1280.                         if (!buffer.Fallback((byte)(iBytes >> 8), (byte)iBytes))
  1281.                             break;
  1282.                     }
  1283.                     else {
  1284.                         if (!buffer.Fallback(ch))
  1285.                             break;
  1286.                     }
  1287.                 }
  1288.                 else {
  1289.                     if (!buffer.AddChar(c, b2Bytes ? 2 : 1))
  1290.                         break;
  1291.                 }
  1292.             }
  1293.            
  1294.             // Make sure our decoder state matches our mode, if not counting
  1295.             if (chars != null && decoder != null) {
  1296.                 // Remember it if we don't flush
  1297.                 if (!decoder.MustFlush || escapeCount != 0) {
  1298.                     // Either not flushing or had state (from convert)
  1299.                     BCLDebug.Assert(!decoder.MustFlush || !decoder.m_throwOnOverflow, "[ISO2022Encoding.GetCharsCP50225KR]Expected no state or not converting or not flushing");
  1300.                    
  1301.                     decoder.currentMode = currentMode;
  1302.                    
  1303.                     // Remember escape buffer
  1304.                     decoder.bytesLeftOverCount = escapeCount;
  1305.                     decoder.bytesLeftOver = escapeBytes;
  1306.                 }
  1307.                 else {
  1308.                     // We flush, clear buffer
  1309.                     decoder.currentMode = ISO2022Modes.ModeASCII;
  1310.                     decoder.shiftInOutMode = ISO2022Modes.ModeASCII;
  1311.                     decoder.bytesLeftOverCount = 0;
  1312.                 }
  1313.                
  1314.                 decoder.m_bytesUsed = buffer.BytesUsed;
  1315.             }
  1316.            
  1317.             // Return # of characters we found
  1318.             return buffer.Count;
  1319.         }
  1320.        
  1321.         // We know we have an escape sequence, so check it starting with the byte after the escape
  1322.         private ISO2022Modes CheckEscapeSequenceKR(byte[] bytes, int escapeCount)
  1323.         {
  1324.             // Have an escape sequence
  1325.             if (bytes[0] != ESCAPE)
  1326.                 return ISO2022Modes.ModeInvalidEscape;
  1327.            
  1328.             if (escapeCount < 4)
  1329.                 return ISO2022Modes.ModeIncompleteEscape;
  1330.            
  1331.             if (bytes[1] == '$' && bytes[2] == ')' && bytes[3] == 'C')
  1332.                 // <esc>$)C
  1333.                 return ISO2022Modes.ModeKR;
  1334.            
  1335.             // If we get here we fell through and have an invalid/unknown escape sequence
  1336.             return ISO2022Modes.ModeInvalidEscape;
  1337.         }
  1338.        
  1339.         // CP52936 is HZ Encoding
  1340.         // HZ Encoding has 4 shift sequences:
  1341.         // ~~ '~' (\u7e)
  1342.         // ~} shift into 1 byte mode,
  1343.         // ~{ shift into 2 byte GB 2312-80
  1344.         // ~<NL> Maintain 2 byte mode across new lines (ignore both ~ and <NL> characters)
  1345.         // (This is for mailers that restrict to 70 or 80 or whatever character lines)
  1346.         //
  1347.         // According to comment in mlang, lead & trail byte ranges are described in RFC 1843
  1348.         // RFC 1843 => valid HZ code range: leading byte 0x21 - 0x77, 2nd byte 0x21 - 0x7e
  1349.         // Our 936 code points are or'd with 0x8080, so lead byte 0xa1 - 0xf7, trail byte 0xa1 - 0xfe
  1350.         //
  1351.         // This encoding is designed for transmission by e-mail and news. No bytes should have high bit set.
  1352.         // (all bytes <= 0x7f)
  1353.         unsafe private int GetCharsCP52936(byte* bytes, int byteCount, char* chars, int charCount, ISO2022Decoder decoder)
  1354.         {
  1355.             BCLDebug.Assert(byteCount >= 0, "[ISO2022Encoding.GetCharsCP52936]count >=0");
  1356.             BCLDebug.Assert(bytes != null, "[ISO2022Encoding.GetCharsCP52936]bytes!=null");
  1357.            
  1358.             // Get our info.
  1359.             Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer(this, decoder, chars, charCount, bytes, byteCount);
  1360.            
  1361.             // No mode information yet
  1362.             ISO2022Modes currentMode = ISO2022Modes.ModeASCII;
  1363.             int byteLeftOver = -1;
  1364.             bool bUsedDecoder = false;
  1365.            
  1366.             if (decoder != null) {
  1367.                 currentMode = decoder.currentMode;
  1368.                 // See if we have leftover decoder buffer to use
  1369.                 // Don't want to mess up decoder if we're counting or throw an exception
  1370.                 if (decoder.bytesLeftOverCount != 0) {
  1371.                     // Load our bytesLeftOver
  1372.                     byteLeftOver = decoder.bytesLeftOver[0];
  1373.                 }
  1374.             }
  1375.            
  1376.             // Do this until the end, just do '?' replacement because we don't have fallbacks for decodings.
  1377.             while (buffer.MoreData || byteLeftOver >= 0) {
  1378.                 byte ch;
  1379.                
  1380.                 // May have a left over byte
  1381.                 if (byteLeftOver >= 0) {
  1382.                     ch = (byte)byteLeftOver;
  1383.                     byteLeftOver = -1;
  1384.                 }
  1385.                 else {
  1386.                     ch = buffer.GetNextByte();
  1387.                 }
  1388.                
  1389.                 // We're in escape mode
  1390.                 if (ch == '~') {
  1391.                     // Next char is type of switch
  1392.                     if (!buffer.MoreData) {
  1393.                         // We don't have anything left, it'll be in decoder or a ?
  1394.                         // don't fail if we are allowing overflows
  1395.                         if (decoder == null || decoder.MustFlush) {
  1396.                             // We'll be a '?'
  1397.                             buffer.Fallback(ch);
  1398.                             // break if we fail & break if we don't (because !MoreData)
  1399.                             // Add succeeded, continue
  1400.                             break;
  1401.                         }
  1402.                        
  1403.                         // Stick it in decoder
  1404.                         if (decoder != null)
  1405.                             decoder.ClearMustFlush();
  1406.                        
  1407.                         if (chars != null) {
  1408.                             decoder.bytesLeftOverCount = 1;
  1409.                             decoder.bytesLeftOver[0] = (byte)'~';
  1410.                             bUsedDecoder = true;
  1411.                         }
  1412.                         break;
  1413.                     }
  1414.                    
  1415.                     // What type is it?, get 2nd byte
  1416.                     ch = buffer.GetNextByte();
  1417.                    
  1418.                     if (ch == '~' && currentMode == ISO2022Modes.ModeASCII) {
  1419.                         // Its just a ~~ replacement for ~, add it
  1420.                         if (!buffer.AddChar((char)ch, 2))
  1421.                             // Add failed, break for converting
  1422.                             break;
  1423.                        
  1424.                         // Add succeeded, continue
  1425.                         continue;
  1426.                     }
  1427.                     else if (ch == '{') {
  1428.                         // Switching to Double Byte mode
  1429.                         currentMode = ISO2022Modes.ModeHZ;
  1430.                         continue;
  1431.                     }
  1432.                     else if (ch == '}') {
  1433.                         // Switching to ASCII mode
  1434.                         currentMode = ISO2022Modes.ModeASCII;
  1435.                         continue;
  1436.                     }
  1437.                     else if (ch == '\n') {
  1438.                         // Ignore ~\n sequence
  1439.                         continue;
  1440.                     }
  1441.                     else {
  1442.                         // Unknown escape, back up and try the '~' as a "normal" byte or lead byte
  1443.                         buffer.AdjustBytes(-1);
  1444.                         ch = (byte)'~';
  1445.                     }
  1446.                 }
  1447.                
  1448.                 // go ahead and add our data
  1449.                 if (currentMode != ISO2022Modes.ModeASCII) {
  1450.                     // Should be ModeHZ
  1451.                     BCLDebug.Assert(currentMode == ISO2022Modes.ModeHZ, "[ISO2022Encoding.GetCharsCP52936]Expected ModeHZ");
  1452.                     char cm;
  1453.                    
  1454.                     if (ch < 32) {
  1455.                         // Emit it as ASCII
  1456.                         goto STOREASCII;
  1457.                     }
  1458.                    
  1459.                     // Its multibyte, should have another byte
  1460.                     if (!buffer.MoreData) {
  1461.                         // No bytes left
  1462.                         // don't fail if we are allowing overflows
  1463.                         if (decoder == null || decoder.MustFlush) {
  1464.                             // Not enough bytes, fallback lead byte
  1465.                             buffer.Fallback(ch);
  1466.                            
  1467.                             // Break if we fail & break because !MoreData
  1468.                             break;
  1469.                         }
  1470.                        
  1471.                         if (decoder != null)
  1472.                             decoder.ClearMustFlush();
  1473.                        
  1474.                         // Stick it in decoder
  1475.                         if (chars != null) {
  1476.                             decoder.bytesLeftOverCount = 1;
  1477.                             decoder.bytesLeftOver[0] = ch;
  1478.                             bUsedDecoder = true;
  1479.                         }
  1480.                         break;
  1481.                     }
  1482.                    
  1483.                     byte ch2 = buffer.GetNextByte();
  1484.                     ushort iBytes = (ushort)(ch << 8 | ch2);
  1485.                    
  1486.                     if (ch == ' ' && ch2 != 0) {
  1487.                         cm = (char)ch2;
  1488.                         goto STOREMULTIBYTE;
  1489.                     }
  1490.                    
  1491.                     // Bytes should be in range: lead byte 0x21-0x77, trail byte: 0x21 - 0x7e
  1492.                     if ((ch < 33 || ch > 119 || ch2 < 33 || ch2 > 126) && (ch < 161 || ch > 247 || ch2 < 161 || ch2 > 254)) {
  1493.                         if (ch2 == 32 && 33 <= ch && ch <= 125) {
  1494.                             iBytes = 8481;
  1495.                             goto MULTIBYTE;
  1496.                         }
  1497.                        
  1498.                         // Illegal char, use fallback. If lead byte is 0 have to do it special and do it first
  1499.                         if (!buffer.Fallback((byte)(iBytes >> 8), (byte)(iBytes)))
  1500.                             break;
  1501.                         continue;
  1502.                     }
  1503.                     MULTIBYTE:
  1504.                    
  1505.                     iBytes |= 32896;
  1506.                     // Look up the multibyte char to stick it in our data
  1507.                    
  1508.                     // We have a iBytes to try to convert.
  1509.                     cm = mapBytesToUnicode[iBytes];
  1510.                     STOREMULTIBYTE:
  1511.                    
  1512.                    
  1513.                     // See if it was unknown
  1514.                     if (cm == UNKNOWN_CHAR_FLAG && iBytes != 0) {
  1515.                         // Fall back the unknown stuff
  1516.                         if (!buffer.Fallback((byte)(iBytes >> 8), (byte)(iBytes)))
  1517.                             break;
  1518.                         continue;
  1519.                     }
  1520.                    
  1521.                     if (!buffer.AddChar(cm, 2))
  1522.                         break;
  1523.                     // convert ran out of buffer, stop
  1524.                     continue;
  1525.                 }
  1526.                 STOREASCII:
  1527.                
  1528.                 char c = mapBytesToUnicode[ch];
  1529.                
  1530.                 // Check if it was unknown
  1531.                 if ((c == UNKNOWN_CHAR_FLAG || c == 0) && (ch != 0)) {
  1532.                     // fallback the unkown bytes
  1533.                     if (!buffer.Fallback((byte)ch))
  1534.                         break;
  1535.                     continue;
  1536.                 }
  1537.                
  1538.                 // Go ahead and add our ASCII character
  1539.                 if (!buffer.AddChar(c))
  1540.                     break;
  1541.                 // convert ran out of buffer, stop
  1542.             }
  1543.            
  1544.             // Need to remember our state, IF we're not counting
  1545.             if (chars != null && decoder != null) {
  1546.                 if (!bUsedDecoder) {
  1547.                     // If we didn't use it, clear the byte left over
  1548.                     decoder.bytesLeftOverCount = 0;
  1549.                 }
  1550.                
  1551.                 if (decoder.MustFlush && decoder.bytesLeftOverCount == 0) {
  1552.                     decoder.currentMode = ISO2022Modes.ModeASCII;
  1553.                 }
  1554.                 else {
  1555.                     // Either not flushing or had state (from convert)
  1556.                     BCLDebug.Assert(!decoder.MustFlush || !decoder.m_throwOnOverflow, "[ISO2022Encoding.GetCharsCP52936]Expected no state or not converting or not flushing");
  1557.                    
  1558.                     decoder.currentMode = currentMode;
  1559.                 }
  1560.                 decoder.m_bytesUsed = buffer.BytesUsed;
  1561.             }
  1562.            
  1563.             // Return # of characters we found
  1564.             return buffer.Count;
  1565.         }
  1566.        
  1567.         // Note: These all end up with 1/2 bytes of average byte count, so unless we're 1 we're always
  1568.         // charCount/2 bytes too big.
  1569.         public override int GetMaxByteCount(int charCount)
  1570.         {
  1571.             if (charCount < 0)
  1572.                 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1573.            
  1574.             // Characters would be # of characters + 1 in case high surrogate is ? * max fallback
  1575.             long byteCount = (long)charCount + 1;
  1576.            
  1577.             if (EncoderFallback.MaxCharCount > 1)
  1578.                 byteCount *= EncoderFallback.MaxCharCount;
  1579.            
  1580.             // Start with just generic DBCS values (sort of).
  1581.             int perChar = 2;
  1582.             int extraStart = 0;
  1583.             int extraEnd = 0;
  1584.            
  1585.             switch (CodePage) {
  1586.                 case 50220:
  1587.                 case 50221:
  1588.                     // 2 bytes per char + 3 bytes switch to JIS 0208 or 1 byte + 3 bytes switch to 1 byte CP
  1589.                     perChar = 5;
  1590.                     // 5 max (4.5 average)
  1591.                     extraEnd = 3;
  1592.                     // 3 bytes to shift back to ASCII
  1593.                     break;
  1594.                 case 50222:
  1595.                     // 2 bytes per char + 3 bytes switch to JIS 0208 or 1 byte + 3 bytes switch to 1 byte CP
  1596.                     perChar = 5;
  1597.                     // 5 max (4.5 average)
  1598.                     extraEnd = 4;
  1599.                     // 1 byte to shift from Katakana -> DBCS, 3 bytes to shift back to ASCII from DBCS
  1600.                     break;
  1601.                 case 50225:
  1602.                     // 2 bytes per char + 1 byte SO, or 1 byte per char + 1 byte SI.
  1603.                     perChar = 3;
  1604.                     // 3 max, (2.5 average)
  1605.                     extraStart = 4;
  1606.                     // EUC-KR marker appears at beginning of file.
  1607.                     extraEnd = 1;
  1608.                     // 1 byte to shift back to ascii if necessary.
  1609.                     break;
  1610.                 case 52936:
  1611.                     // 2 bytes per char + 2 byte shift, or 1 byte + 1 byte shift
  1612.                     // Worst case: left over surrogate with no low surrogate is extra ?, could have to switch to ASCII, then could have HZ and flush to ASCII mode
  1613.                     perChar = 4;
  1614.                     // 4 max, (3.5 average if every other char is HZ/ASCII)
  1615.                     extraEnd = 2;
  1616.                     // 2 if we have to shift back to ASCII
  1617.                     break;
  1618.             }
  1619.            
  1620.             // Return our surrogate and End plus perChar for each char.
  1621.             byteCount *= perChar;
  1622.             byteCount += extraStart + extraEnd;
  1623.            
  1624.             if (byteCount > 2147483647)
  1625.                 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_GetByteCountOverflow"));
  1626.            
  1627.             return (int)byteCount;
  1628.         }
  1629.        
  1630.         public override int GetMaxCharCount(int byteCount)
  1631.         {
  1632.             if (byteCount < 0)
  1633.                 throw new ArgumentOutOfRangeException("byteCount", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1634.            
  1635.             int perChar = 1;
  1636.             int extraDecoder = 1;
  1637.            
  1638.             switch (CodePage) {
  1639.                 case 50220:
  1640.                 case 50221:
  1641.                 case 50222:
  1642.                 case 50225:
  1643.                     perChar = 1;
  1644.                     // Worst case all ASCII
  1645.                     extraDecoder = 3;
  1646.                     // Could have left over 3 chars of 4 char escape sequence, that all become ?
  1647.                     break;
  1648.                 case 52936:
  1649.                     perChar = 1;
  1650.                     // Worst case all ASCII
  1651.                     extraDecoder = 1;
  1652.                     // sequences are 2 chars, so if next one is illegal, then previous 1 could be ?
  1653.                     break;
  1654.             }
  1655.            
  1656.             // Figure out our length, perchar * char + whatever extra our decoder could do to us.
  1657.             long charCount = ((long)byteCount * perChar) + extraDecoder;
  1658.            
  1659.             // Just in case we have to fall back unknown ones.
  1660.             if (DecoderFallback.MaxCharCount > 1)
  1661.                 charCount *= DecoderFallback.MaxCharCount;
  1662.            
  1663.             if (charCount > 2147483647)
  1664.                 throw new ArgumentOutOfRangeException("byteCount", Environment.GetResourceString("ArgumentOutOfRange_GetCharCountOverflow"));
  1665.            
  1666.             return (int)charCount;
  1667.         }
  1668.        
  1669.         public override Encoder GetEncoder()
  1670.         {
  1671.             return new ISO2022Encoder(this);
  1672.         }
  1673.        
  1674.         public override Decoder GetDecoder()
  1675.         {
  1676.             return new ISO2022Decoder(this);
  1677.         }
  1678.        
  1679.         [Serializable()]
  1680.         internal class ISO2022Encoder : System.Text.EncoderNLS
  1681.         {
  1682.             internal ISO2022Modes currentMode;
  1683.             internal ISO2022Modes shiftInOutMode;
  1684.            
  1685.             internal ISO2022Encoder(EncodingNLS encoding) : base(encoding)
  1686.             {
  1687.                 // base calls reset
  1688.             }
  1689.            
  1690.             public override void Reset()
  1691.             {
  1692.                 // Reset
  1693.                 currentMode = ISO2022Modes.ModeASCII;
  1694.                 shiftInOutMode = ISO2022Modes.ModeASCII;
  1695.                 charLeftOver = (char)0;
  1696.                 if (m_fallbackBuffer != null)
  1697.                     m_fallbackBuffer.Reset();
  1698.             }
  1699.            
  1700.             // Anything left in our encoder?
  1701.             internal override bool HasState {
  1702. // Don't check shift-out mode, it may be ascii (JP) or not (KR)
  1703.                 get { return (this.charLeftOver != (char)0 || currentMode != ISO2022Modes.ModeASCII); }
  1704.             }
  1705.         }
  1706.        
  1707.         [Serializable()]
  1708.         internal class ISO2022Decoder : System.Text.DecoderNLS
  1709.         {
  1710.             internal byte[] bytesLeftOver;
  1711.             internal int bytesLeftOverCount;
  1712.             internal ISO2022Modes currentMode;
  1713.             internal ISO2022Modes shiftInOutMode;
  1714.            
  1715.             internal ISO2022Decoder(EncodingNLS encoding) : base(encoding)
  1716.             {
  1717.                 // base calls reset
  1718.             }
  1719.            
  1720.             public override void Reset()
  1721.             {
  1722.                 // Reset
  1723.                 bytesLeftOverCount = 0;
  1724.                 bytesLeftOver = new byte[4];
  1725.                 currentMode = ISO2022Modes.ModeASCII;
  1726.                 shiftInOutMode = ISO2022Modes.ModeASCII;
  1727.                 if (m_fallbackBuffer != null)
  1728.                     m_fallbackBuffer.Reset();
  1729.             }
  1730.            
  1731.             // Anything left in our decoder?
  1732.             internal override bool HasState {
  1733. // If have bytes left over or not shifted back to ASCII then have problem
  1734.                 get { return (this.bytesLeftOverCount != 0 || currentMode != ISO2022Modes.ModeASCII); }
  1735.             }
  1736.         }
  1737.        
  1738.         static ushort[] HalfToFullWidthKanaTable = {41379, 41430, 41431, 41378, 41382, 42482, 42401, 42403, 42405, 42407,
  1739.         42409, 42467, 42469, 42471, 42435, 41404, 42402, 42404, 42406, 42408,
  1740.         42410, 42411, 42413, 42415, 42417, 42419, 42421, 42423, 42425, 42427,
  1741.         42429, 42431, 42433, 42436, 42438, 42440, 42442, 42443, 42444, 42445,
  1742.         42446, 42447, 42450, 42453, 42456, 42459, 42462, 42463, 42464, 42465,
  1743.         42466, 42468, 42470, 42472, 42473, 42474, 42475, 42476, 42477, 42479,
  1744.             // 0x8ea1 : Halfwidth Ideographic Period
  1745.             // 0x8ea2 : Halfwidth Opening Corner Bracket
  1746.             // 0x8ea3 : Halfwidth Closing Corner Bracket
  1747.             // 0x8ea4 : Halfwidth Ideographic Comma
  1748.             // 0x8ea5 : Halfwidth Katakana Middle Dot
  1749.             // 0x8ea6 : Halfwidth Katakana Wo
  1750.             // 0x8ea7 : Halfwidth Katakana Small A
  1751.             // 0x8ea8 : Halfwidth Katakana Small I
  1752.             // 0x8ea9 : Halfwidth Katakana Small U
  1753.             // 0x8eaa : Halfwidth Katakana Small E
  1754.             // 0x8eab : Halfwidth Katakana Small O
  1755.             // 0x8eac : Halfwidth Katakana Small Ya
  1756.             // 0x8ead : Halfwidth Katakana Small Yu
  1757.             // 0x8eae : Halfwidth Katakana Small Yo
  1758.             // 0x8eaf : Halfwidth Katakana Small Tu
  1759.             // 0x8eb0 : Halfwidth Katakana-Hiragana Prolonged Sound Mark
  1760.             // 0x8eb1 : Halfwidth Katakana A
  1761.             // 0x8eb2 : Halfwidth Katakana I
  1762.             // 0x8eb3 : Halfwidth Katakana U
  1763.             // 0x8eb4 : Halfwidth Katakana E
  1764.             // 0x8eb5 : Halfwidth Katakana O
  1765.             // 0x8eb6 : Halfwidth Katakana Ka
  1766.             // 0x8eb7 : Halfwidth Katakana Ki
  1767.             // 0x8eb8 : Halfwidth Katakana Ku
  1768.             // 0x8eb9 : Halfwidth Katakana Ke
  1769.             // 0x8eba : Halfwidth Katakana Ko
  1770.             // 0x8ebb : Halfwidth Katakana Sa
  1771.             // 0x8ebc : Halfwidth Katakana Si
  1772.             // 0x8ebd : Halfwidth Katakana Su
  1773.             // 0x8ebe : Halfwidth Katakana Se
  1774.             // 0x8ebf : Halfwidth Katakana So
  1775.             // 0x8ec0 : Halfwidth Katakana Ta
  1776.             // 0x8ec1 : Halfwidth Katakana Ti
  1777.             // 0x8ec2 : Halfwidth Katakana Tu
  1778.             // 0x8ec3 : Halfwidth Katakana Te
  1779.             // 0x8ec4 : Halfwidth Katakana To
  1780.             // 0x8ec5 : Halfwidth Katakana Na
  1781.             // 0x8ec6 : Halfwidth Katakana Ni
  1782.             // 0x8ec7 : Halfwidth Katakana Nu
  1783.             // 0x8ec8 : Halfwidth Katakana Ne
  1784.             // 0x8ec9 : Halfwidth Katakana No
  1785.             // 0x8eca : Halfwidth Katakana Ha
  1786.             // 0x8ecb : Halfwidth Katakana Hi
  1787.             // 0x8ecc : Halfwidth Katakana Hu
  1788.             // 0x8ecd : Halfwidth Katakana He
  1789.             // 0x8ece : Halfwidth Katakana Ho
  1790.             // 0x8ecf : Halfwidth Katakana Ma
  1791.             // 0x8ed0 : Halfwidth Katakana Mi
  1792.             // 0x8ed1 : Halfwidth Katakana Mu
  1793.             // 0x8ed2 : Halfwidth Katakana Me
  1794.             // 0x8ed3 : Halfwidth Katakana Mo
  1795.             // 0x8ed4 : Halfwidth Katakana Ya
  1796.             // 0x8ed5 : Halfwidth Katakana Yu
  1797.             // 0x8ed6 : Halfwidth Katakana Yo
  1798.             // 0x8ed7 : Halfwidth Katakana Ra
  1799.             // 0x8ed8 : Halfwidth Katakana Ri
  1800.             // 0x8ed9 : Halfwidth Katakana Ru
  1801.             // 0x8eda : Halfwidth Katakana Re
  1802.             // 0x8edb : Halfwidth Katakana Ro
  1803.             // 0x8edc : Halfwidth Katakana Wa
  1804.             // 0x8edd : Halfwidth Katakana N
  1805.             // 0x8ede : Halfwidth Katakana Voiced Sound Mark
  1806.             // 0x8edf : Halfwidth Katakana Semi-Voiced Sound Mark
  1807.         42483, 41387, 41388};
  1808.     }
  1809. }

Developer Fusion