Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   package com.fasterxml.jackson.dataformat.smile;
   
   import java.io.*;
   import java.math.BigDecimal;
   import java.math.BigInteger;
   import java.util.Arrays;
   
  
  import static com.fasterxml.jackson.dataformat.smile.SmileConstants.*;

com.fasterxml.jackson.core.JsonGenerator implementation for the experimental "Binary JSON Infoset".

Author(s):
tatu
  
  public class SmileGenerator
      extends GeneratorBase
  {
    
Enumeration that defines all togglable features for Smile generators.
  
      public enum Feature {
        
Whether to write 4-byte header sequence when starting output or not. If disabled, no header is written; this may be useful in embedded cases where context is enough to know that content is encoded using this format. Note, however, that omitting header means that default settings for shared names/string values can not be changed.

Default setting is true, meaning that header will be written.

  
          WRITE_HEADER(true),

        
Whether write byte marker that signifies end of logical content segment (SmileConstants.BYTE_MARKER_END_OF_CONTENT) when SmileGenerator.close() is called or not. This can be useful when outputting multiple adjacent logical content segments (documents) into single physical output unit (file).

Default setting is false meaning that such marker is not written.

  
          WRITE_END_MARKER(false),
        
        
Whether to use simple 7-bit per byte encoding for binary content when output. This is necessary ensure that byte 0xFF will never be included in content output. For other data types this limitation is handled automatically; but since overhead for binary data (14% size expansion, processing overhead) is non-negligible, it is not enabled by default. If no binary data is output, feature has no effect.

Default setting is true, indicating that binary data is quoted as 7-bit bytes instead of written raw.

  
          ENCODE_BINARY_AS_7BIT(true),

        
Whether generator should check if it can "share" field names during generating content or not. If enabled, can replace repeating field names with back references, which are more compact and should faster to decode. Downside is that there is some overhead for writing (need to track existing values, check), as well as decoding.

Since field names tend to repeat quite often, this setting is enabled by default.

  
          CHECK_SHARED_NAMES(true),

        
Whether generator should check if it can "share" short (at most 64 bytes encoded) String value during generating content or not. If enabled, can replace repeating Short String values with back references, which are more compact and should faster to decode. Downside is that there is some overhead for writing (need to track existing values, check), as well as decoding.

Since efficiency of this option depends a lot on type of content being produced, this option is disabled by default, and should only be enabled if it is likely that same values repeat relatively often.

  
          CHECK_SHARED_STRING_VALUES(false)
          ;
  
          protected final boolean _defaultState;
          protected final int _mask;
        
        
Method that calculates bit set (flags) of all features that are enabled by default.
  
          public static int collectDefaults()
          {
              int flags = 0;
              for (Feature f : values()) {
                  if (f.enabledByDefault()) {
                      flags |= f.getMask();
                  }
             }
             return flags;
         }
         
         private Feature(boolean defaultState) {
              = defaultState;
              = (1 << ordinal());
         }
         
         public boolean enabledByDefault() { return ; }
         public int getMask() { return ; }
     }

    
Helper class used for keeping track of possibly shareable String references (for field names and/or short String values)
 
     protected final static class SharedStringNode
     {
         public final String value;
         public final int index;
         public SharedStringNode next;
         
         public SharedStringNode(String valueint indexSharedStringNode next)
         {
             this. = value;
             this. = index;
             this. = next;
         }
     }
    
    
To simplify certain operations, we require output buffer length to allow outputting of contiguous 256 character UTF-8 encoded String value. Length of the longest UTF-8 code point (from Java char) is 3 bytes, and we need both initial token byte and single-byte end marker so we get following value.

Note: actually we could live with shorter one; absolute minimum would be for encoding 64-character Strings.

 
     private final static int MIN_BUFFER_LENGTH = (3 * 256) + 2;
 
     protected final static byte TOKEN_BYTE_LONG_STRING_ASCII = ;
 
     protected final static byte TOKEN_BYTE_INT_32 =  (byte) (. + );
     protected final static byte TOKEN_BYTE_INT_64 =  (byte) (. + );
     protected final static byte TOKEN_BYTE_BIG_INTEGER =  (byte) (. + );
 
     protected final static byte TOKEN_BYTE_FLOAT_32 =  (byte) (. | );
     protected final static byte TOKEN_BYTE_FLOAT_64 =  (byte) (. | );
     protected final static byte TOKEN_BYTE_BIG_DECIMAL =  (byte) (. | );
     
     protected final static int SURR1_FIRST = 0xD800;
     protected final static int SURR1_LAST = 0xDBFF;
     protected final static int SURR2_FIRST = 0xDC00;
     protected final static int SURR2_LAST = 0xDFFF;
 
     protected final static long MIN_INT_AS_LONG = (long.;
     protected final static long MAX_INT_AS_LONG = (long.;
     
     /*
     /**********************************************************
     /* Configuration
     /**********************************************************
      */
 
     final protected IOContext _ioContext;
 
     final protected OutputStream _out;

    
Bit flag composed of bits that indicate which SmileGenerator.Features are enabled.
 
     protected int _smileFeatures;

    
Helper object used for low-level recycling of Smile-generator specific buffers.
 
     
     /*
     /**********************************************************
     /* Output buffering
     /**********************************************************
      */

    
Intermediate buffer in which contents are buffered before being written using _out.
 
     protected byte[] _outputBuffer;

    
Pointer to the next available byte in _outputBuffer
 
     protected int _outputTail = 0;

    
Offset to index after the last valid index in _outputBuffer. Typically same as length of the buffer.
 
     protected final int _outputEnd;

    
Intermediate buffer in which characters of a String are copied before being encoded.
 
     protected char[] _charBuffer;
 
     protected final int _charBufferLength;
    
    
Let's keep track of how many bytes have been output, may prove useful when debugging. This does not include bytes buffered in the output buffer, just bytes that have been written using underlying stream writer.
 
     protected int _bytesWritten;
     
     /*
     /**********************************************************
     /* Shared String detection
     /**********************************************************
      */

    
Raw data structure used for checking whether field name to write can be output using back reference or not.
 
     protected SharedStringNode[] _seenNames;
    
    
Number of entries in _seenNames; -1 if no shared name detection is enabled
 
     protected int _seenNameCount;

    
Raw data structure used for checking whether String value to write can be output using back reference or not.
 
     protected SharedStringNode[] _seenStringValues;
    
    
Number of entries in _seenStringValues; -1 if no shared text value detection is enabled
 
     protected int _seenStringValueCount;

    
Flag that indicates whether the output buffer is recycable (and needs to be returned to recycler once we are done) or not.
 
     protected boolean _bufferRecyclable;
 
     /*
     /**********************************************************
     /* Thread-local recycling
     /**********************************************************
      */
    
    
This ThreadLocal contains a java.lang.ref.SoftReference to a buffer recycler used to provide a low-cost buffer recycling for Smile-specific buffers.
 
     
     /*
     /**********************************************************
     /* Life-cycle
     /**********************************************************
      */
     
     public SmileGenerator(IOContext ctxtint jsonFeaturesint smileFeatures,
             ObjectCodec codecOutputStream out)
     {
         super(jsonFeaturescodec);
          = smileFeatures;
          = ctxt;
          = out;
          = true;
          = ctxt.allocWriteEncodingBuffer();
          = .;
          = ctxt.allocConcatBuffer();
          = .;
         // let's just sanity check to prevent nasty odd errors
         if ( < ) {
             throw new IllegalStateException("Internal encoding buffer length ("+
                     +") too short, must be at least "+);
         }
         if ((smileFeatures & ..getMask()) == 0) {
              = null;
              = -1;
         } else {
             if ( == null) {
             }
              = 0;
         }
 
         if ((smileFeatures & ..getMask()) == 0) {
              = null;
              = -1;
         } else {
             if ( == null) {
             }
              = 0;
         }
 }
 
     public SmileGenerator(IOContext ctxtint jsonFeaturesint smileFeatures,
             ObjectCodec codecOutputStream outbyte[] outputBufferint offsetboolean bufferRecyclable)
     {
         super(jsonFeaturescodec);
          = smileFeatures;
          = ctxt;
          = out;
          = bufferRecyclable;
          = offset;
          = outputBuffer;
          = .;
          = ctxt.allocConcatBuffer();
          = .;
         // let's just sanity check to prevent nasty odd errors
         if ( < ) {
             throw new IllegalStateException("Internal encoding buffer length ("+
                     +") too short, must be at least "+);
         }
         if ((smileFeatures & ..getMask()) == 0) {
              = null;
              = -1;
         } else {
             if ( == null) {
             }
              = 0;
         }
 
         if ((smileFeatures & ..getMask()) == 0) {
              = null;
              = -1;
         } else {
             if ( == null) {
             }
              = 0;
         }
     }

    
Method that can be called to explicitly write Smile document header. Note that usually you do not need to call this for first document to output, but rather only if you intend to write multiple root-level documents with same generator (and even in that case this is optional thing to do). As a result usually only SmileFactory calls this method.
 
     public void writeHeader() throws IOException
     {
     	int last = ;
         if (( & ..getMask()) != 0) {
             last |= .;
         }
         if (( & ..getMask()) != 0) {
             last |= .;
         }
         if (( & ..getMask()) == 0) {
             last |= .;
         }
         _writeBytes(, (bytelast);
     }
 
     protected final static SmileBufferRecycler<SharedStringNode_smileBufferRecycler()
     {
         SmileBufferRecycler<SharedStringNodebr = (ref == null) ? null : ref.get();
 
         if (br == null) {
             br = new SmileBufferRecycler<SharedStringNode>();
         }
         return br;
     }
 
     /*                                                                                       
     /**********************************************************                              
     /* Versioned                                                                             
     /**********************************************************                              
      */
 
     @Override
     public Version version() {
         return .;
     }
 
     /*
     /**********************************************************
     /* Capability introspection
     /**********************************************************
      */
 
     @Override
     public boolean canWriteBinaryNatively() {
         return true;
     }
 
     /*
     /**********************************************************
     /* Overridden methods, configuration
     /**********************************************************
      */

    
No way (or need) to indent anything, so let's block any attempts. (should we throw an exception instead?)
 
     @Override
     {
         return this;
     }

    
No way (or need) to indent anything, so let's block any attempts. (should we throw an exception instead?)
 
     @Override
         return this;
     }
 
     @Override
     public Object getOutputTarget() {
         return ;
     }
     
     /*
     /**********************************************************
     /* Overridden methods, write methods
     /**********************************************************
      */
 
     /* And then methods overridden to make final, streamline some
      * aspects...
      */
 
     @Override
     public final void writeFieldName(String name)  throws IOExceptionJsonGenerationException
     {
             _reportError("Can not write a field name, expecting a value");
         }
         _writeFieldName(name);
     }
 
     @Override
     public final void writeFieldName(SerializableString name)
         throws IOExceptionJsonGenerationException
     {
         // Object is a value, need to verify it's allowed
             _reportError("Can not write a field name, expecting a value");
         }
         _writeFieldName(name);
     }
 
     @Override
     public final void writeStringField(String fieldNameString value)
         throws IOExceptionJsonGenerationException
     {
         if (.writeFieldName(fieldName) == .) {
             _reportError("Can not write a field name, expecting a value");
         }
         _writeFieldName(fieldName);
         writeString(value);
     }
     
     /*
     /**********************************************************
     /* Extended API, configuration
     /**********************************************************
      */
 
     public SmileGenerator enable(Feature f) {
          |= f.getMask();
         return this;
     }
 
     public SmileGenerator disable(Feature f) {
          &= ~f.getMask();
         return this;
     }
 
     public final boolean isEnabled(Feature f) {
         return ( & f.getMask()) != 0;
     }
 
     public SmileGenerator configure(Feature fboolean state) {
         if (state) {
             enable(f);
         } else {
             disable(f);
         }
         return this;
     }
 
     /*
     /**********************************************************
     /* Extended API, other
     /**********************************************************
      */

    
Method for directly inserting specified byte in output at current position.

NOTE: only use this method if you really know what you are doing.

 
     public void writeRaw(byte bthrows IOExceptionJsonGenerationException
     {
     }

    
Method for directly inserting specified bytes in output at current position.

NOTE: only use this method if you really know what you are doing.

 
     public void writeBytes(byte[] dataint offsetint lenthrows IOException
     {
         _writeBytes(dataoffsetlen);
     }
     
     /*
     /**********************************************************
     /* Output method implementations, structural
     /**********************************************************
      */
 
     @Override
     public final void writeStartArray() throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("start an array");
     }
 
     @Override
     public final void writeEndArray() throws IOExceptionJsonGenerationException
     {
         if (!.inArray()) {
             _reportError("Current context not an ARRAY but "+.getTypeDesc());
         }
          = .getParent();
     }
 
     @Override
     public final void writeStartObject() throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("start an object");
     }
 
     @Override
     public final void writeEndObject() throws IOExceptionJsonGenerationException
     {
         if (!.inObject()) {
             _reportError("Current context not an object but "+.getTypeDesc());
         }
          = .getParent();
     }
 
     private final void _writeFieldName(String name)
         throws IOExceptionJsonGenerationException
     {
         int len = name.length();
         if (len == 0) {
             _writeByte();
             return;
         }
         // First: is it something we can share?
         if ( >= 0) {
             int ix = _findSeenName(name);
             if (ix >= 0) {
                 _writeSharedNameReference(ix);
                 return;
             }
         }
         if (len > ) { // can not be a 'short' String; off-line (rare case)
             _writeNonShortFieldName(namelen);
             return;
         }
 
         // first: ensure we have enough space
         if (( + ) >= ) {
             _flushBuffer();
         }
         // then let's copy String chars to char buffer, faster than using getChar (measured, profiled)
         name.getChars(0, len, 0);
         int origOffset = ;
         ++// to reserve space for type token
         int byteLen = _shortUTF8Encode(, 0, len);
         byte typeToken;
         
         // ASCII?
         if (byteLen == len) {
             if (byteLen <= ) { // yes, is short indeed
                 typeToken = (byte) (( - 1) + byteLen);
             } else { // longer albeit ASCII
                 typeToken = ;
                 // and we will need String end marker byte
                 [++] = ;
             }
         } else { // not all ASCII
             if (byteLen <= ) { // yes, is short indeed
                 // note: since 2 is smaller allowed length, offset differs from one used for
                 typeToken = (byte) (( - 2) + byteLen);
             } else { // nope, longer non-ASCII Strings
                 typeToken = ;
                 // and we will need String end marker byte
                 [++] = ;
             }
         }
         // and then sneak in type token now that know the details
         [origOffset] = typeToken;
         // Also, keep track if we can use back-references (shared names)
         if ( >= 0) {
             _addSeenName(name);
         }
     }
 
     private final void _writeNonShortFieldName(final String namefinal int len)
         throws IOExceptionJsonGenerationException
     {
         // can we still make a temp copy?
         if (len > ) { // nah, not even that
             _slowUTF8Encode(name);
         } else { // yep.
             name.getChars(0, len, 0);
             // but will encoded version fit in buffer?
             int maxLen = len + len + len;
             if (maxLen <= .) { // yes indeed
                 if (( + maxLen) >= ) {
                     _flushBuffer();
                 }
                  _shortUTF8Encode(, 0, len);
             } else { // nope, need bit slower variant
                 _mediumUTF8Encode(, 0, len);
             }
         }
         if ( >= 0) {
             _addSeenName(name);
         }
         if ( >= ) {
             _flushBuffer();
         }
         [++] = ;                
     }
     
     protected final void _writeFieldName(SerializableString name)
         throws IOExceptionJsonGenerationException
     {
         final int charLen = name.charLength();
         if (charLen == 0) {
             _writeByte();
             return;
         }
         // Then: is it something we can share?
         if ( >= 0) {
             int ix = _findSeenName(name.getValue());
             if (ix >= 0) {
                 _writeSharedNameReference(ix);
                 return;
             }
         }
         final byte[] bytes = name.asUnquotedUTF8();
         final int byteLen = bytes.length;
         if (byteLen != charLen) {
             _writeFieldNameUnicode(namebytes);
             return;
         }
         // Common case: short ASCII name that fits in buffer as is
         if (byteLen <= ) {
             // output buffer is bigger than what we need, always, so
             if (( + byteLen) >= ) { // need marker byte and actual bytes
                 _flushBuffer();
             }
             [++] = (byte) (( - 1) + byteLen);
             System.arraycopy(bytes, 0, byteLen);
              += byteLen;
         } else {
             _writeLongAsciiFieldName(bytes);
         }
         // Also, keep track if we can use back-references (shared names)
         if ( >= 0) {
             _addSeenName(name.getValue());
         }
     }
 
     private final void _writeLongAsciiFieldName(byte[] bytes)
         throws IOExceptionJsonGenerationException
     {
         final int byteLen = bytes.length;
         if ( >= ) {
             _flushBuffer();
         }
         // Ok. Enough room?
         if (( + byteLen + 1) < ) {
             System.arraycopy(bytes, 0, byteLen);
              += byteLen;
         } else {
             _flushBuffer();
             // either way, do intermediate copy if name is relatively short
             // Need to copy?
             if (byteLen < ) {
                 System.arraycopy(bytes, 0, byteLen);
                  += byteLen;
             } else {
                 // otherwise, just write as is
                 if ( > 0) {
                     _flushBuffer();
                 }
                 .write(bytes, 0, byteLen);
             }
         }
     }
 
     protected final void _writeFieldNameUnicode(SerializableString namebyte[] bytes)
         throws IOExceptionJsonGenerationException
     {
         final int byteLen = bytes.length;
 
         // Common case: short Unicode name that fits in output buffer
         if (byteLen <= ) {
             if (( + byteLen) >= ) { // need marker byte and actual bytes
                 _flushBuffer();
             }
             // note: since 2 is smaller allowed length, offset differs from one used for
             [++] = (byte) (( - 2) + byteLen);
 
             System.arraycopy(bytes, 0, byteLen);
              += byteLen;
             // Also, keep track if we can use back-references (shared names)
             if ( >= 0) {
                 _addSeenName(name.getValue());
             }
             return;
         }
         if ( >= ) {
             _flushBuffer();
         }
         // Ok. Enough room?
         if (( + byteLen + 1) < ) {
             System.arraycopy(bytes, 0, byteLen);
              += byteLen;
         } else {
             _flushBuffer();
             // either way, do intermediate copy if name is relatively short
             // Need to copy?
             if (byteLen < ) {
                 System.arraycopy(bytes, 0, byteLen);
                  += byteLen;
             } else {
                 // otherwise, just write as is
                 if ( > 0) {
                     _flushBuffer();
                 }
                 .write(bytes, 0, byteLen);
             }
         }
         // Also, keep track if we can use back-references (shared names)
         if ( >= 0) {
             _addSeenName(name.getValue());
         }
     }
 
     private final void _writeSharedNameReference(int ix)
         throws IOException,JsonGenerationException
     {
         // 03-Mar-2011, tatu: Related to [JACKSON-525], let's add a sanity check here
         if (ix >= ) {
             throw new IllegalArgumentException("Internal error: trying to write shared name with index "+ix
                     +"; but have only seen "++" so far!");
         }
         if (ix < 64) {
             _writeByte((byte) ( + ix));
         } else {
             _writeBytes(((byte) ( + (ix >> 8))), (byteix);
         } 
     }    
     
     /*
     /**********************************************************
     /* Output method implementations, textual
     /**********************************************************
      */
 
     @Override
     public void writeString(String textthrows IOException,JsonGenerationException
     {
         if (text == null) {
             writeNull();
             return;
         }
         _verifyValueWrite("write String value");
         int len = text.length();
         if (len == 0) {
             _writeByte();
             return;
         }
         // Longer string handling off-lined
         if (len > ) {
             _writeNonSharedString(textlen);
             return;
         }
         // Then: is it something we can share?
         if ( >= 0) {
             int ix = _findSeenStringValue(text);
             if (ix >= 0) {
                 _writeSharedStringValueReference(ix);
                 return;
             }
         }
             
         // possibly short string (but not necessarily)
         // first: ensure we have enough space
         if (( + ) >= ) {
             _flushBuffer();
         }
         // then let's copy String chars to char buffer, faster than using getChar (measured, profiled)
         text.getChars(0, len, 0);
         int origOffset = ;
         ++// to leave room for type token
         int byteLen = _shortUTF8Encode(, 0, len);
         if (byteLen <= ) { // yes, is short indeed
             // plus keep reference, if it could be shared:
             if ( >= 0) {
                 _addSeenStringValue(text);
             }
             if (byteLen == len) { // and all ASCII
                 [origOffset] = (byte) (( - 1) + byteLen);
             } else { // not just ASCII
                 // note: since length 1 can not be used here, value range is offset by 2, not 1
                 [origOffset] = (byte) (( - 2) +  byteLen);
             }
         } else { // nope, longer String 
             [origOffset] = (byteLen == len) ? 
                     : .;
             // and we will need String end marker byte
             [++] = ;
         }
     }
 
     private final void _writeSharedStringValueReference(int ix)
         throws IOException,JsonGenerationException
     {
         // 03-Mar-2011, tatu: Related to [JACKSON-525], let's add a sanity check here
         if (ix >= ) {
             throw new IllegalArgumentException("Internal error: trying to write shared String value with index "+ix
                     +"; but have only seen "++" so far!");
         }
         if (ix < 31) { // add 1, as byte 0 is omitted
             _writeByte((byte) ( + 1 + ix));
         } else {
             _writeBytes(((byte) ( + (ix >> 8))), (byteix);
         }
     }    
    
    
Helper method called to handle cases where String value to write is known to be long enough not to be shareable.
 
     private final void _writeNonSharedString(final String textfinal int len)
         throws IOException,JsonGenerationException
     {
         // First: can we at least make a copy to char[]?
         if (len > ) { // nope; need to skip copy step (alas; this is slower)
             _slowUTF8Encode(text);
             _writeByte();
             return;
         }
         text.getChars(0, len, 0);
         // Expansion can be 3x for Unicode; and then there's type byte and end marker, so:
         int maxLen = len + len + len + 2;
         // Next: does it always fit within output buffer?
         if (maxLen > .) { // nope
             // can't rewrite type buffer, so can't speculate it might be all-ASCII
             _mediumUTF8Encode(, 0, len);
             _writeByte();
             return;
         }
         
         if (( + maxLen) >= ) {
             _flushBuffer();
         }
         int origOffset = ;
         // can't say for sure if it's ASCII or Unicode, so:
         int byteLen = _shortUTF8Encode(, 0, len);
         // If not ASCII, fix type:
         if (byteLen > len) {
             [origOffset] = .;
         }
         [++] = ;                
     }
     
     @Override
     public void writeString(char[] textint offsetint lenthrows IOExceptionJsonGenerationException
     {
         // Shared strings are tricky; easiest to just construct String, call the other method
         if (len <=  &&  >= 0 && len > 0) {
             writeString(new String(textoffsetlen));
             return;
         }
         _verifyValueWrite("write String value");
         if (len == 0) {
             _writeByte();
             return;
         }
         if (len <= ) { // possibly short strings (not necessarily)
             // first: ensure we have enough space
             if (( + ) >= ) {
                 _flushBuffer();
             }
             int origOffset = ;
             ++// to leave room for type token
             int byteLen = _shortUTF8Encode(textoffsetoffset+len);
             byte typeToken;
             if (byteLen <= ) { // yes, is short indeed
                 if (byteLen == len) { // and all ASCII
                     typeToken = (byte) (( - 1) + byteLen);
                 } else { // not just ASCII
                     typeToken = (byte) (( - 2) + byteLen);
                 }
             } else { // nope, longer non-ASCII Strings
                 typeToken = .;
                 // and we will need String end marker byte
                 [++] = ;
             }
             // and then sneak in type token now that know the details
             [origOffset] = typeToken;
         } else { // "long" String, never shared
             // but might still fit within buffer?
             int maxLen = len + len + len + 2;
             if (maxLen <= .) { // yes indeed
                 if (( + maxLen) >= ) {
                     _flushBuffer();
                 }
                 int origOffset = ;
                 _writeByte(.);
                 int byteLen = _shortUTF8Encode(textoffsetoffset+len);
                 // if it's ASCII, let's revise our type determination (to help decoder optimize)
                 if (byteLen == len) {
                     [origOffset] = ;
                 }
                 [++] = ;
             } else {
                 _writeByte(.);
                 _mediumUTF8Encode(textoffsetoffset+len);
                 _writeByte();
             }
         }
     }
 
     @Override
     public final void writeString(SerializableString sstr)
         throws IOExceptionJsonGenerationException
     {
         _verifyValueWrite("write String value");
         // First: is it empty?
         String str = sstr.getValue();
         int len = str.length();
         if (len == 0) {
             _writeByte();
             return;
         }
         // Second: something we can share?
        if (len <=  &&  >= 0) {
            int ix = _findSeenStringValue(str);
            if (ix >= 0) {
                _writeSharedStringValueReference(ix);
                return;
            }
        }
        // If not, use pre-encoded version
        byte[] raw = sstr.asUnquotedUTF8();
        final int byteLen = raw.length;
        
        if (byteLen <= ) { // short string
            // first: ensure we have enough space
            if (( + byteLen + 1) >= ) {
                _flushBuffer();
            }
            // ASCII or Unicode?
            int typeToken = (byteLen == len)
                    ? (( - 1) + byteLen)
                    : (( - 2) + byteLen)
                    ;
            [++] = (bytetypeToken;
            System.arraycopy(raw, 0, byteLen);
             += byteLen;
            // plus keep reference, if it could be shared:
            if ( >= 0) {
                _addSeenStringValue(sstr.getValue());
            }
        } else { // "long" String, never shared
            // but might still fit within buffer?
            byte typeToken = (byteLen == len) ? 
                    : .;
            _writeByte(typeToken);
            _writeBytes(raw, 0, raw.length);
            _writeByte();
        }
    }
    @Override
    public void writeRawUTF8String(byte[] textint offsetint len)
        throws IOExceptionJsonGenerationException
    {
        _verifyValueWrite("write String value");
        // first: is it empty String?
        if (len == 0) {
            _writeByte();
            return;
        }
        // Sanity check: shared-strings incompatible with raw String writing
        if ( >= 0) {
            throw new UnsupportedOperationException("Can not use direct UTF-8 write methods when 'Feature.CHECK_SHARED_STRING_VALUES' enabled");
        } 
        /* Other practical limitation is that we do not really know if it might be
         * ASCII or not; and figuring it out is rather slow. So, best we can do is
         * to declare we do not know it is ASCII (i.e. "is Unicode").
         */
        if (len <= ) { // up to 65 Unicode bytes
            // first: ensure we have enough space
            if (( + len) >= ) { // bytes, plus one for type indicator
                _flushBuffer();
            }
            /* 11-Feb-2011, tatu: As per [JACKSON-492], mininum length for "Unicode"
             *    String is 2; 1 byte length must be ASCII.
             */
            if (len == 1) {
                [++] = // length of 1 cancels out (len-1)
                [++] = text[offset];
            } else {
                [++] = (byte) (( - 2) + len);
                System.arraycopy(textoffsetlen);
                 += len;
            }
        } else { // "long" String
            // but might still fit within buffer?
            int maxLen = len + len + len + 2;
            if (maxLen <= .) { // yes indeed
                if (( + maxLen) >= ) {
                    _flushBuffer();
                }
                System.arraycopy(textoffsetlen);
                 += len;
                [++] = ;
            } else {
                _writeBytes(textoffsetlen);
                _writeByte();
            }
        }
    }
    @Override
    public final void writeUTF8String(byte[] textint offsetint len)
        throws IOExceptionJsonGenerationException
    {
        // Since no escaping is needed, same as 'writeRawUTF8String'
        writeRawUTF8String(textoffsetlen);
    }
    
    /*
    /**********************************************************
    /* Output method implementations, unprocessed ("raw")
    /**********************************************************
     */
    @Override
    public void writeRaw(String textthrows IOExceptionJsonGenerationException {
        throw _notSupported();
    }
    @Override
    public void writeRaw(String textint offsetint lenthrows IOExceptionJsonGenerationException {
        throw _notSupported();
    }
    @Override
    public void writeRaw(char[] textint offsetint lenthrows IOExceptionJsonGenerationException {
        throw _notSupported();
    }
    @Override
    public void writeRaw(char cthrows IOExceptionJsonGenerationException {
        throw _notSupported();
    }
    @Override
    public void writeRawValue(String textthrows IOExceptionJsonGenerationException {
        throw _notSupported();
    }
    @Override
    public void writeRawValue(String textint offsetint lenthrows IOExceptionJsonGenerationException {
        throw _notSupported();
    }
    @Override
    public void writeRawValue(char[] textint offsetint lenthrows IOExceptionJsonGenerationException {
        throw _notSupported();
    }
    
    /*
    /**********************************************************
    /* Output method implementations, base64-encoded binary
    /**********************************************************
     */
    @Override
    public void writeBinary(Base64Variant b64variantbyte[] dataint offsetint lenthrows IOExceptionJsonGenerationException
    {
        if (data == null) {
            writeNull();
            return;
 &nb