1 package ch.qos.logback.core.util;
2
3 import java.math.BigDecimal;
4 import java.nio.ByteBuffer;
5 import java.nio.charset.StandardCharsets;
6
7
8
9
10
11
12
13
14 public final class DirectJson {
15 private static final int INITIAL_BUFFER_SIZE = 1024;
16 private static final byte QUOTE = '"';
17 private static final byte ENTRY_SEP = ':';
18 private static final byte KV_SEP = ',';
19 private static final byte DOT = '.';
20 private static final byte OPEN_OBJ = '{';
21 private static final byte CLOSE_OBJ = '}';
22 private static final byte OPEN_ARR = '[';
23 private static final byte CLOSE_ARR = ']';
24
25 private static final byte[] NEWLINE = new byte[] {
26 '\\',
27 'n',
28 };
29 private static final byte[] ESCAPE = new byte[] {
30 '\\',
31 '\\',
32 };
33 private static final byte[] LINEBREAK = new byte[] {
34 '\\',
35 'r',
36 };
37 private static final byte[] TAB = new byte[] {
38 '\\',
39 't',
40 };
41 private static final byte[] TRUE = new byte[] {
42 't',
43 'r',
44 'u',
45 'e'
46 };
47 private static final byte[] FALSE = new byte[] {
48 'f',
49 'a',
50 'l',
51 's',
52 'e'
53 };
54 private static final byte[] NULL = new byte[] {
55 'n',
56 'u',
57 'l',
58 'l'
59 };
60
61 private ByteBuffer buffer;
62
63 public DirectJson() {
64 buffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE);
65 }
66
67 public void openObject() { buffer.put(OPEN_OBJ); }
68 public void openArray() { buffer.put(OPEN_ARR); }
69
70 public void openObject(String str) {
71 writeString(str);
72 writeEntrySep();
73 buffer.put(OPEN_OBJ);
74 }
75
76 public void openArray(String str) {
77 writeString(str);
78 writeEntrySep();
79 buffer.put(OPEN_ARR);
80 }
81
82 public void closeObject() {
83 var target = buffer.position() - 1;
84 if (',' == buffer.get(target)) {
85 buffer.put(target, CLOSE_OBJ);
86 } else {
87 buffer.put(CLOSE_OBJ);
88 }
89 }
90
91 public void closeArray() {
92 var target = buffer.position() - 1;
93 if (',' == buffer.get(target)) {
94 buffer.put(target, CLOSE_ARR);
95 } else {
96 buffer.put(CLOSE_ARR);
97 }
98 }
99
100 public void writeRaw(String str) {
101 for(int i = 0; i < str.length(); i++ ){
102 var chr = str.codePointAt(i);
103 switch (chr) {
104 case '\\':
105 buffer.put(ESCAPE);
106 break;
107 case '\n':
108 buffer.put(NEWLINE);
109 break;
110 case '\r':
111 buffer.put(LINEBREAK);
112 break;
113 case '\t':
114 buffer.put(TAB);
115 break;
116 default:
117 if (chr >= 0x80 && chr <= 0x10FFFF) {
118 buffer.put(String.valueOf(str.charAt(i)).getBytes());
119 } else if (chr > 0x1F) buffer.put((byte) chr);
120 }
121
122 }
123 }
124
125 public void writeRaw(char chr) { buffer.put((byte) chr); }
126 public void writeRaw(byte[] chr) { buffer.put(chr); }
127
128 public void writeQuote() { buffer.put(QUOTE); }
129 public void writeString(String str) {
130 checkSpace(str.length() + 3);
131 buffer.put(QUOTE);
132 writeRaw(str);
133 buffer.put(QUOTE);
134 buffer.put(KV_SEP);
135 }
136 public void writeSep() { buffer.put(KV_SEP); }
137
138 public void writeNumberRaw(final long data) {
139 final int pos = buffer.position();
140 final int sz = (int) Math.log10(data) + 1;
141 long dataPointer = data;
142
143 for (int i = sz - 1; i >= 0; i--) {
144 byte chr = (byte) (dataPointer % 10);
145 dataPointer = dataPointer / 10;
146 chr += 48;
147 buffer.put(pos + i, chr);
148 }
149
150 buffer.position(pos + sz);
151 }
152
153 public void writeNumber(final long data) {
154 final int pos = buffer.position();
155 final int sz = data == 0 ? 1 : (int) Math.log10(data) + 1;
156 long dataPointer = data;
157
158 for (int i = sz - 1; i >= 0; i--) {
159 byte chr = (byte) (dataPointer % 10);
160 dataPointer = dataPointer / 10;
161 chr += 48;
162 buffer.put(pos + i, chr);
163 }
164
165 buffer.position(pos + sz);
166 buffer.put(KV_SEP);
167 }
168
169 public void writeNumber(final double data) {
170 int pos = buffer.position();
171 long whole = (long) data;
172 final int sz = (int) Math.log10(whole) + 1;
173
174 for (int i = sz - 1; i >= 0; i--) {
175 byte chr = (byte) (whole % 10);
176 whole = whole / 10;
177 chr += 48;
178 buffer.put(pos + i, chr);
179 }
180 buffer.position(pos + sz);
181 buffer.put(DOT);
182 pos = buffer.position();
183 BigDecimal fractional = BigDecimal.valueOf(data).remainder(BigDecimal.ONE);
184 int decs = 0;
185 while (!fractional.equals(BigDecimal.ZERO)) {
186 fractional = fractional.movePointRight(1);
187 byte chr = (byte) (fractional.intValue() + 48);
188 fractional = fractional.remainder(BigDecimal.ONE);
189 decs += 1;
190 buffer.put(chr);
191 }
192
193 buffer.position(pos + decs);
194 buffer.put(KV_SEP);
195 }
196
197 public void writeEntrySep() { buffer.put(buffer.position() - 1, ENTRY_SEP); }
198
199 public void writeStringValue(String key, String value) {
200 writeString(key);
201 writeEntrySep();
202 writeString(value);
203 }
204
205 public void writeNumberValue(String key, long value) {
206 writeString(key);
207 writeEntrySep();
208 writeNumber(value);
209 }
210
211 public void writeNumberValue(String key, double value) {
212 writeString(key);
213 writeEntrySep();
214 writeNumber(value);
215 }
216
217 public void writeBoolean(boolean value) {
218 buffer.put(value ? TRUE : FALSE);
219 buffer.put(KV_SEP);
220 }
221
222 public void writeNull() {
223 buffer.put(NULL);
224 buffer.put(KV_SEP);
225 }
226
227 public void checkSpace(int size) {
228 if (buffer.position() + size >= buffer.capacity()) {
229 var newSize = (buffer.capacity() + size) * 2;
230 ByteBuffer newBuffer = ByteBuffer.allocateDirect(newSize);
231 buffer.flip();
232 newBuffer.put(buffer);
233 buffer = newBuffer;
234 }
235 }
236
237 public byte[] flush() {
238 byte[] result = new byte[buffer.position()];
239 buffer.flip();
240 buffer.get(result);
241 buffer.clear();
242
243 return result;
244 }
245 }