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 public class UTF8JsonGenerator
14     extends JsonGeneratorImpl
15 {
16     private final static byte BYTE_u = (byte) 'u';
17
18     private final static byte BYTE_0 = (byte) '0';
19
20     private final static byte BYTE_LBRACKET = (byte) '[';
21     private final static byte BYTE_RBRACKET = (byte) ']';
22     private final static byte BYTE_LCURLY = (byte) '{';
23     private final static byte BYTE_RCURLY = (byte) '}';
24
25     private final static byte BYTE_BACKSLASH = (byte) '\\';
26     private final static byte BYTE_COMMA = (byte) ',';
27     private final static byte BYTE_COLON = (byte) ':';
28
29     // intermediate copies only made up to certain length...
30     private final static int MAX_BYTES_TO_BUFFER = 512;
31
32     private final static byte[] HEX_CHARS = CharTypes.copyHexBytes();
33
34     private final static byte[] NULL_BYTES = { 'n', 'u', 'l', 'l' };
35     private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
36     private final static byte[] FALSE_BYTES = { 'f', 'a', 'l', 's', 'e' };
37
38     /*
39     /**********************************************************
40     /* Configuration
41     /**********************************************************
42      */

43
44     /**
45      * Underlying output stream used for writing JSON content.
46      */

47     final protected OutputStream _outputStream;
48
49     /**
50      * Character used for quoting JSON Object property names
51      * and String values.
52      *
53      * @since 2.8
54      */

55     protected byte _quoteChar;
56
57     /*
58     /**********************************************************
59     /* Output buffering
60     /**********************************************************
61      */

62
63     /**
64      * Intermediate buffer in which contents are buffered before
65      * being written using {@link #_outputStream}.
66      */

67     protected byte[] _outputBuffer;
68
69     /**
70      * Pointer to the position right beyond the last character to output
71      * (end marker; may be past the buffer)
72      */

73     protected int _outputTail;
74
75     /**
76      * End marker of the output buffer; one past the last valid position
77      * within the buffer.
78      */

79     protected final int _outputEnd;
80
81     /**
82      * Maximum number of <code>char</code>s that we know will always fit
83      * in the output buffer after escaping
84      */

85     protected final int _outputMaxContiguous;
86
87     /**
88      * Intermediate buffer in which characters of a String are copied
89      * before being encoded.
90      */

91     protected char[] _charBuffer;
92
93     /**
94      * Length of <code>_charBuffer</code>
95      */

96     protected final int _charBufferLength;
97
98     /**
99      * 6 character temporary buffer allocated if needed, for constructing
100      * escape sequences
101      */

102     protected byte[] _entityBuffer;
103
104     /**
105      * Flag that indicates whether the output buffer is recycable (and
106      * needs to be returned to recycler once we are done) or not.
107      */

108     protected boolean _bufferRecyclable;
109
110     /*
111     /**********************************************************
112     /* Life-cycle
113     /**********************************************************
114      */

115
116     /**
117      * @since 2.10
118      */

119     @SuppressWarnings("deprecation")
120     public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
121             OutputStream out, char quoteChar)
122     {
123         super(ctxt, features, codec);
124         _outputStream = out;
125         _quoteChar = (byte) quoteChar;
126         if (quoteChar != '"') { // since 2.10
127             _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar);
128         }
129
130         _bufferRecyclable = true;
131         _outputBuffer = ctxt.allocWriteEncodingBuffer();
132         _outputEnd = _outputBuffer.length;
133
134         /* To be exact, each char can take up to 6 bytes when escaped (Unicode
135          * escape with backslash, 'u' and 4 hex digits); but to avoid fluctuation,
136          * we will actually round down to only do up to 1/8 number of chars
137          */

138         _outputMaxContiguous = _outputEnd >> 3;
139         _charBuffer = ctxt.allocConcatBuffer();
140         _charBufferLength = _charBuffer.length;
141
142         // By default we use this feature to determine additional quoting
143         if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
144             setHighestNonEscapedChar(127);
145         }
146     }
147
148     /**
149      * @since 2.10
150      */

151     public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
152             OutputStream out, char quoteChar,
153             byte[] outputBuffer, int outputOffset, boolean bufferRecyclable)
154     {
155         
156         super(ctxt, features, codec);
157         _outputStream = out;
158         _quoteChar = (byte) quoteChar;
159         if (quoteChar != '"') { // since 2.10
160             _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar);
161         }
162
163         _bufferRecyclable = bufferRecyclable;
164         _outputTail = outputOffset;
165         _outputBuffer = outputBuffer;
166         _outputEnd = _outputBuffer.length;
167         // up to 6 bytes per char (see above), rounded up to 1/8
168         _outputMaxContiguous = (_outputEnd >> 3);
169         _charBuffer = ctxt.allocConcatBuffer();
170         _charBufferLength = _charBuffer.length;
171     }
172
173     @Deprecated // since 2.10
174     public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
175             OutputStream out) {
176         this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR);
177     }
178
179     @Deprecated // since 2.10
180     public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
181             OutputStream out,
182             byte[] outputBuffer, int outputOffset, boolean bufferRecyclable)
183     {
184         this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR,
185                 outputBuffer, outputOffset, bufferRecyclable);
186     }
187
188     /*
189     /**********************************************************
190     /* Overridden configuration methods
191     /**********************************************************
192      */

193
194     @Override
195     public Object getOutputTarget() {
196         return _outputStream;
197     }
198
199     @Override
200     public int getOutputBuffered() {
201         // Assuming tail is always valid, set to 0 on close
202         return _outputTail;
203     }
204
205     /*
206     /**********************************************************
207     /* Overridden methods
208     /**********************************************************
209      */

210
211     @Override
212     public void writeFieldName(String name)  throws IOException
213     {
214         if (_cfgPrettyPrinter != null) {
215             _writePPFieldName(name);
216             return;
217         }
218         final int status = _writeContext.writeFieldName(name);
219         if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
220             _reportError("Can not write a field name, expecting a value");
221         }
222         if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { // need comma
223             if (_outputTail >= _outputEnd) {
224                 _flushBuffer();
225             }
226             _outputBuffer[_outputTail++] = BYTE_COMMA;
227         }
228         /* To support [JACKSON-46], we'll do this:
229          * (Question: should quoting of spaces (etc) still be enabled?)
230          */

231         if (_cfgUnqNames) {
232             _writeStringSegments(name, false);
233             return;
234         }
235         final int len = name.length();
236         // Does it fit in buffer?
237         if (len > _charBufferLength) { // no, offline
238             _writeStringSegments(name, true);
239             return;
240         }
241         if (_outputTail >= _outputEnd) {
242             _flushBuffer();
243         }
244         _outputBuffer[_outputTail++] = _quoteChar;
245         // But as one segment, or multiple?
246         if (len <= _outputMaxContiguous) {
247             if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
248                 _flushBuffer();
249             }
250             _writeStringSegment(name, 0, len);
251         } else {
252             _writeStringSegments(name, 0, len);
253         }
254         // and closing quotes; need room for one more char:
255         if (_outputTail >= _outputEnd) {
256             _flushBuffer();
257         }
258         _outputBuffer[_outputTail++] = _quoteChar;
259     }
260     
261     @Override
262     public void writeFieldName(SerializableString name) throws IOException
263     {
264         if (_cfgPrettyPrinter != null) {
265             _writePPFieldName(name);
266             return;
267         }
268         final int status = _writeContext.writeFieldName(name.getValue());
269         if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
270             _reportError("Can not write a field name, expecting a value");
271         }
272         if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
273             if (_outputTail >= _outputEnd) {
274                 _flushBuffer();
275             }
276             _outputBuffer[_outputTail++] = BYTE_COMMA;
277         }
278         if (_cfgUnqNames) {
279             _writeUnq(name);
280             return;
281         }
282         if (_outputTail >= _outputEnd) {
283             _flushBuffer();
284         }
285         _outputBuffer[_outputTail++] = _quoteChar;
286         int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
287         if (len < 0) { // couldn't append, bit longer processing
288             _writeBytes(name.asQuotedUTF8());
289         } else {
290             _outputTail += len;
291         }
292         if (_outputTail >= _outputEnd) {
293             _flushBuffer();
294         }
295         _outputBuffer[_outputTail++] = _quoteChar;
296     }    
297
298     private final void _writeUnq(SerializableString name) throws IOException {
299         int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
300         if (len < 0) {
301             _writeBytes(name.asQuotedUTF8());
302         } else {
303             _outputTail += len;
304         }
305     }
306     
307     /*
308     /**********************************************************
309     /* Output method implementations, structural
310     /**********************************************************
311      */

