/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.io.buffer;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.function.IntPredicate;

import org.springframework.util.Assert;

Basic abstraction over byte buffers.

DataBuffers has a separate read and write position, as opposed to ByteBuffer's single position. As such, the DataBuffer does not require a flip to read after writing. In general, the following invariant holds for the read and write positions, and the capacity:

0 <= readPosition <= writePosition <= capacity

The capacity of a DataBuffer is expanded on demand, similar to StringBuilder.

The main purpose of the DataBuffer abstraction is to provide a convenient wrapper around ByteBuffer which is similar to Netty's ByteBuf but can also be used on non-Netty platforms (i.e. Servlet containers).

Author:Arjen Poutsma, Brian Clozel
See Also:
Since:5.0
/** * Basic abstraction over byte buffers. * * <p>{@code DataBuffer}s has a separate {@linkplain #readPosition() read} and * {@linkplain #writePosition() write} position, as opposed to {@code ByteBuffer}'s * single {@linkplain ByteBuffer#position() position}. As such, the {@code DataBuffer} * does not require a {@linkplain ByteBuffer#flip() flip} to read after writing. In general, * the following invariant holds for the read and write positions, and the capacity: * * <blockquote> * <tt>0</tt> <tt>&lt;=</tt> * <i>readPosition</i> <tt>&lt;=</tt> * <i>writePosition</i> <tt>&lt;=</tt> * <i>capacity</i> * </blockquote> * * <p>The {@linkplain #capacity() capacity} of a {@code DataBuffer} is expanded on demand, * similar to {@code StringBuilder}. * * <p>The main purpose of the {@code DataBuffer} abstraction is to provide a convenient wrapper * around {@link ByteBuffer} which is similar to Netty's {@link io.netty.buffer.ByteBuf} but * can also be used on non-Netty platforms (i.e. Servlet containers). * * @author Arjen Poutsma * @author Brian Clozel * @since 5.0 * @see DataBufferFactory */
public interface DataBuffer {
Return the DataBufferFactory that created this buffer.
Returns:the creating buffer factory
/** * Return the {@link DataBufferFactory} that created this buffer. * @return the creating buffer factory */
DataBufferFactory factory();
Return the index of the first byte in this buffer that matches the given predicate.
Params:
  • predicate – the predicate to match
  • fromIndex – the index to start the search from
Returns:the index of the first byte that matches predicate; or -1 if none match
/** * Return the index of the first byte in this buffer that matches * the given predicate. * @param predicate the predicate to match * @param fromIndex the index to start the search from * @return the index of the first byte that matches {@code predicate}; * or {@code -1} if none match */
int indexOf(IntPredicate predicate, int fromIndex);
Return the index of the last byte in this buffer that matches the given predicate.
Params:
  • predicate – the predicate to match
  • fromIndex – the index to start the search from
Returns:the index of the last byte that matches predicate; or -1 if none match
/** * Return the index of the last byte in this buffer that matches * the given predicate. * @param predicate the predicate to match * @param fromIndex the index to start the search from * @return the index of the last byte that matches {@code predicate}; * or {@code -1} if none match */
int lastIndexOf(IntPredicate predicate, int fromIndex);
Return the number of bytes that can be read from this data buffer.
Returns:the readable byte count
/** * Return the number of bytes that can be read from this data buffer. * @return the readable byte count */
int readableByteCount();
Return the number of bytes that can be written to this data buffer.
Returns:the writable byte count
Since:5.0.1
/** * Return the number of bytes that can be written to this data buffer. * @return the writable byte count * @since 5.0.1 */
int writableByteCount();
Return the number of bytes that this buffer can contain.
Returns:the capacity
Since:5.0.1
/** * Return the number of bytes that this buffer can contain. * @return the capacity * @since 5.0.1 */
int capacity();
Set the number of bytes that this buffer can contain.

If the new capacity is lower than the current capacity, the contents of this buffer will be truncated. If the new capacity is higher than the current capacity, it will be expanded.

Params:
  • capacity – the new capacity
Returns:this buffer
/** * Set the number of bytes that this buffer can contain. * <p>If the new capacity is lower than the current capacity, the contents * of this buffer will be truncated. If the new capacity is higher than * the current capacity, it will be expanded. * @param capacity the new capacity * @return this buffer */
DataBuffer capacity(int capacity);
Ensure that the current buffer has enough writableByteCount() to write the amount of data given as an argument. If not, the missing capacity will be added to the buffer.
Params:
  • capacity – the writable capacity to check for
Returns:this buffer
Since:5.1.4
/** * Ensure that the current buffer has enough {@link #writableByteCount()} * to write the amount of data given as an argument. If not, the missing * capacity will be added to the buffer. * @param capacity the writable capacity to check for * @return this buffer * @since 5.1.4 */
default DataBuffer ensureCapacity(int capacity) { return this; }
Return the position from which this buffer will read.
Returns:the read position
Since:5.0.1
/** * Return the position from which this buffer will read. * @return the read position * @since 5.0.1 */
int readPosition();
Set the position from which this buffer will read.
Params:
  • readPosition – the new read position
Throws:
Returns:this buffer
Since:5.0.1
/** * Set the position from which this buffer will read. * @param readPosition the new read position * @return this buffer * @throws IndexOutOfBoundsException if {@code readPosition} is smaller than 0 * or greater than {@link #writePosition()} * @since 5.0.1 */
DataBuffer readPosition(int readPosition);
Return the position to which this buffer will write.
Returns:the write position
Since:5.0.1
/** * Return the position to which this buffer will write. * @return the write position * @since 5.0.1 */
int writePosition();
Set the position to which this buffer will write.
Params:
  • writePosition – the new write position
Throws:
Returns:this buffer
Since:5.0.1
/** * Set the position to which this buffer will write. * @param writePosition the new write position * @return this buffer * @throws IndexOutOfBoundsException if {@code writePosition} is smaller than * {@link #readPosition()} or greater than {@link #capacity()} * @since 5.0.1 */
DataBuffer writePosition(int writePosition);
Read a single byte at the given index from this data buffer.
Params:
  • index – the index at which the byte will be read
Throws:
Returns:the byte at the given index
Since:5.0.4
/** * Read a single byte at the given index from this data buffer. * @param index the index at which the byte will be read * @return the byte at the given index * @throws IndexOutOfBoundsException when {@code index} is out of bounds * @since 5.0.4 */
byte getByte(int index);
Read a single byte from the current reading position from this data buffer.
Returns:the byte at this buffer's current reading position
/** * Read a single byte from the current reading position from this data buffer. * @return the byte at this buffer's current reading position */
byte read();
Read this buffer's data into the specified destination, starting at the current reading position of this buffer.
Params:
  • destination – the array into which the bytes are to be written
Returns:this buffer
/** * Read this buffer's data into the specified destination, starting at the current * reading position of this buffer. * @param destination the array into which the bytes are to be written * @return this buffer */
DataBuffer read(byte[] destination);
Read at most length bytes of this buffer into the specified destination, starting at the current reading position of this buffer.
Params:
  • destination – the array into which the bytes are to be written
  • offset – the index within destination of the first byte to be written
  • length – the maximum number of bytes to be written in destination
Returns:this buffer
/** * Read at most {@code length} bytes of this buffer into the specified destination, * starting at the current reading position of this buffer. * @param destination the array into which the bytes are to be written * @param offset the index within {@code destination} of the first byte to be written * @param length the maximum number of bytes to be written in {@code destination} * @return this buffer */
DataBuffer read(byte[] destination, int offset, int length);
Write a single byte into this buffer at the current writing position.
Params:
  • b – the byte to be written
Returns:this buffer
/** * Write a single byte into this buffer at the current writing position. * @param b the byte to be written * @return this buffer */
DataBuffer write(byte b);
Write the given source into this buffer, starting at the current writing position of this buffer.
Params:
  • source – the bytes to be written into this buffer
Returns:this buffer
/** * Write the given source into this buffer, starting at the current writing position * of this buffer. * @param source the bytes to be written into this buffer * @return this buffer */
DataBuffer write(byte[] source);
Write at most length bytes of the given source into this buffer, starting at the current writing position of this buffer.
Params:
  • source – the bytes to be written into this buffer
  • offset – the index within source to start writing from
  • length – the maximum number of bytes to be written from source
Returns:this buffer
/** * Write at most {@code length} bytes of the given source into this buffer, starting * at the current writing position of this buffer. * @param source the bytes to be written into this buffer * @param offset the index within {@code source} to start writing from * @param length the maximum number of bytes to be written from {@code source} * @return this buffer */
DataBuffer write(byte[] source, int offset, int length);
Write one or more DataBuffers to this buffer, starting at the current writing position. It is the responsibility of the caller to release the given data buffers.
Params:
  • buffers – the byte buffers to write into this buffer
Returns:this buffer
/** * Write one or more {@code DataBuffer}s to this buffer, starting at the current * writing position. It is the responsibility of the caller to * {@linkplain DataBufferUtils#release(DataBuffer) release} the given data buffers. * @param buffers the byte buffers to write into this buffer * @return this buffer */
DataBuffer write(DataBuffer... buffers);
Write one or more ByteBuffer to this buffer, starting at the current writing position.
Params:
  • buffers – the byte buffers to write into this buffer
Returns:this buffer
/** * Write one or more {@link ByteBuffer} to this buffer, starting at the current * writing position. * @param buffers the byte buffers to write into this buffer * @return this buffer */
DataBuffer write(ByteBuffer... buffers);
Write the given CharSequence using the given Charset, starting at the current writing position.
Params:
  • charSequence – the char sequence to write into this buffer
  • charset – the charset to encode the char sequence with
Returns:this buffer
Since:5.1.4
/** * Write the given {@code CharSequence} using the given {@code Charset}, * starting at the current writing position. * @param charSequence the char sequence to write into this buffer * @param charset the charset to encode the char sequence with * @return this buffer * @since 5.1.4 */
default DataBuffer write(CharSequence charSequence, Charset charset) { Assert.notNull(charSequence, "CharSequence must not be null"); Assert.notNull(charset, "Charset must not be null"); if (charSequence.length() != 0) { CharsetEncoder charsetEncoder = charset.newEncoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); CharBuffer inBuffer = CharBuffer.wrap(charSequence); int estimatedSize = (int) (inBuffer.remaining() * charsetEncoder.averageBytesPerChar()); ByteBuffer outBuffer = ensureCapacity(estimatedSize) .asByteBuffer(writePosition(), writableByteCount()); while (true) { CoderResult cr = (inBuffer.hasRemaining() ? charsetEncoder.encode(inBuffer, outBuffer, true) : CoderResult.UNDERFLOW); if (cr.isUnderflow()) { cr = charsetEncoder.flush(outBuffer); } if (cr.isUnderflow()) { break; } if (cr.isOverflow()) { writePosition(writePosition() + outBuffer.position()); int maximumSize = (int) (inBuffer.remaining() * charsetEncoder.maxBytesPerChar()); ensureCapacity(maximumSize); outBuffer = asByteBuffer(writePosition(), writableByteCount()); } } writePosition(writePosition() + outBuffer.position()); } return this; }
Create a new DataBuffer whose contents is a shared subsequence of this data buffer's content. Data between this data buffer and the returned buffer is shared; though changes in the returned buffer's position will not be reflected in the reading nor writing position of this data buffer.

