View Javadoc
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 &lt; shortArray.length; i++) {
355      *     shortArray[i] = buf.getShort();
356      * }
357      *
358      * for (int i = 0; i &lt; 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 }