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}