diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/ClassDataItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/ClassDataItem.java index 1c98cf3d..632e56fd 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/ClassDataItem.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/ClassDataItem.java @@ -32,12 +32,14 @@ package org.jf.dexlib2.dexbacked.raw; import com.google.common.base.Joiner; +import com.google.common.collect.Maps; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.dexbacked.DexReader; import org.jf.dexlib2.util.AnnotatedBytes; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.HashMap; import java.util.Map; public class ClassDataItem { @@ -159,4 +161,59 @@ public class ClassDataItem { } }; } + + @Nullable public static Map getCodeItemMethodMap(@Nonnull RawDexFile dexFile) { + MapItem classDataMap = dexFile.getMapItemForSection(ItemType.CLASS_DATA_ITEM); + if (classDataMap != null) { + HashMap codeItemMethodMap = Maps.newHashMap(); + + int classDataOffset = classDataMap.getOffset(); + + DexReader reader = dexFile.readerAt(classDataOffset); + + for (int i=0; i map) { + int methodIndexDelta = reader.readSmallUleb128(); + int methodIndex = previousIndex + methodIndexDelta; + String methodString = MethodIdItem.asString(reader.dexBuf, methodIndex); + + reader.readSmallUleb128(); + + int codeOffset = reader.readSmallUleb128(); + if (codeOffset != 0) { + map.put(codeOffset, methodString); + } + return methodIndex; + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java new file mode 100644 index 00000000..b33a8a36 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java @@ -0,0 +1,380 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.dexbacked.raw; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import org.jf.dexlib2.dexbacked.DexReader; +import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction; +import org.jf.dexlib2.iface.instruction.*; +import org.jf.dexlib2.iface.instruction.formats.*; +import org.jf.dexlib2.util.AnnotatedBytes; +import org.jf.dexlib2.util.ReferenceUtil; +import org.jf.util.NumberUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +public class CodeItem { + public static final int REGISTERS_OFFSET = 0; + public static final int INS_OFFSET = 2; + public static final int OUTS_OFFSET = 4; + public static final int TRIES_OFFSET = 6; + public static final int DEBUG_INFO_OFFSET = 8; + public static final int INSTRUCTION_COUNT_OFFSET = 12; + + private abstract static class CodeItemAnnotator extends SectionAnnotator { + public CodeItemAnnotator() { + } + + @Nonnull @Override public String getItemName() { + return "code_item"; + } + + @Override public int getItemAlignment() { + return 4; + } + + @Override protected void annotateItem(@Nonnull AnnotatedBytes out, @Nonnull RawDexFile dexFile, int itemIndex) { + DexReader reader = dexFile.readerAt(out.getCursor()); + + int registers = reader.readUshort(); + out.annotate(2, "registers_size = %d", registers); + + int inSize = reader.readUshort(); + out.annotate(2, "ins_size = %d", inSize); + + int outSize = reader.readUshort(); + out.annotate(2, "outs_size = %d", outSize); + + int triesCount = reader.readUshort(); + out.annotate(2, "tries_size = %d", triesCount); + + int debugInfoOffset = reader.readSmallUint(); + out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset); + + int instructionSize = reader.readSmallUint(); + out.annotate(4, "insns_size = 0x%x", instructionSize); + + out.annotate(0, "instructions:"); + out.indent(); + + int end = reader.getOffset() + instructionSize*2; + while (reader.getOffset() < end) { + Instruction instruction = DexBackedInstruction.readFrom(reader); + + switch (instruction.getOpcode().format) { + case Format10x: + annotateInstruction10x(out, instruction); + break; + case Format35c: + annotateInstruction35c(out, (Instruction35c)instruction); + break; + case Format3rc: + annotateInstruction3rc(out, (Instruction3rc)instruction); + break; + case ArrayPayload: + annotateArrayPayload(out, (ArrayPayload)instruction); + break; + case PackedSwitchPayload: + annotatePackedSwitchPayload(out, (PackedSwitchPayload)instruction); + break; + case SparseSwitchPayload: + annotateSparseSwitchPayload(out, (SparseSwitchPayload)instruction); + break; + default: + annotateDefaultInstruction(out, instruction); + break; + } + + assert reader.getOffset() == out.getCursor(); + } + out.deindent(); + + if (triesCount > 0) { + if ((reader.getOffset() % 4) != 0) { + reader.readUshort(); + out.annotate(2, "padding"); + } + + out.annotate(0, "try_items:"); + out.indent(); + for (int i=0; i args = Lists.newArrayList(); + + int registerCount = instruction.getRegisterCount(); + if (registerCount == 1) { + args.add(formatRegister(instruction.getRegisterC())); + } else if (registerCount == 2) { + args.add(formatRegister(instruction.getRegisterC())); + args.add(formatRegister(instruction.getRegisterD())); + } else if (registerCount == 3) { + args.add(formatRegister(instruction.getRegisterC())); + args.add(formatRegister(instruction.getRegisterD())); + args.add(formatRegister(instruction.getRegisterE())); + } else if (registerCount == 4) { + args.add(formatRegister(instruction.getRegisterC())); + args.add(formatRegister(instruction.getRegisterD())); + args.add(formatRegister(instruction.getRegisterE())); + args.add(formatRegister(instruction.getRegisterF())); + } else if (registerCount == 5) { + args.add(formatRegister(instruction.getRegisterC())); + args.add(formatRegister(instruction.getRegisterD())); + args.add(formatRegister(instruction.getRegisterE())); + args.add(formatRegister(instruction.getRegisterF())); + args.add(formatRegister(instruction.getRegisterG())); + } + + String reference = ReferenceUtil.getReferenceString(instruction.getReference()); + + out.annotate(6, String.format("%s {%s}, %s", + instruction.getOpcode().name, Joiner.on(", ").join(args), reference)); + } + + private static void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) { + int startRegister = instruction.getStartRegister(); + int endRegister = startRegister + instruction.getRegisterCount() - 1; + String reference = ReferenceUtil.getReferenceString(instruction.getReference()); + out.annotate(6, String.format("%s {%s .. %s}, %s", + instruction.getOpcode().name, formatRegister(startRegister), formatRegister(endRegister), + reference)); + } + + private static void annotateDefaultInstruction(@Nonnull AnnotatedBytes out, @Nonnull Instruction instruction) { + List args = Lists.newArrayList(); + + if (instruction instanceof OneRegisterInstruction) { + args.add(formatRegister(((OneRegisterInstruction)instruction).getRegisterA())); + if (instruction instanceof TwoRegisterInstruction) { + args.add(formatRegister(((TwoRegisterInstruction)instruction).getRegisterB())); + if (instruction instanceof ThreeRegisterInstruction) { + args.add(formatRegister(((ThreeRegisterInstruction)instruction).getRegisterC())); + } + } + } + + if (instruction instanceof ReferenceInstruction) { + args.add(ReferenceUtil.getReferenceString( + ((ReferenceInstruction)instruction).getReference())); + } + + if (instruction instanceof OffsetInstruction) { + int offset = ((OffsetInstruction)instruction).getCodeOffset(); + String sign = offset>=0?"+":"-"; + args.add(String.format("%s0x%x", sign, offset)); + } + + if (instruction instanceof NarrowLiteralInstruction) { + int value = ((NarrowLiteralInstruction)instruction).getNarrowLiteral(); + if (NumberUtils.isLikelyFloat(value)) { + args.add(String.format("%d # %f", value, Float.intBitsToFloat(value))); + } else { + args.add(String.format("%d", value)); + } + } else if (instruction instanceof WideLiteralInstruction) { + long value = ((WideLiteralInstruction)instruction).getWideLiteral(); + if (NumberUtils.isLikelyDouble(value)) { + args.add(String.format("%d # %f", value, Double.longBitsToDouble(value))); + } else { + args.add(String.format("%d", value)); + } + } + + out.annotate(instruction.getCodeUnits()*2, "%s %s", + instruction.getOpcode().name, Joiner.on(", ").join(args)); + } + + private void annotateArrayPayload(@Nonnull AnnotatedBytes out, @Nonnull ArrayPayload instruction) { + List elements = instruction.getArrayElements(); + int elementWidth = instruction.getElementWidth(); + + out.annotate(2, instruction.getOpcode().name); + out.indent(); + out.annotate(2, "element_width = %d", elementWidth); + out.annotate(4, "size = %d", elements.size()); + out.annotate(0, "elements:"); + out.indent(); + for (int i=0; i elements = instruction.getSwitchElements(); + + out.annotate(2, instruction.getOpcode().name); + out.indent(); + + out.annotate(2, "size = %d", elements.size()); + out.annotate(4, "first_key = %d", elements.get(0).getKey()); + out.annotate(0, "targets:"); + out.indent(); + for (int i=0; i elements = instruction.getSwitchElements(); + + out.annotate(2, instruction.getOpcode().name); + out.indent(); + out.annotate(2, "size = %d", elements.size()); + out.annotate(0, "keys:"); + out.indent(); + for (int i=0; i methodMap = ClassDataItem.getCodeItemMethodMap(dexFile); + + SectionAnnotator annotator = new CodeItemAnnotator() { + @Nullable @Override + public String getItemIdentity(@Nonnull RawDexFile dexFile, int itemIndex, int itemOffset) { + if (methodMap != null) { + return methodMap.get(itemOffset); + } + return null; + } + }; + annotator.annotateSection(out, dexFile, itemCount); + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java index 7e249a05..d9d95b8f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java @@ -97,6 +97,7 @@ public class RawDexFile extends DexBackedDexFile.Impl { builder.put(ItemType.ANNOTATION_SET_ITEM, AnnotationSetItem.getAnnotator()); builder.put(ItemType.ANNOTATION_ITEM, AnnotationItem.getAnnotator()); builder.put(ItemType.CLASS_DATA_ITEM, ClassDataItem.getAnnotator()); + builder.put(ItemType.CODE_ITEM, CodeItem.getAnnotator()); annotators = builder.build(); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java index 76e6cedf..6b52a3e5 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java @@ -31,9 +31,10 @@ package org.jf.dexlib2.util; -import org.jf.dexlib2.iface.reference.FieldReference; -import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.*; +import org.jf.util.StringUtils; +import javax.annotation.Nullable; import java.io.IOException; import java.io.Writer; @@ -93,5 +94,22 @@ public final class ReferenceUtil { writer.write(fieldReference.getType()); } + @Nullable + public static String getReferenceString(Reference reference) { + if (reference instanceof StringReference) { + return String.format("\"%s\"", StringUtils.escapeString(((StringReference)reference).getString())); + } + if (reference instanceof TypeReference) { + return ((TypeReference)reference).getType(); + } + if (reference instanceof FieldReference) { + return getFieldDescriptor((FieldReference)reference); + } + if (reference instanceof MethodReference) { + return getMethodDescriptor((MethodReference)reference); + } + return null; + } + private ReferenceUtil() {} } diff --git a/util/src/main/java/org/jf/util/NumberUtils.java b/util/src/main/java/org/jf/util/NumberUtils.java new file mode 100644 index 00000000..401de928 --- /dev/null +++ b/util/src/main/java/org/jf/util/NumberUtils.java @@ -0,0 +1,141 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.util; + +import java.text.DecimalFormat; + +public class NumberUtils { + private static final int canonicalFloatNaN = Float.floatToRawIntBits(Float.NaN); + private static final int maxFloat = Float.floatToRawIntBits(Float.MAX_VALUE); + private static final int piFloat = Float.floatToRawIntBits((float)Math.PI); + private static final int eFloat = Float.floatToRawIntBits((float)Math.E); + + private static final long canonicalDoubleNaN = Double.doubleToRawLongBits(Double.NaN); + private static final long maxDouble = Double.doubleToLongBits(Double.MAX_VALUE); + private static final long piDouble = Double.doubleToLongBits(Math.PI); + private static final long eDouble = Double.doubleToLongBits(Math.E); + + private static final DecimalFormat format = new DecimalFormat("0.####################E0"); + + public static boolean isLikelyFloat(int value) { + // Check for some common named float values + // We don't check for Float.MIN_VALUE, which has an integer representation of 1 + if (value == canonicalFloatNaN || + value == maxFloat || + value == piFloat || + value == eFloat) { + return true; + } + + // Check for some named integer values + if (value == Integer.MAX_VALUE || value == Integer.MIN_VALUE) { + return false; + } + + + // Check for likely resource id + int packageId = value >> 24; + int resourceType = value >> 16 & 0xff; + int resourceId = value & 0xffff; + if ((packageId == 0x7f || packageId == 1) && resourceType < 0x1f && resourceId < 0xfff) { + return false; + } + + // a non-canocical NaN is more likely to be an integer + float floatValue = Float.intBitsToFloat(value); + if (Float.isNaN(floatValue)) { + return false; + } + + // Otherwise, whichever has a shorter scientific notation representation is more likely. + // Integer wins the tie + String asInt = format.format(value); + String asFloat = format.format(floatValue); + + // try to strip off any small imprecision near the end of the mantissa + int decimalPoint = asFloat.indexOf('.'); + int exponent = asFloat.indexOf("E"); + int zeros = asFloat.indexOf("000"); + if (zeros > decimalPoint && zeros < exponent) { + asFloat = asFloat.substring(0, zeros) + asFloat.substring(exponent); + } else { + int nines = asFloat.indexOf("999"); + if (nines > decimalPoint && nines < exponent) { + asFloat = asFloat.substring(0, nines) + asFloat.substring(exponent); + } + } + + return asFloat.length() < asInt.length(); + } + + public static boolean isLikelyDouble(long value) { + // Check for some common named double values + // We don't check for Double.MIN_VALUE, which has a long representation of 1 + if (value == canonicalDoubleNaN || + value == maxDouble || + value == piDouble || + value == eDouble) { + return true; + } + + // Check for some named long values + if (value == Long.MAX_VALUE || value == Long.MIN_VALUE) { + return false; + } + + // a non-canocical NaN is more likely to be an long + double doubleValue = Double.longBitsToDouble(value); + if (Double.isNaN(doubleValue)) { + return false; + } + + // Otherwise, whichever has a shorter scientific notation representation is more likely. + // Long wins the tie + String asLong = format.format(value); + String asDouble = format.format(doubleValue); + + // try to strip off any small imprecision near the end of the mantissa + int decimalPoint = asDouble.indexOf('.'); + int exponent = asDouble.indexOf("E"); + int zeros = asDouble.indexOf("000"); + if (zeros > decimalPoint && zeros < exponent) { + asDouble = asDouble.substring(0, zeros) + asDouble.substring(exponent); + } else { + int nines = asDouble.indexOf("999"); + if (nines > decimalPoint && nines < exponent) { + asDouble = asDouble.substring(0, nines) + asDouble.substring(exponent); + } + } + + return asDouble.length() < asLong.length(); + } +} diff --git a/util/src/test/java/org/jf/util/NumberUtilsTest.java b/util/src/test/java/org/jf/util/NumberUtilsTest.java new file mode 100644 index 00000000..a059c87d --- /dev/null +++ b/util/src/test/java/org/jf/util/NumberUtilsTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.util; + +import junit.framework.Assert; +import org.junit.Test; + +public class NumberUtilsTest { + @Test + public void isLikelyFloatTest() { + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.23f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.0f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.NaN))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.NEGATIVE_INFINITY))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.POSITIVE_INFINITY))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1e-30f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1000f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(-1f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(-5f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.3333f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(4.5f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(.1f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(50000f))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.MAX_VALUE))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits((float)Math.PI))); + Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits((float)Math.E))); + + Assert.assertTrue(NumberUtils.isLikelyFloat(2139095039)); + + + // Float.MIN_VALUE is equivalent to integer value 1 - this should be detected as an integer + //Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(Float.MIN_VALUE))); + + // This one doesn't quite work. It has a series of 2 0's, but that is probably not enough to strip off normally + //Assert.assertTrue(NumberUtils.isLikelyFloat(Float.floatToRawIntBits(1.33333f))); + + Assert.assertFalse(NumberUtils.isLikelyFloat(0)); + Assert.assertFalse(NumberUtils.isLikelyFloat(1)); + Assert.assertFalse(NumberUtils.isLikelyFloat(10)); + Assert.assertFalse(NumberUtils.isLikelyFloat(100)); + Assert.assertFalse(NumberUtils.isLikelyFloat(1000)); + Assert.assertFalse(NumberUtils.isLikelyFloat(1024)); + Assert.assertFalse(NumberUtils.isLikelyFloat(1234)); + Assert.assertFalse(NumberUtils.isLikelyFloat(-5)); + Assert.assertFalse(NumberUtils.isLikelyFloat(-13)); + Assert.assertFalse(NumberUtils.isLikelyFloat(-123)); + Assert.assertFalse(NumberUtils.isLikelyFloat(20000000)); + Assert.assertFalse(NumberUtils.isLikelyFloat(2000000000)); + Assert.assertFalse(NumberUtils.isLikelyFloat(-2000000000)); + Assert.assertFalse(NumberUtils.isLikelyFloat(Integer.MAX_VALUE)); + Assert.assertFalse(NumberUtils.isLikelyFloat(Integer.MIN_VALUE)); + Assert.assertFalse(NumberUtils.isLikelyFloat(Short.MIN_VALUE)); + Assert.assertFalse(NumberUtils.isLikelyFloat(Short.MAX_VALUE)); + } + + @Test + public void isLikelyDoubleTest() { + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.23f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.0f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.NaN))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.POSITIVE_INFINITY))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1e-30f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1000f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(-1f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(-5f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.3333f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(1.33333f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(4.5f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(.1f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(50000f))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.MAX_VALUE))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Math.PI))); + Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Math.E))); + + // Double.MIN_VALUE is equivalent to integer value 1 - this should be detected as an integer + //Assert.assertTrue(NumberUtils.isLikelyDouble(Double.doubleToRawLongBits(Double.MIN_VALUE))); + + Assert.assertFalse(NumberUtils.isLikelyDouble(0)); + Assert.assertFalse(NumberUtils.isLikelyDouble(1)); + Assert.assertFalse(NumberUtils.isLikelyDouble(10)); + Assert.assertFalse(NumberUtils.isLikelyDouble(100)); + Assert.assertFalse(NumberUtils.isLikelyDouble(1000)); + Assert.assertFalse(NumberUtils.isLikelyDouble(1024)); + Assert.assertFalse(NumberUtils.isLikelyDouble(1234)); + Assert.assertFalse(NumberUtils.isLikelyDouble(-5)); + Assert.assertFalse(NumberUtils.isLikelyDouble(-13)); + Assert.assertFalse(NumberUtils.isLikelyDouble(-123)); + Assert.assertFalse(NumberUtils.isLikelyDouble(20000000)); + Assert.assertFalse(NumberUtils.isLikelyDouble(2000000000)); + Assert.assertFalse(NumberUtils.isLikelyDouble(-2000000000)); + Assert.assertFalse(NumberUtils.isLikelyDouble(Integer.MAX_VALUE)); + Assert.assertFalse(NumberUtils.isLikelyDouble(Integer.MIN_VALUE)); + Assert.assertFalse(NumberUtils.isLikelyDouble(Short.MIN_VALUE)); + Assert.assertFalse(NumberUtils.isLikelyDouble(Short.MAX_VALUE)); + } +}