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
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      */

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
124     public Object getOutputTarget() {
125         return _writer;
126     }
127
128     @Override
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
137     public boolean canWriteFormattedNumbers() { return true; }
138
139     /*
140     /**********************************************************
141     /* Overridden methods
142     /**********************************************************
143      */

144
145     @Override
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
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
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
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
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
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.10
262     public void writeStartArray(int size) throws IOException
263     {
264         _verifyValueWrite("start an array");
265         _writeContext = _writeContext.createChildArrayContext();
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
277     public void writeEndArray() throws IOException
278     {
279         if (!_writeContext.inArray()) {
280             _reportError("Current context not Array but "+_writeContext.typeDesc());
281         }
282         if (_cfgPrettyPrinter != null) {
283             _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
284         } else {
285             if (_outputTail >= _outputEnd) {
286                 _flushBuffer();
287             }
288             _outputBuffer[_outputTail++] = ']';
289         }
290         _writeContext = _writeContext.clearAndGetParent();
291     }
292
293     @Override
294     public void writeStartObject() throws IOException
295     {
296         _verifyValueWrite("start an object");
297         _writeContext = _writeContext.createChildObjectContext();
298         if (_cfgPrettyPrinter != null) {
299             _cfgPrettyPrinter.writeStartObject(this);
300         } else {
301             if (_outputTail >= _outputEnd) {
302                 _flushBuffer();
303             }
304             _outputBuffer[_outputTail++] = '{';
305         }
306     }
307
308     @Override // since 2.8
309     public void writeStartObject(Object forValue) throws IOException
310     {
311         _verifyValueWrite("start an object");
312         JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue);
313         _writeContext = ctxt;
314         if (_cfgPrettyPrinter != null) {
315             _cfgPrettyPrinter.writeStartObject(this);
316         } else {
317             if (_outputTail >= _outputEnd) {
318                 _flushBuffer();
319             }
320             _outputBuffer[_outputTail++] = '{';
321         }
322     }
323
324     @Override
325     public void writeEndObject() throws IOException
326     {
327         if (!_writeContext.inObject()) {
328             _reportError("Current context not Object but "+_writeContext.typeDesc());
329         }
330         if (_cfgPrettyPrinter != null) {
331             _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
332         } else {
333             if (_outputTail >= _outputEnd) {
334                 _flushBuffer();
335             }
336             _outputBuffer[_outputTail++] = '}';
337         }
338         _writeContext = _writeContext.clearAndGetParent();
339     }
340
341     /**
342      * Specialized version of <code>_writeFieldName</code>, off-lined
343      * to keep the "fast path" as simple (and hopefully fast) as possible.
344      */

345     protected final void _writePPFieldName(String name, boolean commaBefore) throws IOException
346     {
347         if (commaBefore) {
348             _cfgPrettyPrinter.writeObjectEntrySeparator(this);
349         } else {
350             _cfgPrettyPrinter.beforeObjectEntries(this);
351         }
352
353         if (_cfgUnqNames) {// non-standard, omit quotes
354             _writeString(name);
355         } else { 
356             if (_outputTail >= _outputEnd) {
357                 _flushBuffer();
358             }
359             _outputBuffer[_outputTail++] = _quoteChar;
360             _writeString(name);
361             if (_outputTail >= _outputEnd) {
362                 _flushBuffer();
363             }
364             _outputBuffer[_outputTail++] = _quoteChar;
365         }
366     }
367
368     protected final void _writePPFieldName(SerializableString name, boolean commaBefore) throws IOException
369     {
370         if (commaBefore) {
371             _cfgPrettyPrinter.writeObjectEntrySeparator(this);
372         } else {
373             _cfgPrettyPrinter.beforeObjectEntries(this);
374         }
375         final char[] quoted = name.asQuotedChars();
376         if (_cfgUnqNames) {// non-standard, omit quotes
377             writeRaw(quoted, 0, quoted.length);
378         } else {
379             if (_outputTail >= _outputEnd) {
380                 _flushBuffer();
381             }
382             _outputBuffer[_outputTail++] = _quoteChar;
383             writeRaw(quoted, 0, quoted.length);
384             if (_outputTail >= _outputEnd) {
385                 _flushBuffer();
386             }
387             _outputBuffer[_outputTail++] = _quoteChar;
388         }
389     }
390
391     /*
392     /**********************************************************
393     /* Output method implementations, textual
394     /**********************************************************
395      */

396
397     @Override
398     public void writeString(String text) throws IOException
399     {
400         _verifyValueWrite(WRITE_STRING);
401         if (text == null) {
402             _writeNull();
403             return;
404         }
405         if (_outputTail >= _outputEnd) {
406             _flushBuffer();
407         }
408         _outputBuffer[_outputTail++] = _quoteChar;
409         _writeString(text);
410         // And finally, closing quotes
411         if (_outputTail >= _outputEnd) {
412             _flushBuffer();
413         }
414         _outputBuffer[_outputTail++] = _quoteChar;
415     }
416
417     @Override
418     public void writeString(Reader reader, int len) throws IOException {
419         _verifyValueWrite(WRITE_STRING);
420         if (reader == null) {
421             _reportError("null reader");
422         }
423         int toRead = (len >= 0) ? len : Integer.MAX_VALUE;
424         //Add leading quote
425         if ((_outputTail + len) >= _outputEnd) {
426             _flushBuffer();
427         }
428         _outputBuffer[_outputTail++] = _quoteChar;
429
430         final char[] buf = _allocateCopyBuffer();
431         //read
432         while (toRead > 0) {
433             int toReadNow = Math.min(toRead, buf.length);
434             int numRead = reader.read(buf, 0, toReadNow);
435             if (numRead <= 0) {
436                 break;
437             }
438             if ((_outputTail + len) >= _outputEnd) {
439                 _flushBuffer();
440             }
441             _writeString(buf, 0, numRead);
442
443             //decrease tracker
444             toRead -= numRead;
445         }
446         //Add trailing quote
447         if ((_outputTail + len) >= _outputEnd) {
448             _flushBuffer();
449         }
450         _outputBuffer[_outputTail++] = _quoteChar;
451
452         if (toRead > 0 && len >= 0) {
453             _reportError("Didn't read enough from reader");
454         }
455     }
456
457     @Override
458     public void writeString(char[] text, int offset, int len) throws IOException
459     {
460         _verifyValueWrite(WRITE_STRING);
461         if (_outputTail >= _outputEnd) {
462             _flushBuffer();
463         }
464         _outputBuffer[_outputTail++] = _quoteChar;
465         _writeString(text, offset, len);
466         // And finally, closing quotes
467         if (_outputTail >= _outputEnd) {
468             _flushBuffer();
469         }
470         _outputBuffer[_outputTail++] = _quoteChar;
471     }
472
473     @Override
474     public void writeString(SerializableString sstr) throws IOException
475     {
476         _verifyValueWrite(WRITE_STRING);
477         if (_outputTail >= _outputEnd) {
478             _flushBuffer();
479         }
480         _outputBuffer[_outputTail++] = _quoteChar;
481         int len = sstr.appendQuoted(_outputBuffer, _outputTail);
482         if (len < 0) {
483             _writeString2(sstr);
484             return;
485         }
486         _outputTail += len;
487         if (_outputTail >= _outputEnd) {
488             _flushBuffer();
489         }
490         _outputBuffer[_outputTail++] = _quoteChar;
491     }
492
493     private void _writeString2(SerializableString sstr) throws IOException
494     {
495         // Note: copied from writeRaw:
496         char[] text = sstr.asQuotedChars();
497         final int len = text.length;
498         if (len < SHORT_WRITE) {
499             int room = _outputEnd - _outputTail;
500             if (len > room) {
501                 _flushBuffer();
502             }
503             System.arraycopy(text, 0, _outputBuffer, _outputTail, len);
504             _outputTail += len;
505         } else {
506             _flushBuffer();
507             _writer.write(text, 0, len);
508         }
509         if (_outputTail >= _outputEnd) {
510             _flushBuffer();
511         }
512         _outputBuffer[_outputTail++] = _quoteChar;
513     }
514     
515     @Override
516     public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException {
517         // could add support for buffering if we really want it...
518         _reportUnsupportedOperation();
519     }
520
521     @Override
522     public void writeUTF8String(byte[] text, int offset, int length) throws IOException {
523         // could add support for buffering if we really want it...
524         _reportUnsupportedOperation();
525     }
526     
527     /*
528     /**********************************************************
529     /* Output method implementations, unprocessed ("raw")
530     /**********************************************************
531      */

