• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.core.json;
2 
3 import java.io.*;
4 import java.math.BigDecimal;
5 import java.math.BigInteger;
6 
7 import com.fasterxml.jackson.core.*;
8 import com.fasterxml.jackson.core.io.CharTypes;
9 import com.fasterxml.jackson.core.io.CharacterEscapes;
10 import com.fasterxml.jackson.core.io.IOContext;
11 import com.fasterxml.jackson.core.io.NumberOutput;
12 
13 /**
14  * {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer}
15  * which handles character encoding.
16  */
17 public class WriterBasedJsonGenerator
18     extends JsonGeneratorImpl
19 {
20     final protected static int SHORT_WRITE = 32;
21 
22     final protected static char[] HEX_CHARS = CharTypes.copyHexChars();
23 
24     /*
25     /**********************************************************
26     /* Configuration
27     /**********************************************************
28      */
29 
30     final protected Writer _writer;
31 
32     /**
33      * Character used for quoting JSON Object property names
34      * and String values.
35      */
36     protected char _quoteChar;
37 
38     /*
39     /**********************************************************
40     /* Output buffering
41     /**********************************************************
42      */
43 
44     /**
45      * Intermediate buffer in which contents are buffered before
46      * being written using {@link #_writer}.
47      */
48     protected char[] _outputBuffer;
49 
50     /**
51      * Pointer to the first buffered character to output
52      */
53     protected int _outputHead;
54 
55     /**
56      * Pointer to the position right beyond the last character to output
57      * (end marker; may point to position right beyond the end of the buffer)
58      */
59     protected int _outputTail;
60 
61     /**
62      * End marker of the output buffer; one past the last valid position
63      * within the buffer.
64      */
65     protected int _outputEnd;
66 
67     /**
68      * Short (14 char) temporary buffer allocated if needed, for constructing
69      * escape sequences
70      */
71     protected char[] _entityBuffer;
72 
73     /**
74      * When custom escapes are used, this member variable is used
75      * internally to hold a reference to currently used escape
76      */
77     protected SerializableString _currentEscape;
78 
79     /**
80      * Intermediate buffer in which characters of a String are copied
81      * before being encoded.
82      *
83      * @since 2.10
84      */
85     protected char[] _copyBuffer;
86 
87     /*
88     /**********************************************************
89     /* Life-cycle
90     /**********************************************************
91      */
92 
93     @Deprecated // since 2.10
WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec, Writer w)94     public WriterBasedJsonGenerator(IOContext ctxt, int features,
95             ObjectCodec codec, Writer w) {
96         this(ctxt, features, codec, w, JsonFactory.DEFAULT_QUOTE_CHAR);
97     }
98 
99     /**
100      * @since 2.10
101      */
WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec, Writer w, char quoteChar)102     public WriterBasedJsonGenerator(IOContext ctxt, int features,
103             ObjectCodec codec, Writer w,
104             char quoteChar)
105 
106     {
107         super(ctxt, features, codec);
108         _writer = w;
109         _outputBuffer = ctxt.allocConcatBuffer();
110         _outputEnd = _outputBuffer.length;
111         _quoteChar = quoteChar;
112         if (quoteChar != '"') { // since 2.10
113             _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar);
114         }
115     }
116 
117     /*
118     /**********************************************************
119     /* Overridden configuration, introspection methods
120     /**********************************************************
121      */
122 
123     @Override
getOutputTarget()124     public Object getOutputTarget() {
125         return _writer;
126     }
127 
128     @Override
getOutputBuffered()129     public int getOutputBuffered() {
130         // Assuming tail and head are kept but... trust and verify:
131         int len = _outputTail - _outputHead;
132         return Math.max(0, len);
133     }
134 
135     // json does allow this so
136     @Override
canWriteFormattedNumbers()137     public boolean canWriteFormattedNumbers() { return true; }
138 
139     /*
140     /**********************************************************
141     /* Overridden methods
142     /**********************************************************
143      */
144 
145     @Override
writeFieldName(String name)146     public void writeFieldName(String name)  throws IOException
147     {
148         int status = _writeContext.writeFieldName(name);
149         if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
150             _reportError("Can not write a field name, expecting a value");
151         }
152         _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
153     }
154 
155     @Override
writeFieldName(SerializableString name)156     public void writeFieldName(SerializableString name) throws IOException
157     {
158         // Object is a value, need to verify it's allowed
159         int status = _writeContext.writeFieldName(name.getValue());
160         if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
161             _reportError("Can not write a field name, expecting a value");
162         }
163         _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
164     }
165 
_writeFieldName(String name, boolean commaBefore)166     protected final void _writeFieldName(String name, boolean commaBefore) throws IOException
167     {
168         if (_cfgPrettyPrinter != null) {
169             _writePPFieldName(name, commaBefore);
170             return;
171         }
172         // for fast+std case, need to output up to 2 chars, comma, dquote
173         if ((_outputTail + 1) >= _outputEnd) {
174             _flushBuffer();
175         }
176         if (commaBefore) {
177             _outputBuffer[_outputTail++] = ',';
178         }
179         // Alternate mode, in which quoting of field names disabled?
180         if (_cfgUnqNames) {
181             _writeString(name);
182             return;
183         }
184         // we know there's room for at least one more char
185         _outputBuffer[_outputTail++] = _quoteChar;
186         // The beef:
187         _writeString(name);
188         // and closing quotes; need room for one more char:
189         if (_outputTail >= _outputEnd) {
190             _flushBuffer();
191         }
192         _outputBuffer[_outputTail++] = _quoteChar;
193     }
194 
_writeFieldName(SerializableString name, boolean commaBefore)195     protected final void _writeFieldName(SerializableString name, boolean commaBefore) throws IOException
196     {
197         if (_cfgPrettyPrinter != null) {
198             _writePPFieldName(name, commaBefore);
199             return;
200         }
201         // for fast+std case, need to output up to 2 chars, comma, dquote
202         if ((_outputTail + 1) >= _outputEnd) {
203             _flushBuffer();
204         }
205         if (commaBefore) {
206             _outputBuffer[_outputTail++] = ',';
207         }
208         // Alternate mode, in which quoting of field names disabled?
209         if (_cfgUnqNames) {
210             final char[] ch = name.asQuotedChars();
211             writeRaw(ch, 0, ch.length);
212             return;
213         }
214         // we know there's room for at least one more char
215         _outputBuffer[_outputTail++] = _quoteChar;
216         // The beef:
217 
218         int len = name.appendQuoted(_outputBuffer, _outputTail);
219         if (len < 0) {
220             _writeFieldNameTail(name);
221             return;
222         }
223         _outputTail += len;
224         if (_outputTail >= _outputEnd) {
225             _flushBuffer();
226         }
227         _outputBuffer[_outputTail++] = _quoteChar;
228     }
229 
_writeFieldNameTail(SerializableString name)230     private final void _writeFieldNameTail(SerializableString name) throws IOException
231     {
232         final char[] quoted = name.asQuotedChars();
233         writeRaw(quoted, 0, quoted.length);
234         if (_outputTail >= _outputEnd) {
235             _flushBuffer();
236         }
237         _outputBuffer[_outputTail++] = _quoteChar;
238     }
239 
240     /*
241     /**********************************************************
242     /* Output method implementations, structural
243     /**********************************************************
244      */
245 
246     @Override
writeStartArray()247     public void writeStartArray() throws IOException
248     {
249         _verifyValueWrite("start an array");
250         _writeContext = _writeContext.createChildArrayContext();
251         if (_cfgPrettyPrinter != null) {
252             _cfgPrettyPrinter.writeStartArray(this);
253         } else {
254             if (_outputTail >= _outputEnd) {
255                 _flushBuffer();
256             }
257             _outputBuffer[_outputTail++] = '[';
258         }
259     }
260 
261     @Override // since 2.12
writeStartArray(Object currentValue)262     public void writeStartArray(Object currentValue) throws IOException
263     {
264         _verifyValueWrite("start an array");
265         _writeContext = _writeContext.createChildArrayContext(currentValue);
266         if (_cfgPrettyPrinter != null) {
267             _cfgPrettyPrinter.writeStartArray(this);
268         } else {
269             if (_outputTail >= _outputEnd) {
270                 _flushBuffer();
271             }
272             _outputBuffer[_outputTail++] = '[';
273         }
274     }
275 
276     @Override // since 2.12
writeStartArray(Object currentValue, int size)277     public void writeStartArray(Object currentValue, int size) throws IOException
278     {
279         _verifyValueWrite("start an array");
280         _writeContext = _writeContext.createChildArrayContext(currentValue);
281         if (_cfgPrettyPrinter != null) {
282             _cfgPrettyPrinter.writeStartArray(this);
283         } else {
284             if (_outputTail >= _outputEnd) {
285                 _flushBuffer();
286             }
287             _outputBuffer[_outputTail++] = '[';
288         }
289     }
290 
291     @Override
writeEndArray()292     public void writeEndArray() throws IOException
293     {
294         if (!_writeContext.inArray()) {
295             _reportError("Current context not Array but "+_writeContext.typeDesc());
296         }
297         if (_cfgPrettyPrinter != null) {
298             _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
299         } else {
300             if (_outputTail >= _outputEnd) {
301                 _flushBuffer();
302             }
303             _outputBuffer[_outputTail++] = ']';
304         }
305         _writeContext = _writeContext.clearAndGetParent();
306     }
307 
308     @Override
writeStartObject()309     public void writeStartObject() throws IOException
310     {
311         _verifyValueWrite("start an object");
312         _writeContext = _writeContext.createChildObjectContext();
313         if (_cfgPrettyPrinter != null) {
314             _cfgPrettyPrinter.writeStartObject(this);
315         } else {
316             if (_outputTail >= _outputEnd) {
317                 _flushBuffer();
318             }
319             _outputBuffer[_outputTail++] = '{';
320         }
321     }
322 
323     @Override // since 2.8
writeStartObject(Object forValue)324     public void writeStartObject(Object forValue) throws IOException
325     {
326         _verifyValueWrite("start an object");
327         JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue);
328         _writeContext = ctxt;
329         if (_cfgPrettyPrinter != null) {
330             _cfgPrettyPrinter.writeStartObject(this);
331         } else {
332             if (_outputTail >= _outputEnd) {
333                 _flushBuffer();
334             }
335             _outputBuffer[_outputTail++] = '{';
336         }
337     }
338 
339     @Override
writeEndObject()340     public void writeEndObject() throws IOException
341     {
342         if (!_writeContext.inObject()) {
343             _reportError("Current context not Object but "+_writeContext.typeDesc());
344         }
345         if (_cfgPrettyPrinter != null) {
346             _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
347         } else {
348             if (_outputTail >= _outputEnd) {
349                 _flushBuffer();
350             }
351             _outputBuffer[_outputTail++] = '}';
352         }
353         _writeContext = _writeContext.clearAndGetParent();
354     }
355 
356     /**
357      * Specialized version of <code>_writeFieldName</code>, off-lined
358      * to keep the "fast path" as simple (and hopefully fast) as possible.
359      */
_writePPFieldName(String name, boolean commaBefore)360     protected final void _writePPFieldName(String name, boolean commaBefore) throws IOException
361     {
362         if (commaBefore) {
363             _cfgPrettyPrinter.writeObjectEntrySeparator(this);
364         } else {
365             _cfgPrettyPrinter.beforeObjectEntries(this);
366         }
367 
368         if (_cfgUnqNames) {// non-standard, omit quotes
369             _writeString(name);
370         } else {
371             if (_outputTail >= _outputEnd) {
372                 _flushBuffer();
373             }
374             _outputBuffer[_outputTail++] = _quoteChar;
375             _writeString(name);
376             if (_outputTail >= _outputEnd) {
377                 _flushBuffer();
378             }
379             _outputBuffer[_outputTail++] = _quoteChar;
380         }
381     }
382 
_writePPFieldName(SerializableString name, boolean commaBefore)383     protected final void _writePPFieldName(SerializableString name, boolean commaBefore) throws IOException
384     {
385         if (commaBefore) {
386             _cfgPrettyPrinter.writeObjectEntrySeparator(this);
387         } else {
388             _cfgPrettyPrinter.beforeObjectEntries(this);
389         }
390         final char[] quoted = name.asQuotedChars();
391         if (_cfgUnqNames) {// non-standard, omit quotes
392             writeRaw(quoted, 0, quoted.length);
393         } else {
394             if (_outputTail >= _outputEnd) {
395                 _flushBuffer();
396             }
397             _outputBuffer[_outputTail++] = _quoteChar;
398             writeRaw(quoted, 0, quoted.length);
399             if (_outputTail >= _outputEnd) {
400                 _flushBuffer();
401             }
402             _outputBuffer[_outputTail++] = _quoteChar;
403         }
404     }
405 
406     /*
407     /**********************************************************
408     /* Output method implementations, textual
409     /**********************************************************
410      */
411 
412     @Override
writeString(String text)413     public void writeString(String text) throws IOException
414     {
415         _verifyValueWrite(WRITE_STRING);
416         if (text == null) {
417             _writeNull();
418             return;
419         }
420         if (_outputTail >= _outputEnd) {
421             _flushBuffer();
422         }
423         _outputBuffer[_outputTail++] = _quoteChar;
424         _writeString(text);
425         // And finally, closing quotes
426         if (_outputTail >= _outputEnd) {
427             _flushBuffer();
428         }
429         _outputBuffer[_outputTail++] = _quoteChar;
430     }
431 
432     @Override
writeString(Reader reader, int len)433     public void writeString(Reader reader, int len) throws IOException {
434         _verifyValueWrite(WRITE_STRING);
435         if (reader == null) {
436             _reportError("null reader");
437         }
438         int toRead = (len >= 0) ? len : Integer.MAX_VALUE;
439         //Add leading quote
440         if ((_outputTail + len) >= _outputEnd) {
441             _flushBuffer();
442         }
443         _outputBuffer[_outputTail++] = _quoteChar;
444 
445         final char[] buf = _allocateCopyBuffer();
446         //read
447         while (toRead > 0) {
448             int toReadNow = Math.min(toRead, buf.length);
449             int numRead = reader.read(buf, 0, toReadNow);
450             if (numRead <= 0) {
451                 break;
452             }
453             if ((_outputTail + len) >= _outputEnd) {
454                 _flushBuffer();
455             }
456             _writeString(buf, 0, numRead);
457 
458             //decrease tracker
459             toRead -= numRead;
460         }
461         //Add trailing quote
462         if ((_outputTail + len) >= _outputEnd) {
463             _flushBuffer();
464         }
465         _outputBuffer[_outputTail++] = _quoteChar;
466 
467         if (toRead > 0 && len >= 0) {
468             _reportError("Didn't read enough from reader");
469         }
470     }
471 
472     @Override
writeString(char[] text, int offset, int len)473     public void writeString(char[] text, int offset, int len) throws IOException
474     {
475         _verifyValueWrite(WRITE_STRING);
476         if (_outputTail >= _outputEnd) {
477             _flushBuffer();
478         }
479         _outputBuffer[_outputTail++] = _quoteChar;
480         _writeString(text, offset, len);
481         // And finally, closing quotes
482         if (_outputTail >= _outputEnd) {
483             _flushBuffer();
484         }
485         _outputBuffer[_outputTail++] = _quoteChar;
486     }
487 
488     @Override
writeString(SerializableString sstr)489     public void writeString(SerializableString sstr) throws IOException
490     {
491         _verifyValueWrite(WRITE_STRING);
492         if (_outputTail >= _outputEnd) {
493             _flushBuffer();
494         }
495         _outputBuffer[_outputTail++] = _quoteChar;
496         int len = sstr.appendQuoted(_outputBuffer, _outputTail);
497         if (len < 0) {
498             _writeString2(sstr);
499             return;
500         }
501         _outputTail += len;
502         if (_outputTail >= _outputEnd) {
503             _flushBuffer();
504         }
505         _outputBuffer[_outputTail++] = _quoteChar;
506     }
507 
_writeString2(SerializableString sstr)508     private void _writeString2(SerializableString sstr) throws IOException
509     {
510         // Note: copied from writeRaw:
511         char[] text = sstr.asQuotedChars();
512         final int len = text.length;
513         if (len < SHORT_WRITE) {
514             int room = _outputEnd - _outputTail;
515             if (len > room) {
516                 _flushBuffer();
517             }
518             System.arraycopy(text, 0, _outputBuffer, _outputTail, len);
519             _outputTail += len;
520         } else {
521             _flushBuffer();
522             _writer.write(text, 0, len);
523         }
524         if (_outputTail >= _outputEnd) {
525             _flushBuffer();
526         }
527         _outputBuffer[_outputTail++] = _quoteChar;
528     }
529 
530     @Override
writeRawUTF8String(byte[] text, int offset, int length)531     public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException {
532         // could add support for buffering if we really want it...
533         _reportUnsupportedOperation();
534     }
535 
536     @Override
writeUTF8String(byte[] text, int offset, int length)537     public void writeUTF8String(byte[] text, int offset, int length) throws IOException {
538         // could add support for buffering if we really want it...
539         _reportUnsupportedOperation();
540     }
541 
542     /*
543     /**********************************************************
544     /* Output method implementations, unprocessed ("raw")
545     /**********************************************************
546      */
547 
548     @Override
writeRaw(String text)549     public void writeRaw(String text) throws IOException
550     {
551         // Nothing to check, can just output as is
552         int len = text.length();
553         int room = _outputEnd - _outputTail;
554 
555         if (room == 0) {
556             _flushBuffer();
557             room = _outputEnd - _outputTail;
558         }
559         // But would it nicely fit in? If yes, it's easy
560         if (room >= len) {
561             text.getChars(0, len, _outputBuffer, _outputTail);
562             _outputTail += len;
563         } else {
564             writeRawLong(text);
565         }
566     }
567 
568     @Override
writeRaw(String text, int start, int len)569     public void writeRaw(String text, int start, int len) throws IOException
570     {
571         // Nothing to check, can just output as is
572         int room = _outputEnd - _outputTail;
573 
574         if (room < len) {
575             _flushBuffer();
576             room = _outputEnd - _outputTail;
577         }
578         // But would it nicely fit in? If yes, it's easy
579         if (room >= len) {
580             text.getChars(start, start+len, _outputBuffer, _outputTail);
581             _outputTail += len;
582         } else {
583             writeRawLong(text.substring(start, start+len));
584         }
585     }
586 
587     // @since 2.1
588     @Override
writeRaw(SerializableString text)589     public void writeRaw(SerializableString text) throws IOException {
590         int len = text.appendUnquoted(_outputBuffer, _outputTail);
591         if (len < 0) {
592             writeRaw(text.getValue());
593             return;
594         }
595         _outputTail += len;
596     }
597 
598     @Override
writeRaw(char[] text, int offset, int len)599     public void writeRaw(char[] text, int offset, int len) throws IOException
600     {
601         // Only worth buffering if it's a short write?
602         if (len < SHORT_WRITE) {
603             int room = _outputEnd - _outputTail;
604             if (len > room) {
605                 _flushBuffer();
606             }
607             System.arraycopy(text, offset, _outputBuffer, _outputTail, len);
608             _outputTail += len;
609             return;
610         }
611         // Otherwise, better just pass through:
612         _flushBuffer();
613         _writer.write(text, offset, len);
614     }
615 
616     @Override
writeRaw(char c)617     public void writeRaw(char c) throws IOException
618     {
619         if (_outputTail >= _outputEnd) {
620             _flushBuffer();
621         }
622         _outputBuffer[_outputTail++] = c;
623     }
624 
writeRawLong(String text)625     private void writeRawLong(String text) throws IOException
626     {
627         int room = _outputEnd - _outputTail;
628         // If not, need to do it by looping
629         text.getChars(0, room, _outputBuffer, _outputTail);
630         _outputTail += room;
631         _flushBuffer();
632         int offset = room;
633         int len = text.length() - room;
634 
635         while (len > _outputEnd) {
636             int amount = _outputEnd;
637             text.getChars(offset, offset+amount, _outputBuffer, 0);
638             _outputHead = 0;
639             _outputTail = amount;
640             _flushBuffer();
641             offset += amount;
642             len -= amount;
643         }
644         // And last piece (at most length of buffer)
645         text.getChars(offset, offset+len, _outputBuffer, 0);
646         _outputHead = 0;
647         _outputTail = len;
648     }
649 
650     /*
651     /**********************************************************
652     /* Output method implementations, base64-encoded binary
653     /**********************************************************
654      */
655 
656     @Override
writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)657     public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
658         throws IOException, JsonGenerationException
659     {
660         _verifyValueWrite(WRITE_BINARY);
661         // Starting quotes
662         if (_outputTail >= _outputEnd) {
663             _flushBuffer();
664         }
665         _outputBuffer[_outputTail++] = _quoteChar;
666         _writeBinary(b64variant, data, offset, offset+len);
667         // and closing quotes
668         if (_outputTail >= _outputEnd) {
669             _flushBuffer();
670         }
671         _outputBuffer[_outputTail++] = _quoteChar;
672     }
673 
674     @Override
writeBinary(Base64Variant b64variant, InputStream data, int dataLength)675     public int writeBinary(Base64Variant b64variant,
676             InputStream data, int dataLength)
677         throws IOException, JsonGenerationException
678     {
679         _verifyValueWrite(WRITE_BINARY);
680         // Starting quotes
681         if (_outputTail >= _outputEnd) {
682             _flushBuffer();
683         }
684         _outputBuffer[_outputTail++] = _quoteChar;
685         byte[] encodingBuffer = _ioContext.allocBase64Buffer();
686         int bytes;
687         try {
688             if (dataLength < 0) { // length unknown
689                 bytes = _writeBinary(b64variant, data, encodingBuffer);
690             } else {
691                 int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength);
692                 if (missing > 0) {
693                     _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")");
694                 }
695                 bytes = dataLength;
696             }
697         } finally {
698             _ioContext.releaseBase64Buffer(encodingBuffer);
699         }
700         // and closing quotes
701         if (_outputTail >= _outputEnd) {
702             _flushBuffer();
703         }
704         _outputBuffer[_outputTail++] = _quoteChar;
705         return bytes;
706     }
707 
708     /*
709     /**********************************************************
710     /* Output method implementations, primitive
711     /**********************************************************
712      */
713 
714     @Override
writeNumber(short s)715     public void writeNumber(short s) throws IOException
716     {
717         _verifyValueWrite(WRITE_NUMBER);
718         if (_cfgNumbersAsStrings) {
719             _writeQuotedShort(s);
720             return;
721         }
722         // up to 5 digits and possible minus sign
723         if ((_outputTail + 6) >= _outputEnd) {
724             _flushBuffer();
725         }
726         _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
727     }
728 
_writeQuotedShort(short s)729     private void _writeQuotedShort(short s) throws IOException {
730         if ((_outputTail + 8) >= _outputEnd) {
731             _flushBuffer();
732         }
733         _outputBuffer[_outputTail++] = _quoteChar;
734         _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
735         _outputBuffer[_outputTail++] = _quoteChar;
736     }
737 
738     @Override
writeNumber(int i)739     public void writeNumber(int i) throws IOException
740     {
741         _verifyValueWrite(WRITE_NUMBER);
742         if (_cfgNumbersAsStrings) {
743             _writeQuotedInt(i);
744             return;
745         }
746         // up to 10 digits and possible minus sign
747         if ((_outputTail + 11) >= _outputEnd) {
748             _flushBuffer();
749         }
750         _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
751     }
752 
_writeQuotedInt(int i)753     private void _writeQuotedInt(int i) throws IOException {
754         if ((_outputTail + 13) >= _outputEnd) {
755             _flushBuffer();
756         }
757         _outputBuffer[_outputTail++] = _quoteChar;
758         _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
759         _outputBuffer[_outputTail++] = _quoteChar;
760     }
761 
762     @Override
writeNumber(long l)763     public void writeNumber(long l) throws IOException
764     {
765         _verifyValueWrite(WRITE_NUMBER);
766         if (_cfgNumbersAsStrings) {
767             _writeQuotedLong(l);
768             return;
769         }
770         if ((_outputTail + 21) >= _outputEnd) {
771             // up to 20 digits, minus sign
772             _flushBuffer();
773         }
774         _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
775     }
776 
_writeQuotedLong(long l)777     private void _writeQuotedLong(long l) throws IOException {
778         if ((_outputTail + 23) >= _outputEnd) {
779             _flushBuffer();
780         }
781         _outputBuffer[_outputTail++] = _quoteChar;
782         _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
783         _outputBuffer[_outputTail++] = _quoteChar;
784     }
785 
786     // !!! 05-Aug-2008, tatus: Any ways to optimize these?
787 
788     @Override
writeNumber(BigInteger value)789     public void writeNumber(BigInteger value) throws IOException
790     {
791         _verifyValueWrite(WRITE_NUMBER);
792         if (value == null) {
793             _writeNull();
794         } else if (_cfgNumbersAsStrings) {
795             _writeQuotedRaw(value.toString());
796         } else {
797             writeRaw(value.toString());
798         }
799     }
800 
801     @SuppressWarnings("deprecation")
802     @Override
writeNumber(double d)803     public void writeNumber(double d) throws IOException
804     {
805         if (_cfgNumbersAsStrings ||
806                 (NumberOutput.notFinite(d) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) {
807             writeString(String.valueOf(d));
808             return;
809         }
810         // What is the max length for doubles? 40 chars?
811         _verifyValueWrite(WRITE_NUMBER);
812         writeRaw(String.valueOf(d));
813     }
814 
815     @SuppressWarnings("deprecation")
816     @Override
writeNumber(float f)817     public void writeNumber(float f) throws IOException
818     {
819         if (_cfgNumbersAsStrings ||
820                 (NumberOutput.notFinite(f) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) {
821             writeString(String.valueOf(f));
822             return;
823         }
824         // What is the max length for floats?
825         _verifyValueWrite(WRITE_NUMBER);
826         writeRaw(String.valueOf(f));
827     }
828 
829     @Override
writeNumber(BigDecimal value)830     public void writeNumber(BigDecimal value) throws IOException
831     {
832         // Don't really know max length for big decimal, no point checking
833         _verifyValueWrite(WRITE_NUMBER);
834         if (value == null) {
835             _writeNull();
836         } else  if (_cfgNumbersAsStrings) {
837             _writeQuotedRaw(_asString(value));
838         } else {
839             writeRaw(_asString(value));
840         }
841     }
842 
843     @Override
writeNumber(String encodedValue)844     public void writeNumber(String encodedValue) throws IOException
845     {
846         _verifyValueWrite(WRITE_NUMBER);
847         if (_cfgNumbersAsStrings) {
848             _writeQuotedRaw(encodedValue);
849         } else {
850             writeRaw(encodedValue);
851         }
852     }
853 
854     @Override
writeNumber(char[] encodedValueBuffer, int offset, int length)855     public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException {
856         _verifyValueWrite(WRITE_NUMBER);
857         if (_cfgNumbersAsStrings) {
858             _writeQuotedRaw(encodedValueBuffer, offset, length);
859         } else {
860             writeRaw(encodedValueBuffer, offset, length);
861         }
862     }
863 
_writeQuotedRaw(String value)864     private void _writeQuotedRaw(String value) throws IOException
865     {
866         if (_outputTail >= _outputEnd) {
867             _flushBuffer();
868         }
869         _outputBuffer[_outputTail++] = _quoteChar;
870         writeRaw(value);
871         if (_outputTail >= _outputEnd) {
872             _flushBuffer();
873         }
874         _outputBuffer[_outputTail++] = _quoteChar;
875     }
876 
_writeQuotedRaw(char[] text, int offset, int length)877     private void _writeQuotedRaw(char[] text, int offset, int length) throws IOException
878     {
879         if (_outputTail >= _outputEnd) {
880             _flushBuffer();
881         }
882         _outputBuffer[_outputTail++] = _quoteChar;
883         writeRaw(text, offset, length);
884         if (_outputTail >= _outputEnd) {
885             _flushBuffer();
886         }
887         _outputBuffer[_outputTail++] = _quoteChar;
888     }
889 
890     @Override
writeBoolean(boolean state)891     public void writeBoolean(boolean state) throws IOException
892     {
893         _verifyValueWrite(WRITE_BOOLEAN);
894         if ((_outputTail + 5) >= _outputEnd) {
895             _flushBuffer();
896         }
897         int ptr = _outputTail;
898         char[] buf = _outputBuffer;
899         if (state) {
900             buf[ptr] = 't';
901             buf[++ptr] = 'r';
902             buf[++ptr] = 'u';
903             buf[++ptr] = 'e';
904         } else {
905             buf[ptr] = 'f';
906             buf[++ptr] = 'a';
907             buf[++ptr] = 'l';
908             buf[++ptr] = 's';
909             buf[++ptr] = 'e';
910         }
911         _outputTail = ptr+1;
912     }
913 
914     @Override
writeNull()915     public void writeNull() throws IOException {
916         _verifyValueWrite(WRITE_NULL);
917         _writeNull();
918     }
919 
920     /*
921     /**********************************************************
922     /* Implementations for other methods
923     /**********************************************************
924      */
925 
926     @Override
_verifyValueWrite(String typeMsg)927     protected final void _verifyValueWrite(String typeMsg) throws IOException
928     {
929         final int status = _writeContext.writeValue();
930         if (_cfgPrettyPrinter != null) {
931             // Otherwise, pretty printer knows what to do...
932             _verifyPrettyValueWrite(typeMsg, status);
933             return;
934         }
935         char c;
936         switch (status) {
937         case JsonWriteContext.STATUS_OK_AS_IS:
938         default:
939             return;
940         case JsonWriteContext.STATUS_OK_AFTER_COMMA:
941             c = ',';
942             break;
943         case JsonWriteContext.STATUS_OK_AFTER_COLON:
944             c = ':';
945             break;
946         case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
947             if (_rootValueSeparator != null) {
948                 writeRaw(_rootValueSeparator.getValue());
949             }
950             return;
951         case JsonWriteContext.STATUS_EXPECT_NAME:
952             _reportCantWriteValueExpectName(typeMsg);
953             return;
954         }
955         if (_outputTail >= _outputEnd) {
956             _flushBuffer();
957         }
958         _outputBuffer[_outputTail++] = c;
959     }
960 
961     /*
962     /**********************************************************
963     /* Low-level output handling
964     /**********************************************************
965      */
966 
967     @Override
flush()968     public void flush() throws IOException
969     {
970         _flushBuffer();
971         if (_writer != null) {
972             if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
973                 _writer.flush();
974             }
975         }
976     }
977 
978     @Override
close()979     public void close() throws IOException
980     {
981         super.close();
982 
983         /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
984          *   scopes.
985          */
986         // First: let's see that we still have buffers...
987         if (_outputBuffer != null
988             && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
989             while (true) {
990                 JsonStreamContext ctxt = getOutputContext();
991                 if (ctxt.inArray()) {
992                     writeEndArray();
993                 } else if (ctxt.inObject()) {
994                     writeEndObject();
995                 } else {
996                     break;
997                 }
998             }
999         }
1000         _flushBuffer();
1001         _outputHead = 0;
1002         _outputTail = 0;
1003 
1004         /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
1005          *   on the underlying Reader, unless we "own" it, or auto-closing
1006          *   feature is enabled.
1007          *   One downside: when using UTF8Writer, underlying buffer(s)
1008          *   may not be properly recycled if we don't close the writer.
1009          */
1010         if (_writer != null) {
1011             if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
1012                 _writer.close();
1013             } else  if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1014                 // If we can't close it, we should at least flush
1015                 _writer.flush();
1016             }
1017         }
1018         // Internal buffer(s) generator has can now be released as well
1019         _releaseBuffers();
1020     }
1021 
1022     @Override
_releaseBuffers()1023     protected void _releaseBuffers()
1024     {
1025         char[] buf = _outputBuffer;
1026         if (buf != null) {
1027             _outputBuffer = null;
1028             _ioContext.releaseConcatBuffer(buf);
1029         }
1030         buf = _copyBuffer;
1031         if (buf != null) {
1032             _copyBuffer = null;
1033             _ioContext.releaseNameCopyBuffer(buf);
1034         }
1035     }
1036 
1037     /*
1038     /**********************************************************
1039     /* Internal methods, low-level writing; text, default
1040     /**********************************************************
1041      */
1042 
_writeString(String text)1043     private void _writeString(String text) throws IOException
1044     {
1045         /* One check first: if String won't fit in the buffer, let's
1046          * segment writes. No point in extending buffer to huge sizes
1047          * (like if someone wants to include multi-megabyte base64
1048          * encoded stuff or such)
1049          */
1050         final int len = text.length();
1051         if (len > _outputEnd) { // Let's reserve space for entity at begin/end
1052             _writeLongString(text);
1053             return;
1054         }
1055 
1056         // Ok: we know String will fit in buffer ok
1057         // But do we need to flush first?
1058         if ((_outputTail + len) > _outputEnd) {
1059             _flushBuffer();
1060         }
1061         text.getChars(0, len, _outputBuffer, _outputTail);
1062 
1063         if (_characterEscapes != null) {
1064             _writeStringCustom(len);
1065         } else if (_maximumNonEscapedChar != 0) {
1066             _writeStringASCII(len, _maximumNonEscapedChar);
1067         } else {
1068             _writeString2(len);
1069         }
1070     }
1071 
_writeString2(final int len)1072     private void _writeString2(final int len) throws IOException
1073     {
1074         // And then we'll need to verify need for escaping etc:
1075         final int end = _outputTail + len;
1076         final int[] escCodes = _outputEscapes;
1077         final int escLen = escCodes.length;
1078 
1079         output_loop:
1080         while (_outputTail < end) {
1081             // Fast loop for chars not needing escaping
1082             escape_loop:
1083             while (true) {
1084                 char c = _outputBuffer[_outputTail];
1085                 if (c < escLen && escCodes[c] != 0) {
1086                     break escape_loop;
1087                 }
1088                 if (++_outputTail >= end) {
1089                     break output_loop;
1090                 }
1091             }
1092 
1093             // Ok, bumped into something that needs escaping.
1094             /* First things first: need to flush the buffer.
1095              * Inlined, as we don't want to lose tail pointer
1096              */
1097             int flushLen = (_outputTail - _outputHead);
1098             if (flushLen > 0) {
1099                 _writer.write(_outputBuffer, _outputHead, flushLen);
1100             }
1101             /* In any case, tail will be the new start, so hopefully
1102              * we have room now.
1103              */
1104             char c = _outputBuffer[_outputTail++];
1105             _prependOrWriteCharacterEscape(c, escCodes[c]);
1106         }
1107     }
1108 
1109     /**
1110      * Method called to write "long strings", strings whose length exceeds
1111      * output buffer length.
1112      */
_writeLongString(String text)1113     private void _writeLongString(String text) throws IOException
1114     {
1115         // First things first: let's flush the buffer to get some more room
1116         _flushBuffer();
1117 
1118         // Then we can write
1119         final int textLen = text.length();
1120         int offset = 0;
1121         do {
1122             int max = _outputEnd;
1123             int segmentLen = ((offset + max) > textLen)
1124                 ? (textLen - offset) : max;
1125             text.getChars(offset, offset+segmentLen, _outputBuffer, 0);
1126             if (_characterEscapes != null) {
1127                 _writeSegmentCustom(segmentLen);
1128             } else if (_maximumNonEscapedChar != 0) {
1129                 _writeSegmentASCII(segmentLen, _maximumNonEscapedChar);
1130             } else {
1131                 _writeSegment(segmentLen);
1132             }
1133             offset += segmentLen;
1134         } while (offset < textLen);
1135     }
1136 
1137     /**
1138      * Method called to output textual context which has been copied
1139      * to the output buffer prior to call. If any escaping is needed,
1140      * it will also be handled by the method.
1141      *<p>
1142      * Note: when called, textual content to write is within output
1143      * buffer, right after buffered content (if any). That's why only
1144      * length of that text is passed, as buffer and offset are implied.
1145      */
_writeSegment(int end)1146     private void _writeSegment(int end) throws IOException
1147     {
1148         final int[] escCodes = _outputEscapes;
1149         final int escLen = escCodes.length;
1150 
1151         int ptr = 0;
1152         int start = ptr;
1153 
1154         output_loop:
1155         while (ptr < end) {
1156             // Fast loop for chars not needing escaping
1157             char c;
1158             while (true) {
1159                 c = _outputBuffer[ptr];
1160                 if (c < escLen && escCodes[c] != 0) {
1161                     break;
1162                 }
1163                 if (++ptr >= end) {
1164                     break;
1165                 }
1166             }
1167 
1168             // Ok, bumped into something that needs escaping.
1169             /* First things first: need to flush the buffer.
1170              * Inlined, as we don't want to lose tail pointer
1171              */
1172             int flushLen = (ptr - start);
1173             if (flushLen > 0) {
1174                 _writer.write(_outputBuffer, start, flushLen);
1175                 if (ptr >= end) {
1176                     break output_loop;
1177                 }
1178             }
1179             ++ptr;
1180             // So; either try to prepend (most likely), or write directly:
1181             start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]);
1182         }
1183     }
1184 
1185     /**
1186      * This method called when the string content is already in
1187      * a char buffer, and need not be copied for processing.
1188      */
_writeString(char[] text, int offset, int len)1189     private void _writeString(char[] text, int offset, int len) throws IOException
1190     {
1191         if (_characterEscapes != null) {
1192             _writeStringCustom(text, offset, len);
1193             return;
1194         }
1195         if (_maximumNonEscapedChar != 0) {
1196             _writeStringASCII(text, offset, len, _maximumNonEscapedChar);
1197             return;
1198         }
1199 
1200         // Let's just find longest spans of non-escapable content, and for
1201         // each see if it makes sense to copy them, or write through
1202 
1203         len += offset; // -> len marks the end from now on
1204         final int[] escCodes = _outputEscapes;
1205         final int escLen = escCodes.length;
1206         while (offset < len) {
1207             int start = offset;
1208 
1209             while (true) {
1210                 char c = text[offset];
1211                 if (c < escLen && escCodes[c] != 0) {
1212                     break;
1213                 }
1214                 if (++offset >= len) {
1215                     break;
1216                 }
1217             }
1218 
1219             // Short span? Better just copy it to buffer first:
1220             int newAmount = offset - start;
1221             if (newAmount < SHORT_WRITE) {
1222                 // Note: let's reserve room for escaped char (up to 6 chars)
1223                 if ((_outputTail + newAmount) > _outputEnd) {
1224                     _flushBuffer();
1225                 }
1226                 if (newAmount > 0) {
1227                     System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
1228                     _outputTail += newAmount;
1229                 }
1230             } else { // Nope: better just write through
1231                 _flushBuffer();
1232                 _writer.write(text, start, newAmount);
1233             }
1234             // Was this the end?
1235             if (offset >= len) { // yup
1236                 break;
1237             }
1238             // Nope, need to escape the char.
1239             char c = text[offset++];
1240             _appendCharacterEscape(c, escCodes[c]);
1241         }
1242     }
1243 
1244     /*
1245     /**********************************************************
1246     /* Internal methods, low-level writing, text segment
1247     /* with additional escaping (ASCII or such)
1248     /**********************************************************
1249      */
1250 
1251     /* Same as "_writeString2()", except needs additional escaping
1252      * for subset of characters
1253      */
_writeStringASCII(final int len, final int maxNonEscaped)1254     private void _writeStringASCII(final int len, final int maxNonEscaped)
1255         throws IOException, JsonGenerationException
1256     {
1257         // And then we'll need to verify need for escaping etc:
1258         int end = _outputTail + len;
1259         final int[] escCodes = _outputEscapes;
1260         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1261         int escCode = 0;
1262 
1263         output_loop:
1264         while (_outputTail < end) {
1265             char c;
1266             // Fast loop for chars not needing escaping
1267             escape_loop:
1268             while (true) {
1269                 c = _outputBuffer[_outputTail];
1270                 if (c < escLimit) {
1271                     escCode = escCodes[c];
1272                     if (escCode != 0) {
1273                         break escape_loop;
1274                     }
1275                 } else if (c > maxNonEscaped) {
1276                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1277                     break escape_loop;
1278                 }
1279                 if (++_outputTail >= end) {
1280                     break output_loop;
1281                 }
1282             }
1283             int flushLen = (_outputTail - _outputHead);
1284             if (flushLen > 0) {
1285                 _writer.write(_outputBuffer, _outputHead, flushLen);
1286             }
1287             ++_outputTail;
1288             _prependOrWriteCharacterEscape(c, escCode);
1289         }
1290     }
1291 
_writeSegmentASCII(int end, final int maxNonEscaped)1292     private void _writeSegmentASCII(int end, final int maxNonEscaped)
1293         throws IOException, JsonGenerationException
1294     {
1295         final int[] escCodes = _outputEscapes;
1296         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1297 
1298         int ptr = 0;
1299         int escCode = 0;
1300         int start = ptr;
1301 
1302         output_loop:
1303         while (ptr < end) {
1304             // Fast loop for chars not needing escaping
1305             char c;
1306             while (true) {
1307                 c = _outputBuffer[ptr];
1308                 if (c < escLimit) {
1309                     escCode = escCodes[c];
1310                     if (escCode != 0) {
1311                         break;
1312                     }
1313                 } else if (c > maxNonEscaped) {
1314                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1315                     break;
1316                 }
1317                 if (++ptr >= end) {
1318                     break;
1319                 }
1320             }
1321             int flushLen = (ptr - start);
1322             if (flushLen > 0) {
1323                 _writer.write(_outputBuffer, start, flushLen);
1324                 if (ptr >= end) {
1325                     break output_loop;
1326                 }
1327             }
1328             ++ptr;
1329             start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
1330         }
1331     }
1332 
_writeStringASCII(char[] text, int offset, int len, final int maxNonEscaped)1333     private void _writeStringASCII(char[] text, int offset, int len,
1334             final int maxNonEscaped)
1335         throws IOException, JsonGenerationException
1336     {
1337         len += offset; // -> len marks the end from now on
1338         final int[] escCodes = _outputEscapes;
1339         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1340 
1341         int escCode = 0;
1342 
1343         while (offset < len) {
1344             int start = offset;
1345             char c;
1346 
1347             while (true) {
1348                 c = text[offset];
1349                 if (c < escLimit) {
1350                     escCode = escCodes[c];
1351                     if (escCode != 0) {
1352                         break;
1353                     }
1354                 } else if (c > maxNonEscaped) {
1355                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1356                     break;
1357                 }
1358                 if (++offset >= len) {
1359                     break;
1360                 }
1361             }
1362 
1363             // Short span? Better just copy it to buffer first:
1364             int newAmount = offset - start;
1365             if (newAmount < SHORT_WRITE) {
1366                 // Note: let's reserve room for escaped char (up to 6 chars)
1367                 if ((_outputTail + newAmount) > _outputEnd) {
1368                     _flushBuffer();
1369                 }
1370                 if (newAmount > 0) {
1371                     System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
1372                     _outputTail += newAmount;
1373                 }
1374             } else { // Nope: better just write through
1375                 _flushBuffer();
1376                 _writer.write(text, start, newAmount);
1377             }
1378             // Was this the end?
1379             if (offset >= len) { // yup
1380                 break;
1381             }
1382             // Nope, need to escape the char.
1383             ++offset;
1384             _appendCharacterEscape(c, escCode);
1385         }
1386     }
1387 
1388     /*
1389     /**********************************************************
1390     /* Internal methods, low-level writing, text segment
1391     /* with custom escaping (possibly coupling with ASCII limits)
1392     /**********************************************************
1393      */
1394 
1395     /* Same as "_writeString2()", except needs additional escaping
1396      * for subset of characters
1397      */
_writeStringCustom(final int len)1398     private void _writeStringCustom(final int len)
1399         throws IOException, JsonGenerationException
1400     {
1401         // And then we'll need to verify need for escaping etc:
1402         int end = _outputTail + len;
1403         final int[] escCodes = _outputEscapes;
1404         final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
1405         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1406         int escCode = 0;
1407         final CharacterEscapes customEscapes = _characterEscapes;
1408 
1409         output_loop:
1410         while (_outputTail < end) {
1411             char c;
1412             // Fast loop for chars not needing escaping
1413             escape_loop:
1414             while (true) {
1415                 c = _outputBuffer[_outputTail];
1416                 if (c < escLimit) {
1417                     escCode = escCodes[c];
1418                     if (escCode != 0) {
1419                         break escape_loop;
1420                     }
1421                 } else if (c > maxNonEscaped) {
1422                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1423                     break escape_loop;
1424                 } else {
1425                     if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
1426                         escCode = CharacterEscapes.ESCAPE_CUSTOM;
1427                         break escape_loop;
1428                     }
1429                 }
1430                 if (++_outputTail >= end) {
1431                     break output_loop;
1432                 }
1433             }
1434             int flushLen = (_outputTail - _outputHead);
1435             if (flushLen > 0) {
1436                 _writer.write(_outputBuffer, _outputHead, flushLen);
1437             }
1438             ++_outputTail;
1439             _prependOrWriteCharacterEscape(c, escCode);
1440         }
1441     }
1442 
_writeSegmentCustom(int end)1443     private void _writeSegmentCustom(int end)
1444         throws IOException, JsonGenerationException
1445     {
1446         final int[] escCodes = _outputEscapes;
1447         final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
1448         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1449         final CharacterEscapes customEscapes = _characterEscapes;
1450 
1451         int ptr = 0;
1452         int escCode = 0;
1453         int start = ptr;
1454 
1455         output_loop:
1456         while (ptr < end) {
1457             // Fast loop for chars not needing escaping
1458             char c;
1459             while (true) {
1460                 c = _outputBuffer[ptr];
1461                 if (c < escLimit) {
1462                     escCode = escCodes[c];
1463                     if (escCode != 0) {
1464                         break;
1465                     }
1466                 } else if (c > maxNonEscaped) {
1467                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1468                     break;
1469                 } else {
1470                     if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
1471                         escCode = CharacterEscapes.ESCAPE_CUSTOM;
1472                         break;
1473                     }
1474                 }
1475                 if (++ptr >= end) {
1476                     break;
1477                 }
1478             }
1479             int flushLen = (ptr - start);
1480             if (flushLen > 0) {
1481                 _writer.write(_outputBuffer, start, flushLen);
1482                 if (ptr >= end) {
1483                     break output_loop;
1484                 }
1485             }
1486             ++ptr;
1487             start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
1488         }
1489     }
1490 
_writeStringCustom(char[] text, int offset, int len)1491     private void _writeStringCustom(char[] text, int offset, int len)
1492         throws IOException, JsonGenerationException
1493     {
1494         len += offset; // -> len marks the end from now on
1495         final int[] escCodes = _outputEscapes;
1496         final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
1497         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1498         final CharacterEscapes customEscapes = _characterEscapes;
1499 
1500         int escCode = 0;
1501 
1502         while (offset < len) {
1503             int start = offset;
1504             char c;
1505 
1506             while (true) {
1507                 c = text[offset];
1508                 if (c < escLimit) {
1509                     escCode = escCodes[c];
1510                     if (escCode != 0) {
1511                         break;
1512                     }
1513                 } else if (c > maxNonEscaped) {
1514                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1515                     break;
1516                 } else {
1517                     if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
1518                         escCode = CharacterEscapes.ESCAPE_CUSTOM;
1519                         break;
1520                     }
1521                 }
1522                 if (++offset >= len) {
1523                     break;
1524                 }
1525             }
1526 
1527             // Short span? Better just copy it to buffer first:
1528             int newAmount = offset - start;
1529             if (newAmount < SHORT_WRITE) {
1530                 // Note: let's reserve room for escaped char (up to 6 chars)
1531                 if ((_outputTail + newAmount) > _outputEnd) {
1532                     _flushBuffer();
1533                 }
1534                 if (newAmount > 0) {
1535                     System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
1536                     _outputTail += newAmount;
1537                 }
1538             } else { // Nope: better just write through
1539                 _flushBuffer();
1540                 _writer.write(text, start, newAmount);
1541             }
1542             // Was this the end?
1543             if (offset >= len) { // yup
1544                 break;
1545             }
1546             // Nope, need to escape the char.
1547             ++offset;
1548             _appendCharacterEscape(c, escCode);
1549         }
1550     }
1551 
1552     /*
1553     /**********************************************************
1554     /* Internal methods, low-level writing; binary
1555     /**********************************************************
1556      */
1557 
_writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)1558     protected final void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)
1559         throws IOException, JsonGenerationException
1560     {
1561         // Encoding is by chunks of 3 input, 4 output chars, so:
1562         int safeInputEnd = inputEnd - 3;
1563         // Let's also reserve room for possible (and quoted) lf char each round
1564         int safeOutputEnd = _outputEnd - 6;
1565         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1566 
1567         // Ok, first we loop through all full triplets of data:
1568         while (inputPtr <= safeInputEnd) {
1569             if (_outputTail > safeOutputEnd) { // need to flush
1570                 _flushBuffer();
1571             }
1572             // First, mash 3 bytes into lsb of 32-bit int
1573             int b24 = ((int) input[inputPtr++]) << 8;
1574             b24 |= ((int) input[inputPtr++]) & 0xFF;
1575             b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
1576             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1577             if (--chunksBeforeLF <= 0) {
1578                 // note: must quote in JSON value
1579                 _outputBuffer[_outputTail++] = '\\';
1580                 _outputBuffer[_outputTail++] = 'n';
1581                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1582             }
1583         }
1584 
1585         // And then we may have 1 or 2 leftover bytes to encode
1586         int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
1587         if (inputLeft > 0) { // yes, but do we have room for output?
1588             if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1589                 _flushBuffer();
1590             }
1591             int b24 = ((int) input[inputPtr++]) << 16;
1592             if (inputLeft == 2) {
1593                 b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
1594             }
1595             _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
1596         }
1597     }
1598 
1599     // write-method called when length is definitely known
_writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer, int bytesLeft)1600     protected final int _writeBinary(Base64Variant b64variant,
1601             InputStream data, byte[] readBuffer, int bytesLeft)
1602         throws IOException, JsonGenerationException
1603     {
1604         int inputPtr = 0;
1605         int inputEnd = 0;
1606         int lastFullOffset = -3;
1607 
1608         // Let's also reserve room for possible (and quoted) lf char each round
1609         int safeOutputEnd = _outputEnd - 6;
1610         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1611 
1612         while (bytesLeft > 2) { // main loop for full triplets
1613             if (inputPtr > lastFullOffset) {
1614                 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1615                 inputPtr = 0;
1616                 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1617                     break;
1618                 }
1619                 lastFullOffset = inputEnd-3;
1620             }
1621             if (_outputTail > safeOutputEnd) { // need to flush
1622                 _flushBuffer();
1623             }
1624             int b24 = ((int) readBuffer[inputPtr++]) << 8;
1625             b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1626             b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1627             bytesLeft -= 3;
1628             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1629             if (--chunksBeforeLF <= 0) {
1630                 _outputBuffer[_outputTail++] = '\\';
1631                 _outputBuffer[_outputTail++] = 'n';
1632                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1633             }
1634         }
1635 
1636         // And then we may have 1 or 2 leftover bytes to encode
1637         if (bytesLeft > 0) {
1638             inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1639             inputPtr = 0;
1640             if (inputEnd > 0) { // yes, but do we have room for output?
1641                 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1642                     _flushBuffer();
1643                 }
1644                 int b24 = ((int) readBuffer[inputPtr++]) << 16;
1645                 int amount;
1646                 if (inputPtr < inputEnd) {
1647                     b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
1648                     amount = 2;
1649                 } else {
1650                     amount = 1;
1651                 }
1652                 _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
1653                 bytesLeft -= amount;
1654             }
1655         }
1656         return bytesLeft;
1657     }
1658 
1659     // write method when length is unknown
_writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer)1660     protected final int _writeBinary(Base64Variant b64variant,
1661             InputStream data, byte[] readBuffer)
1662         throws IOException, JsonGenerationException
1663     {
1664         int inputPtr = 0;
1665         int inputEnd = 0;
1666         int lastFullOffset = -3;
1667         int bytesDone = 0;
1668 
1669         // Let's also reserve room for possible (and quoted) LF char each round
1670         int safeOutputEnd = _outputEnd - 6;
1671         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1672 
1673         // Ok, first we loop through all full triplets of data:
1674         while (true) {
1675             if (inputPtr > lastFullOffset) { // need to load more
1676                 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length);
1677                 inputPtr = 0;
1678                 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1679                     break;
1680                 }
1681                 lastFullOffset = inputEnd-3;
1682             }
1683             if (_outputTail > safeOutputEnd) { // need to flush
1684                 _flushBuffer();
1685             }
1686             // First, mash 3 bytes into lsb of 32-bit int
1687             int b24 = ((int) readBuffer[inputPtr++]) << 8;
1688             b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1689             b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1690             bytesDone += 3;
1691             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1692             if (--chunksBeforeLF <= 0) {
1693                 _outputBuffer[_outputTail++] = '\\';
1694                 _outputBuffer[_outputTail++] = 'n';
1695                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1696             }
1697         }
1698 
1699         // And then we may have 1 or 2 leftover bytes to encode
1700         if (inputPtr < inputEnd) { // yes, but do we have room for output?
1701             if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1702                 _flushBuffer();
1703             }
1704             int b24 = ((int) readBuffer[inputPtr++]) << 16;
1705             int amount = 1;
1706             if (inputPtr < inputEnd) {
1707                 b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
1708                 amount = 2;
1709             }
1710             bytesDone += amount;
1711             _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
1712         }
1713         return bytesDone;
1714     }
1715 
_readMore(InputStream in, byte[] readBuffer, int inputPtr, int inputEnd, int maxRead)1716     private int _readMore(InputStream in,
1717             byte[] readBuffer, int inputPtr, int inputEnd,
1718             int maxRead) throws IOException
1719     {
1720         // anything to shift to front?
1721         int i = 0;
1722         while (inputPtr < inputEnd) {
1723             readBuffer[i++]  = readBuffer[inputPtr++];
1724         }
1725         inputPtr = 0;
1726         inputEnd = i;
1727         maxRead = Math.min(maxRead, readBuffer.length);
1728 
1729         do {
1730             int length = maxRead - inputEnd;
1731             if (length == 0) {
1732                 break;
1733             }
1734             int count = in.read(readBuffer, inputEnd, length);
1735             if (count < 0) {
1736                 return inputEnd;
1737             }
1738             inputEnd += count;
1739         } while (inputEnd < 3);
1740         return inputEnd;
1741     }
1742 
1743     /*
1744     /**********************************************************
1745     /* Internal methods, low-level writing, other
1746     /**********************************************************
1747      */
1748 
_writeNull()1749     private final void _writeNull() throws IOException
1750     {
1751         if ((_outputTail + 4) >= _outputEnd) {
1752             _flushBuffer();
1753         }
1754         int ptr = _outputTail;
1755         char[] buf = _outputBuffer;
1756         buf[ptr] = 'n';
1757         buf[++ptr] = 'u';
1758         buf[++ptr] = 'l';
1759         buf[++ptr] = 'l';
1760         _outputTail = ptr+1;
1761     }
1762 
1763     /*
1764     /**********************************************************
1765     /* Internal methods, low-level writing, escapes
1766     /**********************************************************
1767      */
1768 
1769     /**
1770      * Method called to try to either prepend character escape at front of
1771      * given buffer; or if not possible, to write it out directly.
1772      * Uses head and tail pointers (and updates as necessary)
1773      */
_prependOrWriteCharacterEscape(char ch, int escCode)1774     private void _prependOrWriteCharacterEscape(char ch, int escCode)
1775         throws IOException, JsonGenerationException
1776     {
1777         if (escCode >= 0) { // \\N (2 char)
1778             if (_outputTail >= 2) { // fits, just prepend
1779                 int ptr = _outputTail - 2;
1780                 _outputHead = ptr;
1781                 _outputBuffer[ptr++] = '\\';
1782                 _outputBuffer[ptr] = (char) escCode;
1783                 return;
1784             }
1785             // won't fit, write
1786             char[] buf = _entityBuffer;
1787             if (buf == null) {
1788                 buf = _allocateEntityBuffer();
1789             }
1790             _outputHead = _outputTail;
1791             buf[1] = (char) escCode;
1792             _writer.write(buf, 0, 2);
1793             return;
1794         }
1795         if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
1796             if (_outputTail >= 6) { // fits, prepend to buffer
1797                 char[] buf = _outputBuffer;
1798                 int ptr = _outputTail - 6;
1799                 _outputHead = ptr;
1800                 buf[ptr] = '\\';
1801                 buf[++ptr] = 'u';
1802                 // We know it's a control char, so only the last 2 chars are non-0
1803                 if (ch > 0xFF) { // beyond 8 bytes
1804                     int hi = (ch >> 8) & 0xFF;
1805                     buf[++ptr] = HEX_CHARS[hi >> 4];
1806                     buf[++ptr] = HEX_CHARS[hi & 0xF];
1807                     ch &= 0xFF;
1808                 } else {
1809                     buf[++ptr] = '0';
1810                     buf[++ptr] = '0';
1811                 }
1812                 buf[++ptr] = HEX_CHARS[ch >> 4];
1813                 buf[++ptr] = HEX_CHARS[ch & 0xF];
1814                 return;
1815             }
1816             // won't fit, flush and write
1817             char[] buf = _entityBuffer;
1818             if (buf == null) {
1819                 buf = _allocateEntityBuffer();
1820             }
1821             _outputHead = _outputTail;
1822             if (ch > 0xFF) { // beyond 8 bytes
1823                 int hi = (ch >> 8) & 0xFF;
1824                 int lo = ch & 0xFF;
1825                 buf[10] = HEX_CHARS[hi >> 4];
1826                 buf[11] = HEX_CHARS[hi & 0xF];
1827                 buf[12] = HEX_CHARS[lo >> 4];
1828                 buf[13] = HEX_CHARS[lo & 0xF];
1829                 _writer.write(buf, 8, 6);
1830             } else { // We know it's a control char, so only the last 2 chars are non-0
1831                 buf[6] = HEX_CHARS[ch >> 4];
1832                 buf[7] = HEX_CHARS[ch & 0xF];
1833                 _writer.write(buf, 2, 6);
1834             }
1835             return;
1836         }
1837         String escape;
1838 
1839         if (_currentEscape == null) {
1840             escape = _characterEscapes.getEscapeSequence(ch).getValue();
1841         } else {
1842             escape = _currentEscape.getValue();
1843             _currentEscape = null;
1844         }
1845         int len = escape.length();
1846         if (_outputTail >= len) { // fits in, prepend
1847             int ptr = _outputTail - len;
1848             _outputHead = ptr;
1849             escape.getChars(0, len, _outputBuffer, ptr);
1850             return;
1851         }
1852         // won't fit, write separately
1853         _outputHead = _outputTail;
1854         _writer.write(escape);
1855     }
1856 
1857     /**
1858      * Method called to try to either prepend character escape at front of
1859      * given buffer; or if not possible, to write it out directly.
1860      *
1861      * @return Pointer to start of prepended entity (if prepended); or 'ptr'
1862      *   if not.
1863      */
_prependOrWriteCharacterEscape(char[] buffer, int ptr, int end, char ch, int escCode)1864     private int _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end,
1865             char ch, int escCode)
1866         throws IOException, JsonGenerationException
1867     {
1868         if (escCode >= 0) { // \\N (2 char)
1869             if (ptr > 1 && ptr < end) { // fits, just prepend
1870                 ptr -= 2;
1871                 buffer[ptr] = '\\';
1872                 buffer[ptr+1] = (char) escCode;
1873             } else { // won't fit, write
1874                 char[] ent = _entityBuffer;
1875                 if (ent == null) {
1876                     ent = _allocateEntityBuffer();
1877                 }
1878                 ent[1] = (char) escCode;
1879                 _writer.write(ent, 0, 2);
1880             }
1881             return ptr;
1882         }
1883         if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
1884             if (ptr > 5 && ptr < end) { // fits, prepend to buffer
1885                 ptr -= 6;
1886                 buffer[ptr++] = '\\';
1887                 buffer[ptr++] = 'u';
1888                 // We know it's a control char, so only the last 2 chars are non-0
1889                 if (ch > 0xFF) { // beyond 8 bytes
1890                     int hi = (ch >> 8) & 0xFF;
1891                     buffer[ptr++] = HEX_CHARS[hi >> 4];
1892                     buffer[ptr++] = HEX_CHARS[hi & 0xF];
1893                     ch &= 0xFF;
1894                 } else {
1895                     buffer[ptr++] = '0';
1896                     buffer[ptr++] = '0';
1897                 }
1898                 buffer[ptr++] = HEX_CHARS[ch >> 4];
1899                 buffer[ptr] = HEX_CHARS[ch & 0xF];
1900                 ptr -= 5;
1901             } else {
1902                 // won't fit, flush and write
1903                 char[] ent = _entityBuffer;
1904                 if (ent == null) {
1905                     ent = _allocateEntityBuffer();
1906                 }
1907                 _outputHead = _outputTail;
1908                 if (ch > 0xFF) { // beyond 8 bytes
1909                     int hi = (ch >> 8) & 0xFF;
1910                     int lo = ch & 0xFF;
1911                     ent[10] = HEX_CHARS[hi >> 4];
1912                     ent[11] = HEX_CHARS[hi & 0xF];
1913                     ent[12] = HEX_CHARS[lo >> 4];
1914                     ent[13] = HEX_CHARS[lo & 0xF];
1915                     _writer.write(ent, 8, 6);
1916                 } else { // We know it's a control char, so only the last 2 chars are non-0
1917                     ent[6] = HEX_CHARS[ch >> 4];
1918                     ent[7] = HEX_CHARS[ch & 0xF];
1919                     _writer.write(ent, 2, 6);
1920                 }
1921             }
1922             return ptr;
1923         }
1924         String escape;
1925         if (_currentEscape == null) {
1926             escape = _characterEscapes.getEscapeSequence(ch).getValue();
1927         } else {
1928             escape = _currentEscape.getValue();
1929             _currentEscape = null;
1930         }
1931         int len = escape.length();
1932         if (ptr >= len && ptr < end) { // fits in, prepend
1933             ptr -= len;
1934             escape.getChars(0, len, buffer, ptr);
1935         } else { // won't fit, write separately
1936             _writer.write(escape);
1937         }
1938         return ptr;
1939     }
1940 
1941     /**
1942      * Method called to append escape sequence for given character, at the
1943      * end of standard output buffer; or if not possible, write out directly.
1944      */
_appendCharacterEscape(char ch, int escCode)1945     private void _appendCharacterEscape(char ch, int escCode)
1946         throws IOException, JsonGenerationException
1947     {
1948         if (escCode >= 0) { // \\N (2 char)
1949             if ((_outputTail + 2) > _outputEnd) {
1950                 _flushBuffer();
1951             }
1952             _outputBuffer[_outputTail++] = '\\';
1953             _outputBuffer[_outputTail++] = (char) escCode;
1954             return;
1955         }
1956         if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
1957             if ((_outputTail + 5) >= _outputEnd) {
1958                 _flushBuffer();
1959             }
1960             int ptr = _outputTail;
1961             char[] buf = _outputBuffer;
1962             buf[ptr++] = '\\';
1963             buf[ptr++] = 'u';
1964             // We know it's a control char, so only the last 2 chars are non-0
1965             if (ch > 0xFF) { // beyond 8 bytes
1966                 int hi = (ch >> 8) & 0xFF;
1967                 buf[ptr++] = HEX_CHARS[hi >> 4];
1968                 buf[ptr++] = HEX_CHARS[hi & 0xF];
1969                 ch &= 0xFF;
1970             } else {
1971                 buf[ptr++] = '0';
1972                 buf[ptr++] = '0';
1973             }
1974             buf[ptr++] = HEX_CHARS[ch >> 4];
1975             buf[ptr++] = HEX_CHARS[ch & 0xF];
1976             _outputTail = ptr;
1977             return;
1978         }
1979         String escape;
1980         if (_currentEscape == null) {
1981             escape = _characterEscapes.getEscapeSequence(ch).getValue();
1982         } else {
1983             escape = _currentEscape.getValue();
1984             _currentEscape = null;
1985         }
1986         int len = escape.length();
1987         if ((_outputTail + len) > _outputEnd) {
1988             _flushBuffer();
1989             if (len > _outputEnd) { // very very long escape; unlikely but theoretically possible
1990                 _writer.write(escape);
1991                 return;
1992             }
1993         }
1994         escape.getChars(0, len, _outputBuffer, _outputTail);
1995         _outputTail += len;
1996     }
1997 
_allocateEntityBuffer()1998     private char[] _allocateEntityBuffer()
1999     {
2000         char[] buf = new char[14];
2001         // first 2 chars, non-numeric escapes (like \n)
2002         buf[0] = '\\';
2003         // next 6; 8-bit escapes (control chars mostly)
2004         buf[2] = '\\';
2005         buf[3] = 'u';
2006         buf[4] = '0';
2007         buf[5] = '0';
2008         // last 6, beyond 8 bits
2009         buf[8] = '\\';
2010         buf[9] = 'u';
2011         _entityBuffer = buf;
2012         return buf;
2013     }
2014 
2015     /**
2016      * @since 2.9
2017      */
_allocateCopyBuffer()2018     private char[] _allocateCopyBuffer() {
2019         if (_copyBuffer == null) {
2020             _copyBuffer = _ioContext.allocNameCopyBuffer(2000);
2021         }
2022         return _copyBuffer;
2023     }
2024 
_flushBuffer()2025     protected void _flushBuffer() throws IOException
2026     {
2027         int len = _outputTail - _outputHead;
2028         if (len > 0) {
2029             int offset = _outputHead;
2030             _outputTail = _outputHead = 0;
2031             _writer.write(_outputBuffer, offset, len);
2032         }
2033     }
2034 }
2035