org.codehaus.groovy.grails.web.util
Class StreamCharBuffer

java.lang.Object
  extended by org.codehaus.groovy.grails.web.util.StreamCharBuffer
All Implemented Interfaces:
groovy.lang.Writable, Externalizable, Serializable, CharSequence

public class StreamCharBuffer
extends Object
implements groovy.lang.Writable, CharSequence, Externalizable

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 & 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 & 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. 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 & 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 & copy operations.

There can be several different type of chunks: char arrays ( StreamCharBuffer.CharBufferChunk), String chunks (StreamCharBuffer.StringChunk) and other StreamCharBuffers as sub chunks (StreamCharBuffer.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.

Author:
Lari Hotari, Sagire Software Oy
See Also:
Serialized Form

Nested Class Summary
(package private)  class StreamCharBuffer.AbstractChunk
           
(package private)  class StreamCharBuffer.AbstractChunkReader
           
(package private)  class StreamCharBuffer.AllocatedBuffer
           
(package private)  class StreamCharBuffer.AllocatedBufferReader
           
(package private)  class StreamCharBuffer.CharBufferChunk
          The data in the buffer is stored in a linked list of StreamCharBufferChunks.
(package private)  class StreamCharBuffer.CharBufferChunkReader
           
(package private) static class StreamCharBuffer.ChunkReader
           
(package private) static class StreamCharBuffer.ConnectedWriter
          Simple holder class for the connected writer
static interface StreamCharBuffer.LazyInitializingWriter
          Interface for a Writer that gets initialized if it is used Can be used for passing in to "connectTo" method of StreamCharBuffer
(package private) static class StreamCharBuffer.MultiOutputWriter
          delegates to several writers, used in "connectTo" mode.
(package private) static class StreamCharBuffer.SingleOutputWriter
           
 class StreamCharBuffer.StreamCharBufferReader
          This is the java.io.Reader implementation for StreamCharBuffer
(package private)  class StreamCharBuffer.StreamCharBufferSubChunk
           
(package private)  class StreamCharBuffer.StreamCharBufferSubChunkReader
           
 class StreamCharBuffer.StreamCharBufferWriter
          This is the java.io.Writer implementation for StreamCharBuffer
(package private)  class StreamCharBuffer.StringChunk
          StringChunk is a wrapper for java.lang.String.
(package private)  class StreamCharBuffer.StringChunkReader
           
 
Field Summary
(package private)  int allocatedBufferIdSequence
           
(package private)  boolean hasReaders
           
(package private)  boolean preferSubChunkWhenWritingToOtherBuffer
           
(package private)  int readerCount
           
 
Constructor Summary
StreamCharBuffer()
           
StreamCharBuffer(int chunkSize)
           
StreamCharBuffer(int chunkSize, int growProcent)
           
StreamCharBuffer(int chunkSize, int growProcent, int maxChunkSize)
           
 
Method Summary
(package private)  void addChunk(StreamCharBuffer.AbstractChunk newChunk)
           
(package private)  void addParentBuffer(StreamCharBuffer parent)
           
protected  int allocateSpace()
           
 void appendStreamCharBufferChunk(StreamCharBuffer subBuffer)
           
(package private)  void appendStringChunk(String str, int off, int len)
           
protected static void arrayCopy(char[] src, int srcPos, char[] dest, int destPos, int length)
           
(package private)  boolean bufferChanged(StreamCharBuffer buffer)
           
 char charAt(int index)
           
 void connectTo(StreamCharBuffer.LazyInitializingWriter writer)
           
 void connectTo(StreamCharBuffer.LazyInitializingWriter writer, boolean autoFlush)
           
 void connectTo(Writer writer)
          Connect this buffer to a target Writer.
 void connectTo(Writer writer, boolean autoFlush)
           
 boolean equals(Object o)
          equals uses String.equals to check for equality to support compatibility with String instances in maps, sets, etc.
 int getChunkMinSize()
           
 Reader getReader()
          Creates a new java.io.Reader instance for reading/consuming data from the buffer.
 Reader getReader(boolean removeAfterReading)
          Like getReader(), but when removeAfterReading is true, the read data will be removed from the buffer.
 int getSubBufferChunkMinSize()
           
 int getSubStringChunkMinSize()
           
 int getWriteDirectlyToConnectedMinSize()
           
 Writer getWriter()
          Writer interface for adding/writing data to the buffer.
 int hashCode()
          hashCode() uses String's hashCode to support compatibility with String instances in maps, sets, etc.
protected  boolean isChunkSizeResizeable()
           
 boolean isConnectedMode()
           
 boolean isPreferSubChunkWhenWritingToOtherBuffer()
           
 int length()
           
(package private)  void notifyBufferChange()
           
 String plus(Object value)
           
 String plus(String value)
           
 char[] readAsCharArray()
          reads the buffer to a char[]
 String readAsString()
          reads the buffer to a String
 void readExternal(ObjectInput in)
           
 void removeConnections()
           
 void reset()
           
 void reset(boolean resetChunkSize)
          resets the state of this buffer (empties it)
protected  void resizeChunkSizeAsProcentageOfTotalSize()
           
 void setChunkMinSize(int chunkMinSize)
           
 void setPreferSubChunkWhenWritingToOtherBuffer(boolean preferSubChunkWhenWritingToOtherBuffer)
           
 void setSubBufferChunkMinSize(int subBufferChunkMinSize)
           
 void setSubStringChunkMinSize(int stringChunkMinSize)
          Minimum size for a String to be added as a StringChunk instead of copying content to the char[] buffer of the current StreamCharBufferChunk
 void setWriteDirectlyToConnectedMinSize(int writeDirectlyToConnectedMinSize)
          Minimum size for a String or char[] to get written directly to connected writer (in "connectTo" mode).
 int size()
           
 CharSequence subSequence(int start, int end)
           
 char[] toCharArray()
          reads the buffer to a char[] caches the result if there aren't any readers
 String toString()
          reads (and empties) the buffer to a String, but caches the return value for subsequent calls.
 void writeExternal(ObjectOutput out)
           
 Writer writeTo(Writer target)
          Writes the buffer content to a target java.io.Writer
 void writeTo(Writer target, boolean flushTarget, boolean emptyAfter)
          Writes the buffer content to a target java.io.Writer
 
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
 

Field Detail

preferSubChunkWhenWritingToOtherBuffer

boolean preferSubChunkWhenWritingToOtherBuffer

allocatedBufferIdSequence

int allocatedBufferIdSequence

readerCount

int readerCount

hasReaders

boolean hasReaders
Constructor Detail

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 Detail

isPreferSubChunkWhenWritingToOtherBuffer

public boolean isPreferSubChunkWhenWritingToOtherBuffer()

setPreferSubChunkWhenWritingToOtherBuffer

public void setPreferSubChunkWhenWritingToOtherBuffer(boolean preferSubChunkWhenWritingToOtherBuffer)

reset

public void reset()

reset

public void reset(boolean resetChunkSize)
resets the state of this buffer (empties it)

Parameters:
resetChunkSize -

connectTo

public final void connectTo(Writer writer)
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:
writer -

connectTo

public final void connectTo(Writer writer,
                            boolean autoFlush)

connectTo

public final void connectTo(StreamCharBuffer.LazyInitializingWriter writer)

connectTo

public final void connectTo(StreamCharBuffer.LazyInitializingWriter writer,
                            boolean autoFlush)

removeConnections

public final void removeConnections()

getSubStringChunkMinSize

public int getSubStringChunkMinSize()

setSubStringChunkMinSize

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

Parameters:
stringChunkMinSize -

getSubBufferChunkMinSize

public int getSubBufferChunkMinSize()

setSubBufferChunkMinSize

public void setSubBufferChunkMinSize(int subBufferChunkMinSize)

getWriteDirectlyToConnectedMinSize

public int getWriteDirectlyToConnectedMinSize()

setWriteDirectlyToConnectedMinSize

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

Parameters:
writeDirectlyToConnectedMinSize -

getChunkMinSize

public int getChunkMinSize()

setChunkMinSize

public void setChunkMinSize(int chunkMinSize)

getWriter

public Writer getWriter()
Writer interface for adding/writing data to the buffer.

Returns:

getReader

public Reader getReader()
Creates a new java.io.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:

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:

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
flushAll - flush all content in buffer (if this is false, only filled chunks will be written)
flushTarget - calls target.flush() before finishing
Throws:
IOException

readAsCharArray

public char[] readAsCharArray()
reads the buffer to a char[]

Returns:

readAsString

public String readAsString()
reads the buffer to a String

Returns:

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:
Object.toString()

hashCode

public int hashCode()
hashCode() uses String's hashCode to support compatibility with String instances in maps, sets, etc.

Overrides:
hashCode in class Object
See Also:
Object.hashCode()

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:
Object.equals(java.lang.Object)

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:

size

public int size()

allocateSpace

protected int allocateSpace()
                     throws IOException
Throws:
IOException

appendStringChunk

void appendStringChunk(String str,
                       int off,
                       int len)
                 throws IOException
Throws:
IOException

appendStreamCharBufferChunk

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

addChunk

void addChunk(StreamCharBuffer.AbstractChunk newChunk)

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

addParentBuffer

void addParentBuffer(StreamCharBuffer parent)

bufferChanged

boolean bufferChanged(StreamCharBuffer buffer)

notifyBufferChange

void notifyBufferChange()

readExternal

public void readExternal(ObjectInput in)
                  throws IOException,
                         ClassNotFoundException
Specified by:
readExternal in interface Externalizable
Throws:
IOException
ClassNotFoundException
See Also:
Externalizable.readExternal(java.io.ObjectInput)

writeExternal

public void writeExternal(ObjectOutput out)
                   throws IOException
Specified by:
writeExternal in interface Externalizable
Throws:
IOException
See Also:
Externalizable.writeExternal(java.io.ObjectOutput)


Copyright (c) 2005-2009 The Grails project