Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<configuration>
<format>xml</format>
<maxmem>256m</maxmem>
<check/>
</configuration>
</plugin>
<plugin>
Expand Down
26 changes: 4 additions & 22 deletions src/main/java/com/maxmind/db/BufferHolder.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ final class BufferHolder {
private final ByteBuffer buffer;

BufferHolder(File database, FileMode mode) throws IOException {
final RandomAccessFile file = new RandomAccessFile(database, "r");
boolean threw = true;
try {
final FileChannel channel = file.getChannel();
try (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!

final RandomAccessFile file = new RandomAccessFile(database, "r");
final FileChannel channel = file.getChannel();
) {
if (mode == FileMode.MEMORY) {
this.buffer = ByteBuffer.wrap(new byte[(int) channel.size()]);
if (channel.read(this.buffer) != this.buffer.capacity()) {
Expand All @@ -30,19 +30,6 @@ final class BufferHolder {
} else {
this.buffer = channel.map(MapMode.READ_ONLY, 0, channel.size());
}
threw = false;
} finally {
try {
// Also closes the underlying channel.
file.close();
} catch (final IOException e) {
// If an exception was underway when we entered the finally
// block, don't stomp over it due to an error closing the file
// and channel.
if (!threw) {
throw e;
}
}
}
}

Expand All @@ -66,11 +53,6 @@ final class BufferHolder {
this.buffer = ByteBuffer.wrap(baos.toByteArray());
}

// This is just to ease unit testing
BufferHolder(ByteBuffer buffer) {
this.buffer = buffer;
}

/*
* Returns a duplicate of the underlying ByteBuffer. The returned ByteBuffer
* should not be shared between threads.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/maxmind/db/CHMCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public CHMCache() {

public CHMCache(int capacity) {
this.capacity = capacity;
this.cache = new ConcurrentHashMap<Integer, JsonNode>(capacity);
this.cache = new ConcurrentHashMap<>(capacity);
}

@Override
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/com/maxmind/db/Decoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -23,7 +24,7 @@
*/
final class Decoder {

private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final Charset UTF_8 = StandardCharsets.UTF_8;

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

Expand All @@ -41,14 +42,14 @@ final class Decoder {

private final ByteBuffer buffer;

static enum Type {
enum Type {
EXTENDED, POINTER, UTF8_STRING, DOUBLE, BYTES, UINT16, UINT32, MAP, INT32, UINT64, UINT128, ARRAY, CONTAINER, END_MARKER, BOOLEAN, FLOAT;

// Java clones the array when you call values(). Caching it increased
// the speed by about 5000 requests per second on my machine.
final static Type[] values = Type.values();

public static Type get(int i) {
static Type get(int i) {
return Type.values[i];
}

Expand All @@ -57,7 +58,7 @@ private static Type get(byte b) {
return Type.get(b & 0xFF);
}

public static Type fromControlByte(int b) {
static Type fromControlByte(int b) {
// The type is encoded in the first 3 bits of the byte.
return Type.get((byte) ((0xFF & b) >>> 5));
}
Expand Down Expand Up @@ -87,7 +88,7 @@ JsonNode decode(int offset) throws IOException {
return decode();
}

JsonNode decode() throws IOException {
private JsonNode decode() throws IOException {
int ctrlByte = 0xFF & this.buffer.get();

Type type = Type.fromControlByte(ctrlByte);
Expand Down Expand Up @@ -171,7 +172,6 @@ private JsonNode decodeByType(Type type, int size)
case INT32:
return this.decodeInt32(size);
case UINT64:
return this.decodeBigInteger(size);
case UINT128:
return this.decodeBigInteger(size);
default:
Expand Down Expand Up @@ -263,7 +263,7 @@ private static BooleanNode decodeBoolean(int size)

private JsonNode decodeArray(int size) throws IOException {

List<JsonNode> array = new ArrayList<JsonNode>(size);
List<JsonNode> array = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
JsonNode r = this.decode();
array.add(r);
Expand All @@ -274,7 +274,7 @@ private JsonNode decodeArray(int size) throws IOException {

private JsonNode decodeMap(int size) throws IOException {
int capacity = (int) (size / 0.75F + 1.0F);
Map<String, JsonNode> map = new HashMap<String, JsonNode>(capacity);
Map<String, JsonNode> map = new HashMap<>(capacity);

for (int i = 0; i < size; i++) {
String key = this.decode().asText();
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/com/maxmind/db/Network.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.maxmind.db;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
* <code>Network</code> represents an IP network.
*/
public final class Network {
private final InetAddress ipAddress;
private final int prefixLength;
private InetAddress networkAddress = null;

/**
* Construct a <code>Network</code>
*
* @param ipAddress An IP address in the network. This does not have to be
* the first address in the network.
* @param prefixLength The prefix length for the network.
*/
public Network(InetAddress ipAddress, int prefixLength) {
this.ipAddress = ipAddress;
this.prefixLength = prefixLength;
}

/**
* @return The first address in the network.
*/
public InetAddress getNetworkAddress() {
if (networkAddress != null) {
return networkAddress;
}
byte[] ipBytes = ipAddress.getAddress();
byte[] networkBytes = new byte[ipBytes.length];
int curPrefix = prefixLength;
for (int i = 0; i < ipBytes.length && curPrefix > 0; i++) {
byte b = ipBytes[i];
if (curPrefix < 8) {
int shiftN = 8 - curPrefix;
b = (byte) ((b >> shiftN) << shiftN);
}
networkBytes[i] = b;
curPrefix -= 8;
}

try {
networkAddress = InetAddress.getByAddress(networkBytes);
} catch (UnknownHostException e) {
throw new RuntimeException("Illegal network address byte length of " + networkBytes.length);
}
return networkAddress;
}

/**
* @return The prefix length is the number of leading 1 bits in the subnet
* mask. Sometimes also known as netmask length.
*/
public int getPrefixLength() {
return prefixLength;
}

/***
* @return A string representation of the network in CIDR notation, e.g.,
* <code>1.2.3.0/24</code> or <code>2001::/8</code>.
*/
public String toString() {
return getNetworkAddress().getHostAddress() + "/" + prefixLength;
}
}
4 changes: 2 additions & 2 deletions src/main/java/com/maxmind/db/NodeCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

public interface NodeCache {

public interface Loader {
interface Loader {
JsonNode load(int key) throws IOException;
}

public JsonNode get(int key, Loader loader) throws IOException;
JsonNode get(int key, Loader loader) throws IOException;

}
67 changes: 35 additions & 32 deletions src/main/java/com/maxmind/db/Reader.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public Reader(File database, FileMode fileMode, NodeCache cache) throws IOExcept
}

private Reader(BufferHolder bufferHolder, String name, NodeCache cache) throws IOException {
this.bufferHolderReference = new AtomicReference<BufferHolder>(
this.bufferHolderReference = new AtomicReference<>(
bufferHolder);

if (cache == null) {
Expand All @@ -135,52 +135,55 @@ private Reader(BufferHolder bufferHolder, String name, NodeCache cache) throws I
}

/**
* Looks up the <code>address</code> in the MaxMind DB.
* Looks up <code>ipAddress</code> in the MaxMind DB.
*
* @param ipAddress the IP address to look up.
* @return the record for the IP address.
* @return the record data for the IP address.
* @throws IOException if a file I/O error occurs.
*/
public JsonNode get(InetAddress ipAddress) throws IOException {
ByteBuffer buffer = this.getBufferHolder().get();
int pointer = this.findAddressInTree(buffer, ipAddress);
if (pointer == 0) {
return null;
}
return this.resolveDataPointer(buffer, pointer);
}

private BufferHolder getBufferHolder() throws ClosedDatabaseException {
BufferHolder bufferHolder = this.bufferHolderReference.get();
if (bufferHolder == null) {
throw new ClosedDatabaseException();
}
return bufferHolder;
return getRecord(ipAddress).getData();
}
/**
* Looks up <code>ipAddress</code> in the MaxMind DB.
*
* @param ipAddress the IP address to look up.
* @return the record for the IP address. If there is no data for the
* address, the non-null {@link Record} will still be returned.
* @throws IOException if a file I/O error occurs.
*/
public Record getRecord(InetAddress ipAddress)
throws IOException {
ByteBuffer buffer = this.getBufferHolder().get();

private int findAddressInTree(ByteBuffer buffer, InetAddress address)
throws InvalidDatabaseException {
byte[] rawAddress = address.getAddress();
byte[] rawAddress = ipAddress.getAddress();

int bitLength = rawAddress.length * 8;
int record = this.startNode(bitLength);
int nodeCount = this.metadata.getNodeCount();

for (int i = 0; i < bitLength; i++) {
if (record >= this.metadata.getNodeCount()) {
break;
}
int b = 0xFF & rawAddress[i / 8];
int bit = 1 & (b >> 7 - (i % 8));
int pl = 0;
for (; pl < bitLength && record < nodeCount; pl++) {
int b = 0xFF & rawAddress[pl / 8];
int bit = 1 & (b >> 7 - (pl % 8));
record = this.readNode(buffer, record, bit);
}
if (record == this.metadata.getNodeCount()) {
// record is empty
return 0;
} else if (record > this.metadata.getNodeCount()) {

JsonNode dataRecord = null;
if (record > nodeCount) {
// record is a data pointer
return record;
dataRecord = this.resolveDataPointer(buffer, record);
}
throw new InvalidDatabaseException("Something bad happened");

return new Record(dataRecord, ipAddress, pl);
}

private BufferHolder getBufferHolder() throws ClosedDatabaseException {
BufferHolder bufferHolder = this.bufferHolderReference.get();
if (bufferHolder == null) {
throw new ClosedDatabaseException();
}
return bufferHolder;
}

private int startNode(int bitLength) {
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/com/maxmind/db/Record.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.maxmind.db;

import com.fasterxml.jackson.databind.JsonNode;

import java.net.InetAddress;

/**
* Record represents the data and metadata associated with a database lookup.
*/
public final class Record {
private final JsonNode data;
private final Network network;

/**
* Create a new record.
*
* @param data the data for the record in the database.
* @param ipAddress the IP address used in the lookup.
* @param prefixLength the network prefix length associated with the record in the database.
*/
public Record( JsonNode data, InetAddress ipAddress, int prefixLength) {
this.data = data;
this.network = new Network(ipAddress, prefixLength);
}

/**
* @return the data for the record in the database. The record will be
* <code>null</code> if there was no data for the address in the database.
*/
public JsonNode getData() {
return data;
}

/**
* @return the network associated with the record in the database. This is
* the largest network where all of the IPs in the network have the same
* data.
*/
public Network getNetwork() {
return network;
}
}
3 changes: 0 additions & 3 deletions src/main/java/com/maxmind/db/package-info.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/**
*
*/
/**
* @author greg
*
Expand Down
Loading