| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // BEGIN android-note |
| // address length was changed from long to int for performance reasons. |
| // END android-note |
| |
| package org.apache.harmony.luni.platform; |
| |
| import java.io.IOException; |
| |
| |
| /** |
| * This class enables direct access to OS memory. |
| * <p> |
| * Methods that take OS addresses define such parameters as a Java |
| * <code>int</code>. The <code>int</code> value is interpreted based on |
| * the underlying platform pointer size, such that only the lowest significant |
| * <code>POINTER_SIZE</code> bytes of the <code>long</code> value are used. |
| * In practice this means that methods on 64-bit platforms use the full eight |
| * bytes of the address parameter, and on 32-bit platforms the same methods are |
| * truncated to use only the low four bytes. |
| * </p> |
| * <p> |
| * Methods that return OS addresses define the return type to be a Java |
| * <code>long</code>. If the platform pointer size is less than eight bytes |
| * the OS address value is zero-extended to an eight-byte int to correspond to |
| * the subsequent interpretation of that jlong as an OS address as defined |
| * above. |
| * </p> |
| */ |
| final class OSMemory extends OSComponent implements IMemorySystem { |
| |
| /** |
| * Defines the size, in bytes, of a native pointer type for the underlying |
| * platform. This will be 4 (for 32-bit machines) or 8 (for 64-bit |
| * machines). |
| */ |
| public static final int POINTER_SIZE; |
| |
| /** |
| * Defines the natural byte order for this machine. |
| */ |
| public static final Endianness NATIVE_ORDER; |
| |
| private static final OSMemory singleton = new OSMemory(); |
| |
| static { |
| POINTER_SIZE = getPointerSizeImpl(); |
| |
| if (isLittleEndianImpl()) { |
| NATIVE_ORDER = Endianness.LITTLE_ENDIAN; |
| } else { |
| NATIVE_ORDER = Endianness.BIG_ENDIAN; |
| } |
| } |
| |
| public static OSMemory getOSMemory() { |
| return singleton; |
| } |
| |
| /** |
| * This class is not designed to be publically instantiated. |
| * |
| * @see #getOSMemory() |
| */ |
| OSMemory() { |
| super(); |
| } |
| |
| // BEGIN android-note |
| // changed to private |
| // END android-note |
| /** |
| * Returns whether the byte order of this machine is little endian or not.. |
| * |
| * @return <code>false</code> for Big Endian, and |
| * <code>true</code. for Little Endian. |
| */ |
| private static native boolean isLittleEndianImpl(); |
| |
| public boolean isLittleEndian() { |
| return isLittleEndianImpl(); |
| } |
| |
| /** |
| * Returns the natural byte order for this machine. |
| * |
| * @return the native byte order for the current platform. |
| */ |
| public Endianness getNativeOrder() { |
| return NATIVE_ORDER; |
| } |
| |
| /** |
| * Returns the size of a native pointer type for the underlying platform. |
| * |
| * @return the size of a pointer, in bytes. |
| */ |
| private static native int getPointerSizeImpl(); |
| |
| public int getPointerSize() { |
| return POINTER_SIZE; |
| } |
| |
| /** |
| * Allocates and returns a pointer to space for a memory block of |
| * <code>length</code> bytes. The space is uninitialized and may be larger |
| * than the number of bytes requested; however, the guaranteed usable memory |
| * block is exactly <code>length</code> bytes int. |
| * |
| * @param length |
| * number of bytes requested. |
| * @return the address of the start of the memory block. |
| * @throws OutOfMemoryError |
| * if the request cannot be satisfied. |
| */ |
| public native int malloc(int length) throws OutOfMemoryError; |
| |
| /** |
| * Deallocates space for a memory block that was previously allocated by a |
| * call to {@link #malloc(int) malloc(int)}. The number of bytes freed is |
| * identical to the number of bytes acquired when the memory block was |
| * allocated. If <code>address</code> is zero the method does nothing. |
| * <p> |
| * Freeing a pointer to a memory block that was not allocated by |
| * <code>malloc()</code> has unspecified effect. |
| * </p> |
| * |
| * @param address |
| * the address of the memory block to deallocate. |
| */ |
| public native void free(int address); |
| |
| /** |
| * Places <code>value</code> into first <code>length</code> bytes of the |
| * memory block starting at <code>address</code>. |
| * <p> |
| * The behavior is unspecified if |
| * <code>(address ... address + length)</code> is not wholly within the |
| * range that was previously allocated using <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the address of the first memory location. |
| * @param value |
| * the byte value to set at each location. |
| * @param length |
| * the number of byte-length locations to set. |
| */ |
| public native void memset(int address, byte value, long length); |
| |
| /** |
| * Copies <code>length</code> bytes from <code>srcAddress</code> to |
| * <code>destAddress</code>. Where any part of the source memory block |
| * and the destination memory block overlap <code>memmove()</code> ensures |
| * that the original source bytes in the overlapping region are copied |
| * before being overwritten. |
| * <p> |
| * The behavior is unspecified if |
| * <code>(srcAddress ... srcAddress + length)</code> and |
| * <code>(destAddress ... destAddress + length)</code> are not both wholly |
| * within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param destAddress |
| * the address of the destination memory block. |
| * @param srcAddress |
| * the address of the source memory block. |
| * @param length |
| * the number of bytes to move. |
| */ |
| public native void memmove(int destAddress, int srcAddress, long length); |
| |
| /** |
| * Copies <code>length</code> bytes from the memory block at |
| * <code>address</code> into the byte array <code>bytes</code> starting |
| * at element <code>offset</code> within the byte array. |
| * <p> |
| * The behavior of this method is undefined if the range |
| * <code>(address ... address + length)</code> is not within a memory |
| * block that was allocated using {@link #malloc(int) malloc(int)}. |
| * </p> |
| * |
| * @param address |
| * the address of the OS memory block from which to copy bytes. |
| * @param bytes |
| * the byte array into which to copy the bytes. |
| * @param offset |
| * the index of the first element in <code>bytes</code> that |
| * will be overwritten. |
| * @param length |
| * the total number of bytes to copy into the byte array. |
| * @throws NullPointerException |
| * if <code>bytes</code> is <code>null</code>. |
| * @throws IndexOutOfBoundsException |
| * if <code>offset + length > bytes.length</code>. |
| */ |
| public native void getByteArray(int address, byte[] bytes, int offset, |
| int length) throws NullPointerException, IndexOutOfBoundsException; |
| |
| /** |
| * Copies <code>length</code> bytes from the byte array <code>bytes</code> |
| * into the memory block at <code>address</code>, starting at element |
| * <code>offset</code> within the byte array. |
| * <p> |
| * The behavior of this method is undefined if the range |
| * <code>(address ... address + length)</code> is not within a memory |
| * block that was allocated using {@link #malloc(int) malloc(int)}. |
| * </p> |
| * |
| * @param address |
| * the address of the OS memory block into which to copy the |
| * bytes. |
| * @param bytes |
| * the byte array from which to copy the bytes. |
| * @param offset |
| * the index of the first element in <code>bytes</code> that |
| * will be read. |
| * @param length |
| * the total number of bytes to copy from <code>bytes</code> |
| * into the memory block. |
| * @throws NullPointerException |
| * if <code>bytes</code> is <code>null</code>. |
| * @throws IndexOutOfBoundsException |
| * if <code>offset + length > bytes.length</code>. |
| */ |
| public native void setByteArray(int address, byte[] bytes, int offset, |
| int length) throws NullPointerException, IndexOutOfBoundsException; |
| |
| // BEGIN android-added |
| /** |
| * Copies <code>length</code> shorts from the short array <code>shorts</code> |
| * into the memory block at <code>address</code>, starting at element |
| * <code>offset</code> within the short array. |
| * <p> |
| * The behavior of this method is undefined if the range |
| * <code>(address ... address + 2*length)</code> is not within a memory |
| * block that was allocated using {@link #malloc(int) malloc(int)}. |
| * </p> |
| * |
| * @param address |
| * the address of the OS memory block into which to copy the |
| * shorts. |
| * @param shorts |
| * the short array from which to copy the shorts. |
| * @param offset |
| * the index of the first element in <code>shorts</code> that |
| * will be read. |
| * @param length |
| * the total number of shorts to copy from <code>shorts</code> |
| * into the memory block. |
| * @param swap |
| * true if the shorts should be written in reverse byte order. |
| * @throws NullPointerException |
| * if <code>bytes</code> is <code>null</code>. |
| * @throws IndexOutOfBoundsException |
| * if <code>offset + length > bytes.length</code>. |
| */ |
| public native void setShortArray(int address, short[] shorts, int offset, |
| int length, boolean swap) throws NullPointerException, |
| IndexOutOfBoundsException; |
| |
| /** |
| * Copies <code>length</code> ints from the int array <code>ints</code> |
| * into the memory block at <code>address</code>, starting at element |
| * <code>offset</code> within the int array. |
| * <p> |
| * The behavior of this method is undefined if the range |
| * <code>(address ... address + 2*length)</code> is not within a memory |
| * block that was allocated using {@link #malloc(int) malloc(int)}. |
| * </p> |
| * |
| * @param address |
| * the address of the OS memory block into which to copy the |
| * ints. |
| * @param ints |
| * the int array from which to copy the ints. |
| * @param offset |
| * the index of the first element in <code>ints</code> that |
| * will be read. |
| * @param length |
| * the total number of ints to copy from <code>ints</code> |
| * into the memory block. |
| * @param swap |
| * true if the ints should be written in reverse byte order. |
| * @throws NullPointerException |
| * if <code>bytes</code> is <code>null</code>. |
| * @throws IndexOutOfBoundsException |
| * if <code>offset + length > bytes.length</code>. |
| */ |
| public native void setIntArray(int address, int[] ints, int offset, |
| int length, boolean swap) throws NullPointerException, |
| IndexOutOfBoundsException; |
| // END android-added |
| |
| // Primitive get & set methods |
| |
| /** |
| * Gets the value of the single byte at the given address. |
| * <p> |
| * The behavior is unspecified if <code>address</code> is not in the range |
| * that was previously allocated using <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the byte. |
| * @return the byte value. |
| */ |
| public native byte getByte(int address); |
| |
| /** |
| * Sets the given single byte value at the given address. |
| * <p> |
| * The behavior is unspecified if <code>address</code> is not in the range |
| * that was previously allocated using <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the address at which to set the byte value. |
| * @param value |
| * the value to set. |
| */ |
| public native void setByte(int address, byte value); |
| |
| /** |
| * Gets the value of the signed two-byte integer stored in platform byte |
| * order at the given address. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 2)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the two-byte value. |
| * @return the value of the two-byte integer as a Java <code>short</code>. |
| */ |
| public native short getShort(int address); |
| |
| public short getShort(int address, Endianness endianness) { |
| return (endianness == NATIVE_ORDER) ? getShort(address) |
| : swap(getShort(address)); |
| } |
| |
| /** |
| * Sets the value of the signed two-byte integer at the given address in |
| * platform byte order. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 2)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the two-byte value. |
| * @param value |
| * the value of the two-byte integer as a Java <code>short</code>. |
| */ |
| public native void setShort(int address, short value); |
| |
| public void setShort(int address, short value, Endianness endianness) { |
| if (endianness == NATIVE_ORDER) { |
| setShort(address, value); |
| } else { |
| setShort(address, swap(value)); |
| } |
| } |
| |
| /** |
| * Gets the value of the signed four-byte integer stored in platform |
| * byte-order at the given address. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 4)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the four-byte value. |
| * @return the value of the four-byte integer as a Java <code>int</code>. |
| */ |
| public native int getInt(int address); |
| |
| public int getInt(int address, Endianness endianness) { |
| return (endianness == NATIVE_ORDER) ? getInt(address) |
| : swap(getInt(address)); |
| } |
| |
| /** |
| * Sets the value of the signed four-byte integer at the given address in |
| * platform byte order. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 4)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the four-byte value. |
| * @param value |
| * the value of the four-byte integer as a Java <code>int</code>. |
| */ |
| public native void setInt(int address, int value); |
| |
| public void setInt(int address, int value, Endianness endianness) { |
| if (endianness == NATIVE_ORDER) { |
| setInt(address, value); |
| } else { |
| setInt(address, swap(value)); |
| } |
| } |
| |
| /** |
| * Gets the value of the signed eight-byte integer stored in platform byte |
| * order at the given address. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 8)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the eight-byte value. |
| * @return the value of the eight-byte integer as a Java <code>long</code>. |
| */ |
| public native long getLong(int address); |
| |
| public long getLong(int address, Endianness endianness) { |
| return (endianness == NATIVE_ORDER) ? getLong(address) |
| : swap(getLong(address)); |
| } |
| |
| /** |
| * Sets the value of the signed eight-byte integer at the given address in |
| * the platform byte order. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 8)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the eight-byte value. |
| * @param value |
| * the value of the eight-byte integer as a Java |
| * <code>long</code>. |
| */ |
| public native void setLong(int address, long value); |
| |
| public void setLong(int address, long value, Endianness endianness) { |
| if (endianness == NATIVE_ORDER) { |
| setLong(address, value); |
| } else { |
| setLong(address, swap(value)); |
| } |
| } |
| |
| /** |
| * Gets the value of the IEEE754-format four-byte float stored in platform |
| * byte order at the given address. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 4)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the eight-byte value. |
| * @return the value of the four-byte float as a Java <code>float</code>. |
| */ |
| public native float getFloat(int address); |
| |
| public float getFloat(int address, Endianness endianness) { |
| if (endianness == NATIVE_ORDER) { |
| return getFloat(address); |
| } |
| int floatBits = swap(getInt(address)); |
| return Float.intBitsToFloat(floatBits); |
| } |
| |
| /** |
| * Sets the value of the IEEE754-format four-byte float stored in platform |
| * byte order at the given address. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 4)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the eight-byte value. |
| * @param value |
| * the value of the four-byte float as a Java <code>float</code>. |
| */ |
| public native void setFloat(int address, float value); |
| |
| public void setFloat(int address, float value, Endianness endianness) { |
| if (endianness == NATIVE_ORDER) { |
| setFloat(address, value); |
| } else { |
| int floatBits = Float.floatToIntBits(value); |
| setInt(address, swap(floatBits)); |
| } |
| } |
| |
| /** |
| * Gets the value of the IEEE754-format eight-byte float stored in platform |
| * byte order at the given address. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 8)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the eight-byte value. |
| * @return the value of the eight-byte float as a Java <code>double</code>. |
| */ |
| public native double getDouble(int address); |
| |
| public double getDouble(int address, Endianness endianness) { |
| if (endianness == NATIVE_ORDER) { |
| return getDouble(address); |
| } |
| long doubleBits = swap(getLong(address)); |
| return Double.longBitsToDouble(doubleBits); |
| } |
| |
| /** |
| * Sets the value of the IEEE754-format eight-byte float store in platform |
| * byte order at the given address. |
| * <p> |
| * The behavior is unspecified if <code>(address ... address + 8)</code> |
| * is not wholly within the range that was previously allocated using |
| * <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the eight-byte value. |
| * @param value |
| * the value of the eight-byte float as a Java |
| * <code>double</code>. |
| */ |
| public native void setDouble(int address, double value); |
| |
| public void setDouble(int address, double value, Endianness endianness) { |
| if (endianness == NATIVE_ORDER) { |
| setDouble(address, value); |
| } else { |
| long doubleBits = Double.doubleToLongBits(value); |
| setLong(address, swap(doubleBits)); |
| } |
| } |
| |
| /** |
| * Gets the value of the platform pointer at the given address. |
| * <p> |
| * The length of the platform pointer is defined by |
| * <code>POINTER_SIZE</code>. |
| * </p> |
| * The behavior is unspecified if |
| * <code>(address ... address + POINTER_SIZE)</code> is not wholly within |
| * the range that was previously allocated using <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the platform pointer. |
| * @return the value of the platform pointer as a Java <code>int</code>. |
| */ |
| public native int getAddress(int address); |
| |
| /** |
| * Sets the value of the platform pointer at the given address. |
| * <p> |
| * The length of the platform pointer is defined by |
| * <code>POINTER_SIZE</code>. This method only sets |
| * <code>POINTER_SIZE</code> bytes at the given address. |
| * </p> |
| * The behavior is unspecified if |
| * <code>(address ... address + POINTER_SIZE)</code> is not wholly within |
| * the range that was previously allocated using <code>malloc()</code>. |
| * </p> |
| * |
| * @param address |
| * the platform address of the start of the platform pointer. |
| * @param value |
| * the value of the platform pointer as a Java <code>int</code>. |
| */ |
| public native void setAddress(int address, int value); |
| |
| /* |
| * Memory mapped file |
| */ |
| private native int mmapImpl(int fileDescriptor, long alignment, |
| long size, int mapMode); |
| |
| public int mmap(int fileDescriptor, long alignment, long size, |
| int mapMode) throws IOException { |
| int address = mmapImpl(fileDescriptor, alignment, size, mapMode); |
| if (address == -1) { |
| throw new IOException(); |
| } |
| return address; |
| } |
| |
| private native void unmapImpl(int addr, long size); |
| |
| public void unmap(int addr, long size) { |
| unmapImpl(addr, size); |
| } |
| |
| public void load(int addr, long size) { |
| loadImpl(addr, size); |
| } |
| |
| private native int loadImpl(int l, long size); |
| |
| public boolean isLoaded(int addr, long size) { |
| return size == 0 ? true : isLoadedImpl(addr, size); |
| } |
| |
| private native boolean isLoadedImpl(int l, long size); |
| |
| public void flush(int addr, long size) { |
| flushImpl(addr, size); |
| } |
| |
| private native int flushImpl(int l, long size); |
| |
| /* |
| * Helper methods to change byte order. |
| */ |
| private short swap(short value) { |
| int topEnd = value << 8; |
| int btmEnd = (value >> 8) & 0xFF; |
| return (short) (topEnd | btmEnd); |
| } |
| |
| private int swap(int value) { |
| short left = (short) (value >> 16); |
| short right = (short) value; |
| int topEnd = swap(right) << 16; |
| int btmEnd = swap(left) & 0xFFFF; |
| return topEnd | btmEnd; |
| } |
| |
| private long swap(long value) { |
| int left = (int) (value >> 32); |
| int right = (int) value; |
| long topEnd = ((long) swap(right)) << 32; |
| long btmEnd = swap(left) & 0xFFFFFFFFL; |
| return topEnd | btmEnd; |
| } |
| } |