1/*
2 * Copyright (C) 2011 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.base;
18
19import com.google.caliper.BeforeExperiment;
20import com.google.caliper.Benchmark;
21import com.google.caliper.Param;
22import com.google.common.base.Strings;
23
24/**
25 * Microbenchmark for {@link Strings#repeat}
26 *
27 * @author Mike Cripps
28 */
29public class StringsRepeatBenchmark {
30  @Param({"1", "5", "25", "125"}) int count;
31  @Param({"1", "10"}) int length;
32
33  private String originalString;
34
35  @BeforeExperiment void setUp() {
36    originalString = Strings.repeat("x", length);
37  }
38
39  @Benchmark void oldRepeat(int reps) {
40    for (int i = 0; i < reps; i++) {
41      String x = oldRepeat(originalString, count);
42      if (x.length() != (originalString.length() * count)) {
43        throw new RuntimeException("Wrong length: "+x);
44      }
45    }
46  }
47  @Benchmark void mikeRepeat(int reps) {
48    for (int i = 0; i < reps; i++) {
49      String x = mikeRepeat(originalString, count);
50      if (x.length() != (originalString.length() * count)) {
51        throw new RuntimeException("Wrong length: "+x);
52      }
53    }
54  }
55  @Benchmark void martinRepeat(int reps) {
56    for (int i = 0; i < reps; i++) {
57      String x = martinRepeat(originalString, count);
58      if (x.length() != (originalString.length() * count)) {
59        throw new RuntimeException("Wrong length: "+x);
60      }
61    }
62  }
63
64  private static String mikeRepeat(String string, int count) {
65    final int len = string.length();
66    char[] strCopy = new char[len * Integer.highestOneBit(count)];
67    string.getChars(0, len, strCopy, 0);
68
69    char[] array = new char[len * count];
70
71    int strCopyLen = len;
72    int pos = 0;
73    while (count != 0) {
74      if ((count & 1) != 0) {
75        System.arraycopy(strCopy, 0, array, pos,strCopyLen);
76        pos += strCopyLen;
77      }
78      count >>= 1;
79      if (count != 0) {
80        System.arraycopy(strCopy, 0, strCopy, strCopyLen, strCopyLen);
81        strCopyLen <<= 1;
82      }
83    }
84    return new String(array);
85  }
86
87  private static String oldRepeat(String string, int count) {
88    // If this multiplication overflows, a NegativeArraySizeException or
89    // OutOfMemoryError is not far behind
90    final int len = string.length();
91    final int size = len * count;
92    char[] array = new char[size];
93    for (int i = 0; i < size; i+=len) {
94      string.getChars(0, len, array, i);
95    }
96    return new String(array);
97  }
98
99  private static String martinRepeat(String string, int count) {
100    final int len = string.length();
101    final int size = len * count;
102    final char[] array = new char[size];
103    string.getChars(0, len, array, 0);
104    int n;
105    for (n = len; n < size - n; n <<= 1) {
106      System.arraycopy(array, 0, array, n, n);
107    }
108    System.arraycopy(array, 0, array, n, size - n);
109    return new String(array);
110  }
111}
112