String: Improve performance of String.getBytes.

Add special cases for UTF-8, ISO-8859-1 and US-ASCII.

Benchmarks show a 3x to 30x improvement across the board.

BEFORE :
--------------
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=EMPTY}}
        runtime(ns): min=3675.38, 1st qu.=3708.42, median=3765.87, mean=3784.90, 3rd qu.=3889.08, max=3897.30
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_16}}
        runtime(ns): min=11766.89, 1st qu.=11804.59, median=12039.35, mean=12016.62, 3rd qu.=12182.33, max=12421.93
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_64}}
        runtime(ns): min=17947.28, 1st qu.=18181.18, median=18474.02, mean=18444.80, 3rd qu.=18733.02, max=18904.24
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_256}}
        runtime(ns): min=54922.50, 1st qu.=55640.28, median=56511.91, mean=56687.14, 3rd qu.=58096.55, max=58555.82
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_512}}
        runtime(ns): min=93966.35, 1st qu.=96132.46, median=96508.71, mean=96668.19, 3rd qu.=97831.84, max=98228.46

      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=EMPTY}}
        runtime(ns): min=3804.60, 1st qu.=3832.21, median=3870.29, mean=3955.06, 3rd qu.=3998.85, max=4513.34
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_16}}
        runtime(ns): min=12325.98, 1st qu.=12489.95, median=12592.85, mean=12617.96, 3rd qu.=12760.47, max=13013.03
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_64}}
        runtime(ns): min=18154.88, 1st qu.=18353.76, median=18518.34, mean=18540.77, 3rd qu.=18748.34, max=18811.98
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_256}}
        runtime(ns): min=41027.14, 1st qu.=41446.54, median=41877.28, mean=41905.38, 3rd qu.=42363.32, max=42933.43
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_512}}
        runtime(ns): min=91035.64, 1st qu.=92336.02, median=94013.21, mean=93905.50, 3rd qu.=95455.29, max=96344.39

      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=EMPTY}}
        runtime(ns): min=3781.58, 1st qu.=3795.26, median=3839.86, mean=3845.25, 3rd qu.=3866.07, max=3969.57
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_16}}
        runtime(ns): min=12044.17, 1st qu.=12090.35, median=12179.87, mean=12195.30, 3rd qu.=12298.93, max=12376.89
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_64}}
        runtime(ns): min=18972.04, 1st qu.=19136.75, median=19347.64, mean=19409.64, 3rd qu.=19709.62, max=19952.67
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_256}}
        runtime(ns): min=47600.52, 1st qu.=48461.23, median=50291.26, mean=50161.80, 3rd qu.=51811.29, max=52993.30
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_512}}
        runtime(ns): min=75600.36, 1st qu.=75968.53, median=78173.55, mean=78727.87, 3rd qu.=81136.79, max=84758.54

AFTER :
------------
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=EMPTY}}
        runtime(ns): min=1104.34, 1st qu.=1131.09, median=1150.68, mean=1154.42, 3rd qu.=1168.01, max=1243.59
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_16}}
        runtime(ns): min=1137.97, 1st qu.=1151.18, median=1177.42, mean=1172.80, 3rd qu.=1195.89, max=1199.96
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_64}}
        runtime(ns): min=1522.89, 1st qu.=1544.52, median=1571.69, mean=1612.86, 3rd qu.=1690.00, max=1708.94
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_256}}
        runtime(ns): min=2458.72, 1st qu.=2559.36, median=3023.47, mean=2962.93, 3rd qu.=3282.42, max=3426.12
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesAscii, vm=default, parameters={string=L_512}}
        runtime(ns): min=3785.74, 1st qu.=3874.94, median=4552.37, mean=4681.23, 3rd qu.=5574.72, max=6245.85

      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=EMPTY}}
        runtime(ns): min=1137.79, 1st qu.=1149.34, median=1171.17, mean=1166.55, 3rd qu.=1180.61, max=1194.83
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_16}}
        runtime(ns): min=1049.41, 1st qu.=1086.79, median=1134.18, mean=1146.32, 3rd qu.=1172.61, max=1357.04
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_64}}
        runtime(ns): min=1193.05, 1st qu.=1451.91, median=1542.92, mean=1527.42, 3rd qu.=1643.31, max=1695.34
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_256}}
        runtime(ns): min=2515.17, 1st qu.=2940.09, median=3143.91, mean=3114.54, 3rd qu.=3321.73, max=3444.45
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesIso88591, vm=default, parameters={string=L_512}}
        runtime(ns): min=3881.31, 1st qu.=3970.16, median=4328.92, mean=4291.21, 3rd qu.=4582.91, max=4885.02

      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=EMPTY}}
        runtime(ns): min=955.51, 1st qu.=972.96, median=979.11, mean=977.32, 3rd qu.=983.49, max=988.33
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_16}}
        runtime(ns): min=1229.63, 1st qu.=1242.28, median=1251.33, mean=1254.39, 3rd qu.=1268.16, max=1272.79
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_64}}
        runtime(ns): min=1768.46, 1st qu.=1816.73, median=1854.81, mean=1883.12, 3rd qu.=1975.01, max=1996.59
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_256}}
        runtime(ns): min=8710.40, 1st qu.=9348.30, median=9750.93, mean=9935.78, 3rd qu.=10569.36, max=11556.14
      Experiment {instrument=runtime, benchmarkMethod=timeGetBytesUtf8, vm=default, parameters={string=L_512}}
        runtime(ns): min=14126.17, 1st qu.=14228.94, median=15590.25, mean=15130.62, 3rd qu.=15826.37, max=15944.59

