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.lang.reflect.Array; 37 38 import nom.tam.fits.FitsFactory; 39 import nom.tam.util.type.ElementType; 40 41 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 42 43 /** 44 * Decodes FITS-formatted binary data into Java arrays (<i>primarily for internal use</i>) 45 * 46 * @since 1.16 47 * 48 * @see FitsEncoder 49 * @see FitsInputStream 50 * @see FitsFile 51 */ 52 public class FitsDecoder extends InputDecoder { 53 54 /** 55 * The FITS byte value for the binary representation of a boolean 'true' value 56 */ 57 private static final byte FITS_TRUE = (byte) 'T'; 58 59 /** 60 * Instantiates a new decoder of FITS binary data to Java arrays. To be used by subclass constructors only. 61 */ 62 protected FitsDecoder() { 63 super(); 64 } 65 66 /** 67 * Instantiates a new FITS binary data decoder for converting FITS data representations into Java arrays. 68 * 69 * @param i the FITS input. 70 */ 71 public FitsDecoder(InputReader i) { 72 super(i); 73 } 74 75 /** 76 * Gets the <code>boolean</code> equivalent for a FITS byte value representing a logical value. This call does not 77 * support <code>null</code> values, which are allowed by the FITS standard, but the similar 78 * {@link #booleanObjectFor(int)} does. FITS defines 'T' as true, 'F' as false, and 0 as null. However, prior 79 * versions of this library have used the value 1 for true, and 0 for false. Therefore, this implementation will 80 * recognise both 'T' and 1 as <code>true</code>, and will return <code>false</code> for all other byte values. 81 * 82 * @param c The FITS byte that defines a boolean value 83 * 84 * @return <code>true</code> if and only if the byte is the ASCII character 'T' or has the value of 1, otherwise 85 * <code>false</code>. 86 * 87 * @see #booleanObjectFor(int) 88 */ 89 public static final boolean booleanFor(int c) { 90 return c == FITS_TRUE || c == 1; 91 } 92 93 /** 94 * Gets the <code>boolean</code> equivalent for a FITS byte value representing a logical value. This call supports 95 * <code>null</code> values, which are allowed by the FITS standard. FITS defines 'T' as true, 'F' as false, and 0 96 * as null. Prior versions of this library have used the value 1 for true, and 0 for false. Therefore, this 97 * implementation will recognise both 'T' and 1 as <code>true</code>, but 0 will map to <code>null</code> and 98 * everything else will return <code>false</code>. 99 * 100 * @param c The FITS byte that defines a boolean value 101 * 102 * @return <code>true</code> if and only if the byte is the ASCII character 'T' or has the value of 1, 103 * <code>null</code> it the byte is 0, otherwise <code>false</code>. 104 * 105 * @see #booleanFor(int) 106 */ 107 @SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "null values are explicitly allowed by FITS, so we want to support them.") 108 public static final Boolean booleanObjectFor(int c) { 109 if (c == 0) { 110 return null; 111 } 112 return booleanFor(c); 113 } 114 115 /** 116 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 117 * arrays by this library only. 118 * 119 * @return the next boolean value from the input. 120 * 121 * @throws EOFException if already at the end of file. 122 * @throws IOException if there was an IO error reading from the input. 123 */ 124 @Deprecated 125 protected synchronized boolean readBoolean() throws EOFException, IOException { 126 return booleanFor(readByte()); 127 } 128 129 /** 130 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 131 * arrays by this library only. 132 * 133 * @return the next character value from the input. 134 * 135 * @throws EOFException if already at the end of file. 136 * @throws IOException if there was an IO error reading from the input. 137 */ 138 @Deprecated 139 protected synchronized char readChar() throws EOFException, IOException { 140 int b = FitsFactory.isUseUnicodeChars() ? readUnsignedShort() : read(); 141 if (b < 0) { 142 throw new EOFException(); 143 } 144 return (char) b; 145 } 146 147 /** 148 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 149 * arrays by this library only. 150 * 151 * @return the next byte the input. 152 * 153 * @throws IOException if there was an IO error reading from the input. 154 */ 155 @Deprecated 156 protected final byte readByte() throws IOException { 157 int i = read(); 158 if (i < 0) { 159 throw new EOFException(); 160 } 161 return (byte) i; 162 } 163 164 /** 165 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 166 * arrays by this library only. 167 * 168 * @return the next unsigned byte from the input, or -1 if there is no more bytes available. 169 * 170 * @throws IOException if there was an IO error reading from the input, other than the end-of-file. 171 */ 172 @Deprecated 173 protected synchronized int readUnsignedByte() throws IOException { 174 return read(); 175 } 176 177 /** 178 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 179 * arrays by this library only. 180 * 181 * @return the next 16-bit integer value from the input. 182 * 183 * @throws EOFException if already at the end of file. 184 * @throws IOException if there was an IO error reading from the input. 185 */ 186 @Deprecated 187 protected final short readShort() throws EOFException, IOException { 188 int i = readUnsignedShort(); 189 if (i < 0) { 190 throw new EOFException(); 191 } 192 return (short) i; 193 } 194 195 /** 196 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 197 * arrays by this library only. 198 * 199 * @return the next unsigned 16-bit integer value from the input, or -1 if reached the end of stream 200 * 201 * @throws IOException if there was an IO error reading from the input. 202 */ 203 @Deprecated 204 protected synchronized int readUnsignedShort() throws IOException { 205 getInputBuffer().loadOne(Short.BYTES); 206 return getInputBuffer().getUnsignedShort(); 207 } 208 209 /** 210 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 211 * arrays by this library only. 212 * 213 * @return the next 32-bit integer value from the input. 214 * 215 * @throws EOFException if already at the end of file. 216 * @throws IOException if there was an IO error reading from the input. 217 */ 218 @Deprecated 219 protected synchronized int readInt() throws EOFException, IOException { 220 getInputBuffer().loadOne(Integer.BYTES); 221 return getInputBuffer().getInt(); 222 } 223 224 /** 225 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 226 * arrays by this library only. 227 * 228 * @return the next 64-bit integer value from the input. 229 * 230 * @throws EOFException if already at the end of file. 231 * @throws IOException if there was an IO error reading from the input. 232 */ 233 @Deprecated 234 protected synchronized long readLong() throws EOFException, IOException { 235 getInputBuffer().loadOne(Long.BYTES); 236 return getInputBuffer().getLong(); 237 } 238 239 /** 240 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 241 * arrays by this library only. 242 * 243 * @return the next single-precision (32-bit) floating point value from the input. 244 * 245 * @throws EOFException if already at the end of file. 246 * @throws IOException if there was an IO error reading from the input. 247 */ 248 @Deprecated 249 protected synchronized float readFloat() throws EOFException, IOException { 250 getInputBuffer().loadOne(Float.BYTES); 251 return getInputBuffer().getFloat(); 252 } 253 254 /** 255 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 256 * arrays by this library only. 257 * 258 * @return the next double-precision (64-bit) floating point value from the input. 259 * 260 * @throws EOFException if already at the end of file. 261 * @throws IOException if there was an IO error reading from the input. 262 */ 263 @Deprecated 264 protected synchronized double readDouble() throws EOFException, IOException { 265 getInputBuffer().loadOne(Double.BYTES); 266 return getInputBuffer().getDouble(); 267 } 268 269 /** 270 * @deprecated (<i>for internal use</i>) Low-level reading/writing should be handled internally as 271 * arrays by this library only. 272 * 273 * @return the next line of 1-byte ASCII characters, terminated by a LF or EOF. 274 * 275 * @throws EOFException if already at the end of file. 276 * @throws IOException if there was an IO error reading from the input. 277 */ 278 @Deprecated 279 protected synchronized String readAsciiLine() throws EOFException, IOException { 280 StringBuffer str = new StringBuffer(); 281 for (;;) { 282 int c = read(); 283 if (c < 0) { 284 if (str.length() > 0) { 285 break; 286 } 287 throw new EOFException(); 288 } 289 if (c == '\n') { 290 break; 291 } 292 str.append((char) c); 293 } 294 return new String(str); 295 } 296 297 /** 298 * See {@link ArrayDataInput#read(boolean[], int, int)} for the general contract of this method. In FITS, 299 * <code>true</code> values are represented by the ASCII byte for 'T', whereas <code>false</code> is represented by 300 * the ASCII byte for 'F'. 301 * 302 * @param b an array of boolean values. 303 * @param start the buffer index at which to start reading data 304 * @param length the total number of elements to read. 305 * 306 * @return the number of bytes successfully read. 307 * 308 * @throws EOFException if already at the end of file. 309 * @throws IOException if there was an IO error before, before requested number of bytes could be read 310 */ 311 protected synchronized int read(boolean[] b, int start, int length) throws EOFException, IOException { 312 if (length == 0) { 313 return 0; 314 } 315 316 byte[] ascii = new byte[length]; 317 length = read(ascii, 0, length); 318 319 if (length < 0) { 320 throw new EOFException(); 321 } 322 323 for (int i = 0; i < length; i++) { 324 b[start + i] = booleanFor(ascii[i]); 325 } 326 327 return length; 328 } 329 330 /** 331 * See {@link ArrayDataInput#read(Boolean[], int, int)} for the general contract of this method. In FITS, 332 * <code>true</code> values are represented by the ASCII byte for 'T', <code>false</code> is represented by the 333 * ASCII byte for 'F', while <code>null</code> values are represented by the value 0. 334 * 335 * @param b an array of boolean values. 336 * @param start the buffer index at which to start reading data 337 * @param length the total number of elements to read. 338 * 339 * @return the number of bytes successfully read. 340 * 341 * @throws EOFException if already at the end of file. 342 * @throws IOException if there was an IO error before, before requested number of bytes could be read 343 */ 344 protected synchronized int read(Boolean[] b, int start, int length) throws EOFException, IOException { 345 if (length == 0) { 346 return 0; 347 } 348 349 byte[] ascii = new byte[length]; 350 length = read(ascii, 0, length); 351 352 if (length < 0) { 353 throw new EOFException(); 354 } 355 356 for (int i = 0; i < length; i++) { 357 b[start + i] = booleanObjectFor(ascii[i]); 358 } 359 360 return length; 361 } 362 363 /** 364 * See {@link ArrayDataInput#read(char[], int, int)} for the general contract of this method. In FITS characters are 365 * usually represented as 1-byte ASCII, not as the 2-byte Java types. However, previous implementations if this 366 * library have erroneously written 2-byte characters into the FITS. For compatibility both the FITS standard 367 * -1-byte ASCII and the old 2-byte behaviour are supported, and can be selected via 368 * {@link FitsFactory#setUseUnicodeChars(boolean)}. 369 * 370 * @param c a character array. 371 * @param start the buffer index at which to start reading data 372 * @param length the total number of elements to read. 373 * 374 * @return the number of bytes successfully read. 375 * 376 * @throws EOFException if already at the end of file. 377 * @throws IOException if there was an IO error before, before requested number of bytes could be read 378 * 379 * @see FitsFactory#setUseUnicodeChars(boolean) 380 */ 381 protected synchronized int read(char[] c, int start, int length) throws EOFException, IOException { 382 if (length == 0) { 383 return 0; 384 } 385 386 if (ElementType.CHAR.size() == 1) { 387 byte[] ascii = new byte[length]; 388 length = read(ascii, 0, length); 389 390 if (length < 0) { 391 throw new EOFException(); 392 } 393 394 for (int i = 0; i < length; i++) { 395 c[start + i] = (char) (ascii[i] & FitsIO.BYTE_MASK); 396 } 397 } else { 398 getInputBuffer().loadBytes(length, Short.BYTES); 399 short[] s = new short[length]; 400 length = getInputBuffer().get(s, 0, length); 401 for (int i = 0; i < length; i++) { 402 c[start + i] = (char) (s[i] & FitsIO.SHORT_MASK); 403 } 404 } 405 406 return length * ElementType.CHAR.size(); 407 } 408 409 /** 410 * See {@link ArrayDataInput#read(short[], int, int)} for a contract of this method. 411 * 412 * @param s an array of 16-bit integer values. 413 * @param start the buffer index at which to start reading data 414 * @param length the total number of elements to read. 415 * 416 * @return the number of bytes successfully read. 417 * 418 * @throws EOFException if already at the end of file. 419 * @throws IOException if there was an IO error before, before requested number of bytes could be read 420 */ 421 protected synchronized int read(short[] s, int start, int length) throws EOFException, IOException { 422 getInputBuffer().loadBytes(length, Short.BYTES); 423 return getInputBuffer().get(s, start, length) * Short.BYTES; 424 } 425 426 /** 427 * See {@link ArrayDataInput#read(int[], int, int)} for a contract of this method. 428 * 429 * @param j an array of 32-bit integer values. 430 * @param start the buffer index at which to start reading data 431 * @param length the total number of elements to read. 432 * 433 * @return the number of bytes successfully read. 434 * 435 * @throws EOFException if already at the end of file. 436 * @throws IOException if there was an IO error before, before requested number of bytes could be read 437 */ 438 protected synchronized int read(int[] j, int start, int length) throws EOFException, IOException { 439 getInputBuffer().loadBytes(length, Integer.BYTES); 440 return getInputBuffer().get(j, start, length) * Integer.BYTES; 441 } 442 443 /** 444 * See {@link ArrayDataInput#read(long[], int, int)} for a contract of this method. 445 * 446 * @param l an array of 64-bit integer values. 447 * @param start the buffer index at which to start reading data 448 * @param length the total number of elements to read. 449 * 450 * @return the number of bytes successfully read. 451 * 452 * @throws EOFException if already at the end of file. 453 * @throws IOException if there was an IO error before, before requested number of bytes could be read 454 */ 455 protected synchronized int read(long[] l, int start, int length) throws EOFException, IOException { 456 getInputBuffer().loadBytes(length, Long.BYTES); 457 return getInputBuffer().get(l, start, length) * Long.BYTES; 458 } 459 460 /** 461 * See {@link ArrayDataInput#read(float[], int, int)} for a contract of this method. 462 * 463 * @param f an array of single-precision (32-bit) floating point values. 464 * @param start the buffer index at which to start reading data 465 * @param length the total number of elements to read. 466 * 467 * @return the number of bytes successfully read. 468 * 469 * @throws EOFException if already at the end of file. 470 * @throws IOException if there was an IO error before, before requested number of bytes could be read 471 */ 472 protected synchronized int read(float[] f, int start, int length) throws EOFException, IOException { 473 getInputBuffer().loadBytes(length, Float.BYTES); 474 return getInputBuffer().get(f, start, length) * Float.BYTES; 475 } 476 477 /** 478 * See {@link ArrayDataInput#read(double[], int, int)} for a contract of this method. 479 * 480 * @param d an array of double-precision (64-bit) floating point values. 481 * @param start the buffer index at which to start reading data 482 * @param length the total number of elements to read. 483 * 484 * @return the number of bytes successfully read. 485 * 486 * @throws EOFException if already at the end of file. 487 * @throws IOException if there was an IO error before, before requested number of bytes could be read 488 */ 489 protected synchronized int read(double[] d, int start, int length) throws EOFException, IOException { 490 getInputBuffer().loadBytes(length, Double.BYTES); 491 return getInputBuffer().get(d, start, length) * Double.BYTES; 492 } 493 494 @Override 495 public synchronized long readArray(Object o) throws IOException, IllegalArgumentException { 496 if (o == null) { 497 return 0L; 498 } 499 if (!o.getClass().isArray()) { 500 throw new IllegalArgumentException("Not an array: " + o.getClass().getName()); 501 } 502 503 int length = Array.getLength(o); 504 if (length == 0) { 505 return 0L; 506 } 507 508 // This is a 1-d array. Process it using our special 509 // functions. 510 if (o instanceof byte[]) { 511 readFully((byte[]) o, 0, length); 512 return length; 513 } 514 if (o instanceof boolean[]) { 515 return read((boolean[]) o, 0, length); 516 } 517 if (o instanceof char[]) { 518 return read((char[]) o, 0, length); 519 } 520 if (o instanceof short[]) { 521 return read((short[]) o, 0, length); 522 } 523 if (o instanceof int[]) { 524 return read((int[]) o, 0, length); 525 } 526 if (o instanceof float[]) { 527 return read((float[]) o, 0, length); 528 } 529 if (o instanceof long[]) { 530 return read((long[]) o, 0, length); 531 } 532 if (o instanceof double[]) { 533 return read((double[]) o, 0, length); 534 } 535 if (o instanceof Boolean[]) { 536 return read((Boolean[]) o, 0, length); 537 } 538 539 Object[] array = (Object[]) o; 540 long count = 0L; 541 542 // Process multidim arrays recursively. 543 for (int i = 0; i < length; i++) { 544 try { 545 count += readArray(array[i]); 546 } catch (EOFException e) { 547 return eofCheck(e, count, -1L); 548 } 549 } 550 return count; 551 } 552 553 }