1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2001, 2011, Oracle and/or its affiliates. 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. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 /* 28 */ 29 30 package sun.nio.cs; 31 32 import java.io.*; 33 import java.nio.*; 34 import java.nio.channels.*; 35 import java.nio.charset.*; 36 37 public class StreamDecoder extends Reader 38 { 39 40 private static final int MIN_BYTE_BUFFER_SIZE = 32; 41 private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; 42 43 private volatile boolean isOpen = true; 44 ensureOpen()45 private void ensureOpen() throws IOException { 46 if (!isOpen) 47 throw new IOException("Stream closed"); 48 } 49 50 // In order to handle surrogates properly we must never try to produce 51 // fewer than two characters at a time. If we're only asked to return one 52 // character then the other is saved here to be returned later. 53 // 54 private boolean haveLeftoverChar = false; 55 private char leftoverChar; 56 57 // Android-added: Flush the CharsetDecoder correctly. 58 private boolean needsFlush = false; 59 60 // Factories for java.io.InputStreamReader 61 forInputStreamReader(InputStream in, Object lock, String charsetName)62 public static StreamDecoder forInputStreamReader(InputStream in, 63 Object lock, 64 String charsetName) 65 throws UnsupportedEncodingException 66 { 67 String csn = charsetName; 68 if (csn == null) 69 csn = Charset.defaultCharset().name(); 70 try { 71 if (Charset.isSupported(csn)) 72 return new StreamDecoder(in, lock, Charset.forName(csn)); 73 } catch (IllegalCharsetNameException x) { } 74 throw new UnsupportedEncodingException (csn); 75 } 76 forInputStreamReader(InputStream in, Object lock, Charset cs)77 public static StreamDecoder forInputStreamReader(InputStream in, 78 Object lock, 79 Charset cs) 80 { 81 return new StreamDecoder(in, lock, cs); 82 } 83 forInputStreamReader(InputStream in, Object lock, CharsetDecoder dec)84 public static StreamDecoder forInputStreamReader(InputStream in, 85 Object lock, 86 CharsetDecoder dec) 87 { 88 return new StreamDecoder(in, lock, dec); 89 } 90 91 92 // Factory for java.nio.channels.Channels.newReader 93 forDecoder(ReadableByteChannel ch, CharsetDecoder dec, int minBufferCap)94 public static StreamDecoder forDecoder(ReadableByteChannel ch, 95 CharsetDecoder dec, 96 int minBufferCap) 97 { 98 return new StreamDecoder(ch, dec, minBufferCap); 99 } 100 101 102 // -- Public methods corresponding to those in InputStreamReader -- 103 104 // All synchronization and state/argument checking is done in these public 105 // methods; the concrete stream-decoder subclasses defined below need not 106 // do any such checking. 107 getEncoding()108 public String getEncoding() { 109 if (isOpen()) 110 return encodingName(); 111 return null; 112 } 113 read()114 public int read() throws IOException { 115 return read0(); 116 } 117 118 @SuppressWarnings("fallthrough") read0()119 private int read0() throws IOException { 120 synchronized (lock) { 121 122 // Return the leftover char, if there is one 123 if (haveLeftoverChar) { 124 haveLeftoverChar = false; 125 return leftoverChar; 126 } 127 128 // Convert more bytes 129 char cb[] = new char[2]; 130 int n = read(cb, 0, 2); 131 switch (n) { 132 case -1: 133 return -1; 134 case 2: 135 leftoverChar = cb[1]; 136 haveLeftoverChar = true; 137 // FALL THROUGH 138 case 1: 139 return cb[0]; 140 default: 141 assert false : n; 142 return -1; 143 } 144 } 145 } 146 read(char cbuf[], int offset, int length)147 public int read(char cbuf[], int offset, int length) throws IOException { 148 int off = offset; 149 int len = length; 150 synchronized (lock) { 151 ensureOpen(); 152 if ((off < 0) || (off > cbuf.length) || (len < 0) || 153 ((off + len) > cbuf.length) || ((off + len) < 0)) { 154 throw new IndexOutOfBoundsException(); 155 } 156 if (len == 0) 157 return 0; 158 159 int n = 0; 160 161 if (haveLeftoverChar) { 162 // Copy the leftover char into the buffer 163 cbuf[off] = leftoverChar; 164 off++; len--; 165 haveLeftoverChar = false; 166 n = 1; 167 if ((len == 0) || !implReady()) 168 // Return now if this is all we can produce w/o blocking 169 return n; 170 } 171 172 if (len == 1) { 173 // Treat single-character array reads just like read() 174 int c = read0(); 175 if (c == -1) 176 return (n == 0) ? -1 : n; 177 cbuf[off] = (char)c; 178 return n + 1; 179 } 180 181 return n + implRead(cbuf, off, off + len); 182 } 183 } 184 ready()185 public boolean ready() throws IOException { 186 synchronized (lock) { 187 ensureOpen(); 188 return haveLeftoverChar || implReady(); 189 } 190 } 191 close()192 public void close() throws IOException { 193 synchronized (lock) { 194 if (!isOpen) 195 return; 196 implClose(); 197 isOpen = false; 198 } 199 } 200 isOpen()201 private boolean isOpen() { 202 return isOpen; 203 } 204 205 206 // -- Charset-based stream decoder impl -- 207 208 // In the early stages of the build we haven't yet built the NIO native 209 // code, so guard against that by catching the first UnsatisfiedLinkError 210 // and setting this flag so that later attempts fail quickly. 211 // 212 private static volatile boolean channelsAvailable = true; 213 getChannel(FileInputStream in)214 private static FileChannel getChannel(FileInputStream in) { 215 if (!channelsAvailable) 216 return null; 217 try { 218 return in.getChannel(); 219 } catch (UnsatisfiedLinkError x) { 220 channelsAvailable = false; 221 return null; 222 } 223 } 224 225 private Charset cs; 226 private CharsetDecoder decoder; 227 private ByteBuffer bb; 228 229 // Exactly one of these is non-null 230 private InputStream in; 231 private ReadableByteChannel ch; 232 StreamDecoder(InputStream in, Object lock, Charset cs)233 StreamDecoder(InputStream in, Object lock, Charset cs) { 234 this(in, lock, 235 cs.newDecoder() 236 .onMalformedInput(CodingErrorAction.REPLACE) 237 .onUnmappableCharacter(CodingErrorAction.REPLACE)); 238 } 239 StreamDecoder(InputStream in, Object lock, CharsetDecoder dec)240 StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) { 241 super(lock); 242 this.cs = dec.charset(); 243 this.decoder = dec; 244 245 // This path disabled until direct buffers are faster 246 if (false && in instanceof FileInputStream) { 247 ch = getChannel((FileInputStream)in); 248 if (ch != null) 249 bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE); 250 } 251 if (ch == null) { 252 this.in = in; 253 this.ch = null; 254 bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE); 255 } 256 bb.flip(); // So that bb is initially empty 257 } 258 StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc)259 StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) { 260 this.in = null; 261 this.ch = ch; 262 this.decoder = dec; 263 this.cs = dec.charset(); 264 this.bb = ByteBuffer.allocate(mbc < 0 265 ? DEFAULT_BYTE_BUFFER_SIZE 266 : (mbc < MIN_BYTE_BUFFER_SIZE 267 ? MIN_BYTE_BUFFER_SIZE 268 : mbc)); 269 bb.flip(); 270 } 271 readBytes()272 private int readBytes() throws IOException { 273 bb.compact(); 274 try { 275 if (ch != null) { 276 // Read from the channel 277 // Android-changed: Use ChannelInputStream.read which throws on non-blocking channels. 278 // Other implementations of ReadableByteChannel.read do not, and Channels.newReader 279 // is documented to throw on non-blocking. 280 // int n = ch.read(bb); 281 int n = sun.nio.ch.ChannelInputStream.read(ch, bb, true); 282 if (n < 0) 283 return n; 284 } else { 285 // Read from the input stream, and then update the buffer 286 int lim = bb.limit(); 287 int pos = bb.position(); 288 assert (pos <= lim); 289 int rem = (pos <= lim ? lim - pos : 0); 290 assert rem > 0; 291 int n = in.read(bb.array(), bb.arrayOffset() + pos, rem); 292 if (n < 0) 293 return n; 294 if (n == 0) 295 throw new IOException("Underlying input stream returned zero bytes"); 296 assert (n <= rem) : "n = " + n + ", rem = " + rem; 297 bb.position(pos + n); 298 } 299 } finally { 300 // Flip even when an IOException is thrown, 301 // otherwise the stream will stutter 302 bb.flip(); 303 } 304 305 int rem = bb.remaining(); 306 assert (rem != 0) : rem; 307 return rem; 308 } 309 implRead(char[] cbuf, int off, int end)310 int implRead(char[] cbuf, int off, int end) throws IOException { 311 312 // In order to handle surrogate pairs, this method requires that 313 // the invoker attempt to read at least two characters. Saving the 314 // extra character, if any, at a higher level is easier than trying 315 // to deal with it here. 316 assert (end - off > 1); 317 318 CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off); 319 if (cb.position() != 0) 320 // Ensure that cb[0] == cbuf[off] 321 cb = cb.slice(); 322 323 // BEGIN Android-added: Flush the CharsetDecoder properly. 324 if (needsFlush) { 325 CoderResult cr = decoder.flush(cb); 326 if (cr.isOverflow()) { 327 // We've overflowed, we'll have to come back round and ask for more data. 328 return cb.position(); 329 } 330 331 // By definition, we're at the end of the stream here. 332 if (cr.isUnderflow()) { 333 if (cb.position() == 0) { 334 return -1; 335 } 336 337 return cb.position(); 338 } 339 340 cr.throwException(); 341 // Unreachable. 342 } 343 // END Android-added: Flush the CharsetDecoder properly. 344 345 boolean eof = false; 346 for (;;) { 347 CoderResult cr = decoder.decode(bb, cb, eof); 348 if (cr.isUnderflow()) { 349 if (eof) 350 break; 351 if (!cb.hasRemaining()) 352 break; 353 if ((cb.position() > 0) && !inReady()) 354 break; // Block at most once 355 int n = readBytes(); 356 if (n < 0) { 357 eof = true; 358 // Android-removed: Flush the CharsetDecoder correctly. 359 // We want to go 'round the loop one more time with "eof = true". 360 // We also don't want to reset the decoder here because we might potentially need 361 // to flush it later. 362 // if ((cb.position() == 0) && (!bb.hasRemaining())) 363 // break; 364 // decoder.reset(); 365 } 366 continue; 367 } 368 if (cr.isOverflow()) { 369 assert cb.position() > 0; 370 break; 371 } 372 cr.throwException(); 373 } 374 375 if (eof) { 376 // BEGIN Android-changed: Flush the CharsetDecoder correctly. 377 // // ## Need to flush decoder 378 // decoder.reset(); 379 CoderResult cr = decoder.flush(cb); 380 if (cr.isOverflow()) { 381 needsFlush = true; 382 return cb.position(); 383 } 384 385 decoder.reset(); 386 if (!cr.isUnderflow()) { 387 cr.throwException(); 388 } 389 // END Android-changed: Flush the CharsetDecoder correctly. 390 } 391 392 if (cb.position() == 0) { 393 if (eof) 394 return -1; 395 assert false; 396 } 397 return cb.position(); 398 } 399 encodingName()400 String encodingName() { 401 return ((cs instanceof HistoricallyNamedCharset) 402 ? ((HistoricallyNamedCharset)cs).historicalName() 403 : cs.name()); 404 } 405 inReady()406 private boolean inReady() { 407 try { 408 return (((in != null) && (in.available() > 0)) 409 || (ch instanceof FileChannel)); // ## RBC.available()? 410 } catch (IOException x) { 411 return false; 412 } 413 } 414 implReady()415 boolean implReady() { 416 return bb.hasRemaining() || inReady(); 417 } 418 implClose()419 void implClose() throws IOException { 420 if (ch != null) 421 ch.close(); 422 else 423 in.close(); 424 } 425 426 } 427