NetUtil valid IP methods to accept CharSequence (#7827)
* NetUtil valid IP methods to accept CharSequence Motivation: NetUtil has methods to determine if a String is a valid IP address. These methods don't rely upon String specific methods and can use CharSequence instead. Modifications: - Use CharSequence instead of String for the IP validator methods. - Avoid object allocation in AsciiString#indexOf(char,int) and reduce byte code Result: No more copy operation required if a CharSequence exists.
This commit is contained in:
parent
9d51a40df0
commit
602ee5444d
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package io.netty.util;
|
||||
|
||||
import io.netty.util.ByteProcessor.IndexOfProcessor;
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
import io.netty.util.internal.InternalThreadLocalMap;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
@ -673,44 +672,35 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
|
||||
* @throws NullPointerException if {@code subString} is {@code null}.
|
||||
*/
|
||||
public int indexOf(CharSequence subString, int start) {
|
||||
final int subCount = subString.length();
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
final int thisLen = length();
|
||||
|
||||
int subCount = subString.length();
|
||||
if (subCount <= 0) {
|
||||
return start < thisLen ? start : thisLen;
|
||||
return start < length ? start : length;
|
||||
}
|
||||
if (subCount > thisLen - start) {
|
||||
return -1;
|
||||
if (subCount > length - start) {
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
final char firstChar = subString.charAt(0);
|
||||
if (firstChar > MAX_CHAR_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
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
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
final byte firstCharAsByte = c2b0(firstChar);
|
||||
final int len = offset + start + length - subCount;
|
||||
for (int i = start + offset; i <= len; ++i) {
|
||||
if (value[i] == firstCharAsByte) {
|
||||
int o1 = i, o2 = 0;
|
||||
while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
|
||||
while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
|
||||
// Intentionally empty
|
||||
}
|
||||
if (o2 == subCount) {
|
||||
return i;
|
||||
return i - offset;
|
||||
}
|
||||
start = i + 1;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PlatformDependent.throwException(e);
|
||||
return -1;
|
||||
}
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -723,22 +713,22 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
|
||||
* -1 if found no occurrence.
|
||||
*/
|
||||
public int indexOf(char ch, int start) {
|
||||
if (ch > MAX_CHAR_VALUE) {
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
final int thisLen = length();
|
||||
|
||||
if (ch > MAX_CHAR_VALUE) {
|
||||
return -1;
|
||||
final byte chAsByte = c2b0(ch);
|
||||
final int len = offset + start + length;
|
||||
for (int i = start + offset; i < len; ++i) {
|
||||
if (value[i] == chAsByte) {
|
||||
return i - offset;
|
||||
}
|
||||
|
||||
try {
|
||||
return forEachByte(start, thisLen - start, new IndexOfProcessor((byte) ch));
|
||||
} catch (Exception e) {
|
||||
PlatformDependent.throwException(e);
|
||||
return -1;
|
||||
}
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -766,44 +756,35 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
|
||||
* @throws NullPointerException if {@code subString} is {@code null}.
|
||||
*/
|
||||
public int lastIndexOf(CharSequence subString, int start) {
|
||||
final int thisLen = length();
|
||||
final int subCount = subString.length();
|
||||
|
||||
if (subCount > thisLen || start < 0) {
|
||||
return -1;
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (subCount <= 0) {
|
||||
return start < thisLen ? start : thisLen;
|
||||
return start < length ? start : length;
|
||||
}
|
||||
if (subCount > length - start) {
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
start = Math.min(start, thisLen - subCount);
|
||||
|
||||
// count and subCount are both >= 1
|
||||
final char firstChar = subString.charAt(0);
|
||||
if (firstChar > MAX_CHAR_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar);
|
||||
try {
|
||||
for (;;) {
|
||||
int i = forEachByteDesc(start, thisLen - start, IndexOfVisitor);
|
||||
if (i == -1) {
|
||||
return -1;
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
final byte firstCharAsByte = c2b0(firstChar);
|
||||
final int end = offset + start;
|
||||
for (int i = offset + start + length - subCount; i >= end; --i) {
|
||||
if (value[i] == firstCharAsByte) {
|
||||
int o1 = i, o2 = 0;
|
||||
while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
|
||||
while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
|
||||
// Intentionally empty
|
||||
}
|
||||
if (o2 == subCount) {
|
||||
return i;
|
||||
return i - offset;
|
||||
}
|
||||
start = i - 1;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PlatformDependent.throwException(e);
|
||||
return -1;
|
||||
}
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -895,30 +876,24 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
|
||||
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 newCharByte = c2b(newChar);
|
||||
final byte oldCharAsByte = c2b0(oldChar);
|
||||
final byte newCharAsByte = c2b(newChar);
|
||||
final int len = offset + length;
|
||||
for (int i = offset; i < len; ++i) {
|
||||
if (value[i] == oldCharAsByte) {
|
||||
byte[] buffer = new byte[length()];
|
||||
for (int i = 0, j = arrayOffset(); i < buffer.length; i++, j++) {
|
||||
byte b = value[j];
|
||||
if (b == oldCharByte) {
|
||||
b = newCharByte;
|
||||
System.arraycopy(value, offset, buffer, 0, i - offset);
|
||||
buffer[i - offset] = newCharAsByte;
|
||||
++i;
|
||||
for (; i < len; ++i) {
|
||||
byte oldValue = value[i];
|
||||
buffer[i - offset] = oldValue != oldCharAsByte ? oldValue : newCharAsByte;
|
||||
}
|
||||
buffer[i] = b;
|
||||
}
|
||||
|
||||
return new AsciiString(buffer, false);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the specified string to this string to determine if the specified string is a prefix.
|
||||
@ -1832,10 +1807,7 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
|
||||
return INDEX_NOT_FOUND;
|
||||
}
|
||||
final int sz = cs.length();
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
for (int i = start; i < sz; i++) {
|
||||
for (int i = start < 0 ? 0 : start; i < sz; i++) {
|
||||
if (cs.charAt(i) == searchChar) {
|
||||
return i;
|
||||
}
|
||||
@ -1879,6 +1851,10 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
|
||||
return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c);
|
||||
}
|
||||
|
||||
private static byte c2b0(char c) {
|
||||
return (byte) c;
|
||||
}
|
||||
|
||||
public static char b2c(byte b) {
|
||||
return (char) (b & 0xFF);
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.util.AsciiString.indexOf;
|
||||
|
||||
/**
|
||||
* A class that holds a number of network-related constants.
|
||||
* <p/>
|
||||
@ -462,6 +464,10 @@ public final class NetUtil {
|
||||
}
|
||||
|
||||
public static boolean isValidIpV6Address(String ip) {
|
||||
return isValidIpV6Address((CharSequence) ip);
|
||||
}
|
||||
|
||||
public static boolean isValidIpV6Address(CharSequence ip) {
|
||||
int end = ip.length();
|
||||
if (end < 2) {
|
||||
return false;
|
||||
@ -557,7 +563,7 @@ public final class NetUtil {
|
||||
}
|
||||
|
||||
// 7 - is minimum IPv4 address length
|
||||
int ipv4End = ip.indexOf('%', ipv4Start + 7);
|
||||
int ipv4End = indexOf(ip, '%', ipv4Start + 7);
|
||||
if (ipv4End < 0) {
|
||||
ipv4End = end;
|
||||
}
|
||||
@ -623,7 +629,17 @@ public final class NetUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string and parses it to see if it is a valid IPV4 address.
|
||||
* Takes a {@link CharSequence} and parses it to see if it is a valid IPV4 address.
|
||||
*
|
||||
* @return true, if the string represents an IPV4 address in dotted
|
||||
* notation, false otherwise
|
||||
*/
|
||||
public static boolean isValidIpV4Address(CharSequence ip) {
|
||||
return isValidIpV4Address(ip, 0, ip.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a {@link String} and parses it to see if it is a valid IPV4 address.
|
||||
*
|
||||
* @return true, if the string represents an IPV4 address in dotted
|
||||
* notation, false otherwise
|
||||
@ -632,6 +648,12 @@ public final class NetUtil {
|
||||
return isValidIpV4Address(ip, 0, ip.length());
|
||||
}
|
||||
|
||||
private static boolean isValidIpV4Address(CharSequence ip, int from, int toExcluded) {
|
||||
return ip instanceof String ? isValidIpV4Address((String) ip, from, toExcluded) :
|
||||
ip instanceof AsciiString ? isValidIpV4Address((AsciiString) ip, from, toExcluded) :
|
||||
isValidIpV4Address0(ip, from, toExcluded);
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicateBooleanBranch")
|
||||
private static boolean isValidIpV4Address(String ip, int from, int toExcluded) {
|
||||
int len = toExcluded - from;
|
||||
@ -643,6 +665,28 @@ public final class NetUtil {
|
||||
isValidIpV4Word(ip, i + 1, toExcluded);
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicateBooleanBranch")
|
||||
private static boolean isValidIpV4Address(AsciiString ip, int from, int toExcluded) {
|
||||
int len = toExcluded - from;
|
||||
int i;
|
||||
return len <= 15 && len >= 7 &&
|
||||
(i = ip.indexOf('.', from + 1)) > 0 && isValidIpV4Word(ip, from, i) &&
|
||||
(i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
|
||||
(i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
|
||||
isValidIpV4Word(ip, i + 1, toExcluded);
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicateBooleanBranch")
|
||||
private static boolean isValidIpV4Address0(CharSequence ip, int from, int toExcluded) {
|
||||
int len = toExcluded - from;
|
||||
int i;
|
||||
return len <= 15 && len >= 7 &&
|
||||
(i = indexOf(ip, '.', from + 1)) > 0 && isValidIpV4Word(ip, from, i) &&
|
||||
(i = indexOf(ip, '.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
|
||||
(i = indexOf(ip, '.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
|
||||
isValidIpV4Word(ip, i + 1, toExcluded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Inet6Address} representation of a {@link CharSequence} IP address.
|
||||
* <p>
|
||||
|
@ -28,6 +28,7 @@ import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@ -301,6 +302,34 @@ public class AsciiStringCharacterTest {
|
||||
assertEquals(1, AsciiString.of("aabaabaa").indexOf('a', 1));
|
||||
assertEquals(3, AsciiString.of("aabaabaa").indexOf('a', 2));
|
||||
assertEquals(3, AsciiString.of("aabdabaa").indexOf('d', 1));
|
||||
assertEquals(1, new AsciiString("abcd", 1, 2).indexOf('c', 0));
|
||||
assertEquals(2, new AsciiString("abcd", 1, 3).indexOf('d', 2));
|
||||
assertEquals(0, new AsciiString("abcd", 1, 2).indexOf('b', 0));
|
||||
assertEquals(-1, new AsciiString("abcd", 0, 2).indexOf('c', 0));
|
||||
assertEquals(-1, new AsciiString("abcd", 1, 3).indexOf('a', 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexOfCharSequence() {
|
||||
assertEquals(0, new AsciiString("abcd").indexOf("abcd", 0));
|
||||
assertEquals(0, new AsciiString("abcd").indexOf("abc", 0));
|
||||
assertEquals(1, new AsciiString("abcd").indexOf("bcd", 0));
|
||||
assertEquals(1, new AsciiString("abcd").indexOf("bc", 0));
|
||||
assertEquals(1, new AsciiString("abcdabcd").indexOf("bcd", 0));
|
||||
assertEquals(0, new AsciiString("abcd", 1, 2).indexOf("bc", 0));
|
||||
assertEquals(0, new AsciiString("abcd", 1, 3).indexOf("bcd", 0));
|
||||
assertEquals(1, new AsciiString("abcdabcd", 4, 4).indexOf("bcd", 0));
|
||||
|
||||
// Test with empty string
|
||||
assertEquals(0, new AsciiString("abcd").indexOf("", 0));
|
||||
assertEquals(1, new AsciiString("abcd").indexOf("", 1));
|
||||
assertEquals(3, new AsciiString("abcd", 1, 3).indexOf("", 4));
|
||||
|
||||
// Test not found
|
||||
assertEquals(-1, new AsciiString("abcd").indexOf("abcde", 0));
|
||||
assertEquals(-1, new AsciiString("abcdbc").indexOf("bce", 0));
|
||||
assertEquals(-1, new AsciiString("abcd", 1, 3).indexOf("abc", 0));
|
||||
assertEquals(-1, new AsciiString("abcd", 1, 2).indexOf("bd", 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -315,6 +344,42 @@ public class AsciiStringCharacterTest {
|
||||
assertEquals(3, AsciiString.indexOf("aabdabaa", 'd', 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastIndexOfCharSequence() {
|
||||
assertEquals(0, new AsciiString("abcd").lastIndexOf("abcd", 0));
|
||||
assertEquals(0, new AsciiString("abcd").lastIndexOf("abc", 0));
|
||||
assertEquals(1, new AsciiString("abcd").lastIndexOf("bcd", 0));
|
||||
assertEquals(1, new AsciiString("abcd").lastIndexOf("bc", 0));
|
||||
assertEquals(5, new AsciiString("abcdabcd").lastIndexOf("bcd", 0));
|
||||
assertEquals(0, new AsciiString("abcd", 1, 2).lastIndexOf("bc", 0));
|
||||
assertEquals(0, new AsciiString("abcd", 1, 3).lastIndexOf("bcd", 0));
|
||||
assertEquals(1, new AsciiString("abcdabcd", 4, 4).lastIndexOf("bcd", 0));
|
||||
|
||||
// Test with empty string
|
||||
assertEquals(0, new AsciiString("abcd").lastIndexOf("", 0));
|
||||
assertEquals(1, new AsciiString("abcd").lastIndexOf("", 1));
|
||||
assertEquals(3, new AsciiString("abcd", 1, 3).lastIndexOf("", 4));
|
||||
|
||||
// Test not found
|
||||
assertEquals(-1, new AsciiString("abcd").lastIndexOf("abcde", 0));
|
||||
assertEquals(-1, new AsciiString("abcdbc").lastIndexOf("bce", 0));
|
||||
assertEquals(-1, new AsciiString("abcd", 1, 3).lastIndexOf("abc", 0));
|
||||
assertEquals(-1, new AsciiString("abcd", 1, 2).lastIndexOf("bd", 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplace() {
|
||||
AsciiString abcd = new AsciiString("abcd");
|
||||
assertEquals(new AsciiString("adcd"), abcd.replace('b', 'd'));
|
||||
assertEquals(new AsciiString("dbcd"), abcd.replace('a', 'd'));
|
||||
assertEquals(new AsciiString("abca"), abcd.replace('d', 'a'));
|
||||
assertSame(abcd, abcd.replace('x', 'a'));
|
||||
assertEquals(new AsciiString("cc"), new AsciiString("abcd", 1, 2).replace('b', 'c'));
|
||||
assertEquals(new AsciiString("bb"), new AsciiString("abcd", 1, 2).replace('c', 'b'));
|
||||
assertEquals(new AsciiString("bddd"), new AsciiString("abcdc", 1, 4).replace('c', 'd'));
|
||||
assertEquals(new AsciiString("xbcxd"), new AsciiString("abcada", 0, 5).replace('a', 'x'));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubStringHashCode() {
|
||||
//two "123"s
|
||||
|
Loading…
Reference in New Issue
Block a user