Added a permissive code point enumerator, which doesn't choke on lone surrogates.
This commit is contained in:
@@ -15,5 +15,5 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[assembly: AssemblyVersion("1.0.1.0")]
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("1.0.1.0")]
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ namespace UnicodeCharacterInspector
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
if (!string.IsNullOrEmpty(value))
|
if (!string.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
foreach (int codePoint in value.AsCodePointEnumerable())
|
foreach (int codePoint in value.AsPermissiveCodePointEnumerable())
|
||||||
{
|
{
|
||||||
if (index >= codePoints.Length) Array.Resize(ref codePoints, codePoints.Length * 2);
|
if (index >= codePoints.Length) Array.Resize(ref codePoints, codePoints.Length * 2);
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,91 @@ namespace UnicodeInformation.Tests
|
|||||||
Assert.AreEqual(false, legacyEnumerator.MoveNext());
|
Assert.AreEqual(false, legacyEnumerator.MoveNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EnumerationFailTest(string text) { foreach (int codePoint in text.AsCodePointEnumerable()) { } }
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CodePointEnumeratorDirtyTest()
|
||||||
|
{
|
||||||
|
AssertEx.ThrowsExactly<ArgumentNullException>(() => EnumerationFailTest(null));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\uDA00"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\uDCD0"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\uDCD0\uDA00"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\u0041\uDA00"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\u0041\uDCD0"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\uDA00\u0041"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\uDCD0\u0041"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\uDA00\u0041\uDCD0\u0041"));
|
||||||
|
AssertEx.ThrowsExactly<ArgumentException>(() => EnumerationFailTest("\u0041\uDA00\u0041\uDCD0\u0041"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void PermissiveCodePointEnumeratorTest()
|
||||||
|
{
|
||||||
|
string text = "\u0041\U0001F600\u00E9";
|
||||||
|
|
||||||
|
var enumerable = text.AsPermissiveCodePointEnumerable();
|
||||||
|
|
||||||
|
Assert.AreEqual(text, enumerable.Text);
|
||||||
|
|
||||||
|
var enumerator = enumerable.GetEnumerator();
|
||||||
|
|
||||||
|
Assert.AreEqual(true, enumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x0041, enumerator.Current);
|
||||||
|
Assert.AreEqual(true, enumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x1F600, enumerator.Current);
|
||||||
|
Assert.AreEqual(true, enumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x00E9, enumerator.Current);
|
||||||
|
Assert.AreEqual(false, enumerator.MoveNext());
|
||||||
|
|
||||||
|
var genericEnumerator = ((IEnumerable<int>)enumerable).GetEnumerator();
|
||||||
|
|
||||||
|
Assert.AreEqual(true, genericEnumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x0041, genericEnumerator.Current);
|
||||||
|
Assert.AreEqual(true, genericEnumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x1F600, genericEnumerator.Current);
|
||||||
|
Assert.AreEqual(true, genericEnumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x00E9, genericEnumerator.Current);
|
||||||
|
Assert.AreEqual(false, genericEnumerator.MoveNext());
|
||||||
|
|
||||||
|
var legacyEnumerator = ((IEnumerable)enumerable).GetEnumerator();
|
||||||
|
|
||||||
|
Assert.AreEqual(true, legacyEnumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x0041, legacyEnumerator.Current);
|
||||||
|
Assert.AreEqual(true, legacyEnumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x1F600, legacyEnumerator.Current);
|
||||||
|
Assert.AreEqual(true, legacyEnumerator.MoveNext());
|
||||||
|
Assert.AreEqual(0x00E9, legacyEnumerator.Current);
|
||||||
|
Assert.AreEqual(false, legacyEnumerator.MoveNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PermissiveEnumerationTest(string text, int[] expectedCharacters)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (int codePoint in text.AsPermissiveCodePointEnumerable())
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expectedCharacters[i++], codePoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(expectedCharacters.Length, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void PermissiveCodePointEnumeratorDirtyTest()
|
||||||
|
{
|
||||||
|
AssertEx.ThrowsExactly<ArgumentNullException>(() => { foreach (int c in (null as string).AsPermissiveCodePointEnumerable()) { } });
|
||||||
|
PermissiveEnumerationTest(string.Empty, new int[0]);
|
||||||
|
PermissiveEnumerationTest("\uDA00", new int[] { 0xDA00 });
|
||||||
|
PermissiveEnumerationTest("\uDCD0", new int[] { 0xDCD0 });
|
||||||
|
PermissiveEnumerationTest("\uDCD0\uDA00", new int[] { 0xDCD0, 0xDA00 });
|
||||||
|
PermissiveEnumerationTest("\u0041\uDA00", new int[] { 0x0041, 0xDA00 });
|
||||||
|
PermissiveEnumerationTest("\u0041\uDCD0", new int[] { 0x0041, 0xDCD0 });
|
||||||
|
PermissiveEnumerationTest("\uDA00\u0041", new int[] { 0xDA00, 0x0041 });
|
||||||
|
PermissiveEnumerationTest("\uDCD0\u0041", new int[] { 0xDCD0, 0x0041 });
|
||||||
|
PermissiveEnumerationTest("\uDA00\u0041\uDCD0\u0041", new int[] { 0xDA00, 0x0041, 0xDCD0, 0x0041 });
|
||||||
|
PermissiveEnumerationTest("\u0041\uDA00\u0041\uDCD0\u0041", new int[] { 0x0041, 0xDA00, 0x0041, 0xDCD0, 0x0041 });
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void DisplayTextTest()
|
public void DisplayTextTest()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ namespace System.Unicode
|
|||||||
|
|
||||||
public CodePointEnumerable(string text)
|
public CodePointEnumerable(string text)
|
||||||
{
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
|
||||||
this.text = text;
|
this.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace System.Unicode
|
||||||
|
{
|
||||||
|
public struct PermissiveCodePointEnumerable : IEnumerable<int>
|
||||||
|
{
|
||||||
|
private readonly string text;
|
||||||
|
|
||||||
|
public PermissiveCodePointEnumerable(string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text { get { return text; } }
|
||||||
|
|
||||||
|
public PermissiveCodePointEnumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return new PermissiveCodePointEnumerator(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<int> IEnumerable<int>.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace System.Unicode
|
||||||
|
{
|
||||||
|
public struct PermissiveCodePointEnumerator : IEnumerator<int>
|
||||||
|
{
|
||||||
|
private readonly string text;
|
||||||
|
private int current;
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
public PermissiveCodePointEnumerator(string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
|
||||||
|
this.text = text;
|
||||||
|
this.current = 0;
|
||||||
|
this.index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Current { get { return current; } }
|
||||||
|
|
||||||
|
object IEnumerator.Current { get { return current; } }
|
||||||
|
|
||||||
|
void IDisposable.Dispose() { }
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (index < text.Length && (index += current > 0xFFFF ? 2 : 1) < text.Length)
|
||||||
|
{
|
||||||
|
current = GetUtf32(text, index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IEnumerator.Reset()
|
||||||
|
{
|
||||||
|
current = 0;
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetUtf32(string s, int index)
|
||||||
|
{
|
||||||
|
char c1 = s[index];
|
||||||
|
|
||||||
|
if (char.IsHighSurrogate(c1) && ++index < s.Length)
|
||||||
|
{
|
||||||
|
char c2 = s[index];
|
||||||
|
|
||||||
|
if (char.IsLowSurrogate(c2)) return char.ConvertToUtf32(c1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,5 +12,10 @@ namespace System.Unicode
|
|||||||
{
|
{
|
||||||
return new CodePointEnumerable(s);
|
return new CodePointEnumerable(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PermissiveCodePointEnumerable AsPermissiveCodePointEnumerable(this string s)
|
||||||
|
{
|
||||||
|
return new PermissiveCodePointEnumerable(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,8 @@
|
|||||||
<Compile Include="BidirectionalClass.cs" />
|
<Compile Include="BidirectionalClass.cs" />
|
||||||
<Compile Include="CjkRadicalData.cs" />
|
<Compile Include="CjkRadicalData.cs" />
|
||||||
<Compile Include="CjkRadicalInfo.cs" />
|
<Compile Include="CjkRadicalInfo.cs" />
|
||||||
|
<Compile Include="PermissiveCodePointEnumerable.cs" />
|
||||||
|
<Compile Include="PermissiveCodePointEnumerator.cs" />
|
||||||
<Compile Include="CompatibilityFormattingTag.cs" />
|
<Compile Include="CompatibilityFormattingTag.cs" />
|
||||||
<Compile Include="ContributoryProperties.cs" />
|
<Compile Include="ContributoryProperties.cs" />
|
||||||
<Compile Include="CanonicalCombiningClass.cs" />
|
<Compile Include="CanonicalCombiningClass.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user