44import java .io .IOException ;
55import java .io .RandomAccessFile ;
66import java .net .InetAddress ;
7+ import java .nio .ByteBuffer ;
78import java .nio .channels .FileChannel ;
9+ import java .util .Map ;
810
911public 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}
0 commit comments