View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.util;
18  
19  import com.fasterxml.jackson.core.io.CharTypes;
20  
21  /**
22   * This class is borrowed from <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
23   */
24  public final class JsonUtils {
25  
26      private final static char[] HC = CharTypes.copyHexChars();
27  
28      /**
29       * Temporary buffer used for composing quote/escape sequences
30       */
31      private final static ThreadLocal<char[]> _qbufLocal = new ThreadLocal<>();
32  
33      private static char[] getQBuf() {
34          char[] _qbuf = _qbufLocal.get();
35          if (_qbuf == null) {
36              _qbuf = new char[6];
37              _qbuf[0] = '\\';
38              _qbuf[2] = '0';
39              _qbuf[3] = '0';
40  
41              _qbufLocal.set(_qbuf);
42          }
43          return _qbuf;
44      }
45  
46      /**
47       * Quote text contents using JSON standard quoting, and append results to a supplied {@link StringBuilder}.
48       */
49      public static void quoteAsString(final CharSequence input, final StringBuilder output) {
50          final char[] qbuf = getQBuf();
51          final int[] escCodes = CharTypes.get7BitOutputEscapes();
52          final int escCodeCount = escCodes.length;
53          int inPtr = 0;
54          final int inputLen = input.length();
55  
56          outer:
57          while (inPtr < inputLen) {
58              tight_loop:
59              while (true) {
60                  final char c = input.charAt(inPtr);
61                  if (c < escCodeCount && escCodes[c] != 0) {
62                      break tight_loop;
63                  }
64                  output.append(c);
65                  if (++inPtr >= inputLen) {
66                      break outer;
67                  }
68              }
69              // something to escape; 2 or 6-char variant?
70              final char d = input.charAt(inPtr++);
71              final int escCode = escCodes[d];
72              final int length = (escCode < 0)
73                      ? _appendNumeric(d, qbuf)
74                      : _appendNamed(escCode, qbuf);
75  
76              output.append(qbuf, 0, length);
77          }
78      }
79  
80      private static int _appendNumeric(final int value, final char[] qbuf) {
81          qbuf[1] = 'u';
82          // We know it's a control char, so only the last 2 chars are non-0
83          qbuf[4] = HC[value >> 4];
84          qbuf[5] = HC[value & 0xF];
85          return 6;
86      }
87  
88      private static int _appendNamed(final int esc, final char[] qbuf) {
89          qbuf[1] = (char) esc;
90          return 2;
91      }
92  
93  }