Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   package com.fasterxml.jackson.core.json;
   
   import java.io.*;
   import java.math.BigDecimal;
   import java.math.BigInteger;
   
com.fasterxml.jackson.core.JsonGenerator that outputs JSON content using a java.io.Writer which handles character encoding.
  
  public final class WriterBasedJsonGenerator
      extends JsonGeneratorImpl
  {
      final protected static int SHORT_WRITE = 32;
  
      final protected static char[] HEX_CHARS = CharTypes.copyHexChars();
      
      /*
      /**********************************************************
      /* Output buffering
      /**********************************************************
       */
  
      final protected Writer _writer;
    
    
Intermediate buffer in which contents are buffered before being written using _writer.
  
      protected char[] _outputBuffer;

    
Pointer to the first buffered character to output
  
      protected int _outputHead = 0;

    
Pointer to the position right beyond the last character to output (end marker; may point to position right beyond the end of the buffer)
  
      protected int _outputTail = 0;

    
End marker of the output buffer; one past the last valid position within the buffer.
  
      protected int _outputEnd;

    
Short (14 char) temporary buffer allocated if needed, for constructing escape sequences
  
      protected char[] _entityBuffer;

    
When custom escapes are used, this member variable is used internally to hold a reference to currently used escape
  
      protected SerializableString _currentEscape;
      
      
      /*
      /**********************************************************
      /* Life-cycle
      /**********************************************************
       */
  
      public WriterBasedJsonGenerator(IOContext ctxtint features,
              ObjectCodec codecWriter w)
      {
          super(ctxtfeaturescodec);
           = w;
           = ctxt.allocConcatBuffer();
           = .;
      }
      
      /*
      /**********************************************************
      /* Overridden configuration methods
      /**********************************************************
       */
      
      @Override
      public Object getOutputTarget() {
          return ;
      }
      
      /*
      /**********************************************************
      /* Overridden methods
      /**********************************************************
       */
  
      @Override
      public void writeFieldName(String name)  throws IOExceptionJsonGenerationException
      {
         int status = .writeFieldName(name);
         if (status == .) {
             _reportError("Can not write a field name, expecting a value");
         }
         _writeFieldName(name, (status == .));
     }
 
     @Override
     public void writeFieldName(SerializableString name)
         throws IOExceptionJsonGenerationException
     {
         // Object is a value, need to verify it's allowed
         int status = .writeFieldName(name.getValue());
         if (status == .) {
             _reportError("Can not write a field name, expecting a value");
         }
         _writeFieldName(name, (status == .));
     }
     
     /*
     /**********************************************************
     /* Output method implementations, structural
     /**********************************************************
      */
 
     @Override
     public void writeStartArray() throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("start an array");
         if ( != null) {
             .writeStartArray(this);
         } else {
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '[';
         }
     }
 
     @Override
     public void writeEndArray() throws IOExceptionJsonGenerationException
     {
         if (!.inArray()) {
             _reportError("Current context not an ARRAY but "+.getTypeDesc());
         }
         if ( != null) {
             .writeEndArray(this.getEntryCount());
         } else {
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = ']';
         }
          = .getParent();
     }
 
     @Override
     public void writeStartObject() throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("start an object");
         if ( != null) {
             .writeStartObject(this);
         } else {
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '{';
         }
     }
 
     @Override
     public void writeEndObject() throws IOExceptionJsonGenerationException
     {
         if (!.inObject()) {
             _reportError("Current context not an object but "+.getTypeDesc());
         }
         if ( != null) {
             .writeEndObject(this.getEntryCount());
         } else {
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '}';
         }
          = .getParent();
     }
 
     protected void _writeFieldName(String nameboolean commaBefore)
         throws IOExceptionJsonGenerationException
     {
         if ( != null) {
             _writePPFieldName(namecommaBefore);
             return;
         }
         // for fast+std case, need to output up to 2 chars, comma, dquote
         if (( + 1) >= ) {
             _flushBuffer();
         }
         if (commaBefore) {
             [++] = ',';
         }
 
         /* To support [JACKSON-46], we'll do this:
          * (Question: should quoting of spaces (etc) still be enabled?)
          */
         if (!isEnabled(.)) {
             _writeString(name);
             return;
         }
 
         // we know there's room for at least one more char
         [++] = '"';
         // The beef:
         _writeString(name);
         // and closing quotes; need room for one more char:
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
     }
 
     public void _writeFieldName(SerializableString nameboolean commaBefore)
         throws IOExceptionJsonGenerationException
     {
         if ( != null) {
             _writePPFieldName(namecommaBefore);
             return;
         }
         // for fast+std case, need to output up to 2 chars, comma, dquote
         if (( + 1) >= ) {
             _flushBuffer();
         }
         if (commaBefore) {
             [++] = ',';
         }
         /* To support [JACKSON-46], we'll do this:
          * (Question: should quoting of spaces (etc) still be enabled?)
          */
         final char[] quoted = name.asQuotedChars();
         if (!isEnabled(.)) {
             writeRaw(quoted, 0, quoted.length);
             return;
         }
         // we know there's room for at least one more char
         [++] = '"';
         // The beef:
         final int qlen = quoted.length;
         if (( + qlen + 1) >= ) {
             writeRaw(quoted, 0, qlen);
             // and closing quotes; need room for one more char:
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '"';
         } else {
             System.arraycopy(quoted, 0, qlen);
              += qlen;
             [++] = '"';
         }
     }
    
    
