use rupg_common::{Error, Result, SqlState};
use rupg_types::{Inet, NetFamily, Value, bitncmp, bitncommon};
use crate::{Call, Kernel, bad_value};
fn arg(args: &[Value], n: usize) -> Result<&Inet> {
match args.get(n) {
Some(Value::Inet(v)) => Ok(v),
_ => Err(bad_value()),
}
}
fn int8(args: &[Value], n: usize) -> Result<i64> {
match args.get(n) {
Some(Value::Int8(v)) => Ok(*v),
_ => Err(bad_value()),
}
}
fn address(ip: &Inet) -> String {
let mut text = String::new();
rupg_types::net_ntop(ip, ip.family.max_bits(), &mut text);
text
}
fn host(_: &Call<'_>, args: &[Value]) -> Result<Value> {
Ok(Value::Text(address(arg(args, 0)?)))
}
fn show(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
let mut text = address(ip);
if !text.contains('/') {
text.push('/');
text.push_str(&ip.bits.to_string());
}
Ok(Value::Text(text))
}
fn inet_abbrev(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
let mut text = String::new();
rupg_types::net_ntop(ip, ip.bits, &mut text);
Ok(Value::Text(text))
}
fn cidr_abbrev(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let mut text = String::new();
rupg_types::cidr_abbrev(arg(args, 0)?, &mut text);
Ok(Value::Text(text))
}
fn masklen(_: &Call<'_>, args: &[Value]) -> Result<Value> {
Ok(Value::Int4(i32::from(arg(args, 0)?.bits)))
}
fn family(_: &Call<'_>, args: &[Value]) -> Result<Value> {
Ok(Value::Int4(match arg(args, 0)?.family {
NetFamily::V4 => 4,
NetFamily::V6 => 6,
}))
}
fn mask(bits: u8) -> [u8; 16] {
let mut out = [0u8; 16];
for (n, byte) in out.iter_mut().enumerate() {
let left = usize::from(bits).saturating_sub(n * 8).min(8);
*byte = if left == 0 { 0 } else { 0xff << (8 - left) };
}
out
}
fn each_byte(ip: &Inet, bits: u8, f: impl Fn(usize) -> u8) -> Value {
let mut addr = [0u8; 16];
for (n, byte) in addr.iter_mut().take(ip.family.size()).enumerate() {
*byte = f(n);
}
Value::Inet(Inet { family: ip.family, bits, addr })
}
fn broadcast(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
let m = mask(ip.bits);
Ok(each_byte(ip, ip.bits, |n| ip.addr[n] | !m[n]))
}
fn network(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
let m = mask(ip.bits);
Ok(each_byte(ip, ip.bits, |n| ip.addr[n] & m[n]))
}
fn netmask(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
let m = mask(ip.bits);
Ok(each_byte(ip, ip.family.max_bits(), |n| m[n]))
}
fn hostmask(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
let m = mask(ip.bits);
Ok(each_byte(ip, ip.family.max_bits(), |n| !m[n]))
}
fn new_masklen(ip: &Inet, args: &[Value]) -> Result<u8> {
let Some(Value::Int4(bits)) = args.get(1) else { return Err(bad_value()) };
let bits = if *bits == -1 { i32::from(ip.family.max_bits()) } else { *bits };
u8::try_from(bits).ok().filter(|b| *b <= ip.family.max_bits()).ok_or_else(|| {
Error::new(SqlState::INVALID_PARAMETER_VALUE, format!("invalid mask length: {bits}"))
})
}
fn inet_set_masklen(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
Ok(Value::Inet(Inet { bits: new_masklen(ip, args)?, ..*ip }))
}
fn cidr_set_masklen(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
Ok(Value::Inet(ip.masked(new_masklen(ip, args)?)))
}
fn to_cidr(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
Ok(Value::Inet(ip.masked(ip.bits)))
}
fn same_family(_: &Call<'_>, args: &[Value]) -> Result<Value> {
Ok(Value::Bool(arg(args, 0)?.family == arg(args, 1)?.family))
}
fn merge(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let (a, b) = (arg(args, 0)?, arg(args, 1)?);
if a.family != b.family {
return Err(Error::new(
SqlState::INVALID_PARAMETER_VALUE,
"cannot merge addresses from different families",
));
}
Ok(Value::Inet(a.masked(bitncommon(&a.addr, &b.addr, a.bits.min(b.bits)))))
}
fn contains(args: &[Value], lengths: fn(u8, u8) -> bool, bits: fn(u8, u8) -> u8) -> Result<Value> {
let (a, b) = (arg(args, 0)?, arg(args, 1)?);
Ok(Value::Bool(
a.family == b.family
&& lengths(a.bits, b.bits)
&& bitncmp(&a.addr, &b.addr, bits(a.bits, b.bits)) == 0,
))
}
fn sub(_: &Call<'_>, args: &[Value]) -> Result<Value> {
contains(args, |a, b| a > b, |_, b| b)
}
fn subeq(_: &Call<'_>, args: &[Value]) -> Result<Value> {
contains(args, |a, b| a >= b, |_, b| b)
}
fn sup(_: &Call<'_>, args: &[Value]) -> Result<Value> {
contains(args, |a, b| a < b, |a, _| a)
}
fn supeq(_: &Call<'_>, args: &[Value]) -> Result<Value> {
contains(args, |a, b| a <= b, |a, _| a)
}
fn overlap(_: &Call<'_>, args: &[Value]) -> Result<Value> {
contains(args, |_, _| true, u8::min)
}
fn cmp(_: &Call<'_>, args: &[Value]) -> Result<Value> {
Ok(Value::Int4(rupg_types::network_cmp(arg(args, 0)?, arg(args, 1)?)))
}
fn not(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let ip = arg(args, 0)?;
Ok(each_byte(ip, ip.bits, |n| !ip.addr[n]))
}
fn bitwise(args: &[Value], what: &str, f: fn(u8, u8) -> u8) -> Result<Value> {
let (a, b) = (arg(args, 0)?, arg(args, 1)?);
if a.family != b.family {
return Err(Error::new(
SqlState::INVALID_PARAMETER_VALUE,
format!("cannot {what} inet values of different sizes"),
));
}
Ok(each_byte(a, a.bits.max(b.bits), |n| f(a.addr[n], b.addr[n])))
}
fn and(_: &Call<'_>, args: &[Value]) -> Result<Value> {
bitwise(args, "AND", |a, b| a & b)
}
fn or(_: &Call<'_>, args: &[Value]) -> Result<Value> {
bitwise(args, "OR", |a, b| a | b)
}
fn out_of_range() -> Error {
Error::new(SqlState::NUMERIC_VALUE_OUT_OF_RANGE, "result is out of range")
}
fn plus(ip: &Inet, mut addend: i64) -> Result<Value> {
let mut addr = [0u8; 16];
let mut carry = 0i64;
for n in (0..ip.family.size()).rev() {
carry += i64::from(ip.addr[n]) + (addend & 0xff);
addr[n] = u8::try_from(carry & 0xff).unwrap_or_default();
carry >>= 8;
addend = (addend & !0xff) / 0x100;
}
if !(addend == 0 && carry == 0 || addend == -1 && carry == 1) {
return Err(out_of_range());
}
Ok(Value::Inet(Inet { addr, ..*ip }))
}
fn inetpl(_: &Call<'_>, args: &[Value]) -> Result<Value> {
plus(arg(args, 0)?, int8(args, 1)?)
}
pub(crate) fn int8pl_inet(_: &Call<'_>, args: &[Value]) -> Result<Value> {
plus(arg(args, 1)?, int8(args, 0)?)
}
fn inetmi_int8(_: &Call<'_>, args: &[Value]) -> Result<Value> {
plus(arg(args, 0)?, int8(args, 1)?.wrapping_neg())
}
fn inetmi(_: &Call<'_>, args: &[Value]) -> Result<Value> {
let (a, b) = (arg(args, 0)?, arg(args, 1)?);
if a.family != b.family {
return Err(Error::new(
SqlState::INVALID_PARAMETER_VALUE,
"cannot subtract inet values of different sizes",
));
}
let mut res = 0i64;
let mut carry = 1i32;
let mut byte = 0u32;
for n in (0..a.family.size()).rev() {
carry += i32::from(a.addr[n]) + i32::from(!b.addr[n]);
let lobyte = carry & 0xff;
if byte < 8 {
res |= i64::from(lobyte) << (byte * 8);
} else if (res < 0 && lobyte != 0xff) || (res >= 0 && lobyte != 0) {
return Err(out_of_range());
}
carry >>= 8;
byte += 1;
}
if carry == 0 && byte < 8 {
res |= -1i64 << (byte * 8);
}
Ok(Value::Int8(res))
}
pub(crate) fn by_src(src: &str) -> Option<Kernel> {
Some(match src {
"network_host" => host,
"network_show" => show,
"inet_abbrev" => inet_abbrev,
"cidr_abbrev" => cidr_abbrev,
"network_masklen" => masklen,
"network_family" => family,
"network_broadcast" => broadcast,
"network_network" => network,
"network_netmask" => netmask,
"network_hostmask" => hostmask,
"inet_set_masklen" => inet_set_masklen,
"cidr_set_masklen" => cidr_set_masklen,
"inet_to_cidr" => to_cidr,
"inet_same_family" => same_family,
"inet_merge" => merge,
"network_cmp" => cmp,
"network_sub" => sub,
"network_subeq" => subeq,
"network_sup" => sup,
"network_supeq" => supeq,
"network_overlap" => overlap,
"inetnot" => not,
"inetand" => and,
"inetor" => or,
"inetpl" => inetpl,
"inetmi_int8" => inetmi_int8,
"inetmi" => inetmi,
_ => return None,
})
}