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