package org.bson.io;
import org.bson.BsonSerializationException;
import org.bson.ByteBuf;
import org.bson.types.ObjectId;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import static java.lang.String.format;
public class ByteBufferBsonInput implements BsonInput {
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
private static final String[] ONE_BYTE_ASCII_STRINGS = new String[Byte.MAX_VALUE + 1];
static {
for (int b = 0; b < ONE_BYTE_ASCII_STRINGS.length; b++) {
ONE_BYTE_ASCII_STRINGS[b] = String.valueOf((char) b);
}
}
private ByteBuf buffer;
private int mark = -1;
public ByteBufferBsonInput(final ByteBuf buffer) {
if (buffer == null) {
throw new IllegalArgumentException("buffer can not be null");
}
this.buffer = buffer;
buffer.order(ByteOrder.LITTLE_ENDIAN);
}
@Override
public int getPosition() {
ensureOpen();
return buffer.position();
}
@Override
public byte readByte() {
ensureOpen();
ensureAvailable(1);
return buffer.get();
}
@Override
public void readBytes(final byte[] bytes) {
ensureOpen();
ensureAvailable(bytes.length);
buffer.get(bytes);
}
@Override
public void readBytes(final byte[] bytes, final int offset, final int length) {
ensureOpen();
ensureAvailable(length);
buffer.get(bytes, offset, length);
}
@Override
public long readInt64() {
ensureOpen();
ensureAvailable(8);
return buffer.getLong();
}
@Override
public double readDouble() {
ensureOpen();
ensureAvailable(8);
return buffer.getDouble();
}
@Override
public int readInt32() {
ensureOpen();
ensureAvailable(4);
return buffer.getInt();
}
@Override
public ObjectId readObjectId() {
ensureOpen();
byte[] bytes = new byte[12];
readBytes(bytes);
return new ObjectId(bytes);
}
@Override
public String readString() {
ensureOpen();
int size = readInt32();
if (size <= 0) {
throw new BsonSerializationException(format("While decoding a BSON string found a size that is not a positive number: %d",
size));
}
return readString(size);
}
@Override
public String readCString() {
ensureOpen();
int mark = buffer.position();
readUntilNullByte();
int size = buffer.position() - mark;
buffer.position(mark);
return readString(size);
}
private String readString(final int size) {
if (size == 2) {
byte asciiByte = readByte();
byte nullByte = readByte();
if (nullByte != 0) {
throw new BsonSerializationException("Found a BSON string that is not null-terminated");
}
if (asciiByte < 0) {
return UTF8_CHARSET.newDecoder().replacement();
}
return ONE_BYTE_ASCII_STRINGS[asciiByte];
} else {
byte[] bytes = new byte[size - 1];
readBytes(bytes);
byte nullByte = readByte();
if (nullByte != 0) {
throw new BsonSerializationException("Found a BSON string that is not null-terminated");
}
return new String(bytes, UTF8_CHARSET);
}
}
private void readUntilNullByte() {
while (readByte() != 0) {
}
}
@Override
public void skipCString() {
ensureOpen();
readUntilNullByte();
}
@Override
public void skip(final int numBytes) {
ensureOpen();
buffer.position(buffer.position() + numBytes);
}
@Deprecated
@Override
public void mark(final int readLimit) {
ensureOpen();
mark = buffer.position();
}
@Override
public BsonInputMark getMark(final int readLimit) {
return new BsonInputMark() {
private int mark = buffer.position();
@Override
public void reset() {
ensureOpen();
buffer.position(mark);
}
};
}
@Deprecated
@Override
public void reset() {
ensureOpen();
if (mark == -1) {
throw new IllegalStateException("Mark not set");
}
buffer.position(mark);
}
@Override
public boolean hasRemaining() {
ensureOpen();
return buffer.hasRemaining();
}
@Override
public void close() {
buffer.release();
buffer = null;
}
private void ensureOpen() {
if (buffer == null) {
throw new IllegalStateException("Stream is closed");
}
}
private void ensureAvailable(final int bytesNeeded) {
if (buffer.remaining() < bytesNeeded) {
throw new BsonSerializationException(format("While decoding a BSON document %d bytes were required, "
+ "but only %d remain", bytesNeeded, buffer.remaining()));
}
}
}