netty-incubator-buffer-api/src/main/java/io/netty/buffer/api/Buf.java

375 lines
18 KiB
Java

/*
* Copyright 2020 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.buffer.api;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A reference counted buffer of memory, with separate reader and writer offsets.
* <p>
* A buffer is a sequential stretch of memory with a certain capacity, an offset for writing, and an offset for reading.
*
* <h3>Creating a buffer</h3>
*
* Buffers are created by {@linkplain Allocator allocators}, and their {@code allocate} family of methods.
* A number of standard allocators exist, and ara available through static methods on the {@code Allocator} interface.
*
* <h3>Life cycle and reference counting</h3>
*
* The buffer has a life cycle, where it is allocated, used, and deallocated.
* The reference count controls this life cycle.
* <p>
* When the buffer is initially allocated, a pairing {@link #close()} call will deallocate it.
* In this state, the buffer {@linkplain #isOwned() is "owned"}.
* <p>
* The buffer can also be {@linkplain #acquire() acquired} when it's about to be involved in a complicated life time.
* The {@link #acquire()} call increments the reference count of the buffer,
* and a pairing {@link #close()} call will decrement the reference count.
* Each acquire lends out the buffer, and the buffer is said to be in a "borrowed" state.
* <p>
* Certain operations, such as {@link #send()}, are only available on owned buffers.
*
* <h3>Thread-safety</h3>
*
* Buffers are not thread-safe.
* The reference counting implied by the {@link Rc} interface is itself not thread-safe,
* and buffers additionally contain other mutable data that is not thread-safe.
* Depending on the buffer implementation, the buffer may impose confinement restrictions as well,
* so that the buffer cannot even be read using absolute offsets,
* such as with the {@link #getByte(int)} method,
* from multiple threads.
* <p>
* Confined buffers will initially be confined to the thread that allocates them.
* <p>
* If a buffer needs to be accessed by a different thread,
* then the ownership of that buffer must be sent to that thread.
* This can be done with the {@link #send()} method.
* The send method consumes the buffer, if it is in an owned state, and produces a {@link Send} object.
* The {@link Send} object can then be shared in a thread-safe way (so called "safe publication"),
* with the intended recipient thread.
* <p>
* To send a buffer to another thread, the buffer must not have any outstanding borrows.
* That is to say, all {@linkplain #acquire() acquires} must have been paired with a {@link #close()};
* all {@linkplain #slice() slices} must have been closed.
* And if this buffer is a constituent of a {@linkplain Allocator#compose(Buf...) composite buffer},
* then that composite buffer must be closed.
* And if this buffer is itself a composite buffer, then it must own all of its constituent buffers.
* The {@link #isOwned()} method can be used on any buffer to check if it can be sent or not.
*
* <h3>Accessing data</h3>
*
* Data access methods fall into two classes:
* <ol>
* <li>Access that are based on, and updates, the read or write offset positions.</li>
* <ul><li>These accessor methods are typically called {@code readX} or {@code writeX}.</li></ul>
* <li>Access that take offsets as arguments, and do not update read or write offset positions.</li>
* <ul><li>These accessor methods are typically called {@code getX} or {@code setX}.</li></ul>
* </ol>
*
* A buffer contain two mutable offset positions: one for reading and one for writing.
* These positions use <a href="https://en.wikipedia.org/wiki/Zero-based_numbering">zero-based indexing</a>,
* such that the first byte of data in the buffer is placed at offset {@code 0},
* and the last byte in the buffer is at offset {@link #capacity() capacity - 1}.
* The {@link #readerOffset()} is the offset into the buffer from which the next read will take place,
* and is initially zero.
* The reader offset must always be less than or equal to the {@link #writerOffset()}.
* The {@link #writerOffset()} is likewise the offset into the buffer where the next write will take place.
* The writer offset is also initially zero, and must be less than or equal to the {@linkplain #capacity() capacity}.
* <p>
* This carves the buffer into three regions, as demonstrated by this diagram:
* <pre>
* +-------------------+------------------+------------------+
* | discardable bytes | readable bytes | writable bytes |
* | | (CONTENT) | |
* +-------------------+------------------+------------------+
* | | | |
* 0 <= readerOffset <= writerOffset <= capacity
* </pre>
*
*/
public interface Buf extends Rc<Buf>, BufAccessors {
/**
* Change the default byte order of this buffer, and return this buffer.
*
* @param order The new default byte order, used by accessor methods that don't use an explicit byte order.
* @return This buffer instance.
*/
Buf order(ByteOrder order);
/**
* The default byte order of this buffer.
* @return The default byte order of this buffer.
*/
ByteOrder order();
/**
* The capacity of this buffer, that is, the maximum number of bytes it can contain.
*
* @return The capacity in bytes.
*/
int capacity();
/**
* Get the current reader offset. The next read will happen from this byte offset into the buffer.
*
* @return The current reader offset.
*/
int readerOffset();
/**
* Set the reader offset. Make the next read happen from the given offset into the buffer.
*
* @param offset The reader offset to set.
* @return This Buf.
* @throws IndexOutOfBoundsException if the specified {@code offset} is less than zero or greater than the current
* {@link #writerOffset()}.
*/
Buf readerOffset(int offset);
/**
* Get the current writer offset. The next write will happen at this byte offset into the byffer.
*
* @return The current writer offset.
*/
int writerOffset();
/**
* Set the writer offset. Make the next write happen at the given offset.
*
* @param offset The writer offset to set.
* @return This Buf.
* @throws IndexOutOfBoundsException if the specified {@code offset} is less than the current
* {@link #readerOffset()} or greater than {@link #capacity()}.
*/
Buf writerOffset(int offset);
/**
* Returns the number of readable bytes which is equal to {@code (writerOffset() - readerOffset())}.
*/
default int readableBytes() {
return writerOffset() - readerOffset();
}
/**
* Returns the number of writable bytes which is equal to {@code (capacity() - writerOffset())}.
*/
default int writableBytes() {
return capacity() - writerOffset();
}
/**
* Fill the buffer with the given byte value. This method does not respect the {@link #readerOffset()} or {@link
* #writerOffset()}, but copies the full capacity of the buffer. The {@link #readerOffset()} and {@link
* #writerOffset()} are not modified.
*
* @param value The byte value to write at every position in the buffer.
* @return This Buf.
*/
Buf fill(byte value);
/**
* Give the native memory address backing this buffer, or return 0 if this buffer has no native memory address.
* @return The native memory address, if any, otherwise 0.
*/
long getNativeAddress();
/**
* Returns a slice of this buffer's readable bytes.
* Modifying the content of the returned buffer or this buffer affects each other's content,
* while they maintain separate offsets. This method is identical to
* {@code buf.slice(buf.readerOffset(), buf.readableBytes())}.
* This method does not modify {@link #readerOffset()} or {@link #writerOffset()} of this buffer.
* <p>
* This method increments the reference count of this buffer.
* The reference count is decremented again when the slice is deallocated.
* <p>
* The slice is created with a {@linkplain #writerOffset() write offset} equal to the length of the slice,
* so that the entire contents of the slice is ready to be read.
*
* @return A new buffer instance, with independent {@link #readerOffset()} and {@link #writerOffset()},
* that is a view of the readable region of this buffer.
*/
default Buf slice() {
return slice(readerOffset(), readableBytes());
}
/**
* Returns a slice of the given region of this buffer.
* Modifying the content of the returned buffer or this buffer affects each other's content,
* while they maintain separate offsets.
* This method does not modify {@link #readerOffset()} or {@link #writerOffset()} of this buffer.
* <p>
* This method increments the reference count of this buffer.
* The reference count is decremented again when the slice is deallocated.
* <p>
* The slice is created with a {@linkplain #writerOffset() write offset} equal to the length of the slice,
* so that the entire contents of the slice is ready to be read.
*
* @return A new buffer instance, with independent {@link #readerOffset()} and {@link #writerOffset()},
* that is a view of the given region of this buffer.
*/
Buf slice(int offset, int length);
/**
* Copies the given length of data from this buffer into the given destination array, beginning at the given source
* position in this buffer, and the given destination position in the destination array.
* <p>
* This method does not read or modify the {@linkplain #writerOffset() write offset} or the
* {@linkplain #readerOffset() read offset}.
*
* @param srcPos The byte offset into this buffer wherefrom the copying should start; the byte at this offset in
* this buffer will be copied to the {@code destPos} index in the {@code dest} array.
* @param dest The destination byte array.
* @param destPos The index into the {@code dest} array wherefrom the copying should start.
* @param length The number of bytes to copy.
* @throws NullPointerException if the destination array is null.
* @throws IndexOutOfBoundsException if the source or destination positions, or the length, are negative,
* or if the resulting end positions reaches beyond the end of either this buffer or the destination array.
*/
void copyInto(int srcPos, byte[] dest, int destPos, int length);
/**
* Copies the given length of data from this buffer into the given destination byte buffer, beginning at the given
* source position in this buffer, and the given destination position in the destination byte buffer.
* <p>
* This method does not read or modify the {@linkplain #writerOffset() write offset} or the
* {@linkplain #readerOffset() read offset}, nor is the position of the destination buffer changed.
* <p>
* The position and limit of the destination byte buffer are also ignored, and do not influence {@code destPos}
* or {@code length}.
*
* @param srcPos The byte offset into this buffer wherefrom the copying should start; the byte at this offset in
* this buffer will be copied to the {@code destPos} index in the {@code dest} array.
* @param dest The destination byte buffer.
* @param destPos The index into the {@code dest} array wherefrom the copying should start.
* @param length The number of bytes to copy.
* @throws NullPointerException if the destination array is null.
* @throws IndexOutOfBoundsException if the source or destination positions, or the length, are negative,
* or if the resulting end positions reaches beyond the end of either this buffer or the destination array.
*/
void copyInto(int srcPos, ByteBuffer dest, int destPos, int length);
/**
* Copies the given length of data from this buffer into the given destination buffer, beginning at the given
* source position in this buffer, and the given destination position in the destination buffer.
* <p>
* This method does not read or modify the {@linkplain #writerOffset() write offset} or the
* {@linkplain #readerOffset() read offset} on this buffer, nor on the destination buffer.
* <p>
* The read and write offsets of the destination buffer are also ignored, and do not influence {@code destPos}
* or {@code length}.
*
* @param srcPos The byte offset into this buffer wherefrom the copying should start; the byte at this offset in
* this buffer will be copied to the {@code destPos} index in the {@code dest} array.
* @param dest The destination buffer.
* @param destPos The index into the {@code dest} array wherefrom the copying should start.
* @param length The number of bytes to copy.
* @throws NullPointerException if the destination array is null.
* @throws IndexOutOfBoundsException if the source or destination positions, or the length, are negative,
* or if the resulting end positions reaches beyond the end of either this buffer or the destination array.
*/
void copyInto(int srcPos, Buf dest, int destPos, int length);
/**
* Resets the {@linkplain #readerOffset() read offset} and the {@linkplain #writerOffset() write offset} on this
* buffer to their initial values.
*/
default void reset() {
readerOffset(0);
writerOffset(0);
}
/**
* Iterate the readable bytes of this buffer. The {@linkplain #readerOffset() reader offset} and
* {@linkplain #writerOffset() witer offset} are not modified by the iterator.
* <p>
* Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the
* {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while
* the iteration takes place. Otherwise unpredictable behaviour might result.
*
* @return A {@link ByteIterator} for the readable bytes of this buffer.
*/
default ByteIterator iterate() {
return iterate(readerOffset(), readableBytes());
}
/**
* Iterate the given number bytes of this buffer, starting at the given offset.
* The {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() witer offset} are not modified by
* the iterator.
* <p>
* Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the
* {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while
* the iteration takes place. Otherwise unpredictable behaviour might result.
*
* @param fromOffset The offset into the buffer where iteration should start.
* The first byte read from the iterator will be the byte at this offset.
* @param length The number of bytes to iterate.
* @return A {@link ByteIterator} for the given stretch of bytes of this buffer.
* @throws IllegalArgumentException if the length is negative, or if the region given by the {@code fromOffset} and
* the {@code length} reaches outside of the bounds of this buffer.
*/
ByteIterator iterate(int fromOffset, int length);
/**
* Iterate the readable bytes of this buffer, in reverse. The {@linkplain #readerOffset() reader offset} and
* {@linkplain #writerOffset() witer offset} are not modified by the iterator.
* <p>
* Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the
* {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while
* the iteration takes place. Otherwise unpredictable behaviour might result.
*
* @return A {@link ByteIterator} for the readable bytes of this buffer.
*/
default ByteIterator iterateReverse() {
int woff = writerOffset();
return iterateReverse(woff == 0? 0 : woff - 1, readableBytes());
}
/**
* Iterate the given number bytes of this buffer, in reverse, starting at the given offset.
* The {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() witer offset} are not modified by
* the iterator.
* <p>
* Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the
* {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while
* the iteration takes place. Otherwise unpredictable behaviour might result.
*
* @param fromOffset The offset into the buffer where iteration should start.
* The first byte read from the iterator will be the byte at this offset.
* @param length The number of bytes to iterate.
* @return A {@link ByteIterator} for the given stretch of bytes of this buffer.
* @throws IllegalArgumentException if the length is negative, or if the region given by the {@code fromOffset} and
* the {@code length} reaches outside of the bounds of this buffer.
*/
ByteIterator iterateReverse(int fromOffset, int length);
/**
* Ensure that this buffer has {@linkplain #writableBytes() available space for writing} the given number of
* bytes.
* The buffer must be in {@linkplain #isOwned() an owned state}, or an exception will be thrown.
* If this buffer already has the necessary space, then this method returns immediately.
* If this buffer does not already have the necessary space, then it will be expanded using the {@link Allocator}
* the buffer was created with.
*
* @param size The requested number of bytes of space that should be available for writing.
* @throws IllegalStateException if this buffer is not in an owned state.
* That is, if {@link #countBorrows()} is not {@code 0}.
*/
void ensureWritable(int size);
}