Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/nio/cs/StreamDecoder.java
38918 views
/*1* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 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 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*/2728package sun.nio.cs;2930import java.io.*;31import java.nio.*;32import java.nio.channels.*;33import java.nio.charset.*;3435public class StreamDecoder extends Reader36{3738private static final int MIN_BYTE_BUFFER_SIZE = 32;39private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;4041private volatile boolean isOpen = true;4243private void ensureOpen() throws IOException {44if (!isOpen)45throw new IOException("Stream closed");46}4748// In order to handle surrogates properly we must never try to produce49// fewer than two characters at a time. If we're only asked to return one50// character then the other is saved here to be returned later.51//52private boolean haveLeftoverChar = false;53private char leftoverChar;545556// Factories for java.io.InputStreamReader5758public static StreamDecoder forInputStreamReader(InputStream in,59Object lock,60String charsetName)61throws UnsupportedEncodingException62{63String csn = charsetName;64if (csn == null)65csn = Charset.defaultCharset().name();66try {67if (Charset.isSupported(csn))68return new StreamDecoder(in, lock, Charset.forName(csn));69} catch (IllegalCharsetNameException x) { }70throw new UnsupportedEncodingException (csn);71}7273public static StreamDecoder forInputStreamReader(InputStream in,74Object lock,75Charset cs)76{77return new StreamDecoder(in, lock, cs);78}7980public static StreamDecoder forInputStreamReader(InputStream in,81Object lock,82CharsetDecoder dec)83{84return new StreamDecoder(in, lock, dec);85}868788// Factory for java.nio.channels.Channels.newReader8990public static StreamDecoder forDecoder(ReadableByteChannel ch,91CharsetDecoder dec,92int minBufferCap)93{94return new StreamDecoder(ch, dec, minBufferCap);95}969798// -- Public methods corresponding to those in InputStreamReader --99100// All synchronization and state/argument checking is done in these public101// methods; the concrete stream-decoder subclasses defined below need not102// do any such checking.103104public String getEncoding() {105if (isOpen())106return encodingName();107return null;108}109110public int read() throws IOException {111return read0();112}113114@SuppressWarnings("fallthrough")115private int read0() throws IOException {116synchronized (lock) {117118// Return the leftover char, if there is one119if (haveLeftoverChar) {120haveLeftoverChar = false;121return leftoverChar;122}123124// Convert more bytes125char cb[] = new char[2];126int n = read(cb, 0, 2);127switch (n) {128case -1:129return -1;130case 2:131leftoverChar = cb[1];132haveLeftoverChar = true;133// FALL THROUGH134case 1:135return cb[0];136default:137assert false : n;138return -1;139}140}141}142143public int read(char cbuf[], int offset, int length) throws IOException {144int off = offset;145int len = length;146synchronized (lock) {147ensureOpen();148if ((off < 0) || (off > cbuf.length) || (len < 0) ||149((off + len) > cbuf.length) || ((off + len) < 0)) {150throw new IndexOutOfBoundsException();151}152if (len == 0)153return 0;154155int n = 0;156157if (haveLeftoverChar) {158// Copy the leftover char into the buffer159cbuf[off] = leftoverChar;160off++; len--;161haveLeftoverChar = false;162n = 1;163if ((len == 0) || !implReady())164// Return now if this is all we can produce w/o blocking165return n;166}167168if (len == 1) {169// Treat single-character array reads just like read()170int c = read0();171if (c == -1)172return (n == 0) ? -1 : n;173cbuf[off] = (char)c;174return n + 1;175}176177return n + implRead(cbuf, off, off + len);178}179}180181public boolean ready() throws IOException {182synchronized (lock) {183ensureOpen();184return haveLeftoverChar || implReady();185}186}187188public void close() throws IOException {189synchronized (lock) {190if (!isOpen)191return;192implClose();193isOpen = false;194}195}196197private boolean isOpen() {198return isOpen;199}200201202// -- Charset-based stream decoder impl --203204// In the early stages of the build we haven't yet built the NIO native205// code, so guard against that by catching the first UnsatisfiedLinkError206// and setting this flag so that later attempts fail quickly.207//208private static volatile boolean channelsAvailable = true;209210private static FileChannel getChannel(FileInputStream in) {211if (!channelsAvailable)212return null;213try {214return in.getChannel();215} catch (UnsatisfiedLinkError x) {216channelsAvailable = false;217return null;218}219}220221private Charset cs;222private CharsetDecoder decoder;223private ByteBuffer bb;224225// Exactly one of these is non-null226private InputStream in;227private ReadableByteChannel ch;228229StreamDecoder(InputStream in, Object lock, Charset cs) {230this(in, lock,231cs.newDecoder()232.onMalformedInput(CodingErrorAction.REPLACE)233.onUnmappableCharacter(CodingErrorAction.REPLACE));234}235236StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {237super(lock);238this.cs = dec.charset();239this.decoder = dec;240241// This path disabled until direct buffers are faster242if (false && in instanceof FileInputStream) {243ch = getChannel((FileInputStream)in);244if (ch != null)245bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);246}247if (ch == null) {248this.in = in;249this.ch = null;250bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);251}252bb.flip(); // So that bb is initially empty253}254255StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) {256this.in = null;257this.ch = ch;258this.decoder = dec;259this.cs = dec.charset();260this.bb = ByteBuffer.allocate(mbc < 0261? DEFAULT_BYTE_BUFFER_SIZE262: (mbc < MIN_BYTE_BUFFER_SIZE263? MIN_BYTE_BUFFER_SIZE264: mbc));265bb.flip();266}267268private int readBytes() throws IOException {269bb.compact();270try {271if (ch != null) {272// Read from the channel273int n = ch.read(bb);274if (n < 0)275return n;276} else {277// Read from the input stream, and then update the buffer278int lim = bb.limit();279int pos = bb.position();280assert (pos <= lim);281int rem = (pos <= lim ? lim - pos : 0);282assert rem > 0;283int n = in.read(bb.array(), bb.arrayOffset() + pos, rem);284if (n < 0)285return n;286if (n == 0)287throw new IOException("Underlying input stream returned zero bytes");288assert (n <= rem) : "n = " + n + ", rem = " + rem;289bb.position(pos + n);290}291} finally {292// Flip even when an IOException is thrown,293// otherwise the stream will stutter294bb.flip();295}296297int rem = bb.remaining();298assert (rem != 0) : rem;299return rem;300}301302int implRead(char[] cbuf, int off, int end) throws IOException {303304// In order to handle surrogate pairs, this method requires that305// the invoker attempt to read at least two characters. Saving the306// extra character, if any, at a higher level is easier than trying307// to deal with it here.308assert (end - off > 1);309310CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);311if (cb.position() != 0)312// Ensure that cb[0] == cbuf[off]313cb = cb.slice();314315boolean eof = false;316for (;;) {317CoderResult cr = decoder.decode(bb, cb, eof);318if (cr.isUnderflow()) {319if (eof)320break;321if (!cb.hasRemaining())322break;323if ((cb.position() > 0) && !inReady())324break; // Block at most once325int n = readBytes();326if (n < 0) {327eof = true;328if ((cb.position() == 0) && (!bb.hasRemaining()))329break;330decoder.reset();331}332continue;333}334if (cr.isOverflow()) {335assert cb.position() > 0;336break;337}338cr.throwException();339}340341if (eof) {342// ## Need to flush decoder343decoder.reset();344}345346if (cb.position() == 0) {347if (eof)348return -1;349assert false;350}351return cb.position();352}353354String encodingName() {355return ((cs instanceof HistoricallyNamedCharset)356? ((HistoricallyNamedCharset)cs).historicalName()357: cs.name());358}359360private boolean inReady() {361try {362return (((in != null) && (in.available() > 0))363|| (ch instanceof FileChannel)); // ## RBC.available()?364} catch (IOException x) {365return false;366}367}368369boolean implReady() {370return bb.hasRemaining() || inReady();371}372373void implClose() throws IOException {374if (ch != null)375ch.close();376else377in.close();378}379380}381382383