532
533     @Override
534     public void writeRaw(String text) throws IOException
535     {
536         // Nothing to check, can just output as is
537         int len = text.length();
538         int room = _outputEnd - _outputTail;
539
540         if (room == 0) {
541             _flushBuffer();
542             room = _outputEnd - _outputTail;
543         }
544         // But would it nicely fit in? If yes, it's easy
545         if (room >= len) {
546             text.getChars(0, len, _outputBuffer, _outputTail);
547             _outputTail += len;
548         } else {
549             writeRawLong(text);
550         }
551     }
552
553     @Override
554     public void writeRaw(String text, int start, int len) throws IOException
555     {
556         // Nothing to check, can just output as is
557         int room = _outputEnd - _outputTail;
558
559         if (room < len) {
560             _flushBuffer();
561             room = _outputEnd - _outputTail;
562         }
563         // But would it nicely fit in? If yes, it's easy
564         if (room >= len) {
565             text.getChars(start, start+len, _outputBuffer, _outputTail);
566             _outputTail += len;
567         } else {                
568             writeRawLong(text.substring(start, start+len));
569         }
570     }
571
572     // @since 2.1
573     @Override
574     public void writeRaw(SerializableString text) throws IOException {
575         int len = text.appendUnquoted(_outputBuffer, _outputTail);
576         if (len < 0) {
577             writeRaw(text.getValue());
578             return;
579         }
580         _outputTail += len;
581     }
582
583     @Override
584     public void writeRaw(char[] text, int offset, int len) throws IOException
585     {
586         // Only worth buffering if it's a short write?
587         if (len < SHORT_WRITE) {
588             int room = _outputEnd - _outputTail;
589             if (len > room) {
590                 _flushBuffer();
591             }
592             System.arraycopy(text, offset, _outputBuffer, _outputTail, len);
593             _outputTail += len;
594             return;
595         }
596         // Otherwise, better just pass through:
597         _flushBuffer();
598         _writer.write(text, offset, len);
599     }
600
601     @Override
602     public void writeRaw(char c) throws IOException
603     {
604         if (_outputTail >= _outputEnd) {
605             _flushBuffer();
606         }
607         _outputBuffer[_outputTail++] = c;
608     }
609
610     private void writeRawLong(String text) throws IOException
611     {
612         int room = _outputEnd - _outputTail;
613         // If not, need to do it by looping
614         text.getChars(0, room, _outputBuffer, _outputTail);
615         _outputTail += room;
616         _flushBuffer();
617         int offset = room;
618         int len = text.length() - room;
619
620         while (len > _outputEnd) {
621             int amount = _outputEnd;
622             text.getChars(offset, offset+amount, _outputBuffer, 0);
623             _outputHead = 0;
624             _outputTail = amount;
625             _flushBuffer();
626             offset += amount;
627             len -= amount;
628         }
629         // And last piece (at most length of buffer)
630         text.getChars(offset, offset+len, _outputBuffer, 0);
631         _outputHead = 0;
632         _outputTail = len;
633     }
634
635     /*
636     /**********************************************************
637     /* Output method implementations, base64-encoded binary
638     /**********************************************************
639      */

640
641     @Override
642     public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
643         throws IOException, JsonGenerationException
644     {
645         _verifyValueWrite(WRITE_BINARY);
646         // Starting quotes
647         if (_outputTail >= _outputEnd) {
648             _flushBuffer();
649         }
650         _outputBuffer[_outputTail++] = _quoteChar;
651         _writeBinary(b64variant, data, offset, offset+len);
652         // and closing quotes
653         if (_outputTail >= _outputEnd) {
654             _flushBuffer();
655         }
656         _outputBuffer[_outputTail++] = _quoteChar;
657     }
658
659     @Override
660     public int writeBinary(Base64Variant b64variant,
661             InputStream data, int dataLength)
662         throws IOException, JsonGenerationException
663     {
664         _verifyValueWrite(WRITE_BINARY);
665         // Starting quotes
666         if (_outputTail >= _outputEnd) {
667             _flushBuffer();
668         }
669         _outputBuffer[_outputTail++] = _quoteChar;
670         byte[] encodingBuffer = _ioContext.allocBase64Buffer();
671         int bytes;
672         try {
673             if (dataLength < 0) { // length unknown
674                 bytes = _writeBinary(b64variant, data, encodingBuffer);
675             } else {
676                 int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength);
677                 if (missing > 0) {
678                     _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")");
679                 }
680                 bytes = dataLength;
681             }
682         } finally {
683             _ioContext.releaseBase64Buffer(encodingBuffer);
684         }
685         // and closing quotes
686         if (_outputTail >= _outputEnd) {
687             _flushBuffer();
688         }
689         _outputBuffer[_outputTail++] = _quoteChar;
690         return bytes;
691     }
692     
693     /*
694     /**********************************************************
695     /* Output method implementations, primitive
696     /**********************************************************
697      */

