diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 3f97be30c8..7dbc536bb9 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -15,6 +15,7 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.IllegalReferenceCountException; import io.netty.util.ResourceLeakDetector; import io.netty.util.internal.PlatformDependent; @@ -965,7 +966,7 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { int index = readerIndex; int length = writerIndex - index; ensureAccessible(); @@ -973,12 +974,12 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { checkIndex(index, length); return forEachByteAsc0(index, length, processor); } - private int forEachByteAsc0(int index, int length, ByteBufProcessor processor) { + private int forEachByteAsc0(int index, int length, ByteProcessor processor) { if (processor == null) { throw new NullPointerException("processor"); } @@ -1005,7 +1006,7 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { int index = readerIndex; int length = writerIndex - index; ensureAccessible(); @@ -1013,13 +1014,13 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { checkIndex(index, length); return forEachByteDesc0(index, length, processor); } - private int forEachByteDesc0(int index, int length, ByteBufProcessor processor) { + private int forEachByteDesc0(int index, int length, ByteProcessor processor) { if (processor == null) { throw new NullPointerException("processor"); diff --git a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java index 3866baf967..ef683596ca 100644 --- a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java @@ -16,6 +16,7 @@ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.ResourceLeak; import java.io.IOException; @@ -599,25 +600,25 @@ final class AdvancedLeakAwareByteBuf extends WrappedByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { leak.record(); return super.forEachByte(processor); } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { leak.record(); return super.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { leak.record(); return super.forEachByteDesc(processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { leak.record(); return super.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java index 4d399c683e..13d03ca262 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java @@ -15,6 +15,7 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.ReferenceCounted; import java.io.IOException; @@ -178,7 +179,7 @@ import java.nio.charset.UnsupportedCharsetException; * * For simple single-byte searches, use {@link #indexOf(int, int, byte)} and {@link #bytesBefore(int, int, byte)}. * {@link #bytesBefore(byte)} is especially useful when you deal with a {@code NUL}-terminated string. - * For complicated searches, use {@link #forEachByte(int, int, ByteBufProcessor)} with a {@link ByteBufProcessor} + * For complicated searches, use {@link #forEachByte(int, int, ByteProcessor)} with a {@link ByteProcessor} * implementation. * *

Mark and reset

@@ -1605,26 +1606,26 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable { * Iterates over the readable bytes of this buffer with the specified {@code processor} in ascending order. * * @return {@code -1} if the processor iterated to or beyond the end of the readable bytes. - * The last-visited index If the {@link ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByte(ByteBufProcessor processor); + public abstract int forEachByte(ByteProcessor processor); /** * Iterates over the specified area of this buffer with the specified {@code processor} in ascending order. * (i.e. {@code index}, {@code (index + 1)}, .. {@code (index + length - 1)}) * * @return {@code -1} if the processor iterated to or beyond the end of the specified area. - * The last-visited index If the {@link ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByte(int index, int length, ByteBufProcessor processor); + public abstract int forEachByte(int index, int length, ByteProcessor processor); /** * Iterates over the readable bytes of this buffer with the specified {@code processor} in descending order. * * @return {@code -1} if the processor iterated to or beyond the beginning of the readable bytes. - * The last-visited index If the {@link ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByteDesc(ByteBufProcessor processor); + public abstract int forEachByteDesc(ByteProcessor processor); /** * Iterates over the specified area of this buffer with the specified {@code processor} in descending order. @@ -1632,9 +1633,9 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable { * * * @return {@code -1} if the processor iterated to or beyond the beginning of the specified area. - * The last-visited index If the {@link ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByteDesc(int index, int length, ByteBufProcessor processor); + public abstract int forEachByteDesc(int index, int length, ByteProcessor processor); /** * Returns a copy of this buffer's readable bytes. Modifying the content diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java b/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java index a1323a0ce6..13d063ff9e 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java @@ -16,10 +16,15 @@ package io.netty.buffer; -public interface ByteBufProcessor { +import io.netty.util.ByteProcessor; + +/** + * @deprecated Use {@link ByteProcessor}. + */ +public interface ByteBufProcessor extends ByteProcessor { /** - * Aborts on a {@code NUL (0x00)}. + * @deprecated Use {@link ByteProcessor#FIND_NULL}. */ ByteBufProcessor FIND_NUL = new ByteBufProcessor() { @Override @@ -29,7 +34,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a non-{@code NUL (0x00)}. + * @deprecated Use {@link ByteProcessor#FIND_NON_NUL}. */ ByteBufProcessor FIND_NON_NUL = new ByteBufProcessor() { @Override @@ -39,7 +44,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a {@code CR ('\r')}. + * @deprecated Use {@link ByteProcessor#FIND_CR}. */ ByteBufProcessor FIND_CR = new ByteBufProcessor() { @Override @@ -49,7 +54,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a non-{@code CR ('\r')}. + * @deprecated Use {@link ByteProcessor#FIND_NON_CR}. */ ByteBufProcessor FIND_NON_CR = new ByteBufProcessor() { @Override @@ -59,7 +64,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a {@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_LF}. */ ByteBufProcessor FIND_LF = new ByteBufProcessor() { @Override @@ -69,7 +74,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a non-{@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_NON_LF}. */ ByteBufProcessor FIND_NON_LF = new ByteBufProcessor() { @Override @@ -79,7 +84,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a {@code CR ('\r')} or a {@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_CRLF}. */ ByteBufProcessor FIND_CRLF = new ByteBufProcessor() { @Override @@ -89,7 +94,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a byte which is neither a {@code CR ('\r')} nor a {@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_NON_CRLF}. */ ByteBufProcessor FIND_NON_CRLF = new ByteBufProcessor() { @Override @@ -99,7 +104,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a linear whitespace (a ({@code ' '} or a {@code '\t'}). + * @deprecated Use {@link ByteProcessor#FIND_LINEAR_WHITESPACE}. */ ByteBufProcessor FIND_LINEAR_WHITESPACE = new ByteBufProcessor() { @Override @@ -109,7 +114,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a byte which is not a linear whitespace (neither {@code ' '} nor {@code '\t'}). + * @deprecated Use {@link ByteProcessor#FIND_NON_LINEAR_WHITESPACE}. */ ByteBufProcessor FIND_NON_LINEAR_WHITESPACE = new ByteBufProcessor() { @Override @@ -117,10 +122,4 @@ public interface ByteBufProcessor { return value == ' ' || value == '\t'; } }; - - /** - * @return {@code true} if the processor wants to continue the loop and handle the next byte in the buffer. - * {@code false} if the processor wants to stop handling bytes and abort the loop. - */ - boolean process(byte value) throws Exception; } diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 871db9bcdd..fbbe2b1c0f 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import io.netty.util.ByteString; import io.netty.util.CharsetUtil; import io.netty.util.Recycler; import io.netty.util.Recycler.Handle; @@ -31,6 +33,7 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; +import java.util.Arrays; import java.util.Locale; /** @@ -549,6 +552,85 @@ public final class ByteBufUtil { } } + /** + * Create a copy of the underlying storage from {@link value} into a byte array. + * The copy will start at {@link ByteBuf#readerIndex()} and copy {@link ByteBuf#readableBytes()} bytes. + */ + public static byte[] getBytes(ByteBuf buf) { + return getBytes(buf, checkNotNull(buf, "buf").readerIndex(), buf.readableBytes()); + } + + /** + * Create a copy of the underlying storage from {@link buf} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + */ + public static byte[] getBytes(ByteBuf buf, int start, int length) { + return getBytes(buf, start, length, true); + } + + /** + * Return an array of the underlying storage from {@link buf} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + * If {@code copy} is true a copy will be made of the memory. + * If {@code copy} is false the underlying storage will be shared, if possible. + */ + public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) { + if (start < 0 || length > checkNotNull(buf, "buf").capacity() - start) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "buf.capacity(" + buf.capacity() + ')'); + } + + if (buf.hasArray()) { + if (copy || start != 0 || length != buf.capacity()) { + int baseOffset = buf.arrayOffset() + start; + return Arrays.copyOfRange(buf.array(), baseOffset, baseOffset + length); + } else { + return buf.array(); + } + } + + byte[] v = new byte[length]; + buf.getBytes(start, v); + return v; + } + + /** + * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. + * @param src The source of the data to copy. + * @param srcIdx the starting offset of characters to copy. + * @param dst the destination byte array. + * @param dstIdx the starting offset in the destination byte array. + * @param length the number of characters to copy. + */ + public static void copy(ByteString src, int srcIdx, ByteBuf dst, int dstIdx, int length) { + final int thisLen = src.length(); + + if (srcIdx < 0 || length > thisLen - srcIdx) { + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); + } + + checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx, length); + } + + /** + * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. + * @param src The source of the data to copy. + * @param srcIdx the starting offset of characters to copy. + * @param dst the destination byte array. + * @param length the number of characters to copy. + */ + public static void copy(ByteString src, int srcIdx, ByteBuf dst, int length) { + final int thisLen = src.length(); + + if (srcIdx < 0 || length > thisLen - srcIdx) { + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); + } + + checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx, length); + } + static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf { private static final Recycler RECYCLER = diff --git a/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java b/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java index 43ff8b895c..d6f89644cd 100644 --- a/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -293,12 +295,12 @@ public class DuplicatedByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buffer.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buffer.forEachByteDesc(index, length, processor); } } diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java index 9f8db89741..80facdb7fa 100644 --- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java @@ -16,6 +16,7 @@ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; @@ -668,23 +669,23 @@ public final class EmptyByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { return -1; } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { checkIndex(index, length); return -1; } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { return -1; } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { checkIndex(index, length); return -1; } diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java index a284c527d4..b6e46ab64c 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -296,12 +298,12 @@ public class ReadOnlyByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buffer.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buffer.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java index 4c109fe62e..7683e13bc2 100644 --- a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -275,7 +277,7 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { int ret = buffer.forEachByte(index + adjustment, length, processor); if (ret >= adjustment) { return ret - adjustment; @@ -285,7 +287,7 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { int ret = buffer.forEachByteDesc(index + adjustment, length, processor); if (ret >= adjustment) { return ret - adjustment; diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java index 3c917c4f84..df257b45c9 100644 --- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -680,22 +682,22 @@ public class SwappedByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { return buf.forEachByte(processor); } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buf.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { return buf.forEachByteDesc(processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buf.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java index eae17e0e74..d3050851cb 100644 --- a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java @@ -16,6 +16,7 @@ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -668,22 +669,22 @@ public class WrappedByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { return buf.forEachByte(processor); } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buf.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { return buf.forEachByteDesc(processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buf.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java index 684bbb2042..7bccf0b876 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java @@ -15,9 +15,11 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.CharsetUtil; import io.netty.util.IllegalReferenceCountException; import io.netty.util.internal.ThreadLocalRandom; + import org.junit.After; import org.junit.Assume; import org.junit.Before; @@ -1666,7 +1668,7 @@ public abstract class AbstractByteBufTest { final AtomicInteger lastIndex = new AtomicInteger(); buffer.setIndex(CAPACITY / 4, CAPACITY * 3 / 4); - assertThat(buffer.forEachByte(new ByteBufProcessor() { + assertThat(buffer.forEachByte(new ByteProcessor() { int i = CAPACITY / 4; @Override @@ -1689,7 +1691,7 @@ public abstract class AbstractByteBufTest { } final int stop = CAPACITY / 2; - assertThat(buffer.forEachByte(CAPACITY / 3, CAPACITY / 3, new ByteBufProcessor() { + assertThat(buffer.forEachByte(CAPACITY / 3, CAPACITY / 3, new ByteProcessor() { int i = CAPACITY / 3; @Override @@ -1713,7 +1715,7 @@ public abstract class AbstractByteBufTest { } final AtomicInteger lastIndex = new AtomicInteger(); - assertThat(buffer.forEachByteDesc(CAPACITY / 4, CAPACITY * 2 / 4, new ByteBufProcessor() { + assertThat(buffer.forEachByteDesc(CAPACITY / 4, CAPACITY * 2 / 4, new ByteProcessor() { int i = CAPACITY * 3 / 4 - 1; @Override @@ -2377,22 +2379,22 @@ public abstract class AbstractByteBufTest { @Test(expected = IllegalReferenceCountException.class) public void testForEachByteAfterRelease() { - releasedBuffer().forEachByte(new TestByteBufProcessor()); + releasedBuffer().forEachByte(new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) public void testForEachByteAfterRelease1() { - releasedBuffer().forEachByte(0, 1, new TestByteBufProcessor()); + releasedBuffer().forEachByte(0, 1, new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) public void testForEachByteDescAfterRelease() { - releasedBuffer().forEachByteDesc(new TestByteBufProcessor()); + releasedBuffer().forEachByteDesc(new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) public void testForEachByteDescAfterRelease1() { - releasedBuffer().forEachByteDesc(0, 1, new TestByteBufProcessor()); + releasedBuffer().forEachByteDesc(0, 1, new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) @@ -2647,7 +2649,7 @@ public abstract class AbstractByteBufTest { } } - private static final class TestByteBufProcessor implements ByteBufProcessor { + private static final class TestByteProcessor implements ByteProcessor { @Override public boolean process(byte value) throws Exception { return true; diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufProcessorTest.java b/buffer/src/test/java/io/netty/buffer/ByteBufProcessorTest.java deleted file mode 100644 index 26e833489e..0000000000 --- a/buffer/src/test/java/io/netty/buffer/ByteBufProcessorTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you 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 io.netty.buffer; - -import io.netty.util.CharsetUtil; -import org.junit.Test; - -import static io.netty.util.ReferenceCountUtil.releaseLater; -import static org.junit.Assert.*; - -public class ByteBufProcessorTest { - @Test - public void testForward() { - final ByteBuf buf = releaseLater( - Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); - final int length = buf.readableBytes(); - - assertEquals(3, buf.forEachByte(0, length, ByteBufProcessor.FIND_CRLF)); - assertEquals(6, buf.forEachByte(3, length - 3, ByteBufProcessor.FIND_NON_CRLF)); - assertEquals(9, buf.forEachByte(6, length - 6, ByteBufProcessor.FIND_CR)); - assertEquals(11, buf.forEachByte(9, length - 9, ByteBufProcessor.FIND_NON_CR)); - assertEquals(14, buf.forEachByte(11, length - 11, ByteBufProcessor.FIND_LF)); - assertEquals(16, buf.forEachByte(14, length - 14, ByteBufProcessor.FIND_NON_LF)); - assertEquals(19, buf.forEachByte(16, length - 16, ByteBufProcessor.FIND_NUL)); - assertEquals(21, buf.forEachByte(19, length - 19, ByteBufProcessor.FIND_NON_NUL)); - assertEquals(24, buf.forEachByte(21, length - 21, ByteBufProcessor.FIND_LINEAR_WHITESPACE)); - assertEquals(28, buf.forEachByte(24, length - 24, ByteBufProcessor.FIND_NON_LINEAR_WHITESPACE)); - assertEquals(-1, buf.forEachByte(28, length - 28, ByteBufProcessor.FIND_LINEAR_WHITESPACE)); - } - - @Test - public void testBackward() { - final ByteBuf buf = releaseLater( - Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); - final int length = buf.readableBytes(); - - assertEquals(27, buf.forEachByteDesc(0, length, ByteBufProcessor.FIND_LINEAR_WHITESPACE)); - assertEquals(23, buf.forEachByteDesc(0, 28, ByteBufProcessor.FIND_NON_LINEAR_WHITESPACE)); - assertEquals(20, buf.forEachByteDesc(0, 24, ByteBufProcessor.FIND_NUL)); - assertEquals(18, buf.forEachByteDesc(0, 21, ByteBufProcessor.FIND_NON_NUL)); - assertEquals(15, buf.forEachByteDesc(0, 19, ByteBufProcessor.FIND_LF)); - assertEquals(13, buf.forEachByteDesc(0, 16, ByteBufProcessor.FIND_NON_LF)); - assertEquals(10, buf.forEachByteDesc(0, 14, ByteBufProcessor.FIND_CR)); - assertEquals(8, buf.forEachByteDesc(0, 11, ByteBufProcessor.FIND_NON_CR)); - assertEquals(5, buf.forEachByteDesc(0, 9, ByteBufProcessor.FIND_CRLF)); - assertEquals(2, buf.forEachByteDesc(0, 6, ByteBufProcessor.FIND_NON_CRLF)); - assertEquals(-1, buf.forEachByteDesc(0, 3, ByteBufProcessor.FIND_CRLF)); - } -} diff --git a/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java b/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java new file mode 100644 index 0000000000..6f2f3044ed --- /dev/null +++ b/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you 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 io.netty.buffer; + +import static io.netty.util.ReferenceCountUtil.releaseLater; +import static org.junit.Assert.assertEquals; +import io.netty.util.ByteProcessor; +import io.netty.util.CharsetUtil; + +import org.junit.Test; + +public class ByteProcessorTest { + @Test + public void testForward() { + final ByteBuf buf = releaseLater( + Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); + final int length = buf.readableBytes(); + + assertEquals(3, buf.forEachByte(0, length, ByteProcessor.FIND_CRLF)); + assertEquals(6, buf.forEachByte(3, length - 3, ByteProcessor.FIND_NON_CRLF)); + assertEquals(9, buf.forEachByte(6, length - 6, ByteProcessor.FIND_CR)); + assertEquals(11, buf.forEachByte(9, length - 9, ByteProcessor.FIND_NON_CR)); + assertEquals(14, buf.forEachByte(11, length - 11, ByteProcessor.FIND_LF)); + assertEquals(16, buf.forEachByte(14, length - 14, ByteProcessor.FIND_NON_LF)); + assertEquals(19, buf.forEachByte(16, length - 16, ByteProcessor.FIND_NUL)); + assertEquals(21, buf.forEachByte(19, length - 19, ByteProcessor.FIND_NON_NUL)); + assertEquals(24, buf.forEachByte(21, length - 21, ByteProcessor.FIND_LINEAR_WHITESPACE)); + assertEquals(28, buf.forEachByte(24, length - 24, ByteProcessor.FIND_NON_LINEAR_WHITESPACE)); + assertEquals(-1, buf.forEachByte(28, length - 28, ByteProcessor.FIND_LINEAR_WHITESPACE)); + } + + @Test + public void testBackward() { + final ByteBuf buf = releaseLater( + Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); + final int length = buf.readableBytes(); + + assertEquals(27, buf.forEachByteDesc(0, length, ByteProcessor.FIND_LINEAR_WHITESPACE)); + assertEquals(23, buf.forEachByteDesc(0, 28, ByteProcessor.FIND_NON_LINEAR_WHITESPACE)); + assertEquals(20, buf.forEachByteDesc(0, 24, ByteProcessor.FIND_NUL)); + assertEquals(18, buf.forEachByteDesc(0, 21, ByteProcessor.FIND_NON_NUL)); + assertEquals(15, buf.forEachByteDesc(0, 19, ByteProcessor.FIND_LF)); + assertEquals(13, buf.forEachByteDesc(0, 16, ByteProcessor.FIND_NON_LF)); + assertEquals(10, buf.forEachByteDesc(0, 14, ByteProcessor.FIND_CR)); + assertEquals(8, buf.forEachByteDesc(0, 11, ByteProcessor.FIND_NON_CR)); + assertEquals(5, buf.forEachByteDesc(0, 9, ByteProcessor.FIND_CRLF)); + assertEquals(2, buf.forEachByteDesc(0, 6, ByteProcessor.FIND_NON_CRLF)); + assertEquals(-1, buf.forEachByteDesc(0, 3, ByteProcessor.FIND_CRLF)); + } +} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java index f9e9d3370b..7172c00d03 100644 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.haproxy; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufProcessor; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily; +import io.netty.util.ByteProcessor; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.internal.StringUtil; @@ -166,7 +166,7 @@ public final class HAProxyMessage { Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 216+ bytes)"); } int startIdx = header.readerIndex(); - int addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL); + int addressEnd = header.forEachByte(startIdx, 108, ByteProcessor.FIND_NUL); if (addressEnd == -1) { addressLen = 108; } else { @@ -176,7 +176,7 @@ public final class HAProxyMessage { startIdx += 108; - addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL); + addressEnd = header.forEachByte(startIdx, 108, ByteProcessor.FIND_NUL); if (addressEnd == -1) { addressLen = 108; } else { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java index d39df58477..3c8f8248a0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java @@ -16,12 +16,14 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.DefaultHeaders.NameConverter; import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.DefaultTextHeaders.DefaultTextValueTypeConverter; import io.netty.handler.codec.Headers.EntryVisitor; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; +import io.netty.util.ByteProcessor; +import io.netty.util.internal.PlatformDependent; import java.util.Calendar; import java.util.Date; @@ -94,17 +96,34 @@ public class DefaultHttpHeaders extends HttpHeaders { return seq; } - private static void validateValue(AsciiString seq) { - int state = 0; - // Start looping through each of the character - final int start = seq.arrayOffset(); - final int end = start + seq.length(); - final byte[] array = seq.array(); - for (int index = start; index < end; index++) { - state = validateValueChar(seq, state, (char) (array[index] & 0xFF)); + private static final class ValidateValueProcessor implements ByteProcessor { + private final CharSequence seq; + private int state; + + public ValidateValueProcessor(CharSequence seq) { + this.seq = seq; } - if (state != 0) { + @Override + public boolean process(byte value) throws Exception { + state = validateValueChar(state, (char) value, seq); + return true; + } + + public int state() { + return state; + } + } + + private static void validateValue(AsciiString seq) { + ValidateValueProcessor processor = new ValidateValueProcessor(seq); + try { + seq.forEachByte(processor); + } catch (Throwable t) { + PlatformDependent.throwException(t); + } + + if (processor.state() != 0) { throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq); } } @@ -113,7 +132,7 @@ public class DefaultHttpHeaders extends HttpHeaders { int state = 0; // Start looping through each of the character for (int index = 0; index < seq.length(); index++) { - state = validateValueChar(seq, state, seq.charAt(index)); + state = validateValueChar(state, seq.charAt(index), seq); } if (state != 0) { @@ -121,16 +140,16 @@ public class DefaultHttpHeaders extends HttpHeaders { } } - private static int validateValueChar(CharSequence seq, int state, char character) { + private static int validateValueChar(int state, char c, CharSequence seq) { /* * State: * 0: Previous character was neither CR nor LF * 1: The previous character was CR * 2: The previous character was LF */ - if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) { + if ((c & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) { // Check the absolutely prohibited characters. - switch (character) { + switch (c) { case 0x0b: // Vertical tab throw new IllegalArgumentException("a header value contains a prohibited character '\\v': " + seq); case '\f': @@ -141,7 +160,7 @@ public class DefaultHttpHeaders extends HttpHeaders { // Check the CRLF (HT | SP) pattern switch (state) { case 0: - switch (character) { + switch (c) { case '\r': state = 1; break; @@ -151,7 +170,7 @@ public class DefaultHttpHeaders extends HttpHeaders { } break; case 1: - switch (character) { + switch (c) { case '\n': state = 2; break; @@ -160,7 +179,7 @@ public class DefaultHttpHeaders extends HttpHeaders { } break; case 2: - switch (character) { + switch (c) { case '\t': case ' ': state = 0; @@ -176,6 +195,24 @@ public class DefaultHttpHeaders extends HttpHeaders { static class HttpHeadersNameConverter implements NameConverter { protected final boolean validate; + private static final class ValidateNameProcessor implements ByteProcessor { + private final CharSequence seq; + + public ValidateNameProcessor(CharSequence seq) { + this.seq = seq; + } + + @Override + public boolean process(byte value) throws Exception { + // Check to see if the character is not an ASCII character. + if (value < 0) { + throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + seq); + } + validateNameChar(value, seq); + return true; + } + } + HttpHeadersNameConverter(boolean validate) { this.validate = validate; } @@ -194,43 +231,33 @@ public class DefaultHttpHeaders extends HttpHeaders { } private static void validateName(AsciiString name) { - // Go through each characters in the name - final int start = name.arrayOffset(); - final int end = start + name.length(); - final byte[] array = name.array(); - for (int index = start; index < end; index ++) { - byte b = array[index]; - - // Check to see if the character is not an ASCII character - if (b < 0) { - throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name); - } - - // Check for prohibited characters. - validateNameChar(name, b); + try { + name.forEachByte(new ValidateNameProcessor(name)); + } catch (Throwable t) { + PlatformDependent.throwException(t); } } private static void validateName(CharSequence name) { - // Go through each characters in the name + // Go through each characters in the name. for (int index = 0; index < name.length(); index++) { - char character = name.charAt(index); + char c = name.charAt(index); - // Check to see if the character is not an ASCII character - if (character > 127) { + // Check to see if the character is not an ASCII character. + if (c > 127) { throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name); } - // Check for prohibited characters - validateNameChar(name, character); + // Check for prohibited characters. + validateNameChar(c, name); } } - private static void validateNameChar(CharSequence name, int character) { + private static void validateNameChar(int character, CharSequence seq) { if ((character & HIGHEST_INVALID_NAME_CHAR_MASK) == 0 && LOOKUP_TABLE[character] != 0) { throw new IllegalArgumentException( "a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " + - name); + seq); } } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java index a95673bf72..85117c77e4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java @@ -15,7 +15,6 @@ */ package io.netty.handler.codec.http; -import io.netty.util.internal.StringUtil; /** * The default {@link HttpResponse} implementation. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java index b99d55fb77..75c9d5d056 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandler; import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; import java.net.SocketAddress; import java.util.Collection; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java index a79fafebb6..5a6a024606 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * Standard HTTP header names. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java index ce4534337b..f2d1c91a9f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * Standard HTTP header values. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java index c33e8ce2da..a1486b16f5 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java @@ -16,8 +16,9 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; +import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.Headers.EntryVisitor; +import io.netty.util.AsciiString; import io.netty.util.internal.PlatformDependent; import java.text.ParseException; @@ -1283,7 +1284,7 @@ public abstract class HttpHeaders implements Iterable> @Deprecated public static void encodeAscii(CharSequence seq, ByteBuf buf) { if (seq instanceof AsciiString) { - ((AsciiString) seq).copy(0, buf, seq.length()); + ByteBufUtil.copy((AsciiString) seq, 0, buf, seq.length()); } else { encodeAscii0(seq, buf); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java index 6e2e344ec9..559c6f3ecb 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java @@ -17,8 +17,9 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; +import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.TextHeaders.EntryVisitor; +import io.netty.util.AsciiString; import java.util.Map.Entry; @@ -61,7 +62,7 @@ final class HttpHeadersEncoder implements EntryVisitor { } private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) { - value.copy(0, buf, offset, valueLen); + ByteBufUtil.copy(value, 0, buf, offset, valueLen); } private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 54ecd46e79..405b43d8d9 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -16,13 +16,13 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufProcessor; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.TooLongFrameException; +import io.netty.util.ByteProcessor; import io.netty.util.internal.AppendableCharSequence; import java.util.List; @@ -734,7 +734,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder { return result; } - private static class HeaderParser implements ByteBufProcessor { + private static class HeaderParser implements ByteProcessor { private final AppendableCharSequence seq; private final int maxLength; private int size; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java index 5a6c08297d..acf98b3aa4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java @@ -15,12 +15,13 @@ */ package io.netty.handler.codec.http; +import static io.netty.handler.codec.http.HttpConstants.SP; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; +import io.netty.util.ByteProcessor; +import io.netty.util.ByteString; import io.netty.util.CharsetUtil; -import static io.netty.handler.codec.http.HttpConstants.*; - /** * The response code and its description of HTTP or its derived protocols, such as * RTSP and @@ -468,6 +469,85 @@ public class HttpResponseStatus implements Comparable { } } + private static final class HttpStatusLineProcessor implements ByteProcessor { + private static final byte ASCII_SPACE = (byte) ' '; + private final ByteString string; + private int i; + /** + * 0 = New or havn't seen {@link ASCII_SPACE}. + * 1 = Last byte was {@link ASCII_SPACE}. + * 2 = Terminal State. Processed the byte after {@link ASCII_SPACE}, and parsed the status line. + * 3 = Terminal State. There was no byte after {@link ASCII_SPACE} but status has been parsed with what we saw. + */ + private int state; + private HttpResponseStatus status; + + public HttpStatusLineProcessor(ByteString string) { + this.string = string; + } + + @Override + public boolean process(byte value) { + switch (state) { + case 0: + if (value == ASCII_SPACE) { + state = 1; + } + break; + case 1: + parseStatus(i); + state = 2; + return false; + default: + break; + } + ++i; + return true; + } + + private void parseStatus(int codeEnd) { + int code = string.parseAsciiInt(0, codeEnd); + status = valueOf(code); + if (codeEnd < string.length()) { + String actualReason = string.toString(codeEnd + 1, string.length()); + if (!status.reasonPhrase().equals(actualReason)) { + status = new HttpResponseStatus(code, actualReason); + } + } + } + + public HttpResponseStatus status() { + if (state <= 1) { + parseStatus(string.length()); + state = 3; + } + return status; + } + } + + /** + * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are: + *
    + *
  • {@code statusCode} (e.g. 200)
  • + *
  • {@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)
  • + *
+ * + * @throws IllegalArgumentException if the specified status line is malformed + */ + public static HttpResponseStatus parseLine(ByteString line) { + try { + HttpStatusLineProcessor processor = new HttpStatusLineProcessor(line); + line.forEachByte(processor); + HttpResponseStatus status = processor.status(); + if (status == null) { + throw new IllegalArgumentException("unable to get status after parsing input"); + } + return status; + } catch (Exception e) { + throw new IllegalArgumentException("malformed status line: " + line, e); + } + } + private final int code; private final AsciiString codeAsText; private HttpStatusClass codeClass; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java index b5c421c097..620e7df096 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java index 62d9092fb4..9f57e18984 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * The class of HTTP status. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java index d4a612fcbf..3a377e7c5b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java @@ -36,13 +36,13 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufProcessor; import io.netty.handler.codec.CorruptedFrameException; +import io.netty.util.ByteProcessor; /** * Checks UTF8 bytes for validity */ -final class Utf8Validator implements ByteBufProcessor { +final class Utf8Validator implements ByteProcessor { private static final int UTF8_ACCEPT = 0; private static final int UTF8_REJECT = 12; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index 02db5549cc..126d590ab8 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -17,7 +17,6 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -27,6 +26,7 @@ import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.AsciiString; import java.net.URI; import java.nio.ByteBuffer; diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderNames.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderNames.java index 5ab154101b..e42019bc57 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderNames.java +++ b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderNames.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.rtsp; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.util.AsciiString; /** * Standard RTSP header names. diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderValues.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderValues.java index 86551e6963..d0ba393635 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderValues.java +++ b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderValues.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.rtsp; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.util.AsciiString; /** * Standard RTSP header names. diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java index 978e1a5a3f..53c00b087a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java @@ -15,10 +15,10 @@ */ package io.netty.handler.codec.spdy; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.Headers; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; import java.util.Locale; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java index a5a5692541..0b93fa28c4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java @@ -18,8 +18,10 @@ package io.netty.handler.codec.spdy; import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import io.netty.util.CharsetUtil; import java.util.Set; @@ -57,18 +59,20 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { ByteBuf headerBlock = alloc.heapBuffer(); writeLengthField(headerBlock, numHeaders); for (CharSequence name: names) { - byte[] nameBytes = AsciiString.getBytes(name, CharsetUtil.UTF_8); - writeLengthField(headerBlock, nameBytes.length); - headerBlock.writeBytes(nameBytes); + final ByteString nameBytes = new ByteString(name, CharsetUtil.UTF_8); + int length = nameBytes.length(); + writeLengthField(headerBlock, length); + ByteBufUtil.copy(nameBytes, 0, headerBlock, length); int savedIndex = headerBlock.writerIndex(); int valueLength = 0; writeLengthField(headerBlock, valueLength); for (CharSequence value: frame.headers().getAll(name)) { - byte[] valueBytes = AsciiString.getBytes(value, CharsetUtil.UTF_8); - if (valueBytes.length > 0) { - headerBlock.writeBytes(valueBytes); + final ByteString valueBytes = new ByteString(value, CharsetUtil.UTF_8); + length = valueBytes.length(); + if (length > 0) { + ByteBufUtil.copy(valueBytes, 0, headerBlock, length); headerBlock.writeByte(0); - valueLength += valueBytes.length + 1; + valueLength += length + 1; } } if (valueLength != 0) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java index 44a4d7d94b..e82fccef2d 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.spdy; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; /** * Provides the constants for the standard SPDY HTTP header names and commonly diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java index bd9d11b2c3..a3678e0eb8 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.spdy; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * Provides the constants for the header names and the utility methods diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java index dbf20b2a84..7fa415113b 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java @@ -15,7 +15,8 @@ */ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; + import org.junit.Test; import java.util.List; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java index 4d91377550..609a39474d 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java @@ -28,10 +28,10 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromiseAggregator; import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; +import io.netty.util.ByteString; /** * A decorating HTTP2 encoder that will compress data frames according to the {@code content-encoding} header for each @@ -170,11 +170,11 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE * (alternatively, you can throw a {@link Http2Exception} to block unknown encoding). * @throws Http2Exception If the specified encoding is not not supported and warrants an exception */ - protected EmbeddedChannel newContentCompressor(AsciiString contentEncoding) throws Http2Exception { - if (GZIP.equalsIgnoreCase(contentEncoding) || X_GZIP.equalsIgnoreCase(contentEncoding)) { + protected EmbeddedChannel newContentCompressor(ByteString contentEncoding) throws Http2Exception { + if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) { return newCompressionChannel(ZlibWrapper.GZIP); } - if (DEFLATE.equalsIgnoreCase(contentEncoding) || X_DEFLATE.equalsIgnoreCase(contentEncoding)) { + if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) { return newCompressionChannel(ZlibWrapper.ZLIB); } // 'identity' or unsupported @@ -189,7 +189,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE * @return the expected content encoding of the new content. * @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception */ - protected AsciiString getTargetContentEncoding(AsciiString contentEncoding) throws Http2Exception { + protected ByteString getTargetContentEncoding(ByteString contentEncoding) throws Http2Exception { return contentEncoding; } @@ -219,7 +219,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE EmbeddedChannel compressor = stream.getProperty(CompressorHttp2ConnectionEncoder.class); if (compressor == null) { if (!endOfStream) { - AsciiString encoding = headers.get(CONTENT_ENCODING); + ByteString encoding = headers.get(CONTENT_ENCODING); if (encoding == null) { encoding = IDENTITY; } @@ -227,8 +227,8 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE compressor = newContentCompressor(encoding); if (compressor != null) { stream.setProperty(CompressorHttp2ConnectionEncoder.class, compressor); - AsciiString targetContentEncoding = getTargetContentEncoding(encoding); - if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) { + ByteString targetContentEncoding = getTargetContentEncoding(encoding); + if (IDENTITY.equals(targetContentEncoding)) { headers.remove(CONTENT_ENCODING); } else { headers.set(CONTENT_ENCODING, targetContentEncoding); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java index 71f802af37..1b515de968 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java @@ -14,11 +14,63 @@ */ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; +import static io.netty.util.internal.StringUtil.UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET; import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.DefaultBinaryHeaders; +import io.netty.util.AsciiString; +import io.netty.util.ByteProcessor; +import io.netty.util.ByteString; +import io.netty.util.internal.PlatformDependent; public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2Headers { + private static final ByteProcessor HTTP2_ASCII_UPPERCASE_PROCESSOR = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value < 'A' || value > 'Z'; + } + }; + + private static final class Http2AsciiToLowerCaseConverter implements ByteProcessor { + private final byte[] result; + private int i; + + public Http2AsciiToLowerCaseConverter(int length) { + result = new byte[length]; + } + + @Override + public boolean process(byte value) throws Exception { + result[i++] = (value >= 'A' && value <= 'Z') + ? (byte) (value + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET) : value; + return true; + } + + public byte[] result() { + return result; + } + }; + + private static final NameConverter HTTP2_ASCII_TO_LOWER_CONVERTER = new NameConverter() { + @Override + public ByteString convertName(ByteString name) { + if (name instanceof AsciiString) { + return ((AsciiString) name).toLowerCase(); + } + + try { + if (name.forEachByte(HTTP2_ASCII_UPPERCASE_PROCESSOR) == -1) { + return name; + } + + Http2AsciiToLowerCaseConverter converter = new Http2AsciiToLowerCaseConverter(name.length()); + name.forEachByte(converter); + return new ByteString(converter.result(), false); + } catch (Exception e) { + PlatformDependent.throwException(e); + return null; + } + } + }; /** * Creates an instance that will convert all header names to lowercase. @@ -41,95 +93,95 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He * @param forceKeyToLower if @{code false} no header name conversion will be performed */ public DefaultHttp2Headers(boolean forceKeyToLower) { - super(forceKeyToLower); + super(forceKeyToLower ? HTTP2_ASCII_TO_LOWER_CONVERTER : IDENTITY_NAME_CONVERTER); } @Override - public Http2Headers add(AsciiString name, AsciiString value) { + public Http2Headers add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public Http2Headers add(AsciiString name, Iterable values) { + public Http2Headers add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public Http2Headers add(AsciiString name, AsciiString... values) { + public Http2Headers add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object value) { + public Http2Headers addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public Http2Headers addObject(AsciiString name, Iterable values) { + public Http2Headers addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object... values) { + public Http2Headers addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public Http2Headers addBoolean(AsciiString name, boolean value) { + public Http2Headers addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public Http2Headers addChar(AsciiString name, char value) { + public Http2Headers addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public Http2Headers addByte(AsciiString name, byte value) { + public Http2Headers addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public Http2Headers addShort(AsciiString name, short value) { + public Http2Headers addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public Http2Headers addInt(AsciiString name, int value) { + public Http2Headers addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public Http2Headers addLong(AsciiString name, long value) { + public Http2Headers addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public Http2Headers addFloat(AsciiString name, float value) { + public Http2Headers addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public Http2Headers addDouble(AsciiString name, double value) { + public Http2Headers addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public Http2Headers addTimeMillis(AsciiString name, long value) { + public Http2Headers addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -141,91 +193,91 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He } @Override - public Http2Headers set(AsciiString name, AsciiString value) { + public Http2Headers set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public Http2Headers set(AsciiString name, Iterable values) { + public Http2Headers set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public Http2Headers set(AsciiString name, AsciiString... values) { + public Http2Headers set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object value) { + public Http2Headers setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public Http2Headers setObject(AsciiString name, Iterable values) { + public Http2Headers setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object... values) { + public Http2Headers setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public Http2Headers setBoolean(AsciiString name, boolean value) { + public Http2Headers setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public Http2Headers setChar(AsciiString name, char value) { + public Http2Headers setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public Http2Headers setByte(AsciiString name, byte value) { + public Http2Headers setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public Http2Headers setShort(AsciiString name, short value) { + public Http2Headers setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public Http2Headers setInt(AsciiString name, int value) { + public Http2Headers setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public Http2Headers setLong(AsciiString name, long value) { + public Http2Headers setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public Http2Headers setFloat(AsciiString name, float value) { + public Http2Headers setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public Http2Headers setDouble(AsciiString name, double value) { + public Http2Headers setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public Http2Headers setTimeMillis(AsciiString name, long value) { + public Http2Headers setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return this; } @@ -249,57 +301,57 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He } @Override - public Http2Headers method(AsciiString value) { + public Http2Headers method(ByteString value) { set(PseudoHeaderName.METHOD.value(), value); return this; } @Override - public Http2Headers scheme(AsciiString value) { + public Http2Headers scheme(ByteString value) { set(PseudoHeaderName.SCHEME.value(), value); return this; } @Override - public Http2Headers authority(AsciiString value) { + public Http2Headers authority(ByteString value) { set(PseudoHeaderName.AUTHORITY.value(), value); return this; } @Override - public Http2Headers path(AsciiString value) { + public Http2Headers path(ByteString value) { set(PseudoHeaderName.PATH.value(), value); return this; } @Override - public Http2Headers status(AsciiString value) { + public Http2Headers status(ByteString value) { set(PseudoHeaderName.STATUS.value(), value); return this; } @Override - public AsciiString method() { + public ByteString method() { return get(PseudoHeaderName.METHOD.value()); } @Override - public AsciiString scheme() { + public ByteString scheme() { return get(PseudoHeaderName.SCHEME.value()); } @Override - public AsciiString authority() { + public ByteString authority() { return get(PseudoHeaderName.AUTHORITY.value()); } @Override - public AsciiString path() { + public ByteString path() { return get(PseudoHeaderName.PATH.value()); } @Override - public AsciiString status() { + public ByteString status() { return get(PseudoHeaderName.STATUS.value()); } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java index 4f4a3b31db..be207263c2 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java @@ -17,13 +17,13 @@ package io.netty.handler.codec.http2; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_HEADER_SIZE; +import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR; import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; -import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; -import io.netty.handler.codec.AsciiString; +import io.netty.util.ByteString; import java.io.IOException; import java.io.InputStream; @@ -62,7 +62,7 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2Hea HeaderListener listener = new HeaderListener() { @Override public void addHeader(byte[] key, byte[] value, boolean sensitive) { - headers.add(new AsciiString(key, false), new AsciiString(value, false)); + headers.add(new ByteString(key, false), new ByteString(value, false)); } }; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java index 114d5e2ad2..b619606cc5 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java @@ -23,8 +23,8 @@ import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders.EntryVisitor; +import io.netty.util.ByteString; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -67,8 +67,8 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea // Write pseudo headers first as required by the HTTP/2 spec. for (Http2Headers.PseudoHeaderName pseudoHeader : Http2Headers.PseudoHeaderName.values()) { - AsciiString name = pseudoHeader.value(); - AsciiString value = headers.get(name); + ByteString name = pseudoHeader.value(); + ByteString value = headers.get(name); if (value != null) { encodeHeader(name, value, stream); } @@ -76,9 +76,9 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea headers.forEachEntry(new EntryVisitor() { @Override - public boolean visit(Entry entry) throws Exception { - final AsciiString name = entry.getKey(); - final AsciiString value = entry.getValue(); + public boolean visit(Entry entry) throws Exception { + final ByteString name = entry.getKey(); + final ByteString value = entry.getValue(); if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { encodeHeader(name, value, stream); } @@ -108,7 +108,7 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea return this; } - private void encodeHeader(AsciiString key, AsciiString value, OutputStream stream) throws IOException { + private void encodeHeader(ByteString key, ByteString value, OutputStream stream) throws IOException { encoder.encodeHeader(stream, key.array(), value.array(), sensitivityDetector.isSensitive(key, value)); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java index 9cff863d5a..eb8d15c120 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java @@ -28,10 +28,10 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; +import io.netty.util.ByteString; /** * A HTTP2 frame listener that will decompress data frames according to the {@code content-encoding} header for each @@ -159,13 +159,11 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor * (alternatively, you can throw a {@link Http2Exception} to block unknown encoding). * @throws Http2Exception If the specified encoding is not not supported and warrants an exception */ - protected EmbeddedChannel newContentDecompressor(AsciiString contentEncoding) throws Http2Exception { - if (GZIP.equalsIgnoreCase(contentEncoding) || - X_GZIP.equalsIgnoreCase(contentEncoding)) { + protected EmbeddedChannel newContentDecompressor(ByteString contentEncoding) throws Http2Exception { + if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) { return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); } - if (DEFLATE.equalsIgnoreCase(contentEncoding) || - X_DEFLATE.equalsIgnoreCase(contentEncoding)) { + if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) { final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(wrapper)); @@ -182,7 +180,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor * @return the expected content encoding of the new content. * @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception */ - protected AsciiString getTargetContentEncoding(@SuppressWarnings("UnusedParameters") AsciiString contentEncoding) + protected ByteString getTargetContentEncoding(@SuppressWarnings("UnusedParameters") ByteString contentEncoding) throws Http2Exception { return IDENTITY; } @@ -205,7 +203,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor Http2Decompressor decompressor = decompressor(stream); if (decompressor == null && !endOfStream) { // Determine the content encoding. - AsciiString contentEncoding = headers.get(CONTENT_ENCODING); + ByteString contentEncoding = headers.get(CONTENT_ENCODING); if (contentEncoding == null) { contentEncoding = IDENTITY; } @@ -215,8 +213,8 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor stream.setProperty(Http2Decompressor.class, decompressor); // Decode the content and remove or replace the existing headers // so that the message looks like a decoded message. - AsciiString targetContentEncoding = getTargetContentEncoding(contentEncoding); - if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) { + ByteString targetContentEncoding = getTargetContentEncoding(contentEncoding); + if (IDENTITY.equals(targetContentEncoding)) { headers.remove(CONTENT_ENCODING); } else { headers.set(CONTENT_ENCODING, targetContentEncoding); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java index 582c7e346a..e08941ba9f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java @@ -15,9 +15,9 @@ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.EmptyBinaryHeaders; +import io.netty.util.ByteString; public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2Headers { public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers(); @@ -26,91 +26,91 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2 } @Override - public Http2Headers add(AsciiString name, AsciiString value) { + public Http2Headers add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public Http2Headers add(AsciiString name, Iterable values) { + public Http2Headers add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public Http2Headers add(AsciiString name, AsciiString... values) { + public Http2Headers add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object value) { + public Http2Headers addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public Http2Headers addObject(AsciiString name, Iterable values) { + public Http2Headers addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object... values) { + public Http2Headers addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public Http2Headers addBoolean(AsciiString name, boolean value) { + public Http2Headers addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public Http2Headers addChar(AsciiString name, char value) { + public Http2Headers addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public Http2Headers addByte(AsciiString name, byte value) { + public Http2Headers addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public Http2Headers addShort(AsciiString name, short value) { + public Http2Headers addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public Http2Headers addInt(AsciiString name, int value) { + public Http2Headers addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public Http2Headers addLong(AsciiString name, long value) { + public Http2Headers addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public Http2Headers addFloat(AsciiString name, float value) { + public Http2Headers addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public Http2Headers addDouble(AsciiString name, double value) { + public Http2Headers addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public Http2Headers addTimeMillis(AsciiString name, long value) { + public Http2Headers addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -122,91 +122,91 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2 } @Override - public Http2Headers set(AsciiString name, AsciiString value) { + public Http2Headers set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public Http2Headers set(AsciiString name, Iterable values) { + public Http2Headers set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public Http2Headers set(AsciiString name, AsciiString... values) { + public Http2Headers set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object value) { + public Http2Headers setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public Http2Headers setObject(AsciiString name, Iterable values) { + public Http2Headers setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object... values) { + public Http2Headers setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public Http2Headers setBoolean(AsciiString name, boolean value) { + public Http2Headers setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public Http2Headers setChar(AsciiString name, char value) { + public Http2Headers setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public Http2Headers setByte(AsciiString name, byte value) { + public Http2Headers setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public Http2Headers setShort(AsciiString name, short value) { + public Http2Headers setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public Http2Headers setInt(AsciiString name, int value) { + public Http2Headers setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public Http2Headers setLong(AsciiString name, long value) { + public Http2Headers setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public Http2Headers setFloat(AsciiString name, float value) { + public Http2Headers setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public Http2Headers setDouble(AsciiString name, double value) { + public Http2Headers setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public Http2Headers setTimeMillis(AsciiString name, long value) { + public Http2Headers setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return this; } @@ -230,52 +230,52 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2 } @Override - public EmptyHttp2Headers method(AsciiString method) { + public EmptyHttp2Headers method(ByteString method) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers scheme(AsciiString status) { + public EmptyHttp2Headers scheme(ByteString status) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers authority(AsciiString authority) { + public EmptyHttp2Headers authority(ByteString authority) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers path(AsciiString path) { + public EmptyHttp2Headers path(ByteString path) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers status(AsciiString status) { + public EmptyHttp2Headers status(ByteString status) { throw new UnsupportedOperationException(); } @Override - public AsciiString method() { + public ByteString method() { return get(PseudoHeaderName.METHOD.value()); } @Override - public AsciiString scheme() { + public ByteString scheme() { return get(PseudoHeaderName.SCHEME.value()); } @Override - public AsciiString authority() { + public ByteString authority() { return get(PseudoHeaderName.AUTHORITY.value()); } @Override - public AsciiString path() { + public ByteString path() { return get(PseudoHeaderName.PATH.value()); } @Override - public AsciiString status() { + public ByteString status() { return get(PseudoHeaderName.STATUS.value()); } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java index 7adbfc7891..5b30fc713a 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java @@ -15,8 +15,9 @@ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders; +import io.netty.util.ByteString; +import io.netty.util.CharsetUtil; import java.util.HashSet; import java.util.Set; @@ -55,8 +56,8 @@ public interface Http2Headers extends BinaryHeaders { */ STATUS(":status"); - private final AsciiString value; - private static final Set PSEUDO_HEADERS = new HashSet(); + private final ByteString value; + private static final Set PSEUDO_HEADERS = new HashSet(); static { for (PseudoHeaderName pseudoHeader : PseudoHeaderName.values()) { PSEUDO_HEADERS.add(pseudoHeader.value()); @@ -64,10 +65,10 @@ public interface Http2Headers extends BinaryHeaders { } PseudoHeaderName(String value) { - this.value = new AsciiString(value); + this.value = new ByteString(value, CharsetUtil.UTF_8); } - public AsciiString value() { + public ByteString value() { // Return a slice so that the buffer gets its own reader index. return value; } @@ -75,103 +76,103 @@ public interface Http2Headers extends BinaryHeaders { /** * Indicates whether the given header name is a valid HTTP/2 pseudo header. */ - public static boolean isPseudoHeader(AsciiString header) { + public static boolean isPseudoHeader(ByteString header) { return PSEUDO_HEADERS.contains(header); } } @Override - Http2Headers add(AsciiString name, AsciiString value); + Http2Headers add(ByteString name, ByteString value); @Override - Http2Headers add(AsciiString name, Iterable values); + Http2Headers add(ByteString name, Iterable values); @Override - Http2Headers add(AsciiString name, AsciiString... values); + Http2Headers add(ByteString name, ByteString... values); @Override - Http2Headers addObject(AsciiString name, Object value); + Http2Headers addObject(ByteString name, Object value); @Override - Http2Headers addObject(AsciiString name, Iterable values); + Http2Headers addObject(ByteString name, Iterable values); @Override - Http2Headers addObject(AsciiString name, Object... values); + Http2Headers addObject(ByteString name, Object... values); @Override - Http2Headers addBoolean(AsciiString name, boolean value); + Http2Headers addBoolean(ByteString name, boolean value); @Override - Http2Headers addByte(AsciiString name, byte value); + Http2Headers addByte(ByteString name, byte value); @Override - Http2Headers addChar(AsciiString name, char value); + Http2Headers addChar(ByteString name, char value); @Override - Http2Headers addShort(AsciiString name, short value); + Http2Headers addShort(ByteString name, short value); @Override - Http2Headers addInt(AsciiString name, int value); + Http2Headers addInt(ByteString name, int value); @Override - Http2Headers addLong(AsciiString name, long value); + Http2Headers addLong(ByteString name, long value); @Override - Http2Headers addFloat(AsciiString name, float value); + Http2Headers addFloat(ByteString name, float value); @Override - Http2Headers addDouble(AsciiString name, double value); + Http2Headers addDouble(ByteString name, double value); @Override - Http2Headers addTimeMillis(AsciiString name, long value); + Http2Headers addTimeMillis(ByteString name, long value); @Override Http2Headers add(BinaryHeaders headers); @Override - Http2Headers set(AsciiString name, AsciiString value); + Http2Headers set(ByteString name, ByteString value); @Override - Http2Headers set(AsciiString name, Iterable values); + Http2Headers set(ByteString name, Iterable values); @Override - Http2Headers set(AsciiString name, AsciiString... values); + Http2Headers set(ByteString name, ByteString... values); @Override - Http2Headers setObject(AsciiString name, Object value); + Http2Headers setObject(ByteString name, Object value); @Override - Http2Headers setObject(AsciiString name, Iterable values); + Http2Headers setObject(ByteString name, Iterable values); @Override - Http2Headers setObject(AsciiString name, Object... values); + Http2Headers setObject(ByteString name, Object... values); @Override - Http2Headers setBoolean(AsciiString name, boolean value); + Http2Headers setBoolean(ByteString name, boolean value); @Override - Http2Headers setByte(AsciiString name, byte value); + Http2Headers setByte(ByteString name, byte value); @Override - Http2Headers setChar(AsciiString name, char value); + Http2Headers setChar(ByteString name, char value); @Override - Http2Headers setShort(AsciiString name, short value); + Http2Headers setShort(ByteString name, short value); @Override - Http2Headers setInt(AsciiString name, int value); + Http2Headers setInt(ByteString name, int value); @Override - Http2Headers setLong(AsciiString name, long value); + Http2Headers setLong(ByteString name, long value); @Override - Http2Headers setFloat(AsciiString name, float value); + Http2Headers setFloat(ByteString name, float value); @Override - Http2Headers setDouble(AsciiString name, double value); + Http2Headers setDouble(ByteString name, double value); @Override - Http2Headers setTimeMillis(AsciiString name, long value); + Http2Headers setTimeMillis(ByteString name, long value); @Override Http2Headers set(BinaryHeaders headers); @@ -185,50 +186,50 @@ public interface Http2Headers extends BinaryHeaders { /** * Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header */ - Http2Headers method(AsciiString value); + Http2Headers method(ByteString value); /** * Sets the {@link PseudoHeaderName#SCHEME} header if there is no such header */ - Http2Headers scheme(AsciiString value); + Http2Headers scheme(ByteString value); /** * Sets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header */ - Http2Headers authority(AsciiString value); + Http2Headers authority(ByteString value); /** * Sets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header */ - Http2Headers path(AsciiString value); + Http2Headers path(ByteString value); /** * Sets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header */ - Http2Headers status(AsciiString value); + Http2Headers status(ByteString value); /** * Gets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header */ - AsciiString method(); + ByteString method(); /** * Gets the {@link PseudoHeaderName#SCHEME} header or {@code null} if there is no such header */ - AsciiString scheme(); + ByteString scheme(); /** * Gets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header */ - AsciiString authority(); + ByteString authority(); /** * Gets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header */ - AsciiString path(); + ByteString path(); /** * Gets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header */ - AsciiString status(); + ByteString status(); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java index ee8a2904ad..8f816aa679 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; +import io.netty.util.ByteString; /** * Encodes {@link Http2Headers} into HPACK-encoded headers blocks. @@ -47,7 +47,7 @@ public interface Http2HeadersEncoder { * sensitive. * {@code false} otherwise. */ - boolean isSensitive(AsciiString name, AsciiString value); + boolean isSensitive(ByteString name, ByteString value); } /** @@ -64,11 +64,11 @@ public interface Http2HeadersEncoder { Configuration configuration(); /** - * Always return {@code false} for {@link SensitivityDetector#isSensitive(AsciiString, AsciiString)}. + * Always return {@code false} for {@link SensitivityDetector#isSensitive(ByteString, ByteString)}. */ SensitivityDetector NEVER_SENSITIVE = new SensitivityDetector() { @Override - public boolean isSensitive(AsciiString name, AsciiString value) { + public boolean isSensitive(ByteString name, ByteString value) { return false; } }; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java index 4ef3138af4..f7d67b7073 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java @@ -23,15 +23,17 @@ import static io.netty.handler.codec.http2.Http2CodecUtil.writeFrameHeader; import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS; import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpServerUpgradeHandler; +import io.netty.util.ByteString; import io.netty.util.CharsetUtil; +import java.nio.CharBuffer; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -104,8 +106,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade @Override public void upgradeTo(final ChannelHandlerContext ctx, FullHttpRequest upgradeRequest, FullHttpResponse upgradeResponse) { - // Add the HTTP/2 connection handler to the pipeline immediately following the current - // handler. + // Add the HTTP/2 connection handler to the pipeline immediately following the current handler. ctx.pipeline().addAfter(ctx.name(), handlerName, connectionHandler); } @@ -114,7 +115,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade */ private Http2Settings decodeSettingsHeader(ChannelHandlerContext ctx, CharSequence settingsHeader) throws Http2Exception { - ByteBuf header = Unpooled.wrappedBuffer(AsciiString.getBytes(settingsHeader, CharsetUtil.UTF_8)); + ByteBuf header = ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(settingsHeader), CharsetUtil.UTF_8); try { // Decode the SETTINGS payload. ByteBuf payload = Base64.decode(header, URL_SAFE); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java index c535484684..c4c5d4b70b 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java @@ -18,7 +18,6 @@ import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.handler.codec.http2.Http2Exception.streamError; import static io.netty.util.internal.ObjectUtil.checkNotNull; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.TextHeaders.EntryVisitor; import io.netty.handler.codec.http.DefaultFullHttpRequest; @@ -35,6 +34,8 @@ import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import java.net.URI; import java.util.HashMap; @@ -168,7 +169,7 @@ public final class HttpUtil { * @return The HTTP/1.x status * @throws Http2Exception If there is a problem translating from HTTP/2 to HTTP/1.x */ - public static HttpResponseStatus parseStatus(AsciiString status) throws Http2Exception { + public static HttpResponseStatus parseStatus(ByteString status) throws Http2Exception { HttpResponseStatus result; try { result = HttpResponseStatus.parseLine(status); @@ -220,11 +221,10 @@ public final class HttpUtil { */ public static FullHttpRequest toHttpRequest(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders) throws Http2Exception { - // HTTP/2 does not define a way to carry the version identifier that is - // included in the HTTP/1.1 request line. - final AsciiString method = checkNotNull(http2Headers.method(), + // HTTP/2 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line. + final ByteString method = checkNotNull(http2Headers.method(), "method header cannot be null in conversion to HTTP/1.x"); - final AsciiString path = checkNotNull(http2Headers.path(), + final ByteString path = checkNotNull(http2Headers.path(), "path header cannot be null in conversion to HTTP/1.x"); FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method .toString()), path.toString(), validateHttpHeaders); @@ -330,10 +330,10 @@ public final class HttpUtil { /** * Translations from HTTP/2 header name to the HTTP/1.x equivalent. */ - private static final Map - REQUEST_HEADER_TRANSLATIONS = new HashMap(); - private static final Map - RESPONSE_HEADER_TRANSLATIONS = new HashMap(); + private static final Map + REQUEST_HEADER_TRANSLATIONS = new HashMap(); + private static final Map + RESPONSE_HEADER_TRANSLATIONS = new HashMap(); static { RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(), ExtensionHeaderNames.AUTHORITY.text()); @@ -346,7 +346,7 @@ public final class HttpUtil { private final int streamId; private final HttpHeaders output; - private final Map translations; + private final Map translations; /** * Create a new instance @@ -362,10 +362,10 @@ public final class HttpUtil { } @Override - public boolean visit(Entry entry) throws Http2Exception { - final AsciiString name = entry.getKey(); - final AsciiString value = entry.getValue(); - AsciiString translatedName = translations.get(name); + public boolean visit(Entry entry) throws Http2Exception { + final ByteString name = entry.getKey(); + final ByteString value = entry.getValue(); + ByteString translatedName = translations.get(name); if (translatedName != null || !Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { if (translatedName == null) { translatedName = name; @@ -373,11 +373,11 @@ public final class HttpUtil { // http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.3 // All headers that start with ':' are only valid in HTTP/2 context - if (translatedName.isEmpty() || translatedName.charAt(0) == ':') { + if (translatedName.isEmpty() || translatedName.byteAt(0) == ':') { throw streamError(streamId, PROTOCOL_ERROR, "Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", translatedName); } else { - output.add(translatedName, value); + output.add(translatedName.toString(), value); } } return true; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java index 6b6ed9570a..d4e4a0a1d3 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java @@ -16,14 +16,15 @@ package io.netty.handler.codec.http2; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; + import java.util.Map.Entry; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.TextHeaders.EntryVisitor; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.FullHttpMessage; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.util.AsciiString; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; import io.netty.util.internal.PlatformDependent; diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java index 8e28fc2e58..43b917e8c3 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java @@ -40,12 +40,12 @@ import io.netty.channel.ChannelPromise; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http2.Http2TestUtil.FrameAdapter; import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown; import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.concurrent.Future; diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java index 6e00b84297..52dd37bb8f 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java @@ -15,30 +15,41 @@ */ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; -import org.junit.Test; - +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; +import io.netty.util.CharsetUtil; + +import org.junit.Test; public class DefaultHttp2HeadersTest { - private static final AsciiString NAME = new AsciiString("Test"); - private static final AsciiString VALUE = new AsciiString("some value"); + private static final byte[] NAME_BYTES = { 'T', 'E', 's', 'T' }; + private static final byte[] NAME_BYTES_LOWERCASE = { 't', 'e', 's', 't' }; + private static final ByteString NAME_BYTESTRING = new ByteString(NAME_BYTES); + private static final AsciiString NAME_ASCIISTRING = new AsciiString("Test"); + private static final ByteString VALUE = new ByteString("some value", CharsetUtil.UTF_8); @Test public void defaultLowercase() { - Http2Headers headers = new DefaultHttp2Headers().set(NAME, VALUE); - assertEquals(first(headers), NAME.toLowerCase()); + Http2Headers headers = new DefaultHttp2Headers().set(NAME_BYTESTRING, VALUE); + assertArrayEquals(NAME_BYTES_LOWERCASE, first(headers).toByteArray()); + } + + @Test + public void defaultLowercaseAsciiString() { + Http2Headers headers = new DefaultHttp2Headers().set(NAME_ASCIISTRING, VALUE); + assertEquals(NAME_ASCIISTRING.toLowerCase(), first(headers)); } @Test public void caseInsensitive() { - Http2Headers headers = new DefaultHttp2Headers(false).set(NAME, VALUE); - assertEquals(first(headers), NAME); + Http2Headers headers = new DefaultHttp2Headers(false).set(NAME_BYTESTRING, VALUE); + assertArrayEquals(NAME_BYTES, first(headers).toByteArray()); } - private static AsciiString first(Http2Headers headers) { + private static ByteString first(Http2Headers headers) { return headers.names().iterator().next(); } - } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java index 5555cabf4a..7957643f77 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java @@ -17,8 +17,9 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import java.util.List; import java.util.Random; @@ -61,8 +62,8 @@ final class Http2TestUtil { /** * Converts a byte array into an {@link AsciiString}. */ - public static AsciiString as(byte[] value) { - return new AsciiString(value); + public static ByteString as(byte[] value) { + return new ByteString(value); } /** @@ -77,7 +78,7 @@ final class Http2TestUtil { /** * Returns an {@link AsciiString} that wraps a randomly-filled byte array. */ - public static AsciiString randomString() { + public static ByteString randomString() { return as(randomBytes()); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java index ded34c9aa9..807721da6f 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java @@ -40,7 +40,6 @@ import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpMessage; @@ -54,6 +53,7 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http2.Http2TestUtil.FrameAdapter; import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.concurrent.Future; diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java index 5bc5aeccdc..89c93db414 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.stomp; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; /** * The multimap data structure for the STOMP header names and values. It also provides the constants for the standard diff --git a/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java b/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java index 805603ffb3..0531809e81 100644 --- a/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java @@ -20,7 +20,9 @@ package io.netty.handler.codec; import java.util.Map.Entry; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.TextHeaders.EntryVisitor; +import io.netty.util.AsciiString; public final class AsciiHeadersEncoder implements EntryVisitor { @@ -129,7 +131,7 @@ public final class AsciiHeadersEncoder implements EntryVisitor { } private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) { - value.copy(0, buf, offset, valueLen); + ByteBufUtil.copy(value, 0, buf, offset, valueLen); } private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) { diff --git a/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java b/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java index a998decc0a..b220320da3 100644 --- a/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java @@ -16,68 +16,70 @@ package io.netty.handler.codec; +import io.netty.util.ByteString; + /** - * A typical {@code AsciiString} multimap used by protocols that use binary headers (such as HTTP/2) for the - * representation of arbitrary key-value data. {@link AsciiString} is just a wrapper around a byte array but provides + * A typical {@code ByteString} multimap used by protocols that use binary headers (such as HTTP/2) for the + * representation of arbitrary key-value data. {@link ByteString} is just a wrapper around a byte array but provides * some additional utility when handling text data. */ -public interface BinaryHeaders extends Headers { +public interface BinaryHeaders extends Headers { /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ - interface EntryVisitor extends Headers.EntryVisitor { + interface EntryVisitor extends Headers.EntryVisitor { } /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ - interface NameVisitor extends Headers.NameVisitor { + interface NameVisitor extends Headers.NameVisitor { } @Override - BinaryHeaders add(AsciiString name, AsciiString value); + BinaryHeaders add(ByteString name, ByteString value); @Override - BinaryHeaders add(AsciiString name, Iterable values); + BinaryHeaders add(ByteString name, Iterable values); @Override - BinaryHeaders add(AsciiString name, AsciiString... values); + BinaryHeaders add(ByteString name, ByteString... values); @Override - BinaryHeaders addObject(AsciiString name, Object value); + BinaryHeaders addObject(ByteString name, Object value); @Override - BinaryHeaders addObject(AsciiString name, Iterable values); + BinaryHeaders addObject(ByteString name, Iterable values); @Override - BinaryHeaders addObject(AsciiString name, Object... values); + BinaryHeaders addObject(ByteString name, Object... values); @Override - BinaryHeaders addBoolean(AsciiString name, boolean value); + BinaryHeaders addBoolean(ByteString name, boolean value); @Override - BinaryHeaders addByte(AsciiString name, byte value); + BinaryHeaders addByte(ByteString name, byte value); @Override - BinaryHeaders addChar(AsciiString name, char value); + BinaryHeaders addChar(ByteString name, char value); @Override - BinaryHeaders addShort(AsciiString name, short value); + BinaryHeaders addShort(ByteString name, short value); @Override - BinaryHeaders addInt(AsciiString name, int value); + BinaryHeaders addInt(ByteString name, int value); @Override - BinaryHeaders addLong(AsciiString name, long value); + BinaryHeaders addLong(ByteString name, long value); @Override - BinaryHeaders addFloat(AsciiString name, float value); + BinaryHeaders addFloat(ByteString name, float value); @Override - BinaryHeaders addDouble(AsciiString name, double value); + BinaryHeaders addDouble(ByteString name, double value); @Override - BinaryHeaders addTimeMillis(AsciiString name, long value); + BinaryHeaders addTimeMillis(ByteString name, long value); /** * See {@link Headers#add(Headers)} @@ -85,49 +87,49 @@ public interface BinaryHeaders extends Headers { BinaryHeaders add(BinaryHeaders headers); @Override - BinaryHeaders set(AsciiString name, AsciiString value); + BinaryHeaders set(ByteString name, ByteString value); @Override - BinaryHeaders set(AsciiString name, Iterable values); + BinaryHeaders set(ByteString name, Iterable values); @Override - BinaryHeaders set(AsciiString name, AsciiString... values); + BinaryHeaders set(ByteString name, ByteString... values); @Override - BinaryHeaders setObject(AsciiString name, Object value); + BinaryHeaders setObject(ByteString name, Object value); @Override - BinaryHeaders setObject(AsciiString name, Iterable values); + BinaryHeaders setObject(ByteString name, Iterable values); @Override - BinaryHeaders setObject(AsciiString name, Object... values); + BinaryHeaders setObject(ByteString name, Object... values); @Override - BinaryHeaders setBoolean(AsciiString name, boolean value); + BinaryHeaders setBoolean(ByteString name, boolean value); @Override - BinaryHeaders setByte(AsciiString name, byte value); + BinaryHeaders setByte(ByteString name, byte value); @Override - BinaryHeaders setChar(AsciiString name, char value); + BinaryHeaders setChar(ByteString name, char value); @Override - BinaryHeaders setShort(AsciiString name, short value); + BinaryHeaders setShort(ByteString name, short value); @Override - BinaryHeaders setInt(AsciiString name, int value); + BinaryHeaders setInt(ByteString name, int value); @Override - BinaryHeaders setLong(AsciiString name, long value); + BinaryHeaders setLong(ByteString name, long value); @Override - BinaryHeaders setFloat(AsciiString name, float value); + BinaryHeaders setFloat(ByteString name, float value); @Override - BinaryHeaders setDouble(AsciiString name, double value); + BinaryHeaders setDouble(ByteString name, double value); @Override - BinaryHeaders setTimeMillis(AsciiString name, long value); + BinaryHeaders setTimeMillis(ByteString name, long value); /** * See {@link Headers#set(Headers)} diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java index 3c484603ed..ae1b32b507 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java @@ -14,80 +14,74 @@ */ package io.netty.handler.codec; +import io.netty.util.ByteString; +import io.netty.util.CharsetUtil; import io.netty.util.internal.PlatformDependent; +import java.nio.charset.Charset; import java.text.ParseException; -import static io.netty.handler.codec.AsciiString.*; - -public class DefaultBinaryHeaders extends DefaultHeaders implements BinaryHeaders { - private static final HashCodeGenerator ASCII_HASH_CODE_GENERATOR = - new HashCodeGenerator() { +public class DefaultBinaryHeaders extends DefaultHeaders implements BinaryHeaders { + private static final ValueConverter OBJECT_TO_BYTE = new ValueConverter() { + private final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8; @Override - public int generateHashCode(AsciiString name) { - return AsciiString.caseInsensitiveHashCode(name); - } - }; - - private static final ValueConverter OBJECT_TO_ASCII = new ValueConverter() { - @Override - public AsciiString convertObject(Object value) { - if (value instanceof AsciiString) { - return (AsciiString) value; + public ByteString convertObject(Object value) { + if (value instanceof ByteString) { + return (ByteString) value; } if (value instanceof CharSequence) { - return new AsciiString((CharSequence) value); + return new ByteString((CharSequence) value, DEFAULT_CHARSET); } - return new AsciiString(value.toString()); + return new ByteString(value.toString(), DEFAULT_CHARSET); } @Override - public AsciiString convertInt(int value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertInt(int value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertLong(long value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertLong(long value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertDouble(double value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertDouble(double value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertChar(char value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertChar(char value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertBoolean(boolean value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertBoolean(boolean value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertFloat(float value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertFloat(float value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public int convertToInt(AsciiString value) { - return value.parseInt(); + public int convertToInt(ByteString value) { + return value.parseAsciiInt(); } @Override - public long convertToLong(AsciiString value) { - return value.parseLong(); + public long convertToLong(ByteString value) { + return value.parseAsciiLong(); } @Override - public AsciiString convertTimeMillis(long value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertTimeMillis(long value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public long convertToTimeMillis(AsciiString value) { + public long convertToTimeMillis(ByteString value) { try { return HeaderDateFormat.get().parse(value.toString()); } catch (ParseException e) { @@ -97,155 +91,145 @@ public class DefaultBinaryHeaders extends DefaultHeaders implements } @Override - public double convertToDouble(AsciiString value) { - return value.parseDouble(); + public double convertToDouble(ByteString value) { + return value.parseAsciiDouble(); } @Override - public char convertToChar(AsciiString value) { - return value.charAt(0); + public char convertToChar(ByteString value) { + return value.parseChar(); } @Override - public boolean convertToBoolean(AsciiString value) { + public boolean convertToBoolean(ByteString value) { return value.byteAt(0) != 0; } @Override - public float convertToFloat(AsciiString value) { - return value.parseFloat(); + public float convertToFloat(ByteString value) { + return value.parseAsciiFloat(); } @Override - public AsciiString convertShort(short value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertShort(short value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public short convertToShort(AsciiString value) { - return value.parseShort(); + public short convertToShort(ByteString value) { + return value.parseAsciiShort(); } @Override - public AsciiString convertByte(byte value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertByte(byte value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public byte convertToByte(AsciiString value) { + public byte convertToByte(ByteString value) { return value.byteAt(0); } }; - private static final NameConverter ASCII_TO_LOWER_CONVERTER = new NameConverter() { - @Override - public AsciiString convertName(AsciiString name) { - return name.toLowerCase(); - } - }; - - private static final NameConverter ASCII_IDENTITY_CONVERTER = new NameConverter() { - @Override - public AsciiString convertName(AsciiString name) { - return name; - } - }; + private static final HashCodeGenerator JAVA_HASH_CODE_GENERATOR = + new JavaHashCodeGenerator(); + protected static final NameConverter IDENTITY_NAME_CONVERTER = new IdentityNameConverter(); public DefaultBinaryHeaders() { - this(false); + this(IDENTITY_NAME_CONVERTER); } - public DefaultBinaryHeaders(boolean forceKeyToLower) { - super(CASE_INSENSITIVE_ORDER, CASE_INSENSITIVE_ORDER, ASCII_HASH_CODE_GENERATOR, OBJECT_TO_ASCII, - forceKeyToLower ? ASCII_TO_LOWER_CONVERTER : ASCII_IDENTITY_CONVERTER); + public DefaultBinaryHeaders(NameConverter nameConverter) { + super(ByteString.DEFAULT_COMPARATOR, ByteString.DEFAULT_COMPARATOR, + JAVA_HASH_CODE_GENERATOR, OBJECT_TO_BYTE, nameConverter); } @Override - public BinaryHeaders add(AsciiString name, AsciiString value) { + public BinaryHeaders add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public BinaryHeaders add(AsciiString name, Iterable values) { + public BinaryHeaders add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public BinaryHeaders add(AsciiString name, AsciiString... values) { + public BinaryHeaders add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object value) { + public BinaryHeaders addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Iterable values) { + public BinaryHeaders addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object... values) { + public BinaryHeaders addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addBoolean(AsciiString name, boolean value) { + public BinaryHeaders addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public BinaryHeaders addChar(AsciiString name, char value) { + public BinaryHeaders addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public BinaryHeaders addByte(AsciiString name, byte value) { + public BinaryHeaders addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public BinaryHeaders addShort(AsciiString name, short value) { + public BinaryHeaders addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public BinaryHeaders addInt(AsciiString name, int value) { + public BinaryHeaders addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public BinaryHeaders addLong(AsciiString name, long value) { + public BinaryHeaders addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public BinaryHeaders addFloat(AsciiString name, float value) { + public BinaryHeaders addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public BinaryHeaders addDouble(AsciiString name, double value) { + public BinaryHeaders addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public BinaryHeaders addTimeMillis(AsciiString name, long value) { + public BinaryHeaders addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -257,91 +241,91 @@ public class DefaultBinaryHeaders extends DefaultHeaders implements } @Override - public BinaryHeaders set(AsciiString name, AsciiString value) { + public BinaryHeaders set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public BinaryHeaders set(AsciiString name, Iterable values) { + public BinaryHeaders set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public BinaryHeaders set(AsciiString name, AsciiString... values) { + public BinaryHeaders set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object value) { + public BinaryHeaders setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Iterable values) { + public BinaryHeaders setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object... values) { + public BinaryHeaders setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setBoolean(AsciiString name, boolean value) { + public BinaryHeaders setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public BinaryHeaders setChar(AsciiString name, char value) { + public BinaryHeaders setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public BinaryHeaders setByte(AsciiString name, byte value) { + public BinaryHeaders setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public BinaryHeaders setShort(AsciiString name, short value) { + public BinaryHeaders setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public BinaryHeaders setInt(AsciiString name, int value) { + public BinaryHeaders setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public BinaryHeaders setLong(AsciiString name, long value) { + public BinaryHeaders setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public BinaryHeaders setFloat(AsciiString name, float value) { + public BinaryHeaders setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public BinaryHeaders setDouble(AsciiString name, double value) { + public BinaryHeaders setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public BinaryHeaders setTimeMillis(AsciiString name, long value) { + public BinaryHeaders setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return this; } diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java index 60616415a8..37b7ba45a8 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java @@ -66,6 +66,16 @@ public class DefaultHeaders implements Headers { T convertName(T name); } + /** + * Uses the {@link #hashCode()} method to generate the hash code. + */ + public static final class JavaHashCodeGenerator implements HashCodeGenerator { + @Override + public int generateHashCode(T name) { + return name.hashCode(); + } + } + /** * A name converted which does not covert but instead just returns this {@code name} unchanged */ diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java index 53efebcb3c..5f5f309059 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java @@ -16,6 +16,10 @@ package io.netty.handler.codec; +import static io.netty.util.AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER; +import static io.netty.util.AsciiString.CHARSEQUENCE_CASE_SENSITIVE_ORDER; +import static io.netty.util.internal.StringUtil.COMMA; +import io.netty.util.AsciiString; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; @@ -23,9 +27,6 @@ import java.text.ParseException; import java.util.Comparator; import java.util.Iterator; -import static io.netty.handler.codec.AsciiString.*; -import static io.netty.util.internal.StringUtil.COMMA; - public class DefaultTextHeaders extends DefaultConvertibleHeaders implements TextHeaders { private static final HashCodeGenerator CHARSEQUECE_CASE_INSENSITIVE_HASH_CODE_GENERATOR = new HashCodeGenerator() { @@ -36,12 +37,7 @@ public class DefaultTextHeaders extends DefaultConvertibleHeaders CHARSEQUECE_CASE_SENSITIVE_HASH_CODE_GENERATOR = - new HashCodeGenerator() { - @Override - public int generateHashCode(CharSequence name) { - return name.hashCode(); - } - }; + new JavaHashCodeGenerator(); public static class DefaultTextValueTypeConverter implements ValueConverter { @Override diff --git a/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java b/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java index b3e048e58f..98f248b2ac 100644 --- a/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java @@ -16,96 +16,98 @@ package io.netty.handler.codec; -public class EmptyBinaryHeaders extends EmptyHeaders implements BinaryHeaders { +import io.netty.util.ByteString; + +public class EmptyBinaryHeaders extends EmptyHeaders implements BinaryHeaders { protected EmptyBinaryHeaders() { } @Override - public BinaryHeaders add(AsciiString name, AsciiString value) { + public BinaryHeaders add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public BinaryHeaders add(AsciiString name, Iterable values) { + public BinaryHeaders add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public BinaryHeaders add(AsciiString name, AsciiString... values) { + public BinaryHeaders add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object value) { + public BinaryHeaders addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Iterable values) { + public BinaryHeaders addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object... values) { + public BinaryHeaders addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addBoolean(AsciiString name, boolean value) { + public BinaryHeaders addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public BinaryHeaders addChar(AsciiString name, char value) { + public BinaryHeaders addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public BinaryHeaders addByte(AsciiString name, byte value) { + public BinaryHeaders addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public BinaryHeaders addShort(AsciiString name, short value) { + public BinaryHeaders addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public BinaryHeaders addInt(AsciiString name, int value) { + public BinaryHeaders addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public BinaryHeaders addLong(AsciiString name, long value) { + public BinaryHeaders addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public BinaryHeaders addFloat(AsciiString name, float value) { + public BinaryHeaders addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public BinaryHeaders addDouble(AsciiString name, double value) { + public BinaryHeaders addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public BinaryHeaders addTimeMillis(AsciiString name, long value) { + public BinaryHeaders addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -117,91 +119,91 @@ public class EmptyBinaryHeaders extends EmptyHeaders implements Bin } @Override - public BinaryHeaders set(AsciiString name, AsciiString value) { + public BinaryHeaders set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public BinaryHeaders set(AsciiString name, Iterable values) { + public BinaryHeaders set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public BinaryHeaders set(AsciiString name, AsciiString... values) { + public BinaryHeaders set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object value) { + public BinaryHeaders setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Iterable values) { + public BinaryHeaders setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object... values) { + public BinaryHeaders setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setBoolean(AsciiString name, boolean value) { + public BinaryHeaders setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public BinaryHeaders setChar(AsciiString name, char value) { + public BinaryHeaders setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public BinaryHeaders setByte(AsciiString name, byte value) { + public BinaryHeaders setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public BinaryHeaders setShort(AsciiString name, short value) { + public BinaryHeaders setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public BinaryHeaders setInt(AsciiString name, int value) { + public BinaryHeaders setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public BinaryHeaders setLong(AsciiString name, long value) { + public BinaryHeaders setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public BinaryHeaders setFloat(AsciiString name, float value) { + public BinaryHeaders setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public BinaryHeaders setDouble(AsciiString name, double value) { + public BinaryHeaders setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public BinaryHeaders setTimeMillis(AsciiString name, long value) { + public BinaryHeaders setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return this; } diff --git a/codec/src/main/java/io/netty/handler/codec/Headers.java b/codec/src/main/java/io/netty/handler/codec/Headers.java index 643d1b13e8..9718c0c0e2 100644 --- a/codec/src/main/java/io/netty/handler/codec/Headers.java +++ b/codec/src/main/java/io/netty/handler/codec/Headers.java @@ -23,7 +23,7 @@ import java.util.Set; public interface Headers extends Iterable> { /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ interface EntryVisitor { /** @@ -36,7 +36,7 @@ public interface Headers extends Iterable> { } /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ interface NameVisitor { /** @@ -1095,16 +1095,14 @@ public interface Headers extends Iterable> { Iterator> iterator(); /** - * Provide a means of iterating over elements in this map with low GC - * + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. * @param visitor The visitor which will visit each element in this map * @return The last entry before iteration stopped or {@code null} if iteration went past the end */ Map.Entry forEachEntry(EntryVisitor visitor) throws Exception; /** - * Provide a means of iterating over elements in this map with low GC - * + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. * @param visitor The visitor which will visit each element in this map * @return The last key before iteration stopped or {@code null} if iteration went past the end */ diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java index 6d660dba03..24d06dc1ef 100644 --- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java +++ b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java @@ -17,9 +17,9 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufProcessor; import io.netty.buffer.SwappedByteBuf; import io.netty.buffer.Unpooled; +import io.netty.util.ByteProcessor; import io.netty.util.Signal; import io.netty.util.internal.StringUtil; @@ -352,7 +352,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { int ret = buffer.forEachByte(processor); if (ret < 0) { throw REPLAY; @@ -362,7 +362,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { final int writerIndex = buffer.writerIndex(); if (index >= writerIndex) { throw REPLAY; @@ -381,7 +381,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { if (terminated) { return buffer.forEachByteDesc(processor); } else { @@ -391,7 +391,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { if (index + length > buffer.writerIndex()) { throw REPLAY; } diff --git a/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java b/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java index 6d477e07bc..c4bfb43b95 100644 --- a/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java +++ b/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java @@ -17,6 +17,8 @@ package io.netty.handler.codec; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import java.util.HashSet; import java.util.Iterator; @@ -40,11 +42,11 @@ public class DefaultBinaryHeadersTest { byte[] key2 = randomBytes(); byte[] value2 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(key1), as(value1)); h1.set(as(key2), as(value2)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(key1), as(value1)); h2.set(as(key2), as(value2)); @@ -63,13 +65,13 @@ public class DefaultBinaryHeadersTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); @@ -90,13 +92,13 @@ public class DefaultBinaryHeadersTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); @@ -116,18 +118,18 @@ public class DefaultBinaryHeadersTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); - DefaultBinaryHeaders expected = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders expected = new DefaultBinaryHeaders(); expected.set(as(k1), as(v1)); expected.set(as(k2), as(v2)); expected.add(as(k2), as(v3)); @@ -148,14 +150,14 @@ public class DefaultBinaryHeadersTest { byte[] v2 = randomBytes(); byte[] v3 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.add(as(k1), as(v1)); h1.add(as(k1), as(v2)); assertEquals(2, h1.size()); h1.set(as(k1), as(v3)); assertEquals(1, h1.size()); - List list = h1.getAll(as(k1)); + List list = h1.getAll(as(k1)); assertEquals(1, list.size()); assertEquals(as(v3), list.get(0)); } @@ -251,14 +253,14 @@ public class DefaultBinaryHeadersTest { h1.set(as("foo"), as("goo3")); assertEquals(1, h1.size()); - List list = h1.getAll(as("foo")); + List list = h1.getAll(as("foo")); assertEquals(1, list.size()); assertEquals(as("goo3"), list.get(0)); } @Test(expected = NoSuchElementException.class) public void iterateEmptyHeadersShouldThrow() { - Iterator> iterator = new DefaultBinaryHeaders().iterator(); + Iterator> iterator = new DefaultBinaryHeaders().iterator(); assertFalse(iterator.hasNext()); iterator.next(); } @@ -281,7 +283,7 @@ public class DefaultBinaryHeadersTest { } // Now iterate through the headers, removing them from the original set. - for (Map.Entry entry : h1) { + for (Map.Entry entry : h1) { assertTrue(headers.remove(entry.getKey().toString() + ':' + entry.getValue().toString())); } @@ -296,7 +298,7 @@ public class DefaultBinaryHeadersTest { h1.add(as("foo"), as("goo2")); assertEquals(as("goo"), h1.getAndRemove(as("foo"))); assertEquals(0, h1.size()); - List values = h1.getAll(as("foo")); + List values = h1.getAll(as("foo")); assertEquals(0, values.size()); } diff --git a/codec/src/main/java/io/netty/handler/codec/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java similarity index 64% rename from codec/src/main/java/io/netty/handler/codec/AsciiString.java rename to common/src/main/java/io/netty/util/AsciiString.java index 7568c5e8c8..2e3d3d6918 100644 --- a/codec/src/main/java/io/netty/handler/codec/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -13,11 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.handler.codec; +package io.netty.util; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.StringUtil.UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET; +import io.netty.util.ByteProcessor.IndexOfProcessor; import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -32,11 +34,13 @@ import java.util.regex.PatternSyntaxException; * A string which has been encoded into a character encoding whose character always takes a single byte, similarly to * ASCII. It internally keeps its content in a byte array unlike {@link String}, which uses a character array, for * reduced memory footprint and faster data transfer from/to byte-based data structures such as a byte array and - * {@link ByteBuf}. It is often used in conjunction with {@link TextHeaders}. + * {@link ByteBuffer}. It is often used in conjunction with {@link TextHeaders}. */ -public final class AsciiString implements CharSequence, Comparable { +public final class AsciiString extends ByteString implements CharSequence, Comparable { + private static final char MAX_CHAR_VALUE = 255; public static final AsciiString EMPTY_STRING = new AsciiString(""); + public static final Comparator CASE_INSENSITIVE_ORDER = new Comparator() { @Override public int compare(AsciiString o1, AsciiString o2) { @@ -73,8 +77,8 @@ public final class AsciiString implements CharSequence, Comparable if (v1 == v2) { continue; } - int c1 = toLowerCase(v1) & 0xFF; - int c2 = toLowerCase(v2) & 0xFF; + int c1 = toLowerCase(v1); + int c2 = toLowerCase(v2); result = c1 - c2; if (result != 0) { return result; @@ -83,7 +87,7 @@ public final class AsciiString implements CharSequence, Comparable } else if (a1 != null) { byte[] thisValue = a1.value; for (int i = 0; i < minLength; i++) { - int c1 = toLowerCase(thisValue[i]) & 0xFF; + int c1 = toLowerCase(thisValue[i]); int c2 = toLowerCase(o2.charAt(i)); result = c1 - c2; if (result != 0) { @@ -94,7 +98,7 @@ public final class AsciiString implements CharSequence, Comparable byte[] thatValue = a2.value; for (int i = 0; i < minLength; i++) { int c1 = toLowerCase(o1.charAt(i)); - int c2 = toLowerCase(thatValue[i]) & 0xFF; + int c2 = toLowerCase(thatValue[i]); result = c1 - c2; if (result != 0) { return result; @@ -188,7 +192,7 @@ public final class AsciiString implements CharSequence, Comparable int hash = 0; final int end = value.length(); for (int i = 0; i < end; i++) { - hash = hash * 31 ^ value.charAt(i) & 31; + hash = hash * HASH_CODE_PRIME ^ value.charAt(i) & HASH_CODE_PRIME; } return hash; @@ -245,27 +249,7 @@ public final class AsciiString implements CharSequence, Comparable return a.equals(b); } - public static byte[] getBytes(CharSequence v, Charset charset) { - if (v instanceof AsciiString) { - return ((AsciiString) v).array(); - } else if (v instanceof String) { - return ((String) v).getBytes(charset); - } else if (v != null) { - final ByteBuf buf = Unpooled.copiedBuffer(v, charset); - try { - if (buf.hasArray()) { - return buf.array(); - } else { - byte[] result = new byte[buf.readableBytes()]; - buf.readBytes(result); - return result; - } - } finally { - buf.release(); - } - } - return null; - } + private String string; /** * Returns an {@link AsciiString} containing the given character sequence. If the given string is already a @@ -275,259 +259,113 @@ public final class AsciiString implements CharSequence, Comparable return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string); } - private final byte[] value; - private String string; - private int hash; - public AsciiString(byte[] value) { - this(value, true); + super(value); } public AsciiString(byte[] value, boolean copy) { - checkNull(value); - if (copy) { - this.value = value.clone(); - } else { - this.value = value; - } + super(value, copy); } public AsciiString(byte[] value, int start, int length) { - this(value, start, length, true); + super(value, start, length); } public AsciiString(byte[] value, int start, int length, boolean copy) { - checkNull(value); - if (start < 0 || start > value.length - length) { - throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length - + ") <= " + "value.length(" + value.length + ')'); - } + super(value, start, length, copy); + } - if (copy || start != 0 || length != value.length) { - this.value = Arrays.copyOfRange(value, start, start + length); - } else { - this.value = value; - } + public AsciiString(ByteBuffer value) { + super(value); + } + + public AsciiString(ByteBuffer value, int start, int length) { + super(value, start, length); + } + + public AsciiString(ByteBuffer value, int start, int length, boolean copy) { + super(value, start, length, copy); } public AsciiString(char[] value) { - this(checkNull(value), 0, value.length); + this(checkNotNull(value, "value"), 0, value.length); } public AsciiString(char[] value, int start, int length) { - checkNull(value); - if (start < 0 || start > value.length - length) { + super(length); + if (start < 0 || start > checkNotNull(value, "value").length - length) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + ") <= " + "value.length(" + value.length + ')'); } - this.value = new byte[length]; for (int i = 0, j = start; i < length; i++, j++) { this.value[i] = c2b(value[j]); } } public AsciiString(CharSequence value) { - this(checkNull(value), 0, value.length()); + this(checkNotNull(value, "value"), 0, value.length()); } public AsciiString(CharSequence value, int start, int length) { - if (value == null) { - throw new NullPointerException("value"); - } - - if (start < 0 || length < 0 || length > value.length() - start) { + super(length); + if (start < 0 || length < 0 || length > checkNotNull(value, "value").length() - start) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + ") <= " + "value.length(" + value.length() + ')'); } - this.value = new byte[length]; for (int i = 0; i < length; i++) { this.value[i] = c2b(value.charAt(start + i)); } } - public AsciiString(ByteBuffer value) { - this(checkNull(value), value.position(), value.remaining()); - } - - public AsciiString(ByteBuffer value, int start, int length) { - if (value == null) { - throw new NullPointerException("value"); - } - - if (start < 0 || length > value.capacity() - start) { - throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length - + ") <= " + "value.capacity(" + value.capacity() + ')'); - } - - if (value.hasArray()) { - int baseOffset = value.arrayOffset() + start; - this.value = Arrays.copyOfRange(value.array(), baseOffset, baseOffset + length); - } else { - this.value = new byte[length]; - int oldPos = value.position(); - value.get(this.value, 0, this.value.length); - value.position(oldPos); - } - } - - private static T checkNull(T value) { - if (value == null) { - throw new NullPointerException("value"); - } - return value; - } - - @Override - public int length() { - return value.length; - } - @Override public char charAt(int index) { - return (char) (byteAt(index) & 0xFF); - } - - public byte byteAt(int index) { - return value[index]; - } - - public byte[] array() { - return value; - } - - public int arrayOffset() { - return 0; + return b2c(byteAt(index)); } private static byte c2b(char c) { - if (c > 255) { + if (c > MAX_CHAR_VALUE) { return '?'; } return (byte) c; } + private static char b2c(byte b) { + return (char) (b & 0xFF); + } + private static byte toLowerCase(byte b) { if ('A' <= b && b <= 'Z') { - return (byte) (b + 32); + return (byte) (b + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET); } return b; } private static char toLowerCase(char c) { if ('A' <= c && c <= 'Z') { - return (char) (c + 32); + return (char) (c + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET); } return c; } private static byte toUpperCase(byte b) { if ('a' <= b && b <= 'z') { - return (byte) (b - 32); + return (byte) (b - UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET); } return b; } - /** - * Copies a range of characters into a new string. - * - * @param start the offset of the first character. - * @return a new string containing the characters from start to the end of the string. - * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}. - */ - public AsciiString subSequence(int start) { - return subSequence(start, length()); - } - @Override - public AsciiString subSequence(int start, int end) { - if (start < 0 || start > end || end > length()) { - throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length(" - + length() + ')'); - } - - final byte[] value = this.value; - if (start == 0 && end == value.length) { - return this; - } - - if (end == start) { - return EMPTY_STRING; - } - - return new AsciiString(value, start, end - start, false); - } - - @Override - public int hashCode() { - int hash = this.hash; - final byte[] value = this.value; - if (hash != 0 || value.length == 0) { - return hash; - } - - for (int i = 0; i < value.length; ++i) { - hash = hash * 31 ^ value[i] & 31; - } - - return this.hash = hash; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof AsciiString)) { - return false; - } - - if (this == obj) { - return true; - } - - AsciiString that = (AsciiString) obj; - int thisHash = hashCode(); - int thatHash = that.hashCode(); - if (thisHash != thatHash || length() != that.length()) { - return false; - } - - byte[] thisValue = value; - byte[] thatValue = that.value; - int end = thisValue.length; - for (int i = 0, j = 0; i < end; i++, j++) { - if (thisValue[i] != thatValue[j]) { - return false; + public String toString(Charset charset, int start, int end) { + if (start == 0 && end == length()) { + if (string == null) { + string = super.toString(charset, start, end); } - } - - return true; - } - - @Override - @SuppressWarnings("deprecation") - public String toString() { - String string = this.string; - if (string != null) { return string; } - final byte[] value = this.value; - return this.string = new String(value, 0, 0, value.length); - } - - @SuppressWarnings("deprecation") - public String toString(int start, int end) { - final byte[] value = this.value; - if (start == 0 && end == value.length) { - return toString(); - } - - int length = end - start; - if (length == 0) { - return ""; - } - - return new String(value, 0, start, length); + return super.toString(charset, start, end); } /** @@ -543,7 +381,6 @@ public final class AsciiString implements CharSequence, Comparable * positive integer if this string is after the specified string. * @throws NullPointerException if {@code string} is {@code null}. */ - @Override public int compareTo(CharSequence string) { if (this == string) { return 0; @@ -555,7 +392,7 @@ public final class AsciiString implements CharSequence, Comparable int minLength = Math.min(length1, length2); byte[] value = this.value; for (int i = 0, j = 0; j < minLength; i++, j++) { - result = (value[i] & 0xFF) - string.charAt(j); + result = b2c(value[i]) - string.charAt(j); if (result != 0) { return result; } @@ -648,7 +485,6 @@ public final class AsciiString implements CharSequence, Comparable return false; } - final byte[] value = this.value; final int thisLen = value.length; final int thatLen = string.length(); if (thisLen != thatLen) { @@ -656,7 +492,7 @@ public final class AsciiString implements CharSequence, Comparable } for (int i = 0; i < thisLen; i++) { - char c1 = (char) (value[i] & 0xFF); + char c1 = b2c(value[i]); char c2 = string.charAt(i); if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) { return false; @@ -665,24 +501,6 @@ public final class AsciiString implements CharSequence, Comparable return true; } - /** - * Converts this string to a byte array using the ASCII encoding. - * - * @return the byte array encoding of this string. - */ - public byte[] toByteArray() { - return toByteArray(0, length()); - } - - /** - * Converts this string to a byte array using the ASCII encoding. - * - * @return the byte array encoding of this string. - */ - public byte[] toByteArray(int start, int end) { - return Arrays.copyOfRange(value, start, end); - } - /** * Copies the characters in this string to a character array. * @@ -703,85 +521,13 @@ public final class AsciiString implements CharSequence, Comparable return EmptyArrays.EMPTY_CHARS; } - final byte[] value = this.value; final char[] buffer = new char[length]; for (int i = 0, j = start; i < length; i++, j++) { - buffer[i] = (char) (value[j] & 0xFF); + buffer[i] = b2c(value[j]); } return buffer; } - /** - * Copies the content of this string to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. - * - * @param srcIdx the starting offset of characters to copy. - * @param dst the destination byte array. - * @param dstIdx the starting offset in the destination byte array. - * @param length the number of characters to copy. - */ - public void copy(int srcIdx, ByteBuf dst, int dstIdx, int length) { - if (dst == null) { - throw new NullPointerException("dst"); - } - - final byte[] value = this.value; - final int thisLen = value.length; - - if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" - + length + ") <= srcLen(" + thisLen + ')'); - } - - dst.setBytes(dstIdx, value, srcIdx, length); - } - - /** - * Copies the content of this string to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. - * - * @param srcIdx the starting offset of characters to copy. - * @param dst the destination byte array. - * @param length the number of characters to copy. - */ - public void copy(int srcIdx, ByteBuf dst, int length) { - if (dst == null) { - throw new NullPointerException("dst"); - } - - final byte[] value = this.value; - final int thisLen = value.length; - - if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" - + length + ") <= srcLen(" + thisLen + ')'); - } - - dst.writeBytes(value, srcIdx, length); - } - - /** - * Copies the content of this string to a byte array. - * - * @param srcIdx the starting offset of characters to copy. - * @param dst the destination byte array. - * @param dstIdx the starting offset in the destination byte array. - * @param length the number of characters to copy. - */ - public void copy(int srcIdx, byte[] dst, int dstIdx, int length) { - if (dst == null) { - throw new NullPointerException("dst"); - } - - final byte[] value = this.value; - final int thisLen = value.length; - - if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" - + length + ") <= srcLen(" + thisLen + ')'); - } - - System.arraycopy(value, srcIdx, dst, dstIdx, length); - } - /** * Copied the content of this string to a character array. * @@ -795,7 +541,6 @@ public final class AsciiString implements CharSequence, Comparable throw new NullPointerException("dst"); } - final byte[] value = this.value; final int thisLen = value.length; if (srcIdx < 0 || length > thisLen - srcIdx) { @@ -805,44 +550,13 @@ public final class AsciiString implements CharSequence, Comparable final int dstEnd = dstIdx + length; for (int i = srcIdx, j = dstIdx; j < dstEnd; i++, j++) { - dst[j] = (char) (value[i] & 0xFF); + dst[j] = b2c(value[i]); } } - /** - * Searches in this string for the first index of the specified character. The search for the character starts at - * the beginning and moves towards the end of this string. - * - * @param c the character to find. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int indexOf(int c) { - return indexOf(c, 0); - } - - /** - * Searches in this string for the index of the specified character. The search for the character starts at the - * specified offset and moves towards the end of this string. - * - * @param c the character to find. - * @param start the starting offset. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int indexOf(int c, int start) { - final byte[] value = this.value; - final int length = value.length; - if (start < length) { - if (start < 0) { - start = 0; - } - - for (int i = start; i < length; i++) { - if ((value[i] & 0xFF) == c) { - return i; - } - } - } - return -1; + @Override + public AsciiString subSequence(int start, int end) { + return (AsciiString) super.subSequence(start, end); } /** @@ -873,7 +587,6 @@ public final class AsciiString implements CharSequence, Comparable start = 0; } - final byte[] value = this.value; final int thisLen = value.length; int subCount = subString.length(); @@ -884,56 +597,27 @@ public final class AsciiString implements CharSequence, Comparable return -1; } - char firstChar = subString.charAt(0); - for (;;) { - int i = indexOf(firstChar, start); - if (i == -1 || subCount + i > thisLen) { - return -1; // handles subCount > count || start >= count - } - int o1 = i, o2 = 0; - while (++o2 < subCount && (value[++o1] & 0xFF) == subString.charAt(o2)) { - // Intentionally empty - } - if (o2 == subCount) { - return i; - } - start = i + 1; - } - } - - /** - * Searches in this string for the last index of the specified character. The search for the character starts at the - * end and moves towards the beginning of this string. - * - * @param c the character to find. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int lastIndexOf(int c) { - return lastIndexOf(c, length() - 1); - } - - /** - * Searches in this string for the index of the specified character. The search for the character starts at the - * specified offset and moves towards the beginning of this string. - * - * @param c the character to find. - * @param start the starting offset. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int lastIndexOf(int c, int start) { - if (start >= 0) { - final byte[] value = this.value; - final int length = value.length; - if (start >= length) { - start = length - 1; - } - for (int i = start; i >= 0; --i) { - if ((value[i] & 0xFF) == c) { + final char firstChar = subString.charAt(0); + ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar); + try { + for (;;) { + int i = forEachByte(start, thisLen - start, IndexOfVisitor); + if (i == -1 || subCount + i > thisLen) { + return -1; // handles subCount > count || start >= count + } + int o1 = i, o2 = 0; + while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) { + // Intentionally empty + } + if (o2 == subCount) { return i; } + start = i + 1; } + } catch (Exception e) { + PlatformDependent.throwException(e); + return -1; } - return -1; } /** @@ -961,7 +645,6 @@ public final class AsciiString implements CharSequence, Comparable * @throws NullPointerException if {@code subString} is {@code null}. */ public int lastIndexOf(CharSequence subString, int start) { - final byte[] value = this.value; final int thisLen = value.length; final int subCount = subString.length(); @@ -976,32 +659,29 @@ public final class AsciiString implements CharSequence, Comparable start = Math.min(start, thisLen - subCount); // count and subCount are both >= 1 - char firstChar = subString.charAt(0); - for (;;) { - int i = lastIndexOf(firstChar, start); - if (i == -1) { - return -1; + final char firstChar = subString.charAt(0); + ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar); + try { + for (;;) { + int i = forEachByteDesc(start, thisLen - start, IndexOfVisitor); + if (i == -1) { + return -1; + } + int o1 = i, o2 = 0; + while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) { + // Intentionally empty + } + if (o2 == subCount) { + return i; + } + start = i - 1; } - int o1 = i, o2 = 0; - while (++o2 < subCount && (value[++o1] & 0xFF) == subString.charAt(o2)) { - // Intentionally empty - } - if (o2 == subCount) { - return i; - } - start = i - 1; + } catch (Exception e) { + PlatformDependent.throwException(e); + return -1; } } - /** - * Answers if the size of this String is zero. - * - * @return true if the size of this String is zero, false otherwise - */ - public boolean isEmpty() { - return value.length == 0; - } - /** * Compares the specified string to this string and compares the specified range of characters to determine if they * are the same. @@ -1022,7 +702,6 @@ public final class AsciiString implements CharSequence, Comparable return false; } - final byte[] value = this.value; final int thisLen = value.length; if (thisStart < 0 || thisLen - thisStart < length) { return false; @@ -1034,7 +713,7 @@ public final class AsciiString implements CharSequence, Comparable final int thisEnd = thisStart + length; for (int i = thisStart, j = start; i < thisEnd; i++, j++) { - if ((value[i] & 0xFF) != string.charAt(j)) { + if (b2c(value[i]) != string.charAt(j)) { return false; } } @@ -1062,7 +741,6 @@ public final class AsciiString implements CharSequence, Comparable throw new NullPointerException("string"); } - final byte[] value = this.value; final int thisLen = value.length; if (thisStart < 0 || length > thisLen - thisStart) { return false; @@ -1073,7 +751,7 @@ public final class AsciiString implements CharSequence, Comparable int thisEnd = thisStart + length; while (thisStart < thisEnd) { - char c1 = (char) (value[thisStart++] & 0xFF); + char c1 = b2c(value[thisStart++]); char c2 = string.charAt(start++); if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) { return false; @@ -1090,18 +768,29 @@ public final class AsciiString implements CharSequence, Comparable * @return a new string with occurrences of oldChar replaced by newChar. */ public AsciiString replace(char oldChar, char newChar) { - int index = indexOf(oldChar, 0); + if (oldChar > MAX_CHAR_VALUE) { + return this; + } + + final int index; + final byte oldCharByte = c2b(oldChar); + try { + index = forEachByte(new IndexOfProcessor(oldCharByte)); + } catch (Exception e) { + PlatformDependent.throwException(e); + return this; + } if (index == -1) { return this; } - final byte[] value = this.value; final int count = value.length; + final byte newCharByte = c2b(newChar); byte[] buffer = new byte[count]; - for (int i = 0, j = 0; i < value.length; i++, j++) { + for (int i = 0, j = 0; i < count; i++, j++) { byte b = value[i]; - if ((char) (b & 0xFF) == oldChar) { - b = (byte) newChar; + if (b == oldCharByte) { + b = newCharByte; } buffer[j] = b; } @@ -1141,7 +830,6 @@ public final class AsciiString implements CharSequence, Comparable */ public AsciiString toLowerCase() { boolean lowercased = true; - final byte[] value = this.value; int i, j; for (i = 0; i < value.length; ++i) { byte b = value[i]; @@ -1171,7 +859,6 @@ public final class AsciiString implements CharSequence, Comparable * @return a new string containing the uppercase characters equivalent to the characters in this string. */ public AsciiString toUpperCase() { - final byte[] value = this.value; boolean uppercased = true; int i, j; for (i = 0; i < value.length; ++i) { @@ -1202,7 +889,6 @@ public final class AsciiString implements CharSequence, Comparable * @return a new string with characters {@code <= \\u0020} removed from the beginning and the end. */ public AsciiString trim() { - final byte[] value = this.value; int start = 0, last = value.length; int end = last; while (start <= end && value[start] <= ' ') { @@ -1283,7 +969,6 @@ public final class AsciiString implements CharSequence, Comparable final List res = new ArrayList(); int start = 0; - final byte[] value = this.value; final int length = value.length; for (int i = start; i < length; i++) { if (charAt(i) == delim) { @@ -1331,155 +1016,70 @@ public final class AsciiString implements CharSequence, Comparable } public int parseInt() { - return parseInt(0, length(), 10); + return parseAsciiInt(); } public int parseInt(int radix) { - return parseInt(0, length(), radix); + return parseAsciiInt(radix); } public int parseInt(int start, int end) { - return parseInt(start, end, 10); + return parseAsciiInt(start, end); } public int parseInt(int start, int end, int radix) { - if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { - throw new NumberFormatException(); - } - - if (start == end) { - throw new NumberFormatException(); - } - - int i = start; - boolean negative = charAt(i) == '-'; - if (negative && ++i == end) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - - return parseInt(i, end, radix, negative); - } - - private int parseInt(int start, int end, int radix, boolean negative) { - final byte[] value = this.value; - int max = Integer.MIN_VALUE / radix; - int result = 0; - int offset = start; - while (offset < end) { - int digit = Character.digit((char) (value[offset++] & 0xFF), radix); - if (digit == -1) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - if (max > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - int next = result * radix - digit; - if (next > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - result = next; - } - if (!negative) { - result = -result; - if (result < 0) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - } - return result; + return parseAsciiInt(start, end, radix); } public long parseLong() { - return parseLong(0, length(), 10); + return parseAsciiLong(); } public long parseLong(int radix) { - return parseLong(0, length(), radix); + return parseAsciiLong(radix); } public long parseLong(int start, int end) { - return parseLong(start, end, 10); + return parseAsciiLong(start, end); } public long parseLong(int start, int end, int radix) { - if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { - throw new NumberFormatException(); - } - - if (start == end) { - throw new NumberFormatException(); - } - - int i = start; - boolean negative = charAt(i) == '-'; - if (negative && ++i == end) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - - return parseLong(i, end, radix, negative); + return parseAsciiLong(start, end, radix); } - private long parseLong(int start, int end, int radix, boolean negative) { - final byte[] value = this.value; - long max = Long.MIN_VALUE / radix; - long result = 0; - int offset = start; - while (offset < end) { - int digit = Character.digit((char) (value[offset++] & 0xFF), radix); - if (digit == -1) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - if (max > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - long next = result * radix - digit; - if (next > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - result = next; - } - if (!negative) { - result = -result; - if (result < 0) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - } - return result; + public char parseChar(int start) { + return charAt(start); } public short parseShort() { - return parseShort(0, length(), 10); + return parseAsciiShort(); } public short parseShort(int radix) { - return parseShort(0, length(), radix); + return parseAsciiShort(radix); } public short parseShort(int start, int end) { - return parseShort(start, end, 10); + return parseAsciiShort(start, end); } public short parseShort(int start, int end, int radix) { - int intValue = parseInt(start, end, radix); - short result = (short) intValue; - if (result != intValue) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - return result; + return parseAsciiShort(start, end, radix); } public float parseFloat() { - return parseFloat(0, length()); + return parseAsciiFloat(); } public float parseFloat(int start, int end) { - return Float.parseFloat(toString(start, end)); + return parseAsciiFloat(start, end); } public double parseDouble() { - return parseDouble(0, length()); + return parseAsciiDouble(); } public double parseDouble(int start, int end) { - return Double.parseDouble(toString(start, end)); + return parseAsciiDouble(start, end); } } diff --git a/common/src/main/java/io/netty/util/ByteProcessor.java b/common/src/main/java/io/netty/util/ByteProcessor.java new file mode 100644 index 0000000000..2847813c6f --- /dev/null +++ b/common/src/main/java/io/netty/util/ByteProcessor.java @@ -0,0 +1,128 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you 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 io.netty.util; + +/** + * Provides a mechanism to iterate over a collection of bytes. + */ +public interface ByteProcessor { + /** + * A {@link ByteProcessor} which finds the first appearance of a specific byte. + */ + class IndexOfProcessor implements ByteProcessor { + private final byte byteToFind; + + public IndexOfProcessor(byte byteToFind) { + this.byteToFind = byteToFind; + } + + @Override + public boolean process(byte value) { + return value != byteToFind; + } + } + + /** + * A {@link ByteProcessor} which finds the first appearance which is not of a specific byte. + */ + class IndexNotOfProcessor implements ByteProcessor { + private final byte byteToNotFind; + + public IndexNotOfProcessor(byte byteToNotFind) { + this.byteToNotFind = byteToNotFind; + } + + @Override + public boolean process(byte value) { + return value == byteToNotFind; + } + } + + /** + * Aborts on a {@code NUL (0x00)}. + */ + ByteProcessor FIND_NUL = new IndexOfProcessor((byte) 0); + + /** + * Aborts on a non-{@code NUL (0x00)}. + */ + ByteProcessor FIND_NON_NUL = new IndexNotOfProcessor((byte) 0); + + /** + * Aborts on a {@code CR ('\r')}. + */ + ByteProcessor FIND_CR = new IndexOfProcessor((byte) '\r'); + + /** + * Aborts on a non-{@code CR ('\r')}. + */ + ByteProcessor FIND_NON_CR = new IndexNotOfProcessor((byte) '\r'); + + /** + * Aborts on a {@code LF ('\n')}. + */ + ByteProcessor FIND_LF = new IndexOfProcessor((byte) '\n'); + + /** + * Aborts on a non-{@code LF ('\n')}. + */ + ByteProcessor FIND_NON_LF = new IndexNotOfProcessor((byte) '\n'); + + /** + * Aborts on a {@code CR ('\r')} or a {@code LF ('\n')}. + */ + ByteProcessor FIND_CRLF = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value != '\r' && value != '\n'; + } + }; + + /** + * Aborts on a byte which is neither a {@code CR ('\r')} nor a {@code LF ('\n')}. + */ + ByteProcessor FIND_NON_CRLF = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value == '\r' || value == '\n'; + } + }; + + /** + * Aborts on a linear whitespace (a ({@code ' '} or a {@code '\t'}). + */ + ByteProcessor FIND_LINEAR_WHITESPACE = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value != ' ' && value != '\t'; + } + }; + + /** + * Aborts on a byte which is not a linear whitespace (neither {@code ' '} nor {@code '\t'}). + */ + ByteProcessor FIND_NON_LINEAR_WHITESPACE = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value == ' ' || value == '\t'; + } + }; + + /** + * @return {@code true} if the processor wants to continue the loop and handle the next byte in the buffer. + * {@code false} if the processor wants to stop handling bytes and abort the loop. + */ + boolean process(byte value) throws Exception; +} diff --git a/common/src/main/java/io/netty/util/ByteString.java b/common/src/main/java/io/netty/util/ByteString.java new file mode 100644 index 0000000000..258dcca610 --- /dev/null +++ b/common/src/main/java/io/netty/util/ByteString.java @@ -0,0 +1,660 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you 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 io.netty.util; + +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import io.netty.util.internal.StringUtil; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.Arrays; +import java.util.Comparator; + +/** + * The primary use case for this class is to function as an immutable array of bytes. For performance reasons this + * class supports sharing memory with external arrays, and also direct access to the underlying memory via + * {@link #array()}. Care must be taken when directly accessing the memory as that may invalidate assumptions that + * this object is immutable. + */ +public class ByteString { + /** + * A byte wise comparator between two {@link ByteString} objects. + */ + public static final Comparator DEFAULT_COMPARATOR = new Comparator() { + @Override + public int compare(ByteString o1, ByteString o2) { + if (o1 == o2) { + return 0; + } + + int result; + int length1 = o1.length(); + int length2 = o2.length(); + int minLength = Math.min(length1, length2); + for (int i = 0, j = 0; j < minLength; i++, j++) { + result = o1.value[i] - o2.value[j]; + if (result != 0) { + return result; + } + } + + return length1 - length2; + } + }; + public static final ByteString EMPTY_STRING = new ByteString(0); + protected static final int HASH_CODE_PRIME = 31; + + /** + * If this value is modified outside the constructor then call {@link #arrayChanged()}. + */ + protected final byte[] value; + /** + * The hash code is cached after it is first computed. It can be reset with {@link #arrayChanged()}. + */ + private int hash; + + /** + * Used for classes which extend this class and want to initialize the {@link #value} array by them selves. + */ + ByteString(int length) { value = new byte[length]; } + + /** + * Initialize this byte string based upon a byte array. A copy will be made. + */ + public ByteString(byte[] value) { + this(value, true); + } + + /** + * Initialize this byte string based upon a byte array. + * {@code copy} determines if a copy is made or the array is shared. + */ + public ByteString(byte[] value, boolean copy) { + if (copy) { + this.value = checkNotNull(value, "value").clone(); + } else { + this.value = checkNotNull(value, "value"); + } + } + + /** + * Initialize this byte string based upon a range of a byte array. A copy will be made. + */ + public ByteString(byte[] value, int start, int length) { + this(value, start, length, true); + } + + /** + * Construct a new {@link BinaryString} object from a {@code byte[]} array. + * @param copy {@code true} then a copy of the memory will be made. {@code false} the underlying memory + * will be shared. If this shared memory changes then {@link #arrayChanged()} must be called. + */ + public ByteString(byte[] value, int start, int length, boolean copy) { + if (start < 0 || start > checkNotNull(value, "value").length - length) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "value.length(" + value.length + ')'); + } + + if (copy || start != 0 || length != value.length) { + this.value = Arrays.copyOfRange(value, start, start + length); + } else { + this.value = value; + } + } + + /** + * Create a copy of the underlying storage from {@link value}. + * The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes. + */ + public ByteString(ByteBuffer value) { + this.value = getBytes(value); + } + + /** + * Create a copy of the underlying storage from {@link value}. + * The copy will start at {@code start} and copy {@code length} bytes. + */ + public ByteString(ByteBuffer value, int start, int length) { + this.value = getBytes(value, start, length, true); + } + + /** + * Initialize a {@link ByteString} based upon the underlying storage from {@link value}. + * The copy will start at {@code start} and copy {@code length} bytes. + * if {@code copy} is true a copy will be made of the memory. + * if {@code copy} is false the underlying storage will be shared, if possible. + */ + public ByteString(ByteBuffer value, int start, int length, boolean copy) { + this.value = getBytes(value, start, length, copy); + } + + /** + * Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}. + */ + public ByteString(char[] value, Charset charset) { + this.value = getBytes(value, charset); + } + + /** + * Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + public ByteString(char[] value, Charset charset, int start, int length) { + this.value = getBytes(value, charset, start, length); + } + + /** + * Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}. + */ + public ByteString(CharSequence value, Charset charset) { + this.value = getBytes(value, charset); + } + + /** + * Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + public ByteString(CharSequence value, Charset charset, int start, int length) { + this.value = getBytes(value, charset, start, length); + } + + /** + * Create a copy of the underlying storage from {@link value} into a byte array. + * The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes. + */ + private static byte[] getBytes(ByteBuffer value) { + return getBytes(value, value.position(), checkNotNull(value, "value").remaining()); + } + + /** + * Create a copy of the underlying storage from {@link value} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + */ + private static byte[] getBytes(ByteBuffer value, int start, int length) { + return getBytes(value, start, length, true); + } + + /** + * Return an array of the underlying storage from {@link value} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + * if {@code copy} is true a copy will be made of the memory. + * if {@code copy} is false the underlying storage will be shared, if possible. + */ + private static byte[] getBytes(ByteBuffer value, int start, int length, boolean copy) { + if (start < 0 || length > checkNotNull(value, "value").capacity() - start) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "value.capacity(" + value.capacity() + ')'); + } + + if (value.hasArray()) { + if (copy || start != 0 || length != value.capacity()) { + int baseOffset = value.arrayOffset() + start; + return Arrays.copyOfRange(value.array(), baseOffset, baseOffset + length); + } else { + return value.array(); + } + } + + byte[] v = new byte[length]; + int oldPos = value.position(); + value.get(v, 0, length); + value.position(oldPos); + return v; + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + */ + private static byte[] getBytes(char[] value, Charset charset) { + return getBytes(value, charset, 0, checkNotNull(value, "value").length); + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + private static byte[] getBytes(char[] value, Charset charset, int start, int length) { + if (start < 0 || length > checkNotNull(value, "value").length - start) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "length(" + length + ')'); + } + + CharBuffer cbuf = CharBuffer.wrap(value, start, start + length); + CharsetEncoder encoder = CharsetUtil.getEncoder(charset); + ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length)); + encoder.encode(cbuf, nativeBuffer, true); + final int offset = nativeBuffer.arrayOffset(); + return Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position()); + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + */ + private static byte[] getBytes(CharSequence value, Charset charset) { + return getBytes(value, charset, 0, checkNotNull(value, "value").length()); + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + private static byte[] getBytes(CharSequence value, Charset charset, int start, int length) { + if (start < 0 || length > checkNotNull(value, "value").length() - start) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "length(" + value.length() + ')'); + } + + CharBuffer cbuf = CharBuffer.wrap(value, start, start + length); + CharsetEncoder encoder = CharsetUtil.getEncoder(charset); + ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length)); + encoder.encode(cbuf, nativeBuffer, true); + final int offset = nativeBuffer.arrayOffset(); + return Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position()); + } + + /** + * Iterates over the readable bytes of this buffer with the specified {@code processor} in ascending order. + * + * @return {@code -1} if the processor iterated to or beyond the end of the readable bytes. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. + */ + public final int forEachByte(ByteProcessor visitor) throws Exception { + return forEachByte(0, value.length, visitor); + } + + /** + * Iterates over the specified area of this buffer with the specified {@code processor} in ascending order. + * (i.e. {@code index}, {@code (index + 1)}, .. {@code (index + length - 1)}). + * + * @return {@code -1} if the processor iterated to or beyond the end of the specified area. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. + */ + public final int forEachByte(int index, int length, ByteProcessor visitor) throws Exception { + if (index < 0 || length > value.length - index) { + throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length + + ") <= " + "length(" + value.length + ')'); + } + + for (int i = index; i < length; ++i) { + if (!visitor.process(value[i])) { + return i; + } + } + return -1; + } + + /** + * Iterates over the readable bytes of this buffer with the specified {@code processor} in descending order. + * + * @return {@code -1} if the processor iterated to or beyond the beginning of the readable bytes. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. + */ + public final int forEachByteDesc(ByteProcessor visitor) throws Exception { + return forEachByteDesc(0, length(), visitor); + } + + /** + * Iterates over the specified area of this buffer with the specified {@code processor} in descending order. + * (i.e. {@code (index + length - 1)}, {@code (index + length - 2)}, ... {@code index}). + * + * @return {@code -1} if the processor iterated to or beyond the beginning of the specified area. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. + */ + public final int forEachByteDesc(int index, int length, ByteProcessor visitor) throws Exception { + if (index < 0 || length > value.length - index) { + throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length + + ") <= " + "length(" + value.length + ')'); + } + + for (int i = index + length - 1; i >= index; --i) { + if (!visitor.process(value[i])) { + return i; + } + } + return -1; + } + + public final byte byteAt(int index) { + return value[index]; + } + + public final boolean isEmpty() { + return value.length == 0; + } + + public final int length() { + return value.length; + } + + /** + * During normal use cases the {@link ByteString} should be immutable, but if the underlying array is shared, + * and changes then this needs to be called. + */ + public final void arrayChanged() { + hash = 0; + } + + /** + * This gives direct access to the underlying storage array. + * The {@link #toByteArray()} should be preferred over this method. + * If the return value is changed then {@link #arrayChanged()} must be called. + */ + public final byte[] array() { + return value; + } + + /** + * Converts this string to a byte array. + */ + public final byte[] toByteArray() { + return toByteArray(0, length()); + } + + /** + * Converts a subset of this string to a byte array. + * The subset is defined by the range [{@code start}, {@code end}). + */ + public final byte[] toByteArray(int start, int end) { + return Arrays.copyOfRange(value, start, end); + } + + /** + * Copies the content of this string to a byte array. + * + * @param srcIdx the starting offset of characters to copy. + * @param dst the destination byte array. + * @param dstIdx the starting offset in the destination byte array. + * @param length the number of characters to copy. + */ + public final void copy(int srcIdx, byte[] dst, int dstIdx, int length) { + final int thisLen = value.length; + + if (srcIdx < 0 || length > thisLen - srcIdx) { + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); + } + + System.arraycopy(value, srcIdx, checkNotNull(dst, "dst"), dstIdx, length); + } + + @Override + public int hashCode() { + if (hash != 0) { + return hash; + } + + for (int i = 0; i < value.length; ++i) { + hash = hash * HASH_CODE_PRIME ^ value[i] & HASH_CODE_PRIME; + } + + return hash; + } + + /** + * Copies a range of characters into a new string. + * + * @param start the offset of the first character. + * @return a new string containing the characters from start to the end of the string. + * @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}. + */ + public final ByteString subSequence(int start) { + return subSequence(start, length()); + } + + public ByteString subSequence(int start, int end) { + if (start < 0 || start > end || end > length()) { + throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length(" + + length() + ')'); + } + + if (start == 0 && end == value.length) { + return this; + } + + if (end == start) { + return EMPTY_STRING; + } + + return new ByteString(value, start, end - start, false); + } + + public final int parseAsciiInt() { + return parseAsciiInt(0, length(), 10); + } + + public final int parseAsciiInt(int radix) { + return parseAsciiInt(0, length(), radix); + } + + public final int parseAsciiInt(int start, int end) { + return parseAsciiInt(start, end, 10); + } + + public final int parseAsciiInt(int start, int end, int radix) { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { + throw new NumberFormatException(); + } + + if (start == end) { + throw new NumberFormatException(); + } + + int i = start; + boolean negative = byteAt(i) == '-'; + if (negative && ++i == end) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + + return parseAsciiInt(i, end, radix, negative); + } + + private int parseAsciiInt(int start, int end, int radix, boolean negative) { + int max = Integer.MIN_VALUE / radix; + int result = 0; + int offset = start; + while (offset < end) { + int digit = Character.digit((char) (value[offset++] & 0xFF), radix); + if (digit == -1) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + if (max > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + int next = result * radix - digit; + if (next > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + result = next; + } + if (!negative) { + result = -result; + if (result < 0) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + } + return result; + } + + public final long parseAsciiLong() { + return parseAsciiLong(0, length(), 10); + } + + public final long parseAsciiLong(int radix) { + return parseAsciiLong(0, length(), radix); + } + + public final long parseAsciiLong(int start, int end) { + return parseAsciiLong(start, end, 10); + } + + public final long parseAsciiLong(int start, int end, int radix) { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { + throw new NumberFormatException(); + } + + if (start == end) { + throw new NumberFormatException(); + } + + int i = start; + boolean negative = byteAt(i) == '-'; + if (negative && ++i == end) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + + return parseAsciiLong(i, end, radix, negative); + } + + private long parseAsciiLong(int start, int end, int radix, boolean negative) { + long max = Long.MIN_VALUE / radix; + long result = 0; + int offset = start; + while (offset < end) { + int digit = Character.digit((char) (value[offset++] & 0xFF), radix); + if (digit == -1) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + if (max > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + long next = result * radix - digit; + if (next > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + result = next; + } + if (!negative) { + result = -result; + if (result < 0) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + } + return result; + } + + public final char parseChar() { + return parseChar(0); + } + + public char parseChar(int start) { + if (start + 1 >= value.length) { + throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " + + start + " would go out of bounds."); + } + return (char) (((value[start] & 0xFF) << 8) | (value[start + 1] & 0xFF)); + } + + public final short parseAsciiShort() { + return parseAsciiShort(0, length(), 10); + } + + public final short parseAsciiShort(int radix) { + return parseAsciiShort(0, length(), radix); + } + + public final short parseAsciiShort(int start, int end) { + return parseAsciiShort(start, end, 10); + } + + public final short parseAsciiShort(int start, int end, int radix) { + int intValue = parseAsciiInt(start, end, radix); + short result = (short) intValue; + if (result != intValue) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + return result; + } + + public final float parseAsciiFloat() { + return parseAsciiFloat(0, length()); + } + + public final float parseAsciiFloat(int start, int end) { + return Float.parseFloat(toString(start, end)); + } + + public final double parseAsciiDouble() { + return parseAsciiDouble(0, length()); + } + + public final double parseAsciiDouble(int start, int end) { + return Double.parseDouble(toString(start, end)); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ByteString)) { + return false; + } + + if (this == obj) { + return true; + } + + ByteString that = (ByteString) obj; + if (length() != that.length() || hashCode() != that.hashCode()) { + return false; + } + + final int end = value.length; + for (int i = 0, j = 0; i < end; i++, j++) { + if (value[i] != that.value[j]) { + return false; + } + } + + return true; + } + + /** + * Translates the entire byte string to a {@link String}. + * @see {@link #toString(int, int)} + */ + @Override + public String toString() { + return toString(0, length()); + } + + /** + * Translates the entire byte string to a {@link String} using the {@code charset} encoding. + * @see {@link #toString(Charset, int, int)} + */ + public final String toString(Charset charset) { + return toString(charset, 0, length()); + } + + /** + * Translates the [{@code start}, {@code end}) range of this byte string to a {@link String}. + * @see {@link #toString(Charset, int, int)} + */ + public final String toString(int start, int end) { + return toString(CharsetUtil.ISO_8859_1, start, end); + } + + /** + * Translates the [{@code start}, {@code end}) range of this byte string to a {@link String} + * using the {@code charset} encoding. + */ + public String toString(Charset charset, int start, int end) { + int length = end - start; + if (length == 0) { + return StringUtil.EMPTY_STRING; + } + + return new String(value, start, length, charset); + } +} diff --git a/common/src/main/java/io/netty/util/internal/StringUtil.java b/common/src/main/java/io/netty/util/internal/StringUtil.java index ca69b8f707..3d743d6876 100644 --- a/common/src/main/java/io/netty/util/internal/StringUtil.java +++ b/common/src/main/java/io/netty/util/internal/StringUtil.java @@ -34,6 +34,7 @@ public final class StringUtil { public static final char LINE_FEED = '\n'; public static final char CARRIAGE_RETURN = '\r'; public static final String EMPTY_STRING = ""; + public static final byte UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET = (int) 'a' - (int) 'A'; private static final String[] BYTE2HEX_PAD = new String[256]; private static final String[] BYTE2HEX_NOPAD = new String[256]; /** diff --git a/codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java b/common/src/test/java/io/netty/util/AsciiStringTest.java similarity index 69% rename from codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java rename to common/src/test/java/io/netty/util/AsciiStringTest.java index 0d459bc1fc..f21b046245 100644 --- a/codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java +++ b/common/src/test/java/io/netty/util/AsciiStringTest.java @@ -13,15 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.handler.codec; +package io.netty.util; import static org.junit.Assert.assertArrayEquals; -import io.netty.util.CharsetUtil; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.nio.charset.Charset; -import java.nio.charset.CharsetEncoder; import org.junit.Assert; import org.junit.Test; @@ -41,8 +37,8 @@ public class AsciiStringTest { final Charset[] charsets = CharsetUtil.values(); for (int i = 0; i < charsets.length; ++i) { final Charset charset = charsets[i]; - byte[] expected = getBytesWithEncoder(bString, charset); - byte[] actual = AsciiString.getBytes(b, charset); + byte[] expected = bString.getBytes(charset); + byte[] actual = new ByteString(b, charset).toByteArray(); assertArrayEquals("failure for " + charset, expected, actual); } } @@ -58,7 +54,7 @@ public class AsciiStringTest { for (int i = 0; i < charsets.length; ++i) { final Charset charset = charsets[i]; byte[] expected = bString.getBytes(charset); - byte[] actual = AsciiString.getBytes(bString, charset); + byte[] actual = new ByteString(bString, charset).toByteArray(); assertArrayEquals("failure for " + charset, expected, actual); } } @@ -72,12 +68,8 @@ public class AsciiStringTest { final String bString = b.toString(); // The AsciiString class actually limits the Charset to ISO_8859_1 byte[] expected = bString.getBytes(CharsetUtil.ISO_8859_1); - final Charset[] charsets = CharsetUtil.values(); - for (int i = 0; i < charsets.length; ++i) { - final Charset charset = charsets[i]; - byte[] actual = AsciiString.getBytes(new AsciiString(bString), charset); - assertArrayEquals("failure for " + charset, expected, actual); - } + byte[] actual = new AsciiString(bString).toByteArray(); + assertArrayEquals(expected, actual); } @Test @@ -86,11 +78,4 @@ public class AsciiStringTest { AsciiString ascii = new AsciiString(string.toCharArray()); Assert.assertEquals(string, ascii.toString()); } - - private static byte[] getBytesWithEncoder(CharSequence value, Charset charset) { - final CharsetEncoder encoder = CharsetUtil.getEncoder(charset); - final ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * value.length())); - encoder.encode(CharBuffer.wrap(value), nativeBuffer, true); - return nativeBuffer.array(); - } } diff --git a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java index 2999f302e9..e56b3b1f25 100644 --- a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java +++ b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java @@ -19,12 +19,11 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpHeaderUtil; import io.netty.handler.codec.http.HttpRequest; - +import io.netty.util.AsciiString; import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpVersion.*; diff --git a/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java b/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java index 5850b7c0b6..61fff9e4b9 100644 --- a/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java +++ b/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java @@ -22,7 +22,6 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.logging.LogLevel.INFO; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.HttpServerUpgradeHandler; import io.netty.handler.codec.http2.DefaultHttp2Connection; import io.netty.handler.codec.http2.DefaultHttp2FrameReader; @@ -39,6 +38,7 @@ import io.netty.handler.codec.http2.Http2FrameWriter; import io.netty.handler.codec.http2.Http2Headers; import io.netty.handler.codec.http2.Http2InboundFrameLogger; import io.netty.handler.codec.http2.Http2OutboundFrameLogger; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; /** diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java index 527d238c3c..2008f3b1e0 100644 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java +++ b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java @@ -20,7 +20,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; @@ -31,6 +30,7 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import java.net.InetSocketAddress; diff --git a/microbench/src/test/java/io/netty/microbench/http2/Http2FrameWriterBenchmark.java b/microbench/src/test/java/io/netty/microbench/http2/Http2FrameWriterBenchmark.java index 576161c673..a4ac332a76 100644 --- a/microbench/src/test/java/io/netty/microbench/http2/Http2FrameWriterBenchmark.java +++ b/microbench/src/test/java/io/netty/microbench/http2/Http2FrameWriterBenchmark.java @@ -43,7 +43,6 @@ import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.oio.OioServerSocketChannel; import io.netty.channel.socket.oio.OioSocketChannel; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http2.DefaultHttp2Connection; import io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder; import io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder; @@ -61,6 +60,7 @@ import io.netty.handler.codec.http2.Http2LocalFlowController; import io.netty.handler.codec.http2.Http2RemoteFlowController; import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext; import io.netty.microbench.util.AbstractSharedExecutorMicrobenchmark; +import io.netty.util.AsciiString; import io.netty.util.concurrent.Future; import java.net.InetSocketAddress;