Class StreamCharBuffer

java.lang.Object
groovy.lang.GroovyObjectSupport
org.grails.buffer.StreamCharBuffer
All Implemented Interfaces:
groovy.lang.GroovyObject, groovy.lang.Writable, Externalizable, Serializable, CharSequence, Cloneable, Encodeable, EncodedAppenderWriterFactory, StreamEncodeable, StreamingEncoderWritable

public class StreamCharBuffer extends groovy.lang.GroovyObjectSupport implements groovy.lang.Writable, CharSequence, Externalizable, Encodeable, StreamEncodeable, StreamingEncoderWritable, EncodedAppenderWriterFactory, Cloneable

StreamCharBuffer is a multipurpose in-memory buffer that can replace JDK in-memory buffers (StringBuffer, StringBuilder, StringWriter).

Grails GSP rendering uses this class as a buffer that is optimized for performance.

StreamCharBuffer keeps the buffer in a linked list of "chunks". The main difference compared to JDK in-memory buffers (StringBuffer, StringBuilder and StringWriter) is that the buffer can be held in several smaller buffers ("chunks" here). In JDK in-memory buffers, the buffer has to be expanded whenever it gets filled up. The old buffer's data is copied to the new one and the old one is discarded. In StreamCharBuffer, there are several ways to prevent unnecessary allocation and copy operations. The StreamCharBuffer contains a linked list of different type of chunks: char arrays, java.lang.String chunks and other StreamCharBuffers as sub chunks. A StringChunk is appended to the linked list whenever a java.lang.String of a length that exceeds the "stringChunkMinSize" value is written to the buffer.

Grails tag libraries also use a StreamCharBuffer to "capture" the output of the taglib and return it to the caller. The buffer can be appended to it's parent buffer directly without extra object generation (like converting to java.lang.String in between). for example this line of code in a taglib would just append the buffer returned from the body closure evaluation to the buffer of the taglib:
out << body()
other example:
out << g.render(template: '/some/template', model:[somebean: somebean])
There's no extra java.lang.String generation overhead.

There's a java.io.Writer interface for appending character data to the buffer and a java.io.Reader interface for reading data.

Each getReader() call will create a new reader instance that keeps it own state.
There is a alternative method getReader(boolean) for creating the reader. When reader is created by calling getReader(true), the reader will remove already read characters from the buffer. In this mode only a single reader instance is supported.

There's also several other options for reading data:
readAsCharArray()reads the buffer to a char[] array
readAsString() reads the buffer and wraps the char[] data as a String
writeTo(Writer) writes the buffer to a java.io.Writer
toCharArray() returns the buffer as a char[] array, caches the return value internally so that this method can be called several times.
toString() returns the buffer as a String, caches the return value internally

By using the "connectTo" method, one can connect the buffer directly to a target java.io.Writer. The internal buffer gets flushed automaticly to the target whenever the buffer gets filled up. See connectTo(Writer).

This class is not thread-safe. Object instances of this class are intended to be used by a single Thread. The Reader and Writer interfaces can be open simultaneous and the same Thread can write and read several times.

Main operation principle:

StreamCharBuffer keeps the buffer in a linked link of "chunks".
The main difference compared to JDK in-memory buffers (StringBuffer, StringBuilder and StringWriter) is that the buffer can be held in several smaller buffers ("chunks" here).
In JDK in-memory buffers, the buffer has to be expanded whenever it gets filled up. The old buffer's data is copied to the new one and the old one is discarded.
In StreamCharBuffer, there are several ways to prevent unnecessary allocation and copy operations.

There can be several different type of chunks: char arrays ( CharBufferChunk), String chunks (StringChunk) and other StreamCharBuffers as sub chunks (StreamCharBufferSubChunk).

Child StreamCharBuffers can be changed after adding to parent buffer. The flush() method must be called on the child buffer's Writer to notify the parent that the child buffer's content has been changed (used for calculating total size).

A StringChunk is appended to the linked list whenever a java.lang.String of a length that exceeds the "stringChunkMinSize" value is written to the buffer.

If the buffer is in "connectTo" mode, any String or char[] that's length is over writeDirectlyToConnectedMinSize gets written directly to the target. The buffer content will get fully flushed to the target before writing the String or char[].

