ByteString introduced as AsciiString super class

Motivation:
The usage and code within AsciiString has exceeded the original design scope for this class. Its usage as a binary string is confusing and on the verge of violating interface assumptions in some spots.

Modifications:
- ByteString will be created as a base class to AsciiString. All of the generic byte handling processing will live in ByteString and all the special character encoding will live in AsciiString.

Results:
The AsciiString interface will be clarified. Users of AsciiString can now be clear of the limitations the class imposes while users of the ByteString class don't have to live with those limitations.
This commit is contained in:
Scott Mitchell 2015-03-31 16:23:52 -07:00
parent f58fd3f8c7
commit b823bfa950
74 changed files with 1829 additions and 1179 deletions

View File

@ -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");

View File

@ -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);
}

View File

@ -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.
*
* <h3>Mark and reset</h3>
@ -1605,26 +1606,26 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
* 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<ByteBuf> {
*
*
* @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

View File

@ -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;
}

View File

@ -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<ThreadLocalUnsafeDirectByteBuf> RECYCLER =

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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 {

View File

@ -15,9 +15,11 @@
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.DefaultTextHeaders;
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;
@ -81,17 +83,34 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
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);
}
}
@ -100,7 +119,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
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) {
@ -108,16 +127,16 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
}
}
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 0x0: // NULL
throw new IllegalArgumentException("a header value contains a prohibited character '\0': " + seq);
case 0x0b: // Vertical tab
@ -130,7 +149,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
// Check the CRLF (HT | SP) pattern
switch (state) {
case 0:
switch (character) {
switch (c) {
case '\r':
state = 1;
break;
@ -140,7 +159,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
}
break;
case 1:
switch (character) {
switch (c) {
case '\n':
state = 2;
break;
@ -149,7 +168,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
}
break;
case 2:
switch (character) {
switch (c) {
case '\t':
case ' ':
state = 0;
@ -165,6 +184,24 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
static class HttpHeadersNameConverter implements NameConverter<CharSequence> {
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;
}
@ -183,43 +220,33 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
}
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);
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);
}
}
}

View File

@ -16,7 +16,7 @@ package io.netty.handler.codec.http;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.AsciiString;
import io.netty.util.AsciiString;
import java.util.Collection;
import java.util.LinkedHashSet;

View File

@ -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.

View File

@ -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.

View File

