Export a number of APIs.
[SquirrelJME.git] / modules / io / src / main / java / net / multiphasicapps / io / DynamicHistoryInputStream.java
blob2cf32d79ba5803e6f443a0a3be4a5456b60b1e82
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 net.multiphasicapps.io;
12 import cc.squirreljme.runtime.cldc.annotation.Exported;
13 import cc.squirreljme.runtime.cldc.debug.Debugging;
14 import java.io.IOException;
15 import java.io.InputStream;
17 /**
18 * This is an input stream which allows any future data in the stream to be
19 * cached for later actual reading. This class should be used in situations
20 * where it is needed to read future bytes in the stream and react to those
21 * bytes.
23 * This class is not thread safe.
25 * @since 2016/07/19
27 @Exported
28 public class DynamicHistoryInputStream
29 extends InputStream
31 /** The backing buffer. */
32 protected final ByteDeque buffer;
34 /** The source input stream. */
35 protected final InputStream input;
37 /** Closed? */
38 private volatile boolean _closed;
40 /** EOF reached? */
41 private volatile boolean _eof;
43 /**
44 * Initializes a dynamic history stream which sources data from the given
45 * input stream.
47 * @param __is The stream to read data from.
48 * @throws NullPointerException On null arguments.
49 * @since 2016/07/19
51 @Exported
52 public DynamicHistoryInputStream(InputStream __is)
53 throws NullPointerException
55 // Check
56 if (__is == null)
57 throw new NullPointerException("NARG");
59 // Set
60 this.input = __is;
61 this.buffer = new ByteDeque();
64 /**
65 * {@inheritDoc}
66 * @since 2016/07/19
68 @Override
69 public void close()
70 throws IOException
72 // Close
73 this._closed = true;
75 // Clear the buffer since it is not needed
76 this.buffer.clear();
78 // Close wrapped stream
79 this.input.close();
82 /**
83 * Grabs the specified number of bytes and loads them into an internal
84 * queue where they may then be obtained using another method.
86 * @param __i The number of bytes to read ahead and buffer.
87 * @return The number of bytes which are available for input, this may
88 * be less than or greater than the input parameter.
89 * @throws IndexOutOfBoundsException If the count is negative.
90 * @throws IOException On read errors.
91 * @since 2016/07/19
93 @Exported
94 public int grab(int __i)
95 throws IndexOutOfBoundsException, IOException
97 // {@squirreljme.error BD0m A negative number of bytes cannot be
98 // grabbed. (The number of bytes to grab)}
99 if (__i < 0)
100 throw new IndexOutOfBoundsException(String.format("BD0m %s", __i));
102 // Lock
103 ByteDeque buffer = this.buffer;
105 // {@squirreljme.error BD0n Cannot grab bytes because the stream
106 // is closed.}
107 if (this._closed)
108 throw new IOException("BD0n");
110 // Already have this number of bytes grabbed
111 int cursize = buffer.available();
112 if (__i <= cursize)
113 return cursize;
115 // The number of bytes that need to be read
116 int diff = __i - cursize;
118 // Read them from the input
119 byte[] qq = new byte[diff];
120 int total = 0;
121 while (total < diff)
123 // Read in bytes
124 int rc = this.input.read(qq);
126 // If EOF was read, stop
127 if (rc < 0)
128 break;
130 // Add them to the end of the buffer
131 buffer.addLast(qq, 0, rc);
133 // The number of available bytes is the current and the read
134 // count
135 total += rc;
138 // Read total
139 return cursize + total;
143 * Reads a single byte that is ahead of the current read position.
145 * @param __a The position of the byte ahead of the current read position
146 * to read.
147 * @return The read value or a negative value if the byte to be read
148 * exceeds the end of the stream.
149 * @throws IndexOutOfBoundsException If the requested read ahead position
150 * is negative.
151 * @throws IOException On read errors.
152 * @since 2016/07/19
154 @Exported
155 public int peek(int __a)
156 throws IndexOutOfBoundsException, IOException
158 // {@squirreljme.error BD0o Cannot a peek byte which have already been
159 // read. (The requested index)}
160 if (__a < 0)
161 throw new IndexOutOfBoundsException(String.format("BD0o %d", __a));
163 // Lock
164 ByteDeque buffer = this.buffer;
166 // {@squirreljme.error BD0p Cannot peek a single byte because the
167 // stream is closed.}
168 if (this._closed)
169 throw new IOException("BD0p");
171 // Grab bytes, stop if none are available
172 int avail = this.grab(__a + 1);
173 if (avail < __a)
174 return -1;
176 throw Debugging.todo();
180 * Reads multiple bytes which are ahead of the current read position.
182 * @param __a The start position of the bytes ahead of the current read
183 * position.
184 * @param __b The array which receives the bytes being read.
185 * @return The number of bytes read or a negative value if there are no
186 * bytes to be read because they exceed the end of the stream.
187 * @throws IndexOutOfBoundsException If the requested read ahead position
188 * is negative.
189 * @throws IOException On read errors.
190 * @throws NullPointerException On null arguments.
191 * @since 2016/07/19
193 @Exported
194 public int peek(int __a, byte[] __b)
195 throws IndexOutOfBoundsException, IOException, NullPointerException
197 return this.peek(__a, __b, 0, __b.length);
201 * Reads multiple bytes which are ahead of the current read position.
203 * @param __a The start position of the bytes ahead of the current read
204 * position.
205 * @param __b The array which receives the bytes being read.
206 * @param __o The starting offset into the array to write into.
207 * @param __l The number of bytes to read.
208 * @return The number of bytes read or a negative value if there are no
209 * bytes to be read because they exceed the end of the stream.
210 * @throws IndexOutOfBoundsException If the requested read ahead position
211 * is negative; or the offset and or length exceed the array size.
212 * @throws IOException On read errors.
213 * @throws NullPointerException On null arguments.
214 * @since 2016/07/19
216 @Exported
217 public int peek(int __a, byte[] __b, int __o, int __l)
218 throws IndexOutOfBoundsException, IOException, NullPointerException
220 // {@squirreljme.error BD0q Cannot peek bytes which have already been
221 // read. (The requested index)}
222 if (__a < 0)
223 throw new IndexOutOfBoundsException(String.format("BD0q %d", __a));
225 // Check
226 if (__b == null)
227 throw new NullPointerException("NARG");
228 int n = __b.length;
229 if (__o < 0 || __l < 0 || (__o + __l) > n)
230 throw new IndexOutOfBoundsException("IOOB");
232 // Lock
233 ByteDeque buffer = this.buffer;
235 // {@squirreljme.error BD0r Cannot peek multiple bytes because
236 // the stream is closed.}
237 if (this._closed)
238 throw new IOException("BD0r");
240 // Grab bytes, stop if none are available
241 int avail = this.grab(__a + __l);
242 if (avail < __a)
243 return -1;
245 // Not reading anything?
246 int rc = Math.min(__l, avail);
247 if (rc < 0)
248 return 0;
250 // Read from the buffer
251 buffer.get(__a, __b, __o, rc);
252 return rc;
256 * {@inheritDoc}
257 * @since 2016/07/19
259 @Override
260 public int read()
261 throws IOException
263 // {@squirreljme.error BD0s Cannot read a single byte because the
264 // stream has been closed.}
265 if (this._closed)
266 throw new IOException("BD0s");
268 // Grab a single byte
269 int gc = this.grab(1);
271 // Nothing left
272 if (gc <= 0)
273 return -1;
275 // Read single byte
276 return (this.buffer.removeFirst() & 0xFF);
280 * {@inheritDoc}
281 * @since 2016/07/19
283 @Override
284 public int read(byte[] __b, int __o, int __l)
285 throws IndexOutOfBoundsException, IOException, NullPointerException
287 // Check
288 if (__b == null)
289 throw new NullPointerException("NARG");
290 int n = __b.length;
291 if (__o < 0 || __l < 0 || (__o + __l) > n)
292 throw new IndexOutOfBoundsException("IOOB");
294 // Lock
295 ByteDeque buffer = this.buffer;
297 // {@squirreljme BI08 Cannot read multiple bytes because the
298 // stream is closed.}
299 if (this._closed)
300 throw new IOException("BI08");
302 // Grab multiple bytes
303 int gc = this.grab(__l);
305 // Nothing left?
306 if (gc <= 0)
307 return -1;
309 // No bytes to read?
310 int dc = Math.min(gc, __l);
311 if (dc <= 0)
312 return 0;
314 // Remove the early bytes
315 buffer.removeFirst(__b, __o, dc);
316 return dc;