Data update
This commit is contained in:
parent
4d5544505c
commit
4924dd0264
3073 changed files with 55820 additions and 4408 deletions
128
Task/Parse-an-IP-Address/Dart/parse-an-ip-address.dart
Normal file
128
Task/Parse-an-IP-Address/Dart/parse-an-ip-address.dart
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import 'dart:io';
|
||||
|
||||
class ParseIPAddress {
|
||||
static final RegExp _ipv4Pat = RegExp(r'^(\d+)\.(\d+)\.(\d+)\.(\d+)(?::(\d+))?$');
|
||||
static final RegExp _ipv6DoubleColPat = RegExp(r'^\[?([0-9a-f:]*)::([0-9a-f:]*)(?:\]:(\d+))?$');
|
||||
static late final RegExp _ipv6Pat;
|
||||
|
||||
static void _initializeIPv6Pattern() {
|
||||
String ipv6Pattern = r'^\[?';
|
||||
for (int i = 1; i <= 7; i++) {
|
||||
ipv6Pattern += r'([0-9a-f]+):';
|
||||
}
|
||||
ipv6Pattern += r'([0-9a-f]+)(?:\]:(\d+))?$';
|
||||
_ipv6Pat = RegExp(ipv6Pattern);
|
||||
}
|
||||
|
||||
static void main() {
|
||||
// Initialize the IPv6 pattern
|
||||
_initializeIPv6Pattern();
|
||||
|
||||
List<String> tests = [
|
||||
"192.168.0.1",
|
||||
"127.0.0.1",
|
||||
"256.0.0.1",
|
||||
"127.0.0.1:80",
|
||||
"::1",
|
||||
"[::1]:80",
|
||||
"[32e::12f]:80",
|
||||
"2605:2700:0:3::4713:93e3",
|
||||
"[2605:2700:0:3::4713:93e3]:80",
|
||||
"2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
];
|
||||
|
||||
print('${'Test Case'.padRight(40)} ${'Hex Address'.padRight(32)} Port');
|
||||
|
||||
for (String ip in tests) {
|
||||
try {
|
||||
List<String> parsed = _parseIP(ip);
|
||||
print('${ip.padRight(40)} ${parsed[0].padRight(32)} ${parsed[1]}');
|
||||
} catch (e) {
|
||||
print('${ip.padRight(40)} Invalid address: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> _parseIP(String ip) {
|
||||
String hex = "";
|
||||
String port = "";
|
||||
|
||||
// IPv4
|
||||
RegExpMatch? ipv4Match = _ipv4Pat.firstMatch(ip);
|
||||
if (ipv4Match != null) {
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
hex += _toHex4(ipv4Match.group(i)!);
|
||||
}
|
||||
if (ipv4Match.group(5) != null) {
|
||||
port = ipv4Match.group(5)!;
|
||||
}
|
||||
return [hex, port];
|
||||
}
|
||||
|
||||
// IPv6, double colon
|
||||
RegExpMatch? ipv6DoubleColonMatch = _ipv6DoubleColPat.firstMatch(ip);
|
||||
if (ipv6DoubleColonMatch != null) {
|
||||
String p1 = ipv6DoubleColonMatch.group(1) ?? "";
|
||||
if (p1.isEmpty) {
|
||||
p1 = "0";
|
||||
}
|
||||
String p2 = ipv6DoubleColonMatch.group(2) ?? "";
|
||||
if (p2.isEmpty) {
|
||||
p2 = "0";
|
||||
}
|
||||
ip = p1 + _getZero(8 - _numCount(p1) - _numCount(p2)) + p2;
|
||||
if (ipv6DoubleColonMatch.group(3) != null) {
|
||||
ip = "[$ip]:${ipv6DoubleColonMatch.group(3)}";
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6
|
||||
RegExpMatch? ipv6Match = _ipv6Pat.firstMatch(ip);
|
||||
if (ipv6Match != null) {
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
String hexPart = _toHex6(ipv6Match.group(i)!);
|
||||
hex += hexPart.padLeft(4, '0');
|
||||
}
|
||||
if (ipv6Match.group(9) != null) {
|
||||
port = ipv6Match.group(9)!;
|
||||
}
|
||||
return [hex, port];
|
||||
}
|
||||
|
||||
throw ArgumentError("ERROR 103: Unknown address: $ip");
|
||||
}
|
||||
|
||||
static int _numCount(String s) {
|
||||
return s.split(':').length;
|
||||
}
|
||||
|
||||
static String _getZero(int count) {
|
||||
StringBuffer sb = StringBuffer();
|
||||
sb.write(":");
|
||||
while (count > 0) {
|
||||
sb.write("0:");
|
||||
count--;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static String _toHex4(String s) {
|
||||
int val = int.parse(s);
|
||||
if (val < 0 || val > 255) {
|
||||
throw ArgumentError("ERROR 101: Invalid value: $s");
|
||||
}
|
||||
return val.toRadixString(16).toUpperCase().padLeft(2, '0');
|
||||
}
|
||||
|
||||
static String _toHex6(String s) {
|
||||
int val = int.parse(s, radix: 16);
|
||||
if (val < 0 || val > 65536) {
|
||||
throw ArgumentError("ERROR 102: Invalid hex value: $s");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
ParseIPAddress.main();
|
||||
}
|
||||
83
Task/Parse-an-IP-Address/Elixir/parse-an-ip-address.ex
Normal file
83
Task/Parse-an-IP-Address/Elixir/parse-an-ip-address.ex
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env elixir
|
||||
|
||||
defmodule IPParse do
|
||||
@moduledoc """
|
||||
IP address parser that handles IPv4 and IPv6 addresses with optional ports
|
||||
"""
|
||||
|
||||
def main(args \\ []) do
|
||||
addresses = if Enum.empty?(args), do: data(), else: args
|
||||
|
||||
for addr <- addresses do
|
||||
print(addr, parse(addr))
|
||||
end
|
||||
|
||||
System.halt(0)
|
||||
end
|
||||
|
||||
@spec parse(String.t()) ::
|
||||
{String.t(), binary(), String.t()} |
|
||||
{String.t(), binary()}
|
||||
def parse("[" <> addr0) do
|
||||
case String.split(addr0, "]", parts: 2) do
|
||||
[addr] ->
|
||||
{"IPv6", to_hex(:inet.parse_address(String.to_charlist(addr)))}
|
||||
|
||||
[addr, ":" <> port] ->
|
||||
{"IPv6", to_hex(:inet.parse_address(String.to_charlist(addr))), port}
|
||||
end
|
||||
end
|
||||
|
||||
def parse(addr0) do
|
||||
case :inet.parse_address(String.to_charlist(addr0)) do
|
||||
{:error, :einval} ->
|
||||
[addr, port] = String.split(addr0, ":", parts: 2)
|
||||
{"IPv4", to_hex(:inet.parse_address(String.to_charlist(addr))), port}
|
||||
|
||||
{:ok, v6_addr} ->
|
||||
{"IPv6", to_hex({:ok, v6_addr})}
|
||||
end
|
||||
end
|
||||
|
||||
@spec to_hex({:ok, :inet.ip_address()}) :: binary()
|
||||
def to_hex({:ok, {a, b, c, d}}) do
|
||||
Base.encode16(<<a::8, b::8, c::8, d::8>>)
|
||||
end
|
||||
|
||||
def to_hex({:ok, {a, b, c, d, e, f, g, h}}) do
|
||||
Base.encode16(<<a::16, b::16, c::16, d::16, e::16, f::16, g::16, h::16>>)
|
||||
end
|
||||
|
||||
def print(input, {family, hex, port}) do
|
||||
IO.puts("Input: #{input}")
|
||||
IO.puts("Family: #{family}")
|
||||
IO.puts("Hex: #{hex}")
|
||||
IO.puts("Port: #{port}")
|
||||
IO.puts("")
|
||||
end
|
||||
|
||||
def print(input, {family, hex}) do
|
||||
IO.puts("Input: #{input}")
|
||||
IO.puts("Family: #{family}")
|
||||
IO.puts("Hex: #{hex}")
|
||||
IO.puts("")
|
||||
end
|
||||
|
||||
defp data do
|
||||
[
|
||||
"127.0.0.1",
|
||||
"127.0.0.1:80",
|
||||
"::ffff:127.0.0.1",
|
||||
"::1",
|
||||
"[::1]:80",
|
||||
"1::80",
|
||||
"2605:2700:0:3::4713:93e3",
|
||||
"[2605:2700:0:3::4713:93e3]:80"
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
# Run the main function if this script is executed directly
|
||||
if __ENV__.file == Path.absname(:escript.script_name()) do
|
||||
IPParse.main(System.argv())
|
||||
end
|
||||
320
Task/Parse-an-IP-Address/Fortran/parse-an-ip-address.f
Normal file
320
Task/Parse-an-IP-Address/Fortran/parse-an-ip-address.f
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
program parse_ip_address
|
||||
implicit none
|
||||
|
||||
! Parameters
|
||||
integer, parameter :: MAX_TESTS = 10
|
||||
integer, parameter :: MAX_STR_LEN = 100
|
||||
integer, parameter :: MAX_HEX_LEN = 32
|
||||
|
||||
! Variables
|
||||
character(len=MAX_STR_LEN), dimension(MAX_TESTS) :: test_cases
|
||||
character(len=MAX_HEX_LEN) :: hex_address
|
||||
character(len=10) :: port_str
|
||||
integer :: i, status
|
||||
|
||||
! Initialize test cases
|
||||
test_cases(1) = '192.168.0.1'
|
||||
test_cases(2) = '127.0.0.1'
|
||||
test_cases(3) = '256.0.0.1'
|
||||
test_cases(4) = '127.0.0.1:80'
|
||||
test_cases(5) = '::1'
|
||||
test_cases(6) = '[::1]:80'
|
||||
test_cases(7) = '[32e::12f]:80'
|
||||
test_cases(8) = '2605:2700:0:3::4713:93e3'
|
||||
test_cases(9) = '[2605:2700:0:3::4713:93e3]:80'
|
||||
test_cases(10) = '2001:db8:85a3:0:0:8a2e:370:7334'
|
||||
|
||||
! Print header
|
||||
write(*,'(A40,1X,A32,3X,A)') 'Test Case', 'Hex Address', 'Port'
|
||||
write(*,'(A40,1X,A32,3X,A)') repeat('-',40), repeat('-',32), repeat('-',4)
|
||||
|
||||
! Process each test case
|
||||
do i = 1, MAX_TESTS
|
||||
call parse_ip(trim(test_cases(i)), hex_address, port_str, status)
|
||||
if (status == 0) then
|
||||
write(*,'(A40,1X,A32,3X,A)') trim(test_cases(i)), trim(hex_address), trim(port_str)
|
||||
else
|
||||
write(*,'(A40,1X,A)') trim(test_cases(i)), 'Invalid address'
|
||||
end if
|
||||
end do
|
||||
|
||||
end program parse_ip_address
|
||||
|
||||
! Main parsing subroutine
|
||||
subroutine parse_ip(ip_str, hex_address, port_str, status)
|
||||
implicit none
|
||||
character(len=*), intent(in) :: ip_str
|
||||
character(len=*), intent(out) :: hex_address, port_str
|
||||
integer, intent(out) :: status
|
||||
|
||||
! Try IPv4 first
|
||||
call parse_ipv4(ip_str, hex_address, port_str, status)
|
||||
if (status == 0) return
|
||||
|
||||
! Try IPv6
|
||||
call parse_ipv6(ip_str, hex_address, port_str, status)
|
||||
if (status == 0) return
|
||||
|
||||
! If neither worked, return error
|
||||
status = -1
|
||||
|
||||
end subroutine parse_ip
|
||||
|
||||
! Parse IPv4 address
|
||||
subroutine parse_ipv4(ip_str, hex_address, port_str, status)
|
||||
implicit none
|
||||
character(len=*), intent(in) :: ip_str
|
||||
character(len=*), intent(out) :: hex_address, port_str
|
||||
integer, intent(out) :: status
|
||||
|
||||
character(len=100) :: work_str
|
||||
integer :: dot_pos(3), colon_pos
|
||||
integer :: octets(4)
|
||||
integer :: i, start_pos, end_pos, port_val
|
||||
character(len=2) :: hex_part
|
||||
|
||||
work_str = trim(ip_str)
|
||||
hex_address = ''
|
||||
port_str = ''
|
||||
status = 0
|
||||
|
||||
! Find colon for port (if present)
|
||||
colon_pos = index(work_str, ':', .true.) ! Find last colon
|
||||
if (colon_pos > 0) then
|
||||
! Extract port
|
||||
read(work_str(colon_pos+1:), *, iostat=status) port_val
|
||||
if (status /= 0) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
write(port_str, '(I0)') port_val
|
||||
work_str = work_str(1:colon_pos-1)
|
||||
end if
|
||||
|
||||
! Find dots
|
||||
dot_pos(1) = index(work_str, '.')
|
||||
if (dot_pos(1) == 0) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
|
||||
dot_pos(2) = index(work_str(dot_pos(1)+1:), '.') + dot_pos(1)
|
||||
if (dot_pos(2) == dot_pos(1)) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
|
||||
dot_pos(3) = index(work_str(dot_pos(2)+1:), '.') + dot_pos(2)
|
||||
if (dot_pos(3) == dot_pos(2)) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
|
||||
! Parse octets
|
||||
read(work_str(1:dot_pos(1)-1), *, iostat=status) octets(1)
|
||||
if (status /= 0 .or. octets(1) < 0 .or. octets(1) > 255) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
|
||||
read(work_str(dot_pos(1)+1:dot_pos(2)-1), *, iostat=status) octets(2)
|
||||
if (status /= 0 .or. octets(2) < 0 .or. octets(2) > 255) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
|
||||
read(work_str(dot_pos(2)+1:dot_pos(3)-1), *, iostat=status) octets(3)
|
||||
if (status /= 0 .or. octets(3) < 0 .or. octets(3) > 255) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
|
||||
read(work_str(dot_pos(3)+1:), *, iostat=status) octets(4)
|
||||
if (status /= 0 .or. octets(4) < 0 .or. octets(4) > 255) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
|
||||
! Convert to hex
|
||||
do i = 1, 4
|
||||
call int_to_hex2(octets(i), hex_part)
|
||||
hex_address = trim(hex_address) // hex_part
|
||||
end do
|
||||
|
||||
status = 0
|
||||
|
||||
end subroutine parse_ipv4
|
||||
|
||||
! Parse IPv6 address (simplified version)
|
||||
subroutine parse_ipv6(ip_str, hex_address, port_str, status)
|
||||
implicit none
|
||||
character(len=*), intent(in) :: ip_str
|
||||
character(len=*), intent(out) :: hex_address, port_str
|
||||
integer, intent(out) :: status
|
||||
|
||||
character(len=100) :: work_str, expanded_str
|
||||
integer :: bracket_start, bracket_end, colon_pos
|
||||
integer :: port_val
|
||||
|
||||
work_str = trim(ip_str)
|
||||
hex_address = ''
|
||||
port_str = ''
|
||||
status = 0
|
||||
|
||||
! Handle bracketed IPv6 with port
|
||||
bracket_start = index(work_str, '[')
|
||||
bracket_end = index(work_str, ']')
|
||||
|
||||
if (bracket_start > 0 .and. bracket_end > bracket_start) then
|
||||
! Extract IPv6 part
|
||||
work_str = work_str(bracket_start+1:bracket_end-1)
|
||||
|
||||
! Check for port after bracket
|
||||
colon_pos = index(ip_str(bracket_end+1:), ':')
|
||||
if (colon_pos > 0) then
|
||||
read(ip_str(bracket_end+colon_pos+1:), *, iostat=status) port_val
|
||||
if (status /= 0) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
write(port_str, '(I0)') port_val
|
||||
end if
|
||||
end if
|
||||
|
||||
! Expand double colon if present
|
||||
call expand_ipv6(work_str, expanded_str, status)
|
||||
if (status /= 0) return
|
||||
|
||||
! Convert expanded IPv6 to hex
|
||||
call ipv6_to_hex(expanded_str, hex_address, status)
|
||||
|
||||
end subroutine parse_ipv6
|
||||
|
||||
! Expand IPv6 double colon notation
|
||||
subroutine expand_ipv6(ipv6_str, expanded_str, status)
|
||||
implicit none
|
||||
character(len=*), intent(in) :: ipv6_str
|
||||
character(len=*), intent(out) :: expanded_str
|
||||
integer, intent(out) :: status
|
||||
|
||||
character(len=100) :: work_str
|
||||
integer :: double_colon_pos, colon_count, zeros_needed
|
||||
integer :: i, pos
|
||||
|
||||
work_str = trim(ipv6_str)
|
||||
expanded_str = ''
|
||||
status = 0
|
||||
|
||||
! Find double colon
|
||||
double_colon_pos = index(work_str, '::')
|
||||
|
||||
if (double_colon_pos == 0) then
|
||||
! No double colon, just copy
|
||||
expanded_str = work_str
|
||||
return
|
||||
end if
|
||||
|
||||
! Count existing colons (excluding the double colon)
|
||||
colon_count = 0
|
||||
do i = 1, len_trim(work_str)
|
||||
if (work_str(i:i) == ':') colon_count = colon_count + 1
|
||||
end do
|
||||
colon_count = colon_count - 2 ! Subtract the double colon
|
||||
|
||||
! Calculate zeros needed
|
||||
zeros_needed = 8 - colon_count - 1
|
||||
if (double_colon_pos == 1) zeros_needed = zeros_needed + 1
|
||||
if (double_colon_pos == len_trim(work_str)-1) zeros_needed = zeros_needed + 1
|
||||
|
||||
! Build expanded string
|
||||
if (double_colon_pos == 1) then
|
||||
expanded_str = '0'
|
||||
do i = 1, zeros_needed - 1
|
||||
expanded_str = trim(expanded_str) // ':0'
|
||||
end do
|
||||
if (len_trim(work_str) > 2) then
|
||||
expanded_str = trim(expanded_str) // work_str(3:)
|
||||
end if
|
||||
else if (double_colon_pos == len_trim(work_str)-1) then
|
||||
expanded_str = work_str(1:double_colon_pos-1)
|
||||
do i = 1, zeros_needed
|
||||
expanded_str = trim(expanded_str) // ':0'
|
||||
end do
|
||||
else
|
||||
expanded_str = work_str(1:double_colon_pos-1)
|
||||
do i = 1, zeros_needed
|
||||
expanded_str = trim(expanded_str) // ':0'
|
||||
end do
|
||||
expanded_str = trim(expanded_str) // work_str(double_colon_pos+2:)
|
||||
end if
|
||||
|
||||
end subroutine expand_ipv6
|
||||
|
||||
! Convert expanded IPv6 to hex
|
||||
subroutine ipv6_to_hex(ipv6_str, hex_address, status)
|
||||
implicit none
|
||||
character(len=*), intent(in) :: ipv6_str
|
||||
character(len=*), intent(out) :: hex_address
|
||||
integer, intent(out) :: status
|
||||
|
||||
character(len=100) :: work_str
|
||||
character(len=10) :: segment
|
||||
character(len=4) :: hex_segment
|
||||
integer :: colon_pos, start_pos, i
|
||||
|
||||
work_str = trim(ipv6_str) // ':' ! Add trailing colon for easier parsing
|
||||
hex_address = ''
|
||||
start_pos = 1
|
||||
status = 0
|
||||
|
||||
do i = 1, 8
|
||||
colon_pos = index(work_str(start_pos:), ':')
|
||||
if (colon_pos == 0) then
|
||||
status = -1
|
||||
return
|
||||
end if
|
||||
colon_pos = colon_pos + start_pos - 1
|
||||
|
||||
segment = work_str(start_pos:colon_pos-1)
|
||||
if (len_trim(segment) == 0) segment = '0'
|
||||
|
||||
! Pad to 4 hex digits
|
||||
write(hex_segment, '(A4)') segment
|
||||
call pad_hex(hex_segment)
|
||||
hex_address = trim(hex_address) // hex_segment
|
||||
|
||||
start_pos = colon_pos + 1
|
||||
end do
|
||||
|
||||
end subroutine ipv6_to_hex
|
||||
|
||||
! Convert integer to 2-digit hex
|
||||
subroutine int_to_hex2(val, hex_str)
|
||||
implicit none
|
||||
integer, intent(in) :: val
|
||||
character(len=2), intent(out) :: hex_str
|
||||
|
||||
character(len=16), parameter :: hex_digits = '0123456789abcdef'
|
||||
|
||||
hex_str(1:1) = hex_digits(val/16 + 1:val/16 + 1)
|
||||
hex_str(2:2) = hex_digits(mod(val,16) + 1:mod(val,16) + 1)
|
||||
|
||||
end subroutine int_to_hex2
|
||||
|
||||
! Pad hex string with leading zeros
|
||||
subroutine pad_hex(hex_str)
|
||||
implicit none
|
||||
character(len=4), intent(inout) :: hex_str
|
||||
|
||||
integer :: i, j
|
||||
character(len=4) :: temp_str
|
||||
|
||||
! Remove leading spaces and pad with zeros
|
||||
temp_str = adjustl(hex_str)
|
||||
hex_str = '0000'
|
||||
j = 4 - len_trim(temp_str) + 1
|
||||
if (j <= 4) then
|
||||
hex_str(j:4) = trim(temp_str)
|
||||
end if
|
||||
|
||||
end subroutine pad_hex
|
||||
123
Task/Parse-an-IP-Address/JavaScript/parse-an-ip-address.js
Normal file
123
Task/Parse-an-IP-Address/JavaScript/parse-an-ip-address.js
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// IP Address Parser in JavaScript
|
||||
class ParseIPAddress {
|
||||
constructor() {
|
||||
this.IPV4_PAT = /^(\d+)\.(\d+)\.(\d+)\.(\d+)(?::(\d+))?$/;
|
||||
this.IPV6_DOUBL_COL_PAT = /^\[?([0-9a-f:]*)::([0-9a-f:]*)(?:\]:(\d+))?$/;
|
||||
|
||||
// Build IPv6 pattern dynamically
|
||||
let ipv6Pattern = "^\\[?";
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
ipv6Pattern += "([0-9a-f]+):";
|
||||
}
|
||||
ipv6Pattern += "([0-9a-f]+)(?:\\]:(\\d+))?$";
|
||||
this.IPV6_PAT = new RegExp(ipv6Pattern);
|
||||
}
|
||||
|
||||
static main() {
|
||||
const tests = [
|
||||
"192.168.0.1",
|
||||
"127.0.0.1",
|
||||
"256.0.0.1",
|
||||
"127.0.0.1:80",
|
||||
"::1",
|
||||
"[::1]:80",
|
||||
"[32e::12f]:80",
|
||||
"2605:2700:0:3::4713:93e3",
|
||||
"[2605:2700:0:3::4713:93e3]:80",
|
||||
"2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
];
|
||||
|
||||
const parser = new ParseIPAddress();
|
||||
|
||||
console.log(`${"Test Case".padEnd(40)} ${"Hex Address".padEnd(32)} Port`);
|
||||
|
||||
for (const ip of tests) {
|
||||
try {
|
||||
const parsed = parser.parseIP(ip);
|
||||
console.log(`${ip.padEnd(40)} ${parsed[0].padEnd(32)} ${parsed[1]}`);
|
||||
} catch (e) {
|
||||
console.log(`${ip.padEnd(40)} Invalid address: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseIP(ip) {
|
||||
let hex = "";
|
||||
let port = "";
|
||||
|
||||
// IPv4
|
||||
const ipv4Match = ip.match(this.IPV4_PAT);
|
||||
if (ipv4Match) {
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
hex += this.toHex4(ipv4Match[i]);
|
||||
}
|
||||
if (ipv4Match[5] !== undefined) {
|
||||
port = ipv4Match[5];
|
||||
}
|
||||
return [hex, port];
|
||||
}
|
||||
|
||||
// IPv6, double colon
|
||||
const ipv6DoubleColonMatch = ip.match(this.IPV6_DOUBL_COL_PAT);
|
||||
if (ipv6DoubleColonMatch) {
|
||||
let p1 = ipv6DoubleColonMatch[1];
|
||||
if (p1 === "") {
|
||||
p1 = "0";
|
||||
}
|
||||
let p2 = ipv6DoubleColonMatch[2];
|
||||
if (p2 === "") {
|
||||
p2 = "0";
|
||||
}
|
||||
ip = p1 + this.getZero(8 - this.numCount(p1) - this.numCount(p2)) + p2;
|
||||
if (ipv6DoubleColonMatch[3] !== undefined) {
|
||||
ip = "[" + ip + "]:" + ipv6DoubleColonMatch[3];
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6
|
||||
const ipv6Match = ip.match(this.IPV6_PAT);
|
||||
if (ipv6Match) {
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
hex += this.toHex6(ipv6Match[i]).padStart(4, "0");
|
||||
}
|
||||
if (ipv6Match[9] !== undefined) {
|
||||
port = ipv6Match[9];
|
||||
}
|
||||
return [hex, port];
|
||||
}
|
||||
|
||||
throw new Error("ERROR 103: Unknown address: " + ip);
|
||||
}
|
||||
|
||||
numCount(s) {
|
||||
return s.split(":").length;
|
||||
}
|
||||
|
||||
getZero(count) {
|
||||
let result = ":";
|
||||
while (count > 0) {
|
||||
result += "0:";
|
||||
count--;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
toHex4(s) {
|
||||
const val = parseInt(s, 10);
|
||||
if (val < 0 || val > 255) {
|
||||
throw new Error("ERROR 101: Invalid value : " + s);
|
||||
}
|
||||
return val.toString(16).padStart(2, "0");
|
||||
}
|
||||
|
||||
toHex6(s) {
|
||||
const val = parseInt(s, 16);
|
||||
if (val < 0 || val > 65536) {
|
||||
throw new Error("ERROR 102: Invalid hex value : " + s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
ParseIPAddress.main();
|
||||
100
Task/Parse-an-IP-Address/Mathematica/parse-an-ip-address.math
Normal file
100
Task/Parse-an-IP-Address/Mathematica/parse-an-ip-address.math
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
testdata = {"127.0.0.1", "127.0.0.1:80", "::1", "[::1]:80",
|
||||
"2605:2700:0:3::4713:93e3", "[2605:2700:0:3::4713:93e3]:80",
|
||||
"::ffff:192.168.173.22", "[::ffff:192.168.173.22]:80",
|
||||
"1::", "[1::]:80", "::", "[::]:80"};
|
||||
|
||||
maybev4[ip_] := StringCount[ip, "."] > 0 && StringCount[ip, ":"] < 2
|
||||
|
||||
maybev6[ip_] := StringCount[ip, ":"] > 1
|
||||
|
||||
parseip[ip_] := Module[{mat, port, ipaddr, iphex, addressspace},
|
||||
(* Try to match IPv6 with brackets and port *)
|
||||
mat = StringCases[ip,
|
||||
RegularExpression["^\\[([:.\\da-fA-F]+)\\]:(\\d+)$"] -> {"$1", "$2"}];
|
||||
|
||||
If[Length[mat] > 0,
|
||||
{ipaddr, port} = First[mat],
|
||||
(* Try to match IPv4 with port *)
|
||||
mat = StringCases[ip,
|
||||
RegularExpression["^([\\d.]+)[:/](\\d+)$"] -> {"$1", "$2"}];
|
||||
If[Length[mat] > 0,
|
||||
{ipaddr, port} = First[mat],
|
||||
(* No port found *)
|
||||
{ipaddr, port} = {ip, "none"}
|
||||
]
|
||||
];
|
||||
|
||||
If[maybev4[ipaddr],
|
||||
Print["Processing ip v4 ", ipaddr];
|
||||
(* Convert IPv4 to hex *)
|
||||
Module[{octets, ipint},
|
||||
octets = ToExpression /@ StringSplit[ipaddr, "."];
|
||||
ipint = Total[MapIndexed[#1*256^(4 - First[#2]) &, octets]];
|
||||
iphex = IntegerString[ipint, 16];
|
||||
];
|
||||
addressspace = "IPv4",
|
||||
|
||||
If[maybev6[ipaddr],
|
||||
Print["Processing ip v6 ", ipaddr];
|
||||
(* Convert IPv6 to hex *)
|
||||
Module[{cleanip, parts, expandedparts, fullhex},
|
||||
cleanip = StringReplace[ipaddr, {"[" -> "", "]" -> ""}];
|
||||
|
||||
(* Handle embedded IPv4 addresses *)
|
||||
If[StringContainsQ[cleanip, "."],
|
||||
Module[{parts, ipv4part, ipv6parts, ipv4octets, ipv4hex},
|
||||
parts = StringSplit[cleanip, ":"];
|
||||
(* Find the part with dots (IPv4) *)
|
||||
ipv4part = SelectFirst[parts, StringContainsQ[#, "."] &];
|
||||
ipv6parts = DeleteCases[parts, ipv4part];
|
||||
|
||||
If[ipv4part =!= Missing["NotFound"],
|
||||
ipv4octets = ToExpression /@ StringSplit[ipv4part, "."];
|
||||
(* Convert IPv4 to two 16-bit hex values *)
|
||||
ipv4hex = {
|
||||
IntegerString[ipv4octets[[1]]*256 + ipv4octets[[2]], 16],
|
||||
IntegerString[ipv4octets[[3]]*256 + ipv4octets[[4]], 16]
|
||||
};
|
||||
cleanip = StringJoin[Riffle[Join[ipv6parts, ipv4hex], ":"]];
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
(* Handle :: expansion *)
|
||||
If[StringContainsQ[cleanip, "::"],
|
||||
parts = StringSplit[cleanip, "::"];
|
||||
If[Length[parts] == 2,
|
||||
Module[{left, right, leftparts, rightparts, missing},
|
||||
left = If[parts[[1]] == "", {}, StringSplit[parts[[1]], ":"]];
|
||||
right = If[parts[[2]] == "", {}, StringSplit[parts[[2]], ":"]];
|
||||
missing = 8 - Length[left] - Length[right];
|
||||
expandedparts = Join[left, Table["0", missing], right];
|
||||
],
|
||||
expandedparts = StringSplit[cleanip, ":"]
|
||||
],
|
||||
expandedparts = StringSplit[cleanip, ":"]
|
||||
];
|
||||
|
||||
(* Pad each part to 4 hex digits and join *)
|
||||
expandedparts = StringPadLeft[#, 4, "0"] & /@ expandedparts;
|
||||
fullhex = StringJoin[expandedparts];
|
||||
|
||||
(* Remove leading zeros for final output *)
|
||||
iphex = IntegerString[FromDigits[fullhex, 16], 16];
|
||||
];
|
||||
addressspace = "IPv6",
|
||||
|
||||
(* Neither IPv4 nor IPv6 *)
|
||||
Throw["Bad IP address argument " <> ipaddr]
|
||||
]
|
||||
];
|
||||
|
||||
{iphex, addressspace, port}
|
||||
]
|
||||
|
||||
(* Test the function *)
|
||||
Do[
|
||||
{hx, add, por} = parseip[ip];
|
||||
Print["For input ", ip, ", IP in hex is ", hx, ", address space ", add, ", port ", por, "."],
|
||||
{ip, testdata}
|
||||
]
|
||||
192
Task/Parse-an-IP-Address/OCaml/parse-an-ip-address.ml
Normal file
192
Task/Parse-an-IP-Address/OCaml/parse-an-ip-address.ml
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
open Printf
|
||||
|
||||
(* Custom exception for invalid addresses *)
|
||||
exception Invalid_address of string
|
||||
|
||||
(* Helper functions for string parsing *)
|
||||
let is_digit c = c >= '0' && c <= '9'
|
||||
let is_hex_digit c = is_digit c || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
|
||||
|
||||
let split_string s delimiter =
|
||||
let len = String.length s in
|
||||
let rec aux acc start i =
|
||||
if i >= len then
|
||||
if start < len then (String.sub s start (len - start)) :: acc
|
||||
else acc
|
||||
else if s.[i] = delimiter then
|
||||
let part = String.sub s start (i - start) in
|
||||
aux (part :: acc) (i + 1) (i + 1)
|
||||
else
|
||||
aux acc start (i + 1)
|
||||
in
|
||||
List.rev (aux [] 0 0)
|
||||
|
||||
let string_contains s substr =
|
||||
let s_len = String.length s in
|
||||
let substr_len = String.length substr in
|
||||
let rec aux i =
|
||||
if i > s_len - substr_len then false
|
||||
else if String.sub s i substr_len = substr then true
|
||||
else aux (i + 1)
|
||||
in
|
||||
if substr_len = 0 then true else aux 0
|
||||
|
||||
(* Check if string contains only digits *)
|
||||
let is_numeric s =
|
||||
let len = String.length s in
|
||||
len > 0 &&
|
||||
let rec aux i =
|
||||
if i >= len then true
|
||||
else if is_digit s.[i] then aux (i + 1)
|
||||
else false
|
||||
in
|
||||
aux 0
|
||||
|
||||
(* Check if string contains only hex digits *)
|
||||
let is_hex s =
|
||||
let len = String.length s in
|
||||
len > 0 &&
|
||||
let rec aux i =
|
||||
if i >= len then true
|
||||
else if is_hex_digit (Char.lowercase_ascii s.[i]) then aux (i + 1)
|
||||
else false
|
||||
in
|
||||
aux 0
|
||||
|
||||
(* Convert decimal string to 2-digit hex for IPv4 *)
|
||||
let to_hex4 s =
|
||||
if not (is_numeric s) then
|
||||
raise (Invalid_address ("ERROR 101: Invalid value : " ^ s));
|
||||
let val_int = int_of_string s in
|
||||
if val_int < 0 || val_int > 255 then
|
||||
raise (Invalid_address ("ERROR 101: Invalid value : " ^ s))
|
||||
else
|
||||
Printf.sprintf "%02x" val_int
|
||||
|
||||
(* Validate and return hex string for IPv6 *)
|
||||
let to_hex6 s =
|
||||
if not (is_hex s) then
|
||||
raise (Invalid_address ("ERROR 102: Invalid hex value : " ^ s));
|
||||
let val_int = int_of_string ("0x" ^ s) in
|
||||
if val_int < 0 || val_int > 65535 then
|
||||
raise (Invalid_address ("ERROR 102: Invalid hex value : " ^ s))
|
||||
else
|
||||
s
|
||||
|
||||
(* Parse IPv4 address *)
|
||||
let parse_ipv4 ip =
|
||||
let parts = split_string ip '.' in
|
||||
match parts with
|
||||
| [p1; p2; p3; p4_port] ->
|
||||
(* Check if last part has port *)
|
||||
if string_contains p4_port ":" then
|
||||
let port_parts = split_string p4_port ':' in
|
||||
(match port_parts with
|
||||
| [p4; port] when is_numeric p4 && is_numeric port ->
|
||||
let hex = (to_hex4 p1) ^ (to_hex4 p2) ^ (to_hex4 p3) ^ (to_hex4 p4) in
|
||||
(hex, port)
|
||||
| _ -> raise (Invalid_address ("ERROR 103: Unknown address: " ^ ip)))
|
||||
else if is_numeric p4_port then
|
||||
let hex = (to_hex4 p1) ^ (to_hex4 p2) ^ (to_hex4 p3) ^ (to_hex4 p4_port) in
|
||||
(hex, "")
|
||||
else
|
||||
raise (Invalid_address ("ERROR 103: Unknown address: " ^ ip))
|
||||
| _ -> raise (Invalid_address ("ERROR 103: Unknown address: " ^ ip))
|
||||
|
||||
(* Parse IPv6 address *)
|
||||
let parse_ipv6 ip =
|
||||
let (clean_ip, port) =
|
||||
if String.length ip > 0 && ip.[0] = '[' then
|
||||
(* Bracketed IPv6 with possible port *)
|
||||
try
|
||||
let bracket_end = String.index ip ']' in
|
||||
let addr_part = String.sub ip 1 (bracket_end - 1) in
|
||||
if bracket_end + 1 < String.length ip && ip.[bracket_end + 1] = ':' then
|
||||
let port_part = String.sub ip (bracket_end + 2) (String.length ip - bracket_end - 2) in
|
||||
(addr_part, port_part)
|
||||
else
|
||||
(addr_part, "")
|
||||
with Not_found ->
|
||||
raise (Invalid_address ("ERROR 103: Unknown address: " ^ ip))
|
||||
else
|
||||
(ip, "") in
|
||||
|
||||
(* Handle double colon expansion *)
|
||||
let expanded_ip =
|
||||
if string_contains clean_ip "::" then
|
||||
(* Split on :: to get before and after parts *)
|
||||
let double_colon_pos =
|
||||
let rec find_pos i =
|
||||
if i >= String.length clean_ip - 1 then -1
|
||||
else if clean_ip.[i] = ':' && clean_ip.[i+1] = ':' then i
|
||||
else find_pos (i + 1)
|
||||
in
|
||||
find_pos 0 in
|
||||
|
||||
if double_colon_pos = -1 then
|
||||
raise (Invalid_address ("ERROR 103: Unknown address: " ^ ip))
|
||||
else
|
||||
let before_part =
|
||||
if double_colon_pos = 0 then ""
|
||||
else String.sub clean_ip 0 double_colon_pos in
|
||||
let after_part =
|
||||
let start = double_colon_pos + 2 in
|
||||
if start >= String.length clean_ip then ""
|
||||
else String.sub clean_ip start (String.length clean_ip - start) in
|
||||
|
||||
let before_groups = if before_part = "" then [] else split_string before_part ':' in
|
||||
let after_groups = if after_part = "" then [] else split_string after_part ':' in
|
||||
let total_existing = List.length before_groups + List.length after_groups in
|
||||
let zeros_needed = 8 - total_existing in
|
||||
let zero_groups = List.init zeros_needed (fun _ -> "0") in
|
||||
|
||||
String.concat ":" (before_groups @ zero_groups @ after_groups)
|
||||
else
|
||||
clean_ip in
|
||||
|
||||
(* Parse the expanded IPv6 *)
|
||||
let hex_parts = split_string expanded_ip ':' in
|
||||
if List.length hex_parts = 8 then
|
||||
let hex = String.concat "" (List.map (fun part ->
|
||||
Printf.sprintf "%04s" (to_hex6 part) |>
|
||||
String.map (function ' ' -> '0' | c -> c)) hex_parts) in
|
||||
(hex, port)
|
||||
else
|
||||
raise (Invalid_address ("ERROR 103: Unknown address: " ^ ip))
|
||||
|
||||
(* Main parsing function *)
|
||||
let parse_ip ip =
|
||||
(* Check if it looks like IPv4 (contains dots but no colons except maybe one for port) *)
|
||||
if string_contains ip "." &&
|
||||
(not (string_contains ip ":") ||
|
||||
(let colon_count = List.length (split_string ip ':') - 1 in colon_count = 1)) then
|
||||
parse_ipv4 ip
|
||||
else if string_contains ip ":" then
|
||||
parse_ipv6 ip
|
||||
else
|
||||
raise (Invalid_address ("ERROR 103: Unknown address: " ^ ip))
|
||||
|
||||
(* Test cases *)
|
||||
let tests = [|
|
||||
"192.168.0.1";
|
||||
"127.0.0.1";
|
||||
"256.0.0.1";
|
||||
"127.0.0.1:80";
|
||||
"::1";
|
||||
"[::1]:80";
|
||||
"[32e::12f]:80";
|
||||
"2605:2700:0:3::4713:93e3";
|
||||
"[2605:2700:0:3::4713:93e3]:80";
|
||||
"2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
|]
|
||||
|
||||
(* Main function *)
|
||||
let () =
|
||||
printf "%-40s %-32s %s\n" "Test Case" "Hex Address" "Port";
|
||||
Array.iter (fun ip ->
|
||||
try
|
||||
let (hex_addr, port) = parse_ip ip in
|
||||
printf "%-40s %-32s %s\n" ip hex_addr port
|
||||
with Invalid_address msg ->
|
||||
printf "%-40s Invalid address: %s\n" ip msg
|
||||
) tests
|
||||
127
Task/Parse-an-IP-Address/R/parse-an-ip-address.r
Normal file
127
Task/Parse-an-IP-Address/R/parse-an-ip-address.r
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# IP Address Parser in R
|
||||
library(stringr)
|
||||
|
||||
# Helper function to convert decimal to 2-digit hex (for IPv4)
|
||||
to_hex4 <- function(s) {
|
||||
val <- as.integer(s)
|
||||
if (is.na(val) || val < 0 || val > 255) {
|
||||
stop(paste("ERROR 101: Invalid value:", s))
|
||||
}
|
||||
sprintf("%02x", val)
|
||||
}
|
||||
|
||||
# Helper function to validate and return hex value (for IPv6)
|
||||
to_hex6 <- function(s) {
|
||||
val <- strtoi(s, base = 16)
|
||||
if (is.na(val) || val < 0 || val > 65535) { # Fixed: should be 65535, not 65536
|
||||
stop(paste("ERROR 102: Invalid hex value:", s))
|
||||
}
|
||||
return(s)
|
||||
}
|
||||
|
||||
# Helper function to count number of parts separated by colons
|
||||
num_count <- function(s) {
|
||||
if (s == "") return(0)
|
||||
length(strsplit(s, ":")[[1]])
|
||||
}
|
||||
|
||||
# Helper function to generate zero padding for IPv6 double colon expansion
|
||||
get_zero <- function(count) {
|
||||
if (count <= 0) return("")
|
||||
paste0(":", paste(rep("0", count), collapse = ":"), ":")
|
||||
}
|
||||
|
||||
# Main parsing function
|
||||
parse_ip <- function(ip) {
|
||||
hex <- ""
|
||||
port <- ""
|
||||
|
||||
# IPv4 pattern: xxx.xxx.xxx.xxx with optional :port
|
||||
ipv4_pattern <- "^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)(?::(\\d+))?$"
|
||||
ipv4_match <- str_match(ip, ipv4_pattern)
|
||||
|
||||
if (!is.na(ipv4_match[1])) {
|
||||
# Extract IPv4 components
|
||||
for (i in 2:5) {
|
||||
hex <- paste0(hex, to_hex4(ipv4_match[i]))
|
||||
}
|
||||
if (!is.na(ipv4_match[6])) {
|
||||
port <- ipv4_match[6]
|
||||
}
|
||||
return(c(hex, port))
|
||||
}
|
||||
|
||||
# IPv6 with double colon pattern
|
||||
ipv6_double_colon_pattern <- "^\\[?([0-9a-f:]*)::([0-9a-f:]*)(?:\\]:(\\d+))?$"
|
||||
ipv6_dc_match <- str_match(tolower(ip), ipv6_double_colon_pattern)
|
||||
|
||||
if (!is.na(ipv6_dc_match[1])) {
|
||||
p1 <- ipv6_dc_match[2]
|
||||
if (p1 == "") p1 <- "0"
|
||||
|
||||
p2 <- ipv6_dc_match[3]
|
||||
if (p2 == "") p2 <- "0"
|
||||
|
||||
# Expand the double colon
|
||||
zero_count <- 8 - num_count(p1) - num_count(p2)
|
||||
expanded_ip <- paste0(p1, get_zero(zero_count), p2)
|
||||
|
||||
# Add port back if it exists
|
||||
if (!is.na(ipv6_dc_match[4])) {
|
||||
expanded_ip <- paste0("[", expanded_ip, "]:", ipv6_dc_match[4])
|
||||
}
|
||||
|
||||
ip <- expanded_ip
|
||||
}
|
||||
|
||||
# IPv6 full pattern (8 groups of hex digits)
|
||||
ipv6_pattern <- "^\\[?([0-9a-f]+):([0-9a-f]+):([0-9a-f]+):([0-9a-f]+):([0-9a-f]+):([0-9a-f]+):([0-9a-f]+):([0-9a-f]+)(?:\\]:(\\d+))?$"
|
||||
ipv6_match <- str_match(tolower(ip), ipv6_pattern)
|
||||
|
||||
if (!is.na(ipv6_match[1])) {
|
||||
# Extract IPv6 components
|
||||
for (i in 2:9) {
|
||||
hex_part <- to_hex6(ipv6_match[i])
|
||||
# Pad to 4 characters
|
||||
hex <- paste0(hex, sprintf("%04s", hex_part))
|
||||
}
|
||||
hex <- str_replace_all(hex, " ", "0")
|
||||
|
||||
if (!is.na(ipv6_match[10])) {
|
||||
port <- ipv6_match[10]
|
||||
}
|
||||
return(c(hex, port))
|
||||
}
|
||||
|
||||
stop(paste("ERROR 103: Unknown address:", ip))
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main <- function() {
|
||||
tests <- c(
|
||||
"192.168.0.1",
|
||||
"127.0.0.1",
|
||||
"256.0.0.1",
|
||||
"127.0.0.1:80",
|
||||
"::1",
|
||||
"[::1]:80",
|
||||
"[32e::12f]:80",
|
||||
"2605:2700:0:3::4713:93e3",
|
||||
"[2605:2700:0:3::4713:93e3]:80",
|
||||
"2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
)
|
||||
|
||||
cat(sprintf("%-40s %-32s %s\n", "Test Case", "Hex Address", "Port"))
|
||||
|
||||
for (ip in tests) {
|
||||
tryCatch({
|
||||
parsed <- parse_ip(ip)
|
||||
cat(sprintf("%-40s %-32s %s\n", ip, parsed[1], parsed[2]))
|
||||
}, error = function(e) {
|
||||
cat(sprintf("%-40s Invalid address: %s\n", ip, e$message))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
# Run the main function
|
||||
main()
|
||||
354
Task/Parse-an-IP-Address/Rust/parse-an-ip-address-2.rs
Normal file
354
Task/Parse-an-IP-Address/Rust/parse-an-ip-address-2.rs
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum IpAddr {
|
||||
V4([u8; 4]),
|
||||
V6([u8; 16]),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseResult {
|
||||
pub addr: IpAddr,
|
||||
pub port: Option<u16>,
|
||||
pub consumed: usize,
|
||||
}
|
||||
|
||||
pub struct Parser<'a> {
|
||||
input: &'a str,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn new(input: &'a str) -> Self {
|
||||
Self { input, cursor: 0 }
|
||||
}
|
||||
|
||||
fn current_char(&self) -> Option<char> {
|
||||
self.input.chars().nth(self.cursor)
|
||||
}
|
||||
|
||||
fn peek_char(&self, offset: usize) -> Option<char> {
|
||||
self.input.chars().nth(self.cursor + offset)
|
||||
}
|
||||
|
||||
fn advance(&mut self) {
|
||||
if self.cursor < self.input.len() {
|
||||
self.cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn find_char(&self, ch: char) -> Option<usize> {
|
||||
self.input[self.cursor..].find(ch).map(|pos| self.cursor + pos)
|
||||
}
|
||||
|
||||
fn parse_decimal(&mut self) -> Result<u32, &'static str> {
|
||||
let start = self.cursor;
|
||||
let mut val = 0u32;
|
||||
|
||||
while let Some(ch) = self.current_char() {
|
||||
if ch.is_ascii_digit() {
|
||||
val = val.checked_mul(10).ok_or("decimal overflow")?;
|
||||
val = val.checked_add((ch as u32) - ('0' as u32)).ok_or("decimal overflow")?;
|
||||
self.advance();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if self.cursor == start {
|
||||
Err("no digits found")
|
||||
} else {
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_hex(&mut self) -> Result<u32, &'static str> {
|
||||
let start = self.cursor;
|
||||
let mut val = 0u32;
|
||||
|
||||
while let Some(ch) = self.current_char() {
|
||||
let digit = match ch {
|
||||
'0'..='9' => (ch as u32) - ('0' as u32),
|
||||
'a'..='f' => (ch as u32) - ('a' as u32) + 10,
|
||||
'A'..='F' => (ch as u32) - ('A' as u32) + 10,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
val = val.checked_shl(4).ok_or("hex overflow")?;
|
||||
val = val.checked_add(digit).ok_or("hex overflow")?;
|
||||
self.advance();
|
||||
}
|
||||
|
||||
if self.cursor == start {
|
||||
Err("no hex digits found")
|
||||
} else {
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ipv4(&mut self) -> Result<[u8; 4], &'static str> {
|
||||
let mut addr = [0u8; 4];
|
||||
|
||||
for i in 0..4 {
|
||||
let val = self.parse_decimal()?;
|
||||
if val > 255 {
|
||||
return Err("IPv4 octet out of range");
|
||||
}
|
||||
addr[i] = val as u8;
|
||||
|
||||
if i < 3 {
|
||||
if self.current_char() != Some('.') {
|
||||
return Err("expected '.' in IPv4 address");
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
fn parse_ipv6(&mut self) -> Result<[u8; 16], &'static str> {
|
||||
let mut addr = [0u8; 16];
|
||||
let mut groups = Vec::new();
|
||||
let mut compression_pos = None;
|
||||
let mut has_ipv4_suffix = false;
|
||||
|
||||
// Handle brackets
|
||||
let has_brackets = self.current_char() == Some('[');
|
||||
if has_brackets {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
// Parse groups
|
||||
loop {
|
||||
let start_cursor = self.cursor;
|
||||
|
||||
// Check for empty group (compression)
|
||||
if self.current_char() == Some(':') {
|
||||
if compression_pos.is_some() {
|
||||
// Check if this is the end of the address
|
||||
if groups.len() == 0 || (groups.len() == 1 && compression_pos == Some(0)) {
|
||||
break;
|
||||
}
|
||||
return Err("multiple :: compressions not allowed");
|
||||
}
|
||||
|
||||
compression_pos = Some(groups.len());
|
||||
self.advance();
|
||||
|
||||
// Handle leading ::
|
||||
if groups.is_empty() && self.current_char() == Some(':') {
|
||||
self.advance();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to parse hex
|
||||
match self.parse_hex() {
|
||||
Ok(val) => {
|
||||
if val > 0xFFFF {
|
||||
return Err("IPv6 group out of range");
|
||||
}
|
||||
|
||||
// Check for IPv4 suffix
|
||||
if self.current_char() == Some('.') {
|
||||
// Rewind and parse as IPv4
|
||||
self.cursor = start_cursor;
|
||||
let ipv4_addr = self.parse_ipv4()?;
|
||||
|
||||
// Validate IPv4-mapped IPv6 prefix
|
||||
if groups.len() != 6 ||
|
||||
groups[0..5] != [0, 0, 0, 0, 0] ||
|
||||
groups[5] != 0xFFFF {
|
||||
return Err("IPv4 suffix only allowed in ::ffff: mapping");
|
||||
}
|
||||
|
||||
// Add IPv4 bytes as two 16-bit groups
|
||||
groups.push(((ipv4_addr[0] as u16) << 8) | (ipv4_addr[1] as u16));
|
||||
groups.push(((ipv4_addr[2] as u16) << 8) | (ipv4_addr[3] as u16));
|
||||
has_ipv4_suffix = true;
|
||||
break;
|
||||
}
|
||||
|
||||
groups.push(val as u16);
|
||||
|
||||
// Check for continuation
|
||||
if self.current_char() == Some(':') {
|
||||
self.advance();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
if groups.is_empty() {
|
||||
return Err("invalid IPv6 format");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle compression
|
||||
if let Some(pos) = compression_pos {
|
||||
let groups_after = groups.len() - pos;
|
||||
let zeros_needed = 8 - groups.len();
|
||||
|
||||
if zeros_needed > 0 {
|
||||
let mut new_groups = Vec::new();
|
||||
new_groups.extend_from_slice(&groups[..pos]);
|
||||
new_groups.resize(new_groups.len() + zeros_needed, 0);
|
||||
new_groups.extend_from_slice(&groups[pos..]);
|
||||
groups = new_groups;
|
||||
}
|
||||
}
|
||||
|
||||
if groups.len() != 8 {
|
||||
return Err("IPv6 address must have 8 groups");
|
||||
}
|
||||
|
||||
// Convert to bytes
|
||||
for (i, &group) in groups.iter().enumerate() {
|
||||
addr[i * 2] = (group >> 8) as u8;
|
||||
addr[i * 2 + 1] = (group & 0xFF) as u8;
|
||||
}
|
||||
|
||||
// Validate IPv4-mapped address
|
||||
if has_ipv4_suffix {
|
||||
let ipv4_mapped_prefix = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF];
|
||||
if addr[..12] != ipv4_mapped_prefix {
|
||||
return Err("invalid IPv4-mapped IPv6 address");
|
||||
}
|
||||
}
|
||||
|
||||
// Handle closing bracket
|
||||
if has_brackets {
|
||||
if self.current_char() != Some(']') {
|
||||
return Err("expected closing bracket");
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
fn parse_port(&mut self) -> Result<u16, &'static str> {
|
||||
if self.current_char() != Some(':') {
|
||||
return Err("expected ':' before port");
|
||||
}
|
||||
self.advance();
|
||||
|
||||
let port = self.parse_decimal()?;
|
||||
if port > 65535 {
|
||||
return Err("port out of range");
|
||||
}
|
||||
|
||||
Ok(port as u16)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_ipv4_or_ipv6(input: &str) -> Result<ParseResult, &'static str> {
|
||||
let mut parser = Parser::new(input);
|
||||
|
||||
// Determine if this looks like IPv6
|
||||
let colon_pos = parser.find_char(':');
|
||||
let dot_pos = parser.find_char('.');
|
||||
let bracket_pos = parser.find_char('[');
|
||||
|
||||
let is_ipv6 = bracket_pos.is_some()
|
||||
|| dot_pos.is_none()
|
||||
|| (colon_pos.is_some() && (dot_pos.is_none() || colon_pos < dot_pos));
|
||||
|
||||
let addr = if is_ipv6 {
|
||||
IpAddr::V6(parser.parse_ipv6()?)
|
||||
} else {
|
||||
IpAddr::V4(parser.parse_ipv4()?)
|
||||
};
|
||||
|
||||
let port = if parser.current_char() == Some(':') {
|
||||
Some(parser.parse_port()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ParseResult {
|
||||
addr,
|
||||
port,
|
||||
consumed: parser.cursor,
|
||||
})
|
||||
}
|
||||
|
||||
// Helper function for compatibility
|
||||
pub fn parse_ipv4_or_ipv6_simple(input: &str) -> Result<(IpAddr, Option<u16>, bool), &'static str> {
|
||||
let result = parse_ipv4_or_ipv6(input)?;
|
||||
let is_ipv6 = matches!(result.addr, IpAddr::V6(_));
|
||||
Ok((result.addr, result.port, is_ipv6))
|
||||
}
|
||||
|
||||
fn dump_bytes(bytes: &[u8]) {
|
||||
for &byte in bytes {
|
||||
print!("{:02x}", byte);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_case(input: &str) {
|
||||
println!("Test case '{}'", input);
|
||||
|
||||
match parse_ipv4_or_ipv6(input) {
|
||||
Ok(result) => {
|
||||
print!("addr: ");
|
||||
match result.addr {
|
||||
IpAddr::V4(addr) => dump_bytes(&addr),
|
||||
IpAddr::V6(addr) => dump_bytes(&addr),
|
||||
}
|
||||
println!();
|
||||
|
||||
if let Some(port) = result.port {
|
||||
println!("port: {}", port);
|
||||
} else {
|
||||
println!("port absent");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("parse failed: {}", e);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// The "localhost" IPv4 address
|
||||
test_case("127.0.0.1");
|
||||
|
||||
// The "localhost" IPv4 address, with a specified port (80)
|
||||
test_case("127.0.0.1:80");
|
||||
|
||||
// The "localhost" IPv6 address
|
||||
test_case("::1");
|
||||
|
||||
// The "localhost" IPv6 address, with a specified port (80)
|
||||
test_case("[::1]:80");
|
||||
|
||||
// Rosetta Code's primary server's public IPv6 address
|
||||
test_case("2605:2700:0:3::4713:93e3");
|
||||
|
||||
// Rosetta Code's primary server's public IPv6 address, with a specified port (80)
|
||||
test_case("[2605:2700:0:3::4713:93e3]:80");
|
||||
|
||||
// IPv4 space
|
||||
test_case("::ffff:192.168.173.22");
|
||||
|
||||
// IPv4 space with port
|
||||
test_case("[::ffff:192.168.173.22]:80");
|
||||
|
||||
// Trailing compression
|
||||
test_case("1::");
|
||||
|
||||
// Trailing compression with port
|
||||
test_case("[1::]:80");
|
||||
|
||||
// 'any' address compression
|
||||
test_case("::");
|
||||
|
||||
// 'any' address compression with port
|
||||
test_case("[::]:80");
|
||||
}
|
||||
187
Task/Parse-an-IP-Address/Swift/parse-an-ip-address.swift
Normal file
187
Task/Parse-an-IP-Address/Swift/parse-an-ip-address.swift
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
import Foundation
|
||||
|
||||
struct IPParseResult {
|
||||
let hexAddress: String
|
||||
let port: String
|
||||
}
|
||||
|
||||
enum IPParseError: Error {
|
||||
case invalidValue(String)
|
||||
case invalidHexValue(String)
|
||||
case unknownAddress(String)
|
||||
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .invalidValue(let value):
|
||||
return "ERROR 101: Invalid value: \(value)"
|
||||
case .invalidHexValue(let value):
|
||||
return "ERROR 102: Invalid hex value: \(value)"
|
||||
case .unknownAddress(let address):
|
||||
return "ERROR 103: Unknown address: \(address)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ParseIPAddress {
|
||||
|
||||
// IPv4 pattern: matches IP with optional port
|
||||
private static let ipv4Pattern = #"^(\d+)\.(\d+)\.(\d+)\.(\d+)(?::(\d+))?$"#
|
||||
|
||||
// IPv6 double colon pattern: matches compressed IPv6 with optional port
|
||||
private static let ipv6DoubleColonPattern = #"^\[?([0-9a-f:]*)::([0-9a-f:]*)(?:\]:(\d+))?$"#
|
||||
|
||||
// IPv6 full pattern: dynamically built for full IPv6 addresses
|
||||
private static let ipv6Pattern: String = {
|
||||
var pattern = #"^\[?"#
|
||||
for i in 1...7 {
|
||||
pattern += #"([0-9a-f]+):"#
|
||||
}
|
||||
pattern += #"([0-9a-f]+)(?:\]:(\d+))?$"#
|
||||
return pattern
|
||||
}()
|
||||
|
||||
static func main() {
|
||||
let tests = [
|
||||
"192.168.0.1",
|
||||
"127.0.0.1",
|
||||
"256.0.0.1",
|
||||
"127.0.0.1:80",
|
||||
"::1",
|
||||
"[::1]:80",
|
||||
"[32e::12f]:80",
|
||||
"2605:2700:0:3::4713:93e3",
|
||||
"[2605:2700:0:3::4713:93e3]:80",
|
||||
"2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
]
|
||||
|
||||
// Print header with proper padding
|
||||
let header = "Test Case".padding(toLength: 40, withPad: " ", startingAt: 0) +
|
||||
"Hex Address".padding(toLength: 32, withPad: " ", startingAt: 0) +
|
||||
" Port"
|
||||
print(header)
|
||||
|
||||
for ip in tests {
|
||||
do {
|
||||
let result = try parseIP(ip)
|
||||
let output = ip.padding(toLength: 40, withPad: " ", startingAt: 0) +
|
||||
result.hexAddress.padding(toLength: 32, withPad: " ", startingAt: 0) +
|
||||
" " + result.port
|
||||
print(output)
|
||||
} catch {
|
||||
let output = ip.padding(toLength: 40, withPad: " ", startingAt: 0) +
|
||||
"Invalid address: " + error.localizedDescription
|
||||
print(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func parseIP(_ ip: String) throws -> IPParseResult {
|
||||
var hex = ""
|
||||
var port = ""
|
||||
|
||||
// Try IPv4 first
|
||||
if let ipv4Regex = try? NSRegularExpression(pattern: ipv4Pattern, options: [.caseInsensitive]) {
|
||||
let range = NSRange(location: 0, length: ip.utf16.count)
|
||||
if let match = ipv4Regex.firstMatch(in: ip, options: [], range: range) {
|
||||
// Extract the 4 octets
|
||||
for i in 1...4 {
|
||||
let groupRange = match.range(at: i)
|
||||
let octet = String(ip[Range(groupRange, in: ip)!])
|
||||
hex += try toHex4(octet)
|
||||
}
|
||||
|
||||
// Check for port
|
||||
let portRange = match.range(at: 5)
|
||||
if portRange.location != NSNotFound {
|
||||
port = String(ip[Range(portRange, in: ip)!])
|
||||
}
|
||||
|
||||
return IPParseResult(hexAddress: hex, port: port)
|
||||
}
|
||||
}
|
||||
|
||||
var modifiedIP = ip
|
||||
|
||||
// Try IPv6 with double colon
|
||||
if let ipv6DoubleColonRegex = try? NSRegularExpression(pattern: ipv6DoubleColonPattern, options: [.caseInsensitive]) {
|
||||
let range = NSRange(location: 0, length: ip.utf16.count)
|
||||
if let match = ipv6DoubleColonRegex.firstMatch(in: ip, options: [], range: range) {
|
||||
let p1Range = match.range(at: 1)
|
||||
let p2Range = match.range(at: 2)
|
||||
let portRange = match.range(at: 3)
|
||||
|
||||
var p1 = p1Range.location != NSNotFound ? String(ip[Range(p1Range, in: ip)!]) : ""
|
||||
var p2 = p2Range.location != NSNotFound ? String(ip[Range(p2Range, in: ip)!]) : ""
|
||||
|
||||
if p1.isEmpty {
|
||||
p1 = "0"
|
||||
}
|
||||
if p2.isEmpty {
|
||||
p2 = "0"
|
||||
}
|
||||
|
||||
let zeroCount = 8 - numCount(p1) - numCount(p2)
|
||||
modifiedIP = p1 + getZero(zeroCount) + p2
|
||||
|
||||
if portRange.location != NSNotFound {
|
||||
let portStr = String(ip[Range(portRange, in: ip)!])
|
||||
modifiedIP = "[\(modifiedIP)]:\(portStr)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try full IPv6
|
||||
if let ipv6Regex = try? NSRegularExpression(pattern: ipv6Pattern, options: [.caseInsensitive]) {
|
||||
let range = NSRange(location: 0, length: modifiedIP.utf16.count)
|
||||
if let match = ipv6Regex.firstMatch(in: modifiedIP, options: [], range: range) {
|
||||
// Extract the 8 groups
|
||||
for i in 1...8 {
|
||||
let groupRange = match.range(at: i)
|
||||
let group = String(modifiedIP[Range(groupRange, in: modifiedIP)!])
|
||||
let hexGroup = try toHex6(group)
|
||||
let paddedHex = hexGroup.padding(toLength: 4, withPad: "0", startingAt: 0)
|
||||
hex += paddedHex
|
||||
}
|
||||
|
||||
// Check for port
|
||||
let portRange = match.range(at: 9)
|
||||
if portRange.location != NSNotFound {
|
||||
port = String(modifiedIP[Range(portRange, in: modifiedIP)!])
|
||||
}
|
||||
|
||||
return IPParseResult(hexAddress: hex, port: port)
|
||||
}
|
||||
}
|
||||
|
||||
throw IPParseError.unknownAddress(ip)
|
||||
}
|
||||
|
||||
private static func numCount(_ s: String) -> Int {
|
||||
return s.components(separatedBy: ":").count
|
||||
}
|
||||
|
||||
private static func getZero(_ count: Int) -> String {
|
||||
var result = ":"
|
||||
for _ in 0..<count {
|
||||
result += "0:"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static func toHex4(_ s: String) throws -> String {
|
||||
guard let val = Int(s), val >= 0 && val <= 255 else {
|
||||
throw IPParseError.invalidValue(s)
|
||||
}
|
||||
return String(format: "%02x", val)
|
||||
}
|
||||
|
||||
private static func toHex6(_ s: String) throws -> String {
|
||||
guard let val = Int(s, radix: 16), val >= 0 && val <= 65535 else {
|
||||
throw IPParseError.invalidHexValue(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// Run the program
|
||||
ParseIPAddress.main()
|
||||
238
Task/Parse-an-IP-Address/Zig/parse-an-ip-address.zig
Normal file
238
Task/Parse-an-IP-Address/Zig/parse-an-ip-address.zig
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
const std = @import("std");
|
||||
const print = std.debug.print;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
const ParseError = error{
|
||||
InvalidAddress,
|
||||
InvalidValue,
|
||||
InvalidHexValue,
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
const ParseResult = struct {
|
||||
hex_address: []const u8,
|
||||
port: []const u8,
|
||||
|
||||
fn deinit(self: ParseResult, allocator: Allocator) void {
|
||||
allocator.free(self.hex_address);
|
||||
allocator.free(self.port);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const tests = [_][]const u8{
|
||||
"192.168.0.1",
|
||||
"127.0.0.1",
|
||||
"256.0.0.1",
|
||||
"127.0.0.1:80",
|
||||
"::1",
|
||||
"[::1]:80",
|
||||
"[32e::12f]:80",
|
||||
"2605:2700:0:3::4713:93e3",
|
||||
"[2605:2700:0:3::4713:93e3]:80",
|
||||
"2001:db8:85a3:0:0:8a2e:370:7334"
|
||||
};
|
||||
|
||||
print("{s:<40} {s:<32} {s}\n", .{ "Test Case", "Hex Address", "Port" });
|
||||
|
||||
for (tests) |ip| {
|
||||
const result = parseIP(allocator, ip) catch |err| {
|
||||
const error_msg = switch (err) {
|
||||
ParseError.InvalidAddress => "Unknown address",
|
||||
ParseError.InvalidValue => "Invalid value",
|
||||
ParseError.InvalidHexValue => "Invalid hex value",
|
||||
else => "Parse error",
|
||||
};
|
||||
print("{s:<40} Invalid address: {s}\n", .{ ip, error_msg });
|
||||
continue;
|
||||
};
|
||||
defer result.deinit(allocator);
|
||||
|
||||
print("{s:<40} {s:<32} {s}\n", .{ ip, result.hex_address, result.port });
|
||||
}
|
||||
}
|
||||
|
||||
fn parseIP(allocator: Allocator, ip: []const u8) !ParseResult {
|
||||
// Try IPv4 first
|
||||
if (parseIPv4(allocator, ip)) |result| {
|
||||
return result;
|
||||
} else |_| {}
|
||||
|
||||
// Try IPv6 with double colon
|
||||
if (parseIPv6DoubleColon(allocator, ip)) |result| {
|
||||
return result;
|
||||
} else |_| {}
|
||||
|
||||
// Try regular IPv6
|
||||
if (parseIPv6(allocator, ip)) |result| {
|
||||
return result;
|
||||
} else |_| {}
|
||||
|
||||
return ParseError.InvalidAddress;
|
||||
}
|
||||
|
||||
fn parseIPv4(allocator: Allocator, ip: []const u8) !ParseResult {
|
||||
var parts = std.mem.splitSequence(u8, ip, ".");
|
||||
var octets: [4]u8 = undefined;
|
||||
var port_str: []const u8 = "";
|
||||
var i: usize = 0;
|
||||
|
||||
while (parts.next()) |part| {
|
||||
if (i >= 4) return ParseError.InvalidAddress;
|
||||
|
||||
// Check if this part contains a port (last octet with colon)
|
||||
if (i == 3) {
|
||||
if (std.mem.indexOf(u8, part, ":")) |colon_pos| {
|
||||
const octet_str = part[0..colon_pos];
|
||||
port_str = part[colon_pos + 1..];
|
||||
|
||||
const octet = std.fmt.parseInt(u8, octet_str, 10) catch return ParseError.InvalidValue;
|
||||
octets[i] = octet;
|
||||
} else {
|
||||
const octet = std.fmt.parseInt(u8, part, 10) catch return ParseError.InvalidValue;
|
||||
octets[i] = octet;
|
||||
}
|
||||
} else {
|
||||
const octet = std.fmt.parseInt(u8, part, 10) catch return ParseError.InvalidValue;
|
||||
octets[i] = octet;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i != 4) return ParseError.InvalidAddress;
|
||||
|
||||
// Convert to hex
|
||||
var hex = ArrayList(u8).init(allocator);
|
||||
defer hex.deinit();
|
||||
|
||||
for (octets) |octet| {
|
||||
try hex.writer().print("{x:0>2}", .{octet});
|
||||
}
|
||||
|
||||
const port_copy = try allocator.dupe(u8, port_str);
|
||||
|
||||
return ParseResult{
|
||||
.hex_address = try hex.toOwnedSlice(),
|
||||
.port = port_copy,
|
||||
};
|
||||
}
|
||||
|
||||
fn parseIPv6DoubleColon(allocator: Allocator, ip: []const u8) !ParseResult {
|
||||
var working_ip = ip;
|
||||
var port_str: []const u8 = "";
|
||||
|
||||
// Handle brackets and port
|
||||
if (std.mem.startsWith(u8, working_ip, "[")) {
|
||||
if (std.mem.lastIndexOf(u8, working_ip, "]:")) |bracket_pos| {
|
||||
port_str = working_ip[bracket_pos + 2..];
|
||||
working_ip = working_ip[1..bracket_pos];
|
||||
} else if (std.mem.endsWith(u8, working_ip, "]")) {
|
||||
working_ip = working_ip[1..working_ip.len - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it contains ::
|
||||
const double_colon_pos = std.mem.indexOf(u8, working_ip, "::") orelse return ParseError.InvalidAddress;
|
||||
|
||||
const p1 = working_ip[0..double_colon_pos];
|
||||
const p2 = working_ip[double_colon_pos + 2..];
|
||||
|
||||
const p1_count = if (p1.len == 0) 0 else countColons(p1) + 1;
|
||||
const p2_count = if (p2.len == 0) 0 else countColons(p2) + 1;
|
||||
|
||||
const zeros_needed = 8 - p1_count - p2_count;
|
||||
|
||||
// Reconstruct the full IPv6 address
|
||||
var full_ip = ArrayList(u8).init(allocator);
|
||||
defer full_ip.deinit();
|
||||
|
||||
if (p1.len > 0) {
|
||||
try full_ip.appendSlice(p1);
|
||||
try full_ip.append(':');
|
||||
}
|
||||
|
||||
for (0..zeros_needed) |_| {
|
||||
try full_ip.appendSlice("0:");
|
||||
}
|
||||
|
||||
if (p2.len > 0) {
|
||||
try full_ip.appendSlice(p2);
|
||||
} else {
|
||||
// Remove trailing colon if p2 is empty
|
||||
if (full_ip.items.len > 0 and full_ip.items[full_ip.items.len - 1] == ':') {
|
||||
_ = full_ip.pop();
|
||||
}
|
||||
}
|
||||
|
||||
const reconstructed = try full_ip.toOwnedSlice();
|
||||
defer allocator.free(reconstructed);
|
||||
|
||||
return parseIPv6WithPort(allocator, reconstructed, port_str);
|
||||
}
|
||||
|
||||
fn parseIPv6(allocator: Allocator, ip: []const u8) !ParseResult {
|
||||
return parseIPv6WithPort(allocator, ip, "");
|
||||
}
|
||||
|
||||
fn parseIPv6WithPort(allocator: Allocator, ip: []const u8, port_override: []const u8) !ParseResult {
|
||||
var working_ip = ip;
|
||||
var port_str = port_override;
|
||||
|
||||
// Handle brackets and port if not overridden
|
||||
if (port_override.len == 0) {
|
||||
if (std.mem.startsWith(u8, working_ip, "[")) {
|
||||
if (std.mem.lastIndexOf(u8, working_ip, "]:")) |bracket_pos| {
|
||||
port_str = working_ip[bracket_pos + 2..];
|
||||
working_ip = working_ip[1..bracket_pos];
|
||||
} else if (std.mem.endsWith(u8, working_ip, "]")) {
|
||||
working_ip = working_ip[1..working_ip.len - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parts = std.mem.splitSequence(u8, working_ip, ":");
|
||||
var groups: [8][]const u8 = undefined;
|
||||
var i: usize = 0;
|
||||
|
||||
while (parts.next()) |part| {
|
||||
if (i >= 8) return ParseError.InvalidAddress;
|
||||
groups[i] = part;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i != 8) return ParseError.InvalidAddress;
|
||||
|
||||
// Convert to hex
|
||||
var hex = ArrayList(u8).init(allocator);
|
||||
defer hex.deinit();
|
||||
|
||||
for (groups) |group| {
|
||||
if (group.len == 0) {
|
||||
try hex.appendSlice("0000");
|
||||
} else {
|
||||
// Validate hex and pad to 4 digits
|
||||
const val = std.fmt.parseInt(u16, group, 16) catch return ParseError.InvalidHexValue;
|
||||
try hex.writer().print("{x:0>4}", .{val});
|
||||
}
|
||||
}
|
||||
|
||||
const port_copy = try allocator.dupe(u8, port_str);
|
||||
|
||||
return ParseResult{
|
||||
.hex_address = try hex.toOwnedSlice(),
|
||||
.port = port_copy,
|
||||
};
|
||||
}
|
||||
|
||||
fn countColons(s: []const u8) usize {
|
||||
var count: usize = 0;
|
||||
for (s) |c| {
|
||||
if (c == ':') count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue