XJBWriterBufferBoundsTest.java

package tools.jackson.core.unittest.io.xjb;

import java.util.Random;

import org.junit.jupiter.api.Test;

import tools.jackson.core.io.NumberOutput;
import tools.jackson.core.io.xjb.XJBWriter;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Verifies that {@link NumberOutput#MAX_DOUBLE_BYTES} / {@link NumberOutput#MAX_FLOAT_BYTES}
 * (and the char[] equivalents) really bound what {@link XJBWriter} touches: the writers use
 * wide (2/4/8-byte) stores, so they can write past the last logical character of the number.
 */
public class XJBWriterBufferBoundsTest
{
    // Longest possible double output: 17 significant digits, sign, and a 3-digit
    // negative exponent -- the case where the exponent digits are written last.
    private final static double LONGEST_DOUBLE = -2.2250738585072014E-308;

    @Test
    public void longestDoubleFitsExactBuffer() {
        assertEquals(24, Double.toString(LONGEST_DOUBLE).length());
        assertDoubleFits(LONGEST_DOUBLE);
        assertDoubleFits(-1.2345678901234567E308);
        assertDoubleFits(1.2345678901234567E-308);
        assertDoubleFits(-Double.MIN_VALUE);
        assertDoubleFits(-Double.MAX_VALUE);
        assertDoubleFits(-Double.MIN_NORMAL);
    }

    @Test
    public void randomDoublesFitExactBuffer() {
        Random r = new Random(1234);
        for (int i = 0; i < 500_000; ++i) {
            assertDoubleFits(Double.longBitsToDouble(r.nextLong()));
        }
    }

    @Test
    public void randomFloatsFitExactBuffer() {
        assertFloatFits(-Float.MIN_VALUE);
        assertFloatFits(-Float.MAX_VALUE);
        assertFloatFits(-Float.MIN_NORMAL);
        Random r = new Random(1234);
        for (int i = 0; i < 500_000; ++i) {
            assertFloatFits(Float.intBitsToFloat(r.nextInt()));
        }
    }

    // Also check the writers do not scribble on bytes past the value they report
    @Test
    public void doubleDoesNotWritePastReturnedOffset() {
        byte[] buf = new byte[NumberOutput.MAX_DOUBLE_BYTES + 16];
        int end = NumberOutput.outputDouble(LONGEST_DOUBLE, buf, 0);
        for (int i = end; i < buf.length; ++i) {
            assertEquals(0, buf[i], "byte at "+i+" (past end "+end+") was modified");
        }
    }

    private void assertDoubleFits(double d) {
        // exact-size buffers: any wide store past the end throws
        try {
            byte[] bytes = new byte[NumberOutput.MAX_DOUBLE_BYTES];
            NumberOutput.outputDouble(d, bytes, 0);
            char[] chars = new char[NumberOutput.MAX_DOUBLE_CHARS];
            NumberOutput.outputDouble(d, chars, 0);
        } catch (IndexOutOfBoundsException e) {
            fail("double "+d+" ("+Double.toString(d)+") overflowed buffer: "+e);
        }
    }

    private void assertFloatFits(float f) {
        try {
            byte[] bytes = new byte[NumberOutput.MAX_FLOAT_BYTES];
            NumberOutput.outputFloat(f, bytes, 0);
            char[] chars = new char[NumberOutput.MAX_FLOAT_CHARS];
            NumberOutput.outputFloat(f, chars, 0);
        } catch (IndexOutOfBoundsException e) {
            fail("float "+f+" ("+Float.toString(f)+") overflowed buffer: "+e);
        }
    }
}