1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.layout;
18
19 import java.nio.ByteBuffer;
20 import java.nio.CharBuffer;
21 import java.nio.charset.CharacterCodingException;
22 import java.nio.charset.Charset;
23 import java.nio.charset.CharsetEncoder;
24 import java.nio.charset.CoderResult;
25
26
27
28
29
30
31 public class TextEncoderHelper {
32
33 private TextEncoderHelper() {
34 }
35
36 static void encodeTextFallBack(final Charset charset, final StringBuilder text,
37 final ByteBufferDestination destination) {
38 final byte[] bytes = text.toString().getBytes(charset);
39 destination.writeBytes(bytes, 0, bytes.length);
40 }
41
42
43
44
45
46
47
48
49
50
51
52
53 static void encodeText(final CharsetEncoder charsetEncoder, final CharBuffer charBuf, final ByteBuffer byteBuf,
54 final StringBuilder text, final ByteBufferDestination destination)
55 throws CharacterCodingException {
56 charsetEncoder.reset();
57 if (text.length() > charBuf.capacity()) {
58 encodeChunkedText(charsetEncoder, charBuf, byteBuf, text, destination);
59 return;
60 }
61 charBuf.clear();
62 text.getChars(0, text.length(), charBuf.array(), charBuf.arrayOffset());
63 charBuf.limit(text.length());
64 final CoderResult result = charsetEncoder.encode(charBuf, byteBuf, true);
65 writeEncodedText(charsetEncoder, charBuf, byteBuf, destination, result);
66 }
67
68
69
70
71
72
73
74
75
76 private static void writeEncodedText(final CharsetEncoder charsetEncoder, final CharBuffer charBuf,
77 final ByteBuffer byteBuf, final ByteBufferDestination destination, CoderResult result) {
78 if (!result.isUnderflow()) {
79 writeChunkedEncodedText(charsetEncoder, charBuf, destination, byteBuf, result);
80 return;
81 }
82 result = charsetEncoder.flush(byteBuf);
83 if (!result.isUnderflow()) {
84 synchronized (destination) {
85 flushRemainingBytes(charsetEncoder, destination, byteBuf);
86 }
87 return;
88 }
89
90
91
92
93
94 if (byteBuf != destination.getByteBuffer()) {
95 byteBuf.flip();
96 destination.writeBytes(byteBuf);
97 byteBuf.clear();
98 }
99 }
100
101
102
103
104
105
106
107
108
109 private static void writeChunkedEncodedText(final CharsetEncoder charsetEncoder, final CharBuffer charBuf,
110 final ByteBufferDestination destination, ByteBuffer byteBuf, final CoderResult result) {
111 synchronized (destination) {
112 byteBuf = writeAndEncodeAsMuchAsPossible(charsetEncoder, charBuf, true, destination, byteBuf,
113 result);
114 flushRemainingBytes(charsetEncoder, destination, byteBuf);
115 }
116 }
117
118
119
120
121
122
123
124
125
126 private static void encodeChunkedText(final CharsetEncoder charsetEncoder, final CharBuffer charBuf,
127 ByteBuffer byteBuf, final StringBuilder text, final ByteBufferDestination destination) {
128
129
130
131 int start = 0;
132 CoderResult result = CoderResult.UNDERFLOW;
133 boolean endOfInput = false;
134 while (!endOfInput && result.isUnderflow()) {
135 charBuf.clear();
136 final int copied = copy(text, start, charBuf);
137 start += copied;
138 endOfInput = start >= text.length();
139 charBuf.flip();
140 result = charsetEncoder.encode(charBuf, byteBuf, endOfInput);
141 }
142 if (endOfInput) {
143 writeEncodedText(charsetEncoder, charBuf, byteBuf, destination, result);
144 return;
145 }
146 synchronized (destination) {
147 byteBuf = writeAndEncodeAsMuchAsPossible(charsetEncoder, charBuf, endOfInput, destination, byteBuf,
148 result);
149 while (!endOfInput) {
150 result = CoderResult.UNDERFLOW;
151 while (!endOfInput && result.isUnderflow()) {
152 charBuf.clear();
153 final int copied = copy(text, start, charBuf);
154 start += copied;
155 endOfInput = start >= text.length();
156 charBuf.flip();
157 result = charsetEncoder.encode(charBuf, byteBuf, endOfInput);
158 }
159 byteBuf = writeAndEncodeAsMuchAsPossible(charsetEncoder, charBuf, endOfInput, destination, byteBuf,
160 result);
161 }
162 flushRemainingBytes(charsetEncoder, destination, byteBuf);
163 }
164 }
165
166
167
168
169 @Deprecated
170 public static void encodeText(final CharsetEncoder charsetEncoder, final CharBuffer charBuf,
171 final ByteBufferDestination destination) {
172 charsetEncoder.reset();
173 synchronized (destination) {
174 ByteBuffer byteBuf = destination.getByteBuffer();
175 byteBuf = encodeAsMuchAsPossible(charsetEncoder, charBuf, true, destination, byteBuf);
176 flushRemainingBytes(charsetEncoder, destination, byteBuf);
177 }
178 }
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195 private static ByteBuffer writeAndEncodeAsMuchAsPossible(final CharsetEncoder charsetEncoder,
196 final CharBuffer charBuf, final boolean endOfInput, final ByteBufferDestination destination,
197 ByteBuffer temp, CoderResult result) {
198 while (true) {
199 temp = drainIfByteBufferFull(destination, temp, result);
200 if (!result.isOverflow()) {
201 break;
202 }
203 result = charsetEncoder.encode(charBuf, temp, endOfInput);
204 }
205 if (!result.isUnderflow()) {
206 throwException(result);
207 }
208 return temp;
209 }
210
211
212 private static void throwException(final CoderResult result) {
213 try {
214 result.throwException();
215 } catch (final CharacterCodingException e) {
216 throw new IllegalStateException(e);
217 }
218 }
219
220 private static ByteBuffer encodeAsMuchAsPossible(final CharsetEncoder charsetEncoder, final CharBuffer charBuf,
221 final boolean endOfInput, final ByteBufferDestination destination, ByteBuffer temp) {
222 CoderResult result;
223 do {
224 result = charsetEncoder.encode(charBuf, temp, endOfInput);
225 temp = drainIfByteBufferFull(destination, temp, result);
226 } while (result.isOverflow());
227 if (!result.isUnderflow()) {
228 throwException(result);
229 }
230 return temp;
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 private static ByteBuffer drainIfByteBufferFull(final ByteBufferDestination destination, final ByteBuffer temp,
248 final CoderResult result) {
249 if (result.isOverflow()) {
250
251
252 synchronized (destination) {
253 final ByteBuffer destinationBuffer = destination.getByteBuffer();
254 if (destinationBuffer != temp) {
255 temp.flip();
256 ByteBufferDestinationHelper.writeToUnsynchronized(temp, destination);
257 temp.clear();
258 return destination.getByteBuffer();
259 } else {
260 return destination.drain(destinationBuffer);
261 }
262 }
263 } else {
264 return temp;
265 }
266 }
267
268 private static void flushRemainingBytes(final CharsetEncoder charsetEncoder,
269 final ByteBufferDestination destination, ByteBuffer temp) {
270 CoderResult result;
271 do {
272
273 result = charsetEncoder.flush(temp);
274 temp = drainIfByteBufferFull(destination, temp, result);
275 } while (result.isOverflow());
276 if (!result.isUnderflow()) {
277 throwException(result);
278 }
279 if (temp.remaining() > 0 && temp != destination.getByteBuffer()) {
280 temp.flip();
281 ByteBufferDestinationHelper.writeToUnsynchronized(temp, destination);
282 temp.clear();
283 }
284 }
285
286
287
288
289
290
291
292
293 static int copy(final StringBuilder source, final int offset, final CharBuffer destination) {
294 final int length = Math.min(source.length() - offset, destination.remaining());
295 final char[] array = destination.array();
296 final int start = destination.position();
297 source.getChars(offset, offset + length, array, destination.arrayOffset() + start);
298 destination.position(start + length);
299 return length;
300 }
301 }