package net.i2p.util;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.URL;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import freenet.support.CPUInformation.AMDCPUInfo;
import freenet.support.CPUInformation.CPUID;
import freenet.support.CPUInformation.CPUInfo;
import freenet.support.CPUInformation.IntelCPUInfo;
import freenet.support.CPUInformation.VIACPUInfo;
import freenet.support.CPUInformation.UnknownCPUException;
import net.i2p.I2PAppContext;
import net.i2p.crypto.CryptoConstants;
import net.i2p.data.DataHelper;
/**
* <p>BigInteger that takes advantage of the jbigi library for the modPow operation,
* which accounts for a massive segment of the processing cost of asymmetric
* crypto.
*
* The jbigi library itself is basically just a JNI wrapper around the
* GMP library - a collection of insanely efficient routines for dealing with
* big numbers.</p>
*
* There are three environmental properties for configuring this component: <ul>
* <li><b>jbigi.enable</b>: whether to use the native library (defaults to "true")</li>
* <li><b>jbigi.impl</b>: select which resource to use as the native implementation</li>
* <li><b>jbigi.ref</b>: the file specified in this parameter may contain a resource
* name to override jbigi.impl (defaults to "jbigi.cfg")</li>
* </ul>
*
* <p>If jbigi.enable is set to false, this class won't even attempt to use the
* native library, but if it is set to true (or is not specified), it will first
* check the platform specific library path for the "jbigi" library, as defined by
* {@link Runtime#loadLibrary} - e.g. C:\windows\jbigi.dll or /lib/libjbigi.so, as
* well as the CLASSPATH for a resource named 'jbigi'. If that fails, it reviews
* the jbigi.impl environment property - if that is set, it checks all of the
* components in the CLASSPATH for the file specified and attempts to load it as
* the native library. If jbigi.impl is not set, it uses the jcpuid library
* described below. If there is still no matching resource, or if that resource
* is not a valid OS/architecture specific library, the NativeBigInteger will
* revert to using the pure java implementation.</p>
*
* <p>When attempting to load the native implementation as a resource from the CLASSPATH,
* the NativeBigInteger will make use of the jcpuid component which runs some assembly
* code to determine the current CPU implementation, such as "pentium4" or "k623".
* We then use that, combined with the OS, to build an optimized resource name - e.g.
* "net/i2p/util/libjbigi-freebsd-pentium4.so" or "net/i2p/util/jbigi-windows-k623.dll".
* If that resource exists, we use it. If it doesn't (or the jcpuid component fails),
* we try a generic native implementation using "none" for the CPU (ala
* "net/i2p/util/jbigi-windows-none.dll").</p>
*
* <p>Running this class by itself does a basic unit test and benchmarks the
* NativeBigInteger.modPow vs. the BigInteger.modPow by running a 2Kbit op 100
* times. At the end of each test, if the native implementation is loaded this will output
* something like:</p>
* <pre>
* native run time: 6090ms (60ms each)
* java run time: 68067ms (673ms each)
* native = 8.947066860593239% of pure java time
* </pre>
*
* <p>If the native implementation is not loaded, it will start by saying:</p>
* <pre>
* WARN: Native BigInteger library jbigi not loaded - using pure java
* </pre>
* <p>Then go on to run the test, finally outputting:</p>
* <pre>
* java run time: 64653ms (640ms each)
* However, we couldn't load the native library, so this doesn't test much
* </pre>
*
*/
public class NativeBigInteger extends BigInteger {
/** did we load the native lib correctly? */
private static boolean _nativeOk;
/** is native lib loaded and at least version 3? */
private static boolean _nativeOk3;
/** is native lib loaded and at least version 3, and GMP at least version 5? */
private static boolean _nativeCTOk;
private static int _jbigiVersion;
private static String _libGMPVersion = "unknown";
private static String _loadStatus = "uninitialized";
private static String _cpuModel = "uninitialized";
private static String _extractedResource;
/**
* do we want to dump some basic success/failure info to stderr during
* initialization? this would otherwise use the Log component, but this makes
* it easier for other systems to reuse this class
*
* Well, we really want to use Log so if you are one of those "other systems"
* then comment out the I2PAppContext usage below.
*
* Set to false if not in router context, so scripts using TrustedUpdate
* don't spew log messages. main() below overrides to true.
*/
private static boolean _doLog = System.getProperty("jbigi.dontLog") == null &&
I2PAppContext.getCurrentContext() != null &&
I2PAppContext.getCurrentContext().isRouterContext();
/**
* The following libraries are be available in jbigi.jar in all I2P versions
* originally installed as release 0.6.1.10 or later (released 2006-01-16),
* for linux, freebsd, and windows, EXCEPT:
* - k63 was removed for linux and freebsd in 0.8.7 (identical to k62)
* - athlon64 not available for freebsd
* - viac3 not available for windows
*/
private final static String JBIGI_OPTIMIZATION_K6 = "k6";
private final static String JBIGI_OPTIMIZATION_K6_2 = "k62";
private final static String JBIGI_OPTIMIZATION_K6_3 = "k63";
private final static String JBIGI_OPTIMIZATION_ATHLON = "athlon";
private final static String JBIGI_OPTIMIZATION_ATHLON64 = "athlon64";
private final static String JBIGI_OPTIMIZATION_PENTIUM = "pentium";
private final static String JBIGI_OPTIMIZATION_PENTIUMMMX = "pentiummmx";
private final static String JBIGI_OPTIMIZATION_PENTIUM2 = "pentium2";
private final static String JBIGI_OPTIMIZATION_PENTIUM3 = "pentium3";
private final static String JBIGI_OPTIMIZATION_PENTIUM4 = "pentium4";
private final static String JBIGI_OPTIMIZATION_VIAC3 = "viac3";
/**
* The 7 optimizations below here are since 0.8.7. Each of the 32-bit processors below
* needs an explicit fallback in getResourceList() or getMiddleName2().
* 64-bit processors will fallback to athlon64 and athlon in getResourceList().
* @since 0.8.7
*/
private final static String JBIGI_OPTIMIZATION_ATOM = "atom";
private final static String JBIGI_OPTIMIZATION_CORE2 = "core2";
private final static String JBIGI_OPTIMIZATION_COREI = "corei";
private final static String JBIGI_OPTIMIZATION_GEODE = "geode";
private final static String JBIGI_OPTIMIZATION_NANO = "nano";
private final static String JBIGI_OPTIMIZATION_PENTIUMM = "pentiumm";
/** all libjbibi builds are identical to pentium3, case handled in getMiddleName2() */
private final static String JBIGI_OPTIMIZATION_VIAC32 = "viac32";
/**
* The optimization levels defined here are since 0.9.26. Each of the 32-bit processors below
* needs an explicit fallback in getResourceList() or getMiddleName2().
* 64-bit processors will fallback to athlon64 and athlon in getResourceList().
* @since 0.9.26
*/
private final static String JBIGI_OPTIMIZATION_COREI_SBR = "coreisbr";
private final static String JBIGI_OPTIMIZATION_COREI_HWL = "coreihwl";
private final static String JBIGI_OPTIMIZATION_COREI_BWL = "coreibwl";
private final static String JBIGI_OPTIMIZATION_K10 = "k10";
private final static String JBIGI_OPTIMIZATION_BULLDOZER = "bulldozer";
private final static String JBIGI_OPTIMIZATION_PILEDRIVER = "piledriver";
private final static String JBIGI_OPTIMIZATION_STEAMROLLER = "steamroller";
private final static String JBIGI_OPTIMIZATION_EXCAVATOR = "excavator";
private final static String JBIGI_OPTIMIZATION_BOBCAT = "bobcat";
private final static String JBIGI_OPTIMIZATION_JAGUAR = "jaguar";
/**
* Non-x86, no fallbacks to older libs or to "none"
* @since 0.8.7
*/
private final static String JBIGI_OPTIMIZATION_PPC = "ppc";
/**
* ARM
* @since 0.9.26
*/
private final static String JBIGI_OPTIMIZATION_ARM_ARMV5 = "armv5";
private final static String JBIGI_OPTIMIZATION_ARM_ARMV6 = "armv6";
private final static String JBIGI_OPTIMIZATION_ARM_ARMV7 = "armv7";
private final static String JBIGI_OPTIMIZATION_ARM_CORTEX_A5 = "armcortexa5";
private final static String JBIGI_OPTIMIZATION_ARM_CORTEX_A7 = "armcortexa7";
private final static String JBIGI_OPTIMIZATION_ARM_CORTEX_A8 = "armcortexa8";
private final static String JBIGI_OPTIMIZATION_ARM_CORTEX_A9 = "armcortexa9";
private final static String JBIGI_OPTIMIZATION_ARM_CORTEX_A15 = "armcortexa15";
/**
* None, no optimizations. The default fallback for x86.
* @since 0.9.26
*/
private final static String JBIGI_OPTIMIZATION_X86 = "none";
/**
* CPU architecture compatibility lists, in order of preference.
*
* The list is organized by the chronological evolution of architectures.
* Totally unrelated architectures have separate lists. Sequences of
* architectures that aren't backwards compatible (performance wise) for
* some reasons have also been separated out.
*
* Really this could be represented by a DAG, but the benefits don't
* outweigh the implementation time.
*/
// none -> {"none"), since 0.9.30
private final static String[] JBIGI_COMPAT_LIST_NONE = {JBIGI_OPTIMIZATION_X86};
private final static String[] JBIGI_COMPAT_LIST_PPC = {JBIGI_OPTIMIZATION_PPC};
private final static String[] JBIGI_COMPAT_LIST_ARM = {JBIGI_OPTIMIZATION_ARM_CORTEX_A15, JBIGI_OPTIMIZATION_ARM_CORTEX_A9, JBIGI_OPTIMIZATION_ARM_CORTEX_A8,
JBIGI_OPTIMIZATION_ARM_CORTEX_A7, JBIGI_OPTIMIZATION_ARM_CORTEX_A5, JBIGI_OPTIMIZATION_ARM_ARMV7,
JBIGI_OPTIMIZATION_ARM_ARMV6, JBIGI_OPTIMIZATION_ARM_ARMV5};
private final static String[] JBIGI_COMPAT_LIST_VIA = {JBIGI_OPTIMIZATION_NANO, JBIGI_OPTIMIZATION_VIAC32, JBIGI_OPTIMIZATION_VIAC3,
JBIGI_OPTIMIZATION_PENTIUM, JBIGI_OPTIMIZATION_X86};
private final static String[] JBIGI_COMPAT_LIST_AMD_ATHLON = {JBIGI_OPTIMIZATION_K10, JBIGI_OPTIMIZATION_ATHLON64, JBIGI_OPTIMIZATION_ATHLON,
JBIGI_OPTIMIZATION_K6_3, JBIGI_OPTIMIZATION_K6_2, JBIGI_OPTIMIZATION_K6, JBIGI_OPTIMIZATION_X86};
private final static String[] JBIGI_COMPAT_LIST_AMD_GEODE = {JBIGI_OPTIMIZATION_GEODE, JBIGI_OPTIMIZATION_K6_3, JBIGI_OPTIMIZATION_K6_2, JBIGI_OPTIMIZATION_K6,
JBIGI_OPTIMIZATION_X86};
private final static String[] JBIGI_COMPAT_LIST_AMD_APU = {JBIGI_OPTIMIZATION_JAGUAR, JBIGI_OPTIMIZATION_BOBCAT, JBIGI_OPTIMIZATION_ATHLON64};
private final static String[] JBIGI_COMPAT_LIST_AMD_BULLDOZER = {JBIGI_OPTIMIZATION_EXCAVATOR, JBIGI_OPTIMIZATION_STEAMROLLER, JBIGI_OPTIMIZATION_PILEDRIVER,
JBIGI_OPTIMIZATION_BULLDOZER, JBIGI_OPTIMIZATION_ATHLON64, JBIGI_OPTIMIZATION_X86};
private final static String[] JBIGI_COMPAT_LIST_INTEL_ATOM = {JBIGI_OPTIMIZATION_ATOM, JBIGI_OPTIMIZATION_PENTIUM3, JBIGI_OPTIMIZATION_PENTIUM2,
JBIGI_OPTIMIZATION_PENTIUMMMX, JBIGI_OPTIMIZATION_PENTIUM, JBIGI_OPTIMIZATION_X86,
JBIGI_OPTIMIZATION_PENTIUM4};
private final static String[] JBIGI_COMPAT_LIST_INTEL_PENTIUM = {JBIGI_OPTIMIZATION_PENTIUM4, JBIGI_OPTIMIZATION_PENTIUMM, JBIGI_OPTIMIZATION_PENTIUM3,
JBIGI_OPTIMIZATION_PENTIUM2, JBIGI_OPTIMIZATION_PENTIUMMMX, JBIGI_OPTIMIZATION_PENTIUM,
JBIGI_OPTIMIZATION_X86};
private final static String[] JBIGI_COMPAT_LIST_INTEL_CORE = {JBIGI_OPTIMIZATION_COREI_BWL, JBIGI_OPTIMIZATION_COREI_HWL, JBIGI_OPTIMIZATION_COREI_SBR,
JBIGI_OPTIMIZATION_COREI, JBIGI_OPTIMIZATION_CORE2, JBIGI_OPTIMIZATION_PENTIUMM,
JBIGI_OPTIMIZATION_PENTIUM3, JBIGI_OPTIMIZATION_X86};
/**
* The mapping between CPU architecture and its compatibility list.
*/
@SuppressWarnings("serial")
private final static HashMap<String, String[]> JBIGI_COMPAT_MAP = new HashMap<String, String[]>() {{
// none -> {"none"), since 0.9.30
put(JBIGI_OPTIMIZATION_X86, JBIGI_COMPAT_LIST_NONE);
put(JBIGI_OPTIMIZATION_PPC, JBIGI_COMPAT_LIST_PPC);
put(JBIGI_OPTIMIZATION_ARM_ARMV5, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_ARM_ARMV6, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_ARM_ARMV7, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_ARM_CORTEX_A5, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_ARM_CORTEX_A7, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_ARM_CORTEX_A8, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_ARM_CORTEX_A9, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_ARM_CORTEX_A15, JBIGI_COMPAT_LIST_ARM);
put(JBIGI_OPTIMIZATION_VIAC3, JBIGI_COMPAT_LIST_VIA);
put(JBIGI_OPTIMIZATION_VIAC32, JBIGI_COMPAT_LIST_VIA);
put(JBIGI_OPTIMIZATION_NANO, JBIGI_COMPAT_LIST_VIA);
put(JBIGI_OPTIMIZATION_K6, JBIGI_COMPAT_LIST_AMD_ATHLON);
put(JBIGI_OPTIMIZATION_K6_2, JBIGI_COMPAT_LIST_AMD_ATHLON);
put(JBIGI_OPTIMIZATION_K6_3, JBIGI_COMPAT_LIST_AMD_ATHLON);
put(JBIGI_OPTIMIZATION_ATHLON, JBIGI_COMPAT_LIST_AMD_ATHLON);
put(JBIGI_OPTIMIZATION_ATHLON64, JBIGI_COMPAT_LIST_AMD_ATHLON);
put(JBIGI_OPTIMIZATION_K10, JBIGI_COMPAT_LIST_AMD_ATHLON);
put(JBIGI_OPTIMIZATION_GEODE, JBIGI_COMPAT_LIST_AMD_GEODE);
put(JBIGI_OPTIMIZATION_BOBCAT, JBIGI_COMPAT_LIST_AMD_APU);
put(JBIGI_OPTIMIZATION_JAGUAR, JBIGI_COMPAT_LIST_AMD_APU);
put(JBIGI_OPTIMIZATION_BULLDOZER, JBIGI_COMPAT_LIST_AMD_BULLDOZER);
put(JBIGI_OPTIMIZATION_PILEDRIVER, JBIGI_COMPAT_LIST_AMD_BULLDOZER);
put(JBIGI_OPTIMIZATION_STEAMROLLER, JBIGI_COMPAT_LIST_AMD_BULLDOZER);
put(JBIGI_OPTIMIZATION_EXCAVATOR, JBIGI_COMPAT_LIST_AMD_BULLDOZER);
put(JBIGI_OPTIMIZATION_ATOM, JBIGI_COMPAT_LIST_INTEL_ATOM);
put(JBIGI_OPTIMIZATION_PENTIUM, JBIGI_COMPAT_LIST_INTEL_PENTIUM);
put(JBIGI_OPTIMIZATION_PENTIUMMMX, JBIGI_COMPAT_LIST_INTEL_PENTIUM);
put(JBIGI_OPTIMIZATION_PENTIUM2, JBIGI_COMPAT_LIST_INTEL_PENTIUM);
put(JBIGI_OPTIMIZATION_PENTIUM3, JBIGI_COMPAT_LIST_INTEL_PENTIUM);
put(JBIGI_OPTIMIZATION_PENTIUMM, JBIGI_COMPAT_LIST_INTEL_PENTIUM);
put(JBIGI_OPTIMIZATION_PENTIUM4, JBIGI_COMPAT_LIST_INTEL_PENTIUM);
put(JBIGI_OPTIMIZATION_PENTIUM3, JBIGI_COMPAT_LIST_INTEL_CORE);
put(JBIGI_OPTIMIZATION_PENTIUMM, JBIGI_COMPAT_LIST_INTEL_CORE);
put(JBIGI_OPTIMIZATION_CORE2, JBIGI_COMPAT_LIST_INTEL_CORE);
put(JBIGI_OPTIMIZATION_COREI, JBIGI_COMPAT_LIST_INTEL_CORE);
put(JBIGI_OPTIMIZATION_COREI_SBR, JBIGI_COMPAT_LIST_INTEL_CORE);
put(JBIGI_OPTIMIZATION_COREI_HWL, JBIGI_COMPAT_LIST_INTEL_CORE);
put(JBIGI_OPTIMIZATION_COREI_BWL, JBIGI_COMPAT_LIST_INTEL_CORE);
}};
/**
* Operating systems
*/
private static final boolean _isWin = SystemVersion.isWindows();
private static final boolean _isOS2 = System.getProperty("os.name").startsWith("OS/2");
private static final boolean _isMac = SystemVersion.isMac();
private static final boolean _isLinux = System.getProperty("os.name").toLowerCase(Locale.US).contains("linux");
private static final boolean _isKFreebsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("kfreebsd");
private static final boolean _isFreebsd = (!_isKFreebsd) && System.getProperty("os.name").toLowerCase(Locale.US).contains("freebsd");
private static final boolean _isNetbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("netbsd");
private static final boolean _isOpenbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("openbsd");
private static final boolean _isSunos = System.getProperty("os.name").toLowerCase(Locale.US).contains("sunos");
private static final boolean _isAndroid = SystemVersion.isAndroid();
/*
* This isn't always correct.
* http://stackoverflow.com/questions/807263/how-do-i-detect-which-kind-of-jre-is-installed-32bit-vs-64bit
* http://mark.koli.ch/2009/10/javas-osarch-system-property-is-the-bitness-of-the-jre-not-the-operating-system.html
* http://mark.koli.ch/2009/10/reliably-checking-os-bitness-32-or-64-bit-on-windows-with-a-tiny-c-app.html
* sun.arch.data.model not on all JVMs
* sun.arch.data.model == 64 => 64 bit processor
* sun.arch.data.model == 32 => A 32 bit JVM but could be either 32 or 64 bit processor or libs
* os.arch contains "64" could be 32 or 64 bit libs
*/
private static final boolean _is64 = SystemVersion.is64Bit();
private static final boolean _isX86 = SystemVersion.isX86();
private static final boolean _isArm = SystemVersion.isARM();
private static final boolean _isPPC = System.getProperty("os.arch").contains("ppc");
/* libjbigi.so vs jbigi.dll */
private static final String _libPrefix = (_isWin || _isOS2 ? "" : "lib");
private static final String _libSuffix = (_isWin || _isOS2 ? ".dll" : _isMac ? ".jnilib" : ".so");
private final static String sCPUType; //The CPU Type to optimize for (one of the above strings)
static {
sCPUType = resolveCPUType();
loadNative();
}
/**
* Tries to resolve the best type of CPU that we have an optimized jbigi-dll/so for.
* This is for x86 only.
* @return A string containing the CPU-type or null if CPU type is unknown
*/
private static String resolveCPUType() {
if(_isX86) {
try {
//System.out.println("resolveType() x86");
CPUInfo c = CPUID.getInfo();
try {
_cpuModel = c.getCPUModelString();
//System.out.println("CPUModel: " + _cpuModel.toString());
} catch (UnknownCPUException e) {}
if (c instanceof VIACPUInfo) {
VIACPUInfo viacpu = (VIACPUInfo) c;
if (viacpu.IsNanoCompatible())
return JBIGI_OPTIMIZATION_NANO;
return JBIGI_OPTIMIZATION_VIAC3;
} else if (c instanceof AMDCPUInfo) {
AMDCPUInfo amdcpu = (AMDCPUInfo) c;
if (amdcpu.IsExcavatorCompatible())
return JBIGI_OPTIMIZATION_EXCAVATOR;
if (amdcpu.IsSteamrollerCompatible())
return JBIGI_OPTIMIZATION_STEAMROLLER;
if (amdcpu.IsPiledriverCompatible())
return JBIGI_OPTIMIZATION_PILEDRIVER;
if (amdcpu.IsBulldozerCompatible())
return JBIGI_OPTIMIZATION_BULLDOZER;
if (amdcpu.IsJaguarCompatible())
return JBIGI_OPTIMIZATION_JAGUAR;
if (amdcpu.IsBobcatCompatible())
return JBIGI_OPTIMIZATION_BOBCAT;
if (amdcpu.IsK10Compatible())
return JBIGI_OPTIMIZATION_K10;
if (amdcpu.IsAthlon64Compatible())
return JBIGI_OPTIMIZATION_ATHLON64;
if (amdcpu.IsAthlonCompatible())
return JBIGI_OPTIMIZATION_ATHLON;
if (amdcpu.IsGeodeCompatible())
return JBIGI_OPTIMIZATION_GEODE;
if (amdcpu.IsK6_3_Compatible())
return JBIGI_OPTIMIZATION_K6_3;
if (amdcpu.IsK6_2_Compatible())
return JBIGI_OPTIMIZATION_K6_2;
if (amdcpu.IsK6Compatible())
return JBIGI_OPTIMIZATION_K6;
} else if (c instanceof IntelCPUInfo) {
IntelCPUInfo intelcpu = (IntelCPUInfo) c;
if (intelcpu.IsBroadwellCompatible())
return JBIGI_OPTIMIZATION_COREI_BWL;
if (intelcpu.IsHaswellCompatible())
return JBIGI_OPTIMIZATION_COREI_HWL;
if (intelcpu.IsSandyCompatible())
return JBIGI_OPTIMIZATION_COREI_SBR;
if (intelcpu.IsCoreiCompatible())
return JBIGI_OPTIMIZATION_COREI;
if (intelcpu.IsCore2Compatible())
return JBIGI_OPTIMIZATION_CORE2;
// The isAtomCompatible check should be done before the Pentium4
// check since they are compatible, but Atom performs better with
// the JBIGI_OPTIMIZATION_ATOM compability list.
if (intelcpu.IsAtomCompatible())
return JBIGI_OPTIMIZATION_ATOM;
if (intelcpu.IsPentium4Compatible())
return JBIGI_OPTIMIZATION_PENTIUM4;
if (intelcpu.IsPentiumMCompatible())
return JBIGI_OPTIMIZATION_PENTIUMM;
if (intelcpu.IsPentium3Compatible())
return JBIGI_OPTIMIZATION_PENTIUM3;
if (intelcpu.IsPentium2Compatible())
return JBIGI_OPTIMIZATION_PENTIUM2;
if (intelcpu.IsPentiumMMXCompatible())
return JBIGI_OPTIMIZATION_PENTIUMMMX;
if (intelcpu.IsPentiumCompatible())
return JBIGI_OPTIMIZATION_PENTIUM;
}
} catch (UnknownCPUException e) {
}
// always try "none" if we don't know the x86 type,
// in case of CPUID fail or not finding compatibility above
return JBIGI_OPTIMIZATION_X86;
} else if (_isArm) {
if (_isWin)
return null;
Map<String, String> cpuinfo = getCPUInfo();
String implementer = cpuinfo.get("cpu implementer");
String part = cpuinfo.get("cpu part");
// If CPU implementer is ARM
if (implementer != null && part != null && implementer.contains("0x41")) {
if (part.contains("0xc0f")) {
return JBIGI_OPTIMIZATION_ARM_CORTEX_A15;
} else if (part.contains("0xc0e")) {
// Actually A17, but it's derived from A15
// and GMP only support A15
return JBIGI_OPTIMIZATION_ARM_CORTEX_A15;
} else if (part.contains("0xc0d")) {
// Actually A12, but it's derived from A15
// and GMP only supports A15
return JBIGI_OPTIMIZATION_ARM_CORTEX_A15;
} else if (part.contains("0xc09")) {
return JBIGI_OPTIMIZATION_ARM_CORTEX_A9;
} else if (part.contains("0xc08")) {
return JBIGI_OPTIMIZATION_ARM_CORTEX_A8;
} else if (part.contains("0xc07")) {
return JBIGI_OPTIMIZATION_ARM_CORTEX_A7;
} else if (part.contains("0xc05")) {
return JBIGI_OPTIMIZATION_ARM_CORTEX_A5;
}
}
// We couldn't identify the implementer
// Let's try by looking at cpu arch
String arch = cpuinfo.get("cpu architecture");
String model = cpuinfo.get("model name");
if (arch != null) {
//CPU architecture: 5TEJ
//CPU architecture: 7
if (arch.startsWith("7")) {
// Raspberry Pi workaround
// Processor : ARMv6-compatible processor rev 7 (v6l)
// CPU architecture: 7
if (model != null && model.contains("ARMv6"))
return JBIGI_OPTIMIZATION_ARM_ARMV6;
return JBIGI_OPTIMIZATION_ARM_ARMV7;
}
if (arch.startsWith("6"))
return JBIGI_OPTIMIZATION_ARM_ARMV6;
if (arch.startsWith("5"))
return JBIGI_OPTIMIZATION_ARM_ARMV5;
}
// We couldn't identify the architecture
// Let's try by looking at model name
if (model != null) {
if (model.contains("ARMv7"))
return JBIGI_OPTIMIZATION_ARM_ARMV7;
if (model.contains("ARMv6"))
return JBIGI_OPTIMIZATION_ARM_ARMV6;
if (model.contains("ARMv5"))
return JBIGI_OPTIMIZATION_ARM_ARMV5;
}
// If we didn't find a match, return null
return null;
} else if (_isPPC && !_isMac) {
return JBIGI_OPTIMIZATION_PPC;
}
return null;
}
/**
* calculate (base ^ exponent) % modulus.
*
* @param base
* big endian twos complement representation of the base (but it must be positive)
* @param exponent
* big endian twos complement representation of the exponent
* @param modulus
* big endian twos complement representation of the modulus
* @throws ArithmeticException if modulus <= 0 (since libjbigi version 3)
* @return big endian twos complement representation of (base ^ exponent) % modulus
*/
private native static byte[] nativeModPow(byte base[], byte exponent[], byte modulus[]);
/**
* calculate (base ^ exponent) % modulus.
* Constant Time.
*
* @param base
* big endian twos complement representation of the base (but it must be positive)
* @param exponent
* big endian twos complement representation of the exponent
* @param modulus
* big endian twos complement representation of the modulus
* @return big endian twos complement representation of (base ^ exponent) % modulus
* @throws ArithmeticException if modulus <= 0
* @since 0.9.26 and libjbigi version 3
*/
private native static byte[] nativeModPowCT(byte base[], byte exponent[], byte modulus[]);
/**
* @since 0.9.26 and libjbigi version 3
* @throws ArithmeticException
*/
private native static byte[] nativeModInverse(byte base[], byte d[]);
/**
* Only for testing jbigi's negative conversion functions!
* @since 0.9.26
*/
//private native static byte[] nativeNeg(byte d[]);
/**
* Get the jbigi version, only available since jbigi version 3
* Caller must catch Throwable
* @since 0.9.26
*/
private native static int nativeJbigiVersion();
/**
* Get the libmp version, only available since jbigi version 3
* @since 0.9.26
*/
private native static int nativeGMPMajorVersion();
/**
* Get the libmp version, only available since jbigi version 3
* @since 0.9.26
*/
private native static int nativeGMPMinorVersion();
/**
* Get the libmp version, only available since jbigi version 3
* @since 0.9.26
*/
private native static int nativeGMPPatchVersion();
/**
* Get the jbigi version
* @return 0 if no jbigi available, 2 if version not supported
* @since 0.9.26
*/
private static int fetchJbigiVersion() {
if (!_nativeOk)
return 0;
try {
return nativeJbigiVersion();
} catch (Throwable t) {
return 2;
}
}
/**
* Set the jbigi and libgmp versions. Call after loading.
* Sets _jbigiVersion, _nativeOk3, and _libGMPVersion.
* @since 0.9.26
*/
private static void setVersions() {
_jbigiVersion = fetchJbigiVersion();
_nativeOk3 = _jbigiVersion > 2;
if (_nativeOk3) {
try {
int maj = nativeGMPMajorVersion();
int min = nativeGMPMinorVersion();
int pat = nativeGMPPatchVersion();
_libGMPVersion = maj + "." + min + "." + pat;
_nativeCTOk = maj >= 5;
} catch (Throwable t) {
warn("jbigi version " + _jbigiVersion + " but GMP version not available???", t);
}
}
// Don't overwrite _loadStatus
//warn("jbigi version: " + _jbigiVersion + "; GMP version: " + _libGMPVersion);
}
/**
* Get the jbigi version
* @return 0 if no jbigi available, 2 if version info not supported
* @since 0.9.26
*/
public static int getJbigiVersion() {
return _jbigiVersion;
}
/**
* Get the libgmp version
* @return "unknown" if no jbigi available or if version not supported
* @since 0.9.26
*/
public static String getLibGMPVersion() {
return _libGMPVersion;
}
private byte[] cachedBa;
public NativeBigInteger(byte[] val) {
super(val);
}
public NativeBigInteger(int signum, byte[] magnitude) {
super(signum, magnitude);
}
public NativeBigInteger(int bitlen, int certainty, Random rnd) {
super(bitlen, certainty, rnd);
}
public NativeBigInteger(int numbits, Random rnd) {
super(numbits, rnd);
}
public NativeBigInteger(String val) {
super(val);
}
public NativeBigInteger(String val, int radix) {
super(val, radix);
}
/**Creates a new NativeBigInteger with the same value
* as the supplied BigInteger. Warning!, not very efficent
*/
public NativeBigInteger(BigInteger integer) {
//Now, why doesn't sun provide a constructor
//like this one in BigInteger?
this(integer.toByteArray());
}
/**
* @param m must be postive
* @param exponent must be postive
* @throws ArithmeticException if m <= 0 or exponent <=0
*/
@Override
public BigInteger modPow(BigInteger exponent, BigInteger m) {
// Where negative or zero values aren't legal in modPow() anyway, avoid native,
// as the Java code will throw an exception rather than silently fail or crash the JVM
// Negative values supported as of version 3
if (_nativeOk3 || (_nativeOk && signum() >= 0 && exponent.signum() >= 0 && m.signum() > 0))
return new NativeBigInteger(nativeModPow(toByteArray(), exponent.toByteArray(), m.toByteArray()));
else
return super.modPow(exponent, m);
}
/**
* @param exponent must be postive
* @param m must be postive and odd
* @throws ArithmeticException if m <= 0 or m is even or exponent <=0
* @since 0.9.26 and libjbigi version 3 and GMP version 5
*/
public BigInteger modPowCT(BigInteger exponent, BigInteger m) {
if (_nativeCTOk)
return new NativeBigInteger(nativeModPowCT(toByteArray(), exponent.toByteArray(), m.toByteArray()));
else
return modPow(exponent, m);
}
/**
* @throws ArithmeticException if not coprime with m, or m <= 0
* @since 0.9.26 and libjbigi version 3
*/
@Override
public BigInteger modInverse(BigInteger m) {
// Where negative or zero values aren't legal in modInverse() anyway, avoid native,
// as the Java code will throw an exception rather than silently fail or crash the JVM
// Note that 'this' can be negative
// If this and m are not coprime, gmp will do a divide by zero exception and crash the JVM.
// super will throw an ArithmeticException
if (_nativeOk3)
return new NativeBigInteger(nativeModInverse(toByteArray(), m.toByteArray()));
else
return super.modInverse(m);
}
/** caches */
@Override
public byte[] toByteArray(){
if(cachedBa == null) //Since we are immutable it is safe to never update the cached ba after it has initially been generated
cachedBa = super.toByteArray();
return cachedBa;
}
/**
*
* @return True iff native methods will be used by this class
*/
public static boolean isNative(){
return _nativeOk;
}
/**
* @return A string suitable for display to the user
*/
public static String loadStatus() {
return _loadStatus;
}
/**
* The name of the library loaded, if known.
* Null if unknown or not loaded.
* Currently non-null only if extracted from jbigi.jar.
*
* @since 0.9.17
*/
public static String getLoadedResourceName() {
return _extractedResource;
}
public static String cpuType() {
if (sCPUType != null)
return sCPUType;
return "unrecognized";
}
public static String cpuModel() {
return _cpuModel;
}
/**
* <p>Compare the BigInteger.modPow vs the NativeBigInteger.modPow of some
* really big (2Kbit) numbers 100 different times and benchmark the
* performance.</p>
*
* @param args -n Disable native test
*
*/
public static void main(String args[]) {
_doLog = true;
String path = System.getProperty("java.library.path");
String name = _libPrefix + "jbigi" + _libSuffix;
System.out.println("Native library search path: " + path);
if (_nativeOk) {
String sep = System.getProperty("path.separator");
String[] paths = DataHelper.split(path, sep);
for (String p : paths) {
File f = new File(p, name);
if (f.exists()) {
System.out.println("Found native library: " + f);
break;
}
}
} else {
System.out.println("Failed to load native library. Please verify the existence of the " +
name + " file in the library path, or set -Djava.library.path=. in the command line");
}
boolean nativeOnly = args.length > 0 && args[0].equals("-n");
if (nativeOnly && !_nativeOk) {
System.exit(1);
}
if (_nativeOk) {
System.out.println("JBigi Version: " + _jbigiVersion + " GMP Version: " + _libGMPVersion);
if (_extractedResource != null)
System.out.println("Using native resource: " + _extractedResource);
}
System.out.println("DEBUG: Warming up the random number generator...");
SecureRandom rand = RandomSource.getInstance();
rand.nextBoolean();
System.out.println("DEBUG: Random number generator warmed up");
//if (_nativeOk3)
// testnegs();
runModPowTest(100, 1, nativeOnly);
if (_nativeOk3) {
System.out.println("ModPowCT test:");
runModPowTest(100, 2, nativeOnly);
System.out.println("ModInverse test:");
runModPowTest(10000, 3, nativeOnly);
}
}
/** version >= 3 only */
/****
private static void testnegs() {
for (int i = -66000; i <= 66000; i++) {
testneg(i);
}
test(3, 11);
test(25, 4);
}
private static void testneg(long a) {
NativeBigInteger ba = new NativeBigInteger(Long.toString(a));
long r = ba.testNegate().longValue();
if (r != 0 - a)
warn("FAIL Neg test " + a + " = " + r);
}
private static void test(long a, long b) {
BigInteger ba = new NativeBigInteger(Long.toString(a));
BigInteger bb = new NativeBigInteger(Long.toString(b));
long r1 = a * b;
long r2 = ba.multiply(bb).longValue();
if (r1 != r2)
warn("FAIL Mul test " + a + ' ' + b + " = " + r2);
r1 = a / b;
r2 = ba.divide(bb).longValue();
if (r1 != r2)
warn("FAIL Div test " + a + ' ' + b + " = " + r2);
r1 = a % b;
r2 = ba.mod(bb).longValue();
if (r1 != r2)
warn("FAIL Mod test " + a + ' ' + b + " = " + r2);
}
private BigInteger testNegate() {
return new NativeBigInteger(nativeNeg(toByteArray()));
}
****/
/**
* @param mode 1: modPow; 2: modPowCT 3: modInverse
*/
private static void runModPowTest(int numRuns, int mode, boolean nativeOnly) {
SecureRandom rand = RandomSource.getInstance();
/* the sample numbers are elG generator/prime so we can test with reasonable numbers */
byte[] sampleGenerator = CryptoConstants.elgg.toByteArray();
byte[] samplePrime = CryptoConstants.elgp.toByteArray();
BigInteger jg = new BigInteger(sampleGenerator);
NativeBigInteger ng = CryptoConstants.elgg;
BigInteger jp = new BigInteger(samplePrime);
long totalTime = 0;
long javaTime = 0;
int runsProcessed = 0;
for (int i = 0; i < 1000; i++) {
// JIT warmup
BigInteger bi;
do {
bi = new BigInteger(16, rand);
} while (bi.signum() == 0);
if (mode == 1)
jg.modPow(bi, jp);
else if (mode == 2)
ng.modPowCT(bi, jp);
else
bi.modInverse(jp);
}
BigInteger myValue = null, jval;
final NativeBigInteger g = CryptoConstants.elgg;
final NativeBigInteger p = CryptoConstants.elgp;
// Our ElG prime P is 1061 bits, so make K smaller so there's
// no chance of it being equal to or a multiple of P, i.e. not coprime,
// so the modInverse test won't fail
final int numBits = (mode == 3) ? 1060 : 2048;
for (runsProcessed = 0; runsProcessed < numRuns; runsProcessed++) {
// 0 is not coprime with anything
BigInteger bi;
do {
bi = new BigInteger(numBits, rand);
} while (bi.signum() == 0);
NativeBigInteger k = new NativeBigInteger(1, bi.toByteArray());
//// Native
long beforeModPow = System.nanoTime();
if (_nativeOk) {
if (mode == 1)
myValue = g.modPow(k, p);
else if (mode == 2)
myValue = g.modPowCT(bi, jp);
else
myValue = k.modInverse(p);
}
long afterModPow = System.nanoTime();
totalTime += (afterModPow - beforeModPow);
//// Java
if (!nativeOnly) {
if (mode != 3)
jval = jg.modPow(bi, jp);
else
jval = bi.modInverse(jp);
long afterJavaModPow = System.nanoTime();
javaTime += (afterJavaModPow - afterModPow);
if (_nativeOk && !myValue.equals(jval)) {
System.err.println("ERROR: [" + runsProcessed + "]\tnative modPow != java modPow");
System.err.println("ERROR: native modPow value: " + myValue.toString());
System.err.println("ERROR: java modPow value: " + jval.toString());
break;
//} else if (mode == 1) {
// System.out.println(String.format("DEBUG: current run time: %7.3f ms (total: %9.3f ms, %7.3f ms each)",
// (afterModPow - beforeModPow) / 1000000d,
// totalTime / 1000000d,
// totalTime / (1000000d * (runsProcessed + 1))));
}
}
}
double dtotal = totalTime / 1000000f;
double djava = javaTime / 1000000f;
if (_nativeOk)
System.out.println(String.format("INFO: run time: %.3f ms (%.3f ms each)",
dtotal, dtotal / (runsProcessed + 1)));
if (numRuns == runsProcessed)
System.out.println("INFO: " + runsProcessed + " runs complete without any errors");
else
System.out.println("ERROR: " + runsProcessed + " runs until we got an error");
if (_nativeOk) {
System.out.println(String.format("Native run time: \t%9.3f ms (%7.3f ms each)",
dtotal, dtotal / (runsProcessed + 1)));
if (!nativeOnly) {
System.out.println(String.format("Java run time: \t%9.3f ms (%7.3f ms each)",
djava, djava / (runsProcessed + 1)));
System.out.println(String.format("Native = %.3f%% of pure Java time",
dtotal * 100.0d / djava));
if (dtotal < djava)
System.out.println(String.format("Native is BETTER by a factor of %.3f -- YAY!", djava / dtotal));
else
System.out.println(String.format("Native is WORSE by a factor of %.3f -- BOO!", dtotal / djava));
}
} else {
System.out.println(String.format("java run time: \t%.3f ms (%.3f ms each)",
djava, djava / (runsProcessed + 1)));
System.out.println("However, we couldn't load the native library, so this doesn't test much");
}
}
/**
* <p>Do whatever we can to load up the native library backing this BigInteger's native methods.
* If it can find a custom built jbigi.dll / libjbigi.so, it'll use that. Otherwise
* it'll try to look in the classpath for the correct library (see loadFromResource).
* If the user specifies -Djbigi.enable=false it'll skip all of this.</p>
*
* <pre>
* Load order (using linux naming with cpu type "xxx")
* Old order 0.8.6 and earlier:
* - filesystem libjbigi.so
* - jbigi.jar libjbigi.so
* - jbigi.jar libjbigi-linux-xxx.so
* - filesystem libjbigi-linux-xxx.so
* - jbigi.jar libjbigi-linux-none.so
* - filesystem libjbigi-linux-none.so
*
* New order as of 0.8.7:
* - filesystem libjbigi.so
* - jbigi.jar libjbigi-linux-xxx_64.so if it may be 64 bit
* - jbigi.jar libjbigi-linux-athlon64_64.so if it may be 64 bit
* - jbigi.jar libjbigi-linux-xxx.so
* - jbigi.jar libjbigi-linux-athlon64.so if it may be 64 bit
* - jbigi.jar libjbigi-linux-yyy.so 0 or more other alternates
* - jbigi.jar libjbigi-linux-none_64.so if it may be 64 bit
* - jbigi.jar libjbigi-linux-none.so
* </pre>
*/
private static final void loadNative() {
try{
String wantedProp = System.getProperty("jbigi.enable", "true");
boolean wantNative = Boolean.parseBoolean(wantedProp);
if (wantNative) {
debug("trying loadGeneric");
boolean loaded = loadGeneric("jbigi");
if (loaded) {
_nativeOk = true;
String s = I2PAppContext.getGlobalContext().getProperty("jbigi.loadedResource");
if (s != null)
info("Locally optimized library " + s + " loaded from file");
else
info("Locally optimized native BigInteger library loaded from file");
} else {
List<String> toTry = getResourceList();
debug("loadResource list to try is: " + toTry);
for (String s : toTry) {
debug("Trying to load resource " + s);
if (loadFromResource(s)) {
_nativeOk = true;
_extractedResource = s;
info("Native BigInteger library " + s + " loaded from resource");
break;
}
}
}
}
if (!_nativeOk) {
warn("Native BigInteger library jbigi not loaded - using pure Java - " +
"poor performance may result - see http://i2p-projekt.i2p/jbigi for help");
} else {
setVersions();
}
} catch(Exception e) {
warn("Native BigInteger library jbigi not loaded, using pure java", e);
}
}
/** @since 0.8.7 */
private static void debug(String s) {
I2PAppContext.getGlobalContext().logManager().getLog(NativeBigInteger.class).debug(s);
}
private static void info(String s) {
if(_doLog)
System.err.println("INFO: " + s);
I2PAppContext.getGlobalContext().logManager().getLog(NativeBigInteger.class).info(s);
_loadStatus = s;
}
private static void warn(String s) {
warn(s, null);
}
/** @since 0.8.7 */
private static void warn(String s, Throwable t) {
if(_doLog) {
System.err.println("WARNING: " + s);
if (t != null)
t.printStackTrace();
}
I2PAppContext.getGlobalContext().logManager().getLog(NativeBigInteger.class).warn(s, t);
if (t != null)
_loadStatus = s + ' ' + t;
else
_loadStatus = s;
}
/** @since 0.9.26 */
private static void error(String s) {
error(s, null);
}
/** @since 0.9.26 */
private static void error(String s, Throwable t) {
if(_doLog) {
System.err.println("ERROR: " + s);
if (t != null)
t.printStackTrace();
}
I2PAppContext.getGlobalContext().logManager().getLog(NativeBigInteger.class).error(s, t);
if (t != null)
_loadStatus = s + ' ' + t;
else
_loadStatus = s;
}
/**
* <p>Try loading it from an explictly build jbigi.dll / libjbigi.so first, before
* looking into a jbigi.jar for any other libraries.</p>
*
* @return true if it was loaded successfully, else false
*
*/
/****
private static final boolean loadGeneric(boolean optimized) {
return loadGeneric(getMiddleName(optimized));
}
****/
private static final boolean loadGeneric(String name) {
try {
if(name == null)
return false;
System.loadLibrary(name);
return true;
} catch (UnsatisfiedLinkError ule) {
if (_isAndroid) {
// Unfortunately,
// this is not interesting on Android, it says "file not found"
// on link errors too.
warn("jbigi loadLibrary() fail", ule);
}
return false;
}
}
/**
* <p>Check all of the jars in the classpath for the file specified by the
* environmental property "jbigi.impl" and load it as the native library
* implementation. For instance, a windows user on a p4 would define
* -Djbigi.impl=win-686 if there is a jbigi.jar in the classpath containing the
* files "win-686", "win-athlon", "freebsd-p4", "linux-p3", where each
* of those files contain the correct binary file for a native library (e.g.
* windows DLL, or a *nix .so). </p>
*
* <p>This is a pretty ugly hack, using the general technique illustrated by the
* onion FEC libraries. It works by pulling the resource, writing out the
* byte stream to a temporary file, loading the native library from that file.
* We then attempt to copy the file from the temporary dir to the base install dir,
* so we don't have to do this next time - but we don't complain if it fails,
* so we transparently support read-only base dirs.
* </p>
*
* @return true if it was loaded successfully, else false
*
*/
/****
private static final boolean loadFromResource(boolean optimized) {
String resourceName = getResourceName(optimized);
return loadFromResource(resourceName);
}
****/
private static final boolean loadFromResource(String resourceName) {
if (resourceName == null) return false;
//URL resource = NativeBigInteger.class.getClassLoader().getResource(resourceName);
URL resource = ClassLoader.getSystemResource(resourceName);
if (resource == null) {
info("Resource name [" + resourceName + "] was not found");
return false;
}
File outFile = null;
FileOutputStream fos = null;
String filename = _libPrefix + "jbigi" + _libSuffix;
try {
InputStream libStream = resource.openStream();
outFile = new File(I2PAppContext.getGlobalContext().getTempDir(), filename);
fos = new FileOutputStream(outFile);
DataHelper.copy(libStream, fos);
fos.close();
fos = null;
System.load(outFile.getAbsolutePath()); //System.load requires an absolute path to the lib
info("Loaded library: " + resource);
} catch (UnsatisfiedLinkError ule) {
// don't include the exception in the message - too much
warn("Failed to load the resource " + resourceName + " - not a valid library for this platform");
if (outFile != null)
outFile.delete();
return false;
} catch (IOException ioe) {
warn("Problem writing out the temporary native library data: " + ioe);
if (outFile != null)
outFile.delete();
return false;
} finally {
if (fos != null) {
try { fos.close(); } catch (IOException ioe) {}
}
}
// copy to install dir, ignore failure
File newFile = new File(I2PAppContext.getGlobalContext().getBaseDir(), filename);
FileUtil.copy(outFile, newFile, false, true);
return true;
}
/**
* Generate a list of resources to search for, in-order.
* See loadNative() comments for more info.
* @return non-null
* @since 0.8.7
*/
private static List<String> getResourceList() {
if (_isAndroid)
return Collections.emptyList();
List<String> rv = new ArrayList<String>(20);
String primary = getMiddleName2(true);
// primary may be null
String[] compatList = JBIGI_COMPAT_MAP.get(primary);
if (primary != null && compatList == null) {
error("A bug relating to how jbigi is loaded for \"" + primary + "\" has been spotted");
}
if (primary != null &&
compatList != null) {
// Add all architectural parents of this arch to the resource list
// Skip architectures that are newer than our target
int i = 0;
for (; i < compatList.length; ++i) {
if (compatList[i].equals(primary)) {
break;
}
}
for (; i < compatList.length; ++i) {
String middle = getMiddleName1();
if (_is64) {
rv.add(_libPrefix + middle + compatList[i] + "_64" + _libSuffix);
}
rv.add(_libPrefix + middle + compatList[i] + _libSuffix);
}
if (rv.isEmpty()) {
error("Couldn't find the arch \"" + primary + "\" in its compatibility map \"" +
primary + ": " + Arrays.toString(compatList) + "\"");
}
}
//System.out.println("Primary: " + primary);
//System.out.println("ResourceList: " + rv.toString());
return rv;
}
/**
* Return /proc/cpuinfo as a key-value mapping.
* All keys mapped to lower case.
* All keys and values trimmed.
* For dup keys, first one wins.
* Currently used for ARM only.
* @return non-null, empty on failure
* @since 0.9.1
*/
private static Map<String, String> getCPUInfo() {
Map<String, String> rv = new HashMap<String, String>(32);
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/cpuinfo"), "ISO-8859-1"), 4096);
String line = null;
while ( (line = in.readLine()) != null) {
String[] parts = DataHelper.split(line, ":", 2);
if (parts.length < 2)
continue;
String key = parts[0].trim().toLowerCase(Locale.US);
if (!rv.containsKey(key))
rv.put(key, parts[1].trim());
}
} catch (IOException ioe) {
warn("Unable to read /proc/cpuinfo", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
return rv;
}
/**
* @return may be null if optimized is true
*/
/****
private static final String getResourceName(boolean optimized) {
String middle = getMiddleName(optimized);
if (middle == null)
return null;
return _libPrefix + middle + _libSuffix;
}
****/
/**
* @return may be null if optimized is true; returns jbigi-xxx-none if optimize is false
*/
/****
private static final String getMiddleName(boolean optimized) {
String m2 = getMiddleName2(optimized);
if (m2 == null)
return null;
return getMiddleName1() + m2;
}
****/
/**
* @return may be null if optimized is true; returns "none" if optimize is false
* @since 0.8.7
*/
private static final String getMiddleName2(boolean optimized) {
String sAppend;
if (optimized) {
if (sCPUType == null)
return null;
// Add exceptions here if library files are identical,
// instead of adding duplicates to jbigi.jar
if (sCPUType.equals(JBIGI_OPTIMIZATION_K6_3) && !_isWin)
// k62 and k63 identical except on windows
sAppend = JBIGI_OPTIMIZATION_K6_2;
// core2 is always a fallback for corei in getResourceList()
//else if (sCPUType.equals(JBIGI_OPTIMIZATION_COREI) && (!_is64) && ((_isKFreebsd) || (_isNetbsd) || (_isOpenbsd)))
// corei and core2 are identical on 32bit kfreebsd, openbsd, and netbsd
//sAppend = JBIGI_OPTIMIZATION_CORE2;
else if (sCPUType.equals(JBIGI_OPTIMIZATION_PENTIUM2) && _isSunos && _isX86)
// pentium2 and pentium3 identical on X86 Solaris
sAppend = JBIGI_OPTIMIZATION_PENTIUM3;
else if (sCPUType.equals(JBIGI_OPTIMIZATION_VIAC32))
// viac32 and pentium3 identical
sAppend = JBIGI_OPTIMIZATION_PENTIUM3;
//else if (sCPUType.equals(JBIGI_OPTIMIZATION_VIAC3) && _isWin)
// FIXME no viac3 available for windows, what to use instead?
else
sAppend = sCPUType;
} else {
sAppend = "none";
}
return sAppend;
}
/**
* @return "jbigi-xxx-"
* @since 0.8.7
*/
private static final String getMiddleName1() {
if(_isWin)
return "jbigi-windows-";
if(_isKFreebsd)
return "jbigi-kfreebsd-";
if(_isFreebsd)
return "jbigi-freebsd-";
if(_isNetbsd)
return "jbigi-netbsd-";
if(_isOpenbsd)
return "jbigi-openbsd-";
if(_isMac)
return "jbigi-osx-";
if(_isOS2)
return "jbigi-os2-";
if(_isSunos)
return "jbigi-solaris-";
//throw new RuntimeException("Dont know jbigi library name for os type '"+System.getProperty("os.name")+"'");
// use linux as the default, don't throw exception
return "jbigi-linux-";
}
@Override
public boolean equals(Object o) {
// for findbugs
return super.equals(o);
}
@Override
public int hashCode() {
// for findbugs
return super.hashCode();
}
}