/*
* The MIT License (MIT)
*
* Copyright (C) 2013 Aaron Weiss
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package provider.pkgnx.util;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import provider.pkgnx.NXException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
/**
* An indexed accessor for reading Little Endian-formatted data.
*
* @author Aaron Weiss
* @version 1.1.0
* @since 5/26/13
*/
public class SeekableLittleEndianAccessor {
private final static Logger logger = LoggerFactory.getLogger(SeekableLittleEndianAccessor.class);
private static final CharsetDecoder utfDecoder = Charset.forName("UTF-8").newDecoder();
private final ByteBuf buf;
/**
* Creates an immutable {@code SeekableLittleEndianAccessor} from an array of bytes.
*
* @param bytes the array to use
*/
public SeekableLittleEndianAccessor(byte[] bytes) {
this(Unpooled.wrappedBuffer(bytes));
}
/**
* Creates an immutable {@code SeekableLittleEndianAccessor} from a NIO {@code ByteBuffer}.
*
* @param buf the buffer to use
*/
public SeekableLittleEndianAccessor(ByteBuffer buf) {
this(Unpooled.wrappedBuffer(buf));
}
/**
* Creates an immutable {@code SeekableLittleEndianAccessor} wrapping a {@code ByteBuf}.
*
* @param buf the buffer to wrap
*/
public SeekableLittleEndianAccessor(ByteBuf buf) {
this.buf = buf.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Gets the internal {@code ByteBuf} used by this accessor.
*
* @return the internal buffer
*/
public ByteBuf getBuf() {
return buf;
}
/**
* Skips the desired {@code length} in the buffer.
*
* @param length the number of bytes to skip
* @see io.netty.buffer.ByteBuf#skipBytes(int)
*/
public void skip(int length) {
buf.skipBytes(length);
}
/**
* Moves to the desired {@code offset} in the buffer.
*
* @param offset the offset to move to
* @throws NXException if the offset is greater than Integer.MAX_VALUE
*/
public void seek(long offset) {
if (offset > Integer.MAX_VALUE)
throw new NXException("Cannot seek to desired offset (" + offset + ") due to limitations with ByteBuf.");
seek((int) offset);
}
/**
* Moves to the desired {@code offset} in the buffer.
*
* @param offset the offset to move to
* @see io.netty.buffer.ByteBuf#readerIndex(int)
*/
public void seek(int offset) {
buf.readerIndex(offset);
}
/**
* Marks the current index to be returned to later.
*/
public void mark() {
buf.markReaderIndex();
}
/**
* Seeks back to the last marked index.
*/
public void reset() {
buf.resetReaderIndex();
}
/**
* Reads the next byte from the buffer.
*
* @return the next byte
*
* @see io.netty.buffer.ByteBuf#readByte()
*/
public byte getByte() {
return buf.readByte();
}
/**
* Reads the next unsigned byte from the buffer.
*
* @return the next unsigned byte
*
* @see io.netty.buffer.ByteBuf#readUnsignedByte()
*/
public short getUnsignedByte() {
return buf.readUnsignedByte();
}
/**
* Reads the next short from the buffer.
*
* @return the next short
*
* @see io.netty.buffer.ByteBuf#readShort()
*/
public short getShort() {
return buf.readShort();
}
/**
* Reads the next unsigned short from the buffer.
*
* @return the next unsigned short
*
* @see io.netty.buffer.ByteBuf#readUnsignedShort()
*/
public int getUnsignedShort() {
return buf.readUnsignedShort();
}
/**
* Reads the next integer from the buffer.
*
* @return the next integer
*
* @see io.netty.buffer.ByteBuf#readInt()
*/
public int getInt() {
return buf.readInt();
}
/**
* Reads the next unsigned integer from the buffer.
*
* @return the next unsigned integer
*
* @see io.netty.buffer.ByteBuf#readUnsignedInt()
*/
public long getUnsignedInt() {
return buf.readUnsignedInt();
}
/**
* Reads the next long from the buffer.
*
* @return the next long
*
* @see io.netty.buffer.ByteBuf#readLong()
*/
public long getLong() {
return buf.readLong();
}
/**
* Reads the next float from the buffer.
*
* @return the next float
*
* @see io.netty.buffer.ByteBuf#readFloat()
*/
public float getFloat() {
return buf.readFloat();
}
/**
* Reads the next double from the buffer.
*
* @return the next double
*
* @see io.netty.buffer.ByteBuf#readDouble()
*/
public double getDouble() {
return buf.readDouble();
}
/**
* Reads the next {@code length} bytes from the buffer.
*
* @param length the length to read
* @return an array of bytes read
*
* @see io.netty.buffer.ByteBuf#readBytes(int)
*/
public byte[] getBytes(int length) {
byte[] ret = new byte[length];
buf.readBytes(ret);
return ret;
}
/**
* Reads the next {@code length} bytes from the buffer.
*
* @param length the length to read
* @return a buffer of read bytes
*
* @see io.netty.buffer.ByteBuf#readBytes(io.netty.buffer.ByteBuf)
*/
public ByteBuf getBuf(int length) {
ByteBuf ret = Unpooled.buffer(length);
buf.readBytes(ret);
return ret;
}
/**
* Gets the next UTF String from the buffer using the next unsigned short as the length.
*
* @return the next string
*/
public String getUTFString() {
return getUTFString(getUnsignedShort());
}
/**
* Gets the next UTF String of length {@code length} from the buffer.
*
* @param length the length of the string
* @return the next string
*/
public String getUTFString(int length) {
try {
byte[] data = getBytes(length);
return utfDecoder.decode(ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)).toString();
} catch (CharacterCodingException e) {
logger.error("Failed to load UTF String in buffer.", e);
}
return null;
}
}