1 /*
2  * Copyright (C) 2010 The Android Open Source Project
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 
17 package libcore.io;
18 
19 import android.system.ErrnoException;
20 import dalvik.annotation.compat.UnsupportedAppUsage;
21 import java.io.FileDescriptor;
22 import java.io.IOException;
23 import java.io.RandomAccessFile;
24 import java.nio.ByteOrder;
25 import java.nio.channels.FileChannel;
26 import java.nio.NioUtils;
27 import libcore.io.Libcore;
28 import libcore.io.Memory;
29 import static android.system.OsConstants.*;
30 
31 /**
32  * A memory-mapped file. Use {@link #mmapRO} to map a file, {@link #close} to unmap a file,
33  * and either {@link #bigEndianIterator} or {@link #littleEndianIterator} to get a seekable
34  * {@link BufferIterator} over the mapped data. This class is not thread safe.
35  */
36 public final class MemoryMappedFile implements AutoCloseable {
37     private boolean closed;
38     private final long address;
39     private final int size;
40 
41     /** Public for layoutlib only. */
MemoryMappedFile(long address, long size)42     public MemoryMappedFile(long address, long size) {
43         this.address = address;
44         // For simplicity when bounds checking, only sizes up to Integer.MAX_VALUE are supported.
45         if (size < 0 || size > Integer.MAX_VALUE) {
46             throw new IllegalArgumentException("Unsupported file size=" + size);
47         }
48         this.size = (int) size;
49     }
50 
51     /**
52      * Use this to mmap the whole file read-only.
53      */
54     @UnsupportedAppUsage
mmapRO(String path)55     public static MemoryMappedFile mmapRO(String path) throws ErrnoException {
56         FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0);
57         try {
58             long size = Libcore.os.fstat(fd).st_size;
59             long address = Libcore.os.mmap(0L, size, PROT_READ, MAP_SHARED, fd, 0);
60             return new MemoryMappedFile(address, size);
61         } finally {
62             Libcore.os.close(fd);
63         }
64     }
65 
66     /**
67      * Unmaps this memory-mapped file using munmap(2). This is a no-op if close has already been
68      * called. Note that this class does <i>not</i> use finalization; you must call {@code close}
69      * yourself.
70      *
71      * Calling this method invalidates any iterators over this {@code MemoryMappedFile}. It is an
72      * error to use such an iterator after calling {@code close}.
73      */
close()74     public void close() throws ErrnoException {
75         if (!closed) {
76             closed = true;
77             Libcore.os.munmap(address, size);
78         }
79     }
80 
isClosed()81     public boolean isClosed() {
82         return closed;
83     }
84 
85     /**
86      * Returns a new iterator that treats the mapped data as big-endian.
87      */
88     @UnsupportedAppUsage
bigEndianIterator()89     public BufferIterator bigEndianIterator() {
90         return new NioBufferIterator(
91                 this, address, size, ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN);
92     }
93 
94     /**
95      * Returns a new iterator that treats the mapped data as little-endian.
96      */
littleEndianIterator()97     public BufferIterator littleEndianIterator() {
98         return new NioBufferIterator(
99                 this, this.address, this.size, ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN);
100     }
101 
102     /** Throws {@link IllegalStateException} if the file is closed. */
checkNotClosed()103     void checkNotClosed() {
104         if (closed) {
105             throw new IllegalStateException("MemoryMappedFile is closed");
106         }
107     }
108 
109     /**
110      * Returns the size in bytes of the memory-mapped region.
111      */
size()112     public int size() {
113         checkNotClosed();
114         return size;
115     }
116 }
117