/*******************************************************************************
* Copyright 2013 Geoscience Australia
*
* 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 au.gov.ga.earthsci.common.buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import au.gov.ga.earthsci.common.util.Validate;
/**
* Utility methods for working with buffers
*
* @author James Navin (james.navin@ga.gov.au)
*/
/*
* TODO: Could these be made more usable by turning them into something like the BufferWrapper that gives
* access to the correct type from an underlying byte buffer (similar to the typed views in java.nio
* but with support for all types listed in BufferType)
*/
public class BufferUtil
{
/**
* Return the next value from the buffer of the provided type.
* <p/>
* Types will be returned that match the container type given by
* {@link BufferType#getContainerClass()}
*
* @return The next value from the provided buffer of the provided type
*/
public static Number getValue(ByteBuffer buffer, BufferType bufferType)
{
if (buffer == null)
{
return null;
}
Validate.notNull(bufferType, "A valid buffer type is required"); //$NON-NLS-1$
switch (bufferType)
{
case BYTE:
return buffer.get() & 0xff;
case UNSIGNED_SHORT:
return getUInt16(buffer);
case SHORT:
return buffer.getShort();
case UNSIGNED_INT:
return getUInt32(buffer);
case INT:
return buffer.getInt();
case LONG:
return buffer.getLong();
case FLOAT:
return buffer.getFloat();
case DOUBLE:
return buffer.getDouble();
}
throw new UnsupportedOperationException("Unsupported buffer type " + bufferType.name()); //$NON-NLS-1$
}
/**
* Skip forward in the buffer the given number of values of the given type
* <p/>
* This is generally more efficient than using
* {@link #getValue(ByteBuffer, BufferType)} repeatedly and discarding the
* value when the number of values to skip is known ahead of time.
*
* @param num
* The number of values to skip. A negative value will move
* backwards through the buffer.
* @param buffer
* The buffer to skip
* @param bufferType
* The type of value contained in the buffer
*/
public static void skipValues(int num, ByteBuffer buffer, BufferType bufferType)
{
if (buffer == null || num == 0)
{
return;
}
Validate.notNull(bufferType, "A valid buffer type is required"); //$NON-NLS-1$
buffer.position(buffer.position() + num * bufferType.getNumberOfBytes());
}
/**
* Convert the given number into the correct type for use with the given
* buffer type.
* <p/>
* Note that this may lose information (through rounding or truncation) if
* not used correctly.
*
* @param n
* The number to convert
* @param targetType
* The target buffer type to convert the number to
*/
public static Number convertTo(Number n, BufferType targetType)
{
if (n == null)
{
return n;
}
Validate.notNull(targetType, "A valid buffer type is required"); //$NON-NLS-1$
switch (targetType)
{
case BYTE:
return n.byteValue();
case UNSIGNED_SHORT:
return n.intValue();
case SHORT:
return n.shortValue();
case UNSIGNED_INT:
return n.longValue();
case INT:
return n.intValue();
case LONG:
return n.longValue();
case FLOAT:
return n.floatValue();
case DOUBLE:
return n.doubleValue();
}
throw new UnsupportedOperationException("Unsupported buffer type " + targetType.name()); //$NON-NLS-1$
}
private static int getUInt16(ByteBuffer buffer)
{
int first = 0xff & buffer.get();
int second = 0xff & buffer.get();
if (buffer.order() == ByteOrder.LITTLE_ENDIAN)
{
return (first << 8 | second);
}
else
{
return (first | second << 8);
}
}
private static long getUInt32(ByteBuffer buffer)
{
long first = 0xff & buffer.get();
long second = 0xff & buffer.get();
long third = 0xff & buffer.get();
long fourth = 0xff & buffer.get();
if (buffer.order() == ByteOrder.LITTLE_ENDIAN)
{
return (first << 24l | second << 16l | third << 8l | fourth);
}
else
{
return (first | second << 8l | third << 16l | fourth << 24l);
}
}
}