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 } }