Specialized version of _writeFieldName, off-lined to keep the "fast path" as simple (and hopefully fast) as possible.
 
     protected void _writePPFieldName(String nameboolean commaBefore)
         throws IOExceptionJsonGenerationException
     {
         if (commaBefore) {
             .writeObjectEntrySeparator(this);
         } else {
             .beforeObjectEntries(this);
         }
 
         if (isEnabled(.)) { // standard
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '"';
             _writeString(name);
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '"';
         } else { // non-standard, omit quotes
             _writeString(name);
         }
     }
 
     protected void _writePPFieldName(SerializableString nameboolean commaBefore)
         throws IOExceptionJsonGenerationException
     {
         if (commaBefore) {
             .writeObjectEntrySeparator(this);
         } else {
             .beforeObjectEntries(this);
         }
     
         final char[] quoted = name.asQuotedChars();
         if (isEnabled(.)) { // standard
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '"';
             writeRaw(quoted, 0, quoted.length);
             if ( >= ) {
                 _flushBuffer();
             }
             [++] = '"';
         } else { // non-standard, omit quotes
             writeRaw(quoted, 0, quoted.length);
         }
     }
 
     /*
     /**********************************************************
     /* Output method implementations, textual
     /**********************************************************
      */
 
     @Override
     public void writeString(String text)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write text value");
         if (text == null) {
             _writeNull();
             return;
         }
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
         _writeString(text);
         // And finally, closing quotes
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
     }
 
     @Override
     public void writeString(char[] textint offsetint len)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write text value");
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
         _writeString(textoffsetlen);
         // And finally, closing quotes
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
     }
 
     @Override
     public void writeString(SerializableString sstr)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write text value");
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
         // Note: copied from writeRaw:
         char[] text = sstr.asQuotedChars();
         final int len = text.length;
         // Only worth buffering if it's a short write?
         if (len < ) {
             int room =  - ;
             if (len > room) {
                 _flushBuffer();
             }
             System.arraycopy(text, 0, len);
              += len;
         } else {
             // Otherwise, better just pass through:
             _flushBuffer();
             .write(text, 0, len);
         }
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
     }
 
     @Override
     public void writeRawUTF8String(byte[] textint offsetint length)
         throws IOExceptionJsonGenerationException
     {
         // could add support for buffering if we really want it...
         _reportUnsupportedOperation();
     }
 
     @Override
     public void writeUTF8String(byte[] textint offsetint length)
         throws IOExceptionJsonGenerationException
     {
         // could add support for buffering if we really want it...
         _reportUnsupportedOperation();
     }
     
     /*
     /**********************************************************
     /* Output method implementations, unprocessed ("raw")
     /**********************************************************
      */
 
     @Override
     public void writeRaw(String text)
         throws IOExceptionJsonGenerationException
     {
         // Nothing to check, can just output as is
         int len = text.length();
         int room =  - ;
 
         if (room == 0) {
             _flushBuffer();
             room =  - ;
         }
         // But would it nicely fit in? If yes, it's easy
         if (room >= len) {
             text.getChars(0, len);
              += len;
         } else {
             writeRawLong(text);
         }
     }
 
     @Override
     public void writeRaw(String textint startint len)
         throws IOExceptionJsonGenerationException
     {
         // Nothing to check, can just output as is
         int room =  - ;
 
         if (room < len) {
             _flushBuffer();
             room =  - ;
         }
         // But would it nicely fit in? If yes, it's easy
         if (room >= len) {
             text.getChars(startstart+len);
              += len;
         } else {            	
             writeRawLong(text.substring(startstart+len));
         }
     }
 
     // @since 2.1
     @Override
     public void writeRaw(SerializableString textthrows IOExceptionJsonGenerationException {
         writeRaw(text.getValue());
     }
     
     @Override
     public void writeRaw(char[] textint offsetint len)
         throws IOExceptionJsonGenerationException
     {
         // Only worth buffering if it's a short write?
         if (len < ) {
             int room =  - ;
             if (len > room) {
                 _flushBuffer();
             }
             System.arraycopy(textoffsetlen);
              += len;
             return;
         }
         // Otherwise, better just pass through:
         _flushBuffer();
         .write(textoffsetlen);
     }
 
     @Override
     public void writeRaw(char c)
         throws IOExceptionJsonGenerationException
     {
         if ( >= ) {
             _flushBuffer();
         }
         [++] = c;
     }
 
     private void writeRawLong(String text)
         throws IOExceptionJsonGenerationException
     {
         int room =  - ;
         // If not, need to do it by looping
         text.getChars(0, room);
          += room;
         _flushBuffer();
         int offset = room;
         int len = text.length() - room;
 
         while (len > ) {
             int amount = ;
             text.getChars(offsetoffset+amount, 0);
              = 0;
              = amount;
             _flushBuffer();
             offset += amount;
             len -= amount;
         }
         // And last piece (at most length of buffer)
         text.getChars(offsetoffset+len, 0);
          = 0;
          = len;
     }
 
     /*
     /**********************************************************
     /* Output method implementations, base64-encoded binary
     /**********************************************************
      */
 
     @Override
     public void writeBinary(Base64Variant b64variantbyte[] dataint offsetint len)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write binary value");
         // Starting quotes
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
         _writeBinary(b64variantdataoffsetoffset+len);
         // and closing quotes
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
     }
 
     @Override
     public int writeBinary(Base64Variant b64variant,
             InputStream dataint dataLength)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write binary value");
         // Starting quotes
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
         byte[] encodingBuffer = .allocBase64Buffer();
         int bytes;
         try {
             if (dataLength < 0) { // length unknown
                 bytes = _writeBinary(b64variantdataencodingBuffer);
             } else {
                 int missing = _writeBinary(b64variantdataencodingBufferdataLength);
                 if (missing > 0) {
                     _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")");
                 }
                 bytes = dataLength;
             }
         } finally {
             .releaseBase64Buffer(encodingBuffer);
         }
         // and closing quotes
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
         return bytes;
     }
     
     /*
     /**********************************************************
     /* Output method implementations, primitive
     /**********************************************************
      */
 
     @Override
     public void writeNumber(int i)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write number");
         if () {
             _writeQuotedInt(i);
             return;
         }
         // up to 10 digits and possible minus sign
         if (( + 11) >= ) {
             _flushBuffer();
         }
          = NumberOutput.outputInt(i);
     }
 
     private void _writeQuotedInt(int ithrows IOException {
         if (( + 13) >= ) {
             _flushBuffer();
         }
         [++] = '"';
          = NumberOutput.outputInt(i);
         [++] = '"';
     }    
 
     @Override
     public void writeNumber(long l)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write number");
         if () {
             _writeQuotedLong(l);
             return;
         }
         if (( + 21) >= ) {
             // up to 20 digits, minus sign
             _flushBuffer();
         }
          = NumberOutput.outputLong(l);
     }
 
     private void _writeQuotedLong(long lthrows IOException {
         if (( + 23) >= ) {
             _flushBuffer();
         }
         [++] = '"';
          = NumberOutput.outputLong(l);
         [++] = '"';
     }
 
     // !!! 05-Aug-2008, tatus: Any ways to optimize these?
 
     @Override
     public void writeNumber(BigInteger value)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write number");
         if (value == null) {
             _writeNull();
         } else if () {
             _writeQuotedRaw(value);
         } else {
             writeRaw(value.toString());
         }
     }
 
     
     @Override
     public void writeNumber(double d)
         throws IOExceptionJsonGenerationException
     {
         if ( ||
             // [JACKSON-139]
             (((Double.isNaN(d) || Double.isInfinite(d))
                 && isEnabled(.)))) {
             writeString(String.valueOf(d));
             return;
         }
         // What is the max length for doubles? 40 chars?
         _verifyValueWrite("write number");
         writeRaw(String.valueOf(d));
     }
 
     @Override
     public void writeNumber(float f)
         throws IOExceptionJsonGenerationException
     {
         if ( ||
             // [JACKSON-139]
             (((Float.isNaN(f) || Float.isInfinite(f))
                 && isEnabled(.)))) {
             writeString(String.valueOf(f));
             return;
         }
         // What is the max length for floats?
         _verifyValueWrite("write number");
         writeRaw(String.valueOf(f));
     }
 
     @Override
     public void writeNumber(BigDecimal value)
         throws IOExceptionJsonGenerationException
     {
         // Don't really know max length for big decimal, no point checking
         _verifyValueWrite("write number");
         if (value == null) {
             _writeNull();
         } else if () {
             _writeQuotedRaw(value);
         } else {
             writeRaw(value.toString());
         }
     }
 
     @Override
     public void writeNumber(String encodedValue)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write number");
         if () {
             _writeQuotedRaw(encodedValue);            
         } else {
             writeRaw(encodedValue);
         }
     }
 
     private void _writeQuotedRaw(Object valuethrows IOException
     {
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
         writeRaw(value.toString());
         if ( >= ) {
             _flushBuffer();
         }
         [++] = '"';
     }
     
     @Override
     public void writeBoolean(boolean state)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write boolean value");
         if (( + 5) >= ) {
             _flushBuffer();
         }
         int ptr = ;
         char[] buf = ;
         if (state) {
             buf[ptr] = 't';
             buf[++ptr] = 'r';
             buf[++ptr] = 'u';
             buf[++ptr] = 'e';
         } else {
             buf[ptr] = 'f';
             buf[++ptr] = 'a';
             buf[++ptr] = 'l';
             buf[++ptr] = 's';
             buf[++ptr] = 'e';
         }
          = ptr+1;
     }
 
     @Override
     public void writeNull()
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write null value");
         _writeNull();
     }
 
     /*
     /**********************************************************
     /* Implementations for other methods
     /**********************************************************
      */
 
     @Override
     protected void _verifyValueWrite(String typeMsg)
         throws IOExceptionJsonGenerationException
     {
         int status = .writeValue();
         if (status == .) {
             _reportError("Can not "+typeMsg+", expecting field name");
         }
         if ( == null) {
             char c;
             switch (status) {
             case .:
                 c = ',';
                 break;
             case .:
                 c = ':';
                 break;
             case .// root-value separator
                 if ( != null) {
                     writeRaw(.getValue());
                 }
                 return;
             case .:
             default:
                 return;
             }
             if ( >= ) {
                 _flushBuffer();
             }
             [] = c;
             ++;
             return;
         }
         // Otherwise, pretty printer knows what to do...
         _verifyPrettyValueWrite(typeMsgstatus);
     }
 
     protected void _verifyPrettyValueWrite(String typeMsgint status)
         throws IOExceptionJsonGenerationException
     {
         // If we have a pretty printer, it knows what to do:
         switch (status) {
         case .// array
             .writeArrayValueSeparator(this);
             break;
             .writeObjectFieldValueSeparator(this);
             break;
             .writeRootValueSeparator(this);
             break;
         case .:
             // First entry, but of which context?
             if (.inArray()) {
                 .beforeArrayValues(this);
             } else if (.inObject()) {
                 .beforeObjectEntries(this);
             }
             break;
         default:
             _cantHappen();
             break;
         }
     }
 
     /*
     /**********************************************************
     /* Low-level output handling
     /**********************************************************
      */
 
     @Override
     public void flush()
         throws IOException
     {
         _flushBuffer();
         if ( != null) {
             if (isEnabled(.)) {
                 .flush();
             }
         }
     }
 
     @Override
     public void close()
         throws IOException
     {
         super.close();
 
         /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
          *   scopes.
          */
         // First: let's see that we still have buffers...
         if ( != null
             && isEnabled(.)) {
             while (true) {
                 JsonStreamContext ctxt = getOutputContext();
                 if (ctxt.inArray()) {
                     writeEndArray();
                 } else if (ctxt.inObject()) {
                     writeEndObject();
                 } else {
                     break;
                 }
             }
         }
         _flushBuffer();
 
         /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
          *   on the underlying Reader, unless we "own" it, or auto-closing
          *   feature is enabled.
          *   One downside: when using UTF8Writer, underlying buffer(s)
          *   may not be properly recycled if we don't close the writer.
          */
         if ( != null) {
             if (.isResourceManaged() || isEnabled(.)) {
                 .close();
             } else  if (isEnabled(.)) {
                 // If we can't close it, we should at least flush
                 .flush();
             }
         }
         // Internal buffer(s) generator has can now be released as well
         _releaseBuffers();
     }
 
     @Override
     protected void _releaseBuffers()
     {
         char[] buf = ;
         if (buf != null) {
              = null;
             .releaseConcatBuffer(buf);
         }
     }
 
     /*
     /**********************************************************
     /* Internal methods, low-level writing; text, default
     /**********************************************************
      */
 
     private void _writeString(String text)
         throws IOExceptionJsonGenerationException
     {
         /* One check first: if String won't fit in the buffer, let's
          * segment writes. No point in extending buffer to huge sizes
          * (like if someone wants to include multi-megabyte base64
          * encoded stuff or such)
          */
         final int len = text.length();
         if (len > ) { // Let's reserve space for entity at begin/end
             _writeLongString(text);
             return;
         }
 
         // Ok: we know String will fit in buffer ok
         // But do we need to flush first?
         if (( + len) > ) {
             _flushBuffer();
         }
         text.getChars(0, len);
 
         if ( != null) {
             _writeStringCustom(len);
         } else if ( != 0) {
             _writeStringASCII(len);
         } else {
             _writeString2(len);
         }
     }
 
     private void _writeString2(final int len)
         throws IOExceptionJsonGenerationException
     {
         // And then we'll need to verify need for escaping etc:
         int end =  + len;
         final int[] escCodes = ;
         final int escLen = escCodes.length;
 
         output_loop:
         while ( < end) {
             // Fast loop for chars not needing escaping
             escape_loop:
             while (true) {
                 char c = [];
                 if (c < escLen && escCodes[c] != 0) {
                     break escape_loop;
                 }
                 if (++ >= end) {
                     break output_loop;
                 }
             }
 
             // Ok, bumped into something that needs escaping.
             /* First things first: need to flush the buffer.
              * Inlined, as we don't want to lose tail pointer
              */
             int flushLen = ( - );
             if (flushLen > 0) {
                 .write(flushLen);
             }
             /* In any case, tail will be the new start, so hopefully
              * we have room now.
              */
             char c = [++];
             _prependOrWriteCharacterEscape(cescCodes[c]);
         }
     }

    
