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
107 changes: 32 additions & 75 deletions src/main/java/com/maxmind/db/Decoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,34 +58,6 @@ public static Type fromControlByte(int b) {
}
}

static class Result {
private final JsonNode node;
private int offset;

Result(JsonNode node, int offset) {
this.node = node;
this.offset = offset;
}

JsonNode getNode() {
return this.node;
}

int getOffset() {
return this.offset;
}

void setOffset(int offset) {
this.offset = offset;
}

@Override
public String toString() {
return "Result[" + offset + " " + node.getNodeType() + " " + node.asText() + "]";
}

}

Decoder(NodeCache cache, ByteBuffer buffer, long pointerBase) {
this.cache = cache;
this.pointerBase = pointerBase;
Expand All @@ -95,20 +67,23 @@ public String toString() {
private final NodeCache.Loader cacheLoader = new NodeCache.Loader() {
@Override
public JsonNode load(int key) throws IOException {
return decode(key).getNode();
return decode(key);
}
};

Result decode(int offset) throws IOException {
JsonNode decode(int offset) throws IOException {
if (offset >= this.buffer.capacity()) {
throw new InvalidDatabaseException(
"The MaxMind DB file's data section contains bad data: "
+ "pointer larger than the database.");
}

this.buffer.position(offset);
return decode();
}

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

Type type = Type.fromControlByte(ctrlByte);

Expand All @@ -120,16 +95,17 @@ Result decode(int offset) throws IOException {
int base = pointerSize == 4 ? (byte) 0 : (byte) (ctrlByte & 0x7);
int packed = this.decodeInteger(base, pointerSize);
long pointer = packed + this.pointerBase + POINTER_VALUE_OFFSETS[pointerSize];
int newOffset = offset + pointerSize;

// for unit testing
if (this.POINTER_TEST_HACK) {
return new Result(new LongNode(pointer), newOffset);
return new LongNode(pointer);
}

int targetOffset = (int) pointer;
int position = buffer.position();
JsonNode node = cache.get(targetOffset, cacheLoader);
return new Result(node, newOffset);
buffer.position(position);
return node;
}

if (type.equals(Type.EXTENDED)) {
Expand All @@ -145,7 +121,6 @@ Result decode(int offset) throws IOException {
}

type = Type.get(typeNum);
offset++;
}

int size = ctrlByte & 0x1f;
Expand All @@ -162,49 +137,38 @@ Result decode(int offset) throws IOException {
default:
size = 65821 + (i & (0x0FFFFFFF >>> 32 - 8 * bytesToRead));
}
offset += bytesToRead;
}

return this.decodeByType(type, offset, size);
return this.decodeByType(type, size);
}

private Result decodeByType(Type type, int offset, int size)
private JsonNode decodeByType(Type type, int size)
throws IOException {
// MAP, ARRAY, and BOOLEAN do not use newOffset as we don't read the
// next <code>size</code> bytes. For all other types, we do.
int newOffset = offset + size;
switch (type) {
case MAP:
return this.decodeMap(size, offset);
return this.decodeMap(size);
case ARRAY:
return this.decodeArray(size, offset);
return this.decodeArray(size);
case BOOLEAN:
return new Result(Decoder.decodeBoolean(size), offset);
return Decoder.decodeBoolean(size);
case UTF8_STRING:
TextNode s = new TextNode(this.decodeString(size));
return new Result(s, newOffset);
return new TextNode(this.decodeString(size));
case DOUBLE:
return new Result(this.decodeDouble(size), newOffset);
return this.decodeDouble(size);
case FLOAT:
return new Result(this.decodeFloat(size), newOffset);
return this.decodeFloat(size);
case BYTES:
BinaryNode b = new BinaryNode(this.getByteArray(size));
return new Result(b, newOffset);
return new BinaryNode(this.getByteArray(size));
case UINT16:
IntNode i = this.decodeUint16(size);
return new Result(i, newOffset);
return this.decodeUint16(size);
case UINT32:
LongNode l = this.decodeUint32(size);
return new Result(l, newOffset);
return this.decodeUint32(size);
case INT32:
IntNode int32 = this.decodeInt32(size);
return new Result(int32, newOffset);
return this.decodeInt32(size);
case UINT64:
BigIntegerNode bi = this.decodeBigInteger(size);
return new Result(bi, newOffset);
return this.decodeBigInteger(size);
case UINT128:
BigIntegerNode uint128 = this.decodeBigInteger(size);
return new Result(uint128, newOffset);
return this.decodeBigInteger(size);
default:
throw new InvalidDatabaseException(
"Unknown or unexpected type: " + type.name());
Expand Down Expand Up @@ -292,34 +256,27 @@ private static BooleanNode decodeBoolean(int size)
}
}

private Result decodeArray(int size, int offset) throws IOException {
private JsonNode decodeArray(int size) throws IOException {
ArrayNode array = OBJECT_MAPPER.createArrayNode();

for (int i = 0; i < size; i++) {
Result r = this.decode(offset);
offset = r.getOffset();
array.add(r.getNode());
JsonNode r = this.decode();
array.add(r);
}

return new Result(array, offset);
return array;
}

private Result decodeMap(int size, int offset) throws IOException {
private JsonNode decodeMap(int size) throws IOException {
ObjectNode map = OBJECT_MAPPER.createObjectNode();

for (int i = 0; i < size; i++) {
Result keyResult = this.decode(offset);
String key = keyResult.getNode().asText();
offset = keyResult.getOffset();

Result valueResult = this.decode(offset);
JsonNode value = valueResult.getNode();
offset = valueResult.getOffset();

String key = this.decode().asText();
JsonNode value = this.decode();
map.set(key, value);
}

return new Result(map, offset);
return map;
}

private byte[] getByteArray(int length) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/maxmind/db/Reader.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ private Reader(BufferHolder bufferHolder, String name, NodeCache cache) throws I
int start = this.findMetadataStart(buffer, name);

Decoder metadataDecoder = new Decoder(this.cache, buffer, start);
this.metadata = new Metadata(metadataDecoder.decode(start).getNode());
this.metadata = new Metadata(metadataDecoder.decode(start));

this.ipV4Start = this.findIpV4StartNode(buffer);
}
Expand Down Expand Up @@ -251,7 +251,7 @@ private JsonNode resolveDataPointer(ByteBuffer buffer, int pointer)
// found.
Decoder decoder = new Decoder(this.cache, buffer,
this.metadata.getSearchTreeSize() + DATA_SECTION_SEPARATOR_SIZE);
return decoder.decode(resolved).getNode();
return decoder.decode(resolved);
}

/*
Expand Down
28 changes: 10 additions & 18 deletions src/test/java/com/maxmind/db/DecoderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -430,36 +430,28 @@ private static <T> void testTypeDecoding(Decoder.Type type, Map<T, byte[]> tests

// XXX - this could be streamlined
if (type.equals(Decoder.Type.BYTES)) {
assertArrayEquals(desc, (byte[]) expect, decoder.decode(0)
.getNode().binaryValue());
assertArrayEquals(desc, (byte[]) expect, decoder.decode(0).binaryValue());
} else if (type.equals(Decoder.Type.ARRAY)) {
assertEquals(desc, expect, decoder.decode(0).getNode());
assertEquals(desc, expect, decoder.decode(0));
} else if (type.equals(Decoder.Type.UINT16)
|| type.equals(Decoder.Type.INT32)) {
assertEquals(desc, expect, decoder.decode(0).getNode()
.asInt());
assertEquals(desc, expect, decoder.decode(0).asInt());
} else if (type.equals(Decoder.Type.UINT32)
|| type.equals(Decoder.Type.POINTER)) {
assertEquals(desc, expect, decoder.decode(0).getNode()
.asLong());
assertEquals(desc, expect, decoder.decode(0).asLong());
} else if (type.equals(Decoder.Type.UINT64)
|| type.equals(Decoder.Type.UINT128)) {
assertEquals(desc, expect, decoder.decode(0).getNode()
.bigIntegerValue());
assertEquals(desc, expect, decoder.decode(0).bigIntegerValue());
} else if (type.equals(Decoder.Type.DOUBLE)) {
assertEquals(desc, expect, decoder.decode(0).getNode()
.asDouble());
assertEquals(desc, expect, decoder.decode(0).asDouble());
} else if (type.equals(Decoder.Type.FLOAT)) {
assertEquals(desc, new FloatNode((Float) expect), decoder
.decode(0).getNode());
assertEquals(desc, new FloatNode((Float) expect), decoder.decode(0));
} else if (type.equals(Decoder.Type.UTF8_STRING)) {
assertEquals(desc, expect, decoder.decode(0).getNode()
.asText());
assertEquals(desc, expect, decoder.decode(0).asText());
} else if (type.equals(Decoder.Type.BOOLEAN)) {
assertEquals(desc, expect, decoder.decode(0).getNode()
.asBoolean());
assertEquals(desc, expect, decoder.decode(0).asBoolean());
} else {
assertEquals(desc, expect, decoder.decode(0).getNode());
assertEquals(desc, expect, decoder.decode(0));
}
} finally {
fc.close();
Expand Down
12 changes: 6 additions & 6 deletions src/test/java/com/maxmind/db/PointerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@ public void testWithPointers() throws IOException {

ObjectNode map = om.createObjectNode();
map.put("long_key", "long_value1");
assertEquals(map, decoder.decode(0).getNode());
assertEquals(map, decoder.decode(0));

map = om.createObjectNode();
map.put("long_key", "long_value2");
assertEquals(map, decoder.decode(22).getNode());
assertEquals(map, decoder.decode(22));

map = om.createObjectNode();
map.put("long_key2", "long_value1");
assertEquals(map, decoder.decode(37).getNode());
assertEquals(map, decoder.decode(37));

map = om.createObjectNode();
map.put("long_key2", "long_value2");
assertEquals(map, decoder.decode(50).getNode());
assertEquals(map, decoder.decode(50));

map = om.createObjectNode();
map.put("long_key", "long_value1");
assertEquals(map, decoder.decode(55).getNode());
assertEquals(map, decoder.decode(55));

map = om.createObjectNode();
map.put("long_key2", "long_value2");
assertEquals(map, decoder.decode(57).getNode());
assertEquals(map, decoder.decode(57));
}
}