Modify MaxMind-DB-Reader-java

to remove the dependency on the large com.fasterxml.jackson.databind JSON package,
and use POJOs instead.
This commit is contained in:
zzz
2018-11-20 11:03:47 +00:00
parent 98de1ae404
commit 3923db0677
6 changed files with 80 additions and 80 deletions

View File

@ -3,8 +3,6 @@ package com.maxmind.db;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import com.fasterxml.jackson.databind.JsonNode;
/** /**
* A simplistic cache using a {@link ConcurrentHashMap}. There's no eviction * A simplistic cache using a {@link ConcurrentHashMap}. There's no eviction
* policy, it just fills up until reaching the specified capacity <small>(or * policy, it just fills up until reaching the specified capacity <small>(or
@ -15,7 +13,7 @@ public class CHMCache implements NodeCache {
private static final int DEFAULT_CAPACITY = 4096; private static final int DEFAULT_CAPACITY = 4096;
private final int capacity; private final int capacity;
private final ConcurrentHashMap<Integer, JsonNode> cache; private final ConcurrentHashMap<Integer, Object> cache;
private boolean cacheFull = false; private boolean cacheFull = false;
public CHMCache() { public CHMCache() {
@ -24,13 +22,13 @@ public class CHMCache implements NodeCache {
public CHMCache(int capacity) { public CHMCache(int capacity) {
this.capacity = capacity; this.capacity = capacity;
this.cache = new ConcurrentHashMap<Integer, JsonNode>(capacity); this.cache = new ConcurrentHashMap<Integer, Object>(capacity);
} }
@Override @Override
public JsonNode get(int key, Loader loader) throws IOException { public Object get(int key, Loader loader) throws IOException {
Integer k = key; Integer k = key;
JsonNode value = cache.get(k); Object value = cache.get(k);
if (value == null) { if (value == null) {
value = loader.load(key); value = loader.load(key);
if (!cacheFull) { if (!cacheFull) {

View File

@ -12,10 +12,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.*;
/* /*
* Decoder for MaxMind DB data. * Decoder for MaxMind DB data.
* *
@ -25,8 +21,6 @@ final class Decoder {
private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final int[] POINTER_VALUE_OFFSETS = { 0, 0, 1 << 11, (1 << 19) + ((1) << 11), 0 }; private static final int[] POINTER_VALUE_OFFSETS = { 0, 0, 1 << 11, (1 << 19) + ((1) << 11), 0 };
// XXX - This is only for unit testings. We should possibly make a // XXX - This is only for unit testings. We should possibly make a
@ -71,12 +65,12 @@ final class Decoder {
private final NodeCache.Loader cacheLoader = new NodeCache.Loader() { private final NodeCache.Loader cacheLoader = new NodeCache.Loader() {
@Override @Override
public JsonNode load(int key) throws IOException { public Object load(int key) throws IOException {
return decode(key); return decode(key);
} }
}; };
JsonNode decode(int offset) throws IOException { Object decode(int offset) throws IOException {
if (offset >= this.buffer.capacity()) { if (offset >= this.buffer.capacity()) {
throw new InvalidDatabaseException( throw new InvalidDatabaseException(
"The MaxMind DB file's data section contains bad data: " "The MaxMind DB file's data section contains bad data: "
@ -87,7 +81,7 @@ final class Decoder {
return decode(); return decode();
} }
JsonNode decode() throws IOException { Object decode() throws IOException {
int ctrlByte = 0xFF & this.buffer.get(); int ctrlByte = 0xFF & this.buffer.get();
Type type = Type.fromControlByte(ctrlByte); Type type = Type.fromControlByte(ctrlByte);
@ -103,12 +97,12 @@ final class Decoder {
// for unit testing // for unit testing
if (this.POINTER_TEST_HACK) { if (this.POINTER_TEST_HACK) {
return new LongNode(pointer); return Long.valueOf(pointer);
} }
int targetOffset = (int) pointer; int targetOffset = (int) pointer;
int position = buffer.position(); int position = buffer.position();
JsonNode node = cache.get(targetOffset, cacheLoader); Object node = cache.get(targetOffset, cacheLoader);
buffer.position(position); buffer.position(position);
return node; return node;
} }
@ -147,7 +141,7 @@ final class Decoder {
return this.decodeByType(type, size); return this.decodeByType(type, size);
} }
private JsonNode decodeByType(Type type, int size) private Object decodeByType(Type type, int size)
throws IOException { throws IOException {
switch (type) { switch (type) {
case MAP: case MAP:
@ -157,13 +151,13 @@ final class Decoder {
case BOOLEAN: case BOOLEAN:
return Decoder.decodeBoolean(size); return Decoder.decodeBoolean(size);
case UTF8_STRING: case UTF8_STRING:
return new TextNode(this.decodeString(size)); return this.decodeString(size);
case DOUBLE: case DOUBLE:
return this.decodeDouble(size); return this.decodeDouble(size);
case FLOAT: case FLOAT:
return this.decodeFloat(size); return this.decodeFloat(size);
case BYTES: case BYTES:
return new BinaryNode(this.getByteArray(size)); return this.getByteArray(size);
case UINT16: case UINT16:
return this.decodeUint16(size); return this.decodeUint16(size);
case UINT32: case UINT32:
@ -188,12 +182,12 @@ final class Decoder {
return s; return s;
} }
private IntNode decodeUint16(int size) { private Integer decodeUint16(int size) {
return new IntNode(this.decodeInteger(size)); return Integer.valueOf(this.decodeInteger(size));
} }
private IntNode decodeInt32(int size) { private Integer decodeInt32(int size) {
return new IntNode(this.decodeInteger(size)); return Integer.valueOf(this.decodeInteger(size));
} }
private long decodeLong(int size) { private long decodeLong(int size) {
@ -204,8 +198,8 @@ final class Decoder {
return integer; return integer;
} }
private LongNode decodeUint32(int size) { private Long decodeUint32(int size) {
return new LongNode(this.decodeLong(size)); return Long.valueOf(this.decodeLong(size));
} }
private int decodeInteger(int size) { private int decodeInteger(int size) {
@ -224,36 +218,36 @@ final class Decoder {
return integer; return integer;
} }
private BigIntegerNode decodeBigInteger(int size) { private BigInteger decodeBigInteger(int size) {
byte[] bytes = this.getByteArray(size); byte[] bytes = this.getByteArray(size);
return new BigIntegerNode(new BigInteger(1, bytes)); return new BigInteger(1, bytes);
} }
private DoubleNode decodeDouble(int size) throws InvalidDatabaseException { private Double decodeDouble(int size) throws InvalidDatabaseException {
if (size != 8) { if (size != 8) {
throw new InvalidDatabaseException( throw new InvalidDatabaseException(
"The MaxMind DB file's data section contains bad data: " "The MaxMind DB file's data section contains bad data: "
+ "invalid size of double."); + "invalid size of double.");
} }
return new DoubleNode(this.buffer.getDouble()); return Double.valueOf(this.buffer.getDouble());
} }
private FloatNode decodeFloat(int size) throws InvalidDatabaseException { private Float decodeFloat(int size) throws InvalidDatabaseException {
if (size != 4) { if (size != 4) {
throw new InvalidDatabaseException( throw new InvalidDatabaseException(
"The MaxMind DB file's data section contains bad data: " "The MaxMind DB file's data section contains bad data: "
+ "invalid size of float."); + "invalid size of float.");
} }
return new FloatNode(this.buffer.getFloat()); return Float.valueOf(this.buffer.getFloat());
} }
private static BooleanNode decodeBoolean(int size) private static Boolean decodeBoolean(int size)
throws InvalidDatabaseException { throws InvalidDatabaseException {
switch (size) { switch (size) {
case 0: case 0:
return BooleanNode.FALSE; return Boolean.FALSE;
case 1: case 1:
return BooleanNode.TRUE; return Boolean.TRUE;
default: default:
throw new InvalidDatabaseException( throw new InvalidDatabaseException(
"The MaxMind DB file's data section contains bad data: " "The MaxMind DB file's data section contains bad data: "
@ -261,28 +255,28 @@ final class Decoder {
} }
} }
private JsonNode decodeArray(int size) throws IOException { private List<Object> decodeArray(int size) throws IOException {
List<JsonNode> array = new ArrayList<JsonNode>(size); List<Object> array = new ArrayList<Object>(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
JsonNode r = this.decode(); Object r = this.decode();
array.add(r); array.add(r);
} }
return new ArrayNode(OBJECT_MAPPER.getNodeFactory(), Collections.unmodifiableList(array)); return Collections.unmodifiableList(array);
} }
private JsonNode decodeMap(int size) throws IOException { private Map<String, Object> decodeMap(int size) throws IOException {
int capacity = (int) (size / 0.75F + 1.0F); int capacity = (int) (size / 0.75F + 1.0F);
Map<String, JsonNode> map = new HashMap<String, JsonNode>(capacity); Map<String, Object> map = new HashMap<String, Object>(capacity);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
String key = this.decode().asText(); String key = (String) this.decode();
JsonNode value = this.decode(); Object value = this.decode();
map.put(key, value); map.put(key, value);
} }
return new ObjectNode(OBJECT_MAPPER.getNodeFactory(), Collections.unmodifiableMap(map)); return Collections.unmodifiableMap(map);
} }
private byte[] getByteArray(int length) { private byte[] getByteArray(int length) {

View File

@ -6,10 +6,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public final class Metadata { public final class Metadata {
private final int binaryFormatMajorVersion; private final int binaryFormatMajorVersion;
private final int binaryFormatMinorVersion; private final int binaryFormatMinorVersion;
@ -18,11 +14,11 @@ public final class Metadata {
private final String databaseType; private final String databaseType;
private final JsonNode description; private final Map description;
private final int ipVersion; private final int ipVersion;
private final JsonNode languages; private final List languages;
private final int nodeByteSize; private final int nodeByteSize;
@ -32,22 +28,43 @@ public final class Metadata {
private final int searchTreeSize; private final int searchTreeSize;
Metadata(JsonNode metadata) { Metadata(Map metadata) {
this.binaryFormatMajorVersion = metadata.get( this.binaryFormatMajorVersion = getInt(metadata,
"binary_format_major_version").asInt(); "binary_format_major_version");
this.binaryFormatMinorVersion = metadata.get( this.binaryFormatMinorVersion = getInt(metadata,
"binary_format_minor_version").asInt(); "binary_format_minor_version");
this.buildEpoch = metadata.get("build_epoch").asLong(); this.buildEpoch = getLong(metadata, "build_epoch");
this.databaseType = metadata.get("database_type").asText(); this.databaseType = getString(metadata, "database_type");
this.languages = metadata.get("languages"); this.languages = (List) metadata.get("languages");
this.description = metadata.get("description"); this.description = (Map) metadata.get("description");
this.ipVersion = metadata.get("ip_version").asInt(); this.ipVersion = getInt(metadata, "ip_version");
this.nodeCount = metadata.get("node_count").asInt(); this.nodeCount = getInt(metadata, "node_count");
this.recordSize = metadata.get("record_size").asInt(); this.recordSize = getInt(metadata, "record_size");
this.nodeByteSize = this.recordSize / 4; this.nodeByteSize = this.recordSize / 4;
this.searchTreeSize = this.nodeCount * this.nodeByteSize; this.searchTreeSize = this.nodeCount * this.nodeByteSize;
} }
private static int getInt(Object m, String key) {
Map map = (Map) m;
Number i = (Number) map.get(key);
if (i != null)
return i.intValue();
return 0;
}
private static long getLong(Object m, String key) {
Map map = (Map) m;
Number i = (Number) map.get(key);
if (i != null)
return i.longValue();
return 0;
}
private static String getString(Object m, String key) {
Map map = (Map) m;
return (String) map.get(key);
}
/** /**
* @return the major version number for the database's binary format. * @return the major version number for the database's binary format.
*/ */
@ -82,9 +99,7 @@ public final class Metadata {
* @return map from language code to description in that language. * @return map from language code to description in that language.
*/ */
public Map<String, String> getDescription() { public Map<String, String> getDescription() {
return new ObjectMapper().convertValue(this.description, return this.description;
new TypeReference<HashMap<String, String>>() {
});
} }
/** /**
@ -99,9 +114,7 @@ public final class Metadata {
* @return list of languages supported by the database. * @return list of languages supported by the database.
*/ */
public List<String> getLanguages() { public List<String> getLanguages() {
return new ObjectMapper().convertValue(this.languages, return this.languages;
new TypeReference<ArrayList<String>>() {
});
} }
/** /**

View File

@ -2,8 +2,6 @@ package com.maxmind.db;
import java.io.IOException; import java.io.IOException;
import com.fasterxml.jackson.databind.JsonNode;
/** /**
* A no-op cache singleton. * A no-op cache singleton.
*/ */
@ -15,7 +13,7 @@ public class NoCache implements NodeCache {
} }
@Override @Override
public JsonNode get(int key, Loader loader) throws IOException { public Object get(int key, Loader loader) throws IOException {
return loader.load(key); return loader.load(key);
} }