698
699     @Override
700     public void writeNumber(short s) throws IOException
701     {
702         _verifyValueWrite(WRITE_NUMBER);
703         if (_cfgNumbersAsStrings) {
704             _writeQuotedShort(s);
705             return;
706         }
707         // up to 5 digits and possible minus sign
708         if ((_outputTail + 6) >= _outputEnd) {
709             _flushBuffer();
710         }
711         _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
712     }
713
714     private void _writeQuotedShort(short s) throws IOException {
715         if ((_outputTail + 8) >= _outputEnd) {
716             _flushBuffer();
717         }
718         _outputBuffer[_outputTail++] = _quoteChar;
719         _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
720         _outputBuffer[_outputTail++] = _quoteChar;
721     }    
722
723     @Override
724     public void writeNumber(int i) throws IOException
725     {
726         _verifyValueWrite(WRITE_NUMBER);
727         if (_cfgNumbersAsStrings) {
728             _writeQuotedInt(i);
729             return;
730         }
731         // up to 10 digits and possible minus sign
732         if ((_outputTail + 11) >= _outputEnd) {
733             _flushBuffer();
734         }
735         _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
736     }
737
738     private void _writeQuotedInt(int i) throws IOException {
739         if ((_outputTail + 13) >= _outputEnd) {
740             _flushBuffer();
741         }
742         _outputBuffer[_outputTail++] = _quoteChar;
743         _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
744         _outputBuffer[_outputTail++] = _quoteChar;
745     }    
746
747     @Override
748     public void writeNumber(long l) throws IOException
749     {
750         _verifyValueWrite(WRITE_NUMBER);
751         if (_cfgNumbersAsStrings) {
752             _writeQuotedLong(l);
753             return;
754         }
755         if ((_outputTail + 21) >= _outputEnd) {
756             // up to 20 digits, minus sign
757             _flushBuffer();
758         }
759         _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
760     }
761
762     private void _writeQuotedLong(long l) throws IOException {
763         if ((_outputTail + 23) >= _outputEnd) {
764             _flushBuffer();
765         }
766         _outputBuffer[_outputTail++] = _quoteChar;
767         _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
768         _outputBuffer[_outputTail++] = _quoteChar;
769     }
770
771     // !!! 05-Aug-2008, tatus: Any ways to optimize these?
772
773     @Override
774     public void writeNumber(BigInteger value) throws IOException
775     {
776         _verifyValueWrite(WRITE_NUMBER);
777         if (value == null) {
778             _writeNull();
779         } else if (_cfgNumbersAsStrings) {
780             _writeQuotedRaw(value.toString());
781         } else {
782             writeRaw(value.toString());
783         }
784     }
785
786     @SuppressWarnings("deprecation")
787     @Override
788     public void writeNumber(double d) throws IOException
789     {
790         if (_cfgNumbersAsStrings ||
791                 (NumberOutput.notFinite(d) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) {
792             writeString(String.valueOf(d));
793             return;
794         }
795         // What is the max length for doubles? 40 chars?
796         _verifyValueWrite(WRITE_NUMBER);
797         writeRaw(String.valueOf(d));
798     }
799
800     @SuppressWarnings("deprecation")
801     @Override
802     public void writeNumber(float f) throws IOException
803     {
804         if (_cfgNumbersAsStrings ||
805                 (NumberOutput.notFinite(f) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) {
806             writeString(String.valueOf(f));
807             return;
808         }
809         // What is the max length for floats?
810         _verifyValueWrite(WRITE_NUMBER);
811         writeRaw(String.valueOf(f));
812     }
813
814     @Override
815     public void writeNumber(BigDecimal value) throws IOException
816     {
817         // Don't really know max length for big decimal, no point checking
818         _verifyValueWrite(WRITE_NUMBER);
819         if (value == null) {
820             _writeNull();
821         } else  if (_cfgNumbersAsStrings) {
822             _writeQuotedRaw(_asString(value));
823         } else {
824             writeRaw(_asString(value));
825         }
826     }
827
828     @Override
829     public void writeNumber(String encodedValue) throws IOException
830     {
831         _verifyValueWrite(WRITE_NUMBER);
832         if (_cfgNumbersAsStrings) {
833             _writeQuotedRaw(encodedValue);
834         } else {
835             writeRaw(encodedValue);
836         }
837     }
838
839     @Override
840     public void writeNumber(char[] encodedValueBuffer, int offset, int length) throws IOException {
841         _verifyValueWrite(WRITE_NUMBER);
842         if (_cfgNumbersAsStrings) {
843             _writeQuotedRaw(encodedValueBuffer, offset, length);
844         } else {
845             writeRaw(encodedValueBuffer, offset, length);
846         }
847     }
848
849     private void _writeQuotedRaw(String value) throws IOException
850     {
851         if (_outputTail >= _outputEnd) {
852             _flushBuffer();
853         }
854         _outputBuffer[_outputTail++] = _quoteChar;
855         writeRaw(value);
856         if (_outputTail >= _outputEnd) {
857             _flushBuffer();
858         }
859         _outputBuffer[_outputTail++] = _quoteChar;
860     }
861
862     private void _writeQuotedRaw(char[] text, int offset, int length) throws IOException
863     {
864         if (_outputTail >= _outputEnd) {
865             _flushBuffer();
866         }
867         _outputBuffer[_outputTail++] = _quoteChar;
868         writeRaw(text, offset, length);
869         if (_outputTail >= _outputEnd) {
870             _flushBuffer();
871         }
872         _outputBuffer[_outputTail++] = _quoteChar;
873     }
874
875     @Override
876     public void writeBoolean(boolean state) throws IOException
877     {
878         _verifyValueWrite(WRITE_BOOLEAN);
879         if ((_outputTail + 5) >= _outputEnd) {
880             _flushBuffer();
881         }
882         int ptr = _outputTail;
883         char[] buf = _outputBuffer;
884         if (state) {
885             buf[ptr] = 't';
886             buf[++ptr] = 'r';
887             buf[++ptr] = 'u';
888             buf[++ptr] = 'e';
889         } else {
890             buf[ptr] = 'f';
891             buf[++ptr] = 'a';
892             buf[++ptr] = 'l';
893             buf[++ptr] = 's';
894             buf[++ptr] = 'e';
895         }
896         _outputTail = ptr+1;
897     }
898
899     @Override
900     public void writeNull() throws IOException {
901         _verifyValueWrite(WRITE_NULL);
902         _writeNull();
903     }
904
905     /*
906     /**********************************************************
907     /* Implementations for other methods
908     /**********************************************************
909      */

910
911     @Override
912     protected final void _verifyValueWrite(String typeMsg) throws IOException
913     {
914         final int status = _writeContext.writeValue();
915         if (_cfgPrettyPrinter != null) {
916             // Otherwise, pretty printer knows what to do...
917             _verifyPrettyValueWrite(typeMsg, status);
918             return;
919         }
920         char c;
921         switch (status) {
922         case JsonWriteContext.STATUS_OK_AS_IS:
923         default:
924             return;
925         case JsonWriteContext.STATUS_OK_AFTER_COMMA:
926             c = ',';
927             break;
928         case JsonWriteContext.STATUS_OK_AFTER_COLON:
929             c = ':';
930             break;
931         case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
932             if (_rootValueSeparator != null) {
933                 writeRaw(_rootValueSeparator.getValue());
934             }
935             return;
936         case JsonWriteContext.STATUS_EXPECT_NAME:
937             _reportCantWriteValueExpectName(typeMsg);
938             return;
939         }
940         if (_outputTail >= _outputEnd) {
941             _flushBuffer();
942         }
943         _outputBuffer[_outputTail++] = c;
944     }
945
946     /*
947     /**********************************************************
948     /* Low-level output handling
949     /**********************************************************
950      */

951
952     @Override
953     public void flush() throws IOException
954     {
955         _flushBuffer();
956         if (_writer != null) {
957             if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
958                 _writer.flush();
959             }
960         }
961     }
962
963     @Override
964     public void close() throws IOException
965     {
966         super.close();
967
968         /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
969          *   scopes.
970          */

971         // First: let's see that we still have buffers...
972         if (_outputBuffer != null
973             && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
974             while (true) {
975                 JsonStreamContext ctxt = getOutputContext();
976                 if (ctxt.inArray()) {
977                     writeEndArray();
978                 } else if (ctxt.inObject()) {
979                     writeEndObject();
980                 } else {
981                     break;
982                 }
983             }
984         }
985         _flushBuffer();
986         _outputHead = 0;
987         _outputTail = 0;
988
989         /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
990          *   on the underlying Reader, unless we "own" it, or auto-closing
991          *   feature is enabled.
992          *   One downside: when using UTF8Writer, underlying buffer(s)
993          *   may not be properly recycled if we don't close the writer.
994          */

995         if (_writer != null) {
996             if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
997                 _writer.close();
998             } else  if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
999                 // If we can't close it, we should at least flush
1000                 _writer.flush();
1001             }
1002         }
1003         // Internal buffer(s) generator has can now be released as well
1004         _releaseBuffers();
1005     }
1006
1007     @Override
1008     protected void _releaseBuffers()
1009     {
1010         char[] buf = _outputBuffer;
1011         if (buf != null) {
1012             _outputBuffer = null;
1013             _ioContext.releaseConcatBuffer(buf);
1014         }
1015         buf = _copyBuffer;
1016         if (buf != null) {
1017             _copyBuffer = null;
1018             _ioContext.releaseNameCopyBuffer(buf);
1019         }
1020     }
1021
1022     /*
1023     /**********************************************************
1024     /* Internal methods, low-level writing; text, default
1025     /**********************************************************
1026      */