bug: 28189091
Change-Id: Ic477f917f07d61c9524cb8b572a50f00a2a2ae63
diff --git a/benchmarks/src/benchmarks/regression/StringToBytesBenchmark.java b/benchmarks/src/benchmarks/regression/StringToBytesBenchmark.java
new file mode 100644
index 0000000..8b2222472
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/StringToBytesBenchmark.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package benchmarks.regression;
+
+import com.google.caliper.Param;
+
+import java.nio.charset.StandardCharsets;
+
+public class StringToBytesBenchmark {
+    static enum StringLengths {
+        EMPTY(""),
+        L_16(makeString(16)),
+        L_64(makeString(64)),
+        L_256(makeString(256)),
+        L_512(makeString(512));
+
+        private final String value;
+
+        private StringLengths(String s) {
+            this.value = s;
+        }
+    }
+
+    private static final String makeString(int length) {
+        char[] chars = new char[length];
+        for (int i = 0; i < length; ++i) {
+            chars[i] = (char) i;
+        }
+        return new String(chars);
+    }
+
+    @Param StringLengths string;
+
+    public void timeGetBytesUtf8(int nreps) {
+        for (int i = 0; i < nreps; ++i) {
+            string.value.getBytes(StandardCharsets.UTF_8);
+        }
+    }
+
+    public void timeGetBytesIso88591(int nreps) {
+        for (int i = 0; i < nreps; ++i) {
+            string.value.getBytes(StandardCharsets.ISO_8859_1);
+        }
+    }
+
+    public void timeGetBytesAscii(int nreps) {
+        for (int i = 0; i < nreps; ++i) {
+            string.value.getBytes(StandardCharsets.US_ASCII);
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/lang/String.java b/ojluni/src/main/java/java/lang/String.java
index 0e66ad7..81f7156 100755
--- a/ojluni/src/main/java/java/lang/String.java
+++ b/ojluni/src/main/java/java/lang/String.java
@@ -37,6 +37,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
+
+import libcore.util.CharsetUtils;
 import libcore.util.EmptyArray;
 
 /**
@@ -857,7 +859,21 @@
      * @since  1.6
      */
     public byte[] getBytes(Charset charset) {
-        if (charset == null) throw new NullPointerException();
+        if (charset == null) {
+            throw new NullPointerException("charset == null");
+        }
+
+        final String name = charset.name();
+        if ("UTF-8".equals(name)) {
+            return CharsetUtils.toUtf8Bytes(this, 0, count);
+        } else if ("ISO-8859-1".equals(name)) {
+            return CharsetUtils.toIsoLatin1Bytes(this, 0, count);
+        } else if ("US-ASCII".equals(name)) {
+            return CharsetUtils.toAsciiBytes(this, 0, count);
+        } else if ("UTF-16BE".equals(name)) {
+            return CharsetUtils.toBigEndianUtf16Bytes(this, 0, count);
+        }
+
         return StringCoding.encode(charset, this);
     }