///|
pub enum CidrError {
EmptyInput
InvalidCharacter(String)
InvalidOctetCount(Int)
EmptyOctet(Int)
OctetOutOfRange(Int, Int)
InvalidPrefix(String)
PrefixOutOfRange(Int)
InvalidRange(String)
TooManySubnets(Int)
} derive(Eq, Debug)
///|
pub struct Ipv4Address {
value : UInt
} derive(Eq, Compare, Debug)
///|
pub struct Ipv4Network {
address : Ipv4Address
prefix : Int
} derive(Eq, Debug)
///|
pub fn Ipv4Address::from_octets(
a : Int,
b : Int,
c : Int,
d : Int,
) -> Result[Ipv4Address, CidrError] {
match guard_octet(a, 0) {
Err(error) => Err(error)
Ok(_) =>
match guard_octet(b, 1) {
Err(error) => Err(error)
Ok(_) =>
match guard_octet(c, 2) {
Err(error) => Err(error)
Ok(_) =>
match guard_octet(d, 3) {
Err(error) => Err(error)
Ok(_) =>
Ok({
value: (a.reinterpret_as_uint() << 24) |
(b.reinterpret_as_uint() << 16) |
(c.reinterpret_as_uint() << 8) |
d.reinterpret_as_uint(),
})
}
}
}
}
}
///|
pub fn Ipv4Address::parse(input : String) -> Result[Ipv4Address, CidrError] {
if input.length() == 0 {
return Err(EmptyInput)
}
let octets = Array::new()
let mut current = 0
let mut has_digit = false
let mut index = 0
for ch in input {
if ch >= '0' && ch <= '9' {
current = current * 10 + char_digit(ch)
if current > 255 {
return Err(OctetOutOfRange(index, current))
}
has_digit = true
} else if ch == '.' {
if !has_digit {
return Err(EmptyOctet(index))
}
octets.push(current)
current = 0
has_digit = false
index = index + 1
} else {
return Err(InvalidCharacter(ch.to_string()))
}
}
if !has_digit {
return Err(EmptyOctet(index))
}
octets.push(current)
if octets.length() != 4 {
return Err(InvalidOctetCount(octets.length()))
}
Ipv4Address::from_octets(octets[0], octets[1], octets[2], octets[3])
}
///|
pub fn Ipv4Address::to_uint(self : Ipv4Address) -> UInt {
self.value
}
///|
pub fn Ipv4Address::octet(self : Ipv4Address, index : Int) -> Int {
match index {
0 => ((self.value >> 24) & 0xff).reinterpret_as_int()
1 => ((self.value >> 16) & 0xff).reinterpret_as_int()
2 => ((self.value >> 8) & 0xff).reinterpret_as_int()
3 => (self.value & 0xff).reinterpret_as_int()
_ => 0
}
}
///|
pub fn Ipv4Address::to_string(self : Ipv4Address) -> String {
"\{self.octet(0)}.\{self.octet(1)}.\{self.octet(2)}.\{self.octet(3)}"
}
///|
pub fn Ipv4Network::new(
address : Ipv4Address,
prefix : Int,
) -> Result[Ipv4Network, CidrError] {
match guard_prefix(prefix) {
Err(error) => Err(error)
Ok(_) => Ok({ address: address.mask(prefix), prefix })
}
}
///|
pub fn Ipv4Network::parse(input : String) -> Result[Ipv4Network, 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 Ipv4Address::parse(address_text) {
Err(error) => Err(error)
Ok(address) =>
match parse_prefix(prefix_text) {
Err(error) => Err(error)
Ok(prefix) => Ipv4Network::new(address, prefix)
}
}
}
}
}
///|
pub fn Ipv4Network::address(self : Ipv4Network) -> Ipv4Address {
self.address
}
///|
pub fn Ipv4Network::prefix(self : Ipv4Network) -> Int {
self.prefix
}
///|
pub fn Ipv4Network::contains(self : Ipv4Network, address : Ipv4Address) -> Bool {
address.mask(self.prefix).value == self.address.value
}
///|
pub fn Ipv4Network::contains_network(
self : Ipv4Network,
other : Ipv4Network,
) -> Bool {
self.prefix <= other.prefix && self.contains(other.address)
}
///|
pub fn Ipv4Network::is_more_specific_than(
self : Ipv4Network,
other : Ipv4Network,
) -> Bool {
self.prefix > other.prefix && other.contains_network(self)
}
///|
pub fn Ipv4Network::to_string(self : Ipv4Network) -> String {
"\{self.address.to_string()}/\{self.prefix}"
}
///|
pub fn Ipv4Network::broadcast(self : Ipv4Network) -> Ipv4Address {
let host_mask = if self.prefix == 32 {
0U
} else {
4294967295U >> self.prefix
}
{ value: self.address.value | host_mask }
}
///|
pub fn Ipv4Network::first(self : Ipv4Network) -> Ipv4Address {
self.address
}
///|
pub fn Ipv4Network::last(self : Ipv4Network) -> Ipv4Address {
self.broadcast()
}
///|
fn Ipv4Address::mask(self : Ipv4Address, prefix : Int) -> Ipv4Address {
let mask = ipv4_mask(prefix)
{ value: self.value & mask }
}
///|
fn ipv4_mask(prefix : Int) -> UInt {
if prefix == 0 {
0U
} else {
4294967295U << (32 - prefix)
}
}
///|
fn guard_octet(value : Int, index : Int) -> Result[Unit, CidrError] {
if value < 0 || value > 255 {
Err(OctetOutOfRange(index, value))
} else {
Ok(())
}
}
///|
fn guard_prefix(prefix : Int) -> Result[Unit, CidrError] {
if prefix < 0 || prefix > 32 {
Err(PrefixOutOfRange(prefix))
} else {
Ok(())
}
}
///|
fn char_digit(ch : Char) -> Int {
ch.to_int() - '0'.to_int()
}
///|
fn find_single_slash(input : String) -> Result[Int, CidrError] {
let mut slash = -1
let mut index = 0
for ch in input {
if ch == '/' {
if slash != -1 {
return Err(InvalidPrefix(input))
}
slash = index
}
index = index + 1
}
if slash == -1 {
Err(InvalidPrefix(input))
} else {
Ok(slash)
}
}
///|
fn parse_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)
}
match guard_prefix(value) {
Err(error) => Err(error)
Ok(_) => Ok(value)
}
}