-/*\r
- * Copyright (C) 2013 FooProject\r
- * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.\r
-\r
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of \r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- */\r
-using System;\r
-using System.Collections.Generic;\r
-using System.Globalization;\r
-using System.Linq;\r
-using System.Text;\r
-using System.Text.RegularExpressions;\r
-using System.Threading;\r
-using System.Threading.Tasks;\r
-using Slusser.Collections.Generic;\r
-\r
-namespace FooEditEngine\r
-{\r
- /// <summary>\r
- /// ランダムアクセス可能な列挙子を提供するインターフェイス\r
- /// </summary>\r
- /// <typeparam name="T"></typeparam>\r
- public interface IRandomEnumrator<T>\r
- {\r
- /// <summary>\r
- /// インデクサーを表す\r
- /// </summary>\r
- /// <param name="index">インデックス</param>\r
- /// <returns>Tを返す</returns>\r
- T this[int index]{get;}\r
- }\r
-\r
- sealed class StringBuffer : IEnumerable<char>, IRandomEnumrator<char>\r
- {\r
- GapBuffer<char> buf = new GapBuffer<char>();\r
-\r
- public StringBuffer()\r
- {\r
- this.Update = (s, e) => { };\r
- }\r
-\r
- public StringBuffer(StringBuffer buffer)\r
- : this()\r
- {\r
- buf.AddRange(buffer.buf, buffer.Length);\r
- }\r
-\r
- public char this[int index]\r
- {\r
- get\r
- {\r
- char c = buf[index];\r
- return c;\r
- }\r
- }\r
-\r
- public string ToString(int index, int length)\r
- {\r
- StringBuilder temp = new StringBuilder();\r
- temp.Clear();\r
- for (int i = index; i < index + length; i++)\r
- temp.Append(buf[i]);\r
- return temp.ToString();\r
- }\r
-\r
- public IEnumerable<string> GetLines(int startIndex, int endIndex, int maxCharCount = -1)\r
- {\r
- foreach (Tuple<int, int> range in this.ForEachLines(startIndex, endIndex, maxCharCount))\r
- {\r
- StringBuilder temp = new StringBuilder();\r
- temp.Clear();\r
- int lineEndIndex = range.Item1;\r
- if (range.Item2 > 0)\r
- lineEndIndex += range.Item2 - 1;\r
- for (int i = range.Item1; i <= lineEndIndex; i++)\r
- temp.Append(buf[i]);\r
- yield return temp.ToString();\r
- }\r
- }\r
-\r
- public IEnumerable<Tuple<int,int>> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)\r
- {\r
- int currentLineHeadIndex = startIndex;\r
- int currentLineLength = 0;\r
- \r
- for (int i = startIndex; i <= endIndex; i++)\r
- {\r
- currentLineLength++;\r
- char c = this.buf[i];\r
- if (c == Document.NewLine ||\r
- (maxCharCount != -1 && currentLineLength >= maxCharCount))\r
- {\r
- UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);\r
- if (uc != UnicodeCategory.NonSpacingMark &&\r
- uc != UnicodeCategory.SpacingCombiningMark &&\r
- uc != UnicodeCategory.EnclosingMark &&\r
- uc != UnicodeCategory.Surrogate)\r
- {\r
- yield return new Tuple<int,int>(currentLineHeadIndex, currentLineLength);\r
- currentLineHeadIndex += currentLineLength;\r
- currentLineLength = 0;\r
- }\r
- }\r
- }\r
- if (currentLineLength > 0)\r
- yield return new Tuple<int, int>(currentLineHeadIndex, currentLineLength);\r
- }\r
- \r
- public int Length\r
- {\r
- get { return this.buf.Count; }\r
- }\r
-\r
- internal DocumentUpdateEventHandler Update;\r
-\r
- internal void Replace(StringBuffer buf)\r
- {\r
- this.Replace(buf.buf);\r
- }\r
-\r
- internal void Replace(GapBuffer<char> buf)\r
- {\r
- this.Clear();\r
- this.buf = buf;\r
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));\r
- }\r
-\r
- internal void Replace(int index, int length, IEnumerable<char> chars,int count)\r
- {\r
- if (length > 0)\r
- this.buf.RemoveRange(index, length);\r
- this.buf.InsertRange(index, chars,count);\r
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, index, length, count));\r
- }\r
-\r
- internal async Task LoadAsync(IStreamReader fs, CancellationTokenSource tokenSource = null)\r
- {\r
- char[] str = new char[1024 * 256];\r
- int readCount;\r
- do\r
- {\r
- int index = this.Length;\r
- if (index < 0)\r
- index = 0;\r
-\r
- readCount = await fs.ReadAsync(str, 0, str.Length).ConfigureAwait(false);\r
- \r
- //内部形式に変換する\r
- var internal_str = from s in str where s != '\r' && s != '\0' select s;\r
-\r
- //str.lengthは事前に確保しておくために使用するので影響はない\r
- this.buf.InsertRange(index, internal_str, str.Length);\r
-\r
- if (tokenSource != null)\r
- tokenSource.Token.ThrowIfCancellationRequested();\r
-#if TEST_ASYNC\r
- System.Threading.Thread.Sleep(10);\r
-#endif\r
- Array.Clear(str, 0, str.Length);\r
- } while (readCount > 0);\r
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));\r
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));\r
- }\r
-\r
- internal void ReplaceRegexAll(LineToIndexTable layoutlines, Regex regex, string pattern, bool groupReplace)\r
- {\r
- for (int i = 0; i < layoutlines.Count; i++)\r
- {\r
- int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);\r
- int left = lineHeadIndex, right = lineHeadIndex;\r
- string output = regex.Replace(layoutlines[i], (m) => {\r
- if (groupReplace)\r
- return m.Result(pattern);\r
- else\r
- return pattern;\r
- });\r
- this.buf.RemoveRange(lineHeadIndex, lineLength);\r
- this.buf.InsertRange(lineHeadIndex, output, output.Length);\r
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, output.Length, i));\r
- }\r
- }\r
-\r
- internal void ReplaceAll(LineToIndexTable layoutlines,string target, string pattern, bool ci = false)\r
- {\r
- TextSearch ts = new TextSearch(target, ci);\r
- char[] pattern_chars = pattern.ToCharArray();\r
- for(int i = 0; i < layoutlines.Count; i++)\r
- {\r
- int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);\r
- int left = lineHeadIndex, right = lineHeadIndex;\r
- int newLineLength = lineLength;\r
- while ((right = ts.IndexOf(this.buf, left, lineHeadIndex + newLineLength)) != -1)\r
- {\r
- this.buf.RemoveRange(right, target.Length);\r
- this.buf.InsertRange(right, pattern_chars, pattern.Length);\r
- left = right + pattern.Length;\r
- newLineLength += pattern.Length - target.Length;\r
- }\r
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, newLineLength, i));\r
- }\r
- }\r
-\r
- internal int IndexOf(string target, int start,bool ci = false)\r
- {\r
- TextSearch ts = new TextSearch(target,ci);\r
- return ts.IndexOf(this.buf, start,this.buf.Count);\r
- }\r
-\r
- /// <summary>\r
- /// 文字列を削除する\r
- /// </summary>\r
- internal void Clear()\r
- {\r
- this.buf.Clear();\r
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, 0, this.buf.Count,0));\r
- }\r
-\r
- internal IEnumerable<char> GetEnumerator(int start, int length)\r
- {\r
- for (int i = start; i < start + length; i++)\r
- yield return this.buf[i];\r
- }\r
-\r
- #region IEnumerable<char> メンバー\r
-\r
- public IEnumerator<char> GetEnumerator()\r
- {\r
- for (int i = 0; i < this.Length; i++)\r
- yield return this.buf[i];\r
- }\r
-\r
- #endregion\r
-\r
- #region IEnumerable メンバー\r
-\r
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()\r
- {\r
- for (int i = 0; i < this.Length; i++)\r
- yield return this[i];\r
- }\r
-\r
- #endregion\r
- }\r
-\r
- sealed class TextSearch\r
- {\r
- char[] pattern;\r
- int patternLength;\r
- Dictionary<char, int> qsTable = new Dictionary<char, int>();\r
- bool caseInsenstive;\r
- public TextSearch(string pattern,bool ci = false)\r
- {\r
- this.patternLength = pattern.Length;\r
- this.caseInsenstive = ci;\r
- if (ci)\r
- {\r
- this.CreateQSTable(pattern.ToLower());\r
- this.CreateQSTable(pattern.ToUpper());\r
- this.pattern = new char[pattern.Length];\r
- for (int i = 0; i < pattern.Length; i++)\r
- this.pattern[i] = CharTool.ToUpperFastIf(pattern[i]);\r
- }\r
- else\r
- {\r
- this.CreateQSTable(pattern);\r
- this.pattern = pattern.ToCharArray();\r
- }\r
- }\r
- void CreateQSTable(string pattern)\r
- {\r
- int len = pattern.Length;\r
- for (int i = 0; i < len; i++)\r
- {\r
- if (!this.qsTable.ContainsKey(pattern[i]))\r
- this.qsTable.Add(pattern[i], len - i);\r
- else\r
- this.qsTable[pattern[i]] = len - i;\r
- }\r
- }\r
- public int IndexOf(GapBuffer<char> buf, int start,int end)\r
- {\r
- //QuickSearch法\r
- int buflen = buf.Count - 1;\r
- int plen = this.patternLength;\r
- int i = start;\r
- int search_end = end - plen;\r
- //最適化のためわざとコピペした\r
- if (this.caseInsenstive)\r
- {\r
- while (i <= search_end)\r
- {\r
- int j = 0;\r
- while (j < plen)\r
- {\r
- if (CharTool.ToUpperFastIf(buf[i + j]) != this.pattern[j])\r
- break;\r
- j++;\r
- }\r
- if (j == plen)\r
- {\r
- return i;\r
- }\r
- else\r
- {\r
- int k = i + plen;\r
- if (k <= buflen) //buffer以降にアクセスする可能性がある\r
- {\r
- int moveDelta;\r
- if (this.qsTable.TryGetValue(buf[k], out moveDelta))\r
- i += moveDelta;\r
- else\r
- i += plen;\r
- }\r
- else\r
- {\r
- break;\r
- }\r
- }\r
- }\r
-\r
- }\r
- else\r
- {\r
- while (i <= search_end)\r
- {\r
- int j = 0;\r
- while (j < plen)\r
- {\r
- if (buf[i + j] != this.pattern[j])\r
- break;\r
- j++;\r
- }\r
- if (j == plen)\r
- {\r
- return i;\r
- }\r
- else\r
- {\r
- int k = i + plen;\r
- if (k <= buflen) //buffer以降にアクセスする可能性がある\r
- {\r
- int moveDelta;\r
- if (this.qsTable.TryGetValue(buf[k], out moveDelta))\r
- i += moveDelta;\r
- else\r
- i += plen;\r
- }\r
- else\r
- {\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- return -1;\r
- }\r
- }\r
- static class CharTool\r
- {\r
- /// <summary>\r
- /// Converts characters to lowercase.\r
- /// </summary>\r
- const string _lookupStringL =\r
- "---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-";\r
-\r
- /// <summary>\r
- /// Converts characters to uppercase.\r
- /// </summary>\r
- const string _lookupStringU =\r
- "---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[-]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~-";\r
-\r
- /// <summary>\r
- /// Get lowercase version of this ASCII character.\r
- /// </summary>\r
- public static char ToLower(char c)\r
- {\r
- return _lookupStringL[c];\r
- }\r
-\r
- /// <summary>\r
- /// Get uppercase version of this ASCII character.\r
- /// </summary>\r
- public static char ToUpper(char c)\r
- {\r
- return _lookupStringU[c];\r
- }\r
-\r
- /// <summary>\r
- /// Translate uppercase ASCII characters to lowercase.\r
- /// </summary>\r
- public static char ToLowerFastIf(char c)\r
- {\r
- if (c >= 'A' && c <= 'Z')\r
- {\r
- return (char)(c + 32);\r
- }\r
- else\r
- {\r
- return c;\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Translate lowercase ASCII characters to uppercase.\r
- /// </summary>\r
- public static char ToUpperFastIf(char c)\r
- {\r
- if (c >= 'a' && c <= 'z')\r
- {\r
- return (char)(c - 32);\r
- }\r
- else\r
- {\r
- return c;\r
- }\r
- }\r
- }\r
+/*
+ * Copyright (C) 2013 FooProject
+ * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using Slusser.Collections.Generic;
+
+namespace FooEditEngine
+{
+ /// <summary>
+ /// ランダムアクセス可能な列挙子を提供するインターフェイス
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public interface IRandomEnumrator<T>
+ {
+ /// <summary>
+ /// インデクサーを表す
+ /// </summary>
+ /// <param name="index">インデックス</param>
+ /// <returns>Tを返す</returns>
+ T this[int index]{get;}
+ }
+
+ sealed class StringBuffer : IEnumerable<char>, IRandomEnumrator<char>
+ {
+ GapBuffer<char> buf = new GapBuffer<char>();
+
+ public StringBuffer()
+ {
+ this.Update = (s, e) => { };
+ }
+
+ public StringBuffer(StringBuffer buffer)
+ : this()
+ {
+ buf.AddRange(buffer.buf, buffer.Length);
+ }
+
+ public char this[int index]
+ {
+ get
+ {
+ char c = buf[index];
+ return c;
+ }
+ }
+
+ public string ToString(int index, int length)
+ {
+ StringBuilder temp = new StringBuilder();
+ temp.Clear();
+ for (int i = index; i < index + length; i++)
+ temp.Append(buf[i]);
+ return temp.ToString();
+ }
+
+ public IEnumerable<string> GetLines(int startIndex, int endIndex, int maxCharCount = -1)
+ {
+ foreach (Tuple<int, int> range in this.ForEachLines(startIndex, endIndex, maxCharCount))
+ {
+ StringBuilder temp = new StringBuilder();
+ temp.Clear();
+ int lineEndIndex = range.Item1;
+ if (range.Item2 > 0)
+ lineEndIndex += range.Item2 - 1;
+ for (int i = range.Item1; i <= lineEndIndex; i++)
+ temp.Append(buf[i]);
+ yield return temp.ToString();
+ }
+ }
+
+ public IEnumerable<Tuple<int,int>> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)
+ {
+ int currentLineHeadIndex = startIndex;
+ int currentLineLength = 0;
+
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ currentLineLength++;
+ char c = this.buf[i];
+ if (c == Document.NewLine ||
+ (maxCharCount != -1 && currentLineLength >= maxCharCount))
+ {
+ UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);
+ if (uc != UnicodeCategory.NonSpacingMark &&
+ uc != UnicodeCategory.SpacingCombiningMark &&
+ uc != UnicodeCategory.EnclosingMark &&
+ uc != UnicodeCategory.Surrogate)
+ {
+ yield return new Tuple<int,int>(currentLineHeadIndex, currentLineLength);
+ currentLineHeadIndex += currentLineLength;
+ currentLineLength = 0;
+ }
+ }
+ }
+ if (currentLineLength > 0)
+ yield return new Tuple<int, int>(currentLineHeadIndex, currentLineLength);
+ }
+
+ public int Length
+ {
+ get { return this.buf.Count; }
+ }
+
+ internal DocumentUpdateEventHandler Update;
+
+ internal void Replace(StringBuffer buf)
+ {
+ this.Replace(buf.buf);
+ }
+
+ internal void Replace(GapBuffer<char> buf)
+ {
+ this.Clear();
+ this.buf = buf;
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));
+ }
+
+ internal void Replace(int index, int length, IEnumerable<char> chars,int count)
+ {
+ if (length > 0)
+ this.buf.RemoveRange(index, length);
+ this.buf.InsertRange(index, chars,count);
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, index, length, count));
+ }
+
+ internal async Task LoadAsync(IStreamReader fs, CancellationTokenSource tokenSource = null)
+ {
+ char[] str = new char[1024 * 256];
+ int readCount;
+ do
+ {
+ int index = this.Length;
+ if (index < 0)
+ index = 0;
+
+ readCount = await fs.ReadAsync(str, 0, str.Length).ConfigureAwait(false);
+
+ //内部形式に変換する
+ var internal_str = from s in str where s != '\r' && s != '\0' select s;
+
+ //str.lengthは事前に確保しておくために使用するので影響はない
+ this.buf.InsertRange(index, internal_str, str.Length);
+
+ if (tokenSource != null)
+ tokenSource.Token.ThrowIfCancellationRequested();
+#if TEST_ASYNC
+ System.Threading.Thread.Sleep(10);
+#endif
+ Array.Clear(str, 0, str.Length);
+ } while (readCount > 0);
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));
+ }
+
+ internal void ReplaceRegexAll(LineToIndexTable layoutlines, Regex regex, string pattern, bool groupReplace)
+ {
+ for (int i = 0; i < layoutlines.Count; i++)
+ {
+ int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);
+ int left = lineHeadIndex, right = lineHeadIndex;
+ string output = regex.Replace(layoutlines[i], (m) => {
+ if (groupReplace)
+ return m.Result(pattern);
+ else
+ return pattern;
+ });
+ this.buf.RemoveRange(lineHeadIndex, lineLength);
+ this.buf.InsertRange(lineHeadIndex, output, output.Length);
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, output.Length, i));
+ }
+ }
+
+ internal void ReplaceAll(LineToIndexTable layoutlines,string target, string pattern, bool ci = false)
+ {
+ TextSearch ts = new TextSearch(target, ci);
+ char[] pattern_chars = pattern.ToCharArray();
+ for(int i = 0; i < layoutlines.Count; i++)
+ {
+ int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);
+ int left = lineHeadIndex, right = lineHeadIndex;
+ int newLineLength = lineLength;
+ while ((right = ts.IndexOf(this.buf, left, lineHeadIndex + newLineLength)) != -1)
+ {
+ this.buf.RemoveRange(right, target.Length);
+ this.buf.InsertRange(right, pattern_chars, pattern.Length);
+ left = right + pattern.Length;
+ newLineLength += pattern.Length - target.Length;
+ }
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, newLineLength, i));
+ }
+ }
+
+ internal int IndexOf(string target, int start,bool ci = false)
+ {
+ TextSearch ts = new TextSearch(target,ci);
+ return ts.IndexOf(this.buf, start,this.buf.Count);
+ }
+
+ /// <summary>
+ /// 文字列を削除する
+ /// </summary>
+ internal void Clear()
+ {
+ this.buf.Clear();
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, 0, this.buf.Count,0));
+ }
+
+ internal IEnumerable<char> GetEnumerator(int start, int length)
+ {
+ for (int i = start; i < start + length; i++)
+ yield return this.buf[i];
+ }
+
+ #region IEnumerable<char> メンバー
+
+ public IEnumerator<char> GetEnumerator()
+ {
+ for (int i = 0; i < this.Length; i++)
+ yield return this.buf[i];
+ }
+
+ #endregion
+
+ #region IEnumerable メンバー
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ for (int i = 0; i < this.Length; i++)
+ yield return this[i];
+ }
+
+ #endregion
+ }
+
+ sealed class TextSearch
+ {
+ char[] pattern;
+ int patternLength;
+ Dictionary<char, int> qsTable = new Dictionary<char, int>();
+ bool caseInsenstive;
+ public TextSearch(string pattern,bool ci = false)
+ {
+ this.patternLength = pattern.Length;
+ this.caseInsenstive = ci;
+ if (ci)
+ {
+ this.CreateQSTable(pattern.ToLower());
+ this.CreateQSTable(pattern.ToUpper());
+ this.pattern = new char[pattern.Length];
+ for (int i = 0; i < pattern.Length; i++)
+ this.pattern[i] = CharTool.ToUpperFastIf(pattern[i]);
+ }
+ else
+ {
+ this.CreateQSTable(pattern);
+ this.pattern = pattern.ToCharArray();
+ }
+ }
+ void CreateQSTable(string pattern)
+ {
+ int len = pattern.Length;
+ for (int i = 0; i < len; i++)
+ {
+ if (!this.qsTable.ContainsKey(pattern[i]))
+ this.qsTable.Add(pattern[i], len - i);
+ else
+ this.qsTable[pattern[i]] = len - i;
+ }
+ }
+ public int IndexOf(GapBuffer<char> buf, int start,int end)
+ {
+ //QuickSearch法
+ int buflen = buf.Count - 1;
+ int plen = this.patternLength;
+ int i = start;
+ int search_end = end - plen;
+ //最適化のためわざとコピペした
+ if (this.caseInsenstive)
+ {
+ while (i <= search_end)
+ {
+ int j = 0;
+ while (j < plen)
+ {
+ if (CharTool.ToUpperFastIf(buf[i + j]) != this.pattern[j])
+ break;
+ j++;
+ }
+ if (j == plen)
+ {
+ return i;
+ }
+ else
+ {
+ int k = i + plen;
+ if (k <= buflen) //buffer以降にアクセスする可能性がある
+ {
+ int moveDelta;
+ if (this.qsTable.TryGetValue(buf[k], out moveDelta))
+ i += moveDelta;
+ else
+ i += plen;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ }
+ else
+ {
+ while (i <= search_end)
+ {
+ int j = 0;
+ while (j < plen)
+ {
+ if (buf[i + j] != this.pattern[j])
+ break;
+ j++;
+ }
+ if (j == plen)
+ {
+ return i;
+ }
+ else
+ {
+ int k = i + plen;
+ if (k <= buflen) //buffer以降にアクセスする可能性がある
+ {
+ int moveDelta;
+ if (this.qsTable.TryGetValue(buf[k], out moveDelta))
+ i += moveDelta;
+ else
+ i += plen;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+ }
+ static class CharTool
+ {
+ /// <summary>
+ /// Converts characters to lowercase.
+ /// </summary>
+ const string _lookupStringL =
+ "---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-";
+
+ /// <summary>
+ /// Converts characters to uppercase.
+ /// </summary>
+ const string _lookupStringU =
+ "---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[-]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~-";
+
+ /// <summary>
+ /// Get lowercase version of this ASCII character.
+ /// </summary>
+ public static char ToLower(char c)
+ {
+ return _lookupStringL[c];
+ }
+
+ /// <summary>
+ /// Get uppercase version of this ASCII character.
+ /// </summary>
+ public static char ToUpper(char c)
+ {
+ return _lookupStringU[c];
+ }
+
+ /// <summary>
+ /// Translate uppercase ASCII characters to lowercase.
+ /// </summary>
+ public static char ToLowerFastIf(char c)
+ {
+ if (c >= 'A' && c <= 'Z')
+ {
+ return (char)(c + 32);
+ }
+ else
+ {
+ return c;
+ }
+ }
+
+ /// <summary>
+ /// Translate lowercase ASCII characters to uppercase.
+ /// </summary>
+ public static char ToUpperFastIf(char c)
+ {
+ if (c >= 'a' && c <= 'z')
+ {
+ return (char)(c - 32);
+ }
+ else
+ {
+ return c;
+ }
+ }
+ }
}
\ No newline at end of file