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     *
264     * @param buffer
265     *            the byte array in which to store the bytes read.
266     * @return the number of bytes actually read or -1 if the end of the file
267     *         has been reached.
268     * @throws IOException
269     *             if this file is closed or another I/O error occurs.
270     */
271    public int read(byte[] buffer) throws IOException {
272        return read(buffer, 0, buffer.length);
273    }
274
275    /**
276     * Reads at most {@code byteCount} bytes from the current position in this file
277     * and stores them in the byte array {@code buffer} starting at {@code
278     * byteOffset}. Blocks until at least one byte has been
279     * read, the end of the file is detected, or an exception is thrown.
280     *
281     * @return the number of bytes actually read or -1 if the end of the stream
282     *         has been reached.
283     * @throws IndexOutOfBoundsException
284     *             if {@code byteOffset < 0} or {@code byteCount < 0}, or if {@code
285     *             byteOffset + byteCount} is greater than the size of {@code buffer}.
286     * @throws IOException
287     *             if this file is closed or another I/O error occurs.
288     */
289    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
290        return IoBridge.read(fd, buffer, byteOffset, byteCount);
291    }
292
293    /**
294     * Reads a boolean from the current position in this file. Blocks until one
295     * byte has been read, the end of the file is reached or an exception is
296     * thrown.
297     *
298     * @return the next boolean value from this file.
299     * @throws EOFException
300     *             if the end of this file is detected.
301     * @throws IOException
302     *             if this file is closed or another I/O error occurs.
303     * @see #writeBoolean(boolean)
304     */
305    public final boolean readBoolean() throws IOException {
306        int temp = this.read();
307        if (temp < 0) {
308            throw new EOFException();
309        }
310        return temp != 0;
311    }
312
313    /**
314     * Reads an 8-bit byte from the current position in this file. Blocks until
315     * one byte has been read, the end of the file is reached or an exception is
316     * thrown.
317     *
318     * @return the next signed 8-bit byte value from this file.
319     * @throws EOFException
320     *             if the end of this file is detected.
321     * @throws IOException
322     *             if this file is closed or another I/O error occurs.
323     * @see #writeBoolean(boolean)
324     */
325    public final byte readByte() throws IOException {
326        int temp = this.read();
327        if (temp < 0) {
328            throw new EOFException();
329        }
330        return (byte) temp;
331    }
332
333    /**
334     * Reads a big-endian 16-bit character from the current position in this file. Blocks until
335     * two bytes have been read, the end of the file is reached or an exception is
336     * thrown.
337     *
338     * @return the next char value from this file.
339     * @throws EOFException
340     *             if the end of this file is detected.
341     * @throws IOException
342     *             if this file is closed or another I/O error occurs.
343     * @see #writeChar(int)
344     */
345    public final char readChar() throws IOException {
346        return (char) readShort();
347    }
348
349    /**
350     * Reads a big-endian 64-bit double from the current position in this file. Blocks
351     * until eight bytes have been read, the end of the file is reached or an
352     * exception is thrown.
353     *
354     * @return the next double value from this file.
355     * @throws EOFException
356     *             if the end of this file is detected.
357     * @throws IOException
358     *             if this file is closed or another I/O error occurs.
359     * @see #writeDouble(double)
360     */
361    public final double readDouble() throws IOException {
362        return Double.longBitsToDouble(readLong());
363    }
364
365    /**
366     * Reads a big-endian 32-bit float from the current position in this file. Blocks
367     * until four bytes have been read, the end of the file is reached or an
368     * exception is thrown.
369     *
370     * @return the next float value from this file.
371     * @throws EOFException
372     *             if the end of this file is detected.
373     * @throws IOException
374     *             if this file is closed or another I/O error occurs.
375     * @see #writeFloat(float)
376     */
377    public final float readFloat() throws IOException {
378        return Float.intBitsToFloat(readInt());
379    }
380
381    /**
382     * Equivalent to {@code readFully(dst, 0, dst.length);}.
383     */
384    public final void readFully(byte[] dst) throws IOException {
385        readFully(dst, 0, dst.length);
386    }
387
388    /**
389     * Reads {@code byteCount} bytes from this stream and stores them in the byte
390     * array {@code dst} starting at {@code offset}. If {@code byteCount} is zero, then this
391     * method returns without reading any bytes. Otherwise, this method blocks until
392     * {@code byteCount} bytes have been read. If insufficient bytes are available,
393     * {@code EOFException} is thrown. If an I/O error occurs, {@code IOException} is
394     * thrown. When an exception is thrown, some bytes may have been consumed from the stream
395     * and written into the array.
396     *
397     * @param dst
398     *            the byte array into which the data is read.
399     * @param offset
400     *            the offset in {@code dst} at which to store the bytes.
401     * @param byteCount
402     *            the number of bytes to read.
403     * @throws EOFException
404     *             if the end of the source stream is reached before enough
405     *             bytes have been read.
406     * @throws IndexOutOfBoundsException
407     *             if {@code offset < 0} or {@code byteCount < 0}, or
408     *             {@code offset + byteCount > dst.length}.
409     * @throws IOException
410     *             if a problem occurs while reading from this stream.
411     * @throws NullPointerException
412     *             if {@code dst} is null.
413     */
414    public final void readFully(byte[] dst, int offset, int byteCount) throws IOException {
415        Arrays.checkOffsetAndCount(dst.length, offset, byteCount);
416        while (byteCount > 0) {
417            int result = read(dst, offset, byteCount);
418            if (result < 0) {
419                throw new EOFException();
420            }
421            offset += result;
422            byteCount -= result;
423        }
424    }
425
426    /**
427     * Reads a big-endian 32-bit integer from the current position in this file. Blocks
428     * until four bytes have been read, the end of the file is reached or an
429     * exception is thrown.
430     *
431     * @return the next int value from this file.
432     * @throws EOFException
433     *             if the end of this file is detected.
434     * @throws IOException
435     *             if this file is closed or another I/O error occurs.
436     * @see #writeInt(int)
437     */
438    public final int readInt() throws IOException {
439        readFully(scratch, 0, SizeOf.INT);
440        return Memory.peekInt(scratch, 0, ByteOrder.BIG_ENDIAN);
441    }
442
443    /**
444     * Reads a line of text form the current position in this file. A line is
445     * represented by zero or more characters followed by {@code '\n'}, {@code
446     * '\r'}, {@code "\r\n"} or the end of file marker. The string does not
447     * include the line terminating sequence.
448     * <p>
449     * Blocks until a line terminating sequence has been read, the end of the
450     * file is reached or an exception is thrown.
451     *
452     * @return the contents of the line or {@code null} if no characters have
453     *         been read before the end of the file has been reached.
454     * @throws IOException
455     *             if this file is closed or another I/O error occurs.
456     */
457    public final String readLine() throws IOException {
458        StringBuilder line = new StringBuilder(80); // Typical line length
459        boolean foundTerminator = false;
460        long unreadPosition = 0;
461        while (true) {
462            int nextByte = read();
463            switch (nextByte) {
464                case -1:
465                    return line.length() != 0 ? line.toString() : null;
466                case (byte) '\r':
467                    if (foundTerminator) {
468                        seek(unreadPosition);
469                        return line.toString();
470                    }
471                    foundTerminator = true;
472                    /* Have to be able to peek ahead one byte */
473                    unreadPosition = getFilePointer();
474                    break;
475                case (byte) '\n':
476                    return line.toString();
477                default:
478                    if (foundTerminator) {
479                        seek(unreadPosition);
480                        return line.toString();
481                    }
482                    line.append((char) nextByte);
483            }
484        }
485    }
486
487    /**
488     * Reads a big-endian 64-bit long from the current position in this file. Blocks until
489     * eight bytes have been read, the end of the file is reached or an
490     * exception is thrown.
491     *
492     * @return the next long value from this file.
493     * @throws EOFException
494     *             if the end of this file is detected.
495     * @throws IOException
496     *             if this file is closed or another I/O error occurs.
497     * @see #writeLong(long)
498     */
499    public final long readLong() throws IOException {
500        readFully(scratch, 0, SizeOf.LONG);
501        return Memory.peekLong(scratch, 0, ByteOrder.BIG_ENDIAN);
502    }
503
504    /**
505     * Reads a big-endian 16-bit short from the current position in this file. Blocks until
506     * two bytes have been read, the end of the file is reached or an exception
507     * is thrown.
508     *
509     * @return the next short value from this file.
510     * @throws EOFException
511     *             if the end of this file is detected.
512     * @throws IOException
513     *             if this file is closed or another I/O error occurs.
514     * @see #writeShort(int)
515     */
516    public final short readShort() throws IOException {
517        readFully(scratch, 0, SizeOf.SHORT);
518        return Memory.peekShort(scratch, 0, ByteOrder.BIG_ENDIAN);
519    }
520
521    /**
522     * Reads an unsigned 8-bit byte from the current position in this file and
523     * returns it as an integer. Blocks until one byte has been read, the end of
524     * the file is reached or an exception is thrown.
525     *
526     * @return the next unsigned byte value from this file as an int.
527     * @throws EOFException
528     *             if the end of this file is detected.
529     * @throws IOException
530     *             if this file is closed or another I/O error occurs.
531     * @see #writeByte(int)
532     */
533    public final int readUnsignedByte() throws IOException {
534        int temp = this.read();
535        if (temp < 0) {
536            throw new EOFException();
537        }
538        return temp;
539    }
540
541    /**
542     * Reads an unsigned big-endian 16-bit short from the current position in this file and
543     * returns it as an integer. Blocks until two bytes have been read, the end of
544     * the file is reached or an exception is thrown.
545     *
546     * @return the next unsigned short value from this file as an int.
547     * @throws EOFException
548     *             if the end of this file is detected.
549     * @throws IOException
550     *             if this file is closed or another I/O error occurs.
551     * @see #writeShort(int)
552     */
553    public final int readUnsignedShort() throws IOException {
554        return ((int) readShort()) & 0xffff;
555    }
556
557    /**
558     * Reads a string that is encoded in {@link DataInput modified UTF-8} from
559     * this file. The number of bytes that must be read for the complete string
560     * is determined by the first two bytes read from the file. Blocks until all
561     * required bytes have been read, the end of the file is reached or an
562     * exception is thrown.
563     *
564     * @return the next string encoded in {@link DataInput modified UTF-8} from
565     *         this file.
566     * @throws EOFException
567     *             if the end of this file is detected.
568     * @throws IOException
569     *             if this file is closed or another I/O error occurs.
570     * @throws UTFDataFormatException
571     *             if the bytes read cannot be decoded into a character string.
572     * @see #writeUTF(String)
573     */
574    public final String readUTF() throws IOException {
575        int utfSize = readUnsignedShort();
576        if (utfSize == 0) {
577            return "";
578        }
579        byte[] buf = new byte[utfSize];
580        if (read(buf, 0, buf.length) != buf.length) {
581            throw new EOFException();
582        }
583        return ModifiedUtf8.decode(buf, new char[utfSize], 0, utfSize);
584    }
585
586    /**
587     * Moves this file's file pointer to a new position, from where following
588     * {@code read}, {@code write} or {@code skip} operations are done. The
589     * position may be greater than the current length of the file, but the
590     * file's length will only change if the moving of the pointer is followed
591     * by a {@code write} operation.
592     *
593     * @param offset
594     *            the new file pointer position.
595     * @throws IOException
596     *             if this file is closed, {@code pos < 0} or another I/O error
597     *             occurs.
598     */
599    public void seek(long offset) throws IOException {
600        if (offset < 0) {
601            throw new IOException("offset < 0: " + offset);
602        }
603        try {
604            Libcore.os.lseek(fd, offset, SEEK_SET);
605        } catch (ErrnoException errnoException) {
606            throw errnoException.rethrowAsIOException();
607        }
608    }
609
610    /**
611     * Sets the length of this file to {@code newLength}. If the current file is
612     * smaller, it is expanded but the contents from the previous end of the
613     * file to the new end are undefined. The file is truncated if its current
614     * size is bigger than {@code newLength}. If the current file pointer
615     * position is in the truncated part, it is set to the end of the file.
616     *
617     * @param newLength
618     *            the new file length in bytes.
619     * @throws IllegalArgumentException
620     *             if {@code newLength < 0}.
621     * @throws IOException
622     *             if this file is closed or another I/O error occurs.
623     */
624    public void setLength(long newLength) throws IOException {
625        if (newLength < 0) {
626            throw new IllegalArgumentException("newLength < 0");
627        }
628        try {
629            Libcore.os.ftruncate(fd, newLength);
630        } catch (ErrnoException errnoException) {
631            throw errnoException.rethrowAsIOException();
632        }
633
634        long filePointer = getFilePointer();
635        if (filePointer > newLength) {
636            seek(newLength);
637        }
638
639        // if we are in "rws" mode, attempt to sync file+metadata
640        if (syncMetadata) {
641            fd.sync();
642        }
643    }
644
645    /**
646     * Skips over {@code count} bytes in this file. Less than {@code count}
647     * bytes are skipped if the end of the file is reached or an exception is
648     * thrown during the operation. Nothing is done if {@code count} is
649     * negative.
650     *
651     * @param count
652     *            the number of bytes to skip.
653     * @return the number of bytes actually skipped.
654     * @throws IOException
655     *             if this file is closed or another I/O error occurs.
656     */
657    public int skipBytes(int count) throws IOException {
658        if (count > 0) {
659            long currentPos = getFilePointer(), eof = length();
660            int newCount = (int) ((currentPos + count > eof) ? eof - currentPos : count);
661            seek(currentPos + newCount);
662            return newCount;
663        }
664        return 0;
665    }
666
667    /**
668     * Writes the entire contents of the byte array {@code buffer} to this file,
669     * starting at the current file pointer.
670     *
671     * @param buffer
672     *            the buffer to write.
673     * @throws IOException
674     *             if an I/O error occurs while writing to this file.
675     */
676    public void write(byte[] buffer) throws IOException {
677        write(buffer, 0, buffer.length);
678    }
679
680    /**
681     * Writes {@code byteCount} bytes from the byte array {@code buffer} to this
682     * file, starting at the current file pointer and using {@code byteOffset} as
683     * the first position within {@code buffer} to get bytes.
684     *
685     * @throws IndexOutOfBoundsException
686     *             if {@code byteCount < 0}, {@code byteOffset < 0} or {@code byteCount +
687     *             byteOffset} is greater than the size of {@code buffer}.
688     * @throws IOException
689     *             if an I/O error occurs while writing to this file.
690     */
691    public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
692        IoBridge.write(fd, buffer, byteOffset, byteCount);
693        // if we are in "rws" mode, attempt to sync file+metadata
694        if (syncMetadata) {
695            fd.sync();
696        }
697    }
698
699    /**
700     * Writes a byte to this file, starting at the current file pointer. Only
701     * the least significant byte of the integer {@code oneByte} is written.
702     *
703     * @param oneByte
704     *            the byte to write to this file.
705     * @throws IOException
706     *             if this file is closed or another I/O error occurs.
707     * @see #read()
708     */
709    public void write(int oneByte) throws IOException {
710        scratch[0] = (byte) (oneByte & 0xff);
711        write(scratch, 0, 1);
712    }
713
714    /**
715     * Writes a boolean to this file as a single byte (1 for true, 0 for false), starting at the
716     * current file pointer.
717     *
718     * @param val
719     *            the boolean to write to this file.
720     * @throws IOException
721     *             if this file is closed or another I/O error occurs.
722     * @see #readBoolean()
723     */
724    public final void writeBoolean(boolean val) throws IOException {
725        write(val ? 1 : 0);
726    }
727
728    /**
729     * Writes an 8-bit byte to this file, starting at the current file pointer.
730     * Only the least significant byte of the integer {@code val} is written.
731     *
732     * @param val
733     *            the byte to write to this file.
734     * @throws IOException
735     *             if this file is closed or another I/O error occurs.
736     * @see #readByte()
737     * @see #readUnsignedByte()
738     */
739    public final void writeByte(int val) throws IOException {
740        write(val & 0xFF);
741    }
742
743    /**
744     * Writes the low order 8-bit bytes from a string to this file, starting at
745     * the current file pointer.
746     *
747     * @param str
748     *            the string containing the bytes to write to this file
749     * @throws IOException
750     *             if an I/O error occurs while writing to this file.
751     */
752    public final void writeBytes(String str) throws IOException {
753        byte[] bytes = new byte[str.length()];
754        for (int index = 0; index < str.length(); index++) {
755            bytes[index] = (byte) (str.charAt(index) & 0xFF);
756        }
757        write(bytes);
758    }
759
760    /**
761     * Writes a big-endian 16-bit character to this file, starting at the current file
762     * pointer. Only the two least significant bytes of the integer {@code val}
763     * are written, with the high byte first.
764     *
765     * @param val
766     *            the char to write to this file.
767     * @throws IOException
768     *             if an I/O error occurs while writing to this file.
769     * @see #readChar()
770     */
771    public final void writeChar(int val) throws IOException {
772        writeShort(val);
773    }
774
775    /**
776     * Writes big-endian 16-bit characters from {@code str} to this file, starting at the
777     * current file pointer.
778     *
779     * @param str
780     *            the string to write to this file.
781     * @throws IOException
782     *             if an I/O error occurs while writing to this file.
783     * @see #readChar()
784     */
785    public final void writeChars(String str) throws IOException {
786        write(str.getBytes("UTF-16BE"));
787    }
788
789    /**
790     * Writes a big-endian 64-bit double to this file, starting at the current file
791     * pointer. The bytes are those returned by
792     * {@link Double#doubleToLongBits(double)}, meaning a canonical NaN is used.
793     *
794     * @param val
795     *            the double to write to this file.
796     * @throws IOException
797     *             if an I/O error occurs while writing to this file.
798     * @see #readDouble()
799     */
800    public final void writeDouble(double val) throws IOException {
801        writeLong(Double.doubleToLongBits(val));
802    }
803
804    /**
805     * Writes a big-endian 32-bit float to this file, starting at the current file pointer.
806     * The bytes are those returned by {@link Float#floatToIntBits(float)}, meaning a canonical NaN
807     * is used.
808     *
809     * @param val
810     *            the float to write to this file.
811     * @throws IOException
812     *             if an I/O error occurs while writing to this file.
813     * @see #readFloat()
814     */
815    public final void writeFloat(float val) throws IOException {
816        writeInt(Float.floatToIntBits(val));
817    }
818
819    /**
820     * Writes a big-endian 32-bit integer to this file, starting at the current file
821     * pointer.
822     *
823     * @param val
824     *            the int to write to this file.
825     * @throws IOException
826     *             if an I/O error occurs while writing to this file.
827     * @see #readInt()
828     */
829    public final void writeInt(int val) throws IOException {
830        Memory.pokeInt(scratch, 0, val, ByteOrder.BIG_ENDIAN);
831        write(scratch, 0, SizeOf.INT);
832    }
833
834    /**
835     * Writes a big-endian 64-bit long to this file, starting at the current file
836     * pointer.
837     *
838     * @param val
839     *            the long to write to this file.
840     * @throws IOException
841     *             if an I/O error occurs while writing to this file.
842     * @see #readLong()
843     */
844    public final void writeLong(long val) throws IOException {
845        Memory.pokeLong(scratch, 0, val, ByteOrder.BIG_ENDIAN);
846        write(scratch, 0, SizeOf.LONG);
847    }
848
849    /**
850     * Writes a big-endian 16-bit short to this file, starting at the current file
851     * pointer. Only the two least significant bytes of the integer {@code val}
852     * are written.
853     *
854     * @param val
855     *            the short to write to this file.
856     * @throws IOException
857     *             if an I/O error occurs while writing to this file.
858     * @see #readShort()
859     * @see DataInput#readUnsignedShort()
860     */
861    public final void writeShort(int val) throws IOException {
862        Memory.pokeShort(scratch, 0, (short) val, ByteOrder.BIG_ENDIAN);
863        write(scratch, 0, SizeOf.SHORT);
864    }
865
866    /**
867     * Writes a string encoded with {@link DataInput modified UTF-8} to this
868     * file, starting at the current file pointer.
869     *
870     * @param str
871     *            the string to write in {@link DataInput modified UTF-8}
872     *            format.
873     * @throws IOException
874     *             if an I/O error occurs while writing to this file.
875     * @throws UTFDataFormatException
876     *             if the encoded string is longer than 65535 bytes.
877     * @see #readUTF()
878     */
879    public final void writeUTF(String str) throws IOException {
880        write(ModifiedUtf8.encode(str));
881    }
882}
883