Note that this method will not call DataBufferUtils.retain(DataBuffer) on the resulting slice: the reference count will not be increased.

Params:
  • index – the index at which to start the slice
  • length – the length of the slice
Returns:the specified slice of this data buffer
/** * Create a new {@code DataBuffer} whose contents is a shared subsequence of this * data buffer's content. Data between this data buffer and the returned buffer is * shared; though changes in the returned buffer's position will not be reflected * in the reading nor writing position of this data buffer. * <p><strong>Note</strong> that this method will <strong>not</strong> call * {@link DataBufferUtils#retain(DataBuffer)} on the resulting slice: the reference * count will not be increased. * @param index the index at which to start the slice * @param length the length of the slice * @return the specified slice of this data buffer */
DataBuffer slice(int index, int length);
Create a new DataBuffer whose contents is a shared, retained subsequence of this data buffer's content. Data between this data buffer and the returned buffer is shared; though changes in the returned buffer's position will not be reflected in the reading nor writing position of this data buffer.

Note that unlike slice(int, int), this method will call DataBufferUtils.retain(DataBuffer) (or equivalent) on the resulting slice.

Params:
  • index – the index at which to start the slice
  • length – the length of the slice
Returns:the specified, retained slice of this data buffer
Since:5.2
/** * Create a new {@code DataBuffer} whose contents is a shared, retained subsequence of this * data buffer's content. Data between this data buffer and the returned buffer is * shared; though changes in the returned buffer's position will not be reflected * in the reading nor writing position of this data buffer. * <p><strong>Note</strong> that unlike {@link #slice(int, int)}, this method * <strong>will</strong> call {@link DataBufferUtils#retain(DataBuffer)} (or equivalent) on the * resulting slice. * @param index the index at which to start the slice * @param length the length of the slice * @return the specified, retained slice of this data buffer * @since 5.2 */
default DataBuffer retainedSlice(int index, int length) { return DataBufferUtils.retain(slice(index, length)); }
Expose this buffer's bytes as a ByteBuffer. Data between this DataBuffer and the returned ByteBuffer is shared; though changes in the returned buffer's position will not be reflected in the reading nor writing position of this data buffer.
Returns:this data buffer as a byte buffer
/** * Expose this buffer's bytes as a {@link ByteBuffer}. Data between this * {@code DataBuffer} and the returned {@code ByteBuffer} is shared; though * changes in the returned buffer's {@linkplain ByteBuffer#position() position} * will not be reflected in the reading nor writing position of this data buffer. * @return this data buffer as a byte buffer */
ByteBuffer asByteBuffer();
Expose a subsequence of this buffer's bytes as a ByteBuffer. Data between this DataBuffer and the returned ByteBuffer is shared; though changes in the returned buffer's position will not be reflected in the reading nor writing position of this data buffer.
Params:
  • index – the index at which to start the byte buffer
  • length – the length of the returned byte buffer
