1 /* 2 * Copyright (c) 2010, 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 /* @test 25 * @summary Stochastic test of charset-based streams 26 */ 27 28 import java.io.*; 29 import java.util.*; 30 import java.nio.*; 31 import java.nio.channels.*; 32 import java.nio.charset.*; 33 34 35 public class BashStreams { 36 37 static final PrintStream log = System.err; 38 39 40 static class CharacterGenerator { 41 42 private final Random rand; 43 private final int max; 44 private final int limit; 45 private int count = 0; 46 47 CharacterGenerator(long seed, String csn, int limit) { 48 rand = new Random(seed); 49 this.max = Character.MAX_CODE_POINT + 1; 50 this.limit = limit; 51 } 52 53 private char[] saved = new char[10]; 54 private int savedCount = 0; 55 56 void push(char c) { 57 saved[savedCount++] = c; 58 count--; 59 } 60 61 int count() { 62 return count; 63 } 64 65 boolean hasNext() { 66 return count < limit; 67 } 68 69 char next() { 70 if (count >= limit) 71 throw new RuntimeException("EOF"); 72 if (savedCount > 0) { 73 savedCount--; 74 count++; 75 return saved[savedCount]; 76 } 77 int c; 78 for (;;) { 79 c = rand.nextInt(max); 80 if ((Character.isBmpCodePoint(c) 81 && (Character.isSurrogate((char) c) 82 || (c == 0xfffe) || (c == 0xffff)))) 83 continue; 84 if (Character.isSupplementaryCodePoint(c) 85 && (count == limit - 1)) 86 continue; 87 break; 88 } 89 count++; 90 if (Character.isSupplementaryCodePoint(c)) { 91 count++; 92 push(Character.lowSurrogate(c)); 93 return Character.highSurrogate(c); 94 } 95 return (char)c; 96 } 97 98 } 99 100 101 static void mismatch(String csn, int count, char c, char d) { 102 throw new RuntimeException(csn + ": Mismatch at count " 103 + count 104 + ": " + Integer.toHexString(c) 105 + " != " 106 + Integer.toHexString(d)); 107 } 108 109 static void mismatchedEOF(String csn, int count, int cgCount) { 110 throw new RuntimeException(csn + ": Mismatched EOFs: " 111 + count 112 + " != " 113 + cgCount); 114 } 115 116 117 static class Sink // One abomination... 118 extends OutputStream 119 implements WritableByteChannel 120 { 121 122 private final String csn; 123 private final CharacterGenerator cg; 124 private int count = 0; 125 126 Sink(String csn, long seed) { 127 this.csn = csn; 128 this.cg = new CharacterGenerator(seed, csn, Integer.MAX_VALUE); 129 } 130 131 public void write(int b) throws IOException { 132 write (new byte[] { (byte)b }, 0, 1); 133 } 134 135 private int check(byte[] ba, int off, int len) throws IOException { 136 String s = new String(ba, off, len, csn); 137 int n = s.length(); 138 for (int i = 0; i < n; i++) { 139 char c = s.charAt(i); 140 char d = cg.next(); 141 if (c != d) { 142 if (c == '?') { 143 if (Character.isHighSurrogate(d)) 144 cg.next(); 145 continue; 146 } 147 mismatch(csn, count + i, c, d); 148 } 149 } 150 count += n; 151 return len; 152 } 153 154 public void write(byte[] ba, int off, int len) throws IOException { 155 check(ba, off, len); 156 } 157 158 public int write(ByteBuffer bb) throws IOException { 159 int n = check(bb.array(), 160 bb.arrayOffset() + bb.position(), 161 bb.remaining()); 162 bb.position(bb.position() + n); 163 return n; 164 } 165 166 public void close() { 167 count = -1; 168 } 169 170 public boolean isOpen() { 171 return count >= 0; 172 } 173 174 } 175 176 static void testWrite(String csn, int limit, long seed, Writer w) 177 throws IOException 178 { 179 Random rand = new Random(seed); 180 CharacterGenerator cg = new CharacterGenerator(seed, csn, 181 Integer.MAX_VALUE); 182 int count = 0; 183 char[] ca = new char[16384]; 184 185 int n = 0; 186 while (count < limit) { 187 n = rand.nextInt(ca.length); 188 for (int i = 0; i < n; i++) 189 ca[i] = cg.next(); 190 w.write(ca, 0, n); 191 count += n; 192 } 193 if (Character.isHighSurrogate(ca[n - 1])) 194 w.write(cg.next()); 195 w.close(); 196 } 197 198 static void testStreamWrite(String csn, int limit, long seed) 199 throws IOException 200 { 201 log.println(" write stream"); 202 testWrite(csn, limit, seed, 203 new OutputStreamWriter(new Sink(csn, seed), csn)); 204 } 205 206 static void testChannelWrite(String csn, int limit, long seed) 207 throws IOException 208 { 209 log.println(" write channel"); 210 testWrite(csn, limit, seed, 211 Channels.newWriter(new Sink(csn, seed), 212 Charset.forName(csn) 213 .newEncoder() 214 .onMalformedInput(CodingErrorAction.REPLACE) 215 .onUnmappableCharacter(CodingErrorAction.REPLACE), 216 8192)); 217 } 218 219 220 static class Source // ... and another 221 extends InputStream 222 implements ReadableByteChannel 223 { 224 225 private final String csn; 226 private final CharsetEncoder enc; 227 private final CharacterGenerator cg; 228 private int count = 0; 229 230 Source(String csn, long seed, int limit) { 231 this.csn = csn.startsWith("\1") ? csn.substring(1) : csn; 232 this.enc = Charset.forName(this.csn).newEncoder() 233 .onMalformedInput(CodingErrorAction.REPLACE) 234 .onUnmappableCharacter(CodingErrorAction.REPLACE); 235 this.cg = new CharacterGenerator(seed, csn, limit); 236 } 237 238 public int read() throws IOException { 239 byte[] b = new byte[1]; 240 read(b); 241 return b[0]; 242 } 243 244 private CharBuffer cb = CharBuffer.allocate(8192); 245 private ByteBuffer bb = null; 246 247 public int read(byte[] ba, int off, int len) throws IOException { 248 if (!cg.hasNext()) 249 return -1; 250 int end = off + len; 251 int i = off; 252 while (i < end) { 253 if ((bb == null) || !bb.hasRemaining()) { 254 cb.clear(); 255 while (cb.hasRemaining()) { 256 if (!cg.hasNext()) 257 break; 258 char c = cg.next(); 259 if (Character.isHighSurrogate(c) 260 && cb.remaining() == 1) { 261 cg.push(c); 262 break; 263 } 264 cb.put(c); 265 } 266 cb.flip(); 267 if (!cb.hasRemaining()) 268 break; 269 bb = enc.encode(cb); 270 } 271 int d = Math.min(bb.remaining(), end - i); 272 bb.get(ba, i, d); 273 i += d; 274 } 275 return i - off; 276 } 277 278 public int read(ByteBuffer bb) throws IOException { 279 int n = read(bb.array(), 280 bb.arrayOffset() + bb.position(), 281 bb.remaining()); 282 if (n >= 0) 283 bb.position(bb.position() + n); 284 return n; 285 } 286 287 public void close() { 288 count = -1; 289 } 290 291 public boolean isOpen() { 292 return count != -1; 293 } 294 295 } 296 297 static void testRead(String csn, int limit, long seed, int max, 298 Reader rd) 299 throws IOException 300 { 301 Random rand = new Random(seed); 302 CharacterGenerator cg = new CharacterGenerator(seed, csn, limit); 303 int count = 0; 304 char[] ca = new char[16384]; 305 306 int n = 0; 307 while (count < limit) { 308 int rn = rand.nextInt(ca.length); 309 n = rd.read(ca, 0, rn); 310 if (n < 0) 311 break; 312 for (int i = 0; i < n; i++) { 313 char c = ca[i]; 314 if (!cg.hasNext()) 315 mismatchedEOF(csn, count + i, cg.count()); 316 char d = cg.next(); 317 if (c == '?') { 318 if (Character.isHighSurrogate(d)) { 319 cg.next(); 320 continue; 321 } 322 if (d > max) 323 continue; 324 } 325 if (c != d) 326 mismatch(csn, count + i, c, d); 327 } 328 count += n; 329 } 330 if (cg.hasNext()) 331 mismatchedEOF(csn, count, cg.count()); 332 rd.close(); 333 } 334 335 static void testStreamRead(String csn, int limit, long seed, int max) 336 throws IOException 337 { 338 log.println(" read stream"); 339 testRead(csn, limit, seed, max, 340 new InputStreamReader(new Source(csn, seed, limit), csn)); 341 } 342 343 static void testChannelRead(String csn, int limit, long seed, int max) 344 throws IOException 345 { 346 log.println(" read channel"); 347 testRead(csn, limit, seed, max, 348 Channels.newReader(new Source(csn, seed, limit), csn)); 349 } 350 351 352 static final int LIMIT = 1 << 21; 353 354 static void test(String csn, int limit, long seed, int max) 355 throws Exception 356 { 357 log.println(); 358 log.println(csn); 359 360 testStreamWrite(csn, limit, seed); 361 testChannelWrite(csn, limit, seed); 362 testStreamRead(csn, limit, seed, max); 363 testChannelRead(csn, limit, seed, max); 364 } 365 366 public static void main(String[] args) throws Exception { 367 368 if (System.getProperty("os.arch").equals("ia64")) { 369 // This test requires 54 minutes on an Itanium-1 processor 370 return; 371 } 372 373 int ai = 0, an = args.length; 374 375 int limit; 376 if (ai < an) 377 limit = 1 << Integer.parseInt(args[ai++]); 378 else 379 limit = LIMIT; 380 log.println("limit = " + limit); 381 382 long seed; 383 if (ai < an) 384 seed = Long.parseLong(args[ai++]); 385 else 386 seed = System.currentTimeMillis(); 387 log.println("seed = " + seed); 388 389 test("UTF-8", limit, seed, Integer.MAX_VALUE); 390 test("US-ASCII", limit, seed, 0x7f); 391 test("ISO-8859-1", limit, seed, 0xff); 392 393 } 394 395 }