312
313     @Override
314     public final void writeStartArray() throws IOException
315     {
316         _verifyValueWrite("start an array");
317         _writeContext = _writeContext.createChildArrayContext();
318         if (_cfgPrettyPrinter != null) {
319             _cfgPrettyPrinter.writeStartArray(this);
320         } else {
321             if (_outputTail >= _outputEnd) {
322                 _flushBuffer();
323             }
324             _outputBuffer[_outputTail++] = BYTE_LBRACKET;
325         }
326     }
327
328     @Override // since 2.10
329     public void writeStartArray(int size) throws IOException
330     {
331         _verifyValueWrite("start an array");
332         _writeContext = _writeContext.createChildArrayContext();
333         if (_cfgPrettyPrinter != null) {
334             _cfgPrettyPrinter.writeStartArray(this);
335         } else {
336             if (_outputTail >= _outputEnd) {
337                 _flushBuffer();
338             }
339             _outputBuffer[_outputTail++] = BYTE_LBRACKET;
340         }
341     }
342
343     @Override
344     public final void writeEndArray() throws IOException
345     {
346         if (!_writeContext.inArray()) {
347             _reportError("Current context not Array but "+_writeContext.typeDesc());
348         }
349         if (_cfgPrettyPrinter != null) {
350             _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
351         } else {
352             if (_outputTail >= _outputEnd) {
353                 _flushBuffer();
354             }
355             _outputBuffer[_outputTail++] = BYTE_RBRACKET;
356         }
357         _writeContext = _writeContext.clearAndGetParent();
358     }
359
360     @Override
361     public final void writeStartObject() throws IOException
362     {
363         _verifyValueWrite("start an object");
364         _writeContext = _writeContext.createChildObjectContext();
365         if (_cfgPrettyPrinter != null) {
366             _cfgPrettyPrinter.writeStartObject(this);
367         } else {
368             if (_outputTail >= _outputEnd) {
369                 _flushBuffer();
370             }
371             _outputBuffer[_outputTail++] = BYTE_LCURLY;
372         }
373     }
374
375     @Override // since 2.8
376     public void writeStartObject(Object forValue) throws IOException
377     {
378         _verifyValueWrite("start an object");
379         JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue);
380         _writeContext = ctxt;
381         if (_cfgPrettyPrinter != null) {
382             _cfgPrettyPrinter.writeStartObject(this);
383         } else {
384             if (_outputTail >= _outputEnd) {
385                 _flushBuffer();
386             }
387             _outputBuffer[_outputTail++] = '{';
388         }
389     }
390
391     @Override
392     public final void writeEndObject() throws IOException
393     {
394         if (!_writeContext.inObject()) {
395             _reportError("Current context not Object but "+_writeContext.typeDesc());
396         }
397         if (_cfgPrettyPrinter != null) {
398             _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
399         } else {
400             if (_outputTail >= _outputEnd) {
401                 _flushBuffer();
402             }
403             _outputBuffer[_outputTail++] = BYTE_RCURLY;
404         }
405         _writeContext = _writeContext.clearAndGetParent();
406     }
407
408     /**
409      * Specialized version of <code>_writeFieldName</code>, off-lined
410      * to keep the "fast path" as simple (and hopefully fast) as possible.
411      */

412     protected final void _writePPFieldName(String name) throws IOException
413     {
414         int status = _writeContext.writeFieldName(name);
415         if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
416             _reportError("Can not write a field name, expecting a value");
417         }
418         if ((status == JsonWriteContext.STATUS_OK_AFTER_COMMA)) {
419             _cfgPrettyPrinter.writeObjectEntrySeparator(this);
420         } else {
421             _cfgPrettyPrinter.beforeObjectEntries(this);
422         }
423         if (_cfgUnqNames) {
424             _writeStringSegments(name, false);
425             return;
426         }
427         final int len = name.length();
428         if (len > _charBufferLength) {
429             _writeStringSegments(name, true);
430             return;
431         }
432         if (_outputTail >= _outputEnd) {
433             _flushBuffer();
434         }
435         _outputBuffer[_outputTail++] = _quoteChar;
436         name.getChars(0, len, _charBuffer, 0);
437         // But as one segment, or multiple?
438         if (len <= _outputMaxContiguous) {
439             if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
440                 _flushBuffer();
441             }
442             _writeStringSegment(_charBuffer, 0, len);
443         } else {
444             _writeStringSegments(_charBuffer, 0, len);
445         }
446         if (_outputTail >= _outputEnd) {
447             _flushBuffer();
448         }
449         _outputBuffer[_outputTail++] = _quoteChar;
450     }
451
452     protected final void _writePPFieldName(SerializableString name) throws IOException
453     {
454         final int status = _writeContext.writeFieldName(name.getValue());
455         if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
456             _reportError("Can not write a field name, expecting a value");
457         }
458         if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
459             _cfgPrettyPrinter.writeObjectEntrySeparator(this);
460         } else {
461             _cfgPrettyPrinter.beforeObjectEntries(this);
462         }
463
464         final boolean addQuotes = !_cfgUnqNames; // standard
465         if (addQuotes) {
466             if (_outputTail >= _outputEnd) {
467                 _flushBuffer();
468             }
469             _outputBuffer[_outputTail++] = _quoteChar;
470         }
471         int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
472         if (len < 0) {
473             _writeBytes(name.asQuotedUTF8());
474         } else {
475             _outputTail += len;
476         }
477         if (addQuotes) {
478             if (_outputTail >= _outputEnd) {
479                 _flushBuffer();
480             }
481             _outputBuffer[_outputTail++] = _quoteChar;
482         }
483     }
484     
485     /*
486     /**********************************************************
487     /* Output method implementations, textual
488     /**********************************************************
489      */

490
491     @Override
492     public void writeString(String text) throws IOException
493     {
494         _verifyValueWrite(WRITE_STRING);
495         if (text == null) {
496             _writeNull();
497             return;
498         }
499         // First: if we can't guarantee it all fits, quoted, within output, offline
500         final int len = text.length();
501         if (len > _outputMaxContiguous) { // nope: off-line handling
502             _writeStringSegments(text, true);
503             return;
504         }
505         if ((_outputTail + len) >= _outputEnd) {
506             _flushBuffer();
507         }
508         _outputBuffer[_outputTail++] = _quoteChar;
509         _writeStringSegment(text, 0, len); // we checked space already above
510         if (_outputTail >= _outputEnd) {
511             _flushBuffer();
512         }
513         _outputBuffer[_outputTail++] = _quoteChar;
514     }
515
516     @Override
517     public void writeString(Reader reader, int len) throws IOException {
518         _verifyValueWrite(WRITE_STRING);
519         if (reader == null) {
520             _reportError("null reader");
521         }
522
523         int toRead = (len >= 0) ? len : Integer.MAX_VALUE;
524
525         final char[] buf = _charBuffer;
526
527         // Add leading quote
528         if (_outputTail >= _outputEnd) {
529             _flushBuffer();
530         }
531         _outputBuffer[_outputTail++] = _quoteChar;
532
533         // read
534         while (toRead > 0){
535             int toReadNow = Math.min(toRead, buf.length);
536             int numRead = reader.read(buf, 0, toReadNow);
537             if(numRead <= 0){
538                 break;
539             }
540             if ((_outputTail + len) >= _outputEnd) {
541                 _flushBuffer();
542             }
543             _writeStringSegments(buf, 0, numRead);
544             //decrease tracker
545             toRead -= numRead;
546         }
547
548         // Add trailing quote
549         if (_outputTail >= _outputEnd) {
550             _flushBuffer();
551         }
552         _outputBuffer[_outputTail++] = _quoteChar;
553
554         if (toRead > 0 && len >= 0){
555             _reportError("Didn't read enough from reader");
556         }
557     }
558
559     @Override
560     public void writeString(char[] text, int offset, int len) throws IOException
561     {
562         _verifyValueWrite(WRITE_STRING);
563         if (_outputTail >= _outputEnd) {
564             _flushBuffer();
565         }
566         _outputBuffer[_outputTail++] = _quoteChar;
567         // One or multiple segments?
568         if (len <= _outputMaxContiguous) {
569             if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
570                 _flushBuffer();
571             }
572             _writeStringSegment(text, offset, len);
573         } else {
574             _writeStringSegments(text, offset, len);
575         }
576         // And finally, closing quotes
577         if (_outputTail >= _outputEnd) {
578             _flushBuffer();
579         }
580         _outputBuffer[_outputTail++] = _quoteChar;
581     }
582
583     @Override
584     public final void writeString(SerializableString text) throws IOException
585     {
586         _verifyValueWrite(WRITE_STRING);
587         if (_outputTail >= _outputEnd) {
588             _flushBuffer();
589         }
590         _outputBuffer[_outputTail++] = _quoteChar;
591         int len = text.appendQuotedUTF8(_outputBuffer, _outputTail);
592         if (len < 0) {
593             _writeBytes(text.asQuotedUTF8());
594         } else {
595             _outputTail += len;
596         }
597         if (_outputTail >= _outputEnd) {
598             _flushBuffer();
599         }
600         _outputBuffer[_outputTail++] = _quoteChar;
601     }
602     
603     @Override
604     public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException
605     {
606         _verifyValueWrite(WRITE_STRING);
607         if (_outputTail >= _outputEnd) {
608             _flushBuffer();
609         }
610         _outputBuffer[_outputTail++] = _quoteChar;
611         _writeBytes(text, offset, length);
612         if (_outputTail >= _outputEnd) {
613             _flushBuffer();
614         }
615         _outputBuffer[_outputTail++] = _quoteChar;
616     }
617
618     @Override
619     public void writeUTF8String(byte[] text, int offset, int len) throws IOException
620     {
621         _verifyValueWrite(WRITE_STRING);
622         if (_outputTail >= _outputEnd) {
623             _flushBuffer();
624         }
625         _outputBuffer[_outputTail++] = _quoteChar;
626         // One or multiple segments?
627         if (len <= _outputMaxContiguous) {
628             _writeUTF8Segment(text, offset, len);
629         } else {
630             _writeUTF8Segments(text, offset, len);
631         }
632         if (_outputTail >= _outputEnd) {
633             _flushBuffer();
634         }
635         _outputBuffer[_outputTail++] = _quoteChar;
636     }
637
638     /*
639     /**********************************************************
640     /* Output method implementations, unprocessed ("raw")
641     /**********************************************************
642      */

