blob: cbf5ecb40440b630334bf53ce7da0bf14b88c15a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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
*
* http://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.apache.flink.core.memory;
import org.apache.flink.annotation.Internal;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;
import static org.apache.flink.util.Preconditions.checkArgument;
/**
* This class represents a piece of memory managed by Flink.
* The segment may be backed by heap memory (byte array) or by off-heap memory.
*
* <p>The methods for individual memory access are specialized in the classes
* {@link org.apache.flink.core.memory.HeapMemorySegment} and
* {@link org.apache.flink.core.memory.HybridMemorySegment}.
* All methods that operate across two memory segments are implemented in this class,
* to transparently handle the mixing of memory segment types.
*
* <p>This class fulfills conceptually a similar purpose as Java's {@link java.nio.ByteBuffer}.
* We add this specialized class for various reasons:
* <ul>
* <li>It offers additional binary compare, swap, and copy methods.</li>
* <li>It uses collapsed checks for range check and memory segment disposal.</li>
* <li>It offers absolute positioning methods for bulk put/get methods, to guarantee
* thread safe use.</li>
* <li>It offers explicit big-endian / little-endian access methods, rather than tracking internally
* a byte order.</li>
* <li>It transparently and efficiently moves data between on-heap and off-heap variants.</li>
* </ul>
*
* <p><i>Comments on the implementation</i>:
* We make heavy use of operations that are supported by native
* instructions, to achieve a high efficiency. Multi byte types (int, long, float, double, ...)
* are read and written with "unsafe" native commands.
*
* <p>Below is an example of the code generated for the {@link HeapMemorySegment#putLongBigEndian(int, long)}
* function by the just-in-time compiler. The code is grabbed from an Oracle JVM 7 using the
* hotspot disassembler library (hsdis32.dll) and the jvm command
* <i>-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MemorySegment.putLongBigEndian</i>.
* Note that this code realizes both the byte order swapping and the reinterpret cast access to
* get a long from the byte array.
*
* <p><pre>
* [Verified Entry Point]
* 0x00007fc403e19920: sub $0x18,%rsp
* 0x00007fc403e19927: mov %rbp,0x10(%rsp) ;*synchronization entry
* ; - org.apache.flink.runtime.memory.UnsafeMemorySegment::putLongBigEndian@-1 (line 652)
* 0x00007fc403e1992c: mov 0xc(%rsi),%r10d ;*getfield memory
* ; - org.apache.flink.runtime.memory.UnsafeMemorySegment::putLong@4 (line 611)
* ; - org.apache.flink.runtime.memory.UnsafeMemorySegment::putLongBigEndian@12 (line 653)
* 0x00007fc403e19930: bswap %rcx
* 0x00007fc403e19933: shl $0x3,%r10
* 0x00007fc403e19937: movslq %edx,%r11
* 0x00007fc403e1993a: mov %rcx,0x10(%r10,%r11,1) ;*invokevirtual putLong
* ; - org.apache.flink.runtime.memory.UnsafeMemorySegment::putLong@14 (line 611)
* ; - org.apache.flink.runtime.memory.UnsafeMemorySegment::putLongBigEndian@12 (line 653)
* 0x00007fc403e1993f: add $0x10,%rsp
* 0x00007fc403e19943: pop %rbp
* 0x00007fc403e19944: test %eax,0x5ba76b6(%rip) # 0x00007fc4099c1000
* ; {poll_return}
* 0x00007fc403e1994a: retq
* </pre>
*
* <p><i>Note on efficiency</i>:
* For best efficiency, the code that uses this class should make sure that only one
* subclass is loaded, or that the methods that are abstract in this class are used only from one of the
* subclasses (either the {@link org.apache.flink.core.memory.HeapMemorySegment}, or the
* {@link org.apache.flink.core.memory.HybridMemorySegment}).
*
* <p>That way, all the abstract methods in the MemorySegment base class have only one loaded
* actual implementation. This is easy for the JIT to recognize through class hierarchy analysis,
* or by identifying that the invocations are monomorphic (all go to the same concrete
* method implementation). Under these conditions, the JIT can perfectly inline methods.
*/
@Internal
public abstract class MemorySegment {
/**
* The unsafe handle for transparent memory copied (heap / off-heap).
*/
@SuppressWarnings("restriction")
protected static final sun.misc.Unsafe UNSAFE = MemoryUtils.UNSAFE;
/**
* The beginning of the byte array contents, relative to the byte array object.
*/
@SuppressWarnings("restriction")
protected static final long BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
/**
* Constant that flags the byte order. Because this is a boolean constant, the JIT compiler can
* use this well to aggressively eliminate the non-applicable code paths.
*/
private static final boolean LITTLE_ENDIAN = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN);
// ------------------------------------------------------------------------
/**
* The heap byte array object relative to which we access the memory.
*
* <p>Is non-<tt>null</tt> if the memory is on the heap, and is <tt>null</tt>, if the memory if
* off the heap. If we have this buffer, we must never void this reference, or the memory
* segment will point to undefined addresses outside the heap and may in out-of-order execution
* cases cause segmentation faults.
*/
protected byte[] heapMemory;
/**
* The address to the data, relative to the heap memory byte array. If the heap memory byte
* array is <tt>null</tt>, this becomes an absolute memory address outside the heap.
*/
protected long address;
/**
* The address one byte after the last addressable byte, i.e. <tt>address + size</tt> while the
* segment is not disposed.
*/
protected long addressLimit;
/**
* The size in bytes of the memory segment.
*/
protected int size;
/**
* Optional owner of the memory segment.
*/
protected Object owner;
/**
* Creates a new memory segment that represents the memory of the byte array.
*
* <p>Since the byte array is backed by on-heap memory, this memory segment holds its
* data on heap. The buffer must be at least of size 8 bytes.
*
* @param buffer The byte array whose memory is represented by this memory segment.
*/
MemorySegment(byte[] buffer, Object owner) {
if (buffer == null) {
throw new NullPointerException("buffer");
}
this.heapMemory = buffer;
this.address = BYTE_ARRAY_BASE_OFFSET;
this.size = buffer.length;
this.addressLimit = this.address + this.size;
this.owner = owner;
}
/**
* Creates a new memory segment that represents the memory at the absolute address given
* by the pointer.
*
* @param offHeapAddress The address of the memory represented by this memory segment.
* @param size The size of this memory segment.
*/
MemorySegment(long offHeapAddress, int size, Object owner) {
if (offHeapAddress <= 0) {
throw new IllegalArgumentException("negative pointer or size");
}
if (offHeapAddress >= Long.MAX_VALUE - Integer.MAX_VALUE) {
// this is necessary to make sure the collapsed checks are safe against numeric overflows
throw new IllegalArgumentException("Segment initialized with too large address: " + offHeapAddress
+ " ; Max allowed address is " + (Long.MAX_VALUE - Integer.MAX_VALUE - 1));
}
this.heapMemory = null;
this.address = offHeapAddress;
this.addressLimit = this.address + size;
this.size = size;
this.owner = owner;
}
// ------------------------------------------------------------------------
// Memory Segment Operations
// ------------------------------------------------------------------------
/**
* Gets the size of the memory segment, in bytes.
*
* @return The size of the memory segment.
*/
public int size() {
return size;
}
/**
* Checks whether the memory segment was freed.
*
* @return <tt>true</tt>, if the memory segment has been freed, <tt>false</tt> otherwise.
*/
public boolean isFreed() {
return address > addressLimit;
}
/**
* Frees this memory segment.
*
* <p>After this operation has been called, no further operations are possible on the memory
* segment and will fail. The actual memory (heap or off-heap) will only be released after this
* memory segment object has become garbage collected.
*/
public void free() {
// this ensures we can place no more data and trigger
// the checks for the freed segment
address = addressLimit + 1;
}
/**
* Checks whether this memory segment is backed by off-heap memory.
*
* @return <tt>true</tt>, if the memory segment is backed by off-heap memory, <tt>false</tt> if
* it is backed by heap memory.
*/
public boolean isOffHeap() {
return heapMemory == null;
}
/**
* Returns the byte array of on-heap memory segments.
*
* @return underlying byte array
*
* @throws IllegalStateException
* if the memory segment does not represent on-heap memory
*/
public byte[] getArray() {
if (heapMemory != null) {
return heapMemory;
} else {
throw new IllegalStateException("Memory segment does not represent heap memory");
}
}
/**
* Returns the memory address of off-heap memory segments.
*
* @return absolute memory address outside the heap
*
* @throws IllegalStateException
* if the memory segment does not represent off-heap memory
*/
public long getAddress() {
if (heapMemory == null) {
return address;
} else {
throw new IllegalStateException("Memory segment does not represent off heap memory");
}
}
/**
* Wraps the chunk of the underlying memory located between <tt>offset</tt> and
* <tt>length</tt> in a NIO ByteBuffer.
*
* @param offset The offset in the memory segment.
* @param length The number of bytes to be wrapped as a buffer.
*
* @return A <tt>ByteBuffer</tt> backed by the specified portion of the memory segment.
* @throws IndexOutOfBoundsException Thrown, if offset is negative or larger than the memory segment size,
* or if the offset plus the length is larger than the segment size.
*/
public abstract ByteBuffer wrap(int offset, int length);
/**
* Gets the owner of this memory segment. Returns null, if the owner was not set.
*
* @return The owner of the memory segment, or null, if it does not have an owner.
*/
public Object getOwner() {
return owner;
}
// ------------------------------------------------------------------------
// Random Access get() and put() methods
// ------------------------------------------------------------------------
//------------------------------------------------------------------------
// Notes on the implementation: We try to collapse as many checks as
// possible. We need to obey the following rules to make this safe
// against segfaults:
//
// - Grab mutable fields onto the stack before checking and using. This
// guards us against concurrent modifications which invalidate the
// pointers
// - Use subtractions for range checks, as they are tolerant
//------------------------------------------------------------------------
/**
* Reads the byte at the given position.
*
* @param index The position from which the byte will be read
* @return The byte at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger or equal to the size of
* the memory segment.
*/
public abstract byte get(int index);
/**
* Writes the given byte into this buffer at the given position.
*
* @param index The index at which the byte will be written.
* @param b The byte value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger or equal to the size of
* the memory segment.
*/
public abstract void put(int index, byte b);
/**
* Bulk get method. Copies dst.length memory from the specified position to
* the destination memory.
*
* @param index The position at which the first byte will be read.
* @param dst The memory into which the memory will be copied.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or too large that the data between the
* index and the memory segment end is not enough to fill the destination array.
*/
public abstract void get(int index, byte[] dst);
/**
* Bulk put method. Copies src.length memory from the source memory into the
* memory segment beginning at the specified position.
*
* @param index The index in the memory segment array, where the data is put.
* @param src The source array to copy the data from.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or too large such that the array
* size exceed the amount of memory between the index and the memory
* segment's end.
*/
public abstract void put(int index, byte[] src);
/**
* Bulk get method. Copies length memory from the specified position to the
* destination memory, beginning at the given offset.
*
* @param index The position at which the first byte will be read.
* @param dst The memory into which the memory will be copied.
* @param offset The copying offset in the destination memory.
* @param length The number of bytes to be copied.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or too large that the requested number of
* bytes exceed the amount of memory between the index and the memory
* segment's end.
*/
public abstract void get(int index, byte[] dst, int offset, int length);
/**
* Bulk put method. Copies length memory starting at position offset from
* the source memory into the memory segment starting at the specified
* index.
*
* @param index The position in the memory segment array, where the data is put.
* @param src The source array to copy the data from.
* @param offset The offset in the source array where the copying is started.
* @param length The number of bytes to copy.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or too large such that the array
* portion to copy exceed the amount of memory between the index and the memory
* segment's end.
*/
public abstract void put(int index, byte[] src, int offset, int length);
/**
* Reads one byte at the given position and returns its boolean
* representation.
*
* @param index The position from which the memory will be read.
* @return The boolean value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 1.
*/
public abstract boolean getBoolean(int index);
/**
* Writes one byte containing the byte value into this buffer at the given
* position.
*
* @param index The position at which the memory will be written.
* @param value The char value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 1.
*/
public abstract void putBoolean(int index, boolean value);
/**
* Reads a char value from the given position, in the system's native byte order.
*
* @param index The position from which the memory will be read.
* @return The char value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 2.
*/
@SuppressWarnings("restriction")
public final char getChar(int index) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 2) {
return UNSAFE.getChar(heapMemory, pos);
}
else if (address > addressLimit) {
throw new IllegalStateException("This segment has been freed.");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Reads a character value (16 bit, 2 bytes) from the given position, in little-endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getChar(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getChar(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The character value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final char getCharLittleEndian(int index) {
if (LITTLE_ENDIAN) {
return getChar(index);
} else {
return Character.reverseBytes(getChar(index));
}
}
/**
* Reads a character value (16 bit, 2 bytes) from the given position, in big-endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getChar(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getChar(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The character value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final char getCharBigEndian(int index) {
if (LITTLE_ENDIAN) {
return Character.reverseBytes(getChar(index));
} else {
return getChar(index);
}
}
/**
* Writes a char value to the given position, in the system's native byte order.
*
* @param index The position at which the memory will be written.
* @param value The char value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 2.
*/
@SuppressWarnings("restriction")
public final void putChar(int index, char value) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 2) {
UNSAFE.putChar(heapMemory, pos, value);
}
else if (address > addressLimit) {
throw new IllegalStateException("segment has been freed");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Writes the given character (16 bit, 2 bytes) to the given position in little-endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putChar(int, char)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putChar(int, char)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The char value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final void putCharLittleEndian(int index, char value) {
if (LITTLE_ENDIAN) {
putChar(index, value);
} else {
putChar(index, Character.reverseBytes(value));
}
}
/**
* Writes the given character (16 bit, 2 bytes) to the given position in big-endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putChar(int, char)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putChar(int, char)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The char value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final void putCharBigEndian(int index, char value) {
if (LITTLE_ENDIAN) {
putChar(index, Character.reverseBytes(value));
} else {
putChar(index, value);
}
}
/**
* Reads a short integer value (16 bit, 2 bytes) from the given position, composing them into a short value
* according to the current byte order.
*
* @param index The position from which the memory will be read.
* @return The short value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 2.
*/
public final short getShort(int index) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 2) {
return UNSAFE.getShort(heapMemory, pos);
}
else if (address > addressLimit) {
throw new IllegalStateException("segment has been freed");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Reads a short integer value (16 bit, 2 bytes) from the given position, in little-endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getShort(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getShort(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The short value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final short getShortLittleEndian(int index) {
if (LITTLE_ENDIAN) {
return getShort(index);
} else {
return Short.reverseBytes(getShort(index));
}
}
/**
* Reads a short integer value (16 bit, 2 bytes) from the given position, in big-endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getShort(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getShort(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The short value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final short getShortBigEndian(int index) {
if (LITTLE_ENDIAN) {
return Short.reverseBytes(getShort(index));
} else {
return getShort(index);
}
}
/**
* Writes the given short value into this buffer at the given position, using
* the native byte order of the system.
*
* @param index The position at which the value will be written.
* @param value The short value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 2.
*/
public final void putShort(int index, short value) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 2) {
UNSAFE.putShort(heapMemory, pos, value);
}
else if (address > addressLimit) {
throw new IllegalStateException("segment has been freed");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Writes the given short integer value (16 bit, 2 bytes) to the given position in little-endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putShort(int, short)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putShort(int, short)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The short value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final void putShortLittleEndian(int index, short value) {
if (LITTLE_ENDIAN) {
putShort(index, value);
} else {
putShort(index, Short.reverseBytes(value));
}
}
/**
* Writes the given short integer value (16 bit, 2 bytes) to the given position in big-endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putShort(int, short)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putShort(int, short)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The short value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment size minus 2.
*/
public final void putShortBigEndian(int index, short value) {
if (LITTLE_ENDIAN) {
putShort(index, Short.reverseBytes(value));
} else {
putShort(index, value);
}
}
/**
* Reads an int value (32bit, 4 bytes) from the given position, in the system's native byte order.
* This method offers the best speed for integer reading and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position from which the value will be read.
* @return The int value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final int getInt(int index) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 4) {
return UNSAFE.getInt(heapMemory, pos);
}
else if (address > addressLimit) {
throw new IllegalStateException("segment has been freed");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Reads an int value (32bit, 4 bytes) from the given position, in little-endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getInt(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getInt(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The int value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final int getIntLittleEndian(int index) {
if (LITTLE_ENDIAN) {
return getInt(index);
} else {
return Integer.reverseBytes(getInt(index));
}
}
/**
* Reads an int value (32bit, 4 bytes) from the given position, in big-endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getInt(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getInt(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The int value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final int getIntBigEndian(int index) {
if (LITTLE_ENDIAN) {
return Integer.reverseBytes(getInt(index));
} else {
return getInt(index);
}
}
/**
* Writes the given int value (32bit, 4 bytes) to the given position in the system's native
* byte order. This method offers the best speed for integer writing and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The int value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final void putInt(int index, int value) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 4) {
UNSAFE.putInt(heapMemory, pos, value);
}
else if (address > addressLimit) {
throw new IllegalStateException("segment has been freed");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Writes the given int value (32bit, 4 bytes) to the given position in little endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putInt(int, int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putInt(int, int)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The int value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final void putIntLittleEndian(int index, int value) {
if (LITTLE_ENDIAN) {
putInt(index, value);
} else {
putInt(index, Integer.reverseBytes(value));
}
}
/**
* Writes the given int value (32bit, 4 bytes) to the given position in big endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putInt(int, int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putInt(int, int)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The int value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final void putIntBigEndian(int index, int value) {
if (LITTLE_ENDIAN) {
putInt(index, Integer.reverseBytes(value));
} else {
putInt(index, value);
}
}
/**
* Reads a long value (64bit, 8 bytes) from the given position, in the system's native byte order.
* This method offers the best speed for long integer reading and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position from which the value will be read.
* @return The long value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final long getLong(int index) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 8) {
return UNSAFE.getLong(heapMemory, pos);
}
else if (address > addressLimit) {
throw new IllegalStateException("segment has been freed");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Reads a long integer value (64bit, 8 bytes) from the given position, in little endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getLong(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getLong(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The long value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final long getLongLittleEndian(int index) {
if (LITTLE_ENDIAN) {
return getLong(index);
} else {
return Long.reverseBytes(getLong(index));
}
}
/**
* Reads a long integer value (64bit, 8 bytes) from the given position, in big endian byte order.
* This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getLong(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getLong(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The long value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final long getLongBigEndian(int index) {
if (LITTLE_ENDIAN) {
return Long.reverseBytes(getLong(index));
} else {
return getLong(index);
}
}
/**
* Writes the given long value (64bit, 8 bytes) to the given position in the system's native
* byte order. This method offers the best speed for long integer writing and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The long value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putLong(int index, long value) {
final long pos = address + index;
if (index >= 0 && pos <= addressLimit - 8) {
UNSAFE.putLong(heapMemory, pos, value);
}
else if (address > addressLimit) {
throw new IllegalStateException("segment has been freed");
}
else {
// index is in fact invalid
throw new IndexOutOfBoundsException();
}
}
/**
* Writes the given long value (64bit, 8 bytes) to the given position in little endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putLong(int, long)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putLong(int, long)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The long value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putLongLittleEndian(int index, long value) {
if (LITTLE_ENDIAN) {
putLong(index, value);
} else {
putLong(index, Long.reverseBytes(value));
}
}
/**
* Writes the given long value (64bit, 8 bytes) to the given position in big endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putLong(int, long)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putLong(int, long)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The long value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putLongBigEndian(int index, long value) {
if (LITTLE_ENDIAN) {
putLong(index, Long.reverseBytes(value));
} else {
putLong(index, value);
}
}
/**
* Reads a single-precision floating point value (32bit, 4 bytes) from the given position, in the system's
* native byte order. This method offers the best speed for float reading and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position from which the value will be read.
* @return The float value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final float getFloat(int index) {
return Float.intBitsToFloat(getInt(index));
}
/**
* Reads a single-precision floating point value (32bit, 4 bytes) from the given position, in little endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getFloat(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getFloat(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The long value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final float getFloatLittleEndian(int index) {
return Float.intBitsToFloat(getIntLittleEndian(index));
}
/**
* Reads a single-precision floating point value (32bit, 4 bytes) from the given position, in big endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getFloat(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getFloat(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The long value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final float getFloatBigEndian(int index) {
return Float.intBitsToFloat(getIntBigEndian(index));
}
/**
* Writes the given single-precision float value (32bit, 4 bytes) to the given position in the system's native
* byte order. This method offers the best speed for float writing and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The float value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final void putFloat(int index, float value) {
putInt(index, Float.floatToRawIntBits(value));
}
/**
* Writes the given single-precision float value (32bit, 4 bytes) to the given position in little endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putFloat(int, float)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putFloat(int, float)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The long value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putFloatLittleEndian(int index, float value) {
putIntLittleEndian(index, Float.floatToRawIntBits(value));
}
/**
* Writes the given single-precision float value (32bit, 4 bytes) to the given position in big endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putFloat(int, float)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putFloat(int, float)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The long value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putFloatBigEndian(int index, float value) {
putIntBigEndian(index, Float.floatToRawIntBits(value));
}
/**
* Reads a double-precision floating point value (64bit, 8 bytes) from the given position, in the system's
* native byte order. This method offers the best speed for double reading and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position from which the value will be read.
* @return The double value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final double getDouble(int index) {
return Double.longBitsToDouble(getLong(index));
}
/**
* Reads a double-precision floating point value (64bit, 8 bytes) from the given position, in little endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getDouble(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getDouble(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The long value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final double getDoubleLittleEndian(int index) {
return Double.longBitsToDouble(getLongLittleEndian(index));
}
/**
* Reads a double-precision floating point value (64bit, 8 bytes) from the given position, in big endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #getDouble(int)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #getDouble(int)} is the preferable choice.
*
* @param index The position from which the value will be read.
* @return The long value at the given position.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final double getDoubleBigEndian(int index) {
return Double.longBitsToDouble(getLongBigEndian(index));
}
/**
* Writes the given double-precision floating-point value (64bit, 8 bytes) to the given position in the
* system's native byte order. This method offers the best speed for double writing and should be used
* unless a specific byte order is required. In most cases, it suffices to know that the
* byte order in which the value is written is the same as the one in which it is read
* (such as transient storage in memory, or serialization for I/O and network), making this
* method the preferable choice.
*
* @param index The position at which the memory will be written.
* @param value The double value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putDouble(int index, double value) {
putLong(index, Double.doubleToRawLongBits(value));
}
/**
* Writes the given double-precision floating-point value (64bit, 8 bytes) to the given position in little endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putDouble(int, double)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putDouble(int, double)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The long value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putDoubleLittleEndian(int index, double value) {
putLongLittleEndian(index, Double.doubleToRawLongBits(value));
}
/**
* Writes the given double-precision floating-point value (64bit, 8 bytes) to the given position in big endian
* byte order. This method's speed depends on the system's native byte order, and it
* is possibly slower than {@link #putDouble(int, double)}. For most cases (such as
* transient storage in memory or serialization for I/O and network),
* it suffices to know that the byte order in which the value is written is the same as the
* one in which it is read, and {@link #putDouble(int, double)} is the preferable choice.
*
* @param index The position at which the value will be written.
* @param value The long value to be written.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final void putDoubleBigEndian(int index, double value) {
putLongBigEndian(index, Double.doubleToRawLongBits(value));
}
// -------------------------------------------------------------------------
// Bulk Read and Write Methods
// -------------------------------------------------------------------------
public abstract void get(DataOutput out, int offset, int length) throws IOException;
/**
* Bulk put method. Copies length memory from the given DataInput to the
* memory starting at position offset.
*
* @param in The DataInput to get the data from.
* @param offset The position in the memory segment to copy the chunk to.
* @param length The number of bytes to get.
*
* @throws IOException Thrown, if the DataInput encountered a problem upon reading,
* such as an End-Of-File.
*/
public abstract void put(DataInput in, int offset, int length) throws IOException;
/**
* Bulk get method. Copies {@code numBytes} bytes from this memory segment, starting at position
* {@code offset} to the target {@code ByteBuffer}. The bytes will be put into the target buffer
* starting at the buffer's current position. If this method attempts to write more bytes than
* the target byte buffer has remaining (with respect to {@link ByteBuffer#remaining()}),
* this method will cause a {@link java.nio.BufferOverflowException}.
*
* @param offset The position where the bytes are started to be read from in this memory segment.
* @param target The ByteBuffer to copy the bytes to.
* @param numBytes The number of bytes to copy.
*
* @throws IndexOutOfBoundsException If the offset is invalid, or this segment does not
* contain the given number of bytes (starting from offset), or the target byte buffer does
* not have enough space for the bytes.
* @throws ReadOnlyBufferException If the target buffer is read-only.
*/
public abstract void get(int offset, ByteBuffer target, int numBytes);
/**
* Bulk put method. Copies {@code numBytes} bytes from the given {@code ByteBuffer}, into
* this memory segment. The bytes will be read from the target buffer
* starting at the buffer's current position, and will be written to this memory segment starting
* at {@code offset}.
* If this method attempts to read more bytes than
* the target byte buffer has remaining (with respect to {@link ByteBuffer#remaining()}),
* this method will cause a {@link java.nio.BufferUnderflowException}.
*
* @param offset The position where the bytes are started to be written to in this memory segment.
* @param source The ByteBuffer to copy the bytes from.
* @param numBytes The number of bytes to copy.
*
* @throws IndexOutOfBoundsException If the offset is invalid, or the source buffer does not
* contain the given number of bytes, or this segment does
* not have enough space for the bytes (counting from offset).
*/
public abstract void put(int offset, ByteBuffer source, int numBytes);
/**
* Bulk copy method. Copies {@code numBytes} bytes from this memory segment, starting at position
* {@code offset} to the target memory segment. The bytes will be put into the target segment
* starting at position {@code targetOffset}.
*
* @param offset The position where the bytes are started to be read from in this memory segment.
* @param target The memory segment to copy the bytes to.
* @param targetOffset The position in the target memory segment to copy the chunk to.
* @param numBytes The number of bytes to copy.
*
* @throws IndexOutOfBoundsException If either of the offsets is invalid, or the source segment does not
* contain the given number of bytes (starting from offset), or the target segment does
* not have enough space for the bytes (counting from targetOffset).
*/
public final void copyTo(int offset, MemorySegment target, int targetOffset, int numBytes) {
final byte[] thisHeapRef = this.heapMemory;
final byte[] otherHeapRef = target.heapMemory;
final long thisPointer = this.address + offset;
final long otherPointer = target.address + targetOffset;
if ((numBytes | offset | targetOffset) >= 0 &&
thisPointer <= this.addressLimit - numBytes && otherPointer <= target.addressLimit - numBytes) {
UNSAFE.copyMemory(thisHeapRef, thisPointer, otherHeapRef, otherPointer, numBytes);
}
else if (this.address > this.addressLimit) {
throw new IllegalStateException("this memory segment has been freed.");
}
else if (target.address > target.addressLimit) {
throw new IllegalStateException("target memory segment has been freed.");
}
else {
throw new IndexOutOfBoundsException(
String.format("offset=%d, targetOffset=%d, numBytes=%d, address=%d, targetAddress=%d",
offset, targetOffset, numBytes, this.address, target.address));
}
}
/**
* Bulk copy method. Copies {@code numBytes} bytes to target unsafe object and pointer.
* NOTE: This is a unsafe method, no check here, please be carefully.
*/
public final void copyToUnsafe(int offset, Object target, int targetPointer, int numBytes) {
final long thisPointer = this.address + offset;
checkArgument(thisPointer + numBytes <= addressLimit);
UNSAFE.copyMemory(this.heapMemory, thisPointer, target, targetPointer, numBytes);
}
/**
* Bulk copy method. Copies {@code numBytes} bytes from source unsafe object and pointer.
* NOTE: This is a unsafe method, no check here, please be carefully.
*/
public final void copyFromUnsafe(int offset, Object source, int sourcePointer, int numBytes) {
final long thisPointer = this.address + offset;
checkArgument(thisPointer + numBytes <= addressLimit);
UNSAFE.copyMemory(source, sourcePointer, this.heapMemory, thisPointer, numBytes);
}
// -------------------------------------------------------------------------
// Comparisons & Swapping
// -------------------------------------------------------------------------
/**
* Compares two memory segment regions.
*
* @param seg2 Segment to compare this segment with
* @param offset1 Offset of this segment to start comparing
* @param offset2 Offset of seg2 to start comparing
* @param len Length of the compared memory region
*
* @return 0 if equal, -1 if seg1 &lt; seg2, 1 otherwise
*/
public final int compare(MemorySegment seg2, int offset1, int offset2, int len) {
while (len >= 8) {
long l1 = this.getLongBigEndian(offset1);
long l2 = seg2.getLongBigEndian(offset2);
if (l1 != l2) {
return (l1 < l2) ^ (l1 < 0) ^ (l2 < 0) ? -1 : 1;
}
offset1 += 8;
offset2 += 8;
len -= 8;
}
while (len > 0) {
int b1 = this.get(offset1) & 0xff;
int b2 = seg2.get(offset2) & 0xff;
int cmp = b1 - b2;
if (cmp != 0) {
return cmp;
}
offset1++;
offset2++;
len--;
}
return 0;
}
/**
* Equals two memory segment regions.
*
* @param seg2 Segment to equal this segment with
* @param offset1 Offset of this segment to start equaling
* @param offset2 Offset of seg2 to start equaling
* @param len Length of the equaled memory region
*
* @return true if equal, false otherwise
*/
public final boolean equalTo(MemorySegment seg2, int offset1, int offset2, int len) {
while (len >= 8) {
if (this.getLong(offset1) != seg2.getLong(offset2)) {
return false;
}
offset1 += 8;
offset2 += 8;
len -= 8;
}
while (len > 0) {
if (this.get(offset1) != seg2.get(offset2)) {
return false;
}
offset1++;
offset2++;
len--;
}
return true;
}
/**
* Swaps bytes between two memory segments, using the given auxiliary buffer.
*
* @param tempBuffer The auxiliary buffer in which to put data during triangle swap.
* @param seg2 Segment to swap bytes with
* @param offset1 Offset of this segment to start swapping
* @param offset2 Offset of seg2 to start swapping
* @param len Length of the swapped memory region
*/
public final void swapBytes(byte[] tempBuffer, MemorySegment seg2, int offset1, int offset2, int len) {
if ((offset1 | offset2 | len | (tempBuffer.length - len)) >= 0) {
final long thisPos = this.address + offset1;
final long otherPos = seg2.address + offset2;
if (thisPos <= this.addressLimit - len && otherPos <= seg2.addressLimit - len) {
// this -> temp buffer
UNSAFE.copyMemory(this.heapMemory, thisPos, tempBuffer, BYTE_ARRAY_BASE_OFFSET, len);
// other -> this
UNSAFE.copyMemory(seg2.heapMemory, otherPos, this.heapMemory, thisPos, len);
// temp buffer -> other
UNSAFE.copyMemory(tempBuffer, BYTE_ARRAY_BASE_OFFSET, seg2.heapMemory, otherPos, len);
return;
}
else if (this.address > this.addressLimit) {
throw new IllegalStateException("this memory segment has been freed.");
}
else if (seg2.address > seg2.addressLimit) {
throw new IllegalStateException("other memory segment has been freed.");
}
}
// index is in fact invalid
throw new IndexOutOfBoundsException(
String.format("offset1=%d, offset2=%d, len=%d, bufferSize=%d, address1=%d, address2=%d",
offset1, offset2, len, tempBuffer.length, this.address, seg2.address));
}
/**
* Get the heap byte array object.
* @return Return non-null if the memory is on the heap, and return null, if the memory if off the heap.
*/
public byte[] getHeapMemory() {
return heapMemory;
}
/**
* Point this MemorySegment to a new buff and owner (reuse this MemorySegment object).
* @param buffer the new buffer to point to.
*/
public void pointTo(byte[] buffer, Object owner) {
if (buffer == null) {
throw new NullPointerException("buffer should not be null");
}
this.heapMemory = buffer;
this.address = BYTE_ARRAY_BASE_OFFSET;
this.size = buffer.length;
this.addressLimit = this.address + this.size;
this.owner = owner;
}
public void pointTo(byte[] buffer) {
pointTo(buffer, null);
}
/**
* Return a new MemorySegment with the same buffer and clear the data (reuse the buffer).
* @return a new MemorySegment object.
*/
public abstract MemorySegment cloneReference();
}