Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: proof-of-concept for Java 17 API improvements #19

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
17 changes: 10 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -126,20 +127,22 @@
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<version>2.2</version>
</dependency>
</dependencies>
</dependencyManagement>
Expand Down
74 changes: 66 additions & 8 deletions src/main/java/net/ripe/ipresource/jdk17/Ipv6Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,25 @@
import org.jetbrains.annotations.NotNull;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.regex.Pattern;

public final class Ipv6Address implements IpAddress {
public static final int NUMBER_OF_BITS = 128;

public static final Ipv6Address LOWEST = new Ipv6Address(0, 0);
public static final Ipv6Address HIGHEST = new Ipv6Address(-1, -1);

/* Pattern to match IPv6 addresses in forms defined in http://www.ietf.org/rfc/rfc4291.txt */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks quite intimidating and complex.
Don't we want to use some library for parsing?
https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.base/share/classes/sun/net/util/IPAddressUtil.java
Or Guava or something

private static final Pattern IPV6_PATTERN = Pattern.compile("(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))");
private static final int COLON_COUNT_FOR_EMBEDDED_IPV4 = 6;
private static final int COLON_COUNT_IPV6 = 7;
private static final String COLON = ":";

private static final BigInteger MASK_128 = BigInteger.ONE.shiftLeft(NUMBER_OF_BITS).subtract(BigInteger.ONE);
private static final BigInteger MASK_64 = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE);
private static final int MASK_16 = 0xffff;

public static final String COLON = ":";

final long hi, lo;

Ipv6Address(long hi, long lo) {
Expand Down Expand Up @@ -89,7 +95,7 @@ public String toString() {
formatted[i] = Long.toHexString(parts[i]);
}

// Find longest sequence of zeroes. Use the first one if there are
// Find the longest sequence of zeroes. Use the first one if there are
// multiple sequences of zeroes with the same length.
int currentZeroPartsLength = 0;
int currentZeroPartsStart = 0;
Expand Down Expand Up @@ -170,11 +176,6 @@ public Ipv6Address max(Ipv6Address that) {
return this.compareTo(that) >= 0 ? this : that;
}

public static Ipv6Address parse(String s) {
var x = net.ripe.ipresource.Ipv6Address.parse(s);
return new Ipv6Address(x.getValue());
}

public static Ipv6Address of(BigInteger value) {
return new Ipv6Address(value);
}
Expand All @@ -187,4 +188,61 @@ public Ipv6Address getCommonPrefix(Ipv6Address that) {
int length = Ipv6Block.getCommonPrefixLength(this, that);
return Ipv6Prefix.lowerBoundForPrefix(this, length);
}

public static @NotNull Ipv6Address parse(@NotNull String ipAddressString) {
ipAddressString = ipAddressString.trim();
if (!IPV6_PATTERN.matcher(ipAddressString).matches()) {
throw new IllegalArgumentException("Invalid IPv6 address: " + ipAddressString);
}

ipAddressString = expandMissingColons(ipAddressString);
if (isInIpv4EmbeddedIpv6Format(ipAddressString)) {
ipAddressString = getIpv6AddressWithIpv4SectionInIpv6Notation(ipAddressString);
}
return new Ipv6Address(ipv6StringToBigInteger(ipAddressString));
}

private static String expandMissingColons(String ipAddressString) {
int colonCount = isInIpv4EmbeddedIpv6Format(ipAddressString) ? COLON_COUNT_FOR_EMBEDDED_IPV4 : COLON_COUNT_IPV6;
return ipAddressString.replace("::", StringUtils.repeat(":", colonCount - StringUtils.countMatches(ipAddressString, ":") + 2));
}

private static boolean isInIpv4EmbeddedIpv6Format(String ipAddressString) {
return ipAddressString.contains(".");
}

private static String getIpv6AddressWithIpv4SectionInIpv6Notation(String ipAddressString) {
String ipv6Section = StringUtils.substringBeforeLast(ipAddressString, ":");
String ipv4Section = StringUtils.substringAfterLast(ipAddressString, ":");
try {
long ipv4value = Ipv4Address.parse(ipv4Section).longValue();
return ipv6Section + ":" +
Long.toString(ipv4value >>> 16, 16) + ":" +
Long.toString(ipv4value & 0xffff, 16);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Embedded Ipv4 in IPv6 address is invalid: " + ipAddressString, e);
}
}

/**
* Converts a fully expanded IPv6 string to a BigInteger
*
* @param ipAddressString Fully expanded address (i.e. no '::' shortcut)
* @return Address as BigInteger
*/
private static BigInteger ipv6StringToBigInteger(String ipAddressString) {
final ByteBuffer byteBuffer = ByteBuffer.allocate(16);
int groupValue = 0;
for (int i = 0; i < ipAddressString.length(); i++) {
final char c = ipAddressString.charAt(i);
if (c == ':') {
byteBuffer.putShort((short) groupValue);
groupValue = 0;
} else {
groupValue = (groupValue << 4) + Character.digit(c, 16);
}
}
byteBuffer.putShort((short) groupValue);
return new BigInteger(1, byteBuffer.array());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import static net.ripe.ipresource.ImmutableResourceSet.universal;
import static net.ripe.ipresource.IpResource.parse;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class ImmutableResourceSetTest {

Expand Down
Loading