643
644     @Override
645     public void writeRaw(String text) throws IOException {
646         final int len = text.length();
647         final char[] buf = _charBuffer;
648         if (len <= buf.length) {
649             text.getChars(0, len, buf, 0);
650             writeRaw(buf, 0, len);
651         } else {
652             writeRaw(text, 0, len);
653         }
654     }
655
656     @Override
657     public void writeRaw(String text, int offset, int len) throws IOException
658     {
659         final char[] buf = _charBuffer;
660         final int cbufLen = buf.length;
661
662         // minor optimization: see if we can just get and copy
663         if (len <= cbufLen) {
664             text.getChars(offset, offset+len, buf, 0);
665             writeRaw(buf, 0, len);
666             return;
667         }
668
669         // If not, need segmented approach. For speed, let's also use input buffer
670         // size that is guaranteed to fit in output buffer; each char can expand to
671         // at most 3 bytes, so at most 1/3 of buffer size.
672         final int maxChunk = Math.min(cbufLen,
673                 (_outputEnd >> 2) + (_outputEnd >> 4)); // == (1/4 + 1/16) == 5/16
674         final int maxBytes = maxChunk * 3;
675
676         while (len > 0) {
677             int len2 = Math.min(maxChunk, len);
678             text.getChars(offset, offset+len2, buf, 0);
679             if ((_outputTail + maxBytes) > _outputEnd) {
680                 _flushBuffer();
681             }
682             // If this is NOT the last segment and if the last character looks like
683             // split surrogate second half, drop it
684             // 21-Mar-2017, tatu: Note that we could check for either `len` or `len2`;
685             //    point here is really that we only "punt" surrogate if it is NOT the
686             //    only character left; otherwise we'd end up with a poison pill if the
687             //    very last character was unpaired first-surrogate
688             if (len2 > 1) {
689                 char ch = buf[len2-1];
690                 if ((ch >= SURR1_FIRST) && (ch <= SURR1_LAST)) {
691                     --len2;
692                 }
693             }
694             _writeRawSegment(buf, 0, len2);
695             offset += len2;
696             len -= len2;
697         }
698     }
699
700     @Override
701     public void writeRaw(SerializableString text) throws IOException
702     {
703         int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail);
704         if (len < 0) {
705             _writeBytes(text.asUnquotedUTF8());
706         } else {
707             _outputTail += len;
708         }
709     }
710
711     // since 2.5
712     @Override
713     public void writeRawValue(SerializableString text) throws IOException {
714         _verifyValueWrite(WRITE_RAW);
715         int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail);
716         if (len < 0) {
717             _writeBytes(text.asUnquotedUTF8());
718         } else {
719             _outputTail += len;
720         }
721     }
722
723     // @TODO: rewrite for speed...
724     @Override
725     public final void writeRaw(char[] cbuf, int offset, int len) throws IOException
726     {
727         // First: if we have 3 x charCount spaces, we know it'll fit just fine
728         {
729             int len3 = len+len+len;
730             if ((_outputTail + len3) > _outputEnd) {
731                 // maybe we could flush?
732                 if (_outputEnd < len3) { // wouldn't be enough...
733                     _writeSegmentedRaw(cbuf, offset, len);
734                     return;
735                 }
736                 // yes, flushing brings enough space
737                 _flushBuffer();
738             }
739         }
740         len += offset; // now marks the end
741
742         // Note: here we know there is enough room, hence no output boundary checks
743         main_loop:
744         while (offset < len) {
745             inner_loop:
746             while (true) {
747                 int ch = (int) cbuf[offset];
748                 if (ch > 0x7F) {
749                     break inner_loop;
750                 }
751                 _outputBuffer[_outputTail++] = (byte) ch;
752                 if (++offset >= len) {
753                     break main_loop;
754                 }
755             }
756             char ch = cbuf[offset++];
757             if (ch < 0x800) { // 2-byte?
758                 _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
759                 _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
760             } else {
761                 offset = _outputRawMultiByteChar(ch, cbuf, offset, len);
762             }
763         }
764     }
765
766     @Override
767     public void writeRaw(char ch) throws IOException
768     {
769         if ((_outputTail + 3) >= _outputEnd) {
770             _flushBuffer();
771         }
772         final byte[] bbuf = _outputBuffer;
773         if (ch <= 0x7F) {
774             bbuf[_outputTail++] = (byte) ch;
775         } else  if (ch < 0x800) { // 2-byte?
776             bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
777             bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
778         } else {
779             /*offset =*/ _outputRawMultiByteChar(ch, null, 0, 0);
780         }
781     }
782
783     /**
784      * Helper method called when it is possible that output of raw section
785      * to output may cross buffer boundary
786      */

787     private final void _writeSegmentedRaw(char[] cbuf, int offset, int len) throws IOException
788     {
789         final int end = _outputEnd;
790         final byte[] bbuf = _outputBuffer;
791         final int inputEnd = offset + len;
792         
793         main_loop:
794         while (offset < inputEnd) {
795             inner_loop:
796             while (true) {
797                 int ch = (int) cbuf[offset];
798                 if (ch >= 0x80) {
799                     break inner_loop;
800                 }
801                 // !!! TODO: fast(er) writes (roll input, output checks in one)
802                 if (_outputTail >= end) {
803                     _flushBuffer();
804                 }
805                 bbuf[_outputTail++] = (byte) ch;
806                 if (++offset >= inputEnd) {
807                     break main_loop;
808                 }
809             }
810             if ((_outputTail + 3) >= _outputEnd) {
811                 _flushBuffer();
812             }
813             char ch = cbuf[offset++];
814             if (ch < 0x800) { // 2-byte?
815                 bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
816                 bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
817             } else {
818                 offset = _outputRawMultiByteChar(ch, cbuf, offset, inputEnd);
819             }
820         }
821     }
822
823     /**
824      * Helper method that is called for segmented write of raw content
825      * when explicitly outputting a segment of longer thing.
826      * Caller has to take care of ensuring there's no split surrogate
827      * pair at the end (that is, last char can not be first part of a
828      * surrogate char pair).
829      *
830      * @since 2.8.2
831      */

832     private void _writeRawSegment(char[] cbuf, int offset, int end) throws IOException
833     {
834         main_loop:
835         while (offset < end) {
836             inner_loop:
837             while (true) {
838                 int ch = (int) cbuf[offset];
839                 if (ch > 0x7F) {
840                     break inner_loop;
841                 }
842                 _outputBuffer[_outputTail++] = (byte) ch;
843                 if (++offset >= end) {
844                     break main_loop;
845                 }
846             }
847             char ch = cbuf[offset++];
848             if (ch < 0x800) { // 2-byte?
849                 _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
850                 _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
851             } else {
852                 offset = _outputRawMultiByteChar(ch, cbuf, offset, end);
853             }
854         }
855     }
856
857     /*
858     /**********************************************************
859     /* Output method implementations, base64-encoded binary
860     /**********************************************************
861      */

