/**
* Copyright 2013, Landz and its contributors. All rights reserved.
*
* 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.
*/
package z.znr.invoke.linux.x64;
import z.znr.invoke.types.NativeType;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static z.znr.invoke.linux.x64.Native.LOOKUP;
public final class NumberUtil {
private NumberUtil() {}
public static boolean isPrimitiveInt(Class c) {
return byte.class == c || char.class == c || short.class == c || int.class == c || boolean.class == c;
}
public static void widen(SkinnyMethodAdapter mv, Class from, Class to) {
if (long.class == to && long.class != from && isPrimitiveInt(from)) {
mv.i2l();
} else if (boolean.class == to && boolean.class != from && isPrimitiveInt(from)) {
// Ensure only 0x0 and 0x1 values are used for boolean
mv.iconst_1();
mv.iand();
}
}
public static void widen(SkinnyMethodAdapter mv, Class from, Class to, NativeType nativeType) {
if (isPrimitiveInt(from)) {
if (nativeType == NativeType.UCHAR) {
mv.pushInt(0xff);
mv.iand();
} else if (nativeType == NativeType.USHORT) {
mv.pushInt(0xffff);
mv.iand();
}
if (long.class == to) {
mv.i2l();
switch (nativeType) {
case UINT:
case ULONG:
case POINTER:
if (Util.sizeof(nativeType) < 8) {
// strip off bits 32:63
mv.ldc(0xffffffffL);
mv.land();
}
break;
}
}
}
}
public static void narrow(SkinnyMethodAdapter mv, Class from, Class to) {
if (!from.equals(to)) {
if (byte.class == to || short.class == to || char.class == to || int.class == to || boolean.class == to) {
if (long.class == from) {
mv.l2i();
}
if (byte.class == to) {
mv.i2b();
} else if (short.class == to) {
mv.i2s();
} else if (char.class == to) {
mv.i2c();
} else if (boolean.class == to) {
// Ensure only 0x0 and 0x1 values are used for boolean
mv.iconst_1();
mv.iand();
}
}
}
}
public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, final Class to) {
narrow(mv, from, to);
widen(mv, from, to);
}
public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, final Class to, final NativeType nativeType) {
if (boolean.class == to) {
narrow(mv, from, to);
return;
}
switch (nativeType) {
case SCHAR:
narrow(mv, from, byte.class);
widen(mv, byte.class, to);
break;
case SSHORT:
narrow(mv, from, short.class);
widen(mv, short.class, to);
break;
case SINT:
narrow(mv, from, int.class);
widen(mv, int.class, to);
break;
case UCHAR:
narrow(mv, from, int.class);
mv.pushInt(0xff);
mv.iand();
widen(mv, int.class, to);
break;
case USHORT:
narrow(mv, from, int.class);
mv.pushInt(0xffff);
mv.iand();
widen(mv, int.class, to);
break;
case UINT:
case ULONG:
case POINTER:
if (Util.sizeof(nativeType) <= 4) {
narrow(mv, from, int.class);
if (long.class == to) {
mv.i2l();
// strip off bits 32:63
mv.ldc(0xffffffffL);
mv.land();
}
} else {
widen(mv, from, to);
}
break;
case FLOAT:
case DOUBLE:
break;
default:
narrow(mv, from, to);
widen(mv, from, to);
break;
}
}
static MethodHandle getParameterConversionHandle(NativeType nativeType, Class from, Class to) {
try {
switch (nativeType) {
case FLOAT:
return LOOKUP.findStatic(Float.class, "floatToRawIntBits", MethodType.methodType(int.class, float.class))
.asType(MethodType.methodType(to, from));
case DOUBLE:
return LOOKUP.findStatic(Double.class, "doubleToRawLongBits", MethodType.methodType(long.class, double.class))
.asType(MethodType.methodType(to, from));
default:
return getIntegerConversionHandle(nativeType, from, to);
}
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
static MethodHandle getResultConversionHandle(NativeType nativeType, Class from, Class to) {
try {
switch (nativeType) {
case FLOAT:
return MethodHandles.explicitCastArguments(LOOKUP.findStatic(Float.class, "intBitsToFloat", MethodType.methodType(float.class, int.class)),
MethodType.methodType(to, from));
case DOUBLE:
return LOOKUP.findStatic(Double.class, "longBitsToDouble", MethodType.methodType(double.class, long.class))
.asType(MethodType.methodType(to, from));
case VOID:
return null;
default:
return getIntegerConversionHandle(nativeType, from, to);
}
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
static MethodHandle getIntegerConversionHandle(NativeType nativeType, Class from, Class to) throws NoSuchMethodException, IllegalAccessException {
switch (nativeType) {
case SCHAR:
case UCHAR:
case SSHORT:
case USHORT:
case SINT:
case UINT:
case SLONG:
case ULONG:
case SLONG_LONG:
case ULONG_LONG:
case POINTER:
if (nativeType.size() <= 4) {
Class nativeIntType = long.class == to ? long.class : int.class;
String conversionHelper = (nativeType.isUnsigned() ? "u" : "s") + Integer.toString(nativeType.size() * 8);
MethodHandle mh = LOOKUP.findStatic(AsmRuntime.class, conversionHelper, MethodType.methodType(nativeIntType, nativeIntType));
return MethodHandles.explicitCastArguments(mh, MethodType.methodType(to, from));
}
return null;
default:
return null;
}
}
}