Cherry pick the banglets and such from wip-l1summercoat, this will be the basis for...
[SquirrelJME.git] / buildSrc / src / main / java / cc / squirreljme / plugin / multivm / VMTestOutputBuffer.java
blob595aa44657df55ad34641d397efd182766dae34a
1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
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;
18 /**
19 * Used to read the buffer while also printing lines as the output streams
20 * are written to.
22 * @since 2020/09/07
24 public class VMTestOutputBuffer
25 implements Runnable
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;
46 /**
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.
53 * @since 2020/09/07
55 public VMTestOutputBuffer(InputStream __in, OutputStream __out,
56 boolean __unbuffered)
57 throws NullPointerException
59 if (__in == null)
60 throw new NullPointerException("NARG");
62 this.in = __in;
63 this.out = __out;
64 this.unbuffered = __unbuffered;
67 /**
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.
73 * @since 2020/09/07
75 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
76 public byte[] getBytes(Thread __thread)
77 throws NullPointerException
79 if (__thread == null)
80 throw new NullPointerException("NARG");
82 // Wait for the thread to be terminated
83 while (__thread.isAlive())
84 try
86 __thread.join();
88 catch (InterruptedException ignored)
92 // Return the complete buffer output
93 ByteArrayOutputStream complete = this.complete;
94 synchronized (complete)
96 return complete.toByteArray();
101 * {@inheritDoc}
102 * @since 2020/09/07
104 @Override
105 public void run()
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();
116 // Read in
117 int rc = in.read(buf);
119 // EOF?
120 if (rc < 0)
121 break;
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)
130 break;
133 // Is a failed read
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.
150 * @since 2020/09/07
152 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
153 private void __push(byte[] __b, int __o, int __l)
154 throws IndexOutOfBoundsException, IOException, NullPointerException
156 if (__b == null)
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?
162 if (__l == 0)
163 return;
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;
172 if (!unbuffered)
173 for (int at = __o, end = __o + __l; at < end; at++)
174 if (__b[at] == '\n')
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));
179 if (endingNewline)
180 break;
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
190 return;
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
198 if (!unbuffered)
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
209 if (unbuffered)
211 this.out.write(__b, __o, __l);
212 this.out.flush();
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
222 current.reset();