Returns:this data buffer as a byte buffer
Since:5.0.1
/** * Expose a subsequence of this buffer's bytes as a {@link ByteBuffer}. Data between * this {@code DataBuffer} and the returned {@code ByteBuffer} is shared; though * changes in the returned buffer's {@linkplain ByteBuffer#position() position} * will not be reflected in the reading nor writing position of this data buffer. * @param index the index at which to start the byte buffer * @param length the length of the returned byte buffer * @return this data buffer as a byte buffer * @since 5.0.1 */
ByteBuffer asByteBuffer(int index, int length);
Expose this buffer's data as an InputStream. Both data and read position are shared between the returned stream and this data buffer. The underlying buffer will not be released when the input stream is closed.
See Also:
Returns:this data buffer as an input stream
/** * Expose this buffer's data as an {@link InputStream}. Both data and read position are * shared between the returned stream and this data buffer. The underlying buffer will * <strong>not</strong> be {@linkplain DataBufferUtils#release(DataBuffer) released} * when the input stream is {@linkplain InputStream#close() closed}. * @return this data buffer as an input stream * @see #asInputStream(boolean) */
InputStream asInputStream();
Expose this buffer's data as an InputStream. Both data and read position are shared between the returned stream and this data buffer.
Params:
  • releaseOnClose – whether the underlying buffer will be released when the input stream is closed.
