22
33import java .io .File ;
44import java .io .IOException ;
5- import java .io .RandomAccessFile ;
65import java .net .InetAddress ;
76import java .nio .ByteBuffer ;
8- import java .nio .MappedByteBuffer ;
9- import java .nio .channels .FileChannel ;
10- import java .nio .channels .FileChannel .MapMode ;
117import java .util .Arrays ;
128
139import com .fasterxml .jackson .databind .JsonNode ;
1410
15- public class Reader {
11+ public final class Reader {
1612 private static final int DATA_SECTION_SEPARATOR_SIZE = 16 ;
1713 private static final byte [] METADATA_START_MARKER = { (byte ) 0xAB ,
1814 (byte ) 0xCD , (byte ) 0xEF , 'M' , 'a' , 'x' , 'M' , 'i' , 'n' , 'd' , '.' ,
@@ -21,15 +17,23 @@ public class Reader {
2117 private final boolean DEBUG ;
2218 private final Decoder decoder ;
2319 private final Metadata metadata ;
24- private final FileChannel fc ;
25- private final RandomAccessFile raf ;
26- private final MappedByteBuffer mmap ;
20+ private final ThreadBuffer threadBuffer ;
21+
22+ public enum FileMode {
23+ MEMORY_MAPPED , IN_MEMORY
24+ }
2725
2826 public Reader (File database ) throws MaxMindDbException , IOException {
27+ this (database , FileMode .MEMORY_MAPPED );
28+ }
29+
30+ // XXX - loading the file into memory doesn't really provide any performance
31+ // gains on my machine. Consider whether it is even worth providing the
32+ // option.
33+ public Reader (File database , FileMode mode ) throws MaxMindDbException ,
34+ IOException {
2935 this .DEBUG = System .getenv ().get ("MAXMIND_DB_READER_DEBUG" ) != null ;
30- this .raf = new RandomAccessFile (database , "r" );
31- this .fc = this .raf .getChannel ();
32- this .mmap = this .fc .map (MapMode .READ_ONLY , 0 , this .fc .size ());
36+ this .threadBuffer = new ThreadBuffer (database , mode );
3337
3438 /*
3539 * We need to make sure that whatever chunk we read will have the
@@ -54,12 +58,12 @@ public Reader(File database) throws MaxMindDbException, IOException {
5458 + "). Is this a valid MaxMind DB file?" );
5559 }
5660
57- Decoder metadataDecoder = new Decoder (this .mmap , 0 );
61+ Decoder metadataDecoder = new Decoder (this .threadBuffer , 0 );
5862
5963 this .metadata = new Metadata (metadataDecoder .decode (start ).getNode ());
6064
61- this .decoder = new Decoder (this .mmap , this . metadata . searchTreeSize
62- + DATA_SECTION_SEPARATOR_SIZE );
65+ this .decoder = new Decoder (this .threadBuffer ,
66+ this . metadata . searchTreeSize + DATA_SECTION_SEPARATOR_SIZE );
6367
6468 if (this .DEBUG ) {
6569 Log .debug (this .metadata .toString ());
@@ -75,12 +79,11 @@ public JsonNode get(InetAddress address) throws MaxMindDbException,
7579 return null ;
7680 }
7781
78- this .mmap .position ((int ) pointer );
82+ this .threadBuffer . get () .position ((int ) pointer );
7983 return this .resolveDataPointer (pointer );
8084 }
8185
82- long findAddressInTree (InetAddress address ) throws MaxMindDbException ,
83- IOException {
86+ long findAddressInTree (InetAddress address ) throws MaxMindDbException {
8487 byte [] rawAddress = address .getAddress ();
8588
8689 // XXX sort of wasteful
@@ -141,35 +144,34 @@ long record = nodes[bit];
141144 throw new MaxMindDbException ("Something bad happened" );
142145 }
143146
144- private long [] readNode (long nodeNumber ) throws IOException ,
145- MaxMindDbException {
146- this . mmap .position ((int ) nodeNumber * this .metadata .nodeByteSize );
147+ private long [] readNode (long nodeNumber ) throws MaxMindDbException {
148+ ByteBuffer buffer = this . threadBuffer . get ();
149+ buffer .position ((int ) nodeNumber * this .metadata .nodeByteSize );
147150
148- byte [] buffer = Util
149- .getByteArray (this .mmap , this .metadata .nodeByteSize );
151+ byte [] bytes = Decoder .getByteArray (buffer , this .metadata .nodeByteSize );
150152
151153 if (this .DEBUG ) {
152- Log .debug ("Node bytes" , buffer );
154+ Log .debug ("Node bytes" , bytes );
153155 }
154- return this .splitNodeIntoRecords (buffer );
156+ return this .splitNodeIntoRecords (bytes );
155157 }
156158
157159 private long [] splitNodeIntoRecords (byte [] bytes ) throws MaxMindDbException {
158160 long [] nodes = new long [2 ];
159161 switch (this .metadata .recordSize ) {
160162 case 24 :
161- nodes [0 ] = Util .decodeLong (Arrays .copyOfRange (bytes , 0 , 3 ));
162- nodes [1 ] = Util .decodeLong (Arrays .copyOfRange (bytes , 3 , 6 ));
163+ nodes [0 ] = Decoder .decodeLong (Arrays .copyOfRange (bytes , 0 , 3 ));
164+ nodes [1 ] = Decoder .decodeLong (Arrays .copyOfRange (bytes , 3 , 6 ));
163165 return nodes ;
164166 case 28 :
165- nodes [0 ] = Util .decodeLong (Arrays .copyOfRange (bytes , 0 , 3 ));
166- nodes [1 ] = Util .decodeLong (Arrays .copyOfRange (bytes , 4 , 7 ));
167+ nodes [0 ] = Decoder .decodeLong (Arrays .copyOfRange (bytes , 0 , 3 ));
168+ nodes [1 ] = Decoder .decodeLong (Arrays .copyOfRange (bytes , 4 , 7 ));
167169 nodes [0 ] = ((0xF0 & bytes [3 ]) << 20 ) | nodes [0 ];
168170 nodes [1 ] = ((0x0F & bytes [3 ]) << 24 ) | nodes [1 ];
169171 return nodes ;
170172 case 32 :
171- nodes [0 ] = Util .decodeLong (Arrays .copyOfRange (bytes , 0 , 4 ));
172- nodes [1 ] = Util .decodeLong (Arrays .copyOfRange (bytes , 4 , 8 ));
173+ nodes [0 ] = Decoder .decodeLong (Arrays .copyOfRange (bytes , 0 , 4 ));
174+ nodes [1 ] = Decoder .decodeLong (Arrays .copyOfRange (bytes , 4 , 8 ));
173175 return nodes ;
174176 default :
175177 throw new MaxMindDbException ("Unknown record size: "
@@ -203,15 +205,15 @@ private JsonNode resolveDataPointer(long pointer)
203205 * are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
204206 * an issue, but I suspect it won't be.
205207 */
206- private long findMetadataStart () throws IOException {
207- long fileSize = this .fc .size ();
208+ private long findMetadataStart () {
209+ ByteBuffer buffer = this .threadBuffer .get ();
210+ int fileSize = buffer .capacity ();
208211
209- FILE : for (long i = 0 ; i < fileSize - METADATA_START_MARKER .length + 1 ; i ++) {
212+ FILE : for (int i = 0 ; i < fileSize - METADATA_START_MARKER .length + 1 ; i ++) {
210213 for (int j = 0 ; j < METADATA_START_MARKER .length ; j ++) {
211- ByteBuffer b = ByteBuffer .wrap (new byte [1 ]);
212- this .fc .read (b , fileSize - i - j - 1 );
213- if (b .get (0 ) != METADATA_START_MARKER [METADATA_START_MARKER .length
214- - j - 1 ]) {
214+ byte b = buffer .get (fileSize - i - j - 1 );
215+ if (b != METADATA_START_MARKER [METADATA_START_MARKER .length - j
216+ - 1 ]) {
215217 continue FILE ;
216218 }
217219 }
@@ -220,16 +222,11 @@ private long findMetadataStart() throws IOException {
220222 return -1 ;
221223 }
222224
223- public Metadata getMetadata () {
225+ Metadata getMetadata () {
224226 return this .metadata ;
225227 }
226228
227229 public void close () throws IOException {
228- if (this .fc != null ) {
229- this .fc .close ();
230- }
231- if (this .raf != null ) {
232- this .raf .close ();
233- }
230+ this .threadBuffer .close ();
234231 }
235232}
0 commit comments