View File

@ -2,14 +2,12 @@ package com.maxmind.db;
import java.io.IOException; import java.io.IOException;
import com.fasterxml.jackson.databind.JsonNode;
public interface NodeCache { public interface NodeCache {
public interface Loader { public interface Loader {
JsonNode load(int key) throws IOException; Object load(int key) throws IOException;
} }
public JsonNode get(int key, Loader loader) throws IOException; public Object get(int key, Loader loader) throws IOException;
} }

View File

@ -6,10 +6,9 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.databind.JsonNode;
/** /**
* Instances of this class provide a reader for the MaxMind DB format. IP * Instances of this class provide a reader for the MaxMind DB format. IP
* addresses can be looked up using the <code>get</code> method. * addresses can be looked up using the <code>get</code> method.
@ -129,7 +128,7 @@ public final class Reader implements Closeable {
int start = this.findMetadataStart(buffer, name); int start = this.findMetadataStart(buffer, name);
Decoder metadataDecoder = new Decoder(this.cache, buffer, start); Decoder metadataDecoder = new Decoder(this.cache, buffer, start);
this.metadata = new Metadata(metadataDecoder.decode(start)); this.metadata = new Metadata((Map) metadataDecoder.decode(start));
this.ipV4Start = this.findIpV4StartNode(buffer); this.ipV4Start = this.findIpV4StartNode(buffer);
} }
@ -141,7 +140,7 @@ public final class Reader implements Closeable {
* @return the record for the IP address. * @return the record for the IP address.
* @throws IOException if a file I/O error occurs. * @throws IOException if a file I/O error occurs.
*/ */
public JsonNode get(InetAddress ipAddress) throws IOException { public Object get(InetAddress ipAddress) throws IOException {
ByteBuffer buffer = this.getBufferHolder().get(); ByteBuffer buffer = this.getBufferHolder().get();
int pointer = this.findAddressInTree(buffer, ipAddress); int pointer = this.findAddressInTree(buffer, ipAddress);
if (pointer == 0) { if (pointer == 0) {
@ -234,7 +233,7 @@ public final class Reader implements Closeable {
} }
} }
private JsonNode resolveDataPointer(ByteBuffer buffer, int pointer) private Object resolveDataPointer(ByteBuffer buffer, int pointer)
throws IOException { throws IOException {
int resolved = (pointer - this.metadata.getNodeCount()) int resolved = (pointer - this.metadata.getNodeCount())
+ this.metadata.getSearchTreeSize(); + this.metadata.getSearchTreeSize();