commit 5ac9c413bcd6d149e4c52a7ff4944ca0f95c7440
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Thu Oct 25 11:02:52 2012 -0400
Separate combining databases and looking up addresses (#6471).
---
.../java/src/org/torproject/task6471/Database.java | 41 +--
.../src/org/torproject/task6471/DatabaseImpl.java | 444 +------------------
.../org/torproject/task6471/DatabaseImporter.java | 40 ++
.../torproject/task6471/DatabaseImporterImpl.java | 456 ++++++++++++++++++++
.../task6471/DatabasePerformanceExample.java | 25 +-
.../src/org/torproject/task6471/DatabaseTest.java | 188 +++++----
6 files changed, 644 insertions(+), 550 deletions(-)
diff --git a/task-6471/java/src/org/torproject/task6471/Database.java b/task-6471/java/src/org/torproject/task6471/Database.java
index 447b3c2..02fb9a2 100644
--- a/task-6471/java/src/org/torproject/task6471/Database.java
+++ b/task-6471/java/src/org/torproject/task6471/Database.java
@@ -15,41 +15,6 @@ package org.torproject.task6471;
public interface Database {
/**
- * Import the contents of one or more IP address assignments files
- * published by the Regional Internet Registries. The file or files
- * are expected to conform to the RIR Statistics Exchange Format.
- * Only IPv4 address ranges are imported, whereas ASN and IPv6 lines are
- * ignored. Only the country code, start address, and address range
- * length fields are imported. (TODO Extend to IPv6 and find similar
- * data source for ASN.)
- *
- * A typical entry from a RIR file is:
- * "ripencc|FR|ipv4|2.0.0.0|1048576|20100712|allocated".
- *
- * It is important to note that all five registry files (AfriNIC, APNIC,
- * ARIN, LACNIC, and RIPE NCC) published on a given day should be
- * imported, or the missing address ranges will be considered as
- * unassigned from that day until the next database publication day.
- * (TODO We could be smarter here by checking that less than five
- * registry files have been imported for the same day, or something.)
- *
- * @param path Path to a stats file or directory.
- * @return True if importing the file or directory was successful,
- * false otherwise.
- */
- public boolean importRegionalRegistryStatsFileOrDirectory(String path);
-
- /**
- * Save the combined databases in a format that can later be loaded much
- * more efficiently than importing the original RIR files again.
- *
- * @param path Path to the combined database file.
- * @return True if saving the combined database file was successful,
- * false otherwise.
- */
- public boolean saveCombinedDatabases(String path);
-
- /**
* Load a combined databases file.
*
* @param path Path to the combined database file.
@@ -59,12 +24,14 @@ public interface Database {
public boolean loadCombinedDatabases(String path);
/**
- * Query the database for an IPv4 address and assignment date.
+ * Query the database for the country code assigned to an IPv4 address
+ * and date.
*
* @param address IPv4 address in dotted-quad notation.
* @param date Assignment date in format yyyymmdd.
* @return Assigned country code, or null if no assignment could be
* found.
*/
- public String lookupAddress(String address, String date);
+ public String lookupCountryCodeFromIpv4AddressAndDate(String address,
+ String date);
}
diff --git a/task-6471/java/src/org/torproject/task6471/DatabaseImpl.java b/task-6471/java/src/org/torproject/task6471/DatabaseImpl.java
index 0f27ce5..0229d10 100644
--- a/task-6471/java/src/org/torproject/task6471/DatabaseImpl.java
+++ b/task-6471/java/src/org/torproject/task6471/DatabaseImpl.java
@@ -2,21 +2,15 @@
package org.torproject.task6471;
import java.io.BufferedReader;
-import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
-import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
-import java.util.Stack;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
@@ -51,12 +45,13 @@ public class DatabaseImpl implements Database {
* database date are encoded in the key under which the element is
* stored.
*/
- private static class TreeElement {
- private long endAddress;
- private int lastDbDate;
- private String countryCode;
- private boolean modifiedInLastImport;
- TreeElement(long endAddress, int lastDbDate, String countryCode) {
+ protected static class TreeElement {
+ protected long endAddress;
+ protected int lastDbDate;
+ protected String countryCode;
+ protected boolean modifiedInLastImport;
+ protected TreeElement(long endAddress, int lastDbDate,
+ String countryCode) {
this.endAddress = endAddress;
this.lastDbDate = lastDbDate;
this.countryCode = countryCode;
@@ -68,397 +63,30 @@ public class DatabaseImpl implements Database {
* IPv4 address and date ranges, ordered backwards by start address and
* first database date.
*/
- private SortedMap<Long, TreeElement> ranges =
+ protected SortedMap<Long, TreeElement> ranges =
new TreeMap<Long, TreeElement>(Collections.reverseOrder());
/**
- * Return number of contained ranges.
- */
- int getNumberOfElements() {
- return this.ranges.size();
- }
-
- /**
* Database dates ordered from oldest to youngest.
*/
- private SortedSet<Integer> databaseDates = new TreeSet<Integer>();
+ protected SortedSet<Integer> databaseDates = new TreeSet<Integer>();
/**
* Database file names.
*/
- private SortedSet<String> databaseFileNames = new TreeSet<String>();
-
- /**
- * Parse one or more stats files.
- */
- public boolean importRegionalRegistryStatsFileOrDirectory(String path) {
- boolean allImportsSuccessful = true;
- Stack<File> stackedFiles = new Stack<File>();
- stackedFiles.add(new File(path));
- List<File> allFiles = new ArrayList<File>();
- while (!stackedFiles.isEmpty()) {
- File file = stackedFiles.pop();
- if (file.isDirectory()) {
- stackedFiles.addAll(Arrays.asList(file.listFiles()));
- } else if (file.getName().endsWith(".md5") ||
- file.getName().endsWith(".md5.gz") ||
- file.getName().endsWith(".asc") ||
- file.getName().endsWith(".asc.gz")) {
- System.err.println("Signature and digest files are not supported "
- + "yet: '" + file.getAbsolutePath() + "'. Skipping.");
- /* TODO Implement checking signatures/digests. */
- } else if (file.getName().endsWith(".gz") ||
- file.getName().endsWith(".bz2")) {
- System.err.println("Parsing compressed files is not supported "
- + "yet: '" + file.getAbsolutePath() + "'. Skipping.");
- /* TODO Implement parsing compressed files. */
- } else {
- /* TODO Make sure that we're not importing files for a date if we
- * have less than all five of them. */
- allFiles.add(file);
- }
- }
- Collections.sort(allFiles, Collections.reverseOrder());
- for (File file : allFiles) {
- String databaseFileName = file.getName();
- if (this.databaseFileNames.contains(databaseFileName)) {
- /* We already imported this file while loading combined databases
- * from disk. */
- continue;
- }
- if (!this.importRegionalRegistryStatsFile(file)) {
- allImportsSuccessful = false;
- }
- }
- return allImportsSuccessful;
- }
-
- /**
- * Simple and not very robust implementation of an RIR stats file
- * parser.
- */
- private boolean importRegionalRegistryStatsFile(File file) {
- try {
- BufferedReader br = new BufferedReader(new FileReader(file));
- String line;
- String databaseFileName = file.getName();
- while ((line = br.readLine()) != null) {
- if (line.startsWith("#") || line.length() == 0) {
- /* Skip comment line. */
- continue;
- }
- String[] parts = line.split("\\|");
- if (parts[0].equals("2")) {
- continue;
- }
- if (parts[1].equals("*")) {
- /* Skip summary line. */
- continue;
- }
- String type = parts[2];
- if (type.equals("asn")) {
- continue;
- } else if (type.equals("ipv6")) {
- continue;
- }
- String countryCode = parts[1].toLowerCase();
- String startAddressString = parts[3];
- long addresses = Long.parseLong(parts[4]);
- this.addRange(databaseFileName, countryCode, startAddressString,
- addresses);
- }
- br.close();
- this.repairTree();
- } catch (IOException e) {
- return false;
- }
- return true;
- }
-
- /**
- * Internal counters for import statistics.
- */
- private int rangeImports = 0, rangeImportsKeyLookups = 0;
-
- /**
- * Add a single address and date range to the tree, which may require
- * splitting up existing ranges.
- *
- * This method has default visibility and is not specified in the
- * interface, because the caller needs to make sure that repairTree()
- * is called prior to any lookupAddress() calls. No further checks are
- * performed that the tree is repaired before looking up an address.
- */
- void addRange(String databaseFileName, String countryCode,
- String startAddressString, long addresses) {
-
- this.rangeImports++;
- String databaseDateString =
- databaseFileName.substring(databaseFileName.length() - 8);
- int databaseDate = convertDateStringToNumber(databaseDateString);
- long startAddress = convertAddressStringToNumber(startAddressString);
- long endAddress = startAddress + addresses - 1L;
-
- /* Add new database date and file name if we didn't know them yet,
- * and note that we need to repair the tree after importing. */
- if (!this.databaseDates.contains(databaseDate)) {
- this.databaseDates.add(databaseDate);
- this.addedDatabaseDate = databaseDate;
- }
- this.databaseFileNames.add(databaseFileName);
-
- /* We might have to split existing ranges or the new range before
- * adding it to the tree, and we might have to remove existing ranges.
- * We shouldn't mess with the tree directly while iterating over it,
- * so let's for now only calculate what changes we want to make. */
- SortedMap<Long, TreeElement> updateElements =
- this.getUpdatesForAddingRange(databaseDate, countryCode,
- startAddress, endAddress);
-
- /* Apply updates. Elements with non-null values are added, elements
- * with null values are removed. */
- for (Map.Entry<Long, TreeElement> e : updateElements.entrySet()) {
- if (e.getValue() == null) {
- this.ranges.remove(e.getKey());
- } else {
- this.ranges.put(e.getKey(), e.getValue());
- }
- }
- }
-
- /**
- * Calculate necessary changes to the tree to add a range.
- */
- private SortedMap<Long, TreeElement> getUpdatesForAddingRange(
- int databaseDate, String countryCode, long startAddress,
- long endAddress) {
-
- /* Keep updates in a single tree where non-null values will later be
- * added, possibly replacing existing elements, and null values will
- * be removed from the tree. */
- SortedMap<Long, TreeElement> updateElements =
- new TreeMap<Long, TreeElement>();
-
- /* Find out previous and next database, so that we can possibly merge
- * ranges. */
- int previousDatabaseDate =
- this.databaseDates.headSet(databaseDate).isEmpty() ? -1 :
- this.databaseDates.headSet(databaseDate).last();
- int nextDatabaseDate =
- this.databaseDates.tailSet(databaseDate + 1).isEmpty() ? -1 :
- this.databaseDates.tailSet(databaseDate + 1).first();
-
- /* Remember the address boundaries of the next (partial) range to be
- * added. */
- long nextStartAddress = startAddress, nextEndAddress = endAddress;
- int nextFirstDbDate = databaseDate, nextLastDbDate = databaseDate;
-
- /* Iterate backwards over the existing ranges, starting at the end
- * address of the range to be added and at the last conceivable
- * database publication date. */
- for (Map.Entry<Long, TreeElement> e : this.ranges.tailMap(
- convertAddressAndDateToKey(endAddress + 1L, 0) - 1L).entrySet()) {
- this.rangeImportsKeyLookups++;
-
- /* Extract everything we need to know from the next existing range
- * we're looking at. */
- long eStartAddress = convertKeyToAddress(e.getKey());
- long eEndAddress = e.getValue().endAddress;
- int eFirstDbDate = convertKeyToDate(e.getKey());
- int eLastDbDate = e.getValue().lastDbDate;
- String eCountryCode = e.getValue().countryCode;
-
- /* If the next (partial) range starts after the current element
- * ends, add the new range. */
- if (nextStartAddress > eEndAddress &&
- nextEndAddress >= startAddress) {
- updateElements.put(convertAddressAndDateToKey(nextStartAddress,
- nextFirstDbDate), new TreeElement(nextEndAddress,
- nextLastDbDate, countryCode));
- nextEndAddress = nextStartAddress - 1L;
- nextStartAddress = startAddress;
- nextFirstDbDate = databaseDate;
- nextLastDbDate = databaseDate;
- }
-
- /* If the next (partial) range still ends after the current element
- * ends, add the new range. */
- if (nextEndAddress > eEndAddress &&
- nextEndAddress >= startAddress) {
- updateElements.put(convertAddressAndDateToKey(eEndAddress + 1L,
- databaseDate), new TreeElement(nextEndAddress, databaseDate,
- countryCode));
- nextEndAddress = eEndAddress;
- nextStartAddress = startAddress;
- nextFirstDbDate = databaseDate;
- nextLastDbDate = databaseDate;
- }
-
- /* If the existing range ends before the new range starts, we don't
- * have to look at any more existing ranges. */
- if (eEndAddress < startAddress) {
- break;
- }
-
- /* Cut off any address range parts of the existing element that are
- * not contained in the currently added range. First check whether
- * the existing range ends after the newly added range. In that
- * case cut off the overlapping part and store it as a new
- * element.*/
- if (eStartAddress <= endAddress && eEndAddress > endAddress) {
- updateElements.put(convertAddressAndDateToKey(endAddress + 1L,
- eFirstDbDate), new TreeElement(eEndAddress, eLastDbDate,
- eCountryCode));
- updateElements.put(convertAddressAndDateToKey(eStartAddress,
- eFirstDbDate), new TreeElement(endAddress, eLastDbDate,
- eCountryCode));
- eEndAddress = endAddress;
- }
-
- /* Similarly, check whether the existing range starts before the
- * newly added one. If so, cut off the overlapping part and store
- * it as new element. */
- if (eStartAddress < startAddress && eEndAddress >= startAddress) {
- updateElements.put(convertAddressAndDateToKey(eStartAddress,
- eFirstDbDate), new TreeElement(startAddress - 1L, eLastDbDate,
- eCountryCode));
- updateElements.put(convertAddressAndDateToKey(startAddress,
- eFirstDbDate), new TreeElement(eEndAddress, eLastDbDate,
- eCountryCode));
- eStartAddress = startAddress;
- }
-
- /* Now we're sure the existing element doesn't exceed the newly
- * added element, address-wise. */
- nextStartAddress = eStartAddress;
- nextEndAddress = eEndAddress;
-
- /* If the range is already contained and has the same country code,
- * mark it as updated. If it's contained with a different country
- * code, ignore the update. */
- if (eFirstDbDate <= databaseDate && eLastDbDate >= databaseDate) {
- if (eCountryCode.equals(countryCode)) {
- nextFirstDbDate = eFirstDbDate;
- nextLastDbDate = eLastDbDate;
- } else {
- updateElements.clear();
- return updateElements;
- }
- }
-
- /* See if we can merge the new range with the previous or next
- * range. If so, extend our database range and mark the existing
- * element for deletion. */
- if (eCountryCode.equals(countryCode)) {
- if (eLastDbDate == previousDatabaseDate) {
- nextFirstDbDate = eFirstDbDate;
- updateElements.put(convertAddressAndDateToKey(eStartAddress,
- eFirstDbDate), null);
- } else if (eFirstDbDate == nextDatabaseDate) {
- nextLastDbDate = eLastDbDate;
- updateElements.put(convertAddressAndDateToKey(eStartAddress,
- eFirstDbDate), null);
- }
- }
- }
-
- /* If there's still some part (or the whole?) address range left to
- * add, add it now. */
- while (nextEndAddress >= startAddress) {
- updateElements.put(convertAddressAndDateToKey(nextStartAddress,
- nextFirstDbDate), new TreeElement(nextEndAddress,
- nextLastDbDate, countryCode));
- nextEndAddress = nextStartAddress - 1L;
- nextStartAddress = startAddress;
- nextFirstDbDate = databaseDate;
- nextLastDbDate = databaseDate;
- }
-
- /* Return the tree updates that will add the given range. */
- return updateElements;
- }
-
- /**
- * Internal counter for millis spent on repairing the tree.
- */
- private long treeRepairMillis = 0L;
-
- /* Newly added database date, or -1 if a database from an already known
- * date was imported. */
- private int addedDatabaseDate = -1;
-
- /**
- * Repair tree by making sure that any range from a given database date
- * to another is still valid when considering any other database that
- * was imported later.
- *
- * It's okay to split a date range when importing a database from
- * another registry that doesn't contain the given address range. We'll
- * merge the two date ranges when parsing that registry's file.
- *
- * This method has default visibility and is not specified in the
- * interface, because the caller needs to make sure that repairTree()
- * is called prior to any lookupAddress() calls. No further checks are
- * performed that the tree is repaired before look up an address.
- */
- void repairTree() {
- if (this.addedDatabaseDate < 0) {
- return;
- }
- long startedRepairingTree = System.currentTimeMillis();
- SortedMap<Long, TreeElement> updateElements =
- new TreeMap<Long, TreeElement>();
- for (Map.Entry<Long, TreeElement> e : this.ranges.entrySet()) {
- if (e.getValue().modifiedInLastImport) {
- e.getValue().modifiedInLastImport = false;
- } else {
- int eFirstDbDate = convertKeyToDate(e.getKey());
- int eLastDbDate = e.getValue().lastDbDate;
- long eStartAddress = convertKeyToAddress(e.getKey());
- long eEndAddress = e.getValue().endAddress;
- String eCountryCode = e.getValue().countryCode;
- int start = eFirstDbDate, end = eFirstDbDate;
- for (int cur : this.databaseDates.tailSet(eFirstDbDate)) {
- if (cur > eLastDbDate) {
- break;
- }
- if (cur == addedDatabaseDate) {
- if (start >= 0 && end >= 0) {
- updateElements.put(convertAddressAndDateToKey(eStartAddress,
- start), new TreeElement(eEndAddress, end,
- eCountryCode));
- start = end = -1;
- }
- } else if (start < 0) {
- start = end = cur;
- } else {
- end = cur;
- }
- }
- if (start >= 0 && end >= 0) {
- updateElements.put(convertAddressAndDateToKey(eStartAddress,
- start), new TreeElement(eEndAddress, end, eCountryCode));
- }
- }
- }
- for (Map.Entry<Long, TreeElement> e : updateElements.entrySet()) {
- this.ranges.put(e.getKey(), e.getValue());
- }
- this.addedDatabaseDate = -1;
- this.treeRepairMillis += (System.currentTimeMillis()
- - startedRepairingTree);
- }
+ protected SortedSet<String> databaseFileNames = new TreeSet<String>();
/**
* Internal counters for lookup statistics.
*/
- private int addressLookups = 0, addressLookupsKeyLookups = 0;
+ protected int addressLookups = 0, addressLookupsKeyLookups = 0;
/**
* Look up address and date by iterating backwards over possibly
* matching ranges.
*/
- public String lookupAddress(String addressString, String dateString) {
+ public String lookupCountryCodeFromIpv4AddressAndDate(
+ String addressString, String dateString) {
this.addressLookups++;
long address = convertAddressStringToNumber(addressString);
@@ -584,13 +212,10 @@ public class DatabaseImpl implements Database {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Tree contains %d databases and %d combined "
+ "address ranges.\n"
- + "Performed %d address range imports requiring %d lookups.\n"
+ "Performed %d address lookups requiring %d lookups.\n"
- + "Spent %d millis on repairing tree.\n"
+ "First 10 entries, in reverse order, are:",
- this.databaseDates.size(), this.ranges.size(), this.rangeImports,
- this.rangeImportsKeyLookups, this.addressLookups,
- this.addressLookupsKeyLookups, this.treeRepairMillis));
+ this.databaseDates.size(), this.ranges.size(),
+ this.addressLookups, this.addressLookupsKeyLookups));
int entries = 10;
for (Map.Entry<Long, TreeElement> e : this.ranges.entrySet()) {
sb.append(String.format("%n %s %s %s %s %s",
@@ -607,44 +232,6 @@ public class DatabaseImpl implements Database {
}
/**
- * Save the combined databases to disk.
- */
- public boolean saveCombinedDatabases(String path) {
- try {
-
- /* Create parent directories if necessary. */
- File file = new File(path);
- if (file.getParentFile() != null) {
- file.getParentFile().mkdirs();
- }
-
- /* Start with writing all contained database file names to the file
- * header. */
- BufferedWriter bw = new BufferedWriter(new FileWriter(file));
- for (String databaseFileName : this.databaseFileNames) {
- bw.write("!" + databaseFileName + "\n");
- }
-
- /* Next write all database ranges in the same order as they are
- * currently contained in memory. The only information we can drop
- * is the last known database index of each range, because we assume
- * the tree is already in repaired state. */
- for (Map.Entry<Long, TreeElement> e : this.ranges.entrySet()) {
- bw.write(String.format("%s,%s,%s,%s,%s%n",
- convertKeyToAddressString(e.getKey()),
- convertAddressNumberToString(e.getValue().endAddress),
- e.getValue().countryCode,
- convertKeyToDateString(e.getKey()),
- convertDateNumberToString(e.getValue().lastDbDate)));
- }
- bw.close();
- } catch (IOException e) {
- return false;
- }
- return true;
- }
-
- /**
* Load previously saved combined databases from disk. This code is not
* at all robust against external changes of the combined database file.
*/
@@ -683,7 +270,6 @@ public class DatabaseImpl implements Database {
} catch (IOException e) {
return false;
}
- this.repairTree();
return true;
}
}
diff --git a/task-6471/java/src/org/torproject/task6471/DatabaseImporter.java b/task-6471/java/src/org/torproject/task6471/DatabaseImporter.java
new file mode 100644
index 0000000..641def4
--- /dev/null
+++ b/task-6471/java/src/org/torproject/task6471/DatabaseImporter.java
@@ -0,0 +1,40 @@
+package org.torproject.task6471;
+
+public interface DatabaseImporter extends Database {
+
+ /**
+ * Import the contents of one or more IP address assignments files
+ * published by the Regional Internet Registries. The file or files
+ * are expected to conform to the RIR Statistics Exchange Format.
+ * Only IPv4 address ranges are imported, whereas ASN and IPv6 lines are
+ * ignored. Only the country code, start address, and address range
+ * length fields are imported. (TODO Extend to IPv6 and find similar
+ * data source for ASN.)
+ *
+ * A typical entry from a RIR file is:
+ * "ripencc|FR|ipv4|2.0.0.0|1048576|20100712|allocated".
+ *
+ * It is important to note that all five registry files (AfriNIC, APNIC,
+ * ARIN, LACNIC, and RIPE NCC) published on a given day should be
+ * imported, or the missing address ranges will be considered as
+ * unassigned from that day until the next database publication day.
+ * (TODO We could be smarter here by checking that less than five
+ * registry files have been imported for the same day, or something.)
+ *
+ * @param path Path to a stats file or directory.
+ * @return True if importing the file or directory was successful,
+ * false otherwise.
+ */
+ public boolean importRegionalRegistryStatsFileOrDirectory(String path);
+
+ /**
+ * Save the combined databases in a format that can later be loaded much
+ * more efficiently than importing the original RIR files again.
+ *
+ * @param path Path to the combined database file.
+ * @return True if saving the combined database file was successful,
+ * false otherwise.
+ */
+ public boolean saveCombinedDatabases(String path);
+
+}
diff --git a/task-6471/java/src/org/torproject/task6471/DatabaseImporterImpl.java b/task-6471/java/src/org/torproject/task6471/DatabaseImporterImpl.java
new file mode 100644
index 0000000..384b2d0
--- /dev/null
+++ b/task-6471/java/src/org/torproject/task6471/DatabaseImporterImpl.java
@@ -0,0 +1,456 @@
+package org.torproject.task6471;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.Stack;
+import java.util.TreeMap;
+
+public class DatabaseImporterImpl extends DatabaseImpl
+ implements DatabaseImporter {
+
+ /**
+ * Parse one or more stats files.
+ */
+ public boolean importRegionalRegistryStatsFileOrDirectory(String path) {
+ boolean allImportsSuccessful = true;
+ Stack<File> stackedFiles = new Stack<File>();
+ stackedFiles.add(new File(path));
+ List<File> allFiles = new ArrayList<File>();
+ while (!stackedFiles.isEmpty()) {
+ File file = stackedFiles.pop();
+ if (file.isDirectory()) {
+ stackedFiles.addAll(Arrays.asList(file.listFiles()));
+ } else if (file.getName().endsWith(".md5") ||
+ file.getName().endsWith(".md5.gz") ||
+ file.getName().endsWith(".asc") ||
+ file.getName().endsWith(".asc.gz")) {
+ System.err.println("Signature and digest files are not supported "
+ + "yet: '" + file.getAbsolutePath() + "'. Skipping.");
+ /* TODO Implement checking signatures/digests. */
+ } else if (file.getName().endsWith(".gz") ||
+ file.getName().endsWith(".bz2")) {
+ System.err.println("Parsing compressed files is not supported "
+ + "yet: '" + file.getAbsolutePath() + "'. Skipping.");
+ /* TODO Implement parsing compressed files. */
+ } else {
+ /* TODO Make sure that we're not importing files for a date if we
+ * have less than all five of them. */
+ allFiles.add(file);
+ }
+ }
+ Collections.sort(allFiles, Collections.reverseOrder());
+ for (File file : allFiles) {
+ String databaseFileName = file.getName();
+ if (this.databaseFileNames.contains(databaseFileName)) {
+ /* We already imported this file while loading combined databases
+ * from disk. */
+ continue;
+ }
+ if (!this.importRegionalRegistryStatsFile(file)) {
+ allImportsSuccessful = false;
+ }
+ }
+ return allImportsSuccessful;
+ }
+
+ /**
+ * Simple and not very robust implementation of an RIR stats file
+ * parser.
+ */
+ private boolean importRegionalRegistryStatsFile(File file) {
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(file));
+ String line;
+ String databaseFileName = file.getName();
+ while ((line = br.readLine()) != null) {
+ if (line.startsWith("#") || line.length() == 0) {
+ /* Skip comment line. */
+ continue;
+ }
+ String[] parts = line.split("\\|");
+ if (parts[0].equals("2")) {
+ continue;
+ }
+ if (parts[1].equals("*")) {
+ /* Skip summary line. */
+ continue;
+ }
+ String type = parts[2];
+ if (type.equals("asn")) {
+ continue;
+ } else if (type.equals("ipv6")) {
+ continue;
+ }
+ String countryCode = parts[1].toLowerCase();
+ String startAddressString = parts[3];
+ long addresses = Long.parseLong(parts[4]);
+ this.addRange(databaseFileName, countryCode, startAddressString,
+ addresses);
+ }
+ br.close();
+ this.repairTree();
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Internal counters for import statistics.
+ */
+ private int rangeImports = 0, rangeImportsKeyLookups = 0;
+
+ /**
+ * Add a single address and date range to the tree, which may require
+ * splitting up existing ranges.
+ *
+ * This method has default visibility and is not specified in the
+ * interface, because the caller needs to make sure that repairTree()
+ * is called prior to any lookupAddress() calls. No further checks are
+ * performed that the tree is repaired before looking up an address.
+ */
+ void addRange(String databaseFileName, String countryCode,
+ String startAddressString, long addresses) {
+
+ this.rangeImports++;
+ String databaseDateString =
+ databaseFileName.substring(databaseFileName.length() - 8);
+ int databaseDate = convertDateStringToNumber(databaseDateString);
+ long startAddress = convertAddressStringToNumber(startAddressString);
+ long endAddress = startAddress + addresses - 1L;
+
+ /* Add new database date and file name if we didn't know them yet,
+ * and note that we need to repair the tree after importing. */
+ if (!this.databaseDates.contains(databaseDate)) {
+ this.databaseDates.add(databaseDate);
+ this.addedDatabaseDate = databaseDate;
+ }
+ this.databaseFileNames.add(databaseFileName);
+
+ /* We might have to split existing ranges or the new range before
+ * adding it to the tree, and we might have to remove existing ranges.
+ * We shouldn't mess with the tree directly while iterating over it,
+ * so let's for now only calculate what changes we want to make. */
+ SortedMap<Long, TreeElement> updateElements =
+ this.getUpdatesForAddingRange(databaseDate, countryCode,
+ startAddress, endAddress);
+
+ /* Apply updates. Elements with non-null values are added, elements
+ * with null values are removed. */
+ for (Map.Entry<Long, TreeElement> e : updateElements.entrySet()) {
+ if (e.getValue() == null) {
+ this.ranges.remove(e.getKey());
+ } else {
+ this.ranges.put(e.getKey(), e.getValue());
+ }
+ }
+ }
+
+ /**
+ * Calculate necessary changes to the tree to add a range.
+ */
+ private SortedMap<Long, TreeElement> getUpdatesForAddingRange(
+ int databaseDate, String countryCode, long startAddress,
+ long endAddress) {
+
+ /* Keep updates in a single tree where non-null values will later be
+ * added, possibly replacing existing elements, and null values will
+ * be removed from the tree. */
+ SortedMap<Long, TreeElement> updateElements =
+ new TreeMap<Long, TreeElement>();
+
+ /* Find out previous and next database, so that we can possibly merge
+ * ranges. */
+ int previousDatabaseDate =
+ this.databaseDates.headSet(databaseDate).isEmpty() ? -1 :
+ this.databaseDates.headSet(databaseDate).last();
+ int nextDatabaseDate =
+ this.databaseDates.tailSet(databaseDate + 1).isEmpty() ? -1 :
+ this.databaseDates.tailSet(databaseDate + 1).first();
+
+ /* Remember the address boundaries of the next (partial) range to be
+ * added. */
+ long nextStartAddress = startAddress, nextEndAddress = endAddress;
+ int nextFirstDbDate = databaseDate, nextLastDbDate = databaseDate;
+
+ /* Iterate backwards over the existing ranges, starting at the end
+ * address of the range to be added and at the last conceivable
+ * database publication date. */
+ for (Map.Entry<Long, TreeElement> e : this.ranges.tailMap(
+ convertAddressAndDateToKey(endAddress + 1L, 0) - 1L).entrySet()) {
+ this.rangeImportsKeyLookups++;
+
+ /* Extract everything we need to know from the next existing range
+ * we're looking at. */
+ long eStartAddress = convertKeyToAddress(e.getKey());
+ long eEndAddress = e.getValue().endAddress;
+ int eFirstDbDate = convertKeyToDate(e.getKey());
+ int eLastDbDate = e.getValue().lastDbDate;
+ String eCountryCode = e.getValue().countryCode;
+
+ /* If the next (partial) range starts after the current element
+ * ends, add the new range. */
+ if (nextStartAddress > eEndAddress &&
+ nextEndAddress >= startAddress) {
+ updateElements.put(convertAddressAndDateToKey(nextStartAddress,
+ nextFirstDbDate), new TreeElement(nextEndAddress,
+ nextLastDbDate, countryCode));
+ nextEndAddress = nextStartAddress - 1L;
+ nextStartAddress = startAddress;
+ nextFirstDbDate = databaseDate;
+ nextLastDbDate = databaseDate;
+ }
+
+ /* If the next (partial) range still ends after the current element
+ * ends, add the new range. */
+ if (nextEndAddress > eEndAddress &&
+ nextEndAddress >= startAddress) {
+ updateElements.put(convertAddressAndDateToKey(eEndAddress + 1L,
+ databaseDate), new TreeElement(nextEndAddress, databaseDate,
+ countryCode));
+ nextEndAddress = eEndAddress;
+ nextStartAddress = startAddress;
+ nextFirstDbDate = databaseDate;
+ nextLastDbDate = databaseDate;
+ }
+
+ /* If the existing range ends before the new range starts, we don't
+ * have to look at any more existing ranges. */
+ if (eEndAddress < startAddress) {
+ break;
+ }
+
+ /* Cut off any address range parts of the existing element that are
+ * not contained in the currently added range. First check whether
+ * the existing range ends after the newly added range. In that
+ * case cut off the overlapping part and store it as a new
+ * element.*/
+ if (eStartAddress <= endAddress && eEndAddress > endAddress) {
+ updateElements.put(convertAddressAndDateToKey(endAddress + 1L,
+ eFirstDbDate), new TreeElement(eEndAddress, eLastDbDate,
+ eCountryCode));
+ updateElements.put(convertAddressAndDateToKey(eStartAddress,
+ eFirstDbDate), new TreeElement(endAddress, eLastDbDate,
+ eCountryCode));
+ eEndAddress = endAddress;
+ }
+
+ /* Similarly, check whether the existing range starts before the
+ * newly added one. If so, cut off the overlapping part and store
+ * it as new element. */
+ if (eStartAddress < startAddress && eEndAddress >= startAddress) {
+ updateElements.put(convertAddressAndDateToKey(eStartAddress,
+ eFirstDbDate), new TreeElement(startAddress - 1L, eLastDbDate,
+ eCountryCode));
+ updateElements.put(convertAddressAndDateToKey(startAddress,
+ eFirstDbDate), new TreeElement(eEndAddress, eLastDbDate,
+ eCountryCode));
+ eStartAddress = startAddress;
+ }
+
+ /* Now we're sure the existing element doesn't exceed the newly
+ * added element, address-wise. */
+ nextStartAddress = eStartAddress;
+ nextEndAddress = eEndAddress;
+
+ /* If the range is already contained and has the same country code,
+ * mark it as updated. If it's contained with a different country
+ * code, ignore the update. */
+ if (eFirstDbDate <= databaseDate && eLastDbDate >= databaseDate) {
+ if (eCountryCode.equals(countryCode)) {
+ nextFirstDbDate = eFirstDbDate;
+ nextLastDbDate = eLastDbDate;
+ } else {
+ updateElements.clear();
+ return updateElements;
+ }
+ }
+
+ /* See if we can merge the new range with the previous or next
+ * range. If so, extend our database range and mark the existing
+ * element for deletion. */
+ if (eCountryCode.equals(countryCode)) {
+ if (eLastDbDate == previousDatabaseDate) {
+ nextFirstDbDate = eFirstDbDate;
+ updateElements.put(convertAddressAndDateToKey(eStartAddress,
+ eFirstDbDate), null);
+ } else if (eFirstDbDate == nextDatabaseDate) {
+ nextLastDbDate = eLastDbDate;
+ updateElements.put(convertAddressAndDateToKey(eStartAddress,
+ eFirstDbDate), null);
+ }
+ }
+ }
+
+ /* If there's still some part (or the whole?) address range left to
+ * add, add it now. */
+ while (nextEndAddress >= startAddress) {
+ updateElements.put(convertAddressAndDateToKey(nextStartAddress,
+ nextFirstDbDate), new TreeElement(nextEndAddress,
+ nextLastDbDate, countryCode));
+ nextEndAddress = nextStartAddress - 1L;
+ nextStartAddress = startAddress;
+ nextFirstDbDate = databaseDate;
+ nextLastDbDate = databaseDate;
+ }
+
+ /* Return the tree updates that will add the given range. */
+ return updateElements;
+ }
+
+ /**
+ * Internal counter for millis spent on repairing the tree.
+ */
+ private long treeRepairMillis = 0L;
+
+ /* Newly added database date, or -1 if a database from an already known
+ * date was imported. */
+ private int addedDatabaseDate = -1;
+
+ /**
+ * Repair tree by making sure that any range from a given database date
+ * to another is still valid when considering any other database that
+ * was imported later.
+ *
+ * It's okay to split a date range when importing a database from
+ * another registry that doesn't contain the given address range. We'll
+ * merge the two date ranges when parsing that registry's file.
+ *
+ * This method has default visibility and is not specified in the
+ * interface, because the caller needs to make sure that repairTree()
+ * is called prior to any lookupAddress() calls. No further checks are
+ * performed that the tree is repaired before look up an address.
+ */
+ void repairTree() {
+ if (this.addedDatabaseDate < 0) {
+ return;
+ }
+ long startedRepairingTree = System.currentTimeMillis();
+ SortedMap<Long, TreeElement> updateElements =
+ new TreeMap<Long, TreeElement>();
+ for (Map.Entry<Long, TreeElement> e : this.ranges.entrySet()) {
+ if (e.getValue().modifiedInLastImport) {
+ e.getValue().modifiedInLastImport = false;
+ } else {
+ int eFirstDbDate = convertKeyToDate(e.getKey());
+ int eLastDbDate = e.getValue().lastDbDate;
+ long eStartAddress = convertKeyToAddress(e.getKey());
+ long eEndAddress = e.getValue().endAddress;
+ String eCountryCode = e.getValue().countryCode;
+ int start = eFirstDbDate, end = eFirstDbDate;
+ for (int cur : this.databaseDates.tailSet(eFirstDbDate)) {
+ if (cur > eLastDbDate) {
+ break;
+ }
+ if (cur == addedDatabaseDate) {
+ if (start >= 0 && end >= 0) {
+ updateElements.put(convertAddressAndDateToKey(eStartAddress,
+ start), new TreeElement(eEndAddress, end,
+ eCountryCode));
+ start = end = -1;
+ }
+ } else if (start < 0) {
+ start = end = cur;
+ } else {
+ end = cur;
+ }
+ }
+ if (start >= 0 && end >= 0) {
+ updateElements.put(convertAddressAndDateToKey(eStartAddress,
+ start), new TreeElement(eEndAddress, end, eCountryCode));
+ }
+ }
+ }
+ for (Map.Entry<Long, TreeElement> e : updateElements.entrySet()) {
+ this.ranges.put(e.getKey(), e.getValue());
+ }
+ this.addedDatabaseDate = -1;
+ this.treeRepairMillis += (System.currentTimeMillis()
+ - startedRepairingTree);
+ }
+
+ /**
+ * Return number of contained ranges.
+ */
+ int getNumberOfElements() {
+ return this.ranges.size();
+ }
+
+ /**
+ * Save the combined databases to disk.
+ */
+ public boolean saveCombinedDatabases(String path) {
+ try {
+
+ /* Create parent directories if necessary. */
+ File file = new File(path);
+ if (file.getParentFile() != null) {
+ file.getParentFile().mkdirs();
+ }
+
+ /* Start with writing all contained database file names to the file
+ * header. */
+ BufferedWriter bw = new BufferedWriter(new FileWriter(file));
+ for (String databaseFileName : this.databaseFileNames) {
+ bw.write("!" + databaseFileName + "\n");
+ }
+
+ /* Next write all database ranges in the same order as they are
+ * currently contained in memory. The only information we can drop
+ * is the last known database index of each range, because we assume
+ * the tree is already in repaired state. */
+ for (Map.Entry<Long, TreeElement> e : this.ranges.entrySet()) {
+ bw.write(String.format("%s,%s,%s,%s,%s%n",
+ convertKeyToAddressString(e.getKey()),
+ convertAddressNumberToString(e.getValue().endAddress),
+ e.getValue().countryCode,
+ convertKeyToDateString(e.getKey()),
+ convertDateNumberToString(e.getValue().lastDbDate)));
+ }
+ bw.close();
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /* Return a nicely formatted string summarizing database contents and
+ * usage statistics. */
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Tree contains %d databases and %d combined "
+ + "address ranges.\n"
+ + "Performed %d address range imports requiring %d lookups.\n"
+ + "Performed %d address lookups requiring %d lookups.\n"
+ + "Spent %d millis on repairing tree.\n"
+ + "First 10 entries, in reverse order, are:",
+ this.databaseDates.size(), this.ranges.size(), this.rangeImports,
+ this.rangeImportsKeyLookups, this.addressLookups,
+ this.addressLookupsKeyLookups, this.treeRepairMillis));
+ int entries = 10;
+ for (Map.Entry<Long, TreeElement> e : this.ranges.entrySet()) {
+ sb.append(String.format("%n %s %s %s %s %s",
+ convertKeyToAddressString(e.getKey()),
+ convertAddressNumberToString(e.getValue().endAddress),
+ e.getValue().countryCode,
+ convertKeyToDateString(e.getKey()),
+ convertDateNumberToString(e.getValue().lastDbDate)));
+ if (--entries <= 0) {
+ break;
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/task-6471/java/src/org/torproject/task6471/DatabasePerformanceExample.java b/task-6471/java/src/org/torproject/task6471/DatabasePerformanceExample.java
index 3b273bc..4338fad 100644
--- a/task-6471/java/src/org/torproject/task6471/DatabasePerformanceExample.java
+++ b/task-6471/java/src/org/torproject/task6471/DatabasePerformanceExample.java
@@ -55,7 +55,7 @@ public class DatabasePerformanceExample {
String dbMonth = file.getName().substring(
file.getName().length() - 8);
dbMonth = dbMonth.substring(0, 6);
- Database temp = new DatabaseImpl();
+ DatabaseImporter temp = new DatabaseImporterImpl();
temp.importRegionalRegistryStatsFileOrDirectory(
file.getAbsolutePath());
for (long test : tests) {
@@ -67,8 +67,9 @@ public class DatabasePerformanceExample {
convertAddressNumberToString(test >> 16);
String testDateString = DatabaseImpl.convertDateNumberToString(
testDate);
- String countryCode = temp.lookupAddress(testAddressString,
- testDateString);
+ String countryCode =
+ temp.lookupCountryCodeFromIpv4AddressAndDate(
+ testAddressString, testDateString);
if (countryCode != null) {
results.put(test, countryCode);
}
@@ -80,8 +81,9 @@ public class DatabasePerformanceExample {
System.out.print("Importing files... ");
startMillis = endMillis;
- Database database = new DatabaseImpl();
- database.importRegionalRegistryStatsFileOrDirectory("../data");
+ DatabaseImporter combinedDatabase = new DatabaseImporterImpl();
+ combinedDatabase.importRegionalRegistryStatsFileOrDirectory(
+ "../data");
endMillis = System.currentTimeMillis();
System.out.println((endMillis - startMillis) + " millis.");
@@ -94,7 +96,9 @@ public class DatabasePerformanceExample {
String testDate = DatabaseImpl.convertDateNumberToString(
(int) (test & ((1 << 16) - 1)));
String expected = results.get(test);
- String result = database.lookupAddress(testAddress, testDate);
+ String result =
+ combinedDatabase.lookupCountryCodeFromIpv4AddressAndDate(
+ testAddress, testDate);
if ((expected == null && result != null) ||
(expected != null && !expected.equals(result))) {
//System.out.println("Expected " + expected + " for "
@@ -106,18 +110,18 @@ public class DatabasePerformanceExample {
System.out.println((endMillis - startMillis) + " millis, " + failures
+ " out of " + tests.size() + " tests failed.");
- System.out.println(database);
+ System.out.println(combinedDatabase);
System.out.print("Saving combined databases to disk... ");
startMillis = endMillis;
- database.saveCombinedDatabases("geoip-2007-10-2012-09.csv");
+ combinedDatabase.saveCombinedDatabases("geoip-2007-10-2012-09.csv");
endMillis = System.currentTimeMillis();
System.out.println((endMillis - startMillis) + " millis.");
startMillis = endMillis;
System.out.print("Loading combined databases from disk... ");
startMillis = endMillis;
- database = new DatabaseImpl();
+ Database database = new DatabaseImpl();
database.loadCombinedDatabases("geoip-2007-10-2012-09.csv");
endMillis = System.currentTimeMillis();
System.out.println((endMillis - startMillis) + " millis.");
@@ -131,7 +135,8 @@ public class DatabasePerformanceExample {
String testDate = DatabaseImpl.convertDateNumberToString(
(int) (test & ((1 << 16) - 1)));
String expected = results.get(test);
- String result = database.lookupAddress(testAddress, testDate);
+ String result = database.lookupCountryCodeFromIpv4AddressAndDate(
+ testAddress, testDate);
if ((expected == null && result != null) ||
(expected != null && !expected.equals(result))) {
//System.out.println("Expected " + expected + " for "
diff --git a/task-6471/java/src/org/torproject/task6471/DatabaseTest.java b/task-6471/java/src/org/torproject/task6471/DatabaseTest.java
index a56c400..b90ebd0 100644
--- a/task-6471/java/src/org/torproject/task6471/DatabaseTest.java
+++ b/task-6471/java/src/org/torproject/task6471/DatabaseTest.java
@@ -11,135 +11,175 @@ public class DatabaseTest {
@Test()
public void testSingleIpRangeSingleDatebase() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.repairTree();
- assertEquals(1, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals(null, database.lookupAddress("2.255.255.255",
- "19920901"));
- assertEquals(null, database.lookupAddress("2.255.255.255",
- "20020901"));
- assertEquals(null, database.lookupAddress("2.255.255.255",
- "20120901"));
- assertEquals(null, database.lookupAddress("2.255.255.255",
- "20220901"));
- assertEquals("us", database.lookupAddress("3.0.0.0", "19920901"));
- assertEquals("us", database.lookupAddress("3.0.0.0", "20020901"));
- assertEquals("us", database.lookupAddress("3.0.0.0", "20120901"));
- assertEquals("us", database.lookupAddress("3.0.0.0", "20220901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "19920901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20020901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20220901"));
- assertEquals("us", database.lookupAddress("3.255.255.255",
- "19920901"));
- assertEquals("us", database.lookupAddress("3.255.255.255",
- "20020901"));
- assertEquals("us", database.lookupAddress("3.255.255.255",
- "20120901"));
- assertEquals("us", database.lookupAddress("3.255.255.255",
- "20220901"));
- assertEquals(null, database.lookupAddress("4.0.0.0", "19920901"));
- assertEquals(null, database.lookupAddress("4.0.0.0", "20020901"));
- assertEquals(null, database.lookupAddress("4.0.0.0", "20120901"));
- assertEquals(null, database.lookupAddress("4.0.0.0", "20220901"));
+ assertEquals(1, database.getNumberOfElements());
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "2.255.255.255", "19920901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "2.255.255.255", "20020901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "2.255.255.255", "20120901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "2.255.255.255", "20220901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.0.0.0", "19920901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.0.0.0", "20020901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.0.0.0", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.0.0.0", "20220901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "19920901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20020901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20220901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.255.255.255", "19920901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.255.255.255", "20020901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.255.255.255", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.255.255.255", "20220901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.0.0.0", "19920901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.0.0.0", "20020901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.0.0.0", "20120901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.0.0.0", "20220901"));
}
@Test()
public void testTwoAdjacentIpRangesSingleDatabase() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.addRange("20120901", "ca", "4.0.0.0", 16777216);
database.repairTree();
- assertEquals(2, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals(null, database.lookupAddress("2.255.255.255",
- "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals("ca", database.lookupAddress("4.127.0.0", "20120901"));
- assertEquals("ca", database.lookupAddress("4.127.0.0", "20120901"));
- assertEquals("ca", database.lookupAddress("4.127.0.0", "20120901"));
- assertEquals(null, database.lookupAddress("5.0.0.0", "20120901"));
+ assertEquals(2, database.getNumberOfElements());
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "2.255.255.255", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals("ca", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.127.0.0", "20120901"));
+ assertEquals("ca", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.127.0.0", "20120901"));
+ assertEquals("ca", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.127.0.0", "20120901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "5.0.0.0", "20120901"));
}
@Test()
public void testTwoNonAdjacentIpDateRangesSingleDatabase() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.addRange("20120901", "ca", "6.0.0.0", 16777216);
database.repairTree();
- assertEquals(2, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals(null, database.lookupAddress("2.255.255.255", "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals(null, database.lookupAddress("4.255.255.255", "20120901"));
- assertEquals("ca", database.lookupAddress("6.127.0.0", "20120901"));
- assertEquals(null, database.lookupAddress("7.0.0.0", "20120901"));
+ assertEquals(2, database.getNumberOfElements());
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "2.255.255.255", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.255.255.255", "20120901"));
+ assertEquals("ca", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "6.127.0.0", "20120901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "7.0.0.0", "20120901"));
}
@Test()
public void testDuplicateImport() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.repairTree();
- assertEquals(1, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals(null, database.lookupAddress("2.255.255.255", "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals(null, database.lookupAddress("4.0.0.0", "20120901"));
+ assertEquals(1, database.getNumberOfElements());
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "2.255.255.255", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "4.0.0.0", "20120901"));
}
@Test()
public void testDuplicateImportDifferentCountryCode() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.addRange("20120901", "ca", "3.0.0.0", 16777216);
database.repairTree();
- assertEquals(1, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
+ assertEquals(1, database.getNumberOfElements());
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
}
@Test()
public void testLeaveIpChangeUnchanged() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.repairTree();
database.addRange("20121001", "us", "3.0.0.0", 16777216);
database.repairTree();
- assertEquals(1, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120801"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20121001"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20121101"));
+ assertEquals(1, database.getNumberOfElements());
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120801"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20121001"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20121101"));
}
@Test()
public void testLeaveIpChangeUnchangedReverseOrder() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20121001", "us", "3.0.0.0", 16777216);
database.repairTree();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.repairTree();
- assertEquals(1, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120801"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20121001"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20121101"));
+ assertEquals(1, database.getNumberOfElements());
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120801"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20121001"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20121101"));
}
@Test()
public void testMissingIpRange() {
- DatabaseImpl database = new DatabaseImpl();
+ DatabaseImporterImpl database = new DatabaseImporterImpl();
database.addRange("20120901", "us", "3.0.0.0", 16777216);
database.repairTree();
database.addRange("20121101", "us", "3.0.0.0", 16777216);
database.repairTree();
database.addRange("20121001", "us", "6.0.0.0", 16777216);
database.repairTree();
- assertEquals(3, ((DatabaseImpl) database).getNumberOfElements());
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120801"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20120901"));
- assertEquals(null, database.lookupAddress("3.127.0.0", "20121001"));
- assertEquals("us", database.lookupAddress("3.127.0.0", "20121101"));
+ assertEquals(3, database.getNumberOfElements());
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120801"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20120901"));
+ assertEquals(null, database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20121001"));
+ assertEquals("us", database.lookupCountryCodeFromIpv4AddressAndDate(
+ "3.127.0.0", "20121101"));
}
}