blob: 55246913f53bb7cb0a4ff4cd4bf66e69be4224a2 [file] [log] [blame]
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001package com.fasterxml.jackson.core.json;
2
3import java.io.*;
4import java.math.BigDecimal;
5import java.math.BigInteger;
6
7import com.fasterxml.jackson.core.*;
Tatu Saloranta9b59c3b2012-01-17 22:28:20 -08008import com.fasterxml.jackson.core.io.*;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08009
10public class UTF8JsonGenerator
Tatu Salorantae6dfc692012-09-28 15:34:05 -070011 extends JsonGeneratorImpl
Tatu Salorantaf15531c2011-12-22 23:00:40 -080012{
13 private final static byte BYTE_u = (byte) 'u';
14
15 private final static byte BYTE_0 = (byte) '0';
Cowtowncoder5cddffa2015-01-15 16:10:26 -080016
Tatu Salorantaf15531c2011-12-22 23:00:40 -080017 private final static byte BYTE_LBRACKET = (byte) '[';
18 private final static byte BYTE_RBRACKET = (byte) ']';
19 private final static byte BYTE_LCURLY = (byte) '{';
20 private final static byte BYTE_RCURLY = (byte) '}';
Cowtowncoder5cddffa2015-01-15 16:10:26 -080021
Tatu Salorantaf15531c2011-12-22 23:00:40 -080022 private final static byte BYTE_BACKSLASH = (byte) '\\';
Tatu Salorantaf15531c2011-12-22 23:00:40 -080023 private final static byte BYTE_COMMA = (byte) ',';
24 private final static byte BYTE_COLON = (byte) ':';
25 private final static byte BYTE_QUOTE = (byte) '"';
26
Tatu Salorantaf15531c2011-12-22 23:00:40 -080027 // intermediate copies only made up to certain length...
28 private final static int MAX_BYTES_TO_BUFFER = 512;
Cowtowncoder5cddffa2015-01-15 16:10:26 -080029
Faisal Hameed73662a52015-12-09 01:30:31 +050030 private final static byte[] HEX_CHARS = CharTypes.copyHexBytes();
Tatu Salorantaf15531c2011-12-22 23:00:40 -080031
32 private final static byte[] NULL_BYTES = { 'n', 'u', 'l', 'l' };
33 private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
34 private final static byte[] FALSE_BYTES = { 'f', 'a', 'l', 's', 'e' };
35
Tatu Salorantaf15531c2011-12-22 23:00:40 -080036 /*
37 /**********************************************************
38 /* Output buffering
39 /**********************************************************
40 */
Cowtowncoder5cddffa2015-01-15 16:10:26 -080041
Tatu Salorantae6dfc692012-09-28 15:34:05 -070042 /**
43 * Underlying output stream used for writing JSON content.
44 */
45 final protected OutputStream _outputStream;
Tatu Salorantaf15531c2011-12-22 23:00:40 -080046
47 /**
48 * Intermediate buffer in which contents are buffered before
49 * being written using {@link #_outputStream}.
50 */
51 protected byte[] _outputBuffer;
52
53 /**
54 * Pointer to the position right beyond the last character to output
55 * (end marker; may be past the buffer)
56 */
Faisal Hameedb18e3882015-12-09 00:34:01 +050057 protected int _outputTail;
Tatu Salorantaf15531c2011-12-22 23:00:40 -080058
59 /**
60 * End marker of the output buffer; one past the last valid position
61 * within the buffer.
62 */
63 protected final int _outputEnd;
64
65 /**
66 * Maximum number of <code>char</code>s that we know will always fit
67 * in the output buffer after escaping
68 */
69 protected final int _outputMaxContiguous;
Cowtowncoder5cddffa2015-01-15 16:10:26 -080070
Tatu Salorantaf15531c2011-12-22 23:00:40 -080071 /**
72 * Intermediate buffer in which characters of a String are copied
73 * before being encoded.
74 */
75 protected char[] _charBuffer;
Cowtowncoder5cddffa2015-01-15 16:10:26 -080076
Tatu Salorantaf15531c2011-12-22 23:00:40 -080077 /**
78 * Length of <code>_charBuffer</code>
79 */
80 protected final int _charBufferLength;
Cowtowncoder5cddffa2015-01-15 16:10:26 -080081
Tatu Salorantaf15531c2011-12-22 23:00:40 -080082 /**
83 * 6 character temporary buffer allocated if needed, for constructing
84 * escape sequences
85 */
86 protected byte[] _entityBuffer;
87
88 /**
89 * Flag that indicates whether the output buffer is recycable (and
90 * needs to be returned to recycler once we are done) or not.
91 */
92 protected boolean _bufferRecyclable;
Tatu Saloranta8fc42432013-10-03 10:08:27 -070093
94 /*
95 /**********************************************************
Tatu Salorantaf15531c2011-12-22 23:00:40 -080096 /* Life-cycle
97 /**********************************************************
98 */
99
100 public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
101 OutputStream out)
102 {
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700103 super(ctxt, features, codec);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800104 _outputStream = out;
105 _bufferRecyclable = true;
106 _outputBuffer = ctxt.allocWriteEncodingBuffer();
107 _outputEnd = _outputBuffer.length;
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700108
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800109 /* To be exact, each char can take up to 6 bytes when escaped (Unicode
110 * escape with backslash, 'u' and 4 hex digits); but to avoid fluctuation,
111 * we will actually round down to only do up to 1/8 number of chars
112 */
113 _outputMaxContiguous = _outputEnd >> 3;
114 _charBuffer = ctxt.allocConcatBuffer();
115 _charBufferLength = _charBuffer.length;
116
117 // By default we use this feature to determine additional quoting
118 if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
119 setHighestNonEscapedChar(127);
120 }
121 }
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700122
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800123 public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700124 OutputStream out,
125 byte[] outputBuffer, int outputOffset, boolean bufferRecyclable)
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800126 {
127
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700128 super(ctxt, features, codec);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800129 _outputStream = out;
130 _bufferRecyclable = bufferRecyclable;
131 _outputTail = outputOffset;
132 _outputBuffer = outputBuffer;
133 _outputEnd = _outputBuffer.length;
134 // up to 6 bytes per char (see above), rounded up to 1/8
Cowtowncoder46b4f022015-05-26 17:59:11 -0700135 _outputMaxContiguous = (_outputEnd >> 3);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800136 _charBuffer = ctxt.allocConcatBuffer();
137 _charBufferLength = _charBuffer.length;
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800138 }
139
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800140 /*
141 /**********************************************************
142 /* Overridden configuration methods
143 /**********************************************************
144 */
145
146 @Override
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800147 public Object getOutputTarget() {
148 return _outputStream;
149 }
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700150
Cowtowncoder46b4f022015-05-26 17:59:11 -0700151 @Override
152 public int getOutputBuffered() {
153 // Assuming tail is always valid, set to 0 on close
154 return _outputTail;
155 }
156
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800157 /*
158 /**********************************************************
159 /* Overridden methods
160 /**********************************************************
161 */
162
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800163 @Override
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700164 public void writeFieldName(String name) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800165 {
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700166 if (_cfgPrettyPrinter != null) {
167 _writePPFieldName(name);
168 return;
169 }
170 final int status = _writeContext.writeFieldName(name);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800171 if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
172 _reportError("Can not write a field name, expecting a value");
173 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800174 if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { // need comma
175 if (_outputTail >= _outputEnd) {
176 _flushBuffer();
177 }
178 _outputBuffer[_outputTail++] = BYTE_COMMA;
179 }
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700180 /* To support [JACKSON-46], we'll do this:
181 * (Question: should quoting of spaces (etc) still be enabled?)
182 */
Tatu Salorantae6009812014-04-29 22:55:41 -0700183 if (_cfgUnqNames) {
Tatu Saloranta07efb962014-04-29 23:14:56 -0700184 _writeStringSegments(name, false);
185 return;
186 }
187 final int len = name.length();
188 // Does it fit in buffer?
189 if (len > _charBufferLength) { // no, offline
190 _writeStringSegments(name, true);
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700191 return;
192 }
193 if (_outputTail >= _outputEnd) {
194 _flushBuffer();
195 }
196 _outputBuffer[_outputTail++] = BYTE_QUOTE;
Tatu Saloranta07efb962014-04-29 23:14:56 -0700197 // But as one segment, or multiple?
198 if (len <= _outputMaxContiguous) {
199 if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
200 _flushBuffer();
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700201 }
Tatu Salorantaad3a43e2015-03-30 19:29:05 -0700202 _writeStringSegment(name, 0, len);
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700203 } else {
Tatu Salorantaad3a43e2015-03-30 19:29:05 -0700204 _writeStringSegments(name, 0, len);
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700205 }
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700206 // and closing quotes; need room for one more char:
207 if (_outputTail >= _outputEnd) {
208 _flushBuffer();
209 }
210 _outputBuffer[_outputTail++] = BYTE_QUOTE;
211 }
212
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800213 @Override
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700214 public void writeFieldName(SerializableString name) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800215 {
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700216 if (_cfgPrettyPrinter != null) {
217 _writePPFieldName(name);
218 return;
219 }
220 final int status = _writeContext.writeFieldName(name.getValue());
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800221 if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
222 _reportError("Can not write a field name, expecting a value");
223 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800224 if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
225 if (_outputTail >= _outputEnd) {
226 _flushBuffer();
227 }
228 _outputBuffer[_outputTail++] = BYTE_COMMA;
229 }
Tatu Salorantae6009812014-04-29 22:55:41 -0700230 if (_cfgUnqNames) {
Tatu Saloranta8be19132014-01-20 21:55:04 -0800231 _writeUnq(name);
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700232 return;
233 }
234 if (_outputTail >= _outputEnd) {
235 _flushBuffer();
236 }
237 _outputBuffer[_outputTail++] = BYTE_QUOTE;
238 int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
239 if (len < 0) { // couldn't append, bit longer processing
240 _writeBytes(name.asQuotedUTF8());
241 } else {
242 _outputTail += len;
243 }
244 if (_outputTail >= _outputEnd) {
245 _flushBuffer();
246 }
247 _outputBuffer[_outputTail++] = BYTE_QUOTE;
248 }
249
Tatu Saloranta8be19132014-01-20 21:55:04 -0800250 private final void _writeUnq(SerializableString name) throws IOException {
251 int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
252 if (len < 0) {
253 _writeBytes(name.asQuotedUTF8());
254 } else {
255 _outputTail += len;
256 }
257 }
258
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800259 /*
260 /**********************************************************
261 /* Output method implementations, structural
262 /**********************************************************
263 */
264
265 @Override
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700266 public final void writeStartArray() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800267 {
268 _verifyValueWrite("start an array");
269 _writeContext = _writeContext.createChildArrayContext();
270 if (_cfgPrettyPrinter != null) {
271 _cfgPrettyPrinter.writeStartArray(this);
272 } else {
273 if (_outputTail >= _outputEnd) {
274 _flushBuffer();
275 }
276 _outputBuffer[_outputTail++] = BYTE_LBRACKET;
277 }
278 }
279
280 @Override
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700281 public final void writeEndArray() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800282 {
283 if (!_writeContext.inArray()) {
284 _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
285 }
286 if (_cfgPrettyPrinter != null) {
287 _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
288 } else {
289 if (_outputTail >= _outputEnd) {
290 _flushBuffer();
291 }
292 _outputBuffer[_outputTail++] = BYTE_RBRACKET;
293 }
Tatu Salorantab4567ca2015-12-21 20:47:08 -0800294 _writeContext = _writeContext.clearAndGetParent();
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800295 }
296
297 @Override
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700298 public final void writeStartObject() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800299 {
300 _verifyValueWrite("start an object");
301 _writeContext = _writeContext.createChildObjectContext();
302 if (_cfgPrettyPrinter != null) {
303 _cfgPrettyPrinter.writeStartObject(this);
304 } else {
305 if (_outputTail >= _outputEnd) {
306 _flushBuffer();
307 }
308 _outputBuffer[_outputTail++] = BYTE_LCURLY;
309 }
310 }
311
312 @Override
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700313 public final void writeEndObject() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800314 {
315 if (!_writeContext.inObject()) {
316 _reportError("Current context not an object but "+_writeContext.getTypeDesc());
317 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800318 if (_cfgPrettyPrinter != null) {
319 _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
320 } else {
321 if (_outputTail >= _outputEnd) {
322 _flushBuffer();
323 }
324 _outputBuffer[_outputTail++] = BYTE_RCURLY;
325 }
Tatu Salorantab4567ca2015-12-21 20:47:08 -0800326 _writeContext = _writeContext.clearAndGetParent();
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800327 }
328
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800329 /**
330 * Specialized version of <code>_writeFieldName</code>, off-lined
331 * to keep the "fast path" as simple (and hopefully fast) as possible.
332 */
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700333 protected final void _writePPFieldName(String name) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800334 {
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700335 int status = _writeContext.writeFieldName(name);
336 if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
337 _reportError("Can not write a field name, expecting a value");
338 }
339 if ((status == JsonWriteContext.STATUS_OK_AFTER_COMMA)) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800340 _cfgPrettyPrinter.writeObjectEntrySeparator(this);
341 } else {
342 _cfgPrettyPrinter.beforeObjectEntries(this);
343 }
Tatu Saloranta07efb962014-04-29 23:14:56 -0700344 if (_cfgUnqNames) {
345 _writeStringSegments(name, false);
346 return;
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800347 }
Tatu Saloranta07efb962014-04-29 23:14:56 -0700348 final int len = name.length();
349 if (len > _charBufferLength) {
350 _writeStringSegments(name, true);
351 return;
352 }
353 if (_outputTail >= _outputEnd) {
354 _flushBuffer();
355 }
356 _outputBuffer[_outputTail++] = BYTE_QUOTE;
357 name.getChars(0, len, _charBuffer, 0);
358 // But as one segment, or multiple?
359 if (len <= _outputMaxContiguous) {
360 if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
361 _flushBuffer();
362 }
363 _writeStringSegment(_charBuffer, 0, len);
364 } else {
365 _writeStringSegments(_charBuffer, 0, len);
366 }
367 if (_outputTail >= _outputEnd) {
368 _flushBuffer();
369 }
370 _outputBuffer[_outputTail++] = BYTE_QUOTE;
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800371 }
372
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700373 protected final void _writePPFieldName(SerializableString name) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800374 {
Tatu Saloranta3aa24142014-04-29 22:37:39 -0700375 final int status = _writeContext.writeFieldName(name.getValue());
376 if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
377 _reportError("Can not write a field name, expecting a value");
378 }
379 if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800380 _cfgPrettyPrinter.writeObjectEntrySeparator(this);
381 } else {
382 _cfgPrettyPrinter.beforeObjectEntries(this);
383 }
384
Tatu Salorantae6009812014-04-29 22:55:41 -0700385 final boolean addQuotes = !_cfgUnqNames; // standard
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800386 if (addQuotes) {
387 if (_outputTail >= _outputEnd) {
388 _flushBuffer();
389 }
390 _outputBuffer[_outputTail++] = BYTE_QUOTE;
391 }
392 _writeBytes(name.asQuotedUTF8());
393 if (addQuotes) {
394 if (_outputTail >= _outputEnd) {
395 _flushBuffer();
396 }
397 _outputBuffer[_outputTail++] = BYTE_QUOTE;
398 }
399 }
400
401 /*
402 /**********************************************************
403 /* Output method implementations, textual
404 /**********************************************************
405 */
406
407 @Override
Tatu Salorantae6009812014-04-29 22:55:41 -0700408 public void writeString(String text) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800409 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800410 _verifyValueWrite(WRITE_STRING);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800411 if (text == null) {
412 _writeNull();
413 return;
414 }
Tatu Salorantaf93c9c82015-05-25 20:00:41 -0700415 // First: if we can't guarantee it all fits, quoted, within output, offline
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800416 final int len = text.length();
Tatu Salorantaf93c9c82015-05-25 20:00:41 -0700417 if (len > _outputMaxContiguous) { // nope: off-line handling
Tatu Saloranta07efb962014-04-29 23:14:56 -0700418 _writeStringSegments(text, true);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800419 return;
420 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800421 if ((_outputTail + len) >= _outputEnd) {
422 _flushBuffer();
423 }
424 _outputBuffer[_outputTail++] = BYTE_QUOTE;
Tatu Salorantae22dbdb2015-03-30 20:24:50 -0700425 _writeStringSegment(text, 0, len); // we checked space already above
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800426 if (_outputTail >= _outputEnd) {
427 _flushBuffer();
428 }
429 _outputBuffer[_outputTail++] = BYTE_QUOTE;
430 }
431
432 @Override
Tatu Salorantae6009812014-04-29 22:55:41 -0700433 public void writeString(char[] text, int offset, int len) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800434 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800435 _verifyValueWrite(WRITE_STRING);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800436 if (_outputTail >= _outputEnd) {
437 _flushBuffer();
438 }
439 _outputBuffer[_outputTail++] = BYTE_QUOTE;
440 // One or multiple segments?
441 if (len <= _outputMaxContiguous) {
442 if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
443 _flushBuffer();
444 }
445 _writeStringSegment(text, offset, len);
446 } else {
447 _writeStringSegments(text, offset, len);
448 }
449 // And finally, closing quotes
450 if (_outputTail >= _outputEnd) {
451 _flushBuffer();
452 }
453 _outputBuffer[_outputTail++] = BYTE_QUOTE;
454 }
455
456 @Override
Tatu Salorantae6009812014-04-29 22:55:41 -0700457 public final void writeString(SerializableString text) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800458 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800459 _verifyValueWrite(WRITE_STRING);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800460 if (_outputTail >= _outputEnd) {
461 _flushBuffer();
462 }
463 _outputBuffer[_outputTail++] = BYTE_QUOTE;
Tatu Saloranta9b59c3b2012-01-17 22:28:20 -0800464 int len = text.appendQuotedUTF8(_outputBuffer, _outputTail);
465 if (len < 0) {
466 _writeBytes(text.asQuotedUTF8());
467 } else {
468 _outputTail += len;
469 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800470 if (_outputTail >= _outputEnd) {
471 _flushBuffer();
472 }
473 _outputBuffer[_outputTail++] = BYTE_QUOTE;
474 }
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800475
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800476 @Override
Tatu Salorantae6009812014-04-29 22:55:41 -0700477 public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800478 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800479 _verifyValueWrite(WRITE_STRING);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800480 if (_outputTail >= _outputEnd) {
481 _flushBuffer();
482 }
483 _outputBuffer[_outputTail++] = BYTE_QUOTE;
484 _writeBytes(text, offset, length);
485 if (_outputTail >= _outputEnd) {
486 _flushBuffer();
487 }
488 _outputBuffer[_outputTail++] = BYTE_QUOTE;
489 }
490
491 @Override
Tatu Salorantae6009812014-04-29 22:55:41 -0700492 public void writeUTF8String(byte[] text, int offset, int len) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800493 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800494 _verifyValueWrite(WRITE_STRING);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800495 if (_outputTail >= _outputEnd) {
496 _flushBuffer();
497 }
498 _outputBuffer[_outputTail++] = BYTE_QUOTE;
499 // One or multiple segments?
500 if (len <= _outputMaxContiguous) {
501 _writeUTF8Segment(text, offset, len);
502 } else {
503 _writeUTF8Segments(text, offset, len);
504 }
505 if (_outputTail >= _outputEnd) {
506 _flushBuffer();
507 }
508 _outputBuffer[_outputTail++] = BYTE_QUOTE;
509 }
Cowtowncoder7df7c572014-11-07 17:06:29 -0800510
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800511 /*
512 /**********************************************************
513 /* Output method implementations, unprocessed ("raw")
514 /**********************************************************
515 */
516
517 @Override
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700518 public void writeRaw(String text) throws IOException {
Tatu Saloranta956e0ce2016-08-19 11:39:23 -0700519 final int len = text.length();
520 final char[] buf = _charBuffer;
521 if (len <= buf.length) {
522 text.getChars(0, len, buf, 0);
523 writeRaw(buf, 0, len);
524 } else {
525 writeRaw(text, 0, len);
526 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800527 }
528
529 @Override
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700530 public void writeRaw(String text, int offset, int len) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800531 {
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700532 final char[] buf = _charBuffer;
533
534 // minor optimization: see if we can just get and copy
535 if (len <= buf.length) {
536 text.getChars(offset, offset+len, buf, 0);
Tatu Saloranta956e0ce2016-08-19 11:39:23 -0700537 writeRaw(buf, 0, len);
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700538 return;
539 }
540
541 // If not, need segmented approach. For speed, let's also use input buffer
542 // size that is guaranteed to fit in output buffer; each char can expand to
543 // at most 3 bytes, so at most 1/3 of buffer size.
544 final int maxChunk = (_outputEnd >> 2) + (_outputEnd >> 4); // == (1/4 + 1/16) == 5/16
545 final int maxBytes = maxChunk * 3;
546
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800547 while (len > 0) {
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700548 int len2 = Math.min(maxChunk, len);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800549 text.getChars(offset, offset+len2, buf, 0);
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700550 if ((_outputTail + maxBytes) > _outputEnd) {
551 _flushBuffer();
552 }
553 // If this is NOT the last segment and if the last character looks like
554 // split surrogate second half, drop it
555 if (len > 0) {
556 char ch = buf[len2-1];
557 if ((ch >= SURR1_FIRST) && (ch <= SURR1_LAST)) {
558 --len2;
559 }
560 }
561 _writeRawSegment(buf, 0, len2);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800562 offset += len2;
563 len -= len2;
564 }
565 }
566
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700567 @Override
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700568 public void writeRaw(SerializableString text) throws IOException
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700569 {
570 byte[] raw = text.asUnquotedUTF8();
571 if (raw.length > 0) {
572 _writeBytes(raw);
573 }
574 }
Cowtowncoder7df7c572014-11-07 17:06:29 -0800575
576 // since 2.5
577 @Override
578 public void writeRawValue(SerializableString text) throws IOException {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800579 _verifyValueWrite(WRITE_RAW);
Cowtowncoder7df7c572014-11-07 17:06:29 -0800580 byte[] raw = text.asUnquotedUTF8();
581 if (raw.length > 0) {
582 _writeBytes(raw);
583 }
584 }
585
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800586 // @TODO: rewrite for speed...
587 @Override
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700588 public final void writeRaw(char[] cbuf, int offset, int len) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800589 {
590 // First: if we have 3 x charCount spaces, we know it'll fit just fine
591 {
592 int len3 = len+len+len;
593 if ((_outputTail + len3) > _outputEnd) {
594 // maybe we could flush?
595 if (_outputEnd < len3) { // wouldn't be enough...
596 _writeSegmentedRaw(cbuf, offset, len);
597 return;
598 }
599 // yes, flushing brings enough space
600 _flushBuffer();
601 }
602 }
603 len += offset; // now marks the end
604
605 // Note: here we know there is enough room, hence no output boundary checks
606 main_loop:
607 while (offset < len) {
608 inner_loop:
609 while (true) {
610 int ch = (int) cbuf[offset];
611 if (ch > 0x7F) {
612 break inner_loop;
613 }
614 _outputBuffer[_outputTail++] = (byte) ch;
615 if (++offset >= len) {
616 break main_loop;
617 }
618 }
619 char ch = cbuf[offset++];
620 if (ch < 0x800) { // 2-byte?
621 _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
622 _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
623 } else {
Tatu Saloranta5e14c462013-11-08 19:30:54 -0800624 offset = _outputRawMultiByteChar(ch, cbuf, offset, len);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800625 }
626 }
627 }
628
629 @Override
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700630 public void writeRaw(char ch) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800631 {
632 if ((_outputTail + 3) >= _outputEnd) {
633 _flushBuffer();
634 }
635 final byte[] bbuf = _outputBuffer;
636 if (ch <= 0x7F) {
637 bbuf[_outputTail++] = (byte) ch;
638 } else if (ch < 0x800) { // 2-byte?
639 bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
640 bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
641 } else {
Tatu Saloranta5e14c462013-11-08 19:30:54 -0800642 /*offset =*/ _outputRawMultiByteChar(ch, null, 0, 0);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800643 }
644 }
645
646 /**
647 * Helper method called when it is possible that output of raw section
648 * to output may cross buffer boundary
649 */
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700650 private final void _writeSegmentedRaw(char[] cbuf, int offset, int len) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800651 {
652 final int end = _outputEnd;
653 final byte[] bbuf = _outputBuffer;
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700654 final int inputEnd = offset + len;
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800655
656 main_loop:
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700657 while (offset < inputEnd) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800658 inner_loop:
659 while (true) {
660 int ch = (int) cbuf[offset];
661 if (ch >= 0x80) {
662 break inner_loop;
663 }
664 // !!! TODO: fast(er) writes (roll input, output checks in one)
665 if (_outputTail >= end) {
666 _flushBuffer();
667 }
668 bbuf[_outputTail++] = (byte) ch;
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700669 if (++offset >= inputEnd) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800670 break main_loop;
671 }
672 }
673 if ((_outputTail + 3) >= _outputEnd) {
674 _flushBuffer();
675 }
676 char ch = cbuf[offset++];
677 if (ch < 0x800) { // 2-byte?
678 bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
679 bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
680 } else {
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700681 offset = _outputRawMultiByteChar(ch, cbuf, offset, inputEnd);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800682 }
683 }
684 }
Tatu Saloranta554f8db2016-08-18 23:09:52 -0700685
686 /**
687 * Helper method that is called for segmented write of raw content
688 * when explicitly outputting a segment of longer thing.
689 * Caller has to take care of ensuring there's no split surrogate
690 * pair at the end (that is, last char can not be first part of a
691 * surrogate char pair).
692 *
693 * @since 2.8.2
694 */
695 private void _writeRawSegment(char[] cbuf, int offset, int end) throws IOException
696 {
697 main_loop:
698 while (offset < end) {
699 inner_loop:
700 while (true) {
701 int ch = (int) cbuf[offset];
702 if (ch > 0x7F) {
703 break inner_loop;
704 }
705 _outputBuffer[_outputTail++] = (byte) ch;
706 if (++offset >= end) {
707 break main_loop;
708 }
709 }
710 char ch = cbuf[offset++];
711 if (ch < 0x800) { // 2-byte?
712 _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
713 _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
714 } else {
715 offset = _outputRawMultiByteChar(ch, cbuf, offset, end);
716 }
717 }
718 }
719
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800720 /*
721 /**********************************************************
722 /* Output method implementations, base64-encoded binary
723 /**********************************************************
724 */
725
726 @Override
Tatu Saloranta2e70a2a2012-06-04 22:21:45 -0700727 public void writeBinary(Base64Variant b64variant,
728 byte[] data, int offset, int len)
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800729 throws IOException, JsonGenerationException
730 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800731 _verifyValueWrite(WRITE_BINARY);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800732 // Starting quotes
733 if (_outputTail >= _outputEnd) {
734 _flushBuffer();
735 }
736 _outputBuffer[_outputTail++] = BYTE_QUOTE;
737 _writeBinary(b64variant, data, offset, offset+len);
738 // and closing quotes
739 if (_outputTail >= _outputEnd) {
740 _flushBuffer();
741 }
742 _outputBuffer[_outputTail++] = BYTE_QUOTE;
743 }
Tatu Saloranta2e70a2a2012-06-04 22:21:45 -0700744
745 @Override
746 public int writeBinary(Base64Variant b64variant,
747 InputStream data, int dataLength)
748 throws IOException, JsonGenerationException
749 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800750 _verifyValueWrite(WRITE_BINARY);
Tatu Saloranta2e70a2a2012-06-04 22:21:45 -0700751 // Starting quotes
752 if (_outputTail >= _outputEnd) {
753 _flushBuffer();
754 }
755 _outputBuffer[_outputTail++] = BYTE_QUOTE;
756 byte[] encodingBuffer = _ioContext.allocBase64Buffer();
757 int bytes;
758 try {
Tatu Saloranta63ff5742012-06-08 21:30:30 -0700759 if (dataLength < 0) { // length unknown
760 bytes = _writeBinary(b64variant, data, encodingBuffer);
761 } else {
762 int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength);
763 if (missing > 0) {
764 _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")");
765 }
766 bytes = dataLength;
767 }
Tatu Saloranta2e70a2a2012-06-04 22:21:45 -0700768 } finally {
769 _ioContext.releaseBase64Buffer(encodingBuffer);
770 }
771 // and closing quotes
772 if (_outputTail >= _outputEnd) {
773 _flushBuffer();
774 }
775 _outputBuffer[_outputTail++] = BYTE_QUOTE;
776 return bytes;
777 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800778
779 /*
780 /**********************************************************
781 /* Output method implementations, primitive
782 /**********************************************************
783 */
784
785 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800786 public void writeNumber(short s) throws IOException
Martin Steiger7190d202013-04-13 12:55:32 +0200787 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800788 _verifyValueWrite(WRITE_NUMBER);
Martin Steiger7190d202013-04-13 12:55:32 +0200789 // up to 5 digits and possible minus sign
790 if ((_outputTail + 6) >= _outputEnd) {
791 _flushBuffer();
792 }
793 if (_cfgNumbersAsStrings) {
794 _writeQuotedShort(s);
795 return;
796 }
Tatu Saloranta5b7bbe12013-04-16 19:08:21 -0700797 _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
Martin Steiger7190d202013-04-13 12:55:32 +0200798 }
799
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700800 private final void _writeQuotedShort(short s) throws IOException {
Martin Steiger7190d202013-04-13 12:55:32 +0200801 if ((_outputTail + 8) >= _outputEnd) {
802 _flushBuffer();
803 }
804 _outputBuffer[_outputTail++] = BYTE_QUOTE;
Tatu Saloranta5b7bbe12013-04-16 19:08:21 -0700805 _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
Martin Steiger7190d202013-04-13 12:55:32 +0200806 _outputBuffer[_outputTail++] = BYTE_QUOTE;
807 }
808
809 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800810 public void writeNumber(int i) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800811 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800812 _verifyValueWrite(WRITE_NUMBER);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800813 // up to 10 digits and possible minus sign
814 if ((_outputTail + 11) >= _outputEnd) {
815 _flushBuffer();
816 }
817 if (_cfgNumbersAsStrings) {
818 _writeQuotedInt(i);
819 return;
820 }
821 _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
822 }
823
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700824 private final void _writeQuotedInt(int i) throws IOException
825 {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800826 if ((_outputTail + 13) >= _outputEnd) {
827 _flushBuffer();
828 }
829 _outputBuffer[_outputTail++] = BYTE_QUOTE;
830 _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
831 _outputBuffer[_outputTail++] = BYTE_QUOTE;
832 }
833
834 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800835 public void writeNumber(long l) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800836 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800837 _verifyValueWrite(WRITE_NUMBER);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800838 if (_cfgNumbersAsStrings) {
839 _writeQuotedLong(l);
840 return;
841 }
842 if ((_outputTail + 21) >= _outputEnd) {
843 // up to 20 digits, minus sign
844 _flushBuffer();
845 }
846 _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
847 }
848
Tatu Saloranta5db03b62013-10-03 10:13:45 -0700849 private final void _writeQuotedLong(long l) throws IOException
850 {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800851 if ((_outputTail + 23) >= _outputEnd) {
852 _flushBuffer();
853 }
854 _outputBuffer[_outputTail++] = BYTE_QUOTE;
855 _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
856 _outputBuffer[_outputTail++] = BYTE_QUOTE;
857 }
858
859 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800860 public void writeNumber(BigInteger value) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800861 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800862 _verifyValueWrite(WRITE_NUMBER);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800863 if (value == null) {
864 _writeNull();
865 } else if (_cfgNumbersAsStrings) {
Cowtowncoderfca10e32015-02-19 16:39:04 -0800866 _writeQuotedRaw(value.toString());
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800867 } else {
868 writeRaw(value.toString());
869 }
870 }
871
872
873 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800874 public void writeNumber(double d) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800875 {
876 if (_cfgNumbersAsStrings ||
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800877 (((Double.isNaN(d) || Double.isInfinite(d))
Tatu Salorantaec560f32015-11-23 22:50:22 -0800878 && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features)))) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800879 writeString(String.valueOf(d));
880 return;
881 }
882 // What is the max length for doubles? 40 chars?
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800883 _verifyValueWrite(WRITE_NUMBER);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800884 writeRaw(String.valueOf(d));
885 }
886
887 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800888 public void writeNumber(float f) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800889 {
890 if (_cfgNumbersAsStrings ||
891 // [JACKSON-139]
892 (((Float.isNaN(f) || Float.isInfinite(f))
Tatu Salorantaec560f32015-11-23 22:50:22 -0800893 && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features)))) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800894 writeString(String.valueOf(f));
895 return;
896 }
897 // What is the max length for floats?
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800898 _verifyValueWrite(WRITE_NUMBER);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800899 writeRaw(String.valueOf(f));
900 }
901
902 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800903 public void writeNumber(BigDecimal value) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800904 {
905 // Don't really know max length for big decimal, no point checking
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800906 _verifyValueWrite(WRITE_NUMBER);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800907 if (value == null) {
908 _writeNull();
Tatu Saloranta96642972016-08-24 22:53:20 -0700909 } else if (_cfgNumbersAsStrings) {
910 _writeQuotedRaw(_asString(value));
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800911 } else {
Tatu Saloranta96642972016-08-24 22:53:20 -0700912 writeRaw(_asString(value));
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800913 }
914 }
915
916 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800917 public void writeNumber(String encodedValue) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800918 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800919 _verifyValueWrite(WRITE_NUMBER);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800920 if (_cfgNumbersAsStrings) {
921 _writeQuotedRaw(encodedValue);
922 } else {
923 writeRaw(encodedValue);
924 }
925 }
926
Cowtowncoderfca10e32015-02-19 16:39:04 -0800927 private final void _writeQuotedRaw(String value) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800928 {
929 if (_outputTail >= _outputEnd) {
930 _flushBuffer();
931 }
932 _outputBuffer[_outputTail++] = BYTE_QUOTE;
Cowtowncoderfca10e32015-02-19 16:39:04 -0800933 writeRaw(value);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800934 if (_outputTail >= _outputEnd) {
935 _flushBuffer();
936 }
937 _outputBuffer[_outputTail++] = BYTE_QUOTE;
938 }
939
940 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800941 public void writeBoolean(boolean state) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800942 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800943 _verifyValueWrite(WRITE_BOOLEAN);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800944 if ((_outputTail + 5) >= _outputEnd) {
945 _flushBuffer();
946 }
947 byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES;
948 int len = keyword.length;
949 System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len);
950 _outputTail += len;
951 }
952
953 @Override
Tatu Salorantaec560f32015-11-23 22:50:22 -0800954 public void writeNull() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800955 {
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800956 _verifyValueWrite(WRITE_NULL);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800957 _writeNull();
958 }
959
960 /*
961 /**********************************************************
962 /* Implementations for other methods
963 /**********************************************************
964 */
965
966 @Override
Cowtowncoder5cddffa2015-01-15 16:10:26 -0800967 protected final void _verifyValueWrite(String typeMsg) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800968 {
969 int status = _writeContext.writeValue();
970 if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
971 _reportError("Can not "+typeMsg+", expecting field name");
972 }
973 if (_cfgPrettyPrinter == null) {
974 byte b;
975 switch (status) {
976 case JsonWriteContext.STATUS_OK_AFTER_COMMA:
977 b = BYTE_COMMA;
978 break;
979 case JsonWriteContext.STATUS_OK_AFTER_COLON:
980 b = BYTE_COLON;
981 break;
Tatu Salorantae6dfc692012-09-28 15:34:05 -0700982 case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
983 if (_rootValueSeparator != null) {
984 byte[] raw = _rootValueSeparator.asUnquotedUTF8();
985 if (raw.length > 0) {
986 _writeBytes(raw);
987 }
988 }
989 return;
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800990 case JsonWriteContext.STATUS_OK_AS_IS:
991 default:
992 return;
993 }
994 if (_outputTail >= _outputEnd) {
995 _flushBuffer();
996 }
997 _outputBuffer[_outputTail] = b;
998 ++_outputTail;
999 return;
1000 }
1001 // Otherwise, pretty printer knows what to do...
1002 _verifyPrettyValueWrite(typeMsg, status);
1003 }
1004
Cowtowncoder5cddffa2015-01-15 16:10:26 -08001005 protected final void _verifyPrettyValueWrite(String typeMsg, int status) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001006 {
1007 // If we have a pretty printer, it knows what to do:
1008 switch (status) {
1009 case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
1010 _cfgPrettyPrinter.writeArrayValueSeparator(this);
1011 break;
1012 case JsonWriteContext.STATUS_OK_AFTER_COLON:
1013 _cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
1014 break;
1015 case JsonWriteContext.STATUS_OK_AFTER_SPACE:
1016 _cfgPrettyPrinter.writeRootValueSeparator(this);
1017 break;
1018 case JsonWriteContext.STATUS_OK_AS_IS:
1019 // First entry, but of which context?
1020 if (_writeContext.inArray()) {
1021 _cfgPrettyPrinter.beforeArrayValues(this);
1022 } else if (_writeContext.inObject()) {
1023 _cfgPrettyPrinter.beforeObjectEntries(this);
1024 }
1025 break;
1026 default:
Tatu Saloranta23439272013-04-06 20:44:18 -07001027 _throwInternal();
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001028 break;
1029 }
1030 }
1031
1032 /*
1033 /**********************************************************
1034 /* Low-level output handling
1035 /**********************************************************
1036 */
1037
1038 @Override
Cowtowncoder5cddffa2015-01-15 16:10:26 -08001039 public void flush() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001040 {
1041 _flushBuffer();
1042 if (_outputStream != null) {
1043 if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1044 _outputStream.flush();
1045 }
1046 }
1047 }
1048
1049 @Override
Cowtowncoder5cddffa2015-01-15 16:10:26 -08001050 public void close() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001051 {
1052 super.close();
1053
1054 /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
1055 * scopes.
1056 */
1057 // First: let's see that we still have buffers...
Cowtowncoder46b4f022015-05-26 17:59:11 -07001058 if ((_outputBuffer != null)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001059 && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
1060 while (true) {
1061 JsonStreamContext ctxt = getOutputContext();
1062 if (ctxt.inArray()) {
1063 writeEndArray();
1064 } else if (ctxt.inObject()) {
1065 writeEndObject();
1066 } else {
1067 break;
1068 }
1069 }
1070 }
1071 _flushBuffer();
Cowtowncoder46b4f022015-05-26 17:59:11 -07001072 _outputTail = 0; // just to ensure we don't think there's anything buffered
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001073
1074 /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
1075 * on the underlying Reader, unless we "own" it, or auto-closing
1076 * feature is enabled.
1077 * One downside: when using UTF8Writer, underlying buffer(s)
1078 * may not be properly recycled if we don't close the writer.
1079 */
1080 if (_outputStream != null) {
1081 if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
1082 _outputStream.close();
Tatu Saloranta58088f42016-02-01 22:08:22 -08001083 } else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001084 // If we can't close it, we should at least flush
1085 _outputStream.flush();
1086 }
1087 }
1088 // Internal buffer(s) generator has can now be released as well
1089 _releaseBuffers();
1090 }
1091
1092 @Override
1093 protected void _releaseBuffers()
1094 {
1095 byte[] buf = _outputBuffer;
1096 if (buf != null && _bufferRecyclable) {
1097 _outputBuffer = null;
1098 _ioContext.releaseWriteEncodingBuffer(buf);
1099 }
1100 char[] cbuf = _charBuffer;
1101 if (cbuf != null) {
1102 _charBuffer = null;
1103 _ioContext.releaseConcatBuffer(cbuf);
1104 }
1105 }
1106
1107 /*
1108 /**********************************************************
1109 /* Internal methods, low-level writing, raw bytes
1110 /**********************************************************
1111 */
1112
Tatu Saloranta75273be2012-11-11 15:42:06 -08001113 private final void _writeBytes(byte[] bytes) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001114 {
1115 final int len = bytes.length;
1116 if ((_outputTail + len) > _outputEnd) {
1117 _flushBuffer();
1118 // still not enough?
1119 if (len > MAX_BYTES_TO_BUFFER) {
1120 _outputStream.write(bytes, 0, len);
1121 return;
1122 }
1123 }
1124 System.arraycopy(bytes, 0, _outputBuffer, _outputTail, len);
1125 _outputTail += len;
1126 }
1127
Tatu Saloranta75273be2012-11-11 15:42:06 -08001128 private final void _writeBytes(byte[] bytes, int offset, int len) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001129 {
1130 if ((_outputTail + len) > _outputEnd) {
1131 _flushBuffer();
1132 // still not enough?
1133 if (len > MAX_BYTES_TO_BUFFER) {
1134 _outputStream.write(bytes, offset, len);
1135 return;
1136 }
1137 }
1138 System.arraycopy(bytes, offset, _outputBuffer, _outputTail, len);
1139 _outputTail += len;
1140 }
1141
1142 /*
1143 /**********************************************************
1144 /* Internal methods, mid-level writing, String segments
1145 /**********************************************************
1146 */
1147
1148 /**
1149 * Method called when String to write is long enough not to fit
1150 * completely in temporary copy buffer. If so, we will actually
1151 * copy it in small enough chunks so it can be directly fed
1152 * to single-segment writes (instead of maximum slices that
1153 * would fit in copy buffer)
1154 */
Tatu Saloranta07efb962014-04-29 23:14:56 -07001155 private final void _writeStringSegments(String text, boolean addQuotes) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001156 {
Tatu Saloranta07efb962014-04-29 23:14:56 -07001157 if (addQuotes) {
1158 if (_outputTail >= _outputEnd) {
1159 _flushBuffer();
1160 }
1161 _outputBuffer[_outputTail++] = BYTE_QUOTE;
1162 }
1163
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001164 int left = text.length();
1165 int offset = 0;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001166
1167 while (left > 0) {
1168 int len = Math.min(_outputMaxContiguous, left);
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001169 if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
1170 _flushBuffer();
1171 }
Tatu Salorantaf93c9c82015-05-25 20:00:41 -07001172 _writeStringSegment(text, offset, len);
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001173 offset += len;
1174 left -= len;
1175 }
Tatu Saloranta07efb962014-04-29 23:14:56 -07001176
1177 if (addQuotes) {
1178 if (_outputTail >= _outputEnd) {
1179 _flushBuffer();
1180 }
1181 _outputBuffer[_outputTail++] = BYTE_QUOTE;
1182 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001183 }
1184
1185 /**
1186 * Method called when character sequence to write is long enough that
1187 * its maximum encoded and escaped form is not guaranteed to fit in
1188 * the output buffer. If so, we will need to choose smaller output
1189 * chunks to write at a time.
1190 */
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001191 private final void _writeStringSegments(char[] cbuf, int offset, int totalLen) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001192 {
1193 do {
1194 int len = Math.min(_outputMaxContiguous, totalLen);
1195 if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
1196 _flushBuffer();
1197 }
1198 _writeStringSegment(cbuf, offset, len);
1199 offset += len;
1200 totalLen -= len;
1201 } while (totalLen > 0);
1202 }
1203
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001204 private final void _writeStringSegments(String text, int offset, int totalLen) throws IOException
1205 {
1206 do {
1207 int len = Math.min(_outputMaxContiguous, totalLen);
1208 if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
1209 _flushBuffer();
1210 }
1211 _writeStringSegment(text, offset, len);
1212 offset += len;
1213 totalLen -= len;
1214 } while (totalLen > 0);
1215 }
1216
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001217 /*
1218 /**********************************************************
1219 /* Internal methods, low-level writing, text segments
1220 /**********************************************************
1221 */
1222
1223 /**
1224 * This method called when the string content is already in
1225 * a char buffer, and its maximum total encoded and escaped length
1226 * can not exceed size of the output buffer.
1227 * Caller must ensure that there is enough space in output buffer,
1228 * assuming case of all non-escaped ASCII characters, as well as
1229 * potentially enough space for other cases (but not necessarily flushed)
1230 */
Tatu Saloranta75273be2012-11-11 15:42:06 -08001231 private final void _writeStringSegment(char[] cbuf, int offset, int len)
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001232 throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001233 {
1234 // note: caller MUST ensure (via flushing) there's room for ASCII only
1235
1236 // Fast+tight loop for ASCII-only, no-escaping-needed output
1237 len += offset; // becomes end marker, then
1238
1239 int outputPtr = _outputTail;
1240 final byte[] outputBuffer = _outputBuffer;
1241 final int[] escCodes = _outputEscapes;
1242
1243 while (offset < len) {
1244 int ch = cbuf[offset];
1245 // note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
1246 if (ch > 0x7F || escCodes[ch] != 0) {
1247 break;
1248 }
1249 outputBuffer[outputPtr++] = (byte) ch;
1250 ++offset;
1251 }
1252 _outputTail = outputPtr;
1253 if (offset < len) {
1254 // [JACKSON-106]
1255 if (_characterEscapes != null) {
1256 _writeCustomStringSegment2(cbuf, offset, len);
1257 // [JACKSON-102]
1258 } else if (_maximumNonEscapedChar == 0) {
1259 _writeStringSegment2(cbuf, offset, len);
1260 } else {
1261 _writeStringSegmentASCII2(cbuf, offset, len);
1262 }
1263
1264 }
1265 }
1266
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001267 private final void _writeStringSegment(String text, int offset, int len) throws IOException
1268 {
1269 // note: caller MUST ensure (via flushing) there's room for ASCII only
1270 // Fast+tight loop for ASCII-only, no-escaping-needed output
1271 len += offset; // becomes end marker, then
1272
1273 int outputPtr = _outputTail;
1274 final byte[] outputBuffer = _outputBuffer;
1275 final int[] escCodes = _outputEscapes;
1276
1277 while (offset < len) {
1278 int ch = text.charAt(offset);
1279 // note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
1280 if (ch > 0x7F || escCodes[ch] != 0) {
1281 break;
1282 }
1283 outputBuffer[outputPtr++] = (byte) ch;
1284 ++offset;
1285 }
1286 _outputTail = outputPtr;
1287 if (offset < len) {
1288 if (_characterEscapes != null) {
1289 _writeCustomStringSegment2(text, offset, len);
1290 } else if (_maximumNonEscapedChar == 0) {
1291 _writeStringSegment2(text, offset, len);
1292 } else {
1293 _writeStringSegmentASCII2(text, offset, len);
1294 }
1295 }
1296 }
1297
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001298 /**
1299 * Secondary method called when content contains characters to escape,
1300 * and/or multi-byte UTF-8 characters.
1301 */
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001302 private final void _writeStringSegment2(final char[] cbuf, int offset, final int end) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001303 {
1304 // Ok: caller guarantees buffer can have room; but that may require flushing:
1305 if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
1306 _flushBuffer();
1307 }
1308
1309 int outputPtr = _outputTail;
1310
1311 final byte[] outputBuffer = _outputBuffer;
1312 final int[] escCodes = _outputEscapes;
1313
1314 while (offset < end) {
1315 int ch = cbuf[offset++];
1316 if (ch <= 0x7F) {
1317 if (escCodes[ch] == 0) {
1318 outputBuffer[outputPtr++] = (byte) ch;
1319 continue;
1320 }
1321 int escape = escCodes[ch];
1322 if (escape > 0) { // 2-char escape, fine
1323 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1324 outputBuffer[outputPtr++] = (byte) escape;
1325 } else {
1326 // ctrl-char, 6-byte escape...
1327 outputPtr = _writeGenericEscape(ch, outputPtr);
1328 }
1329 continue;
1330 }
1331 if (ch <= 0x7FF) { // fine, just needs 2 byte output
1332 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1333 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1334 } else {
1335 outputPtr = _outputMultiByteChar(ch, outputPtr);
1336 }
1337 }
1338 _outputTail = outputPtr;
1339 }
1340
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001341 private final void _writeStringSegment2(final String text, int offset, final int end) throws IOException
1342 {
1343 if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
1344 _flushBuffer();
1345 }
1346
1347 int outputPtr = _outputTail;
1348
1349 final byte[] outputBuffer = _outputBuffer;
1350 final int[] escCodes = _outputEscapes;
1351
1352 while (offset < end) {
1353 int ch = text.charAt(offset++);
1354 if (ch <= 0x7F) {
1355 if (escCodes[ch] == 0) {
1356 outputBuffer[outputPtr++] = (byte) ch;
1357 continue;
1358 }
1359 int escape = escCodes[ch];
1360 if (escape > 0) { // 2-char escape, fine
1361 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1362 outputBuffer[outputPtr++] = (byte) escape;
1363 } else {
1364 // ctrl-char, 6-byte escape...
1365 outputPtr = _writeGenericEscape(ch, outputPtr);
1366 }
1367 continue;
1368 }
1369 if (ch <= 0x7FF) { // fine, just needs 2 byte output
1370 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1371 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1372 } else {
1373 outputPtr = _outputMultiByteChar(ch, outputPtr);
1374 }
1375 }
1376 _outputTail = outputPtr;
1377 }
1378
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001379 /*
1380 /**********************************************************
1381 /* Internal methods, low-level writing, text segment
1382 /* with additional escaping (ASCII or such)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001383 /**********************************************************
1384 */
1385
1386 /**
1387 * Same as <code>_writeStringSegment2(char[], ...)</code., but with
1388 * additional escaping for high-range code points
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001389 */
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001390 private final void _writeStringSegmentASCII2(final char[] cbuf, int offset, final int end) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001391 {
1392 // Ok: caller guarantees buffer can have room; but that may require flushing:
1393 if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
1394 _flushBuffer();
1395 }
1396
1397 int outputPtr = _outputTail;
1398
1399 final byte[] outputBuffer = _outputBuffer;
1400 final int[] escCodes = _outputEscapes;
1401 final int maxUnescaped = _maximumNonEscapedChar;
1402
1403 while (offset < end) {
1404 int ch = cbuf[offset++];
1405 if (ch <= 0x7F) {
1406 if (escCodes[ch] == 0) {
1407 outputBuffer[outputPtr++] = (byte) ch;
1408 continue;
1409 }
1410 int escape = escCodes[ch];
1411 if (escape > 0) { // 2-char escape, fine
1412 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1413 outputBuffer[outputPtr++] = (byte) escape;
1414 } else {
1415 // ctrl-char, 6-byte escape...
1416 outputPtr = _writeGenericEscape(ch, outputPtr);
1417 }
1418 continue;
1419 }
1420 if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1421 outputPtr = _writeGenericEscape(ch, outputPtr);
1422 continue;
1423 }
1424 if (ch <= 0x7FF) { // fine, just needs 2 byte output
1425 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1426 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1427 } else {
1428 outputPtr = _outputMultiByteChar(ch, outputPtr);
1429 }
1430 }
1431 _outputTail = outputPtr;
1432 }
1433
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001434 private final void _writeStringSegmentASCII2(final String text, int offset, final int end) throws IOException
1435 {
1436 // Ok: caller guarantees buffer can have room; but that may require flushing:
1437 if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
1438 _flushBuffer();
1439 }
1440
1441 int outputPtr = _outputTail;
1442
1443 final byte[] outputBuffer = _outputBuffer;
1444 final int[] escCodes = _outputEscapes;
1445 final int maxUnescaped = _maximumNonEscapedChar;
1446
1447 while (offset < end) {
1448 int ch = text.charAt(offset++);
1449 if (ch <= 0x7F) {
1450 if (escCodes[ch] == 0) {
1451 outputBuffer[outputPtr++] = (byte) ch;
1452 continue;
1453 }
1454 int escape = escCodes[ch];
1455 if (escape > 0) { // 2-char escape, fine
1456 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1457 outputBuffer[outputPtr++] = (byte) escape;
1458 } else {
1459 // ctrl-char, 6-byte escape...
1460 outputPtr = _writeGenericEscape(ch, outputPtr);
1461 }
1462 continue;
1463 }
1464 if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1465 outputPtr = _writeGenericEscape(ch, outputPtr);
1466 continue;
1467 }
1468 if (ch <= 0x7FF) { // fine, just needs 2 byte output
1469 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1470 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1471 } else {
1472 outputPtr = _outputMultiByteChar(ch, outputPtr);
1473 }
1474 }
1475 _outputTail = outputPtr;
1476 }
1477
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001478 /*
1479 /**********************************************************
1480 /* Internal methods, low-level writing, text segment
1481 /* with fully custom escaping (and possibly escaping of non-ASCII
1482 /**********************************************************
1483 */
1484
1485 /**
1486 * Same as <code>_writeStringSegmentASCII2(char[], ...)</code., but with
1487 * additional checking for completely custom escapes
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001488 */
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001489 private final void _writeCustomStringSegment2(final char[] cbuf, int offset, final int end) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001490 {
1491 // Ok: caller guarantees buffer can have room; but that may require flushing:
1492 if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
1493 _flushBuffer();
1494 }
1495 int outputPtr = _outputTail;
1496
1497 final byte[] outputBuffer = _outputBuffer;
1498 final int[] escCodes = _outputEscapes;
1499 // may or may not have this limit
1500 final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
1501 final CharacterEscapes customEscapes = _characterEscapes; // non-null
1502
1503 while (offset < end) {
1504 int ch = cbuf[offset++];
1505 if (ch <= 0x7F) {
1506 if (escCodes[ch] == 0) {
1507 outputBuffer[outputPtr++] = (byte) ch;
1508 continue;
1509 }
1510 int escape = escCodes[ch];
1511 if (escape > 0) { // 2-char escape, fine
1512 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1513 outputBuffer[outputPtr++] = (byte) escape;
1514 } else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
1515 SerializableString esc = customEscapes.getEscapeSequence(ch);
1516 if (esc == null) {
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001517 _reportError("Invalid custom escape definitions; custom escape not found for character code 0x"
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001518 +Integer.toHexString(ch)+", although was supposed to have one");
1519 }
1520 outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1521 } else {
1522 // ctrl-char, 6-byte escape...
1523 outputPtr = _writeGenericEscape(ch, outputPtr);
1524 }
1525 continue;
1526 }
1527 if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1528 outputPtr = _writeGenericEscape(ch, outputPtr);
1529 continue;
1530 }
1531 SerializableString esc = customEscapes.getEscapeSequence(ch);
1532 if (esc != null) {
1533 outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1534 continue;
1535 }
1536 if (ch <= 0x7FF) { // fine, just needs 2 byte output
1537 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1538 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1539 } else {
1540 outputPtr = _outputMultiByteChar(ch, outputPtr);
1541 }
1542 }
1543 _outputTail = outputPtr;
1544 }
1545
Tatu Salorantaad3a43e2015-03-30 19:29:05 -07001546 private final void _writeCustomStringSegment2(final String text, int offset, final int end) throws IOException
1547 {
1548 // Ok: caller guarantees buffer can have room; but that may require flushing:
1549 if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
1550 _flushBuffer();
1551 }
1552 int outputPtr = _outputTail;
1553
1554 final byte[] outputBuffer = _outputBuffer;
1555 final int[] escCodes = _outputEscapes;
1556 // may or may not have this limit
1557 final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
1558 final CharacterEscapes customEscapes = _characterEscapes; // non-null
1559
1560 while (offset < end) {
1561 int ch = text.charAt(offset++);
1562 if (ch <= 0x7F) {
1563 if (escCodes[ch] == 0) {
1564 outputBuffer[outputPtr++] = (byte) ch;
1565 continue;
1566 }
1567 int escape = escCodes[ch];
1568 if (escape > 0) { // 2-char escape, fine
1569 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1570 outputBuffer[outputPtr++] = (byte) escape;
1571 } else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
1572 SerializableString esc = customEscapes.getEscapeSequence(ch);
1573 if (esc == null) {
1574 _reportError("Invalid custom escape definitions; custom escape not found for character code 0x"
1575 +Integer.toHexString(ch)+", although was supposed to have one");
1576 }
1577 outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1578 } else {
1579 // ctrl-char, 6-byte escape...
1580 outputPtr = _writeGenericEscape(ch, outputPtr);
1581 }
1582 continue;
1583 }
1584 if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1585 outputPtr = _writeGenericEscape(ch, outputPtr);
1586 continue;
1587 }
1588 SerializableString esc = customEscapes.getEscapeSequence(ch);
1589 if (esc != null) {
1590 outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1591 continue;
1592 }
1593 if (ch <= 0x7FF) { // fine, just needs 2 byte output
1594 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1595 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1596 } else {
1597 outputPtr = _outputMultiByteChar(ch, outputPtr);
1598 }
1599 }
1600 _outputTail = outputPtr;
1601 }
1602
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001603 private final int _writeCustomEscape(byte[] outputBuffer, int outputPtr, SerializableString esc, int remainingChars)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001604 throws IOException, JsonGenerationException
1605 {
1606 byte[] raw = esc.asUnquotedUTF8(); // must be escaped at this point, shouldn't double-quote
1607 int len = raw.length;
1608 if (len > 6) { // may violate constraints we have, do offline
1609 return _handleLongCustomEscape(outputBuffer, outputPtr, _outputEnd, raw, remainingChars);
1610 }
1611 // otherwise will fit without issues, so:
1612 System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
1613 return (outputPtr + len);
1614 }
1615
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001616 private final int _handleLongCustomEscape(byte[] outputBuffer, int outputPtr, int outputEnd, byte[] raw,
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001617 int remainingChars)
1618 throws IOException, JsonGenerationException
1619 {
1620 int len = raw.length;
1621 if ((outputPtr + len) > outputEnd) {
1622 _outputTail = outputPtr;
1623 _flushBuffer();
1624 outputPtr = _outputTail;
1625 if (len > outputBuffer.length) { // very unlikely, but possible...
1626 _outputStream.write(raw, 0, len);
1627 return outputPtr;
1628 }
1629 System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
1630 outputPtr += len;
1631 }
1632 // but is the invariant still obeyed? If not, flush once more
1633 if ((outputPtr + 6 * remainingChars) > outputEnd) {
1634 _flushBuffer();
1635 return _outputTail;
1636 }
1637 return outputPtr;
1638 }
1639
1640 /*
1641 /**********************************************************
1642 /* Internal methods, low-level writing, "raw UTF-8" segments
1643 /**********************************************************
1644 */
1645
1646 /**
1647 * Method called when UTF-8 encoded (but NOT yet escaped!) content is not guaranteed
1648 * to fit in the output buffer after escaping; as such, we just need to
1649 * chunk writes.
1650 */
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001651 private final void _writeUTF8Segments(byte[] utf8, int offset, int totalLen)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001652 throws IOException, JsonGenerationException
1653 {
1654 do {
1655 int len = Math.min(_outputMaxContiguous, totalLen);
1656 _writeUTF8Segment(utf8, offset, len);
1657 offset += len;
1658 totalLen -= len;
1659 } while (totalLen > 0);
1660 }
1661
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001662 private final void _writeUTF8Segment(byte[] utf8, final int offset, final int len)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001663 throws IOException, JsonGenerationException
1664 {
1665 // fast loop to see if escaping is needed; don't copy, just look
1666 final int[] escCodes = _outputEscapes;
1667
1668 for (int ptr = offset, end = offset + len; ptr < end; ) {
1669 // 28-Feb-2011, tatu: escape codes just cover 7-bit range, so:
1670 int ch = utf8[ptr++];
1671 if ((ch >= 0) && escCodes[ch] != 0) {
1672 _writeUTF8Segment2(utf8, offset, len);
1673 return;
1674 }
1675 }
1676
1677 // yes, fine, just copy the sucker
1678 if ((_outputTail + len) > _outputEnd) { // enough room or need to flush?
1679 _flushBuffer(); // but yes once we flush (caller guarantees length restriction)
1680 }
1681 System.arraycopy(utf8, offset, _outputBuffer, _outputTail, len);
1682 _outputTail += len;
1683 }
1684
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001685 private final void _writeUTF8Segment2(final byte[] utf8, int offset, int len)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001686 throws IOException, JsonGenerationException
1687 {
1688 int outputPtr = _outputTail;
1689
1690 // Ok: caller guarantees buffer can have room; but that may require flushing:
1691 if ((outputPtr + (len * 6)) > _outputEnd) {
1692 _flushBuffer();
1693 outputPtr = _outputTail;
1694 }
1695
1696 final byte[] outputBuffer = _outputBuffer;
1697 final int[] escCodes = _outputEscapes;
1698 len += offset; // so 'len' becomes 'end'
1699
1700 while (offset < len) {
1701 byte b = utf8[offset++];
1702 int ch = b;
1703 if (ch < 0 || escCodes[ch] == 0) {
1704 outputBuffer[outputPtr++] = b;
1705 continue;
1706 }
1707 int escape = escCodes[ch];
1708 if (escape > 0) { // 2-char escape, fine
1709 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1710 outputBuffer[outputPtr++] = (byte) escape;
1711 } else {
1712 // ctrl-char, 6-byte escape...
1713 outputPtr = _writeGenericEscape(ch, outputPtr);
1714 }
1715 }
1716 _outputTail = outputPtr;
1717 }
1718
1719 /*
1720 /**********************************************************
1721 /* Internal methods, low-level writing, base64 encoded
1722 /**********************************************************
1723 */
1724
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001725 protected final void _writeBinary(Base64Variant b64variant,
Tatu Saloranta2e70a2a2012-06-04 22:21:45 -07001726 byte[] input, int inputPtr, final int inputEnd)
1727 throws IOException, JsonGenerationException
1728 {
1729 // Encoding is by chunks of 3 input, 4 output chars, so:
1730 int safeInputEnd = inputEnd - 3;
1731 // Let's also reserve room for possible (and quoted) lf char each round
1732 int safeOutputEnd = _outputEnd - 6;
1733 int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1734
1735 // Ok, first we loop through all full triplets of data:
1736 while (inputPtr <= safeInputEnd) {
1737 if (_outputTail > safeOutputEnd) { // need to flush
1738 _flushBuffer();
1739 }
1740 // First, mash 3 bytes into lsb of 32-bit int
1741 int b24 = ((int) input[inputPtr++]) << 8;
1742 b24 |= ((int) input[inputPtr++]) & 0xFF;
1743 b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
1744 _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1745 if (--chunksBeforeLF <= 0) {
1746 // note: must quote in JSON value
1747 _outputBuffer[_outputTail++] = '\\';
1748 _outputBuffer[_outputTail++] = 'n';
1749 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1750 }
1751 }
1752
1753 // And then we may have 1 or 2 leftover bytes to encode
1754 int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
1755 if (inputLeft > 0) { // yes, but do we have room for output?
1756 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1757 _flushBuffer();
1758 }
1759 int b24 = ((int) input[inputPtr++]) << 16;
1760 if (inputLeft == 2) {
1761 b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
1762 }
1763 _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
1764 }
1765 }
1766
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001767 // write-method called when length is definitely known
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001768 protected final int _writeBinary(Base64Variant b64variant,
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001769 InputStream data, byte[] readBuffer, int bytesLeft)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001770 throws IOException, JsonGenerationException
1771 {
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001772 int inputPtr = 0;
1773 int inputEnd = 0;
1774 int lastFullOffset = -3;
1775
Tatu Salorantad80a2d02012-06-11 20:37:35 -07001776 // Let's also reserve room for possible (and quoted) LF char each round
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001777 int safeOutputEnd = _outputEnd - 6;
1778 int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1779
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001780 while (bytesLeft > 2) { // main loop for full triplets
1781 if (inputPtr > lastFullOffset) {
1782 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1783 inputPtr = 0;
1784 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1785 break;
1786 }
1787 lastFullOffset = inputEnd-3;
1788 }
1789 if (_outputTail > safeOutputEnd) { // need to flush
1790 _flushBuffer();
1791 }
1792 int b24 = ((int) readBuffer[inputPtr++]) << 8;
1793 b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1794 b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1795 bytesLeft -= 3;
1796 _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1797 if (--chunksBeforeLF <= 0) {
1798 _outputBuffer[_outputTail++] = '\\';
1799 _outputBuffer[_outputTail++] = 'n';
1800 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1801 }
1802 }
1803
1804 // And then we may have 1 or 2 leftover bytes to encode
1805 if (bytesLeft > 0) {
1806 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1807 inputPtr = 0;
1808 if (inputEnd > 0) { // yes, but do we have room for output?
1809 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1810 _flushBuffer();
1811 }
1812 int b24 = ((int) readBuffer[inputPtr++]) << 16;
1813 int amount;
1814 if (inputPtr < inputEnd) {
1815 b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
1816 amount = 2;
1817 } else {
1818 amount = 1;
1819 }
1820 _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
1821 bytesLeft -= amount;
1822 }
1823 }
1824 return bytesLeft;
1825 }
1826
1827 // write method when length is unknown
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001828 protected final int _writeBinary(Base64Variant b64variant,
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001829 InputStream data, byte[] readBuffer)
1830 throws IOException, JsonGenerationException
1831 {
1832 int inputPtr = 0;
1833 int inputEnd = 0;
1834 int lastFullOffset = -3;
1835 int bytesDone = 0;
1836
1837 // Let's also reserve room for possible (and quoted) LF char each round
1838 int safeOutputEnd = _outputEnd - 6;
1839 int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1840
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001841 // Ok, first we loop through all full triplets of data:
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001842 while (true) {
1843 if (inputPtr > lastFullOffset) { // need to load more
1844 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length);
1845 inputPtr = 0;
1846 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1847 break;
1848 }
1849 lastFullOffset = inputEnd-3;
1850 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001851 if (_outputTail > safeOutputEnd) { // need to flush
1852 _flushBuffer();
1853 }
1854 // First, mash 3 bytes into lsb of 32-bit int
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001855 int b24 = ((int) readBuffer[inputPtr++]) << 8;
1856 b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1857 b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1858 bytesDone += 3;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001859 _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1860 if (--chunksBeforeLF <= 0) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001861 _outputBuffer[_outputTail++] = '\\';
1862 _outputBuffer[_outputTail++] = 'n';
1863 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1864 }
1865 }
1866
1867 // And then we may have 1 or 2 leftover bytes to encode
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001868 if (inputPtr < inputEnd) { // yes, but do we have room for output?
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001869 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1870 _flushBuffer();
1871 }
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001872 int b24 = ((int) readBuffer[inputPtr++]) << 16;
1873 int amount = 1;
1874 if (inputPtr < inputEnd) {
1875 b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
1876 amount = 2;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001877 }
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001878 bytesDone += amount;
1879 _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001880 }
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001881 return bytesDone;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001882 }
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001883
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001884 private final int _readMore(InputStream in,
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001885 byte[] readBuffer, int inputPtr, int inputEnd,
1886 int maxRead) throws IOException
1887 {
1888 // anything to shift to front?
1889 int i = 0;
1890 while (inputPtr < inputEnd) {
1891 readBuffer[i++] = readBuffer[inputPtr++];
1892 }
1893 inputPtr = 0;
1894 inputEnd = i;
1895 maxRead = Math.min(maxRead, readBuffer.length);
1896
1897 do {
Tatu Saloranta8c2747d2013-02-26 14:51:12 -08001898 int length = maxRead - inputEnd;
1899 if (length == 0) {
1900 break;
1901 }
1902 int count = in.read(readBuffer, inputEnd, length);
Tatu Saloranta63ff5742012-06-08 21:30:30 -07001903 if (count < 0) {
1904 return inputEnd;
1905 }
1906 inputEnd += count;
1907 } while (inputEnd < 3);
1908 return inputEnd;
1909 }
1910
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001911 /*
1912 /**********************************************************
1913 /* Internal methods, character escapes/encoding
1914 /**********************************************************
1915 */
1916
1917 /**
1918 * Method called to output a character that is beyond range of
1919 * 1- and 2-byte UTF-8 encodings, when outputting "raw"
1920 * text (meaning it is not to be escaped or quoted)
1921 */
Tatu Saloranta554f8db2016-08-18 23:09:52 -07001922 private final int _outputRawMultiByteChar(int ch, char[] cbuf, int inputOffset, int inputEnd)
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001923 throws IOException
1924 {
1925 // Let's handle surrogates gracefully (as 4 byte output):
1926 if (ch >= SURR1_FIRST) {
1927 if (ch <= SURR2_LAST) { // yes, outside of BMP
1928 // Do we have second part?
Tatu Saloranta554f8db2016-08-18 23:09:52 -07001929 if (inputOffset >= inputEnd || cbuf == null) { // nope... have to note down
1930 _reportError(String.format(
1931"Split surrogate on writeRaw() input (last character): first character 0x%4x", ch));
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001932 }
1933 _outputSurrogates(ch, cbuf[inputOffset]);
Tatu Saloranta554f8db2016-08-18 23:09:52 -07001934 return inputOffset+1;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001935 }
1936 }
1937 final byte[] bbuf = _outputBuffer;
1938 bbuf[_outputTail++] = (byte) (0xe0 | (ch >> 12));
1939 bbuf[_outputTail++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
1940 bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
1941 return inputOffset;
1942 }
1943
Tatu Salorantaec560f32015-11-23 22:50:22 -08001944 protected final void _outputSurrogates(int surr1, int surr2) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001945 {
1946 int c = _decodeSurrogate(surr1, surr2);
1947 if ((_outputTail + 4) > _outputEnd) {
1948 _flushBuffer();
1949 }
1950 final byte[] bbuf = _outputBuffer;
1951 bbuf[_outputTail++] = (byte) (0xf0 | (c >> 18));
1952 bbuf[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f));
1953 bbuf[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
1954 bbuf[_outputTail++] = (byte) (0x80 | (c & 0x3f));
1955 }
1956
1957 /**
1958 *
1959 * @param ch
1960 * @param outputPtr Position within output buffer to append multi-byte in
1961 *
1962 * @return New output position after appending
1963 *
1964 * @throws IOException
1965 */
Cowtowncoder864e30a2014-11-11 13:11:20 -08001966 private final int _outputMultiByteChar(int ch, int outputPtr) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001967 {
1968 byte[] bbuf = _outputBuffer;
1969 if (ch >= SURR1_FIRST && ch <= SURR2_LAST) { // yes, outside of BMP; add an escape
Tatu Salorantaec560f32015-11-23 22:50:22 -08001970 // 23-Nov-2015, tatu: As per [core#223], may or may not want escapes;
1971 // it would be added here... but as things are, we do not have proper
1972 // access yet...
1973// if (Feature.ESCAPE_UTF8_SURROGATES.enabledIn(_features)) {
1974 bbuf[outputPtr++] = BYTE_BACKSLASH;
1975 bbuf[outputPtr++] = BYTE_u;
1976
1977 bbuf[outputPtr++] = HEX_CHARS[(ch >> 12) & 0xF];
1978 bbuf[outputPtr++] = HEX_CHARS[(ch >> 8) & 0xF];
1979 bbuf[outputPtr++] = HEX_CHARS[(ch >> 4) & 0xF];
1980 bbuf[outputPtr++] = HEX_CHARS[ch & 0xF];
1981// } else { ... }
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001982 } else {
1983 bbuf[outputPtr++] = (byte) (0xe0 | (ch >> 12));
1984 bbuf[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
1985 bbuf[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1986 }
1987 return outputPtr;
1988 }
Tatu Salorantaec560f32015-11-23 22:50:22 -08001989
Tatu Saloranta5db03b62013-10-03 10:13:45 -07001990 private final void _writeNull() throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001991 {
1992 if ((_outputTail + 4) >= _outputEnd) {
1993 _flushBuffer();
1994 }
1995 System.arraycopy(NULL_BYTES, 0, _outputBuffer, _outputTail, 4);
1996 _outputTail += 4;
1997 }
1998
1999 /**
2000 * Method called to write a generic Unicode escape for given character.
2001 *
2002 * @param charToEscape Character to escape using escape sequence (\\uXXXX)
2003 */
Tatu Salorantaec560f32015-11-23 22:50:22 -08002004 private int _writeGenericEscape(int charToEscape, int outputPtr) throws IOException
Tatu Salorantaf15531c2011-12-22 23:00:40 -08002005 {
2006 final byte[] bbuf = _outputBuffer;
2007 bbuf[outputPtr++] = BYTE_BACKSLASH;
2008 bbuf[outputPtr++] = BYTE_u;
2009 if (charToEscape > 0xFF) {
2010 int hi = (charToEscape >> 8) & 0xFF;
2011 bbuf[outputPtr++] = HEX_CHARS[hi >> 4];
2012 bbuf[outputPtr++] = HEX_CHARS[hi & 0xF];
2013 charToEscape &= 0xFF;
2014 } else {
2015 bbuf[outputPtr++] = BYTE_0;
2016 bbuf[outputPtr++] = BYTE_0;
2017 }
2018 // We know it's a control char, so only the last 2 chars are non-0
2019 bbuf[outputPtr++] = HEX_CHARS[charToEscape >> 4];
2020 bbuf[outputPtr++] = HEX_CHARS[charToEscape & 0xF];
2021 return outputPtr;
2022 }
2023
2024 protected final void _flushBuffer() throws IOException
2025 {
2026 int len = _outputTail;
2027 if (len > 0) {
2028 _outputTail = 0;
2029 _outputStream.write(_outputBuffer, 0, len);
2030 }
2031 }
2032}