The Labs \ Source Viewer \ SSCLI \ System.Text.RegularExpressions \ ExclusiveReference

  1. //------------------------------------------------------------------------------
  2. // <copyright file="Regex.cs" company="Microsoft">
  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. // </copyright>
  14. //------------------------------------------------------------------------------
  15. // The Regex class represents a single compiled instance of a regular
  16. // expression.
  17. namespace System.Text.RegularExpressions
  18. {
  19.    
  20.     using System;
  21.     using System.Threading;
  22.     using System.Collections;
  23.     using System.Runtime.Serialization;
  24.     using System.Reflection;
  25.     using System.Reflection.Emit;
  26.     using System.Globalization;
  27.     using System.Security.Policy;
  28.     using System.Security.Permissions;
  29.     using System.Runtime.CompilerServices;
  30.     using System.Collections.Generic;
  31.     using System.Runtime.Versioning;
  32.    
  33.     /// <devdoc>
  34.     /// <para>
  35.     /// Represents an immutable, compiled regular expression. Also
  36.     /// contains static methods that allow use of regular expressions without instantiating
  37.     /// a Regex explicitly.
  38.     /// </para>
  39.     /// </devdoc>
  40.     [Serializable()]
  41.     public class Regex : ISerializable
  42.     {
  43.        
  44.         // Fields used by precompiled regexes
  45.         protected internal string pattern;
  46.         protected internal RegexRunnerFactory factory;
  47.         // if compiled, this is the RegexRunner subclass
  48.         protected internal RegexOptions roptions;
  49.         // the top-level options from the options string
  50.         protected internal Hashtable caps;
  51.         // if captures are sparse, this is the hashtable capnum->index
  52.         protected internal Hashtable capnames;
  53.         // if named captures are used, this maps names->index
  54.         protected internal string[] capslist;
  55.         // if captures are sparse or named captures are used, this is the sorted list of names
  56.         protected internal int capsize;
  57.         // the size of the capture array
  58.         internal ExclusiveReference runnerref;
  59.         // cached runner
  60.         internal SharedReference replref;
  61.         // cached parsed replacement pattern
  62.         internal RegexCode code;
  63.         // if interpreted, this is the code for RegexIntepreter
  64.         internal bool refsInitialized = false;
  65.        
  66.         static internal LinkedList<CachedCodeEntry> livecode = new LinkedList<CachedCodeEntry>();
  67.         // the cached of code and factories that are currently loaded
  68.         static internal int cacheSize = 15;
  69.        
  70.         internal const int MaxOptionShift = 10;
  71.        
  72.         protected Regex()
  73.         {
  74.         }
  75.        
  76. /*
  77.         * Compiles and returns a Regex object corresponding to the given pattern
  78.         */       
  79.         /// <devdoc>
  80.         /// <para>
  81.         /// Creates and compiles a regular expression object for the specified regular
  82.         /// expression.
  83.         /// </para>
  84.         /// </devdoc>
  85.         public Regex(string pattern) : this(pattern, RegexOptions.None, false)
  86.         {
  87.         }
  88.        
  89. /*
  90.         * Returns a Regex object corresponding to the given pattern, compiled with
  91.         * the specified options.
  92.         */       
  93.         /// <devdoc>
  94.         /// <para>
  95.         /// Creates and compiles a regular expression object for the
  96.         /// specified regular expression
  97.         /// with options that modify the pattern.
  98.         /// </para>
  99.         /// </devdoc>
  100.         public Regex(string pattern, RegexOptions options) : this(pattern, options, false)
  101.         {
  102.         }
  103.        
  104.         private Regex(string pattern, RegexOptions options, bool useCache)
  105.         {
  106.             RegexTree tree;
  107.             CachedCodeEntry cached = null;
  108.             string cultureKey = null;
  109.            
  110.             if (pattern == null)
  111.                 throw new ArgumentNullException("pattern");
  112.             if (options < RegexOptions.None || (((int)options) >> MaxOptionShift) != 0)
  113.                 throw new ArgumentOutOfRangeException("options");
  114.             if ((options & RegexOptions.ECMAScript) != 0 && (options & ~(RegexOptions.ECMAScript | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Debug)) != 0)
  115.                 #if DBG
  116.                 #endif
  117.                 throw new ArgumentOutOfRangeException("options");
  118.            
  119.             // Try to look up this regex in the cache. We do this regardless of whether useCache is true since there's
  120.             // really no reason not to.
  121.             if ((options & RegexOptions.CultureInvariant) != 0)
  122.                 cultureKey = CultureInfo.InvariantCulture.ThreeLetterWindowsLanguageName;
  123.             else
  124.                 cultureKey = CultureInfo.CurrentCulture.ThreeLetterWindowsLanguageName;
  125.            
  126.             string key = ((int)options).ToString(NumberFormatInfo.InvariantInfo) + ":" + cultureKey + ":" + pattern;
  127.             cached = LookupCachedAndUpdate(key);
  128.            
  129.             this.pattern = pattern;
  130.             this.roptions = options;
  131.            
  132.             if (cached == null) {
  133.                 // Parse the input
  134.                 tree = RegexParser.Parse(pattern, roptions);
  135.                
  136.                 // Extract the relevant information
  137.                 capnames = tree._capnames;
  138.                 capslist = tree._capslist;
  139.                 code = RegexWriter.Write(tree);
  140.                 caps = code._caps;
  141.                 capsize = code._capsize;
  142.                
  143.                 InitializeReferences();
  144.                
  145.                 tree = null;
  146.                 if (useCache)
  147.                     cached = CacheCode(key);
  148.             }
  149.             else {
  150.                 caps = cached._caps;
  151.                 capnames = cached._capnames;
  152.                 capslist = cached._capslist;
  153.                 capsize = cached._capsize;
  154.                 code = cached._code;
  155.                 factory = cached._factory;
  156.                 runnerref = cached._runnerref;
  157.                 replref = cached._replref;
  158.                 refsInitialized = true;
  159.             }
  160.            
  161.             // if the compile option is set, then compile the code if it's not already
  162.             if (UseOptionC() && factory == null) {
  163.                 factory = Compile(code, roptions);
  164.                
  165.                 if (useCache && cached != null)
  166.                     cached.AddCompiled(factory);
  167.                 code = null;
  168.             }
  169.         }
  170.        
  171. /*
  172.         *  ISerializable constructor
  173.         */       
  174.         protected Regex(SerializationInfo info, StreamingContext context) : this(info.GetString("pattern"), (RegexOptions)info.GetInt32("options"))
  175.         {
  176.         }
  177.        
  178. /*
  179.         *  ISerializable method
  180.         */       
  181.         /// <internalonly/>
  182.         void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
  183.         {
  184.             si.AddValue("pattern", this.ToString());
  185.             si.AddValue("options", this.Options);
  186.         }
  187.        
  188. /*
  189.         * This method is here for perf reasons: if the call to RegexCompiler is NOT in the
  190.         * Regex constructor, we don't load RegexCompiler and its reflection classes when
  191.         * instantiating a non-compiled regex
  192.         * This method is internal virtual so the jit does not inline it.
  193.         */       
  194.         [HostProtection(MayLeakOnAbort = true), MethodImplAttribute(MethodImplOptions.NoInlining)]
  195.         internal RegexRunnerFactory Compile(RegexCode code, RegexOptions roptions)
  196.         {
  197.             return RegexCompiler.Compile(code, roptions);
  198.         }
  199.        
  200. /*
  201.         * Escape metacharacters within the string
  202.         */       
  203.         /// <devdoc>
  204.         /// <para>
  205.         /// Escapes
  206.         /// a minimal set of metacharacters (\, *, +, ?, |, {, [, (, ), ^, $, ., #, and
  207.         /// whitespace) by replacing them with their \ codes. This converts a string so that
  208.         /// it can be used as a constant within a regular expression safely. (Note that the
  209.         /// reason # and whitespace must be escaped is so the string can be used safely
  210.         /// within an expression parsed with x mode. If future Regex features add
  211.         /// additional metacharacters, developers should depend on Escape to escape those
  212.         /// characters as well.)
  213.         /// </para>
  214.         /// </devdoc>
  215.         public static string Escape(string str)
  216.         {
  217.             if (str == null)
  218.                 throw new ArgumentNullException("str");
  219.            
  220.             return RegexParser.Escape(str);
  221.         }
  222.        
  223. /*
  224.         * Unescape character codes within the string
  225.         */       
  226.         /// <devdoc>
  227.         /// <para>
  228.         /// Unescapes any escaped characters in the input string.
  229.         /// </para>
  230.         /// </devdoc>
  231.         public static string Unescape(string str)
  232.         {
  233.             if (str == null)
  234.                 throw new ArgumentNullException("str");
  235.            
  236.             return RegexParser.Unescape(str);
  237.         }
  238.        
  239.         public static int CacheSize {
  240.             get { return cacheSize; }
  241.             set {
  242.                 if (value < 0)
  243.                     throw new ArgumentOutOfRangeException("value");
  244.                
  245.                 cacheSize = value;
  246.                 if (livecode.Count > cacheSize) {
  247.                     lock (livecode) {
  248.                         while (livecode.Count > cacheSize)
  249.                             livecode.RemoveLast();
  250.                     }
  251.                 }
  252.             }
  253.         }
  254.        
  255.         /// <devdoc>
  256.         /// <para>
  257.         /// Returns the options passed into the constructor
  258.         /// </para>
  259.         /// </devdoc>
  260.         public RegexOptions Options {
  261.             get { return roptions; }
  262.         }
  263.        
  264. /*
  265.         * True if the regex is leftward
  266.         */       
  267.         /// <devdoc>
  268.         /// <para>
  269.         /// Indicates whether the regular expression matches from right to
  270.         /// left.
  271.         /// </para>
  272.         /// </devdoc>
  273.         public bool RightToLeft {
  274.             get { return UseOptionR(); }
  275.         }
  276.        
  277.         /// <devdoc>
  278.         /// <para>
  279.         /// Returns the regular expression pattern passed into the constructor
  280.         /// </para>
  281.         /// </devdoc>
  282.         public override string ToString()
  283.         {
  284.             return pattern;
  285.         }
  286.        
  287. /*
  288.         * Returns an array of the group names that are used to capture groups
  289.         * in the regular expression. Only needed if the regex is not known until
  290.         * runtime, and one wants to extract captured groups. (Probably unusual,
  291.         * but supplied for completeness.)
  292.         */       
  293.         /// <devdoc>
  294.         /// Returns
  295.         /// the GroupNameCollection for the regular expression. This collection contains the
  296.         /// set of strings used to name capturing groups in the expression.
  297.         /// </devdoc>
  298.         public string[] GetGroupNames()
  299.         {
  300.             string[] result;
  301.            
  302.             if (capslist == null) {
  303.                 int max = capsize;
  304.                 result = new string[max];
  305.                
  306.                 for (int i = 0; i < max; i++) {
  307.                     result[i] = Convert.ToString(i, CultureInfo.InvariantCulture);
  308.                 }
  309.             }
  310.             else {
  311.                 result = new string[capslist.Length];
  312.                
  313.                 System.Array.Copy(capslist, 0, result, 0, capslist.Length);
  314.             }
  315.            
  316.             return result;
  317.         }
  318.        
  319. /*
  320.         * Returns an array of the group numbers that are used to capture groups
  321.         * in the regular expression. Only needed if the regex is not known until
  322.         * runtime, and one wants to extract captured groups. (Probably unusual,
  323.         * but supplied for completeness.)
  324.         */       
  325.         /// <devdoc>
  326.         /// returns
  327.         /// the integer group number corresponding to a group name.
  328.         /// </devdoc>
  329.         public int[] GetGroupNumbers()
  330.         {
  331.             int[] result;
  332.            
  333.             if (caps == null) {
  334.                 int max = capsize;
  335.                 result = new int[max];
  336.                
  337.                 for (int i = 0; i < max; i++) {
  338.                     result[i] = i;
  339.                 }
  340.             }
  341.             else {
  342.                 result = new int[caps.Count];
  343.                
  344.                 IDictionaryEnumerator de = caps.GetEnumerator();
  345.                 while (de.MoveNext()) {
  346.                     result[(int)de.Value] = (int)de.Key;
  347.                 }
  348.             }
  349.            
  350.             return result;
  351.         }
  352.        
  353. /*
  354.         * Given a group number, maps it to a group name. Note that nubmered
  355.         * groups automatically get a group name that is the decimal string
  356.         * equivalent of its number.
  357.         *
  358.         * Returns null if the number is not a recognized group number.
  359.         */       
  360.         /// <devdoc>
  361.         /// <para>
  362.         /// Retrieves a group name that corresponds to a group number.
  363.         /// </para>
  364.         /// </devdoc>
  365.         public string GroupNameFromNumber(int i)
  366.         {
  367.             if (capslist == null) {
  368.                 if (i >= 0 && i < capsize)
  369.                     return i.ToString(CultureInfo.InvariantCulture);
  370.                
  371.                 return String.Empty;
  372.             }
  373.             else {
  374.                 if (caps != null) {
  375.                     object obj = caps[i];
  376.                     if (obj == null)
  377.                         return String.Empty;
  378.                    
  379.                     i = (int)obj;
  380.                 }
  381.                
  382.                 if (i >= 0 && i < capslist.Length)
  383.                     return capslist[i];
  384.                
  385.                 return String.Empty;
  386.             }
  387.         }
  388.        
  389. /*
  390.         * Given a group name, maps it to a group number. Note that nubmered
  391.         * groups automatically get a group name that is the decimal string
  392.         * equivalent of its number.
  393.         *
  394.         * Returns -1 if the name is not a recognized group name.
  395.         */       
  396.         /// <devdoc>
  397.         /// <para>
  398.         /// Returns a group number that corresponds to a group name.
  399.         /// </para>
  400.         /// </devdoc>
  401.         public int GroupNumberFromName(string name)
  402.         {
  403.             int result = -1;
  404.            
  405.             if (name == null)
  406.                 throw new ArgumentNullException("name");
  407.            
  408.             // look up name if we have a hashtable of names
  409.             if (capnames != null) {
  410.                 object ret = capnames[name];
  411.                
  412.                 if (ret == null)
  413.                     return -1;
  414.                
  415.                 return (int)ret;
  416.             }
  417.            
  418.             // convert to an int if it looks like a number
  419.             result = 0;
  420.             for (int i = 0; i < name.Length; i++) {
  421.                 char ch = name[i];
  422.                
  423.                 if (ch > '9' || ch < '0')
  424.                     return -1;
  425.                
  426.                 result *= 10;
  427.                 result += (ch - '0');
  428.             }
  429.            
  430.             // return int if it's in range
  431.             if (result >= 0 && result < capsize)
  432.                 return result;
  433.            
  434.             return -1;
  435.         }
  436.        
  437. /*
  438.         * Static version of simple IsMatch call
  439.         */       
  440.         /// <devdoc>
  441.         /// <para>
  442.         /// Searches the input
  443.         /// string for one or more occurrences of the text supplied in the pattern
  444.         /// parameter.
  445.         /// </para>
  446.         /// </devdoc>
  447.         public static bool IsMatch(string input, string pattern)
  448.         {
  449.             return new Regex(pattern, RegexOptions.None, true).IsMatch(input);
  450.         }
  451.        
  452. /*
  453.         * Static version of simple IsMatch call
  454.         */       
  455.         /// <devdoc>
  456.         /// <para>
  457.         /// Searches the input string for one or more occurrences of the text
  458.         /// supplied in the pattern parameter with matching options supplied in the options
  459.         /// parameter.
  460.         /// </para>
  461.         /// </devdoc>
  462.         public static bool IsMatch(string input, string pattern, RegexOptions options)
  463.         {
  464.             return new Regex(pattern, options, true).IsMatch(input);
  465.         }
  466.        
  467. /*
  468.         * Returns true if the regex finds a match within the specified string
  469.         */       
  470.         /// <devdoc>
  471.         /// <para>
  472.         /// Searches the input string for one or
  473.         /// more matches using the previous pattern, options, and starting
  474.         /// position.
  475.         /// </para>
  476.         /// </devdoc>
  477.         public bool IsMatch(string input)
  478.         {
  479.             if (input == null)
  480.                 throw new ArgumentNullException("input");
  481.            
  482.             return (null == Run(true, -1, input, 0, input.Length, UseOptionR() ? input.Length : 0));
  483.         }
  484.        
  485. /*
  486.         * Returns true if the regex finds a match after the specified position
  487.         * (proceeding leftward if the regex is leftward and rightward otherwise)
  488.         */       
  489.         /// <devdoc>
  490.         /// <para>
  491.         /// Searches the input
  492.         /// string for one or more matches using the previous pattern and options, with
  493.         /// a new starting position.
  494.         /// </para>
  495.         /// </devdoc>
  496.         public bool IsMatch(string input, int startat)
  497.         {
  498.             if (input == null)
  499.                 throw new ArgumentNullException("input");
  500.            
  501.             return (null == Run(true, -1, input, 0, input.Length, startat));
  502.         }
  503.        
  504. /*
  505.         * Static version of simple Match call
  506.         */       
  507.         /// <devdoc>
  508.         /// <para>
  509.         /// Searches the input string for one or more occurrences of the text
  510.         /// supplied in the pattern parameter.
  511.         /// </para>
  512.         /// </devdoc>
  513.         public static Match Match(string input, string pattern)
  514.         {
  515.             return new Regex(pattern, RegexOptions.None, true).Match(input);
  516.         }
  517.        
  518. /*
  519.         * Static version of simple Match call
  520.         */       
  521.         /// <devdoc>
  522.         /// <para>
  523.         /// Searches the input string for one or more occurrences of the text
  524.         /// supplied in the pattern parameter. Matching is modified with an option
  525.         /// string.
  526.         /// </para>
  527.         /// </devdoc>
  528.         public static Match Match(string input, string pattern, RegexOptions options)
  529.         {
  530.             return new Regex(pattern, options, true).Match(input);
  531.         }
  532.        
  533. /*
  534.         * Finds the first match for the regular expression starting at the beginning
  535.         * of the string (or at the end of the string if the regex is leftward)
  536.         */       
  537.         /// <devdoc>
  538.         /// <para>
  539.         /// Matches a regular expression with a string and returns
  540.         /// the precise result as a RegexMatch object.
  541.         /// </para>
  542.         /// </devdoc>
  543.         public Match Match(string input)
  544.         {
  545.             if (input == null)
  546.                 throw new ArgumentNullException("input");
  547.            
  548.             return Run(false, -1, input, 0, input.Length, UseOptionR() ? input.Length : 0);
  549.         }
  550.        
  551. /*
  552.         * Finds the first match, starting at the specified position
  553.         */       
  554.         /// <devdoc>
  555.         /// Matches a regular expression with a string and returns
  556.         /// the precise result as a RegexMatch object.
  557.         /// </devdoc>
  558.         public Match Match(string input, int startat)
  559.         {
  560.             if (input == null)
  561.                 throw new ArgumentNullException("input");
  562.            
  563.             return Run(false, -1, input, 0, input.Length, startat);
  564.         }
  565.        
  566. /*
  567.         * Finds the first match, restricting the search to the specified interval of
  568.         * the char array.
  569.         */       
  570.         /// <devdoc>
  571.         /// <para>
  572.         /// Matches a
  573.         /// regular expression with a string and returns the precise result as a
  574.         /// RegexMatch object.
  575.         /// </para>
  576.         /// </devdoc>
  577.         public Match Match(string input, int beginning, int length)
  578.         {
  579.             if (input == null)
  580.                 throw new ArgumentNullException("input");
  581.            
  582.             return Run(false, -1, input, beginning, length, UseOptionR() ? beginning + length : beginning);
  583.         }
  584.        
  585. /*
  586.         * Static version of simple Matches call
  587.         */       
  588.         /// <devdoc>
  589.         /// <para>
  590.         /// Returns all the successful matches as if Match were
  591.         /// called iteratively numerous times.
  592.         /// </para>
  593.         /// </devdoc>
  594.         public static MatchCollection Matches(string input, string pattern)
  595.         {
  596.             return new Regex(pattern, RegexOptions.None, true).Matches(input);
  597.         }
  598.        
  599. /*
  600.         * Static version of simple Matches call
  601.         */       
  602.         /// <devdoc>
  603.         /// <para>
  604.         /// Returns all the successful matches as if Match were called iteratively
  605.         /// numerous times.
  606.         /// </para>
  607.         /// </devdoc>
  608.         public static MatchCollection Matches(string input, string pattern, RegexOptions options)
  609.         {
  610.             return new Regex(pattern, options, true).Matches(input);
  611.         }
  612.        
  613. /*
  614.         * Finds the first match for the regular expression starting at the beginning
  615.         * of the string Enumerator(or at the end of the string if the regex is leftward)
  616.         */       
  617.         /// <devdoc>
  618.         /// <para>
  619.         /// Returns
  620.         /// all the successful matches as if Match was called iteratively numerous
  621.         /// times.
  622.         /// </para>
  623.         /// </devdoc>
  624.         public MatchCollection Matches(string input)
  625.         {
  626.             if (input == null)
  627.                 throw new ArgumentNullException("input");
  628.            
  629.             return new MatchCollection(this, input, 0, input.Length, UseOptionR() ? input.Length : 0);
  630.         }
  631.        
  632. /*
  633.         * Finds the first match, starting at the specified position
  634.         */       
  635.         /// <devdoc>
  636.         /// <para>
  637.         /// Returns
  638.         /// all the successful matches as if Match was called iteratively numerous
  639.         /// times.
  640.         /// </para>
  641.         /// </devdoc>
  642.         public MatchCollection Matches(string input, int startat)
  643.         {
  644.             if (input == null)
  645.                 throw new ArgumentNullException("input");
  646.            
  647.             return new MatchCollection(this, input, 0, input.Length, startat);
  648.         }
  649.        
  650. /*
  651.         * Static version of simple Replace call
  652.         */       
  653.         /// <devdoc>
  654.         /// <para>
  655.         /// Replaces
  656.         /// all occurrences of the pattern with the <paramref name="replacement"/> pattern, starting at
  657.         /// the first character in the input string.
  658.         /// </para>
  659.         /// </devdoc>
  660.         public static string Replace(string input, string pattern, string replacement)
  661.         {
  662.             return new Regex(pattern, RegexOptions.None, true).Replace(input, replacement);
  663.         }
  664.        
  665. /*
  666.         * Static version of simple Replace call
  667.         */       
  668.         /// <devdoc>
  669.         /// <para>
  670.         /// Replaces all occurrences of
  671.         /// the <paramref name="pattern "/>with the <paramref name="replacement "/>
  672.         /// pattern, starting at the first character in the input string.
  673.         /// </para>
  674.         /// </devdoc>
  675.         public static string Replace(string input, string pattern, string replacement, RegexOptions options)
  676.         {
  677.             return new Regex(pattern, options, true).Replace(input, replacement);
  678.         }
  679.        
  680. /*
  681.         * Does the replacement
  682.         */       
  683.         /// <devdoc>
  684.         /// <para>
  685.         /// Replaces all occurrences of
  686.         /// the <paramref name="pattern "/> with the <paramref name="replacement"/> pattern, starting at the
  687.         /// first character in the input string, using the previous patten.
  688.         /// </para>
  689.         /// </devdoc>
  690.         public string Replace(string input, string replacement)
  691.         {
  692.             if (input == null)
  693.                 throw new ArgumentNullException("input");
  694.            
  695.             return Replace(input, replacement, -1, UseOptionR() ? input.Length : 0);
  696.         }
  697.        
  698. /*
  699.         * Does the replacement
  700.         */       
  701.         /// <devdoc>
  702.         /// <para>
  703.         /// Replaces all occurrences of the (previously defined) <paramref name="pattern "/>with the
  704.         /// <paramref name="replacement"/> pattern, starting at the first character in the input string.
  705.         /// </para>
  706.         /// </devdoc>
  707.         public string Replace(string input, string replacement, int count)
  708.         {
  709.             if (input == null)
  710.                 throw new ArgumentNullException("input");
  711.            
  712.             return Replace(input, replacement, count, UseOptionR() ? input.Length : 0);
  713.         }
  714.        
  715. /*
  716.         * Does the replacement
  717.         */       
  718.         /// <devdoc>
  719.         /// <para>
  720.         /// Replaces all occurrences of the <paramref name="pattern "/>with the recent
  721.         /// <paramref name="replacement"/> pattern, starting at the character position
  722.         /// <paramref name="startat."/>
  723.         /// </para>
  724.         /// </devdoc>
  725.         public string Replace(string input, string replacement, int count, int startat)
  726.         {
  727.             RegexReplacement repl;
  728.            
  729.             if (input == null)
  730.                 throw new ArgumentNullException("input");
  731.             if (replacement == null)
  732.                 throw new ArgumentNullException("replacement");
  733.            
  734.             // a little code to grab a cached parsed replacement object
  735.             repl = (RegexReplacement)replref.Get();
  736.            
  737.             if (repl == null || !repl.Pattern.Equals(replacement)) {
  738.                 repl = RegexParser.ParseReplacement(replacement, caps, capsize, capnames, this.roptions);
  739.                 replref.Cache(repl);
  740.             }
  741.            
  742.             return repl.Replace(this, input, count, startat);
  743.         }
  744.        
  745. /*
  746.         * Static version of simple Replace call
  747.         */       
  748.         /// <devdoc>
  749.         /// <para>
  750.         /// Replaces all occurrences of the <paramref name="pattern "/>with the
  751.         /// <paramref name="replacement"/> pattern
  752.         /// <paramref name="."/>
  753.         /// </para>
  754.         /// </devdoc>
  755.         public static string Replace(string input, string pattern, MatchEvaluator evaluator)
  756.         {
  757.             return new Regex(pattern, RegexOptions.None, true).Replace(input, evaluator);
  758.         }
  759.        
  760. /*
  761.         * Static version of simple Replace call
  762.         */       
  763.         /// <devdoc>
  764.         /// <para>
  765.         /// Replaces all occurrences of the <paramref name="pattern "/>with the recent
  766.         /// <paramref name="replacement"/> pattern, starting at the first character<paramref name="."/>
  767.         /// </para>
  768.         /// </devdoc>
  769.         public static string Replace(string input, string pattern, MatchEvaluator evaluator, RegexOptions options)
  770.         {
  771.             return new Regex(pattern, options, true).Replace(input, evaluator);
  772.         }
  773.        
  774. /*
  775.         * Does the replacement
  776.         */       
  777.         /// <devdoc>
  778.         /// <para>
  779.         /// Replaces all occurrences of the <paramref name="pattern "/>with the recent
  780.         /// <paramref name="replacement"/> pattern, starting at the first character
  781.         /// position<paramref name="."/>
  782.         /// </para>
  783.         /// </devdoc>
  784.         public string Replace(string input, MatchEvaluator evaluator)
  785.         {
  786.             if (input == null)
  787.                 throw new ArgumentNullException("input");
  788.            
  789.             return Replace(input, evaluator, -1, UseOptionR() ? input.Length : 0);
  790.         }
  791.        
  792. /*
  793.         * Does the replacement
  794.         */       
  795.         /// <devdoc>
  796.         /// <para>
  797.         /// Replaces all occurrences of the <paramref name="pattern "/>with the recent
  798.         /// <paramref name="replacement"/> pattern, starting at the first character
  799.         /// position<paramref name="."/>
  800.         /// </para>
  801.         /// </devdoc>
  802.         public string Replace(string input, MatchEvaluator evaluator, int count)
  803.         {
  804.             if (input == null)
  805.                 throw new ArgumentNullException("input");
  806.            
  807.             return Replace(input, evaluator, count, UseOptionR() ? input.Length : 0);
  808.         }
  809.        
  810. /*
  811.         * Does the replacement
  812.         */       
  813.         /// <devdoc>
  814.         /// <para>
  815.         /// Replaces all occurrences of the (previouly defined) <paramref name="pattern "/>with
  816.         /// the recent <paramref name="replacement"/> pattern, starting at the character
  817.         /// position<paramref name=" startat."/>
  818.         /// </para>
  819.         /// </devdoc>
  820.         public string Replace(string input, MatchEvaluator evaluator, int count, int startat)
  821.         {
  822.             if (input == null)
  823.                 throw new ArgumentNullException("input");
  824.            
  825.             return RegexReplacement.Replace(evaluator, this, input, count, startat);
  826.         }
  827.        
  828. /*
  829.         * Static version of simple Split call
  830.         */       
  831.         /// <devdoc>
  832.         /// <para>
  833.         /// Splits the <paramref name="input "/>string at the position defined
  834.         /// by <paramref name="pattern"/>.
  835.         /// </para>
  836.         /// </devdoc>
  837.         public static string[] Split(string input, string pattern)
  838.         {
  839.             return new Regex(pattern, RegexOptions.None, true).Split(input);
  840.         }
  841.        
  842. /*
  843.         * Static version of simple Split call
  844.         */       
  845.         /// <devdoc>
  846.         /// <para>
  847.         /// Splits the <paramref name="input "/>string at the position defined by <paramref name="pattern"/>.
  848.         /// </para>
  849.         /// </devdoc>
  850.         public static string[] Split(string input, string pattern, RegexOptions options)
  851.         {
  852.             return new Regex(pattern, options, true).Split(input);
  853.         }
  854.        
  855. /*
  856.         * Does a split
  857.         */       
  858.         /// <devdoc>
  859.         /// <para>
  860.         /// Splits the <paramref name="input "/>string at the position defined by
  861.         /// a previous <paramref name="pattern"/>
  862.         /// .
  863.         /// </para>
  864.         /// </devdoc>
  865.         public string[] Split(string input)
  866.         {
  867.             if (input == null)
  868.                 throw new ArgumentNullException("input");
  869.            
  870.             return Split(input, 0, UseOptionR() ? input.Length : 0);
  871.         }
  872.        
  873. /*
  874.         * Does a split
  875.         */       
  876.         /// <devdoc>
  877.         /// <para>
  878.         /// Splits the <paramref name="input "/>string at the position defined by a previous
  879.         /// <paramref name="pattern"/> .
  880.         /// </para>
  881.         /// </devdoc>
  882.         public string[] Split(string input, int count)
  883.         {
  884.             if (input == null)
  885.                 throw new ArgumentNullException("input");
  886.            
  887.             return RegexReplacement.Split(this, input, count, UseOptionR() ? input.Length : 0);
  888.         }
  889.        
  890. /*
  891.         * Does a split
  892.         */       
  893.         /// <devdoc>
  894.         /// <para>
  895.         /// Splits the <paramref name="input "/>string at the position defined by a previous
  896.         /// <paramref name="pattern"/> .
  897.         /// </para>
  898.         /// </devdoc>
  899.         public string[] Split(string input, int count, int startat)
  900.         {
  901.             if (input == null)
  902.                 throw new ArgumentNullException("input");
  903.            
  904.             return RegexReplacement.Split(this, input, count, startat);
  905.         }
  906.        
  907.         /// <devdoc>
  908.         /// </devdoc>
  909.         [HostProtection(MayLeakOnAbort = true)]
  910.         [ResourceExposure(ResourceScope.Machine)]
  911.         // The AssemblyName is interesting.
  912.         [ResourceConsumption(ResourceScope.Machine)]
  913.         public static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname)
  914.         {
  915.            
  916.             CompileToAssemblyInternal(regexinfos, assemblyname, null, null, Assembly.GetCallingAssembly().Evidence);
  917.         }
  918.        
  919.         /// <devdoc>
  920.         /// </devdoc>
  921.         [HostProtection(MayLeakOnAbort = true)]
  922.         [ResourceExposure(ResourceScope.Machine)]
  923.         // The AssemblyName is interesting.
  924.         [ResourceConsumption(ResourceScope.Machine)]
  925.         public static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname, CustomAttributeBuilder[] attributes)
  926.         {
  927.             CompileToAssemblyInternal(regexinfos, assemblyname, attributes, null, Assembly.GetCallingAssembly().Evidence);
  928.         }
  929.        
  930.         [HostProtection(MayLeakOnAbort = true)]
  931.         [ResourceExposure(ResourceScope.Machine)]
  932.         [ResourceConsumption(ResourceScope.Machine)]
  933.         public static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname, CustomAttributeBuilder[] attributes, string resourceFile)
  934.         {
  935.             CompileToAssemblyInternal(regexinfos, assemblyname, attributes, resourceFile, Assembly.GetCallingAssembly().Evidence);
  936.         }
  937.        
  938.         [ResourceExposure(ResourceScope.Machine)]
  939.         // AssemblyName & resourceFile
  940.         [ResourceConsumption(ResourceScope.Machine)]
  941.         private static void CompileToAssemblyInternal(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname, CustomAttributeBuilder[] attributes, string resourceFile, Evidence evidence)
  942.         {
  943.             if (assemblyname == null)
  944.                 throw new ArgumentNullException("assemblyname");
  945.            
  946.             if (regexinfos == null)
  947.                 throw new ArgumentNullException("regexinfos");
  948.            
  949.             RegexCompiler.CompileToAssembly(regexinfos, assemblyname, attributes, resourceFile, evidence);
  950.         }
  951.        
  952.         /// <devdoc>
  953.         /// </devdoc>
  954.         protected void InitializeReferences()
  955.         {
  956.             if (refsInitialized)
  957.                 throw new NotSupportedException(SR.GetString(SR.OnlyAllowedOnce));
  958.            
  959.             refsInitialized = true;
  960.             runnerref = new ExclusiveReference();
  961.             replref = new SharedReference();
  962.         }
  963.        
  964. /*
  965.         * Internal worker called by all the public APIs
  966.         */       
  967.         internal Match Run(bool quick, int prevlen, string input, int beginning, int length, int startat)
  968.         {
  969.             Match match;
  970.             RegexRunner runner = null;
  971.            
  972.             if (startat < 0 || startat > input.Length)
  973.                 throw new ArgumentOutOfRangeException("start", SR.GetString(SR.BeginIndexNotNegative));
  974.            
  975.             if (length < 0 || length > input.Length)
  976.                 throw new ArgumentOutOfRangeException("length", SR.GetString(SR.LengthNotNegative));
  977.            
  978.             // There may be a cached runner; grab ownership of it if we can.
  979.            
  980.             runner = (RegexRunner)runnerref.Get();
  981.            
  982.             // Create a RegexRunner instance if we need to
  983.            
  984.             if (runner == null) {
  985.                 // Use the compiled RegexRunner factory if the code was compiled to MSIL
  986.                
  987.                 if (factory != null)
  988.                     runner = factory.CreateInstance();
  989.                 else
  990.                     runner = new RegexInterpreter(code, UseOptionInvariant() ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture);
  991.             }
  992.            
  993.             // Do the scan starting at the requested position
  994.            
  995.             match = runner.Scan(this, input, beginning, beginning + length, startat, prevlen, quick);
  996.            
  997.             // Release or fill the cache slot
  998.            
  999.             runnerref.Release(runner);
  1000.            
  1001.             #if DBG
  1002.             if (Debug && match != null)
  1003.                 match.Dump();
  1004.             #endif
  1005.             return match;
  1006.         }
  1007.        
  1008. /*
  1009.         * Find code cache based on options+pattern
  1010.         */       
  1011.         private static CachedCodeEntry LookupCachedAndUpdate(string key)
  1012.         {
  1013.             lock (livecode) {
  1014.                 for (LinkedListNode<CachedCodeEntry> current = livecode.First; current != null; current = current.Next) {
  1015.                     if (current.Value._key == key) {
  1016.                         // If we find an entry in the cache, move it to the head at the same time.
  1017.                         livecode.Remove(current);
  1018.                         livecode.AddFirst(current);
  1019.                         return current.Value;
  1020.                     }
  1021.                 }
  1022.             }
  1023.            
  1024.             return null;
  1025.         }
  1026.        
  1027. /*
  1028.         * Add current code to the cache
  1029.         */       
  1030.         private CachedCodeEntry CacheCode(string key)
  1031.         {
  1032.             CachedCodeEntry newcached = null;
  1033.            
  1034.             lock (livecode) {
  1035.                 // first look for it in the cache and move it to the head
  1036.                 for (LinkedListNode<CachedCodeEntry> current = livecode.First; current != null; current = current.Next) {
  1037.                     if (current.Value._key == key) {
  1038.                         livecode.Remove(current);
  1039.                         livecode.AddFirst(current);
  1040.                         return current.Value;
  1041.                     }
  1042.                 }
  1043.                
  1044.                 // it wasn't in the cache, so we'll add a new one. Shortcut out for the case where cacheSize is zero.
  1045.                 if (cacheSize != 0) {
  1046.                     newcached = new CachedCodeEntry(key, capnames, capslist, code, caps, capsize, runnerref, replref);
  1047.                     livecode.AddFirst(newcached);
  1048.                     if (livecode.Count > cacheSize)
  1049.                         livecode.RemoveLast();
  1050.                 }
  1051.             }
  1052.            
  1053.             return newcached;
  1054.         }
  1055.        
  1056. /*
  1057.         * True if the O option was set
  1058.         */       
  1059.         /// <internalonly/>
  1060.         /// <devdoc>
  1061.         /// </devdoc>
  1062.         protected bool UseOptionC()
  1063.         {
  1064.             return (roptions & RegexOptions.Compiled) != 0;
  1065.         }
  1066.        
  1067. /*
  1068.         * True if the L option was set
  1069.         */       
  1070.         /// <internalonly/>
  1071.         /// <devdoc>
  1072.         /// </devdoc>
  1073.         protected bool UseOptionR()
  1074.         {
  1075.             return (roptions & RegexOptions.RightToLeft) != 0;
  1076.         }
  1077.        
  1078.         internal bool UseOptionInvariant()
  1079.         {
  1080.             return (roptions & RegexOptions.CultureInvariant) != 0;
  1081.         }
  1082.        
  1083.        
  1084.         #if DBG
  1085. /*
  1086.         * True if the regex has debugging enabled
  1087.         */       
  1088.         /// <internalonly/>
  1089.         /// <devdoc>
  1090.         /// </devdoc>
  1091.         internal bool Debug {
  1092.             get { return (roptions & RegexOptions.Debug) != 0; }
  1093.         }
  1094.        
  1095.         #endif
  1096.     }
  1097.    
  1098.    
  1099. /*
  1100.     * Callback class
  1101.     */   
  1102.     /// <devdoc>
  1103.     /// </devdoc>
  1104.     [Serializable()]
  1105.     public delegate string MatchEvaluator(Match match);
  1106.    
  1107.    
  1108. /*
  1109.     * Used to cache byte codes or compiled factories
  1110.     */   
  1111.     internal sealed class CachedCodeEntry
  1112.     {
  1113.         internal string _key;
  1114.         internal RegexCode _code;
  1115.         internal Hashtable _caps;
  1116.         internal Hashtable _capnames;
  1117.         internal string[] _capslist;
  1118.         internal int _capsize;
  1119.         internal RegexRunnerFactory _factory;
  1120.         internal ExclusiveReference _runnerref;
  1121.         internal SharedReference _replref;
  1122.        
  1123.         internal CachedCodeEntry(string key, Hashtable capnames, string[] capslist, RegexCode code, Hashtable caps, int capsize, ExclusiveReference runner, SharedReference repl)
  1124.         {
  1125.            
  1126.             _key = key;
  1127.             _capnames = capnames;
  1128.             _capslist = capslist;
  1129.            
  1130.             _code = code;
  1131.             _caps = caps;
  1132.             _capsize = capsize;
  1133.            
  1134.             _runnerref = runner;
  1135.             _replref = repl;
  1136.         }
  1137.        
  1138.         internal void AddCompiled(RegexRunnerFactory factory)
  1139.         {
  1140.             _factory = factory;
  1141.             _code = null;
  1142.         }
  1143.     }
  1144.    
  1145. /*
  1146.     * Used to cache one exclusive runner reference
  1147.     */   
  1148.     internal sealed class ExclusiveReference
  1149.     {
  1150.         RegexRunner _ref;
  1151.         object _obj;
  1152.         int _locked;
  1153.        
  1154. /*
  1155.         * Return an object and grab an exclusive lock.
  1156.         *
  1157.         * If the exclusive lock can't be obtained, null is returned;
  1158.         * if the object can't be returned, the lock is released.
  1159.         *
  1160.         */       
  1161.         internal object Get()
  1162.         {
  1163.             // try to obtain the lock
  1164.            
  1165.             if (0 == Interlocked.Exchange(ref _locked, 1)) {
  1166.                 // grab reference
  1167.                
  1168.                
  1169.                 object obj = _ref;
  1170.                
  1171.                 // release the lock and return null if no reference
  1172.                
  1173.                 if (obj == null) {
  1174.                     _locked = 0;
  1175.                     return null;
  1176.                 }
  1177.                
  1178.                 // remember the reference and keep the lock
  1179.                
  1180.                 _obj = obj;
  1181.                 return obj;
  1182.             }
  1183.            
  1184.             return null;
  1185.         }
  1186.        
  1187. /*
  1188.         * Release an object back to the cache
  1189.         *
  1190.         * If the object is the one that's under lock, the lock
  1191.         * is released.
  1192.         *
  1193.         * If there is no cached object, then the lock is obtained
  1194.         * and the object is placed in the cache.
  1195.         *
  1196.         */       
  1197.         internal void Release(object obj)
  1198.         {
  1199.             if (obj == null)
  1200.                 throw new ArgumentNullException("obj");
  1201.            
  1202.             // if this reference owns the lock, release it
  1203.            
  1204.             if (_obj == obj) {
  1205.                 _obj = null;
  1206.                 _locked = 0;
  1207.                 return;
  1208.             }
  1209.            
  1210.             // if no reference owns the lock, try to cache this reference
  1211.            
  1212.             if (_obj == null) {
  1213.                 // try to obtain the lock
  1214.                
  1215.                 if (0 == Interlocked.Exchange(ref _locked, 1)) {
  1216.                     // if there's really no reference, cache this reference
  1217.                    
  1218.                     if (_ref == null)
  1219.                         _ref = (RegexRunner)obj;
  1220.                    
  1221.                     // release the lock
  1222.                    
  1223.                     _locked = 0;
  1224.                     return;
  1225.                 }
  1226.             }
  1227.         }
  1228.     }
  1229.    
  1230. /*
  1231.     * Used to cache a weak reference in a threadsafe way
  1232.     */   
  1233.     internal sealed class SharedReference
  1234.     {
  1235.         WeakReference _ref = new WeakReference(null);
  1236.         int _locked;
  1237.        
  1238. /*
  1239.         * Return an object from a weakref, protected by a lock.
  1240.         *
  1241.         * If the exclusive lock can't be obtained, null is returned;
  1242.         *
  1243.         * Note that _ref.Target is referenced only under the protection
  1244.         * of the lock. (Is this necessary?)
  1245.         */       
  1246.         internal object Get()
  1247.         {
  1248.             if (0 == Interlocked.Exchange(ref _locked, 1)) {
  1249.                 object obj = _ref.Target;
  1250.                 _locked = 0;
  1251.                 return obj;
  1252.             }
  1253.            
  1254.             return null;
  1255.         }
  1256.        
  1257. /*
  1258.         * Suggest an object into a weakref, protected by a lock.
  1259.         *
  1260.         * Note that _ref.Target is referenced only under the protection
  1261.         * of the lock. (Is this necessary?)
  1262.         */       
  1263.         internal void Cache(object obj)
  1264.         {
  1265.             if (0 == Interlocked.Exchange(ref _locked, 1)) {
  1266.                 _ref.Target = obj;
  1267.                 _locked = 0;
  1268.             }
  1269.         }
  1270.     }
  1271.    
  1272. }

Developer Fusion