1027
1028     private void _writeString(String text) throws IOException
1029     {
1030         /* One check first: if String won't fit in the buffer, let's
1031          * segment writes. No point in extending buffer to huge sizes
1032          * (like if someone wants to include multi-megabyte base64
1033          * encoded stuff or such)
1034          */

1035         final int len = text.length();
1036         if (len > _outputEnd) { // Let's reserve space for entity at begin/end
1037             _writeLongString(text);
1038             return;
1039         }
1040
1041         // Ok: we know String will fit in buffer ok
1042         // But do we need to flush first?
1043         if ((_outputTail + len) > _outputEnd) {
1044             _flushBuffer();
1045         }
1046         text.getChars(0, len, _outputBuffer, _outputTail);
1047
1048         if (_characterEscapes != null) {
1049             _writeStringCustom(len);
1050         } else if (_maximumNonEscapedChar != 0) {
1051             _writeStringASCII(len, _maximumNonEscapedChar);
1052         } else {
1053             _writeString2(len);
1054         }
1055     }
1056
1057     private void _writeString2(final int len) throws IOException
1058     {
1059         // And then we'll need to verify need for escaping etc:
1060         final int end = _outputTail + len;
1061         final int[] escCodes = _outputEscapes;
1062         final int escLen = escCodes.length;
1063
1064         output_loop:
1065         while (_outputTail < end) {
1066             // Fast loop for chars not needing escaping
1067             escape_loop:
1068             while (true) {
1069                 char c = _outputBuffer[_outputTail];
1070                 if (c < escLen && escCodes[c] != 0) {
1071                     break escape_loop;
1072                 }
1073                 if (++_outputTail >= end) {
1074                     break output_loop;
1075                 }
1076             }
1077
1078             // Ok, bumped into something that needs escaping.
1079             /* First things first: need to flush the buffer.
1080              * Inlined, as we don't want to lose tail pointer
1081              */

1082             int flushLen = (_outputTail - _outputHead);
1083             if (flushLen > 0) {
1084                 _writer.write(_outputBuffer, _outputHead, flushLen);
1085             }
1086             /* In any case, tail will be the new start, so hopefully
1087              * we have room now.
1088              */

1089             char c = _outputBuffer[_outputTail++];
1090             _prependOrWriteCharacterEscape(c, escCodes[c]);
1091         }
1092     }
1093
1094     /**
1095      * Method called to write "long strings", strings whose length exceeds
1096      * output buffer length.
1097      */

1098     private void _writeLongString(String text) throws IOException
1099     {
1100         // First things first: let's flush the buffer to get some more room
1101         _flushBuffer();
1102
1103         // Then we can write 
1104         final int textLen = text.length();
1105         int offset = 0;
1106         do {
1107             int max = _outputEnd;
1108             int segmentLen = ((offset + max) > textLen)
1109                 ? (textLen - offset) : max;
1110             text.getChars(offset, offset+segmentLen, _outputBuffer, 0);
1111             if (_characterEscapes != null) {
1112                 _writeSegmentCustom(segmentLen);
1113             } else if (_maximumNonEscapedChar != 0) {
1114                 _writeSegmentASCII(segmentLen, _maximumNonEscapedChar);
1115             } else {
1116                 _writeSegment(segmentLen);
1117             }
1118             offset += segmentLen;
1119         } while (offset < textLen);
1120     }
1121
1122     /**
1123      * Method called to output textual context which has been copied
1124      * to the output buffer prior to call. If any escaping is needed,
1125      * it will also be handled by the method.
1126      *<p>
1127      * Note: when called, textual content to write is within output
1128      * buffer, right after buffered content (if any). That's why only
1129      * length of that text is passed, as buffer and offset are implied.
1130      */

1131     private void _writeSegment(int end) throws IOException
1132     {
1133         final int[] escCodes = _outputEscapes;
1134         final int escLen = escCodes.length;
1135     
1136         int ptr = 0;
1137         int start = ptr;
1138
1139         output_loop:
1140         while (ptr < end) {
1141             // Fast loop for chars not needing escaping
1142             char c;
1143             while (true) {
1144                 c = _outputBuffer[ptr];
1145                 if (c < escLen && escCodes[c] != 0) {
1146                     break;
1147                 }
1148                 if (++ptr >= end) {
1149                     break;
1150                 }
1151             }
1152
1153             // Ok, bumped into something that needs escaping.
1154             /* First things first: need to flush the buffer.
1155              * Inlined, as we don't want to lose tail pointer
1156              */

1157             int flushLen = (ptr - start);
1158             if (flushLen > 0) {
1159                 _writer.write(_outputBuffer, start, flushLen);
1160                 if (ptr >= end) {
1161                     break output_loop;
1162                 }
1163             }
1164             ++ptr;
1165             // So; either try to prepend (most likely), or write directly:
1166             start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]);
1167         }
1168     }
1169     
1170     /**
1171      * This method called when the string content is already in
1172      * a char buffer, and need not be copied for processing.
1173      */

