1/*
2 * Copyright (C) 2007 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#define LOG_TAG "Memory"
18
19#include <errno.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/mman.h>
23
24#include <nativehelper/JNIHelp.h>
25#include <nativehelper/JniConstants.h>
26#include <nativehelper/ScopedBytes.h>
27#include <nativehelper/ScopedPrimitiveArray.h>
28
29#include "nativehelper/jni_macros.h"
30#include "Portability.h"
31
32// Use packed structures for access to unaligned data on targets with alignment restrictions.
33// The compiler will generate appropriate code to access these structures without
34// generating alignment exceptions.
35template <typename T> static inline T get_unaligned(const T* address) {
36    struct unaligned { T v; } __attribute__ ((packed));
37    const unaligned* p = reinterpret_cast<const unaligned*>(address);
38    return p->v;
39}
40
41template <typename T> static inline void put_unaligned(T* address, T v) {
42    struct unaligned { T v; } __attribute__ ((packed));
43    unaligned* p = reinterpret_cast<unaligned*>(address);
44    p->v = v;
45}
46
47template <typename T> static T cast(jlong address) {
48    return reinterpret_cast<T>(static_cast<uintptr_t>(address));
49}
50
51// Byte-swap 2 jshort values packed in a jint.
52static inline jint bswap_2x16(jint v) {
53    // v is initially ABCD
54#if defined(__mips__) && defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
55    __asm__ volatile ("wsbh %0, %0" : "+r" (v));  // v=BADC
56#else
57    v = bswap_32(v);                              // v=DCBA
58    v = (v << 16) | ((v >> 16) & 0xffff);         // v=BADC
59#endif
60    return v;
61}
62
63static inline void swapShorts(jshort* dstShorts, const jshort* srcShorts, size_t count) {
64    // Do 32-bit swaps as long as possible...
65    jint* dst = reinterpret_cast<jint*>(dstShorts);
66    const jint* src = reinterpret_cast<const jint*>(srcShorts);
67    for (size_t i = 0; i < count / 2; ++i) {
68        jint v = get_unaligned<jint>(src++);
69        put_unaligned<jint>(dst++, bswap_2x16(v));
70    }
71    if ((count % 2) != 0) {
72      jshort v = get_unaligned<jshort>(reinterpret_cast<const jshort*>(src));
73      put_unaligned<jshort>(reinterpret_cast<jshort*>(dst), bswap_16(v));
74    }
75}
76
77static inline void swapInts(jint* dstInts, const jint* srcInts, size_t count) {
78    for (size_t i = 0; i < count; ++i) {
79        jint v = get_unaligned<int>(srcInts++);
80        put_unaligned<jint>(dstInts++, bswap_32(v));
81    }
82}
83
84static inline void swapLongs(jlong* dstLongs, const jlong* srcLongs, size_t count) {
85    jint* dst = reinterpret_cast<jint*>(dstLongs);
86    const jint* src = reinterpret_cast<const jint*>(srcLongs);
87    for (size_t i = 0; i < count; ++i) {
88        jint v1 = get_unaligned<jint>(src++);
89        jint v2 = get_unaligned<jint>(src++);
90        put_unaligned<jint>(dst++, bswap_32(v2));
91        put_unaligned<jint>(dst++, bswap_32(v1));
92    }
93}
94
95static void Memory_memmove(JNIEnv* env, jclass, jobject dstObject, jint dstOffset, jobject srcObject, jint srcOffset, jlong length) {
96    ScopedBytesRW dstBytes(env, dstObject);
97    if (dstBytes.get() == NULL) {
98        return;
99    }
100    ScopedBytesRO srcBytes(env, srcObject);
101    if (srcBytes.get() == NULL) {
102        return;
103    }
104    memmove(dstBytes.get() + dstOffset, srcBytes.get() + srcOffset, length);
105}
106
107static jbyte Memory_peekByte(JNIEnv*, jclass, jlong srcAddress) {
108    return *cast<const jbyte*>(srcAddress);
109}
110
111static void Memory_peekByteArray(JNIEnv* env, jclass, jlong srcAddress, jbyteArray dst, jint dstOffset, jint byteCount) {
112    env->SetByteArrayRegion(dst, dstOffset, byteCount, cast<const jbyte*>(srcAddress));
113}
114
115// Implements the peekXArray methods:
116// - For unswapped access, we just use the JNI SetXArrayRegion functions.
117// - For swapped access, we use GetXArrayElements and our own copy-and-swap routines.
118//   GetXArrayElements is disproportionately cheap on Dalvik because it doesn't copy (as opposed
119//   to Hotspot, which always copies). The SWAP_FN copies and swaps in one pass, which is cheaper
120//   than copying and then swapping in a second pass. Depending on future VM/GC changes, the
121//   swapped case might need to be revisited.
122#define PEEKER(SCALAR_TYPE, JNI_NAME, SWAP_TYPE, SWAP_FN) { \
123    if (swap) { \
124        Scoped ## JNI_NAME ## ArrayRW elements(env, dst); \
125        if (elements.get() == NULL) { \
126            return; \
127        } \
128        const SWAP_TYPE* src = cast<const SWAP_TYPE*>(srcAddress); \
129        SWAP_FN(reinterpret_cast<SWAP_TYPE*>(elements.get()) + dstOffset, src, count); /*NOLINT*/ \
130    } else { \
131        const SCALAR_TYPE* src = cast<const SCALAR_TYPE*>(srcAddress); \
132        env->Set ## JNI_NAME ## ArrayRegion(dst, dstOffset, count, src); \
133    } \
134}
135
136static void Memory_peekCharArray(JNIEnv* env, jclass, jlong srcAddress, jcharArray dst, jint dstOffset, jint count, jboolean swap) {
137    PEEKER(jchar, Char, jshort, swapShorts);
138}
139
140static void Memory_peekDoubleArray(JNIEnv* env, jclass, jlong srcAddress, jdoubleArray dst, jint dstOffset, jint count, jboolean swap) {
141    PEEKER(jdouble, Double, jlong, swapLongs);
142}
143
144static void Memory_peekFloatArray(JNIEnv* env, jclass, jlong srcAddress, jfloatArray dst, jint dstOffset, jint count, jboolean swap) {
145    PEEKER(jfloat, Float, jint, swapInts);
146}
147
148static void Memory_peekIntArray(JNIEnv* env, jclass, jlong srcAddress, jintArray dst, jint dstOffset, jint count, jboolean swap) {
149    PEEKER(jint, Int, jint, swapInts);
150}
151
152static void Memory_peekLongArray(JNIEnv* env, jclass, jlong srcAddress, jlongArray dst, jint dstOffset, jint count, jboolean swap) {
153    PEEKER(jlong, Long, jlong, swapLongs);
154}
155
156static void Memory_peekShortArray(JNIEnv* env, jclass, jlong srcAddress, jshortArray dst, jint dstOffset, jint count, jboolean swap) {
157    PEEKER(jshort, Short, jshort, swapShorts);
158}
159
160static void Memory_pokeByte(JNIEnv*, jclass, jlong dstAddress, jbyte value) {
161    *cast<jbyte*>(dstAddress) = value;
162}
163
164static void Memory_pokeByteArray(JNIEnv* env, jclass, jlong dstAddress, jbyteArray src, jint offset, jint length) {
165    env->GetByteArrayRegion(src, offset, length, cast<jbyte*>(dstAddress));
166}
167
168// Implements the pokeXArray methods:
169// - For unswapped access, we just use the JNI GetXArrayRegion functions.
170// - For swapped access, we use GetXArrayElements and our own copy-and-swap routines.
171//   GetXArrayElements is disproportionately cheap on Dalvik because it doesn't copy (as opposed
172//   to Hotspot, which always copies). The SWAP_FN copies and swaps in one pass, which is cheaper
173//   than copying and then swapping in a second pass. Depending on future VM/GC changes, the
174//   swapped case might need to be revisited.
175#define POKER(SCALAR_TYPE, JNI_NAME, SWAP_TYPE, SWAP_FN) { \
176    if (swap) { \
177        Scoped ## JNI_NAME ## ArrayRO elements(env, src); \
178        if (elements.get() == NULL) { \
179            return; \
180        } \
181        const SWAP_TYPE* src = reinterpret_cast<const SWAP_TYPE*>(elements.get()) + srcOffset; \
182        SWAP_FN(cast<SWAP_TYPE*>(dstAddress), src, count); /*NOLINT*/ \
183    } else { \
184        env->Get ## JNI_NAME ## ArrayRegion(src, srcOffset, count, cast<SCALAR_TYPE*>(dstAddress)); /*NOLINT*/ \
185    } \
186}
187
188static void Memory_pokeCharArray(JNIEnv* env, jclass, jlong dstAddress, jcharArray src, jint srcOffset, jint count, jboolean swap) {
189    POKER(jchar, Char, jshort, swapShorts);
190}
191
192static void Memory_pokeDoubleArray(JNIEnv* env, jclass, jlong dstAddress, jdoubleArray src, jint srcOffset, jint count, jboolean swap) {
193    POKER(jdouble, Double, jlong, swapLongs);
194}
195
196static void Memory_pokeFloatArray(JNIEnv* env, jclass, jlong dstAddress, jfloatArray src, jint srcOffset, jint count, jboolean swap) {
197    POKER(jfloat, Float, jint, swapInts);
198}
199
200static void Memory_pokeIntArray(JNIEnv* env, jclass, jlong dstAddress, jintArray src, jint srcOffset, jint count, jboolean swap) {
201    POKER(jint, Int, jint, swapInts);
202}
203
204static void Memory_pokeLongArray(JNIEnv* env, jclass, jlong dstAddress, jlongArray src, jint srcOffset, jint count, jboolean swap) {
205    POKER(jlong, Long, jlong, swapLongs);
206}
207
208static void Memory_pokeShortArray(JNIEnv* env, jclass, jlong dstAddress, jshortArray src, jint srcOffset, jint count, jboolean swap) {
209    POKER(jshort, Short, jshort, swapShorts);
210}
211
212static jshort Memory_peekShortNative(JNIEnv*, jclass, jlong srcAddress) {
213    return get_unaligned<jshort>(cast<const jshort*>(srcAddress));
214}
215
216static void Memory_pokeShortNative(JNIEnv*, jclass, jlong dstAddress, jshort value) {
217    put_unaligned<jshort>(cast<jshort*>(dstAddress), value);
218}
219
220static jint Memory_peekIntNative(JNIEnv*, jclass, jlong srcAddress) {
221    return get_unaligned<jint>(cast<const jint*>(srcAddress));
222}
223
224static void Memory_pokeIntNative(JNIEnv*, jclass, jlong dstAddress, jint value) {
225    put_unaligned<jint>(cast<jint*>(dstAddress), value);
226}
227
228static jlong Memory_peekLongNative(JNIEnv*, jclass, jlong srcAddress) {
229    return get_unaligned<jlong>(cast<const jlong*>(srcAddress));
230}
231
232static void Memory_pokeLongNative(JNIEnv*, jclass, jlong dstAddress, jlong value) {
233    put_unaligned<jlong>(cast<jlong*>(dstAddress), value);
234}
235
236static void unsafeBulkCopy(jbyte* dst, const jbyte* src, jint byteCount,
237        jint sizeofElement, jboolean swap) {
238    if (!swap) {
239        memcpy(dst, src, byteCount);
240        return;
241    }
242
243    if (sizeofElement == 2) {
244        jshort* dstShorts = reinterpret_cast<jshort*>(dst);
245        const jshort* srcShorts = reinterpret_cast<const jshort*>(src);
246        swapShorts(dstShorts, srcShorts, byteCount / 2);
247    } else if (sizeofElement == 4) {
248        jint* dstInts = reinterpret_cast<jint*>(dst);
249        const jint* srcInts = reinterpret_cast<const jint*>(src);
250        swapInts(dstInts, srcInts, byteCount / 4);
251    } else if (sizeofElement == 8) {
252        jlong* dstLongs = reinterpret_cast<jlong*>(dst);
253        const jlong* srcLongs = reinterpret_cast<const jlong*>(src);
254        swapLongs(dstLongs, srcLongs, byteCount / 8);
255    }
256}
257
258static void Memory_unsafeBulkGet(JNIEnv* env, jclass, jobject dstObject, jint dstOffset,
259        jint byteCount, jbyteArray srcArray, jint srcOffset, jint sizeofElement, jboolean swap) {
260    ScopedByteArrayRO srcBytes(env, srcArray);
261    if (srcBytes.get() == NULL) {
262        return;
263    }
264    jarray dstArray = reinterpret_cast<jarray>(dstObject);
265    jbyte* dstBytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(dstArray, NULL));
266    if (dstBytes == NULL) {
267        return;
268    }
269    jbyte* dst = dstBytes + dstOffset*sizeofElement;
270    const jbyte* src = srcBytes.get() + srcOffset;
271    unsafeBulkCopy(dst, src, byteCount, sizeofElement, swap);
272    env->ReleasePrimitiveArrayCritical(dstArray, dstBytes, 0);
273}
274
275static void Memory_unsafeBulkPut(JNIEnv* env, jclass, jbyteArray dstArray, jint dstOffset,
276        jint byteCount, jobject srcObject, jint srcOffset, jint sizeofElement, jboolean swap) {
277    ScopedByteArrayRW dstBytes(env, dstArray);
278    if (dstBytes.get() == NULL) {
279        return;
280    }
281    jarray srcArray = reinterpret_cast<jarray>(srcObject);
282    jbyte* srcBytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(srcArray, NULL));
283    if (srcBytes == NULL) {
284        return;
285    }
286    jbyte* dst = dstBytes.get() + dstOffset;
287    const jbyte* src = srcBytes + srcOffset*sizeofElement;
288    unsafeBulkCopy(dst, src, byteCount, sizeofElement, swap);
289    env->ReleasePrimitiveArrayCritical(srcArray, srcBytes, 0);
290}
291
292static JNINativeMethod gMethods[] = {
293    NATIVE_METHOD(Memory, memmove, "(Ljava/lang/Object;ILjava/lang/Object;IJ)V"),
294    FAST_NATIVE_METHOD(Memory, peekByte, "(J)B"),
295    NATIVE_METHOD(Memory, peekByteArray, "(J[BII)V"),
296    NATIVE_METHOD(Memory, peekCharArray, "(J[CIIZ)V"),
297    NATIVE_METHOD(Memory, peekDoubleArray, "(J[DIIZ)V"),
298    NATIVE_METHOD(Memory, peekFloatArray, "(J[FIIZ)V"),
299    FAST_NATIVE_METHOD(Memory, peekIntNative, "(J)I"),
300    NATIVE_METHOD(Memory, peekIntArray, "(J[IIIZ)V"),
301    FAST_NATIVE_METHOD(Memory, peekLongNative, "(J)J"),
302    NATIVE_METHOD(Memory, peekLongArray, "(J[JIIZ)V"),
303    FAST_NATIVE_METHOD(Memory, peekShortNative, "(J)S"),
304    NATIVE_METHOD(Memory, peekShortArray, "(J[SIIZ)V"),
305    FAST_NATIVE_METHOD(Memory, pokeByte, "(JB)V"),
306    NATIVE_METHOD(Memory, pokeByteArray, "(J[BII)V"),
307    NATIVE_METHOD(Memory, pokeCharArray, "(J[CIIZ)V"),
308    NATIVE_METHOD(Memory, pokeDoubleArray, "(J[DIIZ)V"),
309    NATIVE_METHOD(Memory, pokeFloatArray, "(J[FIIZ)V"),
310    FAST_NATIVE_METHOD(Memory, pokeIntNative, "(JI)V"),
311    NATIVE_METHOD(Memory, pokeIntArray, "(J[IIIZ)V"),
312    FAST_NATIVE_METHOD(Memory, pokeLongNative, "(JJ)V"),
313    NATIVE_METHOD(Memory, pokeLongArray, "(J[JIIZ)V"),
314    FAST_NATIVE_METHOD(Memory, pokeShortNative, "(JS)V"),
315    NATIVE_METHOD(Memory, pokeShortArray, "(J[SIIZ)V"),
316    NATIVE_METHOD(Memory, unsafeBulkGet, "(Ljava/lang/Object;II[BIIZ)V"),
317    NATIVE_METHOD(Memory, unsafeBulkPut, "([BIILjava/lang/Object;IIZ)V"),
318};
319void register_libcore_io_Memory(JNIEnv* env) {
320    jniRegisterNativeMethods(env, "libcore/io/Memory", gMethods, NELEM(gMethods));
321}
322