OSDN Git Service

コア部分を共通プロジェクト化した
[fooeditengine/FooEditEngine.git] / Core / StringBuffer.cs
similarity index 97%
rename from Common/StringBuffer.cs
rename to Core/StringBuffer.cs
index 4ae0b8f..da8503f 100644 (file)
-/*\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