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  /**
20   * This class is borrowed from <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
21   */
22  public final class JsonUtils {
23  
24      private static final char[] HC = "0123456789ABCDEF".toCharArray();
25  
26      /**
27       * Read-only encoding table for first 128 Unicode code points (single-byte UTF-8 characters).
28       * Value of 0 means "no escaping"; other positive values that value is character
29       * to use after backslash; and negative values that generic (backslash - u)
30       * escaping is to be used.
31       */
32      private static final int[] ESC_CODES;
33      static {
34          final int[] table = new int[128];
35          // Control chars need generic escape sequence
36          for (int i = 0; i < 32; ++i) {
37              // 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant
38              table[i] = -1;
39          }
40          /* Others (and some within that range too) have explicit shorter
41           * sequences
42           */
43          table['"'] = '"';
44          table['\\'] = '\\';
45          // Escaping of slash is optional, so let's not add it
46          table[0x08] = 'b';
47          table[0x09] = 't';
48          table[0x0C] = 'f';
49          table[0x0A] = 'n';
50          table[0x0D] = 'r';
51          ESC_CODES = table;
52      }
53  
54      /**
55       * Temporary buffer used for composing quote/escape sequences
56       */
57      private final static ThreadLocal<char[]> _qbufLocal = new ThreadLocal<>();
58  
59      private static char[] getQBuf() {
60          char[] _qbuf = _qbufLocal.get();
61          if (_qbuf == null) {
62              _qbuf = new char[6];
63              _qbuf[0] = '\\';
64              _qbuf[2] = '0';
65              _qbuf[3] = '0';
66  
67              _qbufLocal.set(_qbuf);
68          }
69          return _qbuf;
70      }
71  
72      /**
73       * Quote text contents using JSON standard quoting, and append results to a supplied {@link StringBuilder}.
74       */
75      public static void quoteAsString(final CharSequence input, final StringBuilder output) {
76          final char[] qbuf = getQBuf();
77          final int escCodeCount = ESC_CODES.length;
78          int inPtr = 0;
79          final int inputLen = input.length();
80  
81          outer:
82          while (inPtr < inputLen) {
83              tight_loop:
84              while (true) {
85                  final char c = input.charAt(inPtr);
86                  if (c < escCodeCount && ESC_CODES[c] != 0) {
87                      break tight_loop;
88                  }
89                  output.append(c);
90                  if (++inPtr >= inputLen) {
91                      break outer;
92                  }
93              }
94              // something to escape; 2 or 6-char variant?
95              final char d = input.charAt(inPtr++);
96              final int escCode = ESC_CODES[d];
97              final int length = (escCode < 0)
98                      ? _appendNumeric(d, qbuf)
99                      : _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 }