1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20import dalvik.system.CloseGuard;
21import java.nio.ByteOrder;
22import java.nio.NioUtils;
23import java.nio.channels.FileChannel;
24import java.nio.charset.ModifiedUtf8;
25import java.util.Arrays;
26import libcore.io.ErrnoException;
27import libcore.io.IoBridge;
28import libcore.io.IoUtils;
29import libcore.io.Libcore;
30import libcore.io.Memory;
31import libcore.io.SizeOf;
32import static libcore.io.OsConstants.*;
33
34/**
35 * Allows reading from and writing to a file in a random-access manner. This is
36 * different from the uni-directional sequential access that a
37 * {@link FileInputStream} or {@link FileOutputStream} provides. If the file is
38 * opened in read/write mode, write operations are available as well. The
39 * position of the next read or write operation can be moved forwards and
40 * backwards after every operation.
41 */
42public class RandomAccessFile implements DataInput, DataOutput, Closeable {
43    /**
44     * The FileDescriptor representing this RandomAccessFile.
45     */
46    private FileDescriptor fd;
47
48    private boolean syncMetadata = false;
49
50    // The unique file channel associated with this FileInputStream (lazily
51    // initialized).
52    private FileChannel channel;
53
54    private int mode;
55
56    private final CloseGuard guard = CloseGuard.get();
57
58    private final byte[] scratch = new byte[8];
59
60    /**
61     * Constructs a new {@code RandomAccessFile} based on {@code file} and opens
62     * it according to the access string in {@code mode}.
63     * <p><a id="accessmode"/>
64     * {@code mode} may have one of following values:
65     * <table border="0">
66     * <tr>
67     * <td>{@code "r"}</td>
68     * <td>The file is opened in read-only mode. An {@code IOException} is
69     * thrown if any of the {@code write} methods is called.</td>
70     * </tr>
71     * <tr>
72     * <td>{@code "rw"}</td>
73     * <td>The file is opened for reading and writing. If the file does not
74     * exist, it will be created.</td>
75     * </tr>
76     * <tr>
77     * <td>{@code "rws"}</td>
78     * <td>The file is opened for reading and writing. Every change of the
79     * file's content or metadata must be written synchronously to the target
80     * device.</td>
81     * </tr>
82     * <tr>
83     * <td>{@code "rwd"}</td>
84     * <td>The file is opened for reading and writing. Every change of the
85     * file's content must be written synchronously to the target device.</td>
86     * </tr>
87     * </table>
88     *
89     * @param file
90     *            the file to open.
91     * @param mode
92     *            the file access <a href="#accessmode">mode</a>, either {@code
93     *            "r"}, {@code "rw"}, {@code "rws"} or {@code "rwd"}.
94     * @throws FileNotFoundException
95     *             if the file cannot be opened or created according to {@code
96     *             mode}.
97     * @throws IllegalArgumentException
98     *             if {@code mode} is not {@code "r"}, {@code "rw"}, {@code
99     *             "rws"} or {@code "rwd"}.
100     */
101    public RandomAccessFile(File file, String mode) throws FileNotFoundException {
102        int flags;
103        if (mode.equals("r")) {
104            flags = O_RDONLY;
105        } else if (mode.equals("rw") || mode.equals("rws") || mode.equals("rwd")) {
106            flags = O_RDWR | O_CREAT;
107            if (mode.equals("rws")) {
108                // Sync file and metadata with every write
109                syncMetadata = true;
110            } else if (mode.equals("rwd")) {
111                // Sync file, but not necessarily metadata
112                flags |= O_SYNC;
113            }
114        } else {
115            throw new IllegalArgumentException("Invalid mode: " + mode);
116        }
117        this.mode = flags;
118        this.fd = IoBridge.open(file.getAbsolutePath(), flags);
119
120        // if we are in "rws" mode, attempt to sync file+metadata
121        if (syncMetadata) {
122            try {
123                fd.sync();
124            } catch (IOException e) {
125                // Ignored
126            }
127        }
128        guard.open("close");
129    }
130
131    /**
132     * Constructs a new {@code RandomAccessFile} based on the file named {@code
133     * fileName} and opens it according to the access string in {@code mode}.
134     * The file path may be specified absolutely or relative to the system
135     * property {@code "user.dir"}.
136     *
137     * @param fileName
138     *            the name of the file to open.
139     * @param mode
140     *            the file access <a href="#accessmode">mode</a>, either {@code
141     *            "r"}, {@code "rw"}, {@code "rws"} or {@code "rwd"}.
142     * @throws FileNotFoundException
143     *             if the file cannot be opened or created according to {@code
144     *             mode}.
145     * @throws IllegalArgumentException
146     *             if {@code mode} is not {@code "r"}, {@code "rw"}, {@code
147     *             "rws"} or {@code "rwd"}.
148     */
149    public RandomAccessFile(String fileName, String mode) throws FileNotFoundException {
150        this(new File(fileName), mode);
151    }
152
153    /**
154     * Closes this file.
155     *
156     * @throws IOException
157     *             if an error occurs while closing this file.
158     */
159    public void close() throws IOException {
160        guard.close();
161        synchronized (this) {
162            if (channel != null && channel.isOpen()) {
163                channel.close();
164                channel = null;
165            }
166            IoUtils.close(fd);
167        }
168    }
169
170    @Override protected void finalize() throws Throwable {
171        try {
172            if (guard != null) {
173                guard.warnIfOpen();
174            }
175            close();
176        } finally {
177            super.finalize();
178        }
179    }
180
181    /**
182     * Gets this file's {@link FileChannel} object.
183     * <p>
184     * The file channel's {@link FileChannel#position() position} is the same
185     * as this file's file pointer offset (see {@link #getFilePointer()}). Any
186     * changes made to this file's file pointer offset are also visible in the
187     * file channel's position and vice versa.
188     *
189     * @return this file's file channel instance.
190     */
191    public final synchronized FileChannel getChannel() {
192        if(channel == null) {
193            channel = NioUtils.newFileChannel(this, fd, mode);
194        }
195        return channel;
196    }
197
198    /**
199     * Gets this file's {@link FileDescriptor}. This represents the operating
200     * system resource for this random access file.
201     *
202     * @return this file's file descriptor object.
203     * @throws IOException
204     *             if an error occurs while getting the file descriptor of this
205     *             file.
206     */
207    public final FileDescriptor getFD() throws IOException {
208        return fd;
209    }
210
211    /**
212     * Gets the current position within this file. All reads and
213     * writes take place at the current file pointer position.
214     *
215     * @return the current offset in bytes from the beginning of the file.
216     *
217     * @throws IOException
218     *             if an error occurs while getting the file pointer of this
219     *             file.
220     */
221    public long getFilePointer() throws IOException {
222        try {
223            return Libcore.os.lseek(fd, 0L, SEEK_CUR);
224        } catch (ErrnoException errnoException) {
225            throw errnoException.rethrowAsIOException();
226        }
227    }
228
229    /**
230     * Returns the length of this file in bytes.
231     *
232     * @return the file's length in bytes.
233     * @throws IOException
234     *             if this file is closed or some other I/O error occurs.
235     */
236    public long length() throws IOException {
237        try {
238            return Libcore.os.fstat(fd).st_size;
239        } catch (ErrnoException errnoException) {
240            throw errnoException.rethrowAsIOException();
241        }
242    }
243
244    /**
245     * Reads a single byte from the current position in this file and returns it
246     * as an integer in the range from 0 to 255. Returns -1 if the end of the
247     * file has been reached. Blocks until one byte has been read, the end of
248     * the file is detected, or an exception is thrown.
249     *
250     * @return the byte read or -1 if the end of the file has been reached.
251     * @throws IOException
252     *             if this file is closed or another I/O error occurs.
253     */
254    public int read() throws IOException {
255        return (read(scratch, 0, 1) != -1) ? scratch[0] & 0xff : -1;
256    }
257
258    /**
259     * Reads bytes from the current position in this file and stores them in the
260     * byte array {@code buffer}. The maximum number of bytes read corresponds
261     * to the size of {@code buffer}. Blocks until at least one byte has been
262     * read, the end of the file is detected, or an exception is thrown.
263     * Returns the number of bytes actually read or -1 if the end of the file
264     * has been reached. See also {@link #readFully}.
265     *
266     * @throws IOException
267     *             if this file is closed or another I/O error occurs.
268     */
269    public int read(byte[] buffer) throws IOException {
270        return read(buffer, 0, buffer.length);
271    }
272
273    /**
274     * Reads up to {@code byteCount} bytes from the current position in this file
275     * and stores them in the byte array {@code buffer} starting at {@code
276     * byteOffset}. Blocks until at least one byte has been
277     * read, the end of the file is detected, or an exception is thrown.
278     * Returns the number of bytes actually read or -1 if the end of the stream has been reached.
279     * See also {@link #readFully}.
280     *
281     * @throws IndexOutOfBoundsException
282     *     if {@code byteOffset < 0 || byteCount < 0 || byteOffset + byteCount > buffer.length}.
283     * @throws IOException
284     *             if this file is closed or another I/O error occurs.
285     */
286    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
287        return IoBridge.read(fd, buffer, byteOffset, byteCount);
288    }
289
290    /**
291     * Reads a boolean from the current position in this file. Blocks until one
292     * byte has been read, the end of the file is reached or an exception is
293     * thrown.
294     *
295     * @return the next boolean value from this file.
296     * @throws EOFException
297     *             if the end of this file is detected.
298     * @throws IOException
299     *             if this file is closed or another I/O error occurs.
300     * @see #writeBoolean(boolean)
301     */
302    public final boolean readBoolean() throws IOException {
303        int temp = this.read();
304        if (temp < 0) {
305            throw new EOFException();
306        }
307        return temp != 0;
308    }
309
310    /**
311     * Reads an 8-bit byte from the current position in this file. Blocks until
312     * one byte has been read, the end of the file is reached or an exception is
313     * thrown.
314     *
315     * @return the next signed 8-bit byte value from this file.
316     * @throws EOFException
317     *             if the end of this file is detected.
318     * @throws IOException
319     *             if this file is closed or another I/O error occurs.
320     * @see #writeBoolean(boolean)
321     */
322    public final byte readByte() throws IOException {
323        int temp = this.read();
324        if (temp < 0) {
325            throw new EOFException();
326        }
327        return (byte) temp;
328    }
329
330    /**
331     * Reads a big-endian 16-bit character from the current position in this file. Blocks until
332     * two bytes have been read, the end of the file is reached or an exception is
333     * thrown.
334     *
335     * @return the next char value from this file.
336     * @throws EOFException
337     *             if the end of this file is detected.
338     * @throws IOException
339     *             if this file is closed or another I/O error occurs.
340     * @see #writeChar(int)
341     */
342    public final char readChar() throws IOException {
343        return (char) readShort();
344    }
345
346    /**
347     * Reads a big-endian 64-bit double from the current position in this file. Blocks
348     * until eight bytes have been read, the end of the file is reached or an
349     * exception is thrown.
350     *
351     * @return the next double value from this file.
352     * @throws EOFException
353     *             if the end of this file is detected.
354     * @throws IOException
355     *             if this file is closed or another I/O error occurs.
356     * @see #writeDouble(double)
357     */
358    public final double readDouble() throws IOException {
359        return Double.longBitsToDouble(readLong());
360    }
361
362    /**
363     * Reads a big-endian 32-bit float from the current position in this file. Blocks
364     * until four bytes have been read, the end of the file is reached or an
365     * exception is thrown.
366     *
367     * @return the next float value from this file.
368     * @throws EOFException
369     *             if the end of this file is detected.
370     * @throws IOException
371     *             if this file is closed or another I/O error occurs.
372     * @see #writeFloat(float)
373     */
374    public final float readFloat() throws IOException {
375        return Float.intBitsToFloat(readInt());
376    }
377
378    /**
379     * Equivalent to {@code readFully(dst, 0, dst.length);}.
380     */
381    public final void readFully(byte[] dst) throws IOException {
382        readFully(dst, 0, dst.length);
383    }
384
385    /**
386     * Reads {@code byteCount} bytes from this stream and stores them in the byte
387     * array {@code dst} starting at {@code offset}. If {@code byteCount} is zero, then this
388     * method returns without reading any bytes. Otherwise, this method blocks until
389     * {@code byteCount} bytes have been read. If insufficient bytes are available,
390     * {@code EOFException} is thrown. If an I/O error occurs, {@code IOException} is
391     * thrown. When an exception is thrown, some bytes may have been consumed from the stream
392     * and written into the array.
393     *
394     * @param dst
395     *            the byte array into which the data is read.
396     * @param offset
397     *            the offset in {@code dst} at which to store the bytes.
398     * @param byteCount
399     *            the number of bytes to read.
400     * @throws EOFException
401     *             if the end of the source stream is reached before enough
402     *             bytes have been read.
403     * @throws IndexOutOfBoundsException
404     *             if {@code offset < 0} or {@code byteCount < 0}, or
405     *             {@code offset + byteCount > dst.length}.
406     * @throws IOException
407     *             if a problem occurs while reading from this stream.
408     * @throws NullPointerException
409     *             if {@code dst} is null.
410     */
411    public final void readFully(byte[] dst, int offset, int byteCount) throws IOException {
412        Arrays.checkOffsetAndCount(dst.length, offset, byteCount);
413        while (byteCount > 0) {
414            int result = read(dst, offset, byteCount);
415            if (result < 0) {
416                throw new EOFException();
417            }
418            offset += result;
419            byteCount -= result;
420        }
421    }
422
423    /**
424     * Reads a big-endian 32-bit integer from the current position in this file. Blocks
425     * until four bytes have been read, the end of the file is reached or an
426     * exception is thrown.
427     *
428     * @return the next int value from this file.
429     * @throws EOFException
430     *             if the end of this file is detected.
431     * @throws IOException
432     *             if this file is closed or another I/O error occurs.
433     * @see #writeInt(int)
434     */
435    public final int readInt() throws IOException {
436        readFully(scratch, 0, SizeOf.INT);
437        return Memory.peekInt(scratch, 0, ByteOrder.BIG_ENDIAN);
438    }
439
440    /**
441     * Reads a line of text form the current position in this file. A line is
442     * represented by zero or more characters followed by {@code '\n'}, {@code
443     * '\r'}, {@code "\r\n"} or the end of file marker. The string does not
444     * include the line terminating sequence.
445     * <p>
446     * Blocks until a line terminating sequence has been read, the end of the
447     * file is reached or an exception is thrown.
448     *
449     * @return the contents of the line or {@code null} if no characters have
450     *         been read before the end of the file has been reached.
451     * @throws IOException
452     *             if this file is closed or another I/O error occurs.
453     */
454    public final String readLine() throws IOException {
455        StringBuilder line = new StringBuilder(80); // Typical line length
456        boolean foundTerminator = false;
457        long unreadPosition = 0;
458        while (true) {
459            int nextByte = read();
460            switch (nextByte) {
461                case -1:
462                    return line.length() != 0 ? line.toString() : null;
463                case (byte) '\r':
464                    if (foundTerminator) {
465                        seek(unreadPosition);
466                        return line.toString();
467                    }
468                    foundTerminator = true;
469                    /* Have to be able to peek ahead one byte */
470                    unreadPosition = getFilePointer();
471                    break;
472                case (byte) '\n':
473                    return line.toString();
474                default:
475                    if (foundTerminator) {
476                        seek(unreadPosition);
477                        return line.toString();
478                    }
479                    line.append((char) nextByte);
480            }
481        }
482    }
483
484    /**
485     * Reads a big-endian 64-bit long from the current position in this file. Blocks until
486     * eight bytes have been read, the end of the file is reached or an
487     * exception is thrown.
488     *
489     * @return the next long value from this file.
490     * @throws EOFException
491     *             if the end of this file is detected.
492     * @throws IOException
493     *             if this file is closed or another I/O error occurs.
494     * @see #writeLong(long)
495     */
496    public final long readLong() throws IOException {
497        readFully(scratch, 0, SizeOf.LONG);
498        return Memory.peekLong(scratch, 0, ByteOrder.BIG_ENDIAN);
499    }
500
501    /**
502     * Reads a big-endian 16-bit short from the current position in this file. Blocks until
503     * two bytes have been read, the end of the file is reached or an exception
504     * is thrown.
505     *
506     * @return the next short value from this file.
507     * @throws EOFException
508     *             if the end of this file is detected.
509     * @throws IOException
510     *             if this file is closed or another I/O error occurs.
511     * @see #writeShort(int)
512     */
513    public final short readShort() throws IOException {
514        readFully(scratch, 0, SizeOf.SHORT);
515        return Memory.peekShort(scratch, 0, ByteOrder.BIG_ENDIAN);
516    }
517
518    /**
519     * Reads an unsigned 8-bit byte from the current position in this file and
520     * returns it as an integer. Blocks until one byte has been read, the end of
521     * the file is reached or an exception is thrown.
522     *
523     * @return the next unsigned byte value from this file as an int.
524     * @throws EOFException
525     *             if the end of this file is detected.
526     * @throws IOException
527     *             if this file is closed or another I/O error occurs.
528     * @see #writeByte(int)
529     */
530    public final int readUnsignedByte() throws IOException {
531        int temp = this.read();
532        if (temp < 0) {
533            throw new EOFException();
534        }
535        return temp;
536    }
537
538    /**
539     * Reads an unsigned big-endian 16-bit short from the current position in this file and
540     * returns it as an integer. Blocks until two bytes have been read, the end of
541     * the file is reached or an exception is thrown.
542     *
543     * @return the next unsigned short value from this file as an int.
544     * @throws EOFException
545     *             if the end of this file is detected.
546     * @throws IOException
547     *             if this file is closed or another I/O error occurs.
548     * @see #writeShort(int)
549     */
550    public final int readUnsignedShort() throws IOException {
551        return ((int) readShort()) & 0xffff;
552    }
553
554    /**
555     * Reads a string that is encoded in {@link DataInput modified UTF-8} from
556     * this file. The number of bytes that must be read for the complete string
557     * is determined by the first two bytes read from the file. Blocks until all
558     * required bytes have been read, the end of the file is reached or an
559     * exception is thrown.
560     *
561     * @return the next string encoded in {@link DataInput modified UTF-8} from
562     *         this file.
563     * @throws EOFException
564     *             if the end of this file is detected.
565     * @throws IOException
566     *             if this file is closed or another I/O error occurs.
567     * @throws UTFDataFormatException
568     *             if the bytes read cannot be decoded into a character string.
569     * @see #writeUTF(String)
570     */
571    public final String readUTF() throws IOException {
572        int utfSize = readUnsignedShort();
573        if (utfSize == 0) {
574            return "";
575        }
576        byte[] buf = new byte[utfSize];
577        if (read(buf, 0, buf.length) != buf.length) {
578            throw new EOFException();
579        }
580        return ModifiedUtf8.decode(buf, new char[utfSize], 0, utfSize);
581    }
582
583    /**
584     * Moves this file's file pointer to a new position, from where following
585     * {@code read}, {@code write} or {@code skip} operations are done. The
586     * position may be greater than the current length of the file, but the
587     * file's length will only change if the moving of the pointer is followed
588     * by a {@code write} operation.
589     *
590     * @param offset
591     *            the new file pointer position.
592     * @throws IOException
593     *             if this file is closed, {@code pos < 0} or another I/O error
594     *             occurs.
595     */
596    public void seek(long offset) throws IOException {
597        if (offset < 0) {
598            throw new IOException("offset < 0: " + offset);
599        }
600        try {
601            Libcore.os.lseek(fd, offset, SEEK_SET);
602        } catch (ErrnoException errnoException) {
603            throw errnoException.rethrowAsIOException();
604        }
605    }
606
607    /**
608     * Sets the length of this file to {@code newLength}. If the current file is
609     * smaller, it is expanded but the contents from the previous end of the
610     * file to the new end are undefined. The file is truncated if its current
611     * size is bigger than {@code newLength}. If the current file pointer
612     * position is in the truncated part, it is set to the end of the file.
613     *
614     * @param newLength
615     *            the new file length in bytes.
616     * @throws IllegalArgumentException
617     *             if {@code newLength < 0}.
618     * @throws IOException
619     *             if this file is closed or another I/O error occurs.
620     */
621    public void setLength(long newLength) throws IOException {
622        if (newLength < 0) {
623            throw new IllegalArgumentException("newLength < 0");
624        }
625        try {
626            Libcore.os.ftruncate(fd, newLength);
627        } catch (ErrnoException errnoException) {
628            throw errnoException.rethrowAsIOException();
629        }
630
631        long filePointer = getFilePointer();
632        if (filePointer > newLength) {
633            seek(newLength);
634        }
635
636        // if we are in "rws" mode, attempt to sync file+metadata
637        if (syncMetadata) {
638            fd.sync();
639        }
640    }
641
642    /**
643     * Skips over {@code count} bytes in this file. Less than {@code count}
644     * bytes are skipped if the end of the file is reached or an exception is
645     * thrown during the operation. Nothing is done if {@code count} is
646     * negative.
647     *
648     * @param count
649     *            the number of bytes to skip.
650     * @return the number of bytes actually skipped.
651     * @throws IOException
652     *             if this file is closed or another I/O error occurs.
653     */
654    public int skipBytes(int count) throws IOException {
655        if (count > 0) {
656            long currentPos = getFilePointer(), eof = length();
657            int newCount = (int) ((currentPos + count > eof) ? eof - currentPos : count);
658            seek(currentPos + newCount);
659            return newCount;
660        }
661        return 0;
662    }
663
664    /**
665     * Writes the entire contents of the byte array {@code buffer} to this file,
666     * starting at the current file pointer.
667     *
668     * @param buffer
669     *            the buffer to write.
670     * @throws IOException
671     *             if an I/O error occurs while writing to this file.
672     */
673    public void write(byte[] buffer) throws IOException {
674        write(buffer, 0, buffer.length);
675    }
676
677    /**
678     * Writes {@code byteCount} bytes from the byte array {@code buffer} to this
679     * file, starting at the current file pointer and using {@code byteOffset} as
680     * the first position within {@code buffer} to get bytes.
681     *
682     * @throws IndexOutOfBoundsException
683     *             if {@code byteCount < 0}, {@code byteOffset < 0} or {@code byteCount +
684     *             byteOffset} is greater than the size of {@code buffer}.
685     * @throws IOException
686     *             if an I/O error occurs while writing to this file.
687     */
688    public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
689        IoBridge.write(fd, buffer, byteOffset, byteCount);
690        // if we are in "rws" mode, attempt to sync file+metadata
691        if (syncMetadata) {
692            fd.sync();
693        }
694    }
695
696    /**
697     * Writes a byte to this file, starting at the current file pointer. Only
698     * the least significant byte of the integer {@code oneByte} is written.
699     *
700     * @param oneByte
701     *            the byte to write to this file.
702     * @throws IOException
703     *             if this file is closed or another I/O error occurs.
704     * @see #read()
705     */
706    public void write(int oneByte) throws IOException {
707        scratch[0] = (byte) (oneByte & 0xff);
708        write(scratch, 0, 1);
709    }
710
711    /**
712     * Writes a boolean to this file as a single byte (1 for true, 0 for false), starting at the
713     * current file pointer.
714     *
715     * @param val
716     *            the boolean to write to this file.
717     * @throws IOException
718     *             if this file is closed or another I/O error occurs.
719     * @see #readBoolean()
720     */
721    public final void writeBoolean(boolean val) throws IOException {
722        write(val ? 1 : 0);
723    }
724
725    /**
726     * Writes an 8-bit byte to this file, starting at the current file pointer.
727     * Only the least significant byte of the integer {@code val} is written.
728     *
729     * @param val
730     *            the byte to write to this file.
731     * @throws IOException
732     *             if this file is closed or another I/O error occurs.
733     * @see #readByte()
734     * @see #readUnsignedByte()
735     */
736    public final void writeByte(int val) throws IOException {
737        write(val & 0xFF);
738    }
739
740    /**
741     * Writes the low order 8-bit bytes from a string to this file, starting at
742     * the current file pointer.
743     *
744     * @param str
745     *            the string containing the bytes to write to this file
746     * @throws IOException
747     *             if an I/O error occurs while writing to this file.
748     */
749    public final void writeBytes(String str) throws IOException {
750        byte[] bytes = new byte[str.length()];
751        for (int index = 0; index < str.length(); index++) {
752            bytes[index] = (byte) (str.charAt(index) & 0xFF);
753        }
754        write(bytes);
755    }
756
757    /**
758     * Writes a big-endian 16-bit character to this file, starting at the current file
759     * pointer. Only the two least significant bytes of the integer {@code val}
760     * are written, with the high byte first.
761     *
762     * @param val
763     *            the char to write to this file.
764     * @throws IOException
765     *             if an I/O error occurs while writing to this file.
766     * @see #readChar()
767     */
768    public final void writeChar(int val) throws IOException {
769        writeShort(val);
770    }
771
772    /**
773     * Writes big-endian 16-bit characters from {@code str} to this file, starting at the
774     * current file pointer.
775     *
776     * @param str
777     *            the string to write to this file.
778     * @throws IOException
779     *             if an I/O error occurs while writing to this file.
780     * @see #readChar()
781     */
782    public final void writeChars(String str) throws IOException {
783        write(str.getBytes("UTF-16BE"));
784    }
785
786    /**
787     * Writes a big-endian 64-bit double to this file, starting at the current file
788     * pointer. The bytes are those returned by
789     * {@link Double#doubleToLongBits(double)}, meaning a canonical NaN is used.
790     *
791     * @param val
792     *            the double to write to this file.
793     * @throws IOException
794     *             if an I/O error occurs while writing to this file.
795     * @see #readDouble()
796     */
797    public final void writeDouble(double val) throws IOException {
798        writeLong(Double.doubleToLongBits(val));
799    }
800
801    /**
802     * Writes a big-endian 32-bit float to this file, starting at the current file pointer.
803     * The bytes are those returned by {@link Float#floatToIntBits(float)}, meaning a canonical NaN
804     * is used.
805     *
806     * @param val
807     *            the float to write to this file.
808     * @throws IOException
809     *             if an I/O error occurs while writing to this file.
810     * @see #readFloat()
811     */
812    public final void writeFloat(float val) throws IOException {
813        writeInt(Float.floatToIntBits(val));
814    }
815
816    /**
817     * Writes a big-endian 32-bit integer to this file, starting at the current file
818     * pointer.
819     *
820     * @param val
821     *            the int to write to this file.
822     * @throws IOException
823     *             if an I/O error occurs while writing to this file.
824     * @see #readInt()
825     */
826    public final void writeInt(int val) throws IOException {
827        Memory.pokeInt(scratch, 0, val, ByteOrder.BIG_ENDIAN);
828        write(scratch, 0, SizeOf.INT);
829    }
830
831    /**
832     * Writes a big-endian 64-bit long to this file, starting at the current file
833     * pointer.
834     *
835     * @param val
836     *            the long to write to this file.
837     * @throws IOException
838     *             if an I/O error occurs while writing to this file.
839     * @see #readLong()
840     */
841    public final void writeLong(long val) throws IOException {
842        Memory.pokeLong(scratch, 0, val, ByteOrder.BIG_ENDIAN);
843        write(scratch, 0, SizeOf.LONG);
844    }
845
846    /**
847     * Writes a big-endian 16-bit short to this file, starting at the current file
848     * pointer. Only the two least significant bytes of the integer {@code val}
849     * are written.
850     *
851     * @param val
852     *            the short to write to this file.
853     * @throws IOException
854     *             if an I/O error occurs while writing to this file.
855     * @see #readShort()
856     * @see DataInput#readUnsignedShort()
857     */
858    public final void writeShort(int val) throws IOException {
859        Memory.pokeShort(scratch, 0, (short) val, ByteOrder.BIG_ENDIAN);
860        write(scratch, 0, SizeOf.SHORT);
861    }
862
863    /**
864     * Writes a string encoded with {@link DataInput modified UTF-8} to this
865     * file, starting at the current file pointer.
866     *
867     * @param str
868     *            the string to write in {@link DataInput modified UTF-8}
869     *            format.
870     * @throws IOException
871     *             if an I/O error occurs while writing to this file.
872     * @throws UTFDataFormatException
873     *             if the encoded string is longer than 65535 bytes.
874     * @see #readUTF()
875     */
876    public final void writeUTF(String str) throws IOException {
877        write(ModifiedUtf8.encode(str));
878    }
879}
880