1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2013, 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 package java.io; 28 29 import libcore.io.IoUtils; 30 31 /** 32 * Piped character-input streams. 33 * 34 * @author Mark Reinhold 35 * @since JDK1.1 36 */ 37 38 public class PipedReader extends Reader { 39 boolean closedByWriter = false; 40 boolean closedByReader = false; 41 boolean connected = false; 42 43 /* REMIND: identification of the read and write sides needs to be 44 more sophisticated. Either using thread groups (but what about 45 pipes within a thread?) or using finalization (but it may be a 46 long time until the next GC). */ 47 Thread readSide; 48 Thread writeSide; 49 50 /** 51 * The size of the pipe's circular input buffer. 52 */ 53 private static final int DEFAULT_PIPE_SIZE = 1024; 54 55 /** 56 * The circular buffer into which incoming data is placed. 57 */ 58 char buffer[]; 59 60 /** 61 * The index of the position in the circular buffer at which the 62 * next character of data will be stored when received from the connected 63 * piped writer. <code>in<0</code> implies the buffer is empty, 64 * <code>in==out</code> implies the buffer is full 65 */ 66 int in = -1; 67 68 /** 69 * The index of the position in the circular buffer at which the next 70 * character of data will be read by this piped reader. 71 */ 72 int out = 0; 73 74 /** 75 * Creates a <code>PipedReader</code> so 76 * that it is connected to the piped writer 77 * <code>src</code>. Data written to <code>src</code> 78 * will then be available as input from this stream. 79 * 80 * @param src the stream to connect to. 81 * @exception IOException if an I/O error occurs. 82 */ PipedReader(PipedWriter src)83 public PipedReader(PipedWriter src) throws IOException { 84 this(src, DEFAULT_PIPE_SIZE); 85 } 86 87 /** 88 * Creates a <code>PipedReader</code> so that it is connected 89 * to the piped writer <code>src</code> and uses the specified 90 * pipe size for the pipe's buffer. Data written to <code>src</code> 91 * will then be available as input from this stream. 92 93 * @param src the stream to connect to. 94 * @param pipeSize the size of the pipe's buffer. 95 * @exception IOException if an I/O error occurs. 96 * @exception IllegalArgumentException if {@code pipeSize <= 0}. 97 * @since 1.6 98 */ PipedReader(PipedWriter src, int pipeSize)99 public PipedReader(PipedWriter src, int pipeSize) throws IOException { 100 initPipe(pipeSize); 101 connect(src); 102 } 103 104 105 /** 106 * Creates a <code>PipedReader</code> so 107 * that it is not yet {@linkplain #connect(java.io.PipedWriter) 108 * connected}. It must be {@linkplain java.io.PipedWriter#connect( 109 * java.io.PipedReader) connected} to a <code>PipedWriter</code> 110 * before being used. 111 */ PipedReader()112 public PipedReader() { 113 initPipe(DEFAULT_PIPE_SIZE); 114 } 115 116 /** 117 * Creates a <code>PipedReader</code> so that it is not yet 118 * {@link #connect(java.io.PipedWriter) connected} and uses 119 * the specified pipe size for the pipe's buffer. 120 * It must be {@linkplain java.io.PipedWriter#connect( 121 * java.io.PipedReader) connected} to a <code>PipedWriter</code> 122 * before being used. 123 * 124 * @param pipeSize the size of the pipe's buffer. 125 * @exception IllegalArgumentException if {@code pipeSize <= 0}. 126 * @since 1.6 127 */ PipedReader(int pipeSize)128 public PipedReader(int pipeSize) { 129 initPipe(pipeSize); 130 } 131 initPipe(int pipeSize)132 private void initPipe(int pipeSize) { 133 if (pipeSize <= 0) { 134 throw new IllegalArgumentException("Pipe size <= 0"); 135 } 136 buffer = new char[pipeSize]; 137 } 138 139 /** 140 * Causes this piped reader to be connected 141 * to the piped writer <code>src</code>. 142 * If this object is already connected to some 143 * other piped writer, an <code>IOException</code> 144 * is thrown. 145 * <p> 146 * If <code>src</code> is an 147 * unconnected piped writer and <code>snk</code> 148 * is an unconnected piped reader, they 149 * may be connected by either the call: 150 * 151 * <pre><code>snk.connect(src)</code> </pre> 152 * <p> 153 * or the call: 154 * 155 * <pre><code>src.connect(snk)</code> </pre> 156 * <p> 157 * The two calls have the same effect. 158 * 159 * @param src The piped writer to connect to. 160 * @exception IOException if an I/O error occurs. 161 */ connect(PipedWriter src)162 public void connect(PipedWriter src) throws IOException { 163 src.connect(this); 164 } 165 166 /** 167 * Receives a char of data. This method will block if no input is 168 * available. 169 */ receive(int c)170 synchronized void receive(int c) throws IOException { 171 if (!connected) { 172 throw new IOException("Pipe not connected"); 173 } else if (closedByWriter || closedByReader) { 174 throw new IOException("Pipe closed"); 175 } else if (readSide != null && !readSide.isAlive()) { 176 throw new IOException("Read end dead"); 177 } 178 179 writeSide = Thread.currentThread(); 180 while (in == out) { 181 if ((readSide != null) && !readSide.isAlive()) { 182 throw new IOException("Pipe broken"); 183 } 184 /* full: kick any waiting readers */ 185 notifyAll(); 186 try { 187 wait(1000); 188 } catch (InterruptedException ex) { 189 // Android-changed: re-set the thread's interrupt status 190 // throw new java.io.InterruptedIOException(); 191 IoUtils.throwInterruptedIoException(); 192 } 193 } 194 if (in < 0) { 195 in = 0; 196 out = 0; 197 } 198 buffer[in++] = (char) c; 199 if (in >= buffer.length) { 200 in = 0; 201 } 202 } 203 204 /** 205 * Receives data into an array of characters. This method will 206 * block until some input is available. 207 */ receive(char c[], int off, int len)208 synchronized void receive(char c[], int off, int len) throws IOException { 209 while (--len >= 0) { 210 receive(c[off++]); 211 } 212 } 213 214 /** 215 * Notifies all waiting threads that the last character of data has been 216 * received. 217 */ receivedLast()218 synchronized void receivedLast() { 219 closedByWriter = true; 220 notifyAll(); 221 } 222 223 /** 224 * Reads the next character of data from this piped stream. 225 * If no character is available because the end of the stream 226 * has been reached, the value <code>-1</code> is returned. 227 * This method blocks until input data is available, the end of 228 * the stream is detected, or an exception is thrown. 229 * 230 * @return the next character of data, or <code>-1</code> if the end of the 231 * stream is reached. 232 * @exception IOException if the pipe is 233 * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>, 234 * {@link #connect(java.io.PipedWriter) unconnected}, closed, 235 * or an I/O error occurs. 236 */ read()237 public synchronized int read() throws IOException { 238 if (!connected) { 239 throw new IOException("Pipe not connected"); 240 } else if (closedByReader) { 241 throw new IOException("Pipe closed"); 242 } else if (writeSide != null && !writeSide.isAlive() 243 && !closedByWriter && (in < 0)) { 244 throw new IOException("Write end dead"); 245 } 246 247 readSide = Thread.currentThread(); 248 int trials = 2; 249 while (in < 0) { 250 if (closedByWriter) { 251 /* closed by writer, return EOF */ 252 return -1; 253 } 254 if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) { 255 throw new IOException("Pipe broken"); 256 } 257 /* might be a writer waiting */ 258 notifyAll(); 259 try { 260 wait(1000); 261 } catch (InterruptedException ex) { 262 // Android-changed: re-set the thread's interrupt status 263 // throw new java.io.InterruptedIOException(); 264 IoUtils.throwInterruptedIoException(); 265 } 266 } 267 int ret = buffer[out++]; 268 if (out >= buffer.length) { 269 out = 0; 270 } 271 if (in == out) { 272 /* now empty */ 273 in = -1; 274 } 275 return ret; 276 } 277 278 /** 279 * Reads up to <code>len</code> characters of data from this piped 280 * stream into an array of characters. Less than <code>len</code> characters 281 * will be read if the end of the data stream is reached or if 282 * <code>len</code> exceeds the pipe's buffer size. This method 283 * blocks until at least one character of input is available. 284 * 285 * @param cbuf the buffer into which the data is read. 286 * @param off the start offset of the data. 287 * @param len the maximum number of characters read. 288 * @return the total number of characters read into the buffer, or 289 * <code>-1</code> if there is no more data because the end of 290 * the stream has been reached. 291 * @exception IOException if the pipe is 292 * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>, 293 * {@link #connect(java.io.PipedWriter) unconnected}, closed, 294 * or an I/O error occurs. 295 */ read(char cbuf[], int off, int len)296 public synchronized int read(char cbuf[], int off, int len) throws IOException { 297 if (!connected) { 298 throw new IOException("Pipe not connected"); 299 } else if (closedByReader) { 300 throw new IOException("Pipe closed"); 301 } else if (writeSide != null && !writeSide.isAlive() 302 && !closedByWriter && (in < 0)) { 303 throw new IOException("Write end dead"); 304 } 305 306 if ((off < 0) || (off > cbuf.length) || (len < 0) || 307 ((off + len) > cbuf.length) || ((off + len) < 0)) { 308 throw new IndexOutOfBoundsException(); 309 } else if (len == 0) { 310 return 0; 311 } 312 313 /* possibly wait on the first character */ 314 int c = read(); 315 if (c < 0) { 316 return -1; 317 } 318 cbuf[off] = (char)c; 319 int rlen = 1; 320 while ((in >= 0) && (--len > 0)) { 321 cbuf[off + rlen] = buffer[out++]; 322 rlen++; 323 if (out >= buffer.length) { 324 out = 0; 325 } 326 if (in == out) { 327 /* now empty */ 328 in = -1; 329 } 330 } 331 return rlen; 332 } 333 334 /** 335 * Tell whether this stream is ready to be read. A piped character 336 * stream is ready if the circular buffer is not empty. 337 * 338 * @exception IOException if the pipe is 339 * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>, 340 * {@link #connect(java.io.PipedWriter) unconnected}, or closed. 341 */ ready()342 public synchronized boolean ready() throws IOException { 343 if (!connected) { 344 throw new IOException("Pipe not connected"); 345 } else if (closedByReader) { 346 throw new IOException("Pipe closed"); 347 } else if (writeSide != null && !writeSide.isAlive() 348 && !closedByWriter && (in < 0)) { 349 throw new IOException("Write end dead"); 350 } 351 if (in < 0) { 352 return false; 353 } else { 354 return true; 355 } 356 } 357 358 /** 359 * Closes this piped stream and releases any system resources 360 * associated with the stream. 361 * 362 * @exception IOException if an I/O error occurs. 363 */ close()364 public void close() throws IOException { 365 in = -1; 366 closedByReader = true; 367 } 368 } 369