1 /* 2 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com) 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.irurueta.geometry.io; 17 18 import java.io.File; 19 import java.io.IOException; 20 import java.io.RandomAccessFile; 21 import java.nio.MappedByteBuffer; 22 import java.nio.channels.FileChannel; 23 24 /** 25 * This class provides methods to access file data at random positions using 26 * memory mapping for faster data access. 27 */ 28 @SuppressWarnings("DuplicatedCode") 29 public class MappedFileReaderAndWriter extends AbstractFileReaderAndWriter { 30 31 /** 32 * Underlying input file. 33 */ 34 private final RandomAccessFile randomAccessFile; 35 36 /** 37 * Buffer where data is stored. 38 */ 39 private final MappedByteBuffer buffer; 40 41 /** 42 * Internal value indicating if read has been produced. 43 */ 44 private boolean read; 45 46 /** 47 * Constructor. 48 * 49 * @param f file to read from or write to. 50 * @param mode file opening mode (read only or read write). 51 * @throws IOException if an I/O error occurs . 52 */ 53 public MappedFileReaderAndWriter(final File f, final FileChannel.MapMode mode) throws IOException { 54 if (mode == FileChannel.MapMode.READ_ONLY) { 55 this.randomAccessFile = new RandomAccessFile(f, "r"); 56 } else { 57 this.randomAccessFile = new RandomAccessFile(f, "rw"); 58 } 59 this.buffer = randomAccessFile.getChannel().map(mode, 0, randomAccessFile.length()); 60 this.buffer.load(); 61 } 62 63 /** 64 * Reads one byte at current file position and advances one position. 65 * 66 * @return Next byte of data or -1 if end of file is reached. 67 * @throws IOException if an I/O error occurs. Not thrown if end-of-file has 68 * been reached. 69 */ 70 @Override 71 public int read() throws IOException { 72 if (isEndOfStream()) { 73 return -1; 74 } 75 int value = buffer.get(); 76 value = ((value << 8) >> 8) & 0xff; 77 read = true; 78 return value; 79 } 80 81 /** 82 * Reads up to b.length bytes of data from this file into an array of bytes. 83 * This method blocks until at least one byte of input is available. 84 * 85 * @param b The buffer into which the data is read. 86 * @return The total number of bytes read into the buffer, or -1 if there is 87 * no more data because the end of the file has been reached. 88 * @throws IOException If the first byte cannot be read for any reason other 89 * than end of file, or if the file has been closed, or if some other I/O 90 * error occurs. 91 */ 92 @Override 93 public int read(final byte[] b) throws IOException { 94 if (isEndOfStream()) { 95 return -1; 96 } 97 final var length = Math.min(buffer.remaining(), b.length); 98 if (length > 0) { 99 buffer.get(b, 0, length); 100 } 101 read = true; 102 return length; 103 } 104 105 /** 106 * Reads up to len bytes of data from this file into an array of bytes. This 107 * method blocks until at least one byte of input is available. 108 * This method behaves in exactly the same way as the 109 * InputStream.read(byte[], int, int) method of InputStream. 110 * 111 * @param b the buffer into which the data is read. 112 * @param off the start offset in array b at which the data is written. 113 * @param len the maximum number of bytes read. 114 * @return the total number of bytes read into the buffer, or -1 if there is 115 * no more data because the end of the file has been reached. 116 * @throws IOException If the first byte cannot be read for any reason other 117 * than end of file, or if the random access file has been closed, or if 118 * some other I/O error occurs. 119 */ 120 @Override 121 public int read(final byte[] b, final int off, final int len) throws IOException { 122 if (isEndOfStream()) { 123 return -1; 124 } 125 final var length = Math.min(buffer.remaining(), len); 126 if (length > 0) { 127 buffer.get(b, off, length); 128 } 129 read = true; 130 return length; 131 } 132 133 /** 134 * Attempts to skip over n byte of input discarding the skipped bytes. 135 * <p> 136 * This method may skip over some number of bytes, possibly zero. This may 137 * result from any of a number of conditions; reaching end of file before n 138 * bytes have been skipped is only one possibility. The actual number of 139 * bytes skipped is returned. If n is negative, no bytes are skipped. 140 * 141 * @param n the number of bytes to be skipped. 142 * @return the actual number of bytes skipped. 143 */ 144 @Override 145 public long skip(final long n) { 146 if (n < 0) { 147 return 0; 148 } 149 150 final var skipped = Math.min(buffer.remaining(), (int) n); 151 final var newPos = buffer.position() + skipped; 152 buffer.position(newPos); 153 read = true; 154 return skipped; 155 } 156 157 /** 158 * Writes the specified byte to this file. The write starts at the current 159 * file pointer. 160 * 161 * @param b the byte to be written. 162 * @throws IOException if an I/O error occurs. 163 */ 164 @Override 165 public void write(final int b) throws IOException { 166 randomAccessFile.write(b); 167 read = false; 168 } 169 170 /** 171 * Writes b.length bytes from the specified byte array to this file, 172 * starting at the current file pointer. 173 * 174 * @param b the data. 175 * @throws IOException if an I/O error occurs. 176 */ 177 @Override 178 public void write(final byte[] b) throws IOException { 179 randomAccessFile.write(b); 180 read = false; 181 } 182 183 /** 184 * Writes len bytes from the specified byte array starting at offset off to 185 * this file. 186 * 187 * @param b the data. 188 * @param off the start offset in the data. 189 * @param len the number of bytes to write. 190 * @throws IOException if an I/O error occurs. 191 */ 192 @Override 193 public void write(final byte[] b, final int off, final int len) throws IOException { 194 randomAccessFile.write(b, off, len); 195 read = false; 196 } 197 198 /** 199 * Returns the current offset in this file. 200 * 201 * @return the offset from the beginning of the file, in bytes, at which the 202 * next read or write occurs. 203 * @throws IOException in an I/O error occurs. 204 */ 205 @Override 206 public long getPosition() throws IOException { 207 if (read) { 208 return buffer.position(); 209 } else { 210 return randomAccessFile.getFilePointer(); 211 } 212 } 213 214 /** 215 * Determines whether end of file has been reached (next read() will return 216 * -1). or not. 217 * 218 * @return True if end of file has been reached, false otherwise. 219 * @throws IOException if an I/O error occurs.. 220 */ 221 @Override 222 public boolean isEndOfStream() throws IOException { 223 return getPosition() >= randomAccessFile.length(); 224 } 225 226 /** 227 * Sets the file-pointer offset, measured from the beginning of this file, 228 * at which the next read or write occurs. Setting the offset beyond the end 229 * of the file will raise an exception. 230 * 231 * @param pos the offset position, measured in bytes from the beginning of 232 * the file, at which to set the file pointer. 233 */ 234 @Override 235 public void seek(final long pos) { 236 buffer.position((int) pos); 237 read = true; 238 } 239 240 /** 241 * Closes this file stream and releases any system resources associated with 242 * the stream. A closed file cannot perform input or output operations and 243 * cannot be reopened. 244 * If this file has an associated channel then the channel is closed as 245 * well. 246 * 247 * @throws IOException if an I/O error occurs. 248 */ 249 @Override 250 public void close() throws IOException { 251 buffer.force(); 252 randomAccessFile.close(); 253 } 254 255 /** 256 * Reads a boolean from this file. This method reads a single byte from the 257 * file, starting at the current file pointer. A value of 0 represents 258 * false. Any other value represents true. This method blocks until the byte 259 * is read, the end of the stream is detected, or an exception is thrown. 260 * 261 * @return the boolean value read. 262 */ 263 @Override 264 public boolean readBoolean() { 265 read = true; 266 return buffer.get() != 0; 267 } 268 269 /** 270 * Reads a signed eight-bit value from this file. This method reads a byte 271 * from the file, starting from the current file pointer. If the byte read 272 * is b, where 0 <= b <= 255, then the result is: (byte)(b) 273 * This method blocks until the byte is read, the end of the stream is 274 * detected, or an exception is thrown. 275 * 276 * @return the next byte of this file is a signed eight-bit byte. 277 */ 278 @Override 279 public byte readByte() { 280 read = true; 281 return buffer.get(); 282 } 283 284 /** 285 * Reads an unsigned eight-bit number from this file. This method reads a 286 * byte from this file, starting at the current file pointer, and returns 287 * that byte. 288 * This method blocks until the byte is read, the end of the stream is 289 * detected, or an exception is thrown. 290 * 291 * @return the next byte of this file, interpreted as an unsigned eight-bit 292 * number. 293 */ 294 @Override 295 public short readUnsignedByte() { 296 read = true; 297 return (short) (((buffer.get() << 8) >> 8) & 0xff); 298 } 299 300 /** 301 * Reads a signed 16-bit number from this file. The method reads two byte 302 * from this file, starting at the current file pointer. If the two bytes 303 * read, in order, are b1 and b2, where each of the two values is between 0 304 * and 255, inclusive, then the result is equal to: (short)(b1 << 8 | 305 * b2). 306 * This method blocks until the two bytes are read, the end of the stream is 307 * detected, or an exception is thrown. 308 * 309 * @return the next two bytes of this file, interpreted as a signed 16-bit 310 * number. 311 */ 312 @Override 313 public short readShort() { 314 read = true; 315 return buffer.getShort(); 316 } 317 318 /** 319 * Reads a signed 16-bit number from this file assuming that file is encoded 320 * using provided endian type. If endian type is big endian type, then 321 * natural binary order is preserved, otherwise byte order is reversed. 322 * This method blocks until the two bytes of the 16-bit number are read, the 323 * end of the stream is detected, or an exception is thrown. 324 * 325 * @param endianType Endian type. Big endian preserves natural binary order, 326 * little endian reverses byte order. 327 * @return the next two bytes of this file, interpreted as a signed 16-bit 328 * number encoded in provided endian type. 329 */ 330 @Override 331 public short readShort(final EndianType endianType) { 332 read = true; 333 return Util.fromEndianType(endianType, buffer.getShort()); 334 } 335 336 /** 337 * Reads an unsigned 16-bit number from this file. This method reads two 338 * bytes from this file, starting at the current file pointer. If the bytes 339 * read, in order, are b1 and b2, where 0 <= b1, b2 <= 255, then the 340 * result is equal to: (b1 << 8) | b2 341 * This method blocks until the two bytes are read, the end of the stream is 342 * detected, or an exception is thrown. 343 * 344 * @return the next two bytes of this file, interpreted as an unsigned 345 * 16-bit integer. 346 */ 347 @Override 348 public int readUnsignedShort() { 349 read = true; 350 final var value = buffer.getShort(); 351 352 // convert value to byte array 353 final var firstShortByte = 0xff & (value >> 8); 354 final var secondShortByte = 0xff & ((value << 8) >> 8); 355 356 // return it as integer 357 return 0xffff & ((firstShortByte << 8) | secondShortByte); 358 } 359 360 /** 361 * Reads an unsigned 16-bit number from this file. This method reads two 362 * bytes from this file, starting at the current file pointer and using 363 * provided endian type. If endian type is big endian, then natural binary 364 * order is preserved, otherwise byte order is reversed. 365 * This method blocks until the two bytes are read, the end of the stream is 366 * detected, or an exception is thrown. 367 * 368 * @param endianType Endian type. Big endian preserves natural binary order, 369 * little endian reverses byte order. 370 * @return the next two bytes of this file, interpreted as an unsigned 371 * 16-bit integer. 372 */ 373 @Override 374 public int readUnsignedShort(final EndianType endianType) { 375 read = true; 376 final var streamValue = buffer.getShort(); 377 final var value = Util.fromEndianType(endianType, streamValue); 378 379 // convert value to byte array 380 final var secondShortByte = 0xff & ((value << 8) >> 8); 381 final var firstShortByte = 0xff & (value >> 8); 382 383 // return it as integer 384 return 0xffff & ((firstShortByte << 8) | secondShortByte); 385 } 386 387 /** 388 * Reads a signed 32-bit integer from this file. This method reads 4 bytes 389 * from the file, starting at the current file pointer. If the bytes read, 390 * in order, are b1, b2, b3, and b4, where 0 <= b1, b3, b4 <= 255, then 391 * the result is equal to: (b1 << 24) | (b2 << 16) + (b3 << 8) + 392 * b4. 393 * This method blocks until the four bytes are read, the end of the stream 394 * is detected, or an exception is thrown. 395 * 396 * @return the next four bytes of this file, interpreted as an int. 397 */ 398 @Override 399 public int readInt() { 400 read = true; 401 return buffer.getInt(); 402 } 403 404 /** 405 * Reads a signed 32-bit integer from this file. This method reads 4 bytes 406 * from the file, starting at the current file pointer and using provided 407 * endian type. If endian type is big endian, then natural binary order is 408 * preserved, otherwise byte order is reversed. 409 * This method blocks until the four bytes are read, the end of the stream 410 * is detected, or an exception is thrown. 411 * 412 * @param endianType Endian type. Big endian preserves natural binary order, 413 * little endian reverses byte order. 414 * @return the next four bytes of this file, interpreted as an int. 415 */ 416 @Override 417 public int readInt(final EndianType endianType) { 418 read = true; 419 return Util.fromEndianType(endianType, buffer.getInt()); 420 } 421 422 /** 423 * Reads an unsigned 32-bit integer from this file. This method reads 4 424 * bytes from the file, starting at the current file pointer. If the bytes 425 * read, in order, are b1, b2, b3, and b4, where 0 <= b1, b3, b4 <= 255, 426 * then the result is equal to: (b1 << 24) | (b2 << 16) + 427 * (b3 << 8) + b4. 428 * This method blocks until the four bytes are read, the end of the stream 429 * is detected, or an exception is thrown. 430 * 431 * @return the next four bytes of this file, interpreted as a long. 432 */ 433 @Override 434 public long readUnsignedInt() { 435 read = true; 436 final var value = buffer.getInt(); 437 438 // convert value to byte array 439 final var firstIntByte = 0xff & (value >> 24); 440 final var secondIntByte = 0xff & ((value << 8) >> 24); 441 final var thirdIntByte = 0xff & ((value << 16) >> 24); 442 final var fourthIntByte = 0xff & ((value << 24) >> 24); 443 444 // return it as integer 445 return ((long) firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte; 446 } 447 448 /** 449 * Reads an unsigned 32-bit integer from this file. This method reads 4 450 * bytes from the file, starting at the current file pointer and using 451 * provided endian type. If endian type is big endian, then natural binary 452 * order is preserved, otherwise byte order is reversed. 453 * This method blocks until the four bytes are read, the end of the stream 454 * is detected, or an exception is thrown. 455 * 456 * @param endianType Endian type. Big endian preserves natural binary order, 457 * little endian reverses byte order. 458 * @return the next four bytes of this file, interpreted as an int. 459 */ 460 @Override 461 public long readUnsignedInt(final EndianType endianType) { 462 read = true; 463 final var streamValue = buffer.getInt(); 464 final var value = Util.fromEndianType(endianType, streamValue); 465 466 // convert value to byte array 467 final var firstIntByte = 0xff & (value >> 24); 468 final var secondIntByte = 0xff & ((value << 8) >> 24); 469 final var thirdIntByte = 0xff & ((value << 16) >> 24); 470 final var fourthIntByte = 0xff & ((value << 24) >> 24); 471 472 // return it as integer 473 return ((long) firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte; 474 } 475 476 /** 477 * Reads a signed 64-bit integer from this file. This method reads eight 478 * bytes from the file, starting at the current file pointer. If the bytes 479 * read, in order, are b1, b2, b3, b4, b5, b6, b7, and b8, where: 480 * 0 <= b1, b2, b3, b4. b5. b6. b7. b8 <= 255, 481 * then the result is equal to: 482 * ((long)b1 << 56) + ((long)b2 << 48) 483 * + ((long)b3 << 40) + ((long)b4 << 32) 484 * + ((long)b5 << 24) + ((long)b6 << 16) 485 * + ((long)b7 << 8) + b8 486 * This method blocks until the eight bytes are read, the end of the stream 487 * is detected, or an exception is thrown. 488 * 489 * @return the next eight bytes of this file, interpreted as a long 490 */ 491 @Override 492 public long readLong() { 493 read = true; 494 return buffer.getLong(); 495 } 496 497 /** 498 * Reads a signed 64-bit integer from this file. This method reads eight 499 * bytes from the file, starting at the current file pointer and using 500 * provided endian type. If endian type is big endian, then natural binary 501 * order is preserved, otherwise byte order is reversed. 502 * This method blocks until the eight bytes are read, the end of the stream 503 * is detected, or an exception is thrown. 504 * 505 * @param endianType Endian type. Big endian preserves natural binary order, 506 * little endian reverses byte order. 507 * @return the next eight bytes of this file, interpreted as a long 508 */ 509 @Override 510 public long readLong(final EndianType endianType) { 511 read = true; 512 return Util.fromEndianType(endianType, buffer.getLong()); 513 } 514 515 /** 516 * Reads a float from this file. This method reads an int value, starting at 517 * the current file pointer, as if by the readInt method and then converts 518 * that in to a float using the intBitsToFloat method in class Float. 519 * This method blocks until the four bytes are read, the end of the stream 520 * is detected, or an exception is thrown. 521 * 522 * @return the next four bytes of this file, interpreted as a float. 523 */ 524 @Override 525 public float readFloat() { 526 read = true; 527 return buffer.getFloat(); 528 } 529 530 /** 531 * Reads a float from this file. This method reads four bytes using 532 * provided endian type. If endian type is big endian, then natural binary 533 * order is preserved, otherwise byte order is reversed. 534 * This method blocks until the four bytes are read, the end of the stream 535 * is detected, or an exception is thrown. 536 * 537 * @param endianType Endian type. Big endian preserves natural binary order, 538 * little endian reverses byte order. 539 * @return the next four bytes of this file, interpreted as a float. 540 */ 541 @Override 542 public float readFloat(final EndianType endianType) { 543 read = true; 544 return Util.fromEndianType(endianType, buffer.getFloat()); 545 } 546 547 /** 548 * Reads a double from this file. This method reads a long value, starting 549 * at the current file pointer, as if by the readLong method and then 550 * converts that long to a double using the longBitsToDouble method in class 551 * Double. 552 * This method blocks until the eight bytes are read, the end of the stream 553 * is detected, or an exception is thrown. 554 * 555 * @return the next eight bytes of this file, interpreted as a double. 556 */ 557 @Override 558 public double readDouble() { 559 read = true; 560 return buffer.getDouble(); 561 } 562 563 /** 564 * Reads a double from this file. This method reads eight bytes using 565 * provided endian type. If endian type is big endian, then natural binary 566 * order is preserved, otherwise byte order is reversed. 567 * This method blocks until the eight bytes are read, the end of the stream 568 * is detected, or an exception is thrown. 569 * 570 * @param endianType Endian type. Big endian preserves natural binary order, 571 * little endian reverses byte order. 572 * @return the next eight bytes of this file, interpreted as a double. 573 */ 574 @Override 575 public double readDouble(final EndianType endianType) { 576 read = true; 577 return Util.fromEndianType(endianType, buffer.getDouble()); 578 } 579 580 /** 581 * Reads the next line of text from this file. This method successively 582 * reads bytes from the file, starting at the current file pointer, until it 583 * reaches a line terminator of the end of the file. Each byte is converted 584 * into a character by taking the byte's value for the lower eight bits of 585 * the character and setting the high eight bits of the character to zero. 586 * This method does not, therefore, support the full Unicode character set. 587 * A line of text is terminated by a carriage-return character ('\r'), a 588 * newline character('\n'), a carriage-return character immediately followed 589 * by a newline character, or the end of the file. Line-terminating 590 * characters are discarded and are not included as part of the string 591 * returned. 592 * This method blocks until a newline character is read, a carriage return 593 * and the byte following it are read (to see if it is a newline), the end 594 * of the file is reached, or an exception is thrown. 595 * 596 * @return the next line of text from this file, or null if end of file is 597 * encountered before even one byte is read. 598 */ 599 @Override 600 public String readLine() { 601 read = true; 602 if (buffer.hasRemaining()) { 603 return readUntilAnyOfTheseCharactersIsFound("\n"); 604 } else { 605 return null; 606 } 607 } 608 609 /** 610 * Sequentially reads characters starting at current file position until one 611 * of the characters in provided pattern is found. 612 * All characters read so far will be returned without including any of the 613 * pattern characters. 614 * 615 * @param pattern Stop characters to stop reading when they are found. 616 * @return String read so far until any of the pattern characters was found 617 * or an empty string if the first character is contained in provided 618 * pattern. 619 * @throws IllegalArgumentException if no pattern characters are provided. 620 */ 621 @Override 622 public String readUntilAnyOfTheseCharactersIsFound(final String pattern) { 623 if (pattern.isEmpty()) { 624 throw new IllegalArgumentException(); 625 } 626 627 read = true; 628 629 final var stringBuffer = new StringBuilder(); 630 final var charBuffer = new byte[1]; 631 String character; 632 while (buffer.hasRemaining()) { 633 charBuffer[0] = buffer.get(); 634 character = new String(charBuffer); 635 if (pattern.contains(character)) { 636 // character found 637 break; 638 } 639 // add character to output buffer 640 stringBuffer.append(character); 641 } 642 643 return stringBuffer.toString(); 644 } 645 646 /** 647 * Writes a boolean to the file as a one-byte value. The value true is 648 * written out as the value (byte)1; the value false is written out as the 649 * value (byte)0. The write starts at the current position of the file 650 * pointer. 651 * 652 * @param v a boolean value to be written. 653 * @throws IOException if an I/O error occurs. 654 */ 655 @Override 656 public void writeBoolean(final boolean v) throws IOException { 657 randomAccessFile.writeBoolean(v); 658 read = false; 659 } 660 661 /** 662 * Writes a byte to the file as a one-byte value. The write starts at the 663 * current position of the file pointer. 664 * 665 * @param v a byte value to be written. 666 * @throws IOException if an I/O error occurs. 667 */ 668 @Override 669 public void writeByte(final byte v) throws IOException { 670 randomAccessFile.writeByte(v); 671 read = false; 672 } 673 674 /** 675 * Writes provided value in the range 0-255 as an unsigned byte. The write 676 * starts at the current position of the file pointer. 677 * 678 * @param v a value to be written as an unsigned byte. 679 * @throws IOException if an I/O error occurs. 680 */ 681 @Override 682 public void writeUnsignedByte(final short v) throws IOException { 683 final var data = (byte) (0xff & v); 684 randomAccessFile.writeByte(data); 685 read = false; 686 } 687 688 /** 689 * Writes a short to the file as two bytes, high byte first. The write 690 * starts at the current position of the file pointer. 691 * 692 * @param v a short to be written. 693 * @throws IOException if an I/O error occurs. 694 */ 695 @Override 696 public void writeShort(final short v) throws IOException { 697 randomAccessFile.writeShort(v); 698 read = false; 699 } 700 701 /** 702 * Writes a short to the file as two bytes using provided endian type. 703 * If endian type is big endian, then natural byte order is preserved (and 704 * high byte is written first), if little endian order is chosen, then byte 705 * order is reversed 706 * 707 * @param v a short to be written 708 * @param endianType endian type. If it is big endian, natural byte order is 709 * preserved, otherwise byte order is reversed. 710 * @throws IOException if an I/O error occurs. 711 */ 712 @Override 713 public void writeShort(final short v, final EndianType endianType) throws IOException { 714 final var value = Util.toEndianType(endianType, v); 715 randomAccessFile.writeShort(value); 716 read = false; 717 } 718 719 /** 720 * Writes an unsigned short to the file as two bytes, high byte first. 721 * Provided integer value is converted to an unsigned short by taking into 722 * account only the two lower bytes. The write starts at the current 723 * position of the file pointer. 724 * 725 * @param v an unsigned short to be written (int is converted to unsigned 726 * short). 727 * @throws IOException if an I/O error occurs. 728 */ 729 @Override 730 public void writeUnsignedShort(final int v) throws IOException { 731 final var firstShortByte = 0xff & (v >> 8); 732 final var secondShortByte = 0xff & ((v << 8) >> 8); 733 734 final var value = (short) ((firstShortByte << 8) | secondShortByte); 735 736 randomAccessFile.writeShort(value); 737 read = false; 738 } 739 740 /** 741 * Writes an unsigned short to the file as two bytes, using provided endian 742 * type. 743 * Provided integer value is converted to an unsigned short by taking into 744 * account only the two lower bytes. 745 * If endian type is big endian, then natural byte order is preserved (and 746 * high byte is written first), if little endian order is chosen, then byte 747 * order is reversed 748 * The write starts at the current position of the file pointer. 749 * 750 * @param v an unsigned short to be written (int is converted to unsigned 751 * short). 752 * @param endianType endian type. If it is big endian, natural byte order is 753 * preserved, otherwise byte order is reversed. 754 * @throws IOException if an I/O error occurs. 755 */ 756 @Override 757 public void writeUnsignedShort(final int v, final EndianType endianType) throws IOException { 758 final var firstShortByte = 0xff & (v >> 8); 759 final var secondShortByte = 0xff & ((v << 8) >> 8); 760 761 final var machineValue = (short) ((firstShortByte << 8) | secondShortByte); 762 final var value = Util.toEndianType(endianType, machineValue); 763 764 randomAccessFile.writeShort(value); 765 read = false; 766 } 767 768 /** 769 * Writes an int to the file as four bytes, high byte first. The write 770 * starts at the current position of the file pointer. 771 * 772 * @param v an int to be written. 773 * @throws IOException if an I/O error occurs. 774 */ 775 @Override 776 public void writeInt(final int v) throws IOException { 777 randomAccessFile.writeInt(v); 778 read = false; 779 } 780 781 /** 782 * Writes an int to the file as four bytes, using provided endian type. 783 * If endian type is big endian, then natural byte order is preserved (and 784 * high byte is written first), if little endian order is chosen, then byte 785 * order is reversed. 786 * The write starts at the current position of the file pointer. 787 * 788 * @param v an int to be written. 789 * @param endianType endian type. If it is big endian, natural byte order is 790 * preserved, otherwise byte order is reversed. 791 * @throws IOException if an I/O error occurs. 792 */ 793 @Override 794 public void writeInt(final int v, final EndianType endianType) throws IOException { 795 final var value = Util.toEndianType(endianType, v); 796 randomAccessFile.writeInt(value); 797 read = false; 798 } 799 800 /** 801 * Writes an unsigned int to the file as four bytes, high byte first. 802 * Provided integer value is converted to an unsigned int by taking into 803 * account only the four lower bytes. The write starts at the current 804 * position of the file pointer. 805 * 806 * @param v an unsigned int to be written (long is converted to unsigned 807 * int). 808 * @throws IOException if an I/O error occurs. 809 */ 810 @Override 811 public void writeUnsignedInt(final long v) throws IOException { 812 final var firstIntByte = (int) (0xff & (v >> 24)); 813 final var secondIntByte = (int) (0xff & ((v << 8) >> 24)); 814 final var thirdIntByte = (int) (0xff & ((v << 16) >> 24)); 815 final var fourthIntByte = (int) (0xff & ((v << 24) >> 24)); 816 817 final var value = (firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte; 818 819 randomAccessFile.writeInt(value); 820 read = false; 821 } 822 823 /** 824 * Writes an unsigned int to the file as four bytes, using provided endian 825 * type. 826 * Provided integer value is converted to an unsigned int by taking into 827 * account only the four lower bytes. 828 * If endian type is big endian, then natural byte order is preserved (and 829 * high byte is written first), if little endian order is chosen, then byte 830 * order is reversed. 831 * The write starts at the current position of the file pointer. 832 * 833 * @param v an unsigned int to be written (long is converted to unsigned 834 * short). 835 * @param endianType endian type. If it is big endian, natural byte order is 836 * preserved, otherwise byte order is reversed. 837 * @throws IOException if an I/O error occurs. 838 */ 839 @Override 840 public void writeUnsignedInt(final long v, final EndianType endianType) throws IOException { 841 final var firstIntByte = (int) (0xff & (v >> 24)); 842 final var secondIntByte = (int) (0xff & ((v << 8) >> 24)); 843 final var thirdIntByte = (int) (0xff & ((v << 16) >> 24)); 844 final var fourthIntByte = (int) (0xff & ((v << 24) >> 24)); 845 846 final var machineValue = (firstIntByte << 24) | (secondIntByte << 16) | (thirdIntByte << 8) | fourthIntByte; 847 final var value = Util.toEndianType(endianType, machineValue); 848 849 randomAccessFile.writeInt(value); 850 read = false; 851 } 852 853 /** 854 * Writes a long to the file as eight bytes, high byte first. The write 855 * starts at the current position of the file pointer. 856 * 857 * @param v a long to be written. 858 * @throws IOException if an I/O error occurs. 859 */ 860 @Override 861 public void writeLong(final long v) throws IOException { 862 randomAccessFile.writeLong(v); 863 read = false; 864 } 865 866 /** 867 * Writes a long to the file as eight bytes, using provided endian type. 868 * If endian type is big endian, then natural byte order is preserved (and 869 * high byte is written first), if little endian order is chosen, then byte 870 * order is reversed. 871 * The write starts at the current position of the file pointer. 872 * 873 * @param v a long to be written. 874 * @param endianType endian type. If it is big endian, natural byte order is 875 * preserved, otherwise byte order is reversed. 876 * @throws IOException if an I/O error occurs. 877 */ 878 @Override 879 public void writeLong(final long v, final EndianType endianType) throws IOException { 880 final var value = Util.toEndianType(endianType, v); 881 randomAccessFile.writeLong(value); 882 read = false; 883 } 884 885 /** 886 * Converts the float argument to an int using the floatToIntBits method in 887 * class Float, and then write that int value to the file as a four-byte 888 * quantity, high byte first. The write starts at the current position of 889 * the file pointer. 890 * 891 * @param v a float value to be written 892 * @throws IOException if an I/O error occurs. 893 */ 894 @Override 895 public void writeFloat(final float v) throws IOException { 896 randomAccessFile.writeFloat(v); 897 read = false; 898 } 899 900 /** 901 * Converts the float argument to an int using the floatToIntBits method in 902 * class Float, and then write that int value to the file as a four-byte 903 * quantity, using provided endian type. 904 * If endian type is big endian, then natural byte order is preserved (and 905 * high byte is written first), if little endian order is chosen, then byte 906 * order is reversed. 907 * The write starts at the current position of the file pointer. 908 * 909 * @param v a float value to be written 910 * @param endianType endian type. If it is big endian, natural byte order is 911 * preserved, otherwise byte order is reversed. 912 * @throws IOException if an I/O error occurs. 913 */ 914 @Override 915 public void writeFloat(final float v, final EndianType endianType) throws IOException { 916 final var value = Util.toEndianType(endianType, v); 917 randomAccessFile.writeFloat(value); 918 read = false; 919 } 920 921 /** 922 * Converts the double argument to a long using the doubleToLongBits method 923 * in class Double, and then writes that long value to the file as an eight 924 * byte quantity, high byte first. The write starts at the current position 925 * of the file pointer. 926 * 927 * @param v a double value to be written. 928 * @throws IOException if an I/O error occurs. 929 */ 930 @Override 931 public void writeDouble(final double v) throws IOException { 932 randomAccessFile.writeDouble(v); 933 read = false; 934 } 935 936 /** 937 * Converts the double argument to a long using the doubleToLongBits method 938 * in class Double, and then writes that long value to the file as an eight 939 * byte quantity, using provided endian type. 940 * If endian type is big endian, then natural byte order is preserved (and 941 * high byte is written first), if little endian order is chosen, then byte 942 * order is reversed. 943 * The write starts at the current position of the file pointer. 944 * 945 * @param v a double value to be written. 946 * @param endianType endian type. If it is big endian, natural byte order is 947 * preserved, otherwise byte order is reversed. 948 * @throws IOException if an I/O error occurs. 949 */ 950 @Override 951 public void writeDouble(final double v, final EndianType endianType) throws IOException { 952 final var value = Util.toEndianType(endianType, v); 953 randomAccessFile.writeDouble(value); 954 read = false; 955 } 956 957 /** 958 * Writes the string to the file as a sequence of bytes. Each character in 959 * the string is written out, in sequence, by discarding its high eight 960 * bits. The write starts at the current position of the file pointer. 961 * 962 * @param s a string of bytes to be written. 963 * @throws IOException if an I/O error occurs. 964 */ 965 @Override 966 public void writeASCII(final String s) throws IOException { 967 randomAccessFile.writeBytes(s); 968 read = false; 969 } 970 }