@ -17,7 +17,6 @@ package io.netty.handler.codec.http;
import io.netty.handler.codec.TextHeaders;
/**
* Provides the constants for the standard HTTP header names and values and
* commonly used utility methods that accesses an {@link HttpMessage}.

View File

@ -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) {

View File

@ -15,7 +15,7 @@
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.AsciiString;
import io.netty.util.AsciiString;
import java.util.HashMap;
import java.util.Map;

View File

@ -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;

View File

@ -15,12 +15,13 @@
*/
package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpConstants.CR;
import static io.netty.handler.codec.http.HttpConstants.LF;
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.CharsetUtil;
import static io.netty.handler.codec.http.HttpConstants.*;
/**
* Encodes an {@link HttpRequest} or an {@link HttpContent} into
* a {@link ByteBuf}.
@ -38,7 +39,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
@Override
protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception {
AsciiString method = request.method().name();
buf.writeBytes(method.array(), method.arrayOffset(), method.length());
buf.writeBytes(method.array());
buf.writeByte(SP);
// Add / as absolute path if no is present.
@ -75,7 +76,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
buf.writeByte(SP);
AsciiString version = request.protocolVersion().text();
buf.writeBytes(version.array(), version.arrayOffset(), version.length());
buf.writeBytes(version.array());
buf.writeBytes(CRLF);
}
}

View File

@ -15,10 +15,11 @@
*/
package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpConstants.CR;
import static io.netty.handler.codec.http.HttpConstants.LF;
import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString;
import static io.netty.handler.codec.http.HttpConstants.*;
import io.netty.util.AsciiString;
/**
* Encodes an {@link HttpResponse} or an {@link HttpContent} into
@ -35,15 +36,15 @@ public class HttpResponseEncoder extends HttpObjectEncoder<HttpResponse> {
@Override
protected void encodeInitialLine(ByteBuf buf, HttpResponse response) throws Exception {
AsciiString version = response.protocolVersion().text();
buf.writeBytes(version.array(), version.arrayOffset(), version.length());
buf.writeBytes(version.array());
buf.writeByte(SP);
AsciiString code = response.status().codeAsText();
buf.writeBytes(code.array(), code.arrayOffset(), code.length());
buf.writeBytes(code.array());
buf.writeByte(SP);
AsciiString reasonPhrase = response.status().reasonPhrase();
buf.writeBytes(reasonPhrase.array(), reasonPhrase.arrayOffset(), reasonPhrase.length());
buf.writeBytes(reasonPhrase.array());
buf.writeBytes(CRLF);
}
}

View File

@ -15,7 +15,9 @@
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.AsciiString;
import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.ByteString;
/**
* The response code and its description of HTTP or its derived protocols, such as
@ -464,6 +466,85 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
}
}
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:
* <ul>
* <li>{@code statusCode} (e.g. 200)</li>
* <li>{@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)</li>
* </ul>
*
* @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;

View File

@ -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;

View File

@ -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.

View File

@ -15,7 +15,7 @@
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.AsciiString;
import io.netty.util.AsciiString;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

View File

@ -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;

View File

@ -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;

View File

@ -15,12 +15,12 @@
*/
package io.netty.handler.codec.http.websocketx;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
@ -28,11 +28,10 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.AsciiString;
import java.util.regex.Pattern;
import static io.netty.handler.codec.http.HttpVersion.*;
/**
* <p>
* Performs server side opening and closing handshakes for web socket specification version <a

View File

@ -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.

View File

@ -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.

View File

@ -15,14 +15,15 @@
*/
package io.netty.handler.codec.rtsp;
import static io.netty.handler.codec.http.HttpConstants.CR;
import static io.netty.handler.codec.http.HttpConstants.LF;
import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
import static io.netty.handler.codec.http.HttpConstants.*;
/**
* Encodes an RTSP request represented in {@link FullHttpRequest} into
* a {@link ByteBuf}.
@ -39,14 +40,14 @@ public class RtspRequestEncoder extends RtspObjectEncoder<HttpRequest> {
@Override
protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception {
AsciiString method = request.method().name();
buf.writeBytes(method.array(), method.arrayOffset(), method.length());
buf.writeBytes(method.array());
buf.writeByte(SP);
buf.writeBytes(request.uri().getBytes(CharsetUtil.UTF_8));
buf.writeByte(SP);
AsciiString version = request.protocolVersion().text();
buf.writeBytes(version.array(), version.arrayOffset(), version.length());
buf.writeBytes(version.array());
buf.writeBytes(CRLF);
}
}

View File

@ -15,12 +15,13 @@
*/
package io.netty.handler.codec.rtsp;
import static io.netty.handler.codec.http.HttpConstants.CR;
import static io.netty.handler.codec.http.HttpConstants.LF;
import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponse;
import static io.netty.handler.codec.http.HttpConstants.*;
import io.netty.util.AsciiString;
/**
* Encodes an RTSP response represented in {@link FullHttpResponse} into
@ -37,15 +38,15 @@ public class RtspResponseEncoder extends RtspObjectEncoder<HttpResponse> {
@Override
protected void encodeInitialLine(ByteBuf buf, HttpResponse response) throws Exception {
AsciiString version = response.protocolVersion().text();
buf.writeBytes(version.array(), version.arrayOffset(), version.length());
buf.writeBytes(version.array());
buf.writeByte(SP);
AsciiString code = response.status().codeAsText();
buf.writeBytes(code.array(), code.arrayOffset(), code.length());
buf.writeBytes(code.array());
buf.writeByte(SP);
AsciiString reasonPhrase = response.status().reasonPhrase();
buf.writeBytes(reasonPhrase.array(), reasonPhrase.arrayOffset(), reasonPhrase.length());
buf.writeBytes(reasonPhrase.array());
buf.writeBytes(CRLF);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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 used by the {@link SpdyHttpDecoder} and {@link SpdyHttpEncoder}.

View File

@ -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;

View File

@ -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);

View File

@ -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<ByteString> HTTP2_ASCII_TO_LOWER_CONVERTER = new NameConverter<ByteString>() {
@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<? extends AsciiString> values) {
public Http2Headers add(ByteString name, Iterable<? extends ByteString> 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<? extends AsciiString> values) {
public Http2Headers set(ByteString name, Iterable<? extends ByteString> 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());
}
}

View File

@ -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));
}
};

View File

@ -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<AsciiString, AsciiString> entry) throws Exception {
final AsciiString name = entry.getKey();
final AsciiString value = entry.getValue();
public boolean visit(Entry<ByteString, ByteString> 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));
}

View File

@ -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);

View File

@ -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<? extends AsciiString> values) {
public Http2Headers add(ByteString name, Iterable<? extends ByteString> 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<? extends AsciiString> values) {
public Http2Headers set(ByteString name, Iterable<? extends ByteString> 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());
}
}

View File

@ -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<AsciiString> PSEUDO_HEADERS = new HashSet<AsciiString>();
private final ByteString value;
private static final Set<ByteString> PSEUDO_HEADERS = new HashSet<ByteString>();
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<? extends AsciiString> values);
Http2Headers add(ByteString name, Iterable<? extends ByteString> 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<? extends AsciiString> values);
Http2Headers set(ByteString name, Iterable<? extends ByteString> 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();
}

View File

@ -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 {
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-7.1.3">sensitive</a>.
* {@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;
}
};

View File

@ -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);

View File

@ -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<AsciiString, AsciiString>
REQUEST_HEADER_TRANSLATIONS = new HashMap<AsciiString, AsciiString>();
private static final Map<AsciiString, AsciiString>
RESPONSE_HEADER_TRANSLATIONS = new HashMap<AsciiString, AsciiString>();
private static final Map<ByteString, ByteString>
REQUEST_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>();
private static final Map<ByteString, ByteString>
RESPONSE_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>();
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<AsciiString, AsciiString> translations;
private final Map<ByteString, ByteString> translations;
/**
* Create a new instance
@ -362,10 +362,10 @@ public final class HttpUtil {
}
@Override
public boolean visit(Entry<AsciiString, AsciiString> entry) throws Http2Exception {
final AsciiString name = entry.getKey();
final AsciiString value = entry.getValue();
AsciiString translatedName = translations.get(name);
public boolean visit(Entry<ByteString, ByteString> 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(new AsciiString(translatedName.array(), false), new AsciiString(value.array(), false));
}
}
return true;

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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());
}

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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<AsciiString> {
public interface BinaryHeaders extends Headers<ByteString> {
/**
* 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<AsciiString> {
interface EntryVisitor extends Headers.EntryVisitor<ByteString> {
}
/**
* 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<AsciiString> {
interface NameVisitor extends Headers.NameVisitor<ByteString> {
}
@Override
BinaryHeaders add(AsciiString name, AsciiString value);
BinaryHeaders add(ByteString name, ByteString value);
@Override
BinaryHeaders add(AsciiString name, Iterable<? extends AsciiString> values);
BinaryHeaders add(ByteString name, Iterable<? extends ByteString> 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<AsciiString> {
BinaryHeaders add(BinaryHeaders headers);
@Override
BinaryHeaders set(AsciiString name, AsciiString value);
BinaryHeaders set(ByteString name, ByteString value);
@Override
BinaryHeaders set(AsciiString name, Iterable<? extends AsciiString> values);
BinaryHeaders set(ByteString name, Iterable<? extends ByteString> 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)}

View File

@ -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<AsciiString> implements BinaryHeaders {
private static final HashCodeGenerator<AsciiString> ASCII_HASH_CODE_GENERATOR =
new HashCodeGenerator<AsciiString>() {
public class DefaultBinaryHeaders extends DefaultHeaders<ByteString> implements BinaryHeaders {
private static final ValueConverter<ByteString> OBJECT_TO_BYTE = new ValueConverter<ByteString>() {
private final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
@Override
public int generateHashCode(AsciiString name) {
return AsciiString.caseInsensitiveHashCode(name);
}
};
private static final ValueConverter<AsciiString> OBJECT_TO_ASCII = new ValueConverter<AsciiString>() {
@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<AsciiString> 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<AsciiString> ASCII_TO_LOWER_CONVERTER = new NameConverter<AsciiString>() {
@Override
public AsciiString convertName(AsciiString name) {
return name.toLowerCase();
}
};
private static final NameConverter<AsciiString> ASCII_IDENTITY_CONVERTER = new NameConverter<AsciiString>() {
@Override
public AsciiString convertName(AsciiString name) {
return name;
}
};
private static final HashCodeGenerator<ByteString> JAVA_HASH_CODE_GENERATOR =
new JavaHashCodeGenerator<ByteString>();
protected static final NameConverter<ByteString> IDENTITY_NAME_CONVERTER = new IdentityNameConverter<ByteString>();
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<ByteString> 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<? extends AsciiString> values) {
public BinaryHeaders add(ByteString name, Iterable<? extends ByteString> 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<AsciiString> 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<? extends AsciiString> values) {
public BinaryHeaders set(ByteString name, Iterable<? extends ByteString> 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;
}

View File

@ -66,6 +66,16 @@ public class DefaultHeaders<T> implements Headers<T> {
T convertName(T name);
}
/**
* Uses the {@link #hashCode()} method to generate the hash code.
*/
public static final class JavaHashCodeGenerator<T> implements HashCodeGenerator<T> {
@Override
public int generateHashCode(T name) {
return name.hashCode();
}
}
/**
* A name converted which does not covert but instead just returns this {@code name} unchanged
*/

