Add CharSequence operations to ByteBuf

Motivation:

Often users either need to read or write CharSequences to a ByteBuf. We should add methods for this to ByteBuf as we can do some optimizations for this depending on the implementation.

Modifications:

Add setCharSequence, writeCharSequence, getCharSequence and readCharSequence

Result:

Easier reading / writing of CharSequence with ByteBuf.
This commit is contained in:
Norman Maurer 2016-04-11 11:47:35 +02:00
parent b2bb72b2de
commit 9f5eb7d698
10 changed files with 258 additions and 12 deletions

View File

@ -16,6 +16,7 @@
package io.netty.buffer;
import io.netty.util.ByteProcessor;
import io.netty.util.CharsetUtil;
import io.netty.util.IllegalReferenceCountException;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.internal.PlatformDependent;
@ -483,6 +484,19 @@ public abstract class AbstractByteBuf extends ByteBuf {
return this;
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
// TODO: We could optimize this for UTF8 and US_ASCII
return toString(index, length, charset);
}
@Override
public CharSequence readCharSequence(int length, Charset charset) {
CharSequence sequence = getCharSequence(readerIndex, length, charset);
readerIndex += length;
return sequence;
}
@Override
public ByteBuf setByte(int index, int value) {
checkIndex(index);
@ -649,6 +663,23 @@ public abstract class AbstractByteBuf extends ByteBuf {
return this;
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
if (charset.equals(CharsetUtil.UTF_8)) {
ensureWritable(ByteBufUtil.utf8MaxBytes(sequence));
return ByteBufUtil.writeUtf8(this, index, sequence, sequence.length());
}
if (charset.equals(CharsetUtil.US_ASCII)) {
int len = sequence.length();
ensureWritable(len);
return ByteBufUtil.writeAscii(this, index, sequence, len);
}
byte[] bytes = sequence.toString().getBytes(charset);
ensureWritable(bytes.length);
setBytes(index, bytes);
return bytes.length;
}
@Override
public byte readByte() {
checkReadableBytes0(1);
@ -1111,6 +1142,13 @@ public abstract class AbstractByteBuf extends ByteBuf {
return this;
}
@Override
public int writeCharSequence(CharSequence sequence, Charset charset) {
int written = setCharSequence(writerIndex, sequence, charset);
writerIndex += written;
return written;
}
@Override
public ByteBuf copy() {
return copy(readerIndex, readableBytes());

View File

@ -244,6 +244,12 @@ final class AdvancedLeakAwareByteBuf extends WrappedByteBuf {
return super.getBytes(index, out, length);
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.getCharSequence(index, length, charset);
}
@Override
public ByteBuf setBoolean(int index, boolean value) {
recordLeakNonRefCountingOperation(leak);
@ -352,6 +358,12 @@ final class AdvancedLeakAwareByteBuf extends WrappedByteBuf {
return super.setZero(index, length);
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.setCharSequence(index, sequence, charset);
}
@Override
public boolean readBoolean() {
recordLeakNonRefCountingOperation(leak);
@ -484,6 +496,12 @@ final class AdvancedLeakAwareByteBuf extends WrappedByteBuf {
return super.readBytes(out, length);
}
@Override
public CharSequence readCharSequence(int length, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.readCharSequence(length, charset);
}
@Override
public ByteBuf skipBytes(int length) {
recordLeakNonRefCountingOperation(leak);
@ -844,6 +862,12 @@ final class AdvancedLeakAwareByteBuf extends WrappedByteBuf {
return super.writeLongLE(value);
}
@Override
public int writeCharSequence(CharSequence sequence, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.writeCharSequence(sequence, charset);
}
@Override
public int getBytes(int index, FileChannel out, long position, int length) throws IOException {
recordLeakNonRefCountingOperation(leak);

View File

@ -226,6 +226,12 @@ final class AdvancedLeakAwareCompositeByteBuf extends WrappedCompositeByteBuf {
return super.getBytes(index, out, length);
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.getCharSequence(index, length, charset);
}
@Override
public CompositeByteBuf setBoolean(int index, boolean value) {
recordLeakNonRefCountingOperation(leak);
@ -466,6 +472,12 @@ final class AdvancedLeakAwareCompositeByteBuf extends WrappedCompositeByteBuf {
return super.readBytes(out, length);
}
@Override
public CharSequence readCharSequence(int length, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.readCharSequence(length, charset);
}
@Override
public CompositeByteBuf skipBytes(int length) {
recordLeakNonRefCountingOperation(leak);
@ -580,6 +592,12 @@ final class AdvancedLeakAwareCompositeByteBuf extends WrappedCompositeByteBuf {
return super.writeZero(length);
}
@Override
public int writeCharSequence(CharSequence sequence, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.writeCharSequence(sequence, charset);
}
@Override
public int indexOf(int fromIndex, int toIndex, byte value) {
recordLeakNonRefCountingOperation(leak);
@ -760,6 +778,12 @@ final class AdvancedLeakAwareCompositeByteBuf extends WrappedCompositeByteBuf {
return super.setLongLE(index, value);
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
recordLeakNonRefCountingOperation(leak);
return super.setCharSequence(index, sequence, charset);
}
@Override
public short readShortLE() {
recordLeakNonRefCountingOperation(leak);

View File

@ -913,6 +913,17 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
*/
public abstract int getBytes(int index, FileChannel out, long position, int length) throws IOException;
/**
* Gets a {@link CharSequence} with the given length at the given index.
*
* @param length the length to read
* @param charset that should be used
* @return the sequence
* @throws IndexOutOfBoundsException
* if {@code length} is greater than {@code this.readableBytes}
*/
public abstract CharSequence getCharSequence(int index, int length, Charset charset);
/**
* Sets the specified boolean at the specified absolute {@code index} in this
* buffer.
@ -1247,6 +1258,19 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
*/
public abstract ByteBuf setZero(int index, int length);
/**
* Writes the specified {@link CharSequence} at the current {@code writerIndex} and increases
* the {@code writerIndex} by the written bytes.
*
* @param index on which the sequence should be written
* @param sequence to write
* @param charset that should be used.
* @return the written number of bytes.
* @throws IndexOutOfBoundsException
* if {@code this.writableBytes} is not large enough to write the whole sequence
*/
public abstract int setCharSequence(int index, CharSequence sequence, Charset charset);
/**
* Gets a boolean at the current {@code readerIndex} and increases
* the {@code readerIndex} by {@code 1} in this buffer.
@ -1579,6 +1603,18 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
*/
public abstract int readBytes(GatheringByteChannel out, int length) throws IOException;
/**
* Gets a {@link CharSequence} with the given length at the current {@code readerIndex}
* and increases the {@code readerIndex} by the given length.
*
* @param length the length to read
* @param charset that should be used
* @return the sequence
* @throws IndexOutOfBoundsException
* if {@code length} is greater than {@code this.readableBytes}
*/
public abstract CharSequence readCharSequence(int length, Charset charset);
/**
* Transfers this buffer's data starting at the current {@code readerIndex}
* to the specified channel starting at the given file position.
@ -1885,6 +1921,19 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
*/
public abstract ByteBuf writeZero(int length);
/**
* Writes the specified {@link CharSequence} at the current {@code writerIndex} and increases
* the {@code writerIndex} by the written bytes.
* in this buffer.
*
* @param sequence to write
* @param charset that should be used
* @return the written number of bytes
* @throws IndexOutOfBoundsException
* if {@code this.writableBytes} is not large enough to write the whole sequence
*/
public abstract int writeCharSequence(CharSequence sequence, Charset charset);
/**
* Locates the first occurrence of the specified {@code value} in this
* buffer. The search takes place from the specified {@code fromIndex}

View File

@ -382,7 +382,10 @@ public final class ByteBufUtil {
for (;;) {
if (buf instanceof AbstractByteBuf) {
return writeUtf8((AbstractByteBuf) buf, seq, len);
AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
int written = writeUtf8(byteBuf, byteBuf.writerIndex, seq, len);
byteBuf.writerIndex += written;
return written;
} else if (buf instanceof WrappedByteBuf) {
// Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
buf = buf.unwrap();
@ -395,9 +398,8 @@ public final class ByteBufUtil {
}
// Fast-Path implementation
private static int writeUtf8(AbstractByteBuf buffer, CharSequence seq, int len) {
int oldWriterIndex = buffer.writerIndex;
int writerIndex = oldWriterIndex;
static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
int oldWriterIndex = writerIndex;
// We can use the _set methods as these not need to do any index checks and reference checks.
// This is possible as we called ensureWritable(...) before.
@ -440,8 +442,6 @@ public final class ByteBufUtil {
buffer._setByte(writerIndex++, (byte) (0x80 | (c & 0x3f)));
}
}
// update the writerIndex without any extra checks for performance reasons
buffer.writerIndex = writerIndex;
return writerIndex - oldWriterIndex;
}
@ -483,8 +483,10 @@ public final class ByteBufUtil {
} else {
for (;;) {
if (buf instanceof AbstractByteBuf) {
writeAscii((AbstractByteBuf) buf, seq, len);
break;
AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len);
byteBuf.writerIndex += written;
return written;
} else if (buf instanceof WrappedByteBuf) {
// Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
buf = buf.unwrap();
@ -497,16 +499,14 @@ public final class ByteBufUtil {
}
// Fast-Path implementation
private static void writeAscii(AbstractByteBuf buffer, CharSequence seq, int len) {
int writerIndex = buffer.writerIndex;
static int writeAscii(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {
// We can use the _set methods as these not need to do any index checks and reference checks.
// This is possible as we called ensureWritable(...) before.
for (int i = 0; i < len; i++) {
buffer._setByte(writerIndex++, (byte) seq.charAt(i));
}
// update the writerIndex without any extra checks for performance reasons
buffer.writerIndex = writerIndex;
return len;
}
/**

View File

@ -391,6 +391,12 @@ public final class EmptyByteBuf extends ByteBuf {
return 0;
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
checkIndex(index, length);
return null;
}
@Override
public ByteBuf setBoolean(int index, boolean value) {
throw new IndexOutOfBoundsException();
@ -509,6 +515,11 @@ public final class EmptyByteBuf extends ByteBuf {
return checkIndex(index, length);
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
throw new IndexOutOfBoundsException();
}
@Override
public boolean readBoolean() {
throw new IndexOutOfBoundsException();
@ -666,6 +677,12 @@ public final class EmptyByteBuf extends ByteBuf {
return 0;
}
@Override
public CharSequence readCharSequence(int length, Charset charset) {
checkLength(length);
return null;
}
@Override
public ByteBuf skipBytes(int length) {
return checkLength(length);
@ -789,6 +806,11 @@ public final class EmptyByteBuf extends ByteBuf {
return checkLength(length);
}
@Override
public int writeCharSequence(CharSequence sequence, Charset charset) {
throw new IndexOutOfBoundsException();
}
@Override
public int indexOf(int fromIndex, int toIndex, byte value) {
checkIndex(fromIndex);

View File

@ -16,6 +16,7 @@
package io.netty.buffer;
import io.netty.util.ByteProcessor;
import io.netty.util.CharsetUtil;
import java.io.IOException;
import java.io.InputStream;
@ -25,6 +26,7 @@ import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset;
/**
* A derived buffer which exposes its parent's sub-region only. It is
@ -264,6 +266,12 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf {
return this;
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
checkIndex0(index, length);
return buffer.getCharSequence(idx(index), length, charset);
}
@Override
protected void _setByte(int index, int value) {
unwrap().setByte(idx(index), value);
@ -386,6 +394,23 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf {
return this;
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
if (charset.equals(CharsetUtil.UTF_8)) {
checkIndex0(index, ByteBufUtil.utf8MaxBytes(sequence));
return ByteBufUtil.writeUtf8(this, idx(index), sequence, sequence.length());
}
if (charset.equals(CharsetUtil.US_ASCII)) {
int len = sequence.length();
checkIndex0(index, len);
return ByteBufUtil.writeAscii(this, idx(index), sequence, len);
}
byte[] bytes = sequence.toString().getBytes(charset);
checkIndex0(index, bytes.length);
buffer.setBytes(idx(index), bytes);
return bytes.length;
}
@Override
public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
checkIndex0(index, length);

View File

@ -376,6 +376,11 @@ public class SwappedByteBuf extends ByteBuf {
return buf.getBytes(index, out, position, length);
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
return buf.getCharSequence(index, length, charset);
}
@Override
public ByteBuf setBoolean(int index, boolean value) {
buf.setBoolean(index, value);
@ -511,6 +516,11 @@ public class SwappedByteBuf extends ByteBuf {
return this;
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
return buf.setCharSequence(index, sequence, charset);
}
@Override
public boolean readBoolean() {
return buf.readBoolean();
@ -673,6 +683,11 @@ public class SwappedByteBuf extends ByteBuf {
return buf.readBytes(out, position, length);
}
@Override
public CharSequence readCharSequence(int length, Charset charset) {
return buf.readCharSequence(length, charset);
}
@Override
public ByteBuf skipBytes(int length) {
buf.skipBytes(length);
@ -814,6 +829,11 @@ public class SwappedByteBuf extends ByteBuf {
return this;
}
@Override
public int writeCharSequence(CharSequence sequence, Charset charset) {
return buf.writeCharSequence(sequence, charset);
}
@Override
public int indexOf(int fromIndex, int toIndex, byte value) {
return buf.indexOf(fromIndex, toIndex, value);

View File

@ -366,6 +366,11 @@ class WrappedByteBuf extends ByteBuf {
return buf.getBytes(index, out, position, length);
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
return buf.getCharSequence(index, length, charset);
}
@Override
public ByteBuf setBoolean(int index, boolean value) {
buf.setBoolean(index, value);
@ -501,6 +506,11 @@ class WrappedByteBuf extends ByteBuf {
return this;
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
return buf.setCharSequence(index, sequence, charset);
}
@Override
public boolean readBoolean() {
return buf.readBoolean();
@ -663,6 +673,11 @@ class WrappedByteBuf extends ByteBuf {
return buf.readBytes(out, position, length);
}
@Override
public CharSequence readCharSequence(int length, Charset charset) {
return buf.readCharSequence(length, charset);
}
@Override
public ByteBuf skipBytes(int length) {
buf.skipBytes(length);
@ -804,6 +819,11 @@ class WrappedByteBuf extends ByteBuf {
return this;
}
@Override
public int writeCharSequence(CharSequence sequence, Charset charset) {
return buf.writeCharSequence(sequence, charset);
}
@Override
public int indexOf(int fromIndex, int toIndex, byte value) {
return buf.indexOf(fromIndex, toIndex, value);

View File

@ -359,6 +359,12 @@ final class ReplayingDecoderByteBuf extends ByteBuf {
return buffer.getDouble(index);
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
checkIndex(index, length);
return buffer.getCharSequence(index, length, charset);
}
@Override
public int hashCode() {
reject();
@ -712,6 +718,12 @@ final class ReplayingDecoderByteBuf extends ByteBuf {
return buffer.readDouble();
}
@Override
public CharSequence readCharSequence(int length, Charset charset) {
checkReadableBytes(length);
return buffer.readCharSequence(length, charset);
}
@Override
public ByteBuf resetReaderIndex() {
buffer.resetReaderIndex();
@ -1114,6 +1126,18 @@ final class ReplayingDecoderByteBuf extends ByteBuf {
return this;
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
reject();
return -1;
}
@Override
public int writeCharSequence(CharSequence sequence, Charset charset) {
reject();
return -1;
}
private void checkIndex(int index, int length) {
if (index + length > buffer.writerIndex()) {
throw REPLAY;