Skip to content

Commit 0465d9a

Browse files
committed
Added basic handling of the metadata
1 parent efffeaf commit 0465d9a

3 files changed

Lines changed: 121 additions & 7 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.maxmind.maxminddb;
2+
3+
import java.math.BigInteger;
4+
import java.util.Map;
5+
6+
// XXX - if we make this public, add getters
7+
class Metadata {
8+
Integer binaryFormatMajorVersion;
9+
Integer binaryFormatMinorVersion;
10+
BigInteger buildEpoch;
11+
String databaseType;
12+
Map<String, Object> description;
13+
Integer ipVersion;
14+
Long nodeCount;
15+
Long recordSize;
16+
17+
// XXX - think about how I want to construct this. Maybe look at how JSON
18+
// parsers deal with types
19+
public Metadata(Map<String, Object> metadata) {
20+
this.binaryFormatMajorVersion = (Integer) metadata
21+
.get("binary_format_major_version");
22+
this.binaryFormatMinorVersion = (Integer) metadata
23+
.get("binary_format_minor_version");
24+
this.buildEpoch = (BigInteger) metadata.get("build_epoch");
25+
this.databaseType = (String) metadata.get("database_type");
26+
this.description = (Map<String, Object>) metadata.get("description");
27+
this.ipVersion = (Integer) metadata.get("ip_version");
28+
this.nodeCount = (Long) metadata.get("node_count");
29+
this.recordSize = (Long) metadata.get("record_size");
30+
}
31+
32+
/*
33+
* (non-Javadoc)
34+
*
35+
* @see java.lang.Object#toString()
36+
*/
37+
@Override
38+
public String toString() {
39+
return "Metadata [binaryFormatMajorVersion="
40+
+ this.binaryFormatMajorVersion + ", binaryFormatMinorVersion="
41+
+ this.binaryFormatMinorVersion + ", buildEpoch="
42+
+ this.buildEpoch + ", databaseType=" + this.databaseType
43+
+ ", description=" + this.description + ", ipVersion="
44+
+ this.ipVersion + ", nodeCount=" + this.nodeCount
45+
+ ", recordSize=" + this.recordSize + "]";
46+
}
47+
48+
}

src/main/java/com/maxmind/maxminddb/Reader.java

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,64 @@
44
import java.io.IOException;
55
import java.io.RandomAccessFile;
66
import java.net.InetAddress;
7+
import java.nio.ByteBuffer;
78
import java.nio.channels.FileChannel;
9+
import java.util.Map;
810

911
public class Reader {
1012
private static final boolean DEBUG = true;
1113
private Decoder decoder;
1214
private long nodeCount;
13-
private final long dataSourceSize;
14-
private static byte METADATE_START_MARKER[] = { (byte) 0xab, (byte) 0xcd,
15-
(byte) 0xef, 'M', 'a', 'x', 'M', 'i', 'n', 'd', '.', 'c', 'o', 'm' };
15+
private final long dataSectionEnd;
16+
private static byte METADATE_START_MARKER[] = { (byte) 0xAB, (byte) 0xCD,
17+
(byte) 0xEF, 'M', 'a', 'x', 'M', 'i', 'n', 'd', '.', 'c', 'o', 'm' };
18+
private final FileChannel fc;
1619

17-
public Reader(File dataSource) {
20+
public Reader(File dataSource) throws MaxMindDbException, IOException {
1821

1922
RandomAccessFile raf = new RandomAccessFile(dataSource, "r");
20-
FileChannel fc = raf.getChannel();
23+
this.fc = raf.getChannel();
2124
// XXX - we will want
2225
// MappedByteBuffer in = fc.map(FileChannel.MapMode.READ_ONLY, 0,
2326
// fc.size());
2427

28+
/*
29+
* We need to make sure that whatever chunk we read will have the
30+
* metadata in it. The description metadata key is a hash of
31+
* descriptions, one per language. The description could be something
32+
* verbose like "GeoIP 2.0 City Database, Multilingual - English,
33+
* Chinese (Taiwan), Chinese (China), French, German, Portuguese" (but
34+
* with c. 20 languages). That comes out to about 250 bytes _per key_.
35+
* Multiply that by 20 languages, and the description alon ecould use up
36+
* about 5k. The other keys in the metadata are very, very tiny.
37+
*
38+
* Given all this, reading 20k seems fairly future-proof. We'd have to
39+
* have extremely long descriptions or descriptions in 80 languages
40+
* before this became too long.
41+
*/
42+
long start = this.findMetadataStart();
43+
44+
if (start < 0) {
45+
throw new MaxMindDbException(
46+
"Could not find a MaxMind DB metadata marker in this file ("
47+
+ dataSource.getName()
48+
+ "). Is this a valid MaxMind DB file?");
49+
}
50+
51+
// XXX - right?
52+
this.dataSectionEnd = start - METADATE_START_MARKER.length;
53+
54+
Decoder decoder = new Decoder(this.fc, 0);
55+
56+
// FIXME - pretty ugly that I am setting the position outside of the
57+
// decoder. Move this all into
58+
// the decoder and make sure it is thread safe
59+
this.fc.position(start);
60+
Metadata metadata = new Metadata((Map<String, Object>) decoder
61+
.decode(0).getObject());
62+
if (DEBUG) {
63+
Log.debug(metadata.toString());
64+
}
2565
}
2666

2767
// FIXME - figure out what we are returning
@@ -100,11 +140,10 @@ private Object resolveDataPointer(long pointer) throws MaxMindDbException,
100140
long resolved = (pointer - this.nodeCount) + this.searchTreeSize();
101141

102142
if (DEBUG) {
103-
long nodeCount = this.nodeCount;
104143
long treeSize = this.searchTreeSize();
105144

106145
Log.debug("Resolved data pointer", "( " + pointer + " - "
107-
+ nodeCount + " ) + " + treeSize + " = " + resolved);
146+
+ this.nodeCount + " ) + " + treeSize + " = " + resolved);
108147

109148
}
110149

@@ -116,4 +155,30 @@ private Object resolveDataPointer(long pointer) throws MaxMindDbException,
116155
private long searchTreeSize() {
117156
throw new AssertionError("not implemented");
118157
}
158+
159+
/*
160+
* And here I though searching a file was a solved problem.
161+
*
162+
* This is an extremely naive but reasonably readable implementation. There
163+
* are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
164+
* an issue, but I suspect it won't be.
165+
*/
166+
private long findMetadataStart() throws IOException {
167+
long fileSize = this.fc.size();
168+
System.out.println(fileSize);
169+
170+
FILE: for (long i = 0; i < fileSize - METADATE_START_MARKER.length + 1; i++) {
171+
for (int j = 0; j < METADATE_START_MARKER.length; j++) {
172+
ByteBuffer b = ByteBuffer.wrap(new byte[1]);
173+
this.fc.read(b, fileSize - i - j - 1);
174+
System.out.println(b.get(0));
175+
if (b.get(0) != METADATE_START_MARKER[METADATE_START_MARKER.length
176+
- j - 1]) {
177+
continue FILE;
178+
}
179+
}
180+
return fileSize - i;
181+
}
182+
return -1;
183+
}
119184
}

src/test/java/com/maxmind/maxminddb/DecoderTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,4 +486,5 @@ private FileChannel getFileChannel(byte[] data) throws IOException {
486486

487487
return raf.getChannel();
488488
}
489+
489490
}

0 commit comments

Comments
 (0)