1 /*
2 * #%L
3 * nom.tam FITS library
4 * %%
5 * Copyright (C) 1996 - 2024 nom-tam-fits
6 * %%
7 * This is free and unencumbered software released into the public domain.
8 *
9 * Anyone is free to copy, modify, publish, use, compile, sell, or
10 * distribute this software, either in source code form or as a compiled
11 * binary, for any purpose, commercial or non-commercial, and by any
12 * means.
13 *
14 * In jurisdictions that recognize copyright laws, the author or authors
15 * of this software dedicate any and all copyright interest in the
16 * software to the public domain. We make this dedication for the benefit
17 * of the public at large and to the detriment of our heirs and
18 * successors. We intend this dedication to be an overt act of
19 * relinquishment in perpetuity of all present and future rights to this
20 * software under copyright law.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 * #L%
30 */
31
32 package nom.tam.util;
33
34 import java.io.EOFException;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.lang.reflect.Array;
38 import java.nio.Buffer;
39 import java.nio.ByteBuffer;
40 import java.nio.ByteOrder;
41
42 import nom.tam.fits.FitsFactory;
43 import nom.tam.util.type.ElementType;
44
45 /**
46 * Efficient base class for decoding of binary input into Java arrays (<i>primarily for internal use</i>)
47 *
48 * @author Attila Kovacs
49 *
50 * @since 1.16
51 *
52 * @see OutputEncoder
53 * @see ArrayDataFile
54 * @see ArrayInputStream
55 * @see ArrayOutputStream
56 */
57 public abstract class InputDecoder {
58
59 /** The buffer size for array translation */
60 private static final int BUFFER_SIZE = 8 * FitsFactory.FITS_BLOCK_SIZE;
61
62 /** bit mask for 1 byte */
63 private static final int BYTE_MASK = 0xFF;
64
65 /** bit mask for a 16-byte integer (a Java <code>short</code>). */
66 private static final int SHORT_MASK = 0xFFFF;
67
68 /** the input providing the binary representation of data */
69 private InputReader in;
70
71 /** the conversion buffer */
72 private InputBuffer buf;
73
74 /**
75 * Instantiates a new decoder of binary input to Java arrays. To be used by subclass constructors only.
76 *
77 * @see #setInput(InputReader)
78 */
79 protected InputDecoder() {
80 buf = new InputBuffer(BUFFER_SIZE);
81 }
82
83 /**
84 * Instantiates a new decoder for converting data representations into Java arrays.
85 *
86 * @param i the binary input.
87 */
88 public InputDecoder(InputReader i) {
89 this();
90 setInput(i);
91 }
92
93 /**
94 * Sets the input from which to read the binary output.
95 *
96 * @param i the new binary input.
97 */
98 protected void setInput(InputReader i) {
99 in = i;
100 }
101
102 /**
103 * Returns the buffer that is used for conversion, which can be used to bulk read bytes ahead from the input (see
104 * {@link InputBuffer#loadBytes(long, int)}) and {@link InputBuffer#loadOne(int)}) before doing conversions to Java
105 * types locally.
106 *
107 * @return the conversion buffer used by this decoder.
108 */
109 protected InputBuffer getInputBuffer() {
110 return buf;
111 }
112
113 /**
114 * Makes sure that an elements of the specified size is fully available in the buffer, prompting additional reading
115 * of the underlying stream as appropriate (but not beyond the limit set by {@link #loadBytes(long, int)}.
116 *
117 * @param size the number of bytes we need at once from the buffer
118 *
119 * @return <code>true</code> if the requested number of bytes are, or could be made, available.
120 * Otherwise <code>false</code>.
121 *
122 * @throws IOException if there was an underlying IO error, other than the end of file, while trying to fetch
123 * additional data from the underlying input
124 */
125 boolean makeAvailable(int size) throws IOException {
126 // TODO Once the deprecated BufferDecoder is retired, this should become
127 // a private method of InputBuffer (with buf. prefixed removed below).
128 while (buf.buffer.remaining() < size) {
129 if (!buf.fetch()) {
130 return false;
131 }
132 }
133 return true;
134 }
135
136 /**
137 * Reads one byte from the input. See the contract of {@link InputStream#read()}.
138 *
139 * @return the byte, or -1 if at the end of the file.
140 *
141 * @throws IOException if an IO error, other than the end-of-file prevented the read.
142 */
143 protected synchronized int read() throws IOException {
144 return in.read();
145 }
146
147 /**
148 * Reads bytes into an array from the input. See the contract of {@link InputStream#read(byte[], int, int)}.
149 *
150 * @param b the destination array
151 * @param start the first index in the array to be populated
152 * @param length the number of bytes to read into the array.
153 *
154 * @return the number of bytes successfully read, or -1 if at the end of the file.
155 *
156 * @throws IOException if an IO error, other than the end-of-file prevented the read.
157 */
158 protected synchronized int read(byte[] b, int start, int length) throws IOException {
159 return in.read(b, start, length);
160 }
161
162 /**
163 * Reads bytes to fill the supplied buffer with the requested number of bytes from the given starting buffer index.
164 * If not enough bytes are avaialable in the file to deliver the reqauested number of bytes the buffer, an
165 * {@link EOFException} will be thrown.
166 *
167 * @param b the buffer
168 * @param off the buffer index at which to start reading data
169 * @param len the total number of bytes to read.
170 *
171 * @throws EOFException if already at the end of file.
172 * @throws IOException if there was an IO error before the requested number of bytes could all be read.
173 */
174 protected void readFully(byte[] b, int off, int len) throws EOFException, IOException {
175 while (len > 0) {
176 int n = read(b, off, len);
177 if (n < 0) {
178 throw new EOFException();
179 }
180 off += n;
181 len -= n;
182 }
183 }
184
185 /**
186 * Based on {@link #readArray(Object)}, but guaranteeing a complete read of the supplied object or else an
187 * {@link EOFException} is thrown.
188 *
189 * @param o the array, including multi-dimensional, and heterogeneous arrays of arrays.
190 *
191 * @throws EOFException if already at the end of file.
192 * @throws IOException if there was an IO error
193 * @throws IllegalArgumentException if the argument is not a Java array, or is or contains elements that do not have
194 * supported conversions from binary representation.
195 *
196 * @see #readArray(Object)
197 * @see #readImage(Object)
198 */
199 public void readArrayFully(Object o) throws IOException, IllegalArgumentException {
200 if (readArray(o) != FitsEncoder.computeSize(o)) {
201 throw new EOFException("Incomplete array read (FITS encoding).");
202 }
203 }
204
205 /**
206 * See the contract of {@link ArrayDataInput#readLArray(Object)}.
207 *
208 * @param o an array, to be populated
209 *
210 * @return the actual number of bytes read from the input, or -1 if already at the
211 * end-of-file.
212 *
213 * @throws IllegalArgumentException if the argument is not an array or if it contains an element that is not
214 * supported for decoding.
215 * @throws IOException if there was an IO error reading from the input
216 *
217 * @see #readArrayFully(Object)
218 */
219 public abstract long readArray(Object o) throws IOException, IllegalArgumentException;
220
221 /**
222 * Like {@link #readArrayFully(Object)} but strictly for numerical types only.
223 *
224 * @param o An any-dimensional array containing only numerical types
225 *
226 * @throws IllegalArgumentException if the argument is not an array or if it contains an element that is not
227 * supported.
228 * @throws EOFException if already at the end of file.
229 * @throws IOException if there was an IO error
230 *
231 * @see #readArrayFully(Object)
232 *
233 * @since 1.18
234 */
235 public void readImage(Object o) throws IOException, IllegalArgumentException {
236 if (o == null) {
237 return;
238 }
239
240 if (!o.getClass().isArray()) {
241 throw new IllegalArgumentException("Not an array: " + o.getClass().getName());
242 }
243
244 long size = FitsEncoder.computeSize(o);
245 if (size == 0) {
246 return;
247 }
248
249 getInputBuffer().loadBytes(size, 1);
250 if (getImage(o) != size) {
251 throw new EOFException("Incomplete image read.");
252 }
253 }
254
255 private long getImage(Object o) throws IOException, IllegalArgumentException {
256 int length = Array.getLength(o);
257 if (length == 0) {
258 return 0L;
259 }
260
261 if (o instanceof byte[]) {
262 return buf.get((byte[]) o, 0, length);
263 }
264 if (o instanceof short[]) {
265 return buf.get((short[]) o, 0, length) * Short.BYTES;
266 }
267 if (o instanceof int[]) {
268 return buf.get((int[]) o, 0, length) * Integer.BYTES;
269 }
270 if (o instanceof float[]) {
271 return buf.get((float[]) o, 0, length) * Float.BYTES;
272 }
273 if (o instanceof long[]) {
274 return buf.get((long[]) o, 0, length) * Long.BYTES;
275 }
276 if (o instanceof double[]) {
277 return buf.get((double[]) o, 0, length) * Double.BYTES;
278 }
279 if (!(o instanceof Object[])) {
280 throw new IllegalArgumentException("Not a numerical image type: " + o.getClass().getName());
281 }
282
283 Object[] array = (Object[]) o;
284 long count = 0L;
285
286 // Process multidim arrays recursively.
287 for (int i = 0; i < length; i++) {
288 try {
289 count += getImage(array[i]);
290 } catch (EOFException e) {
291 return eofCheck(e, count, -1L);
292 }
293 }
294 return count;
295 }
296
297 /**
298 * Decides what to do when an {@link EOFException} is encountered after having read some number of bytes from the
299 * input. The default behavior is to re-throw the exception only if no data at all was obtained from the input,
300 * otherwise return the non-zero byte count of data that were successfully read. Subclass implementations may
301 * override this method to adjust if an when {@link EOFException} is thrown upon an incomplete read.
302 *
303 * @param e the exception that was thrown, or <code>null</code>.
304 * @param got the number of elements successfully read
305 * @param expected the number of elements expected
306 *
307 * @return the number of elements successfully read (same as <code>got</code>).
308 *
309 * @throws EOFException the rethrown exception, or a new one, as appropriate
310 */
311 long eofCheck(EOFException e, long got, long expected) throws EOFException {
312 if (got == 0) {
313 if (e == null) {
314 throw new EOFException();
315 }
316 throw e;
317 }
318 return got;
319 }
320
321 /**
322 * <p>
323 * The conversion buffer for decoding binary data representation into Java arrays (objects).
324 * </p>
325 * <p>
326 * The buffering is most efficient, if we fist specify how many bytes of input maybe be consumed first (buffered
327 * from the input), via {@link #loadBytes(long, int)}. After that, we can call the get routines of this class to
328 * return binary data converted to Java format until we exhaust the specified alotment of bytes.
329 * </p>
330 *
331 * <pre>
332 * // The data we want to retrieve
333 * double d;
334 * int i;
335 * short[] shortArray = new short[100];
336 * float[] floaTarray = new float[48];
337 *
338 * // We convert from the binary format to Java format using
339 * // the local conversion buffer
340 * ConversionBuffer buf = getBuffer();
341 *
342 * // We can allow the conversion buffer to read enough bytes for all
343 * // data we want to retrieve:
344 * buf.loadBytes(FitsIO.BYTES_IN_DOUBLE + FitsIO.BYTES_IN_INT + FitsIO.BYTES_IN_SHORT * shortArray.length
345 * + FitsIO.BYTES_IN_FLOAT * floatArray.length);
346 *
347 * // Now we can get the data with minimal underlying IO calls...
348 * d = buf.getDouble();
349 * i = buf.getInt();
350 *
351 * for (int i = 0; i < shortArray.length; i++) {
352 * shortArray[i] = buf.getShort();
353 * }
354 *
355 * for (int i = 0; i < floatArray.length; i++) {
356 * floatArray[i] = buf.getFloat();
357 * }
358 * </pre>
359 * <p>
360 * In the special case that one needs just a single element (or a few single elements) from the input, rather than
361 * lots of elements or arrays, one may use {@link #loadOne(int)} instead of {@link #loadBytes(long, int)} to read
362 * just enough bytes for a single data element from the input before each conversion. For example:
363 * </p>
364 *
365 * <pre>
366 * ConversionBuffer buf = getBuffer();
367 *
368 * buf.loadOne(FitsIO.BYTES_IN_FLOAT);
369 * float f = buf.getFloat();
370 * </pre>
371 *
372 * @author Attila Kovacs
373 */
374 protected final class InputBuffer {
375
376 /** the byte array in which to buffer data from the input */
377 private final byte[] data;
378
379 /** the buffer wrapped for NIO access */
380 private final ByteBuffer buffer;
381
382 /** The current type-specific view of the buffer or null */
383 private Buffer view;
384
385 /** the number of bytes requested, but not yet buffered */
386 private long pending = 0;
387
388 private InputBuffer(int size) {
389 data = new byte[size];
390 buffer = ByteBuffer.wrap(data);
391 }
392
393 /**
394 * Sets the byte order of the binary data representation from which we are decoding data.
395 *
396 * @param order the new byte order
397 *
398 * @see #byteOrder()
399 * @see ByteBuffer#order(ByteOrder)
400 */
401 protected void setByteOrder(ByteOrder order) {
402 buffer.order(order);
403 }
404
405 /**
406 * Returns the current byte order of the binary data representation from which we are decoding.
407 *
408 * @return the byte order
409 *
410 * @see #setByteOrder(ByteOrder)
411 * @see ByteBuffer#order()
412 */
413 protected ByteOrder byteOrder() {
414 return buffer.order();
415 }
416
417 private boolean isViewingAs(Class<? extends Buffer> type) {
418 if (view == null) {
419 return false;
420 }
421 return type.isAssignableFrom(view.getClass());
422 }
423
424 private void assertView(ElementType<?> type) {
425 if (!isViewingAs(type.bufferClass())) {
426 view = type.asTypedBuffer(buffer);
427 }
428 }
429
430 private void rewind() {
431 buffer.rewind();
432 view = null;
433 }
434
435 /**
436 * Set the number of bytes we can buffer from the input for subsequent retrieval from this buffer. The get
437 * methods of this class will be ensured not to fetch data from the input beyond the requested size.
438 *
439 * @param n the number of elements we can read and buffer from the input
440 * @param size the number of bytes in each elements.
441 */
442 protected void loadBytes(long n, int size) {
443 rewind();
444 buffer.limit(0);
445 pending = n * size;
446 }
447
448 /**
449 * Loads just a single element of the specified byte size. The element must fit into the conversion buffer, and
450 * it is up to the caller to ensure that. The method itself does not check.
451 *
452 * @param size The number of bytes in the element
453 *
454 * @return <code>true</code> if the data was successfully read from the uderlying stream or file,
455 * otherwise <code>false</code>.
456 *
457 * @throws IOException if there was an IO error, other than the end-of-file.
458 */
459 protected boolean loadOne(int size) throws IOException {
460 pending = size;
461 rewind();
462 buffer.limit(0);
463 return makeAvailable(size);
464 }
465
466 /**
467 * Reads more data into the buffer from the underlying stream, attempting to fill the buffer if possible.
468 *
469 * @return <code>true</code> if data was successfully buffered from the underlying intput or the
470 * buffer is already full. Otherwise <code>false</code>.
471 *
472 * @throws IOException if there as an IO error, other than the end of file, while trying to read more data from
473 * the underlying input into the buffer.
474 */
475 private boolean fetch() throws IOException {
476 int remaining = buffer.remaining();
477
478 if (remaining > 0) {
479 System.arraycopy(data, buffer.position(), data, 0, remaining);
480 }
481 rewind();
482
483 int n = (int) Math.min(pending, data.length - remaining);
484 n = in.read(data, remaining, n);
485 if (n < 0) {
486 return false;
487 }
488 buffer.limit(remaining + n);
489 pending -= n;
490
491 return true;
492 }
493
494 /**
495 * Retrieves a single byte from the buffer. Before data can be retrieved with this method they should be
496 * 'loaded' into the buffer via {@link #loadOne(int)} or {@link #loadBytes(long, int)}. This method is
497 * appropriate for retrieving one or a fwew bytes at a time. For bulk input of bytes, you should use
498 * {@link InputDecoder#read(byte[], int, int)} instead for superior performance.
499 *
500 * @return the byte value, or -1 if no more data is available from the buffer or the underlying
501 * input.
502 *
503 * @throws IOException if there as an IO error, other than the end of file, while trying to read more data from
504 * the underlying input into the buffer.
505 *
506 * @see #loadOne(int)
507 * @see #loadBytes(long, int)
508 */
509 protected int get() throws IOException {
510 if (makeAvailable(1)) {
511 view = null;
512 return buffer.get() & BYTE_MASK;
513 }
514 return -1;
515 }
516
517 /**
518 * Retrieves a 2-byte unsigned integer from the buffer. Before data can be retrieved with this method the should
519 * be 'loaded' into the buffer via {@link #loadOne(int)} or {@link #loadBytes(long, int)}.
520 *
521 * @return the 16-bit integer value, or -1 if no more data is available from the buffer or the
522 * underlying input.
523 *
524 * @throws IOException if there as an IO error, other than the end of file, while trying to read more data from
525 * the underlying input into the buffer.
526 *
527 * @see #loadOne(int)
528 * @see #loadBytes(long, int)
529 */
530 protected int getUnsignedShort() throws IOException {
531 if (makeAvailable(Short.BYTES)) {
532 view = null;
533 return buffer.getShort() & SHORT_MASK;
534 }
535 return -1;
536 }
537
538 /**
539 * Retrieves a 4-byte integer from the buffer. Before data can be retrieved with this method the should be
540 * 'loaded' into the buffer via {@link #loadOne(int)} or {@link #loadBytes(long, int)}.
541 *
542 * @return the 32-bit integer value.
543 *
544 * @throws EOFException if already at the end of file.
545 * @throws IOException if there as an IO error
546 *
547 * @see #loadOne(int)
548 * @see #loadBytes(long, int)
549 */
550 protected int getInt() throws EOFException, IOException {
551 if (makeAvailable(Integer.BYTES)) {
552 view = null;
553 return buffer.getInt();
554 }
555 throw new EOFException();
556 }
557
558 /**
559 * Retrieves a 8-byte integer from the buffer. Before data can be retrieved with this method the should be
560 * 'loaded' into the buffer via {@link #loadOne(int)} or {@link #loadBytes(long, int)}.
561 *
562 * @return the 64-bit integer value.
563 *
564 * @throws EOFException if already at the end of file.
565 * @throws IOException if there as an IO error
566 *
567 * @see #loadOne(int)
568 * @see #loadBytes(long, int)
569 */
570 protected long getLong() throws EOFException, IOException {
571 if (makeAvailable(Long.BYTES)) {
572 view = null;
573 return buffer.getLong();
574 }
575 throw new EOFException();
576 }
577
578 /**
579 * Retrieves a 4-byte single-precision floating point value from the buffer. Before data can be retrieved with
580 * this method the shold be 'loaded' into the buffer via {@link #loadOne(int)} or {@link #loadBytes(long, int)}.
581 *
582 * @return the 32-bit single-precision floating-point value.
583 *
584 * @throws EOFException if already at the end of file.
585 * @throws IOException if there as an IO error
586 *
587 * @see #loadOne(int)
588 * @see #loadBytes(long, int)
589 */
590 protected float getFloat() throws EOFException, IOException {
591 if (makeAvailable(Float.BYTES)) {
592 view = null;
593 return buffer.getFloat();
594 }
595 throw new EOFException();
596 }
597
598 /**
599 * Retrieves a 8-byte double-precision floating point value from the buffer. Before data can be retrieved with
600 * this method they should be 'loaded' into the buffer via {@link #loadOne(int)} or
601 * {@link #loadBytes(long, int)}.
602 *
603 * @return the 64-bit double-precision floating-point value.
604 *
605 * @throws EOFException if already at the end of file.
606 * @throws IOException if there as an IO error
607 *
608 * @see #loadOne(int)
609 * @see #loadBytes(long, int)
610 */
611 protected double getDouble() throws EOFException, IOException {
612 if (makeAvailable(Double.BYTES)) {
613 view = null;
614 return buffer.getDouble();
615 }
616 throw new EOFException();
617 }
618
619 /**
620 * Retrieves a sequence of signed bytes from the buffer. Before data can be retrieved with this method the
621 * should be 'loaded' into the buffer via {@link #loadBytes(long, int)}.
622 *
623 * @param dst Java array in which to store the retrieved sequence of elements
624 * @param from the array index for storing the first element retrieved
625 * @param n the number of elements to retrieve
626 *
627 * @return the number of elements successfully retrieved
628 *
629 * @throws EOFException if already at the end of file.
630 * @throws IOException if there was an IO error before, before requested number of bytes could be read
631 *
632 * @see #loadBytes(long, int)
633 *
634 * @since 1.18
635 */
636 protected int get(byte[] dst, int from, int n) throws EOFException, IOException {
637 if (n == 1) {
638 int i = get();
639 if (i < 0) {
640 throw new EOFException();
641 }
642 dst[from] = (byte) i;
643 return 1;
644 }
645
646 view = null;
647 int got = 0;
648
649 while (got < n) {
650 if (!makeAvailable(1)) {
651 return (int) eofCheck(null, got, n);
652 }
653 int m = Math.min(n - got, buffer.remaining());
654 buffer.get(dst, from + got, m);
655 got += m;
656 }
657
658 return got;
659 }
660
661 /**
662 * Retrieves a sequence of big-endian 16-bit signed integers from the buffer. Before data can be retrieved with
663 * this method they should be 'loaded' into the buffer via {@link #loadBytes(long, int)}.
664 *
665 * @param dst Java array in which to store the retrieved sequence of elements
666 * @param from the array index for storing the first element retrieved
667 * @param n the number of elements to retrieve
668 *
669 * @return the number of elements successfully retrieved
670 *
671 * @throws EOFException if already at the end of file.
672 * @throws IOException if there was an IO error before, before requested number of bytes could be read
673 *
674 * @see #loadBytes(long, int)
675 *
676 * @since 1.18
677 */
678 protected int get(short[] dst, int from, int n) throws EOFException, IOException {
679 if (n == 1 && !isViewingAs(ElementType.SHORT.bufferClass())) {
680 int i = getUnsignedShort();
681 if (i < 0) {
682 throw new EOFException();
683 }
684 dst[from] = (short) i;
685 return 1;
686 }
687 return get(ElementType.SHORT, dst, from, n);
688 }
689
690 /**
691 * Retrieves a sequence of big-endian 32-bit signed integers from the buffer. Before data can be retrieved with
692 * this method they should be 'loaded' into the buffer via {@link #loadBytes(long, int)}.
693 *
694 * @param dst Java array in which to store the retrieved sequence of elements
695 * @param from the array index for storing the first element retrieved
696 * @param n the number of elements to retrieve
697 *
698 * @return the number of elements successfully retrieved
699 *
700 * @throws EOFException if already at the end of file.
701 * @throws IOException if there was an IO error before, before requested number of bytes could be read
702 *
703 * @see #loadBytes(long, int)
704 *
705 * @since 1.18
706 */
707 protected int get(int[] dst, int from, int n) throws EOFException, IOException {
708 if (n == 1 && !isViewingAs(ElementType.INT.bufferClass())) {
709 dst[from] = getInt();
710 return 1;
711 }
712 return get(ElementType.INT, dst, from, n);
713 }
714
715 /**
716 * Retrieves a sequence of big-endian 64-bit signed integers from the buffer. Before data can be retrieved with
717 * this method they should be 'loaded' into the buffer via {@link #loadBytes(long, int)}.
718 *
719 * @param dst Java array in which to store the retrieved sequence of elements
720 * @param from the array index for storing the first element retrieved
721 * @param n the number of elements to retrieve
722 *
723 * @return the number of elements successfully retrieved
724 *
725 * @throws EOFException if already at the end of file.
726 * @throws IOException if there was an IO error before, before requested number of bytes could be read
727 *
728 * @see #loadBytes(long, int)
729 *
730 * @since 1.18
731 */
732 protected int get(long[] dst, int from, int n) throws EOFException, IOException {
733 if (n == 1 && !isViewingAs(ElementType.LONG.bufferClass())) {
734 dst[from] = getLong();
735 return 1;
736 }
737 return get(ElementType.LONG, dst, from, n);
738 }
739
740 /**
741 * Retrieves a sequence of big-endian 32-bit floating-point values from the buffer. Before data can be retrieved
742 * with this method they should be 'loaded' into the buffer via {@link #loadBytes(long, int)}.
743 *
744 * @param dst Java array in which to store the retrieved sequence of elements
745 * @param from the array index for storing the first element retrieved
746 * @param n the number of elements to retrieve
747 *
748 * @return the number of elements successfully retrieved
749 *
750 * @throws EOFException if already at the end of file.
751 * @throws IOException if there was an IO error before, before requested number of bytes could be read
752 *
753 * @see #loadBytes(long, int)
754 *
755 * @since 1.18
756 */
757 protected int get(float[] dst, int from, int n) throws EOFException, IOException {
758 if (n == 1 && !isViewingAs(ElementType.FLOAT.bufferClass())) {
759 dst[from] = getFloat();
760 return 1;
761 }
762 return get(ElementType.FLOAT, dst, from, n);
763 }
764
765 /**
766 * Retrieves a sequence of big-endian 64-bit floating-point values from the buffer. Before data can be retrieved
767 * with this method they should be 'loaded' into the buffer via {@link #loadBytes(long, int)}.
768 *
769 * @param dst Java array in which to store the retrieved sequence of elements
770 * @param from the array index for storing the first element retrieved
771 * @param n the number of elements to retrieve
772 *
773 * @return the number of elements successfully retrieved
774 *
775 * @throws EOFException if already at the end of file.
776 * @throws IOException if there was an IO error before, before requested number of bytes could be read
777 *
778 * @see #loadBytes(long, int)
779 *
780 * @since 1.18
781 */
782 protected int get(double[] dst, int from, int n) throws EOFException, IOException {
783 if (n == 1 && !isViewingAs(ElementType.DOUBLE.bufferClass())) {
784 dst[from] = getDouble();
785 return 1;
786 }
787 return get(ElementType.DOUBLE, dst, from, n);
788 }
789
790 /**
791 * Retrieves a sequence of values from the buffer. Before data can be retrieved with this method the should be
792 * 'loaded' into the buffer via {@link #loadBytes(long, int)}.
793 *
794 * @param dst Java array in which to store the retrieved sequence of elements
795 * @param from the array index for storing the first element retrieved
796 * @param n the number of elements to retrieve
797 *
798 * @return the number of elements successfully retrieved
799 *
800 * @throws EOFException if already at the end of file.
801 * @throws IOException if there was an IO error before, before requested number of bytes could be read
802 *
803 * @see #loadBytes(long, int)
804 *
805 * @since 1.18
806 */
807 @SuppressWarnings("unchecked")
808 private <B extends Buffer> int get(ElementType<B> e, Object dst, int from, int n) throws EOFException, IOException {
809 int got = 0;
810
811 while (got < n) {
812 if (!makeAvailable(e.size())) {
813 return (int) eofCheck(null, got, n);
814 }
815 assertView(e);
816 int m = Math.min(n - got, view.remaining());
817 e.getArray((B) view, dst, from + got, m);
818 buffer.position(buffer.position() + m * e.size());
819 got += m;
820 }
821
822 return got;
823 }
824 }
825 }