| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed 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. |
| */ |
| |
| #define LOG_TAG "Memory" |
| |
| #include "JNIHelp.h" |
| #include "JniConstants.h" |
| #include "ScopedBytes.h" |
| #include "ScopedPrimitiveArray.h" |
| #include "UniquePtr.h" |
| |
| #include <byteswap.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| |
| #if defined(__arm__) |
| // 32-bit ARM has load/store alignment restrictions for longs. |
| #define LONG_ALIGNMENT_MASK 0x3 |
| #elif defined(__i386__) |
| // x86 can load anything at any alignment. |
| #define LONG_ALIGNMENT_MASK 0x0 |
| #else |
| #error unknown load/store alignment restrictions for this architecture |
| #endif |
| |
| template <typename T> static T cast(jint address) { |
| return reinterpret_cast<T>(static_cast<uintptr_t>(address)); |
| } |
| |
| static inline void swapShorts(jshort* dstShorts, const jshort* srcShorts, size_t count) { |
| // Do 32-bit swaps as long as possible... |
| jint* dst = reinterpret_cast<jint*>(dstShorts); |
| const jint* src = reinterpret_cast<const jint*>(srcShorts); |
| for (size_t i = 0; i < count / 2; ++i) { |
| jint v = *src++; // v=ABCD |
| v = bswap_32(v); // v=DCBA |
| jint v2 = (v << 16) | ((v >> 16) & 0xffff); // v=BADC |
| *dst++ = v2; |
| } |
| // ...with one last 16-bit swap if necessary. |
| if ((count % 2) != 0) { |
| jshort v = *reinterpret_cast<const jshort*>(src); |
| *reinterpret_cast<jshort*>(dst) = bswap_16(v); |
| } |
| } |
| |
| static inline void swapInts(jint* dstInts, const jint* srcInts, size_t count) { |
| for (size_t i = 0; i < count; ++i) { |
| jint v = *srcInts++; |
| *dstInts++ = bswap_32(v); |
| } |
| } |
| |
| static inline void swapLongs(jlong* dstLongs, const jlong* srcLongs, size_t count) { |
| jint* dst = reinterpret_cast<jint*>(dstLongs); |
| const jint* src = reinterpret_cast<const jint*>(srcLongs); |
| for (size_t i = 0; i < count; ++i) { |
| jint v1 = *src++; |
| jint v2 = *src++; |
| *dst++ = bswap_32(v2); |
| *dst++ = bswap_32(v1); |
| } |
| } |
| |
| static void Memory_memmove(JNIEnv* env, jclass, jobject dstObject, jint dstOffset, jobject srcObject, jint srcOffset, jlong length) { |
| ScopedBytesRW dstBytes(env, dstObject); |
| if (dstBytes.get() == NULL) { |
| return; |
| } |
| ScopedBytesRO srcBytes(env, srcObject); |
| if (srcBytes.get() == NULL) { |
| return; |
| } |
| memmove(dstBytes.get() + dstOffset, srcBytes.get() + srcOffset, length); |
| } |
| |
| static jbyte Memory_peekByte(JNIEnv*, jclass, jint srcAddress) { |
| return *cast<const jbyte*>(srcAddress); |
| } |
| |
| static void Memory_peekByteArray(JNIEnv* env, jclass, jint srcAddress, jbyteArray dst, jint dstOffset, jint byteCount) { |
| env->SetByteArrayRegion(dst, dstOffset, byteCount, cast<const jbyte*>(srcAddress)); |
| } |
| |
| // Implements the peekXArray methods: |
| // - For unswapped access, we just use the JNI SetXArrayRegion functions. |
| // - For swapped access, we use GetXArrayElements and our own copy-and-swap routines. |
| // GetXArrayElements is disproportionately cheap on Dalvik because it doesn't copy (as opposed |
| // to Hotspot, which always copies). The SWAP_FN copies and swaps in one pass, which is cheaper |
| // than copying and then swapping in a second pass. Depending on future VM/GC changes, the |
| // swapped case might need to be revisited. |
| #define PEEKER(SCALAR_TYPE, JNI_NAME, SWAP_TYPE, SWAP_FN) { \ |
| if (swap) { \ |
| Scoped ## JNI_NAME ## ArrayRW elements(env, dst); \ |
| if (elements.get() == NULL) { \ |
| return; \ |
| } \ |
| const SWAP_TYPE* src = cast<const SWAP_TYPE*>(srcAddress); \ |
| SWAP_FN(reinterpret_cast<SWAP_TYPE*>(elements.get()) + dstOffset, src, count); \ |
| } else { \ |
| const SCALAR_TYPE* src = cast<const SCALAR_TYPE*>(srcAddress); \ |
| env->Set ## JNI_NAME ## ArrayRegion(dst, dstOffset, count, src); \ |
| } \ |
| } |
| |
| static void Memory_peekCharArray(JNIEnv* env, jclass, jint srcAddress, jcharArray dst, jint dstOffset, jint count, jboolean swap) { |
| PEEKER(jchar, Char, jshort, swapShorts); |
| } |
| |
| static void Memory_peekDoubleArray(JNIEnv* env, jclass, jint srcAddress, jdoubleArray dst, jint dstOffset, jint count, jboolean swap) { |
| PEEKER(jdouble, Double, jlong, swapLongs); |
| } |
| |
| static void Memory_peekFloatArray(JNIEnv* env, jclass, jint srcAddress, jfloatArray dst, jint dstOffset, jint count, jboolean swap) { |
| PEEKER(jfloat, Float, jint, swapInts); |
| } |
| |
| static void Memory_peekIntArray(JNIEnv* env, jclass, jint srcAddress, jintArray dst, jint dstOffset, jint count, jboolean swap) { |
| PEEKER(jint, Int, jint, swapInts); |
| } |
| |
| static void Memory_peekLongArray(JNIEnv* env, jclass, jint srcAddress, jlongArray dst, jint dstOffset, jint count, jboolean swap) { |
| PEEKER(jlong, Long, jlong, swapLongs); |
| } |
| |
| static void Memory_peekShortArray(JNIEnv* env, jclass, jint srcAddress, jshortArray dst, jint dstOffset, jint count, jboolean swap) { |
| PEEKER(jshort, Short, jshort, swapShorts); |
| } |
| |
| static void Memory_pokeByte(JNIEnv*, jclass, jint dstAddress, jbyte value) { |
| *cast<jbyte*>(dstAddress) = value; |
| } |
| |
| static void Memory_pokeByteArray(JNIEnv* env, jclass, jint dstAddress, jbyteArray src, jint offset, jint length) { |
| env->GetByteArrayRegion(src, offset, length, cast<jbyte*>(dstAddress)); |
| } |
| |
| // Implements the pokeXArray methods: |
| // - For unswapped access, we just use the JNI GetXArrayRegion functions. |
| // - For swapped access, we use GetXArrayElements and our own copy-and-swap routines. |
| // GetXArrayElements is disproportionately cheap on Dalvik because it doesn't copy (as opposed |
| // to Hotspot, which always copies). The SWAP_FN copies and swaps in one pass, which is cheaper |
| // than copying and then swapping in a second pass. Depending on future VM/GC changes, the |
| // swapped case might need to be revisited. |
| #define POKER(SCALAR_TYPE, JNI_NAME, SWAP_TYPE, SWAP_FN) { \ |
| if (swap) { \ |
| Scoped ## JNI_NAME ## ArrayRO elements(env, src); \ |
| if (elements.get() == NULL) { \ |
| return; \ |
| } \ |
| const SWAP_TYPE* src = reinterpret_cast<const SWAP_TYPE*>(elements.get()) + srcOffset; \ |
| SWAP_FN(cast<SWAP_TYPE*>(dstAddress), src, count); \ |
| } else { \ |
| env->Get ## JNI_NAME ## ArrayRegion(src, srcOffset, count, cast<SCALAR_TYPE*>(dstAddress)); \ |
| } \ |
| } |
| |
| static void Memory_pokeCharArray(JNIEnv* env, jclass, jint dstAddress, jcharArray src, jint srcOffset, jint count, jboolean swap) { |
| POKER(jchar, Char, jshort, swapShorts); |
| } |
| |
| static void Memory_pokeDoubleArray(JNIEnv* env, jclass, jint dstAddress, jdoubleArray src, jint srcOffset, jint count, jboolean swap) { |
| POKER(jdouble, Double, jlong, swapLongs); |
| } |
| |
| static void Memory_pokeFloatArray(JNIEnv* env, jclass, jint dstAddress, jfloatArray src, jint srcOffset, jint count, jboolean swap) { |
| POKER(jfloat, Float, jint, swapInts); |
| } |
| |
| static void Memory_pokeIntArray(JNIEnv* env, jclass, jint dstAddress, jintArray src, jint srcOffset, jint count, jboolean swap) { |
| POKER(jint, Int, jint, swapInts); |
| } |
| |
| static void Memory_pokeLongArray(JNIEnv* env, jclass, jint dstAddress, jlongArray src, jint srcOffset, jint count, jboolean swap) { |
| POKER(jlong, Long, jlong, swapLongs); |
| } |
| |
| static void Memory_pokeShortArray(JNIEnv* env, jclass, jint dstAddress, jshortArray src, jint srcOffset, jint count, jboolean swap) { |
| POKER(jshort, Short, jshort, swapShorts); |
| } |
| |
| static jshort Memory_peekShort(JNIEnv*, jclass, jint srcAddress, jboolean swap) { |
| jshort result = *cast<const jshort*>(srcAddress); |
| if (swap) { |
| result = bswap_16(result); |
| } |
| return result; |
| } |
| |
| static void Memory_pokeShort(JNIEnv*, jclass, jint dstAddress, jshort value, jboolean swap) { |
| if (swap) { |
| value = bswap_16(value); |
| } |
| *cast<jshort*>(dstAddress) = value; |
| } |
| |
| static jint Memory_peekInt(JNIEnv*, jclass, jint srcAddress, jboolean swap) { |
| jint result = *cast<const jint*>(srcAddress); |
| if (swap) { |
| result = bswap_32(result); |
| } |
| return result; |
| } |
| |
| static void Memory_pokeInt(JNIEnv*, jclass, jint dstAddress, jint value, jboolean swap) { |
| if (swap) { |
| value = bswap_32(value); |
| } |
| *cast<jint*>(dstAddress) = value; |
| } |
| |
| static jlong Memory_peekLong(JNIEnv*, jclass, jint srcAddress, jboolean swap) { |
| jlong result; |
| if ((srcAddress & LONG_ALIGNMENT_MASK) == 0) { |
| result = *cast<const jlong*>(srcAddress); |
| } else { |
| // Handle unaligned memory access one byte at a time |
| const jbyte* src = cast<const jbyte*>(srcAddress); |
| jbyte* dst = reinterpret_cast<jbyte*>(&result); |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = src[2]; |
| dst[3] = src[3]; |
| dst[4] = src[4]; |
| dst[5] = src[5]; |
| dst[6] = src[6]; |
| dst[7] = src[7]; |
| } |
| if (swap) { |
| result = bswap_64(result); |
| } |
| return result; |
| } |
| |
| static void Memory_pokeLong(JNIEnv*, jclass, jint dstAddress, jlong value, jboolean swap) { |
| if (swap) { |
| value = bswap_64(value); |
| } |
| if ((dstAddress & LONG_ALIGNMENT_MASK) == 0) { |
| *cast<jlong*>(dstAddress) = value; |
| } else { |
| // Handle unaligned memory access one byte at a time |
| const jbyte* src = reinterpret_cast<const jbyte*>(&value); |
| jbyte* dst = cast<jbyte*>(dstAddress); |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = src[2]; |
| dst[3] = src[3]; |
| dst[4] = src[4]; |
| dst[5] = src[5]; |
| dst[6] = src[6]; |
| dst[7] = src[7]; |
| } |
| } |
| |
| static void unsafeBulkCopy(jbyte* dst, const jbyte* src, jint byteCount, |
| jint sizeofElement, jboolean swap) { |
| if (!swap) { |
| memcpy(dst, src, byteCount); |
| return; |
| } |
| |
| if (sizeofElement == 2) { |
| jshort* dstShorts = reinterpret_cast<jshort*>(dst); |
| const jshort* srcShorts = reinterpret_cast<const jshort*>(src); |
| swapShorts(dstShorts, srcShorts, byteCount / 2); |
| } else if (sizeofElement == 4) { |
| jint* dstInts = reinterpret_cast<jint*>(dst); |
| const jint* srcInts = reinterpret_cast<const jint*>(src); |
| swapInts(dstInts, srcInts, byteCount / 4); |
| } else if (sizeofElement == 8) { |
| jlong* dstLongs = reinterpret_cast<jlong*>(dst); |
| const jlong* srcLongs = reinterpret_cast<const jlong*>(src); |
| swapLongs(dstLongs, srcLongs, byteCount / 8); |
| } |
| } |
| |
| static void Memory_unsafeBulkGet(JNIEnv* env, jclass, jobject dstObject, jint dstOffset, |
| jint byteCount, jbyteArray srcArray, jint srcOffset, jint sizeofElement, jboolean swap) { |
| ScopedByteArrayRO srcBytes(env, srcArray); |
| if (srcBytes.get() == NULL) { |
| return; |
| } |
| jarray dstArray = reinterpret_cast<jarray>(dstObject); |
| jbyte* dstBytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(dstArray, NULL)); |
| if (dstBytes == NULL) { |
| return; |
| } |
| jbyte* dst = dstBytes + dstOffset*sizeofElement; |
| const jbyte* src = srcBytes.get() + srcOffset; |
| unsafeBulkCopy(dst, src, byteCount, sizeofElement, swap); |
| env->ReleasePrimitiveArrayCritical(dstArray, dstBytes, 0); |
| } |
| |
| static void Memory_unsafeBulkPut(JNIEnv* env, jclass, jbyteArray dstArray, jint dstOffset, |
| jint byteCount, jobject srcObject, jint srcOffset, jint sizeofElement, jboolean swap) { |
| ScopedByteArrayRW dstBytes(env, dstArray); |
| if (dstBytes.get() == NULL) { |
| return; |
| } |
| jarray srcArray = reinterpret_cast<jarray>(srcObject); |
| jbyte* srcBytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(srcArray, NULL)); |
| if (srcBytes == NULL) { |
| return; |
| } |
| jbyte* dst = dstBytes.get() + dstOffset; |
| const jbyte* src = srcBytes + srcOffset*sizeofElement; |
| unsafeBulkCopy(dst, src, byteCount, sizeofElement, swap); |
| env->ReleasePrimitiveArrayCritical(srcArray, srcBytes, 0); |
| } |
| |
| static JNINativeMethod gMethods[] = { |
| NATIVE_METHOD(Memory, memmove, "(Ljava/lang/Object;ILjava/lang/Object;IJ)V"), |
| NATIVE_METHOD(Memory, peekByte, "!(I)B"), |
| NATIVE_METHOD(Memory, peekByteArray, "(I[BII)V"), |
| NATIVE_METHOD(Memory, peekCharArray, "(I[CIIZ)V"), |
| NATIVE_METHOD(Memory, peekDoubleArray, "(I[DIIZ)V"), |
| NATIVE_METHOD(Memory, peekFloatArray, "(I[FIIZ)V"), |
| NATIVE_METHOD(Memory, peekInt, "!(IZ)I"), |
| NATIVE_METHOD(Memory, peekIntArray, "(I[IIIZ)V"), |
| NATIVE_METHOD(Memory, peekLong, "!(IZ)J"), |
| NATIVE_METHOD(Memory, peekLongArray, "(I[JIIZ)V"), |
| NATIVE_METHOD(Memory, peekShort, "!(IZ)S"), |
| NATIVE_METHOD(Memory, peekShortArray, "(I[SIIZ)V"), |
| NATIVE_METHOD(Memory, pokeByte, "!(IB)V"), |
| NATIVE_METHOD(Memory, pokeByteArray, "(I[BII)V"), |
| NATIVE_METHOD(Memory, pokeCharArray, "(I[CIIZ)V"), |
| NATIVE_METHOD(Memory, pokeDoubleArray, "(I[DIIZ)V"), |
| NATIVE_METHOD(Memory, pokeFloatArray, "(I[FIIZ)V"), |
| NATIVE_METHOD(Memory, pokeInt, "!(IIZ)V"), |
| NATIVE_METHOD(Memory, pokeIntArray, "(I[IIIZ)V"), |
| NATIVE_METHOD(Memory, pokeLong, "!(IJZ)V"), |
| NATIVE_METHOD(Memory, pokeLongArray, "(I[JIIZ)V"), |
| NATIVE_METHOD(Memory, pokeShort, "!(ISZ)V"), |
| NATIVE_METHOD(Memory, pokeShortArray, "(I[SIIZ)V"), |
| NATIVE_METHOD(Memory, unsafeBulkGet, "(Ljava/lang/Object;II[BIIZ)V"), |
| NATIVE_METHOD(Memory, unsafeBulkPut, "([BIILjava/lang/Object;IIZ)V"), |
| }; |
| int register_libcore_io_Memory(JNIEnv* env) { |
| return jniRegisterNativeMethods(env, "libcore/io/Memory", gMethods, NELEM(gMethods)); |
| } |