862
863     @Override
864     public void writeBinary(Base64Variant b64variant,
865             byte[] data, int offset, int len)
866         throws IOException, JsonGenerationException
867     {
868         _verifyValueWrite(WRITE_BINARY);
869         // Starting quotes
870         if (_outputTail >= _outputEnd) {
871             _flushBuffer();
872         }
873         _outputBuffer[_outputTail++] = _quoteChar;
874         _writeBinary(b64variant, data, offset, offset+len);
875         // and closing quotes
876         if (_outputTail >= _outputEnd) {
877             _flushBuffer();
878         }
879         _outputBuffer[_outputTail++] = _quoteChar;
880     }
881
882     @Override
883     public int writeBinary(Base64Variant b64variant,
884             InputStream data, int dataLength)
885         throws IOException, JsonGenerationException
886     {
887         _verifyValueWrite(WRITE_BINARY);
888         // Starting quotes
889         if (_outputTail >= _outputEnd) {
890             _flushBuffer();
891         }
892         _outputBuffer[_outputTail++] = _quoteChar;
893         byte[] encodingBuffer = _ioContext.allocBase64Buffer();
894         int bytes;
895         try {
896             if (dataLength < 0) { // length unknown
897                 bytes = _writeBinary(b64variant, data, encodingBuffer);
898             } else {
899                 int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength);
900                 if (missing > 0) {
901                     _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")");
902                 }
903                 bytes = dataLength;
904             }
905         } finally {
906             _ioContext.releaseBase64Buffer(encodingBuffer);
907         }
908         // and closing quotes
909         if (_outputTail >= _outputEnd) {
910             _flushBuffer();
911         }
912         _outputBuffer[_outputTail++] = _quoteChar;
913         return bytes;
914     }
915     
916     /*
917     /**********************************************************
918     /* Output method implementations, primitive
919     /**********************************************************
920      */

921
922     @Override
923     public void writeNumber(short s) throws IOException
924     {
925         _verifyValueWrite(WRITE_NUMBER);
926         // up to 5 digits and possible minus sign
927         if ((_outputTail + 6) >= _outputEnd) {
928             _flushBuffer();
929         }
930         if (_cfgNumbersAsStrings) {
931             _writeQuotedShort(s);
932             return;
933         }
934         _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
935     }
936     
937     private final void _writeQuotedShort(short s) throws IOException {
938         if ((_outputTail + 8) >= _outputEnd) {
939             _flushBuffer();
940         }
941         _outputBuffer[_outputTail++] = _quoteChar;
942         _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
943         _outputBuffer[_outputTail++] = _quoteChar;
944     } 
945     
946     @Override
947     public void writeNumber(int i) throws IOException
948     {
949         _verifyValueWrite(WRITE_NUMBER);
950         // up to 10 digits and possible minus sign
951         if ((_outputTail + 11) >= _outputEnd) {
952             _flushBuffer();
953         }
954         if (_cfgNumbersAsStrings) {
955             _writeQuotedInt(i);
956             return;
957         }
958         _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
959     }
960
961     private final void _writeQuotedInt(int i) throws IOException
962     {
963         if ((_outputTail + 13) >= _outputEnd) {
964             _flushBuffer();
965         }
966         _outputBuffer[_outputTail++] = _quoteChar;
967         _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
968         _outputBuffer[_outputTail++] = _quoteChar;
969     }    
970
971     @Override
972     public void writeNumber(long l) throws IOException
973     {
974         _verifyValueWrite(WRITE_NUMBER);
975         if (_cfgNumbersAsStrings) {
976             _writeQuotedLong(l);
977             return;
978         }
979         if ((_outputTail + 21) >= _outputEnd) {
980             // up to 20 digits, minus sign
981             _flushBuffer();
982         }
983         _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
984     }
985
986     private final void _writeQuotedLong(long l) throws IOException
987     {
988         if ((_outputTail + 23) >= _outputEnd) {
989             _flushBuffer();
990         }
991         _outputBuffer[_outputTail++] = _quoteChar;
992         _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
993         _outputBuffer[_outputTail++] = _quoteChar;
994     }
995
996     @Override
997     public void writeNumber(BigInteger value) throws IOException
998     {
999         _verifyValueWrite(WRITE_NUMBER);
1000         if (value == null) {
1001             _writeNull();
1002         } else if (_cfgNumbersAsStrings) {
1003             _writeQuotedRaw(value.toString());
1004         } else {
1005             writeRaw(value.toString());
1006         }
1007     }
1008
1009     @SuppressWarnings("deprecation")
1010     @Override
1011     public void writeNumber(double d) throws IOException
1012     {
1013         if (_cfgNumbersAsStrings ||
1014             (NumberOutput.notFinite(d)
1015                 && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) {
1016             writeString(String.valueOf(d));
1017             return;
1018         }
1019         // What is the max length for doubles? 40 chars?
1020         _verifyValueWrite(WRITE_NUMBER);
1021         writeRaw(String.valueOf(d));
1022     }
1023
1024     @SuppressWarnings("deprecation")
1025     @Override
1026     public void writeNumber(float f) throws IOException
1027     {
1028         if (_cfgNumbersAsStrings ||
1029             (NumberOutput.notFinite(f)
1030                 && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) {
1031             writeString(String.valueOf(f));
1032             return;
1033         }
1034         // What is the max length for floats?
1035         _verifyValueWrite(WRITE_NUMBER);
1036         writeRaw(String.valueOf(f));
1037     }
1038
1039     @Override
1040     public void writeNumber(BigDecimal value) throws IOException
1041     {
1042         // Don't really know max length for big decimal, no point checking
1043         _verifyValueWrite(WRITE_NUMBER);
1044         if (value == null) {
1045             _writeNull();
1046         } else  if (_cfgNumbersAsStrings) {
1047             _writeQuotedRaw(_asString(value));
1048         } else {
1049             writeRaw(_asString(value));
1050         }
1051     }
1052
1053     @Override
1054     public void writeNumber(String encodedValue) throws IOException
1055     {
1056         _verifyValueWrite(WRITE_NUMBER);
1057         if (_cfgNumbersAsStrings) {
1058             _writeQuotedRaw(encodedValue);            
1059         } else {
1060             writeRaw(encodedValue);
1061         }
1062     }
1063
1064     @Override
1065     public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException {
1066         _verifyValueWrite(WRITE_NUMBER);
1067         if (_cfgNumbersAsStrings) {
1068             _writeQuotedRaw(encodedValueBuffer, offset, length);
1069         } else {
1070             writeRaw(encodedValueBuffer, offset, length);
1071         }
1072     }
1073
1074     private final void _writeQuotedRaw(String value) throws IOException
1075     {
1076         if (_outputTail >= _outputEnd) {
1077             _flushBuffer();
1078         }
1079         _outputBuffer[_outputTail++] = _quoteChar;
1080         writeRaw(value);
1081         if (_outputTail >= _outputEnd) {
1082             _flushBuffer();
1083         }
1084         _outputBuffer[_outputTail++] = _quoteChar;
1085     }
1086
1087     private void _writeQuotedRaw(char[] text, int offset, int length) throws IOException
1088     {
1089         if (_outputTail >= _outputEnd) {
1090             _flushBuffer();
1091         }
1092         _outputBuffer[_outputTail++] = _quoteChar;
1093         writeRaw(text, offset, length);
1094         if (_outputTail >= _outputEnd) {
1095             _flushBuffer();
1096         }
1097         _outputBuffer[_outputTail++] = _quoteChar;
1098     }
1099
1100     @Override
1101     public void writeBoolean(boolean state) throws IOException
1102     {
1103         _verifyValueWrite(WRITE_BOOLEAN);
1104         if ((_outputTail + 5) >= _outputEnd) {
1105             _flushBuffer();
1106         }
1107         byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES;
1108         int len = keyword.length;
1109         System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len);
1110         _outputTail += len;
1111     }
1112
1113     @Override
1114     public void writeNull() throws IOException
1115     {
1116         _verifyValueWrite(WRITE_NULL);
1117         _writeNull();
1118     }
1119
1120     /*
1121     /**********************************************************
1122     /* Implementations for other methods
1123     /**********************************************************
1124      */

1125
1126     @Override
1127     protected final void _verifyValueWrite(String typeMsg) throws IOException
1128     {
1129         final int status = _writeContext.writeValue();
1130         if (_cfgPrettyPrinter != null) {
1131             // Otherwise, pretty printer knows what to do...
1132             _verifyPrettyValueWrite(typeMsg, status);
1133             return;
1134         }
1135         byte b;
1136         switch (status) {
1137         case JsonWriteContext.STATUS_OK_AS_IS:
1138         default:
1139             return;
1140         case JsonWriteContext.STATUS_OK_AFTER_COMMA:
1141             b = BYTE_COMMA;
1142             break;
1143         case JsonWriteContext.STATUS_OK_AFTER_COLON:
1144             b = BYTE_COLON;
1145             break;
1146         case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
1147             if (_rootValueSeparator != null) {
1148                 byte[] raw = _rootValueSeparator.asUnquotedUTF8();
1149                 if (raw.length > 0) {
1150                     _writeBytes(raw);
1151                 }
1152             }
1153             return;
1154         case JsonWriteContext.STATUS_EXPECT_NAME:
1155             _reportCantWriteValueExpectName(typeMsg);
1156             return;
1157         }
1158         if (_outputTail >= _outputEnd) {
1159             _flushBuffer();
1160         }
1161         _outputBuffer[_outputTail++] = b;
1162     }
1163
1164     /*
1165     /**********************************************************
1166     /* Low-level output handling
1167     /**********************************************************
1168      */

1169
1170     @Override
1171     public void flush() throws IOException
1172     {
1173         _flushBuffer();
1174         if (_outputStream != null) {
1175             if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1176                 _outputStream.flush();
1177             }
1178         }
1179     }
1180
1181     @Override
1182     public void close() throws IOException
1183     {
1184         super.close();
1185
1186         /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
1187          *   scopes.
1188          */

1189         // First: let's see that we still have buffers...
1190         if ((_outputBuffer != null)
1191             && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
1192             while (true) {
1193                 JsonStreamContext ctxt = getOutputContext();
1194                 if (ctxt.inArray()) {
1195                     writeEndArray();
1196                 } else if (ctxt.inObject()) {
1197                     writeEndObject();
1198                 } else {
1199                     break;
1200                 }
1201             }
1202         }
1203         _flushBuffer();
1204         _outputTail = 0; // just to ensure we don't think there's anything buffered
1205
1206         /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
1207          *   on the underlying Reader, unless we "own" it, or auto-closing
1208          *   feature is enabled.
1209          *   One downside: when using UTF8Writer, underlying buffer(s)
1210          *   may not be properly recycled if we don't close the writer.
1211          */

1212         if (_outputStream != null) {
1213             if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
1214                 _outputStream.close();
1215             } else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1216                 // If we can't close it, we should at least flush
1217                 _outputStream.flush();
1218             }
1219         }
1220         // Internal buffer(s) generator has can now be released as well
1221         _releaseBuffers();
1222     }
1223
1224     @Override
1225     protected void _releaseBuffers()
1226     {
1227         byte[] buf = _outputBuffer;
1228         if (buf != null && _bufferRecyclable) {
1229             _outputBuffer = null;
1230             _ioContext.releaseWriteEncodingBuffer(buf);
1231         }
1232         char[] cbuf = _charBuffer;
1233         if (cbuf != null) {
1234             _charBuffer = null;
1235             _ioContext.releaseConcatBuffer(cbuf);
1236         }
1237     }
1238
1239     /*
1240     /**********************************************************
1241     /* Internal methods, low-level writing, raw bytes
1242     /**********************************************************
1243      */

