Class StreamCharBuffer
- java.lang.Object
-
- groovy.lang.GroovyObjectSupport
-
- org.grails.buffer.StreamCharBuffer
-
- All Implemented Interfaces:
groovy.lang.GroovyObject,groovy.lang.Writable,java.io.Externalizable,java.io.Serializable,java.lang.CharSequence,java.lang.Cloneable,Encodeable,EncodedAppenderWriterFactory,StreamEncodeable,StreamingEncoderWritable
public class StreamCharBuffer extends groovy.lang.GroovyObjectSupport implements groovy.lang.Writable, java.lang.CharSequence, java.io.Externalizable, Encodeable, StreamEncodeable, StreamingEncoderWritable, EncodedAppenderWriterFactory, java.lang.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 methodgetReader(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:
Configuration values can also be changed for each instance of StreamCharBuffer individually. Default values are defined with System Properties.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 - See Also:
- Serialized Form
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static classStreamCharBuffer.EncodedPartstatic interfaceStreamCharBuffer.LazyInitializingMultipleWriterstatic interfaceStreamCharBuffer.LazyInitializingWriterInterface for a Writer that gets initialized if it is used Can be used for passing in to "connectTo" method of StreamCharBufferclassStreamCharBuffer.StreamCharBufferReaderThis is the java.io.Reader implementation for StreamCharBufferclassStreamCharBuffer.StreamCharBufferWriterThis is the java.io.Writer implementation for StreamCharBuffer
-
Constructor Summary
Constructors Constructor Description StreamCharBuffer()StreamCharBuffer(int chunkSize)StreamCharBuffer(int chunkSize, int growProcent)StreamCharBuffer(int chunkSize, int growProcent, int maxChunkSize)
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description voidappendStreamCharBufferChunk(StreamCharBuffer subBuffer)voidappendStreamCharBufferChunk(StreamCharBuffer subBuffer, java.util.List<Encoder> encoders)protected static voidarrayCopy(char[] src, int srcPos, char[] dest, int destPos, int length)booleanasBoolean()java.lang.ObjectasType(java.lang.Class clazz)protected booleanbufferChanged(StreamCharBuffer buffer)charcharAt(int index)voidclear()Clears the buffer and notifies the parents of this buffer of the change.StreamCharBufferclone()voidconnectTo(java.io.Writer w)Connect this buffer to a target Writer.voidconnectTo(java.io.Writer w, boolean autoFlush)voidconnectTo(StreamCharBuffer.LazyInitializingWriter w)voidconnectTo(StreamCharBuffer.LazyInitializingWriter w, boolean autoFlush)StreamCharBuffer.LazyInitializingWritercreateEncodingInitializer(EncoderAware encoderLookup, EncodingStateRegistryLookup encodingStateRegistryLookup, StreamCharBuffer.LazyInitializingWriter... writers)java.util.List<StreamCharBuffer.EncodedPart>dumpEncodedParts()protected voidemptyAfterReading()java.lang.CharSequenceencode(Encoder encoder)Encode with given encoder.voidencodeInStreamingModeTo(EncoderAware encoderLookup, EncodingStateRegistryLookup encodingStateRegistryLookup, boolean autoFlush, java.io.Writer w)voidencodeInStreamingModeTo(EncoderAware encoderLookup, EncodingStateRegistryLookup encodingStateRegistryLookup, boolean autoFlush, StreamCharBuffer.LazyInitializingWriter... writers)voidencodeTo(java.io.Writer writer, EncodesToWriter encoder)Asks the instance to use given writer and EncodesToWriter instance to encode it's contentvoidencodeTo(EncodedAppender appender, Encoder encoder)Calls the encoder to encode the current content of the StreamEncodable instance (itself) to the EncodedAppender.StreamCharBufferencodeToBuffer(java.util.List<Encoder> encoders)StreamCharBufferencodeToBuffer(java.util.List<Encoder> encoders, boolean allowSubBuffers, boolean notifyParentBuffersEnabled)StreamCharBufferencodeToBuffer(Encoder encoder)StreamCharBufferencodeToBuffer(Encoder encoder, boolean allowSubBuffers, boolean notifyParentBuffersEnabled)booleanequals(java.lang.Object o)equals uses String.equals to check for equality to support compatibility with String instances in maps, sets, etc.intgetBufferChangesCounter()intgetChunkMinSize()protected java.util.List<StreamCharBuffer>getCurrentParentBuffers()java.io.ReadergetReader()Creates a new Reader instance for reading/consuming data from the buffer.java.io.ReadergetReader(boolean removeAfterReading)Like getReader(), but when removeAfterReading is true, the read data will be removed from the buffer.intgetSubBufferChunkMinSize()intgetSubStringChunkMinSize()intgetWriteDirectlyToConnectedMinSize()java.io.WritergetWriter()Writer interface for adding/writing data to the buffer.java.io.WritergetWriterForEncoder()java.io.WritergetWriterForEncoder(Encoder encoder)java.io.WritergetWriterForEncoder(Encoder encoder, EncodingStateRegistry encodingStateRegistry)Gets the EncodedAppenderWriter instance that is connected to this instance implementation with a certain encoder fixed.java.io.WritergetWriterForEncoder(Encoder encoder, EncodingStateRegistry encodingStateRegistry, boolean ignoreEncodingState)inthashCode()Uses String's hashCode to support compatibility with String instances in maps, sets, etc.booleanisAllowSubBuffers()protected booleanisChunkSizeResizeable()booleanisConnectedMode()booleanisEmpty()booleanisNotifyParentBuffersEnabled()booleanisPreferSubChunkWhenWritingToOtherBuffer()intlength()protected EncodingStateRegistrylookupDefaultEncodingStateRegistry()protected intmarkBufferChanged()voidmarkEncoded(org.grails.buffer.StreamCharBuffer.StringChunk strChunk)java.lang.ObjectmethodMissing(java.lang.String name, java.lang.Object args)Delegates methodMissing to String objectprotected voidnotifyBufferChange()protected voidnotifyPreferSubChunkEnabled()java.lang.Stringplus(java.lang.Object value)java.lang.Stringplus(java.lang.String value)voidreadExternal(java.io.ObjectInput in)org.grails.buffer.StreamCharBuffer.StringChunkreadToSingleStringChunk(boolean registerEncodingState)voidremoveConnections()voidreset()voidreset(boolean resetChunkSize)resets the state of this buffer (empties it)protected voidresizeChunkSizeAsProcentageOfTotalSize()voidsetAllowSubBuffers(boolean allowSubBuffers)voidsetChunkMinSize(int size)voidsetNotifyParentBuffersEnabled(boolean notifyParentBuffersEnabled)By default the parent buffers (a buffer where this buffer has been appended to) get notified of changed to this buffer.voidsetPreferSubChunkWhenWritingToOtherBuffer(boolean prefer)voidsetSubBufferChunkMinSize(int size)voidsetSubStringChunkMinSize(int size)Minimum size for a String to be added as a StringChunk instead of copying content to the char[] buffer of the current StreamCharBufferChunkvoidsetWriteDirectlyToConnectedMinSize(int size)Minimum size for a String or char[] to get written directly to connected writer (in "connectTo" mode).intsize()java.lang.CharSequencesubSequence(int start, int end)char[]toCharArray()Reads the buffer to a char[].java.lang.StringtoString()Reads (and empties) the buffer to a String, but caches the return value for subsequent calls.voidwriteExternal(java.io.ObjectOutput out)java.io.WriterwriteTo(java.io.Writer target)Writes the buffer content to a target java.io.WritervoidwriteTo(java.io.Writer target, boolean flushTarget, boolean emptyAfter)Writes the buffer content to a target java.io.Writer
-
-
-
Method Detail
-
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(java.io.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(java.io.Writer w, boolean autoFlush)
-
encodeInStreamingModeTo
public final void encodeInStreamingModeTo(EncoderAware encoderLookup, EncodingStateRegistryLookup encodingStateRegistryLookup, boolean autoFlush, java.io.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 java.io.Writer getWriter()
Writer interface for adding/writing data to the buffer.- Returns:
- the Writer
-
getReader
public java.io.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 java.io.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 java.io.Writer writeTo(java.io.Writer target) throws java.io.IOExceptionWrites the buffer content to a target java.io.Writer- Specified by:
writeToin interfacegroovy.lang.Writable- Parameters:
target-- Throws:
java.io.IOException
-
writeTo
public void writeTo(java.io.Writer target, boolean flushTarget, boolean emptyAfter) throws java.io.IOExceptionWrites the buffer content to a target java.io.Writer- Parameters:
target- WriterflushTarget- calls target.flush() before finishingemptyAfter- empties the buffer if true- Throws:
java.io.IOException
-
emptyAfterReading
protected void emptyAfterReading()
-
toString
public java.lang.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:
toStringin interfacejava.lang.CharSequence- Overrides:
toStringin classjava.lang.Object- See Also:
Object.toString()
-
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:
hashCodein classjava.lang.Object- See Also:
Object.hashCode()
-
equals
public boolean equals(java.lang.Object o)
equals uses String.equals to check for equality to support compatibility with String instances in maps, sets, etc.- Overrides:
equalsin classjava.lang.Object- See Also:
Object.equals(java.lang.Object)
-
plus
public java.lang.String plus(java.lang.String value)
-
plus
public java.lang.String plus(java.lang.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 java.util.List<StreamCharBuffer.EncodedPart> dumpEncodedParts()
-
size
public int size()
-
isEmpty
public boolean isEmpty()
-
appendStreamCharBufferChunk
public void appendStreamCharBufferChunk(StreamCharBuffer subBuffer) throws java.io.IOException
- Throws:
java.io.IOException
-
appendStreamCharBufferChunk
public void appendStreamCharBufferChunk(StreamCharBuffer subBuffer, java.util.List<Encoder> encoders) throws java.io.IOException
- Throws:
java.io.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:
charAtin interfacejava.lang.CharSequence
-
length
public int length()
- Specified by:
lengthin interfacejava.lang.CharSequence
-
subSequence
public java.lang.CharSequence subSequence(int start, int end)- Specified by:
subSequencein interfacejava.lang.CharSequence
-
asBoolean
public boolean asBoolean()
-
bufferChanged
protected boolean bufferChanged(StreamCharBuffer buffer)
-
getCurrentParentBuffers
protected java.util.List<StreamCharBuffer> getCurrentParentBuffers()
-
notifyBufferChange
protected void notifyBufferChange()
-
getBufferChangesCounter
public int getBufferChangesCounter()
-
markBufferChanged
protected int markBufferChanged()
-
clone
public StreamCharBuffer clone()
- Overrides:
clonein classjava.lang.Object
-
readExternal
public void readExternal(java.io.ObjectInput in) throws java.io.IOException, java.lang.ClassNotFoundException- Specified by:
readExternalin interfacejava.io.Externalizable- Throws:
java.io.IOExceptionjava.lang.ClassNotFoundException
-
writeExternal
public void writeExternal(java.io.ObjectOutput out) throws java.io.IOException- Specified by:
writeExternalin interfacejava.io.Externalizable- Throws:
java.io.IOException
-
encodeToBuffer
public StreamCharBuffer encodeToBuffer(Encoder encoder)
-
encodeToBuffer
public StreamCharBuffer encodeToBuffer(Encoder encoder, boolean allowSubBuffers, boolean notifyParentBuffersEnabled)
-
encodeToBuffer
public StreamCharBuffer encodeToBuffer(java.util.List<Encoder> encoders)
-
encodeToBuffer
public StreamCharBuffer encodeToBuffer(java.util.List<Encoder> encoders, boolean allowSubBuffers, boolean notifyParentBuffersEnabled)
-
encodeTo
public void encodeTo(EncodedAppender appender, Encoder encoder) throws java.io.IOException
Description copied from interface:StreamEncodeableCalls 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 aStreamingEncoderinstance and takes use of that interface.- Specified by:
encodeToin interfaceStreamEncodeable- Parameters:
appender- the EncodedAppender instanceencoder- the encoder- Throws:
java.io.IOException- Signals that an I/O exception has occurred.
-
isAllowSubBuffers
public boolean isAllowSubBuffers()
-
setAllowSubBuffers
public void setAllowSubBuffers(boolean allowSubBuffers)
-
encode
public java.lang.CharSequence encode(Encoder encoder)
Description copied from interface:EncodeableEncode with given encoder.- Specified by:
encodein interfaceEncodeable- Parameters:
encoder- the encoder- Returns:
- the encoded result
-
getWriterForEncoder
public java.io.Writer getWriterForEncoder()
-
getWriterForEncoder
public java.io.Writer getWriterForEncoder(Encoder encoder)
-
lookupDefaultEncodingStateRegistry
protected EncodingStateRegistry lookupDefaultEncodingStateRegistry()
-
getWriterForEncoder
public java.io.Writer getWriterForEncoder(Encoder encoder, EncodingStateRegistry encodingStateRegistry)
Description copied from interface:EncodedAppenderWriterFactoryGets the EncodedAppenderWriter instance that is connected to this instance implementation with a certain encoder fixed.- Specified by:
getWriterForEncoderin interfaceEncodedAppenderWriterFactory- Parameters:
encoder- the encoder to useencodingStateRegistry- the current EncodingStateRegistry to use- Returns:
- the java.io.Writer instance
-
getWriterForEncoder
public java.io.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(java.io.Writer writer, EncodesToWriter encoder) throws java.io.IOExceptionDescription copied from interface:StreamingEncoderWritableAsks the instance to use given writer and EncodesToWriter instance to encode it's content- Specified by:
encodeToin interfaceStreamingEncoderWritable- Parameters:
writer- the target writer instanceencoder- the encoder- Throws:
java.io.IOException- Signals that an I/O exception has occurred.
-
methodMissing
public java.lang.Object methodMissing(java.lang.String name, java.lang.Object args)Delegates methodMissing to String object- Parameters:
name- The name of the methodargs- The arguments- Returns:
- The return value
-
asType
public java.lang.Object asType(java.lang.Class clazz)
-
-