Class UnsafeBuffer

java.lang.Object
org.agrona.AbstractMutableDirectBuffer
org.agrona.concurrent.UnsafeBuffer
All Implemented Interfaces:
Comparable<DirectBuffer>, AtomicBuffer, DirectBuffer, MutableDirectBuffer

public class UnsafeBuffer extends AbstractMutableDirectBuffer implements AtomicBuffer
Supports regular, byte ordered, and atomic (memory ordered) access to an underlying buffer. The buffer can be a byte[], one of the various ByteBuffer implementations, or an off Java heap memory address.

ByteOrder of a wrapped buffer is not applied to the UnsafeBuffer. UnsafeBuffers are effectively stateless and can be used concurrently, the wrapping methods are an exception. To control ByteOrder use the appropriate method with the ByteOrder overload.

Note: This class has a natural ordering that is inconsistent with equals. Types may be different but equal on buffer contents.

Note: The wrap methods on this class are not thread safe. Concurrent access should only happen after a successful wrap.

  • Field Details

  • Constructor Details

    • UnsafeBuffer

      public UnsafeBuffer()
      Empty constructor for a reusable wrapper buffer.
    • UnsafeBuffer

      public UnsafeBuffer(byte[] buffer)
      Attach a view to a byte[] for providing direct access.
      Parameters:
      buffer - to which the view is attached.
      See Also:
    • UnsafeBuffer

      public UnsafeBuffer(byte[] buffer, int offset, int length)
      Attach a view to a byte[] for providing direct access.
      Parameters:
      buffer - to which the view is attached.
      offset - in bytes within the buffer to begin.
      length - in bytes of the buffer included in the view.
      See Also:
    • UnsafeBuffer

      public UnsafeBuffer(ByteBuffer buffer)
      Attach a view to a ByteBuffer for providing direct access, the ByteBuffer can be heap based or direct.
      Parameters:
      buffer - to which the view is attached.
    • UnsafeBuffer

      public UnsafeBuffer(ByteBuffer buffer, int offset, int length)
      Attach a view to a ByteBuffer for providing direct access, the ByteBuffer can be heap based or direct.
      Parameters:
      buffer - to which the view is attached.
      offset - in bytes within the buffer to begin.
      length - in bytes of the buffer included in the view.
    • UnsafeBuffer

      public UnsafeBuffer(DirectBuffer buffer)
      Attach a view to an existing DirectBuffer.
      Parameters:
      buffer - to which the view is attached.
    • UnsafeBuffer

      public UnsafeBuffer(DirectBuffer buffer, int offset, int length)
      Attach a view to an existing DirectBuffer.
      Parameters:
      buffer - to which the view is attached.
      offset - in bytes within the buffer to begin.
      length - in bytes of the buffer included in the view.
    • UnsafeBuffer

      public UnsafeBuffer(long address, int length)
      Attach a view to an off-heap memory region by address. This is useful for interacting with native libraries.
      Parameters:
      address - where the memory begins off-heap.
      length - of the buffer from the given address.
  • Method Details

    • wrap

      public void wrap(byte[] buffer)
      Attach a view to a byte[] for providing direct access.
      Specified by:
      wrap in interface DirectBuffer
      Parameters:
      buffer - to which the view is attached.
    • wrap

      public void wrap(byte[] buffer, int offset, int length)
      Attach a view to a byte[] for providing direct access.
      Specified by:
      wrap in interface DirectBuffer
      Parameters:
      buffer - to which the view is attached.
      offset - in bytes at which the view begins.
      length - in bytes of the buffer included in the view.
    • wrap

      public void wrap(ByteBuffer buffer)
      Attach a view to a ByteBuffer for providing direct access, the ByteBuffer can be heap based or direct. The ByteBuffer.order() is not relevant for accessing the wrapped buffer.

      When using this method to wrap the view of the ByteBuffer the entire ByteBuffer gets wrapped between index 0 and capacity. If you want to just wrap the ByteBuffer between the position and the limit then you should use the DirectBuffer.wrap(ByteBuffer, int, int) method, eg:

      directBuffer.wrap(byteBuffer, byteBuffer.position(), byteBuffer.remaining());

      Specified by:
      wrap in interface DirectBuffer
      Parameters:
      buffer - to which the view is attached.
    • wrap

      public void wrap(ByteBuffer buffer, int offset, int length)
      Attach a view to a ByteBuffer for providing direct access.

      The ByteBuffer.order() is not relevant for accessing the wrapped buffer.

      Specified by:
      wrap in interface DirectBuffer
      Parameters:
      buffer - to which the view is attached.
      offset - in bytes at which the view begins.
      length - in bytes of the buffer included in the view.
    • wrap

      public void wrap(DirectBuffer buffer)
      Attach a view to an existing DirectBuffer.
      Specified by:
      wrap in interface DirectBuffer
      Parameters:
      buffer - to which the view is attached.
    • wrap

      public void wrap(DirectBuffer buffer, int offset, int length)
      Attach a view to a DirectBuffer for providing direct access.
      Specified by:
      wrap in interface DirectBuffer
      Parameters:
      buffer - to which the view is attached.
      offset - in bytes at which the view begins.
      length - in bytes of the buffer included in the view.
    • wrap

      public void wrap(long address, int length)
      Attach a view to an off-heap memory region by address.
      Specified by:
      wrap in interface DirectBuffer
      Parameters:
      address - where the memory begins off-heap.
      length - of the buffer from the given address.
    • byteBuffer

      public ByteBuffer byteBuffer()
      Get the underlying ByteBuffer if one exists.

      NB: there may not be a one-to-one mapping between indices on this buffer and the underlying byte[], see DirectBuffer.wrapAdjustment().

      Specified by:
      byteBuffer in interface DirectBuffer
      Returns:
      the underlying ByteBuffer if one exists.
    • wrapAdjustment

      public int wrapAdjustment()
      Get the adjustment in indices between an index in this buffer and the wrapped object. The wrapped object might be a ByteBuffer or a byte[].

      You only need to use this adjustment if you plan to perform operations on the underlying byte array or byte buffer that rely on their indices.

      Specified by:
      wrapAdjustment in interface DirectBuffer
      Returns:
      the adjustment in indices between an index in this buffer and the wrapped object.
      See Also:
    • isExpandable

      public boolean isExpandable()
      Is this buffer expandable to accommodate putting data into it beyond the current capacity?
      Specified by:
      isExpandable in interface MutableDirectBuffer
      Returns:
      true is the underlying storage can expand otherwise false.
    • verifyAlignment

      public void verifyAlignment()
      Verify that the underlying buffer is correctly aligned to prevent word tearing, other ordering issues and the JVM crashes. In particular this method verifies that the starting offset of the underlying buffer is properly aligned. However, the actual atomic call must ensure that the index is properly aligned, i.e. it must be aligned to the size of the operand. For example a call to any of the following methods AtomicBuffer.putIntRelease(int, int), AtomicBuffer.putIntVolatile(int, int), AtomicBuffer.addIntRelease(int, int) (int, int)}, AtomicBuffer.getIntVolatile(int), AtomicBuffer.getAndAddInt(int, int) or AtomicBuffer.getAndSetInt(int, int), must have the index aligned by four bytes (e.g. 0, 4, 8, 12, 60 etc.).

      Users are encouraged to call this method after constructing the AtomicBuffer instance in order to ensure that the underlying buffer supports atomic access to long values.

      Agrona provides an agent (org.agrona.agent.BufferAlignmentAgent) that checks the alignment of indexes for all operations at runtime. The agent throws an exception if the unaligned access is detected.

      Note: on some platforms unaligned atomic access can lead to the JVM crashes, e.g.:

       
       # Java VM: OpenJDK 64-Bit Server VM (25.352-b08 mixed mode bsd-aarch64 compressed oops)
       #
       # siginfo: si_signo: 10 (SIGBUS), si_code: 1 (BUS_ADRALN)
       
       
      Specified by:
      verifyAlignment in interface AtomicBuffer
      See Also:
    • getLongVolatile

      public long getLongVolatile(int index)
      Atomically get the value at a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      getLongVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes from which to get.
      Returns:
      the value for at a given index.
    • putLongVolatile

      public void putLongVolatile(int index, long value)
      Atomically put a value to a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      putLongVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • getLongAcquire

      public long getLongAcquire(int index)
      Atomically get the value at a given index with acquire semantics.
      Specified by:
      getLongAcquire in interface AtomicBuffer
      Parameters:
      index - in bytes from which to get.
      Returns:
      the value for at a given index.
    • putLongOrdered

      public void putLongOrdered(int index, long value)
      Atomically put a value to a given index with ordered store semantics.

      Instead of using this method, use AtomicBuffer.putLongRelease(int, long) instead. They are identical and the putLongRelease is the preferred version.

      Specified by:
      putLongOrdered in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • putLongRelease

      public void putLongRelease(int index, long value)
      Atomically put a value to a given index with release semantics.
      Specified by:
      putLongRelease in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • addLongOrdered

      public long addLongOrdered(int index, long increment)
      Atomically adds a value to a given index with ordered store semantics. Use a negative increment to decrement.

      The load has no ordering semantics. The store has release semantics.

      Instead of using this method, use AtomicBuffer.addLongRelease(int, long) instead. They are identical but the addLongRelease is the preferred version.

      Specified by:
      addLongOrdered in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      increment - by which the value at the index will be adjusted.
      Returns:
      the previous value at the index.
    • addLongRelease

      public long addLongRelease(int index, long increment)
      Atomically adds a value to a given index with ordered store semantics. Use a negative increment to decrement.

      The load has no ordering semantics. The store has release semantics.

      Specified by:
      addLongRelease in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      increment - by which the value at the index will be adjusted.
      Returns:
      the previous value at the index.
    • putLongOpaque

      public void putLongOpaque(int index, long value)
      Atomically put a value to a given index with opaque semantics.
      Specified by:
      putLongOpaque in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • getLongOpaque

      public long getLongOpaque(int index)
      Atomically get a value to a given index with opaque semantics.
      Specified by:
      getLongOpaque in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      Returns:
      the value for at a given index.
    • addLongOpaque

      public long addLongOpaque(int index, long increment)
      Adds a value to a given index with opaque semantics. The read and write will be atomic, but the combination is not atomic. So don't use this method concurrently because you can run into lost updates due to a race-condition.
      Specified by:
      addLongOpaque in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      increment - by which the value at the index will be adjusted.
      Returns:
      the previous value at the index.
    • compareAndSetLong

      public boolean compareAndSetLong(int index, long expectedValue, long updateValue)
      Atomic compare and set of a long given an expected value.

      This call has sequential-consistent semantics.

      Specified by:
      compareAndSetLong in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      expectedValue - at to be compared.
      updateValue - to be exchanged.
      Returns:
      set successful or not.
    • compareAndExchangeLong

      public long compareAndExchangeLong(int index, long expectedValue, long updateValue)
      Atomic compare and exchange of a long given an expected value.

      This call has sequential-consistent semantics.

      Specified by:
      compareAndExchangeLong in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      expectedValue - at to be compared.
      updateValue - to be exchanged.
      Returns:
      the old value no matter if the expected value was found.
    • getAndSetLong

      public long getAndSetLong(int index, long value)
      Atomically exchange a value at a location returning the previous contents.

      This call has sequential-consistent semantics.

      Specified by:
      getAndSetLong in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
      Returns:
      previous value at the index.
    • getAndAddLong

      public long getAndAddLong(int index, long delta)
      Atomically add a delta to a value at a location returning the previous contents. To decrement a negative delta can be provided.

      This call has sequential-consistent semantics.

      Specified by:
      getAndAddLong in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      delta - to be added to the value at the index.
      Returns:
      previous value.
    • getIntVolatile

      public int getIntVolatile(int index)
      Atomically get the value at a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      getIntVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes from which to get.
      Returns:
      the value for at a given index.
    • putIntVolatile

      public void putIntVolatile(int index, int value)
      Atomically put a value to a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      putIntVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • getIntAcquire

      public int getIntAcquire(int index)
      Atomically get the value at a given index with acquire semantics.
      Specified by:
      getIntAcquire in interface AtomicBuffer
      Parameters:
      index - in bytes from which to get.
      Returns:
      the value for at a given index.
    • putIntOrdered

      public void putIntOrdered(int index, int value)
      Atomically put a value to a given index with ordered semantics.

      Instead of using this method, use AtomicBuffer.putIntRelease(int, int) instead. They are identical but the putIntRelease is the preferred version.

      Specified by:
      putIntOrdered in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • putIntRelease

      public void putIntRelease(int index, int value)
      Atomically put a value to a given index with release semantics.
      Specified by:
      putIntRelease in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • addIntOrdered

      public int addIntOrdered(int index, int increment)
      Atomically add a value to a given index with ordered store semantics. Use a negative increment to decrement.

      The load has no ordering semantics. The store has release semantics.

      Instead of using this method, use AtomicBuffer.addIntRelease(int, int) instead. They are identical but the addIntRelease is the preferred version.

      Specified by:
      addIntOrdered in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      increment - by which the value at the index will be adjusted.
      Returns:
      the previous value at the index.
    • addIntRelease

      public int addIntRelease(int index, int increment)
      Atomically add a value to a given index with release semantics. Use a negative increment to decrement.

      The load has no ordering semantics. The store has release semantics.

      Specified by:
      addIntRelease in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      increment - by which the value at the index will be adjusted.
      Returns:
      the previous value at the index.
    • putIntOpaque

      public void putIntOpaque(int index, int value)
      Atomically put a value to a given index with opaque semantics.
      Specified by:
      putIntOpaque in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • getIntOpaque

      public int getIntOpaque(int index)
      Atomically get a value to a given index with opaque semantics.
      Specified by:
      getIntOpaque in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      Returns:
      the value for at a given index.
    • addIntOpaque

      public int addIntOpaque(int index, int increment)
      Adds a value to a given index with opaque semantics. The read and write will be atomic, but the combination is not atomic. So don't use this method concurrently because you can run into lost updates due to a race-condition.
      Specified by:
      addIntOpaque in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      increment - by which the value at the index will be adjusted.
      Returns:
      the previous value at the index.
    • compareAndSetInt

      public boolean compareAndSetInt(int index, int expectedValue, int updateValue)
      Atomic compare and set of an int given an expected value.

      This call has sequential-consistent semantics.

      Specified by:
      compareAndSetInt in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      expectedValue - at to be compared.
      updateValue - to be exchanged.
      Returns:
      successful or not.
    • compareAndExchangeInt

      public int compareAndExchangeInt(int index, int expectedValue, int updateValue)
      Atomic compare and exchange of a int given an expected value.

      This call has sequential-consistent semantics.

      Specified by:
      compareAndExchangeInt in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      expectedValue - at to be compared.
      updateValue - to be exchanged.
      Returns:
      the old value no matter if the expected value was found.
    • getAndSetInt

      public int getAndSetInt(int index, int value)
      Atomically exchange a value at a location returning the previous contents.

      This call has sequential-consistent semantics.

      Specified by:
      getAndSetInt in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
      Returns:
      previous value.
    • getAndAddInt

      public int getAndAddInt(int index, int delta)
      Atomically add a delta to a value at a location returning the previous contents. To decrement a negative delta can be provided.

      This call has sequential-consistent semantics.

      Specified by:
      getAndAddInt in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      delta - to be added to the value at the index.
      Returns:
      previous value.
    • getShortVolatile

      public short getShortVolatile(int index)
      Atomically get the value at a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      getShortVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes from which to get.
      Returns:
      the value for at a given index.
    • putShortVolatile

      public void putShortVolatile(int index, short value)
      Atomically put a value to a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      putShortVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • getByteVolatile

      public byte getByteVolatile(int index)
      Atomically get the value at a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      getByteVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes from which to get.
      Returns:
      the value for at a given index.
    • putByteVolatile

      public void putByteVolatile(int index, byte value)
      Atomically put a value to a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      putByteVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • getCharVolatile

      public char getCharVolatile(int index)
      Atomically get the value at a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      getCharVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes from which to get.
      Returns:
      the value for at a given index.
    • putCharVolatile

      public void putCharVolatile(int index, char value)
      Atomically put a value to a given index with volatile semantics.

      This call has sequential-consistent semantics.

      Specified by:
      putCharVolatile in interface AtomicBuffer
      Parameters:
      index - in bytes for where to put.
      value - for at a given index.
    • toString

      public String toString()
      Overrides:
      toString in class Object
    • ensureCapacity

      protected final void ensureCapacity(int index, int length)
      Description copied from class: AbstractMutableDirectBuffer
      A hook to ensure the underlying buffer has enough capacity for writing data into the buffer.
      Specified by:
      ensureCapacity in class AbstractMutableDirectBuffer
      Parameters:
      index - at which write occurs.
      length - in bytes.
    • boundsCheckWrap

      private static void boundsCheckWrap(int offset, int length, int capacity)