1244
1245     private final void _writeBytes(byte[] bytes) throws IOException
1246     {
1247         final int len = bytes.length;
1248         if ((_outputTail + len) > _outputEnd) {
1249             _flushBuffer();
1250             // still not enough?
1251             if (len > MAX_BYTES_TO_BUFFER) {
1252                 _outputStream.write(bytes, 0, len);
1253                 return;
1254             }
1255         }
1256         System.arraycopy(bytes, 0, _outputBuffer, _outputTail, len);
1257         _outputTail += len;
1258     }
1259
1260     private final void _writeBytes(byte[] bytes, int offset, int len) throws IOException
1261     {
1262         if ((_outputTail + len) > _outputEnd) {
1263             _flushBuffer();
1264             // still not enough?
1265             if (len > MAX_BYTES_TO_BUFFER) {
1266                 _outputStream.write(bytes, offset, len);
1267                 return;
1268             }
1269         }
1270         System.arraycopy(bytes, offset, _outputBuffer, _outputTail, len);
1271         _outputTail += len;
1272     }
1273
1274     /*
1275     /**********************************************************
1276     /* Internal methods, mid-level writing, String segments
1277     /**********************************************************
1278      */

1279     
1280     /**
1281      * Method called when String to write is long enough not to fit
1282      * completely in temporary copy buffer. If so, we will actually
1283      * copy it in small enough chunks so it can be directly fed
1284      * to single-segment writes (instead of maximum slices that
1285      * would fit in copy buffer)
1286      */

1287     private final void _writeStringSegments(String text, boolean addQuotes) throws IOException
1288     {
1289         if (addQuotes) {
1290             if (_outputTail >= _outputEnd) {
1291                 _flushBuffer();
1292             }
1293             _outputBuffer[_outputTail++] = _quoteChar;        
1294         }
1295
1296         int left = text.length();
1297         int offset = 0;
1298
1299         while (left > 0) {
1300             int len = Math.min(_outputMaxContiguous, left);
1301             if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
1302                 _flushBuffer();
1303             }
1304             _writeStringSegment(text, offset, len);
1305             offset += len;
1306             left -= len;
1307         }
1308
1309         if (addQuotes) {
1310             if (_outputTail >= _outputEnd) {
1311                 _flushBuffer();
1312             }
1313             _outputBuffer[_outputTail++] = _quoteChar;
1314         }
1315     }
1316
1317     /**
1318      * Method called when character sequence to write is long enough that
1319      * its maximum encoded and escaped form is not guaranteed to fit in
1320      * the output buffer. If so, we will need to choose smaller output
1321      * chunks to write at a time.
1322      */

1323     private final void _writeStringSegments(char[] cbuf, int offset, int totalLen) throws IOException
1324     {
1325         do {
1326             int len = Math.min(_outputMaxContiguous, totalLen);
1327             if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
1328                 _flushBuffer();
1329             }
1330             _writeStringSegment(cbuf, offset, len);
1331             offset += len;
1332             totalLen -= len;
1333         } while (totalLen > 0);
1334     }
1335
1336     private final void _writeStringSegments(String text, int offset, int totalLen) throws IOException
1337     {
1338         do {
1339             int len = Math.min(_outputMaxContiguous, totalLen);
1340             if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
1341                 _flushBuffer();
1342             }
1343             _writeStringSegment(text, offset, len);
1344             offset += len;
1345             totalLen -= len;
1346         } while (totalLen > 0);
1347     }
1348
1349     /*
1350     /**********************************************************
1351     /* Internal methods, low-level writing, text segments
1352     /**********************************************************
1353      */

1354
1355     /**
1356      * This method called when the string content is already in
1357      * a char buffer, and its maximum total encoded and escaped length
1358      * can not exceed size of the output buffer.
1359      * Caller must ensure that there is enough space in output buffer,
1360      * assuming case of all non-escaped ASCII characters, as well as
1361      * potentially enough space for other cases (but not necessarily flushed)
1362      */

1363     private final void _writeStringSegment(char[] cbuf, int offset, int len)
1364         throws IOException
1365     {
1366         // note: caller MUST ensure (via flushing) there's room for ASCII only
1367         
1368         // Fast+tight loop for ASCII-only, no-escaping-needed output
1369         len += offset; // becomes end marker, then
1370
1371         int outputPtr = _outputTail;
1372         final byte[] outputBuffer = _outputBuffer;
1373         final int[] escCodes = _outputEscapes;
1374
1375         while (offset < len) {
1376             int ch = cbuf[offset];
1377             // note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
1378             if (ch > 0x7F || escCodes[ch] != 0) {
1379                 break;
1380             }
1381             outputBuffer[outputPtr++] = (byte) ch;
1382             ++offset;
1383         }
1384         _outputTail = outputPtr;
1385         if (offset < len) {
1386             if (_characterEscapes != null) {
1387                 _writeCustomStringSegment2(cbuf, offset, len);
1388             } else if (_maximumNonEscapedChar == 0) {
1389                 _writeStringSegment2(cbuf, offset, len);
1390             } else {
1391                 _writeStringSegmentASCII2(cbuf, offset, len);
1392             }
1393
1394         }
1395     }
1396
1397     private final void _writeStringSegment(String text, int offset, int len) throws IOException
1398     {
1399         // note: caller MUST ensure (via flushing) there's room for ASCII only
1400         // Fast+tight loop for ASCII-only, no-escaping-needed output
1401         len += offset; // becomes end marker, then
1402
1403         int outputPtr = _outputTail;
1404         final byte[] outputBuffer = _outputBuffer;
1405         final int[] escCodes = _outputEscapes;
1406
1407         while (offset < len) {
1408             int ch = text.charAt(offset);
1409             // note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
1410             if (ch > 0x7F || escCodes[ch] != 0) {
1411                 break;
1412             }
1413             outputBuffer[outputPtr++] = (byte) ch;
1414             ++offset;
1415         }
1416         _outputTail = outputPtr;
1417         if (offset < len) {
1418             if (_characterEscapes != null) {
1419                 _writeCustomStringSegment2(text, offset, len);
1420             } else if (_maximumNonEscapedChar == 0) {
1421                 _writeStringSegment2(text, offset, len);
1422             } else {
1423                 _writeStringSegmentASCII2(text, offset, len);
1424             }
1425         }
1426     }
1427
1428     /**
1429      * Secondary method called when content contains characters to escape,
1430      * and/or multi-byte UTF-8 characters.
1431      */

