The Labs \ Source Viewer \ SSCLI \ System.Security.Util \ StreamTokenReader

  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. /*============================================================
  16. **
  17. ** CLASS:    Tokenizer.cs
  18. **
  19. **
  20. ** PURPOSE:  Tokenize "Elementary XML", that is, XML without
  21. **          attributes or DTDs, in other words, XML with
  22. **          elements only.
  23. **
  24. **
  25. ===========================================================*/
  26. namespace System.Security.Util
  27. {
  28.     using System.Text;
  29.     using System;
  30.     using System.IO;
  31.     using System.Globalization;
  32.    
  33.     internal sealed class Tokenizer
  34.     {
  35.         // There are five externally knowable token types: bras, kets,
  36.         // slashes, cstrs, and equals.
  37.        
  38.         internal const byte bra = 0;
  39.         internal const byte ket = 1;
  40.         internal const byte slash = 2;
  41.         internal const byte cstr = 3;
  42.         internal const byte equals = 4;
  43.         internal const byte quest = 5;
  44.         internal const byte bang = 6;
  45.         internal const byte dash = 7;
  46.        
  47.         // these are the assorted text characters that the tokenizer must
  48.         // understand to do its job
  49.        
  50.         internal const int intOpenBracket = (int)'<';
  51.         internal const int intCloseBracket = (int)'>';
  52.         internal const int intSlash = (int)'/';
  53.         internal const int intEquals = (int)'=';
  54.         internal const int intQuote = (int)'"';
  55.         internal const int intQuest = (int)'?';
  56.         internal const int intBang = (int)'!';
  57.         internal const int intDash = (int)'-';
  58.         internal const int intTab = (int)'\t';
  59.         internal const int intCR = (int)'\r';
  60.         internal const int intLF = (int)'\n';
  61.         internal const int intSpace = (int)' ';
  62.        
  63.         // this tells us where we will be getting our input from
  64.         // and what the encoding is (if any)
  65.        
  66.         private enum TokenSource
  67.         {
  68.             UnicodeByteArray,
  69.             // this is little-endian unicode (there are other encodings)
  70.             UTF8ByteArray,
  71.             ASCIIByteArray,
  72.             CharArray,
  73.             String,
  74.             NestedStrings,
  75.             Other
  76.         }
  77.        
  78.         // byte streams can come in 3 different flavors
  79.        
  80.         internal enum ByteTokenEncoding
  81.         {
  82.             UnicodeTokens,
  83.             // this is little-endian unicode (there are other encodings)
  84.             UTF8Tokens,
  85.             ByteTokens
  86.         }
  87.        
  88.         public int LineNo;
  89.        
  90.         // these variables represent the input state of the of the tokenizer
  91.        
  92.         private int _inProcessingTag;
  93.         private byte[] _inBytes;
  94.         private char[] _inChars;
  95.         private string _inString;
  96.         private int _inIndex;
  97.         private int _inSize;
  98.         private int _inSavedCharacter;
  99.         private TokenSource _inTokenSource;
  100.         private ITokenReader _inTokenReader;
  101.        
  102.         // these variables are used to build and deliver tokenizer output strings
  103.        
  104.         private StringMaker _maker = null;
  105.        
  106.         private string[] _searchStrings;
  107.         private string[] _replaceStrings;
  108.        
  109.         private int _inNestedIndex;
  110.         private int _inNestedSize;
  111.         private string _inNestedString;
  112.        
  113.        
  114.         //================================================================
  115.         // Constructor uses given ICharInputStream
  116.         //
  117.        
  118.         internal void BasicInitialization()
  119.         {
  120.             LineNo = 1;
  121.             _inProcessingTag = 0;
  122.             _inSavedCharacter = -1;
  123.             _inIndex = 0;
  124.             _inSize = 0;
  125.             _inNestedSize = 0;
  126.             _inNestedIndex = 0;
  127.             _inTokenSource = TokenSource.Other;
  128.             _maker = System.SharedStatics.GetSharedStringMaker();
  129.         }
  130.        
  131.         public void Recycle()
  132.         {
  133.             System.SharedStatics.ReleaseSharedStringMaker(ref _maker);
  134.             // will set _maker to null
  135.         }
  136.        
  137.         internal Tokenizer(string input)
  138.         {
  139.             BasicInitialization();
  140.             _inString = input;
  141.             _inSize = input.Length;
  142.             _inTokenSource = TokenSource.String;
  143.         }
  144.        
  145.         internal Tokenizer(string input, string[] searchStrings, string[] replaceStrings)
  146.         {
  147.             BasicInitialization();
  148.             _inString = input;
  149.             _inSize = _inString.Length;
  150.             _inTokenSource = TokenSource.NestedStrings;
  151.             _searchStrings = searchStrings;
  152.             _replaceStrings = replaceStrings;
  153.            
  154.             #if DEBUG
  155.             BCLDebug.Assert(searchStrings.Length == replaceStrings.Length, "different number of search/replace strings");
  156.             BCLDebug.Assert(searchStrings.Length != 0, "no search replace strings, shouldn't be using this ctor");
  157.            
  158.             for (int istr = 0; istr < searchStrings.Length; istr++) {
  159.                 string str = searchStrings[istr];
  160.                 BCLDebug.Assert(str != null, "XML Slug null");
  161.                 BCLDebug.Assert(str.Length >= 3, "XML Slug too small");
  162.                 BCLDebug.Assert(str[0] == '{', "XML Slug doesn't start with '{'");
  163.                 BCLDebug.Assert(str[str.Length - 1] == '}', "XML Slug doesn't end with '}'");
  164.                
  165.                 str = replaceStrings[istr];
  166.                 BCLDebug.Assert(str != null, "XML Replacement null");
  167.                 BCLDebug.Assert(str.Length >= 1, "XML Replacement empty");
  168.             }
  169.             #endif
  170.         }
  171.        
  172.         internal Tokenizer(byte[] array, ByteTokenEncoding encoding, int startIndex)
  173.         {
  174.             BasicInitialization();
  175.             _inBytes = array;
  176.             _inSize = array.Length;
  177.             _inIndex = startIndex;
  178.            
  179.             switch (encoding) {
  180.                 case ByteTokenEncoding.UnicodeTokens:
  181.                     _inTokenSource = TokenSource.UnicodeByteArray;
  182.                     break;
  183.                 case ByteTokenEncoding.UTF8Tokens:
  184.                    
  185.                     _inTokenSource = TokenSource.UTF8ByteArray;
  186.                     break;
  187.                 case ByteTokenEncoding.ByteTokens:
  188.                    
  189.                     _inTokenSource = TokenSource.ASCIIByteArray;
  190.                     break;
  191.                 default:
  192.                    
  193.                     throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Arg_EnumIllegalVal"), (int)encoding));
  194.                     break;
  195.             }
  196.         }
  197.        
  198.         internal Tokenizer(char[] array)
  199.         {
  200.             BasicInitialization();
  201.             _inChars = array;
  202.             _inSize = array.Length;
  203.             _inTokenSource = TokenSource.CharArray;
  204.         }
  205.        
  206.         internal Tokenizer(StreamReader input)
  207.         {
  208.             BasicInitialization();
  209.             _inTokenReader = new StreamTokenReader(input);
  210.         }
  211.        
  212.         internal void ChangeFormat(System.Text.Encoding encoding)
  213.         {
  214.             if (encoding == null) {
  215.                 return;
  216.             }
  217.            
  218.             BCLDebug.Assert(_inSavedCharacter == -1, "There was a lookahead character at the stream change point, that means the parser is changing encodings too late");
  219.            
  220.             switch (_inTokenSource) {
  221.                 case TokenSource.UnicodeByteArray:
  222.                 case TokenSource.UTF8ByteArray:
  223.                 case TokenSource.ASCIIByteArray:
  224.                     // these are the ones we can change on the fly
  225.                    
  226.                     if (encoding == System.Text.Encoding.Unicode) {
  227.                         _inTokenSource = TokenSource.UnicodeByteArray;
  228.                         return;
  229.                     }
  230.                    
  231.                     if (encoding == System.Text.Encoding.UTF8) {
  232.                         _inTokenSource = TokenSource.UTF8ByteArray;
  233.                         return;
  234.                     }
  235.                    
  236.                     if (encoding == System.Text.Encoding.ASCII) {
  237.                         _inTokenSource = TokenSource.ASCIIByteArray;
  238.                         return;
  239.                     }
  240.                     break;
  241.                 case TokenSource.String:
  242.                 case TokenSource.CharArray:
  243.                 case TokenSource.NestedStrings:
  244.                    
  245.                     // these are already unicode and encoding changes are moot
  246.                     // they can't be further decoded
  247.                     return;
  248.             }
  249.            
  250.             // if we're here it means we don't know how to change
  251.             // to the desired encoding with the memory that we have
  252.             // we'll have to do this the hard way -- that means
  253.             // creating a suitable stream from what we've got
  254.            
  255.             // this is thankfully the rare case as UTF8 and unicode
  256.             // dominate the scene
  257.            
  258.             Stream stream = null;
  259.            
  260.             switch (_inTokenSource) {
  261.                 case TokenSource.UnicodeByteArray:
  262.                 case TokenSource.UTF8ByteArray:
  263.                 case TokenSource.ASCIIByteArray:
  264.                     stream = new MemoryStream(_inBytes, _inIndex, _inSize - _inIndex);
  265.                     break;
  266.                 case TokenSource.CharArray:
  267.                 case TokenSource.String:
  268.                 case TokenSource.NestedStrings:
  269.                    
  270.                     BCLDebug.Assert(false, "attempting to change encoding on a non-changable source, should have been prevented earlier");
  271.                     return;
  272.                 default:
  273.                    
  274.                     StreamTokenReader reader = _inTokenReader as StreamTokenReader;
  275.                    
  276.                     if (reader == null) {
  277.                         BCLDebug.Assert(false, "A new input source type has been added to the Tokenizer but it doesn't support encoding changes");
  278.                         return;
  279.                     }
  280.                    
  281.                     stream = reader._in.BaseStream;
  282.                    
  283.                     BCLDebug.Assert(reader._in.CurrentEncoding != null, "Tokenizer's StreamReader does not have an encoding");
  284.                    
  285.                     string fakeReadString = new string(' ', reader.NumCharEncountered);
  286.                     stream.Position = reader._in.CurrentEncoding.GetByteCount(fakeReadString);
  287.                     break;
  288.             }
  289.            
  290.             BCLDebug.Assert(stream != null, "The XML stream with new encoding was not properly initialized for kind of input we had");
  291.            
  292.             // we now have an initialized memory stream based on whatever source we had before
  293.             _inTokenReader = new StreamTokenReader(new StreamReader(stream, encoding));
  294.             _inTokenSource = TokenSource.Other;
  295.         }
  296.        
  297.         internal void GetTokens(TokenizerStream stream, int maxNum, bool endAfterKet)
  298.         {
  299.             while (maxNum == -1 || stream.GetTokenCount() < maxNum) {
  300.                 int i = -1;
  301.                 byte ch;
  302.                 int cb = 0;
  303.                 bool inLiteral = false;
  304.                 bool inQuotedString = false;
  305.                
  306.                 StringMaker m = _maker;
  307.                
  308.                 m._outStringBuilder = null;
  309.                 m._outIndex = 0;
  310.                 BEGINNING:
  311.                
  312.                
  313.                 if (_inSavedCharacter != -1) {
  314.                     i = _inSavedCharacter;
  315.                     _inSavedCharacter = -1;
  316.                 }
  317.                 else
  318.                     switch (_inTokenSource) {
  319.                         case TokenSource.UnicodeByteArray:
  320.                             if (_inIndex + 1 >= _inSize) {
  321.                                 stream.AddToken(-1);
  322.                                 return;
  323.                             }
  324.                            
  325.                             i = (int)((_inBytes[_inIndex + 1] << 8) + _inBytes[_inIndex]);
  326.                             _inIndex += 2;
  327.                             break;
  328.                         case TokenSource.UTF8ByteArray:
  329.                            
  330.                             if (_inIndex >= _inSize) {
  331.                                 stream.AddToken(-1);
  332.                                 return;
  333.                             }
  334.                            
  335.                             i = (int)(_inBytes[_inIndex++]);
  336.                            
  337.                             // single byte -- case, early out as we're done already
  338.                             if ((i & 128) == 0)
  339.                                 break;
  340.                            
  341.                             // to decode the lead byte switch on the high nibble
  342.                             // shifted down so the switch gets dense integers
  343.                             switch ((i & 240) >> 4) {
  344.                                 case 8:
  345.                                 case 9:
  346.                                 case 10:
  347.                                 case 11:
  348.                                     // 1000 (together these 4 make 10xxxxx)
  349.                                     // 1001
  350.                                     // 1010
  351.                                     // 1011
  352.                                     // trail byte is an error
  353.                                     throw new XmlSyntaxException(LineNo);
  354.                                     break;
  355.                                 case 12:
  356.                                 case 13:
  357.                                    
  358.                                     // 1100 (these two make 110xxxxx)
  359.                                     // 1101
  360.                                     // two byte encoding (1 trail byte)
  361.                                     i &= 31;
  362.                                     cb = 2;
  363.                                     break;
  364.                                 case 14:
  365.                                    
  366.                                     // 1110 (this gets us 1110xxxx)
  367.                                     // three byte encoding (2 trail bytes)
  368.                                     i &= 15;
  369.                                     cb = 3;
  370.                                     break;
  371.                                 case 15:
  372.                                    
  373.                                     // 1111 (and finally 1111xxxx)
  374.                                     // 4 byte encoding is an error
  375.                                     throw new XmlSyntaxException(LineNo);
  376.                                     break;
  377.                             }
  378.                            
  379.                             // at least one trail byte, fetch it
  380.                             if (_inIndex >= _inSize)
  381.                                 throw new XmlSyntaxException(LineNo, Environment.GetResourceString("XMLSyntax_UnexpectedEndOfFile"));
  382.                            
  383.                             ch = _inBytes[_inIndex++];
  384.                            
  385.                             // must be trail byte encoding
  386.                             if ((ch & 192) != 128)
  387.                                 throw new XmlSyntaxException(LineNo);
  388.                            
  389.                             i = (i << 6) | (ch & 63);
  390.                            
  391.                             // done now if 2 byte encoding, otherwise go for 3
  392.                             if (cb == 2)
  393.                                 break;
  394.                            
  395.                             if (_inIndex >= _inSize)
  396.                                 throw new XmlSyntaxException(LineNo, Environment.GetResourceString("XMLSyntax_UnexpectedEndOfFile"));
  397.                            
  398.                             ch = _inBytes[_inIndex++];
  399.                            
  400.                             // must be trail byte encoding
  401.                             if ((ch & 192) != 128)
  402.                                 throw new XmlSyntaxException(LineNo);
  403.                            
  404.                             i = (i << 6) | (ch & 63);
  405.                             break;
  406.                         case TokenSource.ASCIIByteArray:
  407.                            
  408.                             if (_inIndex >= _inSize) {
  409.                                 stream.AddToken(-1);
  410.                                 return;
  411.                             }
  412.                            
  413.                             i = (int)(_inBytes[_inIndex++]);
  414.                             break;
  415.                         case TokenSource.CharArray:
  416.                            
  417.                             if (_inIndex >= _inSize) {
  418.                                 stream.AddToken(-1);
  419.                                 return;
  420.                             }
  421.                            
  422.                             i = (int)(_inChars[_inIndex++]);
  423.                             break;
  424.                         case TokenSource.String:
  425.                            
  426.                             if (_inIndex >= _inSize) {
  427.                                 stream.AddToken(-1);
  428.                                 return;
  429.                             }
  430.                            
  431.                             i = (int)(_inString[_inIndex++]);
  432.                             break;
  433.                         case TokenSource.NestedStrings:
  434.                            
  435.                             if (_inNestedSize != 0) {
  436.                                 if (_inNestedIndex < _inNestedSize) {
  437.                                     i = _inNestedString[_inNestedIndex++];
  438.                                     break;
  439.                                 }
  440.                                
  441.                                 _inNestedSize = 0;
  442.                             }
  443.                            
  444.                             if (_inIndex >= _inSize) {
  445.                                 stream.AddToken(-1);
  446.                                 return;
  447.                             }
  448.                            
  449.                             i = (int)(_inString[_inIndex++]);
  450.                            
  451.                             if (i != '{')
  452.                                 break;
  453.                            
  454.                             for (int istr = 0; istr < _searchStrings.Length; istr++) {
  455.                                 if (0 == String.Compare(_searchStrings[istr], 0, _inString, _inIndex - 1, _searchStrings[istr].Length, StringComparison.Ordinal)) {
  456.                                     _inNestedString = _replaceStrings[istr];
  457.                                     _inNestedSize = _inNestedString.Length;
  458.                                     _inNestedIndex = 1;
  459.                                     i = _inNestedString[0];
  460.                                     _inIndex += _searchStrings[istr].Length - 1;
  461.                                     break;
  462.                                 }
  463.                             }
  464.                            
  465.                             break;
  466.                         default:
  467.                            
  468.                             i = _inTokenReader.Read();
  469.                             if (i == -1) {
  470.                                 stream.AddToken(-1);
  471.                                 return;
  472.                             }
  473.                             break;
  474.                     }
  475.                
  476.                 if (!inLiteral) {
  477.                     switch (i) {
  478.                         case intSpace:
  479.                         case intTab:
  480.                         case intCR:
  481.                             // skip whitespace
  482.                             goto BEGINNING;
  483.                             break;
  484.                         case intLF:
  485.                            
  486.                             // count linefeeds
  487.                             LineNo++;
  488.                             goto BEGINNING;
  489.                             break;
  490.                         case intOpenBracket:
  491.                            
  492.                             _inProcessingTag++;
  493.                             stream.AddToken(bra);
  494.                             continue;
  495.                         case intCloseBracket:
  496.                            
  497.                             _inProcessingTag--;
  498.                             stream.AddToken(ket);
  499.                             if (endAfterKet)
  500.                                 return;
  501.                             continue;
  502.                         case intEquals:
  503.                            
  504.                             stream.AddToken(equals);
  505.                             continue;
  506.                         case intSlash:
  507.                            
  508.                             if (_inProcessingTag != 0) {
  509.                                 stream.AddToken(slash);
  510.                                 continue;
  511.                             }
  512.                             break;
  513.                         case intQuest:
  514.                            
  515.                             if (_inProcessingTag != 0) {
  516.                                 stream.AddToken(quest);
  517.                                 continue;
  518.                             }
  519.                             break;
  520.                         case intBang:
  521.                            
  522.                             if (_inProcessingTag != 0) {
  523.                                 stream.AddToken(bang);
  524.                                 continue;
  525.                             }
  526.                             break;
  527.                         case intDash:
  528.                            
  529.                             if (_inProcessingTag != 0) {
  530.                                 stream.AddToken(dash);
  531.                                 continue;
  532.                             }
  533.                             break;
  534.                         case intQuote:
  535.                            
  536.                             inLiteral = true;
  537.                             inQuotedString = true;
  538.                             goto BEGINNING;
  539.                             break;
  540.                     }
  541.                 }
  542.                 else {
  543.                     switch (i) {
  544.                         case intOpenBracket:
  545.                             if (!inQuotedString) {
  546.                                 _inSavedCharacter = i;
  547.                                 stream.AddToken(cstr);
  548.                                 stream.AddString(this.GetStringToken());
  549.                                 continue;
  550.                             }
  551.                             break;
  552.                         case intCloseBracket:
  553.                         case intEquals:
  554.                         case intSlash:
  555.                            
  556.                             if (!inQuotedString && _inProcessingTag != 0) {
  557.                                 _inSavedCharacter = i;
  558.                                 stream.AddToken(cstr);
  559.                                 stream.AddString(this.GetStringToken());
  560.                                 continue;
  561.                             }
  562.                             break;
  563.                         case intQuote:
  564.                            
  565.                             if (inQuotedString) {
  566.                                 stream.AddToken(cstr);
  567.                                 stream.AddString(this.GetStringToken());
  568.                                 continue;
  569.                             }
  570.                             break;
  571.                         case intTab:
  572.                         case intCR:
  573.                         case intSpace:
  574.                            
  575.                             if (!inQuotedString) {
  576.                                 stream.AddToken(cstr);
  577.                                 stream.AddString(this.GetStringToken());
  578.                                 continue;
  579.                             }
  580.                             break;
  581.                         case intLF:
  582.                            
  583.                             // count linefeeds
  584.                             LineNo++;
  585.                            
  586.                             if (!inQuotedString) {
  587.                                 stream.AddToken(cstr);
  588.                                 stream.AddString(this.GetStringToken());
  589.                                 continue;
  590.                             }
  591.                             break;
  592.                     }
  593.                 }
  594.                
  595.                 inLiteral = true;
  596.                
  597.                 // add character to the string
  598.                 if (m._outIndex < StringMaker.outMaxSize) {
  599.                     // easy case
  600.                     m._outChars[m._outIndex++] = (char)i;
  601.                 }
  602.                 else {
  603.                     if (m._outStringBuilder == null) {
  604.                         // OK, first check if we have to init the StringBuilder
  605.                         m._outStringBuilder = new StringBuilder();
  606.                     }
  607.                    
  608.                     // OK, copy from _outChars to _outStringBuilder
  609.                     m._outStringBuilder.Append(m._outChars, 0, StringMaker.outMaxSize);
  610.                    
  611.                     // reset _outChars pointer
  612.                     m._outChars[0] = (char)i;
  613.                     m._outIndex = 1;
  614.                 }
  615.                
  616.                 goto BEGINNING;
  617.             }
  618.         }
  619.        
  620.         [Serializable()]
  621.         internal sealed class StringMaker
  622.         {
  623.             string[] aStrings;
  624.             uint cStringsMax;
  625.             uint cStringsUsed;
  626.            
  627.             public StringBuilder _outStringBuilder;
  628.             public char[] _outChars;
  629.             public int _outIndex;
  630.            
  631.             public const int outMaxSize = 512;
  632.            
  633.             static uint HashString(string str)
  634.             {
  635.                 uint hash = 0;
  636.                
  637.                 int l = str.Length;
  638.                
  639.                 // rotate in string character
  640.                 for (int i = 0; i < l; i++) {
  641.                     hash = (hash << 3) ^ (uint)str[i] ^ (hash >> 29);
  642.                 }
  643.                
  644.                 return hash;
  645.             }
  646.            
  647.             static uint HashCharArray(char[] a, int l)
  648.             {
  649.                 uint hash = 0;
  650.                
  651.                 // rotate in a character
  652.                 for (int i = 0; i < l; i++) {
  653.                     hash = (hash << 3) ^ (uint)a[i] ^ (hash >> 29);
  654.                 }
  655.                
  656.                 return hash;
  657.             }
  658.            
  659.             public StringMaker()
  660.             {
  661.                 cStringsMax = 2048;
  662.                 cStringsUsed = 0;
  663.                 aStrings = new string[cStringsMax];
  664.                 _outChars = new char[outMaxSize];
  665.             }
  666.            
  667.             bool CompareStringAndChars(string str, char[] a, int l)
  668.             {
  669.                 if (str.Length != l)
  670.                     return false;
  671.                
  672.                 for (int i = 0; i < l; i++)
  673.                     if (a[i] != str[i])
  674.                         return false;
  675.                
  676.                 return true;
  677.             }
  678.            
  679.             public string MakeString()
  680.             {
  681.                 uint hash;
  682.                 char[] a = _outChars;
  683.                 int l = _outIndex;
  684.                
  685.                 // if we have a stringbuilder then we have to append... slow case
  686.                 if (_outStringBuilder != null) {
  687.                     _outStringBuilder.Append(_outChars, 0, _outIndex);
  688.                     return _outStringBuilder.ToString();
  689.                 }
  690.                
  691.                 // no stringbuilder, fast case, shareable string
  692.                
  693.                 if (cStringsUsed > (cStringsMax / 4) * 3) {
  694.                     // we need to rehash
  695.                    
  696.                     uint cNewMax = cStringsMax * 2;
  697.                     string[] aStringsNew = new string[cNewMax];
  698.                    
  699.                     for (int i = 0; i < cStringsMax; i++) {
  700.                         if (aStrings[i] != null) {
  701.                             hash = HashString(aStrings[i]) % cNewMax;
  702.                            
  703.                             while (aStringsNew[hash] != null) {
  704.                                 // slot full, skip
  705.                                 if (++hash >= cNewMax)
  706.                                     hash = 0;
  707.                             }
  708.                            
  709.                             aStringsNew[hash] = aStrings[i];
  710.                         }
  711.                     }
  712.                    
  713.                     // all done, cutover to the new hash table
  714.                     cStringsMax = cNewMax;
  715.                     aStrings = aStringsNew;
  716.                 }
  717.                
  718.                 hash = HashCharArray(a, l) % cStringsMax;
  719.                
  720.                 string str;
  721.                
  722.                 while ((str = aStrings[hash]) != null) {
  723.                     if (CompareStringAndChars(str, a, l))
  724.                         return str;
  725.                    
  726.                     if (++hash >= cStringsMax)
  727.                         hash = 0;
  728.                 }
  729.                
  730.                 str = new string(a, 0, l);
  731.                 aStrings[hash] = str;
  732.                 cStringsUsed++;
  733.                
  734.                 return str;
  735.             }
  736.         }
  737.        
  738.         //================================================================
  739.         //
  740.         //
  741.        
  742.         private string GetStringToken()
  743.         {
  744.             return _maker.MakeString();
  745.         }
  746.        
  747.         internal interface ITokenReader
  748.         {
  749.             int Read();
  750.         }
  751.        
  752.         internal class StreamTokenReader : ITokenReader
  753.         {
  754.            
  755.             internal StreamReader _in;
  756.             internal int _numCharRead;
  757.            
  758.             internal StreamTokenReader(StreamReader input)
  759.             {
  760.                 _in = input;
  761.                 _numCharRead = 0;
  762.             }
  763.            
  764.             public virtual int Read()
  765.             {
  766.                 int value = _in.Read();
  767.                 if (value != -1)
  768.                     _numCharRead++;
  769.                 return value;
  770.             }
  771.            
  772.             internal int NumCharEncountered {
  773.                 get { return _numCharRead; }
  774.             }
  775.         }
  776.     }
  777.    
  778.     internal sealed class TokenizerShortBlock
  779.     {
  780.         internal short[] m_block = new short[16];
  781.         internal TokenizerShortBlock m_next = null;
  782.     }
  783.    
  784.     internal sealed class TokenizerStringBlock
  785.     {
  786.         internal string[] m_block = new string[16];
  787.         internal TokenizerStringBlock m_next = null;
  788.     }
  789.    
  790.    
  791.     internal sealed class TokenizerStream
  792.     {
  793.         private int m_countTokens;
  794.        
  795.         private TokenizerShortBlock m_headTokens;
  796.         private TokenizerShortBlock m_lastTokens;
  797.         private TokenizerShortBlock m_currentTokens;
  798.         private int m_indexTokens;
  799.        
  800.         private TokenizerStringBlock m_headStrings;
  801.         private TokenizerStringBlock m_currentStrings;
  802.         private int m_indexStrings;
  803.        
  804.         #if _DEBUG
  805.         private bool m_bLastWasCStr;
  806.         #endif
  807.        
  808.         internal TokenizerStream()
  809.         {
  810.             m_countTokens = 0;
  811.             m_headTokens = new TokenizerShortBlock();
  812.             m_headStrings = new TokenizerStringBlock();
  813.             Reset();
  814.         }
  815.        
  816.         internal void AddToken(short token)
  817.         {
  818.             if (m_currentTokens.m_block.Length <= m_indexTokens) {
  819.                 m_currentTokens.m_next = new TokenizerShortBlock();
  820.                 m_currentTokens = m_currentTokens.m_next;
  821.                 m_indexTokens = 0;
  822.             }
  823.            
  824.             m_countTokens++;
  825.             m_currentTokens.m_block[m_indexTokens++] = token;
  826.         }
  827.        
  828.         internal void AddString(string str)
  829.         {
  830.             if (m_currentStrings.m_block.Length <= m_indexStrings) {
  831.                 m_currentStrings.m_next = new TokenizerStringBlock();
  832.                 m_currentStrings = m_currentStrings.m_next;
  833.                 m_indexStrings = 0;
  834.             }
  835.            
  836.             m_currentStrings.m_block[m_indexStrings++] = str;
  837.         }
  838.        
  839.         internal void Reset()
  840.         {
  841.             m_lastTokens = null;
  842.             m_currentTokens = m_headTokens;
  843.             m_currentStrings = m_headStrings;
  844.             m_indexTokens = 0;
  845.             m_indexStrings = 0;
  846.             #if _DEBUG
  847.             m_bLastWasCStr = false;
  848.             #endif
  849.         }
  850.        
  851.         internal short GetNextFullToken()
  852.         {
  853.             if (m_currentTokens.m_block.Length <= m_indexTokens) {
  854.                 m_lastTokens = m_currentTokens;
  855.                 m_currentTokens = m_currentTokens.m_next;
  856.                 m_indexTokens = 0;
  857.             }
  858.            
  859.             return m_currentTokens.m_block[m_indexTokens++];
  860.         }
  861.        
  862.         internal short GetNextToken()
  863.         {
  864.             short retval = (short)(GetNextFullToken() & 255);
  865.             #if _DEBUG
  866.             BCLDebug.Assert(!m_bLastWasCStr, "CStr token not followed by GetNextString()");
  867.             m_bLastWasCStr = (retval == Tokenizer.cstr);
  868.             #endif
  869.             return retval;
  870.         }
  871.        
  872.         internal string GetNextString()
  873.         {
  874.             if (m_currentStrings.m_block.Length <= m_indexStrings) {
  875.                 m_currentStrings = m_currentStrings.m_next;
  876.                 m_indexStrings = 0;
  877.             }
  878.             #if _DEBUG
  879.             m_bLastWasCStr = false;
  880.             #endif
  881.             return m_currentStrings.m_block[m_indexStrings++];
  882.         }
  883.        
  884.         internal void ThrowAwayNextString()
  885.         {
  886.             GetNextString();
  887.         }
  888.        
  889.         internal void TagLastToken(short tag)
  890.         {
  891.             if (m_indexTokens == 0)
  892.                 m_lastTokens.m_block[m_lastTokens.m_block.Length - 1] = (short)((ushort)m_lastTokens.m_block[m_lastTokens.m_block.Length - 1] | (ushort)tag);
  893.             else
  894.                 m_currentTokens.m_block[m_indexTokens - 1] = (short)((ushort)m_currentTokens.m_block[m_indexTokens - 1] | (ushort)tag);
  895.         }
  896.        
  897.         internal int GetTokenCount()
  898.         {
  899.             return m_countTokens;
  900.         }
  901.        
  902.         internal void GoToPosition(int position)
  903.         {
  904.             Reset();
  905.            
  906.             for (int count = 0; count < position; ++count) {
  907.                 if (GetNextToken() == Tokenizer.cstr)
  908.                     ThrowAwayNextString();
  909.             }
  910.         }
  911.     }
  912. }

Developer Fusion