Skip to content

Commit 7b56195

Browse files
committed
Merge pull request #10 from maxmind/greg/public-metadata
Greg/public metadata
2 parents 7b09c12 + 4f7059e commit 7b56195

File tree

4 files changed

+180
-67
lines changed

4 files changed

+180
-67
lines changed

src/main/java/com/maxmind/db/Decoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ private Result decodeMap(int size, int offset) throws IOException {
285285
JsonNode value = valueResult.getNode();
286286
offset = valueResult.getOffset();
287287

288-
map.put(key, value);
288+
map.set(key, value);
289289
}
290290

291291
return new Result(map, offset);

src/main/java/com/maxmind/db/Metadata.java

Lines changed: 116 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
11
package com.maxmind.db;
22

3-
import java.math.BigInteger;
3+
import java.util.ArrayList;
4+
import java.util.Date;
5+
import java.util.HashMap;
6+
import java.util.List;
7+
import java.util.Map;
48

9+
import com.fasterxml.jackson.core.type.TypeReference;
510
import com.fasterxml.jackson.databind.JsonNode;
11+
import com.fasterxml.jackson.databind.ObjectMapper;
612

7-
// XXX - if we make this public, add getters
8-
final class Metadata {
9-
final int binaryFormatMajorVersion;
10-
final int binaryFormatMinorVersion;
11-
private final BigInteger buildEpoch;
12-
final String databaseType;
13-
final JsonNode description;
14-
final int ipVersion;
15-
final int nodeCount;
16-
final int recordSize;
17-
final int nodeByteSize;
18-
final int searchTreeSize;
19-
final JsonNode languages;
20-
21-
public Metadata(JsonNode metadata) {
13+
public final class Metadata {
14+
private final int binaryFormatMajorVersion;
15+
private final int binaryFormatMinorVersion;
16+
17+
private final long buildEpoch;
18+
19+
private final String databaseType;
20+
21+
private final JsonNode description;
22+
23+
private final int ipVersion;
24+
25+
private final JsonNode languages;
26+
27+
private final int nodeByteSize;
28+
29+
private final int nodeCount;
30+
31+
private final int recordSize;
32+
33+
private final int searchTreeSize;
34+
35+
Metadata(JsonNode metadata) {
2236
this.binaryFormatMajorVersion = metadata.get(
2337
"binary_format_major_version").asInt();
2438
this.binaryFormatMinorVersion = metadata.get(
2539
"binary_format_minor_version").asInt();
26-
this.buildEpoch = metadata.get("build_epoch").bigIntegerValue();
40+
this.buildEpoch = metadata.get("build_epoch").asLong();
2741
this.databaseType = metadata.get("database_type").asText();
2842
this.languages = metadata.get("languages");
2943
this.description = metadata.get("description");
@@ -34,6 +48,91 @@ public Metadata(JsonNode metadata) {
3448
this.searchTreeSize = this.nodeCount * this.nodeByteSize;
3549
}
3650

51+
/**
52+
* @return the major version number for the database's binary format.
53+
*/
54+
public int getBinaryFormatMajorVersion() {
55+
return this.binaryFormatMajorVersion;
56+
}
57+
58+
/**
59+
* @return the minor version number for the database's binary format.
60+
*/
61+
public int getBinaryFormatMinorVersion() {
62+
return this.binaryFormatMinorVersion;
63+
}
64+
65+
/**
66+
* @return the date of the database build.
67+
*/
68+
public Date getBuildDate() {
69+
return new Date(this.buildEpoch * 1000);
70+
}
71+
72+
/**
73+
* @return a string that indicates the structure of each data record
74+
* associated with an IP address. The actual definition of these
75+
* structures is left up to the database creator.
76+
*/
77+
public String getDatabaseType() {
78+
return this.databaseType;
79+
}
80+
81+
/**
82+
* @return map from language code to description in that language.
83+
*/
84+
public Map<String, String> getDescription() {
85+
return new ObjectMapper().convertValue(this.description,
86+
new TypeReference<HashMap<String, String>>() {
87+
});
88+
}
89+
90+
/**
91+
* @return whether the database contains IPv4 or IPv6 address data. The only
92+
* possible values are 4 and 6.
93+
*/
94+
public int getIpVersion() {
95+
return this.ipVersion;
96+
}
97+
98+
/**
99+
* @return list of languages supported by the database.
100+
*/
101+
public List<String> getLanguages() {
102+
return new ObjectMapper().convertValue(this.languages,
103+
new TypeReference<ArrayList<String>>() {
104+
});
105+
}
106+
107+
/**
108+
* @return the nodeByteSize
109+
*/
110+
int getNodeByteSize() {
111+
return this.nodeByteSize;
112+
}
113+
114+
/**
115+
* @return the number of nodes in the search tree.
116+
*/
117+
int getNodeCount() {
118+
return this.nodeCount;
119+
}
120+
121+
/**
122+
* @return the number of bits in a record in the search tree. Note that each
123+
* node consists of two records.
124+
*/
125+
int getRecordSize() {
126+
return this.recordSize;
127+
}
128+
129+
/**
130+
* @return the searchTreeSize
131+
*/
132+
int getSearchTreeSize() {
133+
return this.searchTreeSize;
134+
}
135+
37136
/*
38137
* (non-Javadoc)
39138
*

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

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,17 @@ private int findAddressInTree(ByteBuffer buffer, InetAddress address)
128128
int record = this.startNode(buffer, bitLength);
129129

130130
for (int i = 0; i < bitLength; i++) {
131-
if (record >= this.metadata.nodeCount) {
131+
if (record >= this.metadata.getNodeCount()) {
132132
break;
133133
}
134134
int b = 0xFF & rawAddress[i / 8];
135135
int bit = 1 & (b >> 7 - (i % 8));
136136
record = this.readNode(buffer, record, bit);
137137
}
138-
if (record == this.metadata.nodeCount) {
138+
if (record == this.metadata.getNodeCount()) {
139139
// record is empty
140140
return 0;
141-
} else if (record > this.metadata.nodeCount) {
141+
} else if (record > this.metadata.getNodeCount()) {
142142
// record is a data pointer
143143
return record;
144144
}
@@ -149,7 +149,7 @@ private int startNode(ByteBuffer buffer, int bitLength)
149149
throws InvalidDatabaseException {
150150
// Check if we are looking up an IPv4 address in an IPv6 tree. If this
151151
// is the case, we can skip over the first 96 nodes.
152-
if (this.metadata.ipVersion == 6 && bitLength == 32) {
152+
if (this.metadata.getIpVersion() == 6 && bitLength == 32) {
153153
return this.ipV4Start;
154154
}
155155
// The first node of the tree is always node 0, at the beginning of the
@@ -159,22 +159,22 @@ private int startNode(ByteBuffer buffer, int bitLength)
159159

160160
private int findIpV4StartNode(ByteBuffer buffer)
161161
throws InvalidDatabaseException {
162-
if (this.metadata.ipVersion == 4) {
162+
if (this.metadata.getIpVersion() == 4) {
163163
return 0;
164164
}
165165

166166
int node = 0;
167-
for (int i = 0; i < 96 && node < this.metadata.nodeCount; i++) {
167+
for (int i = 0; i < 96 && node < this.metadata.getNodeCount(); i++) {
168168
node = this.readNode(buffer, node, 0);
169169
}
170170
return node;
171171
}
172172

173173
private int readNode(ByteBuffer buffer, int nodeNumber, int index)
174174
throws InvalidDatabaseException {
175-
int baseOffset = nodeNumber * this.metadata.nodeByteSize;
175+
int baseOffset = nodeNumber * this.metadata.getNodeByteSize();
176176

177-
switch (this.metadata.recordSize) {
177+
switch (this.metadata.getRecordSize()) {
178178
case 24:
179179
buffer.position(baseOffset + index * 3);
180180
return Decoder.decodeInteger(buffer, 0, 3);
@@ -193,14 +193,14 @@ private int readNode(ByteBuffer buffer, int nodeNumber, int index)
193193
return Decoder.decodeInteger(buffer, 0, 4);
194194
default:
195195
throw new InvalidDatabaseException("Unknown record size: "
196-
+ this.metadata.recordSize);
196+
+ this.metadata.getRecordSize());
197197
}
198198
}
199199

200200
private JsonNode resolveDataPointer(ByteBuffer buffer, int pointer)
201201
throws IOException {
202-
int resolved = (pointer - this.metadata.nodeCount)
203-
+ this.metadata.searchTreeSize;
202+
int resolved = (pointer - this.metadata.getNodeCount())
203+
+ this.metadata.getSearchTreeSize();
204204

205205
if (resolved >= buffer.capacity()) {
206206
throw new InvalidDatabaseException(
@@ -210,7 +210,7 @@ private JsonNode resolveDataPointer(ByteBuffer buffer, int pointer)
210210

211211
// We only want the data from the decoder, not the offset where it was
212212
// found.
213-
Decoder decoder = new Decoder(buffer, this.metadata.searchTreeSize
213+
Decoder decoder = new Decoder(buffer, this.metadata.getSearchTreeSize()
214214
+ DATA_SECTION_SEPARATOR_SIZE);
215215
return decoder.decode(resolved).getNode();
216216
}
@@ -242,7 +242,10 @@ private int findMetadataStart(ByteBuffer buffer, String databaseName)
242242
+ databaseName + "). Is this a valid MaxMind DB file?");
243243
}
244244

245-
Metadata getMetadata() {
245+
/**
246+
* @return the metadata for the MaxMind DB file.
247+
*/
248+
public Metadata getMetadata() {
246249
return this.metadata;
247250
}
248251

0 commit comments

Comments
 (0)