1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2019, BELLSOFT. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  * @test
  27  * @requires os.arch=="aarch64"
  28  * @summary String::compareTo implementation uses different algorithms for
  29  *          different string length. This test creates various strings of
  30  *          specified size, which are different at all possible index values and
  31  *          compares them. Expecting separately calculated result to be returned.
  32  *          String size is specified via commandline. Various size values can
  33  *          be specified during intrinsic development in order to test cases
  34  *          specific for new or modified intrinsic implementation. Aarch64
  35  *          implementation has 1, 4, 8 -characters loops for length < 72 and
  36  *          16, 32, 64 -characters loops for string length >= 72. Code is also
  37  *          affected by SoftwarePrefetchHintDistance flag value.
  38  *          Test class can also accept "-fullmode" parameter
  39  *          with maxLength paramter after it. Then it will iterate through all
  40  *          string length values up to maxLength parameter (inclusive). It takes
  41  *          a lot of time but is useful for development.
  42  * @run main/othervm -XX:SoftwarePrefetchHintDistance=192 compiler.intrinsics.string.TestStringCompareToSameLength 2 5 10 13 17 20 25 71 72 73 88 90 192 193 208 209
  43  * @run main/othervm -XX:SoftwarePrefetchHintDistance=16 compiler.intrinsics.string.TestStringCompareToSameLength 2 5 10 13 17 20 25 71 72 73 88 90
  44  * @run main/othervm -XX:SoftwarePrefetchHintDistance=-1 compiler.intrinsics.string.TestStringCompareToSameLength 2 5 10 13 17 20 25 71 72 73 88 90
  45  */
  46 
  47 package compiler.intrinsics.string;
  48 
  49 public class TestStringCompareToSameLength {
  50     private final int size;
  51 
  52     public static void main(String args[]) {
  53         if (args.length == 0) {
  54             throw new IllegalArgumentException("Usage: $testClass $testLength1"
  55                     + " [$testLength2 [...]] | -fullmode $maxLength");
  56         }
  57         if (args.length == 2 && "-fullmode".equals(args[0])) {
  58             int maxLength = Integer.parseInt(args[1]);
  59             for (int length = 1; length <= maxLength; length++) {
  60                 TestStringCompareToSameLength test = new TestStringCompareToSameLength(length);
  61                 for (int mismatchIdx = 0; mismatchIdx <= length; mismatchIdx++) {
  62                     test.testCompareTo(mismatchIdx);
  63                 }
  64             }
  65         } else {
  66             for (String arg : args) {
  67                 int size = Integer.parseInt(arg);
  68                 TestStringCompareToSameLength test = new TestStringCompareToSameLength(size);
  69                 for (int mismatchIdx = 0; mismatchIdx <= size; mismatchIdx++) {
  70                     test.testCompareTo(mismatchIdx);
  71                 }
  72             }
  73         }
  74     }
  75 
  76     private TestStringCompareToSameLength(int size) {
  77         this.size = size;
  78     }
  79 
  80     private void testCompareTo(int mismatchIdx) {
  81         // Create Latin1 strings: latin1, latin2, which are different at index.
  82         // Case of index == size is a case of equal strings
  83         char latinSrc[] = new char[size];
  84         // generate ASCII string
  85         for (int i = 0; i < size; i++) {
  86             latinSrc[i] = (char) ('a' + (i % 26));
  87         }
  88         String latinStr1 = new String(latinSrc);
  89         if (mismatchIdx != size) latinSrc[mismatchIdx] = (char) ('a' - 1);
  90         String latinStr2 = new String(latinSrc);
  91 
  92         // Create 3 utf strings: utfStr1, utfStr2: same as latinStr1, but has UTF-16 character
  93         // utfStr1 and utfStr2 are different at requested index and character value is greater
  94         // than same index character in latinStr1.
  95         // utfStr3 is different at requested index and character value is less than same
  96         // index character in latinStr1. Will be a Latin1-encoded string in case difference
  97         // is requested at last character. This case not applicable and is skipped below.
  98         char cArray[] = latinStr1.toCharArray();
  99         cArray[cArray.length - 1] = '\uBEEF'; // at least last character is UTF-16
 100         if (mismatchIdx != size) cArray[mismatchIdx] = '\u1234';
 101         String utfStr1 = new String(cArray);
 102         if (mismatchIdx != size) cArray[mismatchIdx] = '\u5678';
 103         String utfStr2 = new String(cArray);
 104         if (mismatchIdx != size) cArray[mismatchIdx] = (char) ('a' - 2); // less than Latin1 index position
 105         // utfStr3 will be Latin1 if last character differ. Will skip this case
 106         String utfStr3 = new String(cArray);
 107 
 108         for (int i = 0; i < 10000; i++) {
 109             checkCase(mismatchIdx, latinStr1, latinStr2, "LL"); // compare Latin1 with Latin1
 110 
 111             checkCase(mismatchIdx, utfStr1, utfStr2, "UU"); // compare UTF-16 vs UTF-16
 112 
 113             if (size != mismatchIdx) { // UTF-16 and Latin1 strings can't be equal. Then skip this case
 114                 // compare UTF16 string, which is expected to be > than Latin1
 115                 checkCase(mismatchIdx, latinStr1, utfStr1, "U(large)L");
 116                 if (mismatchIdx != size - 1) {
 117                     // compare UTF16 string, which is expected to be < than Latin1
 118                     checkCase(mismatchIdx,  latinStr1, utfStr3, "U(small)L");
 119                 }
 120             }
 121         }
 122     }
 123 
 124     private void checkCase(int mismatchIdx, String str1, String str2, String caseName) {
 125         int expected;
 126         if (mismatchIdx != size) {
 127             expected = str1.charAt(mismatchIdx) - str2.charAt(mismatchIdx);
 128         } else {
 129             expected = str1.length() - str2.length();
 130         }
 131         int result = str1.compareTo(str2);
 132         int reversedResult = str2.compareTo(str1);
 133         if (expected != result || result != -reversedResult) {
 134             throw new AssertionError(String.format("%s CASE FAILED: size = %d, "
 135                     + "mismatchIdx = %d, expected = %d, but got result = %d, "
 136                     + "reversedResult = %d for string1 = '%s', string2 = '%s'",
 137                     caseName, size, mismatchIdx, expected, result,
 138                     reversedResult, str1, str2));
 139         }
 140     }
 141 }
 142