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 1.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 * @exception IndexOutOfBoundsException {@inheritDoc} 296 */ read(char cbuf[], int off, int len)297 public synchronized int read(char cbuf[], int off, int len) throws IOException { 298 if (!connected) { 299 throw new IOException("Pipe not connected"); 300 } else if (closedByReader) { 301 throw new IOException("Pipe closed"); 302 } else if (writeSide != null && !writeSide.isAlive() 303 && !closedByWriter && (in < 0)) { 304 throw new IOException("Write end dead"); 305 } 306 307 if ((off < 0) || (off > cbuf.length) || (len < 0) || 308 ((off + len) > cbuf.length) || ((off + len) < 0)) { 309 throw new IndexOutOfBoundsException(); 310 } else if (len == 0) { 311 return 0; 312 } 313 314 /* possibly wait on the first character */ 315 int c = read(); 316 if (c < 0) { 317 return -1; 318 } 319 cbuf[off] = (char)c; 320 int rlen = 1; 321 while ((in >= 0) && (--len > 0)) { 322 cbuf[off + rlen] = buffer[out++]; 323 rlen++; 324 if (out >= buffer.length) { 325 out = 0; 326 } 327 if (in == out) { 328 /* now empty */ 329 in = -1; 330 } 331 } 332 return rlen; 333 } 334 335 /** 336 * Tell whether this stream is ready to be read. A piped character 337 * stream is ready if the circular buffer is not empty. 338 * 339 * @exception IOException if the pipe is 340 * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>, 341 * {@link #connect(java.io.PipedWriter) unconnected}, or closed. 342 */ ready()343 public synchronized boolean ready() throws IOException { 344 if (!connected) { 345 throw new IOException("Pipe not connected"); 346 } else if (closedByReader) { 347 throw new IOException("Pipe closed"); 348 } else if (writeSide != null && !writeSide.isAlive() 349 && !closedByWriter && (in < 0)) { 350 throw new IOException("Write end dead"); 351 } 352 if (in < 0) { 353 return false; 354 } else { 355 return true; 356 } 357 } 358 359 /** 360 * Closes this piped stream and releases any system resources 361 * associated with the stream. 362 * 363 * @exception IOException if an I/O error occurs. 364 */ close()365 public void close() throws IOException { 366 in = -1; 367 closedByReader = true; 368 } 369 } 370