Method called to write "long strings", strings whose length exceeds output buffer length.
 
     private void _writeLongString(String text)
         throws IOExceptionJsonGenerationException
     {
         // First things first: let's flush the buffer to get some more room
         _flushBuffer();
 
         // Then we can write 
         final int textLen = text.length();
         int offset = 0;
         do {
             int max = ;
             int segmentLen = ((offset + max) > textLen)
                 ? (textLen - offset) : max;
             text.getChars(offsetoffset+segmentLen, 0);
             if ( != null) {
                 _writeSegmentCustom(segmentLen);
             } else if ( != 0) {
                 _writeSegmentASCII(segmentLen);
             } else {
                 _writeSegment(segmentLen);
             }
             offset += segmentLen;
         } while (offset < textLen);
     }

    
Method called to output textual context which has been copied to the output buffer prior to call. If any escaping is needed, it will also be handled by the method.

Note: when called, textual content to write is within output buffer, right after buffered content (if any). That's why only length of that text is passed, as buffer and offset are implied.

    private void _writeSegment(int end)
        throws IOExceptionJsonGenerationException
    {
        final int[] escCodes = ;
        final int escLen = escCodes.length;
    
        int ptr = 0;
        int start = ptr;
        output_loop:
        while (ptr < end) {
            // Fast loop for chars not needing escaping
            char c;
            while (true) {
                c = [ptr];
                if (c < escLen && escCodes[c] != 0) {
                    break;
                }
                if (++ptr >= end) {
                    break;
                }
            }
            // Ok, bumped into something that needs escaping.
            /* First things first: need to flush the buffer.
             * Inlined, as we don't want to lose tail pointer
             */
            int flushLen = (ptr - start);
            if (flushLen > 0) {
                .write(startflushLen);
                if (ptr >= end) {
                    break output_loop;
                }
            }
            ++ptr;
            // So; either try to prepend (most likely), or write directly:
            start = _prependOrWriteCharacterEscape(ptrendcescCodes[c]);
        }
    }
    
    
This method called when the string content is already in a char buffer, and need not be copied for processing.
    private void _writeString(char[] textint offsetint len)
        throws IOExceptionJsonGenerationException
    {
        if ( != null) {
            _writeStringCustom(textoffsetlen);
            return;
        }
        if ( != 0) {
            _writeStringASCII(textoffsetlen);
            return;
        }
        
        /* Let's just find longest spans of non-escapable
         * content, and for each see if it makes sense
         * to copy them, or write through
         */
        len += offset// -> len marks the end from now on
        final int[] escCodes = ;
        final int escLen = escCodes.length;
        while (offset < len) {
            int start = offset;
            while (true) {
                char c = text[offset];
                if (c < escLen && escCodes[c] != 0) {
                    break;
                }
                if (++offset >= len) {
                    break;
                }
            }
            // Short span? Better just copy it to buffer first:
            int newAmount = offset - start;
            if (newAmount < ) {
                // Note: let's reserve room for escaped char (up to 6 chars)
                if (( + newAmount) > ) {
                    _flushBuffer();
                }
                if (newAmount > 0) {
                    System.arraycopy(textstartnewAmount);