You've already forked linux-packaging-mono
Imported Upstream version 5.16.0.100
Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
parent
0a9828183b
commit
7d7f676260
@@ -7,6 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Text.RegularExpressi
|
||||
{2C58640B-5BED-4E83-9554-CD2B9762643F} = {2C58640B-5BED-4E83-9554-CD2B9762643F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Text.RegularExpressions.Performance.Tests", "tests\Performance\System.Text.RegularExpressions.Performance.Tests.csproj", "{7f4b8c48-8692-4885-bf84-feb7ea82e34b}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2C58640B-5BED-4E83-9554-CD2B9762643F} = {2C58640B-5BED-4E83-9554-CD2B9762643F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Text.RegularExpressions", "src\System.Text.RegularExpressions.csproj", "{2C58640B-5BED-4E83-9554-CD2B9762643F}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{B262B15E-13E6-4C1E-A25E-16D06E222A09} = {B262B15E-13E6-4C1E-A25E-16D06E222A09}
|
||||
@@ -30,6 +35,10 @@ Global
|
||||
{94B106C2-D574-4392-80AB-3EE308A078DF}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU
|
||||
{94B106C2-D574-4392-80AB-3EE308A078DF}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU
|
||||
{94B106C2-D574-4392-80AB-3EE308A078DF}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU
|
||||
{7f4b8c48-8692-4885-bf84-feb7ea82e34b}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU
|
||||
{7f4b8c48-8692-4885-bf84-feb7ea82e34b}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU
|
||||
{7f4b8c48-8692-4885-bf84-feb7ea82e34b}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU
|
||||
{7f4b8c48-8692-4885-bf84-feb7ea82e34b}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU
|
||||
{2C58640B-5BED-4E83-9554-CD2B9762643F}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU
|
||||
{2C58640B-5BED-4E83-9554-CD2B9762643F}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU
|
||||
{2C58640B-5BED-4E83-9554-CD2B9762643F}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU
|
||||
@@ -44,6 +53,7 @@ Global
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{94B106C2-D574-4392-80AB-3EE308A078DF} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
|
||||
{7f4b8c48-8692-4885-bf84-feb7ea82e34b} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
|
||||
{2C58640B-5BED-4E83-9554-CD2B9762643F} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD}
|
||||
{B262B15E-13E6-4C1E-A25E-16D06E222A09} = {2E666815-2EDB-464B-9DF6-380BF4789AD4}
|
||||
EndGlobalSection
|
||||
|
||||
2
external/corefx/src/System.Text.RegularExpressions/src/MatchingRefApiCompatBaseline.txt
vendored
Normal file
2
external/corefx/src/System.Text.RegularExpressions/src/MatchingRefApiCompatBaseline.txt
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# API is only for Debug purposes
|
||||
MembersMustExist : Member 'System.Text.RegularExpressions.RegexOptions System.Text.RegularExpressions.RegexOptions.Debug' does not exist in the implementation but it does exist in the contract.
|
||||
@@ -5,42 +5,54 @@
|
||||
<ProjectGuid>{2C58640B-5BED-4E83-9554-CD2B9762643F}</ProjectGuid>
|
||||
<AssemblyName>System.Text.RegularExpressions</AssemblyName>
|
||||
<DefineConstants Condition="'$(TargetGroup)' == 'netcoreapp'">$(DefineConstants);FEATURE_COMPILED</DefineConstants>
|
||||
<ILLinkClearInitLocals>true</ILLinkClearInitLocals>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
|
||||
<ItemGroup>
|
||||
<Compile Include="System\Text\RegularExpressions\HashtableExtensions.cs" />
|
||||
<Compile Include="System\Collections\Generic\ValueListBuilder.Pop.cs" />
|
||||
<Compile Include="System\Collections\HashtableExtensions.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Capture.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\CaptureCollection.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\CollectionDebuggerProxy.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Group.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\GroupCollection.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Match.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\MatchCollection.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Reference.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Regex.Cache.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Regex.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Regex.Match.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Regex.Replace.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Regex.Split.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\Regex.Timeout.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexBoyerMoore.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexCapture.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexCaptureCollection.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexCharClass.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexCode.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexCollectionDebuggerProxy.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexCompilationInfo.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexFCD.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexGroup.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexGroupCollection.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexInterpreter.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexMatch.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexMatchCollection.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexMatchTimeoutException.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexNode.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexOptions.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexParser.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexPrefix.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexReplacement.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexRunner.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexRunnerFactory.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexTree.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexWriter.cs" />
|
||||
<Compile Include="System\Text\RegularExpressions\RegexRunnerFactory.cs" />
|
||||
<!-- Common or Common-branched source files -->
|
||||
<Compile Include="$(CommonPath)\System\NotImplemented.cs">
|
||||
<Link>Common\System\NotImplemented.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\System\IO\StringBuilderCache.cs">
|
||||
<Link>Common\System\IO\StringBuilderCache.cs</Link>
|
||||
<Compile Include="$(CommonPath)\CoreLib\System\Text\StringBuilderCache.cs">
|
||||
<Link>Common\System\Text\StringBuilderCache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\CoreLib\System\Collections\Generic\ValueListBuilder.cs">
|
||||
<Link>Common\System\Collections\Generic\ValueListBuilder.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<!-- Files that enable compiled feature -->
|
||||
@@ -51,9 +63,11 @@
|
||||
<Compile Include="System\Text\RegularExpressions\RegexLWCGCompiler.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Buffers" />
|
||||
<Reference Include="System.Collections" />
|
||||
<Reference Include="System.Diagnostics.Debug" />
|
||||
<Reference Include="System.Diagnostics.Tools" />
|
||||
<Reference Include="System.Memory" />
|
||||
<Reference Include="System.Resources.ResourceManager" />
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.Extensions" />
|
||||
@@ -66,4 +80,4 @@
|
||||
<Reference Include="System.Reflection.Primitives" />
|
||||
</ItemGroup>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
/// <summary>
|
||||
/// These public methods are required by RegexWriter.
|
||||
/// </summary>
|
||||
internal ref partial struct ValueListBuilder<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Pop()
|
||||
{
|
||||
_pos--;
|
||||
return _span[_pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,7 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
namespace System.Collections
|
||||
{
|
||||
internal static class HashtableExtensions
|
||||
{
|
||||
@@ -15,7 +13,7 @@ namespace System.Text.RegularExpressions
|
||||
value = (T)table[key];
|
||||
return true;
|
||||
}
|
||||
value = default(T);
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
61
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Capture.cs
vendored
Normal file
61
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Capture.cs
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// Capture is just a location/length pair that indicates the
|
||||
// location of a regular expression match. A single regexp
|
||||
// search may return multiple Capture within each capturing
|
||||
// RegexGroup.
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the results from a single subexpression capture. The object represents
|
||||
/// one substring for a single successful capture.
|
||||
/// </summary>
|
||||
public class Capture
|
||||
{
|
||||
internal Capture(string text, int index, int length)
|
||||
{
|
||||
Text = text;
|
||||
Index = index;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the position in the original string where the first character of
|
||||
/// captured substring was found.
|
||||
/// </summary>
|
||||
public int Index { get; private protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the captured substring.
|
||||
/// </summary>
|
||||
public int Length { get; private protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The original string
|
||||
/// </summary>
|
||||
internal string Text { get; private protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of this Regex Capture.
|
||||
/// </summary>
|
||||
public string Value => Text.Substring(Index, Length);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the substring that was matched.
|
||||
/// </summary>
|
||||
public override string ToString() => Value;
|
||||
|
||||
/// <summary>
|
||||
/// The substring to the left of the capture
|
||||
/// </summary>
|
||||
internal ReadOnlySpan<char> GetLeftSubstring() => Text.AsSpan(0, Index);
|
||||
|
||||
/// <summary>
|
||||
/// The substring to the right of the capture
|
||||
/// </summary>
|
||||
internal ReadOnlySpan<char> GetRightSubstring() => Text.AsSpan(Index + Length, Text.Length - Index - Length);
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,10 @@ namespace System.Text.RegularExpressions
|
||||
/// to return the set of captures done by a single capturing group.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
[DebuggerTypeProxy(typeof(RegexCollectionDebuggerProxy<Capture>))]
|
||||
#if !MONO
|
||||
[Serializable]
|
||||
#endif
|
||||
[DebuggerTypeProxy(typeof(CollectionDebuggerProxy<Capture>))]
|
||||
public class CaptureCollection : IList<Capture>, IReadOnlyList<Capture>, IList
|
||||
{
|
||||
private readonly Group _group;
|
||||
@@ -68,16 +70,24 @@ namespace System.Text.RegularExpressions
|
||||
// first time a capture is accessed, compute them all
|
||||
if (_captures == null)
|
||||
{
|
||||
_captures = new Capture[_capcount];
|
||||
for (int j = 0; j < _capcount - 1; j++)
|
||||
{
|
||||
_captures[j] = new Capture(_group._text, _group._caps[j * 2], _group._caps[j * 2 + 1]);
|
||||
}
|
||||
ForceInitialized();
|
||||
}
|
||||
|
||||
return _captures[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute all captures
|
||||
/// </summary>
|
||||
internal void ForceInitialized()
|
||||
{
|
||||
_captures = new Capture[_capcount];
|
||||
for (int j = 0; j < _capcount - 1; j++)
|
||||
{
|
||||
_captures[j] = new Capture(_group.Text, _group._caps[j * 2], _group._caps[j * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSynchronized => false;
|
||||
|
||||
public object SyncRoot => _group;
|
||||
@@ -110,12 +120,14 @@ namespace System.Text.RegularExpressions
|
||||
|
||||
int IList<Capture>.IndexOf(Capture item)
|
||||
{
|
||||
var comparer = EqualityComparer<Capture>.Default;
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (comparer.Equals(this[i], item))
|
||||
if (EqualityComparer<Capture>.Default.Equals(this[i], item))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -7,16 +7,13 @@ using System.Diagnostics;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
internal sealed class RegexCollectionDebuggerProxy<T>
|
||||
internal sealed class CollectionDebuggerProxy<T>
|
||||
{
|
||||
private readonly ICollection<T> _collection;
|
||||
|
||||
public RegexCollectionDebuggerProxy(ICollection<T> collection)
|
||||
public CollectionDebuggerProxy(ICollection<T> collection)
|
||||
{
|
||||
if (collection == null)
|
||||
throw new ArgumentNullException(nameof(collection));
|
||||
|
||||
_collection = collection;
|
||||
_collection = collection ?? throw new ArgumentNullException(nameof(collection));
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
||||
@@ -24,7 +21,7 @@ namespace System.Text.RegularExpressions
|
||||
{
|
||||
get
|
||||
{
|
||||
T[] items = new T[_collection.Count];
|
||||
var items = new T[_collection.Count];
|
||||
_collection.CopyTo(items, 0);
|
||||
return items;
|
||||
}
|
||||
@@ -10,9 +10,9 @@ namespace System.Text.RegularExpressions
|
||||
private Func<RegexRunner, bool> _findFirstCharMethod;
|
||||
private Action<RegexRunner> _initTrackCountMethod;
|
||||
|
||||
internal CompiledRegexRunner() { }
|
||||
public CompiledRegexRunner() { }
|
||||
|
||||
internal void SetDelegates(Action<RegexRunner> go, Func<RegexRunner,bool> firstChar, Action<RegexRunner> trackCount)
|
||||
public void SetDelegates(Action<RegexRunner> go, Func<RegexRunner,bool> firstChar, Action<RegexRunner> trackCount)
|
||||
{
|
||||
_goMethod = go;
|
||||
_findFirstCharMethod = firstChar;
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// This is the only concrete implementation of RegexRunnerFactory,
|
||||
// but we cannot combine them due to RegexRunnerFactory having shipped public.
|
||||
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
@@ -12,7 +15,7 @@ namespace System.Text.RegularExpressions
|
||||
private readonly DynamicMethod _findFirstCharMethod;
|
||||
private readonly DynamicMethod _initTrackCountMethod;
|
||||
|
||||
internal CompiledRegexRunnerFactory(DynamicMethod go, DynamicMethod firstChar, DynamicMethod trackCount)
|
||||
public CompiledRegexRunnerFactory(DynamicMethod go, DynamicMethod firstChar, DynamicMethod trackCount)
|
||||
{
|
||||
_goMethod = go;
|
||||
_findFirstCharMethod = firstChar;
|
||||
|
||||
@@ -16,46 +16,28 @@ namespace System.Text.RegularExpressions
|
||||
[Serializable]
|
||||
public class Group : Capture
|
||||
{
|
||||
// the empty group object
|
||||
internal static readonly Group s_emptyGroup = new Group(string.Empty, Array.Empty<int>(), 0, string.Empty);
|
||||
|
||||
internal readonly int[] _caps;
|
||||
internal int _capcount;
|
||||
internal CaptureCollection _capcoll;
|
||||
internal readonly string _name;
|
||||
|
||||
internal Group(string text, int[] caps, int capcount, string name)
|
||||
|
||||
: base(text, capcount == 0 ? 0 : caps[(capcount - 1) * 2],
|
||||
: base(text, capcount == 0 ? 0 : caps[(capcount - 1) * 2],
|
||||
capcount == 0 ? 0 : caps[(capcount * 2) - 1])
|
||||
{
|
||||
_caps = caps;
|
||||
_capcount = capcount;
|
||||
_name = name;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the match is successful.
|
||||
/// </summary>
|
||||
public bool Success
|
||||
{
|
||||
get
|
||||
{
|
||||
return _capcount != 0;
|
||||
}
|
||||
}
|
||||
public bool Success => _capcount != 0;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
public string Name { get; }
|
||||
|
||||
/*
|
||||
* The collection of all captures for this group
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns a collection of all the captures matched by the capturing
|
||||
/// group, in innermost-leftmost-first order (or innermost-rightmost-first order if
|
||||
@@ -72,9 +54,6 @@ namespace System.Text.RegularExpressions
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert to a thread-safe object by precomputing cache contents
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns a Group object equivalent to the one supplied that is safe to share between
|
||||
/// multiple threads.
|
||||
@@ -85,14 +64,11 @@ namespace System.Text.RegularExpressions
|
||||
throw new ArgumentNullException(nameof(inner));
|
||||
|
||||
// force Captures to be computed.
|
||||
|
||||
CaptureCollection capcoll;
|
||||
Capture dummy;
|
||||
|
||||
capcoll = inner.Captures;
|
||||
|
||||
if (inner._capcount > 0)
|
||||
dummy = capcoll[0];
|
||||
CaptureCollection capcoll = inner.Captures;
|
||||
if (inner.Success)
|
||||
{
|
||||
capcoll.ForceInitialized();
|
||||
}
|
||||
|
||||
return inner;
|
||||
}
|
||||
@@ -16,8 +16,10 @@ namespace System.Text.RegularExpressions
|
||||
/// to return the set of captures done by a single capturing group.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
[DebuggerTypeProxy(typeof(RegexCollectionDebuggerProxy<Group>))]
|
||||
#if MONO
|
||||
[Serializable]
|
||||
#endif
|
||||
[DebuggerTypeProxy(typeof(CollectionDebuggerProxy<Group>))]
|
||||
public class GroupCollection : IList<Group>, IReadOnlyList<Group>, IList
|
||||
{
|
||||
private readonly Match _match;
|
||||
@@ -56,8 +58,7 @@ namespace System.Text.RegularExpressions
|
||||
{
|
||||
if (_captureMap != null)
|
||||
{
|
||||
int groupNumImpl;
|
||||
if (_captureMap.TryGetValue(groupnum, out groupNumImpl))
|
||||
if (_captureMap.TryGetValue(groupnum, out int groupNumImpl))
|
||||
{
|
||||
return GetGroupImpl(groupNumImpl);
|
||||
}
|
||||
@@ -86,7 +87,7 @@ namespace System.Text.RegularExpressions
|
||||
for (int i = 0; i < _groups.Length; i++)
|
||||
{
|
||||
string groupname = _match._regex.GroupNameFromNumber(i + 1);
|
||||
_groups[i] = new Group(_match._text, _match._matches[i + 1], _match._matchcount[i + 1], groupname);
|
||||
_groups[i] = new Group(_match.Text, _match._matches[i + 1], _match._matchcount[i + 1], groupname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace System.Text.RegularExpressions
|
||||
[Serializable]
|
||||
public class Match : Group
|
||||
{
|
||||
internal static readonly Match s_empty = new Match(null, 1, string.Empty, 0, 0, 0);
|
||||
internal GroupCollection _groupcoll;
|
||||
|
||||
// input to the match
|
||||
@@ -52,23 +51,11 @@ namespace System.Text.RegularExpressions
|
||||
internal bool _balancing; // whether we've done any balancing with this match. If we
|
||||
// have done balancing, we'll need to do extra work in Tidy().
|
||||
|
||||
/// <summary>
|
||||
/// Returns an empty Match object.
|
||||
/// </summary>
|
||||
public static Match Empty
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_empty;
|
||||
}
|
||||
}
|
||||
|
||||
internal Match(Regex regex, int capcount, string text, int begpos, int len, int startpos)
|
||||
: base(text, new int[2], 0, "0")
|
||||
{
|
||||
_regex = regex;
|
||||
_matchcount = new int[capcount];
|
||||
|
||||
_matches = new int[capcount][];
|
||||
_matches[0] = _caps;
|
||||
_textbeg = begpos;
|
||||
@@ -77,17 +64,19 @@ namespace System.Text.RegularExpressions
|
||||
_balancing = false;
|
||||
|
||||
// No need for an exception here. This is only called internally, so we'll use an Assert instead
|
||||
System.Diagnostics.Debug.Assert(!(_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || _text.Length < _textend),
|
||||
System.Diagnostics.Debug.Assert(!(_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || Text.Length < _textend),
|
||||
"The parameters are out of range.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Nonpublic set-text method
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns an empty Match object.
|
||||
/// </summary>
|
||||
public static Match Empty { get; } = new Match(null, 1, string.Empty, 0, 0, 0);
|
||||
|
||||
internal virtual void Reset(Regex regex, string text, int textbeg, int textend, int textstart)
|
||||
{
|
||||
_regex = regex;
|
||||
_text = text;
|
||||
Text = text;
|
||||
_textbeg = textbeg;
|
||||
_textend = textend;
|
||||
_textstart = textstart;
|
||||
@@ -121,7 +110,7 @@ namespace System.Text.RegularExpressions
|
||||
if (_regex == null)
|
||||
return this;
|
||||
|
||||
return _regex.Run(false, _length, _text, _textbeg, _textend - _textbeg, _textpos);
|
||||
return _regex.Run(false, Length, Text, _textbeg, _textend - _textbeg, _textpos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -131,29 +120,20 @@ namespace System.Text.RegularExpressions
|
||||
/// </summary>
|
||||
public virtual string Result(string replacement)
|
||||
{
|
||||
RegexReplacement repl;
|
||||
|
||||
if (replacement == null)
|
||||
throw new ArgumentNullException(nameof(replacement));
|
||||
|
||||
if (_regex == null)
|
||||
throw new NotSupportedException(SR.NoResultOnFailed);
|
||||
|
||||
repl = (RegexReplacement)_regex._replref.Get();
|
||||
|
||||
if (repl == null || !repl.Pattern.Equals(replacement))
|
||||
{
|
||||
repl = RegexParser.ParseReplacement(replacement, _regex.caps, _regex.capsize, _regex.capnames, _regex.roptions);
|
||||
_regex._replref.Cache(repl);
|
||||
}
|
||||
// Gets the weakly cached replacement helper or creates one if there isn't one already.
|
||||
RegexReplacement repl = RegexReplacement.GetOrCreate(_regex._replref, replacement, _regex.caps, _regex.capsize,
|
||||
_regex.capnames, _regex.roptions);
|
||||
|
||||
return repl.Replacement(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used by the replacement code
|
||||
*/
|
||||
internal virtual string GroupToStringImpl(int groupnum)
|
||||
internal virtual ReadOnlySpan<char> GroupToStringImpl(int groupnum)
|
||||
{
|
||||
int c = _matchcount[groupnum];
|
||||
if (c == 0)
|
||||
@@ -161,26 +141,18 @@ namespace System.Text.RegularExpressions
|
||||
|
||||
int[] matches = _matches[groupnum];
|
||||
|
||||
return _text.Substring(matches[(c - 1) * 2], matches[(c * 2) - 1]);
|
||||
return Text.AsSpan(matches[(c - 1) * 2], matches[(c * 2) - 1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used by the replacement code
|
||||
*/
|
||||
internal string LastGroupToStringImpl()
|
||||
internal ReadOnlySpan<char> LastGroupToStringImpl()
|
||||
{
|
||||
return GroupToStringImpl(_matchcount.Length - 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert to a thread-safe object by precomputing cache contents
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns a Match instance equivalent to the one supplied that is safe to share
|
||||
/// between multiple threads.
|
||||
/// </summary>
|
||||
|
||||
public static Match Synchronized(Match inner)
|
||||
{
|
||||
if (inner == null)
|
||||
@@ -201,9 +173,9 @@ namespace System.Text.RegularExpressions
|
||||
return inner;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nonpublic builder: add a capture to the group specified by "cap"
|
||||
*/
|
||||
/// <summary>
|
||||
/// Adds a capture to the group specified by "cap"
|
||||
/// </summary>
|
||||
internal virtual void AddMatch(int cap, int start, int len)
|
||||
{
|
||||
int capcount;
|
||||
@@ -236,14 +208,11 @@ namespace System.Text.RegularExpressions
|
||||
*/
|
||||
internal virtual void BalanceMatch(int cap)
|
||||
{
|
||||
int capcount;
|
||||
int target;
|
||||
|
||||
_balancing = true;
|
||||
|
||||
// we'll look at the last capture first
|
||||
capcount = _matchcount[cap];
|
||||
target = capcount * 2 - 2;
|
||||
int capcount = _matchcount[cap];
|
||||
int target = capcount * 2 - 2;
|
||||
|
||||
// first see if it is negative, and therefore is a reference to the next available
|
||||
// capture group for balancing. If it is, we'll reset target to point to that capture.
|
||||
@@ -260,25 +229,25 @@ namespace System.Text.RegularExpressions
|
||||
AddMatch(cap, -3 - target, -4 - target /* == -3 - (target + 1) */ );
|
||||
}
|
||||
|
||||
/*
|
||||
* Nonpublic builder: removes a group match by capnum
|
||||
*/
|
||||
/// <summary>
|
||||
/// Removes a group match by capnum
|
||||
/// </summary>
|
||||
internal virtual void RemoveMatch(int cap)
|
||||
{
|
||||
_matchcount[cap]--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nonpublic: tells if a group was matched by capnum
|
||||
*/
|
||||
/// <summary>
|
||||
/// Tells if a group was matched by capnum
|
||||
/// </summary>
|
||||
internal virtual bool IsMatched(int cap)
|
||||
{
|
||||
return cap < _matchcount.Length && _matchcount[cap] > 0 && _matches[cap][_matchcount[cap] * 2 - 1] != (-3 + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Nonpublic: returns the index of the last specified matched group by capnum
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns the index of the last specified matched group by capnum
|
||||
/// </summary>
|
||||
internal virtual int MatchIndex(int cap)
|
||||
{
|
||||
int i = _matches[cap][_matchcount[cap] * 2 - 2];
|
||||
@@ -288,9 +257,9 @@ namespace System.Text.RegularExpressions
|
||||
return _matches[cap][-3 - i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Nonpublic: returns the length of the last specified matched group by capnum
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns the length of the last specified matched group by capnum
|
||||
/// </summary>
|
||||
internal virtual int MatchLength(int cap)
|
||||
{
|
||||
int i = _matches[cap][_matchcount[cap] * 2 - 1];
|
||||
@@ -300,16 +269,14 @@ namespace System.Text.RegularExpressions
|
||||
return _matches[cap][-3 - i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Nonpublic: tidy the match so that it can be used as an immutable result
|
||||
*/
|
||||
/// <summary>
|
||||
/// Tidy the match so that it can be used as an immutable result
|
||||
/// </summary>
|
||||
internal virtual void Tidy(int textpos)
|
||||
{
|
||||
int[] interval;
|
||||
|
||||
interval = _matches[0];
|
||||
_index = interval[0];
|
||||
_length = interval[1];
|
||||
int[] interval = _matches[0];
|
||||
Index = interval[0];
|
||||
Length = interval[1];
|
||||
_textpos = textpos;
|
||||
_capcount = _matchcount[0];
|
||||
|
||||
@@ -387,7 +354,7 @@ namespace System.Text.RegularExpressions
|
||||
string text = "";
|
||||
|
||||
if (_matches[i][j * 2] >= 0)
|
||||
text = _text.Substring(_matches[i][j * 2], _matches[i][j * 2 + 1]);
|
||||
text = Text.Substring(_matches[i][j * 2], _matches[i][j * 2 + 1]);
|
||||
|
||||
System.Diagnostics.Debug.WriteLine(" (" + _matches[i][j * 2].ToString(CultureInfo.InvariantCulture) + "," + _matches[i][j * 2 + 1].ToString(CultureInfo.InvariantCulture) + ") " + text);
|
||||
}
|
||||
@@ -396,23 +363,16 @@ namespace System.Text.RegularExpressions
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MatchSparse is for handling the case where slots are
|
||||
* sparsely arranged (e.g., if somebody says use slot 100000)
|
||||
*/
|
||||
/// <summary>
|
||||
/// MatchSparse is for handling the case where slots are sparsely arranged (e.g., if somebody says use slot 100000)
|
||||
/// </summary>
|
||||
internal class MatchSparse : Match
|
||||
{
|
||||
// the lookup hashtable
|
||||
new internal Hashtable _caps;
|
||||
new internal readonly Hashtable _caps;
|
||||
|
||||
/*
|
||||
* Nonpublic constructor
|
||||
*/
|
||||
internal MatchSparse(Regex regex, Hashtable caps, int capcount,
|
||||
string text, int begpos, int len, int startpos)
|
||||
|
||||
: base(regex, capcount, text, begpos, len, startpos)
|
||||
internal MatchSparse(Regex regex, Hashtable caps, int capcount, string text, int begpos, int len, int startpos)
|
||||
: base(regex, capcount, text, begpos, len, startpos)
|
||||
{
|
||||
_caps = caps;
|
||||
}
|
||||
@@ -21,8 +21,10 @@ namespace System.Text.RegularExpressions
|
||||
/// names in a regular expression.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
[DebuggerTypeProxy(typeof(RegexCollectionDebuggerProxy<Match>))]
|
||||
#if MONO
|
||||
[Serializable]
|
||||
#endif
|
||||
[DebuggerTypeProxy(typeof(CollectionDebuggerProxy<Match>))]
|
||||
public class MatchCollection : IList<Match>, IReadOnlyList<Match>, IList
|
||||
{
|
||||
private readonly Regex _regex;
|
||||
@@ -113,7 +115,7 @@ namespace System.Text.RegularExpressions
|
||||
|
||||
_matches.Add(match);
|
||||
|
||||
_prevlen = match._length;
|
||||
_prevlen = match.Length;
|
||||
_startat = match._textpos;
|
||||
} while (_matches.Count <= i);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to cache one exclusive runner reference
|
||||
/// </summary>
|
||||
internal sealed class ExclusiveReference
|
||||
{
|
||||
private RegexRunner _ref;
|
||||
private RegexRunner _obj;
|
||||
private volatile int _locked;
|
||||
|
||||
/// <summary>
|
||||
/// Return an object and grab an exclusive lock.
|
||||
///
|
||||
/// If the exclusive lock can't be obtained, null is returned;
|
||||
/// if the object can't be returned, the lock is released.
|
||||
/// </summary>
|
||||
public RegexRunner Get()
|
||||
{
|
||||
// try to obtain the lock
|
||||
|
||||
if (0 == Interlocked.Exchange(ref _locked, 1))
|
||||
{
|
||||
// grab reference
|
||||
RegexRunner obj = _ref;
|
||||
|
||||
// release the lock and return null if no reference
|
||||
if (obj == null)
|
||||
{
|
||||
_locked = 0;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// remember the reference and keep the lock
|
||||
_obj = obj;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release an object back to the cache.
|
||||
///
|
||||
/// If the object is the one that's under lock, the lock is released.
|
||||
/// If there is no cached object, then the lock is obtained and the object is placed in the cache.
|
||||
/// </summary>
|
||||
public void Release(RegexRunner obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
// if this reference owns the lock, release it
|
||||
if (_obj == obj)
|
||||
{
|
||||
_obj = null;
|
||||
_locked = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if no reference owns the lock, try to cache this reference
|
||||
if (_obj == null)
|
||||
{
|
||||
// try to obtain the lock
|
||||
if (0 == Interlocked.Exchange(ref _locked, 1))
|
||||
{
|
||||
// if there's really no reference, cache this reference
|
||||
if (_ref == null)
|
||||
_ref = obj;
|
||||
|
||||
// release the lock
|
||||
_locked = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
303
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Cache.cs
vendored
Normal file
303
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Cache.cs
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using SysDebug = System.Diagnostics.Debug; // as Regex.Debug
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
public partial class Regex
|
||||
{
|
||||
private const int CacheDictionarySwitchLimit = 10;
|
||||
|
||||
private static int s_cacheSize = 15;
|
||||
// the cache of code and factories that are currently loaded:
|
||||
// Dictionary for large cache
|
||||
private static readonly Dictionary<CachedCodeEntryKey, CachedCodeEntry> s_cache = new Dictionary<CachedCodeEntryKey, CachedCodeEntry>(s_cacheSize);
|
||||
// linked list for MRU and for small cache
|
||||
private static int s_cacheCount = 0;
|
||||
private static CachedCodeEntry s_cacheFirst;
|
||||
private static CachedCodeEntry s_cacheLast;
|
||||
|
||||
public static int CacheSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_cacheSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
|
||||
lock (s_cache)
|
||||
{
|
||||
s_cacheSize = value; // not to allow other thread to change it while we use cache
|
||||
while (s_cacheCount > s_cacheSize)
|
||||
{
|
||||
CachedCodeEntry last = s_cacheLast;
|
||||
if (s_cacheCount >= CacheDictionarySwitchLimit)
|
||||
{
|
||||
SysDebug.Assert(s_cache.ContainsKey(last.Key));
|
||||
s_cache.Remove(last.Key);
|
||||
}
|
||||
|
||||
// update linked list:
|
||||
s_cacheLast = last.Next;
|
||||
if (last.Next != null)
|
||||
{
|
||||
SysDebug.Assert(s_cacheFirst != null);
|
||||
SysDebug.Assert(s_cacheFirst != last);
|
||||
SysDebug.Assert(last.Next.Previous == last);
|
||||
last.Next.Previous = null;
|
||||
}
|
||||
else // last one removed
|
||||
{
|
||||
SysDebug.Assert(s_cacheFirst == last);
|
||||
s_cacheFirst = null;
|
||||
}
|
||||
|
||||
s_cacheCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find cache based on options+pattern+culture and optionally add new cache if not found
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private CachedCodeEntry GetCachedCode(CachedCodeEntryKey key, bool isToAdd)
|
||||
{
|
||||
// to avoid lock:
|
||||
CachedCodeEntry first = s_cacheFirst;
|
||||
if (first?.Key == key)
|
||||
return first;
|
||||
if (s_cacheSize == 0)
|
||||
return null;
|
||||
|
||||
return GetCachedCodeEntryInternal(key, isToAdd);
|
||||
}
|
||||
|
||||
private CachedCodeEntry GetCachedCodeEntryInternal(CachedCodeEntryKey key, bool isToAdd)
|
||||
{
|
||||
lock (s_cache)
|
||||
{
|
||||
// first look for it in the cache and move it to the head
|
||||
CachedCodeEntry entry = LookupCachedAndPromote(key);
|
||||
// it wasn't in the cache, so we'll add a new one
|
||||
if (entry == null && isToAdd && s_cacheSize != 0) // check cache size again in case it changed
|
||||
{
|
||||
entry = new CachedCodeEntry(key, capnames, capslist, _code, caps, capsize, _runnerref, _replref);
|
||||
// put first in linked list:
|
||||
if (s_cacheFirst != null)
|
||||
{
|
||||
SysDebug.Assert(s_cacheFirst.Next == null);
|
||||
s_cacheFirst.Next = entry;
|
||||
entry.Previous = s_cacheFirst;
|
||||
}
|
||||
s_cacheFirst = entry;
|
||||
|
||||
s_cacheCount++;
|
||||
if (s_cacheCount >= CacheDictionarySwitchLimit)
|
||||
{
|
||||
if (s_cacheCount == CacheDictionarySwitchLimit)
|
||||
FillCacheDictionary();
|
||||
else
|
||||
s_cache.Add(key, entry);
|
||||
SysDebug.Assert(s_cacheCount == s_cache.Count);
|
||||
}
|
||||
|
||||
// update last in linked list:
|
||||
if (s_cacheLast == null)
|
||||
{
|
||||
s_cacheLast = entry;
|
||||
}
|
||||
else if (s_cacheCount > s_cacheSize) // remove last
|
||||
{
|
||||
CachedCodeEntry last = s_cacheLast;
|
||||
if (s_cacheCount >= CacheDictionarySwitchLimit)
|
||||
{
|
||||
SysDebug.Assert(s_cache[last.Key] == s_cacheLast);
|
||||
s_cache.Remove(last.Key);
|
||||
}
|
||||
|
||||
SysDebug.Assert(last.Previous == null);
|
||||
SysDebug.Assert(last.Next != null);
|
||||
SysDebug.Assert(last.Next.Previous == last);
|
||||
last.Next.Previous = null;
|
||||
s_cacheLast = last.Next;
|
||||
s_cacheCount--;
|
||||
}
|
||||
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
private void FillCacheDictionary()
|
||||
{
|
||||
s_cache.Clear();
|
||||
CachedCodeEntry next = s_cacheFirst;
|
||||
while (next != null)
|
||||
{
|
||||
s_cache.Add(next.Key, next);
|
||||
next = next.Previous;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Unprofitable inline - JIT overly pessimistic
|
||||
private static bool TryGetCacheValue(CachedCodeEntryKey key, out CachedCodeEntry entry)
|
||||
{
|
||||
if (s_cacheCount >= CacheDictionarySwitchLimit)
|
||||
{
|
||||
SysDebug.Assert((s_cacheFirst != null && s_cacheLast != null && s_cache.Count > 0) ||
|
||||
(s_cacheFirst == null && s_cacheLast == null && s_cache.Count == 0),
|
||||
"Linked list and Dict should be synchronized");
|
||||
return s_cache.TryGetValue(key, out entry);
|
||||
}
|
||||
|
||||
return TryGetCacheValueSmall(key, out entry);
|
||||
}
|
||||
|
||||
private static bool TryGetCacheValueSmall(CachedCodeEntryKey key, out CachedCodeEntry entry)
|
||||
{
|
||||
entry = s_cacheFirst?.Previous; // first already checked
|
||||
while (entry != null)
|
||||
{
|
||||
if (entry.Key == key)
|
||||
return true;
|
||||
entry = entry.Previous;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static CachedCodeEntry LookupCachedAndPromote(CachedCodeEntryKey key)
|
||||
{
|
||||
SysDebug.Assert(Monitor.IsEntered(s_cache));
|
||||
if (s_cacheFirst?.Key == key) // again check this as could have been promoted by other thread
|
||||
return s_cacheFirst;
|
||||
|
||||
if (TryGetCacheValue(key, out CachedCodeEntry entry))
|
||||
{
|
||||
// promote:
|
||||
SysDebug.Assert(s_cacheFirst != entry, "key should not get s_livecode_first");
|
||||
SysDebug.Assert(s_cacheFirst != null, "as Dict has at least one");
|
||||
SysDebug.Assert(s_cacheFirst.Next == null);
|
||||
SysDebug.Assert(s_cacheFirst.Previous != null);
|
||||
SysDebug.Assert(entry.Next != null, "not first so Next should exist");
|
||||
SysDebug.Assert(entry.Next.Previous == entry);
|
||||
if (s_cacheLast == entry)
|
||||
{
|
||||
SysDebug.Assert(entry.Previous == null, "last");
|
||||
s_cacheLast = entry.Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
SysDebug.Assert(entry.Previous != null, "in middle");
|
||||
SysDebug.Assert(entry.Previous.Next == entry);
|
||||
entry.Previous.Next = entry.Next;
|
||||
}
|
||||
entry.Next.Previous = entry.Previous;
|
||||
|
||||
s_cacheFirst.Next = entry;
|
||||
entry.Previous = s_cacheFirst;
|
||||
entry.Next = null;
|
||||
s_cacheFirst = entry;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used as a key for CacheCodeEntry
|
||||
/// </summary>
|
||||
internal readonly struct CachedCodeEntryKey : IEquatable<CachedCodeEntryKey>
|
||||
{
|
||||
private readonly RegexOptions _options;
|
||||
private readonly string _cultureKey;
|
||||
private readonly string _pattern;
|
||||
|
||||
public CachedCodeEntryKey(RegexOptions options, string cultureKey, string pattern)
|
||||
{
|
||||
SysDebug.Assert(cultureKey != null, "Culture must be provided");
|
||||
SysDebug.Assert(pattern != null, "Pattern must be provided");
|
||||
|
||||
_options = options;
|
||||
_cultureKey = cultureKey;
|
||||
_pattern = pattern;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is CachedCodeEntryKey && Equals((CachedCodeEntryKey)obj);
|
||||
}
|
||||
|
||||
public bool Equals(CachedCodeEntryKey other)
|
||||
{
|
||||
return _pattern.Equals(other._pattern) && _options == other._options && _cultureKey.Equals(other._cultureKey);
|
||||
}
|
||||
|
||||
public static bool operator ==(CachedCodeEntryKey left, CachedCodeEntryKey right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CachedCodeEntryKey left, CachedCodeEntryKey right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ((int)_options) ^ _cultureKey.GetHashCode() ^ _pattern.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache byte codes
|
||||
/// </summary>
|
||||
internal sealed class CachedCodeEntry
|
||||
{
|
||||
public CachedCodeEntry Next;
|
||||
public CachedCodeEntry Previous;
|
||||
public readonly CachedCodeEntryKey Key;
|
||||
public RegexCode Code;
|
||||
public readonly Hashtable Caps;
|
||||
public readonly Hashtable Capnames;
|
||||
public readonly string[] Capslist;
|
||||
#if FEATURE_COMPILED
|
||||
public RegexRunnerFactory Factory;
|
||||
#endif
|
||||
public readonly int Capsize;
|
||||
public readonly ExclusiveReference Runnerref;
|
||||
public readonly WeakReference<RegexReplacement> ReplRef;
|
||||
|
||||
public CachedCodeEntry(CachedCodeEntryKey key, Hashtable capnames, string[] capslist, RegexCode code,
|
||||
Hashtable caps, int capsize, ExclusiveReference runner, WeakReference<RegexReplacement> replref)
|
||||
{
|
||||
Key = key;
|
||||
Capnames = capnames;
|
||||
Capslist = capslist;
|
||||
Code = code;
|
||||
Caps = caps;
|
||||
Capsize = capsize;
|
||||
Runnerref = runner;
|
||||
ReplRef = replref;
|
||||
}
|
||||
|
||||
#if FEATURE_COMPILED
|
||||
public void AddCompiled(RegexRunnerFactory factory)
|
||||
{
|
||||
Factory = factory;
|
||||
Code = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
184
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Match.cs
vendored
Normal file
184
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Match.cs
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
public partial class Regex
|
||||
{
|
||||
/// <summary>
|
||||
/// Searches the input string for one or more occurrences of the text supplied in the given pattern.
|
||||
/// </summary>
|
||||
public static bool IsMatch(string input, string pattern)
|
||||
{
|
||||
return IsMatch(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the input string for one or more occurrences of the text
|
||||
/// supplied in the pattern parameter with matching options supplied in the options
|
||||
/// parameter.
|
||||
/// </summary>
|
||||
public static bool IsMatch(string input, string pattern, RegexOptions options)
|
||||
{
|
||||
return IsMatch(input, pattern, options, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
public static bool IsMatch(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
|
||||
{
|
||||
return new Regex(pattern, options, matchTimeout, true).IsMatch(input);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the regex finds a match within the specified string
|
||||
*/
|
||||
/// <summary>
|
||||
/// Searches the input string for one or more matches using the previous pattern,
|
||||
/// options, and starting position.
|
||||
/// </summary>
|
||||
public bool IsMatch(string input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return IsMatch(input, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the regex finds a match after the specified position
|
||||
* (proceeding leftward if the regex is leftward and rightward otherwise)
|
||||
*/
|
||||
/// <summary>
|
||||
/// Searches the input string for one or more matches using the previous pattern and options,
|
||||
/// with a new starting position.
|
||||
/// </summary>
|
||||
public bool IsMatch(string input, int startat)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return (null == Run(true, -1, input, 0, input.Length, startat));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the input string for one or more occurrences of the text
|
||||
/// supplied in the pattern parameter.
|
||||
/// </summary>
|
||||
public static Match Match(string input, string pattern)
|
||||
{
|
||||
return Match(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the input string for one or more occurrences of the text
|
||||
/// supplied in the pattern parameter. Matching is modified with an option
|
||||
/// string.
|
||||
/// </summary>
|
||||
public static Match Match(string input, string pattern, RegexOptions options)
|
||||
{
|
||||
return Match(input, pattern, options, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
public static Match Match(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
|
||||
{
|
||||
return new Regex(pattern, options, matchTimeout, true).Match(input);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the first match for the regular expression starting at the beginning
|
||||
* of the string (or at the end of the string if the regex is leftward)
|
||||
*/
|
||||
/// <summary>
|
||||
/// Matches a regular expression with a string and returns
|
||||
/// the precise result as a RegexMatch object.
|
||||
/// </summary>
|
||||
public Match Match(string input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Match(input, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the first match, starting at the specified position
|
||||
*/
|
||||
/// <summary>
|
||||
/// Matches a regular expression with a string and returns
|
||||
/// the precise result as a RegexMatch object.
|
||||
/// </summary>
|
||||
public Match Match(string input, int startat)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Run(false, -1, input, 0, input.Length, startat);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the first match, restricting the search to the specified interval of
|
||||
* the char array.
|
||||
*/
|
||||
/// <summary>
|
||||
/// Matches a regular expression with a string and returns the precise result as a
|
||||
/// RegexMatch object.
|
||||
/// </summary>
|
||||
public Match Match(string input, int beginning, int length)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Run(false, -1, input, beginning, length, UseOptionR() ? beginning + length : beginning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the successful matches as if Match were called iteratively numerous times.
|
||||
/// </summary>
|
||||
public static MatchCollection Matches(string input, string pattern)
|
||||
{
|
||||
return Matches(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the successful matches as if Match were called iteratively numerous times.
|
||||
/// </summary>
|
||||
public static MatchCollection Matches(string input, string pattern, RegexOptions options)
|
||||
{
|
||||
return Matches(input, pattern, options, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
public static MatchCollection Matches(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
|
||||
{
|
||||
return new Regex(pattern, options, matchTimeout, true).Matches(input);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the first match for the regular expression starting at the beginning
|
||||
* of the string Enumerator(or at the end of the string if the regex is leftward)
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns all the successful matches as if Match was called iteratively numerous times.
|
||||
/// </summary>
|
||||
public MatchCollection Matches(string input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Matches(input, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the first match, starting at the specified position
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns all the successful matches as if Match was called iteratively numerous times.
|
||||
/// </summary>
|
||||
public MatchCollection Matches(string input, int startat)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return new MatchCollection(this, input, 0, input.Length, startat);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
// Callback class
|
||||
public delegate string MatchEvaluator(Match match);
|
||||
|
||||
public partial class Regex
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the pattern with the <paramref name="replacement"/> pattern, starting at
|
||||
/// the first character in the input string.
|
||||
/// </summary>
|
||||
public static string Replace(string input, string pattern, string replacement)
|
||||
{
|
||||
return Replace(input, pattern, replacement, RegexOptions.None, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of
|
||||
/// the <paramref name="pattern "/>with the <paramref name="replacement "/>
|
||||
/// pattern, starting at the first character in the input string.
|
||||
/// </summary>
|
||||
public static string Replace(string input, string pattern, string replacement, RegexOptions options)
|
||||
{
|
||||
return Replace(input, pattern, replacement, options, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
public static string Replace(string input, string pattern, string replacement, RegexOptions options, TimeSpan matchTimeout)
|
||||
{
|
||||
return new Regex(pattern, options, matchTimeout, true).Replace(input, replacement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the previously defined pattern with the
|
||||
/// <paramref name="replacement"/> pattern, starting at the first character in the
|
||||
/// input string.
|
||||
/// </summary>
|
||||
public string Replace(string input, string replacement)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Replace(input, replacement, -1, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the previously defined pattern with the
|
||||
/// <paramref name="replacement"/> pattern, starting at the first character in the
|
||||
/// input string.
|
||||
/// </summary>
|
||||
public string Replace(string input, string replacement, int count)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Replace(input, replacement, count, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the previously defined pattern with the
|
||||
/// <paramref name="replacement"/> pattern, starting at the character position
|
||||
/// <paramref name="startat"/>.
|
||||
/// </summary>
|
||||
public string Replace(string input, string replacement, int count, int startat)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
if (replacement == null)
|
||||
throw new ArgumentNullException(nameof(replacement));
|
||||
|
||||
// Gets the weakly cached replacement helper or creates one if there isn't one already.
|
||||
RegexReplacement repl = RegexReplacement.GetOrCreate(_replref, replacement, caps, capsize, capnames, roptions);
|
||||
|
||||
return repl.Replace(this, input, count, startat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the <paramref name="pattern"/> with the recent
|
||||
/// replacement pattern.
|
||||
/// </summary>
|
||||
public static string Replace(string input, string pattern, MatchEvaluator evaluator)
|
||||
{
|
||||
return Replace(input, pattern, evaluator, RegexOptions.None, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the <paramref name="pattern"/> with the recent
|
||||
/// replacement pattern, starting at the first character.
|
||||
/// </summary>
|
||||
public static string Replace(string input, string pattern, MatchEvaluator evaluator, RegexOptions options)
|
||||
{
|
||||
return Replace(input, pattern, evaluator, options, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
public static string Replace(string input, string pattern, MatchEvaluator evaluator, RegexOptions options, TimeSpan matchTimeout)
|
||||
{
|
||||
return new Regex(pattern, options, matchTimeout, true).Replace(input, evaluator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the previously defined pattern with the recent
|
||||
/// replacement pattern, starting at the first character position.
|
||||
/// </summary>
|
||||
public string Replace(string input, MatchEvaluator evaluator)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Replace(input, evaluator, -1, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the previously defined pattern with the recent
|
||||
/// replacement pattern, starting at the first character position.
|
||||
/// </summary>
|
||||
public string Replace(string input, MatchEvaluator evaluator, int count)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Replace(input, evaluator, count, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the previously defined pattern with the recent
|
||||
/// replacement pattern, starting at the character position
|
||||
/// <paramref name="startat"/>.
|
||||
/// </summary>
|
||||
public string Replace(string input, MatchEvaluator evaluator, int count, int startat)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Replace(evaluator, this, input, count, startat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces all occurrences of the regex in the string with the
|
||||
/// replacement evaluator.
|
||||
///
|
||||
/// Note that the special case of no matches is handled on its own:
|
||||
/// with no matches, the input string is returned unchanged.
|
||||
/// The right-to-left case is split out because StringBuilder
|
||||
/// doesn't handle right-to-left string building directly very well.
|
||||
/// </summary>
|
||||
private static string Replace(MatchEvaluator evaluator, Regex regex, string input, int count, int startat)
|
||||
{
|
||||
if (evaluator == null)
|
||||
throw new ArgumentNullException(nameof(evaluator));
|
||||
if (count < -1)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), SR.CountTooSmall);
|
||||
if (startat < 0 || startat > input.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(startat), SR.BeginIndexNotNegative);
|
||||
|
||||
if (count == 0)
|
||||
return input;
|
||||
|
||||
Match match = regex.Match(input, startat);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder sb = StringBuilderCache.Acquire();
|
||||
|
||||
if (!regex.RightToLeft)
|
||||
{
|
||||
int prevat = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (match.Index != prevat)
|
||||
sb.Append(input, prevat, match.Index - prevat);
|
||||
|
||||
prevat = match.Index + match.Length;
|
||||
|
||||
sb.Append(evaluator(match));
|
||||
|
||||
if (--count == 0)
|
||||
break;
|
||||
|
||||
match = match.NextMatch();
|
||||
} while (match.Success);
|
||||
|
||||
if (prevat < input.Length)
|
||||
sb.Append(input, prevat, input.Length - prevat);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<string> al = new List<string>();
|
||||
int prevat = input.Length;
|
||||
|
||||
do
|
||||
{
|
||||
if (match.Index + match.Length != prevat)
|
||||
al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
|
||||
|
||||
prevat = match.Index;
|
||||
|
||||
al.Add(evaluator(match));
|
||||
|
||||
if (--count == 0)
|
||||
break;
|
||||
|
||||
match = match.NextMatch();
|
||||
} while (match.Success);
|
||||
|
||||
if (prevat > 0)
|
||||
sb.Append(input, 0, prevat);
|
||||
|
||||
for (int i = al.Count - 1; i >= 0; i--)
|
||||
{
|
||||
sb.Append(al[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return StringBuilderCache.GetStringAndRelease(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
165
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Split.cs
vendored
Normal file
165
external/corefx/src/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Split.cs
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
public partial class Regex
|
||||
{
|
||||
/// <summary>
|
||||
/// Splits the <paramref name="input "/>string at the position defined
|
||||
/// by <paramref name="pattern"/>.
|
||||
/// </summary>
|
||||
public static string[] Split(string input, string pattern)
|
||||
{
|
||||
return Split(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the <paramref name="input "/>string at the position defined by <paramref name="pattern"/>.
|
||||
/// </summary>
|
||||
public static string[] Split(string input, string pattern, RegexOptions options)
|
||||
{
|
||||
return Split(input, pattern, options, s_defaultMatchTimeout);
|
||||
}
|
||||
|
||||
public static string[] Split(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
|
||||
{
|
||||
return new Regex(pattern, options, matchTimeout, true).Split(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the <paramref name="input"/> string at the position defined by a
|
||||
/// previous pattern.
|
||||
/// </summary>
|
||||
public string[] Split(string input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Split(input, 0, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the <paramref name="input"/> string at the position defined by a
|
||||
/// previous pattern.
|
||||
/// </summary>
|
||||
public string[] Split(string input, int count)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Split(this, input, count, UseOptionR() ? input.Length : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the <paramref name="input"/> string at the position defined by a previous pattern.
|
||||
/// </summary>
|
||||
public string[] Split(string input, int count, int startat)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return Split(this, input, count, startat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a split. In the right-to-left case we reorder the
|
||||
/// array to be forwards.
|
||||
/// </summary>
|
||||
private static string[] Split(Regex regex, string input, int count, int startat)
|
||||
{
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count), SR.CountTooSmall);
|
||||
if (startat < 0 || startat > input.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(startat), SR.BeginIndexNotNegative);
|
||||
|
||||
string[] result;
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
result = new string[1];
|
||||
result[0] = input;
|
||||
return result;
|
||||
}
|
||||
|
||||
count -= 1;
|
||||
|
||||
Match match = regex.Match(input, startat);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
result = new string[1];
|
||||
result[0] = input;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<string> al = new List<string>();
|
||||
|
||||
if (!regex.RightToLeft)
|
||||
{
|
||||
int prevat = 0;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
al.Add(input.Substring(prevat, match.Index - prevat));
|
||||
|
||||
prevat = match.Index + match.Length;
|
||||
|
||||
// add all matched capture groups to the list.
|
||||
for (int i = 1; i < match.Groups.Count; i++)
|
||||
{
|
||||
if (match.IsMatched(i))
|
||||
al.Add(match.Groups[i].ToString());
|
||||
}
|
||||
|
||||
if (--count == 0)
|
||||
break;
|
||||
|
||||
match = match.NextMatch();
|
||||
|
||||
if (!match.Success)
|
||||
break;
|
||||
}
|
||||
|
||||
al.Add(input.Substring(prevat, input.Length - prevat));
|
||||
}
|
||||
else
|
||||
{
|
||||
int prevat = input.Length;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
|
||||
|
||||
prevat = match.Index;
|
||||
|
||||
// add all matched capture groups to the list.
|
||||
for (int i = 1; i < match.Groups.Count; i++)
|
||||
{
|
||||
if (match.IsMatched(i))
|
||||
al.Add(match.Groups[i].ToString());
|
||||
}
|
||||
|
||||
if (--count == 0)
|
||||
break;
|
||||
|
||||
match = match.NextMatch();
|
||||
|
||||
if (!match.Success)
|
||||
break;
|
||||
}
|
||||
|
||||
al.Add(input.Substring(0, prevat));
|
||||
|
||||
al.Reverse(0, al.Count);
|
||||
}
|
||||
|
||||
return al.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Text.RegularExpressions
|
||||
{
|
||||
public partial class Regex
|
||||
{
|
||||
// We need this because time is queried using Environment.TickCount for performance reasons
|
||||
// (Environment.TickCount returns milliseconds as an int and cycles):
|
||||
private static readonly TimeSpan s_maximumMatchTimeout = TimeSpan.FromMilliseconds(int.MaxValue - 1);
|
||||
|
||||
// During static initialisation of Regex we check
|
||||
private const string DefaultMatchTimeout_ConfigKeyName = "REGEX_DEFAULT_MATCH_TIMEOUT";
|
||||
|
||||
// DefaultMatchTimeout specifies the match timeout to use if no other timeout was specified
|
||||
// by one means or another. Typically, it is set to InfiniteMatchTimeout.
|
||||
internal static readonly TimeSpan s_defaultMatchTimeout;
|
||||
|
||||
// InfiniteMatchTimeout specifies that match timeout is switched OFF. It allows for faster code paths
|
||||
// compared to simply having a very large timeout.
|
||||
// We do not want to ask users to use System.Threading.Timeout.InfiniteTimeSpan as a parameter because:
|
||||
// (1) We do not want to imply any relation between having using a RegEx timeout and using multi-threading.
|
||||
// (2) We do not want to require users to take ref to a contract assembly for threading just to use RegEx.
|
||||
// There may in theory be a SKU that has RegEx, but no multithreading.
|
||||
// We create a public Regex.InfiniteMatchTimeout constant, which for consistency uses the save underlying
|
||||
// value as Timeout.InfiniteTimeSpan creating an implementation detail dependency only.
|
||||
public static readonly TimeSpan InfiniteMatchTimeout = Timeout.InfiniteTimeSpan;
|
||||
|
||||
// timeout for the execution of this regex
|
||||
protected internal TimeSpan internalMatchTimeout;
|
||||
|
||||
static Regex()
|
||||
{
|
||||
s_defaultMatchTimeout = InitDefaultMatchTimeout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The match timeout used by this Regex instance.
|
||||
/// </summary>
|
||||
public TimeSpan MatchTimeout => internalMatchTimeout;
|
||||
|
||||
// Note: "<" is the XML entity for smaller ("<").
|
||||
/// <summary>
|
||||
/// Validates that the specified match timeout value is valid.
|
||||
/// The valid range is <code>TimeSpan.Zero < matchTimeout <= Regex.MaximumMatchTimeout</code>.
|
||||
/// </summary>
|
||||
/// <param name="matchTimeout">The timeout value to validate.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">If the specified timeout is not within a valid range.
|
||||
/// </exception>
|
||||
protected internal static void ValidateMatchTimeout(TimeSpan matchTimeout)
|
||||
{
|
||||
if (InfiniteMatchTimeout == matchTimeout)
|
||||
return;
|
||||
|
||||
// Change this to make sure timeout is not longer then Environment.Ticks cycle length:
|
||||
if (TimeSpan.Zero < matchTimeout && matchTimeout <= s_maximumMatchTimeout)
|
||||
return;
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(matchTimeout));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the default RegEx matching timeout value (i.e. the timeout that will be used if no
|
||||
/// explicit timeout is specified).
|
||||
/// The default is queried from the current <code>AppDomain</code>.
|
||||
/// If the AddDomain's data value for that key is not a <code>TimeSpan</code> value or if it is outside the
|
||||
/// valid range, an exception is thrown.
|
||||
/// If the AddDomain's data value for that key is <code>null</code>, a fallback value is returned.
|
||||
/// </summary>
|
||||
/// <returns>The default RegEx matching timeout for this AppDomain</returns>
|
||||
private static TimeSpan InitDefaultMatchTimeout()
|
||||
{
|
||||
// Query AppDomain
|
||||
AppDomain ad = AppDomain.CurrentDomain;
|
||||
object defaultMatchTimeoutObj = ad.GetData(DefaultMatchTimeout_ConfigKeyName);
|
||||
|
||||
// If no default is specified, use fallback
|
||||
if (defaultMatchTimeoutObj == null)
|
||||
{
|
||||
return InfiniteMatchTimeout;
|
||||
}
|
||||
|
||||
if (defaultMatchTimeoutObj is TimeSpan defaultMatchTimeOut)
|
||||
{
|
||||
// If default timeout is outside the valid range, throw. It will result in a TypeInitializationException:
|
||||
try
|
||||
{
|
||||
ValidateMatchTimeout(defaultMatchTimeOut);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(SR.Format(SR.IllegalDefaultRegexMatchTimeoutInAppDomain, DefaultMatchTimeout_ConfigKeyName, defaultMatchTimeOut));
|
||||
}
|
||||
|
||||
return defaultMatchTimeOut;
|
||||
}
|
||||
|
||||
throw new InvalidCastException(SR.Format(SR.IllegalDefaultRegexMatchTimeoutInAppDomain, DefaultMatchTimeout_ConfigKeyName, defaultMatchTimeoutObj));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user