CustomEscaping1668Test.java

package tools.jackson.core.unittest.json;

import java.io.*;

import org.junit.jupiter.api.Test;

import tools.jackson.core.*;
import tools.jackson.core.io.CharacterEscapes;
import tools.jackson.core.io.SerializedString;
import tools.jackson.core.json.JsonFactory;

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

// [core#1668]: zero-length custom escape ending exactly at output buffer boundary
class CustomEscaping1668Test extends tools.jackson.core.unittest.JacksonCoreTestBase
{
    @SuppressWarnings("serial")
    static class ZeroLengthNulEscape extends CharacterEscapes
    {
        private final static SerializableString EMPTY = new SerializedString("");

        private final int[] _asciiEscapes;

        public ZeroLengthNulEscape() {
            _asciiEscapes = standardAsciiEscapesForJSON();
            _asciiEscapes[0] = CharacterEscapes.ESCAPE_CUSTOM;
        }

        @Override
        public int[] getEscapeCodesForAscii() { return _asciiEscapes; }

        @Override
        public SerializableString getEscapeSequence(int ch) {
            return (ch == 0) ? EMPTY : null;
        }
    }

    private final JsonFactory JSON_F = JsonFactory.builder()
            .characterEscapes(new ZeroLengthNulEscape())
            .build();

    // Escaped char is last of the String, and at various offsets straddling
    // the output buffer boundary (4000 chars for Writer-backed, 8000 bytes for byte-backed)
    @Test
    void zeroLengthEscapeAtBufferBoundaryChars() throws Exception
    {
        for (int pad = 3900; pad <= 4100; ++pad) {
            StringWriter w = new StringWriter();
            try (JsonGenerator g = JSON_F.createGenerator(ObjectWriteContext.empty(), w)) {
                _writeDoc(g, pad);
            }
            assertEquals(_expected(pad), w.toString(), "Failure with pad="+pad);
        }
    }

    @Test
    void zeroLengthEscapeAtBufferBoundaryBytes() throws Exception
    {
        for (int pad = 7900; pad <= 8100; ++pad) {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            try (JsonGenerator g = JSON_F.createGenerator(ObjectWriteContext.empty(), out, JsonEncoding.UTF8)) {
                _writeDoc(g, pad);
            }
            assertEquals(_expected(pad), out.toString("UTF-8"), "Failure with pad="+pad);
        }
    }

    private void _writeDoc(JsonGenerator g, int pad) throws Exception
    {
        g.writeStartArray();
        g.writeString(_repeat('x', pad));
        g.writeString("abc\0"); // last char is the custom-escaped one
        g.writeEndArray();
    }

    private String _expected(int pad) {
        return "[\"" + _repeat('x', pad) + "\",\"abc\"]";
    }

    private String _repeat(char c, int count) {
        StringBuilder sb = new StringBuilder(count);
        for (int i = 0; i < count; ++i) {
            sb.append(c);
        }
        return sb.toString();
    }
}