/***********************************************************************************************************************
* 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.nephele.services.memorymanager;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* 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.
*/
public class CheckedMemorySegment {
/**
* The array in which the data is stored.
*/
protected byte[] memory;
/**
* The offset in the memory array where this segment starts.
*/
protected final int offset;
/**
* The size of the memory segment.
*/
protected final int size;
/**
* Wrapper for I/O requests.
*/
protected ByteBuffer wrapper;
// -------------------------------------------------------------------------
// Constructors
// -------------------------------------------------------------------------
/**
* Creates a new memory segment of given size with the provided views.
*
* @param size The size of the memory segment.
* @param inputView The input view to use.
* @param outputView The output view to use.
*/
public CheckedMemorySegment(byte[] memory) {
this.memory = memory;
this.offset = 0;
this.size = memory.length;
}
// -------------------------------------------------------------------------
// 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 boolean isFreed() {
return 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 size;
}
/**
* Gets the byte array that backs the memory segment and this random access view.
* Since different regions of the backing array are used by different segments, the logical
* positions in this view do not correspond to the indexes in the backing array and need
* to be translated via the {@link #translateOffset(int)} method.
*
* @return The backing byte array.
*/
public final byte[] getBackingArray() {
return this.memory;
}
/**
* Translates the given offset for this view into the offset for the backing array.
*
* @param offset The offset to be translated.
* @return The corresponding position in the backing array.
*/
public final int translateOffset(int offset) {
return this.offset + offset;
}
// -------------------------------------------------------------------------
// Helper methods
// -------------------------------------------------------------------------
/**
* 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.size || offset > this.size - length) {
throw new IndexOutOfBoundsException();
}
if (this.wrapper == null) {
this.wrapper = ByteBuffer.wrap(this.memory, this.offset + offset, length);
}
else {
this.wrapper.position(this.offset + offset);
this.wrapper.limit(this.offset + offset + length);
}
return this.wrapper;
}
// --------------------------------------------------------------------
// Random Access
// --------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
// 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 position 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) {
if (index >= 0 && index < this.size) {
return this.memory[this.offset + index];
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Writes the given byte into this buffer at the given position.
*
* @param position The position at which the byte will be written.
* @param b The byte value to be written.
* @return This view itself.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger or equal to the size of
* the memory segment.
*/
public final CheckedMemorySegment put(int index, byte b) {
if (index >= 0 && index < this.size) {
this.memory[this.offset + index] = b;
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Bulk get method. Copies dst.length memory from the specified position to
* the destination memory.
*
* @param position The position at which the first byte will be read.
* @param dst The memory into which the memory will be copied.
* @return This view itself.
*
* @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 CheckedMemorySegment get(int index, byte[] dst) {
return 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 position in the memory segment array, where the data is put.
* @param src The source array to copy the data from.
* @return This random access view itself.
*
* @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 CheckedMemorySegment put(int index, byte[] src) {
return 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 position
* 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.
* @return This view itself.
*
* @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 CheckedMemorySegment get(int index, byte[] dst, int offset, int length) {
if (index >= 0 && index < this.size && index <= this.size - length && offset <= dst.length - length) {
System.arraycopy(this.memory, this.offset + index, dst, offset, length);
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* 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.
* @return This random access view itself.
*
* @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 CheckedMemorySegment put(int index, byte[] src, int offset, int length) {
if (index >= 0 && index < this.size && index <= this.size - length && offset <= src.length - length) {
System.arraycopy(src, offset, this.memory, this.offset + index, length);
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* 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.
* @return This view itself.
*
* @throws IOException Thrown, if the DataOutput encountered a problem upon writing.
*/
public final CheckedMemorySegment get(DataOutput out, int offset, int length) throws IOException {
if (offset >= 0 && offset < this.size && length >= 0 && offset <= this.size - length) {
out.write(this.memory, this.offset + offset, length);
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* 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.
* @return This random access view itself.
*
* @throws IOException Thrown, if the DataInput encountered a problem upon reading,
* such as an End-Of-File.
*/
public final CheckedMemorySegment put(DataInput in, int offset, int length) throws IOException {
if (offset >= 0 && offset < this.size && length >= 0 && offset <= this.size - length) {
in.readFully(this.memory, this.offset + offset, length);
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Reads one byte at the given position and returns its boolean
* representation.
*
* @param position 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 1.
*/
public final boolean getBoolean(int index) {
if (index >= 0 && index < this.size) {
return this.memory[this.offset + index] != 0;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Writes one byte containing the byte value into this buffer at the given
* position.
*
* @param position The position at which the memory will be written.
* @param value The char value to be written.
* @return This view itself.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 1.
*/
public final CheckedMemorySegment putBoolean(int index, boolean value) {
if (index >= 0 && index < this.size) {
this.memory[this.offset + index] = (byte) (value ? 1 : 0);
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Reads two memory at the given position, composing them into a char value
* according to the current byte order.
*
* @param position 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) {
if (index >= 0 && index < this.size - 1) {
return (char) ( ((this.memory[this.offset + index + 0] & 0xff) << 8) |
(this.memory[this.offset + index + 1] & 0xff) );
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Writes two memory containing the given char value, in the current byte
* order, into this buffer at the given position.
*
* @param position The position at which the memory will be written.
* @param value The char value to be written.
* @return This view itself.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 2.
*/
public final CheckedMemorySegment putChar(int index, char value) {
if (index >= 0 && index < this.size - 1) {
this.memory[this.offset + index + 0] = (byte) (value >> 8);
this.memory[this.offset + index + 1] = (byte) value;
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Reads two memory at the given position, composing them into a short value
* according to the current byte order.
*
* @param position 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) {
if (index >= 0 && index < this.size - 1) {
return (short) (
((this.memory[this.offset + index + 0] & 0xff) << 8) |
((this.memory[this.offset + index + 1] & 0xff)) );
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Writes two memory containing the given short value, in the current byte
* order, into this buffer at the given position.
*
* @param position The position at which the memory will be written.
* @param value The short value to be written.
* @return This view itself.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 2.
*/
public final CheckedMemorySegment putShort(int index, short value) {
if (index >= 0 && index < this.size - 1) {
this.memory[this.offset + index + 0] = (byte) (value >> 8);
this.memory[this.offset + index + 1] = (byte) value;
return this;
} else {
throw new IndexOutOfBoundsException();
}
}
public final int getInt(int index) {
if (index >= 0 && index < this.size - 3) {
return ((this.memory[index ] & 0xff) << 24)
| ((this.memory[index + 1] & 0xff) << 16)
| ((this.memory[index + 2] & 0xff) << 8)
| ((this.memory[index + 3] & 0xff) );
} else {
throw new IndexOutOfBoundsException();
}
}
public final int getIntLittleEndian(int index) {
if (index >= 0 && index < this.size - 3) {
return ((this.memory[index ] & 0xff) )
| ((this.memory[index + 1] & 0xff) << 8)
| ((this.memory[index + 2] & 0xff) << 16)
| ((this.memory[index + 3] & 0xff) << 24);
} else {
throw new IndexOutOfBoundsException();
}
}
public final int getIntBigEndian(int index) {
if (index >= 0 && index < this.size - 3) {
return ((this.memory[index ] & 0xff) << 24)
| ((this.memory[index + 1] & 0xff) << 16)
| ((this.memory[index + 2] & 0xff) << 8)
| ((this.memory[index + 3] & 0xff) );
} else {
throw new IndexOutOfBoundsException();
}
}
public final void putInt(int index, int value) {
if (index >= 0 && index < this.size - 3) {
this.memory[index ] = (byte) (value >> 24);
this.memory[index + 1] = (byte) (value >> 16);
this.memory[index + 2] = (byte) (value >> 8);
this.memory[index + 3] = (byte) value;
} else {
throw new IndexOutOfBoundsException();
}
}
public final void putIntLittleEndian(int index, int value) {
if (index >= 0 && index < this.size - 3) {
this.memory[index ] = (byte) value;
this.memory[index + 1] = (byte) (value >> 8);
this.memory[index + 2] = (byte) (value >> 16);
this.memory[index + 3] = (byte) (value >> 24);
} else {
throw new IndexOutOfBoundsException();
}
}
public final void putIntBigEndian(int index, int value) {
if (index >= 0 && index < this.size - 3) {
this.memory[index ] = (byte) (value >> 24);
this.memory[index + 1] = (byte) (value >> 16);
this.memory[index + 2] = (byte) (value >> 8);
this.memory[index + 3] = (byte) value;
} else {
throw new IndexOutOfBoundsException();
}
}
public final long getLong(int index) {
if (index >= 0 && index < this.size - 7) {
return (((long) this.memory[index ] & 0xff) << 56)
| (((long) this.memory[index + 1] & 0xff) << 48)
| (((long) this.memory[index + 2] & 0xff) << 40)
| (((long) this.memory[index + 3] & 0xff) << 32)
| (((long) this.memory[index + 4] & 0xff) << 24)
| (((long) this.memory[index + 5] & 0xff) << 16)
| (((long) this.memory[index + 6] & 0xff) << 8)
| (((long) this.memory[index + 7] & 0xff) );
} else {
throw new IndexOutOfBoundsException();
}
}
public final long getLongLittleEndian(int index) {
if (index >= 0 && index < this.size - 7) {
return (((long) this.memory[index ] & 0xff) )
| (((long) this.memory[index + 1] & 0xff) << 8)
| (((long) this.memory[index + 2] & 0xff) << 16)
| (((long) this.memory[index + 3] & 0xff) << 24)
| (((long) this.memory[index + 4] & 0xff) << 32)
| (((long) this.memory[index + 5] & 0xff) << 40)
| (((long) this.memory[index + 6] & 0xff) << 48)
| (((long) this.memory[index + 7] & 0xff) << 56);
} else {
throw new IndexOutOfBoundsException();
}
}
public final long getLongBigEndian(int index) {
if (index >= 0 && index < this.size - 7) {
return (((long) this.memory[index ] & 0xff) << 56)
| (((long) this.memory[index + 1] & 0xff) << 48)
| (((long) this.memory[index + 2] & 0xff) << 40)
| (((long) this.memory[index + 3] & 0xff) << 32)
| (((long) this.memory[index + 4] & 0xff) << 24)
| (((long) this.memory[index + 5] & 0xff) << 16)
| (((long) this.memory[index + 6] & 0xff) << 8)
| (((long) this.memory[index + 7] & 0xff) );
} else {
throw new IndexOutOfBoundsException();
}
}
public final void putLong(int index, long value) {
if (index >= 0 && index < this.size - 7) {
this.memory[index ] = (byte) (value >> 56);
this.memory[index + 1] = (byte) (value >> 48);
this.memory[index + 2] = (byte) (value >> 40);
this.memory[index + 3] = (byte) (value >> 32);
this.memory[index + 4] = (byte) (value >> 24);
this.memory[index + 5] = (byte) (value >> 16);
this.memory[index + 6] = (byte) (value >> 8);
this.memory[index + 7] = (byte) value;
} else {
throw new IndexOutOfBoundsException();
}
}
public final void putLongLittleEndian(int index, long value) {
if (index >= 0 && index < this.size - 7) {
this.memory[index ] = (byte) value;
this.memory[index + 1] = (byte) (value >> 8);
this.memory[index + 2] = (byte) (value >> 16);
this.memory[index + 3] = (byte) (value >> 24);
this.memory[index + 4] = (byte) (value >> 32);
this.memory[index + 5] = (byte) (value >> 40);
this.memory[index + 6] = (byte) (value >> 48);
this.memory[index + 7] = (byte) (value >> 56);
} else {
throw new IndexOutOfBoundsException();
}
}
public final void putLongBigEndian(int index, long value) {
if (index >= 0 && index < this.size - 7) {
this.memory[index ] = (byte) (value >> 56);
this.memory[index + 1] = (byte) (value >> 48);
this.memory[index + 2] = (byte) (value >> 40);
this.memory[index + 3] = (byte) (value >> 32);
this.memory[index + 4] = (byte) (value >> 24);
this.memory[index + 5] = (byte) (value >> 16);
this.memory[index + 6] = (byte) (value >> 8);
this.memory[index + 7] = (byte) value;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Reads four memory at the given position, composing them into a float
* value according to the current byte order.
*
* @param position The position from which the memory 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));
}
/**
* Writes four memory containing the given float value, in the current byte
* order, into this buffer at the given position.
*
* @param position The position at which the memory will be written.
* @param value The float value to be written.
* @return This view itself.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 4.
*/
public final CheckedMemorySegment putFloat(int index, float value) {
putLong(index, Float.floatToIntBits(value));
return this;
}
/**
* Reads eight memory at the given position, composing them into a double
* value according to the current byte order.
*
* @param position The position from which the memory 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));
}
/**
* Writes eight memory containing the given double value, in the current
* byte order, into this buffer at the given position.
*
* @param position The position at which the memory will be written.
* @param value The double value to be written.
* @return This view itself.
*
* @throws IndexOutOfBoundsException Thrown, if the index is negative, or larger then the segment
* size minus 8.
*/
public final CheckedMemorySegment putDouble(int index, double value) {
putLong(index, Double.doubleToLongBits(value));
return this;
}
}