/* * 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.b2; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * A reference counted buffer of memory, with separate reader and writer offsets. *

* A buffer is a sequential stretch of memory with a certain capacity, an offset for writing, and an offset for reading. * *

Creating a buffer

* * 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. * *

Life cycle and reference counting

* * The buffer has a life cycle, where it is allocated, used, and deallocated. * The reference count controls this life cycle. *

* When the buffer is initially allocated, a pairing {@link #close()} call will deallocate it. * In this state, the buffer {@linkplain #isOwned() is "owned"}. *

* 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. *

* Certain operations, such as {@link #send()}, are only available on owned buffers. * *

Thread-safety

* * 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. *

* Confined buffers will initially be confined to the thread that allocates them. *

* 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. *

* 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. * *

Accessing data

* * Data access methods fall into two classes: *
    *
  1. Access that are based on, and updates, the read or write offset positions.
  2. * *
  3. Access that take offsets as arguments, and do not update read or write offset positions.
  4. * *
* * A buffer contain two mutable offset positions: one for reading and one for writing. * These positions use zero-based indexing, * 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}. *

* This carves the buffer into three regions, as demonstrated by this diagram: *

 *      +-------------------+------------------+------------------+
 *      | discardable bytes |  readable bytes  |  writable bytes  |
 *      |                   |     (CONTENT)    |                  |
 *      +-------------------+------------------+------------------+
 *      |                   |                  |                  |
 *      0      <=     readerOffset  <=   writerOffset    <=    capacity
 * 
* */ public interface Buf extends Rc, 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. *

* This method increments the reference count of this buffer. * The reference count is decremented again when the slice is deallocated. *

* 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. *

* This method increments the reference count of this buffer. * The reference count is decremented again when the slice is deallocated. *

* 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. *

* 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. *

* 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. *

* 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. *

* 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. *

* 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. *

* 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. *

* 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. *

* 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. *

* 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); }