diff --git a/IPAddress/src/inet.ipaddr/inet/ipaddr/IPAddress.java b/IPAddress/src/inet.ipaddr/inet/ipaddr/IPAddress.java index 710884e9..c1151ed3 100644 --- a/IPAddress/src/inet.ipaddr/inet/ipaddr/IPAddress.java +++ b/IPAddress/src/inet.ipaddr/inet/ipaddr/IPAddress.java @@ -1587,11 +1587,96 @@ static List spanWithBlocks(IPAddressSegmentSer * @return */ public abstract IPAddress[] mergeToPrefixBlocks(IPAddress ...addresses) throws AddressConversionException; - - protected static List getMergedPrefixBlocks(IPAddressSegmentSeries sections[]) { + + protected static List 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> 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> merger) { + ArrayList ipv4List = null; + ArrayList 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(addresses.length); + } + ipv4List.add(addr); + } else if(addr.isIPv6()) { + if(ipv6List == null) { + ipv6List = new ArrayList(addresses.length); + } + ipv6List.add(addr); + } + } + } + IPv4Address addressesIPv4[]; + if(ipv4List != null){ + List 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 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. *

diff --git a/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv4/IPv4Address.java b/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv4/IPv4Address.java index 551e466e..7f1b3cfa 100644 --- a/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv4/IPv4Address.java +++ b/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv4/IPv4Address.java @@ -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; @@ -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(); diff --git a/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv6/IPv6Address.java b/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv6/IPv6Address.java index 35a1b11a..4fcc9fea 100644 --- a/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv6/IPv6Address.java +++ b/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv6/IPv6Address.java @@ -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; @@ -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 * diff --git a/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressRangeTest.java b/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressRangeTest.java index a140c1b6..bef474c7 100644 --- a/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressRangeTest.java +++ b/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressRangeTest.java @@ -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", diff --git a/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressTest.java b/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressTest.java index 608c9781..25f8a902 100644 --- a/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressTest.java +++ b/IPAddress/src/inet.ipaddr/inet/ipaddr/test/IPAddressTest.java @@ -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; @@ -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());