Skip to content

Commit e626e8f

Browse files
committed
More allocation optimizations for parsing ip4v addresses
1 parent 0c16cab commit e626e8f

File tree

2 files changed

+34
-12
lines changed

2 files changed

+34
-12
lines changed

server/src/main/java/org/elasticsearch/common/network/InetAddresses.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ public class InetAddresses {
3737

3838
public static boolean isInetAddress(String ipString) {
3939
XContentString.UTF8Bytes bytes = new Text(ipString).bytes();
40-
return ipStringToBytes(bytes.bytes(), bytes.offset(), bytes.length()) != null;
40+
return ipStringToBytes(bytes.bytes(), bytes.offset(), bytes.length(), false) != null;
4141
}
4242

4343
public static String getIpOrHost(String ipString) {
4444
XContentString.UTF8Bytes utf8Bytes = new Text(ipString).bytes();
45-
byte[] bytes = ipStringToBytes(utf8Bytes.bytes(), utf8Bytes.offset(), utf8Bytes.length());
45+
byte[] bytes = ipStringToBytes(utf8Bytes.bytes(), utf8Bytes.offset(), utf8Bytes.length(), false);
4646
if (bytes == null) { // is not InetAddress
4747
return ipString;
4848
}
@@ -59,15 +59,15 @@ public static String getIpOrHost(String ipString) {
5959
*/
6060
public static byte[] encodeAsIpv6(XContentString ipString) {
6161
XContentString.UTF8Bytes uft8Bytes = ipString.bytes();
62-
byte[] address = ipStringToBytes(uft8Bytes.bytes(), uft8Bytes.offset(), uft8Bytes.length());
62+
byte[] address = ipStringToBytes(uft8Bytes.bytes(), uft8Bytes.offset(), uft8Bytes.length(), true);
6363
// The argument was malformed, i.e. not an IP string literal.
6464
if (address == null) {
6565
throw new IllegalArgumentException(String.format(Locale.ROOT, "'%s' is not an IP string literal.", ipString.string()));
6666
}
6767
return CIDRUtils.encode(address);
6868
}
6969

70-
private static byte[] ipStringToBytes(byte[] ipUtf8, int offset, int length) {
70+
private static byte[] ipStringToBytes(byte[] ipUtf8, int offset, int length, boolean asIpv6) {
7171
// Make a first pass to categorize the characters in this string.
7272
boolean hasColon = false;
7373
boolean hasDot = false;
@@ -103,7 +103,7 @@ private static byte[] ipStringToBytes(byte[] ipUtf8, int offset, int length) {
103103
}
104104
return textToNumericFormatV6(ipUtf8, offset, length);
105105
} else if (hasDot) {
106-
return textToNumericFormatV4(ipUtf8, offset, length);
106+
return textToNumericFormatV4(ipUtf8, offset, length, asIpv6);
107107
}
108108
return null;
109109
}
@@ -116,7 +116,7 @@ private static byte[] convertDottedQuadToHex(byte[] ipUtf8, int offset, int leng
116116
}
117117
}
118118
assert quadOffset >= 0 : "Expected at least one colon in dotted quad IPv6 address";
119-
byte[] quad = textToNumericFormatV4(ipUtf8, offset + quadOffset, length - quadOffset);
119+
byte[] quad = textToNumericFormatV4(ipUtf8, offset + quadOffset, length - quadOffset, false);
120120
if (quad == null) {
121121
return null;
122122
}
@@ -130,15 +130,25 @@ private static byte[] convertDottedQuadToHex(byte[] ipUtf8, int offset, int leng
130130
return result;
131131
}
132132

133-
private static byte[] textToNumericFormatV4(byte[] ipUtf8, int offset, int length) {
134-
byte[] bytes = new byte[IPV4_PART_COUNT];
135-
byte octet = 0;
133+
private static byte[] textToNumericFormatV4(byte[] ipUtf8, int offset, int length, boolean asIpv6) {
134+
byte[] bytes;
135+
byte octet;
136+
if (asIpv6) {
137+
bytes = new byte[IPV6_PART_COUNT * 2];
138+
System.arraycopy(CIDRUtils.IPV4_PREFIX, 0, bytes, 0, CIDRUtils.IPV4_PREFIX.length);
139+
octet = (byte) CIDRUtils.IPV4_PREFIX.length;
140+
} else {
141+
bytes = new byte[IPV4_PART_COUNT];
142+
octet = 0;
143+
}
136144
byte digits = 0;
137145
int current = 0;
138146
for (int i = offset; i < offset + length; i++) {
139147
byte c = ipUtf8[i];
140148
if (c == '.') {
141-
if (octet > 3 /* too many octets */ || digits == 0 /* empty octet */ || current > 255 /* octet is outside a byte range */) {
149+
if (octet >= bytes.length /* too many octets */
150+
|| digits == 0 /* empty octet */
151+
|| current > 255 /* octet is outside a byte range */) {
142152
return null;
143153
}
144154
bytes[octet++] = (byte) current;
@@ -154,7 +164,9 @@ private static byte[] textToNumericFormatV4(byte[] ipUtf8, int offset, int lengt
154164
return null;
155165
}
156166
}
157-
if (octet != 3 /* too many octets */ || digits == 0 /* empty octet */ || current > 255 /* octet is outside a byte range */) {
167+
if (octet != bytes.length - 1 /* too many or too few octets */
168+
|| digits == 0 /* empty octet */
169+
|| current > 255 /* octet is outside a byte range */) {
158170
return null;
159171
}
160172
bytes[octet] = (byte) current;
@@ -419,7 +431,7 @@ public static InetAddress forString(XContentString.UTF8Bytes bytes) {
419431
* which utilizes a more efficient implementation for parsing the IP address.
420432
*/
421433
public static InetAddress forString(byte[] ipUtf8, int offset, int length) {
422-
byte[] addr = ipStringToBytes(ipUtf8, offset, length);
434+
byte[] addr = ipStringToBytes(ipUtf8, offset, length, false);
423435

424436
// The argument was malformed, i.e. not an IP string literal.
425437
if (addr == null) {

server/src/test/java/org/elasticsearch/common/network/InetAddressesTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.elasticsearch.core.Tuple;
2121
import org.elasticsearch.test.ESTestCase;
22+
import org.elasticsearch.xcontent.Text;
2223
import org.hamcrest.Matchers;
2324

2425
import java.net.InetAddress;
@@ -249,4 +250,13 @@ public void testParseCidr() {
249250
assertEquals(InetAddresses.forString("::fffe:0:0"), cidr.v1());
250251
assertEquals(Integer.valueOf(128), cidr.v2());
251252
}
253+
254+
public void testEncodeAsIpv6() throws Exception {
255+
assertEquals(16, InetAddresses.encodeAsIpv6(new Text("::1")).length);
256+
assertEquals(16, InetAddresses.encodeAsIpv6(new Text("192.168.0.0")).length);
257+
assertEquals(
258+
"192.168.0.0",
259+
InetAddresses.toAddrString(InetAddress.getByAddress(InetAddresses.encodeAsIpv6(new Text("192.168.0.0"))))
260+
);
261+
}
252262
}

0 commit comments

Comments
 (0)