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 "JNIHelp.h"
20#include "JniConstants.h"
21#include "ScopedBytes.h"
22#include "ScopedPrimitiveArray.h"
23#include "UniquePtr.h"
24
25#include <byteswap.h>
26#include <errno.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/mman.h>
30
31#if defined(__arm__)
32// 32-bit ARM has load/store alignment restrictions for longs.
33#define LONG_ALIGNMENT_MASK 0x3
34#define INT_ALIGNMENT_MASK 0x0
35#define SHORT_ALIGNMENT_MASK 0x0
36#elif defined(__mips__)
37// MIPS has load/store alignment restrictions for longs, ints and shorts.
38#define LONG_ALIGNMENT_MASK 0x7
39#define INT_ALIGNMENT_MASK 0x3
40#define SHORT_ALIGNMENT_MASK 0x1
41#elif defined(__i386__)
42// x86 can load anything at any alignment.
43#define LONG_ALIGNMENT_MASK 0x0
44#define INT_ALIGNMENT_MASK 0x0
45#define SHORT_ALIGNMENT_MASK 0x0
46#else
47#error unknown load/store alignment restrictions for this architecture
48#endif
49
50// Use packed structures for access to unaligned data on targets with alignment restrictions.
51// The compiler will generate appropriate code to access these structures without
52// generating alignment exceptions.
53template <typename T> static inline T get_unaligned(const T* address) {
54    struct unaligned { T v; } __attribute__ ((packed));
55    const unaligned* p = reinterpret_cast<const unaligned*>(address);
56    return p->v;
57}
58
59template <typename T> static inline void put_unaligned(T* address, T v) {
60    struct unaligned { T v; } __attribute__ ((packed));
61    unaligned* p = reinterpret_cast<unaligned*>(address);
62    p->v = v;
63}
64
65template <typename T> static T cast(jint address) {
66    return reinterpret_cast<T>(static_cast<uintptr_t>(address));
67}
68
69// Byte-swap 2 jshort values packed in a jint.
70static inline jint bswap_2x16(jint v) {
71    // v is initially ABCD
72#if defined(__mips__) && defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
73    __asm__ volatile ("wsbh %0, %0" : "+r" (v));  // v=BADC
74#else
75    v = bswap_32(v);                              // v=DCBA
76    v = (v << 16) | ((v >> 16) & 0xffff);         // v=BADC
77#endif
78    return v;
79}
80
81static inline void swapShorts(jshort* dstShorts, const jshort* srcShorts, size_t count) {
82    // Do 32-bit swaps as long as possible...
83    jint* dst = reinterpret_cast<jint*>(dstShorts);
84    const jint* src = reinterpret_cast<const jint*>(srcShorts);
85
86    if ((reinterpret_cast<uintptr_t>(dst) & INT_ALIGNMENT_MASK) == 0 &&
87        (reinterpret_cast<uintptr_t>(src) & INT_ALIGNMENT_MASK) == 0) {
88        for (size_t i = 0; i < count / 2; ++i) {
89            jint v = *src++;
90            *dst++ = bswap_2x16(v);
91        }
92        // ...with one last 16-bit swap if necessary.
93        if ((count % 2) != 0) {
94            jshort v = *reinterpret_cast<const jshort*>(src);
95            *reinterpret_cast<jshort*>(dst) = bswap_16(v);
96        }
97    } else {
98        for (size_t i = 0; i < count / 2; ++i) {
99            jint v = get_unaligned<jint>(src++);
100            put_unaligned<jint>(dst++, bswap_2x16(v));
101        }
102        if ((count % 2) != 0) {
103          jshort v = get_unaligned<jshort>(reinterpret_cast<const jshort*>(src));
104          put_unaligned<jshort>(reinterpret_cast<jshort*>(dst), bswap_16(v));
105        }
106    }
107}
108
109static inline void swapInts(jint* dstInts, const jint* srcInts, size_t count) {
110    if ((reinterpret_cast<uintptr_t>(dstInts) & INT_ALIGNMENT_MASK) == 0 &&
111        (reinterpret_cast<uintptr_t>(srcInts) & INT_ALIGNMENT_MASK) == 0) {
112        for (size_t i = 0; i < count; ++i) {
113            jint v = *srcInts++;
114            *dstInts++ = bswap_32(v);
115        }
116    } else {
117        for (size_t i = 0; i < count; ++i) {
118            jint v = get_unaligned<int>(srcInts++);
119            put_unaligned<jint>(dstInts++, bswap_32(v));
120        }
121    }
122}
123
124static inline void swapLongs(jlong* dstLongs, const jlong* srcLongs, size_t count) {
125    jint* dst = reinterpret_cast<jint*>(dstLongs);
126    const jint* src = reinterpret_cast<const jint*>(srcLongs);
127    if ((reinterpret_cast<uintptr_t>(dstLongs) & INT_ALIGNMENT_MASK) == 0 &&
128        (reinterpret_cast<uintptr_t>(srcLongs) & INT_ALIGNMENT_MASK) == 0) {
129        for (size_t i = 0; i < count; ++i) {
130          jint v1 = *src++;
131          jint v2 = *src++;
132          *dst++ = bswap_32(v2);
133          *dst++ = bswap_32(v1);
134        }
135    } else {
136        for (size_t i = 0; i < count; ++i) {
137            jint v1 = get_unaligned<jint>(src++);
138            jint v2 = get_unaligned<jint>(src++);
139            put_unaligned<jint>(dst++, bswap_32(v2));
140            put_unaligned<jint>(dst++, bswap_32(v1));
141        }
142    }
143}
144
145static void Memory_memmove(JNIEnv* env, jclass, jobject dstObject, jint dstOffset, jobject srcObject, jint srcOffset, jlong length) {
146    ScopedBytesRW dstBytes(env, dstObject);
147    if (dstBytes.get() == NULL) {
148        return;
149    }
150    ScopedBytesRO srcBytes(env, srcObject);
151    if (srcBytes.get() == NULL) {
152        return;
153    }
154    memmove(dstBytes.get() + dstOffset, srcBytes.get() + srcOffset, length);
155}
156
157static jbyte Memory_peekByte(JNIEnv*, jclass, jint srcAddress) {
158    return *cast<const jbyte*>(srcAddress);
159}
160
161static void Memory_peekByteArray(JNIEnv* env, jclass, jint srcAddress, jbyteArray dst, jint dstOffset, jint byteCount) {
162    env->SetByteArrayRegion(dst, dstOffset, byteCount, cast<const jbyte*>(srcAddress));
163}
164
165// Implements the peekXArray methods:
166// - For unswapped access, we just use the JNI SetXArrayRegion functions.
167// - For swapped access, we use GetXArrayElements and our own copy-and-swap routines.
168//   GetXArrayElements is disproportionately cheap on Dalvik because it doesn't copy (as opposed
169//   to Hotspot, which always copies). The SWAP_FN copies and swaps in one pass, which is cheaper
170//   than copying and then swapping in a second pass. Depending on future VM/GC changes, the
171//   swapped case might need to be revisited.
172#define PEEKER(SCALAR_TYPE, JNI_NAME, SWAP_TYPE, SWAP_FN) { \
173    if (swap) { \
174        Scoped ## JNI_NAME ## ArrayRW elements(env, dst); \
175        if (elements.get() == NULL) { \
176            return; \
177        } \
178        const SWAP_TYPE* src = cast<const SWAP_TYPE*>(srcAddress); \
179        SWAP_FN(reinterpret_cast<SWAP_TYPE*>(elements.get()) + dstOffset, src, count); \
180    } else { \
181        const SCALAR_TYPE* src = cast<const SCALAR_TYPE*>(srcAddress); \
182        env->Set ## JNI_NAME ## ArrayRegion(dst, dstOffset, count, src); \
183    } \
184}
185
186static void Memory_peekCharArray(JNIEnv* env, jclass, jint srcAddress, jcharArray dst, jint dstOffset, jint count, jboolean swap) {
187    PEEKER(jchar, Char, jshort, swapShorts);
188}
189
190static void Memory_peekDoubleArray(JNIEnv* env, jclass, jint srcAddress, jdoubleArray dst, jint dstOffset, jint count, jboolean swap) {
191    PEEKER(jdouble, Double, jlong, swapLongs);
192}
193
194static void Memory_peekFloatArray(JNIEnv* env, jclass, jint srcAddress, jfloatArray dst, jint dstOffset, jint count, jboolean swap) {
195    PEEKER(jfloat, Float, jint, swapInts);
196}
197
198static void Memory_peekIntArray(JNIEnv* env, jclass, jint srcAddress, jintArray dst, jint dstOffset, jint count, jboolean swap) {
199    PEEKER(jint, Int, jint, swapInts);
200}
201
202static void Memory_peekLongArray(JNIEnv* env, jclass, jint srcAddress, jlongArray dst, jint dstOffset, jint count, jboolean swap) {
203    PEEKER(jlong, Long, jlong, swapLongs);
204}
205
206static void Memory_peekShortArray(JNIEnv* env, jclass, jint srcAddress, jshortArray dst, jint dstOffset, jint count, jboolean swap) {
207    PEEKER(jshort, Short, jshort, swapShorts);
208}
209
210static void Memory_pokeByte(JNIEnv*, jclass, jint dstAddress, jbyte value) {
211    *cast<jbyte*>(dstAddress) = value;
212}
213
214static void Memory_pokeByteArray(JNIEnv* env, jclass, jint dstAddress, jbyteArray src, jint offset, jint length) {
215    env->GetByteArrayRegion(src, offset, length, cast<jbyte*>(dstAddress));
216}
217
218// Implements the pokeXArray methods:
219// - For unswapped access, we just use the JNI GetXArrayRegion functions.
220// - For swapped access, we use GetXArrayElements and our own copy-and-swap routines.
221//   GetXArrayElements is disproportionately cheap on Dalvik because it doesn't copy (as opposed
222//   to Hotspot, which always copies). The SWAP_FN copies and swaps in one pass, which is cheaper
223//   than copying and then swapping in a second pass. Depending on future VM/GC changes, the
224//   swapped case might need to be revisited.
225#define POKER(SCALAR_TYPE, JNI_NAME, SWAP_TYPE, SWAP_FN) { \
226    if (swap) { \
227        Scoped ## JNI_NAME ## ArrayRO elements(env, src); \
228        if (elements.get() == NULL) { \
229            return; \
230        } \
231        const SWAP_TYPE* src = reinterpret_cast<const SWAP_TYPE*>(elements.get()) + srcOffset; \
232        SWAP_FN(cast<SWAP_TYPE*>(dstAddress), src, count); \
233    } else { \
234        env->Get ## JNI_NAME ## ArrayRegion(src, srcOffset, count, cast<SCALAR_TYPE*>(dstAddress)); \
235    } \
236}
237
238static void Memory_pokeCharArray(JNIEnv* env, jclass, jint dstAddress, jcharArray src, jint srcOffset, jint count, jboolean swap) {
239    POKER(jchar, Char, jshort, swapShorts);
240}
241
242static void Memory_pokeDoubleArray(JNIEnv* env, jclass, jint dstAddress, jdoubleArray src, jint srcOffset, jint count, jboolean swap) {
243    POKER(jdouble, Double, jlong, swapLongs);
244}
245
246static void Memory_pokeFloatArray(JNIEnv* env, jclass, jint dstAddress, jfloatArray src, jint srcOffset, jint count, jboolean swap) {
247    POKER(jfloat, Float, jint, swapInts);
248}
249
250static void Memory_pokeIntArray(JNIEnv* env, jclass, jint dstAddress, jintArray src, jint srcOffset, jint count, jboolean swap) {
251    POKER(jint, Int, jint, swapInts);
252}
253
254static void Memory_pokeLongArray(JNIEnv* env, jclass, jint dstAddress, jlongArray src, jint srcOffset, jint count, jboolean swap) {
255    POKER(jlong, Long, jlong, swapLongs);
256}
257
258static void Memory_pokeShortArray(JNIEnv* env, jclass, jint dstAddress, jshortArray src, jint srcOffset, jint count, jboolean swap) {
259    POKER(jshort, Short, jshort, swapShorts);
260}
261
262static jshort Memory_peekShort(JNIEnv*, jclass, jint srcAddress, jboolean swap) {
263    jshort result = *cast<const jshort*>(srcAddress);
264    if (swap) {
265        result = bswap_16(result);
266    }
267    return result;
268}
269
270static void Memory_pokeShort(JNIEnv*, jclass, jint dstAddress, jshort value, jboolean swap) {
271    if (swap) {
272        value = bswap_16(value);
273    }
274    *cast<jshort*>(dstAddress) = value;
275}
276
277static jint Memory_peekInt(JNIEnv*, jclass, jint srcAddress, jboolean swap) {
278    jint result = *cast<const jint*>(srcAddress);
279    if (swap) {
280        result = bswap_32(result);
281    }
282    return result;
283}
284
285static void Memory_pokeInt(JNIEnv*, jclass, jint dstAddress, jint value, jboolean swap) {
286    if (swap) {
287        value = bswap_32(value);
288    }
289    *cast<jint*>(dstAddress) = value;
290}
291
292static jlong Memory_peekLong(JNIEnv*, jclass, jint srcAddress, jboolean swap) {
293    jlong result;
294    const jlong* src = cast<const jlong*>(srcAddress);
295    if ((srcAddress & LONG_ALIGNMENT_MASK) == 0) {
296        result = *src;
297    } else {
298        result = get_unaligned<jlong>(src);
299    }
300    if (swap) {
301        result = bswap_64(result);
302    }
303    return result;
304}
305
306static void Memory_pokeLong(JNIEnv*, jclass, jint dstAddress, jlong value, jboolean swap) {
307    jlong* dst = cast<jlong*>(dstAddress);
308    if (swap) {
309        value = bswap_64(value);
310    }
311    if ((dstAddress & LONG_ALIGNMENT_MASK) == 0) {
312        *dst = value;
313    } else {
314        put_unaligned<jlong>(dst, value);
315    }
316}
317
318static void unsafeBulkCopy(jbyte* dst, const jbyte* src, jint byteCount,
319        jint sizeofElement, jboolean swap) {
320    if (!swap) {
321        memcpy(dst, src, byteCount);
322        return;
323    }
324
325    if (sizeofElement == 2) {
326        jshort* dstShorts = reinterpret_cast<jshort*>(dst);
327        const jshort* srcShorts = reinterpret_cast<const jshort*>(src);
328        swapShorts(dstShorts, srcShorts, byteCount / 2);
329    } else if (sizeofElement == 4) {
330        jint* dstInts = reinterpret_cast<jint*>(dst);
331        const jint* srcInts = reinterpret_cast<const jint*>(src);
332        swapInts(dstInts, srcInts, byteCount / 4);
333    } else if (sizeofElement == 8) {
334        jlong* dstLongs = reinterpret_cast<jlong*>(dst);
335        const jlong* srcLongs = reinterpret_cast<const jlong*>(src);
336        swapLongs(dstLongs, srcLongs, byteCount / 8);
337    }
338}
339
340static void Memory_unsafeBulkGet(JNIEnv* env, jclass, jobject dstObject, jint dstOffset,
341        jint byteCount, jbyteArray srcArray, jint srcOffset, jint sizeofElement, jboolean swap) {
342    ScopedByteArrayRO srcBytes(env, srcArray);
343    if (srcBytes.get() == NULL) {
344        return;
345    }
346    jarray dstArray = reinterpret_cast<jarray>(dstObject);
347    jbyte* dstBytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(dstArray, NULL));
348    if (dstBytes == NULL) {
349        return;
350    }
351    jbyte* dst = dstBytes + dstOffset*sizeofElement;
352    const jbyte* src = srcBytes.get() + srcOffset;
353    unsafeBulkCopy(dst, src, byteCount, sizeofElement, swap);
354    env->ReleasePrimitiveArrayCritical(dstArray, dstBytes, 0);
355}
356
357static void Memory_unsafeBulkPut(JNIEnv* env, jclass, jbyteArray dstArray, jint dstOffset,
358        jint byteCount, jobject srcObject, jint srcOffset, jint sizeofElement, jboolean swap) {
359    ScopedByteArrayRW dstBytes(env, dstArray);
360    if (dstBytes.get() == NULL) {
361        return;
362    }
363    jarray srcArray = reinterpret_cast<jarray>(srcObject);
364    jbyte* srcBytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(srcArray, NULL));
365    if (srcBytes == NULL) {
366        return;
367    }
368    jbyte* dst = dstBytes.get() + dstOffset;
369    const jbyte* src = srcBytes + srcOffset*sizeofElement;
370    unsafeBulkCopy(dst, src, byteCount, sizeofElement, swap);
371    env->ReleasePrimitiveArrayCritical(srcArray, srcBytes, 0);
372}
373
374static JNINativeMethod gMethods[] = {
375    NATIVE_METHOD(Memory, memmove, "(Ljava/lang/Object;ILjava/lang/Object;IJ)V"),
376    NATIVE_METHOD(Memory, peekByte, "!(I)B"),
377    NATIVE_METHOD(Memory, peekByteArray, "(I[BII)V"),
378    NATIVE_METHOD(Memory, peekCharArray, "(I[CIIZ)V"),
379    NATIVE_METHOD(Memory, peekDoubleArray, "(I[DIIZ)V"),
380    NATIVE_METHOD(Memory, peekFloatArray, "(I[FIIZ)V"),
381    NATIVE_METHOD(Memory, peekInt, "!(IZ)I"),
382    NATIVE_METHOD(Memory, peekIntArray, "(I[IIIZ)V"),
383    NATIVE_METHOD(Memory, peekLong, "!(IZ)J"),
384    NATIVE_METHOD(Memory, peekLongArray, "(I[JIIZ)V"),
385    NATIVE_METHOD(Memory, peekShort, "!(IZ)S"),
386    NATIVE_METHOD(Memory, peekShortArray, "(I[SIIZ)V"),
387    NATIVE_METHOD(Memory, pokeByte, "!(IB)V"),
388    NATIVE_METHOD(Memory, pokeByteArray, "(I[BII)V"),
389    NATIVE_METHOD(Memory, pokeCharArray, "(I[CIIZ)V"),
390    NATIVE_METHOD(Memory, pokeDoubleArray, "(I[DIIZ)V"),
391    NATIVE_METHOD(Memory, pokeFloatArray, "(I[FIIZ)V"),
392    NATIVE_METHOD(Memory, pokeInt, "!(IIZ)V"),
393    NATIVE_METHOD(Memory, pokeIntArray, "(I[IIIZ)V"),
394    NATIVE_METHOD(Memory, pokeLong, "!(IJZ)V"),
395    NATIVE_METHOD(Memory, pokeLongArray, "(I[JIIZ)V"),
396    NATIVE_METHOD(Memory, pokeShort, "!(ISZ)V"),
397    NATIVE_METHOD(Memory, pokeShortArray, "(I[SIIZ)V"),
398    NATIVE_METHOD(Memory, unsafeBulkGet, "(Ljava/lang/Object;II[BIIZ)V"),
399    NATIVE_METHOD(Memory, unsafeBulkPut, "([BIILjava/lang/Object;IIZ)V"),
400};
401void register_libcore_io_Memory(JNIEnv* env) {
402    jniRegisterNativeMethods(env, "libcore/io/Memory", gMethods, NELEM(gMethods));
403}
404