/*
* Copyright 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality
*
* This is free software; you can redistribute and/or modify it under the
* terms of the GNU Lesser General Licence as published by the Free Software
* Foundation. See the LICENSE.txt file for more information.
*
* This software is distributed WITHOUT ANY WARRANTY and without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package com.revolsys.io.channels;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import com.revolsys.io.BaseCloseable;
import com.revolsys.util.Exceptions;
public class ChannelReader implements BaseCloseable {
private ByteBuffer buffer;
private ReadableByteChannel channel;
private int available = 0;
private ByteBuffer tempBuffer = ByteBuffer.allocate(8);
public ChannelReader(final ReadableByteChannel channel) {
this(channel, 8096);
}
public ChannelReader(final ReadableByteChannel channel, final ByteBuffer buffer) {
this.channel = channel;
this.buffer = buffer;
this.tempBuffer.order(buffer.order());
}
public ChannelReader(final ReadableByteChannel channel, final int capacity) {
this(channel, ByteBuffer.allocateDirect(capacity));
}
public ChannelReader(final ReadableByteChannel channel, final int capacity,
final ByteOrder byteOrder) {
this(channel, ByteBuffer.allocateDirect(capacity));
setByteOrder(byteOrder);
}
@Override
public void close() {
final ReadableByteChannel channel = this.channel;
this.channel = null;
if (channel != null) {
try {
channel.close();
} catch (final IOException e) {
throw Exceptions.wrap(e);
}
}
this.buffer = null;
this.tempBuffer = null;
}
public byte getByte() {
if (this.available == 0) {
read(1);
}
this.available--;
return this.buffer.get();
}
public ByteOrder getByteOrder() {
return this.buffer.order();
}
public byte[] getBytes(final byte[] bytes) {
final int byteCount = bytes.length;
if (this.available < byteCount) {
this.buffer.get(bytes, 0, this.available);
int offset = this.available;
do {
int bytesToRead = byteCount - offset;
read(bytesToRead);
if (bytesToRead > this.available) {
bytesToRead = this.available;
}
this.available -= bytesToRead;
this.buffer.get(bytes, offset, bytesToRead);
offset += bytesToRead;
} while (offset < byteCount);
} else {
this.available -= byteCount;
this.buffer.get(bytes);
}
return bytes;
}
public byte[] getBytes(final int byteCount) {
final byte[] bytes = new byte[byteCount];
return getBytes(bytes);
}
public ReadableByteChannel getChannel() {
return this.channel;
}
public double getDouble() {
if (this.available < 8) {
final ByteBuffer tempBuffer = readTempBytes(8);
return tempBuffer.getDouble();
} else {
this.available -= 8;
return this.buffer.getDouble();
}
}
public float getFloat() {
if (this.available < 4) {
final ByteBuffer tempBuffer = readTempBytes(4);
return tempBuffer.getFloat();
} else {
this.available -= 4;
return this.buffer.getFloat();
}
}
public int getInt() {
if (this.available < 4) {
final ByteBuffer tempBuffer = readTempBytes(4);
return tempBuffer.getInt();
} else {
this.available -= 4;
return this.buffer.getInt();
}
}
public long getLong() {
if (this.available < 8) {
final ByteBuffer tempBuffer = readTempBytes(8);
return tempBuffer.getLong();
} else {
this.available -= 8;
return this.buffer.getLong();
}
}
public short getShort() {
if (this.available < 2) {
final ByteBuffer tempBuffer = readTempBytes(2);
return tempBuffer.getShort();
} else {
this.available -= 2;
return this.buffer.getShort();
}
}
public String getString(final int byteCount, final Charset charset) {
final byte[] bytes = getBytes(byteCount);
int i = 0;
for (; i < bytes.length; i++) {
final byte character = bytes[i];
if (character == 0) {
return new String(bytes, 0, i, charset);
}
}
return new String(bytes, 0, i, charset);
}
public short getUnsignedByte() {
final byte signedByte = getByte();
return (short)Byte.toUnsignedInt(signedByte);
}
public long getUnsignedInt() {
final int signedInt = getInt();
return Integer.toUnsignedLong(signedInt);
}
/**
* Unsigned longs don't actually work channel Java
* @return
*/
public long getUnsignedLong() {
final long signedLong = getLong();
return signedLong;
}
public int getUnsignedShort() {
final short signedShort = getShort();
return Short.toUnsignedInt(signedShort);
}
public String getUsAsciiString(final int byteCount) {
return getString(byteCount, StandardCharsets.US_ASCII);
}
private void read(final int minCount) {
try {
this.buffer.clear();
while (this.available < minCount) {
final int readCount = this.channel.read(this.buffer);
if (readCount == -1) {
throw new EOFException();
} else {
this.available += readCount;
}
}
this.buffer.flip();
} catch (final IOException e) {
throw Exceptions.wrap(e);
}
}
private ByteBuffer readTempBytes(final int count) {
final ByteBuffer buffer = this.buffer;
final ByteBuffer tempBuffer = this.tempBuffer;
tempBuffer.clear();
tempBuffer.put(buffer);
final int readCount = count - this.available;
this.available = 0;
read(readCount);
this.available -= readCount;
for (int i = 0; i < readCount; i++) {
final byte b = buffer.get();
tempBuffer.put(b);
}
tempBuffer.flip();
return tempBuffer;
}
public void setByteOrder(final ByteOrder byteOrder) {
this.buffer.order(byteOrder);
this.tempBuffer.order(byteOrder);
}
public void skipBytes(int count) {
while (count > this.available) {
count -= this.available;
this.available = 0;
read(count);
}
this.available -= count;
final int position = this.buffer.position();
this.buffer.position(position + count);
}
}