blob: c23f4b33e69727119cd972fe1848baa572511ec8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.util;
/**
* This class is borrowed from <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
*/
public final class JsonUtils {
private static final char[] HC = "0123456789ABCDEF".toCharArray();
/**
* Read-only encoding table for first 128 Unicode code points (single-byte UTF-8 characters).
* Value of 0 means "no escaping"; other positive values that value is character
* to use after backslash; and negative values that generic (backslash - u)
* escaping is to be used.
*/
private static final int[] ESC_CODES;
static {
final int[] table = new int[128];
// Control chars need generic escape sequence
for (int i = 0; i < 32; ++i) {
// 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant
table[i] = -1;
}
/* Others (and some within that range too) have explicit shorter
* sequences
*/
table['"'] = '"';
table['\\'] = '\\';
// Escaping of slash is optional, so let's not add it
table[0x08] = 'b';
table[0x09] = 't';
table[0x0C] = 'f';
table[0x0A] = 'n';
table[0x0D] = 'r';
ESC_CODES = table;
}
/**
* Temporary buffer used for composing quote/escape sequences
*/
private final static ThreadLocal<char[]> _qbufLocal = new ThreadLocal<>();
private static char[] getQBuf() {
char[] _qbuf = _qbufLocal.get();
if (_qbuf == null) {
_qbuf = new char[6];
_qbuf[0] = '\\';
_qbuf[2] = '0';
_qbuf[3] = '0';
_qbufLocal.set(_qbuf);
}
return _qbuf;
}
/**
* Quote text contents using JSON standard quoting, and append results to a supplied {@link StringBuilder}.
*/
public static void quoteAsString(final CharSequence input, final StringBuilder output) {
final char[] qbuf = getQBuf();
final int escCodeCount = ESC_CODES.length;
int inPtr = 0;
final int inputLen = input.length();
outer:
while (inPtr < inputLen) {
tight_loop:
while (true) {
final char c = input.charAt(inPtr);
if (c < escCodeCount && ESC_CODES[c] != 0) {
break tight_loop;
}
output.append(c);
if (++inPtr >= inputLen) {
break outer;
}
}
// something to escape; 2 or 6-char variant?
final char d = input.charAt(inPtr++);
final int escCode = ESC_CODES[d];
final int length = (escCode < 0)
? _appendNumeric(d, qbuf)
: _appendNamed(escCode, qbuf);
output.append(qbuf, 0, length);
}
}
private static int _appendNumeric(final int value, final char[] qbuf) {
qbuf[1] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
qbuf[4] = HC[value >> 4];
qbuf[5] = HC[value & 0xF];
return 6;
}
private static int _appendNamed(final int esc, final char[] qbuf) {
qbuf[1] = (char) esc;
return 2;
}
}