Skip to content

Commit 73c021d

Browse files
committed
Support IEEE 754 floats and doubles.
1 parent c65369a commit 73c021d

2 files changed

Lines changed: 66 additions & 30 deletions

File tree

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

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.fasterxml.jackson.databind.node.BinaryNode;
1313
import com.fasterxml.jackson.databind.node.BooleanNode;
1414
import com.fasterxml.jackson.databind.node.DoubleNode;
15+
import com.fasterxml.jackson.databind.node.FloatNode;
1516
import com.fasterxml.jackson.databind.node.IntNode;
1617
import com.fasterxml.jackson.databind.node.LongNode;
1718
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -30,7 +31,7 @@ final class Decoder {
3031
private final ThreadBuffer threadBuffer;
3132

3233
static enum Type {
33-
EXTENDED, POINTER, UTF8_STRING, DOUBLE, BYTES, UINT16, UINT32, MAP, INT32, UINT64, UINT128, ARRAY, CONTAINER, END_MARKER, BOOLEAN;
34+
EXTENDED, POINTER, UTF8_STRING, DOUBLE, BYTES, UINT16, UINT32, MAP, INT32, UINT64, UINT128, ARRAY, CONTAINER, END_MARKER, BOOLEAN, FLOAT;
3435

3536
public static Type get(int i) {
3637
// XXX - Type.values() might be expensive. Consider caching it.
@@ -166,32 +167,33 @@ Result decode(long offset) throws IOException {
166167
Log.debug("Size", size);
167168
}
168169

169-
long new_offset = offset + size;
170+
long newOffset = offset + size;
170171
switch (type) {
171172
case UTF8_STRING:
172173
TextNode s = new TextNode(this.decodeString(size));
173-
return new Result(s, new_offset);
174+
return new Result(s, newOffset);
174175
case DOUBLE:
175-
DoubleNode d = this.decodeDouble(size);
176-
return new Result(d, new_offset);
176+
return new Result(this.decodeDouble(), newOffset);
177+
case FLOAT:
178+
return new Result(this.decodeFloat(), newOffset);
177179
case BYTES:
178180
BinaryNode b = new BinaryNode(this.getByteArray(size));
179-
return new Result(b, new_offset);
181+
return new Result(b, newOffset);
180182
case UINT16:
181183
IntNode i = this.decodeUint16(size);
182-
return new Result(i, new_offset);
184+
return new Result(i, newOffset);
183185
case UINT32:
184186
LongNode l = this.decodeUint32(size);
185-
return new Result(l, new_offset);
187+
return new Result(l, newOffset);
186188
case INT32:
187189
IntNode int32 = this.decodeInt32(size);
188-
return new Result(int32, new_offset);
190+
return new Result(int32, newOffset);
189191
case UINT64:
190192
BigIntegerNode bi = this.decodeBigInteger(size);
191-
return new Result(bi, new_offset);
193+
return new Result(bi, newOffset);
192194
case UINT128:
193195
BigIntegerNode uint128 = this.decodeBigInteger(size);
194-
return new Result(uint128, new_offset);
196+
return new Result(uint128, newOffset);
195197
default:
196198
throw new InvalidDatabaseException(
197199
"Unknown or unexpected type: " + type.name());
@@ -281,10 +283,12 @@ private BigIntegerNode decodeBigInteger(int size) {
281283
return new BigIntegerNode(new BigInteger(1, bytes));
282284
}
283285

284-
private DoubleNode decodeDouble(int size) {
285-
byte[] bytes = this.getByteArray(size);
286-
return new DoubleNode(Double.parseDouble(new String(bytes, Charset
287-
.forName("US-ASCII"))));
286+
private DoubleNode decodeDouble() {
287+
return new DoubleNode(this.threadBuffer.get().getDouble());
288+
}
289+
290+
private FloatNode decodeFloat() {
291+
return new FloatNode(this.threadBuffer.get().getFloat());
288292
}
289293

290294
private Result decodeBoolean(long size, long offset) {

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

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.fasterxml.jackson.databind.ObjectMapper;
2222
import com.fasterxml.jackson.databind.node.ArrayNode;
23+
import com.fasterxml.jackson.databind.node.FloatNode;
2324
import com.fasterxml.jackson.databind.node.ObjectNode;
2425

2526
@SuppressWarnings({ "boxing", "static-method" })
@@ -208,24 +209,47 @@ private static void addTestString(Map<String, byte[]> tests, byte ctrl[],
208209

209210
static Map<Double, byte[]> doubles() {
210211
Map<Double, byte[]> doubles = new HashMap<Double, byte[]>();
211-
DecoderTest.addTestDouble(doubles, (byte) 0x71, "-1073741824.12457");
212-
DecoderTest.addTestDouble(doubles, (byte) 0x70, "1073741824.12457");
213-
DecoderTest.addTestDouble(doubles, (byte) 0x6e, "-3.14159265359");
214-
DecoderTest.addTestDouble(doubles, (byte) 0x63, "123");
215-
DecoderTest.addTestDouble(doubles, (byte) 0x62, ".5");
216-
DecoderTest.addTestDouble(doubles, (byte) 0x63, "-.5");
212+
doubles.put(0.0, new byte[] { 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
213+
0x0 });
214+
doubles.put(0.5, new byte[] { 0x68, 0x3F, (byte) 0xE0, 0x0, 0x0, 0x0,
215+
0x0, 0x0, 0x0 });
216+
doubles.put(3.14159265359, new byte[] { 0x68, 0x40, 0x9, 0x21,
217+
(byte) 0xFB, 0x54, 0x44, 0x2E, (byte) 0xEA });
218+
doubles.put(123.0, new byte[] { 0x68, 0x40, 0x5E, (byte) 0xC0, 0x0,
219+
0x0, 0x0, 0x0, 0x0 });
220+
doubles.put(1073741824.12457, new byte[] { 0x68, 0x41, (byte) 0xD0,
221+
0x0, 0x0, 0x0, 0x7, (byte) 0xF8, (byte) 0xF4 });
222+
doubles.put(-0.5, new byte[] { 0x68, (byte) 0xBF, (byte) 0xE0, 0x0,
223+
0x0, 0x0, 0x0, 0x0, 0x0 });
224+
doubles.put(-3.14159265359, new byte[] { 0x68, (byte) 0xC0, 0x9, 0x21,
225+
(byte) 0xFB, 0x54, 0x44, 0x2E, (byte) 0xEA });
226+
doubles.put(-1073741824.12457, new byte[] { 0x68, (byte) 0xC1,
227+
(byte) 0xD0, 0x0, 0x0, 0x0, 0x7, (byte) 0xF8, (byte) 0xF4 });
228+
217229
return doubles;
218230
}
219231

220-
private static void addTestDouble(Map<Double, byte[]> tests, byte ctrl,
221-
String str) {
222-
223-
byte[] sb = str.getBytes(Charset.forName("US-ASCII"));
224-
byte[] bytes = new byte[1 + sb.length];
225-
226-
bytes[0] = ctrl;
227-
System.arraycopy(sb, 0, bytes, 1, sb.length);
228-
tests.put(new Double(str), bytes);
232+
static Map<Float, byte[]> floats() {
233+
Map<Float, byte[]> floats = new HashMap<Float, byte[]>();
234+
floats.put((float) 0.0, new byte[] { 0x4, 0x8, 0x0, 0x0, 0x0, 0x0 });
235+
floats.put((float) 1.0, new byte[] { 0x4, 0x8, 0x3F, (byte) 0x80, 0x0,
236+
0x0 });
237+
floats.put((float) 1.1, new byte[] { 0x4, 0x8, 0x3F, (byte) 0x8C,
238+
(byte) 0xCC, (byte) 0xCD });
239+
floats.put((float) 3.14, new byte[] { 0x4, 0x8, 0x40, 0x48,
240+
(byte) 0xF5, (byte) 0xC3 });
241+
floats.put((float) 9999.99, new byte[] { 0x4, 0x8, 0x46, 0x1C, 0x3F,
242+
(byte) 0xF6 });
243+
floats.put((float) -1.0, new byte[] { 0x4, 0x8, (byte) 0xBF,
244+
(byte) 0x80, 0x0, 0x0 });
245+
floats.put((float) -1.1, new byte[] { 0x4, 0x8, (byte) 0xBF,
246+
(byte) 0x8C, (byte) 0xCC, (byte) 0xCD });
247+
floats.put((float) -3.14, new byte[] { 0x4, 0x8, (byte) 0xC0, 0x48,
248+
(byte) 0xF5, (byte) 0xC3 });
249+
floats.put((float) -9999.99, new byte[] { 0x4, 0x8, (byte) 0xC6, 0x1C,
250+
0x3F, (byte) 0xF6 });
251+
252+
return floats;
229253
}
230254

231255
static Map<Boolean, byte[]> booleans() {
@@ -348,6 +372,11 @@ public void testDoubles() throws InvalidDatabaseException, IOException {
348372
.testTypeDecoding(Decoder.Type.DOUBLE, DecoderTest.doubles());
349373
}
350374

375+
@Test
376+
public void testFloats() throws InvalidDatabaseException, IOException {
377+
DecoderTest.testTypeDecoding(Decoder.Type.FLOAT, DecoderTest.floats());
378+
}
379+
351380
@Test
352381
public void testPointers() throws InvalidDatabaseException, IOException {
353382
DecoderTest.testTypeDecoding(Decoder.Type.POINTER, pointers());
@@ -416,6 +445,9 @@ static <T> void testTypeDecoding(Decoder.Type type, Map<T, byte[]> tests)
416445
} else if (type.equals(Decoder.Type.DOUBLE)) {
417446
assertEquals(desc, expect, decoder.decode(0).getNode()
418447
.asDouble());
448+
} else if (type.equals(Decoder.Type.FLOAT)) {
449+
assertEquals(desc, new FloatNode((Float) expect), decoder
450+
.decode(0).getNode());
419451
} else if (type.equals(Decoder.Type.UTF8_STRING)) {
420452
assertEquals(desc, expect, decoder.decode(0).getNode()
421453
.asText());

0 commit comments

Comments
 (0)