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      /**
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 &lt; shortArray.length; i++) {
352      *     shortArray[i] = buf.getShort();
353      * }
354      *
355      * for (int i = 0; i &lt; 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 }