package org.jscsi.target.util; import java.nio.ByteBuffer; import org.jscsi.utils.ByteBufferCache; /** * This utility class contains static methods for reading/writing integers of * various lengths and character strings from/to {@link ByteBuffer} objects and * byte arrays. * * @author Andreas Ergenzinger */ public final class ReadWrite { /** * Reads a specified byte from a {@link ByteBuffer} and returns its value as * an unsigned integer. * * @param buffer * the {@link ByteBuffer} containing the byte * @param start * the index position of the byte in the {@link ByteBuffer} * @return the unsigned integer value of the byte */ public static final int readOneByteInt(ByteBuffer buffer, int start) { final byte b = buffer.get(start); return (b & 255); } /** * Reads a specified byte from a byte array and returns its value as an * unsigned integer. * * @param array * the array containing the byte * @param start * the index position of the byte in the array * @return the unsigned integer value of the byte */ public static final int readOneByteInt(byte[] array, int start) { return (array[start] & 255); } /** * Reads an unsigned 2-byte integer from two consecutive big-endian-ordered * bytes of a {@link ByteBuffer} object and returns the value. * * @param buffer * the {@link ByteBuffer} containing the bytes * @param start * the index position of the most-significant byte * @return the unsigned value of the two-byte integer */ public static final int readTwoByteInt(ByteBuffer buffer, int start) { final byte b1 = buffer.get(start); final byte b2 = buffer.get(start + 1); return ((b1 & 255) << 8) | (b2 & 255); } /** * Reads an unsigned 2-byte integer from two consecutive big-endian-ordered * bytes of a byte array and returns the value. * * @param array * the byte array containing the bytes * @param start * the index position of the most-significant byte * @return the unsigned value of the two-byte integer */ public static final int readTwoByteInt(byte[] array, int start) { return ((array[start] & 255) << 8) | (array[start + 1] & 255); } /** * Reads an unsigned 3-byte integer from three consecutive * big-endian-ordered bytes of a {@link ByteBuffer} object and returns the * value. * * @param buffer * the {@link ByteBuffer} containing the bytes * @param start * the index position of the most-significant byte * @return the unsigned value of the three-byte integer */ public static final int readThreeByteInt(ByteBuffer buffer, int start) { final byte b1 = buffer.get(start); final byte b2 = buffer.get(start + 1); final byte b3 = buffer.get(start + 2); return ((b1 & 255) << 16) | ((b2 & 255) << 8) | (b3 & 255); } /** * Reads an unsigned 3-byte integer from three consecutive * big-endian-ordered bytes of a byte array and returns the value. * * @param array * the byte array containing the bytes * @param start * the index position of the most-significant byte * @return the unsigned value of the three-byte integer */ public static final int readThreeByteInt(byte[] array, int start) { return ((array[start] & 255) << 16) | ((array[start + 1] & 255) << 8) | (array[start + 2] & 255); } /** * Reads a (signed) 4-byte integer from four consecutive big-endian-ordered * bytes of a {@link ByteBuffer} object and returns the value. * * @param buffer * the {@link ByteBuffer} containing the bytes * @param start * the index position of the most-significant byte * @return the value of the four-byte integer */ public static final int readFourByteInt(ByteBuffer buffer, int start) { final byte b1 = buffer.get(start); final byte b2 = buffer.get(start + 1); final byte b3 = buffer.get(start + 2); final byte b4 = buffer.get(start + 3); return ((b1 & 255) << 24) | ((b2 & 255) << 16) | ((b3 & 255) << 8) | (b4 & 255); } /** * Reads a (signed) 4-byte integer from four consecutive big-endian-ordered * bytes of a byte array and returns the value. * * @param array * the byte array containing the bytes * @param start * the index position of the most-significant byte * @return the unsigned value of the four-byte integer */ public static final int readFourByteInt(byte[] array, int start) { return ((array[start] & 255) << 24) | ((array[start + 1] & 255) << 16) | ((array[start + 2] & 255) << 8) | (array[start + 3] & 255); } /** * Puts the characters in the passed String into an array of one or more * ByteBuffers and returns it. * <p> * If the String does not end with a null character, one will be appended. If the String's length is * larger than the specified <i>bufferSize</i>, all but the last ByteBuffer will have <i>capacity() = * bufferSize</i>, the last one will contain the remaining String characters. * * @param string * the String to be put into the ByteBuffer * @param bufferSize * the maximum size of the returned ByteBuffers * @return an array of ByteBuffers containing the passed String */ public static ByteBuffer[] stringToTextDataSegments(String string, int bufferSize) { int numberOfParts = string.length() / bufferSize + 1; ByteBuffer[] segments = new ByteBuffer[numberOfParts]; int bytesRemaining = string.length(); for (int i = 0; i < numberOfParts; ++i) { if (bytesRemaining > bufferSize) { segments[i] = stringToByteBuffer(string.substring(i * bufferSize, (i + 1) * bufferSize)); } else { segments[i] = stringToByteBuffer(string.substring(i * bufferSize)); } } return segments; } /** * Puts the characters in the passed String into a ByteBuffer of equal * length and returns it. * * @param string * any String * @return a ByteBuffer containing the characters of the passed String */ private static ByteBuffer stringToByteBuffer(final String string) { final ByteBuffer buffer = ByteBufferCache.allocate(string.length()); for (int i = 0; i < string.length(); ++i) buffer.put((byte)string.charAt(i)); buffer.clear(); return buffer; } /** * Appends the content of a <code>ProtocolDataUnit</code> (text) data segment * to a {@link StringBuilder}; * * @param byteBuffer * the PDU's data segment * @param stringBuilder * the {@link StringBuilder} that will be extended */ public static final void appendTextDataSegmentToStringBuffer(final ByteBuffer byteBuffer, final StringBuilder stringBuilder) { stringBuilder.append(byteBufferToString(byteBuffer)); } /** * Writes the given <i>value</i> to the <i>buffer</i> in big-endian format, * with the index position of the most significant byte being <i>start</i>. * * To get the value back from the buffer, use readUnsignedInt. * * @param value * the integer to write to the ByteBuffer * @param buffer * where the integer will be stored * @param start * index of the most significant byte of the stored <i>value</i> */ public static final void writeInt(int value, final ByteBuffer buffer, int start) { buffer.position(start); buffer.put((byte)(value >>> 24)); buffer.put((byte)(value >>> 16)); buffer.put((byte)(value >>> 8)); buffer.put((byte)value); } /** * Returns the bytes in a {@link ByteBuffer} as a UTF-8 encoded {@link String}. * * @param buffer * a {@link ByteBuffer} containing UTF-8 encoded characters. * @return a String representation of the <i>buffer</i>'s content */ public static String byteBufferToString(final ByteBuffer buffer) { if (buffer.hasArray()){ buffer.rewind(); return new String(buffer.array()); } else { buffer.rewind(); final byte[] bufferArray = new byte[buffer.capacity()]; buffer.get(bufferArray); buffer.rewind(); return new String(bufferArray); } } /** * Splits the passed <i>value</i> into bytes and returns them in an array, * in big-endian format. * * @param value * the long to split * @return byte representation of the parameter */ public static byte[] longToBytes(final long value) { final byte[] bytes = new byte[8]; for (int i = 0; i < bytes.length; ++i) bytes[i] = (byte)(value >> (8 * (bytes.length - 1 - i))); return bytes; } /** * Writes the given <i>value</i> to the <i>buffer</i> in big-endian format, * with the index position of the most significant byte being <i>index</i>. * * @param value * the integer to write to the ByteBuffer * @param buffer * where the integer will be stored * @param index * index of the most significant byte of the stored <i>value</i> */ public static void writeLong(final ByteBuffer buffer, final long value, final int index) { final byte[] bytes = longToBytes(value); buffer.position(index); for (int i = 0; i < bytes.length; ++i) buffer.put(bytes[i]); } /** * Writes the two least-significant big-endian-ordered bytes of an integer * the a specified position in a {@link ByteBuffer}. * * @param buffer * where the bytes will be written * @param value * the value to convert and copy * @param index * the position of the most significant byte in the {@link ByteBuffer} */ public static void writeTwoByteInt(final ByteBuffer buffer, final int value, final int index) { buffer.position(index); buffer.put((byte)(value >> 8));// most significant byte buffer.put((byte)value);// least significant byte } /** * Writes the three least-significant big-endian-ordered bytes of an integer * the a specified position in a {@link ByteBuffer}. * * @param buffer * where the bytes will be written * @param value * the value to convert and copy * @param index * the position of the most significant byte in the {@link ByteBuffer} */ public static void writeThreeByteInt(final ByteBuffer buffer, final int value, final int index) { buffer.position(index); buffer.put((byte)(value >> 16));// most significant byte buffer.put((byte)(value >> 8)); buffer.put((byte)value);// least significant byte } /** * Reads an unsigned 4-byte integer from four consecutive big-endian-ordered * bytes of a {@link ByteBuffer} object and returns the value. * * @param buffer * the {@link ByteBuffer} containing the bytes * @param start * the index position of the most-significant byte * @return the value of the unsigned four-byte integer */ public static long readUnsignedInt(final ByteBuffer buffer, final int start) { return readFourByteInt(buffer, start) & 0xffffffffL; } }