1174     private void _writeString(char[] text, int offset, int len) throws IOException
1175     {
1176         if (_characterEscapes != null) {
1177             _writeStringCustom(text, offset, len);
1178             return;
1179         }
1180         if (_maximumNonEscapedChar != 0) {
1181             _writeStringASCII(text, offset, len, _maximumNonEscapedChar);
1182             return;
1183         }
1184
1185         // Let's just find longest spans of non-escapable content, and for
1186         // each see if it makes sense to copy them, or write through
1187
1188         len += offset; // -> len marks the end from now on
1189         final int[] escCodes = _outputEscapes;
1190         final int escLen = escCodes.length;
1191         while (offset < len) {
1192             int start = offset;
1193
1194             while (true) {
1195                 char c = text[offset];
1196                 if (c < escLen && escCodes[c] != 0) {
1197                     break;
1198                 }
1199                 if (++offset >= len) {
1200                     break;
1201                 }
1202             }
1203
1204             // Short span? Better just copy it to buffer first:
1205             int newAmount = offset - start;
1206             if (newAmount < SHORT_WRITE) {
1207                 // Note: let's reserve room for escaped char (up to 6 chars)
1208                 if ((_outputTail + newAmount) > _outputEnd) {
1209                     _flushBuffer();
1210                 }
1211                 if (newAmount > 0) {
1212                     System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
1213                     _outputTail += newAmount;
1214                 }
1215             } else { // Nope: better just write through
1216                 _flushBuffer();
1217                 _writer.write(text, start, newAmount);
1218             }
1219             // Was this the end?
1220             if (offset >= len) { // yup
1221                 break;
1222             }
1223             // Nope, need to escape the char.
1224             char c = text[offset++];
1225             _appendCharacterEscape(c, escCodes[c]);          
1226         }
1227     }
1228
1229     /*
1230     /**********************************************************
1231     /* Internal methods, low-level writing, text segment
1232     /* with additional escaping (ASCII or such)
1233     /**********************************************************
1234      */

1235
1236     /* Same as "_writeString2()", except needs additional escaping
1237      * for subset of characters
1238      */

1239     private void _writeStringASCII(final int len, final int maxNonEscaped)
1240         throws IOException, JsonGenerationException
1241     {
1242         // And then we'll need to verify need for escaping etc:
1243         int end = _outputTail + len;
1244         final int[] escCodes = _outputEscapes;
1245         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1246         int escCode = 0;
1247         
1248         output_loop:
1249         while (_outputTail < end) {
1250             char c;
1251             // Fast loop for chars not needing escaping
1252             escape_loop:
1253             while (true) {
1254                 c = _outputBuffer[_outputTail];
1255                 if (c < escLimit) {
1256                     escCode = escCodes[c];
1257                     if (escCode != 0) {
1258                         break escape_loop;
1259                     }
1260                 } else if (c > maxNonEscaped) {
1261                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1262                     break escape_loop;
1263                 }
1264                 if (++_outputTail >= end) {
1265                     break output_loop;
1266                 }
1267             }
1268             int flushLen = (_outputTail - _outputHead);
1269             if (flushLen > 0) {
1270                 _writer.write(_outputBuffer, _outputHead, flushLen);
1271             }
1272             ++_outputTail;
1273             _prependOrWriteCharacterEscape(c, escCode);
1274         }
1275     }
1276
1277     private void _writeSegmentASCII(int end, final int maxNonEscaped)
1278         throws IOException, JsonGenerationException
1279     {
1280         final int[] escCodes = _outputEscapes;
1281         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1282     
1283         int ptr = 0;
1284         int escCode = 0;
1285         int start = ptr;
1286     
1287         output_loop:
1288         while (ptr < end) {
1289             // Fast loop for chars not needing escaping
1290             char c;
1291             while (true) {
1292                 c = _outputBuffer[ptr];
1293                 if (c < escLimit) {
1294                     escCode = escCodes[c];
1295                     if (escCode != 0) {
1296                         break;
1297                     }
1298                 } else if (c > maxNonEscaped) {
1299                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1300                     break;
1301                 }
1302                 if (++ptr >= end) {
1303                     break;
1304                 }
1305             }
1306             int flushLen = (ptr - start);
1307             if (flushLen > 0) {
1308                 _writer.write(_outputBuffer, start, flushLen);
1309                 if (ptr >= end) {
1310                     break output_loop;
1311                 }
1312             }
1313             ++ptr;
1314             start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
1315         }
1316     }
1317
1318     private void _writeStringASCII(char[] text, int offset, int len,
1319             final int maxNonEscaped)
1320         throws IOException, JsonGenerationException
1321     {
1322         len += offset; // -> len marks the end from now on
1323         final int[] escCodes = _outputEscapes;
1324         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1325
1326         int escCode = 0;
1327         
1328         while (offset < len) {
1329             int start = offset;
1330             char c;
1331             
1332             while (true) {
1333                 c = text[offset];
1334                 if (c < escLimit) {
1335                     escCode = escCodes[c];
1336                     if (escCode != 0) {
1337                         break;
1338                     }
1339                 } else if (c > maxNonEscaped) {
1340                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1341                     break;
1342                 }
1343                 if (++offset >= len) {
1344                     break;
1345                 }
1346             }
1347
1348             // Short span? Better just copy it to buffer first:
1349             int newAmount = offset - start;
1350             if (newAmount < SHORT_WRITE) {
1351                 // Note: let's reserve room for escaped char (up to 6 chars)
1352                 if ((_outputTail + newAmount) > _outputEnd) {
1353                     _flushBuffer();
1354                 }
1355                 if (newAmount > 0) {
1356                     System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
1357                     _outputTail += newAmount;
1358                 }
1359             } else { // Nope: better just write through
1360                 _flushBuffer();
1361                 _writer.write(text, start, newAmount);
1362             }
1363             // Was this the end?
1364             if (offset >= len) { // yup
1365                 break;
1366             }
1367             // Nope, need to escape the char.
1368             ++offset;
1369             _appendCharacterEscape(c, escCode);
1370         }
1371     }
1372
1373     /*
1374     /**********************************************************
1375     /* Internal methods, low-level writing, text segment
1376     /* with custom escaping (possibly coupling with ASCII limits)
1377     /**********************************************************
1378      */

1379
1380     /* Same as "_writeString2()", except needs additional escaping
1381      * for subset of characters
1382      */

