propagate from branch 'i2p.i2p' (head eb2151f9d804ec432bfe97214896ee62da08943e)

to branch 'i2p.i2p.zzz.test2' (head 2ea50c5f22fe1e24d37dff2f283b77feaa9190ee)
This commit is contained in:
zzz
2016-06-04 13:00:36 +00:00
564 changed files with 52737 additions and 24572 deletions

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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");

View File

@@ -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");
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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'

View File

@@ -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.

View File

@@ -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();
}
}

View File

@@ -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);

View 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();
}
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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());

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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";
}
}
}

View 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();
}
}

View File

@@ -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");

View File

@@ -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" : "&darr;&darr;&darr;") : "^^^";
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();
}
}

View File

@@ -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" : "&darr;&darr;&darr;") : "^^^";
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);
}
}
}
}

View File

@@ -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 &lt;= 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 &lt;= 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 &lt;= 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 &lt;= 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 &lt;= 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";
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}
}
}
}

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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]);
}
/**

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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; }
}

View File

@@ -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) |

View File

@@ -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);
}
}

View File

@@ -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; }
}

View File

@@ -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"); }

View File

@@ -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"); }

View File

@@ -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(); }

View File

@@ -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; }
}

View File

@@ -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() {

View File

@@ -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];
}
}