1432     private final void _writeStringSegment2(final char[] cbuf, int offset, final int end) throws IOException
1433     {
1434         // Ok: caller guarantees buffer can have room; but that may require flushing:
1435         if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
1436             _flushBuffer();
1437         }
1438
1439         int outputPtr = _outputTail;
1440
1441         final byte[] outputBuffer = _outputBuffer;
1442         final int[] escCodes = _outputEscapes;
1443         
1444         while (offset < end) {
1445             int ch = cbuf[offset++];
1446             if (ch <= 0x7F) {
1447                  if (escCodes[ch] == 0) {
1448                      outputBuffer[outputPtr++] = (byte) ch;
1449                      continue;
1450                  }
1451                  int escape = escCodes[ch];
1452                  if (escape > 0) { // 2-char escape, fine
1453                      outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1454                      outputBuffer[outputPtr++] = (byte) escape;
1455                  } else {
1456                      // ctrl-char, 6-byte escape...
1457                      outputPtr = _writeGenericEscape(ch, outputPtr);
1458                 }
1459                 continue;
1460             }
1461             if (ch <= 0x7FF) { // fine, just needs 2 byte output
1462                 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1463                 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1464             } else {
1465                 outputPtr = _outputMultiByteChar(ch, outputPtr);
1466             }
1467         }
1468         _outputTail = outputPtr;
1469     }
1470
1471     private final void _writeStringSegment2(final String text, int offset, final int end) throws IOException
1472     {
1473         if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
1474             _flushBuffer();
1475         }
1476
1477         int outputPtr = _outputTail;
1478
1479         final byte[] outputBuffer = _outputBuffer;
1480         final int[] escCodes = _outputEscapes;
1481         
1482         while (offset < end) {
1483             int ch = text.charAt(offset++);
1484             if (ch <= 0x7F) {
1485                  if (escCodes[ch] == 0) {
1486                      outputBuffer[outputPtr++] = (byte) ch;
1487                      continue;
1488                  }
1489                  int escape = escCodes[ch];
1490                  if (escape > 0) { // 2-char escape, fine
1491                      outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1492                      outputBuffer[outputPtr++] = (byte) escape;
1493                  } else {
1494                      // ctrl-char, 6-byte escape...
1495                      outputPtr = _writeGenericEscape(ch, outputPtr);
1496                 }
1497                 continue;
1498             }
1499             if (ch <= 0x7FF) { // fine, just needs 2 byte output
1500                 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1501                 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1502             } else {
1503                 outputPtr = _outputMultiByteChar(ch, outputPtr);
1504             }
1505         }
1506         _outputTail = outputPtr;
1507     }
1508     
1509     /*
1510     /**********************************************************
1511     /* Internal methods, low-level writing, text segment
1512     /* with additional escaping (ASCII or such)
1513     /**********************************************************
1514      */

1515
1516     /**
1517      * Same as <code>_writeStringSegment2(char[], ...)</code., but with
1518      * additional escaping for high-range code points
1519      */

1520     private final void _writeStringSegmentASCII2(final char[] cbuf, int offset, final int end) throws IOException
1521     {
1522         // Ok: caller guarantees buffer can have room; but that may require flushing:
1523         if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
1524             _flushBuffer();
1525         }
1526         int outputPtr = _outputTail;
1527     
1528         final byte[] outputBuffer = _outputBuffer;
1529         final int[] escCodes = _outputEscapes;
1530         final int maxUnescaped = _maximumNonEscapedChar;
1531         
1532         while (offset < end) {
1533             int ch = cbuf[offset++];
1534             if (ch <= 0x7F) {
1535                  if (escCodes[ch] == 0) {
1536                      outputBuffer[outputPtr++] = (byte) ch;
1537                      continue;
1538                  }
1539                  int escape = escCodes[ch];
1540                  if (escape > 0) { // 2-char escape, fine
1541                      outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1542                      outputBuffer[outputPtr++] = (byte) escape;
1543                  } else {
1544                      // ctrl-char, 6-byte escape...
1545                      outputPtr = _writeGenericEscape(ch, outputPtr);
1546                  }
1547                  continue;
1548             }
1549             if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1550                 outputPtr = _writeGenericEscape(ch, outputPtr);
1551                 continue;
1552             }
1553             if (ch <= 0x7FF) { // fine, just needs 2 byte output
1554                 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1555                 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1556             } else {
1557                 outputPtr = _outputMultiByteChar(ch, outputPtr);
1558             }
1559         }
1560         _outputTail = outputPtr;
1561     }
1562
1563     private final void _writeStringSegmentASCII2(final String text, int offset, final int end) throws IOException
1564     {
1565         // Ok: caller guarantees buffer can have room; but that may require flushing:
1566         if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
1567             _flushBuffer();
1568         }
1569     
1570         int outputPtr = _outputTail;
1571     
1572         final byte[] outputBuffer = _outputBuffer;
1573         final int[] escCodes = _outputEscapes;
1574         final int maxUnescaped = _maximumNonEscapedChar;
1575         
1576         while (offset < end) {
1577             int ch = text.charAt(offset++);
1578             if (ch <= 0x7F) {
1579                  if (escCodes[ch] == 0) {
1580                      outputBuffer[outputPtr++] = (byte) ch;
1581                      continue;
1582                  }
1583                  int escape = escCodes[ch];
1584                  if (escape > 0) { // 2-char escape, fine
1585                      outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1586                      outputBuffer[outputPtr++] = (byte) escape;
1587                  } else {
1588                      // ctrl-char, 6-byte escape...
1589                      outputPtr = _writeGenericEscape(ch, outputPtr);
1590                  }
1591                  continue;
1592             }
1593             if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1594                 outputPtr = _writeGenericEscape(ch, outputPtr);
1595                 continue;
1596             }
1597             if (ch <= 0x7FF) { // fine, just needs 2 byte output
1598                 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1599                 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1600             } else {
1601                 outputPtr = _outputMultiByteChar(ch, outputPtr);
1602             }
1603         }
1604         _outputTail = outputPtr;
1605     }
1606     
1607     /*
1608     /**********************************************************
1609     /* Internal methods, low-level writing, text segment
1610     /* with fully custom escaping (and possibly escaping of non-ASCII
1611     /**********************************************************
1612      */

1613
1614     /**
1615      * Same as <code>_writeStringSegmentASCII2(char[], ...)</code., but with
1616      * additional checking for completely custom escapes
1617      */