1383     private void _writeStringCustom(final int len)
1384         throws IOException, JsonGenerationException
1385     {
1386         // And then we'll need to verify need for escaping etc:
1387         int end = _outputTail + len;
1388         final int[] escCodes = _outputEscapes;
1389         final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
1390         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1391         int escCode = 0;
1392         final CharacterEscapes customEscapes = _characterEscapes;
1393
1394         output_loop:
1395         while (_outputTail < end) {
1396             char c;
1397             // Fast loop for chars not needing escaping
1398             escape_loop:
1399             while (true) {
1400                 c = _outputBuffer[_outputTail];
1401                 if (c < escLimit) {
1402                     escCode = escCodes[c];
1403                     if (escCode != 0) {
1404                         break escape_loop;
1405                     }
1406                 } else if (c > maxNonEscaped) {
1407                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1408                     break escape_loop;
1409                 } else {
1410                     if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
1411                         escCode = CharacterEscapes.ESCAPE_CUSTOM;
1412                         break escape_loop;
1413                     }
1414                 }
1415                 if (++_outputTail >= end) {
1416                     break output_loop;
1417                 }
1418             }
1419             int flushLen = (_outputTail - _outputHead);
1420             if (flushLen > 0) {
1421                 _writer.write(_outputBuffer, _outputHead, flushLen);
1422             }
1423             ++_outputTail;
1424             _prependOrWriteCharacterEscape(c, escCode);
1425         }
1426     }
1427
1428     private void _writeSegmentCustom(int end)
1429         throws IOException, JsonGenerationException
1430     {
1431         final int[] escCodes = _outputEscapes;
1432         final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
1433         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1434         final CharacterEscapes customEscapes = _characterEscapes;
1435     
1436         int ptr = 0;
1437         int escCode = 0;
1438         int start = ptr;
1439     
1440         output_loop:
1441         while (ptr < end) {
1442             // Fast loop for chars not needing escaping
1443             char c;
1444             while (true) {
1445                 c = _outputBuffer[ptr];
1446                 if (c < escLimit) {
1447                     escCode = escCodes[c];
1448                     if (escCode != 0) {
1449                         break;
1450                     }
1451                 } else if (c > maxNonEscaped) {
1452                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1453                     break;
1454                 } else {
1455                     if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
1456                         escCode = CharacterEscapes.ESCAPE_CUSTOM;
1457                         break;
1458                     }
1459                 }
1460                 if (++ptr >= end) {
1461                     break;
1462                 }
1463             }
1464             int flushLen = (ptr - start);
1465             if (flushLen > 0) {
1466                 _writer.write(_outputBuffer, start, flushLen);
1467                 if (ptr >= end) {
1468                     break output_loop;
1469                 }
1470             }
1471             ++ptr;
1472             start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
1473         }
1474     }
1475
1476     private void _writeStringCustom(char[] text, int offset, int len)
1477         throws IOException, JsonGenerationException
1478     {
1479         len += offset; // -> len marks the end from now on
1480         final int[] escCodes = _outputEscapes;
1481         final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
1482         final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
1483         final CharacterEscapes customEscapes = _characterEscapes;
1484
1485         int escCode = 0;
1486         
1487         while (offset < len) {
1488             int start = offset;
1489             char c;
1490             
1491             while (true) {
1492                 c = text[offset];
1493                 if (c < escLimit) {
1494                     escCode = escCodes[c];
1495                     if (escCode != 0) {
1496                         break;
1497                     }
1498                 } else if (c > maxNonEscaped) {
1499                     escCode = CharacterEscapes.ESCAPE_STANDARD;
1500                     break;
1501                 } else {
1502                     if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
1503                         escCode = CharacterEscapes.ESCAPE_CUSTOM;
1504                         break;
1505                     }
1506                 }
1507                 if (++offset >= len) {
1508                     break;
1509                 }
1510             }
1511     
1512             // Short span? Better just copy it to buffer first:
1513             int newAmount = offset - start;
1514             if (newAmount < SHORT_WRITE) {
1515                 // Note: let's reserve room for escaped char (up to 6 chars)
1516                 if ((_outputTail + newAmount) > _outputEnd) {
1517                     _flushBuffer();
1518                 }
1519                 if (newAmount > 0) {
1520                     System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
1521                     _outputTail += newAmount;
1522                 }
1523             } else { // Nope: better just write through
1524                 _flushBuffer();
1525                 _writer.write(text, start, newAmount);
1526             }
1527             // Was this the end?
1528             if (offset >= len) { // yup
1529                 break;
1530             }
1531             // Nope, need to escape the char.
1532             ++offset;
1533             _appendCharacterEscape(c, escCode);
1534         }
1535     }
1536     
1537     /*
1538     /**********************************************************
1539     /* Internal methods, low-level writing; binary
1540     /**********************************************************
1541      */

1542     
1543     protected final void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)
1544         throws IOException, JsonGenerationException
1545     {
1546         // Encoding is by chunks of 3 input, 4 output chars, so:
1547         int safeInputEnd = inputEnd - 3;
1548         // Let's also reserve room for possible (and quoted) lf char each round
1549         int safeOutputEnd = _outputEnd - 6;
1550         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1551
1552         // Ok, first we loop through all full triplets of data:
1553         while (inputPtr <= safeInputEnd) {
1554             if (_outputTail > safeOutputEnd) { // need to flush
1555                 _flushBuffer();
1556             }
1557             // First, mash 3 bytes into lsb of 32-bit int
1558             int b24 = ((int) input[inputPtr++]) << 8;
1559             b24 |= ((int) input[inputPtr++]) & 0xFF;
1560             b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
1561             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1562             if (--chunksBeforeLF <= 0) {
1563                 // note: must quote in JSON value
1564                 _outputBuffer[_outputTail++] = '\\';
1565                 _outputBuffer[_outputTail++] = 'n';
1566                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1567             }
1568         }
1569
1570         // And then we may have 1 or 2 leftover bytes to encode
1571         int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
1572         if (inputLeft > 0) { // yes, but do we have room for output?
1573             if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1574                 _flushBuffer();
1575             }
1576             int b24 = ((int) input[inputPtr++]) << 16;
1577             if (inputLeft == 2) {
1578                 b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
1579             }
1580             _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
1581         }
1582     }
1583
1584     // write-method called when length is definitely known
1585     protected final int _writeBinary(Base64Variant b64variant,
1586             InputStream data, byte[] readBuffer, int bytesLeft)
1587         throws IOException, JsonGenerationException
1588     {
1589         int inputPtr = 0;
1590         int inputEnd = 0;
1591         int lastFullOffset = -3;       
1592         
1593         // Let's also reserve room for possible (and quoted) lf char each round
1594         int safeOutputEnd = _outputEnd - 6;
1595         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1596
1597         while (bytesLeft > 2) { // main loop for full triplets
1598             if (inputPtr > lastFullOffset) {
1599                 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1600                 inputPtr = 0;
1601                 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1602                     break;
1603                 }
1604                 lastFullOffset = inputEnd-3;
1605             }
1606             if (_outputTail > safeOutputEnd) { // need to flush
1607                 _flushBuffer();
1608             }
1609             int b24 = ((int) readBuffer[inputPtr++]) << 8;
1610             b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1611             b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1612             bytesLeft -= 3;
1613             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1614             if (--chunksBeforeLF <= 0) {
1615                 _outputBuffer[_outputTail++] = '\\';
1616                 _outputBuffer[_outputTail++] = 'n';
1617                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1618             }
1619         }
1620         
1621         // And then we may have 1 or 2 leftover bytes to encode
1622         if (bytesLeft > 0) {
1623             inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
1624             inputPtr = 0;
1625             if (inputEnd > 0) { // yes, but do we have room for output?
1626                 if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1627                     _flushBuffer();
1628                 }
1629                 int b24 = ((int) readBuffer[inputPtr++]) << 16;
1630                 int amount;
1631                 if (inputPtr < inputEnd) {
1632                     b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
1633                     amount = 2;
1634                 } else {
1635                     amount = 1;
1636                 }
1637                 _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
1638                 bytesLeft -= amount;
1639             }
1640         }
1641         return bytesLeft;
1642     }
1643     
1644     // write method when length is unknown
1645     protected final int _writeBinary(Base64Variant b64variant,
1646             InputStream data, byte[] readBuffer)
1647         throws IOException, JsonGenerationException
1648     {
1649         int inputPtr = 0;
1650         int inputEnd = 0;
1651         int lastFullOffset = -3;
1652         int bytesDone = 0;
1653         
1654         // Let's also reserve room for possible (and quoted) LF char each round
1655         int safeOutputEnd = _outputEnd - 6;
1656         int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1657
1658         // Ok, first we loop through all full triplets of data:
1659         while (true) {
1660             if (inputPtr > lastFullOffset) { // need to load more
1661                 inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length);
1662                 inputPtr = 0;
1663                 if (inputEnd < 3) { // required to try to read to have at least 3 bytes
1664                     break;
1665                 }
1666                 lastFullOffset = inputEnd-3;
1667             }
1668             if (_outputTail > safeOutputEnd) { // need to flush
1669                 _flushBuffer();
1670             }
1671             // First, mash 3 bytes into lsb of 32-bit int
1672             int b24 = ((int) readBuffer[inputPtr++]) << 8;
1673             b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
1674             b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
1675             bytesDone += 3;
1676             _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
1677             if (--chunksBeforeLF <= 0) {
1678                 _outputBuffer[_outputTail++] = '\\';
1679                 _outputBuffer[_outputTail++] = 'n';
1680                 chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
1681             }
1682         }
1683
1684         // And then we may have 1 or 2 leftover bytes to encode
1685         if (inputPtr < inputEnd) { // yes, but do we have room for output?
1686             if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
1687                 _flushBuffer();
1688             }
1689             int b24 = ((int) readBuffer[inputPtr++]) << 16;
1690             int amount = 1;
1691             if (inputPtr < inputEnd) {
1692                 b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
1693                 amount = 2;
1694             }
1695             bytesDone += amount;
1696             _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
1697         }
1698         return bytesDone;
1699     }
1700     
1701     private int _readMore(InputStream in,
1702             byte[] readBuffer, int inputPtr, int inputEnd,
1703             int maxRead) throws IOException
1704     {
1705         // anything to shift to front?
1706         int i = 0;
1707         while (inputPtr < inputEnd) {
1708             readBuffer[i++]  = readBuffer[inputPtr++];
1709         }
1710         inputPtr = 0;
1711         inputEnd = i;
1712         maxRead = Math.min(maxRead, readBuffer.length);
1713         
1714         do {
1715             int length = maxRead - inputEnd;
1716             if (length == 0) {
1717                 break;
1718             }
1719             int count = in.read(readBuffer, inputEnd, length);            
1720             if (count < 0) {
1721                 return inputEnd;
1722             }
1723             inputEnd += count;
1724         } while (inputEnd < 3);
1725         return inputEnd;
1726     }
1727     
1728     /*
1729     /**********************************************************
1730     /* Internal methods, low-level writing, other
1731     /**********************************************************
1732      */

