diff --git a/src/System.Net.IPNetwork/IPNetwork2Members.cs b/src/System.Net.IPNetwork/IPNetwork2Members.cs index 0c9bcf5..a9418cf 100644 --- a/src/System.Net.IPNetwork/IPNetwork2Members.cs +++ b/src/System.Net.IPNetwork/IPNetwork2Members.cs @@ -93,7 +93,7 @@ public IPAddress Broadcast return ToIPAddress(this.InternalBroadcast, this.family); } } - + /// /// Gets first usable IPAddress in Network. /// @@ -127,7 +127,31 @@ public IPAddress LastUsable return ToIPAddress(last, this.family); } } - + + /// + /// Gets first IPAddress in Network. + /// + public IPAddress First + { + get + { + BigInteger first = this.InternalNetwork; + return ToIPAddress(first, this.family); + } + } + + /// + /// Gets last IPAddress in Network. + /// + public IPAddress Last + { + get + { + BigInteger last = this.InternalBroadcast; + return ToIPAddress(last, this.family); + } + } + /// /// Gets number of usable IPAddress in Network. /// diff --git a/src/System.Net.IPNetwork/IPNetwork2Operators.cs b/src/System.Net.IPNetwork/IPNetwork2Operators.cs index 33d50e1..47e3723 100644 --- a/src/System.Net.IPNetwork/IPNetwork2Operators.cs +++ b/src/System.Net.IPNetwork/IPNetwork2Operators.cs @@ -103,4 +103,80 @@ public sealed partial class IPNetwork2 } return [left, right]; } + + /// + /// Behavior + /// The addition operator (+) performs the following operations: + /// Network Expansion: Adds the specified number of IP addresses to the network range + /// Optimal Grouping: Attempts to create the most efficient network representation + /// Multiple Networks: When a single contiguous network cannot represent the result, returns multiple networks + /// CIDR Optimization: Automatically calculates the appropriate subnet mask for the expanded range + /// + /// left instance. + /// number. + /// Adds the specified number of IP addresses to the network range. + public static IEnumerable operator +(IPNetwork2 left, int add) + { + if (add < 0) + { + return left - (add * -1); + } + if (add == 0) + { + return new[] { left }; + } + var start = ToBigInteger(left.First); + var last = ToBigInteger(left.Last); + var end = last+ add; + + if (end < start) + { + return []; + } + + var startIp = ToIPAddress(start, left.AddressFamily); + var endIp = ToIPAddress(end, left.AddressFamily); + + var uintStart = ToBigInteger(startIp); + var uintEnd = ToBigInteger(endIp); + + if (uintEnd <= uintStart) + { + throw new OverflowException("IPNetwork overflow"); + } + InternalParseRange(true, startIp, endIp, out IEnumerable networks); + return networks; + } + + /// + /// Add IPNetwork. + /// + /// left instance. + /// number. + /// Try to supernet two consecutive cidr equal subnet into a single one, otherwise return both netowkrs. + public static IEnumerable operator -(IPNetwork2 left, int subtract) + { + if (subtract < 0) + { + return left + (subtract * -1); + } + if (subtract == 0) + { + return new[] { left }; + } + var start = ToBigInteger(left.First); + var last = ToBigInteger(left.Last); + var end = last- subtract; + + if (end < start) + { + return []; + } + + var startIp = ToIPAddress(start, left.AddressFamily); + var endIp = ToIPAddress(end, left.AddressFamily); + + InternalParseRange(true, startIp, endIp, out IEnumerable networks); + return networks; + } } \ No newline at end of file diff --git a/src/TestProject/BigIntegerToUnitTest.cs b/src/TestProject/BigIntegerToUnitTest.cs index db3c157..3927ea8 100644 --- a/src/TestProject/BigIntegerToUnitTest.cs +++ b/src/TestProject/BigIntegerToUnitTest.cs @@ -16,12 +16,12 @@ public class BigIntegerToUnitTest [TestMethod] public void TestToOctalString1() { - byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0x00]; - var convertme = new BigInteger(bytes); - string result = convertme.ToOctalString(); + byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0x00]; + var convertme = new BigInteger(bytes); + string result = convertme.ToOctalString(); - Assert.AreEqual("037777777777", result); - } + Assert.AreEqual("037777777777", result); + } /// /// Test. @@ -29,12 +29,12 @@ public void TestToOctalString1() [TestMethod] public void TestToOctalString3() { - var bigi = BigInteger.Parse("1048576"); - bigi++; - string result = bigi.ToOctalString(); + var bigi = BigInteger.Parse("1048576"); + bigi++; + string result = bigi.ToOctalString(); - Assert.AreEqual("04000001", result); - } + Assert.AreEqual("04000001", result); + } /// /// Test. @@ -42,12 +42,12 @@ public void TestToOctalString3() [TestMethod] public void TestToOctalString01() { - BigInteger bigi = BigInteger.Zero; - bigi++; - string result = bigi.ToOctalString(); + BigInteger bigi = BigInteger.Zero; + bigi++; + string result = bigi.ToOctalString(); - Assert.AreEqual("01", result); - } + Assert.AreEqual("01", result); + } /// /// Test. @@ -55,12 +55,12 @@ public void TestToOctalString01() [TestMethod] public void TestToOctalString02() { - BigInteger bigi = BigInteger.Zero; - bigi--; - string result = bigi.ToOctalString(); + BigInteger bigi = BigInteger.Zero; + bigi--; + string result = bigi.ToOctalString(); - Assert.AreEqual("377", result); - } + Assert.AreEqual("377", result); + } /// /// Test. @@ -68,18 +68,18 @@ public void TestToOctalString02() [TestMethod] public void TestToOctalString03() { - BigInteger bigi = BigInteger.Zero; - bigi--; - bigi--; - bigi--; - bigi--; - bigi--; - bigi--; - bigi--; - string result = bigi.ToOctalString(); - - Assert.AreEqual("371", result); - } + BigInteger bigi = BigInteger.Zero; + bigi--; + bigi--; + bigi--; + bigi--; + bigi--; + bigi--; + bigi--; + string result = bigi.ToOctalString(); + + Assert.AreEqual("371", result); + } /// /// Test. @@ -87,12 +87,12 @@ public void TestToOctalString03() [TestMethod] public void TestToHexadecimalString1() { - byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0x00]; - var convertme = new BigInteger(bytes); - string result = convertme.ToHexadecimalString(); + byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0x00]; + var convertme = new BigInteger(bytes); + string result = convertme.ToHexadecimalString(); - Assert.AreEqual("0FFFFFFFF", result); - } + Assert.AreEqual("0FFFFFFFF", result); + } /// /// Test. @@ -100,12 +100,12 @@ public void TestToHexadecimalString1() [TestMethod] public void TestToBinaryString1() { - byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0x00]; - var convertme = new BigInteger(bytes); - string result = convertme.ToBinaryString(); + byte[] bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0x00]; + var convertme = new BigInteger(bytes); + string result = convertme.ToBinaryString(); - Assert.AreEqual("011111111111111111111111111111111", result); - } + Assert.AreEqual("011111111111111111111111111111111", result); + } /// /// Test. @@ -113,12 +113,12 @@ public void TestToBinaryString1() [TestMethod] public void TestToBinaryString01() { - BigInteger bigi = BigInteger.Zero; - bigi++; - string result = bigi.ToBinaryString(); + BigInteger bigi = BigInteger.Zero; + bigi++; + string result = bigi.ToBinaryString(); - Assert.AreEqual("01", result); - } + Assert.AreEqual("01", result); + } /// /// Test. @@ -126,11 +126,11 @@ public void TestToBinaryString01() [TestMethod] public void TestToBinaryString2() { - var convertme = new BigInteger(-1); - string result = convertme.ToBinaryString(); + var convertme = new BigInteger(-1); + string result = convertme.ToBinaryString(); - Assert.AreEqual("11111111", result); - } + Assert.AreEqual("11111111", result); + } /// /// Test. @@ -138,14 +138,14 @@ public void TestToBinaryString2() [TestMethod] public void TestToBinaryString3() { - byte[] bytes = - [ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - ]; - var convertme = new BigInteger(bytes); - string result = convertme.ToBinaryString(); - - Assert.AreEqual("11111111", result); - } + byte[] bytes = + [ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + ]; + var convertme = new BigInteger(bytes); + string result = convertme.ToBinaryString(); + + Assert.AreEqual("11111111", result); + } } \ No newline at end of file diff --git a/src/TestProject/IPAddressTest.cs b/src/TestProject/IPAddressTest.cs new file mode 100644 index 0000000..51b15c9 --- /dev/null +++ b/src/TestProject/IPAddressTest.cs @@ -0,0 +1,25 @@ +namespace TestProject; + +/// +/// Test IPAddress Behaviour on MacOS +/// +[TestClass] +public class IPAddressTest +{ + /// + /// Mixed IPv6 / IPvç notation + /// Why This Happens + /// .NET automatically uses mixed notation for IPv6 addresses when: + /// The first 96 bits are zero (IPv4-compatible) + /// The first 80 bits are zero and bits 81-96 are either all zeros or all ones (IPv4-mapped) + /// The pattern suggests an embedded IPv4 address + /// Your bytes ::0.1.0.0 are being interpreted as an IPv4-compatible IPv6 address. + /// + [TestMethod] + public void TestIPAddress() + { + byte[] bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]; + var ip = new IPAddress(bytes); + Assert.AreEqual("::0.1.0.0", ip.ToString()); + } +} \ No newline at end of file diff --git a/src/TestProject/IPNetworkTest/IPNetworkOperatorTests.cs b/src/TestProject/IPNetworkTest/IPNetworkOperatorTests.cs index 42c7e4f..7eb02cd 100644 --- a/src/TestProject/IPNetworkTest/IPNetworkOperatorTests.cs +++ b/src/TestProject/IPNetworkTest/IPNetworkOperatorTests.cs @@ -5,7 +5,7 @@ namespace TestProject.IPNetworkTest; /// -/// Tests with operators. +/// Tests with operators. /// [TestClass] public class IPNetworkOperatorTests @@ -23,7 +23,7 @@ public void TestOperatorGreater1() Assert.IsFalse(greater, "greater"); } - + /// /// Tests Operator functionality with Operator Greater. /// @@ -37,7 +37,7 @@ public void TestOperatorGreater2() Assert.IsTrue(greater, "greater"); } - + /// /// Tests Operator functionality with Operator Greater. /// @@ -51,8 +51,8 @@ public void TestOperatorGreater3() Assert.IsFalse(greater, "greater"); } - - + + /// /// Tests Operator functionality with Operator Greater. /// @@ -80,7 +80,7 @@ public void TestOperatorGreaterOrEqual1() Assert.IsFalse(greater, "greater"); } - + /// /// Tests Operator functionality with Operator Greater. /// @@ -94,7 +94,7 @@ public void TestOperatorGreaterOrEqual2() Assert.IsTrue(greater, "greater"); } - + /// /// Tests Operator functionality with Operator Greater. /// @@ -108,9 +108,9 @@ public void TestOperatorGreaterOrEqual3() Assert.IsTrue(greater, "greater"); } - + /// - /// Tests Operator functionality with Operator Greater. + /// Tests Operator functionality with Operator Greater. /// [TestMethod] public void TestOperatorGreaterOrEqual4() @@ -122,9 +122,9 @@ public void TestOperatorGreaterOrEqual4() Assert.IsFalse(greater, "greater"); } - + /// - /// Tests Operator functionality with Operator Greater. + /// Tests Operator functionality with Operator Greater. /// [TestMethod] public void TestOperatorGreaterOrEqual5() @@ -166,7 +166,7 @@ public void TestOperatorLower2() } /// - /// Tests Operator functionality with Operator Lower. + /// Tests Operator functionality with Operator Lower. /// [TestMethod] public void TestOperatorLower3() @@ -208,7 +208,7 @@ public void TestOperatorLowerOrEqual2() } /// - /// Tests Operator functionality with Operator Lower. + /// Tests Operator functionality with Operator Lower. /// [TestMethod] public void TestOperatorLowerOrEqual3() @@ -277,9 +277,9 @@ public void TestOperatorEqual2() Assert.IsTrue(eq, "eq"); } - + /// - /// Tests Operator functionality with Operator -. + /// Tests Operator functionality with Operator -. /// [TestMethod] [DataRow("10.0.0.1/32", "10.0.0.1/32", 0)] @@ -297,14 +297,14 @@ public void TestOperatorSubtract(string left, string right, int count) var ipn1 = IPNetwork2.Parse(left); var ipn2 = IPNetwork2.Parse(right); - var result = ipn1 - ipn2; + List result = ipn1 - ipn2; Assert.HasCount(count, result, "subtract"); } - - + + /// - /// Tests Operator functionality with Operator -. + /// Tests Operator functionality with Operator -. /// [TestMethod] [DataRow("10.0.0.0/32", "10.0.0.1/32", 1)] @@ -327,8 +327,98 @@ public void TestOperatorAdd(string left, string right, int count) var ipn1 = IPNetwork2.Parse(left); var ipn2 = IPNetwork2.Parse(right); - var result = ipn1 + ipn2; + List result = ipn1 + ipn2; Assert.HasCount(count, result, "add"); } + + /// + /// Tests Operator functionality with Operator +. + /// + [TestMethod] + [DataRow("10.0.0.0/32", 0, "10.0.0.0/32")] + [DataRow("10.0.0.0/32", 1, "10.0.0.0/31")] + [DataRow("10.0.0.0/32", 2, "10.0.0.0/31, 10.0.0.2/32")] + [DataRow("10.0.0.0/32", 3, "10.0.0.0/30")] + [DataRow("10.0.0.0/32", 4, "10.0.0.0/30, 10.0.0.4/32")] + [DataRow("10.0.0.0/32", 7, "10.0.0.0/29")] + [DataRow("10.0.0.0/32", 15, "10.0.0.0/28")] + [DataRow("10.0.0.0/32", 31, "10.0.0.0/27")] + [DataRow("10.0.0.0/32", 63, "10.0.0.0/26")] + [DataRow("10.0.0.0/32", 127, "10.0.0.0/25")] + [DataRow("10.0.0.0/32", 255, "10.0.0.0/24")] + [DataRow("10.0.0.0/32", 511, "10.0.0.0/23")] + [DataRow("10.0.0.0/32", 1023, "10.0.0.0/22")] + [DataRow("10.0.0.0/32", 2047, "10.0.0.0/21")] + [DataRow("10.0.0.0/32", 4095, "10.0.0.0/20")] + [DataRow("10.0.0.0/32", -1, "")] + [DataRow("10.0.0.0/32", -10, "")] + [DataRow("::/128", 1, "::/127")] + [DataRow("::/128", 3, "::/126")] + [DataRow("::f/128", 1, "::f/128, ::10/128")] + [DataRow("1::ffff/128", 1, "1::ffff/128, 1::1:0/128")] + public void TestOperatorAdd2(string left, int right, string expected) + { + var ipn1 = IPNetwork2.Parse(left); + IEnumerable result = ipn1 + right; + string sresult = string.Join(", ", result); + + Assert.AreEqual(expected, sresult); + } + + /// + /// Tests Operator functionality with Operator +. + /// + [TestMethod] + [DataRow("0.0.0.0/0", 1, "0.0.0.0/32")] + [DataRow("255.255.255.255/32", 1, "10.0.0.0/31")] + public void TestOperatorAddOverflow(string left, int right, string expected) + { + var ipn1 = IPNetwork2.Parse(left); + Assert.ThrowsExactly(() => ipn1 + right); + } + + /// + /// Tests Operator functionality with Operator -. + /// + [TestMethod] + [DataRow("10.0.0.0/32", 0, "10.0.0.0/32")] + [DataRow("10.0.0.0/32", 1, "")] + [DataRow("10.0.0.0/32", 3, "")] + [DataRow("10.0.0.0/32", 7, "")] + [DataRow("10.0.0.0/32", 15, "")] + [DataRow("10.0.0.0/32", 31, "")] + [DataRow("10.0.0.0/32", 63, "")] + [DataRow("10.0.0.0/32", 4095, "")] + [DataRow("10.0.0.0/32", -1, "10.0.0.0/31")] + [DataRow("10.0.0.0/32", -10, "10.0.0.0/29, 10.0.0.8/31, 10.0.0.10/32")] + [DataRow("255.255.255.255/32", 1, "")] + [DataRow("::/128", 1, "")] + [DataRow("::/127", 1, "::/128")] + [DataRow("::/128", 3, "")] + [DataRow("::f/128", 1, "")] + public void TestOperatorsubtract2(string left, int right, string expected) + { + var ipn1 = IPNetwork2.Parse(left); + IEnumerable result = ipn1 - right; + string sresult = string.Join(", ", result); + + Assert.AreEqual(expected, sresult); + } + + /// + /// Tests Operator functionality with Operator -. + /// + [TestMethod] + [DataRow("0.0.0.0/0", -1)] + [DataRow("255.255.255.255/32", -1)] + public void TestOperatorsubtractOverflow(string left, int right) + { + var ipn1 = IPNetwork2.Parse(left); + Assert.ThrowsExactly(() => + { + var result = ipn1 - right; + Assert.AreEqual("32", result.ToString()); + }); + } } \ No newline at end of file