1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2023, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.core.encoder;
16  
17  public class JsonEscapeUtil {
18  
19      protected final static char[] HEXADECIMALS_TABLE = "0123456789ABCDEF".toCharArray();
20  
21      static final int ESCAPE_CODES_COUNT = 32;
22  
23      static final String[] ESCAPE_CODES = new String[ESCAPE_CODES_COUNT];
24  
25      // From RFC-8259 page 5
26  
27      //  "    quotation mark  U+0022 -- escaped as \"
28      //  \    reverse solidus U+005C -- escaped as \\
29      //  /    solidus         U+002F -- escaped as \/
30  
31      //  backspace       U+0008  -- escaped as \b
32      //  tab             U+0009  -- escaped as \t
33      //  line feed       U+000A  -- escaped as \n
34      //  form feed       U+000C -- escaped as \f
35      //  carriage return U+000D -- escaped as \r
36  
37      static {
38          for (char c = 0; c < ESCAPE_CODES_COUNT; c++) {
39  
40              switch (c) {
41              case 0x08:
42                  ESCAPE_CODES[c] = "\\b";
43                  break;
44              case 0x09:
45                  ESCAPE_CODES[c] = "\\t";
46                  break;
47              case 0x0A:
48                  ESCAPE_CODES[c] = "\\n";
49                  break;
50              case 0x0C:
51                  ESCAPE_CODES[c] = "\\f";
52                  break;
53              case 0x0D:
54                  ESCAPE_CODES[c] = "\\r";
55                  break;
56              default:
57                  ESCAPE_CODES[c] = _computeEscapeCodeBelowASCII32(c);
58              }
59          }
60      }
61  
62      // this method should not be called by methods except the static initializer
63      private static String _computeEscapeCodeBelowASCII32(char c) {
64          if (c > 32) {
65              throw new IllegalArgumentException("input must be less than 32");
66          }
67  
68          StringBuilder sb = new StringBuilder(6);
69          sb.append("\\u00");
70  
71          int highPart = c >> 4;
72          sb.append(HEXADECIMALS_TABLE[highPart]);
73  
74          int lowPart = c & 0x0F;
75          sb.append(HEXADECIMALS_TABLE[lowPart]);
76  
77          return sb.toString();
78      }
79  
80      //  "    quotation mark  U+0022 -- escaped as \"
81      //  \    reverse solidus U+005C -- escaped as \\
82      //  /    solidus         U+002F -- escaped as \/
83  
84      static String getObligatoryEscapeCode(char c) {
85          if (c < 32)
86              return ESCAPE_CODES[c];
87          if (c == 0x22)
88              return "\\\"";
89          if (c == 0x2F)
90              return "\\/";
91          if (c == 0x5C)
92              return "\\\\";
93  
94          return null;
95      }
96  
97      static public String jsonEscapeString(String input) {
98          int length = input.length();
99          int lenthWithLeeway = (int) (length * 1.1);
100 
101         StringBuilder sb = new StringBuilder(lenthWithLeeway);
102         for (int i = 0; i < length; i++) {
103             final char c = input.charAt(i);
104             String escaped = getObligatoryEscapeCode(c);
105             if (escaped == null)
106                 sb.append(c);
107             else {
108                 sb.append(escaped);
109             }
110         }
111 
112         return sb.toString();
113     }
114 
115 }