1733     
1734     private final void _writeNull() throws IOException
1735     {
1736         if ((_outputTail + 4) >= _outputEnd) {
1737             _flushBuffer();
1738         }
1739         int ptr = _outputTail;
1740         char[] buf = _outputBuffer;
1741         buf[ptr] = 'n';
1742         buf[++ptr] = 'u';
1743         buf[++ptr] = 'l';
1744         buf[++ptr] = 'l';
1745         _outputTail = ptr+1;
1746     }
1747         
1748     /*
1749     /**********************************************************
1750     /* Internal methods, low-level writing, escapes
1751     /**********************************************************
1752      */

1753
1754     /**
1755      * Method called to try to either prepend character escape at front of
1756      * given buffer; or if not possible, to write it out directly.
1757      * Uses head and tail pointers (and updates as necessary)
1758      */

1759     private void _prependOrWriteCharacterEscape(char ch, int escCode)
1760         throws IOException, JsonGenerationException
1761     {
1762         if (escCode >= 0) { // \\N (2 char)
1763             if (_outputTail >= 2) { // fits, just prepend
1764                 int ptr = _outputTail - 2;
1765                 _outputHead = ptr;
1766                 _outputBuffer[ptr++] = '\\';
1767                 _outputBuffer[ptr] = (char) escCode;
1768                 return;
1769             }
1770             // won't fit, write
1771             char[] buf = _entityBuffer;
1772             if (buf == null) {
1773                 buf = _allocateEntityBuffer();
1774             }
1775             _outputHead = _outputTail;
1776             buf[1] = (char) escCode;
1777             _writer.write(buf, 0, 2);
1778             return;
1779         }
1780         if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
1781             if (_outputTail >= 6) { // fits, prepend to buffer
1782                 char[] buf = _outputBuffer;
1783                 int ptr = _outputTail - 6;
1784                 _outputHead = ptr;
1785                 buf[ptr] = '\\';
1786                 buf[++ptr] = 'u';
1787                 // We know it's a control char, so only the last 2 chars are non-0
1788                 if (ch > 0xFF) { // beyond 8 bytes
1789                     int hi = (ch >> 8) & 0xFF;
1790                     buf[++ptr] = HEX_CHARS[hi >> 4];
1791                     buf[++ptr] = HEX_CHARS[hi & 0xF];
1792                     ch &= 0xFF;
1793                 } else {
1794                     buf[++ptr] = '0';
1795                     buf[++ptr] = '0';
1796                 }
1797                 buf[++ptr] = HEX_CHARS[ch >> 4];
1798                 buf[++ptr] = HEX_CHARS[ch & 0xF];
1799                 return;
1800             }
1801             // won't fit, flush and write
1802             char[] buf = _entityBuffer;
1803             if (buf == null) {
1804                 buf = _allocateEntityBuffer();
1805             }
1806             _outputHead = _outputTail;
1807             if (ch > 0xFF) { // beyond 8 bytes
1808                 int hi = (ch >> 8) & 0xFF;
1809                 int lo = ch & 0xFF;
1810                 buf[10] = HEX_CHARS[hi >> 4];
1811                 buf[11] = HEX_CHARS[hi & 0xF];
1812                 buf[12] = HEX_CHARS[lo >> 4];
1813                 buf[13] = HEX_CHARS[lo & 0xF];
1814                 _writer.write(buf, 8, 6);
1815             } else { // We know it's a control char, so only the last 2 chars are non-0
1816                 buf[6] = HEX_CHARS[ch >> 4];
1817                 buf[7] = HEX_CHARS[ch & 0xF];
1818                 _writer.write(buf, 2, 6);
1819             }
1820             return;
1821         }
1822         String escape;
1823
1824         if (_currentEscape == null) {
1825             escape = _characterEscapes.getEscapeSequence(ch).getValue();
1826         } else {
1827             escape = _currentEscape.getValue();
1828             _currentEscape = null;
1829         }
1830         int len = escape.length();
1831         if (_outputTail >= len) { // fits in, prepend
1832             int ptr = _outputTail - len;
1833             _outputHead = ptr;
1834             escape.getChars(0, len, _outputBuffer, ptr);
1835             return;
1836         }
1837         // won't fit, write separately
1838         _outputHead = _outputTail;
1839         _writer.write(escape);
1840     }
1841
1842     /**
1843      * Method called to try to either prepend character escape at front of
1844      * given buffer; or if not possible, to write it out directly.
1845      * 
1846      * @return Pointer to start of prepended entity (if prepended); or 'ptr'
1847      *   if not.
1848      */

