///|
pub fn parse_ipv4(text : String) -> Result[Array[Int], Failure] {
let parts = text.split(".").collect()
if parts.length() != 4 {
return Err(Invalid("IPv4 address needs four octets"))
}
let result : Array[Int] = []
for part in parts {
let segment = part.to_owned()
if segment.length() > 1 && segment.has_prefix("0") {
return Err(Invalid("IPv4 octets cannot have leading zeroes"))
}
match unsigned_decimal(segment, 255L) {
Err(e) => return Err(e)
Ok(value) => result.push(value.to_int())
}
}
Ok(result)
}
///|
fn ipv6_half(text : String, allow_ipv4 : Bool) -> Result[Array[Int], Failure] {
if text.is_empty() {
return Ok([])
}
let parts = text.split(":").collect()
let result : Array[Int] = []
for i = 0; i < parts.length(); i = i + 1 {
let part = parts[i].to_owned()
if part.contains(".") {
if !allow_ipv4 || i != parts.length() - 1 {
return Err(Invalid("embedded IPv4 must be the final IPv6 component"))
}
let octets = match parse_ipv4(part) {
Err(e) => return Err(e)
Ok(v) => v
}
result.push(octets[0] * 256 + octets[1])
result.push(octets[2] * 256 + octets[3])
continue
}
if part.is_empty() || part.length() > 4 {
return Err(Invalid("invalid IPv6 component length"))
}
let mut value = 0
for character in part.iter() {
let digit = hex_digit(character.to_int())
if digit < 0 {
return Err(Invalid("invalid IPv6 hexadecimal component"))
}
value = value * 16 + digit
}
result.push(value)
}
Ok(result)
}
///|
pub fn parse_ipv6(text : String) -> Result[Array[Int], Failure] {
if text.is_empty() ||
text.contains("%") ||
text.contains("[") ||
text.contains("]") {
return Err(Invalid("invalid or scoped IPv6 literal"))
}
match text.split_once("::") {
None => {
let words = match ipv6_half(text, true) {
Err(e) => return Err(e)
Ok(v) => v
}
if words.length() != 8 {
return Err(Invalid("uncompressed IPv6 address requires eight words"))
}
Ok(words)
}
Some((left_text, right_text)) => {
if right_text.contains("::") {
return Err(Invalid("multiple IPv6 compression markers"))
}
let left = match ipv6_half(left_text.to_owned(), false) {
Err(e) => return Err(e)
Ok(v) => v
}
let right = match ipv6_half(right_text.to_owned(), true) {
Err(e) => return Err(e)
Ok(v) => v
}
if left.length() + right.length() >= 8 {
return Err(Invalid("IPv6 compression must replace at least one word"))
}
let result = left.copy()
for _i = left.length() + right.length(); _i < 8; _i = _i + 1 {
result.push(0)
}
for word in right {
result.push(word)
}
Ok(result)
}
}
}
///|
pub fn canonical_ipv6(text : String) -> Result[String, Failure] {
let words = match parse_ipv6(text) {
Err(e) => return Err(e)
Ok(v) => v
}
let mut longest_start = -1
let mut longest_length = 1
let mut i = 0
while i < words.length() {
if words[i] != 0 {
i = i + 1
continue
}
let start = i
while i < words.length() && words[i] == 0 {
i = i + 1
}
if i - start > longest_length {
longest_start = start
longest_length = i - start
}
}
let digits = "0123456789abcdef"
let mut result = ""
let mut cursor = 0
while cursor < 8 {
if cursor == longest_start {
result = result + "::"
cursor = cursor + longest_length
continue
}
if !result.is_empty() && !result.has_suffix(":") {
result = result + ":"
}
let mut value = words[cursor]
let characters : Array[Char] = []
while value > 0 {
characters.push(digits.get_char(value & 15).unwrap())
value = value >> 4
}
if characters.is_empty() {
result = result + "0"
} else {
for j = characters.length() - 1; j >= 0; j = j - 1 {
result = result + characters[j].to_string()
}
}
cursor = cursor + 1
}
Ok(result)
}