Enhance split in StringUtil (#32)

diff --git a/pom.xml b/pom.xml
index aa11784..2349b14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     <groupId>com.baidu.hugegraph</groupId>
     <artifactId>hugegraph-common</artifactId>
-    <version>1.6.8</version>
+    <version>1.6.9</version>
 
     <name>hugegraph-common</name>
     <url>https://github.com/hugegraph/hugegraph-common</url>
@@ -212,7 +212,7 @@
                         <manifestEntries>
                             <!-- Must be on one line, otherwise the automatic
                                  upgrade script cannot replace the version number -->
-                            <Implementation-Version>1.6.8.0</Implementation-Version>
+                            <Implementation-Version>1.6.9.0</Implementation-Version>
                         </manifestEntries>
                     </archive>
                 </configuration>
diff --git a/src/main/java/com/baidu/hugegraph/util/StringUtil.java b/src/main/java/com/baidu/hugegraph/util/StringUtil.java
index 8a2a723..f7dcbb9 100644
--- a/src/main/java/com/baidu/hugegraph/util/StringUtil.java
+++ b/src/main/java/com/baidu/hugegraph/util/StringUtil.java
@@ -19,22 +19,107 @@
 
 package com.baidu.hugegraph.util;
 
-import java.nio.CharBuffer;
-import java.util.ArrayList;
-import java.util.List;
-
 public final class StringUtil {
 
-    public static List<CharSequence> split(String line, String delimiter) {
+    public static Chars[] splitToCharsArray(String text, String delimiter) {
         E.checkArgument(delimiter.length() > 0,
                         "The delimiter can't be empty");
-        List<CharSequence> results = new ArrayList<>();
-        int from = 0;
-        for (int to; (to = line.indexOf(delimiter, from)) >= 0;
-             from = to + delimiter.length()) {
-            results.add(CharBuffer.wrap(line, from, to));
+        Chars[] buffer = new Chars[text.length()];
+        int count = Chars.split(text, delimiter, buffer);
+        if (count == buffer.length) {
+            return buffer;
         }
-        results.add(CharBuffer.wrap(line, from, line.length()));
-        return results;
+        Chars[] result = new Chars[count];
+        System.arraycopy(buffer, 0, result, 0, count);
+        return result;
+    }
+
+    public static String[] split(String text, String delimiter) {
+        E.checkArgument(delimiter.length() > 0,
+                        "The delimiter can't be empty");
+        Chars[] buffer = new Chars[text.length()];
+        int count = Chars.split(text, delimiter, buffer);
+        String[] result = new String[count];
+        for (int i = 0; i < count; i++) {
+            result[i] = buffer[i].toString();
+        }
+        return result;
+    }
+
+    public static class Chars implements CharSequence {
+
+        private final char[] chars;
+        private final int start;
+        private final int end;
+
+        public Chars(char[] chars, int start, int end) {
+            E.checkArgument(0 < start && start < chars.length || start == 0,
+                            "Invalid start parameter %s", start);
+            E.checkArgument(start <= end && end <= chars.length,
+                            "Invalid end parameter %s", end);
+            this.chars = chars;
+            this.start = start;
+            this.end = end;
+        }
+
+        @Override
+        public int length() {
+            return this.end - this.start;
+        }
+
+        @Override
+        public char charAt(int index) {
+            return this.chars[this.start + index];
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return new Chars(this.chars, this.start + start, this.start + end);
+        }
+
+        @Override
+        public boolean equals(Object object) {
+            if (!(object instanceof Chars)) {
+                return false;
+            }
+            Chars other = (Chars) object;
+            return this.toString().equals(other.toString());
+        }
+
+        @Override
+        public int hashCode() {
+            return this.toString().hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return new String(this.chars, this.start, this.length());
+        }
+
+        public static Chars of(String string) {
+            return new Chars(string.toCharArray(), 0, string.length());
+        }
+
+        public static Chars[] of(String... strings) {
+            Chars[] results = new Chars[strings.length];
+            for (int i = 0; i < strings.length; i++) {
+                results[i] = Chars.of(strings[i]);
+            }
+            return results;
+        }
+
+        public static int split(String text, String delimiter, Chars[] buffer) {
+            int count = 0;
+            int from = 0;
+            char[] chars = text.toCharArray();
+            for (int to; (to = text.indexOf(delimiter, from)) >= 0;
+                 from = to + delimiter.length()) {
+                buffer[count++] = new Chars(chars, from, to);
+            }
+            if (from < text.length()) {
+                buffer[count++] = new Chars(chars, from, text.length());
+            }
+            return count;
+        }
     }
 }
diff --git a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
index 81c9333..5d16637 100644
--- a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
+++ b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
@@ -27,5 +27,5 @@
 
     // The second parameter of Version.of() is for all-in-one JAR
     public static final Version VERSION = Version.of(CommonVersion.class,
-                                                     "1.6.8");
+                                                     "1.6.9");
 }
diff --git a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
index 629e986..e5f65d7 100644
--- a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
+++ b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
@@ -43,6 +43,7 @@
 import com.baidu.hugegraph.unit.util.NumericUtilTest;
 import com.baidu.hugegraph.unit.util.OrderLimitMapTest;
 import com.baidu.hugegraph.unit.util.ReflectionUtilTest;
+import com.baidu.hugegraph.unit.util.StringUtilTest;
 import com.baidu.hugegraph.unit.util.TimeUtilTest;
 import com.baidu.hugegraph.unit.util.VersionUtilTest;
 import com.baidu.hugegraph.unit.version.VersionTest;
@@ -69,6 +70,7 @@
     InsertionOrderUtilTest.class,
     NumericUtilTest.class,
     ReflectionUtilTest.class,
+    StringUtilTest.class,
     TimeUtilTest.class,
     VersionUtilTest.class,
     LongEncodingTest.class,
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java b/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java
index 2c9fd4f..f37ba41 100644
--- a/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java
@@ -26,12 +26,20 @@
 
 import com.baidu.hugegraph.testutil.Assert;
 import com.baidu.hugegraph.util.StringUtil;
+import com.baidu.hugegraph.util.StringUtil.Chars;
 import com.google.common.base.Splitter;
 
 public class StringUtilTest {
 
     @Test
     public void testSplit() {
+        Assert.assertArrayEquals(new String[]{"1", "2", "3"},
+                                 StringUtil.split("1, 2, 3", ", "));
+        Assert.assertArrayEquals(new String[]{"1", "1", "1"},
+                                 StringUtil.split("1 1 1", " "));
+        Assert.assertArrayEquals(new String[]{"", "", ""},
+                                 StringUtil.split("111", "1"));
+
         Assert.assertEquals(guavaSplit("123", " "),
                             toStringList(StringUtil.split("123", " ")));
         Assert.assertEquals(guavaSplit("1 2 3", " "),
@@ -48,22 +56,41 @@
                             toStringList(StringUtil.split("1:|2|:3", "|")));
         Assert.assertEquals(guavaSplit("1\t2\t3", "\t"),
                             toStringList(StringUtil.split("1\t2\t3", "\t")));
-        Assert.assertEquals(guavaSplit("111", "1"),
-                            toStringList(StringUtil.split("111", "1")));
 
         Assert.assertThrows(IllegalArgumentException.class, () -> {
             StringUtil.split("123", "");
         });
     }
 
+    @Test
+    public void testSplitToCharsArray() {
+        Assert.assertArrayEquals(Chars.of("1", "2", "3"),
+                                 StringUtil.splitToCharsArray("1, 2, 3", ", "));
+        Assert.assertArrayEquals(Chars.of("1", "1", "1"),
+                                 StringUtil.splitToCharsArray("1 1 1", " "));
+        Assert.assertArrayEquals(Chars.of("", "", ""),
+                                 StringUtil.splitToCharsArray("111", "1"));
+
+        Assert.assertArrayEquals(new Chars[]{Chars.of("123")},
+                                 StringUtil.splitToCharsArray("123", " "));
+        Assert.assertArrayEquals(Chars.of("1", "", "2", "3"),
+                                 StringUtil.splitToCharsArray("1::2:3", ":"));
+        Assert.assertArrayEquals(Chars.of("1", "", "2", "", "3"),
+                                 StringUtil.splitToCharsArray("1::2::3", ":"));
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            StringUtil.splitToCharsArray("123", "");
+        });
+    }
+
     private static List<String> guavaSplit(String line, String delimiter) {
         return Splitter.on(delimiter).splitToList(line);
     }
 
-    private static List<String> toStringList(List<CharSequence> chars) {
-        List<String> results = new ArrayList<>(chars.size());
-        for (CharSequence seq : chars) {
-            results.add(seq.toString());
+    private static List<String> toStringList(String[] stringArray) {
+        List<String> results = new ArrayList<>(stringArray.length);
+        for (String str : stringArray) {
+            results.add(str);
         }
         return results;
     }