forked from I2P_Developers/i2p.i2p
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:
@ -3,8 +3,6 @@ package com.maxmind.db;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* A simplistic cache using a {@link ConcurrentHashMap}. There's no eviction
|
||||
* 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 final int capacity;
|
||||
private final ConcurrentHashMap<Integer, JsonNode> cache;
|
||||
private final ConcurrentHashMap<Integer, Object> cache;
|
||||
private boolean cacheFull = false;
|
||||
|
||||
public CHMCache() {
|
||||
@ -24,13 +22,13 @@ public class CHMCache implements NodeCache {
|
||||
|
||||
public CHMCache(int capacity) {
|
||||
this.capacity = capacity;
|
||||
this.cache = new ConcurrentHashMap<Integer, JsonNode>(capacity);
|
||||
this.cache = new ConcurrentHashMap<Integer, Object>(capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode get(int key, Loader loader) throws IOException {
|
||||
public Object get(int key, Loader loader) throws IOException {
|
||||
Integer k = key;
|
||||
JsonNode value = cache.get(k);
|
||||
Object value = cache.get(k);
|
||||
if (value == null) {
|
||||
value = loader.load(key);
|
||||
if (!cacheFull) {
|
||||
|
@ -12,10 +12,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
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.
|
||||
*
|
||||
@ -25,8 +21,6 @@ final class Decoder {
|
||||
|
||||
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 };
|
||||
|
||||
// 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() {
|
||||
@Override
|
||||
public JsonNode load(int key) throws IOException {
|
||||
public Object load(int key) throws IOException {
|
||||
return decode(key);
|
||||
}
|
||||
};
|
||||
|
||||
JsonNode decode(int offset) throws IOException {
|
||||
Object decode(int offset) throws IOException {
|
||||
if (offset >= this.buffer.capacity()) {
|
||||
throw new InvalidDatabaseException(
|
||||
"The MaxMind DB file's data section contains bad data: "
|
||||
@ -87,7 +81,7 @@ final class Decoder {
|
||||
return decode();
|
||||
}
|
||||
|
||||
JsonNode decode() throws IOException {
|
||||
Object decode() throws IOException {
|
||||
int ctrlByte = 0xFF & this.buffer.get();
|
||||
|
||||
Type type = Type.fromControlByte(ctrlByte);
|
||||
@ -103,12 +97,12 @@ final class Decoder {
|
||||
|
||||
// for unit testing
|
||||
if (this.POINTER_TEST_HACK) {
|
||||
return new LongNode(pointer);
|
||||
return Long.valueOf(pointer);
|
||||
}
|
||||
|
||||
int targetOffset = (int) pointer;
|
||||
int position = buffer.position();
|
||||
JsonNode node = cache.get(targetOffset, cacheLoader);
|
||||
Object node = cache.get(targetOffset, cacheLoader);
|
||||
buffer.position(position);
|
||||
return node;
|
||||
}
|
||||
@ -147,7 +141,7 @@ final class Decoder {
|
||||
return this.decodeByType(type, size);
|
||||
}
|
||||
|
||||
private JsonNode decodeByType(Type type, int size)
|
||||
private Object decodeByType(Type type, int size)
|
||||
throws IOException {
|
||||
switch (type) {
|
||||
case MAP:
|
||||
@ -157,13 +151,13 @@ final class Decoder {
|
||||
case BOOLEAN:
|
||||
return Decoder.decodeBoolean(size);
|
||||
case UTF8_STRING:
|
||||
return new TextNode(this.decodeString(size));
|
||||
return this.decodeString(size);
|
||||
case DOUBLE:
|
||||
return this.decodeDouble(size);
|
||||
case FLOAT:
|
||||
return this.decodeFloat(size);
|
||||
case BYTES:
|
||||
return new BinaryNode(this.getByteArray(size));
|
||||
return this.getByteArray(size);
|
||||
case UINT16:
|
||||
return this.decodeUint16(size);
|
||||
case UINT32:
|
||||
@ -188,12 +182,12 @@ final class Decoder {
|
||||
return s;
|
||||
}
|
||||
|
||||
private IntNode decodeUint16(int size) {
|
||||
return new IntNode(this.decodeInteger(size));
|
||||
private Integer decodeUint16(int size) {
|
||||
return Integer.valueOf(this.decodeInteger(size));
|
||||
}
|
||||
|
||||
private IntNode decodeInt32(int size) {
|
||||
return new IntNode(this.decodeInteger(size));
|
||||
private Integer decodeInt32(int size) {
|
||||
return Integer.valueOf(this.decodeInteger(size));
|
||||
}
|
||||
|
||||
private long decodeLong(int size) {
|
||||
@ -204,8 +198,8 @@ final class Decoder {
|
||||
return integer;
|
||||
}
|
||||
|
||||
private LongNode decodeUint32(int size) {
|
||||
return new LongNode(this.decodeLong(size));
|
||||
private Long decodeUint32(int size) {
|
||||
return Long.valueOf(this.decodeLong(size));
|
||||
}
|
||||
|
||||
private int decodeInteger(int size) {
|
||||
@ -224,36 +218,36 @@ final class Decoder {
|
||||
return integer;
|
||||
}
|
||||
|
||||
private BigIntegerNode decodeBigInteger(int size) {
|
||||
private BigInteger decodeBigInteger(int 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) {
|
||||
throw new InvalidDatabaseException(
|
||||
"The MaxMind DB file's data section contains bad data: "
|
||||
+ "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) {
|
||||
throw new InvalidDatabaseException(
|
||||
"The MaxMind DB file's data section contains bad data: "
|
||||
+ "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 {
|
||||
switch (size) {
|
||||
case 0:
|
||||
return BooleanNode.FALSE;
|
||||
return Boolean.FALSE;
|
||||
case 1:
|
||||
return BooleanNode.TRUE;
|
||||
return Boolean.TRUE;
|
||||
default:
|
||||
throw new InvalidDatabaseException(
|
||||
"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++) {
|
||||
JsonNode r = this.decode();
|
||||
Object r = this.decode();
|
||||
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);
|
||||
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++) {
|
||||
String key = this.decode().asText();
|
||||
JsonNode value = this.decode();
|
||||
String key = (String) this.decode();
|
||||
Object value = this.decode();
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
return new ObjectNode(OBJECT_MAPPER.getNodeFactory(), Collections.unmodifiableMap(map));
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
private byte[] getByteArray(int length) {
|
||||
|
@ -6,10 +6,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
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 {
|
||||
private final int binaryFormatMajorVersion;
|
||||
private final int binaryFormatMinorVersion;
|
||||
@ -18,11 +14,11 @@ public final class Metadata {
|
||||
|
||||
private final String databaseType;
|
||||
|
||||
private final JsonNode description;
|
||||
private final Map description;
|
||||
|
||||
private final int ipVersion;
|
||||
|
||||
private final JsonNode languages;
|
||||
private final List languages;
|
||||
|
||||
private final int nodeByteSize;
|
||||
|
||||
@ -32,22 +28,43 @@ public final class Metadata {
|
||||
|
||||
private final int searchTreeSize;
|
||||
|
||||
Metadata(JsonNode metadata) {
|
||||
this.binaryFormatMajorVersion = metadata.get(
|
||||
"binary_format_major_version").asInt();
|
||||
this.binaryFormatMinorVersion = metadata.get(
|
||||
"binary_format_minor_version").asInt();
|
||||
this.buildEpoch = metadata.get("build_epoch").asLong();
|
||||
this.databaseType = metadata.get("database_type").asText();
|
||||
this.languages = metadata.get("languages");
|
||||
this.description = metadata.get("description");
|
||||
this.ipVersion = metadata.get("ip_version").asInt();
|
||||
this.nodeCount = metadata.get("node_count").asInt();
|
||||
this.recordSize = metadata.get("record_size").asInt();
|
||||
Metadata(Map metadata) {
|
||||
this.binaryFormatMajorVersion = getInt(metadata,
|
||||
"binary_format_major_version");
|
||||
this.binaryFormatMinorVersion = getInt(metadata,
|
||||
"binary_format_minor_version");
|
||||
this.buildEpoch = getLong(metadata, "build_epoch");
|
||||
this.databaseType = getString(metadata, "database_type");
|
||||
this.languages = (List) metadata.get("languages");
|
||||
this.description = (Map) metadata.get("description");
|
||||
this.ipVersion = getInt(metadata, "ip_version");
|
||||
this.nodeCount = getInt(metadata, "node_count");
|
||||
this.recordSize = getInt(metadata, "record_size");
|
||||
this.nodeByteSize = this.recordSize / 4;
|
||||
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.
|
||||
*/
|
||||
@ -82,9 +99,7 @@ public final class Metadata {
|
||||
* @return map from language code to description in that language.
|
||||
*/
|
||||
public Map<String, String> getDescription() {
|
||||
return new ObjectMapper().convertValue(this.description,
|
||||
new TypeReference<HashMap<String, String>>() {
|
||||
});
|
||||
return this.description;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,9 +114,7 @@ public final class Metadata {
|
||||
* @return list of languages supported by the database.
|
||||
*/
|
||||
public List<String> getLanguages() {
|
||||
return new ObjectMapper().convertValue(this.languages,
|
||||
new TypeReference<ArrayList<String>>() {
|
||||
});
|
||||
return this.languages;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,8 +2,6 @@ package com.maxmind.db;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* A no-op cache singleton.
|
||||
*/
|
||||
@ -15,7 +13,7 @@ public class NoCache implements NodeCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode get(int key, Loader loader) throws IOException {
|
||||
public Object get(int key, Loader loader) throws IOException {
|
||||
return loader.load(key);
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,12 @@ package com.maxmind.db;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public interface NodeCache {
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
@ -6,10 +6,9 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
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
|
||||
* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -141,7 +140,7 @@ public final class Reader implements Closeable {
|
||||
* @return the record for the IP address.
|
||||
* @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();
|
||||
int pointer = this.findAddressInTree(buffer, ipAddress);
|
||||
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 {
|
||||
int resolved = (pointer - this.metadata.getNodeCount())
|
||||
+ this.metadata.getSearchTreeSize();
|
||||
|
Reference in New Issue
Block a user