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.nio;
19
20import android.system.ErrnoException;
21import dalvik.system.VMRuntime;
22import java.io.FileDescriptor;
23import java.io.IOException;
24import java.nio.channels.FileChannel.MapMode;
25import libcore.io.Libcore;
26import libcore.io.Memory;
27
28import static android.system.OsConstants.MAP_PRIVATE;
29import static android.system.OsConstants.MAP_SHARED;
30import static android.system.OsConstants.PROT_READ;
31import static android.system.OsConstants.PROT_WRITE;
32
33class MemoryBlock {
34    /**
35     * Handles calling munmap(2) on a memory-mapped region.
36     */
37    private static class MemoryMappedBlock extends MemoryBlock {
38        private MemoryMappedBlock(long address, long byteCount) {
39            super(address, byteCount);
40        }
41
42        @Override public void free() {
43            if (address != 0) {
44                try {
45                    Libcore.os.munmap(address, size);
46                } catch (ErrnoException errnoException) {
47                    // The RI doesn't throw, presumably on the assumption that you can't get into
48                    // a state where munmap(2) could return an error.
49                    throw new AssertionError(errnoException);
50                }
51            }
52            super.free();
53        }
54
55        @Override protected void finalize() throws Throwable {
56            free();
57        }
58    }
59
60    /**
61     * Non-movable heap blocks are byte arrays on the Java heap that the GC
62     * guarantees not to move. Used to implement DirectByteBuffer.
63     *
64     * Losing the strong reference to the array is sufficient
65     * to allow the GC to reclaim the storage. No finalizer needed.
66     */
67    private static class NonMovableHeapBlock extends MemoryBlock {
68        private byte[] array;
69
70        private NonMovableHeapBlock(byte[] array, long address, long byteCount) {
71            super(address, byteCount);
72            this.array = array;
73        }
74
75        @Override public byte[] array() {
76            return array;
77        }
78
79        @Override public void free() {
80            array = null;
81            super.free();
82        }
83    }
84
85    /**
86     * Represents a block of memory we don't own. (We don't take ownership of memory corresponding
87     * to direct buffers created by the JNI NewDirectByteBuffer function.)
88     */
89    private static class UnmanagedBlock extends MemoryBlock {
90        private UnmanagedBlock(long address, long byteCount) {
91            super(address, byteCount);
92        }
93    }
94
95    protected long address;
96    protected final long size;
97    private boolean accessible;
98    private boolean freed;
99
100    public static MemoryBlock mmap(FileDescriptor fd, long offset, long size, MapMode mapMode) throws IOException {
101        if (size == 0) {
102            // You can't mmap(2) a zero-length region, but Java allows it.
103            return new MemoryBlock(0, 0);
104        }
105        // Check just those errors mmap(2) won't detect.
106        if (offset < 0 || size < 0 || offset > Integer.MAX_VALUE || size > Integer.MAX_VALUE) {
107            throw new IllegalArgumentException("offset=" + offset + " size=" + size);
108        }
109        int prot;
110        int flags;
111        if (mapMode == MapMode.PRIVATE) {
112            prot = PROT_READ|PROT_WRITE;
113            flags = MAP_PRIVATE;
114        } else if (mapMode == MapMode.READ_ONLY) {
115            prot = PROT_READ;
116            flags = MAP_SHARED;
117        } else { // mapMode == MapMode.READ_WRITE
118            prot = PROT_READ|PROT_WRITE;
119            flags = MAP_SHARED;
120        }
121        try {
122            long address = Libcore.os.mmap(0L, size, prot, flags, fd, offset);
123            return new MemoryMappedBlock(address, size);
124        } catch (ErrnoException errnoException) {
125            throw errnoException.rethrowAsIOException();
126        }
127    }
128
129    public static MemoryBlock allocate(int byteCount) {
130        VMRuntime runtime = VMRuntime.getRuntime();
131        byte[] array = (byte[]) runtime.newNonMovableArray(byte.class, byteCount);
132        long address = runtime.addressOf(array);
133        return new NonMovableHeapBlock(array, address, byteCount);
134    }
135
136    public static MemoryBlock wrapFromJni(long address, long byteCount) {
137        return new UnmanagedBlock(address, byteCount);
138    }
139
140    private MemoryBlock(long address, long size) {
141        this.address = address;
142        this.size = size;
143        accessible = true;
144        freed = false;
145    }
146
147    // Used to support array/arrayOffset/hasArray for direct buffers.
148    public byte[] array() {
149        return null;
150    }
151
152    public void free() {
153        address = 0;
154        freed = true;
155    }
156
157    public boolean isFreed() {
158        return freed;
159    }
160
161    public boolean isAccessible() {
162        return !isFreed() && accessible;
163    }
164
165    public final void setAccessible(boolean accessible) {
166        this.accessible = accessible;
167    }
168
169    public final void pokeByte(int offset, byte value) {
170        Memory.pokeByte(address + offset, value);
171    }
172
173    public final void pokeByteArray(int offset, byte[] src, int srcOffset, int byteCount) {
174        Memory.pokeByteArray(address + offset, src, srcOffset, byteCount);
175    }
176
177    public final void pokeCharArray(int offset, char[] src, int srcOffset, int charCount, boolean swap) {
178        Memory.pokeCharArray(address + offset, src, srcOffset, charCount, swap);
179    }
180
181    public final void pokeDoubleArray(int offset, double[] src, int srcOffset, int doubleCount, boolean swap) {
182        Memory.pokeDoubleArray(address + offset, src, srcOffset, doubleCount, swap);
183    }
184
185    public final void pokeFloatArray(int offset, float[] src, int srcOffset, int floatCount, boolean swap) {
186        Memory.pokeFloatArray(address + offset, src, srcOffset, floatCount, swap);
187    }
188
189    public final void pokeIntArray(int offset, int[] src, int srcOffset, int intCount, boolean swap) {
190        Memory.pokeIntArray(address + offset, src, srcOffset, intCount, swap);
191    }
192
193    public final void pokeLongArray(int offset, long[] src, int srcOffset, int longCount, boolean swap) {
194        Memory.pokeLongArray(address + offset, src, srcOffset, longCount, swap);
195    }
196
197    public final void pokeShortArray(int offset, short[] src, int srcOffset, int shortCount, boolean swap) {
198        Memory.pokeShortArray(address + offset, src, srcOffset, shortCount, swap);
199    }
200
201    public final byte peekByte(int offset) {
202        return Memory.peekByte(address + offset);
203    }
204
205    public final void peekByteArray(int offset, byte[] dst, int dstOffset, int byteCount) {
206        Memory.peekByteArray(address + offset, dst, dstOffset, byteCount);
207    }
208
209    public final void peekCharArray(int offset, char[] dst, int dstOffset, int charCount, boolean swap) {
210        Memory.peekCharArray(address + offset, dst, dstOffset, charCount, swap);
211    }
212
213    public final void peekDoubleArray(int offset, double[] dst, int dstOffset, int doubleCount, boolean swap) {
214        Memory.peekDoubleArray(address + offset, dst, dstOffset, doubleCount, swap);
215    }
216
217    public final void peekFloatArray(int offset, float[] dst, int dstOffset, int floatCount, boolean swap) {
218        Memory.peekFloatArray(address + offset, dst, dstOffset, floatCount, swap);
219    }
220
221    public final void peekIntArray(int offset, int[] dst, int dstOffset, int intCount, boolean swap) {
222        Memory.peekIntArray(address + offset, dst, dstOffset, intCount, swap);
223    }
224
225    public final void peekLongArray(int offset, long[] dst, int dstOffset, int longCount, boolean swap) {
226        Memory.peekLongArray(address + offset, dst, dstOffset, longCount, swap);
227    }
228
229    public final void peekShortArray(int offset, short[] dst, int dstOffset, int shortCount, boolean swap) {
230        Memory.peekShortArray(address + offset, dst, dstOffset, shortCount, swap);
231    }
232
233    public final void pokeShort(int offset, short value, ByteOrder order) {
234        Memory.pokeShort(address + offset, value, order.needsSwap);
235    }
236
237    public final short peekShort(int offset, ByteOrder order) {
238        return Memory.peekShort(address + offset, order.needsSwap);
239    }
240
241    public final void pokeInt(int offset, int value, ByteOrder order) {
242        Memory.pokeInt(address + offset, value, order.needsSwap);
243    }
244
245    public final int peekInt(int offset, ByteOrder order) {
246        return Memory.peekInt(address + offset, order.needsSwap);
247    }
248
249    public final void pokeLong(int offset, long value, ByteOrder order) {
250        Memory.pokeLong(address + offset, value, order.needsSwap);
251    }
252
253    public final long peekLong(int offset, ByteOrder order) {
254        return Memory.peekLong(address + offset, order.needsSwap);
255    }
256
257    public final long toLong() {
258        return address;
259    }
260
261    public final String toString() {
262        return getClass().getName() + "[" + address + "]";
263    }
264
265    public final long getSize() {
266        return size;
267    }
268}
269