1/*
2 * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 6896617
27 * @summary Optimize sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() with SSE instructions on x86
28 * @library /test/lib
29 * @modules java.base/jdk.internal.misc
30 *          java.base/sun.nio.cs
31 *          java.management
32 *
33 * @run main/othervm/timeout=1200 -Xbatch -Xmx256m compiler.codegen.Test6896617
34 */
35
36package compiler.codegen;
37
38import jdk.test.lib.Utils;
39
40import java.nio.ByteBuffer;
41import java.nio.CharBuffer;
42import java.nio.charset.Charset;
43import java.nio.charset.CharsetDecoder;
44import java.nio.charset.CharsetEncoder;
45import java.nio.charset.CodingErrorAction;
46import java.util.Arrays;
47import java.util.Random;
48
49public class Test6896617 {
50    final static int SIZE = 256;
51
52    public static void main(String[] args) {
53        String csn = "ISO-8859-1";
54        Charset cs = Charset.forName(csn);
55        CharsetEncoder enc = cs.newEncoder();
56        enc.onMalformedInput(CodingErrorAction.REPLACE)
57           .onUnmappableCharacter(CodingErrorAction.REPLACE);
58        CharsetDecoder dec = cs.newDecoder();
59        dec.onMalformedInput(CodingErrorAction.REPLACE)
60           .onUnmappableCharacter(CodingErrorAction.REPLACE);
61
62        byte repl = (byte)'?';
63        enc.replaceWith(new byte[] { repl });
64
65        // Use internal API for tests.
66        sun.nio.cs.ArrayEncoder arrenc = (sun.nio.cs.ArrayEncoder)enc;
67        sun.nio.cs.ArrayDecoder arrdec = (sun.nio.cs.ArrayDecoder)dec;
68
69        // Populate char[] with chars which can be encoded by ISO_8859_1 (<= 0xFF)
70        Random rnd = Utils.getRandomInstance();
71        int maxchar = 0xFF;
72        char[] a = new char[SIZE];
73        byte[] b = new byte[SIZE];
74        char[] at = new char[SIZE];
75        byte[] bt = new byte[SIZE];
76        for (int i = 0; i < SIZE; i++) {
77            char c = (char) rnd.nextInt(maxchar);
78            if (!enc.canEncode(c)) {
79                System.out.printf("Something wrong: can't encode c=%03x\n", (int)c);
80                System.exit(97);
81            }
82            a[i] = c;
83            b[i] = (byte)c;
84            at[i] = (char)-1;
85            bt[i] = (byte)-1;
86        }
87        if (arrenc.encode(a, 0, SIZE, bt) != SIZE || !Arrays.equals(b, bt)) {
88            System.out.println("Something wrong: ArrayEncoder.encode failed");
89            System.exit(97);
90        }
91        if (arrdec.decode(b, 0, SIZE, at) != SIZE || !Arrays.equals(a, at)) {
92            System.out.println("Something wrong: ArrayDecoder.decode failed");
93            System.exit(97);
94        }
95        for (int i = 0; i < SIZE; i++) {
96            at[i] = (char)-1;
97            bt[i] = (byte)-1;
98        }
99
100        ByteBuffer bb  = ByteBuffer.wrap(b);
101        CharBuffer ba  = CharBuffer.wrap(a);
102        ByteBuffer bbt = ByteBuffer.wrap(bt);
103        CharBuffer bat = CharBuffer.wrap(at);
104        if (!enc.encode(ba, bbt, true).isUnderflow() || !Arrays.equals(b, bt)) {
105            System.out.println("Something wrong: Encoder.encode failed");
106            System.exit(97);
107        }
108        if (!dec.decode(bb, bat, true).isUnderflow() || !Arrays.equals(a, at)) {
109            System.out.println("Something wrong: Decoder.decode failed");
110            System.exit(97);
111        }
112        for (int i = 0; i < SIZE; i++) {
113            at[i] = (char)-1;
114            bt[i] = (byte)-1;
115        }
116
117        // Warm up
118        boolean failed = false;
119        int result = 0;
120        for (int i = 0; i < 10000; i++) {
121            result += arrenc.encode(a, 0, SIZE, bt);
122            result -= arrdec.decode(b, 0, SIZE, at);
123        }
124        for (int i = 0; i < 10000; i++) {
125            result += arrenc.encode(a, 0, SIZE, bt);
126            result -= arrdec.decode(b, 0, SIZE, at);
127        }
128        for (int i = 0; i < 10000; i++) {
129            result += arrenc.encode(a, 0, SIZE, bt);
130            result -= arrdec.decode(b, 0, SIZE, at);
131        }
132        if (result != 0 || !Arrays.equals(b, bt) || !Arrays.equals(a, at)) {
133            failed = true;
134            System.out.println("Failed: ArrayEncoder.encode char[" + SIZE + "] and ArrayDecoder.decode byte[" + SIZE + "]");
135        }
136        for (int i = 0; i < SIZE; i++) {
137            at[i] = (char)-1;
138            bt[i] = (byte)-1;
139        }
140
141        boolean is_underflow = true;
142        for (int i = 0; i < 10000; i++) {
143            ba.clear(); bb.clear(); bat.clear(); bbt.clear();
144            boolean enc_res = enc.encode(ba, bbt, true).isUnderflow();
145            boolean dec_res = dec.decode(bb, bat, true).isUnderflow();
146            is_underflow = is_underflow && enc_res && dec_res;
147        }
148        for (int i = 0; i < SIZE; i++) {
149            at[i] = (char)-1;
150            bt[i] = (byte)-1;
151        }
152        for (int i = 0; i < 10000; i++) {
153            ba.clear(); bb.clear(); bat.clear(); bbt.clear();
154            boolean enc_res = enc.encode(ba, bbt, true).isUnderflow();
155            boolean dec_res = dec.decode(bb, bat, true).isUnderflow();
156            is_underflow = is_underflow && enc_res && dec_res;
157        }
158        for (int i = 0; i < SIZE; i++) {
159            at[i] = (char)-1;
160            bt[i] = (byte)-1;
161        }
162        for (int i = 0; i < 10000; i++) {
163            ba.clear(); bb.clear(); bat.clear(); bbt.clear();
164            boolean enc_res = enc.encode(ba, bbt, true).isUnderflow();
165            boolean dec_res = dec.decode(bb, bat, true).isUnderflow();
166            is_underflow = is_underflow && enc_res && dec_res;
167        }
168        if (!is_underflow || !Arrays.equals(b, bt) || !Arrays.equals(a, at)) {
169            failed = true;
170            System.out.println("Failed: Encoder.encode char[" + SIZE + "] and Decoder.decode byte[" + SIZE + "]");
171        }
172
173        // Test encoder with different source and destination sizes
174        System.out.println("Testing different source and destination sizes");
175        for (int i = 1; i <= SIZE; i++) {
176            for (int j = 1; j <= SIZE; j++) {
177                bt = new byte[j];
178                // very source's SIZE
179                result = arrenc.encode(a, 0, i, bt);
180                int l = Math.min(i, j);
181                if (result != l) {
182                    failed = true;
183                    System.out.println("Failed: encode char[" + i + "] to byte[" + j + "]: result = " + result + ", expected " + l);
184                }
185                for (int k = 0; k < l; k++) {
186                    if (bt[k] != b[k]) {
187                        failed = true;
188                        System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]);
189                    }
190                }
191                // very source's offset
192                int sz = SIZE - i + 1;
193                result = arrenc.encode(a, i-1, sz, bt);
194                l = Math.min(sz, j);
195                if (result != l) {
196                    failed = true;
197                    System.out.println("Failed: encode char[" + sz + "] to byte[" + j + "]: result = " + result + ", expected " + l);
198                }
199                for (int k = 0; k < l; k++) {
200                    if (bt[k] != b[i+k-1]) {
201                        failed = true;
202                        System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[i+k-1]);
203                    }
204                }
205            }
206        }
207
208        // Test encoder with char > 0xFF
209        System.out.println("Testing big char");
210
211        byte orig = (byte)'A';
212        bt = new byte[SIZE];
213        for (int i = 1; i <= SIZE; i++) {
214            for (int j = 0; j < i; j++) {
215                a[j] += 0x100;
216                // make sure to replace a different byte
217                bt[j] = orig;
218                result = arrenc.encode(a, 0, i, bt);
219                if (result != i) {
220                    failed = true;
221                    System.out.println("Failed: encode char[" + i + "] to byte[" + i + "]: result = " + result + ", expected " + i);
222                }
223                if (bt[j] != repl) {
224                    failed = true;
225                    System.out.println("Failed: encoded replace byte[" + j + "] (" + bt[j] + ") != " + repl);
226                }
227                bt[j] = b[j]; // Restore to compare whole array
228                for (int k = 0; k < i; k++) {
229                    if (bt[k] != b[k]) {
230                        failed = true;
231                        System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]);
232                    }
233                }
234                a[j] -= 0x100; // Restore
235            }
236        }
237
238        // Test sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() performance.
239
240        int itrs = Integer.getInteger("iterations", 1000000);
241        int size = Integer.getInteger("size", 256);
242        a  = new char[size];
243        b  = new byte[size];
244        bt = new byte[size];
245        for (int i = 0; i < size; i++) {
246            char c = (char) rnd.nextInt(maxchar);
247            if (!enc.canEncode(c)) {
248                System.out.printf("Something wrong: can't encode c=%03x\n", (int)c);
249                System.exit(97);
250            }
251            a[i] = c;
252            b[i]  = (byte)-1;
253            bt[i] = (byte)c;
254        }
255        ba = CharBuffer.wrap(a);
256        bb = ByteBuffer.wrap(b);
257        boolean enc_res = enc.encode(ba, bb, true).isUnderflow();
258        if (!enc_res || !Arrays.equals(b, bt)) {
259            failed = true;
260            System.out.println("Failed 1: Encoder.encode char[" + size + "]");
261        }
262        for (int i = 0; i < size; i++) {
263            b[i] = (byte)-1;
264        }
265
266        // Make sure to recompile method if needed before performance run.
267        for (int i = 0; i < 10000; i++) {
268            ba.clear(); bb.clear();
269            enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow();
270        }
271        for (int i = 0; i < size; i++) {
272            b[i] = (byte)-1;
273        }
274        for (int i = 0; i < 10000; i++) {
275            ba.clear(); bb.clear();
276            enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow();
277        }
278        if (!enc_res || !Arrays.equals(b, bt)) {
279            failed = true;
280            System.out.println("Failed 2: Encoder.encode char[" + size + "]");
281        }
282        for (int i = 0; i < size; i++) {
283            b[i] = (byte)-1;
284        }
285
286        System.out.println("Testing ISO_8859_1$Encode.encodeArrayLoop() performance");
287        long start = System.currentTimeMillis();
288        for (int i = 0; i < itrs; i++) {
289            ba.clear(); bb.clear();
290            enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow();
291        }
292        long end = System.currentTimeMillis();
293        if (!enc_res || !Arrays.equals(b, bt)) {
294            failed = true;
295            System.out.println("Failed 3: Encoder.encode char[" + size + "]");
296        } else {
297            System.out.println("size: " + size + " time: " + (end - start));
298        }
299
300        // Test sun.nio.cs.ISO_8859_1$Encode.encode() performance.
301
302        // Make sure to recompile method if needed before performance run.
303        result = 0;
304        for (int i = 0; i < size; i++) {
305            b[i] = (byte)-1;
306        }
307        for (int i = 0; i < 10000; i++) {
308            result += arrenc.encode(a, 0, size, b);
309        }
310        for (int i = 0; i < size; i++) {
311            b[i] = (byte)-1;
312        }
313        for (int i = 0; i < 10000; i++) {
314            result += arrenc.encode(a, 0, size, b);
315        }
316        if (result != size*20000 || !Arrays.equals(b, bt)) {
317            failed = true;
318            System.out.println("Failed 1: ArrayEncoder.encode char[" + SIZE + "]");
319        }
320        for (int i = 0; i < size; i++) {
321            b[i] = (byte)-1;
322        }
323
324        System.out.println("Testing ISO_8859_1$Encode.encode() performance");
325        result = 0;
326        start = System.currentTimeMillis();
327        for (int i = 0; i < itrs; i++) {
328            result += arrenc.encode(a, 0, size, b);
329        }
330        end = System.currentTimeMillis();
331        if (!Arrays.equals(b, bt)) {
332            failed = true;
333            System.out.println("Failed 2: ArrayEncoder.encode char[" + size + "]");
334        } else {
335            System.out.println("size: " + size + " time: " + (end - start));
336        }
337
338        if (failed) {
339          System.out.println("FAILED");
340          System.exit(97);
341        }
342        System.out.println("PASSED");
343    }
344}
345