use std::collections::BTreeSet;
use std::fmt::Debug;
use std::hash::Hash;
use std::io::{Read, Seek, SeekFrom};
use strict_encoding::StrictDecode;
use crate::{
KeyType, PrimitiveType, StructField, StructType, TypeConstr, TypeName, TypeRef, TypeSystem,
};
pub trait Verify {
fn verify(&self, ts: &TypeSystem, buf: &mut (impl Read + Seek)) -> bool;
}
impl Verify for TypeName {
fn verify(&self, ts: &TypeSystem, buf: &mut (impl Read + Seek)) -> bool {
match ts.get(self) {
None => false,
Some(ty) => ty.verify(ts, buf),
}
}
}
impl Verify for StructType {
fn verify(&self, ts: &TypeSystem, buf: &mut (impl Read + Seek)) -> bool {
for field in self {
if !field.verify(ts, buf) {
return false;
}
}
true
}
}
impl Verify for StructField {
fn verify(&self, ts: &TypeSystem, mut buf: &mut (impl Read + Seek)) -> bool {
if self.optional {
match u8::strict_decode(&mut buf) {
Err(_) => false,
Ok(0) => true,
Ok(1) => self.ty.verify(ts, buf),
Ok(_) => false,
}
} else {
self.ty.verify(ts, buf)
}
}
}
impl Verify for TypeRef {
fn verify(&self, ts: &TypeSystem, buf: &mut (impl Read + Seek)) -> bool {
match self {
TypeRef::InPlace(ty) => ty.verify(ts, buf),
TypeRef::NameRef(ty) => ty.verify(ts, buf),
}
}
}
impl<T> Verify for TypeConstr<T>
where T: Clone + Ord + Eq + Hash + Debug + Verify
{
fn verify(&self, ts: &TypeSystem, mut buf: &mut (impl Read + Seek)) -> bool {
macro_rules! pos {
() => {
buf.stream_position().expect("medium without stream position")
};
}
macro_rules! read {
($pos_from:expr, $pos_to:expr) => {{
let len = $pos_to - $pos_from;
buf.seek(SeekFrom::Current(-(len as i64))).expect("medium without seek operation");
let mut vec = vec![0u8; len as usize];
buf.read_exact(&mut vec).expect("medium without seek operation");
vec
}};
}
match self {
TypeConstr::Plain(ty) => ty.verify(ts, buf),
TypeConstr::Array(len, ty) => {
for _ in 0..*len {
if !ty.verify(ts, buf) {
return false;
}
}
true
}
TypeConstr::List(ty) => {
let len = match u16::strict_decode(&mut buf) {
Err(_) => return false,
Ok(len) => len,
};
for _ in 0..len {
if !ty.verify(ts, buf) {
return false;
}
}
true
}
TypeConstr::Set(ty) => {
let len = match u16::strict_decode(&mut buf) {
Err(_) => return false,
Ok(len) => len,
};
let mut set = BTreeSet::new();
for _ in 0..len {
let pos_from = pos!();
if !ty.verify(ts, buf) {
return false;
}
let pos_to = pos!();
let val = read!(pos_from, pos_to);
if let Some(last) = set.iter().last() {
if val <= *last {
return false;
}
}
if !set.insert(val) {
return false;
}
}
true
}
TypeConstr::Map(key, val) => {
let len = match u16::strict_decode(&mut buf) {
Err(_) => return false,
Ok(len) => len,
};
let mut set = BTreeSet::new();
for _ in 0..len {
let pos_from = pos!();
if !key.verify(ts, buf) {
return false;
}
let pos_to = pos!();
let k = read!(pos_from, pos_to);
if let Some(last) = set.iter().last() {
if k <= *last {
return false;
}
}
if !set.insert(k) {
return false;
}
if !val.verify(ts, buf) {
return false;
}
}
true
}
}
}
}
impl Verify for KeyType {
fn verify(&self, ts: &TypeSystem, buf: &mut (impl Read + Seek)) -> bool {
match self {
KeyType::Primitive(ty) => ty.verify(ts, buf),
KeyType::Array(len, ty) => TypeConstr::Array(*len, *ty).verify(ts, buf),
KeyType::List(ty) => TypeConstr::List(*ty).verify(ts, buf),
}
}
}
impl Verify for PrimitiveType {
fn verify(&self, _: &TypeSystem, mut buf: &mut (impl Read + Seek)) -> bool {
let len = match self {
PrimitiveType::U8 => 1,
PrimitiveType::U16 => 2,
PrimitiveType::U32 => 4,
PrimitiveType::U64 => 8,
PrimitiveType::U128 => 16,
PrimitiveType::U256 => 32,
PrimitiveType::U512 => 64,
PrimitiveType::U1024 => 128,
PrimitiveType::I8 => 1,
PrimitiveType::I16 => 2,
PrimitiveType::I32 => 4,
PrimitiveType::I64 => 8,
PrimitiveType::I128 => 16,
PrimitiveType::I256 => 32,
PrimitiveType::I512 => 64,
PrimitiveType::I1024 => 128,
PrimitiveType::F16b => 2,
PrimitiveType::F16 => 2,
PrimitiveType::F32 => 4,
PrimitiveType::F64 => 8,
PrimitiveType::F80 => 10,
PrimitiveType::F128 => 16,
PrimitiveType::F256 => 32,
PrimitiveType::F512 => 64,
PrimitiveType::AsciiChar | PrimitiveType::UnicodeChar => {
match u16::strict_decode(&mut buf) {
Err(_) => return false,
Ok(len) => len,
}
}
};
match buf.seek(SeekFrom::Current(len as i64)) {
Err(_) => false,
Ok(_) => true,
}
}
}