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