1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package cc
.squirreljme
.plugin
.multivm
;
12 import java
.io
.ByteArrayOutputStream
;
13 import java
.io
.IOException
;
14 import java
.io
.InputStream
;
15 import java
.io
.InterruptedIOException
;
16 import java
.io
.OutputStream
;
19 * Used to read the buffer while also printing lines as the output streams
24 public class VMTestOutputBuffer
27 /** The stream to read from. */
28 protected final InputStream in
;
30 /** The complete output buffer with all byte data. */
31 @SuppressWarnings("resource")
32 protected final ByteArrayOutputStream complete
=
33 new ByteArrayOutputStream();
35 /** The current line of output. */
36 @SuppressWarnings("resource")
37 protected final ByteArrayOutputStream current
=
38 new ByteArrayOutputStream();
40 /** The output stream to write to. */
41 protected final OutputStream out
;
43 /** Should the output not be buffered? */
44 protected final boolean unbuffered
;
47 * Initializes the output buffer.
49 * @param __in The input stream to read from.
50 * @param __out The stream to write partial data to.
51 * @param __unbuffered Should the output not be buffered?
52 * @throws NullPointerException On null arguments.
55 public VMTestOutputBuffer(InputStream __in
, OutputStream __out
,
57 throws NullPointerException
60 throw new NullPointerException("NARG");
64 this.unbuffered
= __unbuffered
;
68 * Returns bytes that were read from the queue.
70 * @param __thread The associated thread.
71 * @return All the read bytes.
72 * @throws NullPointerException On null arguments.
75 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
76 public byte[] getBytes(Thread __thread
)
77 throws NullPointerException
80 throw new NullPointerException("NARG");
82 // Wait for the thread to be terminated
83 while (__thread
.isAlive())
88 catch (InterruptedException ignored
)
92 // Return the complete buffer output
93 ByteArrayOutputStream complete
= this.complete
;
94 synchronized (complete
)
96 return complete
.toByteArray();
107 // Constant reading of buffers
108 InputStream in
= this.in
;
109 for (byte[] buf
= new byte[32768];;)
112 // Check if we got interrupted before we read from the buffer
113 if (Thread
.currentThread().isInterrupted())
114 throw new InterruptedException();
117 int rc
= in
.read(buf
);
123 // Push the buffer bytes forward
124 this.__push(buf
, 0, rc
);
127 // Thread was interrupted, so probably want to stop doing this
128 catch (InterruptedIOException
|InterruptedException ignored
)
134 catch (IOException e
)
136 throw new RuntimeException("Stream read error.", e
);
141 * Pushes bytes to the output.
143 * @param __b The buffer.
144 * @param __o The offset.
145 * @param __l The length.
146 * @throws IndexOutOfBoundsException If the offset and/or length exceed
147 * the array bounds or are negative.
148 * @throws IOException On read/write errors.
149 * @throws NullPointerException On null arguments.
152 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
153 private void __push(byte[] __b
, int __o
, int __l
)
154 throws IndexOutOfBoundsException
, IOException
, NullPointerException
157 throw new NullPointerException("NARG");
158 if (__o
< 0 || __l
< 0 || (__o
+ __l
) < 0 || (__o
+ __l
) > __b
.length
)
159 throw new IndexOutOfBoundsException("IOOB");
161 // Nothing being written?
165 // This override is used for standard error so that any output here
166 // is done directly without considering the current output at all
167 boolean unbuffered
= this.unbuffered
;
169 // Split the buffer at newlines, but if we are doing unbuffered data
170 // we just push it out as fast as we possibly can
171 boolean endingNewline
= false;
173 for (int at
= __o
, end
= __o
+ __l
; at
< end
; at
++)
176 // If this ends in a newline, then we will be outputting
177 // the newline and flushing whatever we put in
178 endingNewline
= (at
== (end
- 1));
182 // Push the first chunk of bytes
183 this.__push(__b
, __o
, (at
+ 1) - __o
);
185 // Then the second chunk of bytes at the trailing end, this
186 // may result in more splits
187 this.__push(__b
, at
+ 1, end
- (at
+ 1));
189 // Do not process anything
193 // Current and complete output buffers
194 ByteArrayOutputStream complete
= this.complete
;
195 ByteArrayOutputStream current
= this.current
;
197 // Add to the current buffer, ignored when buffered
199 current
.write(__b
, __o
, __l
);
201 // Then to the complete buffer for later
202 synchronized (complete
)
204 complete
.write(__b
, __o
, __l
);
207 // If not buffered write out output as fast as we possibly can do so
208 // Do a flush so our output is always sent through
211 this.out
.write(__b
, __o
, __l
);
215 // If we ended in a newline, flush out the write buffer
216 else if (endingNewline
)
218 // Send all the bytes out
219 this.out
.write(current
.toByteArray());
221 // Discard everything in the buffer