There can be several targets "listening" the buffer in "connectTo" mode. The same content will be written to all targets.

Growable chunksize: By default, a newly allocated chunk's size will grow based on the total size of all written chunks.
The default growProcent value is 100. If the total size is currently 1024, the newly created chunk will have a internal buffer that's size is 1024.
Growable chunksize can be turned off by setting the growProcent to 0.
There's a default maximum chunksize of 1MB by default. The minimum size is the initial chunksize size.

System properties to change default configuration parameters:

System Property name Description Default value
streamcharbuffer.chunksize default chunk size - the size the first allocated buffer 512
streamcharbuffer.maxchunksize maximum chunk size - the maximum size of the allocated buffer 1048576
streamcharbuffer.growprocent growing buffer percentage - the newly allocated buffer is defined by total_size * (growpercent/100) 100
streamcharbuffer.subbufferchunkminsize minimum size of child StreamCharBuffer chunk - if the size is smaller, the content is copied to the parent buffer 512
streamcharbuffer.substringchunkminsize minimum size of String chunks - if the size is smaller, the content is copied to the buffer 512
streamcharbuffer.chunkminsize minimum size of chunk that gets written directly to the target in connected mode. 256
Configuration values can also be changed for each instance of StreamCharBuffer individually. Default values are defined with System Properties.