1618     private final void _writeCustomStringSegment2(final char[] cbuf, int offset, final int end) throws IOException
1619     {
1620         // Ok: caller guarantees buffer can have room; but that may require flushing:
1621         if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
1622             _flushBuffer();
1623         }
1624         int outputPtr = _outputTail;
1625     
1626         final byte[] outputBuffer = _outputBuffer;
1627         final int[] escCodes = _outputEscapes;
1628         // may or may not have this limit
1629         final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
1630         final CharacterEscapes customEscapes = _characterEscapes; // non-null
1631         
1632         while (offset < end) {
1633             int ch = cbuf[offset++];
1634             if (ch <= 0x7F) {
1635                  if (escCodes[ch] == 0) {
1636                      outputBuffer[outputPtr++] = (byte) ch;
1637                      continue;
1638                  }
1639                  int escape = escCodes[ch];
1640                  if (escape > 0) { // 2-char escape, fine
1641                      outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1642                      outputBuffer[outputPtr++] = (byte) escape;
1643                  } else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
1644                      SerializableString esc = customEscapes.getEscapeSequence(ch);
1645                      if (esc == null) {
1646                          _reportError("Invalid custom escape definitions; custom escape not found for character code 0x"
1647                                  +Integer.toHexString(ch)+", although was supposed to have one");
1648                      }
1649                      outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1650                  } else {
1651                      // ctrl-char, 6-byte escape...
1652                      outputPtr = _writeGenericEscape(ch, outputPtr);
1653                  }
1654                  continue;
1655             }
1656             if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1657                 outputPtr = _writeGenericEscape(ch, outputPtr);
1658                 continue;
1659             }
1660             SerializableString esc = customEscapes.getEscapeSequence(ch);
1661             if (esc != null) {
1662                 outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1663                 continue;
1664             }
1665             if (ch <= 0x7FF) { // fine, just needs 2 byte output
1666                 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1667                 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1668             } else {
1669                 outputPtr = _outputMultiByteChar(ch, outputPtr);
1670             }
1671         }
1672         _outputTail = outputPtr;
1673     }
1674
1675     private final void _writeCustomStringSegment2(final String text, int offset, final int end) throws IOException
1676     {
1677         // Ok: caller guarantees buffer can have room; but that may require flushing:
1678         if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
1679             _flushBuffer();
1680         }
1681         int outputPtr = _outputTail;
1682     
1683         final byte[] outputBuffer = _outputBuffer;
1684         final int[] escCodes = _outputEscapes;
1685         // may or may not have this limit
1686         final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
1687         final CharacterEscapes customEscapes = _characterEscapes; // non-null
1688         
1689         while (offset < end) {
1690             int ch = text.charAt(offset++);
1691             if (ch <= 0x7F) {
1692                  if (escCodes[ch] == 0) {
1693                      outputBuffer[outputPtr++] = (byte) ch;
1694                      continue;
1695                  }
1696                  int escape = escCodes[ch];
1697                  if (escape > 0) { // 2-char escape, fine
1698                      outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1699                      outputBuffer[outputPtr++] = (byte) escape;
1700                  } else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
1701                      SerializableString esc = customEscapes.getEscapeSequence(ch);
1702                      if (esc == null) {
1703                          _reportError("Invalid custom escape definitions; custom escape not found for character code 0x"
1704                                  +Integer.toHexString(ch)+", although was supposed to have one");
1705                      }
1706                      outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1707                  } else {
1708                      // ctrl-char, 6-byte escape...
1709                      outputPtr = _writeGenericEscape(ch, outputPtr);
1710                  }
1711                  continue;
1712             }
1713             if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
1714                 outputPtr = _writeGenericEscape(ch, outputPtr);
1715                 continue;
1716             }
1717             SerializableString esc = customEscapes.getEscapeSequence(ch);
1718             if (esc != null) {
1719                 outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
1720                 continue;
1721             }
1722             if (ch <= 0x7FF) { // fine, just needs 2 byte output
1723                 outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
1724                 outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
1725             } else {
1726                 outputPtr = _outputMultiByteChar(ch, outputPtr);
1727             }
1728         }
1729         _outputTail = outputPtr;
1730     }
1731
1732     private final int _writeCustomEscape(byte[] outputBuffer, int outputPtr, SerializableString esc, int remainingChars)
1733         throws IOException, JsonGenerationException
1734     {
1735         byte[] raw = esc.asUnquotedUTF8(); // must be escaped at this point, shouldn't double-quote
1736         int len = raw.length;
1737         if (len > 6) { // may violate constraints we have, do offline
1738             return _handleLongCustomEscape(outputBuffer, outputPtr, _outputEnd, raw, remainingChars);
1739         }
1740         // otherwise will fit without issues, so:
1741         System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
1742         return (outputPtr + len);
1743     }
1744     
1745     private final int _handleLongCustomEscape(byte[] outputBuffer, int outputPtr, int outputEnd,
1746             byte[] raw, int remainingChars)
1747         throws IOException, JsonGenerationException
1748     {
1749         final int len = raw.length;
1750         if ((outputPtr + len) > outputEnd) {
1751             _outputTail = outputPtr;
1752             _flushBuffer();
1753             outputPtr = _outputTail;
1754             if (len > outputBuffer.length) { // very unlikely, but possible...
1755                 _outputStream.write(raw, 0, len);
1756                 return outputPtr;
1757             }
1758         }
1759         System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
1760         outputPtr += len;
1761         // but is the invariant still obeyed? If not, flush once more
1762         if ((outputPtr +  6 * remainingChars) > outputEnd) {
1763             _outputTail = outputPtr;
1764             _flushBuffer();
1765             return _outputTail;
1766         }
1767         return outputPtr;
1768     }
1769
1770     /*
1771     /**********************************************************
1772     /* Internal methods, low-level writing, "raw UTF-8" segments
1773     /**********************************************************
1774      */

1775     
1776     /**
1777      * Method called when UTF-8 encoded (but NOT yet escaped!) content is not guaranteed
1778      * to fit in the output buffer after escaping; as such, we just need to
1779      * chunk writes.
1780      */

1781     private final void _writeUTF8Segments(byte[] utf8, int offset, int totalLen)
1782         throws IOException, JsonGenerationException
1783     {
1784         do {
1785             int len = Math.min(_outputMaxContiguous, totalLen);
1786             _writeUTF8Segment(utf8, offset, len);
1787             offset += len;
1788             totalLen -= len;
1789         } while (totalLen > 0);
1790     }
1791     
1792     private final void _writeUTF8Segment(byte[] utf8, final int offset, final int len)
1793         throws IOException, JsonGenerationException
1794     {
1795         // fast loop to see if escaping is needed; don't copy, just look
1796         final int[] escCodes = _outputEscapes;
1797
1798         for (int ptr = offset, end = offset + len; ptr < end; ) {
1799             // 28-Feb-2011, tatu: escape codes just cover 7-bit range, so:
1800             int ch = utf8[ptr++];
1801             if ((ch >= 0) && escCodes[ch] != 0) {
1802                 _writeUTF8Segment2(utf8, offset, len);
1803                 return;
1804             }
1805         }
1806         
1807         // yes, fine, just copy the sucker
1808         if ((_outputTail + len) > _outputEnd) { // enough room or need to flush?
1809             _flushBuffer(); // but yes once we flush (caller guarantees length restriction)
1810         }
1811         System.arraycopy(utf8, offset, _outputBuffer, _outputTail, len);
1812         _outputTail += len;
1813     }
1814
1815     private final void _writeUTF8Segment2(final byte[] utf8, int offset, int len)
1816         throws IOException, JsonGenerationException
1817     {
1818         int outputPtr = _outputTail;
1819
1820         // Ok: caller guarantees buffer can have room; but that may require flushing:
1821         if ((outputPtr + (len * 6)) > _outputEnd) {
1822             _flushBuffer();
1823             outputPtr = _outputTail;
1824         }
1825         
1826         final byte[] outputBuffer = _outputBuffer;
1827         final int[] escCodes = _outputEscapes;
1828         len += offset; // so 'len' becomes 'end'
1829         
1830         while (offset < len) {
1831             byte b = utf8[offset++];
1832             int ch = b;
1833             if (ch < 0 || escCodes[ch] == 0) {
1834                 outputBuffer[outputPtr++] = b;
1835                 continue;
1836             }
1837             int escape = escCodes[ch];
1838             if (escape > 0) { // 2-char escape, fine
1839                 outputBuffer[outputPtr++] = BYTE_BACKSLASH;
1840                 outputBuffer[outputPtr++] = (byte) escape;
1841             } else {
1842                 // ctrl-char, 6-byte escape...
1843                 outputPtr = _writeGenericEscape(ch, outputPtr);
1844             }
1845         }
1846         _outputTail = outputPtr;
1847     }
1848     
1849     /*
1850     /**********************************************************
1851     /* Internal methods, low-level writing, base64 encoded
1852     /**********************************************************
1853      */

