--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.statevalue;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferReader;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferWriter;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.SafeByteBufferFactory;
+import org.eclipse.tracecompass.internal.statesystem.core.statevalue.SafeByteBufferWrapper;
+import org.junit.Test;
+
+/**
+ * Test for the {@link SafeByteBufferWrapper} class
+ *
+ * @author Geneviève Bastien
+ */
+public class SafeByteBufferWrapperTest {
+
+ private final ByteBuffer fMainBuffer;
+
+ /**
+ * Constructor. Prepares the main buffer and safe buffer
+ */
+ public SafeByteBufferWrapperTest() {
+ fMainBuffer = ByteBuffer.allocate(1024);
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#put(byte)}
+ * {@link SafeByteBufferWrapper#get()} methods
+ */
+ @Test
+ public void testReadWriteByte() {
+ byte val = Byte.MAX_VALUE;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.put(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.get());
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#put(byte[])}
+ * {@link SafeByteBufferWrapper#get(byte[])} methods
+ */
+ @Test
+ public void testReadWriteByteArray() {
+ byte[] val = { 0, 2, 1, 3 };
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.put(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ byte[] ret = new byte[4];
+ reader.get(ret);
+ assertArrayEquals(val, ret);
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#putChar(char)}
+ * {@link SafeByteBufferWrapper#getChar()} methods
+ */
+ @Test
+ public void testReadWriteChar() {
+ char val = 24;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putChar(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.getChar());
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#putDouble(double)}
+ * {@link SafeByteBufferWrapper#getDouble()} methods
+ */
+ @Test
+ public void testReadWriteDouble() {
+ double val = Double.MAX_VALUE;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putDouble(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.getDouble(), 10);
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#putFloat(float)}
+ * {@link SafeByteBufferWrapper#getFloat()} methods
+ */
+ @Test
+ public void testReadWriteFloat() {
+ float val = Float.MIN_VALUE;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putFloat(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.getFloat(), 10);
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#putInt(int)}
+ * {@link SafeByteBufferWrapper#getInt()} methods
+ */
+ @Test
+ public void testReadWriteInt() {
+ int val = Integer.MAX_VALUE;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putInt(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.getInt());
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#putLong(long)}
+ * {@link SafeByteBufferWrapper#getLong()} methods
+ */
+ @Test
+ public void testReadWriteLong() {
+ long val = Long.MIN_VALUE;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putLong(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.getLong());
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#putShort(short)}
+ * {@link SafeByteBufferWrapper#getShort()} methods
+ */
+ @Test
+ public void testReadWriteShort() {
+ short val = Short.MIN_VALUE;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putShort(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.getShort());
+ }
+
+ /**
+ * Test the {@link SafeByteBufferWrapper#putString(String)}
+ * {@link SafeByteBufferWrapper#getString()} methods
+ */
+ @Test
+ public void testReadWriteString() {
+ String val = "abcdefg";
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putString(val);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(val, reader.getString());
+ }
+
+ /**
+ * Test adding multiple values to the buffer, inside the limits
+ */
+ @Test
+ public void testMultipleValues() {
+ int valInt = 98;
+ short valShort = 34;
+ String valStr = "myString";
+ long valLong = 254238908543254L;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 512);
+ buffer.putInt(valInt);
+ buffer.putShort(valShort);
+ buffer.putString(valStr);
+ buffer.putLong(valLong);
+
+ // Reset the buffer and read it again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, 512);
+ assertEquals(valInt, reader.getInt());
+ assertEquals(valShort, reader.getShort());
+ assertEquals(valStr, reader.getString());
+ assertEquals(valLong, reader.getLong());
+ }
+
+ /**
+ * Test writing over the limit of the buffer
+ */
+ @Test(expected = BufferOverflowException.class)
+ public void testLimit() {
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, 5);
+ buffer.putDouble(Double.MIN_VALUE);
+ }
+
+ /**
+ * Test writing to main buffer after writing to safe buffer
+ */
+ @Test
+ public void testMainBuffer() {
+ String valString = "defghi";
+ long valLong = 54262542352L;
+ int valInt = 2048;
+ int bufferSize = Integer.BYTES + valString.length() + Long.BYTES;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, bufferSize);
+ buffer.putString(valString);
+ buffer.putLong(valLong);
+ fMainBuffer.putInt(valInt);
+
+ // Flip the main buffer to read again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, bufferSize);
+ assertEquals(valString, reader.getString());
+ assertEquals(valLong, reader.getLong());
+ assertEquals(valInt, fMainBuffer.getInt());
+ }
+
+ /**
+ * Test writing to main buffer after writing to safe buffer but not
+ * completely
+ */
+ @Test
+ public void testMainBuffer2() {
+ String valString = "defghi";
+ long valLong = 54262542352L;
+ int valInt = 2048;
+ int bufferSize = Integer.BYTES + valString.length() + Long.BYTES + Long.BYTES;
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, bufferSize);
+ buffer.putString(valString);
+ buffer.putLong(valLong);
+
+ // Assert the main buffer's position is after the safe buffer, even
+ // though it is not completely written
+ assertEquals(bufferSize, fMainBuffer.position());
+ fMainBuffer.putInt(valInt);
+
+ // Write the extra long at the end of the safe buffer
+ buffer.putLong(valLong);
+
+ // Start reading again
+ fMainBuffer.flip();
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, bufferSize);
+ assertEquals(valString, reader.getString());
+ assertEquals(valLong, reader.getLong());
+ assertEquals(valLong, reader.getLong());
+ assertEquals(valInt, fMainBuffer.getInt());
+ }
+
+ /**
+ * Test writing to main buffer before writing to safe buffer
+ */
+ @Test
+ public void testMainBuffer3() {
+ String valString = "defghi";
+ long valLong = 54262542352L;
+ int valInt = 2048;
+ int bufferSize = Integer.BYTES + valString.length() + Long.BYTES;
+
+ fMainBuffer.putLong(valLong);
+ fMainBuffer.putInt(valInt);
+
+ ISafeByteBufferWriter buffer = SafeByteBufferFactory.wrapWriter(fMainBuffer, bufferSize);
+ buffer.putString(valString);
+ buffer.putLong(valLong);
+
+ fMainBuffer.flip();
+ assertEquals(valLong, fMainBuffer.getLong());
+ assertEquals(valInt, fMainBuffer.getInt());
+
+ // Flip the main buffer
+ ISafeByteBufferReader reader = SafeByteBufferFactory.wrapReader(fMainBuffer, bufferSize);
+ assertEquals(valString, reader.getString());
+ assertEquals(valLong, reader.getLong());
+ }
+
+}
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.core.resources,
org.eclipse.tracecompass.common.core
-Export-Package: org.eclipse.tracecompass.internal.statesystem.core;x-friends:="org.eclipse.tracecompass.statesystem.core.tests",
+Export-Package: org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;x-friends:="org.eclipse.tracecompass.statesystem.core.tests",
+ org.eclipse.tracecompass.internal.statesystem.core;x-friends:="org.eclipse.tracecompass.statesystem.core.tests",
org.eclipse.tracecompass.internal.statesystem.core.backend;x-internal:=true,
org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;x-friends:="org.eclipse.tracecompass.statesystem.core.tests",
+ org.eclipse.tracecompass.internal.statesystem.core.statevalue;x-friends:="org.eclipse.tracecompass.statesystem.core.tests",
org.eclipse.tracecompass.statesystem.core,
org.eclipse.tracecompass.statesystem.core.backend,
org.eclipse.tracecompass.statesystem.core.exceptions,
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;
+
+/**
+ * Interface for a safe ByteBuffer for reading purposes. This interface allows
+ * only to read data from a buffer, no other operation is allowed on it. The
+ * implementations must make sure that only the allowed data can be read.
+ *
+ * @author Geneviève Bastien
+ */
+public interface ISafeByteBufferReader {
+
+ /**
+ * Reads a byte at the buffer's current position
+ *
+ * @return The byte read
+ */
+ byte get();
+
+ /**
+ * Transfers bytes from this buffer's current position into the destination
+ * array
+ *
+ * @param dst
+ * The destination array
+ */
+ void get(byte[] dst);
+
+ /**
+ * Reads the char at the buffer's current position
+ *
+ * @return The char read
+ */
+ char getChar();
+
+ /**
+ * Reads the double at the buffer's current position
+ *
+ * @return The double read
+ */
+ double getDouble();
+
+ /**
+ * Reads the float at the buffer's current position
+ *
+ * @return The float read
+ */
+ float getFloat();
+
+ /**
+ * Reads the int at the buffer's current position
+ *
+ * @return The int read
+ */
+ int getInt();
+
+ /**
+ * Reads the long at the buffer's current position
+ *
+ * @return The long read
+ */
+ long getLong();
+
+ /**
+ * Reads the short at the buffer's current position
+ *
+ * @return The short read
+ */
+ short getShort();
+
+ /**
+ * Gets a string from the byte buffer.
+ *
+ * @return The string value read
+ */
+ String getString();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;
+
+/**
+ * Interface for a safe ByteBuffer for writing purposes. This interface allows
+ * only to write data from a buffer, no other operation is allowed on it. The
+ * implementation needs to make sure that the buffer does not write over the
+ * limits of the buffer.
+ *
+ * @author Geneviève Bastien
+ */
+public interface ISafeByteBufferWriter {
+
+ /**
+ * Writes a byte at the buffer's current position
+ *
+ * @param value
+ * The byte to write
+ */
+ void put(byte value);
+
+ /**
+ * Transfers the bytes from the src array in the buffer at the current
+ * position
+ *
+ * @param src
+ * the byte array to write
+ */
+ void put(byte[] src);
+
+ /**
+ * Writes a char at the buffer's current position
+ *
+ * @param value
+ * The char to write
+ */
+ void putChar(char value);
+
+ /**
+ * Writes a double at the buffer's current position
+ *
+ * @param value
+ * The double to write
+ */
+ void putDouble(double value);
+
+ /**
+ * Writes a float at the buffer's current position
+ *
+ * @param value
+ * The float to write
+ */
+ void putFloat(float value);
+
+ /**
+ * Writes an int at the buffer's current position
+ *
+ * @param value
+ * The int to write
+ */
+ void putInt(int value);
+
+ /**
+ * Writes a long at the buffer's current position
+ *
+ * @param value
+ * The long to write
+ */
+ void putLong(long value);
+
+ /**
+ * Writes a short at the buffer's current position
+ *
+ * @param value
+ * The short to write
+ */
+ void putShort(short value);
+
+ /**
+ * Writes a string value in the byte buffer. The implementation can decide
+ * what format it will use. They can also have a maximum size, in which case
+ * string should be truncated if they are larger than that.
+ *
+ * @param value
+ * The String value to write to the buffer
+ */
+ void putString(String value);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.tracecompass.internal.statesystem.core.statevalue.SafeByteBufferWrapper;
+
+/**
+ * Class that creates instances of safe byte buffers wrappers from a part of a
+ * ByteBuffer instance
+ *
+ * @author Geneviève Bastien
+ */
+public final class SafeByteBufferFactory {
+
+ private SafeByteBufferFactory() {
+
+ }
+
+ /**
+ * Creates a new safe byte buffer reader from the ByteBuffer's current
+ * position with a size limited to 'size'.
+ *
+ * @param buffer
+ * The big ByteBuffer to safely wrap for reading
+ * @param size
+ * The size of the new sub-buffer
+ * @return The safe byte buffer reader instance
+ */
+ public static ISafeByteBufferReader wrapReader(ByteBuffer buffer, int size) {
+ int pos = buffer.position();
+ // Slice the main buffer, so that position 0 is the current position
+ // set it as read-only also
+ ByteBuffer readOnlyBuffer = buffer.slice().asReadOnlyBuffer();
+ // Set its limit to the request limit
+ readOnlyBuffer.limit(size);
+ // Operations on fBuffer will not affect the main buffer's position, so
+ // we set its position to after the limit
+ buffer.position(pos + size);
+ return new SafeByteBufferWrapper(readOnlyBuffer);
+ }
+
+ /**
+ * Creates a new safe byte buffer writer from the ByteBuffer's current
+ * position with a size limited to 'size'.
+ *
+ * @param buffer
+ * The big ByteBuffer to safely wrap for reading
+ * @param size
+ * The size of the new sub-buffer
+ * @return The safe byte buffer writer instance
+ */
+ public static ISafeByteBufferWriter wrapWriter(ByteBuffer buffer, int size) {
+ int pos = buffer.position();
+ // Slice the main buffer, so that position 0 is the current position
+ ByteBuffer readWriteBuffer = buffer.slice();
+ // Set its limit to the request limit
+ readWriteBuffer.limit(size);
+ // Operations on fBuffer will not affect the main buffer's position, so
+ // we set its position to after the limit
+ buffer.position(pos + size);
+ return new SafeByteBufferWrapper(readWriteBuffer);
+ }
+
+ /**
+ * Get the serialized of a string object if it uses the
+ * {@link ISafeByteBufferWriter#putString(String)} method
+ *
+ * @param string
+ * The string to write to the buffer
+ * @return The size of the string serialized by the
+ * {@link ISafeByteBufferWriter#putString(String)} method, or -1 if
+ * the string cannot be serialized
+ */
+ public static int getStringSizeInBuffer(String string) {
+ return SafeByteBufferWrapper.getStringSizeInBuffer(string);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.statesystem.core.statevalue;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferReader;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferWriter;
+
+/**
+ * This class is a wrapper around a ByteBuffer. The size to read/write to the
+ * buffer must be known from the beginning. It will not overflow onto the main
+ * buffer.
+ *
+ * This class may be used to wrap a small part of a bigger ByteBuffer in such a
+ * way that it limits the number of bytes to read/write. It will not overflow
+ * over or below the allowed positions in the big ByteBuffer while not requiring
+ * extra copies of byte arrays.
+ *
+ * This allows sequential read and write operations but does not allow resizes or
+ * seeks.
+ *
+ * @author Geneviève Bastien
+ * @since 2.0
+ */
+public class SafeByteBufferWrapper implements ISafeByteBufferReader, ISafeByteBufferWriter {
+
+ private final ByteBuffer fBuffer;
+
+ /**
+ * Constructor.
+ *
+ * @param buffer
+ * The big ByteBuffer to safely wrap
+ */
+ public SafeByteBufferWrapper(ByteBuffer buffer) {
+ fBuffer = buffer;
+ }
+
+ @Override
+ public byte get() {
+ return fBuffer.get();
+ }
+
+ @Override
+ public void get(byte[] dst) {
+ fBuffer.get(dst);
+ }
+
+ @Override
+ public char getChar() {
+ return fBuffer.getChar();
+ }
+
+ @Override
+ public double getDouble() {
+ return fBuffer.getDouble();
+ }
+
+ @Override
+ public float getFloat() {
+ return fBuffer.getFloat();
+ }
+
+ @Override
+ public int getInt() {
+ return fBuffer.getInt();
+ }
+
+ @Override
+ public long getLong() {
+ return fBuffer.getLong();
+ }
+
+ @Override
+ public short getShort() {
+ return fBuffer.getShort();
+ }
+
+ @Override
+ public String getString() {
+ int strSize = fBuffer.getShort();
+ byte[] array = new byte[strSize];
+ fBuffer.get(array);
+ return new String(array);
+ }
+
+ @Override
+ public void put(byte value) {
+ fBuffer.put(value);
+ }
+
+ @Override
+ public void put(byte[] src) {
+ fBuffer.put(src);
+ }
+
+ @Override
+ public void putChar(char value) {
+ fBuffer.putChar(value);
+ }
+
+ @Override
+ public void putDouble(double value) {
+ fBuffer.putDouble(value);
+ }
+
+ @Override
+ public void putFloat(float value) {
+ fBuffer.putFloat(value);
+ }
+
+ @Override
+ public void putInt(int value) {
+ fBuffer.putInt(value);
+ }
+
+ @Override
+ public void putLong(long value) {
+ fBuffer.putLong(value);
+ }
+
+ @Override
+ public void putShort(short value) {
+ fBuffer.putShort(value);
+ }
+
+ @Override
+ public void putString(String value) {
+ String toWrite = value;
+ if (value.length() > Short.MAX_VALUE) {
+ toWrite = toWrite.substring(0, Short.MAX_VALUE);
+ }
+ fBuffer.putShort((short) value.length());
+ fBuffer.put(toWrite.getBytes());
+ }
+
+ /**
+ * Return the serialized size of the string in this byte buffer. The maximum
+ * size is {@link Short#MAX_VALUE}. A string with larger size will be
+ * truncated.
+ *
+ * @param string
+ * The string to serialize
+ * @return The size of the serialized string
+ */
+ public static int getStringSizeInBuffer(String string) {
+ return Short.BYTES + Math.min(Short.MAX_VALUE, string.length());
+ }
+
+}