Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ private static boolean _checkVarHandleAvailable() {
}
}


/*
/**********************************************************************
/* Input source config
Expand Down
1 change: 1 addition & 0 deletions avro/src/test/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
opens tools.jackson.dataformat.avro;
opens tools.jackson.dataformat.avro.annotation;
opens tools.jackson.dataformat.avro.constraints;
opens tools.jackson.dataformat.avro.deser;
opens tools.jackson.dataformat.avro.dos;
opens tools.jackson.dataformat.avro.fuzz;
opens tools.jackson.dataformat.avro.gen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,60 @@ private void _verifyGetNumberTypeFail(JsonParser p, String token) throws Excepti
// In 2.x got exception; in 3.x null
assertNull(p.getNumberType());
}

// Characterization of `decodeFloat()`/`decodeDouble()` (see `JacksonAvroParserImpl`)
// across IEEE-754 boundary values, both from a plain byte array (single read)
// and from a `LimitingInputStream` (forces the buffer-refill/`_loadToHaveAtLeast`
// path to be exercised for every 4/8-byte read).
private static final float[] FLOAT_EDGE_CASES = new float[] {
0.0f, -0.0f, 1.0f, -1.0f,
Float.MIN_VALUE, Float.MAX_VALUE, Float.MIN_NORMAL,
Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY,
123.456f, -123.456f
};

private static final double[] DOUBLE_EDGE_CASES = new double[] {
0.0, -0.0, 1.0, -1.0,
Double.MIN_VALUE, Double.MAX_VALUE, Double.MIN_NORMAL,
Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
123456.789, -123456.789
};

@Test
public void testFloatEdgeCasesRoundTrip() throws Exception
{
AvroSchema schema = MAPPER.schemaFor(Numbers.class);
for (float f : FLOAT_EDGE_CASES) {
Numbers input = new Numbers(0, 0L, f, 0.0);
byte[] bytes = MAPPER.writer(schema).writeValueAsBytes(input);

Numbers direct = MAPPER.readerFor(Numbers.class).with(schema).readValue(bytes);
assertEquals(Float.floatToRawIntBits(f), Float.floatToRawIntBits(direct.f),
"direct read of " + f);

Numbers chunked = MAPPER.readerFor(Numbers.class).with(schema)
.readValue(LimitingInputStream.wrap(bytes, 42));
assertEquals(Float.floatToRawIntBits(f), Float.floatToRawIntBits(chunked.f),
"chunked read of " + f);
}
}

@Test
public void testDoubleEdgeCasesRoundTrip() throws Exception
{
AvroSchema schema = MAPPER.schemaFor(Numbers.class);
for (double d : DOUBLE_EDGE_CASES) {
Numbers input = new Numbers(0, 0L, 0.0f, d);
byte[] bytes = MAPPER.writer(schema).writeValueAsBytes(input);

Numbers direct = MAPPER.readerFor(Numbers.class).with(schema).readValue(bytes);
assertEquals(Double.doubleToRawLongBits(d), Double.doubleToRawLongBits(direct.d),
"direct read of " + d);

Numbers chunked = MAPPER.readerFor(Numbers.class).with(schema)
.readValue(LimitingInputStream.wrap(bytes, 42));
assertEquals(Double.doubleToRawLongBits(d), Double.doubleToRawLongBits(chunked.d),
"chunked read of " + d);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package tools.jackson.dataformat.avro.deser;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Direct characterization of {@link AvroVarHandleUtil}'s little-endian
* byte-array reads, independent of the full Avro parser round trip.
*/
public class AvroVarHandleUtilTest
{
// On any JDK 17+ runtime (this project's baseline) VarHandle byte-array
// views are always available.
@Test
public void testIsAvailable() {
assertTrue(AvroVarHandleUtil.isAvailable());
}

@Test
public void testGetIntLEZero() {
byte[] buf = new byte[] { 0, 0, 0, 0 };
assertEquals(0, AvroVarHandleUtil.getIntLE(buf, 0));
}

@Test
public void testGetIntLEPositive() {
// little-endian encoding of 0x04030201
byte[] buf = new byte[] { 0x01, 0x02, 0x03, 0x04 };
assertEquals(0x04030201, AvroVarHandleUtil.getIntLE(buf, 0));
}

@Test
public void testGetIntLENegative() {
// little-endian encoding of 0xFFFFFFFF (-1) and 0x80000000 (Integer.MIN_VALUE)
byte[] allOnes = new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
assertEquals(-1, AvroVarHandleUtil.getIntLE(allOnes, 0));

byte[] minValue = new byte[] { 0x00, 0x00, 0x00, (byte) 0x80 };
assertEquals(Integer.MIN_VALUE, AvroVarHandleUtil.getIntLE(minValue, 0));
}

@Test
public void testGetIntLEAtOffset() {
// leading padding byte followed by little-endian 0x04030201
byte[] buf = new byte[] { (byte) 0xAA, 0x01, 0x02, 0x03, 0x04 };
assertEquals(0x04030201, AvroVarHandleUtil.getIntLE(buf, 1));
}

@Test
public void testGetLongLEZero() {
byte[] buf = new byte[8];
assertEquals(0L, AvroVarHandleUtil.getLongLE(buf, 0));
}

@Test
public void testGetLongLEPositive() {
// little-endian encoding of 0x0807060504030201L
byte[] buf = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
assertEquals(0x0807060504030201L, AvroVarHandleUtil.getLongLE(buf, 0));
}

@Test
public void testGetLongLENegative() {
byte[] allOnes = new byte[] {
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
assertEquals(-1L, AvroVarHandleUtil.getLongLE(allOnes, 0));

byte[] minValue = new byte[] { 0, 0, 0, 0, 0, 0, 0, (byte) 0x80 };
assertEquals(Long.MIN_VALUE, AvroVarHandleUtil.getLongLE(minValue, 0));
}

@Test
public void testGetLongLEAtOffset() {
byte[] buf = new byte[] {
(byte) 0xAA, (byte) 0xBB,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
assertEquals(0x0807060504030201L, AvroVarHandleUtil.getLongLE(buf, 2));
}
}