///|
pub struct Ipv6Address {
hi : UInt64
lo : UInt64
} derive(Eq, Compare, Debug)
///|
pub struct Ipv6Network {
address : Ipv6Address
prefix : Int
} derive(Eq, Debug)
///|
pub fn Ipv6Address::from_segments(
s0 : Int,
s1 : Int,
s2 : Int,
s3 : Int,
s4 : Int,
s5 : Int,
s6 : Int,
s7 : Int,
) -> Result[Ipv6Address, CidrError] {
let segments = [s0, s1, s2, s3, s4, s5, s6, s7]
let mut index = 0
while index < segments.length() {
if segments[index] < 0 || segments[index] > 65535 {
return Err(OctetOutOfRange(index, segments[index]))
}
index = index + 1
}
Ok({
hi: (s0.to_uint64() << 48) |
(s1.to_uint64() << 32) |
(s2.to_uint64() << 16) |
s3.to_uint64(),
lo: (s4.to_uint64() << 48) |
(s5.to_uint64() << 32) |
(s6.to_uint64() << 16) |
s7.to_uint64(),
})
}
///|
pub fn Ipv6Address::parse(input : String) -> Result[Ipv6Address, CidrError] {
if input.length() == 0 {
return Err(EmptyInput)
}
if contains_ipv6_compression(input) {
parse_compressed_ipv6(input)
} else {
parse_full_ipv6(input)
}
}
///|
pub fn Ipv6Address::segment(self : Ipv6Address, index : Int) -> Int {
match index {
0 => ((self.hi >> 48) & 0xffffUL).to_int()
1 => ((self.hi >> 32) & 0xffffUL).to_int()
2 => ((self.hi >> 16) & 0xffffUL).to_int()
3 => (self.hi & 0xffffUL).to_int()
4 => ((self.lo >> 48) & 0xffffUL).to_int()
5 => ((self.lo >> 32) & 0xffffUL).to_int()
6 => ((self.lo >> 16) & 0xffffUL).to_int()
7 => (self.lo & 0xffffUL).to_int()
_ => 0
}
}
///|
pub fn Ipv6Address::to_string(self : Ipv6Address) -> String {
"\{hex16(self.segment(0))}:\{hex16(self.segment(1))}:\{hex16(self.segment(2))}:\{hex16(self.segment(3))}:\{hex16(self.segment(4))}:\{hex16(self.segment(5))}:\{hex16(self.segment(6))}:\{hex16(self.segment(7))}"
}
///|
pub fn Ipv6Address::is_unspecified(self : Ipv6Address) -> Bool {
self.hi == 0UL && self.lo == 0UL
}
///|
pub fn Ipv6Address::is_loopback(self : Ipv6Address) -> Bool {
self.hi == 0UL && self.lo == 1UL
}
///|
pub fn Ipv6Address::is_multicast(self : Ipv6Address) -> Bool {
((self.hi >> 56) & 0xffUL) == 0xffUL
}
///|
pub fn Ipv6Address::is_unique_local(self : Ipv6Address) -> Bool {
((self.hi >> 56) & 0xfeUL) == 0xfcUL
}
///|
pub fn Ipv6Address::is_link_local(self : Ipv6Address) -> Bool {
((self.hi >> 54) & 0x3ffUL) == 0x3faUL
}
///|
pub fn Ipv6Address::mask(self : Ipv6Address, prefix : Int) -> Ipv6Address {
if prefix <= 0 {
{ hi: 0UL, lo: 0UL }
} else if prefix < 64 {
{ hi: self.hi & (18446744073709551615UL << (64 - prefix)), lo: 0UL }
} else if prefix == 64 {
{ hi: self.hi, lo: 0UL }
} else if prefix < 128 {
{ hi: self.hi, lo: self.lo & (18446744073709551615UL << (128 - prefix)) }
} else {
self
}
}
///|
pub fn Ipv6Network::new(
address : Ipv6Address,
prefix : Int,
) -> Result[Ipv6Network, CidrError] {
if prefix < 0 || prefix > 128 {
Err(PrefixOutOfRange(prefix))
} else {
Ok({ address: address.mask(prefix), prefix })
}
}
///|
pub fn Ipv6Network::parse(input : String) -> Result[Ipv6Network, CidrError] {
match find_single_slash(input) {
Err(error) => Err(error)
Ok(slash) => {
let address_text = input[:slash].to_owned()
let prefix_text = input[slash + 1:].to_owned()
match Ipv6Address::parse(address_text) {
Err(error) => Err(error)
Ok(address) =>
match parse_ipv6_prefix(prefix_text) {
Err(error) => Err(error)
Ok(prefix) => Ipv6Network::new(address, prefix)
}
}
}
}
}
///|
pub fn Ipv6Network::contains(self : Ipv6Network, address : Ipv6Address) -> Bool {
address.mask(self.prefix) == self.address
}
///|
pub fn Ipv6Network::address(self : Ipv6Network) -> Ipv6Address {
self.address
}
///|
pub fn Ipv6Network::prefix(self : Ipv6Network) -> Int {
self.prefix
}
///|
pub fn Ipv6Network::to_string(self : Ipv6Network) -> String {
"\{self.address.to_string()}/\{self.prefix}"
}
///|
fn parse_full_ipv6(input : String) -> Result[Ipv6Address, CidrError] {
let pieces = split_ipv6_groups(input)
if pieces.length() != 8 {
return Err(InvalidOctetCount(pieces.length()))
}
let segments = Array::new()
for piece in pieces {
match parse_hex16(piece) {
Err(error) => return Err(error)
Ok(segment) => segments.push(segment)
}
}
Ipv6Address::from_segments(
segments[0],
segments[1],
segments[2],
segments[3],
segments[4],
segments[5],
segments[6],
segments[7],
)
}
///|
fn parse_compressed_ipv6(input : String) -> Result[Ipv6Address, CidrError] {
let double = find_double_colon(input)
if double < 0 {
return Err(InvalidPrefix(input))
}
let left_text = input[:double].to_owned()
let right_text = input[double + 2:].to_owned()
let left = if left_text.length() == 0 {
[]
} else {
split_ipv6_groups(left_text)
}
let right = if right_text.length() == 0 {
[]
} else {
split_ipv6_groups(right_text)
}
if left.length() + right.length() >= 8 {
return Err(InvalidOctetCount(left.length() + right.length()))
}
let zeros = 8 - left.length() - right.length()
let segments = Array::new()
for piece in left {
match parse_hex16(piece) {
Err(error) => return Err(error)
Ok(segment) => segments.push(segment)
}
}
let mut inserted = 0
while inserted < zeros {
segments.push(0)
inserted = inserted + 1
}
for piece in right {
match parse_hex16(piece) {
Err(error) => return Err(error)
Ok(segment) => segments.push(segment)
}
}
Ipv6Address::from_segments(
segments[0],
segments[1],
segments[2],
segments[3],
segments[4],
segments[5],
segments[6],
segments[7],
)
}
///|
fn split_ipv6_groups(input : String) -> Array[String] {
let pieces = Array::new()
let mut current = ""
for ch in input {
if ch == ':' {
pieces.push(current)
current = ""
} else {
current = current + ch.to_string()
}
}
pieces.push(current)
pieces
}
///|
fn contains_ipv6_compression(input : String) -> Bool {
find_double_colon(input) >= 0
}
///|
fn find_double_colon(input : String) -> Int {
let mut previous = '\u{0}'
let mut index = 0
let mut found = -1
for ch in input {
if previous == ':' && ch == ':' {
if found != -1 {
return -1
}
found = index - 1
}
previous = ch
index = index + 1
}
found
}
///|
fn parse_hex16(input : String) -> Result[Int, CidrError] {
if input.length() == 0 || input.length() > 4 {
return Err(InvalidPrefix(input))
}
let mut value = 0
for ch in input {
let digit = hex_digit(ch)
if digit < 0 {
return Err(InvalidCharacter(ch.to_string()))
}
value = value * 16 + digit
}
Ok(value)
}
///|
fn parse_ipv6_prefix(input : String) -> Result[Int, CidrError] {
if input.length() == 0 {
return Err(InvalidPrefix(input))
}
let mut value = 0
for ch in input {
if ch < '0' || ch > '9' {
return Err(InvalidPrefix(input))
}
value = value * 10 + char_digit(ch)
}
if value < 0 || value > 128 {
Err(PrefixOutOfRange(value))
} else {
Ok(value)
}
}
///|
fn hex_digit(ch : Char) -> Int {
if ch >= '0' && ch <= '9' {
ch.to_int() - '0'.to_int()
} else if ch >= 'a' && ch <= 'f' {
ch.to_int() - 'a'.to_int() + 10
} else if ch >= 'A' && ch <= 'F' {
ch.to_int() - 'A'.to_int() + 10
} else {
-1
}
}
///|
fn hex16(value : Int) -> String {
let digits = "0123456789abcdef"
let a = (value >> 12) & 0xf
let b = (value >> 8) & 0xf
let c = (value >> 4) & 0xf
let d = value & 0xf
char_at_hex(digits, a) +
char_at_hex(digits, b) +
char_at_hex(digits, c) +
char_at_hex(digits, d)
}
///|
fn char_at_hex(digits : String, index : Int) -> String {
let mut i = 0
for ch in digits {
if i == index {
return ch.to_string()
}
i = i + 1
}
"0"
}