From bad519099946d6bf2e4470d1e501566d312d378a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Genevi=C3=A8ve=20Bastien?= Date: Mon, 9 May 2016 21:56:31 -0400 Subject: [PATCH] ss: Introduce a safe byte buffer wrapper for use by custom state values MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This byte buffer wrapper safely wraps a ByteBuffer inside a class that does not allow to pass the limit or change the position so that custom state values can safely write directly to it, without passing by byte arrays. Change-Id: I428ee4406aa2a913d91ebc2c075bc92861286b3f Signed-off-by: Geneviève Bastien Reviewed-on: https://git.eclipse.org/r/72359 Reviewed-by: Jean-Christian Kouame Tested-by: Jean-Christian Kouame Reviewed-by: Hudson CI Reviewed-by: Alexandre Montplaisir --- .../statevalue/SafeByteBufferWrapperTest.java | 311 ++++++++++++++++++ .../META-INF/MANIFEST.MF | 4 +- .../statevalue/ISafeByteBufferReader.java | 86 +++++ .../statevalue/ISafeByteBufferWriter.java | 97 ++++++ .../statevalue/SafeByteBufferFactory.java | 87 +++++ .../statevalue/SafeByteBufferWrapper.java | 158 +++++++++ 6 files changed, 742 insertions(+), 1 deletion(-) create mode 100644 statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/statevalue/SafeByteBufferWrapperTest.java create mode 100644 statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferReader.java create mode 100644 statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferWriter.java create mode 100644 statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/SafeByteBufferFactory.java create mode 100644 statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/statevalue/SafeByteBufferWrapper.java diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/statevalue/SafeByteBufferWrapperTest.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/statevalue/SafeByteBufferWrapperTest.java new file mode 100644 index 0000000000..ec75013991 --- /dev/null +++ b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/statevalue/SafeByteBufferWrapperTest.java @@ -0,0 +1,311 @@ +/******************************************************************************* + * 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()); + } + +} diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/META-INF/MANIFEST.MF b/statesystem/org.eclipse.tracecompass.statesystem.core/META-INF/MANIFEST.MF index e7b6481826..c9be6f07bb 100644 --- a/statesystem/org.eclipse.tracecompass.statesystem.core/META-INF/MANIFEST.MF +++ b/statesystem/org.eclipse.tracecompass.statesystem.core/META-INF/MANIFEST.MF @@ -11,9 +11,11 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 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, diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferReader.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferReader.java new file mode 100644 index 0000000000..29d0f7986e --- /dev/null +++ b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferReader.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * 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(); + +} diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferWriter.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferWriter.java new file mode 100644 index 0000000000..f50f288760 --- /dev/null +++ b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/ISafeByteBufferWriter.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * 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); + +} diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/SafeByteBufferFactory.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/SafeByteBufferFactory.java new file mode 100644 index 0000000000..86122a586a --- /dev/null +++ b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/SafeByteBufferFactory.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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); + } + +} diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/statevalue/SafeByteBufferWrapper.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/statevalue/SafeByteBufferWrapper.java new file mode 100644 index 0000000000..c642f40110 --- /dev/null +++ b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/statevalue/SafeByteBufferWrapper.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * 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()); + } + +} -- 2.34.1