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
019import com.fasterxml.jackson.core.io.CharTypes;
020
021/**
022 * This class is borrowed from <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
023 */
024public final class JsonUtils {
025
026    private final static char[] HC = CharTypes.copyHexChars();
027
028    /**
029     * Temporary buffer used for composing quote/escape sequences
030     */
031    private final static ThreadLocal<char[]> _qbufLocal = new ThreadLocal<>();
032
033    private static char[] getQBuf() {
034        char[] _qbuf = _qbufLocal.get();
035        if (_qbuf == null) {
036            _qbuf = new char[6];
037            _qbuf[0] = '\\';
038            _qbuf[2] = '0';
039            _qbuf[3] = '0';
040
041            _qbufLocal.set(_qbuf);
042        }
043        return _qbuf;
044    }
045
046    /**
047     * Quote text contents using JSON standard quoting, and append results to a supplied {@link StringBuilder}.
048     */
049    public static void quoteAsString(CharSequence input, StringBuilder output) {
050        final char[] qbuf = getQBuf();
051        final int[] escCodes = CharTypes.get7BitOutputEscapes();
052        final int escCodeCount = escCodes.length;
053        int inPtr = 0;
054        final int inputLen = input.length();
055
056        outer:
057        while (inPtr < inputLen) {
058            tight_loop:
059            while (true) {
060                char c = input.charAt(inPtr);
061                if (c < escCodeCount && escCodes[c] != 0) {
062                    break tight_loop;
063                }
064                output.append(c);
065                if (++inPtr >= inputLen) {
066                    break outer;
067                }
068            }
069            // something to escape; 2 or 6-char variant?
070            char d = input.charAt(inPtr++);
071            int escCode = escCodes[d];
072            int length = (escCode < 0)
073                    ? _appendNumeric(d, qbuf)
074                    : _appendNamed(escCode, qbuf);
075
076            output.append(qbuf, 0, length);
077        }
078    }
079
080    private static int _appendNumeric(int value, char[] qbuf) {
081        qbuf[1] = 'u';
082        // We know it's a control char, so only the last 2 chars are non-0
083        qbuf[4] = HC[value >> 4];
084        qbuf[5] = HC[value & 0xF];
085        return 6;
086    }
087
088    private static int _appendNamed(int esc, char[] qbuf) {
089        qbuf[1] = (char) esc;
090        return 2;
091    }
092
093}