forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head eb2151f9d804ec432bfe97214896ee62da08943e)
to branch 'i2p.i2p.zzz.test2' (head 2ea50c5f22fe1e24d37dff2f283b77feaa9190ee)
This commit is contained in:
@@ -39,12 +39,33 @@ public interface AMDCPUInfo extends CPUInfo {
|
||||
* @return true if the CPU present in the machine is at least an 'k8' CPU (Atlhon 64, Opteron etc. and better)
|
||||
*/
|
||||
public boolean IsAthlon64Compatible();
|
||||
/**
|
||||
* @return true if the CPU present in the machine is at least an 'k10' CPU
|
||||
*/
|
||||
public boolean IsK10Compatible();
|
||||
/**
|
||||
* @return true if the CPU present in the machine is at least an 'k8' CPU (Atlhon 64, Opteron etc. and better)
|
||||
* @return true if the CPU present in the machine is at least an 'bobcat' CPU
|
||||
*/
|
||||
public boolean IsBobcatCompatible();
|
||||
/**
|
||||
* @return true if the CPU present in the machine is at least an 'jaguar' CPU
|
||||
*/
|
||||
public boolean IsJaguarCompatible();
|
||||
/**
|
||||
* @return true if the CPU present in the machine is at least a 'bulldozer' CPU
|
||||
*/
|
||||
public boolean IsBulldozerCompatible();
|
||||
/**
|
||||
* @return true if the CPU present in the machine is at least a 'piledriver' CPU
|
||||
*/
|
||||
public boolean IsPiledriverCompatible();
|
||||
/**
|
||||
* @return true if the CPU present in the machine is at least a 'steamroller' CPU
|
||||
*/
|
||||
public boolean IsSteamrollerCompatible();
|
||||
/**
|
||||
* @return true if the CPU present in the machine is at least a 'excavator' CPU
|
||||
*/
|
||||
public boolean IsExcavatorCompatible();
|
||||
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
private static boolean isGeodeCompatible;
|
||||
private static boolean isAthlonCompatible;
|
||||
private static boolean isAthlon64Compatible;
|
||||
private static boolean isK10Compatible;
|
||||
private static boolean isBobcatCompatible;
|
||||
private static boolean isJaguarCompatible;
|
||||
private static boolean isBulldozerCompatible;
|
||||
private static boolean isPiledriverCompatible;
|
||||
private static boolean isSteamrollerCompatible;
|
||||
private static boolean isExcavatorCompatible;
|
||||
|
||||
// If modelString != null, the cpu is considered correctly identified.
|
||||
private static final String smodel = identifyCPU();
|
||||
|
||||
public boolean IsK6Compatible(){ return isK6Compatible; }
|
||||
|
||||
@@ -33,22 +36,34 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
|
||||
public boolean IsAthlon64Compatible(){ return isAthlon64Compatible; }
|
||||
|
||||
public boolean IsK10Compatible(){ return isK10Compatible; }
|
||||
|
||||
public boolean IsBobcatCompatible(){ return isBobcatCompatible; }
|
||||
|
||||
public boolean IsJaguarCompatible(){ return isJaguarCompatible; }
|
||||
|
||||
public boolean IsBulldozerCompatible(){ return isBulldozerCompatible; }
|
||||
|
||||
public boolean IsPiledriverCompatible(){ return isPiledriverCompatible; }
|
||||
|
||||
public boolean IsSteamrollerCompatible(){ return isSteamrollerCompatible; }
|
||||
|
||||
public boolean IsExcavatorCompatible(){ return isExcavatorCompatible; }
|
||||
|
||||
|
||||
public String getCPUModelString() throws UnknownCPUException
|
||||
{
|
||||
String smodel = identifyCPU();
|
||||
if (smodel != null)
|
||||
return smodel;
|
||||
throw new UnknownCPUException("Unknown AMD CPU; Family="+CPUID.getCPUFamily() + '/' + CPUID.getCPUExtendedFamily()+
|
||||
", Model="+CPUID.getCPUModel() + '/' + CPUID.getCPUExtendedModel());
|
||||
}
|
||||
|
||||
|
||||
private static String identifyCPU()
|
||||
private String identifyCPU()
|
||||
{
|
||||
// http://en.wikipedia.org/wiki/Cpuid
|
||||
// #include "llvm/Support/Host.h", http://llvm.org/docs/doxygen/html/Host_8cpp_source.html
|
||||
String modelString = null;
|
||||
int family = CPUID.getCPUFamily();
|
||||
int model = CPUID.getCPUModel();
|
||||
@@ -181,7 +196,6 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
isK6_3_Compatible = true;
|
||||
isAthlonCompatible = true;
|
||||
isAthlon64Compatible = true;
|
||||
isX64 = true;
|
||||
switch (model) {
|
||||
case 4:
|
||||
modelString = "Athlon 64/Mobile XP-M";
|
||||
@@ -313,7 +327,7 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
isK6_3_Compatible = true;
|
||||
isAthlonCompatible = true;
|
||||
isAthlon64Compatible = true;
|
||||
isX64 = true;
|
||||
isK10Compatible = true;
|
||||
switch (model) {
|
||||
case 2:
|
||||
modelString = "Phenom / Athlon / Opteron Gen 3 (Barcelona/Agena/Toliman/Kuma, 65 nm)";
|
||||
@@ -350,7 +364,6 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
isK6_3_Compatible = true;
|
||||
isAthlonCompatible = true;
|
||||
isAthlon64Compatible = true;
|
||||
isX64 = true;
|
||||
switch (model) {
|
||||
case 3:
|
||||
modelString = "AMD Turion X2/Athlon X2/Sempron (Lion/Sable, 65 nm)";
|
||||
@@ -373,7 +386,6 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
isK6_3_Compatible = true;
|
||||
isAthlonCompatible = true;
|
||||
isAthlon64Compatible = true;
|
||||
isX64 = true;
|
||||
modelString = "AMD APU model " + model;
|
||||
}
|
||||
break;
|
||||
@@ -386,7 +398,6 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
isAthlonCompatible = true;
|
||||
isAthlon64Compatible = true;
|
||||
isBobcatCompatible = true;
|
||||
isX64 = true;
|
||||
switch (model) {
|
||||
case 1:
|
||||
// Case 3 is uncertain but most likely a Bobcat APU
|
||||
@@ -407,26 +418,26 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
isK6_3_Compatible = true;
|
||||
isAthlonCompatible = true;
|
||||
isAthlon64Compatible = true;
|
||||
isBobcatCompatible = true;
|
||||
isBulldozerCompatible = true;
|
||||
isX64 = true;
|
||||
switch (model) {
|
||||
// 32 nm
|
||||
case 1:
|
||||
modelString = "Bulldozer FX-6100/8100";
|
||||
break;
|
||||
// 32 nm
|
||||
case 2:
|
||||
modelString = "Bulldozer FX-6300/8300";
|
||||
break;
|
||||
// 28 nm ?
|
||||
case 3:
|
||||
modelString = "Bulldozer FX-6500/8500";
|
||||
break;
|
||||
default:
|
||||
modelString = "AMD Bulldozer model " + model;
|
||||
break;
|
||||
if (!this.hasAVX()) {
|
||||
modelString = "Bulldozer";
|
||||
break;
|
||||
}
|
||||
if (model >= 0x50 && model <= 0x5F) {
|
||||
isPiledriverCompatible = true;
|
||||
isSteamrollerCompatible = true;
|
||||
isExcavatorCompatible = true;
|
||||
modelString = "Excavator";
|
||||
} else if (model >= 0x30 && model <= 0x3F) {
|
||||
isPiledriverCompatible = true;
|
||||
isSteamrollerCompatible = true;
|
||||
modelString = "Steamroller";
|
||||
} else if ((model >= 0x10 && model <= 0x1F) || hasTBM()) {
|
||||
isPiledriverCompatible = true;
|
||||
modelString = "Piledriver";
|
||||
} else {
|
||||
modelString = "Bulldozer";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -438,24 +449,11 @@ class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
|
||||
isAthlonCompatible = true;
|
||||
isAthlon64Compatible = true;
|
||||
isBobcatCompatible = true;
|
||||
isX64 = true;
|
||||
switch (model) {
|
||||
case 0:
|
||||
modelString = "Athlon 5350 APU";
|
||||
break;
|
||||
default:
|
||||
modelString = "AMD Jaguar APU model " + model;
|
||||
break;
|
||||
}
|
||||
isJaguarCompatible = true;
|
||||
modelString = "Jaguar";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return modelString;
|
||||
}
|
||||
|
||||
public boolean hasX64()
|
||||
{
|
||||
return isX64;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public class CPUID {
|
||||
|
||||
/** did we load the native lib correctly? */
|
||||
private static boolean _nativeOk = false;
|
||||
private static int _jcpuidVersion;
|
||||
|
||||
/**
|
||||
* do we want to dump some basic success/failure info to stderr during
|
||||
@@ -105,6 +106,36 @@ public class CPUID {
|
||||
*/
|
||||
private static native CPUIDResult doCPUID(int iFunction);
|
||||
|
||||
/**
|
||||
* Get the jbigi version, only available since jbigi version 3
|
||||
* Caller must catch Throwable
|
||||
* @since 0.9.26
|
||||
*/
|
||||
private native static int nativeJcpuidVersion();
|
||||
|
||||
/**
|
||||
* Get the jcpuid version
|
||||
* @return 0 if no jcpuid available, 2 if version not supported
|
||||
* @since 0.9.26
|
||||
*/
|
||||
private static int fetchJcpuidVersion() {
|
||||
if (!_nativeOk)
|
||||
return 0;
|
||||
try {
|
||||
return nativeJcpuidVersion();
|
||||
} catch (Throwable t) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the jcpuid version
|
||||
* @return 0 if no jcpuid available, 2 if version not supported
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public static int getJcpuidVersion() {
|
||||
return _jcpuidVersion;
|
||||
}
|
||||
|
||||
static String getCPUVendorID()
|
||||
{
|
||||
@@ -190,12 +221,6 @@ public class CPUID {
|
||||
return c.ECX;
|
||||
}
|
||||
|
||||
static int getExtendedEBXCPUFlags()
|
||||
{
|
||||
CPUIDResult c = doCPUID(0x80000001);
|
||||
return c.EBX;
|
||||
}
|
||||
|
||||
static int getExtendedECXCPUFlags()
|
||||
{
|
||||
CPUIDResult c = doCPUID(0x80000001);
|
||||
@@ -209,6 +234,31 @@ public class CPUID {
|
||||
return c.EDX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.26
|
||||
*/
|
||||
static int getExtendedEBXFeatureFlags()
|
||||
{
|
||||
// Supposed to set ECX to 0 before calling?
|
||||
// But we don't have support for that in jcpuid.
|
||||
// And it works just fine without that.
|
||||
CPUIDResult c = doCPUID(7);
|
||||
return c.EBX;
|
||||
}
|
||||
|
||||
/**
|
||||
* There's almost nothing in here.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
static int getExtendedECXFeatureFlags()
|
||||
{
|
||||
// Supposed to set ECX to 0 before calling?
|
||||
// But we don't have support for that in jcpuid.
|
||||
// And it works just fine without that.
|
||||
CPUIDResult c = doCPUID(7);
|
||||
return c.ECX;
|
||||
}
|
||||
|
||||
/**
|
||||
* The model name string, up to 48 characters, as reported by
|
||||
* the processor itself.
|
||||
@@ -255,15 +305,16 @@ public class CPUID {
|
||||
{
|
||||
if(!_nativeOk)
|
||||
throw new UnknownCPUException("Failed to read CPU information from the system. Please verify the existence of the jcpuid dll/so.");
|
||||
if(getCPUVendorID().equals("CentaurHauls"))
|
||||
String id = getCPUVendorID();
|
||||
if(id.equals("CentaurHauls"))
|
||||
return new VIAInfoImpl();
|
||||
if(!isX86)
|
||||
throw new UnknownCPUException("Failed to read CPU information from the system. The CPUID instruction exists on x86 CPU's only");
|
||||
if(getCPUVendorID().equals("AuthenticAMD"))
|
||||
if(id.equals("AuthenticAMD"))
|
||||
return new AMDInfoImpl();
|
||||
if(getCPUVendorID().equals("GenuineIntel"))
|
||||
if(id.equals("GenuineIntel"))
|
||||
return new IntelInfoImpl();
|
||||
throw new UnknownCPUException("Unknown CPU type: '"+getCPUVendorID()+"'");
|
||||
throw new UnknownCPUException("Unknown CPU type: '" + id + '\'');
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +324,7 @@ public class CPUID {
|
||||
if(!_nativeOk){
|
||||
System.out.println("**Failed to retrieve CPUInfo. Please verify the existence of jcpuid dll/so**");
|
||||
}
|
||||
System.out.println("JCPUID Version: " + _jcpuidVersion);
|
||||
System.out.println(" **CPUInfo**");
|
||||
String mname = getCPUModelName();
|
||||
if (mname != null)
|
||||
@@ -294,19 +346,34 @@ public class CPUID {
|
||||
System.out.println("CPU Family: " + family);
|
||||
System.out.println("CPU Model: " + model);
|
||||
System.out.println("CPU Stepping: " + getCPUStepping());
|
||||
System.out.println("CPU Flags: 0x" + Integer.toHexString(getEDXCPUFlags()));
|
||||
System.out.println("CPU Flags (EDX): 0x" + Integer.toHexString(getEDXCPUFlags()));
|
||||
System.out.println("CPU Flags (ECX): 0x" + Integer.toHexString(getECXCPUFlags()));
|
||||
System.out.println("CPU Ext. Info. (EDX): 0x" + Integer.toHexString(getExtendedEDXCPUFlags()));
|
||||
System.out.println("CPU Ext. Info. (ECX): 0x" + Integer.toHexString(getExtendedECXCPUFlags()));
|
||||
System.out.println("CPU Ext. Feat. (EBX): 0x" + Integer.toHexString(getExtendedEBXFeatureFlags()));
|
||||
System.out.println("CPU Ext. Feat. (ECX): 0x" + Integer.toHexString(getExtendedECXFeatureFlags()));
|
||||
|
||||
CPUInfo c = getInfo();
|
||||
System.out.println("\n **More CPUInfo**");
|
||||
System.out.println("CPU model string: " + c.getCPUModelString());
|
||||
System.out.println("CPU has MMX: " + c.hasMMX());
|
||||
System.out.println("CPU has SSE: " + c.hasSSE());
|
||||
System.out.println("CPU has SSE2: " + c.hasSSE2());
|
||||
System.out.println("CPU has SSE3: " + c.hasSSE3());
|
||||
System.out.println("CPU has MMX: " + c.hasMMX());
|
||||
System.out.println("CPU has SSE: " + c.hasSSE());
|
||||
System.out.println("CPU has SSE2: " + c.hasSSE2());
|
||||
System.out.println("CPU has SSE3: " + c.hasSSE3());
|
||||
System.out.println("CPU has SSE4.1: " + c.hasSSE41());
|
||||
System.out.println("CPU has SSE4.2: " + c.hasSSE42());
|
||||
System.out.println("CPU has SSE4A: " + c.hasSSE4A());
|
||||
System.out.println("CPU has SSE4A: " + c.hasSSE4A());
|
||||
System.out.println("CPU has AES-NI: " + c.hasAES());
|
||||
System.out.println("CPU has AVX: " + c.hasAVX());
|
||||
System.out.println("CPU has AVX2: " + c.hasAVX2());
|
||||
System.out.println("CPU has AVX512: " + c.hasAVX512());
|
||||
System.out.println("CPU has ADX: " + c.hasADX());
|
||||
System.out.println("CPU has TBM: " + c.hasTBM());
|
||||
System.out.println("CPU has BMI1: " + c.hasBMI1());
|
||||
System.out.println("CPU has BMI2: " + c.hasBMI2());
|
||||
System.out.println("CPU has FMA3: " + c.hasFMA3());
|
||||
System.out.println("CPU has MOVBE: " + c.hasMOVBE());
|
||||
System.out.println("CPU has ABM: " + c.hasABM());
|
||||
if(c instanceof IntelCPUInfo){
|
||||
System.out.println("\n **Intel-info**");
|
||||
System.out.println("Is PII-compatible: "+((IntelCPUInfo)c).IsPentium2Compatible());
|
||||
@@ -316,13 +383,17 @@ public class CPUID {
|
||||
System.out.println("Is Pentium M compatible: "+((IntelCPUInfo)c).IsPentiumMCompatible());
|
||||
System.out.println("Is Core2-compatible: "+((IntelCPUInfo)c).IsCore2Compatible());
|
||||
System.out.println("Is Corei-compatible: "+((IntelCPUInfo)c).IsCoreiCompatible());
|
||||
System.out.println("Is Sandy-compatible: "+((IntelCPUInfo)c).IsSandyCompatible());
|
||||
System.out.println("Is Ivy-compatible: "+((IntelCPUInfo)c).IsIvyCompatible());
|
||||
System.out.println("Is Haswell-compatible: "+((IntelCPUInfo)c).IsHaswellCompatible());
|
||||
System.out.println("Is Broadwell-compatible: "+((IntelCPUInfo)c).IsBroadwellCompatible());
|
||||
}
|
||||
if(c instanceof AMDCPUInfo){
|
||||
System.out.println("\n **AMD-info**");
|
||||
System.out.println("Is K6-compatible: "+((AMDCPUInfo)c).IsK6Compatible());
|
||||
System.out.println("Is K6_2-compatible: "+((AMDCPUInfo)c).IsK6_2_Compatible());
|
||||
System.out.println("Is K6_3-compatible: "+((AMDCPUInfo)c).IsK6_3_Compatible());
|
||||
System.out.println("Is K6-compatible: "+((AMDCPUInfo)c).IsGeodeCompatible());
|
||||
System.out.println("Is Geode-compatible: "+((AMDCPUInfo)c).IsGeodeCompatible());
|
||||
System.out.println("Is Athlon-compatible: "+((AMDCPUInfo)c).IsAthlonCompatible());
|
||||
System.out.println("Is Athlon64-compatible: "+((AMDCPUInfo)c).IsAthlon64Compatible());
|
||||
System.out.println("Is Bobcat-compatible: "+((AMDCPUInfo)c).IsBobcatCompatible());
|
||||
@@ -358,6 +429,7 @@ public class CPUID {
|
||||
System.err.println("WARNING: Native CPUID library jcpuid not loaded - will not be able to read CPU information using CPUID");
|
||||
}
|
||||
}
|
||||
_jcpuidVersion = fetchJcpuidVersion();
|
||||
} else {
|
||||
if (_doLog)
|
||||
System.err.println("INFO: Native CPUID library jcpuid not loaded - will not be able to read CPU information using CPUID");
|
||||
|
||||
@@ -5,10 +5,8 @@ package freenet.support.CPUInformation;
|
||||
* Ref: http://en.wikipedia.org/wiki/CPUID
|
||||
* @since 0.8.7
|
||||
*/
|
||||
abstract class CPUIDCPUInfo implements CPUInfo
|
||||
class CPUIDCPUInfo implements CPUInfo
|
||||
{
|
||||
protected static boolean isX64 = false;
|
||||
|
||||
public String getVendor()
|
||||
{
|
||||
return CPUID.getCPUVendorID();
|
||||
@@ -16,45 +14,158 @@ abstract class CPUIDCPUInfo implements CPUInfo
|
||||
|
||||
public boolean hasMMX()
|
||||
{
|
||||
return (CPUID.getEDXCPUFlags() & 0x800000) != 0; //EDX Bit 23
|
||||
return (CPUID.getEDXCPUFlags() & (1 << 23)) != 0; //EDX Bit 23
|
||||
}
|
||||
|
||||
public boolean hasSSE(){
|
||||
return (CPUID.getEDXCPUFlags() & 0x2000000) != 0; //EDX Bit 25
|
||||
return (CPUID.getEDXCPUFlags() & (1 << 25)) != 0; //EDX Bit 25
|
||||
}
|
||||
|
||||
public boolean hasSSE2()
|
||||
{
|
||||
return (CPUID.getEDXCPUFlags() & 0x4000000) != 0; //EDX Bit 26
|
||||
return (CPUID.getEDXCPUFlags() & (1 << 26)) != 0; //EDX Bit 26
|
||||
}
|
||||
|
||||
public boolean hasSSE3()
|
||||
{
|
||||
return (CPUID.getECXCPUFlags() & 0x1) != 0; //ECX Bit 0
|
||||
return (CPUID.getECXCPUFlags() & (1 << 0)) != 0; //ECX Bit 0
|
||||
}
|
||||
|
||||
public boolean hasSSE41()
|
||||
{
|
||||
return (CPUID.getECXCPUFlags() & 0x80000) != 0; //ECX Bit 19
|
||||
return (CPUID.getECXCPUFlags() & (1 << 19)) != 0; //ECX Bit 19
|
||||
}
|
||||
|
||||
public boolean hasSSE42()
|
||||
{
|
||||
return (CPUID.getECXCPUFlags() & 0x100000) != 0; //ECX Bit 20
|
||||
return (CPUID.getECXCPUFlags() & (1 << 20)) != 0; //ECX Bit 20
|
||||
}
|
||||
|
||||
public boolean hasSSE4A()
|
||||
{
|
||||
return (CPUID.getExtendedECXCPUFlags() & 0x40) != 0; //Extended ECX Bit 6
|
||||
return (CPUID.getExtendedECXCPUFlags() & (1 << 6)) != 0; //Extended ECX Bit 6
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the AVX instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasAVX()
|
||||
{
|
||||
int ecx = CPUID.getECXCPUFlags();
|
||||
return (ecx & (1 << 28)) != 0 && //AVX: ECX Bit 28
|
||||
(ecx & (1 << 27)) != 0; //XSAVE enabled by OS: ECX Bit 27
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the AVX2 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasAVX2() {
|
||||
return (CPUID.getExtendedEBXFeatureFlags() & (1 << 5)) != 0; //Extended EBX Feature Bit 5
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the CPU supports the AVX-512 Foundation instruction set?
|
||||
*
|
||||
* Quote wikipedia:
|
||||
*
|
||||
* AVX-512 consists of multiple extensions not all meant to be supported
|
||||
* by all processors implementing them. Only the core extension AVX-512F
|
||||
* (AVX-512 Foundation) is required by all implementations.
|
||||
*
|
||||
* ref: https://en.wikipedia.org/wiki/AVX-512
|
||||
*
|
||||
* @return true iff the CPU supports the AVX-512 Foundation instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasAVX512()
|
||||
{
|
||||
return (CPUID.getExtendedEBXFeatureFlags() & (1 << 16)) != 0; //Extended EBX Bit 16
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Intel Multi-Precision Add-Carry Instruction Extensions
|
||||
* Available in Broadwell.
|
||||
* Unused until GMP 6.1.
|
||||
*
|
||||
* @return true iff the CPU supports the ADX instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasADX()
|
||||
{
|
||||
return (CPUID.getExtendedEBXFeatureFlags() & (1 << 19)) != 0; //Extended EBX Bit 19
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports TBM.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasTBM()
|
||||
{
|
||||
return (CPUID.getECXCPUFlags() & (1 << 21)) != 0; //ECX Bit 21
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the AES-NI instruction set.
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public boolean hasAES() {
|
||||
return (CPUID.getECXCPUFlags() & 0x2000000) != 0; //ECX Bit 25
|
||||
return (CPUID.getECXCPUFlags() & (1 << 25)) != 0; //ECX Bit 25
|
||||
}
|
||||
|
||||
public abstract boolean hasX64();
|
||||
/**
|
||||
* @return true iff the CPU supports the 64-bit support
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasX64() {
|
||||
return (CPUID.getExtendedEDXCPUFlags() & (1 << 29)) != 0; //Extended EDX Bit 29
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the BMI1 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasBMI1() {
|
||||
return (CPUID.getExtendedEBXFeatureFlags() & (1 << 3)) != 0; // Extended EBX Feature Bit 3
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the BMI2 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasBMI2() {
|
||||
return (CPUID.getExtendedEBXFeatureFlags() & (1 << 8)) != 0; // Extended EBX Feature Bit 8
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the FMA3 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasFMA3() {
|
||||
return (CPUID.getECXCPUFlags() & (1 << 12)) != 0; // ECX Bit 12
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the MOVBE instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasMOVBE() {
|
||||
return (CPUID.getECXCPUFlags() & (1 << 22)) != 0; // ECX Bit 22
|
||||
}
|
||||
|
||||
/**
|
||||
* Also known as LZCNT
|
||||
* @return true iff the CPU supports the ABM instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasABM() {
|
||||
return (CPUID.getExtendedECXCPUFlags() & (1 << 5)) != 0; // Extended ECX Bit 5
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCPUModelString() throws UnknownCPUException {
|
||||
throw new UnknownCPUException("Class CPUIDCPUInfo cannot indentify CPUs");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public interface CPUInfo
|
||||
* @return A string indicating the vendor of the CPU.
|
||||
*/
|
||||
public String getVendor();
|
||||
|
||||
/**
|
||||
* @return A string detailing what type of CPU that is present in the machine. I.e. 'Pentium IV' etc.
|
||||
* @throws UnknownCPUException If for any reason the retrieval of the requested information
|
||||
@@ -29,43 +30,130 @@ public interface CPUInfo
|
||||
* cause of the failure.
|
||||
*/
|
||||
public String getCPUModelString() throws UnknownCPUException;
|
||||
|
||||
|
||||
/**
|
||||
* @return true iff the CPU support the MMX instruction set.
|
||||
* @return true iff the CPU supports the MMX instruction set.
|
||||
*/
|
||||
public boolean hasMMX();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU support the SSE instruction set.
|
||||
* @return true iff the CPU supports the SSE instruction set.
|
||||
*/
|
||||
public boolean hasSSE();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU support the SSE2 instruction set.
|
||||
* @return true iff the CPU supports the SSE2 instruction set.
|
||||
*/
|
||||
public boolean hasSSE2();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU support the SSE3 instruction set.
|
||||
* @return true iff the CPU supports the SSE3 instruction set.
|
||||
*/
|
||||
public boolean hasSSE3();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU support the SSE4.1 instruction set.
|
||||
* @return true iff the CPU supports the SSE4.1 instruction set.
|
||||
*/
|
||||
public boolean hasSSE41();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU support the SSE4.2 instruction set.
|
||||
* @return true iff the CPU supports the SSE4.2 instruction set.
|
||||
*/
|
||||
public boolean hasSSE42();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU support the SSE4A instruction set.
|
||||
* AMD K10 only. Not supported on Intel.
|
||||
* ref: https://en.wikipedia.org/wiki/SSE4.2#SSE4a
|
||||
*
|
||||
* @return true iff the CPU supports the SSE4A instruction set.
|
||||
*/
|
||||
public boolean hasSSE4A();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the AVX instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasAVX();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the AVX2 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasAVX2();
|
||||
|
||||
/**
|
||||
* Does the CPU supports the AVX-512 Foundation instruction set?
|
||||
*
|
||||
* Quote wikipedia:
|
||||
*
|
||||
* AVX-512 consists of multiple extensions not all meant to be supported
|
||||
* by all processors implementing them. Only the core extension AVX-512F
|
||||
* (AVX-512 Foundation) is required by all implementations.
|
||||
*
|
||||
* ref: https://en.wikipedia.org/wiki/AVX-512
|
||||
*
|
||||
* @return true iff the CPU supports the AVX-512 Foundation instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasAVX512();
|
||||
|
||||
/**
|
||||
*
|
||||
* Intel Multi-Precision Add-Carry Instruction Extensions
|
||||
* Available in Broadwell.
|
||||
* Unused until GMP 6.1.
|
||||
*
|
||||
* @return true iff the CPU supports the ADX instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasADX();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports TBM.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasTBM();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the AES-NI instruction set.
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public boolean hasAES();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the 64-bit support
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasX64();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the BMI1 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasBMI1();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the BMI2 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasBMI2();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the FMA3 instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasFMA3();
|
||||
|
||||
/**
|
||||
* @return true iff the CPU supports the MOVBE instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasMOVBE();
|
||||
|
||||
/**
|
||||
* Also known as LZCNT
|
||||
* @return true iff the CPU supports the ABM instruction set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean hasABM();
|
||||
}
|
||||
|
||||
@@ -15,33 +15,44 @@ package freenet.support.CPUInformation;
|
||||
* @author Iakin
|
||||
*/
|
||||
public interface IntelCPUInfo extends CPUInfo {
|
||||
|
||||
/**
|
||||
* @return true if the CPU is at least a Pentium CPU.
|
||||
*/
|
||||
public boolean IsPentiumCompatible();
|
||||
|
||||
/**
|
||||
* @return true if the CPU is at least a Pentium which implements the MMX instruction/feature set.
|
||||
*/
|
||||
public boolean IsPentiumMMXCompatible();
|
||||
|
||||
/**
|
||||
* @return true if the CPU implements at least the p6 instruction set (Pentium II or better).
|
||||
* Please note that an PentimPro CPU causes/should cause this method to return false (due to that CPU using a
|
||||
* very early implementation of the p6 instruction set. No MMX etc.)
|
||||
*/
|
||||
public boolean IsPentium2Compatible();
|
||||
|
||||
/**
|
||||
* @return true if the CPU implements at least a Pentium III level of the p6 instruction/feature set.
|
||||
*/
|
||||
public boolean IsPentium3Compatible();
|
||||
|
||||
/**
|
||||
* Supports the SSE 2 instructions. Does not necessarily support SSE 3.
|
||||
* https://en.wikipedia.org/wiki/Pentium_4
|
||||
* @return true if the CPU implements at least a Pentium IV level instruction/feature set.
|
||||
*/
|
||||
public boolean IsPentium4Compatible();
|
||||
|
||||
/**
|
||||
* @return true if the CPU implements at least a Pentium M level instruction/feature set.
|
||||
*/
|
||||
public boolean IsPentiumMCompatible();
|
||||
|
||||
/**
|
||||
* Supports the SSE 2 and SSE 3 instructions.
|
||||
* https://en.wikipedia.org/wiki/Atom_processor
|
||||
* @return true if the CPU implements at least a Atom level instruction/feature set.
|
||||
*/
|
||||
public boolean IsAtomCompatible();
|
||||
@@ -55,7 +66,77 @@ public interface IntelCPUInfo extends CPUInfo {
|
||||
/**
|
||||
* Supports the SSE 3, 4.1, 4.2 instructions.
|
||||
* In general, this requires 45nm or smaller process.
|
||||
*
|
||||
* This is the Nehalem architecture.
|
||||
* ref: https://en.wikipedia.org/wiki/Nehalem_%28microarchitecture%29
|
||||
*
|
||||
* @return true if the CPU implements at least a Corei level instruction/feature set.
|
||||
*/
|
||||
public boolean IsCoreiCompatible();
|
||||
|
||||
/**
|
||||
* Supports the SSE 3, 4.1, 4.2 instructions.
|
||||
* Supports the AVX 1 instructions.
|
||||
* In general, this requires 32nm or smaller process.
|
||||
* @return true if the CPU implements at least a SandyBridge level instruction/feature set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean IsSandyCompatible();
|
||||
|
||||
/**
|
||||
* Supports the SSE 3, 4.1, 4.2 instructions.
|
||||
* Supports the AVX 1 instructions.
|
||||
* In general, this requires 22nm or smaller process.
|
||||
*
|
||||
* UNUSED, there is no specific GMP build for Ivy Bridge,
|
||||
* and this is never called from NativeBigInteger.
|
||||
* Ivy Bridge is a successor to Sandy Bridge, so use IsSandyCompatible().
|
||||
*
|
||||
* @return true if the CPU implements at least a IvyBridge level instruction/feature set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean IsIvyCompatible();
|
||||
|
||||
/**
|
||||
* Supports the SSE 3, 4.1, 4.2 instructions.
|
||||
* Supports the AVX 1, 2 instructions.
|
||||
* Supports the BMI 1, 2 instructions.
|
||||
*
|
||||
* WARNING - GMP 6 uses the BMI2 MULX instruction for the "coreihwl" binaries.
|
||||
* Only Core i3/i5/i7 Haswell processors support BMI2.
|
||||
*
|
||||
* Requires support for all 6 of these Corei features: FMA3 MOVBE ABM AVX2 BMI1 BMI2
|
||||
* Pentium/Celeron Haswell processors do NOT support BMI2 and are NOT compatible.
|
||||
* Those processors will be Sandy-compatible if they have AVX 1 support,
|
||||
* and Corei-compatible if they do not.
|
||||
*
|
||||
* ref: https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
|
||||
* ref: https://en.wikipedia.org/wiki/Haswell_%28microarchitecture%29
|
||||
*
|
||||
* In general, this requires 22nm or smaller process.
|
||||
* @return true if the CPU implements at least a Haswell level instruction/feature set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean IsHaswellCompatible();
|
||||
|
||||
/**
|
||||
* Supports the SSE 3, 4.1, 4.2 instructions.
|
||||
* Supports the AVX 1, 2 instructions.
|
||||
* In general, this requires 14nm or smaller process.
|
||||
*
|
||||
* NOT FULLY USED as of GMP 6.0.
|
||||
* All GMP coreibwl binaries are duplicates of binaries for older technologies,
|
||||
* so we do not distribute any. However, this is called from NativeBigInteger.
|
||||
*
|
||||
* Broadwell is supported in GMP 6.1 and requires the ADX instructions.
|
||||
*
|
||||
* Requires support for all 7 of these Corei features: FMA3 MOVBE ABM AVX2 BMI1 BMI2 ADX
|
||||
* Pentium/Celeron Broadwell processors that do not support these instruction sets are not compatible.
|
||||
* Those processors will be Sandy-compatible if they have AVX 1 support,
|
||||
* and Corei-compatible if they do not.
|
||||
*
|
||||
* @return true if the CPU implements at least a Broadwell level instruction/feature set.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean IsBroadwellCompatible();
|
||||
}
|
||||
|
||||
@@ -19,41 +19,34 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
private static boolean isAtomCompatible;
|
||||
private static boolean isCore2Compatible;
|
||||
private static boolean isCoreiCompatible;
|
||||
private static boolean isSandyCompatible;
|
||||
private static boolean isIvyCompatible;
|
||||
private static boolean isHaswellCompatible;
|
||||
private static boolean isBroadwellCompatible;
|
||||
|
||||
// If modelString != null, the cpu is considered correctly identified.
|
||||
private static final String smodel = identifyCPU();
|
||||
|
||||
public boolean IsPentiumCompatible(){ return isPentiumCompatible; }
|
||||
|
||||
public boolean IsPentiumMMXCompatible(){ return isPentiumMMXCompatible; }
|
||||
|
||||
public boolean IsPentium2Compatible(){ return isPentium2Compatible; }
|
||||
|
||||
public boolean IsPentium3Compatible(){ return isPentium3Compatible; }
|
||||
|
||||
public boolean IsPentium4Compatible(){ return isPentium4Compatible; }
|
||||
|
||||
public boolean IsPentiumMCompatible(){ return isPentiumMCompatible; }
|
||||
|
||||
public boolean IsAtomCompatible(){ return isAtomCompatible; }
|
||||
|
||||
/**
|
||||
* Supports the SSE 3 instructions
|
||||
*/
|
||||
public boolean IsCore2Compatible(){ return isCore2Compatible; }
|
||||
|
||||
/**
|
||||
* Supports the SSE 3, 4.1, 4.2 instructions.
|
||||
* In general, this requires 45nm or smaller process.
|
||||
*/
|
||||
public boolean IsCoreiCompatible(){ return isCoreiCompatible; }
|
||||
public boolean IsCoreiCompatible(){ return isCoreiCompatible; }
|
||||
public boolean IsSandyCompatible(){ return isSandyCompatible; }
|
||||
public boolean IsIvyCompatible(){ return isIvyCompatible; }
|
||||
public boolean IsHaswellCompatible(){ return isHaswellCompatible; }
|
||||
public boolean IsBroadwellCompatible(){ return isBroadwellCompatible; }
|
||||
|
||||
public String getCPUModelString() throws UnknownCPUException
|
||||
{
|
||||
if (smodel != null)
|
||||
return smodel;
|
||||
throw new UnknownCPUException("Unknown Intel CPU; Family="+CPUID.getCPUFamily() + '/' + CPUID.getCPUExtendedFamily()+
|
||||
", Model="+CPUID.getCPUModel() + '/' + CPUID.getCPUExtendedModel());
|
||||
", Model="+CPUID.getCPUModel() + '/' + CPUID.getCPUExtendedModel()); //
|
||||
|
||||
}
|
||||
|
||||
private static String identifyCPU()
|
||||
@@ -61,6 +54,7 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
// http://en.wikipedia.org/wiki/Cpuid
|
||||
// http://web.archive.org/web/20110307080258/http://www.intel.com/Assets/PDF/appnote/241618.pdf
|
||||
// http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf
|
||||
// #include "llvm/Support/Host.h", http://llvm.org/docs/doxygen/html/Host_8cpp_source.html
|
||||
String modelString = null;
|
||||
int family = CPUID.getCPUFamily();
|
||||
int model = CPUID.getCPUModel();
|
||||
@@ -155,7 +149,6 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
isPentium4Compatible = true;
|
||||
isPentiumMCompatible = true;
|
||||
isCore2Compatible = true;
|
||||
isX64 = true;
|
||||
if (extmodel >= 2)
|
||||
isCoreiCompatible = true;
|
||||
}
|
||||
@@ -211,7 +204,6 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
isPentium2Compatible = true;
|
||||
isPentium3Compatible = true;
|
||||
isPentiumMCompatible = true;
|
||||
isX64 = true;
|
||||
modelString = "Pentium M (Banias)";
|
||||
break;
|
||||
case 10:
|
||||
@@ -228,7 +220,6 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
isPentium2Compatible = true;
|
||||
isPentium3Compatible = true;
|
||||
isPentiumMCompatible = true;
|
||||
isX64 = true;
|
||||
modelString = "Core (Yonah)";
|
||||
break;
|
||||
case 14:
|
||||
@@ -237,8 +228,7 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
isPentium3Compatible = true;
|
||||
isPentiumMCompatible = true;
|
||||
isCore2Compatible = true;
|
||||
isX64 = true;
|
||||
modelString = "Core 2 (Conroe)";
|
||||
modelString = "Penryn";
|
||||
break;
|
||||
|
||||
// following are for extended model == 1
|
||||
@@ -246,16 +236,16 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
|
||||
// Celeron 65 nm
|
||||
case 0x16:
|
||||
modelString = "Celeron";
|
||||
modelString = "Merom";
|
||||
break;
|
||||
// Penryn 45 nm
|
||||
case 0x17:
|
||||
modelString = "Core 2 (45nm)";
|
||||
modelString = "Penryn";
|
||||
break;
|
||||
// Nehalem 45 nm
|
||||
case 0x1a:
|
||||
isCoreiCompatible = true;
|
||||
modelString = "Core i7 (45nm)";
|
||||
modelString = "Nehalem";
|
||||
break;
|
||||
// Atom Pineview / Silverthorne 45 nm
|
||||
case 0x1c:
|
||||
@@ -263,18 +253,17 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
// Some support SSE3? true for Pineview? TBD...
|
||||
isCore2Compatible = false;
|
||||
isPentium4Compatible = false;
|
||||
isX64 = true;
|
||||
modelString = "Atom";
|
||||
break;
|
||||
// Penryn 45 nm
|
||||
case 0x1d:
|
||||
isCoreiCompatible = true;
|
||||
modelString = "Xeon MP (45nm)";
|
||||
modelString = "Penryn";
|
||||
break;
|
||||
// Nehalem 45 nm
|
||||
case 0x1e:
|
||||
isCoreiCompatible = true;
|
||||
modelString = "Core i5/i7 (45nm)";
|
||||
modelString = "Nehalem";
|
||||
break;
|
||||
|
||||
// following are for extended model == 2
|
||||
@@ -283,37 +272,44 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
|
||||
// Westmere 32 nm
|
||||
case 0x25:
|
||||
modelString = "Core i3 or i5/i7 mobile (32nm)";
|
||||
modelString = "Westmere";
|
||||
break;
|
||||
// Atom Lincroft 45 nm
|
||||
case 0x26:
|
||||
isAtomCompatible = true;
|
||||
// Supports SSE 3
|
||||
isCoreiCompatible = false;
|
||||
modelString = "Atom Z600";
|
||||
modelString = "Atom";
|
||||
break;
|
||||
// Sandy bridge 32 nm
|
||||
// 1, 2, or 4 cores
|
||||
// ref: https://en.wikipedia.org/wiki/Sandy_Bridge_%28microarchitecture%29
|
||||
case 0x2a:
|
||||
modelString = "Sandy Bridge H/M";
|
||||
isSandyCompatible = true;
|
||||
modelString = "Sandy Bridge";
|
||||
break;
|
||||
// Details unknown, please add a proper model string if 0x2B model is found
|
||||
case 0x2b:
|
||||
modelString = "Core i7/i5 (32nm)";
|
||||
break;
|
||||
// Westmere
|
||||
case 0x2c:
|
||||
modelString = "Core i7 (32nm)";
|
||||
modelString = "Westmere";
|
||||
break;
|
||||
// Sandy bridge 32 nm
|
||||
// Sandy Bridge 32 nm
|
||||
// Sandy Bridge-E up to 8 cores
|
||||
// ref: https://en.wikipedia.org/wiki/Sandy_Bridge_%28microarchitecture%29
|
||||
case 0x2d:
|
||||
modelString = "Sandy Bridge EP";
|
||||
isSandyCompatible = true;
|
||||
modelString = "Sandy Bridge";
|
||||
break;
|
||||
// Nehalem 45 nm
|
||||
case 0x2e:
|
||||
modelString = "Xeon MP (45nm)";
|
||||
modelString = "Nehalem";
|
||||
break;
|
||||
// Westmere 32 nm
|
||||
case 0x2f:
|
||||
modelString = "Xeon MP (32nm)";
|
||||
modelString = "Westemere";
|
||||
break;
|
||||
|
||||
// following are for extended model == 3
|
||||
@@ -325,46 +321,151 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
isAtomCompatible = true;
|
||||
// Supports SSE 3
|
||||
isCore2Compatible = false;
|
||||
modelString = "Atom N2000/D2000";
|
||||
isCoreiCompatible = false;
|
||||
modelString = "Atom";
|
||||
break;
|
||||
// Silvermont 22 nm Celeron
|
||||
case 0x37:
|
||||
isAtomCompatible = true;
|
||||
isCore2Compatible = false;
|
||||
isCoreiCompatible = false;
|
||||
modelString = "Atom";
|
||||
break;
|
||||
// Ivy Bridge 22 nm
|
||||
// ref: https://en.wikipedia.org/wiki/Sandy_Bridge_%28microarchitecture%29
|
||||
case 0x3a:
|
||||
modelString = "Ivy Bridge (22nm)";
|
||||
break;
|
||||
// Haswell 22 nm
|
||||
case 0x3c:
|
||||
modelString = "Haswell (22nm)";
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
modelString = "Ivy Bridge";
|
||||
break;
|
||||
|
||||
// case 0x3c: See below
|
||||
|
||||
// Broadwell 14 nm
|
||||
case 0x3d:
|
||||
modelString = "Broadwell (14nm)";
|
||||
// See Haswell notes below
|
||||
case 0x3d: {
|
||||
CPUIDCPUInfo c = new CPUIDCPUInfo();
|
||||
if (c.hasAVX2() && c.hasBMI1() && c.hasBMI2() &&
|
||||
c.hasFMA3() && c.hasMOVBE() && c.hasABM()) {
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
isHaswellCompatible = true;
|
||||
if (c.hasADX())
|
||||
isBroadwellCompatible = true;
|
||||
modelString = "Broadwell Core i3/i5/i7";
|
||||
} else {
|
||||
// This processor is "corei" compatible, as we define it,
|
||||
// i.e. SSE4.2 but not necessarily AVX.
|
||||
if (c.hasAVX()) {
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
modelString = "Broadwell Celeron/Pentium w/ AVX";
|
||||
} else {
|
||||
modelString = "Broadwell Celeron/Pentium";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Ivy Bridge 22 nm
|
||||
case 0x3e:
|
||||
modelString = "Xeon Ivy Bridge (22nm)";
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
modelString = "Ivy Bridge";
|
||||
break;
|
||||
|
||||
|
||||
|
||||
// case 0x3f: See below
|
||||
|
||||
// following are for extended model == 4
|
||||
// most flags are set above
|
||||
// isCoreiCompatible = true is the default
|
||||
|
||||
// 22 nm
|
||||
|
||||
// Haswell 22 nm
|
||||
// Pentium and Celeron Haswells do not support new Haswell instructions,
|
||||
// only Corei ones do, but we can't tell that from the model alone.
|
||||
//
|
||||
// We know for sure that GMP coreihwl uses the MULX instruction from BMI2,
|
||||
// unsure about the others, but let's be safe and check all 6 feature bits, as
|
||||
// the Intel app note suggests.
|
||||
//
|
||||
// ref: https://en.wikipedia.org/wiki/Haswell_%28microarchitecture%29
|
||||
// ref: https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
|
||||
case 0x3c:
|
||||
case 0x3f:
|
||||
case 0x45:
|
||||
modelString = "Mobile Celeron";
|
||||
case 0x46:
|
||||
{
|
||||
CPUIDCPUInfo c = new CPUIDCPUInfo();
|
||||
if (c.hasAVX2() && c.hasBMI1() && c.hasBMI2() &&
|
||||
c.hasFMA3() && c.hasMOVBE() && c.hasABM()) {
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
isHaswellCompatible = true;
|
||||
modelString = "Haswell Core i3/i5/i7 model " + model;
|
||||
} else {
|
||||
// This processor is "corei" compatible, as we define it,
|
||||
// i.e. SSE4.2 but not necessarily AVX.
|
||||
if (c.hasAVX()) {
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
modelString = "Haswell Celeron/Pentium w/ AVX model " + model;
|
||||
} else {
|
||||
modelString = "Haswell Celeron/Pentium model " + model;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Atom Silvermont / Bay Trail / Avoton 22 nm
|
||||
}
|
||||
|
||||
// Quark 32nm
|
||||
case 0x4a:
|
||||
isCore2Compatible = false;
|
||||
isCoreiCompatible = false;
|
||||
modelString = "Quark";
|
||||
break;
|
||||
// Silvermont 22 nm
|
||||
// Supports SSE 4.2
|
||||
case 0x4d:
|
||||
isAtomCompatible = true;
|
||||
modelString = "Bay Trail / Avoton";
|
||||
modelString = "Atom";
|
||||
break;
|
||||
|
||||
// following are for extended model == 5
|
||||
// most flags are set above
|
||||
// isCoreiCompatible = true is the default
|
||||
|
||||
// Skylake 14 nm
|
||||
// See reference link errata #SKD052 re: BMI
|
||||
// ref: http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/desktop-6th-gen-core-family-spec-update.pdf
|
||||
// See Haswell notes above
|
||||
case 0x5e: {
|
||||
CPUIDCPUInfo c = new CPUIDCPUInfo();
|
||||
if (c.hasAVX2() && c.hasBMI1() && c.hasBMI2() &&
|
||||
c.hasFMA3() && c.hasMOVBE() && c.hasABM()) {
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
isHaswellCompatible = true;
|
||||
if (c.hasADX())
|
||||
isBroadwellCompatible = true;
|
||||
modelString = "Skylake Core i3/i5/i7";
|
||||
} else {
|
||||
// This processor is "corei" compatible, as we define it,
|
||||
// i.e. SSE4.2 but not necessarily AVX.
|
||||
if (c.hasAVX()) {
|
||||
isSandyCompatible = true;
|
||||
isIvyCompatible = true;
|
||||
modelString = "Skylake Celeron/Pentium w/ AVX";
|
||||
} else {
|
||||
modelString = "Skylake Celeron/Pentium";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// others
|
||||
default:
|
||||
modelString = "Intel model " + model;
|
||||
break;
|
||||
|
||||
} // switch model
|
||||
} // case 6
|
||||
break;
|
||||
@@ -392,11 +493,9 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
modelString = "Pentium IV (90 nm)";
|
||||
break;
|
||||
case 4:
|
||||
isX64 = true;
|
||||
modelString = "Pentium IV (90 nm)";
|
||||
break;
|
||||
case 6:
|
||||
isX64 = true;
|
||||
modelString = "Pentium IV (65 nm)";
|
||||
break;
|
||||
default:
|
||||
@@ -412,8 +511,4 @@ class IntelInfoImpl extends CPUIDCPUInfo implements IntelCPUInfo
|
||||
}
|
||||
return modelString;
|
||||
}
|
||||
|
||||
public boolean hasX64() {
|
||||
return isX64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
#* Boston, MA 02111-1307 USA
|
||||
#**************************************************************************/
|
||||
getopt.ambigious={0}\: valitsin \u201d{1}\u201d ei ole yksiselitteinen
|
||||
getopt.arguments1={0}\: valitsin ''--{1}'' ei salli argumenttia
|
||||
getopt.arguments2={0}\: valitsin ''{1}{2}'' ei salli argumenttia
|
||||
getopt.arguments1={0}\: valitsin ''--{1}'' ei hyväksy argumentteja
|
||||
getopt.arguments2={0}\: valitsin ''{1}{2}'' ei hyväksy argumentteja
|
||||
getopt.requires={0}\: valitsin ''{1}'' vaatii argumentin
|
||||
getopt.unrecognized={0}\: tunnistamaton valitsin ''--{1}''
|
||||
getopt.unrecognized2={0}\: tunnistamaton valitsin ''{1}{2}''
|
||||
getopt.illegal={0}\: virheellinen valitsin -- {1}
|
||||
getopt.invalid={0}\: virheellinen valitsin -- {1}
|
||||
getopt.illegal={0}\: kielletty valitsin -- {1}
|
||||
getopt.invalid={0}\: kelvoton valitsin -- {1}
|
||||
getopt.requires2={0}\: valitsin vaatii argumentin -- {1}
|
||||
getopt.invalidValue=Virheellinen arvo {0} parametrille 'has_arg'
|
||||
getopt.invalidValue=Kelvoton arvo {0} parametrille 'has_arg'
|
||||
|
||||
@@ -18,7 +18,7 @@ public class CoreVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
|
||||
public final static String VERSION = "0.9.24";
|
||||
public final static String VERSION = "0.9.25";
|
||||
|
||||
/**
|
||||
* For Vuze.
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package net.i2p.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Bridge to Unix domain socket (or similar).
|
||||
* <p/>
|
||||
* This is a stub that does nothing.
|
||||
* This class is replaced in the Android build.
|
||||
*
|
||||
* @author str4d
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public class DomainSocketFactory {
|
||||
public static final String I2CP_SOCKET_ADDRESS = "net.i2p.client.i2cp";
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public DomainSocketFactory(I2PAppContext context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override in Android.
|
||||
* @throws IOException
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public Socket createSocket(String name) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override in Android.
|
||||
* @throws IOException
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public ServerSocket createServerSocket(String name) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
@@ -29,7 +32,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.CoreVersion;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.DomainSocketFactory;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
@@ -597,9 +599,28 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
||||
_reader = new QueuedI2CPMessageReader(_queue, this);
|
||||
} else {
|
||||
if (SystemVersion.isAndroid() &&
|
||||
Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET))) {
|
||||
final DomainSocketFactory fact = new DomainSocketFactory(_context);
|
||||
_socket = fact.createSocket(DomainSocketFactory.I2CP_SOCKET_ADDRESS);
|
||||
_options.getProperty(PROP_DOMAIN_SOCKET) != null) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName("net.i2p.client.DomainSocketFactory");
|
||||
Constructor<?> ctor = clazz.getDeclaredConstructor(I2PAppContext.class);
|
||||
Object fact = ctor.newInstance(_context);
|
||||
Method createSocket = clazz.getDeclaredMethod("createSocket", String.class);
|
||||
try {
|
||||
_socket = (Socket) createSocket.invoke(fact, _options.getProperty(PROP_DOMAIN_SOCKET));
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new I2PSessionException("Cannot create domain socket", e);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new I2PSessionException("Cannot load DomainSocketFactory", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new I2PSessionException("Cannot load DomainSocketFactory", e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new I2PSessionException("Cannot load DomainSocketFactory", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new I2PSessionException("Cannot load DomainSocketFactory", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new I2PSessionException("Cannot load DomainSocketFactory", e);
|
||||
}
|
||||
} else if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL))) {
|
||||
try {
|
||||
I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
|
||||
@@ -684,8 +705,8 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
||||
if (_context.isRouterContext())
|
||||
msg = "Failed to build tunnels";
|
||||
else if (SystemVersion.isAndroid() &&
|
||||
Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET)))
|
||||
msg = "Failed to bind to the router and build tunnels";
|
||||
_options.getProperty(PROP_DOMAIN_SOCKET) != null)
|
||||
msg = "Failed to bind to the router on " + _options.getProperty(PROP_DOMAIN_SOCKET) + " and build tunnels";
|
||||
else
|
||||
msg = "Cannot connect to the router on " + _hostname + ':' + _portNum + " and build tunnels";
|
||||
throw new I2PSessionException(getPrefix() + msg, ioe);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
534
core/java/src/net/i2p/client/naming/HostTxtEntry.java
Normal file
534
core/java/src/net/i2p/client/naming/HostTxtEntry.java
Normal file
@@ -0,0 +1,534 @@
|
||||
package net.i2p.client.naming;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
// for testing only
|
||||
import java.io.File;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Arrays;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
|
||||
/**
|
||||
* A hostname, b64 destination, and optional properties.
|
||||
* Includes methods to sign and verify the entry.
|
||||
* Used by addressbook to parse subscription data,
|
||||
* and by i2ptunnel to generate signed metadata.
|
||||
*
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public class HostTxtEntry {
|
||||
|
||||
private final String name;
|
||||
private final String dest;
|
||||
private final OrderedProperties props;
|
||||
private boolean isValidated;
|
||||
private boolean isValid;
|
||||
|
||||
public static final char KV_SEPARATOR = '=';
|
||||
public static final String PROPS_SEPARATOR = "#!";
|
||||
public static final char PROP_SEPARATOR = '#';
|
||||
public static final String PROP_ACTION = "action";
|
||||
public static final String PROP_DATE = "date";
|
||||
public static final String PROP_DEST = "dest";
|
||||
public static final String PROP_EXPIRES = "expires";
|
||||
public static final String PROP_NAME = "name";
|
||||
public static final String PROP_OLDDEST = "olddest";
|
||||
public static final String PROP_OLDNAME = "oldname";
|
||||
public static final String PROP_OLDSIG = "oldsig";
|
||||
public static final String PROP_SIG = "sig";
|
||||
public static final String ACTION_ADDDEST = "adddest";
|
||||
public static final String ACTION_ADDNAME = "addname";
|
||||
public static final String ACTION_ADDSUBDOMAIN = "addsubdomain";
|
||||
public static final String ACTION_CHANGEDEST = "changedest";
|
||||
public static final String ACTION_CHANGENAME = "changename";
|
||||
public static final String ACTION_REMOVE = "remove";
|
||||
public static final String ACTION_REMOVEALL = "removeall";
|
||||
public static final String ACTION_UPDATE = "update";
|
||||
|
||||
/**
|
||||
* Properties will be null
|
||||
*/
|
||||
public HostTxtEntry(String name, String dest) {
|
||||
this(name, dest, (OrderedProperties) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sprops line part after the #!, non-null
|
||||
* @throws IllegalArgumentException on dup key in sprops and other errors
|
||||
*/
|
||||
public HostTxtEntry(String name, String dest, String sprops) throws IllegalArgumentException {
|
||||
this(name, dest, parseProps(sprops));
|
||||
}
|
||||
|
||||
/**
|
||||
* A 'remove' entry. Name and Dest will be null.
|
||||
* @param sprops line part after the #!, non-null
|
||||
* @throws IllegalArgumentException on dup key in sprops and other errors
|
||||
*/
|
||||
public HostTxtEntry(String sprops) throws IllegalArgumentException {
|
||||
this(null, null, parseProps(sprops));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param props may be null
|
||||
*/
|
||||
public HostTxtEntry(String name, String dest, OrderedProperties props) {
|
||||
this.name = name;
|
||||
this.dest = dest;
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDest() {
|
||||
return dest;
|
||||
}
|
||||
|
||||
public OrderedProperties getProps() {
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line part after the #!
|
||||
* @throws IllegalArgumentException on dup key and other errors
|
||||
*/
|
||||
private static OrderedProperties parseProps(String line) throws IllegalArgumentException {
|
||||
line = line.trim();
|
||||
OrderedProperties rv = new OrderedProperties();
|
||||
String[] entries = DataHelper.split(line, "#");
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
String kv = entries[i];
|
||||
int eq = kv.indexOf("=");
|
||||
if (eq <= 0 || eq == kv.length() - 1)
|
||||
throw new IllegalArgumentException("No value: \"" + kv + '"');
|
||||
String k = kv.substring(0, eq);
|
||||
String v = kv.substring(eq + 1);
|
||||
Object old = rv.setProperty(k, v);
|
||||
if (old != null)
|
||||
throw new IllegalArgumentException("Dup key: " + k);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write as a standard line name=dest[#!k1=v1#k2=v2...]
|
||||
* Includes newline.
|
||||
*/
|
||||
public void write(BufferedWriter out) throws IOException {
|
||||
write((Writer) out);
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write as a standard line name=dest[#!k1=v1#k2=v2...]
|
||||
* Does not include newline.
|
||||
*/
|
||||
public void write(Writer out) throws IOException {
|
||||
if (name != null && dest != null) {
|
||||
out.write(name);
|
||||
out.write(KV_SEPARATOR);
|
||||
out.write(dest);
|
||||
}
|
||||
writeProps(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
|
||||
* This works whether constructed with name and dest, or just properties.
|
||||
* Includes newline.
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void writeRemoveLine(BufferedWriter out) throws IOException {
|
||||
writeRemove(out);
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
|
||||
* This works whether constructed with name and dest, or just properties.
|
||||
* Does not include newline.
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void writeRemove(Writer out) throws IOException {
|
||||
if (props == null)
|
||||
throw new IllegalStateException();
|
||||
if (name != null && dest != null) {
|
||||
props.setProperty(PROP_NAME, name);
|
||||
props.setProperty(PROP_DEST, dest);
|
||||
}
|
||||
writeProps(out);
|
||||
if (name != null && dest != null) {
|
||||
props.remove(PROP_NAME);
|
||||
props.remove(PROP_DEST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the props part (if any) only, without newline
|
||||
*/
|
||||
public void writeProps(Writer out) throws IOException {
|
||||
writeProps(out, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the props part (if any) only, without newline
|
||||
*/
|
||||
private void writeProps(Writer out, boolean omitSig, boolean omitOldSig) throws IOException {
|
||||
if (props == null)
|
||||
return;
|
||||
boolean started = false;
|
||||
for (Map.Entry<Object, Object> e : props.entrySet()) {
|
||||
String k = (String) e.getKey();
|
||||
if (omitSig && k.equals(PROP_SIG))
|
||||
continue;
|
||||
if (omitOldSig && k.equals(PROP_OLDSIG))
|
||||
continue;
|
||||
if (started) {
|
||||
out.write(PROP_SEPARATOR);
|
||||
} else {
|
||||
started = true;
|
||||
out.write(PROPS_SEPARATOR);
|
||||
}
|
||||
String v = (String) e.getValue();
|
||||
out.write(k);
|
||||
out.write(KV_SEPARATOR);
|
||||
out.write(v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify with the dest public key using the "sig" property
|
||||
*/
|
||||
public boolean hasValidSig() {
|
||||
if (props == null || name == null || dest == null)
|
||||
return false;
|
||||
if (!isValidated) {
|
||||
isValidated = true;
|
||||
StringWriter buf = new StringWriter(1024);
|
||||
String sig = props.getProperty(PROP_SIG);
|
||||
if (sig == null)
|
||||
return false;
|
||||
buf.append(name);
|
||||
buf.append(KV_SEPARATOR);
|
||||
buf.append(dest);
|
||||
try {
|
||||
writeProps(buf, true, false);
|
||||
} catch (IOException ioe) {
|
||||
// won't happen
|
||||
return false;
|
||||
}
|
||||
byte[] sdata = Base64.decode(sig);
|
||||
if (sdata == null)
|
||||
return false;
|
||||
Destination d;
|
||||
try {
|
||||
d = new Destination(dest);
|
||||
} catch (DataFormatException dfe) {
|
||||
return false;
|
||||
}
|
||||
SigningPublicKey spk = d.getSigningPublicKey();
|
||||
SigType type = spk.getType();
|
||||
if (type == null)
|
||||
return false;
|
||||
Signature s;
|
||||
try {
|
||||
s = new Signature(type, sdata);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return false;
|
||||
}
|
||||
isValid = DSAEngine.getInstance().verifySignature(s, DataHelper.getUTF8(buf.toString()), spk);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify with the "olddest" property's public key using the "oldsig" property
|
||||
*/
|
||||
public boolean hasValidInnerSig() {
|
||||
if (props == null || name == null || dest == null)
|
||||
return false;
|
||||
boolean rv = false;
|
||||
// don't cache result
|
||||
if (true) {
|
||||
StringWriter buf = new StringWriter(1024);
|
||||
String sig = props.getProperty(PROP_OLDSIG);
|
||||
String olddest = props.getProperty(PROP_OLDDEST);
|
||||
if (sig == null || olddest == null)
|
||||
return false;
|
||||
buf.append(name);
|
||||
buf.append(KV_SEPARATOR);
|
||||
buf.append(dest);
|
||||
try {
|
||||
writeProps(buf, true, true);
|
||||
} catch (IOException ioe) {
|
||||
// won't happen
|
||||
return false;
|
||||
}
|
||||
byte[] sdata = Base64.decode(sig);
|
||||
if (sdata == null)
|
||||
return false;
|
||||
Destination d;
|
||||
try {
|
||||
d = new Destination(olddest);
|
||||
} catch (DataFormatException dfe) {
|
||||
return false;
|
||||
}
|
||||
SigningPublicKey spk = d.getSigningPublicKey();
|
||||
SigType type = spk.getType();
|
||||
if (type == null)
|
||||
return false;
|
||||
Signature s;
|
||||
try {
|
||||
s = new Signature(type, sdata);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return false;
|
||||
}
|
||||
rv = DSAEngine.getInstance().verifySignature(s, DataHelper.getUTF8(buf.toString()), spk);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify with the "dest" property's public key using the "sig" property
|
||||
*/
|
||||
public boolean hasValidRemoveSig() {
|
||||
if (props == null)
|
||||
return false;
|
||||
boolean rv = false;
|
||||
// don't cache result
|
||||
if (true) {
|
||||
StringWriter buf = new StringWriter(1024);
|
||||
String sig = props.getProperty(PROP_SIG);
|
||||
String olddest = props.getProperty(PROP_DEST);
|
||||
if (sig == null || olddest == null)
|
||||
return false;
|
||||
try {
|
||||
writeProps(buf, true, true);
|
||||
} catch (IOException ioe) {
|
||||
// won't happen
|
||||
return false;
|
||||
}
|
||||
byte[] sdata = Base64.decode(sig);
|
||||
if (sdata == null)
|
||||
return false;
|
||||
Destination d;
|
||||
try {
|
||||
d = new Destination(olddest);
|
||||
} catch (DataFormatException dfe) {
|
||||
return false;
|
||||
}
|
||||
SigningPublicKey spk = d.getSigningPublicKey();
|
||||
SigType type = spk.getType();
|
||||
if (type == null)
|
||||
return false;
|
||||
Signature s;
|
||||
try {
|
||||
s = new Signature(type, sdata);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return false;
|
||||
}
|
||||
rv = DSAEngine.getInstance().verifySignature(s, DataHelper.getUTF8(buf.toString()), spk);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return dest.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares Destination only, not properties
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof HostTxtEntry))
|
||||
return false;
|
||||
HostTxtEntry he = (HostTxtEntry) o;
|
||||
return dest.equals(he.getDest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign and set the "sig" property
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void sign(SigningPrivateKey spk) {
|
||||
signIt(spk, PROP_SIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign and set the "oldsig" property
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void signInner(SigningPrivateKey spk) {
|
||||
signIt(spk, PROP_OLDSIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void signRemove(SigningPrivateKey spk) {
|
||||
if (props == null)
|
||||
throw new IllegalStateException();
|
||||
if (props.containsKey(PROP_SIG))
|
||||
throw new IllegalStateException();
|
||||
props.setProperty(PROP_NAME, name);
|
||||
props.setProperty(PROP_DEST, dest);
|
||||
StringWriter buf = new StringWriter(1024);
|
||||
try {
|
||||
writeProps(buf);
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalStateException(ioe);
|
||||
}
|
||||
props.remove(PROP_NAME);
|
||||
props.remove(PROP_DEST);
|
||||
Signature s = DSAEngine.getInstance().sign(DataHelper.getUTF8(buf.toString()), spk);
|
||||
if (s == null)
|
||||
throw new IllegalArgumentException("sig failed");
|
||||
props.setProperty(PROP_SIG, s.toBase64());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sigprop The signature property to set
|
||||
*/
|
||||
private void signIt(SigningPrivateKey spk, String sigprop) {
|
||||
if (props == null)
|
||||
throw new IllegalStateException();
|
||||
if (props.containsKey(sigprop))
|
||||
throw new IllegalStateException();
|
||||
StringWriter buf = new StringWriter(1024);
|
||||
buf.append(name);
|
||||
buf.append(KV_SEPARATOR);
|
||||
buf.append(dest);
|
||||
try {
|
||||
writeProps(buf);
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalStateException(ioe);
|
||||
}
|
||||
Signature s = DSAEngine.getInstance().sign(DataHelper.getUTF8(buf.toString()), spk);
|
||||
if (s == null)
|
||||
throw new IllegalArgumentException("sig failed");
|
||||
props.setProperty(sigprop, s.toBase64());
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage: HostTxtEntry [-i] [-x] [hostname.i2p] [key=val]...
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
boolean inner = false;
|
||||
boolean remove = false;
|
||||
if (args.length > 0 && args[0].equals("-i")) {
|
||||
inner = true;
|
||||
args = Arrays.copyOfRange(args, 1, args.length);
|
||||
}
|
||||
if (args.length > 0 && args[0].equals("-x")) {
|
||||
remove = true;
|
||||
args = Arrays.copyOfRange(args, 1, args.length);
|
||||
}
|
||||
String host;
|
||||
if (args.length > 0 && args[0].endsWith(".i2p")) {
|
||||
host = args[0];
|
||||
args = Arrays.copyOfRange(args, 1, args.length);
|
||||
} else {
|
||||
byte[] rand = new byte[5];
|
||||
RandomSource.getInstance().nextBytes(rand);
|
||||
host = Base32.encode(rand) + ".i2p";
|
||||
}
|
||||
OrderedProperties props = new OrderedProperties();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
int eq = args[i].indexOf("=");
|
||||
props.setProperty(args[i].substring(0, eq), args[i].substring(eq + 1));
|
||||
}
|
||||
props.setProperty("zzzz", "zzzzzzzzzzzzzzz");
|
||||
// outer
|
||||
File f = new File("tmp-eepPriv.dat");
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(f);
|
||||
pkf.createIfAbsent(SigType.EdDSA_SHA512_Ed25519);
|
||||
//f.delete();
|
||||
PrivateKeyFile pkf2;
|
||||
if (inner) {
|
||||
// inner
|
||||
File f2 = new File("tmp-eepPriv2.dat");
|
||||
pkf2 = new PrivateKeyFile(f2);
|
||||
pkf2.createIfAbsent(SigType.DSA_SHA1);
|
||||
//f2.delete();
|
||||
props.setProperty(PROP_OLDDEST, pkf2.getDestination().toBase64());
|
||||
} else {
|
||||
pkf2 = null;
|
||||
}
|
||||
HostTxtEntry he = new HostTxtEntry(host, pkf.getDestination().toBase64(), props);
|
||||
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
|
||||
//out.write("Before signing:\n");
|
||||
//he.write(out);
|
||||
//out.flush();
|
||||
SigningPrivateKey priv = pkf.getSigningPrivKey();
|
||||
StringWriter sw = new StringWriter(1024);
|
||||
BufferedWriter buf = new BufferedWriter(sw);
|
||||
if (!remove) {
|
||||
if (inner) {
|
||||
SigningPrivateKey priv2 = pkf2.getSigningPrivKey();
|
||||
he.signInner(priv2);
|
||||
//out.write("After signing inner:\n");
|
||||
//he.write(out);
|
||||
}
|
||||
he.sign(priv);
|
||||
//out.write("After signing:\n");
|
||||
he.write(out);
|
||||
out.flush();
|
||||
if (inner && !he.hasValidInnerSig())
|
||||
throw new IllegalStateException("Inner fail 1");
|
||||
if (!he.hasValidSig())
|
||||
throw new IllegalStateException("Outer fail 1");
|
||||
// now create 2nd, read in
|
||||
he.write(buf);
|
||||
buf.flush();
|
||||
String line = sw.toString();
|
||||
line = line.substring(line.indexOf(PROPS_SEPARATOR) + 2);
|
||||
HostTxtEntry he2 = new HostTxtEntry(host, pkf.getDestination().toBase64(), line);
|
||||
if (inner && !he2.hasValidInnerSig())
|
||||
throw new IllegalStateException("Inner fail 2");
|
||||
if (!he2.hasValidSig())
|
||||
throw new IllegalStateException("Outer fail 2");
|
||||
} else {
|
||||
// 'remove' tests (corrupts earlier sigs)
|
||||
he.getProps().remove(PROP_SIG);
|
||||
he.signRemove(priv);
|
||||
//out.write("Remove entry:\n");
|
||||
sw = new StringWriter(1024);
|
||||
buf = new BufferedWriter(sw);
|
||||
he.writeRemoveLine(buf);
|
||||
buf.flush();
|
||||
out.write(sw.toString());
|
||||
out.flush();
|
||||
String line = sw.toString().substring(2).trim();
|
||||
HostTxtEntry he3 = new HostTxtEntry(line);
|
||||
if (!he3.hasValidRemoveSig())
|
||||
throw new IllegalStateException("Remove verify fail");
|
||||
}
|
||||
|
||||
//out.write("Test passed\n");
|
||||
//out.flush();
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,8 @@ public abstract class NamingService {
|
||||
|
||||
/**
|
||||
* Reverse lookup a destination
|
||||
* This implementation returns reverseLookup(dest, null).
|
||||
*
|
||||
* @param dest non-null
|
||||
* @return a host name for this Destination, or <code>null</code>
|
||||
* if none is known. It is safe for subclasses to always return
|
||||
@@ -74,7 +76,10 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse lookup a hash
|
||||
* Reverse lookup a hash.
|
||||
* This implementation returns null.
|
||||
* Subclasses implementing reverse lookups should override.
|
||||
*
|
||||
* @param h non-null
|
||||
* @return a host name for this hash, or <code>null</code>
|
||||
* if none is known. It is safe for subclasses to always return
|
||||
@@ -83,8 +88,8 @@ public abstract class NamingService {
|
||||
public String reverseLookup(Hash h) { return null; }
|
||||
|
||||
/**
|
||||
* Check if host name is valid Base64 encoded dest and return this
|
||||
* dest in that case. Useful as a "fallback" in custom naming
|
||||
* If the host name is a valid Base64 encoded destination, return the
|
||||
* decoded Destination. Useful as a "fallback" in custom naming
|
||||
* implementations.
|
||||
* This is misnamed as it isn't a "lookup" at all, but
|
||||
* a simple conversion from a Base64 string to a Destination.
|
||||
@@ -120,6 +125,7 @@ public abstract class NamingService {
|
||||
|
||||
/**
|
||||
* Warning - unimplemented in any subclass.
|
||||
* Returns null always.
|
||||
*
|
||||
* @return NamingService-specific options or null
|
||||
* @since 0.8.7
|
||||
@@ -130,6 +136,7 @@ public abstract class NamingService {
|
||||
|
||||
/**
|
||||
* Warning - unimplemented in any subclass.
|
||||
* Returns true always.
|
||||
*
|
||||
* @return success
|
||||
* @since 0.8.7
|
||||
@@ -141,6 +148,9 @@ public abstract class NamingService {
|
||||
// These are for daisy chaining (MetaNamingService)
|
||||
|
||||
/**
|
||||
* This implementation returns null.
|
||||
* Subclasses implementing chaining should override.
|
||||
*
|
||||
* @return chained naming services or null
|
||||
* @since 0.8.7
|
||||
*/
|
||||
@@ -149,6 +159,9 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns null.
|
||||
* Subclasses implementing chaining should override.
|
||||
*
|
||||
* @return parent naming service or null if this is the root
|
||||
* @since 0.8.7
|
||||
*/
|
||||
@@ -167,7 +180,10 @@ public abstract class NamingService {
|
||||
|
||||
|
||||
/**
|
||||
* Only for chaining-capable NamingServices
|
||||
* Only for chaining-capable NamingServices.
|
||||
* This implementation returns false.
|
||||
* Subclasses implementing chaining should override.
|
||||
*
|
||||
* @param head or tail
|
||||
* @return success
|
||||
* @since 0.8.7
|
||||
@@ -177,7 +193,10 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for chaining-capable NamingServices
|
||||
* Only for chaining-capable NamingServices.
|
||||
* This implementation returns false.
|
||||
* Subclasses implementing chaining should override.
|
||||
*
|
||||
* @return success
|
||||
* @since 0.8.7
|
||||
*/
|
||||
@@ -198,6 +217,9 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns -1.
|
||||
* Most subclasses should override.
|
||||
*
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return number of entries (matching the options if non-null) or -1 if unknown
|
||||
* @since 0.8.7
|
||||
@@ -340,6 +362,10 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hostname and Destination to the addressbook.
|
||||
* Overwrites old entry if it exists.
|
||||
* See also putIfAbsent() and update().
|
||||
*
|
||||
* @return success
|
||||
* @since 0.8.7
|
||||
*/
|
||||
@@ -348,6 +374,10 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hostname and Destination to the addressbook.
|
||||
* Overwrites old entry if it exists.
|
||||
* See also putIfAbsent() and update().
|
||||
*
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return success
|
||||
* @since 0.8.7
|
||||
@@ -357,7 +387,10 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails if entry previously exists
|
||||
* Add a hostname and Destination to the addressbook.
|
||||
* Fails if entry previously exists.
|
||||
* See also put() and update().
|
||||
*
|
||||
* @return success
|
||||
* @since 0.8.7
|
||||
*/
|
||||
@@ -366,7 +399,10 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails if entry previously exists
|
||||
* Add a hostname and Destination to the addressbook.
|
||||
* Fails if entry previously exists.
|
||||
* See also put() and update().
|
||||
*
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return success
|
||||
* @since 0.8.7
|
||||
@@ -376,8 +412,12 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all the entries, each with the given options.
|
||||
* This implementation calls put() for each entry.
|
||||
* Subclasses may override if a more efficient implementation is available.
|
||||
*
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return success
|
||||
* @return total success, or false if any put failed
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public boolean putAll(Map<String, Destination> entries, Properties options) {
|
||||
@@ -392,6 +432,7 @@ public abstract class NamingService {
|
||||
/**
|
||||
* Fails if entry did not previously exist.
|
||||
* Warning - unimplemented in any subclass.
|
||||
* This implementation returns false.
|
||||
*
|
||||
* @param d may be null if only options are changing
|
||||
* @param options NamingService-specific, can be null
|
||||
@@ -403,16 +444,18 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return success
|
||||
* Delete the entry.
|
||||
* @return true if removed successfully, false on error or if it did not exist
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public boolean remove(String hostname) {
|
||||
return remove(hostname, null);
|
||||
return remove(hostname, (Properties) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the entry.
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return success
|
||||
* @return true if removed successfully, false on error or if it did not exist
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public boolean remove(String hostname, Properties options) {
|
||||
@@ -460,8 +503,9 @@ public abstract class NamingService {
|
||||
|
||||
/**
|
||||
* Same as lookup(hostname) but with in and out options
|
||||
* Note that whether this (and lookup(hostname)) resolve B32 addresses is
|
||||
* NamingService-specific.
|
||||
* Note that whether this (and lookup(hostname)) resolve Base 32 addresses
|
||||
* in the form {52 chars}.b32.i2p is NamingService-specific.
|
||||
*
|
||||
* @param lookupOptions input parameter, NamingService-specific, can be null
|
||||
* @param storedOptions output parameter, NamingService-specific, any stored properties will be added if non-null
|
||||
* @return dest or null
|
||||
@@ -471,6 +515,9 @@ public abstract class NamingService {
|
||||
|
||||
/**
|
||||
* Same as reverseLookup(dest) but with options
|
||||
* This implementation returns null.
|
||||
* Subclasses implementing reverse lookups should override.
|
||||
*
|
||||
* @param d non-null
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return host name or null
|
||||
@@ -483,6 +530,9 @@ public abstract class NamingService {
|
||||
/**
|
||||
* Lookup a Base 32 address. This may require the router to fetch the LeaseSet,
|
||||
* which may take quite a while.
|
||||
* This implementation returns null.
|
||||
* See also lookup(Hash, int).
|
||||
*
|
||||
* @param hostname must be {52 chars}.b32.i2p
|
||||
* @param timeout in seconds; <= 0 means use router default
|
||||
* @return dest or null
|
||||
@@ -493,7 +543,9 @@ public abstract class NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as lookupB32 but with the SHA256 Hash precalculated
|
||||
* Same as lookupBase32() but with the SHA256 Hash precalculated
|
||||
* This implementation returns null.
|
||||
*
|
||||
* @param timeout in seconds; <= 0 means use router default
|
||||
* @return dest or null
|
||||
* @since 0.8.7
|
||||
@@ -520,6 +572,172 @@ public abstract class NamingService {
|
||||
|
||||
//// End New API
|
||||
|
||||
//// Begin new API for multiple Destinations
|
||||
|
||||
/**
|
||||
* For NamingServices that support multiple Destinations for a single host name,
|
||||
* return all of them.
|
||||
*
|
||||
* It is recommended that the returned list is in order of priority, highest-first,
|
||||
* but this is NamingService-specific.
|
||||
*
|
||||
* Not recommended for resolving Base 32 addresses;
|
||||
* whether this does resolve Base 32 addresses
|
||||
* in the form {52 chars}.b32.i2p is NamingService-specific.
|
||||
*
|
||||
* @return non-empty List of Destinations, or null if nothing found
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public List<Destination> lookupAll(String hostname) {
|
||||
return lookupAll(hostname, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For NamingServices that support multiple Destinations and Properties for a single host name,
|
||||
* return all of them.
|
||||
*
|
||||
* It is recommended that the returned list is in order of priority, highest-first,
|
||||
* but this is NamingService-specific.
|
||||
*
|
||||
* If storedOptions is non-null, it must be a List that supports null entries.
|
||||
* If the returned value (the List of Destinations) is non-null,
|
||||
* the same number of Properties objects will be added to storedOptions.
|
||||
* If no properties were found for a given Destination, the corresponding
|
||||
* entry in the storedOptions list will be null.
|
||||
*
|
||||
* Not recommended for resolving Base 32 addresses;
|
||||
* whether this does resolve Base 32 addresses
|
||||
* in the form {52 chars}.b32.i2p is NamingService-specific.
|
||||
*
|
||||
* This implementation simply calls lookup().
|
||||
* Subclasses implementing multiple destinations per hostname should override.
|
||||
*
|
||||
* @param lookupOptions input parameter, NamingService-specific, may be null
|
||||
* @param storedOptions output parameter, NamingService-specific, any stored properties will be added if non-null
|
||||
* @return non-empty List of Destinations, or null if nothing found
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public List<Destination> lookupAll(String hostname, Properties lookupOptions, List<Properties> storedOptions) {
|
||||
Properties props = storedOptions != null ? new Properties() : null;
|
||||
Destination d = lookup(hostname, lookupOptions, props);
|
||||
List<Destination> rv;
|
||||
if (d != null) {
|
||||
rv = Collections.singletonList(d);
|
||||
if (storedOptions != null)
|
||||
storedOptions.add(props.isEmpty() ? null : props);
|
||||
} else {
|
||||
rv = null;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Destination to an existing hostname's entry in the addressbook.
|
||||
*
|
||||
* @return success
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean addDestination(String hostname, Destination d) {
|
||||
return addDestination(hostname, d, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Destination to an existing hostname's entry in the addressbook.
|
||||
* This implementation simply calls putIfAbsent().
|
||||
* Subclasses implementing multiple destinations per hostname should override.
|
||||
*
|
||||
* @param options NamingService-specific, may be null
|
||||
* @return success
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean addDestination(String hostname, Destination d, Properties options) {
|
||||
return putIfAbsent(hostname, d, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a hostname's entry only if it contains the Destination d.
|
||||
* If the NamingService supports multiple Destinations per hostname,
|
||||
* and this is the only Destination, removes the entire entry.
|
||||
* If aditional Destinations remain, it only removes the
|
||||
* specified Destination from the entry.
|
||||
*
|
||||
* @return true if entry containing d was successfully removed.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean remove(String hostname, Destination d) {
|
||||
return remove(hostname, d, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a hostname's entry only if it contains the Destination d.
|
||||
* If the NamingService supports multiple Destinations per hostname,
|
||||
* and this is the only Destination, removes the entire entry.
|
||||
* If aditional Destinations remain, it only removes the
|
||||
* specified Destination from the entry.
|
||||
*
|
||||
* This implementation simply calls lookup() and remove().
|
||||
* Subclasses implementing multiple destinations per hostname,
|
||||
* or with more efficient implementations, should override.
|
||||
* Fails if entry previously exists.
|
||||
*
|
||||
* @param options NamingService-specific, may be null
|
||||
* @return true if entry containing d was successfully removed.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public boolean remove(String hostname, Destination d, Properties options) {
|
||||
Destination old = lookup(hostname, options, null);
|
||||
if (!d.equals(old))
|
||||
return false;
|
||||
return remove(hostname, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse lookup a hash.
|
||||
* This implementation returns the result from reverseLookup, or null.
|
||||
* Subclasses implementing reverse lookups should override.
|
||||
*
|
||||
* @param h non-null
|
||||
* @return a non-empty list of host names for this hash, or <code>null</code>
|
||||
* if none is known. It is safe for subclasses to always return
|
||||
* <code>null</code> if no reverse lookup is possible.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public List<String> reverseLookupAll(Hash h) {
|
||||
String s = reverseLookup(h);
|
||||
return (s != null) ? Collections.singletonList(s) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse lookup a destination
|
||||
* This implementation returns reverseLookupAll(dest, null).
|
||||
*
|
||||
* @param dest non-null
|
||||
* @return a non-empty list of host names for this Destination, or <code>null</code>
|
||||
* if none is known. It is safe for subclasses to always return
|
||||
* <code>null</code> if no reverse lookup is possible.
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public List<String> reverseLookupAll(Destination dest) {
|
||||
return reverseLookupAll(dest, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as reverseLookupAll(dest) but with options
|
||||
* This implementation returns the result from reverseLookup, or null.
|
||||
* Subclasses implementing reverse lookups should override.
|
||||
*
|
||||
* @param d non-null
|
||||
* @param options NamingService-specific, can be null
|
||||
* @return a non-empty list of host names for this Destination, or <code>null</code>
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public List<String> reverseLookupAll(Destination d, Properties options) {
|
||||
String s = reverseLookup(d, options);
|
||||
return (s != null) ? Collections.singletonList(s) : null;
|
||||
}
|
||||
|
||||
//// End new API for multiple Destinations
|
||||
|
||||
/**
|
||||
* WARNING - for use by I2PAppContext only - others must use
|
||||
* I2PAppContext.namingService()
|
||||
|
||||
@@ -165,7 +165,8 @@ public class SingleFileNamingService extends NamingService {
|
||||
|
||||
/**
|
||||
* @param hostname case-sensitive; caller should convert to lower case
|
||||
* @param options ignored
|
||||
* @param options if non-null, any prefixed with '=' will be appended
|
||||
* in subscription format
|
||||
*/
|
||||
@Override
|
||||
public boolean put(String hostname, Destination d, Properties options) {
|
||||
@@ -196,6 +197,9 @@ public class SingleFileNamingService extends NamingService {
|
||||
out.write(hostname);
|
||||
out.write('=');
|
||||
out.write(d.toBase64());
|
||||
// subscription options
|
||||
if (options != null)
|
||||
writeOptions(options, out);
|
||||
out.newLine();
|
||||
out.close();
|
||||
boolean success = FileUtil.rename(tmp, _file);
|
||||
@@ -215,11 +219,12 @@ public class SingleFileNamingService extends NamingService {
|
||||
|
||||
/**
|
||||
* @param hostname case-sensitive; caller should convert to lower case
|
||||
* @param options ignored
|
||||
* @param options if non-null, any prefixed with '=' will be appended
|
||||
* in subscription format
|
||||
*/
|
||||
@Override
|
||||
public boolean putIfAbsent(String hostname, Destination d, Properties options) {
|
||||
OutputStream out = null;
|
||||
BufferedWriter out = null;
|
||||
if (!getWriteLock())
|
||||
return false;
|
||||
try {
|
||||
@@ -236,11 +241,14 @@ public class SingleFileNamingService extends NamingService {
|
||||
}
|
||||
// else new file
|
||||
}
|
||||
out = new SecureFileOutputStream(_file, true);
|
||||
out = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(_file, true), "UTF-8"));
|
||||
// FIXME fails if previous last line didn't have a trailing \n
|
||||
out.write(hostname.getBytes("UTF-8"));
|
||||
out.write(hostname);
|
||||
out.write('=');
|
||||
out.write(DataHelper.getASCII(d.toBase64()));
|
||||
out.write(d.toBase64());
|
||||
// subscription options
|
||||
if (options != null)
|
||||
writeOptions(options, out);
|
||||
out.write('\n');
|
||||
out.close();
|
||||
for (NamingServiceListener nsl : _listeners) {
|
||||
@@ -254,6 +262,34 @@ public class SingleFileNamingService extends NamingService {
|
||||
} finally { releaseWriteLock(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the subscription options part of the line (including the #!).
|
||||
* Only options starting with '=' (if any) are written (with the '=' stripped).
|
||||
* Does not write a newline.
|
||||
*
|
||||
* @param options non-null
|
||||
* @since 0.9.26
|
||||
*/
|
||||
private static void writeOptions(Properties options, Writer out) throws IOException {
|
||||
boolean started = false;
|
||||
for (Map.Entry<Object, Object> e : options.entrySet()) {
|
||||
String k = (String) e.getKey();
|
||||
if (!k.startsWith("="))
|
||||
continue;
|
||||
k = k.substring(1);
|
||||
String v = (String) e.getValue();
|
||||
if (started) {
|
||||
out.write(HostTxtEntry.PROP_SEPARATOR);
|
||||
} else {
|
||||
started = true;
|
||||
out.write(HostTxtEntry.PROPS_SEPARATOR);
|
||||
}
|
||||
out.write(k);
|
||||
out.write('=');
|
||||
out.write(v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param hostname case-sensitive; caller should convert to lower case
|
||||
* @param options ignored
|
||||
|
||||
@@ -7,6 +7,9 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
@@ -31,9 +34,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.naming.InvalidNameException;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import javax.naming.ldap.Rdn;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -199,13 +199,25 @@ public final class CertUtil {
|
||||
return null;
|
||||
type = type.toUpperCase(Locale.US);
|
||||
String subj = p.getName();
|
||||
// Use reflection for this to avoid VerifyErrors on some Androids
|
||||
try {
|
||||
LdapName name = new LdapName(subj);
|
||||
for (Rdn rdn : name.getRdns()) {
|
||||
if (type.equals(rdn.getType().toUpperCase(Locale.US)))
|
||||
return (String) rdn.getValue();
|
||||
Class<?> ldapName = Class.forName("javax.naming.ldap.LdapName");
|
||||
Constructor<?> ldapCtor = ldapName.getConstructor(String.class);
|
||||
Object name = ldapCtor.newInstance(subj);
|
||||
Method getRdns = ldapName.getDeclaredMethod("getRdns");
|
||||
Class<?> rdnClass = Class.forName("javax.naming.ldap.Rdn");
|
||||
Method getType = rdnClass.getDeclaredMethod("getType");
|
||||
Method getValue = rdnClass.getDeclaredMethod("getValue");
|
||||
for (Object rdn : (List) getRdns.invoke(name)) {
|
||||
if (type.equals(((String) getType.invoke(rdn)).toUpperCase(Locale.US)))
|
||||
return (String) getValue.invoke(rdn);
|
||||
}
|
||||
} catch (InvalidNameException ine) {}
|
||||
} catch (ClassNotFoundException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (InstantiationException e) {
|
||||
} catch (InvocationTargetException e) {
|
||||
} catch (NoSuchMethodException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -515,9 +527,9 @@ public final class CertUtil {
|
||||
* Load a CRL. Does NOT Close the stream.
|
||||
*
|
||||
* @return non-null
|
||||
* @since 0.9.25
|
||||
* @since 0.9.25 public since 0.9.26
|
||||
*/
|
||||
private static X509CRL loadCRL(InputStream in) throws GeneralSecurityException {
|
||||
public static X509CRL loadCRL(InputStream in) throws GeneralSecurityException {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
return (X509CRL) cf.generateCRL(in);
|
||||
}
|
||||
|
||||
@@ -45,14 +45,14 @@ import net.i2p.util.NativeBigInteger;
|
||||
*
|
||||
*/
|
||||
public final class CryptoConstants {
|
||||
public static final BigInteger dsap = new NativeBigInteger(
|
||||
public static final NativeBigInteger dsap = new NativeBigInteger(
|
||||
"9c05b2aa960d9b97b8931963c9cc9e8c3026e9b8ed92fad0a69cc886d5bf8015fcadae31"
|
||||
+ "a0ad18fab3f01b00a358de237655c4964afaa2b337e96ad316b9fb1cc564b5aec5b69a9f"
|
||||
+ "f6c3e4548707fef8503d91dd8602e867e6d35d2235c1869ce2479c3b9d5401de04e0727f"
|
||||
+ "b33d6511285d4cf29538d9e3b6051f5b22cc1c93",
|
||||
16);
|
||||
public static final BigInteger dsaq = new NativeBigInteger("a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16);
|
||||
public static final BigInteger dsag = new NativeBigInteger(
|
||||
public static final NativeBigInteger dsaq = new NativeBigInteger("a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16);
|
||||
public static final NativeBigInteger dsag = new NativeBigInteger(
|
||||
"c1f4d27d40093b429e962d7223824e0bbc47e7c832a39236fc683af84889581075ff9082"
|
||||
+ "ed32353d4374d7301cda1d23c431f4698599dda02451824ff369752593647cc3ddc197de"
|
||||
+ "985e43d136cdcfc6bd5409cd2f450821142a5e6f8eb1c3ab5d0484b8129fcf17bce4f7f3"
|
||||
@@ -60,7 +60,7 @@ public final class CryptoConstants {
|
||||
16);
|
||||
|
||||
/** 2048-bit MODP Group from RFC 3526 */
|
||||
public static final BigInteger elgp = new NativeBigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
|
||||
public static final NativeBigInteger elgp = new NativeBigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
|
||||
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
|
||||
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
|
||||
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
|
||||
@@ -72,7 +72,7 @@ public final class CryptoConstants {
|
||||
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
|
||||
+ "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
|
||||
|
||||
public static final BigInteger elgg = new NativeBigInteger("2");
|
||||
public static final NativeBigInteger elgg = new NativeBigInteger("2");
|
||||
|
||||
/**
|
||||
* @since 0.9.9
|
||||
|
||||
@@ -404,13 +404,13 @@ public final class DSAEngine {
|
||||
BigInteger k;
|
||||
boolean ok;
|
||||
do {
|
||||
k = new BigInteger(160, _context.random());
|
||||
k = new NativeBigInteger(160, _context.random());
|
||||
ok = k.compareTo(CryptoConstants.dsaq) != 1;
|
||||
ok = ok && !k.equals(BigInteger.ZERO);
|
||||
//System.out.println("K picked (ok? " + ok + "): " + k.bitLength() + ": " + k.toString());
|
||||
} while (!ok);
|
||||
|
||||
BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
BigInteger r = CryptoConstants.dsag.modPowCT(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
BigInteger kinv = k.modInverse(CryptoConstants.dsaq);
|
||||
|
||||
BigInteger M = new NativeBigInteger(1, hash.getData());
|
||||
|
||||
@@ -52,7 +52,10 @@ final class ECUtil {
|
||||
else if (s.equals(ECPoint.POINT_INFINITY))
|
||||
return r;
|
||||
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
|
||||
BigInteger slope = (r.getAffineY().subtract(s.getAffineY())).multiply(r.getAffineX().subtract(s.getAffineX()).modInverse(prime)).mod(prime);
|
||||
// use NBI modInverse();
|
||||
BigInteger tmp = r.getAffineX().subtract(s.getAffineX());
|
||||
tmp = new NativeBigInteger(tmp);
|
||||
BigInteger slope = (r.getAffineY().subtract(s.getAffineY())).multiply(tmp.modInverse(prime)).mod(prime);
|
||||
slope = new NativeBigInteger(slope);
|
||||
BigInteger xOut = (slope.modPow(TWO, prime).subtract(r.getAffineX())).subtract(s.getAffineX()).mod(prime);
|
||||
BigInteger yOut = s.getAffineY().negate().mod(prime);
|
||||
@@ -67,7 +70,10 @@ final class ECUtil {
|
||||
BigInteger slope = (r.getAffineX().pow(2)).multiply(THREE);
|
||||
slope = slope.add(curve.getA());
|
||||
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
|
||||
slope = slope.multiply((r.getAffineY().multiply(TWO)).modInverse(prime));
|
||||
// use NBI modInverse();
|
||||
BigInteger tmp = r.getAffineY().multiply(TWO);
|
||||
tmp = new NativeBigInteger(tmp);
|
||||
slope = slope.multiply(tmp.modInverse(prime));
|
||||
BigInteger xOut = slope.pow(2).subtract(r.getAffineX().multiply(TWO)).mod(prime);
|
||||
BigInteger yOut = (r.getAffineY().negate()).add(slope.multiply(r.getAffineX().subtract(xOut))).mod(prime);
|
||||
ECPoint out = new ECPoint(xOut, yOut);
|
||||
|
||||
@@ -197,8 +197,8 @@ public final class ElGamalEngine {
|
||||
// we use this buf first for Y, then for D, then for the hash
|
||||
byte[] buf = SimpleByteCache.acquire(257);
|
||||
System.arraycopy(encrypted, 0, buf, 0, 257);
|
||||
BigInteger y = new NativeBigInteger(1, buf);
|
||||
BigInteger ya = y.modPow(y1p, CryptoConstants.elgp);
|
||||
NativeBigInteger y = new NativeBigInteger(1, buf);
|
||||
BigInteger ya = y.modPowCT(y1p, CryptoConstants.elgp);
|
||||
System.arraycopy(encrypted, 257, buf, 0, 257);
|
||||
BigInteger d = new NativeBigInteger(1, buf);
|
||||
BigInteger m = ya.multiply(d);
|
||||
|
||||
@@ -56,8 +56,6 @@ import net.i2p.util.Translate;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DataHelper {
|
||||
private static final byte[] EQUAL_BYTES = getUTF8("=");
|
||||
private static final byte[] SEMICOLON_BYTES = getUTF8(";");
|
||||
|
||||
/**
|
||||
* Map of String to itself to cache common
|
||||
@@ -91,7 +89,7 @@ public class DataHelper {
|
||||
"family", "family.key", "family.sig",
|
||||
// BlockfileNamingService
|
||||
"version", "created", "upgraded", "lists",
|
||||
"a", "s",
|
||||
"a", "m", "s", "v"
|
||||
};
|
||||
_propertiesKeyCache = new HashMap<String, String>(keys.length);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
@@ -148,22 +146,18 @@ public class DataHelper {
|
||||
int read = read(rawStream, data);
|
||||
if (read != size) throw new DataFormatException("Not enough data to read the properties, expected " + size + " but got " + read);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(data);
|
||||
byte eqBuf[] = new byte[EQUAL_BYTES.length];
|
||||
byte semiBuf[] = new byte[SEMICOLON_BYTES.length];
|
||||
while (in.available() > 0) {
|
||||
String key = readString(in);
|
||||
String cached = _propertiesKeyCache.get(key);
|
||||
if (cached != null)
|
||||
key = cached;
|
||||
read = read(in, eqBuf);
|
||||
if ((read != eqBuf.length) || (!eq(eqBuf, EQUAL_BYTES))) {
|
||||
int b = in.read();
|
||||
if (b != '=')
|
||||
throw new DataFormatException("Bad key");
|
||||
}
|
||||
String val = readString(in);
|
||||
read = read(in, semiBuf);
|
||||
if ((read != semiBuf.length) || (!eq(semiBuf, SEMICOLON_BYTES))) {
|
||||
b = in.read();
|
||||
if (b != ';')
|
||||
throw new DataFormatException("Bad value");
|
||||
}
|
||||
Object old = props.put(key, val);
|
||||
if (old != null)
|
||||
throw new DataFormatException("Duplicate key " + key);
|
||||
@@ -182,7 +176,7 @@ public class DataHelper {
|
||||
* Properties from the defaults table of props (if any) are not written out by this method.
|
||||
*
|
||||
* @param rawStream stream to write to
|
||||
* @param props properties to write out
|
||||
* @param props properties to write out, may be null
|
||||
* @throws DataFormatException if there is not enough valid data to write out,
|
||||
* or a length limit is exceeded
|
||||
* @throws IOException if there is an IO error writing out the data
|
||||
@@ -239,7 +233,7 @@ public class DataHelper {
|
||||
*/
|
||||
public static void writeProperties(OutputStream rawStream, Properties props, boolean utf8, boolean sort)
|
||||
throws DataFormatException, IOException {
|
||||
if (props != null) {
|
||||
if (props != null && !props.isEmpty()) {
|
||||
Properties p;
|
||||
if (sort) {
|
||||
p = new OrderedProperties();
|
||||
@@ -255,12 +249,12 @@ public class DataHelper {
|
||||
writeStringUTF8(baos, key);
|
||||
else
|
||||
writeString(baos, key);
|
||||
baos.write(EQUAL_BYTES);
|
||||
baos.write('=');
|
||||
if (utf8)
|
||||
writeStringUTF8(baos, val);
|
||||
else
|
||||
writeString(baos, val);
|
||||
baos.write(SEMICOLON_BYTES);
|
||||
baos.write(';');
|
||||
}
|
||||
if (baos.size() > 65535)
|
||||
throw new DataFormatException("Properties too big (65535 max): " + baos.size());
|
||||
@@ -301,9 +295,9 @@ public class DataHelper {
|
||||
String key = (String) entry.getKey();
|
||||
String val = (String) entry.getValue();
|
||||
writeStringUTF8(baos, key);
|
||||
baos.write(EQUAL_BYTES);
|
||||
baos.write('=');
|
||||
writeStringUTF8(baos, val);
|
||||
baos.write(SEMICOLON_BYTES);
|
||||
baos.write(';');
|
||||
}
|
||||
if (baos.size() > 65535)
|
||||
throw new DataFormatException("Properties too big (65535 max): " + baos.size());
|
||||
@@ -335,8 +329,6 @@ public class DataHelper {
|
||||
int size = (int)fromLong(source, offset, 2);
|
||||
offset += 2;
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(source, offset, size);
|
||||
byte eqBuf[] = new byte[EQUAL_BYTES.length];
|
||||
byte semiBuf[] = new byte[SEMICOLON_BYTES.length];
|
||||
while (in.available() > 0) {
|
||||
String key;
|
||||
try {
|
||||
@@ -344,20 +336,18 @@ public class DataHelper {
|
||||
String cached = _propertiesKeyCache.get(key);
|
||||
if (cached != null)
|
||||
key = cached;
|
||||
int read = read(in, eqBuf);
|
||||
if ((read != eqBuf.length) || (!eq(eqBuf, EQUAL_BYTES))) {
|
||||
int b = in.read();
|
||||
if (b != '=')
|
||||
throw new DataFormatException("Bad key");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new DataFormatException("Bad key", ioe);
|
||||
}
|
||||
String val;
|
||||
try {
|
||||
val = readString(in);
|
||||
int read = read(in, semiBuf);
|
||||
if ((read != semiBuf.length) || (!eq(semiBuf, SEMICOLON_BYTES))) {
|
||||
int b = in.read();
|
||||
if (b != ';')
|
||||
throw new DataFormatException("Bad value");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new DataFormatException("Bad value", ioe);
|
||||
}
|
||||
@@ -910,8 +900,9 @@ public class DataHelper {
|
||||
* cause a DataFormatException to be thrown
|
||||
* @throws DataFormatException if the string is not valid
|
||||
* @throws IOException if there is an IO error writing the string
|
||||
* @since public since 0.9.26
|
||||
*/
|
||||
private static void writeStringUTF8(OutputStream out, String string)
|
||||
public static void writeStringUTF8(OutputStream out, String string)
|
||||
throws DataFormatException, IOException {
|
||||
if (string == null) {
|
||||
out.write((byte) 0);
|
||||
@@ -936,6 +927,7 @@ public class DataHelper {
|
||||
* @return boolean value, or null
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public static Boolean readBoolean(InputStream in) throws DataFormatException, IOException {
|
||||
int val = in.read();
|
||||
switch (val) {
|
||||
@@ -1881,7 +1873,9 @@ public class DataHelper {
|
||||
* Roughly the same as orig.getBytes("ISO-8859-1") but much faster and
|
||||
* will not throw an exception.
|
||||
*
|
||||
* @param orig non-null, must be 7-bit chars
|
||||
* Warning - misnamed, converts to ISO-8859-1.
|
||||
*
|
||||
* @param orig non-null, truncates to 8-bit chars
|
||||
* @since 0.9.5
|
||||
*/
|
||||
public static byte[] getASCII(String orig) {
|
||||
|
||||
@@ -74,10 +74,14 @@ public class PrivateKeyFile {
|
||||
String stype = null;
|
||||
int mode = 0;
|
||||
boolean error = false;
|
||||
Getopt g = new Getopt("pkf", args, "t:nuxhse:");
|
||||
Getopt g = new Getopt("pkf", args, "t:nuxhse:c:");
|
||||
int c;
|
||||
while ((c = g.getopt()) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
stype = g.getOptarg();
|
||||
break;
|
||||
|
||||
case 't':
|
||||
stype = g.getOptarg();
|
||||
// fall thru...
|
||||
@@ -117,9 +121,21 @@ public class PrivateKeyFile {
|
||||
|
||||
try {
|
||||
File f = new File(filearg);
|
||||
boolean exists = f.exists();
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(f, client);
|
||||
Destination d = pkf.createIfAbsent();
|
||||
System.out.println("Original Destination:");
|
||||
Destination d;
|
||||
if (stype != null) {
|
||||
SigType type = SigType.parseSigType(stype);
|
||||
if (type == null)
|
||||
throw new IllegalArgumentException("Signature type " + stype + " is not supported");
|
||||
d = pkf.createIfAbsent(type);
|
||||
} else {
|
||||
d = pkf.createIfAbsent();
|
||||
}
|
||||
if (exists)
|
||||
System.out.println("Original Destination:");
|
||||
else
|
||||
System.out.println("Created Destination:");
|
||||
System.out.println(pkf);
|
||||
verifySignature(d);
|
||||
switch (mode) {
|
||||
@@ -185,12 +201,12 @@ public class PrivateKeyFile {
|
||||
}
|
||||
|
||||
private static void usage() {
|
||||
System.err.println("Usage: PrivateKeyFile filename (generates if nonexistent, then prints)\n" +
|
||||
System.err.println("Usage: PrivateKeyFile [-c sigtype] filename (generates if nonexistent, then prints)\n" +
|
||||
" PrivateKeyFile -h filename (generates if nonexistent, adds hashcash cert)\n" +
|
||||
" PrivateKeyFile -h -e effort filename (specify HashCash effort instead of default " + HASH_EFFORT + ")\n" +
|
||||
" PrivateKeyFile -n filename (changes to null cert)\n" +
|
||||
" PrivateKeyFile -s filename signwithdestfile (generates if nonexistent, adds cert signed by 2nd dest)\n" +
|
||||
" PrivateKeyFile -t sigtype filename (changes to KeyCertificate of the given sig type\n" +
|
||||
" PrivateKeyFile -t sigtype filename (changes to KeyCertificate of the given sig type)\n" +
|
||||
" PrivateKeyFile -u filename (changes to unknown cert)\n" +
|
||||
" PrivateKeyFile -x filename (changes to hidden cert)\n");
|
||||
}
|
||||
@@ -257,16 +273,43 @@ public class PrivateKeyFile {
|
||||
this.signingPrivKey = spk;
|
||||
}
|
||||
|
||||
/** Also reads in the file to get the privKey and signingPrivKey,
|
||||
/**
|
||||
* Can't be used for writing
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public PrivateKeyFile(InputStream in) throws I2PSessionException {
|
||||
this("/dev/null");
|
||||
I2PSession s = this.client.createSession(in, new Properties());
|
||||
this.dest = s.getMyDestination();
|
||||
this.privKey = s.getDecryptionKey();
|
||||
this.signingPrivKey = s.getPrivateKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with the default signature type if nonexistent.
|
||||
*
|
||||
* Also reads in the file to get the privKey and signingPrivKey,
|
||||
* which aren't available from I2PClient.
|
||||
*/
|
||||
public Destination createIfAbsent() throws I2PException, IOException, DataFormatException {
|
||||
return createIfAbsent(I2PClient.DEFAULT_SIGTYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with the specified signature type if nonexistent.
|
||||
*
|
||||
* Also reads in the file to get the privKey and signingPrivKey,
|
||||
* which aren't available from I2PClient.
|
||||
*
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public Destination createIfAbsent(SigType type) throws I2PException, IOException, DataFormatException {
|
||||
if(!this.file.exists()) {
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = new SecureFileOutputStream(this.file);
|
||||
if (this.client != null)
|
||||
this.client.createDestination(out);
|
||||
this.client.createDestination(out, type);
|
||||
else
|
||||
write();
|
||||
} finally {
|
||||
@@ -424,11 +467,29 @@ public class PrivateKeyFile {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null on error or if not initialized
|
||||
*/
|
||||
public PrivateKey getPrivKey() {
|
||||
try {
|
||||
// call this to force initialization
|
||||
getDestination();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return this.privKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null on error or if not initialized
|
||||
*/
|
||||
public SigningPrivateKey getSigningPrivKey() {
|
||||
try {
|
||||
// call this to force initialization
|
||||
getDestination();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return this.signingPrivKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,10 @@ public class SigningPublicKey extends SimpleDataStructure {
|
||||
* @since 0.9.12
|
||||
*/
|
||||
public void writeTruncatedBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_type.getPubkeyLen() <= KEYSIZE_BYTES)
|
||||
// we don't use _type here so we can write the data even for unknown type
|
||||
//if (_type.getPubkeyLen() <= KEYSIZE_BYTES)
|
||||
if (_data == null) throw new DataFormatException("No data to write out");
|
||||
if (_data.length <= KEYSIZE_BYTES)
|
||||
out.write(_data);
|
||||
else
|
||||
out.write(_data, 0, KEYSIZE_BYTES);
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.io.OutputStream;
|
||||
public class TunnelId extends DataStructureImpl {
|
||||
private long _tunnelId;
|
||||
|
||||
public static final long MAX_ID_VALUE = (1l<<32l)-2l;
|
||||
public static final long MAX_ID_VALUE = (1L << 32) - 2L;
|
||||
|
||||
public TunnelId() {
|
||||
_tunnelId = -1;
|
||||
@@ -35,22 +35,22 @@ public class TunnelId extends DataStructureImpl {
|
||||
|
||||
/**
|
||||
* @param id 1 to 0xffffffff
|
||||
* @throws IllegalArgumentException if less than or equal to zero
|
||||
* @throws IllegalArgumentException if less than or equal to zero or greater than max value
|
||||
*/
|
||||
public TunnelId(long id) {
|
||||
if (id <= 0) throw new IllegalArgumentException("wtf, tunnelId " + id);
|
||||
_tunnelId = id;
|
||||
setTunnelId(id);
|
||||
}
|
||||
|
||||
public long getTunnelId() { return _tunnelId; }
|
||||
|
||||
/**
|
||||
* @param id 1 to 0xffffffff
|
||||
* @throws IllegalArgumentException if less than or equal to zero
|
||||
* @throws IllegalArgumentException if less than or equal to zero or greater than max value
|
||||
*/
|
||||
public void setTunnelId(long id) {
|
||||
if (id <= 0 || id > MAX_ID_VALUE)
|
||||
throw new IllegalArgumentException("bad id " + id);
|
||||
_tunnelId = id;
|
||||
if (id <= 0) throw new IllegalArgumentException("wtf, tunnelId " + id);
|
||||
}
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
|
||||
@@ -207,5 +207,10 @@ public final class ByteCache {
|
||||
// _log.debug("Removing " + toRemove + " cached entries of size " + _entrySize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Cleaner for " + _entrySize + " byte cache";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
219
core/java/src/net/i2p/util/FileLogWriter.java
Normal file
219
core/java/src/net/i2p/util/FileLogWriter.java
Normal file
@@ -0,0 +1,219 @@
|
||||
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.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* File-based log writer thread that pulls log records from the LogManager,
|
||||
* writes them to the current logfile, and rotates the logs as necessary.
|
||||
*
|
||||
* @since 0.9.26 moved from LogWriter
|
||||
*/
|
||||
class FileLogWriter extends LogWriter {
|
||||
// volatile as it changes on log file rotation
|
||||
private volatile Writer _currentOut;
|
||||
private int _rotationNum = -1;
|
||||
private File _currentFile;
|
||||
private long _numBytesInCurrentFile;
|
||||
|
||||
private static final int MAX_DISKFULL_MESSAGES = 8;
|
||||
private int _diskFullMessageCount;
|
||||
|
||||
public FileLogWriter(LogManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* File may not exist or have old logs in it if not opened yet
|
||||
*/
|
||||
public synchronized String currentFile() {
|
||||
if (_currentFile != null)
|
||||
return _currentFile.getAbsolutePath();
|
||||
String rv = getNextFile().getAbsolutePath();
|
||||
// so it doesn't increment every time we call this
|
||||
_rotationNum = -1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected void writeRecord(LogRecord rec, String formatted) {
|
||||
writeRecord(rec.getPriority(), formatted);
|
||||
}
|
||||
|
||||
protected synchronized void writeRecord(int priority, String val) {
|
||||
if (val == null) return;
|
||||
if (_currentOut == null) {
|
||||
rotateFile();
|
||||
if (_currentOut == null)
|
||||
return; // hosed
|
||||
}
|
||||
|
||||
try {
|
||||
_currentOut.write(val);
|
||||
// may be a little off if a lot of multi-byte chars, but unlikely
|
||||
_numBytesInCurrentFile += val.length();
|
||||
} catch (Throwable t) {
|
||||
if (!_write)
|
||||
return;
|
||||
if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error writing log, disk full? " + t);
|
||||
//t.printStackTrace();
|
||||
}
|
||||
if (_numBytesInCurrentFile >= _manager.getFileSize()) {
|
||||
rotateFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.19
|
||||
*/
|
||||
protected void flushWriter() {
|
||||
try {
|
||||
if (_currentOut != null)
|
||||
_currentOut.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_write && ++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error writing the router log - disk full? " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.19 renamed from closeFile()
|
||||
*/
|
||||
protected void closeWriter() {
|
||||
Writer out = _currentOut;
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate to the next file (or the first file if this is the first call)
|
||||
*
|
||||
* Caller must synch
|
||||
*/
|
||||
private void rotateFile() {
|
||||
File f = getNextFile();
|
||||
_currentFile = f;
|
||||
_numBytesInCurrentFile = 0;
|
||||
File parent = f.getParentFile();
|
||||
if (parent != null) {
|
||||
if (!parent.exists()) {
|
||||
File sd = new SecureDirectory(parent.getAbsolutePath());
|
||||
boolean ok = sd.mkdirs();
|
||||
if (!ok) {
|
||||
System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath());
|
||||
//System.exit(0);
|
||||
}
|
||||
}
|
||||
if (!parent.isDirectory()) {
|
||||
System.err.println("Cannot put the logs in a subdirectory of a plain file: " + f.getAbsolutePath());
|
||||
//System.exit(0);
|
||||
}
|
||||
}
|
||||
closeWriter();
|
||||
try {
|
||||
_currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8"));
|
||||
} catch (IOException ioe) {
|
||||
if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error creating log file [" + f.getAbsolutePath() + "]" + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next file in the rotation
|
||||
*
|
||||
* Caller must synch
|
||||
*/
|
||||
private File getNextFile() {
|
||||
String pattern = _manager.getBaseLogfilename();
|
||||
File f = new File(pattern);
|
||||
File base = null;
|
||||
if (!f.isAbsolute())
|
||||
base = _manager.getContext().getLogDir();
|
||||
|
||||
if ( (pattern.indexOf('#') < 0) && (pattern.indexOf('@') <= 0) ) {
|
||||
if (base != null)
|
||||
return new File(base, pattern);
|
||||
else
|
||||
return f;
|
||||
}
|
||||
|
||||
int max = _manager.getRotationLimit();
|
||||
if (_rotationNum == -1) {
|
||||
return getFirstFile(base, pattern, max);
|
||||
}
|
||||
|
||||
// we're in rotation, just go to the next
|
||||
_rotationNum++;
|
||||
if (_rotationNum > max) _rotationNum = 0;
|
||||
|
||||
String newf = replace(pattern, _rotationNum);
|
||||
if (base != null)
|
||||
return new File(base, newf);
|
||||
return new File(newf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the first file, updating the rotation number accordingly
|
||||
*
|
||||
* Caller must synch
|
||||
*/
|
||||
private File getFirstFile(File base, String pattern, int max) {
|
||||
for (int i = 0; i < max; i++) {
|
||||
File f;
|
||||
if (base != null)
|
||||
f = new File(base, replace(pattern, i));
|
||||
else
|
||||
f = new File(replace(pattern, i));
|
||||
if (!f.exists()) {
|
||||
_rotationNum = i;
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
// all exist, pick the oldest to replace
|
||||
File oldest = null;
|
||||
for (int i = 0; i < max; i++) {
|
||||
File f;
|
||||
if (base != null)
|
||||
f = new File(base, replace(pattern, i));
|
||||
else
|
||||
f = new File(replace(pattern, i));
|
||||
if (oldest == null) {
|
||||
oldest = f;
|
||||
} else {
|
||||
if (f.lastModified() < oldest.lastModified()) {
|
||||
_rotationNum = i;
|
||||
oldest = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
return oldest;
|
||||
}
|
||||
|
||||
private static final String replace(String pattern, int num) {
|
||||
char c[] = pattern.toCharArray();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
if ( (c[i] != '#') && (c[i] != '@') )
|
||||
buf.append(c[i]);
|
||||
else
|
||||
buf.append(num);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ package net.i2p.util;
|
||||
import java.io.File;
|
||||
import java.io.Flushable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -163,7 +165,23 @@ public class LogManager implements Flushable {
|
||||
// yeah, this doesn't always work, _writer should be volatile
|
||||
if (_writer != null)
|
||||
return;
|
||||
_writer = new LogWriter(this);
|
||||
if (SystemVersion.isAndroid()) {
|
||||
try {
|
||||
Class<? extends LogWriter> clazz = Class.forName(
|
||||
"net.i2p.util.AndroidLogWriter"
|
||||
).asSubclass(LogWriter.class);
|
||||
Constructor<? extends LogWriter> ctor = clazz.getDeclaredConstructor(LogManager.class);
|
||||
_writer = ctor.newInstance(this);
|
||||
} catch (ClassNotFoundException e) {
|
||||
} catch (InstantiationException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (InvocationTargetException e) {
|
||||
} catch (NoSuchMethodException e) {
|
||||
}
|
||||
}
|
||||
// Default writer
|
||||
if (_writer == null)
|
||||
_writer = new FileLogWriter(this);
|
||||
_writer.setFlushInterval(_flushInterval * 1000);
|
||||
// if you enable logging in I2PThread again, you MUST change this back to Thread
|
||||
Thread t = new I2PThread(_writer, "LogWriter");
|
||||
|
||||
@@ -9,210 +9,182 @@ package net.i2p.util;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* File-based log writer thread that pulls log records from the LogManager,
|
||||
* writes them to the current logfile, and rotates the logs as necessary.
|
||||
* Log writer thread that pulls log records from the LogManager and writes them to
|
||||
* the log. This also periodically instructs the LogManager to reread its config
|
||||
* file.
|
||||
*
|
||||
* @since 0.9.19 pulled from FileLogWriter so Android may extend; renamed from LogWriterBase in 0.9.26
|
||||
*/
|
||||
class LogWriter extends LogWriterBase {
|
||||
// volatile as it changes on log file rotation
|
||||
private volatile Writer _currentOut;
|
||||
private int _rotationNum = -1;
|
||||
private File _currentFile;
|
||||
private long _numBytesInCurrentFile;
|
||||
abstract class LogWriter implements Runnable {
|
||||
/** every 10 seconds? why? Just have the gui force a reread after a change?? */
|
||||
private final static long CONFIG_READ_INTERVAL = 50 * 1000;
|
||||
final static long FLUSH_INTERVAL = 29 * 1000;
|
||||
private final static long MIN_FLUSH_INTERVAL = 2*1000;
|
||||
private final static long MAX_FLUSH_INTERVAL = 5*60*1000;
|
||||
private long _lastReadConfig;
|
||||
protected final LogManager _manager;
|
||||
|
||||
private static final int MAX_DISKFULL_MESSAGES = 8;
|
||||
private int _diskFullMessageCount;
|
||||
protected volatile boolean _write;
|
||||
private LogRecord _last;
|
||||
// ms
|
||||
private volatile long _flushInterval = FLUSH_INTERVAL;
|
||||
|
||||
public LogWriter(LogManager manager) {
|
||||
super(manager);
|
||||
_manager = manager;
|
||||
_lastReadConfig = Clock.getInstance().now();
|
||||
}
|
||||
|
||||
public abstract String currentFile();
|
||||
/**
|
||||
* Write the provided LogRecord to the writer.
|
||||
* @param rec the LogRecord to write.
|
||||
* @param formatted a String pre-formatted from rec, may be ignored.
|
||||
*/
|
||||
protected abstract void writeRecord(LogRecord rec, String formatted);
|
||||
/**
|
||||
* Write a single String verbatim to the writer.
|
||||
* @param priority the level to log the line at.
|
||||
* @param line the String to write.
|
||||
*/
|
||||
protected abstract void writeRecord(int priority, String line);
|
||||
protected abstract void flushWriter();
|
||||
protected abstract void closeWriter();
|
||||
|
||||
public void stopWriting() {
|
||||
_write = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* File may not exist or have old logs in it if not opened yet
|
||||
* @param interval ms
|
||||
* @since 0.9.18
|
||||
*/
|
||||
public synchronized String currentFile() {
|
||||
if (_currentFile != null)
|
||||
return _currentFile.getAbsolutePath();
|
||||
String rv = getNextFile().getAbsolutePath();
|
||||
// so it doesn't increment every time we call this
|
||||
_rotationNum = -1;
|
||||
return rv;
|
||||
public void setFlushInterval(long interval) {
|
||||
_flushInterval = Math.min(MAX_FLUSH_INTERVAL, Math.max(MIN_FLUSH_INTERVAL, interval));
|
||||
}
|
||||
|
||||
protected void writeRecord(LogRecord rec, String formatted) {
|
||||
writeRecord(rec.getPriority(), formatted);
|
||||
}
|
||||
|
||||
protected synchronized void writeRecord(int priority, String val) {
|
||||
if (val == null) return;
|
||||
if (_currentOut == null) {
|
||||
rotateFile();
|
||||
if (_currentOut == null)
|
||||
return; // hosed
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_write = true;
|
||||
// don't bother on Android
|
||||
final boolean shouldReadConfig = !SystemVersion.isAndroid();
|
||||
try {
|
||||
_currentOut.write(val);
|
||||
// may be a little off if a lot of multi-byte chars, but unlikely
|
||||
_numBytesInCurrentFile += val.length();
|
||||
} catch (Throwable t) {
|
||||
if (!_write)
|
||||
return;
|
||||
if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error writing log, disk full? " + t);
|
||||
//t.printStackTrace();
|
||||
}
|
||||
if (_numBytesInCurrentFile >= _manager.getFileSize()) {
|
||||
rotateFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.19
|
||||
*/
|
||||
protected void flushWriter() {
|
||||
try {
|
||||
if (_currentOut != null)
|
||||
_currentOut.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_write && ++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error writing the router log - disk full? " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.19 renamed from closeFile()
|
||||
*/
|
||||
protected void closeWriter() {
|
||||
Writer out = _currentOut;
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate to the next file (or the first file if this is the first call)
|
||||
*
|
||||
* Caller must synch
|
||||
*/
|
||||
private void rotateFile() {
|
||||
File f = getNextFile();
|
||||
_currentFile = f;
|
||||
_numBytesInCurrentFile = 0;
|
||||
File parent = f.getParentFile();
|
||||
if (parent != null) {
|
||||
if (!parent.exists()) {
|
||||
File sd = new SecureDirectory(parent.getAbsolutePath());
|
||||
boolean ok = sd.mkdirs();
|
||||
if (!ok) {
|
||||
System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath());
|
||||
//System.exit(0);
|
||||
}
|
||||
}
|
||||
if (!parent.isDirectory()) {
|
||||
System.err.println("Cannot put the logs in a subdirectory of a plain file: " + f.getAbsolutePath());
|
||||
//System.exit(0);
|
||||
while (_write) {
|
||||
flushRecords();
|
||||
if (_write && shouldReadConfig)
|
||||
rereadConfig();
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
System.err.println("Error writing the log: " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
closeWriter();
|
||||
}
|
||||
|
||||
public void flushRecords() { flushRecords(true); }
|
||||
|
||||
public void flushRecords(boolean shouldWait) {
|
||||
try {
|
||||
_currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8"));
|
||||
} catch (IOException ioe) {
|
||||
if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error creating log file [" + f.getAbsolutePath() + "]" + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next file in the rotation
|
||||
*
|
||||
* Caller must synch
|
||||
*/
|
||||
private File getNextFile() {
|
||||
String pattern = _manager.getBaseLogfilename();
|
||||
File f = new File(pattern);
|
||||
File base = null;
|
||||
if (!f.isAbsolute())
|
||||
base = _manager.getContext().getLogDir();
|
||||
|
||||
if ( (pattern.indexOf('#') < 0) && (pattern.indexOf('@') <= 0) ) {
|
||||
if (base != null)
|
||||
return new File(base, pattern);
|
||||
else
|
||||
return f;
|
||||
}
|
||||
|
||||
int max = _manager.getRotationLimit();
|
||||
if (_rotationNum == -1) {
|
||||
return getFirstFile(base, pattern, max);
|
||||
}
|
||||
|
||||
// we're in rotation, just go to the next
|
||||
_rotationNum++;
|
||||
if (_rotationNum > max) _rotationNum = 0;
|
||||
|
||||
String newf = replace(pattern, _rotationNum);
|
||||
if (base != null)
|
||||
return new File(base, newf);
|
||||
return new File(newf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the first file, updating the rotation number accordingly
|
||||
*
|
||||
* Caller must synch
|
||||
*/
|
||||
private File getFirstFile(File base, String pattern, int max) {
|
||||
for (int i = 0; i < max; i++) {
|
||||
File f;
|
||||
if (base != null)
|
||||
f = new File(base, replace(pattern, i));
|
||||
else
|
||||
f = new File(replace(pattern, i));
|
||||
if (!f.exists()) {
|
||||
_rotationNum = i;
|
||||
return f;
|
||||
// zero copy, drain the manager queue directly
|
||||
Queue<LogRecord> records = _manager.getQueue();
|
||||
if (records == null) return;
|
||||
if (!records.isEmpty()) {
|
||||
if (_last != null && _last.getDate() < _manager.getContext().clock().now() - 30*60*1000)
|
||||
_last = null;
|
||||
LogRecord rec;
|
||||
int dupCount = 0;
|
||||
while ((rec = records.poll()) != null) {
|
||||
if (_manager.shouldDropDuplicates() && rec.equals(_last)) {
|
||||
dupCount++;
|
||||
} else {
|
||||
if (dupCount > 0) {
|
||||
writeDupMessage(dupCount, _last);
|
||||
dupCount = 0;
|
||||
}
|
||||
writeRecord(rec);
|
||||
}
|
||||
_last = rec;
|
||||
}
|
||||
if (dupCount > 0) {
|
||||
writeDupMessage(dupCount, _last);
|
||||
}
|
||||
flushWriter();
|
||||
}
|
||||
}
|
||||
|
||||
// all exist, pick the oldest to replace
|
||||
File oldest = null;
|
||||
for (int i = 0; i < max; i++) {
|
||||
File f;
|
||||
if (base != null)
|
||||
f = new File(base, replace(pattern, i));
|
||||
else
|
||||
f = new File(replace(pattern, i));
|
||||
if (oldest == null) {
|
||||
oldest = f;
|
||||
} else {
|
||||
if (f.lastModified() < oldest.lastModified()) {
|
||||
_rotationNum = i;
|
||||
oldest = f;
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
} finally {
|
||||
if (shouldWait) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
this.wait(_flushInterval);
|
||||
}
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
}
|
||||
return oldest;
|
||||
}
|
||||
|
||||
private static final String replace(String pattern, int num) {
|
||||
char c[] = pattern.toCharArray();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
if ( (c[i] != '#') && (c[i] != '@') )
|
||||
buf.append(c[i]);
|
||||
else
|
||||
buf.append(num);
|
||||
/**
|
||||
* Write a msg with the date stamp of the last duplicate
|
||||
* @since 0.9.21
|
||||
*/
|
||||
private void writeDupMessage(int dupCount, LogRecord lastRecord) {
|
||||
String dmsg = dupMessage(dupCount, lastRecord, false);
|
||||
writeRecord(lastRecord.getPriority(), dmsg);
|
||||
if (_manager.getDisplayOnScreenLevel() <= lastRecord.getPriority() && _manager.displayOnScreen())
|
||||
System.out.print(dmsg);
|
||||
dmsg = dupMessage(dupCount, lastRecord, true);
|
||||
_manager.getBuffer().add(dmsg);
|
||||
if (lastRecord.getPriority() >= Log.CRIT)
|
||||
_manager.getBuffer().addCritical(dmsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a msg with the date stamp of the last duplicate
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String dupMessage(int dupCount, LogRecord lastRecord, boolean reverse) {
|
||||
String arrows = reverse ? (SystemVersion.isAndroid() ? "vvv" : "↓↓↓") : "^^^";
|
||||
return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' +
|
||||
_t(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows +
|
||||
LogRecordFormatter.NL;
|
||||
}
|
||||
|
||||
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
|
||||
|
||||
/**
|
||||
* gettext
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String _t(int a, String b, String c) {
|
||||
return Translate.getString(a, b, c, _manager.getContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
private void rereadConfig() {
|
||||
long now = Clock.getInstance().now();
|
||||
if (now - _lastReadConfig > CONFIG_READ_INTERVAL) {
|
||||
_manager.rereadConfig();
|
||||
_lastReadConfig = now;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRecord(LogRecord rec) {
|
||||
String val = LogRecordFormatter.formatRecord(_manager, rec, true);
|
||||
writeRecord(rec, val);
|
||||
|
||||
// we always add to the console buffer, but only sometimes write to stdout
|
||||
_manager.getBuffer().add(val);
|
||||
if (rec.getPriority() >= Log.CRIT)
|
||||
_manager.getBuffer().addCritical(val);
|
||||
if (_manager.getDisplayOnScreenLevel() <= rec.getPriority()) {
|
||||
if (_manager.displayOnScreen()) {
|
||||
// wrapper and android logs already do time stamps, so reformat without the date
|
||||
if (_manager.getContext().hasWrapper() || SystemVersion.isAndroid())
|
||||
System.out.print(LogRecordFormatter.formatRecord(_manager, rec, false));
|
||||
else
|
||||
System.out.print(val);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
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.util.Queue;
|
||||
|
||||
/**
|
||||
* Log writer thread that pulls log records from the LogManager and writes them to
|
||||
* the log. This also periodically instructs the LogManager to reread its config
|
||||
* file.
|
||||
*
|
||||
* @since 0.9.19 pulled from LogWriter so Android may extend
|
||||
*/
|
||||
abstract class LogWriterBase implements Runnable {
|
||||
/** every 10 seconds? why? Just have the gui force a reread after a change?? */
|
||||
private final static long CONFIG_READ_INTERVAL = 50 * 1000;
|
||||
final static long FLUSH_INTERVAL = 29 * 1000;
|
||||
private final static long MIN_FLUSH_INTERVAL = 2*1000;
|
||||
private final static long MAX_FLUSH_INTERVAL = 5*60*1000;
|
||||
private long _lastReadConfig;
|
||||
protected final LogManager _manager;
|
||||
|
||||
protected volatile boolean _write;
|
||||
private LogRecord _last;
|
||||
// ms
|
||||
private volatile long _flushInterval = FLUSH_INTERVAL;
|
||||
|
||||
public LogWriterBase(LogManager manager) {
|
||||
_manager = manager;
|
||||
_lastReadConfig = Clock.getInstance().now();
|
||||
}
|
||||
|
||||
public abstract String currentFile();
|
||||
/**
|
||||
* Write the provided LogRecord to the writer.
|
||||
* @param rec the LogRecord to write.
|
||||
* @param formatted a String pre-formatted from rec, may be ignored.
|
||||
*/
|
||||
protected abstract void writeRecord(LogRecord rec, String formatted);
|
||||
/**
|
||||
* Write a single String verbatim to the writer.
|
||||
* @param priority the level to log the line at.
|
||||
* @param line the String to write.
|
||||
*/
|
||||
protected abstract void writeRecord(int priority, String line);
|
||||
protected abstract void flushWriter();
|
||||
protected abstract void closeWriter();
|
||||
|
||||
public void stopWriting() {
|
||||
_write = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param interval ms
|
||||
* @since 0.9.18
|
||||
*/
|
||||
public void setFlushInterval(long interval) {
|
||||
_flushInterval = Math.min(MAX_FLUSH_INTERVAL, Math.max(MIN_FLUSH_INTERVAL, interval));
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_write = true;
|
||||
// don't bother on Android
|
||||
final boolean shouldReadConfig = !SystemVersion.isAndroid();
|
||||
try {
|
||||
while (_write) {
|
||||
flushRecords();
|
||||
if (_write && shouldReadConfig)
|
||||
rereadConfig();
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
System.err.println("Error writing the log: " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
closeWriter();
|
||||
}
|
||||
|
||||
public void flushRecords() { flushRecords(true); }
|
||||
|
||||
public void flushRecords(boolean shouldWait) {
|
||||
try {
|
||||
// zero copy, drain the manager queue directly
|
||||
Queue<LogRecord> records = _manager.getQueue();
|
||||
if (records == null) return;
|
||||
if (!records.isEmpty()) {
|
||||
if (_last != null && _last.getDate() < _manager.getContext().clock().now() - 30*60*1000)
|
||||
_last = null;
|
||||
LogRecord rec;
|
||||
int dupCount = 0;
|
||||
while ((rec = records.poll()) != null) {
|
||||
if (_manager.shouldDropDuplicates() && rec.equals(_last)) {
|
||||
dupCount++;
|
||||
} else {
|
||||
if (dupCount > 0) {
|
||||
writeDupMessage(dupCount, _last);
|
||||
dupCount = 0;
|
||||
}
|
||||
writeRecord(rec);
|
||||
}
|
||||
_last = rec;
|
||||
}
|
||||
if (dupCount > 0) {
|
||||
writeDupMessage(dupCount, _last);
|
||||
}
|
||||
flushWriter();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
} finally {
|
||||
if (shouldWait) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
this.wait(_flushInterval);
|
||||
}
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a msg with the date stamp of the last duplicate
|
||||
* @since 0.9.21
|
||||
*/
|
||||
private void writeDupMessage(int dupCount, LogRecord lastRecord) {
|
||||
String dmsg = dupMessage(dupCount, lastRecord, false);
|
||||
writeRecord(lastRecord.getPriority(), dmsg);
|
||||
if (_manager.getDisplayOnScreenLevel() <= lastRecord.getPriority() && _manager.displayOnScreen())
|
||||
System.out.print(dmsg);
|
||||
dmsg = dupMessage(dupCount, lastRecord, true);
|
||||
_manager.getBuffer().add(dmsg);
|
||||
if (lastRecord.getPriority() >= Log.CRIT)
|
||||
_manager.getBuffer().addCritical(dmsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a msg with the date stamp of the last duplicate
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String dupMessage(int dupCount, LogRecord lastRecord, boolean reverse) {
|
||||
String arrows = reverse ? (SystemVersion.isAndroid() ? "vvv" : "↓↓↓") : "^^^";
|
||||
return LogRecordFormatter.getWhen(_manager, lastRecord) + ' ' + arrows + ' ' +
|
||||
_t(dupCount, "1 similar message omitted", "{0} similar messages omitted") + ' ' + arrows +
|
||||
LogRecordFormatter.NL;
|
||||
}
|
||||
|
||||
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
|
||||
|
||||
/**
|
||||
* gettext
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private String _t(int a, String b, String c) {
|
||||
return Translate.getString(a, b, c, _manager.getContext(), BUNDLE_NAME);
|
||||
}
|
||||
|
||||
private void rereadConfig() {
|
||||
long now = Clock.getInstance().now();
|
||||
if (now - _lastReadConfig > CONFIG_READ_INTERVAL) {
|
||||
_manager.rereadConfig();
|
||||
_lastReadConfig = now;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRecord(LogRecord rec) {
|
||||
String val = LogRecordFormatter.formatRecord(_manager, rec, true);
|
||||
writeRecord(rec, val);
|
||||
|
||||
// we always add to the console buffer, but only sometimes write to stdout
|
||||
_manager.getBuffer().add(val);
|
||||
if (rec.getPriority() >= Log.CRIT)
|
||||
_manager.getBuffer().addCritical(val);
|
||||
if (_manager.getDisplayOnScreenLevel() <= rec.getPriority()) {
|
||||
if (_manager.displayOnScreen()) {
|
||||
// wrapper and android logs already do time stamps, so reformat without the date
|
||||
if (_manager.getContext().hasWrapper() || SystemVersion.isAndroid())
|
||||
System.out.print(LogRecordFormatter.formatRecord(_manager, rec, false));
|
||||
else
|
||||
System.out.print(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ 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;
|
||||
@@ -32,7 +33,6 @@ 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;
|
||||
@@ -98,6 +98,12 @@ import net.i2p.data.DataHelper;
|
||||
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;
|
||||
@@ -150,13 +156,135 @@ public class NativeBigInteger extends BigInteger {
|
||||
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_ARM = "arm";
|
||||
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.
|
||||
*/
|
||||
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[]>() {{
|
||||
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
|
||||
@@ -197,15 +325,7 @@ public class NativeBigInteger extends BigInteger {
|
||||
private final static String sCPUType; //The CPU Type to optimize for (one of the above strings)
|
||||
|
||||
static {
|
||||
if (_isX86) { // Don't try to resolve CPU type on non x86 hardware
|
||||
sCPUType = resolveCPUType();
|
||||
} else if (_isArm) {
|
||||
sCPUType = JBIGI_OPTIMIZATION_ARM;
|
||||
} else if (_isPPC && !_isMac) {
|
||||
sCPUType = JBIGI_OPTIMIZATION_PPC;
|
||||
} else {
|
||||
sCPUType = null;
|
||||
}
|
||||
sCPUType = resolveCPUType();
|
||||
loadNative();
|
||||
}
|
||||
|
||||
@@ -215,59 +335,150 @@ public class NativeBigInteger extends BigInteger {
|
||||
* @return A string containing the CPU-type or null if CPU type is unknown
|
||||
*/
|
||||
private static String resolveCPUType() {
|
||||
try {
|
||||
CPUInfo c = CPUID.getInfo();
|
||||
if(_isX86) {
|
||||
try {
|
||||
_cpuModel = c.getCPUModelString();
|
||||
} 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;
|
||||
// Supported in CPUID, no GMP support
|
||||
//if (amdcpu.IsBobcatCompatible())
|
||||
// return JBIGI_OPTIMIZATION_BOBCAT;
|
||||
if (amdcpu.IsAthlon64Compatible())
|
||||
return JBIGI_OPTIMIZATION_ATHLON64;
|
||||
if (amdcpu.IsAthlonCompatible())
|
||||
return JBIGI_OPTIMIZATION_ATHLON;
|
||||
// FIXME lots of geodes, but GMP configures like a K6-3
|
||||
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.IsCoreiCompatible())
|
||||
return JBIGI_OPTIMIZATION_COREI;
|
||||
if (intelcpu.IsCore2Compatible())
|
||||
return JBIGI_OPTIMIZATION_CORE2;
|
||||
if (intelcpu.IsPentium4Compatible())
|
||||
return JBIGI_OPTIMIZATION_PENTIUM4;
|
||||
if (intelcpu.IsAtomCompatible())
|
||||
return JBIGI_OPTIMIZATION_ATOM;
|
||||
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;
|
||||
//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;
|
||||
}
|
||||
return null;
|
||||
} catch (UnknownCPUException e) {
|
||||
return null;
|
||||
}
|
||||
} 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;
|
||||
} catch (UnknownCPUException e) {
|
||||
return null; //TODO: Log something here maybe..
|
||||
} else if (_isPPC && !_isMac) {
|
||||
return JBIGI_OPTIMIZATION_PPC;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,10 +490,120 @@ public class NativeBigInteger extends BigInteger {
|
||||
* 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
|
||||
*/
|
||||
public native static byte[] nativeModPow(byte base[], byte exponent[], byte 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) {
|
||||
@@ -318,17 +639,49 @@ public class NativeBigInteger extends BigInteger {
|
||||
this(integer.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ArithmeticException if m <= 0
|
||||
*/
|
||||
@Override
|
||||
public BigInteger modPow(BigInteger exponent, BigInteger m) {
|
||||
// jbigi.c convert_j2mp() and convert_mp2j() do NOT currently support negative numbers
|
||||
// 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
|
||||
if (_nativeOk && signum() >= 0 && exponent.signum() >= 0 && m.signum() > 0)
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ArithmeticException if m <= 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
|
||||
@@ -376,67 +729,183 @@ public class NativeBigInteger extends BigInteger {
|
||||
* <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;
|
||||
runModPowTest(100);
|
||||
}
|
||||
|
||||
private static void runModPowTest(int numRuns) {
|
||||
boolean nativeOnly = args.length > 0 && args[0].equals("-n");
|
||||
if (nativeOnly && !_nativeOk) {
|
||||
System.out.println("Failed to load native library");
|
||||
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 = new SecureRandom();
|
||||
SecureRandom rand = RandomSource.getInstance();
|
||||
rand.nextBoolean();
|
||||
System.out.println("DEBUG: Random number generator warmed up");
|
||||
|
||||
/* the sample numbers are elG generator/prime so we can test with reasonable numbers */
|
||||
byte[] _sampleGenerator = CryptoConstants.elgg.toByteArray();
|
||||
byte[] _samplePrime = CryptoConstants.elgp.toByteArray();
|
||||
//if (_nativeOk3)
|
||||
// testnegs();
|
||||
|
||||
BigInteger jg = new BigInteger(_sampleGenerator);
|
||||
BigInteger jp = new BigInteger(_samplePrime);
|
||||
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++) {
|
||||
BigInteger bi = new BigInteger(226, rand); // 2048, rand); //
|
||||
NativeBigInteger g = new NativeBigInteger(_sampleGenerator);
|
||||
NativeBigInteger p = new NativeBigInteger(_samplePrime);
|
||||
// 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());
|
||||
long beforeModPow = System.currentTimeMillis();
|
||||
BigInteger myValue = g.modPow(k, p);
|
||||
long afterModPow = System.currentTimeMillis();
|
||||
BigInteger jval = jg.modPow(bi, jp);
|
||||
long afterJavaModPow = System.currentTimeMillis();
|
||||
|
||||
//// 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);
|
||||
javaTime += (afterJavaModPow - afterModPow);
|
||||
if (!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());
|
||||
System.err.println("ERROR: run time: " + totalTime + "ms (" + (totalTime / (runsProcessed + 1)) + "ms each)");
|
||||
break;
|
||||
} else {
|
||||
System.out.println("DEBUG: current run time: " + (afterModPow - beforeModPow) + "ms (total: "
|
||||
+ totalTime + "ms, " + (totalTime / (runsProcessed + 1)) + "ms each)");
|
||||
//// 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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("INFO: run time: " + totalTime + "ms (" + (totalTime / (runsProcessed + 1)) + "ms each)");
|
||||
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("native run time: \t" + totalTime + "ms (" + (totalTime / (runsProcessed + 1))
|
||||
+ "ms each)");
|
||||
System.out.println("java run time: \t" + javaTime + "ms (" + (javaTime / (runsProcessed + 1)) + "ms each)");
|
||||
System.out.println("native = " + ((totalTime * 100.0d) / javaTime) + "% of pure java time");
|
||||
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("java run time: \t" + javaTime + "ms (" + (javaTime / (runsProcessed + 1)) + "ms each)");
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -486,7 +955,7 @@ public class NativeBigInteger extends BigInteger {
|
||||
List<String> toTry = getResourceList();
|
||||
debug("loadResource list to try is: " + toTry);
|
||||
for (String s : toTry) {
|
||||
debug("trying loadResource " + s);
|
||||
System.out.println("trying to load resource: " + s);
|
||||
if (loadFromResource(s)) {
|
||||
_nativeOk = true;
|
||||
_extractedResource = s;
|
||||
@@ -499,6 +968,8 @@ public class NativeBigInteger extends BigInteger {
|
||||
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);
|
||||
@@ -535,7 +1006,26 @@ public class NativeBigInteger extends BigInteger {
|
||||
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>
|
||||
@@ -598,7 +1088,7 @@ public class NativeBigInteger extends BigInteger {
|
||||
//URL resource = NativeBigInteger.class.getClassLoader().getResource(resourceName);
|
||||
URL resource = ClassLoader.getSystemResource(resourceName);
|
||||
if (resource == null) {
|
||||
info("Resource name [" + resourceName + "] was not found");
|
||||
System.out.println("Resource name [" + resourceName + "] was not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -618,14 +1108,15 @@ public class NativeBigInteger extends BigInteger {
|
||||
fos.close();
|
||||
fos = null;
|
||||
System.load(outFile.getAbsolutePath()); //System.load requires an absolute path to the lib
|
||||
System.out.println("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");
|
||||
System.out.println("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);
|
||||
System.out.println("Problem writing out the temporary native library data: " + ioe.toString());
|
||||
if (outFile != null)
|
||||
outFile.delete();
|
||||
return false;
|
||||
@@ -649,73 +1140,40 @@ public class NativeBigInteger extends BigInteger {
|
||||
private static List<String> getResourceList() {
|
||||
if (_isAndroid)
|
||||
return Collections.emptyList();
|
||||
List<String> rv = new ArrayList<String>(8);
|
||||
List<String> rv = new ArrayList<String>(20);
|
||||
String primary = getMiddleName2(true);
|
||||
if (primary != null) {
|
||||
if (_is64) {
|
||||
// add 64 bit variants at the front
|
||||
if (!primary.equals(JBIGI_OPTIMIZATION_ATHLON64))
|
||||
rv.add(_libPrefix + getMiddleName1() + primary + "_64" + _libSuffix);
|
||||
// athlon64_64 is always a fallback for 64 bit
|
||||
rv.add(_libPrefix + getMiddleName1() + JBIGI_OPTIMIZATION_ATHLON64 + "_64" + _libSuffix);
|
||||
}
|
||||
String[] compatList = JBIGI_COMPAT_MAP.get(primary);
|
||||
|
||||
if (_isArm) {
|
||||
Map<String, String> cpuinfo = getCPUInfo();
|
||||
int ver = 0;
|
||||
String proc = cpuinfo.get("processor");
|
||||
String arch = cpuinfo.get("cpu architecture");
|
||||
if (proc != null && proc.contains("ARMv6")) {
|
||||
// Raspberry Pi workaround
|
||||
// Processor : ARMv6-compatible processor rev 7 (v6l)
|
||||
// CPU architecture: 7
|
||||
ver = 6;
|
||||
} else if (arch != null && arch.length() > 0) {
|
||||
//CPU architecture: 5TEJ
|
||||
//CPU architecture: 7
|
||||
String sver = arch.substring(0, 1);
|
||||
try {
|
||||
ver = Integer.parseInt(sver);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
// add libjbigi-linux-armv7.so, libjbigi-linux-armv6.so, ...
|
||||
for (int i = ver; i >= 3; i--) {
|
||||
rv.add(_libPrefix + getMiddleName1() + primary + 'v' + i + _libSuffix);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// the preferred selection
|
||||
rv.add(_libPrefix + getMiddleName1() + primary + _libSuffix);
|
||||
|
||||
// athlon64 is always a fallback for 64 bit
|
||||
if (_is64 && !primary.equals(JBIGI_OPTIMIZATION_ATHLON64))
|
||||
rv.add(_libPrefix + getMiddleName1() + JBIGI_OPTIMIZATION_ATHLON64 + _libSuffix);
|
||||
|
||||
// Add fallbacks for any 32-bit that were added 0.8.7 or later here
|
||||
// FIXME lots of geodes, but GMP configures like a K6-3, so pentium3 is probably a good backup
|
||||
if (primary.equals(JBIGI_OPTIMIZATION_ATOM) ||
|
||||
primary.equals(JBIGI_OPTIMIZATION_PENTIUMM) ||
|
||||
primary.equals(JBIGI_OPTIMIZATION_GEODE))
|
||||
rv.add(_libPrefix + getMiddleName1() + JBIGI_OPTIMIZATION_PENTIUM3 + _libSuffix);
|
||||
|
||||
// athlon is always a fallback for 64 bit, we have it for all architectures
|
||||
// and it should be much better than "none"
|
||||
if (_is64)
|
||||
rv.add(_libPrefix + getMiddleName1() + JBIGI_OPTIMIZATION_ATHLON + _libSuffix);
|
||||
|
||||
} else {
|
||||
if (_is64) {
|
||||
rv.add(_libPrefix + getMiddleName1() + JBIGI_OPTIMIZATION_ATHLON64 + "_64" + _libSuffix);
|
||||
rv.add(_libPrefix + getMiddleName1() + JBIGI_OPTIMIZATION_ATHLON64 + _libSuffix);
|
||||
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) + "\"");
|
||||
}
|
||||
}
|
||||
// Add libjbigi-xxx-none_64.so
|
||||
if (_is64)
|
||||
rv.add(_libPrefix + getMiddleName1() + "none_64" + _libSuffix);
|
||||
// Add libjbigi-xxx-none.so
|
||||
// Note that libjbigi-osx-none.jnilib is a 'fat binary' with both PPC and x86-32
|
||||
if (!_isArm && !_isPPC && !_isMac)
|
||||
rv.add(getResourceName(false));
|
||||
|
||||
//System.out.println("Primary: " + primary);
|
||||
//System.out.println("ResourceList: " + rv.toString());
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -753,12 +1211,14 @@ public class NativeBigInteger extends BigInteger {
|
||||
/**
|
||||
* @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
|
||||
@@ -784,9 +1244,10 @@ public class NativeBigInteger extends BigInteger {
|
||||
if (sCPUType.equals(JBIGI_OPTIMIZATION_K6_3) && !_isWin)
|
||||
// k62 and k63 identical except on windows
|
||||
sAppend = JBIGI_OPTIMIZATION_K6_2;
|
||||
else if (sCPUType.equals(JBIGI_OPTIMIZATION_COREI) && (!_is64) && ((_isKFreebsd) || (_isNetbsd) || (_isOpenbsd)))
|
||||
// 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;
|
||||
//sAppend = JBIGI_OPTIMIZATION_CORE2;
|
||||
else if (sCPUType.equals(JBIGI_OPTIMIZATION_PENTIUM2) && _isSunos && _isX86)
|
||||
// pentium2 and pentium3 identical on X86 Solaris
|
||||
sAppend = JBIGI_OPTIMIZATION_PENTIUM3;
|
||||
@@ -796,7 +1257,7 @@ public class NativeBigInteger extends BigInteger {
|
||||
//else if (sCPUType.equals(JBIGI_OPTIMIZATION_VIAC3) && _isWin)
|
||||
// FIXME no viac3 available for windows, what to use instead?
|
||||
else
|
||||
sAppend = sCPUType;
|
||||
sAppend = sCPUType;
|
||||
} else {
|
||||
sAppend = "none";
|
||||
}
|
||||
|
||||
@@ -37,11 +37,15 @@ public class OrderedProperties extends Properties {
|
||||
|
||||
@Override
|
||||
public Set<Object> keySet() {
|
||||
if (size() <= 1)
|
||||
return super.keySet();
|
||||
return Collections.unmodifiableSortedSet(new TreeSet<Object>(super.keySet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
if (size() <= 1)
|
||||
return super.entrySet();
|
||||
TreeSet<Map.Entry<Object, Object>> rv = new TreeSet<Map.Entry<Object, Object>>(new EntryComparator());
|
||||
rv.addAll(super.entrySet());
|
||||
return Collections.unmodifiableSortedSet(rv);
|
||||
|
||||
@@ -30,7 +30,7 @@ public class PasswordManager {
|
||||
protected static final String PROP_PW = ".password";
|
||||
/** stored obfuscated as b64 of the UTF-8 bytes */
|
||||
protected static final String PROP_B64 = ".b64";
|
||||
/** stored as the hex of the MD5 hash of the ISO-8859-1 bytes. Compatible with Jetty. */
|
||||
/** stored as the hex of the MD5 hash of the UTF-8 bytes. Compatible with Jetty. */
|
||||
protected static final String PROP_MD5 = ".md5";
|
||||
/** stored as a Unix crypt string */
|
||||
protected static final String PROP_CRYPT = ".crypt";
|
||||
@@ -185,6 +185,10 @@ public class PasswordManager {
|
||||
* Will return the MD5 sum of "user:subrealm:pw", compatible with Jetty
|
||||
* and RFC 2617.
|
||||
*
|
||||
* Updated in 0.9.26 to use UTF-8, as implied in RFC 7616/7617
|
||||
* See also http://stackoverflow.com/questions/7242316/what-encoding-should-i-use-for-http-basic-authentication
|
||||
* http://stackoverflow.com/questions/702629/utf-8-characters-mangled-in-http-basic-auth-username
|
||||
*
|
||||
* @param subrealm to be used in creating the checksum
|
||||
* @param user non-null, non-empty, already trimmed
|
||||
* @param pw non-null, plain text, already trimmed
|
||||
@@ -200,17 +204,18 @@ public class PasswordManager {
|
||||
* Will return the MD5 sum of the data, compatible with Jetty
|
||||
* and RFC 2617.
|
||||
*
|
||||
* Updated in 0.9.26 to use UTF-8, as implied in RFC 7616/7617
|
||||
* See also http://stackoverflow.com/questions/7242316/what-encoding-should-i-use-for-http-basic-authentication
|
||||
*
|
||||
* @param fullpw non-null, plain text, already trimmed
|
||||
* @return lower-case hex with leading zeros, 32 chars, or null on error
|
||||
*/
|
||||
public static String md5Hex(String fullpw) {
|
||||
try {
|
||||
byte[] data = fullpw.getBytes("ISO-8859-1");
|
||||
byte[] sum = md5Sum(data);
|
||||
if (sum != null)
|
||||
// adds leading zeros if necessary
|
||||
return DataHelper.toString(sum);
|
||||
} catch (UnsupportedEncodingException uee) {}
|
||||
byte[] data = DataHelper.getUTF8(fullpw);
|
||||
byte[] sum = md5Sum(data);
|
||||
if (sum != null)
|
||||
// adds leading zeros if necessary
|
||||
return DataHelper.toString(sum);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ public class SimpleTimer2 {
|
||||
// ... and I expect cancelling this way is more efficient
|
||||
|
||||
/** state of the current event. All access should be under lock. */
|
||||
private TimedEventState _state;
|
||||
protected TimedEventState _state;
|
||||
/** absolute time this event should run next time. LOCKING: this */
|
||||
private long _nextRun;
|
||||
/** whether this was scheduled during RUNNING state. LOCKING: this */
|
||||
@@ -338,8 +338,8 @@ public class SimpleTimer2 {
|
||||
*/
|
||||
public synchronized void reschedule(long timeoutMs, boolean useEarliestTime) {
|
||||
if (timeoutMs <= 0) {
|
||||
if (timeoutMs < 0 && _log.shouldWarn())
|
||||
_log.warn("Resched. timeout < 0: " + this + " timeout = " + timeoutMs + " state: " + _state);
|
||||
if (timeoutMs < 0 && _log.shouldInfo())
|
||||
_log.info("Resched. timeout < 0: " + this + " timeout = " + timeoutMs + " state: " + _state);
|
||||
timeoutMs = 1;
|
||||
}
|
||||
final long now = System.currentTimeMillis();
|
||||
@@ -423,7 +423,8 @@ public class SimpleTimer2 {
|
||||
long delay = 0;
|
||||
synchronized(this) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
_log.warn("I was interrupted in run, state "+_state+" event "+this);
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("I was interrupted in run, state "+_state+" event "+this);
|
||||
return;
|
||||
}
|
||||
if (_rescheduleAfterRun)
|
||||
@@ -431,6 +432,8 @@ public class SimpleTimer2 {
|
||||
|
||||
switch(_state) {
|
||||
case CANCELLED:
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Not actually running: CANCELLED " + this);
|
||||
return; // goodbye
|
||||
case IDLE: // fall through
|
||||
case RUNNING:
|
||||
@@ -444,6 +447,8 @@ public class SimpleTimer2 {
|
||||
if (difference > _fuzz) {
|
||||
// proceed, switch to IDLE to reschedule
|
||||
_state = TimedEventState.IDLE;
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Early execution, Rescheduling for " + difference + " later: " + this);
|
||||
schedule(difference);
|
||||
return;
|
||||
}
|
||||
@@ -485,6 +490,8 @@ public class SimpleTimer2 {
|
||||
// do we need to reschedule?
|
||||
if (_rescheduleAfterRun) {
|
||||
_rescheduleAfterRun = false;
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Reschedule after run: " + this);
|
||||
schedule(_nextRun - System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
@@ -494,6 +501,8 @@ public class SimpleTimer2 {
|
||||
long time = System.currentTimeMillis() - before;
|
||||
if (time > 500 && _log.shouldLog(Log.WARN))
|
||||
_log.warn(_pool + " event execution took " + time + ": " + this);
|
||||
else if (_log.shouldDebug())
|
||||
_log.debug("Execution finished in " + time + ": " + this);
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
// this call is slow - iterates through a HashMap -
|
||||
// would be better to have a local AtomicLong if we care
|
||||
@@ -537,7 +546,7 @@ public class SimpleTimer2 {
|
||||
* @since 0.9.20
|
||||
*/
|
||||
private static abstract class PeriodicTimedEvent extends TimedEvent {
|
||||
private long _timeoutMs;
|
||||
private final long _timeoutMs;
|
||||
|
||||
/**
|
||||
* Schedule periodic event
|
||||
@@ -556,7 +565,13 @@ public class SimpleTimer2 {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
schedule(_timeoutMs);
|
||||
synchronized(this) {
|
||||
// Task may have rescheduled itself without actually running.
|
||||
// If we schedule again, it will be stuck in a scheduling loop.
|
||||
// This happens after a backwards clock shift.
|
||||
if (_state == TimedEventState.IDLE)
|
||||
schedule(_timeoutMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class RAIFile implements RandomAccessInterface, DataInput, DataOutput {
|
||||
private File f;
|
||||
private RandomAccessFile delegate;
|
||||
private final File f;
|
||||
private final RandomAccessFile delegate;
|
||||
private final boolean r, w;
|
||||
|
||||
public RAIFile(RandomAccessFile file) throws FileNotFoundException {
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2006, Matthew Estes
|
||||
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 Metanotion Software 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 net.metanotion.io;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.metanotion.io.Serializer;
|
||||
|
||||
public abstract class SerialStreams implements Serializer {
|
||||
public byte[] getBytes(Object o) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
writeOut(dos, o);
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) { throw new Error(); }
|
||||
}
|
||||
|
||||
public Object construct(byte[] b) {
|
||||
try {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(b);
|
||||
DataInputStream dis = new DataInputStream(bais);
|
||||
return readIn(dis);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
abstract public void writeOut(DataOutputStream dos, Object o) throws IOException;
|
||||
abstract public Object readIn(DataInputStream dis) throws IOException;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.metanotion.io;
|
||||
|
||||
public interface Serializer {
|
||||
public byte[] getBytes(Object o);
|
||||
public Object construct(byte[] b);
|
||||
public interface Serializer<T> {
|
||||
public byte[] getBytes(T o);
|
||||
public T construct(byte[] b);
|
||||
}
|
||||
|
||||
@@ -32,8 +32,11 @@ import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import net.metanotion.io.RAIFile;
|
||||
@@ -94,7 +97,7 @@ public class BlockFile implements Closeable {
|
||||
/** I2P was the file locked when we opened it? */
|
||||
private final boolean _wasMounted;
|
||||
|
||||
private final BSkipList metaIndex;
|
||||
private final BSkipList<String, Integer> metaIndex;
|
||||
private boolean _isClosed;
|
||||
/** cached list of free pages, only valid if freListStart > 0 */
|
||||
private FreeListBlock flb;
|
||||
@@ -320,7 +323,7 @@ public class BlockFile implements Closeable {
|
||||
if (rai.canWrite())
|
||||
mount();
|
||||
|
||||
metaIndex = new BSkipList(spanSize, this, METAINDEX_PAGE, new StringBytes(), new IntBytes());
|
||||
metaIndex = new BSkipList<String, Integer>(spanSize, this, METAINDEX_PAGE, new StringBytes(), new IntBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,18 +434,25 @@ public class BlockFile implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a skiplist if it exists.
|
||||
* Returns null if the skiplist does not exist.
|
||||
* Empty skiplists are not preserved after close.
|
||||
*
|
||||
* If the file is writable, this runs an integrity check and repair
|
||||
* on first open.
|
||||
*
|
||||
* @return null if not found
|
||||
*/
|
||||
public BSkipList getIndex(String name, Serializer key, Serializer val) throws IOException {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K extends Comparable<? super K>, V> BSkipList<K, V> getIndex(String name, Serializer<K> key, Serializer<V> val) throws IOException {
|
||||
// added I2P
|
||||
BSkipList bsl = openIndices.get(name);
|
||||
BSkipList<K, V> bsl = (BSkipList<K, V>) openIndices.get(name);
|
||||
if (bsl != null)
|
||||
return bsl;
|
||||
|
||||
Integer page = (Integer) metaIndex.get(name);
|
||||
Integer page = metaIndex.get(name);
|
||||
if (page == null) { return null; }
|
||||
bsl = new BSkipList(spanSize, this, page.intValue(), key, val, true);
|
||||
bsl = new BSkipList<K, V>(spanSize, this, page.intValue(), key, val, true);
|
||||
if (file.canWrite()) {
|
||||
log.info("Checking skiplist " + name + " in blockfile " + file);
|
||||
if (bsl.bslck(true, false))
|
||||
@@ -454,25 +464,43 @@ public class BlockFile implements Closeable {
|
||||
return bsl;
|
||||
}
|
||||
|
||||
public BSkipList makeIndex(String name, Serializer key, Serializer val) throws IOException {
|
||||
/**
|
||||
* Create and open a new skiplist if it does not exist.
|
||||
* Throws IOException if it already exists.
|
||||
*
|
||||
* @throws IOException if already exists or other errors
|
||||
*/
|
||||
public <K extends Comparable<? super K>, V> BSkipList<K, V> makeIndex(String name, Serializer<K> key, Serializer<V> val) throws IOException {
|
||||
if(metaIndex.get(name) != null) { throw new IOException("Index already exists"); }
|
||||
int page = allocPage();
|
||||
metaIndex.put(name, Integer.valueOf(page));
|
||||
BSkipList.init(this, page, spanSize);
|
||||
BSkipList bsl = new BSkipList(spanSize, this, page, key, val, true);
|
||||
BSkipList<K, V> bsl = new BSkipList<K, V>(spanSize, this, page, key, val, true);
|
||||
openIndices.put(name, bsl);
|
||||
return bsl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a skiplist if it exists.
|
||||
* Must be open. Throws IOException if exists but is closed.
|
||||
* Broken before 0.9.26.
|
||||
*
|
||||
* @throws IOException if it is closed.
|
||||
*/
|
||||
public void delIndex(String name) throws IOException {
|
||||
Integer page = (Integer) metaIndex.remove(name);
|
||||
if (page == null) { return; }
|
||||
Serializer nb = new IdentityBytes();
|
||||
BSkipList bsl = new BSkipList(spanSize, this, page.intValue(), nb, nb, true);
|
||||
if (metaIndex.get(name) == null)
|
||||
return;
|
||||
BSkipList bsl = openIndices.get(name);
|
||||
if (bsl == null)
|
||||
throw new IOException("Cannot delete closed skiplist, open it first: " + name);
|
||||
bsl.delete();
|
||||
openIndices.remove(name);
|
||||
metaIndex.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a skiplist if it is open.
|
||||
*
|
||||
* Added I2P
|
||||
*/
|
||||
public void closeIndex(String name) {
|
||||
@@ -482,8 +510,75 @@ public class BlockFile implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reformat a skiplist with new Serializers if it exists.
|
||||
* The skiplist must be closed.
|
||||
* Throws IOException if the skiplist is open.
|
||||
* The skiplist will remain closed after completion.
|
||||
*
|
||||
* @throws IOException if it is open or on errors
|
||||
* @since 0.9.26
|
||||
*/
|
||||
public <K extends Comparable<? super K>, V> void reformatIndex(String name, Serializer<K> oldKey, Serializer<V> oldVal,
|
||||
Serializer<K> newKey, Serializer<V> newVal) throws IOException {
|
||||
if (openIndices.containsKey(name))
|
||||
throw new IOException("Cannot reformat open skiplist " + name);
|
||||
BSkipList<K, V> old = getIndex(name, oldKey, oldVal);
|
||||
if (old == null)
|
||||
return;
|
||||
long start = System.currentTimeMillis();
|
||||
String tmpName = "---tmp---" + name + "---tmp---";
|
||||
BSkipList<K, V> tmp = getIndex(tmpName, newKey, newVal);
|
||||
if (tmp != null) {
|
||||
log.logAlways(Log.WARN, "Continuing on aborted reformat of list " + name);
|
||||
} else {
|
||||
tmp = makeIndex(tmpName, newKey, newVal);
|
||||
}
|
||||
|
||||
// It could be much more efficient to do this at the
|
||||
// SkipSpan layer but that's way too hard.
|
||||
final int loop = 32;
|
||||
List<K> keys = new ArrayList<K>(loop);
|
||||
List<V> vals = new ArrayList<V>(loop);
|
||||
while (true) {
|
||||
SkipIterator<K, V> iter = old.iterator();
|
||||
for (int i = 0; iter.hasNext() && i < loop; i++) {
|
||||
try {
|
||||
keys.add(iter.nextKey());
|
||||
vals.add(iter.next());
|
||||
} catch (NoSuchElementException nsee) {
|
||||
throw new IOException("Unable to reformat corrupt list " + name, nsee);
|
||||
}
|
||||
}
|
||||
// save state, as deleting corrupts the iterator
|
||||
boolean done = !iter.hasNext();
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
tmp.put(keys.get(i), vals.get(i));
|
||||
}
|
||||
for (int i = keys.size() - 1; i >= 0; i--) {
|
||||
old.remove(keys.get(i));
|
||||
}
|
||||
if (done)
|
||||
break;
|
||||
keys.clear();
|
||||
vals.clear();
|
||||
}
|
||||
|
||||
delIndex(name);
|
||||
closeIndex(name);
|
||||
closeIndex(tmpName);
|
||||
Integer page = metaIndex.get(tmpName);
|
||||
metaIndex.put(name, page);
|
||||
metaIndex.remove(tmpName);
|
||||
if (log.shouldWarn())
|
||||
log.warn("reformatted list: " + name + " in " +
|
||||
(System.currentTimeMillis() - start) + "ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all open skiplists and then the blockfile itself.
|
||||
*
|
||||
* Note (I2P)
|
||||
* Does NOT close the RAF / RAI.
|
||||
* Does NOT close the RAF / RAI.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
// added I2P
|
||||
@@ -539,9 +634,15 @@ public class BlockFile implements Closeable {
|
||||
try {
|
||||
// This uses IdentityBytes, so the value class won't be right, but at least
|
||||
// it won't fail the out-of-order check
|
||||
Serializer keyser = slname.equals("%%__REVERSE__%%") ? new IntBytes() : new UTF8StringBytes();
|
||||
BSkipList bsl = getIndex(slname, keyser, new IdentityBytes());
|
||||
if (bsl == null) {
|
||||
boolean fail;
|
||||
if (slname.equals("%%__REVERSE__%%")) {
|
||||
Serializer<Integer> keyser = new IntBytes();
|
||||
fail = getIndex(slname, keyser, new IdentityBytes()) == null;
|
||||
} else {
|
||||
Serializer<String> keyser = new UTF8StringBytes();
|
||||
fail = getIndex(slname, keyser, new IdentityBytes()) == null;
|
||||
}
|
||||
if (fail) {
|
||||
log.error("Can't find list? " + slname);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ import net.i2p.util.Log;
|
||||
*
|
||||
* Always fits on one page.
|
||||
*/
|
||||
public class BSkipLevels extends SkipLevels {
|
||||
public class BSkipLevels<K extends Comparable<? super K>, V> extends SkipLevels<K, V> {
|
||||
private static final long MAGIC = 0x42534c6576656c73l; // "BSLevels"
|
||||
static final int HEADER_LEN = 16;
|
||||
public final int levelPage;
|
||||
public final int spanPage;
|
||||
public final BlockFile bf;
|
||||
private final BSkipList bsl;
|
||||
private final BSkipList<K, V> bsl;
|
||||
private boolean isKilled;
|
||||
// the level pages, passed from the constructor to initializeLevels(),
|
||||
// NOT kept up to date
|
||||
@@ -73,7 +73,8 @@ public class BSkipLevels extends SkipLevels {
|
||||
* after the constructor, unless it's a new empty
|
||||
* level and init() was previously called.
|
||||
*/
|
||||
public BSkipLevels(BlockFile bf, int levelPage, BSkipList bsl) throws IOException {
|
||||
@SuppressWarnings("unchecked")
|
||||
public BSkipLevels(BlockFile bf, int levelPage, BSkipList<K, V> bsl) throws IOException {
|
||||
this.levelPage = levelPage;
|
||||
this.bf = bf;
|
||||
this.bsl = bsl;
|
||||
@@ -97,7 +98,7 @@ public class BSkipLevels extends SkipLevels {
|
||||
throw new IOException("No span found in cache???");
|
||||
}
|
||||
|
||||
this.levels = new BSkipLevels[maxLen];
|
||||
this.levels = (BSkipLevels<K, V>[]) new BSkipLevels[maxLen];
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Reading New BSkipLevels with " + nonNull + " / " + maxLen + " valid levels page " + levelPage +
|
||||
" in skiplist " + bsl);
|
||||
@@ -118,14 +119,14 @@ public class BSkipLevels extends SkipLevels {
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void initializeLevels() {
|
||||
List<BSkipLevels> toInit = new ArrayList<BSkipLevels>(32);
|
||||
List<BSkipLevels> nextInit = new ArrayList<BSkipLevels>(32);
|
||||
List<BSkipLevels<K, V>> toInit = new ArrayList<BSkipLevels<K, V>>(32);
|
||||
List<BSkipLevels<K, V>> nextInit = new ArrayList<BSkipLevels<K, V>>(32);
|
||||
initializeLevels(toInit);
|
||||
while (!toInit.isEmpty()) {
|
||||
for (BSkipLevels bsl : toInit) {
|
||||
for (BSkipLevels<K, V> bsl : toInit) {
|
||||
bsl.initializeLevels(nextInit);
|
||||
}
|
||||
List<BSkipLevels> tmp = toInit;
|
||||
List<BSkipLevels<K, V>> tmp = toInit;
|
||||
toInit = nextInit;
|
||||
nextInit = tmp;
|
||||
nextInit.clear();
|
||||
@@ -139,7 +140,7 @@ public class BSkipLevels extends SkipLevels {
|
||||
* @param nextInit out parameter, next levels to initialize
|
||||
* @since 0.9.20
|
||||
*/
|
||||
private void initializeLevels(List<BSkipLevels> nextInit) {
|
||||
private void initializeLevels(List<BSkipLevels<K, V>> nextInit) {
|
||||
boolean fail = false;
|
||||
for(int i = 0; i < lps.length; i++) {
|
||||
int lp = lps[i];
|
||||
@@ -147,7 +148,7 @@ public class BSkipLevels extends SkipLevels {
|
||||
levels[i] = bsl.levelHash.get(Integer.valueOf(lp));
|
||||
if(levels[i] == null) {
|
||||
try {
|
||||
BSkipLevels lev = new BSkipLevels(bf, lp, bsl);
|
||||
BSkipLevels<K, V> lev = new BSkipLevels<K, V>(bf, lp, bsl);
|
||||
levels[i] = lev;
|
||||
nextInit.add(lev);
|
||||
} catch (IOException ioe) {
|
||||
@@ -158,8 +159,8 @@ public class BSkipLevels extends SkipLevels {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Comparable ourKey = key();
|
||||
Comparable nextKey = levels[i].key();
|
||||
K ourKey = key();
|
||||
K nextKey = levels[i].key();
|
||||
if (ourKey != null && nextKey != null &&
|
||||
ourKey.compareTo(nextKey) >= 0) {
|
||||
bf.log.warn("Corrupt database, level out of order " + this +
|
||||
@@ -215,9 +216,9 @@ public class BSkipLevels extends SkipLevels {
|
||||
break;
|
||||
}
|
||||
bf.file.writeShort(i);
|
||||
bf.file.writeInt(((BSkipSpan) bottom).page);
|
||||
bf.file.writeInt(((BSkipSpan<K, V>) bottom).page);
|
||||
for(int j = 0; j < i; j++) {
|
||||
bf.file.writeInt(((BSkipLevels) levels[j]).levelPage);
|
||||
bf.file.writeInt(((BSkipLevels<K, V>) levels[j]).levelPage);
|
||||
}
|
||||
} catch (IOException ioe) { throw new RuntimeException("Error writing to database", ioe); }
|
||||
}
|
||||
@@ -229,22 +230,22 @@ public class BSkipLevels extends SkipLevels {
|
||||
return;
|
||||
}
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Killing " + this + ' ' + print(), new Exception());
|
||||
bf.log.debug("Killing " + this + ' ' + print() /* , new Exception() */ );
|
||||
isKilled = true;
|
||||
bsl.levelHash.remove(Integer.valueOf(levelPage));
|
||||
bf.freePage(levelPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipLevels newInstance(int levels, SkipSpan ss, SkipList sl) {
|
||||
public SkipLevels<K, V> newInstance(int levels, SkipSpan<K, V> ss, SkipList<K, V> sl) {
|
||||
try {
|
||||
BSkipSpan bss = (BSkipSpan) ss;
|
||||
BSkipList bsl = (BSkipList) sl;
|
||||
BSkipSpan<K, V> bss = (BSkipSpan<K, V>) ss;
|
||||
BSkipList<K, V> bsl = (BSkipList<K, V>) sl;
|
||||
int page = bf.allocPage();
|
||||
BSkipLevels.init(bf, page, bss.page, levels);
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("New BSkipLevels height " + levels + " page " + page);
|
||||
return new BSkipLevels(bf, page, bsl);
|
||||
return new BSkipLevels<K, V>(bf, page, bsl);
|
||||
// do not need to call initLevels() here
|
||||
} catch (IOException ioe) { throw new RuntimeException("Error creating database page", ioe); }
|
||||
}
|
||||
@@ -273,7 +274,7 @@ public class BSkipLevels extends SkipLevels {
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private boolean blvlfix() {
|
||||
TreeSet<SkipLevels> lvls = new TreeSet<SkipLevels>(new LevelComparator());
|
||||
TreeSet<SkipLevels<K, V>> lvls = new TreeSet<SkipLevels<K, V>>(new LevelComparator<K, V>());
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Starting level search");
|
||||
getAllLevels(this, lvls);
|
||||
@@ -285,15 +286,15 @@ public class BSkipLevels extends SkipLevels {
|
||||
}
|
||||
// traverse the levels, back-to-front
|
||||
boolean rv = false;
|
||||
SkipLevels after = null;
|
||||
for (SkipLevels lv : lvls) {
|
||||
SkipLevels<K, V> after = null;
|
||||
for (SkipLevels<K, V> lv : lvls) {
|
||||
boolean modified = false;
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Checking " + lv.print());
|
||||
if (after != null) {
|
||||
int min = Math.min(after.levels.length, lv.levels.length);
|
||||
for (int i = 0; i < min; i++) {
|
||||
SkipLevels cur = lv.levels[i];
|
||||
SkipLevels<K, V> cur = lv.levels[i];
|
||||
if (cur != after) {
|
||||
if (cur != null)
|
||||
bf.log.warn("Level " + i + " was wrong, fixing for " + lv.print());
|
||||
@@ -331,12 +332,12 @@ public class BSkipLevels extends SkipLevels {
|
||||
* @param lvlSet out parameter, the result
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private void getAllLevels(SkipLevels l, Set<SkipLevels> lvlSet) {
|
||||
private void getAllLevels(SkipLevels<K, V> l, Set<SkipLevels<K, V>> lvlSet) {
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("GAL " + l.print());
|
||||
// Do level 0 without recursion, on the assumption everything is findable
|
||||
// from the root
|
||||
SkipLevels cur = l;
|
||||
SkipLevels<K, V> cur = l;
|
||||
while (cur != null && lvlSet.add(cur)) {
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Adding " + cur.print());
|
||||
@@ -347,7 +348,7 @@ public class BSkipLevels extends SkipLevels {
|
||||
// If there were no nulls at level 0 in the middle,
|
||||
// i.e. there are no problems, this won't find anything
|
||||
for (int i = 1; i < l.levels.length; i++) {
|
||||
SkipLevels lv = l.levels[i];
|
||||
SkipLevels<K, V> lv = l.levels[i];
|
||||
if (lv != null && !lvlSet.contains(lv))
|
||||
getAllLevels(lv, lvlSet);
|
||||
}
|
||||
@@ -358,10 +359,10 @@ public class BSkipLevels extends SkipLevels {
|
||||
* Sorts in REVERSE order.
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private static class LevelComparator implements Comparator<SkipLevels>, Serializable {
|
||||
public int compare(SkipLevels l, SkipLevels r) {
|
||||
Comparable lk = l.key();
|
||||
Comparable rk = r.key();
|
||||
private static class LevelComparator<K extends Comparable<? super K>, V> implements Comparator<SkipLevels<K, V>>, Serializable {
|
||||
public int compare(SkipLevels<K, V> l, SkipLevels<K, V> r) {
|
||||
K lk = l.key();
|
||||
K rk = r.key();
|
||||
if (lk == null && rk == null)
|
||||
return 0;
|
||||
if (lk == null)
|
||||
@@ -378,13 +379,14 @@ public class BSkipLevels extends SkipLevels {
|
||||
* This needs work.
|
||||
*/
|
||||
@Override
|
||||
public boolean blvlck(boolean fix, int width, SkipLevels[] prevLevels) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean blvlck(boolean fix, int width, SkipLevels<K, V>[] prevLevels) {
|
||||
bf.log.warn(" Skip level at width " + width);
|
||||
bf.log.warn(" levels " + this.levels.length);
|
||||
bf.log.warn(" first key " + this.key());
|
||||
bf.log.warn(" spanPage " + this.spanPage);
|
||||
bf.log.warn(" levelPage " + this.levelPage);
|
||||
SkipLevels higher = null;
|
||||
SkipLevels<K, V> higher = null;
|
||||
for (int i = levels.length - 1; i >= 0; i--) {
|
||||
if (levels[i] != null) {
|
||||
bf.log.info(" level " + i + " -> " + levels[i].key() + " ");
|
||||
@@ -418,7 +420,7 @@ public class BSkipLevels extends SkipLevels {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prevLevels = new SkipLevels[levels.length];
|
||||
prevLevels = (SkipLevels<K, V>[]) new SkipLevels[levels.length];
|
||||
System.arraycopy(levels, 0, prevLevels, 0, levels.length);
|
||||
}
|
||||
if (levels[0] != null)
|
||||
|
||||
@@ -51,7 +51,7 @@ import net.i2p.util.Log;
|
||||
*
|
||||
* Always fits on one page.
|
||||
*/
|
||||
public class BSkipList extends SkipList implements Closeable {
|
||||
public class BSkipList<K extends Comparable<? super K>, V> extends SkipList<K, V> implements Closeable {
|
||||
private static final long MAGIC = 0x536b69704c697374l; // "SkipList"
|
||||
public int firstSpanPage = 0;
|
||||
public int firstLevelPage = 0;
|
||||
@@ -59,16 +59,16 @@ public class BSkipList extends SkipList implements Closeable {
|
||||
public final BlockFile bf;
|
||||
private boolean isClosed;
|
||||
|
||||
final HashMap<Integer, BSkipSpan> spanHash = new HashMap<Integer, BSkipSpan>();
|
||||
final HashMap<Integer, SkipLevels> levelHash = new HashMap<Integer, SkipLevels>();
|
||||
final HashMap<Integer, BSkipSpan<K, V>> spanHash = new HashMap<Integer, BSkipSpan<K, V>>();
|
||||
final HashMap<Integer, SkipLevels<K, V>> levelHash = new HashMap<Integer, SkipLevels<K, V>>();
|
||||
|
||||
private final boolean fileOnly;
|
||||
|
||||
public BSkipList(int spanSize, BlockFile bf, int skipPage, Serializer key, Serializer val) throws IOException {
|
||||
public BSkipList(int spanSize, BlockFile bf, int skipPage, Serializer<K> key, Serializer<V> val) throws IOException {
|
||||
this(spanSize, bf, skipPage, key, val, false);
|
||||
}
|
||||
|
||||
public BSkipList(int spanSize, BlockFile bf, int skipPage, Serializer key, Serializer val, boolean fileOnly) throws IOException {
|
||||
public BSkipList(int spanSize, BlockFile bf, int skipPage, Serializer<K> key, Serializer<V> val, boolean fileOnly) throws IOException {
|
||||
if(spanSize < 1) { throw new RuntimeException("Span size too small"); }
|
||||
|
||||
this.skipPage = skipPage;
|
||||
@@ -89,10 +89,10 @@ public class BSkipList extends SkipList implements Closeable {
|
||||
|
||||
this.fileOnly = fileOnly;
|
||||
if (fileOnly)
|
||||
first = new IBSkipSpan(bf, this, firstSpanPage, key, val);
|
||||
first = new IBSkipSpan<K, V>(bf, this, firstSpanPage, key, val);
|
||||
else
|
||||
first = new BSkipSpan(bf, this, firstSpanPage, key, val);
|
||||
BSkipLevels bstack = new BSkipLevels(bf, firstLevelPage, this);
|
||||
first = new BSkipSpan<K, V>(bf, this, firstSpanPage, key, val);
|
||||
BSkipLevels<K, V> bstack = new BSkipLevels<K, V>(bf, firstLevelPage, this);
|
||||
bstack.initializeLevels();
|
||||
stack = bstack;
|
||||
int total = 0;
|
||||
@@ -104,7 +104,8 @@ public class BSkipList extends SkipList implements Closeable {
|
||||
if (bf.file.canWrite() &&
|
||||
(levelCount != levelHash.size() || spans != spanHash.size() || size != total)) {
|
||||
if (bf.log.shouldLog(Log.WARN))
|
||||
bf.log.warn("On-disk counts were " + levelCount + " / " + spans + " / " + size + ", correcting");
|
||||
bf.log.warn("On-disk counts were " + levelCount + " levels / " + spans +
|
||||
" spans / " + size + " entries, correcting to " + total + " entries");
|
||||
size = total;
|
||||
flush();
|
||||
}
|
||||
@@ -151,7 +152,6 @@ public class BSkipList extends SkipList implements Closeable {
|
||||
curLevel.killInstance();
|
||||
curLevel = nextLevel;
|
||||
}
|
||||
stack.killInstance();
|
||||
|
||||
SkipSpan curSpan = first;
|
||||
while(curSpan != null) {
|
||||
@@ -200,33 +200,35 @@ public class BSkipList extends SkipList implements Closeable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipIterator iterator() {
|
||||
public SkipIterator<K, V> iterator() {
|
||||
if (!this.fileOnly)
|
||||
return super.iterator();
|
||||
return new IBSkipIterator(first, 0);
|
||||
return new IBSkipIterator<K, V>(first, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipIterator min() {
|
||||
/****
|
||||
//@Override
|
||||
public SkipIterator<K, V> min() {
|
||||
return iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipIterator max() {
|
||||
//@Override
|
||||
public SkipIterator<K, V> max() {
|
||||
if (!this.fileOnly)
|
||||
return super.max();
|
||||
SkipSpan ss = stack.getEnd();
|
||||
return new IBSkipIterator(ss, ss.nKeys - 1);
|
||||
SkipSpan<K, V> ss = stack.getEnd();
|
||||
return new IBSkipIterator<K, V>(ss, ss.nKeys - 1);
|
||||
}
|
||||
****/
|
||||
|
||||
@Override
|
||||
public SkipIterator find(Comparable key) {
|
||||
public SkipIterator<K, V> find(K key) {
|
||||
if (!this.fileOnly)
|
||||
return super.find(key);
|
||||
int[] search = new int[1];
|
||||
SkipSpan ss = stack.getSpan(stack.levels.length - 1, key, search);
|
||||
SkipSpan<K, V> ss = stack.getSpan(stack.levels.length - 1, key, search);
|
||||
if(search[0] < 0) { search[0] = -1 * (search[0] + 1); }
|
||||
return new IBSkipIterator(ss, search[0]);
|
||||
return new IBSkipIterator<K, V>(ss, search[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,19 +59,19 @@ import net.i2p.util.Log;
|
||||
* next overflow page (unsigned int)
|
||||
*</pre>
|
||||
*/
|
||||
public class BSkipSpan extends SkipSpan {
|
||||
public class BSkipSpan<K extends Comparable<? super K>, V> extends SkipSpan<K, V> {
|
||||
protected static final int MAGIC = 0x5370616e; // "Span"
|
||||
protected static final int HEADER_LEN = 20;
|
||||
public static final int CONT_HEADER_LEN = 8;
|
||||
protected final BlockFile bf;
|
||||
private final BSkipList bsl;
|
||||
private final BSkipList<K, V> bsl;
|
||||
protected int page;
|
||||
protected int overflowPage;
|
||||
|
||||
protected int prevPage;
|
||||
protected int nextPage = 0;
|
||||
protected Serializer keySer;
|
||||
protected Serializer valSer;
|
||||
protected Serializer<K> keySer;
|
||||
protected Serializer<V> valSer;
|
||||
|
||||
// I2P
|
||||
protected int spanSize;
|
||||
@@ -88,11 +88,11 @@ public class BSkipSpan extends SkipSpan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipSpan newInstance(SkipList sl) {
|
||||
public SkipSpan<K, V> newInstance(SkipList<K, V> sl) {
|
||||
try {
|
||||
int newPage = bf.allocPage();
|
||||
init(bf, newPage, bf.spanSize);
|
||||
return new BSkipSpan(bf, (BSkipList) sl, newPage, keySer, valSer);
|
||||
return new BSkipSpan<K, V>(bf, (BSkipList<K, V>) sl, newPage, keySer, valSer);
|
||||
} catch (IOException ioe) { throw new RuntimeException("Error creating database page", ioe); }
|
||||
}
|
||||
|
||||
@@ -237,7 +237,8 @@ public class BSkipSpan extends SkipSpan {
|
||||
//bsl.flush();
|
||||
}
|
||||
|
||||
private static void load(BSkipSpan bss, BlockFile bf, BSkipList bsl, int spanPage, Serializer key, Serializer val) throws IOException {
|
||||
private static <X extends Comparable<? super X>, Y> void load(BSkipSpan<X, Y> bss, BlockFile bf, BSkipList<X, Y> bsl,
|
||||
int spanPage, Serializer<X> key, Serializer<Y> val) throws IOException {
|
||||
loadInit(bss, bf, bsl, spanPage, key, val);
|
||||
bss.loadData();
|
||||
}
|
||||
@@ -246,7 +247,8 @@ public class BSkipSpan extends SkipSpan {
|
||||
* I2P - first half of load()
|
||||
* Only read the span headers
|
||||
*/
|
||||
protected static void loadInit(BSkipSpan bss, BlockFile bf, BSkipList bsl, int spanPage, Serializer key, Serializer val) throws IOException {
|
||||
protected static <X extends Comparable<? super X>, Y> void loadInit(BSkipSpan<X, Y> bss, BlockFile bf, BSkipList<X, Y> bsl,
|
||||
int spanPage, Serializer<X> key, Serializer<Y> val) throws IOException {
|
||||
if (bss.isKilled)
|
||||
throw new IOException("Already killed!! " + bss);
|
||||
bss.page = spanPage;
|
||||
@@ -285,11 +287,12 @@ public class BSkipSpan extends SkipSpan {
|
||||
* Load the whole span's keys and values into memory
|
||||
* @param flushOnError set to false if you are going to flush anyway
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadData(boolean flushOnError) throws IOException {
|
||||
if (isKilled)
|
||||
throw new IOException("Already killed!! " + this);
|
||||
this.keys = new Comparable[this.spanSize];
|
||||
this.vals = new Object[this.spanSize];
|
||||
this.keys = (K[]) new Comparable[this.spanSize];
|
||||
this.vals = (V[]) new Object[this.spanSize];
|
||||
|
||||
int ksz, vsz;
|
||||
int curPage = this.page;
|
||||
@@ -327,7 +330,7 @@ public class BSkipSpan extends SkipSpan {
|
||||
break;
|
||||
}
|
||||
// System.out.println("i=" + i + ", Page " + curPage + ", offset " + pageCounter[0] + " ksz " + ksz + " vsz " + vsz);
|
||||
this.keys[i] = (Comparable) this.keySer.construct(k);
|
||||
this.keys[i] = this.keySer.construct(k);
|
||||
this.vals[i] = this.valSer.construct(v);
|
||||
// Drop bad entry without throwing exception
|
||||
if (this.keys[i] == null || this.vals[i] == null) {
|
||||
@@ -377,31 +380,31 @@ public class BSkipSpan extends SkipSpan {
|
||||
}
|
||||
}
|
||||
|
||||
protected BSkipSpan(BlockFile bf, BSkipList bsl) {
|
||||
protected BSkipSpan(BlockFile bf, BSkipList<K, V> bsl) {
|
||||
this.bf = bf;
|
||||
this.bsl = bsl;
|
||||
}
|
||||
|
||||
public BSkipSpan(BlockFile bf, BSkipList bsl, int spanPage, Serializer key, Serializer val) throws IOException {
|
||||
public BSkipSpan(BlockFile bf, BSkipList<K, V> bsl, int spanPage, Serializer<K> key, Serializer<V> val) throws IOException {
|
||||
this.bf = bf;
|
||||
this.bsl = bsl;
|
||||
BSkipSpan.load(this, bf, bsl, spanPage, key, val);
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
|
||||
BSkipSpan bss = this;
|
||||
BSkipSpan<K, V> bss = this;
|
||||
// findbugs ok (set in load() above)
|
||||
int np = nextPage;
|
||||
while(np != 0) {
|
||||
BSkipSpan temp = bsl.spanHash.get(Integer.valueOf(np));
|
||||
BSkipSpan<K, V> temp = bsl.spanHash.get(Integer.valueOf(np));
|
||||
if(temp != null) {
|
||||
bss.next = temp;
|
||||
break;
|
||||
}
|
||||
bss.next = new BSkipSpan(bf, bsl);
|
||||
bss.next = new BSkipSpan<K, V>(bf, bsl);
|
||||
bss.next.next = null;
|
||||
bss.next.prev = bss;
|
||||
bss = (BSkipSpan) bss.next;
|
||||
bss = (BSkipSpan<K, V>) bss.next;
|
||||
|
||||
BSkipSpan.load(bss, bf, bsl, np, key, val);
|
||||
np = bss.nextPage;
|
||||
@@ -411,15 +414,15 @@ public class BSkipSpan extends SkipSpan {
|
||||
bss = this;
|
||||
np = prevPage;
|
||||
while(np != 0) {
|
||||
BSkipSpan temp = bsl.spanHash.get(Integer.valueOf(np));
|
||||
BSkipSpan<K, V> temp = bsl.spanHash.get(Integer.valueOf(np));
|
||||
if(temp != null) {
|
||||
bss.prev = temp;
|
||||
break;
|
||||
}
|
||||
bss.prev = new BSkipSpan(bf, bsl);
|
||||
bss.prev = new BSkipSpan<K, V>(bf, bsl);
|
||||
bss.prev.next = bss;
|
||||
bss.prev.prev = null;
|
||||
bss = (BSkipSpan) bss.prev;
|
||||
bss = (BSkipSpan<K, V>) bss.prev;
|
||||
|
||||
BSkipSpan.load(bss, bf, bsl, np, key, val);
|
||||
np = bss.prevPage;
|
||||
|
||||
@@ -41,9 +41,9 @@ import net.metanotion.util.skiplist.SkipSpan;
|
||||
If the caller does not iterate all the way through, the last span
|
||||
will remain in memory.
|
||||
*/
|
||||
public class IBSkipIterator extends SkipIterator {
|
||||
public class IBSkipIterator<K extends Comparable<? super K>, V> extends SkipIterator<K, V> {
|
||||
|
||||
public IBSkipIterator(SkipSpan ss, int index) {
|
||||
public IBSkipIterator(SkipSpan<K, V> ss, int index) {
|
||||
super(ss, index);
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ public class IBSkipIterator extends SkipIterator {
|
||||
* @throws RuntimeException on IOE
|
||||
*/
|
||||
@Override
|
||||
public Object next() {
|
||||
Object o;
|
||||
public V next() {
|
||||
V o;
|
||||
if(index < ss.nKeys) {
|
||||
if (ss.vals == null) {
|
||||
try {
|
||||
@@ -90,7 +90,7 @@ public class IBSkipIterator extends SkipIterator {
|
||||
* @throws RuntimeException on IOE
|
||||
*/
|
||||
@Override
|
||||
public Comparable nextKey() {
|
||||
public K nextKey() {
|
||||
if(index < ss.nKeys) {
|
||||
if (ss.keys == null) {
|
||||
try {
|
||||
@@ -110,7 +110,7 @@ public class IBSkipIterator extends SkipIterator {
|
||||
* @throws RuntimeException on IOE
|
||||
*/
|
||||
@Override
|
||||
public Object previous() {
|
||||
public V previous() {
|
||||
if(index > 0) {
|
||||
index--;
|
||||
} else if(ss.prev != null) {
|
||||
|
||||
@@ -54,21 +54,22 @@ import net.i2p.util.Log;
|
||||
*
|
||||
* @author zzz
|
||||
*/
|
||||
public class IBSkipSpan extends BSkipSpan {
|
||||
public class IBSkipSpan<K extends Comparable<? super K>, V> extends BSkipSpan<K, V> {
|
||||
|
||||
private Comparable firstKey;
|
||||
private K firstKey;
|
||||
|
||||
@Override
|
||||
public SkipSpan newInstance(SkipList sl) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public SkipSpan<K, V> newInstance(SkipList<K, V> sl) {
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Splitting page " + this.page + " containing " + this.nKeys + '/' + this.spanSize);
|
||||
try {
|
||||
int newPage = bf.allocPage();
|
||||
init(bf, newPage, bf.spanSize);
|
||||
SkipSpan rv = new IBSkipSpan(bf, (BSkipList) sl, newPage, keySer, valSer);
|
||||
SkipSpan<K, V> rv = new IBSkipSpan<K, V>(bf, (BSkipList<K, V>) sl, newPage, keySer, valSer);
|
||||
// this is called after a split, so we need the data arrays initialized
|
||||
rv.keys = new Comparable[bf.spanSize];
|
||||
rv.vals = new Object[bf.spanSize];
|
||||
rv.keys = (K[]) new Comparable[bf.spanSize];
|
||||
rv.vals = (V[]) new Object[bf.spanSize];
|
||||
return rv;
|
||||
} catch (IOException ioe) { throw new RuntimeException("Error creating database page", ioe); }
|
||||
}
|
||||
@@ -125,7 +126,7 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
pageCounter[0] +=4;
|
||||
byte[] k = new byte[ksz];
|
||||
curPage = this.bf.readMultiPageData(k, curPage, pageCounter, curNextPage);
|
||||
this.firstKey = (Comparable) this.keySer.construct(k);
|
||||
this.firstKey = this.keySer.construct(k);
|
||||
if (this.firstKey == null) {
|
||||
bf.log.error("Null deserialized first key in page " + curPage);
|
||||
repair(1);
|
||||
@@ -160,7 +161,7 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
/**
|
||||
* Linear search through the span in the file for the value.
|
||||
*/
|
||||
private Object getData(Comparable key) throws IOException {
|
||||
private V getData(K key) throws IOException {
|
||||
seekData();
|
||||
int curPage = this.page;
|
||||
int[] curNextPage = new int[1];
|
||||
@@ -194,7 +195,7 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
break;
|
||||
}
|
||||
//System.out.println("i=" + i + ", Page " + curPage + ", offset " + pageCounter[0] + " ksz " + ksz + " vsz " + vsz);
|
||||
Comparable ckey = (Comparable) this.keySer.construct(k);
|
||||
K ckey = this.keySer.construct(k);
|
||||
if (ckey == null) {
|
||||
// skip the value and keep going
|
||||
curPage = this.bf.skipMultiPageBytes(vsz, curPage, pageCounter, curNextPage);
|
||||
@@ -213,7 +214,7 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
lostEntries(i, curPage);
|
||||
break;
|
||||
}
|
||||
Object rv = this.valSer.construct(v);
|
||||
V rv = this.valSer.construct(v);
|
||||
if (rv == null) {
|
||||
bf.log.error("Null deserialized value in entry " + i + " page " + curPage +
|
||||
" key=" + ckey);
|
||||
@@ -252,11 +253,11 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
*****/
|
||||
}
|
||||
|
||||
private IBSkipSpan(BlockFile bf, BSkipList bsl) {
|
||||
private IBSkipSpan(BlockFile bf, BSkipList<K, V> bsl) {
|
||||
super(bf, bsl);
|
||||
}
|
||||
|
||||
public IBSkipSpan(BlockFile bf, BSkipList bsl, int spanPage, Serializer key, Serializer val) throws IOException {
|
||||
public IBSkipSpan(BlockFile bf, BSkipList<K, V> bsl, int spanPage, Serializer<K> key, Serializer<V> val) throws IOException {
|
||||
super(bf, bsl);
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("New ibss page " + spanPage);
|
||||
@@ -265,24 +266,24 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
|
||||
IBSkipSpan bss = this;
|
||||
IBSkipSpan temp;
|
||||
IBSkipSpan<K, V> bss = this;
|
||||
IBSkipSpan<K, V> temp;
|
||||
int np = nextPage;
|
||||
while(np != 0) {
|
||||
temp = (IBSkipSpan) bsl.spanHash.get(Integer.valueOf(np));
|
||||
temp = (IBSkipSpan<K, V>) bsl.spanHash.get(Integer.valueOf(np));
|
||||
if(temp != null) {
|
||||
bss.next = temp;
|
||||
break;
|
||||
}
|
||||
bss.next = new IBSkipSpan(bf, bsl);
|
||||
bss.next = new IBSkipSpan<K, V>(bf, bsl);
|
||||
bss.next.next = null;
|
||||
bss.next.prev = bss;
|
||||
Comparable previousFirstKey = bss.firstKey;
|
||||
bss = (IBSkipSpan) bss.next;
|
||||
K previousFirstKey = bss.firstKey;
|
||||
bss = (IBSkipSpan<K, V>) bss.next;
|
||||
|
||||
BSkipSpan.loadInit(bss, bf, bsl, np, key, val);
|
||||
bss.loadFirstKey();
|
||||
Comparable nextFirstKey = bss.firstKey;
|
||||
K nextFirstKey = bss.firstKey;
|
||||
if (previousFirstKey == null || nextFirstKey == null ||
|
||||
previousFirstKey.compareTo(nextFirstKey) >= 0) {
|
||||
// TODO remove, but if we are at the bottom of a level
|
||||
@@ -299,20 +300,20 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
bss = this;
|
||||
np = prevPage;
|
||||
while(np != 0) {
|
||||
temp = (IBSkipSpan) bsl.spanHash.get(Integer.valueOf(np));
|
||||
temp = (IBSkipSpan<K, V>) bsl.spanHash.get(Integer.valueOf(np));
|
||||
if(temp != null) {
|
||||
bss.prev = temp;
|
||||
break;
|
||||
}
|
||||
bss.prev = new IBSkipSpan(bf, bsl);
|
||||
bss.prev = new IBSkipSpan<K, V>(bf, bsl);
|
||||
bss.prev.next = bss;
|
||||
bss.prev.prev = null;
|
||||
Comparable nextFirstKey = bss.firstKey;
|
||||
bss = (IBSkipSpan) bss.prev;
|
||||
K nextFirstKey = bss.firstKey;
|
||||
bss = (IBSkipSpan<K, V>) bss.prev;
|
||||
|
||||
BSkipSpan.loadInit(bss, bf, bsl, np, key, val);
|
||||
bss.loadFirstKey();
|
||||
Comparable previousFirstKey = bss.firstKey;
|
||||
K previousFirstKey = bss.firstKey;
|
||||
if (previousFirstKey == null || nextFirstKey == null ||
|
||||
previousFirstKey.compareTo(nextFirstKey) >= 0) {
|
||||
// TODO remove, but if we are at the bottom of a level
|
||||
@@ -330,7 +331,7 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
* Does not call super, we always store first key here
|
||||
*/
|
||||
@Override
|
||||
public Comparable firstKey() {
|
||||
public K firstKey() {
|
||||
return this.firstKey;
|
||||
}
|
||||
|
||||
@@ -339,13 +340,13 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
* This is called only via SkipList.find()
|
||||
*/
|
||||
@Override
|
||||
public SkipSpan getSpan(Comparable key, int[] search) {
|
||||
public SkipSpan<K, V> getSpan(K key, int[] search) {
|
||||
try {
|
||||
seekAndLoadData();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Error reading database", ioe);
|
||||
}
|
||||
SkipSpan rv = super.getSpan(key, search);
|
||||
SkipSpan<K, V> rv = super.getSpan(key, search);
|
||||
this.keys = null;
|
||||
this.vals = null;
|
||||
return rv;
|
||||
@@ -355,7 +356,7 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
* Linear search if in file, Binary search if in memory
|
||||
*/
|
||||
@Override
|
||||
public Object get(Comparable key) {
|
||||
public V get(K key) {
|
||||
try {
|
||||
if (nKeys == 0) { return null; }
|
||||
if (this.next != null && this.next.firstKey().compareTo(key) <= 0)
|
||||
@@ -370,13 +371,13 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
* Load whole span from file, do the operation, flush out, then null out in-memory data again.
|
||||
*/
|
||||
@Override
|
||||
public SkipSpan put(Comparable key, Object val, SkipList sl) {
|
||||
public SkipSpan<K, V> put(K key, V val, SkipList<K, V> sl) {
|
||||
try {
|
||||
seekAndLoadData();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Error reading database", ioe);
|
||||
}
|
||||
SkipSpan rv = super.put(key, val, sl);
|
||||
SkipSpan<K, V> rv = super.put(key, val, sl);
|
||||
// flush() nulls out the data
|
||||
return rv;
|
||||
}
|
||||
@@ -385,7 +386,7 @@ public class IBSkipSpan extends BSkipSpan {
|
||||
* Load whole span from file, do the operation, flush out, then null out in-memory data again.
|
||||
*/
|
||||
@Override
|
||||
public Object[] remove(Comparable key, SkipList sl) {
|
||||
public Object[] remove(K key, SkipList<K, V> sl) {
|
||||
if (bf.log.shouldLog(Log.DEBUG))
|
||||
bf.log.debug("Remove " + key + " in " + this);
|
||||
if (nKeys <= 0)
|
||||
|
||||
@@ -35,11 +35,11 @@ import net.metanotion.io.Serializer;
|
||||
* Will never return null.
|
||||
* Added by I2P.
|
||||
*/
|
||||
public class IdentityBytes implements Serializer {
|
||||
public class IdentityBytes implements Serializer<byte[]> {
|
||||
|
||||
/** @return byte[] */
|
||||
public byte[] getBytes(Object o) { return (byte[])o; }
|
||||
public byte[] getBytes(byte[] o) { return o; }
|
||||
|
||||
/** @return b */
|
||||
public Object construct(byte[] b) { return b; }
|
||||
public byte[] construct(byte[] b) { return b; }
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ package net.metanotion.io.data;
|
||||
|
||||
import net.metanotion.io.Serializer;
|
||||
|
||||
public class IntBytes implements Serializer {
|
||||
public byte[] getBytes(Object o) {
|
||||
public class IntBytes implements Serializer<Integer> {
|
||||
public byte[] getBytes(Integer o) {
|
||||
byte[] b = new byte[4];
|
||||
int v = ((Integer) o).intValue();
|
||||
int v = o.intValue();
|
||||
b[0] = (byte)(0xff & (v >> 24));
|
||||
b[1] = (byte)(0xff & (v >> 16));
|
||||
b[2] = (byte)(0xff & (v >> 8));
|
||||
@@ -41,7 +41,7 @@ public class IntBytes implements Serializer {
|
||||
return b;
|
||||
}
|
||||
|
||||
public Object construct(byte[] b) {
|
||||
public Integer construct(byte[] b) {
|
||||
int v = (((b[0] & 0xff) << 24) |
|
||||
((b[1] & 0xff) << 16) |
|
||||
((b[2] & 0xff) << 8) |
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2006, Matthew Estes
|
||||
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 Metanotion Software 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 net.metanotion.io.data;
|
||||
|
||||
import net.metanotion.io.Serializer;
|
||||
|
||||
public class LongBytes implements Serializer {
|
||||
public byte[] getBytes(Object o) {
|
||||
byte[] b = new byte[8];
|
||||
long v = ((Long) o).longValue();
|
||||
b[0] = (byte)(0xff & (v >> 56));
|
||||
b[1] = (byte)(0xff & (v >> 48));
|
||||
b[2] = (byte)(0xff & (v >> 40));
|
||||
b[3] = (byte)(0xff & (v >> 32));
|
||||
b[4] = (byte)(0xff & (v >> 24));
|
||||
b[5] = (byte)(0xff & (v >> 16));
|
||||
b[6] = (byte)(0xff & (v >> 8));
|
||||
b[7] = (byte)(0xff & v);
|
||||
return b;
|
||||
}
|
||||
|
||||
public Object construct(byte[] b) {
|
||||
long v =(((long)(b[0] & 0xff) << 56) |
|
||||
((long)(b[1] & 0xff) << 48) |
|
||||
((long)(b[2] & 0xff) << 40) |
|
||||
((long)(b[3] & 0xff) << 32) |
|
||||
((long)(b[4] & 0xff) << 24) |
|
||||
((long)(b[5] & 0xff) << 16) |
|
||||
((long)(b[6] & 0xff) << 8) |
|
||||
(b[7] & 0xff));
|
||||
return Long.valueOf(v);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2006, Matthew Estes
|
||||
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 Metanotion Software 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 net.metanotion.io.data;
|
||||
|
||||
import net.metanotion.io.Serializer;
|
||||
|
||||
public class NullBytes implements Serializer {
|
||||
public byte[] getBytes(Object o) { return null; }
|
||||
public Object construct(byte[] b) { return null; }
|
||||
}
|
||||
@@ -32,14 +32,14 @@ import java.io.UnsupportedEncodingException;
|
||||
|
||||
import net.metanotion.io.Serializer;
|
||||
|
||||
public class StringBytes implements Serializer {
|
||||
public byte[] getBytes(Object o) {
|
||||
public class StringBytes implements Serializer<String> {
|
||||
public byte[] getBytes(String o) {
|
||||
try {
|
||||
return ((String) o).getBytes("US-ASCII");
|
||||
return o.getBytes("US-ASCII");
|
||||
} catch (UnsupportedEncodingException uee) { throw new Error("Unsupported Encoding"); }
|
||||
}
|
||||
|
||||
public Object construct(byte[] b) {
|
||||
public String construct(byte[] b) {
|
||||
try {
|
||||
return new String(b, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException uee) { throw new Error("Unsupported Encoding"); }
|
||||
|
||||
@@ -35,14 +35,14 @@ import net.metanotion.io.Serializer;
|
||||
/**
|
||||
* Added by I2P
|
||||
*/
|
||||
public class UTF8StringBytes implements Serializer {
|
||||
public byte[] getBytes(Object o) {
|
||||
public class UTF8StringBytes implements Serializer<String> {
|
||||
public byte[] getBytes(String o) {
|
||||
try {
|
||||
return ((String) o).getBytes("UTF-8");
|
||||
return o.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) { throw new Error("Unsupported Encoding"); }
|
||||
}
|
||||
|
||||
public Object construct(byte[] b) {
|
||||
public String construct(byte[] b) {
|
||||
try {
|
||||
return new String(b, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) { throw new Error("Unsupported Encoding"); }
|
||||
|
||||
@@ -39,12 +39,13 @@ import java.util.NoSuchElementException;
|
||||
To be clear, this is an iterator through the values.
|
||||
To get the key, call nextKey() BEFORE calling next().
|
||||
*/
|
||||
public class SkipIterator implements ListIterator {
|
||||
protected SkipSpan ss;
|
||||
public class SkipIterator<K extends Comparable<? super K>, V> implements ListIterator<V> {
|
||||
protected SkipSpan<K, V> ss;
|
||||
protected int index;
|
||||
|
||||
protected SkipIterator() { }
|
||||
public SkipIterator(SkipSpan ss, int index) {
|
||||
|
||||
public SkipIterator(SkipSpan<K, V> ss, int index) {
|
||||
if(ss==null) { throw new NullPointerException(); }
|
||||
this.ss = ss;
|
||||
this.index = index;
|
||||
@@ -59,8 +60,8 @@ public class SkipIterator implements ListIterator {
|
||||
* @return the next value, and advances the index
|
||||
* @throws NoSuchElementException
|
||||
*/
|
||||
public Object next() {
|
||||
Object o;
|
||||
public V next() {
|
||||
V o;
|
||||
if(index < ss.nKeys) {
|
||||
o = ss.vals[index];
|
||||
} else {
|
||||
@@ -83,7 +84,7 @@ public class SkipIterator implements ListIterator {
|
||||
* @return the key for which the value will be returned in the subsequent call to next()
|
||||
* @throws NoSuchElementException
|
||||
*/
|
||||
public Comparable nextKey() {
|
||||
public K nextKey() {
|
||||
if(index < ss.nKeys) { return ss.keys[index]; }
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
@@ -98,7 +99,7 @@ public class SkipIterator implements ListIterator {
|
||||
* @return the previous value, and decrements the index
|
||||
* @throws NoSuchElementException
|
||||
*/
|
||||
public Object previous() {
|
||||
public V previous() {
|
||||
if(index > 0) {
|
||||
index--;
|
||||
} else if(ss.prev != null) {
|
||||
@@ -111,9 +112,9 @@ public class SkipIterator implements ListIterator {
|
||||
|
||||
|
||||
// Optional methods
|
||||
public void add(Object o) { throw new UnsupportedOperationException(); }
|
||||
public void add(V o) { throw new UnsupportedOperationException(); }
|
||||
public void remove() { throw new UnsupportedOperationException(); }
|
||||
public void set(Object o) { throw new UnsupportedOperationException(); }
|
||||
public void set(V o) { throw new UnsupportedOperationException(); }
|
||||
public int nextIndex() { throw new UnsupportedOperationException(); }
|
||||
public int previousIndex() { throw new UnsupportedOperationException(); }
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import net.metanotion.io.block.BlockFile;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class SkipLevels implements Flushable {
|
||||
public class SkipLevels<K extends Comparable<? super K>, V> implements Flushable {
|
||||
/** We can't have more than 2**32 pages */
|
||||
public static final int MAX_SIZE = 32;
|
||||
|
||||
@@ -45,12 +45,15 @@ public class SkipLevels implements Flushable {
|
||||
* The "bottom" level is the direct pointer to a SkipSpan.
|
||||
*/
|
||||
// levels is almost final
|
||||
public SkipLevels[] levels;
|
||||
public SkipLevels<K, V>[] levels;
|
||||
// bottom is final
|
||||
public SkipSpan bottom;
|
||||
public SkipSpan<K, V> bottom;
|
||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(BlockFile.class);
|
||||
|
||||
public SkipLevels newInstance(int levels, SkipSpan ss, SkipList sl) { return new SkipLevels(levels, ss); }
|
||||
public SkipLevels<K, V> newInstance(int levels, SkipSpan<K, V> ss, SkipList<K, V> sl) {
|
||||
return new SkipLevels<K, V>(levels, ss);
|
||||
}
|
||||
|
||||
public void killInstance() { }
|
||||
public void flush() { }
|
||||
|
||||
@@ -59,10 +62,11 @@ public class SkipLevels implements Flushable {
|
||||
/*
|
||||
* @throws IllegalArgumentException if size too big or too small
|
||||
*/
|
||||
public SkipLevels(int size, SkipSpan span) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public SkipLevels(int size, SkipSpan<K, V> span) {
|
||||
if(size < 1 || size > MAX_SIZE)
|
||||
throw new IllegalArgumentException("Invalid Level Skip size");
|
||||
levels = new SkipLevels[size];
|
||||
levels = (SkipLevels<K, V>[]) new SkipLevels[size];
|
||||
bottom = span;
|
||||
}
|
||||
|
||||
@@ -92,14 +96,14 @@ public class SkipLevels implements Flushable {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public SkipSpan getEnd() {
|
||||
public SkipSpan<K, V> getEnd() {
|
||||
for(int i=(levels.length - 1);i>=0;i--) {
|
||||
if(levels[i] != null) { return levels[i].getEnd(); }
|
||||
}
|
||||
return bottom.getEnd();
|
||||
}
|
||||
|
||||
public SkipSpan getSpan(int start, Comparable key, int[] search) {
|
||||
public SkipSpan<K, V> getSpan(int start, K key, int[] search) {
|
||||
for(int i=Math.min(start, levels.length - 1);i>=0;i--) {
|
||||
if((levels[i] != null) && (levels[i].key().compareTo(key) <= 0)) {
|
||||
return levels[i].getSpan(i,key,search);
|
||||
@@ -108,9 +112,9 @@ public class SkipLevels implements Flushable {
|
||||
return bottom.getSpan(key, search);
|
||||
}
|
||||
|
||||
public Comparable key() { return bottom.firstKey(); }
|
||||
public K key() { return bottom.firstKey(); }
|
||||
|
||||
public Object get(int start, Comparable key) {
|
||||
public V get(int start, K key) {
|
||||
for(int i=Math.min(start, levels.length - 1);i>=0;i--) {
|
||||
if((levels[i] != null) && (levels[i].key().compareTo(key) <= 0)) {
|
||||
return levels[i].get(i,key);
|
||||
@@ -126,16 +130,17 @@ public class SkipLevels implements Flushable {
|
||||
* and the deleted SkipLevels is taller than this SkipLevels.
|
||||
* rv is null if no object was removed.
|
||||
*/
|
||||
public Object[] remove(int start, Comparable key, SkipList sl) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object[] remove(int start, K key, SkipList<K, V> sl) {
|
||||
Object[] res = null;
|
||||
SkipLevels slvls = null;
|
||||
SkipLevels<K, V> slvls = null;
|
||||
for(int i = Math.min(start, levels.length - 1); i >= 0; i--) {
|
||||
if(levels[i] != null) {
|
||||
int cmp = levels[i].key().compareTo(key);
|
||||
if((cmp < 0) || ((i==0) && (cmp <= 0))) {
|
||||
res = levels[i].remove(i, key, sl);
|
||||
if((res != null) && (res[1] != null)) {
|
||||
slvls = (SkipLevels) res[1];
|
||||
slvls = (SkipLevels<K, V>) res[1];
|
||||
if(levels.length >= slvls.levels.length) {
|
||||
res[1] = null;
|
||||
}
|
||||
@@ -159,7 +164,7 @@ public class SkipLevels implements Flushable {
|
||||
// if the returned SkipSpan was already copied to us
|
||||
boolean isFirst = sl.first == bottom;
|
||||
if (isFirst && levels[0] != null) {
|
||||
SkipSpan ssres = (SkipSpan)res[1];
|
||||
SkipSpan<K, V> ssres = (SkipSpan<K, V>)res[1];
|
||||
if (bottom.firstKey().equals(ssres.firstKey())) {
|
||||
// bottom copied the next span to itself
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
@@ -171,7 +176,7 @@ public class SkipLevels implements Flushable {
|
||||
_log.info("FIXUP TIME");
|
||||
}
|
||||
|
||||
SkipLevels replace = levels[0];
|
||||
SkipLevels<K, V> replace = levels[0];
|
||||
for (int i = 0; i < levels.length; i++) {
|
||||
if (levels[i] == null)
|
||||
break;
|
||||
@@ -213,12 +218,12 @@ public class SkipLevels implements Flushable {
|
||||
* and the new level is taller than our level;
|
||||
* else null if it went in an existing level or the new level is our height or less.
|
||||
*/
|
||||
public SkipLevels put(int start, Comparable key, Object val, SkipList sl) {
|
||||
public SkipLevels<K, V> put(int start, K key, V val, SkipList<K, V> sl) {
|
||||
boolean modified = false;
|
||||
for(int i = Math.min(start, levels.length - 1); i >= 0; i--) {
|
||||
// is key equal to or after the start of the level?
|
||||
if((levels[i] != null) && (levels[i].key().compareTo(key) <= 0)) {
|
||||
SkipLevels slvls = levels[i].put(i, key, val, sl);
|
||||
SkipLevels<K, V> slvls = levels[i].put(i, key, val, sl);
|
||||
if(slvls != null) {
|
||||
for (int j = i + 1; j < Math.min(slvls.levels.length, levels.length); j++) {
|
||||
// he points to where we used to point
|
||||
@@ -243,11 +248,11 @@ public class SkipLevels implements Flushable {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
SkipSpan ss = bottom.put(key,val,sl);
|
||||
SkipSpan<K, V> ss = bottom.put(key,val,sl);
|
||||
if(ss!=null) {
|
||||
int height = sl.generateColHeight();
|
||||
if(height != 0) {
|
||||
SkipLevels slvls = this.newInstance(height, ss, sl);
|
||||
SkipLevels<K, V> slvls = this.newInstance(height, ss, sl);
|
||||
for(int i=0;i<(Math.min(height,levels.length));i++) {
|
||||
// he points to where we used to point
|
||||
// and we now point to him
|
||||
@@ -267,6 +272,6 @@ public class SkipLevels implements Flushable {
|
||||
}
|
||||
|
||||
public boolean blvlck(boolean fix) { return false; }
|
||||
public boolean blvlck(boolean fix, int width, SkipLevels[] prevLevels) { return false; }
|
||||
public boolean blvlck(boolean fix, int width, SkipLevels<K, V>[] prevLevels) { return false; }
|
||||
}
|
||||
|
||||
|
||||
@@ -35,13 +35,13 @@ import net.i2p.util.RandomSource;
|
||||
|
||||
//import net.metanotion.io.block.BlockFile;
|
||||
|
||||
public class SkipList implements Flushable {
|
||||
public class SkipList<K extends Comparable<? super K>, V> implements Flushable, Iterable<V> {
|
||||
/** the probability of each next higher level */
|
||||
protected static final int P = 2;
|
||||
private static final int MIN_SLOTS = 4;
|
||||
// these two are really final
|
||||
protected SkipSpan first;
|
||||
protected SkipLevels stack;
|
||||
protected SkipSpan<K, V> first;
|
||||
protected SkipLevels<K, V> stack;
|
||||
// I2P mod
|
||||
public static final Random rng = RandomSource.getInstance();
|
||||
|
||||
@@ -57,8 +57,8 @@ public class SkipList implements Flushable {
|
||||
public SkipList(int span) {
|
||||
if(span < 1 || span > SkipSpan.MAX_SIZE)
|
||||
throw new IllegalArgumentException("Invalid span size");
|
||||
first = new SkipSpan(span);
|
||||
stack = new SkipLevels(1, first);
|
||||
first = new SkipSpan<K, V>(span);
|
||||
stack = new SkipLevels<K, V>(1, first);
|
||||
//rng = new Random(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@@ -95,14 +95,15 @@ public class SkipList implements Flushable {
|
||||
return max;
|
||||
}
|
||||
|
||||
public void put(Comparable key, Object val) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void put(K key, V val) {
|
||||
if(key == null) { throw new NullPointerException(); }
|
||||
if(val == null) { throw new NullPointerException(); }
|
||||
SkipLevels slvls = stack.put(stack.levels.length - 1, key, val, this);
|
||||
SkipLevels<K, V> slvls = stack.put(stack.levels.length - 1, key, val, this);
|
||||
if(slvls != null) {
|
||||
// grow our stack
|
||||
//BlockFile.log.info("Top level old hgt " + stack.levels.length + " new hgt " + slvls.levels.length);
|
||||
SkipLevels[] levels = new SkipLevels[slvls.levels.length];
|
||||
SkipLevels<K, V>[] levels = (SkipLevels<K, V>[]) new SkipLevels[slvls.levels.length];
|
||||
for(int i=0;i < slvls.levels.length; i++) {
|
||||
if(i < stack.levels.length) {
|
||||
levels[i] = stack.levels[i];
|
||||
@@ -116,12 +117,13 @@ public class SkipList implements Flushable {
|
||||
}
|
||||
}
|
||||
|
||||
public Object remove(Comparable key) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public V remove(K key) {
|
||||
if(key == null) { throw new NullPointerException(); }
|
||||
Object[] res = stack.remove(stack.levels.length - 1, key, this);
|
||||
if(res != null) {
|
||||
if(res[1] != null) {
|
||||
SkipLevels slvls = (SkipLevels) res[1];
|
||||
SkipLevels<K, V> slvls = (SkipLevels<K, V>) res[1];
|
||||
for(int i=0;i < slvls.levels.length; i++) {
|
||||
if(stack.levels[i] == slvls) {
|
||||
stack.levels[i] = slvls.levels[i];
|
||||
@@ -130,7 +132,7 @@ public class SkipList implements Flushable {
|
||||
stack.flush();
|
||||
}
|
||||
flush();
|
||||
return res[0];
|
||||
return (V) res[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -149,34 +151,36 @@ public class SkipList implements Flushable {
|
||||
* dumps all the data
|
||||
* @deprecated goes to System.out
|
||||
*/
|
||||
@Deprecated
|
||||
public void print() {
|
||||
System.out.println("List size " + size);
|
||||
System.out.println(first.print());
|
||||
}
|
||||
|
||||
public Object get(Comparable key) {
|
||||
public V get(K key) {
|
||||
if(key == null) { throw new NullPointerException(); }
|
||||
return stack.get(stack.levels.length - 1, key);
|
||||
}
|
||||
|
||||
public SkipIterator iterator() { return new SkipIterator(first, 0); }
|
||||
public SkipIterator<K, V> iterator() { return new SkipIterator<K, V>(first, 0); }
|
||||
|
||||
public SkipIterator min() { return new SkipIterator(first, 0); }
|
||||
/****
|
||||
public SkipIterator<K, V> min() { return new SkipIterator<K, V>(first, 0); }
|
||||
|
||||
public SkipIterator max() {
|
||||
SkipSpan ss = stack.getEnd();
|
||||
return new SkipIterator(ss, ss.nKeys - 1);
|
||||
public SkipIterator<K, V> max() {
|
||||
SkipSpan<K, V> ss = stack.getEnd();
|
||||
return new SkipIterator<K, V>(ss, ss.nKeys - 1);
|
||||
}
|
||||
****/
|
||||
|
||||
/** @return an iterator where nextKey() is the first one greater than or equal to 'key' */
|
||||
public SkipIterator find(Comparable key) {
|
||||
public SkipIterator<K, V> find(K key) {
|
||||
int[] search = new int[1];
|
||||
SkipSpan ss = stack.getSpan(stack.levels.length - 1, key, search);
|
||||
SkipSpan<K, V> ss = stack.getSpan(stack.levels.length - 1, key, search);
|
||||
if(search[0] < 0) { search[0] = -1 * (search[0] + 1); }
|
||||
return new SkipIterator(ss, search[0]);
|
||||
return new SkipIterator<K, V>(ss, search[0]);
|
||||
}
|
||||
|
||||
|
||||
// Levels adjusted to guarantee O(log n) search
|
||||
// This is expensive proportional to the number of spans.
|
||||
public void balance() {
|
||||
|
||||
@@ -32,16 +32,16 @@ import java.io.Flushable;
|
||||
|
||||
//import net.metanotion.io.block.BlockFile;
|
||||
|
||||
public class SkipSpan implements Flushable {
|
||||
public class SkipSpan<K extends Comparable<? super K>, V> implements Flushable {
|
||||
/** This is actually limited by BlockFile.spanSize which is much smaller */
|
||||
public static final int MAX_SIZE = 256;
|
||||
|
||||
public int nKeys = 0;
|
||||
public Comparable[] keys;
|
||||
public Object[] vals;
|
||||
public SkipSpan next, prev;
|
||||
public K[] keys;
|
||||
public V[] vals;
|
||||
public SkipSpan<K, V> next, prev;
|
||||
|
||||
public SkipSpan newInstance(SkipList sl) { return new SkipSpan(keys.length); }
|
||||
public SkipSpan<K, V> newInstance(SkipList<K, V> sl) { return new SkipSpan<K, V>(keys.length); }
|
||||
public void killInstance() { }
|
||||
public void flush() { }
|
||||
|
||||
@@ -50,11 +50,12 @@ public class SkipSpan implements Flushable {
|
||||
/*
|
||||
* @throws IllegalArgumentException if size too big or too small
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public SkipSpan(int size) {
|
||||
if(size < 1 || size > MAX_SIZE)
|
||||
throw new IllegalArgumentException("Invalid span size " + size);
|
||||
keys = new Comparable[size];
|
||||
vals = new Object[size];
|
||||
keys = (K[]) new Comparable[size];
|
||||
vals = (V[]) new Object[size];
|
||||
}
|
||||
|
||||
/** dumps all the data from here to the end */
|
||||
@@ -70,7 +71,7 @@ public class SkipSpan implements Flushable {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private int binarySearch(Comparable key) {
|
||||
private int binarySearch(K key) {
|
||||
int high = nKeys - 1;
|
||||
int low = 0;
|
||||
int cur;
|
||||
@@ -89,12 +90,12 @@ public class SkipSpan implements Flushable {
|
||||
return (-1 * (low + 1));
|
||||
}
|
||||
|
||||
public SkipSpan getEnd() {
|
||||
public SkipSpan<K, V> getEnd() {
|
||||
if(next == null) { return this; }
|
||||
return next.getEnd();
|
||||
}
|
||||
|
||||
public SkipSpan getSpan(Comparable key, int[] search) {
|
||||
public SkipSpan<K, V> getSpan(K key, int[] search) {
|
||||
if(nKeys == 0) {
|
||||
search[0] = -1;
|
||||
return this;
|
||||
@@ -111,7 +112,7 @@ public class SkipSpan implements Flushable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object get(Comparable key) {
|
||||
public V get(K key) {
|
||||
if(nKeys == 0) { return null; }
|
||||
if(keys[nKeys - 1].compareTo(key) < 0) {
|
||||
if(next == null) { return null; }
|
||||
@@ -138,8 +139,8 @@ public class SkipSpan implements Flushable {
|
||||
nKeys++;
|
||||
}
|
||||
|
||||
private void split(int loc, Comparable key, Object val, SkipList sl) {
|
||||
SkipSpan right = newInstance(sl);
|
||||
private void split(int loc, K key, V val, SkipList<K, V> sl) {
|
||||
SkipSpan<K, V> right = newInstance(sl);
|
||||
|
||||
if(this.next != null) { this.next.prev = right; }
|
||||
right.next = this.next;
|
||||
@@ -175,7 +176,7 @@ public class SkipSpan implements Flushable {
|
||||
/**
|
||||
* @return the new span if it caused a split, else null if it went in this span
|
||||
*/
|
||||
private SkipSpan insert(int loc, Comparable key, Object val, SkipList sl) {
|
||||
private SkipSpan<K, V> insert(int loc, K key, V val, SkipList<K, V> sl) {
|
||||
sl.addItem();
|
||||
if(nKeys == keys.length) {
|
||||
// split.
|
||||
@@ -193,7 +194,7 @@ public class SkipSpan implements Flushable {
|
||||
/**
|
||||
* @return the new span if it caused a split, else null if it went in an existing span
|
||||
*/
|
||||
public SkipSpan put(Comparable key, Object val, SkipList sl) {
|
||||
public SkipSpan<K, V> put(K key, V val, SkipList<K, V> sl) {
|
||||
if(nKeys == 0) {
|
||||
sl.addItem();
|
||||
keys[0] = key;
|
||||
@@ -246,7 +247,7 @@ public class SkipSpan implements Flushable {
|
||||
* rv[1] is the deleted SkipSpan if the removed object was the last in the SkipSpan.
|
||||
* rv is null if no object was removed.
|
||||
*/
|
||||
public Object[] remove(Comparable key, SkipList sl) {
|
||||
public Object[] remove(K key, SkipList<K, V> sl) {
|
||||
if(nKeys == 0) { return null; }
|
||||
if(keys[nKeys - 1].compareTo(key) < 0) {
|
||||
if(next == null) { return null; }
|
||||
@@ -270,7 +271,7 @@ public class SkipSpan implements Flushable {
|
||||
nKeys = next.nKeys;
|
||||
//BlockFile.log.error("Killing next span " + next + ") and copying to this span " + this + " in remove of " + key);
|
||||
// Make us point to next.next and him point back to us
|
||||
SkipSpan nn = next.next;
|
||||
SkipSpan<K, V> nn = next.next;
|
||||
next.killInstance();
|
||||
if (nn != null) {
|
||||
nn.prev = this;
|
||||
@@ -311,7 +312,7 @@ public class SkipSpan implements Flushable {
|
||||
}
|
||||
|
||||
/** I2P */
|
||||
public Comparable firstKey() {
|
||||
public K firstKey() {
|
||||
return keys[0];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user