///|
pub struct Ipv4Address {
octet_values : Array[Int]
} derive(Eq, Debug)
///|
pub struct Ipv6Address {
group_values : Array[Int]
} derive(Eq, Debug)
///|
fn address_error(message : String, column? : Int = 0) -> ZoneError {
ZoneError::new(InvalidRecordData, message, SourceSpan::point(0, column))
}
///|
fn split_address_text(text : String, separator : Int) -> Array[String] {
let result : Array[String] = []
let mut current = ""
for index = 0; index < text.length(); index = index + 1 {
let code = text[index].to_int()
if code == separator {
result.push(current)
current = ""
} else {
current = current + ascii_character(code)
}
}
result.push(current)
result
}
///|
fn parse_decimal_octet(text : String) -> Result[Int, ZoneError] {
if text.length() == 0 || text.length() > 3 {
return Err(address_error("IPv4 octet has an invalid length"))
}
if text.length() > 1 && text[0].to_int() == 48 {
return Err(address_error("IPv4 octet cannot contain a leading zero"))
}
let mut value = 0
for index = 0; index < text.length(); index = index + 1 {
let code = text[index].to_int()
if code < 48 || code > 57 {
return Err(address_error("IPv4 octet must be decimal", column=index))
}
value = value * 10 + code - 48
}
if value > 255 {
Err(address_error("IPv4 octet exceeds 255"))
} else {
Ok(value)
}
}
///|
pub fn parse_ipv4(text : String) -> Result[Ipv4Address, ZoneError] {
let parts = split_address_text(text, 46)
if parts.length() != 4 {
return Err(address_error("IPv4 address must contain four octets"))
}
let octets : Array[Int] = []
for part in parts {
match parse_decimal_octet(part) {
Ok(value) => octets.push(value)
Err(error) => return Err(error)
}
}
Ok({ octet_values: octets })
}
///|
pub fn Ipv4Address::octets(self : Ipv4Address) -> Array[Int] {
self.octet_values.copy()
}
///|
pub fn Ipv4Address::to_text(self : Ipv4Address) -> String {
self.octet_values[0].to_string() +
"." +
self.octet_values[1].to_string() +
"." +
self.octet_values[2].to_string() +
"." +
self.octet_values[3].to_string()
}
///|
fn hex_digit_value(code : Int) -> Int? {
if code >= 48 && code <= 57 {
Some(code - 48)
} else if code >= 65 && code <= 70 {
Some(code - 65 + 10)
} else if code >= 97 && code <= 102 {
Some(code - 97 + 10)
} else {
None
}
}
///|
fn parse_ipv6_group(text : String) -> Result[Int, ZoneError] {
if text.length() == 0 || text.length() > 4 {
return Err(address_error("IPv6 group must contain one to four hex digits"))
}
let mut value = 0
for index = 0; index < text.length(); index = index + 1 {
match hex_digit_value(text[index].to_int()) {
Some(digit) => value = value * 16 + digit
None =>
return Err(
address_error("IPv6 group contains a non-hex digit", column=index),
)
}
}
Ok(value)
}
///|
fn parse_ipv6_side(text : String) -> Result[Array[Int], ZoneError] {
if text.length() == 0 {
return Ok([])
}
let parts = split_address_text(text, 58)
let groups : Array[Int] = []
for part in parts {
if part.length() == 0 {
return Err(address_error("IPv6 address contains an empty group"))
}
match parse_ipv6_group(part) {
Ok(value) => groups.push(value)
Err(error) => return Err(error)
}
}
Ok(groups)
}
///|
fn find_double_colon(text : String) -> Result[Int?, ZoneError] {
let mut found : Int? = None
for index = 0; index + 1 < text.length(); index = index + 1 {
if text[index].to_int() == 58 && text[index + 1].to_int() == 58 {
if found is Some(_) {
return Err(address_error("IPv6 address contains multiple :: markers"))
}
found = Some(index)
}
}
Ok(found)
}
///|
pub fn parse_ipv6(text : String) -> Result[Ipv6Address, ZoneError] {
if text.length() == 0 {
return Err(address_error("IPv6 address is empty"))
}
let marker = match find_double_colon(text) {
Ok(value) => value
Err(error) => return Err(error)
}
let groups : Array[Int] = []
match marker {
Some(position) => {
let left_text = text[0:position].to_owned()
let right_text = text[position + 2:].to_owned()
let left = match parse_ipv6_side(left_text) {
Ok(value) => value
Err(error) => return Err(error)
}
let right = match parse_ipv6_side(right_text) {
Ok(value) => value
Err(error) => return Err(error)
}
if left.length() + right.length() >= 8 {
return Err(address_error("IPv6 :: must replace at least one group"))
}
for value in left {
groups.push(value)
}
let zero_count = 8 - left.length() - right.length()
for zero_index = 0; zero_index < zero_count; zero_index = zero_index + 1 {
groups.push(0)
}
for value in right {
groups.push(value)
}
}
None => {
let parsed = match parse_ipv6_side(text) {
Ok(value) => value
Err(error) => return Err(error)
}
if parsed.length() != 8 {
return Err(address_error("IPv6 address must contain eight groups"))
}
for value in parsed {
groups.push(value)
}
}
}
Ok({ group_values: groups })
}
///|
pub fn Ipv6Address::groups(self : Ipv6Address) -> Array[Int] {
self.group_values.copy()
}
///|
fn longest_zero_run(groups : Array[Int]) -> (Int, Int) {
let mut best_start = -1
let mut best_length = 0
let mut index = 0
while index < groups.length() {
if groups[index] != 0 {
index = index + 1
continue
}
let start = index
while index < groups.length() && groups[index] == 0 {
index = index + 1
}
let length = index - start
if length >= 2 && length > best_length {
best_start = start
best_length = length
}
}
(best_start, best_length)
}
///|
pub fn Ipv6Address::to_text(self : Ipv6Address) -> String {
let (zero_start, zero_length) = longest_zero_run(self.group_values)
let mut result = ""
let mut index = 0
while index < 8 {
if index == zero_start {
result = result + "::"
index = index + zero_length
} else {
if result.length() > 0 && result[result.length() - 1].to_int() != 58 {
result = result + ":"
}
result = result + self.group_values[index].to_string(radix=16)
index = index + 1
}
}
result
}