サロゲートペアを考慮してSubstringできるやつ

using System;
using System.Collections.Generic;
using System.Text;
namespace Liberfy
{
public sealed unsafe class TwStringInfo : IDisposable
{
#region fields
private int* surrogateIndices;
private int surrogateIndicesLength;
#endregion
#region properties
public string String { get; private set; }
public int Length { get; private set; }
#endregion
public TwStringInfo(string text)
{
if (text == null) return;
var surrogateIndicesList = new LinkedList<int>();
String = text.Normalize(NormalizationForm.FormC);
char c;
int Length = String.Length;
int surrogateIndex;
for (surrogateIndex = 0; surrogateIndex < Length; surrogateIndex++)
{
if ('\uD800' <= (c = String[surrogateIndex]) && c <= '\uDBFF')
{
surrogateIndicesList.AddLast(surrogateIndex);
surrogateIndex++;
}
}
surrogateIndicesLength = surrogateIndicesList.Count;
if (surrogateIndicesLength > 0)
{
int* indices = stackalloc int[surrogateIndicesLength];
LinkedListNode<int> node = surrogateIndicesList.First;
for (int i = 0; i < surrogateIndicesLength; i++)
{
indices[i] = node.Value;
node = node.Next;
}
surrogateIndices = indices;
indices = null;
surrogateIndicesList.Clear();
}
surrogateIndicesList = null;
}
public string Substring(int startIndex)
{
int substringStartIndex = startIndex;
for (int i = 0; i < surrogateIndicesLength; i++)
{
if (surrogateIndices[i] < startIndex)
{
substringStartIndex++;
}
else
{
break;
}
}
return String.Substring(substringStartIndex);
}
public string Substring(int startIndex, int length)
{
int i = 0;
int substringStartIndex = startIndex;
int substringLength = length;
int totalLength = startIndex + length;
for (; i < surrogateIndicesLength; i++)
{
if (surrogateIndices[i] < startIndex)
{
substringStartIndex++;
}
else
{
break;
}
}
for (; i < surrogateIndicesLength; i++)
{
if (surrogateIndices[i] < totalLength)
{
substringLength++;
}
else
{
break;
}
}
return String.Substring(substringStartIndex, substringLength);
}
#region IDisposable Support
private bool disposedValue = false;
private void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
Length = 0;
String = null;
}
else
{
surrogateIndices = null;
surrogateIndicesLength = 0;
}
disposedValue = true;
}
}
~TwStringInfo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX