/*********************************************************************************************************************** * Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * 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 eu.stratosphere.core.memory; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * This class represents a piece of memory allocated from the memory manager. The segment is backed * by a byte array and features random put and get methods for the basic types that are stored in a byte-wise * fashion in the memory. * * <p> * * Comments on the implementation: 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. Little-endian to big-endian conversion and * vice versa are done using the static <i>reverseBytes</i> methods in the boxing data types * (for example {@link Integer#reverseBytes(int)}). On x86/amd64, these are translated by the * jit compiler to <i>bswap</i> intrinsic commands. * * Below is an example of the code generated for the {@link MemorySegment#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,*UnsafeMemorySegment.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. * * <pre> * [Verified Entry Point] * 0x00007fc403e19920: sub $0x18,%rsp * 0x00007fc403e19927: mov %rbp,0x10(%rsp) ;*synchronization entry * ; - eu.stratosphere.nephele.services.memorymanager.UnsafeMemorySegment::putLongBigEndian@-1 (line 652) * 0x00007fc403e1992c: mov 0xc(%rsi),%r10d ;*getfield memory * ; - eu.stratosphere.nephele.services.memorymanager.UnsafeMemorySegment::putLong@4 (line 611) * ; - eu.stratosphere.nephele.services.memorymanager.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 * ; - eu.stratosphere.nephele.services.memorymanager.UnsafeMemorySegment::putLong@14 (line 611) * ; - eu.stratosphere.nephele.services.memorymanager.UnsafeMemorySegment::putLongBigEndian@12 (line 653) * 0x00007fc403e1993f: add $0x10,%rsp * 0x00007fc403e19943: pop %rbp * 0x00007fc403e19944: test %eax,0x5ba76b6(%rip) # 0x00007fc4099c1000 * ; {poll_return} * 0x00007fc403e1994a: retq * </pre> */ public class MemorySegment { // flag to enable / disable boundary checks. Note that the compiler eliminates the // code paths of the checks (as dead code) when this constant is set to false. private static final boolean CHECKED = true; /** * The array in which the data is stored. */ protected byte[] memory; /** * Wrapper for I/O requests. */ protected ByteBuffer wrapper; // ------------------------------------------------------------------------- // Constructors // ------------------------------------------------------------------------- /** * Creates a new memory segment that represents the data in the given byte array. * * @param memory The byte array that holds the data. */ public MemorySegment(byte[] memory) { this.memory = memory; } // ------------------------------------------------------------------------- // MemorySegment Accessors // ------------------------------------------------------------------------- /** * Checks whether this memory segment has already been freed. In that case, the * segment must not be used any more. * * @return True, if the segment has been freed, false otherwise. */ public final boolean isFreed() { return this.memory == null; } public final void free() { this.wrapper = null; this.memory = null; } /** * Gets the size of the memory segment, in bytes. Because segments * are backed by arrays, they cannot be larger than two GiBytes. * * @return The size in bytes. */ public final int size() { return this.memory.length; } /** * 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 ByteBuffer wrap(int offset, int length) { if (offset > this.memory.length || offset > this.memory.length - length) { throw new IndexOutOfBoundsException(); } if (this.wrapper == null) { this.wrapper = ByteBuffer.wrap(this.memory, offset, length); } else { this.wrapper.position(offset); this.wrapper.limit(offset + length); } return this.wrapper; } // ------------------------------------------------------------------------ // Random Access get() and put() methods // ------------------------------------------------------------------------ // -------------------------------------------------------------------------------------------- // WARNING: Any code for range checking must take care to avoid integer overflows. The position // integer may go up to <code>Integer.MAX_VALUE</tt>. Range checks that work after the principle // <code>position + 3 < end</code> may fail because <code>position + 3</code> becomes negative. // A safe solution is to subtract the delta from the limit, for example // <code>position < end - 3</code>. Since all indices are always positive, and the integer domain // has one more negative value than positive values, this can never cause an underflow. // -------------------------------------------------------------------------------------------- /** * 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 final byte get(int index) { return this.memory[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 final void put(int index, byte b) { this.memory[index] = 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 final void get(int index, byte[] dst) { get(index, dst, 0, dst.length); } /** * 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 final void put(int index, byte[] src) { put(index, src, 0, src.length); } /** * 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 final void get(int index, byte[] dst, int offset, int length) { // system arraycopy does the boundary checks anyways, no need to check extra System.arraycopy(this.memory, index, dst, offset, 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 final void put(int index, byte[] src, int offset, int length) { // system arraycopy does the boundary checks anyways, no need to check extra System.arraycopy(src, offset, this.memory, index, 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 final boolean getBoolean(int index) { return this.memory[index] != 0; } /** * 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 final void putBoolean(int index, boolean value) { this.memory[index] = (byte) (value ? 1 : 0); } /** * Reads two memory at the given position, composing them into a char value * according to the current 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. */ public final char getChar(int index) { return (char) ( ((this.memory[index ] & 0xff) << 8) | (this.memory[index + 1] & 0xff) ); } /** * Writes two memory containing the given char value, in the current byte * order, 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 2. */ public final void putChar(int index, char value) { this.memory[index ] = (byte) (value >> 8); this.memory[index + 1] = (byte) value; } /** * Reads two memory at 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) { return (short) ( ((this.memory[index ] & 0xff) << 8) | ((this.memory[index + 1] & 0xff)) ); } /** * 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) { this.memory[index ] = (byte) (value >> 8); this.memory[index + 1] = (byte) 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. */ @SuppressWarnings("restriction") public final int getInt(int index) { if (CHECKED) { if (index >= 0 && index <= this.memory.length - 4) { return UNSAFE.getInt(this.memory, BASE_OFFSET + index); } else { throw new IndexOutOfBoundsException(); } } else { return UNSAFE.getInt(this.memory, BASE_OFFSET + index); } } /** * 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. */ @SuppressWarnings("restriction") public final void putInt(int index, int value) { if (CHECKED) { if (index >= 0 && index <= this.memory.length - 4) { UNSAFE.putInt(this.memory, BASE_OFFSET + index, value); } else { throw new IndexOutOfBoundsException(); } } else { UNSAFE.putInt(this.memory, BASE_OFFSET + index, value); } } /** * 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. */ @SuppressWarnings("restriction") public final long getLong(int index) { if (CHECKED) { if (index >= 0 && index <= this.memory.length - 8) { return UNSAFE.getLong(this.memory, BASE_OFFSET + index); } else { throw new IndexOutOfBoundsException(); } } else { return UNSAFE.getLong(this.memory, BASE_OFFSET + index); } } /** * 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. */ @SuppressWarnings("restriction") public final void putLong(int index, long value) { if (CHECKED) { if (index >= 0 && index <= this.memory.length - 8) { UNSAFE.putLong(this.memory, BASE_OFFSET + index, value); } else { throw new IndexOutOfBoundsException(); } } else { UNSAFE.putLong(this.memory, BASE_OFFSET + index, value); } } /** * 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 // ------------------------------------------------------------------------- /** * Bulk get method. Copies length memory from the specified offset to the * provided <tt>DataOutput</tt>. * * @param out The data output object to copy the data to. * @param offset The first byte to by copied. * @param length The number of bytes to copy. * * @throws IOException Thrown, if the DataOutput encountered a problem upon writing. */ public final void get(DataOutput out, int offset, int length) throws IOException { out.write(this.memory, offset, length); } /** * 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 final void put(DataInput in, int offset, int length) throws IOException { in.readFully(this.memory, offset, length); } /** * 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. */ public final void get(int offset, ByteBuffer target, int numBytes) { // ByteBuffer performs the boundy checks target.put(this.memory, offset, 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 final void put(int offset, ByteBuffer source, int numBytes) { // ByteBuffer performs the boundy checks source.get(this.memory, offset, 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) { // system arraycopy does the boundary checks anyways, no need to check extra System.arraycopy(this.memory, offset, target.memory, targetOffset, numBytes); } // ------------------------------------------------------------------------- // Comparisons & Swapping // ------------------------------------------------------------------------- public static final int compare(MemorySegment seg1, MemorySegment seg2, int offset1, int offset2, int len) { final byte[] b1 = seg1.memory; final byte[] b2 = seg2.memory; int val = 0; for (int pos = 0; pos < len && (val = (b1[offset1 + pos] & 0xff) - (b2[offset2 + pos] & 0xff)) == 0; pos++); return val; } public static final void swapBytes(MemorySegment seg1, MemorySegment seg2, byte[] tempBuffer, int offset1, int offset2, int len) { // system arraycopy does the boundary checks anyways, no need to check extra System.arraycopy(seg1.memory, offset1, tempBuffer, 0, len); System.arraycopy(seg2.memory, offset2, seg1.memory, offset1, len); System.arraycopy(tempBuffer, 0, seg2.memory, offset2, len); } // -------------------------------------------------------------------------------------------- // Utilities for native memory accesses and checks // -------------------------------------------------------------------------------------------- @SuppressWarnings("restriction") private static final sun.misc.Unsafe UNSAFE = MemoryUtils.UNSAFE; @SuppressWarnings("restriction") private static final long BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); private static final boolean LITTLE_ENDIAN = (MemoryUtils.NATIVE_BYTE_ORDER == ByteOrder.LITTLE_ENDIAN); }