Skip to content

Commit

Permalink
new merge methods for dual IPv4/v6 merging
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean Foley committed Aug 23, 2024
1 parent 6e87f76 commit 81df5d5
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 9 deletions.
89 changes: 87 additions & 2 deletions IPAddress/src/inet.ipaddr/inet/ipaddr/IPAddress.java
Original file line number Diff line number Diff line change
Expand Up @@ -1587,11 +1587,96 @@ static List<? extends IPAddressSegmentSeries> spanWithBlocks(IPAddressSegmentSer
* @return
*/
public abstract IPAddress[] mergeToPrefixBlocks(IPAddress ...addresses) throws AddressConversionException;
protected static <T extends IPAddress, S extends IPAddressSegment> List<IPAddressSegmentSeries> getMergedPrefixBlocks(IPAddressSegmentSeries sections[]) {

protected static List<IPAddressSegmentSeries> getMergedPrefixBlocks(IPAddressSegmentSeries sections[]) {
return IPAddressSection.getMergedPrefixBlocks(sections);
}

private static final IPv6Address EMPTY_IPV6_ADDRESS[] = {};
private static final IPv4Address EMPTY_IPV4_ADDRESS[] = {};

public static class DualIPv4Pv6Arrays {
public final IPv4Address addressesIPv4[];
public final IPv6Address addressesIPv6[];

DualIPv4Pv6Arrays(IPv4Address addressesIPv4[], IPv6Address addressesIPv6[]) {
this.addressesIPv4 = addressesIPv4;
this.addressesIPv6 = addressesIPv6;
}
}

/**
* merges the given set of IP addresses and subnets into a minimal number of prefix blocks.
*
* This function complements the MergeToPrefixBlock methods of each IP address type.
* Those instance methods attempt to convert arguments that do not match the IP version of the method receiver, while this function does not.
* This static method merges every non-null argument into one of the two returned slices.
*
* @param addresses
* @return
*/
public static DualIPv4Pv6Arrays mergeToDualSequentialBlocks(IPAddress ...addresses) {
Function<IPAddressSegmentSeries[], List<IPAddressSegmentSeries>> merger = (series) -> {
SeriesCreator seriesCreator = ((IPAddress) series[0]).getSequentialSeriesCreator();
return IPAddressSection.getMergedSequentialBlocks(series, seriesCreator);
};
return mergeToBlocks(addresses, merger);
}

protected abstract SeriesCreator getSequentialSeriesCreator();

/**
* merges the given set of IP addresses and subnets into a minimal number of prefix blocks.
*
* This function complements the MergeToPrefixBlock methods of each IP address type.
* Those instance methods attempt to convert arguments that do not match the IP version of the method receiver, while this function does not.
* This static method merges every non-null argument into one of the two returned slices.
*
* @param addresses
* @return
*/
public static DualIPv4Pv6Arrays mergeToDualPrefixBlocks(IPAddress ...addresses) {
return mergeToBlocks(addresses, IPAddressSection::getMergedPrefixBlocks);
}

private static DualIPv4Pv6Arrays mergeToBlocks(
IPAddress addresses[],
Function<IPAddressSegmentSeries[], List<IPAddressSegmentSeries>> merger) {
ArrayList<IPAddress> ipv4List = null;
ArrayList<IPAddress> ipv6List = null;
for(int i = 0; i < addresses.length; i++) {
IPAddress addr = addresses[i];
if(addr != null) {
if(addr.isIPv4()) {
if(ipv4List == null) {
ipv4List = new ArrayList<IPAddress>(addresses.length);
}
ipv4List.add(addr);
} else if(addr.isIPv6()) {
if(ipv6List == null) {
ipv6List = new ArrayList<IPAddress>(addresses.length);
}
ipv6List.add(addr);
}
}
}
IPv4Address addressesIPv4[];
if(ipv4List != null){
List<IPAddressSegmentSeries> blocks = merger.apply(ipv4List.toArray(new IPAddressSegmentSeries[ipv4List.size()]));
addressesIPv4 = blocks.toArray(new IPv4Address[blocks.size()]);
} else {
addressesIPv4 = EMPTY_IPV4_ADDRESS;
}
IPv6Address addressesIPv6[];
if(ipv6List != null){
List<IPAddressSegmentSeries> blocks = merger.apply(ipv6List.toArray(new IPAddressSegmentSeries[ipv6List.size()]));
addressesIPv6 = blocks.toArray(new IPv6Address[blocks.size()]);
} else {
addressesIPv6 = EMPTY_IPV6_ADDRESS;
}
return new DualIPv4Pv6Arrays(addressesIPv4, addressesIPv6);
}

/**
* Merges this with the list of subnets to produce the smallest list of block subnets that are sequential.
* <p>
Expand Down
6 changes: 6 additions & 0 deletions IPAddress/src/inet.ipaddr/inet/ipaddr/ipv4/IPv4Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressConverter;
import inet.ipaddr.IPAddressSection.IPStringBuilderOptions;
import inet.ipaddr.IPAddressSection.SeriesCreator;
import inet.ipaddr.IPAddressSegmentSeries;
import inet.ipaddr.IPAddressStringParameters;
import inet.ipaddr.IncompatibleAddressException;
Expand Down Expand Up @@ -1098,6 +1099,11 @@ public IPv4Address[] mergeToSequentialBlocks(IPAddress ...addresses) throws Addr
return blocks.toArray(new IPv4Address[blocks.size()]);
}

@Override
protected SeriesCreator getSequentialSeriesCreator() {
return getAddressCreator()::createSequentialBlockAddress;
}

@Override
public Inet4Address toUpperInetAddress() {
return (Inet4Address) super.toUpperInetAddress();
Expand Down
6 changes: 6 additions & 0 deletions IPAddress/src/inet.ipaddr/inet/ipaddr/ipv6/IPv6Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import inet.ipaddr.IPAddressConverter;
import inet.ipaddr.IPAddressSection.IPStringBuilderOptions;
import inet.ipaddr.IPAddressSection.IPStringOptions;
import inet.ipaddr.IPAddressSection.SeriesCreator;
import inet.ipaddr.IPAddressSegmentSeries;
import inet.ipaddr.IPAddressString;
import inet.ipaddr.IPAddressStringParameters;
Expand Down Expand Up @@ -2201,6 +2202,11 @@ public IPv6Address[] mergeToSequentialBlocks(IPAddress ...addresses) throws Addr
return blocks.toArray(new IPv6Address[blocks.size()]);
}

@Override
protected SeriesCreator getSequentialSeriesCreator() {
return getDefaultCreator()::createSequentialBlockAddress;
}

/**
* Returns whether {@link #getZone()} returns a non-null value
*
Expand Down
40 changes: 33 additions & 7 deletions IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressRangeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5642,28 +5642,54 @@ void runTest() {
testMerge("*:*", "8000::/1", "::/1");
testMerge("8000::/1", "8000::/1", "8000::/1");
testMerge("::/1", "::/1", "::/1");

testMergeRange("*:*", "::/1", "8000::/1");
testMergeRange("*:*", "8000::/1", "::/1");
testMergeRange("8000::/1", "8000::/1", "8000::/1");
testMergeRange("::/1", "::/1", "::/1");
}

testMerge("0-127.*", "0-127.*", "1.2.3.4");

testMergeRange("*.*", "*.*", "1.2.3.4");
testMergeRange("*.*", "1.2.3.4", "*.*");
testMergeRange("*.*", "*.*", "*.*");

testMergeRange("*:*", "*:*", "::");
testMergeRange("*:*", "::", "*:*");
testMergeRange("*:*", "*:*", "*:*");


if(!isNoAutoSubnets) {
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, "192.168.0.0/29", "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66");
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, null, "192.168.0.0/29", null, "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66");
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, null, null, "192.168.0.0/29", null, "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66");
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, "192.168.0.0/29", null, "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66");
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, "192.168.0.0/29", "192.168.0.8/29", null, "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66");
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, "192.168.0.0/29", "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", null, "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66");
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, "192.168.0.0/29", "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66", null);
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, "192.168.0.0/29", "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66", null, null);
testMergeMixed(new String[]{"1.2.2.0/25", "1.2.3.128/25", "192.168.0.0/28"}, new String[]{"1:2:3:4::/64"}, null, null, null, "192.168.0.0/29", "192.168.0.8/29", "1:2:3:4:8000::/65", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25", "1:2:3:4::/66", "1:2:3:4:4000::/66", null, null);

testMergeMixed(new String[]{"1.2.3.0/24", "1.2.4.0/23"}, new String[]{"1:0-ff:*"}, new String[]{"1.2.3-5.*"}, null, null, "1.2.5.0/24", "1:1:*", "1:3-ff:*", "1:0:*", "1.2.4.0/24", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*");
testMergeMixed(new String[]{"1.2.3.0/24", "1.2.4.0/23"}, new String[]{"1:0-ff:*"}, new String[]{"1.2.3-5.*"}, null, null, null, "1.2.5.0/24", "1:1:*", "1:3-ff:*", "1:0:*", "1.2.4.0/24", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*");
testMergeMixed(new String[]{"1.2.3.0/24", "1.2.4.0/23"}, new String[]{"1:0-ff:*"}, new String[]{"1.2.3-5.*"}, null, null, "1.2.5.0/24", "1:1:*", "1:3-ff:*", "1:0:*", null, "1.2.4.0/24", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*");
testMergeMixed(new String[]{"1.2.3.0/24", "1.2.4.0/23"}, new String[]{"1:0-ff:*"}, new String[]{"1.2.3-5.*"}, null, null, "1.2.5.0/24", "1:1:*", "1:3-ff:*", "1:0:*", "1.2.4.0/24", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*", null);
testMergeMixed(new String[]{"1.2.3.0/24", "1.2.4.0/23"}, new String[]{"1:0-ff:*"}, new String[]{"1.2.3-5.*"}, null, null, null, "1.2.5.0/24", "1:1:*", "1:3-ff:*", "1:0:*", "1.2.4.0/24", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*", null);
testMergeMixed(new String[]{"1.2.3.0/24", "1.2.4.0/23"}, new String[]{"1:0-ff:*"}, new String[]{"1.2.3-5.*"}, null, "1.2.5.0/24", "1:1:*", "1:3-ff:*", "1:0:*", null, null, "1.2.4.0/24", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*");
testMergeMixed(new String[]{"1.2.3.0/24", "1.2.4.0/23"}, new String[]{"1:0-ff:*"}, new String[]{"1.2.3-5.*"}, null, "1.2.5.0/24", "1:1:*", "1:3-ff:*", "1:0:*", "1.2.4.0/24", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*", null, null);

// sequential merge shorter for both ipv4 and ipv6 with this one
testMergeMixed(
new String[]{"1.2.3.0/24", "1.2.4.0/23", "128.0.0.0"}, new String[]{"1:0-ff:*", "8::2:3:0/112", "8::2:4:0/111"},
new String[]{"1.2.3-5.*", "128.0.0.0"}, new String[]{"1:0-ff:*", "8::2:3-5:*"},
"1.2.5.0/24", "1:1:*", "1:3-ff:*", "8::2:3-5:*", "1:0:*", "1.2.4.0/24", "128.0.0.0", "1:2:ffff:*", "1.2.3.0/24", "1:2:0-1:*", "1:2:1-fffe:*", null, null);
}

testMergeRange("0-127.*", "0-127.*", "1.2.3.4");

testMerge("1.2.3.4/32", "1.2.3.4");
testMergeRange("1.2.3.4", "1.2.3.4");

testMerge(isNoAutoSubnets ? "192.168.0.0-15/28" : "192.168.0.0/28", "192.168.0.0", "192.168.0.1", "192.168.0.2",
"192.168.0.3", "192.168.0.4", "192.168.0.5",
"192.168.0.6", "192.168.0.7", "192.168.0.8",
Expand Down
96 changes: 96 additions & 0 deletions IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import inet.ipaddr.AddressValueException;
import inet.ipaddr.HostIdentifierString;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddress.DualIPv4Pv6Arrays;
import inet.ipaddr.IPAddress.IPVersion;
import inet.ipaddr.IPAddressNetwork;
import inet.ipaddr.IPAddressNetwork.IPAddressCreator;
Expand Down Expand Up @@ -3477,6 +3478,101 @@ void testMerge2(String result, String result2, boolean prefix, String ... addres
}
incrementTestCount();
}

void testMergeMixed(String ipv4ResultPref[], String ipv6ResultPref[], String ipv4ResultSeq[], String ipv6ResultSeq[], String ... addresses) {
IPAddress[] ipv4ResultsPref, ipv6ResultsPref, ipv4ResultsSeq, ipv6ResultsSeq, addrs1;

ipv4ResultsPref = new IPAddress[ipv4ResultPref.length];
int i = 0;
for(String str: ipv4ResultPref) {
ipv4ResultsPref[i++] = createAddress(str).getAddress();
}

ipv6ResultsPref = new IPAddress[ipv6ResultPref.length];
i = 0;
for(String str: ipv6ResultPref) {
ipv6ResultsPref[i++] = createAddress(str).getAddress();
}

if(ipv4ResultSeq == null) {
ipv4ResultsSeq = ipv4ResultsPref;
} else {
ipv4ResultsSeq = new IPAddress[ipv4ResultSeq.length];
i = 0;
for(String str: ipv4ResultSeq) {
ipv4ResultsSeq[i++] = createAddress(str).getAddress();
}
}

if(ipv6ResultSeq == null) {
ipv6ResultsSeq = ipv6ResultsPref;
} else {
ipv6ResultsSeq = new IPAddress[ipv6ResultSeq.length];
i = 0;
for(String str: ipv6ResultSeq) {
ipv6ResultsSeq[i++] = createAddress(str).getAddress();
}
}

i = 0;
addrs1 = new IPAddress[addresses.length];
for(String str: addresses) {
if(str == null) {
addrs1[i++] = null;
} else {
addrs1[i++] = createAddress(str).getAddress();
}
}

DualIPv4Pv6Arrays arrays = IPAddress.mergeToDualPrefixBlocks(addrs1);
if(arrays.addressesIPv4.length != ipv4ResultsPref.length) {
addFailure(new Failure("merged ipv4 addr len " + arrays.addressesIPv4.length + " does not match "+ ipv4ResultsPref.length, (AddressItem) null));
} else {
i = 0;
for(IPAddress addr : arrays.addressesIPv4) {
IPAddress expected = ipv4ResultsPref[i++];
if(!addr.equals(expected)) {
addFailure(new Failure("merged addr "+ addr +" does not match "+expected, expected));
}
}
}
if(arrays.addressesIPv6.length != ipv6ResultsPref.length) {
addFailure(new Failure("merged ipv6 addr len " + arrays.addressesIPv6.length + " does not match "+ ipv6ResultsPref.length, (AddressItem) null));
} else {
i = 0;
for(IPAddress addr: arrays.addressesIPv6) {
IPAddress expected = ipv6ResultsPref[i++];
if(!addr.equals(expected)) {
addFailure(new Failure("merged addr " + addr + " does not match "+ expected, expected));
}
}
}

arrays = IPAddress.mergeToDualSequentialBlocks(addrs1);
if(arrays.addressesIPv4.length != ipv4ResultsSeq.length) {
addFailure(new Failure("merged ipv4 seq addr len " + arrays.addressesIPv4.length + " does not match " +ipv4ResultsSeq.length, (AddressItem) null));
} else {
i = 0;
for(IPAddress addr: arrays.addressesIPv4) {
IPAddress expected = ipv4ResultsSeq[i++];
if(!addr.equals(expected)) {
addFailure(new Failure("merged addr "+ addr +" does not match " + expected, expected));
}
}
}
if(arrays.addressesIPv6.length != ipv6ResultsSeq.length) {
addFailure(new Failure("merged ipv6 seq addr len " + arrays.addressesIPv6.length + " does not match "+ ipv6ResultsSeq.length, (AddressItem) null));
} else {
i = 0;
for(IPAddress addr: arrays.addressesIPv6) {
IPAddress expected = ipv6ResultsSeq[i++];
if(!addr.equals(expected)) {
addFailure(new Failure("merged addr " + addr + " does not match " + expected, expected));
}
}
}
incrementTestCount();
}

void testIncrement(String originalStr, long increment, String resultStr) {
testIncrement(createAddress(originalStr).getAddress(), increment, resultStr == null ? null : createAddress(resultStr).getAddress());
Expand Down

0 comments on commit 81df5d5

Please sign in to comment.