001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.util;
018
019/**
020 * This class is borrowed from <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
021 */
022public final class JsonUtils {
023
024    private static final char[] HC = "0123456789ABCDEF".toCharArray();
025
026    /**
027     * Read-only encoding table for first 128 Unicode code points (single-byte UTF-8 characters).
028     * Value of 0 means "no escaping"; other positive values that value is character
029     * to use after backslash; and negative values that generic (backslash - u)
030     * escaping is to be used.
031     */
032    private static final int[] ESC_CODES;
033    static {
034        final int[] table = new int[128];
035        // Control chars need generic escape sequence
036        for (int i = 0; i < 32; ++i) {
037            // 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant
038            table[i] = -1;
039        }
040        /* Others (and some within that range too) have explicit shorter
041         * sequences
042         */
043        table['"'] = '"';
044        table['\\'] = '\\';
045        // Escaping of slash is optional, so let's not add it
046        table[0x08] = 'b';
047        table[0x09] = 't';
048        table[0x0C] = 'f';
049        table[0x0A] = 'n';
050        table[0x0D] = 'r';
051        ESC_CODES = table;
052    }
053
054    /**
055     * Temporary buffer used for composing quote/escape sequences
056     */
057    private final static ThreadLocal<char[]> _qbufLocal = new ThreadLocal<>();
058
059    private static char[] getQBuf() {
060        char[] _qbuf = _qbufLocal.get();
061        if (_qbuf == null) {
062            _qbuf = new char[6];
063            _qbuf[0] = '\\';
064            _qbuf[2] = '0';
065            _qbuf[3] = '0';
066
067            _qbufLocal.set(_qbuf);
068        }
069        return _qbuf;
070    }
071
072    /**
073     * Quote text contents using JSON standard quoting, and append results to a supplied {@link StringBuilder}.
074     */
075    public static void quoteAsString(final CharSequence input, final StringBuilder output) {
076        final char[] qbuf = getQBuf();
077        final int escCodeCount = ESC_CODES.length;
078        int inPtr = 0;
079        final int inputLen = input.length();
080
081        outer:
082        while (inPtr < inputLen) {
083            tight_loop:
084            while (true) {
085                final char c = input.charAt(inPtr);
086                if (c < escCodeCount && ESC_CODES[c] != 0) {
087                    break tight_loop;
088                }
089                output.append(c);
090                if (++inPtr >= inputLen) {
091                    break outer;
092                }
093            }
094            // something to escape; 2 or 6-char variant?
095            final char d = input.charAt(inPtr++);
096            final int escCode = ESC_CODES[d];
097            final int length = (escCode < 0)
098                    ? _appendNumeric(d, qbuf)
099                    : _appendNamed(escCode, qbuf);
100
101            output.append(qbuf, 0, length);
102        }
103    }
104
105    private static int _appendNumeric(final int value, final char[] qbuf) {
106        qbuf[1] = 'u';
107        // We know it's a control char, so only the last 2 chars are non-0
108        qbuf[4] = HC[value >> 4];
109        qbuf[5] = HC[value & 0xF];
110        return 6;
111    }
112
113    private static int _appendNamed(final int esc, final char[] qbuf) {
114        qbuf[1] = (char) esc;
115        return 2;
116    }
117
118}