1854     
1855     protected final void _writeBinary(Base64Variant b64variant,
1856             byte[] input, int inputPtr, final int inputEnd)
1857         throws IOException, JsonGenerationException
1858     {
1859         // Encoding is by chunks of 3 input, 4 output chars, so:
1860         int safeInputEnd = inputEnd - 3;
1861         // Let's also reserve room for possible (and quoted) lf char each round
1862         int safeOutputEnd = _outputEnd - 6;
1863         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1864
1865         // Ok, first we loop through all full triplets of data:
1866         while (inputPtr <= safeInputEnd) {
1867             if (_outputTail > safeOutputEnd) { // need to flush
1868                 _flushBuffer();
1869             }
1870             // First, mash 3 bytes into lsb of 32-bit int
1871             int b24 = ((int) input[inputPtr++]) << 8;
1872             b24 |= ((int) input[inputPtr++]) & 0xFF;
1873             b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
1874             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1875             if (--chunksBeforeLF <= 0) {
1876                 // note: must quote in JSON value
1877                 _outputBuffer[_outputTail++] = '\\';
1878                 _outputBuffer[_outputTail++] = 'n';
1879                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1880             }
1881         }
1882
1883         // And then we may have 1 or 2 leftover bytes to encode
1884         int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
1885         if (inputLeft > 0) { // yes, but do we have room for output?
1886             if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1887                 _flushBuffer();
1888             }
1889             int b24 = ((int) input[inputPtr++]) << 16;
1890             if (inputLeft == 2) {
1891                 b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
1892             }
1893             _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
1894         }
1895     }
1896
1897     // write-method called when length is definitely known
1898     protected final int _writeBinary(Base64Variant b64variant,
1899             InputStream data, byte[] readBuffer, int bytesLeft)
1900         throws IOException, JsonGenerationException
1901     {
1902         int inputPtr = 0;
1903         int inputEnd = 0;
1904         int lastFullOffset = -3;       
1905         
1906         // Let's also reserve room for possible (and quoted) LF char each round
1907         int safeOutputEnd = _outputEnd - 6;
1908         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1909
1910         while (bytesLeft > 2) { // main loop for full triplets
1911             if (inputPtr > lastFullOffset) {
1912                 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1913                 inputPtr = 0;
1914                 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1915                     break;
1916                 }
1917                 lastFullOffset = inputEnd-3;
1918             }
1919             if (_outputTail > safeOutputEnd) { // need to flush
1920                 _flushBuffer();
1921             }
1922             int b24 = ((int) readBuffer[inputPtr++]) << 8;
1923             b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1924             b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1925             bytesLeft -= 3;
1926             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1927             if (--chunksBeforeLF <= 0) {
1928                 _outputBuffer[_outputTail++] = '\\';
1929                 _outputBuffer[_outputTail++] = 'n';
1930                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1931             }
1932         }
1933         
1934         // And then we may have 1 or 2 leftover bytes to encode
1935         if (bytesLeft > 0) {
1936             inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1937             inputPtr = 0;
1938             if (inputEnd > 0) { // yes, but do we have room for output?
1939                 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1940                     _flushBuffer();
1941                 }
1942                 int b24 = ((int) readBuffer[inputPtr++]) << 16;
1943                 int amount;
1944                 if (inputPtr < inputEnd) {
1945                     b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
1946                     amount = 2;
1947                 } else {
1948                     amount = 1;
1949                 }
1950                 _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
1951                 bytesLeft -= amount;
1952             }
1953         }
1954         return bytesLeft;
1955     }
1956
1957     // write method when length is unknown
1958     protected final int _writeBinary(Base64Variant b64variant,
1959             InputStream data, byte[] readBuffer)
1960         throws IOException, JsonGenerationException
1961     {
1962         int inputPtr = 0;
1963         int inputEnd = 0;
1964         int lastFullOffset = -3;
1965         int bytesDone = 0;
1966         
1967         // Let's also reserve room for possible (and quoted) LF char each round
1968         int safeOutputEnd = _outputEnd - 6;
1969         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1970
1971         // Ok, first we loop through all full triplets of data:
1972         while (true) {
1973             if (inputPtr > lastFullOffset) { // need to load more
1974                 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length);
1975                 inputPtr = 0;
1976                 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1977                     break;
1978                 }
1979                 lastFullOffset = inputEnd-3;
1980             }
1981             if (_outputTail > safeOutputEnd) { // need to flush
1982                 _flushBuffer();
1983             }
1984             // First, mash 3 bytes into lsb of 32-bit int
1985             int b24 = ((int) readBuffer[inputPtr++]) << 8;
1986             b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1987             b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1988             bytesDone += 3;
1989             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1990             if (--chunksBeforeLF <= 0) {
1991                 _outputBuffer[_outputTail++] = '\\';
1992                 _outputBuffer[_outputTail++] = 'n';
1993                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1994             }
1995         }
1996
1997         // And then we may have 1 or 2 leftover bytes to encode
1998         if (inputPtr < inputEnd) { // yes, but do we have room for output?
1999             if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
2000                 _flushBuffer();
2001             }
2002             int b24 = ((int) readBuffer[inputPtr++]) << 16;
2003             int amount = 1;
2004             if (inputPtr < inputEnd) {
2005                 b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
2006                 amount = 2;
2007             }
2008             bytesDone += amount;
2009             _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
2010         }
2011         return bytesDone;
2012     }
2013     
2014     private final int _readMore(InputStream in,
2015             byte[] readBuffer, int inputPtr, int inputEnd,
2016             int maxRead) throws IOException
2017     {
2018         // anything to shift to front?
2019         int i = 0;
2020         while (inputPtr < inputEnd) {
2021             readBuffer[i++]  = readBuffer[inputPtr++];
2022         }
2023         inputPtr = 0;
2024         inputEnd = i;
2025         maxRead = Math.min(maxRead, readBuffer.length);
2026         
2027         do {
2028             int length = maxRead - inputEnd;
2029             if (length == 0) {
2030                 break;
2031             }
2032             int count = in.read(readBuffer, inputEnd, length);            
2033             if (count < 0) {
2034                 return inputEnd;
2035             }
2036             inputEnd += count;
2037         } while (inputEnd < 3);
2038         return inputEnd;
2039     }
2040     
2041     /*
2042     /**********************************************************
2043     /* Internal methods, character escapes/encoding
2044     /**********************************************************
2045      */

2046     
2047     /**
2048      * Method called to output a character that is beyond range of
2049      * 1- and 2-byte UTF-8 encodings, when outputting "raw" 
2050      * text (meaning it is not to be escaped or quoted)
2051      */

2052     private final int _outputRawMultiByteChar(int ch, char[] cbuf, int inputOffset, int inputEnd)
2053         throws IOException
2054     {
2055         // Let's handle surrogates gracefully (as 4 byte output):
2056         if (ch >= SURR1_FIRST) {
2057             if (ch <= SURR2_LAST) { // yes, outside of BMP
2058                 // Do we have second part?
2059                 if (inputOffset >= inputEnd || cbuf == null) { // nope... have to note down
2060                     _reportError(String.format(
2061 "Split surrogate on writeRaw() input (last character): first character 0x%4x", ch));
2062                 }
2063                 _outputSurrogates(ch, cbuf[inputOffset]);
2064                 return inputOffset+1;
2065             }
2066         }
2067         final byte[] bbuf = _outputBuffer;
2068         bbuf[_outputTail++] = (byte) (0xe0 | (ch >> 12));
2069         bbuf[_outputTail++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
2070         bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
2071         return inputOffset;
2072     }
2073
2074     protected final void _outputSurrogates(int surr1, int surr2) throws IOException
2075     {
2076         int c = _decodeSurrogate(surr1, surr2);
2077         if ((_outputTail + 4) > _outputEnd) {
2078             _flushBuffer();
2079         }
2080         final byte[] bbuf = _outputBuffer;
2081         bbuf[_outputTail++] = (byte) (0xf0 | (c >> 18));
2082         bbuf[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f));
2083         bbuf[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
2084         bbuf[_outputTail++] = (byte) (0x80 | (c & 0x3f));
2085     }
2086     
2087     /**
2088      * 
2089      * @param ch
2090      * @param outputPtr Position within output buffer to append multi-byte in
2091      * 
2092      * @return New output position after appending
2093      * 
2094      * @throws IOException
2095      */

2096     private final int _outputMultiByteChar(int ch, int outputPtr) throws IOException
2097     {
2098         byte[] bbuf = _outputBuffer;
2099         if (ch >= SURR1_FIRST && ch <= SURR2_LAST) { // yes, outside of BMP; add an escape
2100             // 23-Nov-2015, tatu: As per [core#223], may or may not want escapes;
2101             //   it would be added here... but as things are, we do not have proper
2102             //   access yet...
2103 //            if (Feature.ESCAPE_UTF8_SURROGATES.enabledIn(_features)) {
2104                 bbuf[outputPtr++] = BYTE_BACKSLASH;
2105                 bbuf[outputPtr++] = BYTE_u;
2106                 
2107                 bbuf[outputPtr++] = HEX_CHARS[(ch >> 12) & 0xF];
2108                 bbuf[outputPtr++] = HEX_CHARS[(ch >> 8) & 0xF];
2109                 bbuf[outputPtr++] = HEX_CHARS[(ch >> 4) & 0xF];
2110                 bbuf[outputPtr++] = HEX_CHARS[ch & 0xF];
2111 //            } else { ... }
2112         } else {
2113             bbuf[outputPtr++] = (byte) (0xe0 | (ch >> 12));
2114             bbuf[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
2115             bbuf[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
2116         }
2117         return outputPtr;
2118     }
2119
2120     private final void _writeNull() throws IOException
2121     {
2122         if ((_outputTail + 4) >= _outputEnd) {
2123             _flushBuffer();
2124         }
2125         System.arraycopy(NULL_BYTES, 0, _outputBuffer, _outputTail, 4);
2126         _outputTail += 4;
2127     }
2128         
2129     /**
2130      * Method called to write a generic Unicode escape for given character.
2131      * 
2132      * @param charToEscape Character to escape using escape sequence (\\uXXXX)
2133      */

2134     private int _writeGenericEscape(int charToEscape, int outputPtr) throws IOException
2135     {
2136         final byte[] bbuf = _outputBuffer;
2137         bbuf[outputPtr++] = BYTE_BACKSLASH;
2138         bbuf[outputPtr++] = BYTE_u;
2139         if (charToEscape > 0xFF) {
2140             int hi = (charToEscape >> 8) & 0xFF;
2141             bbuf[outputPtr++] = HEX_CHARS[hi >> 4];
2142             bbuf[outputPtr++] = HEX_CHARS[hi & 0xF];
2143             charToEscape &= 0xFF;
2144         } else {
2145             bbuf[outputPtr++] = BYTE_0;
2146             bbuf[outputPtr++] = BYTE_0;
2147         }
2148         // We know it's a control char, so only the last 2 chars are non-0
2149         bbuf[outputPtr++] = HEX_CHARS[charToEscape >> 4];
2150         bbuf[outputPtr++] = HEX_CHARS[charToEscape & 0xF];
2151         return outputPtr;
2152     }
2153
2154     protected final void _flushBuffer() throws IOException
2155     {
2156         int len = _outputTail;
2157         if (len > 0) {
2158             _outputTail = 0;
2159             _outputStream.write(_outputBuffer, 0, len);
2160         }
2161     }
2162 }
2163