See Also:
  • Constructor Details

    • StreamCharBuffer

      public StreamCharBuffer()
    • StreamCharBuffer

      public StreamCharBuffer(int chunkSize)
    • StreamCharBuffer

      public StreamCharBuffer(int chunkSize, int growProcent)
    • StreamCharBuffer

      public StreamCharBuffer(int chunkSize, int growProcent, int maxChunkSize)
  • Method Details

    • isPreferSubChunkWhenWritingToOtherBuffer

      public boolean isPreferSubChunkWhenWritingToOtherBuffer()
    • setPreferSubChunkWhenWritingToOtherBuffer

      public void setPreferSubChunkWhenWritingToOtherBuffer(boolean prefer)
    • notifyPreferSubChunkEnabled

      protected void notifyPreferSubChunkEnabled()
    • reset

      public final void reset()
    • reset

      public final void reset(boolean resetChunkSize)
      resets the state of this buffer (empties it)
      Parameters:
      resetChunkSize -
    • clear

      public final void clear()
      Clears the buffer and notifies the parents of this buffer of the change.
    • connectTo

      public final void connectTo(Writer w)
      Connect this buffer to a target Writer. When the buffer (a chunk) get filled up, it will automaticly write it's content to the Writer
      Parameters:
      w -
    • connectTo

      public final void connectTo(Writer w, boolean autoFlush)
    • encodeInStreamingModeTo

      public final void encodeInStreamingModeTo(EncoderAware encoderLookup, EncodingStateRegistryLookup encodingStateRegistryLookup, boolean autoFlush, Writer w)
    • encodeInStreamingModeTo

      public final void encodeInStreamingModeTo(EncoderAware encoderLookup, EncodingStateRegistryLookup encodingStateRegistryLookup, boolean autoFlush, StreamCharBuffer.LazyInitializingWriter... writers)
    • createEncodingInitializer

      public StreamCharBuffer.LazyInitializingWriter createEncodingInitializer(EncoderAware encoderLookup, EncodingStateRegistryLookup encodingStateRegistryLookup, StreamCharBuffer.LazyInitializingWriter... writers)
    • connectTo

      public final void connectTo(StreamCharBuffer.LazyInitializingWriter w)
    • connectTo

      public final void connectTo(StreamCharBuffer.LazyInitializingWriter w, boolean autoFlush)
    • removeConnections

      public final void removeConnections()
    • getSubStringChunkMinSize

      public int getSubStringChunkMinSize()
    • setSubStringChunkMinSize

      public void setSubStringChunkMinSize(int size)
      Minimum size for a String to be added as a StringChunk instead of copying content to the char[] buffer of the current StreamCharBufferChunk
      Parameters:
      size -
    • getSubBufferChunkMinSize

      public int getSubBufferChunkMinSize()
    • setSubBufferChunkMinSize

      public void setSubBufferChunkMinSize(int size)
    • getWriteDirectlyToConnectedMinSize

      public int getWriteDirectlyToConnectedMinSize()
    • setWriteDirectlyToConnectedMinSize

      public void setWriteDirectlyToConnectedMinSize(int size)
      Minimum size for a String or char[] to get written directly to connected writer (in "connectTo" mode).
      Parameters:
      size -
    • getChunkMinSize

      public int getChunkMinSize()
    • setChunkMinSize

      public void setChunkMinSize(int size)
    • getWriter

      public Writer getWriter()
      Writer interface for adding/writing data to the buffer.
      Returns:
      the Writer
    • getReader

      public Reader getReader()
      Creates a new Reader instance for reading/consuming data from the buffer. Each call creates a new instance that will keep it's reading state. There can be several readers on the buffer. (single thread only supported)
      Returns:
      the Reader
    • getReader

      public Reader getReader(boolean removeAfterReading)
      Like getReader(), but when removeAfterReading is true, the read data will be removed from the buffer.
      Parameters:
      removeAfterReading -
      Returns:
      the Reader
    • writeTo

      public Writer writeTo(Writer target) throws IOException
      Writes the buffer content to a target java.io.Writer
      Specified by:
      writeTo in interface groovy.lang.Writable
      Parameters:
      target -
      Throws:
      IOException
    • writeTo

      public void writeTo(Writer target, boolean flushTarget, boolean emptyAfter) throws IOException
      Writes the buffer content to a target java.io.Writer
      Parameters:
      target - Writer
      flushTarget - calls target.flush() before finishing
      emptyAfter - empties the buffer if true
      Throws:
      IOException
    • emptyAfterReading

      protected void emptyAfterReading()
    • toString

      public String toString()
      Reads (and empties) the buffer to a String, but caches the return value for subsequent calls. If more content has been added between 2 calls, the returned value will be joined from the previously cached value and the data read from the buffer.
      Specified by:
      toString in interface CharSequence
      Overrides:
      toString in class Object
      See Also:
    • readToSingleStringChunk

      public org.grails.buffer.StreamCharBuffer.StringChunk readToSingleStringChunk(boolean registerEncodingState)
    • markEncoded

      public void markEncoded(org.grails.buffer.StreamCharBuffer.StringChunk strChunk)
    • hashCode

      public int hashCode()
      Uses String's hashCode to support compatibility with String instances in maps, sets, etc.
      Overrides:
      hashCode in class Object
      See Also:
    • equals

      public boolean equals(Object o)
      equals uses String.equals to check for equality to support compatibility with String instances in maps, sets, etc.
      Overrides:
      equals in class Object
      See Also:
    • plus

      public String plus(String value)
    • plus

      public String plus(Object value)
    • toCharArray

      public char[] toCharArray()
      Reads the buffer to a char[]. Caches the result if there aren't any readers.
      Returns:
      the chars
    • dumpEncodedParts

      public List<StreamCharBuffer.EncodedPart> dumpEncodedParts()
    • size

      public int size()
    • isEmpty

      public boolean isEmpty()
      Specified by:
      isEmpty in interface CharSequence
    • appendStreamCharBufferChunk

      public void appendStreamCharBufferChunk(StreamCharBuffer subBuffer) throws IOException
      Throws:
      IOException
    • appendStreamCharBufferChunk

      public void appendStreamCharBufferChunk(StreamCharBuffer subBuffer, List<Encoder> encoders) throws IOException
      Throws:
      IOException
    • isConnectedMode

      public boolean isConnectedMode()
    • isChunkSizeResizeable

      protected boolean isChunkSizeResizeable()
    • resizeChunkSizeAsProcentageOfTotalSize

      protected void resizeChunkSizeAsProcentageOfTotalSize()
    • arrayCopy

      protected static final void arrayCopy(char[] src, int srcPos, char[] dest, int destPos, int length)
    • charAt

      public char charAt(int index)
      Specified by:
      charAt in interface CharSequence
    • length

      public int length()
      Specified by:
      length in interface CharSequence
    • subSequence

      public CharSequence subSequence(int start, int end)
      Specified by:
      subSequence in interface CharSequence
    • asBoolean

      public boolean asBoolean()
    • bufferChanged

      protected boolean bufferChanged(StreamCharBuffer buffer)
    • getCurrentParentBuffers

      protected List<StreamCharBuffer> getCurrentParentBuffers()
    • notifyBufferChange

      protected void notifyBufferChange()
    • getBufferChangesCounter

      public int getBufferChangesCounter()
    • markBufferChanged

      protected int markBufferChanged()
    • clone

      public StreamCharBuffer clone()
      Overrides:
      clone in class Object
    • readExternal

      public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
      Specified by:
      readExternal in interface Externalizable
      Throws:
      IOException
      ClassNotFoundException
    • writeExternal

      public void writeExternal(ObjectOutput out) throws IOException
      Specified by:
      writeExternal in interface Externalizable
      Throws:
      IOException
    • encodeToBuffer

      public StreamCharBuffer encodeToBuffer(Encoder encoder)
    • encodeToBuffer

      public StreamCharBuffer encodeToBuffer(Encoder encoder, boolean allowSubBuffers, boolean notifyParentBuffersEnabled)
    • encodeToBuffer

      public StreamCharBuffer encodeToBuffer(List<Encoder> encoders)
    • encodeToBuffer

      public StreamCharBuffer encodeToBuffer(List<Encoder> encoders, boolean allowSubBuffers, boolean notifyParentBuffersEnabled)
    • encodeTo

      public void encodeTo(EncodedAppender appender, Encoder encoder) throws IOException
      Description copied from interface: StreamEncodeable
      Calls the encoder to encode the current content of the StreamEncodable instance (itself) to the EncodedAppender. It is recommended that the implementation checks if Encoder is a StreamingEncoder instance and takes use of that interface.
      Specified by:
      encodeTo in interface StreamEncodeable
      Parameters:
      appender - the EncodedAppender instance
      encoder - the encoder
      Throws:
      IOException - Signals that an I/O exception has occurred.
    • isAllowSubBuffers

      public boolean isAllowSubBuffers()
    • setAllowSubBuffers

      public void setAllowSubBuffers(boolean allowSubBuffers)
    • encode

      public CharSequence encode(Encoder encoder)
      Description copied from interface: Encodeable
      Encode with given encoder.
      Specified by:
      encode in interface Encodeable
      Parameters:
      encoder - the encoder
      Returns:
      the encoded result
    • getWriterForEncoder

      public Writer getWriterForEncoder()
    • getWriterForEncoder

      public Writer getWriterForEncoder(Encoder encoder)
    • lookupDefaultEncodingStateRegistry

      protected EncodingStateRegistry lookupDefaultEncodingStateRegistry()
    • getWriterForEncoder

      public Writer getWriterForEncoder(Encoder encoder, EncodingStateRegistry encodingStateRegistry)
      Description copied from interface: EncodedAppenderWriterFactory
      Gets the EncodedAppenderWriter instance that is connected to this instance implementation with a certain encoder fixed.
      Specified by:
      getWriterForEncoder in interface EncodedAppenderWriterFactory
      Parameters:
      encoder - the encoder to use
      encodingStateRegistry - the current EncodingStateRegistry to use
      Returns:
      the java.io.Writer instance
    • getWriterForEncoder

      public Writer getWriterForEncoder(Encoder encoder, EncodingStateRegistry encodingStateRegistry, boolean ignoreEncodingState)
    • isNotifyParentBuffersEnabled

      public boolean isNotifyParentBuffersEnabled()
    • setNotifyParentBuffersEnabled

      public void setNotifyParentBuffersEnabled(boolean notifyParentBuffersEnabled)
      By default the parent buffers (a buffer where this buffer has been appended to) get notified of changed to this buffer. You can control the notification behavior with this property. Setting this property to false will also clear the references to parent buffers if there are any.
      Parameters:
      notifyParentBuffersEnabled -
    • encodeTo

      public void encodeTo(Writer writer, EncodesToWriter encoder) throws IOException
      Description copied from interface: StreamingEncoderWritable
      Asks the instance to use given writer and EncodesToWriter instance to encode it's content
      Specified by:
      encodeTo in interface StreamingEncoderWritable
      Parameters:
      writer - the target writer instance
      encoder - the encoder
      Throws:
      IOException - Signals that an I/O exception has occurred.
    • methodMissing

      public Object methodMissing(String name, Object args)
      Delegates methodMissing to String object
      Parameters:
      name - The name of the method
      args - The arguments
      Returns:
      The return value
    • asType

      public Object asType(Class clazz)