View File

@ -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<CharSequence, String> implements TextHeaders {
private static final HashCodeGenerator<CharSequence> CHARSEQUECE_CASE_INSENSITIVE_HASH_CODE_GENERATOR =
new HashCodeGenerator<CharSequence>() {
@ -36,12 +37,7 @@ public class DefaultTextHeaders extends DefaultConvertibleHeaders<CharSequence,
};
private static final HashCodeGenerator<CharSequence> CHARSEQUECE_CASE_SENSITIVE_HASH_CODE_GENERATOR =
new HashCodeGenerator<CharSequence>() {
@Override
public int generateHashCode(CharSequence name) {
return name.hashCode();
}
};
new JavaHashCodeGenerator<CharSequence>();
public static class DefaultTextValueTypeConverter implements ValueConverter<CharSequence> {
@Override

View File

@ -16,96 +16,98 @@
package io.netty.handler.codec;
public class EmptyBinaryHeaders extends EmptyHeaders<AsciiString> implements BinaryHeaders {
import io.netty.util.ByteString;
public class EmptyBinaryHeaders extends EmptyHeaders<ByteString> 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<? extends AsciiString> values) {
public BinaryHeaders add(ByteString name, Iterable<? extends ByteString> 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<AsciiString> 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<? extends AsciiString> values) {
public BinaryHeaders set(ByteString name, Iterable<? extends ByteString> 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;
}

View File

@ -23,7 +23,7 @@ import java.util.Set;
public interface Headers<T> extends Iterable<Map.Entry<T, T>> {
/**
* 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<T> {
/**
@ -36,7 +36,7 @@ public interface Headers<T> extends Iterable<Map.Entry<T, T>> {
}
/**
* 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<T> {
/**
@ -1095,16 +1095,14 @@ public interface Headers<T> extends Iterable<Map.Entry<T, T>> {
Iterator<Entry<T, T>> 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<T, T> forEachEntry(EntryVisitor<T> 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
*/

View File

@ -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;
}

View File

@ -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<AsciiString> list = h1.getAll(as(k1));
List<ByteString> 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<AsciiString> list = h1.getAll(as("foo"));
List<ByteString> list = h1.getAll(as("foo"));
assertEquals(1, list.size());
assertEquals(as("goo3"), list.get(0));
}
@Test(expected = NoSuchElementException.class)
public void iterateEmptyHeadersShouldThrow() {
Iterator<Map.Entry<AsciiString, AsciiString>> iterator = new DefaultBinaryHeaders().iterator();
Iterator<Map.Entry<ByteString, ByteString>> 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<AsciiString, AsciiString> entry : h1) {
for (Map.Entry<ByteString, ByteString> 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<AsciiString> values = h1.getAll(as("foo"));
List<ByteString> values = h1.getAll(as("foo"));
assertEquals(0, values.size());
}

View File

@ -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<CharSequence> {
public final class AsciiString extends ByteString implements CharSequence, Comparable<CharSequence> {
private static final char MAX_CHAR_VALUE = 255;
public static final AsciiString EMPTY_STRING = new AsciiString("");
public static final Comparator<AsciiString> CASE_INSENSITIVE_ORDER = new Comparator<AsciiString>() {
@Override
public int compare(AsciiString o1, AsciiString o2) {
@ -73,8 +77,8 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
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<CharSequence>
} 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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
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> 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<CharSequence>
* 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<CharSequence>
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<CharSequence>
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<CharSequence>
}
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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
* @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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
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<CharSequence>
* @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<CharSequence>
*/
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<CharSequence>
* @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<CharSequence>
* @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<CharSequence>
final List<AsciiString> res = new ArrayList<AsciiString>();
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<CharSequence>
}
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);
}
}

View File

@ -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;
}

View File

@ -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<ByteString> DEFAULT_COMPARATOR = new Comparator<ByteString>() {
@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);
}
}

View File

@ -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];
/**

View File

@ -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();
}
}

View File

@ -15,6 +15,12 @@
*/
package io.netty.example.http.helloworld;
import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
@ -25,10 +31,6 @@ import io.netty.handler.codec.http.HttpHeaderUtil;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
public class HttpHelloWorldServerHandler extends ChannelHandlerAdapter {
private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };

View File

@ -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;
/**

View File

@ -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;