Returns:this data buffer as an input stream
Since:5.0.4
/** * Expose this buffer's data as an {@link InputStream}. Both data and read position are * shared between the returned stream and this data buffer. * @param releaseOnClose whether the underlying buffer will be * {@linkplain DataBufferUtils#release(DataBuffer) released} when the input stream is * {@linkplain InputStream#close() closed}. * @return this data buffer as an input stream * @since 5.0.4 */
InputStream asInputStream(boolean releaseOnClose);
Expose this buffer's data as an OutputStream. Both data and write position are shared between the returned stream and this data buffer.
Returns:this data buffer as an output stream
/** * Expose this buffer's data as an {@link OutputStream}. Both data and write position are * shared between the returned stream and this data buffer. * @return this data buffer as an output stream */
OutputStream asOutputStream();
Return this buffer's data a String using the specified charset. Default implementation delegates to toString(readPosition(), readableByteCount(), charset).
Params:
  • charset – the character set to use
Returns:a string representation of all this buffers data
Since:5.2
/** * Return this buffer's data a String using the specified charset. Default implementation * delegates to {@code toString(readPosition(), readableByteCount(), charset)}. * @param charset the character set to use * @return a string representation of all this buffers data * @since 5.2 */
default String toString(Charset charset) { Assert.notNull(charset, "Charset must not be null"); return toString(readPosition(), readableByteCount(), charset); }
Return a part of this buffer's data as a String using the specified charset.
Params:
  • index – the index at which to start the string
  • length – the number of bytes to use for the string
  • charset – the charset to use
Returns:a string representation of a part of this buffers data
Since:5.2
/** * Return a part of this buffer's data as a String using the specified charset. * @param index the index at which to start the string * @param length the number of bytes to use for the string * @param charset the charset to use * @return a string representation of a part of this buffers data * @since 5.2 */
String toString(int index, int length, Charset charset); }