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&lt;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