///|
fn hex_value(ch : Char) -> Int {
let n = ch.to_int()
if n >= '0'.to_int() && n <= '9'.to_int() {
n - '0'.to_int()
} else if n >= 'a'.to_int() && n <= 'f'.to_int() {
n - 'a'.to_int() + 10
} else if n >= 'A'.to_int() && n <= 'F'.to_int() {
n - 'A'.to_int() + 10
} else {
-1
}
}
///|
fn ipv6_side_groups(
side : String,
offset : Int,
) -> Result[Array[Int], ProxyError] {
if side.length() == 0 {
return Ok(Array::make(0, 0))
}
let pieces = side.split(":").to_array()
let result = Array::make(0, 0)
let mut pos = offset
for piece_view in pieces {
let piece = piece_view.to_owned()
let chars = piece.to_array()
if chars.length() == 0 || chars.length() > 4 {
return Err(
proxy_error(
InvalidIpv6,
pos,
"IPv6 group must be 1..4 hexadecimal digits",
),
)
}
let mut value = 0
for ch in chars {
let digit = hex_value(ch)
if digit < 0 {
return Err(
proxy_error(InvalidIpv6, pos, "invalid IPv6 hexadecimal digit"),
)
}
value = (value << 4) | digit
}
result.push(value)
pos = pos + piece.length() + 1
}
Ok(result)
}
///|
pub fn parse_ipv6_text(input : String) -> Result[Ipv6Address, ProxyError] {
if input.length() == 0 || input.contains("%") || input.contains(".") {
return Err(
proxy_error(
InvalidIpv6,
0,
"empty, scoped, and IPv4-embedded IPv6 text is rejected",
),
)
}
let halves = input.split("::").to_array()
if halves.length() > 2 {
return Err(
proxy_error(
InvalidIpv6,
0,
"at most one :: compression marker is allowed",
),
)
}
let compressed = halves.length() == 2
let left = match ipv6_side_groups(halves[0].to_owned(), 0) {
Err(err) => return Err(err)
Ok(v) => v
}
let right = if compressed {
match ipv6_side_groups(halves[1].to_owned(), halves[0].length() + 2) {
Err(err) => return Err(err)
Ok(v) => v
}
} else {
Array::make(0, 0)
}
let group_count = left.length() + right.length()
if (compressed && group_count >= 8) || (!compressed && group_count != 8) {
return Err(proxy_error(InvalidIpv6, 0, "invalid IPv6 group count"))
}
let bytes = Array::make(16, b'\x00')
let mut at = 0
for value in left {
bytes[at] = (value >> 8).to_byte()
bytes[at + 1] = value.to_byte()
at = at + 2
}
at = (8 - right.length()) * 2
for value in right {
bytes[at] = (value >> 8).to_byte()
bytes[at + 1] = value.to_byte()
at = at + 2
}
Ipv6Address::new(Bytes::from_array(bytes).to_fixedarray())
}
///|
fn group_hex(value : Int) -> String {
if value == 0 {
return "0"
}
let chars = Array::make(0, '0')
let mut started = false
for shift = 12; shift >= 0; shift = shift - 4 {
let digit = (value >> shift) & 15
if digit != 0 || started {
chars.push(
if digit < 10 {
('0'.to_int() + digit).to_char().unwrap()
} else {
('a'.to_int() + digit - 10).to_char().unwrap()
},
)
started = true
}
}
String::from_array(chars)
}
///|
pub fn format_ipv6(address : Ipv6Address) -> String {
let groups = Array::make(8, 0)
for i = 0; i < 8; i = i + 1 {
groups[i] = (address.octets[i * 2].to_int() << 8) |
address.octets[i * 2 + 1].to_int()
}
let mut best_start = -1
let mut best_len = 0
let mut run_start = 0
let mut run_len = 0
for i = 0; i <= 8; i = i + 1 {
if i < 8 && groups[i] == 0 {
if run_len == 0 {
run_start = i
}
run_len = run_len + 1
} else {
if run_len >= 2 && run_len > best_len {
best_start = run_start
best_len = run_len
}
run_len = 0
}
}
let buffer = @buffer.Buffer(size_hint=40)
let mut i = 0
while i < 8 {
if i == best_start {
buffer.write_string_utf8("::")
i = i + best_len
} else {
if i > 0 && i != best_start + best_len {
buffer.write_string_utf8(":")
}
buffer.write_string_utf8(group_hex(groups[i]))
i = i + 1
}
}
let chars = Array::make(0, ' ')
for byte in buffer.to_bytes() {
chars.push(byte.to_int().to_char().unwrap())
}
String::from_array(chars)
}