1849     private int _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end,
1850             char ch, int escCode)
1851         throws IOException, JsonGenerationException
1852     {
1853         if (escCode >= 0) { // \\N (2 char)
1854             if (ptr > 1 && ptr < end) { // fits, just prepend
1855                 ptr -= 2;
1856                 buffer[ptr] = '\\';
1857                 buffer[ptr+1] = (char) escCode;
1858             } else { // won't fit, write
1859                 char[] ent = _entityBuffer;
1860                 if (ent == null) {
1861                     ent = _allocateEntityBuffer();
1862                 }
1863                 ent[1] = (char) escCode;
1864                 _writer.write(ent, 0, 2);
1865             }
1866             return ptr;
1867         }
1868         if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
1869             if (ptr > 5 && ptr < end) { // fits, prepend to buffer
1870                 ptr -= 6;
1871                 buffer[ptr++] = '\\';
1872                 buffer[ptr++] = 'u';
1873                 // We know it's a control char, so only the last 2 chars are non-0
1874                 if (ch > 0xFF) { // beyond 8 bytes
1875                     int hi = (ch >> 8) & 0xFF;
1876                     buffer[ptr++] = HEX_CHARS[hi >> 4];
1877                     buffer[ptr++] = HEX_CHARS[hi & 0xF];
1878                     ch &= 0xFF;
1879                 } else {
1880                     buffer[ptr++] = '0';
1881                     buffer[ptr++] = '0';
1882                 }
1883                 buffer[ptr++] = HEX_CHARS[ch >> 4];
1884                 buffer[ptr] = HEX_CHARS[ch & 0xF];
1885                 ptr -= 5;
1886             } else {
1887                 // won't fit, flush and write
1888                 char[] ent = _entityBuffer;
1889                 if (ent == null) {
1890                     ent = _allocateEntityBuffer();
1891                 }
1892                 _outputHead = _outputTail;
1893                 if (ch > 0xFF) { // beyond 8 bytes
1894                     int hi = (ch >> 8) & 0xFF;
1895                     int lo = ch & 0xFF;
1896                     ent[10] = HEX_CHARS[hi >> 4];
1897                     ent[11] = HEX_CHARS[hi & 0xF];
1898                     ent[12] = HEX_CHARS[lo >> 4];
1899                     ent[13] = HEX_CHARS[lo & 0xF];
1900                     _writer.write(ent, 8, 6);
1901                 } else { // We know it's a control char, so only the last 2 chars are non-0
1902                     ent[6] = HEX_CHARS[ch >> 4];
1903                     ent[7] = HEX_CHARS[ch & 0xF];
1904                     _writer.write(ent, 2, 6);
1905                 }
1906             }
1907             return ptr;
1908         }
1909         String escape;
1910         if (_currentEscape == null) {
1911             escape = _characterEscapes.getEscapeSequence(ch).getValue();
1912         } else {
1913             escape = _currentEscape.getValue();
1914             _currentEscape = null;
1915         }
1916         int len = escape.length();
1917         if (ptr >= len && ptr < end) { // fits in, prepend
1918             ptr -= len;
1919             escape.getChars(0, len, buffer, ptr);
1920         } else { // won't fit, write separately
1921             _writer.write(escape);
1922         }
1923         return ptr;
1924     }
1925
1926     /**
1927      * Method called to append escape sequence for given character, at the
1928      * end of standard output buffer; or if not possible, write out directly.
1929      */

1930     private void _appendCharacterEscape(char ch, int escCode)
1931         throws IOException, JsonGenerationException
1932     {
1933         if (escCode >= 0) { // \\N (2 char)
1934             if ((_outputTail + 2) > _outputEnd) {
1935                 _flushBuffer();
1936             }
1937             _outputBuffer[_outputTail++] = '\\';
1938             _outputBuffer[_outputTail++] = (char) escCode;
1939             return;
1940         }
1941         if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
1942             if ((_outputTail + 5) >= _outputEnd) {
1943                 _flushBuffer();
1944             }
1945             int ptr = _outputTail;
1946             char[] buf = _outputBuffer;
1947             buf[ptr++] = '\\';
1948             buf[ptr++] = 'u';
1949             // We know it's a control char, so only the last 2 chars are non-0
1950             if (ch > 0xFF) { // beyond 8 bytes
1951                 int hi = (ch >> 8) & 0xFF;
1952                 buf[ptr++] = HEX_CHARS[hi >> 4];
1953                 buf[ptr++] = HEX_CHARS[hi & 0xF];
1954                 ch &= 0xFF;
1955             } else {
1956                 buf[ptr++] = '0';
1957                 buf[ptr++] = '0';
1958             }
1959             buf[ptr++] = HEX_CHARS[ch >> 4];
1960             buf[ptr++] = HEX_CHARS[ch & 0xF];
1961             _outputTail = ptr;
1962             return;
1963         }
1964         String escape;
1965         if (_currentEscape == null) {
1966             escape = _characterEscapes.getEscapeSequence(ch).getValue();
1967         } else {
1968             escape = _currentEscape.getValue();
1969             _currentEscape = null;
1970         }
1971         int len = escape.length();
1972         if ((_outputTail + len) > _outputEnd) {
1973             _flushBuffer();
1974             if (len > _outputEnd) { // very very long escape; unlikely but theoretically possible
1975                 _writer.write(escape);
1976                 return;
1977             }
1978         }
1979         escape.getChars(0, len, _outputBuffer, _outputTail);
1980         _outputTail += len;
1981     }
1982     
1983     private char[] _allocateEntityBuffer()
1984     {
1985         char[] buf = new char[14];
1986         // first 2 chars, non-numeric escapes (like \n)
1987         buf[0] = '\\';
1988         // next 6; 8-bit escapes (control chars mostly)
1989         buf[2] = '\\';
1990         buf[3] = 'u';
1991         buf[4] = '0';
1992         buf[5] = '0';
1993         // last 6, beyond 8 bits
1994         buf[8] = '\\';
1995         buf[9] = 'u';
1996         _entityBuffer = buf;
1997         return buf;
1998     }
1999
2000     /**
2001      * @since 2.9
2002      */

2003     private char[] _allocateCopyBuffer() {
2004         if (_copyBuffer == null) {
2005             _copyBuffer = _ioContext.allocNameCopyBuffer(2000);
2006         }
2007         return _copyBuffer;
2008     }
2009
2010     protected void _flushBuffer() throws IOException
2011     {
2012         int len = _outputTail - _outputHead;
2013         if (len > 0) {
2014             int offset = _outputHead;
2015             _outputTail = _outputHead = 0;
2016             _writer.write(_outputBuffer, offset, len);
2017         }
2018     }
2019 }
2020