use super::*;
use crate::element::*;
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;
use std::default::Default;
#[inline]
fn is_false(v: &bool) -> bool {
!v
}
#[inline]
fn bytes_empty(v: &ByteBuf) -> bool {
v.is_empty()
}
#[inline]
fn u32_is_zero(v: &u32) -> bool {
*v == 0
}
#[inline]
fn u32_is_max(v: &u32) -> bool {
*v == u32::MAX
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct BinValidator {
#[serde(skip_serializing_if = "String::is_empty")]
pub comment: String,
#[serde(skip_serializing_if = "bytes_empty")]
pub bits_clr: ByteBuf,
#[serde(skip_serializing_if = "bytes_empty")]
pub bits_set: ByteBuf,
#[serde(skip_serializing_if = "bytes_empty")]
pub max: ByteBuf,
#[serde(skip_serializing_if = "bytes_empty")]
pub min: ByteBuf,
#[serde(skip_serializing_if = "is_false")]
pub ex_max: bool,
#[serde(skip_serializing_if = "is_false")]
pub ex_min: bool,
#[serde(skip_serializing_if = "u32_is_max")]
pub max_len: u32,
#[serde(skip_serializing_if = "u32_is_zero")]
pub min_len: u32,
#[serde(rename = "in", skip_serializing_if = "Vec::is_empty")]
pub in_list: Vec<ByteBuf>,
#[serde(rename = "nin", skip_serializing_if = "Vec::is_empty")]
pub nin_list: Vec<ByteBuf>,
#[serde(skip_serializing_if = "is_false")]
pub query: bool,
#[serde(skip_serializing_if = "is_false")]
pub bit: bool,
#[serde(skip_serializing_if = "is_false")]
pub ord: bool,
#[serde(skip_serializing_if = "is_false")]
pub size: bool,
}
impl Default for BinValidator {
fn default() -> Self {
Self {
comment: String::new(),
bits_clr: ByteBuf::new(),
bits_set: ByteBuf::new(),
ex_max: false,
ex_min: false,
max: ByteBuf::new(),
min: ByteBuf::new(),
max_len: u32::MAX,
min_len: u32::MIN,
in_list: Vec::new(),
nin_list: Vec::new(),
query: false,
bit: false,
ord: false,
size: false,
}
}
}
impl BinValidator {
pub fn new() -> Self {
Self::default()
}
pub fn comment(mut self, comment: impl Into<String>) -> Self {
self.comment = comment.into();
self
}
pub fn bits_set(mut self, bits_set: impl Into<Vec<u8>>) -> Self {
self.bits_set = ByteBuf::from(bits_set);
self
}
pub fn bits_clr(mut self, bits_clr: impl Into<Vec<u8>>) -> Self {
self.bits_clr = ByteBuf::from(bits_clr);
self
}
pub fn max(mut self, max: impl Into<Vec<u8>>) -> Self {
self.max = ByteBuf::from(max);
self
}
pub fn min(mut self, min: impl Into<Vec<u8>>) -> Self {
self.min = ByteBuf::from(min);
self
}
pub fn ex_max(mut self, ex_max: bool) -> Self {
self.ex_max = ex_max;
self
}
pub fn ex_min(mut self, ex_min: bool) -> Self {
self.ex_min = ex_min;
self
}
pub fn max_len(mut self, max_len: u32) -> Self {
self.max_len = max_len;
self
}
pub fn min_len(mut self, min_len: u32) -> Self {
self.min_len = min_len;
self
}
pub fn in_add(mut self, add: impl Into<Vec<u8>>) -> Self {
self.in_list.push(ByteBuf::from(add));
self
}
pub fn nin_add(mut self, add: impl Into<Vec<u8>>) -> Self {
self.nin_list.push(ByteBuf::from(add));
self
}
pub fn query(mut self, query: bool) -> Self {
self.query = query;
self
}
pub fn bit(mut self, bit: bool) -> Self {
self.bit = bit;
self
}
pub fn ord(mut self, ord: bool) -> Self {
self.ord = ord;
self
}
pub fn size(mut self, size: bool) -> Self {
self.size = size;
self
}
pub fn build(self) -> Validator {
Validator::Bin(self)
}
pub(crate) fn validate(&self, parser: &mut Parser) -> Result<()> {
use std::iter::repeat;
let elem = parser
.next()
.ok_or_else(|| Error::FailValidate("expected binary data".to_string()))??;
let val = if let Element::Bin(v) = elem {
v
} else {
return Err(Error::FailValidate(format!(
"expected Bin, got {}",
elem.name()
)));
};
if (val.len() as u32) > self.max_len {
return Err(Error::FailValidate(
"Bin is longer than max_len".to_string(),
));
}
if (val.len() as u32) < self.min_len {
return Err(Error::FailValidate(
"Bin is shorter than min_len".to_string(),
));
}
if self
.bits_set
.iter()
.zip(val.iter().chain(repeat(&0u8)))
.any(|(bit, val)| (bit & val) != *bit)
{
return Err(Error::FailValidate(
"Bin does not have all required bits set".to_string(),
));
}
if self
.bits_clr
.iter()
.zip(val.iter().chain(repeat(&0u8)))
.any(|(bit, val)| (bit & val) != 0)
{
return Err(Error::FailValidate(
"Bin does not have all required bits cleared".to_string(),
));
}
use std::cmp::Ordering;
fn compare(lhs: &[u8], rhs: &[u8]) -> Ordering {
match lhs.len().cmp(&rhs.len()) {
Ordering::Equal => Iterator::cmp(lhs.iter().rev(), rhs.iter().rev()),
other => other,
}
}
fn trim(val: &[u8]) -> &[u8] {
let trim_amount = val.iter().rev().take_while(|v| **v == 0).count();
&val[0..(val.len() - trim_amount)]
}
if !self.max.is_empty() || !self.min.is_empty() || self.ex_min {
let trimmed_val = trim(val);
let max_pass = match (self.max.is_empty(), self.ex_max) {
(true, _) => true,
(false, true) => compare(trimmed_val, trim(&self.max)) == Ordering::Less,
(false, false) => compare(trimmed_val, trim(&self.max)) != Ordering::Greater,
};
let min_pass = match (self.min.is_empty(), self.ex_min) {
(true, true) => !trimmed_val.is_empty(),
(true, false) => true,
(false, true) => compare(trimmed_val, trim(&self.min)) == Ordering::Greater,
(false, false) => compare(trimmed_val, trim(&self.min)) != Ordering::Less,
};
if !max_pass {
return Err(Error::FailValidate(
"Bin greater than maximum allowed".to_string(),
));
}
if !min_pass {
return Err(Error::FailValidate(
"Bin less than minimum allowed".to_string(),
));
}
}
if !self.in_list.is_empty() && !self.in_list.iter().any(|v| *v == val) {
return Err(Error::FailValidate("Bin is not on `in` list".to_string()));
}
if self.nin_list.iter().any(|v| *v == val) {
return Err(Error::FailValidate("Bin is on `nin` list".to_string()));
}
Ok(())
}
fn query_check_self(&self, other: &Self) -> bool {
(self.query || (other.in_list.is_empty() && other.nin_list.is_empty()))
&& (self.bit || (other.bits_set.is_empty() && other.bits_clr.is_empty()))
&& (self.ord
|| (!other.ex_min && !other.ex_max && other.min.is_empty() && other.max.is_empty()))
&& (self.size || (u32_is_max(&other.max_len) && u32_is_zero(&other.min_len)))
}
pub(crate) fn query_check(&self, other: &Validator) -> bool {
match other {
Validator::Bin(other) => self.query_check_self(other),
Validator::Multi(list) => list.iter().all(|other| match other {
Validator::Bin(other) => self.query_check_self(other),
_ => false,
}),
Validator::Any => true,
_ => false,
}
}
}