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 {@link #getReader()} call will create a new reader instance that keepsit own state.
There is a alternative method {@link #getReader(boolean)} for creating thereader. 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:
{@link #readAsCharArray()} reads the buffer to a char[] array
{@link #readAsString()} reads the buffer and wraps the char[] data as aString
{@link #writeTo(Writer)} writes the buffer to a java.io.Writer
{@link #toCharArray()} returns the buffer as a char[] array, caches thereturn value internally so that this method can be called several times.
{@link #toString()} returns the buffer as a String, caches the return valueinternally
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 & 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 ( {@code CharBufferChunk}), String chunks ( {@code StringChunk}) and other StreamCharBuffers as sub chunks ( {@code 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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|