Skip to content

feat: Add IPv6Converter new algorithm with Junit tests #5783

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

Merged
merged 13 commits into from
Oct 26, 2024
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
* [IntegerToEnglish](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IntegerToEnglish.java)
* [IntegerToRoman](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IntegerToRoman.java)
* [IPConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IPConverter.java)
* [IPv6Converter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IPv6Converter.java)
* [MorseCodeConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/MorseCodeConverter.java)
* [OctalToBinary](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/OctalToBinary.java)
* [OctalToDecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/OctalToDecimal.java)
Expand Down Expand Up @@ -784,6 +785,7 @@
* [IntegerToEnglishTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IntegerToEnglishTest.java)
* [IntegerToRomanTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IntegerToRomanTest.java)
* [IPConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IPConverterTest.java)
* [IPv6ConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IPv6ConverterTest.java)
* [MorseCodeConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/MorseCodeConverterTest.java)
* [OctalToBinaryTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/OctalToBinaryTest.java)
* [OctalToDecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/OctalToDecimalTest.java)
Expand Down
98 changes: 98 additions & 0 deletions src/main/java/com/thealgorithms/conversions/IPv6Converter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.thealgorithms.conversions;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

/**
* A utility class for converting between IPv6 and IPv4 addresses.
*
* - Converts IPv4 to IPv6-mapped IPv6 address.
* - Extracts IPv4 address from IPv6-mapped IPv6.
* - Handles exceptions for invalid inputs.
*
* @author Hardvan
*/
public final class IPv6Converter {
private IPv6Converter() {
}

/**
* Converts an IPv4 address (e.g., "192.0.2.128") to an IPv6-mapped IPv6 address.
* Example: IPv4 "192.0.2.128" -> IPv6 "::ffff:192.0.2.128"
*
* @param ipv4Address The IPv4 address in string format.
* @return The corresponding IPv6-mapped IPv6 address.
* @throws UnknownHostException If the IPv4 address is invalid.
* @throws IllegalArgumentException If the IPv6 address is not a mapped IPv4 address.
*/
public static String ipv4ToIpv6(String ipv4Address) throws UnknownHostException {
if (ipv4Address == null || ipv4Address.isEmpty()) {
throw new UnknownHostException("IPv4 address is empty.");
}

InetAddress ipv4 = InetAddress.getByName(ipv4Address);
byte[] ipv4Bytes = ipv4.getAddress();

// Create IPv6-mapped IPv6 address (starts with ::ffff:)
byte[] ipv6Bytes = new byte[16];
ipv6Bytes[10] = (byte) 0xff;
ipv6Bytes[11] = (byte) 0xff;
System.arraycopy(ipv4Bytes, 0, ipv6Bytes, 12, 4);

// Manually format to "::ffff:x.x.x.x" format
StringBuilder ipv6String = new StringBuilder("::ffff:");
for (int i = 12; i < 16; i++) {
ipv6String.append(ipv6Bytes[i] & 0xFF);
if (i < 15) {
ipv6String.append('.');
}
}
return ipv6String.toString();
}

/**
* Extracts the IPv4 address from an IPv6-mapped IPv6 address.
* Example: IPv6 "::ffff:192.0.2.128" -> IPv4 "192.0.2.128"
*
* @param ipv6Address The IPv6 address in string format.
* @return The extracted IPv4 address.
* @throws UnknownHostException If the IPv6 address is invalid or not a mapped IPv4 address.
*/
public static String ipv6ToIpv4(String ipv6Address) throws UnknownHostException {
InetAddress ipv6 = InetAddress.getByName(ipv6Address);
byte[] ipv6Bytes = ipv6.getAddress();

// Check if the address is an IPv6-mapped IPv4 address
if (isValidIpv6MappedIpv4(ipv6Bytes)) {
byte[] ipv4Bytes = Arrays.copyOfRange(ipv6Bytes, 12, 16);
InetAddress ipv4 = InetAddress.getByAddress(ipv4Bytes);
return ipv4.getHostAddress();
} else {
throw new IllegalArgumentException("Not a valid IPv6-mapped IPv4 address.");
}
}

/**
* Helper function to check if the given byte array represents
* an IPv6-mapped IPv4 address (prefix 0:0:0:0:0:ffff).
*
* @param ipv6Bytes Byte array representation of the IPv6 address.
* @return True if the address is IPv6-mapped IPv4, otherwise false.
*/
private static boolean isValidIpv6MappedIpv4(byte[] ipv6Bytes) {
// IPv6-mapped IPv4 addresses are 16 bytes long, with the first 10 bytes set to 0,
// followed by 0xff, 0xff, and the last 4 bytes representing the IPv4 address.
if (ipv6Bytes.length != 16) {
return false;
}

for (int i = 0; i < 10; i++) {
if (ipv6Bytes[i] != 0) {
return false;
}
}

return ipv6Bytes[10] == (byte) 0xff && ipv6Bytes[11] == (byte) 0xff;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.thealgorithms.conversions;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.net.UnknownHostException;
import org.junit.jupiter.api.Test;

public class IPv6ConverterTest {

private static final String VALID_IPV4 = "192."
+ "0."
+ "2."
+ "128";
private static final String EXPECTED_IPV6_MAPPED = ":"
+ ":ff"
+ "ff"
+ ":19"
+ "2."
+ "0."
+ "2.128";
private static final String INVALID_IPV6_MAPPED = "2001:"
+ "db8"
+ ":"
+ ":1";
private static final String INVALID_IPV4 = "999."
+ "999."
+ "999."
+ "999";
private static final String INVALID_IPV6_FORMAT = "invalid:ipv6"
+ "::address";
private static final String EMPTY_STRING = "";

@Test
public void testIpv4ToIpv6ValidInput() throws UnknownHostException {
String actualIpv6 = IPv6Converter.ipv4ToIpv6(VALID_IPV4);
assertEquals(EXPECTED_IPV6_MAPPED, actualIpv6);
}

@Test
public void testIpv6ToIpv4InvalidIPv6MappedAddress() {
assertThrows(IllegalArgumentException.class, () -> IPv6Converter.ipv6ToIpv4(INVALID_IPV6_MAPPED));
}

@Test
public void testIpv4ToIpv6InvalidIPv4Address() {
assertThrows(UnknownHostException.class, () -> IPv6Converter.ipv4ToIpv6(INVALID_IPV4));
}

@Test
public void testIpv6ToIpv4InvalidFormat() {
assertThrows(UnknownHostException.class, () -> IPv6Converter.ipv6ToIpv4(INVALID_IPV6_FORMAT));
}

@Test
public void testIpv4ToIpv6EmptyString() {
assertThrows(UnknownHostException.class, () -> IPv6Converter.ipv4ToIpv6(EMPTY_STRING));
}

@Test
public void testIpv6ToIpv4EmptyString() {
assertThrows(IllegalArgumentException.class, () -> IPv6Converter.ipv6ToIpv4(EMPTY_STRING));
}
}