From 03a5c97d4261b97d9562d84b69d2a1e5e58ff81b Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Fri, 12 Nov 2021 11:38:01 -0600 Subject: [PATCH 1/3] Narrowed dependencies --- lib/allocate.rs | 502 +++++++++------- lib/ddlog_bigint.rs | 1264 +++++++++++++++++++++-------------------- lib/ddlog_bigint.toml | 2 + lib/ddlog_log.rs | 271 +++++---- lib/ddlog_log.toml | 2 + lib/ddlog_std.rs | 128 ++++- lib/ddlog_std.toml | 7 + lib/fp.toml | 2 + lib/hashset.rs | 826 +++++++++++++-------------- lib/intern.toml | 12 +- lib/json.toml | 2 + lib/net/ipv6.rs | 484 ++++++++-------- lib/tinyset.rs | 681 +++++++++++----------- lib/url.rs | 246 ++++---- 14 files changed, 2294 insertions(+), 2135 deletions(-) create mode 100644 lib/ddlog_bigint.toml create mode 100644 lib/ddlog_log.toml create mode 100644 lib/ddlog_std.toml create mode 100644 lib/fp.toml create mode 100644 lib/json.toml diff --git a/lib/allocate.rs b/lib/allocate.rs index 9fed5cf05..a2e4f8b42 100644 --- a/lib/allocate.rs +++ b/lib/allocate.rs @@ -1,221 +1,281 @@ -/* -Copyright (c) 2021 VMware, Inc. -SPDX-License-Identifier: MIT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -use std::cmp; -use std::collections::BTreeSet; -use std::ops; - -pub fn allocate( - allocated: &ddlog_std::Set, - toallocate: ddlog_std::Vec, - min_val: &N, - max_val: &N, -) -> ddlog_std::Vec> { - assert!(*max_val >= *min_val); - let range = *max_val - *min_val; - - // Next index to consider - let mut next = *allocated.x.range(..).next_back().unwrap_or(&max_val); - // allocated may contain values outside of the [min_val..max_val] range. - if next < *min_val || next > *max_val { - next = *max_val; - }; - let mut offset = N::zero(); - let mut res = ddlog_std::Vec::new(); - for b in toallocate.into_iter() { - loop { - next = if next == *max_val { - *min_val - } else { - next + N::one() - }; - - if allocated.x.contains(&next) { - if offset == range { - return res; - }; - offset = offset + N::one(); - continue; - } else { - res.push(ddlog_std::tuple2(b, next)); - if offset == range { - return res; - }; - offset = offset + N::one(); - break; - } - } - } - return res; -} - -pub fn allocate_with_hint( - allocated: &ddlog_std::Set, - toallocate: ddlog_std::Vec>>, - min_val: &N, - max_val: &N, -) -> ddlog_std::Vec> { - assert!(*max_val >= *min_val); - let range = *max_val - *min_val; - let mut new_allocations: BTreeSet = BTreeSet::new(); - - // Next index to consider - let mut next = *allocated.x.range(..).next_back().unwrap_or(&min_val); - // allocated may contain values outside of the [min_val..max_val] range. - if next < *min_val || next > *max_val { - next = *min_val; - }; - let mut res = ddlog_std::Vec::new(); - for ddlog_std::tuple2(b, hint) in toallocate.into_iter() { - let mut offset = N::zero(); - next = match hint { - ddlog_std::Option::None => next, - ddlog_std::Option::Some { x: h } => h, - }; - loop { - if allocated.x.contains(&next) || new_allocations.contains(&next) { - if offset == range { - return res; - }; - offset = offset + N::one(); - next = if next == *max_val { - *min_val - } else { - next + N::one() - }; - continue; - } else { - res.push(ddlog_std::tuple2(b, next)); - new_allocations.insert(next); - if offset == range { - return res; - }; - next = if next == *max_val { - *min_val - } else { - next + N::one() - }; - break; - } - } - } - return res; -} - -pub fn allocate_opt( - allocated: &ddlog_std::Set, - toallocate: ddlog_std::Vec, - min_val: &N, - max_val: &N, -) -> ddlog_std::Vec>> { - assert!(*max_val >= *min_val); - let range = *max_val - *min_val; - - // Next index to consider - let mut next = *allocated.x.range(..).next_back().unwrap_or(&max_val); - // allocated may contain values outside of the [min_val..max_val] range. - if next < *min_val || next > *max_val { - next = *max_val; - }; - let mut offset = N::zero(); - let mut res = ddlog_std::Vec::new(); - // Signal that address space has been exhausted, but iteration must continue to - // assign `None` to all remaining items. - let mut exhausted = false; - for b in toallocate.into_iter() { - loop { - if exhausted { - res.push(ddlog_std::tuple2(b, ddlog_std::Option::None)); - break; - }; - if offset == range { - exhausted = true; - }; - next = if next == *max_val { - *min_val - } else { - next + N::one() - }; - offset = offset + N::one(); - - if allocated.x.contains(&next) { - continue; - } else { - res.push(ddlog_std::tuple2(b, ddlog_std::Option::Some { x: next })); - break; - } - } - } - return res; -} - -pub fn adjust_allocation( - allocated: &ddlog_std::Map, - toallocate: &ddlog_std::Vec, - min_val: &N, - max_val: &N, -) -> ddlog_std::Vec> { - assert!(*max_val >= *min_val); - let range = *max_val - *min_val; - - let allocated_ids: BTreeSet = allocated.x.values().cloned().collect(); - let mut next = *allocated_ids.range(..).next_back().unwrap_or(&max_val); - // allocated may contain values outside of the [min_val..max_val] range. - if next < *min_val || next > *max_val { - next = *max_val; - }; - let mut offset = N::zero(); - let mut res = ddlog_std::Vec::new(); - // Signal that address space has been exhausted, but iteration must continue to - // preserve existing allocations. - let mut exhausted = false; - for b in toallocate.iter() { - match allocated.x.get(b) { - Some(x) => { - res.push(ddlog_std::tuple2((*b).clone(), x.clone())); - } - None => loop { - if exhausted { - break; - }; - if offset == range { - exhausted = true; - }; - next = if next == *max_val { - *min_val - } else { - next + N::one() - }; - offset = offset + N::one(); - - if allocated_ids.contains(&next) { - continue; - } else { - res.push(ddlog_std::tuple2((*b).clone(), next)); - break; - } - }, - } - } - return res; -} +/* +Copyright (c) 2021 VMware, Inc. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use std::{cmp, collections::BTreeSet, ops}; + +pub fn allocate( + allocated: &ddlog_std::Set, + toallocate: ddlog_std::Vec, + &min_val: &N, + &max_val: &N, +) -> ddlog_std::Vec> { + assert!(max_val >= min_val); + let range = max_val - min_val; + + // Next index to consider + let mut next = *allocated.x.range(..).next_back().unwrap_or(&max_val); + // allocated may contain values outside of the [min_val..max_val] range. + if next < min_val || next > max_val { + next = max_val; + } + + let mut offset = N::zero(); + let mut res = ddlog_std::Vec::new(); + for b in toallocate.into_iter() { + loop { + next = if next == max_val { + min_val + } else { + next + N::one() + }; + + if allocated.x.contains(&next) { + if offset == range { + return res; + } + offset = offset + N::one(); + + continue; + } else { + res.push(ddlog_std::tuple2(b, next)); + if offset == range { + return res; + } + offset = offset + N::one(); + + break; + } + } + } + + res +} + +pub fn allocate_with_hint( + allocated: &ddlog_std::Set, + toallocate: ddlog_std::Vec>>, + &min_val: &N, + &max_val: &N, +) -> ddlog_std::Vec> { + assert!(max_val >= min_val); + let range = max_val - min_val; + let mut new_allocations: BTreeSet = BTreeSet::new(); + + // Next index to consider + let mut next = *allocated.x.range(..).next_back().unwrap_or(&min_val); + // allocated may contain values outside of the [min_val..max_val] range. + if next < min_val || next > max_val { + next = min_val; + } + + let mut res = ddlog_std::Vec::new(); + for ddlog_std::tuple2(b, hint) in toallocate.into_iter() { + let mut offset = N::zero(); + next = match hint { + ddlog_std::Option::None => next, + ddlog_std::Option::Some { x: h } => h, + }; + + loop { + if allocated.x.contains(&next) || new_allocations.contains(&next) { + if offset == range { + return res; + } + + offset = offset + N::one(); + next = if next == max_val { + min_val + } else { + next + N::one() + }; + + continue; + } else { + res.push(ddlog_std::tuple2(b, next)); + new_allocations.insert(next); + + if offset == range { + return res; + } + + next = if next == max_val { + min_val + } else { + next + N::one() + }; + + break; + } + } + } + + res +} + +pub fn allocate_opt( + allocated: &ddlog_std::Set, + toallocate: ddlog_std::Vec, + &min_val: &N, + &max_val: &N, +) -> ddlog_std::Vec>> { + assert!(max_val >= min_val); + let range = max_val - min_val; + + // Next index to consider + let mut next = *allocated.x.range(..).next_back().unwrap_or(&max_val); + // allocated may contain values outside of the [min_val..max_val] range. + if next < min_val || next > max_val { + next = max_val; + } + + let mut offset = N::zero(); + let mut res = ddlog_std::Vec::new(); + + // Signal that address space has been exhausted, but iteration must continue to + // assign `None` to all remaining items. + let mut exhausted = false; + for b in toallocate.into_iter() { + loop { + if exhausted { + res.push(ddlog_std::tuple2(b, ddlog_std::Option::None)); + break; + } + + if offset == range { + exhausted = true; + } + + next = if next == max_val { + min_val + } else { + next + N::one() + }; + + offset = offset + N::one(); + + if allocated.x.contains(&next) { + continue; + } else { + res.push(ddlog_std::tuple2(b, ddlog_std::Option::Some { x: next })); + break; + } + } + } + + res +} + +pub fn adjust_allocation( + allocated: &ddlog_std::Map, + toallocate: &ddlog_std::Vec, + &min_val: &N, + &max_val: &N, +) -> ddlog_std::Vec> { + assert!(max_val >= min_val); + let range = max_val - min_val; + + let allocated_ids: BTreeSet = allocated.x.values().cloned().collect(); + let mut next = *allocated_ids.range(..).next_back().unwrap_or(&max_val); + + // allocated may contain values outside of the [min_val..max_val] range. + if next < min_val || next > max_val { + next = max_val; + } + + let mut offset = N::zero(); + let mut res = ddlog_std::Vec::new(); + + // Signal that address space has been exhausted, but iteration must continue to + // preserve existing allocations. + let mut exhausted = false; + for b in toallocate.iter() { + match allocated.x.get(b) { + Some(&x) => res.push(ddlog_std::tuple2((*b).clone(), x)), + None => loop { + if exhausted { + break; + } + if offset == range { + exhausted = true; + } + + next = if next == max_val { + min_val + } else { + next + N::one() + }; + offset = offset + N::one(); + + if allocated_ids.contains(&next) { + continue; + } else { + res.push(ddlog_std::tuple2((*b).clone(), next)); + break; + } + }, + } + } + + res +} + +pub trait AllocatedId: + std::marker::Copy + std::cmp::Ord + std::ops::Add + std::ops::Sub +{ + fn zero() -> Self; + + fn one() -> Self; +} + +macro_rules! impl_id { + ($($ty:ident),* $(,)?) => { + $( + impl AllocatedId for $ty { + #[inline] + fn zero() -> Self { + 0 + } + + #[inline] + fn one() -> Self { + 1 + } + } + )* + }; +} + +impl_id! { + u8, + i8, + u16, + i16, + u32, + i32, + u64, + i64, + usize, + isize, + u128, + i128, +} diff --git a/lib/ddlog_bigint.rs b/lib/ddlog_bigint.rs index 615b54c64..6a1f13bcf 100644 --- a/lib/ddlog_bigint.rs +++ b/lib/ddlog_bigint.rs @@ -1,618 +1,646 @@ -/* -Copyright (c) 2021 VMware, Inc. -SPDX-License-Identifier: MIT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -use abomonation::Abomonation; -use num::bigint::BigInt; -use num::bigint::BigUint; -pub use num::bigint::Sign; -use num::bigint::ToBigInt; -use num::ToPrimitive; -use ordered_float::OrderedFloat; -use serde::de::Error; -use serde::de::*; -use serde::ser::*; -use std::fmt; -use std::ops::*; -use std::str::FromStr; - -/* This module is designed to be imported both as a standard DDlog library and as a normal Rust - * module, e.g., from `differential_datalog_test`. We therefore need to import these traits - * so that they are available in the latter case and rename them so that they don't cause duplicate - * import error in the former case. */ -use differential_datalog::record::FromRecord as FromRec; -use differential_datalog::record::IntoRecord as IntoRec; -use differential_datalog::record::Record; - -#[derive(Eq, PartialOrd, PartialEq, Ord, Clone, Hash)] -pub struct Int { - x: BigInt, -} - -impl Default for Int { - fn default() -> Int { - Int { - x: BigInt::default(), - } - } -} - -impl Abomonation for Int {} - -impl From for Int { - fn from(other: Uint) -> Self { - other.to_Int().unwrap() - } -} - -// Generated code expects `from_()`, `to_()` functions for all -// supported integer conversions. -impl Int { - pub fn from_bigint(v: BigInt) -> Int { - Int { x: v } - } - pub fn from_u8(v: u8) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_i8(v: i8) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_u16(v: u16) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_i16(v: i16) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_u32(v: u32) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_i32(v: i32) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_u64(v: u64) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_i64(v: i64) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_u128(v: u128) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_i128(v: i128) -> Int { - Int { x: BigInt::from(v) } - } - pub fn from_Uint(v: Uint) -> Int { - Self::from(v) - } - pub fn from_bytes_be(sign: bool, bytes: &[u8]) -> Int { - Int { - x: BigInt::from_bytes_be(if sign { Sign::Plus } else { Sign::Minus }, bytes), - } - } - pub fn to_bytes_be(&self) -> (Sign, Vec) { - self.x.to_bytes_be() - } - pub fn to_i8(&self) -> Option { - self.x.to_i8() - } - pub fn to_u8(&self) -> Option { - self.x.to_u8() - } - /* Extract 8 low-order bits and convert to u8 */ - pub fn truncate_to_u8(&self) -> u8 { - (&self.x & &BigInt::from(0xffu8)).to_u8().unwrap() - } - pub fn to_i16(&self) -> Option { - self.x.to_i16() - } - pub fn to_u16(&self) -> Option { - self.x.to_u16() - } - /* Extract 16 low-order bits and convert to u16 */ - pub fn truncate_to_u16(&self) -> u16 { - (&self.x & &BigInt::from(0xffffu16)).to_u16().unwrap() - } - pub fn to_i32(&self) -> Option { - self.x.to_i32() - } - pub fn to_u32(&self) -> Option { - self.x.to_u32() - } - /* Extract 32 low-order bits and convert to u32 */ - pub fn truncate_to_u32(&self) -> u32 { - (&self.x & &BigInt::from(0xffff_ffffu32)).to_u32().unwrap() - } - pub fn to_i64(&self) -> Option { - self.x.to_i64() - } - pub fn to_u64(&self) -> Option { - self.x.to_u64() - } - /* Extract 64 low-order bits and convert to u64 */ - pub fn truncate_to_u64(&self) -> u64 { - (&self.x & &BigInt::from(0xffff_ffff_ffff_ffffu64)) - .to_u64() - .unwrap() - } - pub fn to_i128(&self) -> Option { - self.x.to_i128() - } - pub fn to_u128(&self) -> Option { - self.x.to_u128() - } - /* Extract 128 low-order bits and convert to u128 */ - pub fn truncate_to_u128(&self) -> u128 { - (&self.x & &BigInt::from(0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffffu128)) - .to_u128() - .unwrap() - } - pub fn to_float(&self) -> OrderedFloat { - match self.x.to_f32() { - None => OrderedFloat::(std::f32::NAN), - Some(x) => OrderedFloat::(x), - } - } - pub fn to_double(&self) -> OrderedFloat { - match self.x.to_f64() { - None => OrderedFloat::(std::f64::NAN), - Some(x) => OrderedFloat::(x), - } - } - pub fn to_Uint(&self) -> Option { - self.x.to_biguint().map(Uint::from_biguint) - } - pub fn parse_bytes(buf: &[u8], radix: u32) -> Int { - Int { - x: BigInt::parse_bytes(buf, radix).unwrap(), - } - } -} - -impl fmt::Display for Int { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.x) - } -} - -impl fmt::LowerHex for Int { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:x}", self.x) - } -} - -impl fmt::Debug for Int { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self, f) - } -} - -impl Serialize for Int { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.x.to_str_radix(10)) - } -} - -impl<'de> Deserialize<'de> for Int { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - match String::deserialize(deserializer) { - Ok(s) => match BigInt::from_str(&s) { - Ok(i) => Ok(Int { x: i }), - Err(_) => Err(D::Error::custom(format!("invalid integer value: {}", s))), - }, - Err(e) => Err(e), - } - } -} - -impl differential_datalog::record::FromRecord for Int { - fn from_record(val: &Record) -> Result { - Ok(Int::from_bigint(BigInt::from_record(val)?)) - } -} - -impl differential_datalog::record::IntoRecord for Int { - fn into_record(self) -> Record { - self.x.into_record() - } -} - -impl differential_datalog::record::Mutator for Record { - fn mutate(&self, i: &mut Int) -> Result<(), String> { - self.mutate(&mut i.x) - } -} - -#[test] -fn test_int_fromrecord() { - let v = (-25_i64).to_bigint().unwrap(); - assert_eq!( - Int::from_record(&Record::Int(v.clone())), - Ok(Int::from_bigint(v)) - ); -} - -impl Shr for Int { - type Output = Int; - - #[inline] - fn shr(self, rhs: u32) -> Int { - Int { - x: self.x.shr(rhs as usize), - } - } -} - -impl Shl for Int { - type Output = Int; - - #[inline] - fn shl(self, rhs: u32) -> Int { - Int { - x: self.x.shl(rhs as usize), - } - } -} - -impl Neg for Int { - type Output = Int; - - #[inline] - fn neg(self) -> Self::Output { - Int { x: self.x.neg() } - } -} - -macro_rules! forward_binop { - (impl $imp:ident for $res:ty, $method:ident) => { - impl $imp<$res> for $res { - type Output = $res; - - #[inline] - fn $method(self, other: $res) -> $res { - // forward to val-ref - Int { - x: $imp::$method(self.x, other.x), - } - } - } - }; -} - -forward_binop!(impl Add for Int, add); -forward_binop!(impl Sub for Int, sub); -forward_binop!(impl Div for Int, div); -forward_binop!(impl Rem for Int, rem); -forward_binop!(impl Mul for Int, mul); -forward_binop!(impl BitAnd for Int, bitand); -forward_binop!(impl BitOr for Int, bitor); - -impl num::One for Int { - fn one() -> Int { - Int { x: BigInt::one() } - } -} - -impl num::Zero for Int { - fn zero() -> Int { - Int { x: BigInt::zero() } - } - - fn is_zero(&self) -> bool { - self.x == BigInt::zero() - } -} - -#[derive(Eq, PartialOrd, PartialEq, Ord, Clone, Hash)] -pub struct Uint { - x: BigUint, -} - -impl Default for Uint { - fn default() -> Uint { - Uint { - x: BigUint::default(), - } - } -} - -impl Abomonation for Uint {} - -// Generated code expects `from_()`, `to_()` functions for all -// supported integer conversions. -impl Uint { - pub fn from_biguint(v: BigUint) -> Uint { - Uint { x: v } - } - pub fn from_bigint(v: BigInt) -> Uint { - Uint { - x: v.to_biguint().unwrap(), - } - } - pub fn from_u8(v: u8) -> Uint { - Uint { - x: BigUint::from(v), - } - } - pub fn from_u16(v: u16) -> Uint { - Uint { - x: BigUint::from(v), - } - } - pub fn from_u32(v: u32) -> Uint { - Uint { - x: BigUint::from(v), - } - } - pub fn from_u64(v: u64) -> Uint { - Uint { - x: BigUint::from(v), - } - } - pub fn from_u128(v: u128) -> Uint { - Uint { - x: BigUint::from(v), - } - } - pub fn from_bytes_be(bytes: &[u8]) -> Uint { - Uint { - x: BigUint::from_bytes_be(bytes), - } - } - pub fn to_bytes_be(&self) -> Vec { - self.x.to_bytes_be() - } - pub fn to_u8(&self) -> Option { - self.x.to_u8() - } - pub fn to_u16(&self) -> Option { - self.x.to_u16() - } - pub fn to_u32(&self) -> Option { - self.x.to_u32() - } - pub fn to_u64(&self) -> Option { - self.x.to_u64() - } - pub fn to_u128(&self) -> Option { - self.x.to_u128() - } - pub fn to_Int(&self) -> Option { - self.x.to_bigint().map(Int::from_bigint) - } - pub fn to_float(&self) -> OrderedFloat { - match self.x.to_f32() { - None => OrderedFloat::(std::f32::NAN), - Some(x) => OrderedFloat::(x), - } - } - pub fn to_double(&self) -> OrderedFloat { - match self.x.to_f64() { - None => OrderedFloat::(std::f64::NAN), - Some(x) => OrderedFloat::(x), - } - } - pub fn parse_bytes(buf: &[u8], radix: u32) -> Uint { - Uint { - x: BigUint::parse_bytes(buf, radix).unwrap(), - } - } -} - -impl fmt::Display for Uint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.x) - } -} - -impl fmt::LowerHex for Uint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:x}", self.x) - } -} - -impl fmt::Debug for Uint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self, f) - } -} - -impl Serialize for Uint { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.x.to_str_radix(10)) - } -} - -impl<'de> Deserialize<'de> for Uint { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - match String::deserialize(deserializer) { - Ok(s) => match BigUint::from_str(&s) { - Ok(i) => Ok(Uint { x: i }), - Err(_) => Err(D::Error::custom(format!("invalid integer value: {}", s))), - }, - Err(e) => Err(e), - } - } -} - -impl differential_datalog::record::FromRecord for Uint { - fn from_record(val: &Record) -> Result { - Ok(Uint::from_biguint(BigUint::from_record(val)?)) - } -} - -impl differential_datalog::record::IntoRecord for Uint { - fn into_record(self) -> Record { - self.x.into_record() - } -} - -impl differential_datalog::record::Mutator for Record { - fn mutate(&self, i: &mut Uint) -> Result<(), String> { - self.mutate(&mut i.x) - } -} - -#[test] -fn test_uint_fromrecord() { - let v = (25_u64).to_bigint().unwrap(); - assert_eq!( - Uint::from_record(&Record::Int(v.clone())), - Ok(Uint::from_bigint(v)) - ); -} - -/* -impl Uint { - #[inline] - pub fn parse_bytes(buf: &[u8], radix: u32) -> Uint { - Uint{x: BigUint::parse_bytes(buf, radix).unwrap()} - } -} -*/ - -/* DDlog supports 32-bit shifts */ -impl Shr for Uint { - type Output = Uint; - - #[inline] - fn shr(self, rhs: u32) -> Uint { - Uint { - x: self.x.shr(rhs as usize), - } - } -} - -impl Shl for Uint { - type Output = Uint; - - #[inline] - fn shl(self, rhs: u32) -> Uint { - Uint { - x: self.x.shl(rhs as usize), - } - } -} - -macro_rules! forward_binop { - (impl $imp:ident for $res:ty, $method:ident) => { - impl $imp<$res> for $res { - type Output = $res; - - #[inline] - fn $method(self, other: $res) -> $res { - // forward to val-ref - Uint { - x: $imp::$method(self.x, other.x), - } - } - } - }; -} - -forward_binop!(impl Add for Uint, add); -forward_binop!(impl Sub for Uint, sub); -forward_binop!(impl Div for Uint, div); -forward_binop!(impl Rem for Uint, rem); -forward_binop!(impl Mul for Uint, mul); -forward_binop!(impl BitAnd for Uint, bitand); -forward_binop!(impl BitOr for Uint, bitor); - -impl num::One for Uint { - fn one() -> Uint { - Uint { x: BigUint::one() } - } -} - -impl num::Zero for Uint { - fn zero() -> Uint { - Uint { x: BigUint::zero() } - } - - fn is_zero(&self) -> bool { - self.x == BigUint::zero() - } -} - -#[cfg(feature = "c_api")] -mod c_api { - - use super::Int; - use super::Uint; - - use std::ffi::CStr; - use std::os::raw::c_char; - - #[no_mangle] - pub extern "C" fn int_from_i64(v: i64) -> *mut Int { - Box::into_raw(Box::new(Int::from_i64(v))) - } - - #[no_mangle] - pub extern "C" fn int_from_u64(v: u64) -> *mut Int { - Box::into_raw(Box::new(Int::from_u64(v))) - } - - #[no_mangle] - pub unsafe extern "C" fn int_from_str(s: *const c_char, radix: u32) -> *mut Int { - let c_str = CStr::from_ptr(s); - Box::into_raw(Box::new(Int::parse_bytes(c_str.to_bytes(), radix))) - } - - #[no_mangle] - pub unsafe extern "C" fn int_free(x: *mut Int) { - if x.is_null() { - return; - } - Box::from_raw(x); - } - - #[no_mangle] - pub extern "C" fn uint_from_u64(v: u64) -> *mut Uint { - Box::into_raw(Box::new(Uint::from_u64(v))) - } - - #[no_mangle] - pub unsafe extern "C" fn uint_from_str(s: *const c_char, radix: u32) -> *mut Uint { - let c_str = CStr::from_ptr(s); - Box::into_raw(Box::new(Uint::parse_bytes(c_str.to_bytes(), radix))) - } - - #[no_mangle] - pub unsafe extern "C" fn uint_free(x: *mut Uint) { - if x.is_null() { - return; - } - Box::from_raw(x); - } -} +/* +Copyright (c) 2021 VMware, Inc. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use abomonation::Abomonation; +use num::bigint::BigInt; +use num::bigint::BigUint; +pub use num::bigint::Sign; +use num::bigint::ToBigInt; +use num::ToPrimitive; +use ordered_float::OrderedFloat; +use serde::de::Error; +use serde::de::*; +use serde::ser::*; +use std::fmt; +use std::ops::*; +use std::str::FromStr; + +/* This module is designed to be imported both as a standard DDlog library and as a normal Rust + * module, e.g., from `differential_datalog_test`. We therefore need to import these traits + * so that they are available in the latter case and rename them so that they don't cause duplicate + * import error in the former case. */ +use differential_datalog::record::FromRecord as FromRec; +use differential_datalog::record::IntoRecord as IntoRec; +use differential_datalog::record::Record; + +#[derive(Eq, PartialOrd, PartialEq, Ord, Clone, Hash)] +#[repr(transparent)] +pub struct Int { + x: BigInt, +} + +impl Default for Int { + fn default() -> Int { + Int { + x: BigInt::default(), + } + } +} + +impl Abomonation for Int {} + +impl From for Int { + fn from(other: Uint) -> Self { + other.to_Int().unwrap() + } +} + +// Generated code expects `from_()`, `to_()` functions for all +// supported integer conversions. +impl Int { + pub fn from_bigint(v: BigInt) -> Self { + Self { x: v } + } + pub fn from_u8(v: u8) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_i8(v: i8) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_u16(v: u16) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_i16(v: i16) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_u32(v: u32) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_i32(v: i32) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_u64(v: u64) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_i64(v: i64) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_u128(v: u128) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_i128(v: i128) -> Self { + Self { x: BigInt::from(v) } + } + pub fn from_Uint(v: Uint) -> Self { + Self::from(v) + } + pub fn from_bytes_be(sign: bool, bytes: &[u8]) -> Self { + Self { + x: BigInt::from_bytes_be(if sign { Sign::Plus } else { Sign::Minus }, bytes), + } + } + pub fn to_bytes_be(&self) -> (Sign, Vec) { + self.x.to_bytes_be() + } + pub fn to_i8(&self) -> Option { + self.x.to_i8() + } + pub fn to_u8(&self) -> Option { + self.x.to_u8() + } + /* Extract 8 low-order bits and convert to u8 */ + pub fn truncate_to_u8(&self) -> u8 { + (&self.x & &BigInt::from(0xffu8)).to_u8().unwrap() + } + pub fn to_i16(&self) -> Option { + self.x.to_i16() + } + pub fn to_u16(&self) -> Option { + self.x.to_u16() + } + /* Extract 16 low-order bits and convert to u16 */ + pub fn truncate_to_u16(&self) -> u16 { + (&self.x & &BigInt::from(0xffffu16)).to_u16().unwrap() + } + pub fn to_i32(&self) -> Option { + self.x.to_i32() + } + pub fn to_u32(&self) -> Option { + self.x.to_u32() + } + /* Extract 32 low-order bits and convert to u32 */ + pub fn truncate_to_u32(&self) -> u32 { + (&self.x & &BigInt::from(0xffff_ffffu32)).to_u32().unwrap() + } + pub fn to_i64(&self) -> Option { + self.x.to_i64() + } + pub fn to_u64(&self) -> Option { + self.x.to_u64() + } + /* Extract 64 low-order bits and convert to u64 */ + pub fn truncate_to_u64(&self) -> u64 { + (&self.x & &BigInt::from(0xffff_ffff_ffff_ffffu64)) + .to_u64() + .unwrap() + } + pub fn to_i128(&self) -> Option { + self.x.to_i128() + } + pub fn to_u128(&self) -> Option { + self.x.to_u128() + } + /* Extract 128 low-order bits and convert to u128 */ + pub fn truncate_to_u128(&self) -> u128 { + (&self.x & &BigInt::from(0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffffu128)) + .to_u128() + .unwrap() + } + pub fn to_float(&self) -> OrderedFloat { + match self.x.to_f32() { + None => OrderedFloat::(std::f32::NAN), + Some(x) => OrderedFloat::(x), + } + } + pub fn to_double(&self) -> OrderedFloat { + match self.x.to_f64() { + None => OrderedFloat::(std::f64::NAN), + Some(x) => OrderedFloat::(x), + } + } + pub fn to_Uint(&self) -> Option { + self.x.to_biguint().map(Uint::from_biguint) + } + pub fn parse_bytes(buf: &[u8], radix: u32) -> Int { + Int { + x: BigInt::parse_bytes(buf, radix).unwrap(), + } + } + + #[inline] + pub fn one() -> Self { + Self { + x: BigInt::new(Sign::Plus, vec![1]), + } + } + + #[inline] + pub fn zero() -> Self { + Self { + x: BigInt::new(Sign::Plus, Vec::new()), + } + } +} + +impl fmt::Display for Int { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.x) + } +} + +impl fmt::LowerHex for Int { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:x}", self.x) + } +} + +impl fmt::Debug for Int { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self, f) + } +} + +impl Serialize for Int { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.x.to_str_radix(10)) + } +} + +impl<'de> Deserialize<'de> for Int { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match String::deserialize(deserializer) { + Ok(s) => match BigInt::from_str(&s) { + Ok(i) => Ok(Int { x: i }), + Err(_) => Err(D::Error::custom(format!("invalid integer value: {}", s))), + }, + Err(e) => Err(e), + } + } +} + +impl differential_datalog::record::FromRecord for Int { + fn from_record(val: &Record) -> Result { + Ok(Int::from_bigint(BigInt::from_record(val)?)) + } +} + +impl differential_datalog::record::IntoRecord for Int { + fn into_record(self) -> Record { + self.x.into_record() + } +} + +impl differential_datalog::record::Mutator for Record { + fn mutate(&self, i: &mut Int) -> Result<(), String> { + self.mutate(&mut i.x) + } +} + +#[test] +fn test_int_fromrecord() { + let v = (-25_i64).to_bigint().unwrap(); + assert_eq!( + Int::from_record(&Record::Int(v.clone())), + Ok(Int::from_bigint(v)) + ); +} + +impl Shr for Int { + type Output = Int; + + #[inline] + fn shr(self, rhs: u32) -> Int { + Int { + x: self.x.shr(rhs as usize), + } + } +} + +impl Shl for Int { + type Output = Int; + + #[inline] + fn shl(self, rhs: u32) -> Int { + Int { + x: self.x.shl(rhs as usize), + } + } +} + +impl Neg for Int { + type Output = Int; + + #[inline] + fn neg(self) -> Self::Output { + Int { x: self.x.neg() } + } +} + +macro_rules! forward_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl $imp<$res> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: $res) -> $res { + // forward to val-ref + Int { + x: $imp::$method(self.x, other.x), + } + } + } + }; +} + +forward_binop!(impl Add for Int, add); +forward_binop!(impl Sub for Int, sub); +forward_binop!(impl Div for Int, div); +forward_binop!(impl Rem for Int, rem); +forward_binop!(impl Mul for Int, mul); +forward_binop!(impl BitAnd for Int, bitand); +forward_binop!(impl BitOr for Int, bitor); + +impl num::One for Int { + fn one() -> Int { + Int { x: BigInt::one() } + } +} + +impl num::Zero for Int { + fn zero() -> Int { + Int { x: BigInt::zero() } + } + + fn is_zero(&self) -> bool { + self.x == BigInt::zero() + } +} + +#[derive(Eq, PartialOrd, PartialEq, Ord, Clone, Hash)] +#[repr(transparent)] +pub struct Uint { + x: BigUint, +} + +impl Default for Uint { + fn default() -> Uint { + Uint { + x: BigUint::default(), + } + } +} + +impl Abomonation for Uint {} + +// Generated code expects `from_()`, `to_()` functions for all +// supported integer conversions. +impl Uint { + pub fn from_biguint(v: BigUint) -> Uint { + Uint { x: v } + } + pub fn from_bigint(v: BigInt) -> Uint { + Uint { + x: v.to_biguint().unwrap(), + } + } + pub fn from_u8(v: u8) -> Uint { + Uint { + x: BigUint::from(v), + } + } + pub fn from_u16(v: u16) -> Uint { + Uint { + x: BigUint::from(v), + } + } + pub fn from_u32(v: u32) -> Uint { + Uint { + x: BigUint::from(v), + } + } + pub fn from_u64(v: u64) -> Uint { + Uint { + x: BigUint::from(v), + } + } + pub fn from_u128(v: u128) -> Uint { + Uint { + x: BigUint::from(v), + } + } + pub fn from_bytes_be(bytes: &[u8]) -> Uint { + Uint { + x: BigUint::from_bytes_be(bytes), + } + } + pub fn to_bytes_be(&self) -> Vec { + self.x.to_bytes_be() + } + pub fn to_u8(&self) -> Option { + self.x.to_u8() + } + pub fn to_u16(&self) -> Option { + self.x.to_u16() + } + pub fn to_u32(&self) -> Option { + self.x.to_u32() + } + pub fn to_u64(&self) -> Option { + self.x.to_u64() + } + pub fn to_u128(&self) -> Option { + self.x.to_u128() + } + pub fn to_Int(&self) -> Option { + self.x.to_bigint().map(Int::from_bigint) + } + pub fn to_float(&self) -> OrderedFloat { + match self.x.to_f32() { + None => OrderedFloat::(std::f32::NAN), + Some(x) => OrderedFloat::(x), + } + } + pub fn to_double(&self) -> OrderedFloat { + match self.x.to_f64() { + None => OrderedFloat::(std::f64::NAN), + Some(x) => OrderedFloat::(x), + } + } + pub fn parse_bytes(buf: &[u8], radix: u32) -> Uint { + Uint { + x: BigUint::parse_bytes(buf, radix).unwrap(), + } + } + + #[inline] + pub fn one() -> Self { + Self { + x: BigUint::new(vec![1]), + } + } + + #[inline] + pub fn zero() -> Self { + Self { + x: BigUint::new(Vec::new()), + } + } +} + +impl fmt::Display for Uint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.x) + } +} + +impl fmt::LowerHex for Uint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:x}", self.x) + } +} + +impl fmt::Debug for Uint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self, f) + } +} + +impl Serialize for Uint { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.x.to_str_radix(10)) + } +} + +impl<'de> Deserialize<'de> for Uint { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match String::deserialize(deserializer) { + Ok(s) => match BigUint::from_str(&s) { + Ok(i) => Ok(Uint { x: i }), + Err(_) => Err(D::Error::custom(format!("invalid integer value: {}", s))), + }, + Err(e) => Err(e), + } + } +} + +impl differential_datalog::record::FromRecord for Uint { + fn from_record(val: &Record) -> Result { + Ok(Uint::from_biguint(BigUint::from_record(val)?)) + } +} + +impl differential_datalog::record::IntoRecord for Uint { + fn into_record(self) -> Record { + self.x.into_record() + } +} + +impl differential_datalog::record::Mutator for Record { + fn mutate(&self, i: &mut Uint) -> Result<(), String> { + self.mutate(&mut i.x) + } +} + +#[test] +fn test_uint_fromrecord() { + let v = (25_u64).to_bigint().unwrap(); + assert_eq!( + Uint::from_record(&Record::Int(v.clone())), + Ok(Uint::from_bigint(v)) + ); +} + +/* +impl Uint { + #[inline] + pub fn parse_bytes(buf: &[u8], radix: u32) -> Uint { + Uint{x: BigUint::parse_bytes(buf, radix).unwrap()} + } +} +*/ + +/* DDlog supports 32-bit shifts */ +impl Shr for Uint { + type Output = Uint; + + #[inline] + fn shr(self, rhs: u32) -> Uint { + Uint { + x: self.x.shr(rhs as usize), + } + } +} + +impl Shl for Uint { + type Output = Uint; + + #[inline] + fn shl(self, rhs: u32) -> Uint { + Uint { + x: self.x.shl(rhs as usize), + } + } +} + +macro_rules! forward_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl $imp<$res> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: $res) -> $res { + // forward to val-ref + Uint { + x: $imp::$method(self.x, other.x), + } + } + } + }; +} + +forward_binop!(impl Add for Uint, add); +forward_binop!(impl Sub for Uint, sub); +forward_binop!(impl Div for Uint, div); +forward_binop!(impl Rem for Uint, rem); +forward_binop!(impl Mul for Uint, mul); +forward_binop!(impl BitAnd for Uint, bitand); +forward_binop!(impl BitOr for Uint, bitor); + +impl num::One for Uint { + fn one() -> Uint { + Uint { x: BigUint::one() } + } +} + +impl num::Zero for Uint { + fn zero() -> Uint { + Uint { x: BigUint::zero() } + } + + fn is_zero(&self) -> bool { + self.x == BigUint::zero() + } +} + +#[cfg(feature = "c_api")] +mod c_api { + use super::{Int, Uint}; + use std::{ffi::CStr, os::raw::c_char}; + + #[no_mangle] + pub extern "C" fn int_from_i64(v: i64) -> *mut Int { + Box::into_raw(Box::new(Int::from_i64(v))) + } + + #[no_mangle] + pub extern "C" fn int_from_u64(v: u64) -> *mut Int { + Box::into_raw(Box::new(Int::from_u64(v))) + } + + #[no_mangle] + pub unsafe extern "C" fn int_from_str(s: *const c_char, radix: u32) -> *mut Int { + let c_str = CStr::from_ptr(s); + Box::into_raw(Box::new(Int::parse_bytes(c_str.to_bytes(), radix))) + } + + #[no_mangle] + pub unsafe extern "C" fn int_free(x: *mut Int) { + if x.is_null() { + return; + } + + Box::from_raw(x); + } + + #[no_mangle] + pub extern "C" fn uint_from_u64(v: u64) -> *mut Uint { + Box::into_raw(Box::new(Uint::from_u64(v))) + } + + #[no_mangle] + pub unsafe extern "C" fn uint_from_str(s: *const c_char, radix: u32) -> *mut Uint { + let c_str = CStr::from_ptr(s); + Box::into_raw(Box::new(Uint::parse_bytes(c_str.to_bytes(), radix))) + } + + #[no_mangle] + pub unsafe extern "C" fn uint_free(x: *mut Uint) { + if x.is_null() { + return; + } + + Box::from_raw(x); + } +} diff --git a/lib/ddlog_bigint.toml b/lib/ddlog_bigint.toml new file mode 100644 index 000000000..6b241285f --- /dev/null +++ b/lib/ddlog_bigint.toml @@ -0,0 +1,2 @@ +[dependencies.num] +version = "0.4.0" diff --git a/lib/ddlog_log.rs b/lib/ddlog_log.rs index 1aecbcd71..7c26dbdf3 100644 --- a/lib/ddlog_log.rs +++ b/lib/ddlog_log.rs @@ -1,137 +1,134 @@ -/* -Copyright (c) 2021 VMware, Inc. -SPDX-License-Identifier: MIT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/* Logging Configuration API, (detailed documentation in `ddlog_log.h`) */ - -use once_cell::sync::Lazy; -use std::collections; -use std::ffi; -use std::os::raw; -use std::sync; - -type log_callback_t = Box; - -struct LogConfig { - default_callback: Option, - default_level: i32, - mod_callbacks: collections::HashMap, -} - -impl LogConfig { - fn new() -> LogConfig { - LogConfig { - default_callback: None, - default_level: std::i32::MAX, - mod_callbacks: collections::HashMap::new(), - } - } -} - -/// Logger configuration for each module consists of the maximal enabled -/// log level (messages above this level are ignored) and callback. -static LOG_CONFIG: Lazy> = - Lazy::new(|| sync::RwLock::new(LogConfig::new())); - -/// Logging API exposed to the DDlog program. -/// (see detailed documentation in `log.dl`) -#[allow(clippy::ptr_arg, clippy::trivially_copy_pass_by_ref)] -pub fn log(module: &i32, level: &i32, msg: &String) { - let cfg = LOG_CONFIG.read().unwrap(); - if let Some((cb, current_level)) = cfg.mod_callbacks.get(module) { - if *level <= *current_level { - cb(*level, msg.as_str()); - } - } else if *level <= cfg.default_level && cfg.default_callback.is_some() { - cfg.default_callback.as_ref().unwrap()(*level, msg.as_str()); - } -} - -/// `cb = None` - disables logging for the given module. -// NOTE: we set callback and log level simultaneously. A more flexible API -// would allow changing log level without changing the callback. -pub fn log_set_callback(module: i32, cb: Option, max_level: i32) { - let mut cfg = LOG_CONFIG.write().unwrap(); - match cb { - Some(cb) => { - cfg.mod_callbacks.insert(module, (cb, max_level)); - } - None => { - cfg.mod_callbacks.remove(&module); - } - } -} - -/// Set default callback and log level for modules that were not configured -/// via `log_set_callback`. -pub fn log_set_default_callback(cb: Option, max_level: i32) { - let mut cfg = LOG_CONFIG.write().unwrap(); - cfg.default_callback = cb; - cfg.default_level = max_level; -} - -/// C bindings for the config API -#[no_mangle] -#[cfg(feature = "c_api")] -pub unsafe extern "C" fn ddlog_log_set_callback( - module: raw::c_int, - cb: Option, - cb_arg: libc::uintptr_t, - max_level: raw::c_int, -) { - match cb { - Some(cb) => log_set_callback( - module as i32, - Some(Box::new(move |level, msg| { - cb( - cb_arg, - level as raw::c_int, - ffi::CString::new(msg).unwrap_or_default().as_ptr(), - ) - })), - max_level as i32, - ), - None => log_set_callback(module as i32, None, max_level as i32), - } -} - -#[no_mangle] -#[cfg(feature = "c_api")] -pub unsafe extern "C" fn ddlog_log_set_default_callback( - cb: Option, - cb_arg: libc::uintptr_t, - max_level: raw::c_int, -) { - match cb { - Some(cb) => log_set_default_callback( - Some(Box::new(move |level, msg| { - cb( - cb_arg, - level as raw::c_int, - ffi::CString::new(msg).unwrap_or_default().as_ptr(), - ) - })), - max_level as i32, - ), - None => log_set_default_callback(None, max_level as i32), - } -} +/* +Copyright (c) 2021 VMware, Inc. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* Logging Configuration API, (detailed documentation in `ddlog_log.h`) */ + +use once_cell::sync::Lazy; +use std::{collections, ffi, os::raw, sync}; + +type log_callback_t = Box; + +struct LogConfig { + default_callback: Option, + default_level: i32, + mod_callbacks: collections::HashMap, +} + +impl LogConfig { + fn new() -> LogConfig { + LogConfig { + default_callback: None, + default_level: std::i32::MAX, + mod_callbacks: collections::HashMap::new(), + } + } +} + +/// Logger configuration for each module consists of the maximal enabled +/// log level (messages above this level are ignored) and callback. +static LOG_CONFIG: Lazy> = + Lazy::new(|| sync::RwLock::new(LogConfig::new())); + +/// Logging API exposed to the DDlog program. +/// (see detailed documentation in `log.dl`) +#[allow(clippy::ptr_arg, clippy::trivially_copy_pass_by_ref)] +pub fn log(module: &i32, &level: &i32, msg: &String) { + let cfg = LOG_CONFIG.read().unwrap(); + if let Some(&(ref cb, current_level)) = cfg.mod_callbacks.get(module) { + if level <= current_level { + cb(level, msg.as_str()); + } + } else if level <= cfg.default_level && cfg.default_callback.is_some() { + cfg.default_callback.as_ref().unwrap()(level, msg.as_str()); + } +} + +/// `cb = None` - disables logging for the given module. +// NOTE: we set callback and log level simultaneously. A more flexible API +// would allow changing log level without changing the callback. +pub fn log_set_callback(module: i32, cb: Option, max_level: i32) { + let mut cfg = LOG_CONFIG.write().unwrap(); + match cb { + Some(cb) => { + cfg.mod_callbacks.insert(module, (cb, max_level)); + } + None => { + cfg.mod_callbacks.remove(&module); + } + } +} + +/// Set default callback and log level for modules that were not configured +/// via `log_set_callback`. +pub fn log_set_default_callback(cb: Option, max_level: i32) { + let mut cfg = LOG_CONFIG.write().unwrap(); + cfg.default_callback = cb; + cfg.default_level = max_level; +} + +/// C bindings for the config API +#[no_mangle] +#[cfg(feature = "c_api")] +pub unsafe extern "C" fn ddlog_log_set_callback( + module: raw::c_int, + cb: Option, + cb_arg: libc::uintptr_t, + max_level: raw::c_int, +) { + let _ = std::panic::catch_unwind(|| match cb { + Some(cb) => log_set_callback( + module as i32, + Some(Box::new(move |level, msg| { + cb( + cb_arg, + level as raw::c_int, + ffi::CString::new(msg).unwrap_or_default().as_ptr(), + ) + })), + max_level as i32, + ), + None => log_set_callback(module as i32, None, max_level as i32), + }); +} + +#[no_mangle] +#[cfg(feature = "c_api")] +pub unsafe extern "C" fn ddlog_log_set_default_callback( + cb: Option, + cb_arg: libc::uintptr_t, + max_level: raw::c_int, +) { + let _ = std::panic::catch_unwind(|| match cb { + Some(cb) => log_set_default_callback( + Some(Box::new(move |level, msg| { + cb( + cb_arg, + level as raw::c_int, + ffi::CString::new(msg).unwrap_or_default().as_ptr(), + ) + })), + max_level as i32, + ), + None => log_set_default_callback(None, max_level as i32), + }); +} diff --git a/lib/ddlog_log.toml b/lib/ddlog_log.toml new file mode 100644 index 000000000..5976a36fd --- /dev/null +++ b/lib/ddlog_log.toml @@ -0,0 +1,2 @@ +[dependencies.libc] +version = "0.2.107" diff --git a/lib/ddlog_std.rs b/lib/ddlog_std.rs index 9d06df8f6..e8e1e6445 100644 --- a/lib/ddlog_std.rs +++ b/lib/ddlog_std.rs @@ -47,11 +47,12 @@ use std::{ sync::Arc as StdArc, vec::{self, Vec as StdVec}, }; -use twox_hash::{XxHash32, XxHash64}; +use xxhash_rust::{xxh3::Xxh3, xxh32::Xxh32}; const XX_SEED1: u64 = 0x23b691a751d0e108; const XX_SEED2: u64 = 0x20b09801dce5ff84; +#[inline] pub fn default() -> T { T::default() } @@ -63,7 +64,7 @@ pub fn to_any(x: T) -> Any { } pub fn from_any(value: Any) -> Option { - >::from(T::try_from_ddvalue(DDValue::from(value))) + T::try_from_ddvalue(DDValue::from(value)).ok().into() } // Result @@ -96,11 +97,13 @@ pub fn result_unwrap_or(res: Result, def: T) -> T { /// An atomically reference counted reference #[derive(Eq, PartialOrd, PartialEq, Ord, Clone, Hash)] +#[repr(transparent)] pub struct Ref { x: Arc, } impl Default for Ref { + #[inline] fn default() -> Self { Self { x: Arc::new(T::default()), @@ -111,12 +114,14 @@ impl Default for Ref { impl Deref for Ref { type Target = T; + #[inline] fn deref(&self) -> &T { &*self.x } } impl From for Ref { + #[inline] fn from(x: T) -> Self { Self { x: Arc::new(x) } } @@ -137,6 +142,7 @@ impl Abomonation for Ref { } impl Ref { + #[inline] pub fn get_mut(this: &mut Self) -> StdOption<&mut T> { Arc::get_mut(&mut this.x) } @@ -198,45 +204,69 @@ where } } +#[inline] pub fn ref_new(x: A) -> Ref { Ref::from(x) } +#[inline] pub fn deref(x: &Ref) -> &A { x.deref() } // Arithmetic functions + +#[inline] pub fn u8_pow32(base: &u8, exp: &u32) -> u8 { base.wrapping_pow(*exp) } + +#[inline] pub fn u16_pow32(base: &u16, exp: &u32) -> u16 { base.wrapping_pow(*exp) } + +#[inline] pub fn u32_pow32(base: &u32, exp: &u32) -> u32 { base.wrapping_pow(*exp) } + +#[inline] pub fn u64_pow32(base: &u64, exp: &u32) -> u64 { base.wrapping_pow(*exp) } + +#[inline] pub fn u128_pow32(base: &u128, exp: &u32) -> u128 { base.wrapping_pow(*exp) } + +#[inline] pub fn s8_pow32(base: &i8, exp: &u32) -> i8 { base.wrapping_pow(*exp) } + +#[inline] pub fn s16_pow32(base: &i16, exp: &u32) -> i16 { base.wrapping_pow(*exp) } + +#[inline] pub fn s32_pow32(base: &i32, exp: &u32) -> i32 { base.wrapping_pow(*exp) } + +#[inline] pub fn s64_pow32(base: &i64, exp: &u32) -> i64 { base.wrapping_pow(*exp) } + +#[inline] pub fn s128_pow32(base: &i128, exp: &u32) -> i128 { base.wrapping_pow(*exp) } + +#[inline] pub fn bigint_pow32(base: &ddlog_bigint::Int, exp: &u32) -> ddlog_bigint::Int { num::pow::pow(base.clone(), *exp as usize) } @@ -244,30 +274,34 @@ pub fn bigint_pow32(base: &ddlog_bigint::Int, exp: &u32) -> ddlog_bigint::Int { // Option impl Copy for Option {} -pub fn option2std(x: StdOption) -> Option { - match x { - StdOption::None => Option::None, - StdOption::Some(v) => Option::Some { x: v }, - } +#[deprecated = "Use `From> for ddlog_std::Option`"] +pub fn option2std(option: StdOption) -> Option { + option.into() } -pub fn std2option(x: Option) -> StdOption { - match x { - Option::None => StdOption::None, - Option::Some { x } => StdOption::Some(x), - } +#[deprecated = "Use `ddlog_std::Option for From>`"] +pub fn std2option(option: Option) -> StdOption { + option.into() } impl From> for Option { - fn from(x: StdOption) -> Self { - option2std(x) + #[inline] + fn from(option: StdOption) -> Self { + match option { + StdOption::None => Option::None, + StdOption::Some(v) => Option::Some { x: v }, + } } } // this requires Rust 1.41+ impl From> for StdOption { - fn from(x: Option) -> Self { - std2option(x) + #[inline] + fn from(option: Option) -> Self { + match option { + Option::None => StdOption::None, + Option::Some { x } => StdOption::Some(x), + } } } @@ -324,6 +358,7 @@ where } } +#[inline] pub fn option_unwrap_or_default(opt: Option) -> T { match opt { Option::Some { x } => x, @@ -331,6 +366,7 @@ pub fn option_unwrap_or_default(opt: Option) -> T { } } +#[inline] pub fn option_unwrap_or(opt: Option, def: T) -> T { match opt { Option::Some { x } => x, @@ -372,11 +408,13 @@ pub struct Vec { impl Vec { /// Creates a new, empty vector + #[inline] pub const fn new() -> Self { Vec { vec: StdVec::new() } } /// Creates a new, empty `Vec` with the specified capacity + #[inline] pub fn with_capacity(capacity: usize) -> Self { Vec { vec: StdVec::with_capacity(capacity), @@ -384,18 +422,21 @@ impl Vec { } /// Returns an iterator over the vector + #[inline] pub fn iter<'a>(&'a self) -> VecIter<'a, T> { VecIter::new(self) } } impl Vec { + #[inline] pub fn resize(&mut self, new_len: usize, value: &T) { self.vec.resize_with(new_len, || value.clone()); } } impl From<&[T]> for Vec { + #[inline] fn from(slice: &[T]) -> Self { Vec { vec: slice.to_vec(), @@ -404,6 +445,7 @@ impl From<&[T]> for Vec { } impl FromIterator for Vec { + #[inline] fn from_iter(iter: I) -> Self where I: IntoIterator, @@ -415,6 +457,7 @@ impl FromIterator for Vec { } impl From> for Vec { + #[inline] fn from(vec: StdVec) -> Self { Vec { vec } } @@ -423,36 +466,42 @@ impl From> for Vec { impl Deref for Vec { type Target = StdVec; + #[inline] fn deref(&self) -> &Self::Target { &self.vec } } impl DerefMut for Vec { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.vec } } impl AsRef> for Vec { + #[inline] fn as_ref(&self) -> &StdVec { &self.vec } } impl AsMut> for Vec { + #[inline] fn as_mut(&mut self) -> &mut StdVec { &mut self.vec } } impl AsRef<[T]> for Vec { + #[inline] fn as_ref(&self) -> &[T] { self.as_slice() } } impl AsMut<[T]> for Vec { + #[inline] fn as_mut(&mut self) -> &mut [T] { self.as_mut_slice() } @@ -515,6 +564,7 @@ pub struct VecIter<'a, X> { } impl<'a, X> VecIter<'a, X> { + #[inline] pub fn new(vec: &'a Vec) -> VecIter<'a, X> { VecIter { iter: vec.vec.iter(), @@ -525,21 +575,25 @@ impl<'a, X> VecIter<'a, X> { impl<'a, X> Iterator for VecIter<'a, X> { type Item = &'a X; + #[inline] fn next(&mut self) -> StdOption { self.iter.next() } + #[inline] fn size_hint(&self) -> (usize, StdOption) { self.iter.size_hint() } } /// Get the length of a vec +#[inline] pub fn vec_len(vec: &Vec) -> std_usize { vec.len() as std_usize } /// Create a new, empty vec +#[inline] pub const fn vec_empty() -> Vec { Vec::new() } @@ -579,8 +633,8 @@ pub fn vec_push_imm(immutable_vec: &Vec, x: T) -> Vec { mutable_vec } -pub fn vec_pop(v: &mut Vec) -> Option { - option2std(v.pop()) +pub fn vec_pop(vec: &mut Vec) -> Option { + vec.pop().into() } pub fn vec_contains(vec: &Vec, x: &T) -> bool { @@ -838,7 +892,7 @@ pub fn set_is_empty(s: &Set) -> bool { } pub fn set_nth(s: &Set, n: &std_usize) -> Option { - option2std(s.x.iter().nth(*n as usize).cloned()) + s.x.iter().nth(*n as usize).cloned().into() } pub fn set_to_vec(set: Set) -> Vec { @@ -1035,7 +1089,7 @@ pub fn map_insert(m: &mut Map, k: K, v: V) { } pub fn map_remove(m: &mut Map, k: &K) -> Option { - option2std(m.x.remove(k)) + m.x.remove(k).into() } pub fn map_insert_imm(mut m: Map, k: K, v: V) -> Map { @@ -1044,7 +1098,7 @@ pub fn map_insert_imm(mut m: Map, k: K, v: V) -> Map { } pub fn map_get(m: &Map, k: &K) -> Option { - option2std(m.x.get(k).cloned()) + m.x.get(k).cloned().into() } pub fn map_contains_key(s: &Map, k: &K) -> bool { @@ -1073,11 +1127,11 @@ pub fn map_values(map: &Map) -> Vec { } pub fn map_nth_value(m: &Map, n: &std_usize) -> Option { - option2std(m.x.iter().nth(*n as usize).map(|(_, v)| v.clone())) + m.x.iter().nth(*n as usize).map(|(_, v)| v.clone()).into() } pub fn map_nth_key(m: &Map, n: &std_usize) -> Option { - option2std(m.x.iter().nth(*n as usize).map(|(k, _)| k.clone())) + m.x.iter().nth(*n as usize).map(|(k, _)| k.clone()).into() } // strings @@ -1095,11 +1149,11 @@ pub fn hex(x: &T) -> String { } pub fn parse_dec_u64(s: &String) -> Option { - option2std(s.parse::().ok()) + s.parse::().ok().into() } pub fn parse_dec_i64(s: &String) -> Option { - option2std(s.parse::().ok()) + s.parse::().ok().into() } pub fn string_join(strings: &Vec, sep: &String) -> String { @@ -1178,23 +1232,39 @@ pub fn string_reverse(s: &String) -> String { // Hashing pub fn hash32(x: &T) -> u32 { - let mut hasher = XxHash32::with_seed(XX_SEED1 as u32); + // This is a workaround because `Xxh32` doesn't implement `Hasher` directly + #[repr(transparent)] + struct Xxh32Hasher(Xxh32); + + impl core::hash::Hasher for Xxh32Hasher { + #[inline] + fn finish(&self) -> u64 { + self.0.digest() as u64 + } + + #[inline] + fn write(&mut self, input: &[u8]) { + self.0.update(input); + } + } + + let mut hasher = Xxh32Hasher(Xxh32::new(XX_SEED1 as u32)); x.hash(&mut hasher); hasher.finish() as u32 } pub fn hash64(x: &T) -> u64 { - let mut hasher = XxHash64::with_seed(XX_SEED1); + let mut hasher = Xxh3::with_seed(XX_SEED1); x.hash(&mut hasher); hasher.finish() } pub fn hash128(x: &T) -> u128 { - let mut hasher = XxHash64::with_seed(XX_SEED1); + let mut hasher = Xxh3::with_seed(XX_SEED1); x.hash(&mut hasher); let w1 = hasher.finish(); - let mut hasher = XxHash64::with_seed(XX_SEED2); + let mut hasher = Xxh3::with_seed(XX_SEED2); x.hash(&mut hasher); let w2 = hasher.finish(); diff --git a/lib/ddlog_std.toml b/lib/ddlog_std.toml new file mode 100644 index 000000000..3e9630fe2 --- /dev/null +++ b/lib/ddlog_std.toml @@ -0,0 +1,7 @@ +[dependencies.xxhash-rust] +version = "0.8.2" +features = ["xxh3", "xxh32"] +default-features = false + +[dependencies.num] +version = "0.4.0" diff --git a/lib/fp.toml b/lib/fp.toml new file mode 100644 index 000000000..6b241285f --- /dev/null +++ b/lib/fp.toml @@ -0,0 +1,2 @@ +[dependencies.num] +version = "0.4.0" diff --git a/lib/hashset.rs b/lib/hashset.rs index d2d866836..64fbddf6f 100644 --- a/lib/hashset.rs +++ b/lib/hashset.rs @@ -1,413 +1,413 @@ -/* -Copyright (c) 2021 VMware, Inc. -SPDX-License-Identifier: MIT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -use ddlog_rt::Closure; -use ddlog_std::{option2std, tuple2, Group, Option as DDOption, Vec as DDVec}; - -use differential_datalog::record::{CollectionKind, Record}; -use im::hashset::{ConsumingIter, HashSet as IMHashSet, OrderedConsumingIter, OrderedIter}; -use serde::{ - de::Deserializer, - ser::{SerializeSeq, Serializer}, -}; -use std::{ - borrow::Borrow, - cmp::Ordering, - collections::hash_map::DefaultHasher, - fmt::{Debug, Formatter, Result as FmtResult}, - hash::{BuildHasherDefault, Hash, Hasher}, - iter::FromIterator, - ops::DerefMut, - result::Result as StdResult, -}; - -#[derive(Clone)] -pub struct HashSet { - // We must use a deterministic hasher here instead of the default `RandomState` - // to ensure that hashset iterators yield elements in a consistent order. - set: IMHashSet>, -} - -impl Default for HashSet { - fn default() -> Self { - HashSet { - set: IMHashSet::with_hasher(>::default()), - } - } -} - -impl Hash for HashSet { - fn hash(&self, state: &mut H) { - // Our modified `im::HashSet` implementation ensures that `hash` is - // deterministic, i.e., given the same hasher and two sets with the - // same elements it returns the same hash value. In contrast, the - // original `im` crate currently violates this property. - self.set.hash(state); - } -} - -impl PartialEq for HashSet { - fn eq(&self, other: &Self) -> bool { - // The `Eq` implementation in `im::HashSet` is unable to rely on the - // ordered iterator (which requires the `Ord` trait) and is therefore - // inefficient compared to this. - - // Check if `other` is a clone of `self`. - if self.set.ptr_eq(&other.set) { - return true; - } - if self.len() != other.len() { - return false; - } - self.iter().eq(other.iter()) - } -} - -impl Eq for HashSet {} - -impl PartialOrd for HashSet { - fn partial_cmp(&self, other: &Self) -> Option { - if self.set.ptr_eq(&other.set) { - return Some(Ordering::Equal); - } - // We don't need to implement lexicographical ordering. If the two - // sets have different sizes, just compare the sizes. - match self.len().cmp(&other.len()) { - Ordering::Equal => (), - ord => return Some(ord), - } - // Our modified `im::HashSet` implementation ensures that `partial_cmp` - // is consistent: given the same hasher and two sets with the - // same elements, it returns `Ordering::Equal`. In contrast, the - // original `im` crate currently violates this property. - self.set.partial_cmp(&other.set) - } -} - -impl Ord for HashSet { - fn cmp(&self, other: &Self) -> Ordering { - if self.set.ptr_eq(&other.set) { - return Ordering::Equal; - } - match self.len().cmp(&other.len()) { - Ordering::Equal => (), - ord => return ord, - } - - // Our modified `IMHashSet` implementation ensures that `cmp` - // is consistent: given the same hasher and two sets with the - // same elements it returns `Ordering::Equal`. In contrast, the - // original `im` crate currently violates this property. - self.set.cmp(&other.set) - } -} - -impl Serialize for HashSet { - fn serialize(&self, serializer: S) -> StdResult - where - S: Serializer, - { - // Don't use IMHashSet's implementation of `serialize` to - // ensure deterministic order. - let mut s = serializer.serialize_seq(Some(self.len()))?; - for i in self.iter() { - s.serialize_element(i)?; - } - s.end() - } -} - -impl<'de, T: Deserialize<'de> + Hash + Eq + Clone> Deserialize<'de> for HashSet { - fn deserialize(deserializer: D) -> StdResult - where - D: Deserializer<'de>, - { - IMHashSet::deserialize(deserializer).map(HashSet::from) - } -} - -impl HashSet { - pub fn len(&self) -> usize { - self.set.len() - } -} - -impl<'a, T: Hash + Ord> HashSet { - // Always use this iterator over `im::HashSet::iter` in scenarios where - // order matters (serialization, formatting, etc.). - pub fn iter(&'a self) -> OrderedIter<'a, T> { - // The default IMHashSet iterator does not guarantee consistent - // ordering: two sets with the same elements can iterate over them - // in different order even when using the same hasher. This can - // lead to non-deterministic behavior in DDlog code that iterates - // over hashsets. We must therefore use a deterministic iterator. - self.set.ordered_iter() - } -} - -impl IntoIterator for HashSet { - type Item = T; - type IntoIter = OrderedConsumingIter; - - fn into_iter(self) -> Self::IntoIter { - // Use ordered iterator that guarantees consistent ordering. - self.set.into_ordered_iter() - } -} - -impl, RT> FromIterator for HashSet { - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - HashSet { - set: IMHashSet::from_iter(iter), - } - } -} - -impl HashSet { - pub fn new() -> Self { - Self::default() - } - pub fn is_empty(&self) -> bool { - self.set.is_empty() - } -} - -impl From>> for HashSet { - fn from(set: IMHashSet>) -> Self { - HashSet { set } - } -} - -impl HashSet { - pub fn contains(&self, x: &BT) -> bool - where - BT: Hash + Eq, - T: Borrow, - { - self.set.contains(x) - } -} - -impl HashSet { - pub fn unit(x: T) -> Self { - let mut set = IMHashSet::default(); - set.insert(x); - HashSet::from(set) - } - - pub fn insert(&mut self, x: T) -> Option { - self.set.insert(x) - } - - pub fn remove(&mut self, x: &BT) -> Option - where - BT: Hash + Eq, - T: Borrow, - { - self.set.remove(x) - } - - pub fn without(&self, x: &T) -> Self { - HashSet::from(self.set.without(x)) - } - - pub fn update(&self, x: T) -> Self { - HashSet::from(self.set.update(x)) - } - - pub fn union(self, other: Self) -> Self { - // `im::HashSet` implements union by adding elements from `other` to - // `self`. This is inefficient when `other` is much - // larger than `self`. The following optimization will become - // unnecessary once this PR or something similar has landed: - // https://github.com/bodil/im-rs/pull/163. - if self.len() >= other.len() { - HashSet::from(self.set.union(other.set)) - } else { - HashSet::from(other.set.union(self.set)) - } - } - pub fn unions(i: I) -> Self - where - I: IntoIterator, - { - let mut sets: Vec<_> = i.into_iter().collect(); - - // Start with the largest set. - if let Some((largest_idx, _)) = sets.iter().enumerate().max_by_key(|(_, s)| s.len()) { - let mut largest_set = Self::default(); - std::mem::swap(&mut largest_set, &mut sets[largest_idx]); - - sets.into_iter().fold(largest_set, Self::union) - } else { - Self::default() - } - } - pub fn intersection(self, other: Self) -> Self { - HashSet::from(self.set.intersection(other.set)) - } - pub fn difference(self, other: Self) -> Self { - HashSet::from(self.set.difference(other.set)) - } -} - -// I don't want to implement `Deref` for `HashSet` to prevent -// accidentally calling `im::HashSet` methods. For example, -// `im::HashSet::iter` returns the non-deterministic iterator (which can -// mess up DDlog in ways one can easily miss unless they test on big -// enough sets). - -// impl Deref for HashSet { -// type Target = IMHashSet; -// -// fn deref(//self) -> //Self::Target { -// //self.set -// } -// } -// -// impl DerefMut for HashSet { -// fn deref_mut(//mut self) -> //mut Self::Target { -// //mut self.set -// } -// } - -impl FromRecordInner for HashSet { - fn from_record_inner(val: &Record) -> StdResult { - match val { - Record::Array(_, args) => StdResult::from_iter(args.iter().map(T::from_record)), - v => T::from_record(v).map(Self::unit), - } - } -} - -impl IntoRecord for HashSet { - fn into_record(self) -> Record { - Record::Array( - CollectionKind::Set, - self.into_iter().map(|x| x.into_record()).collect(), - ) - } -} - -// Set update semantics: update contains values that are in one of the sets -// but not the other. -impl - MutatorInner> for Record -{ - fn mutate_inner(&self, set: &mut HashSet) -> StdResult<(), String> { - let upd = >::from_record(self)?; - *set = HashSet::from(set.set.clone().symmetric_difference(upd.set)); - Ok(()) - } -} - -impl Debug for HashSet { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - f.debug_set().entries(self.iter()).finish() - } -} - -pub fn hashset_size(s: &HashSet) -> std_usize { - s.len() as std_usize -} - -pub fn hashset_empty() -> HashSet { - HashSet::new() -} - -pub fn hashset_singleton(v: &T) -> HashSet { - HashSet::unit((*v).clone()) -} - -pub fn hashset_insert(s: &mut HashSet, v: &T) { - s.insert(v.clone()); -} - -pub fn hashset_insert_imm(s: &HashSet, v: &T) -> HashSet { - s.update((*v).clone()) -} - -pub fn hashset_remove(s: &mut HashSet, v: &T) { - s.remove(v); -} - -pub fn hashset_remove_imm(s: &HashSet, v: &T) -> HashSet { - s.without(v) -} - -pub fn hashset_contains(s: &HashSet, v: &T) -> bool { - s.contains(v) -} - -pub fn hashset_is_empty(s: &HashSet) -> bool { - s.is_empty() -} - -pub fn hashset_nth(s: &HashSet, n: &std_usize) -> DDOption { - option2std(s.iter().nth(*n as usize).cloned()) -} - -pub fn hashset_to_vec(s: &HashSet) -> DDVec { - DDVec { - vec: s.iter().cloned().collect(), - } -} - -pub fn hashset_union(s1: &HashSet, s2: &HashSet) -> HashSet { - s1.clone().union(s2.clone()) -} - -pub fn hashset_unions(sets: &Vec>) -> HashSet { - HashSet::unions(sets.iter().cloned()) -} - -pub fn group_hashset_unions( - sets: &Group>, -) -> HashSet { - HashSet::unions(sets.iter().map(|tuple2(x, _)| x)) -} - -pub fn hashset_intersection(s1: &HashSet, s2: &HashSet) -> HashSet { - s1.clone().intersection(s2.clone()) -} - -pub fn hashset_difference(s1: &HashSet, s2: &HashSet) -> HashSet { - s1.clone().difference(s2.clone()) -} - -pub fn hashset_arg_min( - s: &HashSet, - f: &Box>, -) -> DDOption { - DDOption::from(s.iter().min_by_key(|x| f.call(*x)).map(|x| (*x).clone())) -} - -pub fn hashset_arg_max( - s: &HashSet, - f: &Box>, -) -> DDOption { - DDOption::from(s.iter().max_by_key(|x| f.call(*x)).map(|x| (*x).clone())) -} +/* +Copyright (c) 2021 VMware, Inc. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use ddlog_rt::Closure; +use ddlog_std::{tuple2, Group, Option as DDOption, Vec as DDVec}; + +use differential_datalog::record::{CollectionKind, Record}; +use im::hashset::{ConsumingIter, HashSet as IMHashSet, OrderedConsumingIter, OrderedIter}; +use serde::{ + de::Deserializer, + ser::{SerializeSeq, Serializer}, +}; +use std::{ + borrow::Borrow, + cmp::Ordering, + collections::hash_map::DefaultHasher, + fmt::{Debug, Formatter, Result as FmtResult}, + hash::{BuildHasherDefault, Hash, Hasher}, + iter::FromIterator, + ops::DerefMut, + result::Result as StdResult, +}; + +#[derive(Clone)] +pub struct HashSet { + // We must use a deterministic hasher here instead of the default `RandomState` + // to ensure that hashset iterators yield elements in a consistent order. + set: IMHashSet>, +} + +impl Default for HashSet { + fn default() -> Self { + HashSet { + set: IMHashSet::with_hasher(>::default()), + } + } +} + +impl Hash for HashSet { + fn hash(&self, state: &mut H) { + // Our modified `im::HashSet` implementation ensures that `hash` is + // deterministic, i.e., given the same hasher and two sets with the + // same elements it returns the same hash value. In contrast, the + // original `im` crate currently violates this property. + self.set.hash(state); + } +} + +impl PartialEq for HashSet { + fn eq(&self, other: &Self) -> bool { + // The `Eq` implementation in `im::HashSet` is unable to rely on the + // ordered iterator (which requires the `Ord` trait) and is therefore + // inefficient compared to this. + + // Check if `other` is a clone of `self`. + if self.set.ptr_eq(&other.set) { + return true; + } + if self.len() != other.len() { + return false; + } + self.iter().eq(other.iter()) + } +} + +impl Eq for HashSet {} + +impl PartialOrd for HashSet { + fn partial_cmp(&self, other: &Self) -> Option { + if self.set.ptr_eq(&other.set) { + return Some(Ordering::Equal); + } + // We don't need to implement lexicographical ordering. If the two + // sets have different sizes, just compare the sizes. + match self.len().cmp(&other.len()) { + Ordering::Equal => (), + ord => return Some(ord), + } + // Our modified `im::HashSet` implementation ensures that `partial_cmp` + // is consistent: given the same hasher and two sets with the + // same elements, it returns `Ordering::Equal`. In contrast, the + // original `im` crate currently violates this property. + self.set.partial_cmp(&other.set) + } +} + +impl Ord for HashSet { + fn cmp(&self, other: &Self) -> Ordering { + if self.set.ptr_eq(&other.set) { + return Ordering::Equal; + } + match self.len().cmp(&other.len()) { + Ordering::Equal => (), + ord => return ord, + } + + // Our modified `IMHashSet` implementation ensures that `cmp` + // is consistent: given the same hasher and two sets with the + // same elements it returns `Ordering::Equal`. In contrast, the + // original `im` crate currently violates this property. + self.set.cmp(&other.set) + } +} + +impl Serialize for HashSet { + fn serialize(&self, serializer: S) -> StdResult + where + S: Serializer, + { + // Don't use IMHashSet's implementation of `serialize` to + // ensure deterministic order. + let mut s = serializer.serialize_seq(Some(self.len()))?; + for i in self.iter() { + s.serialize_element(i)?; + } + s.end() + } +} + +impl<'de, T: Deserialize<'de> + Hash + Eq + Clone> Deserialize<'de> for HashSet { + fn deserialize(deserializer: D) -> StdResult + where + D: Deserializer<'de>, + { + IMHashSet::deserialize(deserializer).map(HashSet::from) + } +} + +impl HashSet { + pub fn len(&self) -> usize { + self.set.len() + } +} + +impl<'a, T: Hash + Ord> HashSet { + // Always use this iterator over `im::HashSet::iter` in scenarios where + // order matters (serialization, formatting, etc.). + pub fn iter(&'a self) -> OrderedIter<'a, T> { + // The default IMHashSet iterator does not guarantee consistent + // ordering: two sets with the same elements can iterate over them + // in different order even when using the same hasher. This can + // lead to non-deterministic behavior in DDlog code that iterates + // over hashsets. We must therefore use a deterministic iterator. + self.set.ordered_iter() + } +} + +impl IntoIterator for HashSet { + type Item = T; + type IntoIter = OrderedConsumingIter; + + fn into_iter(self) -> Self::IntoIter { + // Use ordered iterator that guarantees consistent ordering. + self.set.into_ordered_iter() + } +} + +impl, RT> FromIterator for HashSet { + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + HashSet { + set: IMHashSet::from_iter(iter), + } + } +} + +impl HashSet { + pub fn new() -> Self { + Self::default() + } + pub fn is_empty(&self) -> bool { + self.set.is_empty() + } +} + +impl From>> for HashSet { + fn from(set: IMHashSet>) -> Self { + HashSet { set } + } +} + +impl HashSet { + pub fn contains(&self, x: &BT) -> bool + where + BT: Hash + Eq, + T: Borrow, + { + self.set.contains(x) + } +} + +impl HashSet { + pub fn unit(x: T) -> Self { + let mut set = IMHashSet::default(); + set.insert(x); + HashSet::from(set) + } + + pub fn insert(&mut self, x: T) -> Option { + self.set.insert(x) + } + + pub fn remove(&mut self, x: &BT) -> Option + where + BT: Hash + Eq, + T: Borrow, + { + self.set.remove(x) + } + + pub fn without(&self, x: &T) -> Self { + HashSet::from(self.set.without(x)) + } + + pub fn update(&self, x: T) -> Self { + HashSet::from(self.set.update(x)) + } + + pub fn union(self, other: Self) -> Self { + // `im::HashSet` implements union by adding elements from `other` to + // `self`. This is inefficient when `other` is much + // larger than `self`. The following optimization will become + // unnecessary once this PR or something similar has landed: + // https://github.com/bodil/im-rs/pull/163. + if self.len() >= other.len() { + HashSet::from(self.set.union(other.set)) + } else { + HashSet::from(other.set.union(self.set)) + } + } + pub fn unions(i: I) -> Self + where + I: IntoIterator, + { + let mut sets: Vec<_> = i.into_iter().collect(); + + // Start with the largest set. + if let Some((largest_idx, _)) = sets.iter().enumerate().max_by_key(|(_, s)| s.len()) { + let mut largest_set = Self::default(); + std::mem::swap(&mut largest_set, &mut sets[largest_idx]); + + sets.into_iter().fold(largest_set, Self::union) + } else { + Self::default() + } + } + pub fn intersection(self, other: Self) -> Self { + HashSet::from(self.set.intersection(other.set)) + } + pub fn difference(self, other: Self) -> Self { + HashSet::from(self.set.difference(other.set)) + } +} + +// I don't want to implement `Deref` for `HashSet` to prevent +// accidentally calling `im::HashSet` methods. For example, +// `im::HashSet::iter` returns the non-deterministic iterator (which can +// mess up DDlog in ways one can easily miss unless they test on big +// enough sets). + +// impl Deref for HashSet { +// type Target = IMHashSet; +// +// fn deref(//self) -> //Self::Target { +// //self.set +// } +// } +// +// impl DerefMut for HashSet { +// fn deref_mut(//mut self) -> //mut Self::Target { +// //mut self.set +// } +// } + +impl FromRecordInner for HashSet { + fn from_record_inner(val: &Record) -> StdResult { + match val { + Record::Array(_, args) => StdResult::from_iter(args.iter().map(T::from_record)), + v => T::from_record(v).map(Self::unit), + } + } +} + +impl IntoRecord for HashSet { + fn into_record(self) -> Record { + Record::Array( + CollectionKind::Set, + self.into_iter().map(|x| x.into_record()).collect(), + ) + } +} + +// Set update semantics: update contains values that are in one of the sets +// but not the other. +impl + MutatorInner> for Record +{ + fn mutate_inner(&self, set: &mut HashSet) -> StdResult<(), String> { + let upd = >::from_record(self)?; + *set = HashSet::from(set.set.clone().symmetric_difference(upd.set)); + Ok(()) + } +} + +impl Debug for HashSet { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + f.debug_set().entries(self.iter()).finish() + } +} + +pub fn hashset_size(s: &HashSet) -> std_usize { + s.len() as std_usize +} + +pub fn hashset_empty() -> HashSet { + HashSet::new() +} + +pub fn hashset_singleton(v: &T) -> HashSet { + HashSet::unit((*v).clone()) +} + +pub fn hashset_insert(s: &mut HashSet, v: &T) { + s.insert(v.clone()); +} + +pub fn hashset_insert_imm(s: &HashSet, v: &T) -> HashSet { + s.update((*v).clone()) +} + +pub fn hashset_remove(s: &mut HashSet, v: &T) { + s.remove(v); +} + +pub fn hashset_remove_imm(s: &HashSet, v: &T) -> HashSet { + s.without(v) +} + +pub fn hashset_contains(s: &HashSet, v: &T) -> bool { + s.contains(v) +} + +pub fn hashset_is_empty(s: &HashSet) -> bool { + s.is_empty() +} + +pub fn hashset_nth(s: &HashSet, n: &std_usize) -> DDOption { + s.iter().nth(*n as usize).cloned().into() +} + +pub fn hashset_to_vec(s: &HashSet) -> DDVec { + DDVec { + vec: s.iter().cloned().collect(), + } +} + +pub fn hashset_union(s1: &HashSet, s2: &HashSet) -> HashSet { + s1.clone().union(s2.clone()) +} + +pub fn hashset_unions(sets: &Vec>) -> HashSet { + HashSet::unions(sets.iter().cloned()) +} + +pub fn group_hashset_unions( + sets: &Group>, +) -> HashSet { + HashSet::unions(sets.iter().map(|tuple2(x, _)| x)) +} + +pub fn hashset_intersection(s1: &HashSet, s2: &HashSet) -> HashSet { + s1.clone().intersection(s2.clone()) +} + +pub fn hashset_difference(s1: &HashSet, s2: &HashSet) -> HashSet { + s1.clone().difference(s2.clone()) +} + +pub fn hashset_arg_min( + s: &HashSet, + f: &Box>, +) -> DDOption { + DDOption::from(s.iter().min_by_key(|x| f.call(*x)).map(|x| (*x).clone())) +} + +pub fn hashset_arg_max( + s: &HashSet, + f: &Box>, +) -> DDOption { + DDOption::from(s.iter().max_by_key(|x| f.call(*x)).map(|x| (*x).clone())) +} diff --git a/lib/intern.toml b/lib/intern.toml index ab981e946..2d0857460 100644 --- a/lib/intern.toml +++ b/lib/intern.toml @@ -1,6 +1,6 @@ -[dependencies.lasso] -version = "0.4.0" -features = ["multi-threaded"] - -[dependencies.fxhash] -version = "0.2.1" +[dependencies.lasso] +version = "0.4.0" +features = ["multi-threaded"] + +[dependencies.fxhash] +version = "0.2.1" diff --git a/lib/json.toml b/lib/json.toml new file mode 100644 index 000000000..66f6cbdc5 --- /dev/null +++ b/lib/json.toml @@ -0,0 +1,2 @@ +[dependencies.serde_json] +version = "1.0.69" diff --git a/lib/net/ipv6.rs b/lib/net/ipv6.rs index 2ea6ece65..2e0557d00 100644 --- a/lib/net/ipv6.rs +++ b/lib/net/ipv6.rs @@ -1,246 +1,238 @@ -/* -Copyright (c) 2021 VMware, Inc. -SPDX-License-Identifier: MIT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -use differential_datalog::record::Record; -use serde::de::Deserializer; -use serde::ser::Serializer; -use std::fmt; -use std::hash::Hash; -use std::str::FromStr; - -use ddlog_std::option2std; - -#[derive(Eq, Ord, Clone, Hash, PartialEq, PartialOrd)] -pub struct Ipv6Addr(::std::net::Ipv6Addr); - -impl Ipv6Addr { - pub fn new(addr: ::std::net::Ipv6Addr) -> Self { - Ipv6Addr(addr) - } -} - -impl Deref for Ipv6Addr { - type Target = ::std::net::Ipv6Addr; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Default for Ipv6Addr { - fn default() -> Ipv6Addr { - iPV6_UNSPECIFIED() - } -} - -impl fmt::Display for Ipv6Addr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", ipv6_to_u128(self)) - } -} - -impl fmt::Debug for Ipv6Addr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl Serialize for Ipv6Addr { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - ipv6_to_u128(self).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for Ipv6Addr { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - u128::deserialize(deserializer).map(|x| ipv6_from_u128(&x)) - } -} - -impl FromRecord for Ipv6Addr { - fn from_record(val: &Record) -> Result { - u128::from_record(val).map(|x| ipv6_from_u128(&x)) - } -} - -impl IntoRecord for Ipv6Addr { - fn into_record(self) -> Record { - ipv6_to_u128(&self).into_record() - } -} - -impl Mutator for Record { - fn mutate(&self, addr: &mut Ipv6Addr) -> Result<(), String> { - Ipv6Addr::from_record(self).map(|a| *addr = a) - } -} - -pub fn ipv6_new( - a: &u16, - b: &u16, - c: &u16, - d: &u16, - e: &u16, - f: &u16, - g: &u16, - h: &u16, -) -> Ipv6Addr { - Ipv6Addr::new(::std::net::Ipv6Addr::new(*a, *b, *c, *d, *e, *f, *g, *h)) -} - -pub fn ipv6_from_segment_vec( - segments: &ddlog_std::Vec, -) -> ddlog_std::Option { - if segments.len() != 8 { - return ddlog_std::Option::None; - }; - ddlog_std::Option::Some { - x: Ipv6Addr::new(::std::net::Ipv6Addr::from([ - segments[0], - segments[1], - segments[2], - segments[3], - segments[4], - segments[5], - segments[6], - segments[7], - ])), - } -} - -pub fn ipv6_from_octets( - b0: &u8, - b1: &u8, - b2: &u8, - b3: &u8, - b4: &u8, - b5: &u8, - b6: &u8, - b7: &u8, - b8: &u8, - b9: &u8, - b10: &u8, - b11: &u8, - b12: &u8, - b13: &u8, - b14: &u8, - b15: &u8, -) -> Ipv6Addr { - Ipv6Addr::new(::std::net::Ipv6Addr::from([ - *b0, *b1, *b2, *b3, *b4, *b5, *b6, *b7, *b8, *b9, *b10, *b11, *b12, *b13, *b14, *b15, - ])) -} - -pub fn ipv6_from_octet_vec( - octets: &ddlog_std::Vec, -) -> ddlog_std::Option { - if octets.len() != 16 { - return ddlog_std::Option::None; - }; - ddlog_std::Option::Some { - x: Ipv6Addr::new(::std::net::Ipv6Addr::from([ - octets[0], octets[1], octets[2], octets[3], octets[4], octets[5], octets[6], octets[7], - octets[8], octets[9], octets[10], octets[11], octets[12], octets[13], octets[14], - octets[15], - ])), - } -} - -pub fn ipv6_from_u128(ip: &u128) -> Ipv6Addr { - Ipv6Addr::new(::std::net::Ipv6Addr::from(*ip)) -} - -pub fn ipv6_from_str(s: &String) -> ddlog_std::Result { - ddlog_std::res2std(::std::net::Ipv6Addr::from_str(&*s).map(Ipv6Addr::new)) -} - -pub fn ipv6Addr2string(addr: &Ipv6Addr) -> String { - (**addr).to_string() -} - -pub fn ipv6_to_u128(addr: &Ipv6Addr) -> u128 { - u128::from(**addr) -} - -pub fn iPV6_LOCALHOST() -> Ipv6Addr { - Ipv6Addr::new(::std::net::Ipv6Addr::LOCALHOST) -} - -pub fn iPV6_UNSPECIFIED() -> Ipv6Addr { - Ipv6Addr::new(::std::net::Ipv6Addr::UNSPECIFIED) -} - -pub fn ipv6_segments(addr: &Ipv6Addr) -> ddlog_std::tuple8 { - let segments = addr.segments(); - ddlog_std::tuple8( - segments[0], - segments[1], - segments[2], - segments[3], - segments[4], - segments[5], - segments[6], - segments[7], - ) -} - -pub fn ipv6_segment_vec(addr: &Ipv6Addr) -> ddlog_std::Vec { - ddlog_std::Vec::from(addr.segments().as_ref()) -} - -pub fn ipv6_octets( - addr: &Ipv6Addr, -) -> ddlog_std::tuple16 { - let octets = addr.octets(); - ddlog_std::tuple16( - octets[0], octets[1], octets[2], octets[3], octets[4], octets[5], octets[6], octets[7], - octets[8], octets[9], octets[10], octets[11], octets[12], octets[13], octets[14], - octets[15], - ) -} - -pub fn ipv6_octet_vec(addr: &Ipv6Addr) -> ddlog_std::Vec { - ddlog_std::Vec::from(addr.octets().as_ref()) -} - -pub fn ipv6_is_unspecified(addr: &Ipv6Addr) -> bool { - addr.is_unspecified() -} - -pub fn ipv6_is_loopback(addr: &Ipv6Addr) -> bool { - addr.is_loopback() -} - -pub fn ipv6_is_multicast(addr: &Ipv6Addr) -> bool { - addr.is_multicast() -} - -pub fn ipv6_to_ipv4(addr: &Ipv6Addr) -> ddlog_std::Option { - option2std(addr.to_ipv4().map(super::ipv4::Ipv4Addr::new)) -} +/* +Copyright (c) 2021 VMware, Inc. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use differential_datalog::record::Record; +use serde::{de::Deserializer, ser::Serializer}; +use std::{fmt, hash::Hash, str::FromStr}; + +#[derive(Eq, Ord, Clone, Hash, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct Ipv6Addr(::std::net::Ipv6Addr); + +impl Ipv6Addr { + pub fn new(addr: ::std::net::Ipv6Addr) -> Self { + Ipv6Addr(addr) + } +} + +impl Deref for Ipv6Addr { + type Target = ::std::net::Ipv6Addr; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for Ipv6Addr { + fn default() -> Ipv6Addr { + iPV6_UNSPECIFIED() + } +} + +impl fmt::Display for Ipv6Addr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", ipv6_to_u128(self)) + } +} + +impl fmt::Debug for Ipv6Addr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl Serialize for Ipv6Addr { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + ipv6_to_u128(self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Ipv6Addr { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + u128::deserialize(deserializer).map(|x| ipv6_from_u128(&x)) + } +} + +impl FromRecord for Ipv6Addr { + fn from_record(val: &Record) -> Result { + u128::from_record(val).map(|x| ipv6_from_u128(&x)) + } +} + +impl IntoRecord for Ipv6Addr { + fn into_record(self) -> Record { + ipv6_to_u128(&self).into_record() + } +} + +impl Mutator for Record { + fn mutate(&self, addr: &mut Ipv6Addr) -> Result<(), String> { + Ipv6Addr::from_record(self).map(|a| *addr = a) + } +} + +pub fn ipv6_new( + a: &u16, + b: &u16, + c: &u16, + d: &u16, + e: &u16, + f: &u16, + g: &u16, + h: &u16, +) -> Ipv6Addr { + Ipv6Addr::new(::std::net::Ipv6Addr::new(*a, *b, *c, *d, *e, *f, *g, *h)) +} + +pub fn ipv6_from_segment_vec(segments: &ddlog_std::Vec) -> ddlog_std::Option { + if segments.len() != 8 { + return ddlog_std::Option::None; + }; + ddlog_std::Option::Some { + x: Ipv6Addr::new(::std::net::Ipv6Addr::from([ + segments[0], + segments[1], + segments[2], + segments[3], + segments[4], + segments[5], + segments[6], + segments[7], + ])), + } +} + +pub fn ipv6_from_octets( + b0: &u8, + b1: &u8, + b2: &u8, + b3: &u8, + b4: &u8, + b5: &u8, + b6: &u8, + b7: &u8, + b8: &u8, + b9: &u8, + b10: &u8, + b11: &u8, + b12: &u8, + b13: &u8, + b14: &u8, + b15: &u8, +) -> Ipv6Addr { + Ipv6Addr::new(::std::net::Ipv6Addr::from([ + *b0, *b1, *b2, *b3, *b4, *b5, *b6, *b7, *b8, *b9, *b10, *b11, *b12, *b13, *b14, *b15, + ])) +} + +pub fn ipv6_from_octet_vec(octets: &ddlog_std::Vec) -> ddlog_std::Option { + if octets.len() != 16 { + return ddlog_std::Option::None; + }; + ddlog_std::Option::Some { + x: Ipv6Addr::new(::std::net::Ipv6Addr::from([ + octets[0], octets[1], octets[2], octets[3], octets[4], octets[5], octets[6], octets[7], + octets[8], octets[9], octets[10], octets[11], octets[12], octets[13], octets[14], + octets[15], + ])), + } +} + +pub fn ipv6_from_u128(ip: &u128) -> Ipv6Addr { + Ipv6Addr::new(::std::net::Ipv6Addr::from(*ip)) +} + +pub fn ipv6_from_str(s: &String) -> ddlog_std::Result { + ddlog_std::res2std(::std::net::Ipv6Addr::from_str(&*s).map(Ipv6Addr::new)) +} + +pub fn ipv6Addr2string(addr: &Ipv6Addr) -> String { + (**addr).to_string() +} + +pub fn ipv6_to_u128(addr: &Ipv6Addr) -> u128 { + u128::from(**addr) +} + +pub fn iPV6_LOCALHOST() -> Ipv6Addr { + Ipv6Addr::new(::std::net::Ipv6Addr::LOCALHOST) +} + +pub fn iPV6_UNSPECIFIED() -> Ipv6Addr { + Ipv6Addr::new(::std::net::Ipv6Addr::UNSPECIFIED) +} + +pub fn ipv6_segments(addr: &Ipv6Addr) -> ddlog_std::tuple8 { + let segments = addr.segments(); + ddlog_std::tuple8( + segments[0], + segments[1], + segments[2], + segments[3], + segments[4], + segments[5], + segments[6], + segments[7], + ) +} + +pub fn ipv6_segment_vec(addr: &Ipv6Addr) -> ddlog_std::Vec { + ddlog_std::Vec::from(addr.segments().as_ref()) +} + +pub fn ipv6_octets( + addr: &Ipv6Addr, +) -> ddlog_std::tuple16 { + let octets = addr.octets(); + ddlog_std::tuple16( + octets[0], octets[1], octets[2], octets[3], octets[4], octets[5], octets[6], octets[7], + octets[8], octets[9], octets[10], octets[11], octets[12], octets[13], octets[14], + octets[15], + ) +} + +pub fn ipv6_octet_vec(addr: &Ipv6Addr) -> ddlog_std::Vec { + ddlog_std::Vec::from(addr.octets().as_ref()) +} + +pub fn ipv6_is_unspecified(addr: &Ipv6Addr) -> bool { + addr.is_unspecified() +} + +pub fn ipv6_is_loopback(addr: &Ipv6Addr) -> bool { + addr.is_loopback() +} + +pub fn ipv6_is_multicast(addr: &Ipv6Addr) -> bool { + addr.is_multicast() +} + +pub fn ipv6_to_ipv4(addr: &Ipv6Addr) -> ddlog_std::Option { + addr.to_ipv4().map(super::ipv4::Ipv4Addr::new).into() +} diff --git a/lib/tinyset.rs b/lib/tinyset.rs index 6f56f6392..f31ccdbff 100644 --- a/lib/tinyset.rs +++ b/lib/tinyset.rs @@ -1,342 +1,339 @@ -/* -Copyright (c) 2021 VMware, Inc. -SPDX-License-Identifier: MIT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -use differential_datalog::record::*; -use serde; -use std::cmp; -use std::collections; -use std::fmt; -use std::iter; -use std::iter::FromIterator; -use std::ops::BitOr; -use std::vec; -pub use tinyset::u64set; - -use ddlog_std::option2std; - -#[derive(Eq, Clone, Hash, PartialEq)] -pub struct Set64 { - pub x: u64set::Set64, -} - -/* This is needed so we can support for-loops over `Set64`'s. - * - * IMPORTANT: We iterate over Set64 by converting it into a sorted vector. - * This can be costly, but is necessary to make sure that all `Set64` - * operations are deterministic. - */ -pub struct Set64Iter { - iter: vec::IntoIter, -} - -impl Set64Iter { - pub fn new(set: &Set64) -> Set64Iter { - let mut v: Vec<_> = set.x.iter().collect(); - v.sort(); - Set64Iter { - iter: v.into_iter(), - } - } -} - -impl Iterator for Set64Iter { - type Item = X; - - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl Set64 { - pub fn iter(&self) -> Set64Iter { - Set64Iter::new(self) - } -} - -impl Set64 { - /* In cases when order really, definitely, 100% does not matter, - * a more efficient iterator can be used. - */ - pub fn unsorted_iter(&self) -> u64set::Iter64 { - self.x.iter() - } -} - -impl Ord for Set64 { - fn cmp(&self, other: &Self) -> cmp::Ordering { - let size_cmp = self.x.len().cmp(&other.x.len()); - if size_cmp != cmp::Ordering::Equal { - size_cmp - } else { - self.iter().cmp(other.iter()) - } - } -} - -impl PartialOrd for Set64 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Default for Set64 { - fn default() -> Self { - Set64 { - x: u64set::Set64::default(), - } - } -} - -impl serde::Serialize for Set64 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_seq(self.iter()) - } -} - -impl<'de, T: u64set::Fits64 + serde::Deserialize<'de> + Ord> serde::Deserialize<'de> for Set64 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - panic!("Set64::deserialize is not implemented") - } -} - -impl Set64 { - pub fn new() -> Self { - Set64 { - x: u64set::Set64::new(), - } - } - pub fn insert(&mut self, v: T) { - self.x.insert(v); - } - pub fn remove(&mut self, v: &T) -> bool { - self.x.remove(v) - } -} - -impl FromRecordInner for Set64 { - fn from_record_inner(val: &Record) -> Result { - vec::Vec::from_record(val).map(|v| Set64 { - x: u64set::Set64::from_iter(v), - }) - } -} - -impl IntoRecord for Set64 { - fn into_record(self) -> Record { - Record::Array( - CollectionKind::Set, - self.into_iter().map(|x| x.into_record()).collect(), - ) - } -} - -impl Mutator> - for Record -{ - fn mutate(&self, set: &mut Set64) -> Result<(), String> { - let upd = >::from_record(self)?; - for v in upd.into_iter() { - if !set.remove(&v) { - set.insert(v); - } - } - Ok(()) - } -} - -impl iter::IntoIterator for &Set64 { - type Item = T; - type IntoIter = Set64Iter; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl FromIterator for Set64 { - fn from_iter(iter: I) -> Self - where - I: iter::IntoIterator, - { - Set64 { - x: u64set::Set64::from_iter(iter), - } - } -} - -impl fmt::Display for Set64 { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let len = self.x.len(); - formatter.write_str("[")?; - for (i, v) in self.iter().enumerate() { - formatter.write_fmt(format_args!("{}", v))?; - if i < len - 1 { - formatter.write_str(",")?; - } - } - formatter.write_str("]")?; - Ok(()) - } -} - -impl fmt::Debug for Set64 { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let len = self.x.len(); - formatter.write_str("[")?; - for (i, v) in self.iter().enumerate() { - formatter.write_fmt(format_args!("{:?}", v))?; - if i < len - 1 { - formatter.write_str(",")?; - } - } - formatter.write_str("]")?; - Ok(()) - } -} - -pub fn size(s: &Set64) -> u64 { - s.x.len() as u64 -} - -pub fn empty() -> Set64 { - Set64::new() -} - -pub fn singleton(v: &X) -> Set64 { - let mut s = Set64::new(); - s.insert(v.clone()); - s -} - -pub fn insert(s: &mut Set64, v: &X) { - s.x.insert((*v).clone()); -} - -pub fn insert_imm(mut s: Set64, v: X) -> Set64 { - s.insert(v); - s -} - -pub fn contains(s: &Set64, v: &X) -> bool { - s.x.contains(*v) -} - -pub fn is_empty(s: &Set64) -> bool { - s.x.len() == 0 -} - -pub fn nth(s: &Set64, n: &u64) -> ddlog_std::Option { - option2std(s.iter().nth(*n as usize)) -} - -pub fn set2vec(s: &Set64) -> ddlog_std::Vec { - let mut v: Vec<_> = s.x.iter().collect(); - v.sort(); - ddlog_std::Vec::from(v) -} - -pub fn union(s1: Set64, s2: &Set64) -> Set64 { - Set64 { - x: s1.x.bitor(&s2.x), - } -} - -pub fn unions(sets: &ddlog_std::Vec>) -> Set64 { - let mut s = u64set::Set64::new(); - for si in sets.iter() { - for v in si.unsorted_iter() { - s.insert(v); - } - } - Set64 { x: s } -} - -pub fn intersection(s1: &Set64, s2: &Set64) -> Set64 { - let mut s = u64set::Set64::new(); - for v in s1.unsorted_iter() { - if s2.x.contains(v) { - s.insert(v); - } - } - Set64 { x: s } -} - -pub fn difference(s1: &Set64, s2: &Set64) -> Set64 { - Set64 { - x: std::ops::Sub::sub(&s1.x, &s2.x), - } -} - -pub fn group_to_set(g: &ddlog_std::Group) -> Set64 { - let mut res = Set64::new(); - for ref v in g.val_iter() { - insert(&mut res, v.clone()); - } - res -} - -pub fn group_set_unions( - g: &ddlog_std::Group>, -) -> Set64 { - let mut res = u64set::Set64::new(); - for gr in g.val_iter() { - for v in gr.unsorted_iter() { - res.insert(v.clone()); - } - //`extend` should be more efficient, but is not yet implemented in the tinyset library - //res.extend(&mut g.ith(i).x.iter()); - } - Set64 { x: res } -} - -pub fn group_setref_unions( - g: &ddlog_std::Group>>, -) -> ddlog_std::Ref> { - if ddlog_std::group_count_distinct(g) == 1 { - ddlog_std::group_first(g) - } else { - let mut res = ddlog_std::ref_new(Set64 { - x: u64set::Set64::new(), - }); - { - let rres = ddlog_std::Ref::get_mut(&mut res).unwrap(); - for gr in g.val_iter() { - for v in gr.unsorted_iter() { - rres.insert(v.clone()); - } - //not implemented - //res.extend(&mut g.ith(i).x.iter()); - } - } - res - } -} +/* +Copyright (c) 2021 VMware, Inc. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use differential_datalog::record::*; +use serde; +use std::{ + cmp, collections, fmt, + iter::{self, FromIterator}, + ops::BitOr, + vec, +}; +pub use tinyset::u64set; + +#[derive(Eq, Clone, Hash, PartialEq)] +pub struct Set64 { + pub x: u64set::Set64, +} + +/* This is needed so we can support for-loops over `Set64`'s. + * + * IMPORTANT: We iterate over Set64 by converting it into a sorted vector. + * This can be costly, but is necessary to make sure that all `Set64` + * operations are deterministic. + */ +pub struct Set64Iter { + iter: vec::IntoIter, +} + +impl Set64Iter { + pub fn new(set: &Set64) -> Set64Iter { + let mut v: Vec<_> = set.x.iter().collect(); + v.sort(); + Set64Iter { + iter: v.into_iter(), + } + } +} + +impl Iterator for Set64Iter { + type Item = X; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl Set64 { + pub fn iter(&self) -> Set64Iter { + Set64Iter::new(self) + } +} + +impl Set64 { + /* In cases when order really, definitely, 100% does not matter, + * a more efficient iterator can be used. + */ + pub fn unsorted_iter(&self) -> u64set::Iter64 { + self.x.iter() + } +} + +impl Ord for Set64 { + fn cmp(&self, other: &Self) -> cmp::Ordering { + let size_cmp = self.x.len().cmp(&other.x.len()); + if size_cmp != cmp::Ordering::Equal { + size_cmp + } else { + self.iter().cmp(other.iter()) + } + } +} + +impl PartialOrd for Set64 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Default for Set64 { + fn default() -> Self { + Set64 { + x: u64set::Set64::default(), + } + } +} + +impl serde::Serialize for Set64 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_seq(self.iter()) + } +} + +impl<'de, T: u64set::Fits64 + serde::Deserialize<'de> + Ord> serde::Deserialize<'de> for Set64 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + panic!("Set64::deserialize is not implemented") + } +} + +impl Set64 { + pub fn new() -> Self { + Set64 { + x: u64set::Set64::new(), + } + } + pub fn insert(&mut self, v: T) { + self.x.insert(v); + } + pub fn remove(&mut self, v: &T) -> bool { + self.x.remove(v) + } +} + +impl FromRecordInner for Set64 { + fn from_record_inner(val: &Record) -> Result { + vec::Vec::from_record(val).map(|v| Set64 { + x: u64set::Set64::from_iter(v), + }) + } +} + +impl IntoRecord for Set64 { + fn into_record(self) -> Record { + Record::Array( + CollectionKind::Set, + self.into_iter().map(|x| x.into_record()).collect(), + ) + } +} + +impl Mutator> + for Record +{ + fn mutate(&self, set: &mut Set64) -> Result<(), String> { + let upd = >::from_record(self)?; + for v in upd.into_iter() { + if !set.remove(&v) { + set.insert(v); + } + } + Ok(()) + } +} + +impl iter::IntoIterator for &Set64 { + type Item = T; + type IntoIter = Set64Iter; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl FromIterator for Set64 { + fn from_iter(iter: I) -> Self + where + I: iter::IntoIterator, + { + Set64 { + x: u64set::Set64::from_iter(iter), + } + } +} + +impl fmt::Display for Set64 { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let len = self.x.len(); + formatter.write_str("[")?; + for (i, v) in self.iter().enumerate() { + formatter.write_fmt(format_args!("{}", v))?; + if i < len - 1 { + formatter.write_str(",")?; + } + } + formatter.write_str("]")?; + Ok(()) + } +} + +impl fmt::Debug for Set64 { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let len = self.x.len(); + formatter.write_str("[")?; + for (i, v) in self.iter().enumerate() { + formatter.write_fmt(format_args!("{:?}", v))?; + if i < len - 1 { + formatter.write_str(",")?; + } + } + formatter.write_str("]")?; + Ok(()) + } +} + +pub fn size(s: &Set64) -> u64 { + s.x.len() as u64 +} + +pub fn empty() -> Set64 { + Set64::new() +} + +pub fn singleton(v: &X) -> Set64 { + let mut s = Set64::new(); + s.insert(v.clone()); + s +} + +pub fn insert(s: &mut Set64, v: &X) { + s.x.insert((*v).clone()); +} + +pub fn insert_imm(mut s: Set64, v: X) -> Set64 { + s.insert(v); + s +} + +pub fn contains(s: &Set64, v: &X) -> bool { + s.x.contains(*v) +} + +pub fn is_empty(s: &Set64) -> bool { + s.x.len() == 0 +} + +pub fn nth(s: &Set64, n: &u64) -> ddlog_std::Option { + s.iter().nth(*n as usize).into() +} + +pub fn set2vec(s: &Set64) -> ddlog_std::Vec { + let mut v: Vec<_> = s.x.iter().collect(); + v.sort(); + ddlog_std::Vec::from(v) +} + +pub fn union(s1: Set64, s2: &Set64) -> Set64 { + Set64 { + x: s1.x.bitor(&s2.x), + } +} + +pub fn unions(sets: &ddlog_std::Vec>) -> Set64 { + let mut s = u64set::Set64::new(); + for si in sets.iter() { + for v in si.unsorted_iter() { + s.insert(v); + } + } + Set64 { x: s } +} + +pub fn intersection(s1: &Set64, s2: &Set64) -> Set64 { + let mut s = u64set::Set64::new(); + for v in s1.unsorted_iter() { + if s2.x.contains(v) { + s.insert(v); + } + } + Set64 { x: s } +} + +pub fn difference(s1: &Set64, s2: &Set64) -> Set64 { + Set64 { + x: std::ops::Sub::sub(&s1.x, &s2.x), + } +} + +pub fn group_to_set(g: &ddlog_std::Group) -> Set64 { + let mut res = Set64::new(); + for ref v in g.val_iter() { + insert(&mut res, v.clone()); + } + res +} + +pub fn group_set_unions( + g: &ddlog_std::Group>, +) -> Set64 { + let mut res = u64set::Set64::new(); + for gr in g.val_iter() { + for v in gr.unsorted_iter() { + res.insert(v.clone()); + } + //`extend` should be more efficient, but is not yet implemented in the tinyset library + //res.extend(&mut g.ith(i).x.iter()); + } + Set64 { x: res } +} + +pub fn group_setref_unions( + g: &ddlog_std::Group>>, +) -> ddlog_std::Ref> { + if ddlog_std::group_count_distinct(g) == 1 { + ddlog_std::group_first(g) + } else { + let mut res = ddlog_std::ref_new(Set64 { + x: u64set::Set64::new(), + }); + { + let rres = ddlog_std::Ref::get_mut(&mut res).unwrap(); + for gr in g.val_iter() { + for v in gr.unsorted_iter() { + rres.insert(v.clone()); + } + //not implemented + //res.extend(&mut g.ith(i).x.iter()); + } + } + res + } +} diff --git a/lib/url.rs b/lib/url.rs index 2a8728cc9..b48c5a53b 100644 --- a/lib/url.rs +++ b/lib/url.rs @@ -1,123 +1,123 @@ -/* -Copyright (c) 2021 VMware, Inc. -SPDX-License-Identifier: MIT - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -use differential_datalog::record; - -#[derive(Eq, Ord, Clone, Hash, PartialEq, PartialOrd, Serialize, Deserialize, Debug)] -pub struct Url { - url: ::url::Url, -} - -impl Default for Url { - fn default() -> Self { - Url { - url: ::url::Url::parse("http://127.0.0.1/").unwrap(), - } - } -} - -impl FromRecordInner for Url { - fn from_record_inner(val: &record::Record) -> ::std::result::Result { - match (val) { - record::Record::String(s) => match (::url::Url::parse(&s)) { - Ok(url) => Ok(Url { url }), - Err(e) => Err(format!("{}", e)), - }, - _ => Err(String::from("Unexpected type")), - } - } -} - -impl IntoRecord for Url { - fn into_record(self) -> record::Record { - record::Record::String(self.url.as_str().to_string()) - } -} - -impl record::Mutator for record::Record { - fn mutate(&self, t: &mut Url) -> ::std::result::Result<(), String> { - *t = Url::from_record(self)?; - Ok(()) - } -} - -pub fn url_parse(s: &String) -> ddlog_std::Result { - match ::url::Url::parse(s) { - Ok(url) => ddlog_std::Result::Ok { res: Url { url } }, - Err(e) => ddlog_std::Result::Err { - err: format!("{}", e), - }, - } -} - -pub fn join(url: &Url, other: &String) -> ddlog_std::Result { - match url.url.join(other.as_str()) { - Ok(url) => ddlog_std::Result::Ok { res: Url { url } }, - Err(e) => ddlog_std::Result::Err { - err: format!("{}", e), - }, - } -} - -pub fn url_to_string(url: &Url) -> String { - format!("{}", url.url) -} -pub fn scheme(url: &Url) -> String { - url.url.scheme().to_string() -} -pub fn has_authority(url: &Url) -> bool { - url.url.has_authority() -} -pub fn cannot_be_a_base(url: &Url) -> bool { - url.url.cannot_be_a_base() -} -pub fn username(url: &Url) -> String { - url.url.username().to_string() -} -pub fn password(url: &Url) -> ddlog_std::Option { - ddlog_std::option2std(url.url.password().map(|x| x.to_string())) -} -pub fn has_host(url: &Url) -> bool { - url.url.has_host() -} -pub fn host_str(url: &Url) -> ddlog_std::Option { - ddlog_std::option2std(url.url.host_str().map(|x| x.to_string())) -} -pub fn domain(url: &Url) -> ddlog_std::Option { - ddlog_std::option2std(url.url.domain().map(|x| x.to_string())) -} -pub fn port(url: &Url) -> ddlog_std::Option { - ddlog_std::option2std(url.url.port()) -} -pub fn port_or_known_default(url: &Url) -> ddlog_std::Option { - ddlog_std::option2std(url.url.port_or_known_default()) -} -pub fn path(url: &Url) -> String { - url.url.path().to_string() -} -pub fn query(url: &Url) -> ddlog_std::Option { - ddlog_std::option2std(url.url.query().map(|x| x.to_string())) -} -pub fn fragment(url: &Url) -> ddlog_std::Option { - ddlog_std::option2std(url.url.fragment().map(|x| x.to_string())) -} +/* +Copyright (c) 2021 VMware, Inc. +SPDX-License-Identifier: MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use differential_datalog::record; + +#[derive(Eq, Ord, Clone, Hash, PartialEq, PartialOrd, Serialize, Deserialize, Debug)] +pub struct Url { + url: ::url::Url, +} + +impl Default for Url { + fn default() -> Self { + Url { + url: ::url::Url::parse("http://127.0.0.1/").unwrap(), + } + } +} + +impl FromRecordInner for Url { + fn from_record_inner(val: &record::Record) -> ::std::result::Result { + match (val) { + record::Record::String(s) => match (::url::Url::parse(&s)) { + Ok(url) => Ok(Url { url }), + Err(e) => Err(format!("{}", e)), + }, + _ => Err(String::from("Unexpected type")), + } + } +} + +impl IntoRecord for Url { + fn into_record(self) -> record::Record { + record::Record::String(self.url.as_str().to_string()) + } +} + +impl record::Mutator for record::Record { + fn mutate(&self, t: &mut Url) -> ::std::result::Result<(), String> { + *t = Url::from_record(self)?; + Ok(()) + } +} + +pub fn url_parse(s: &String) -> ddlog_std::Result { + match ::url::Url::parse(s) { + Ok(url) => ddlog_std::Result::Ok { res: Url { url } }, + Err(e) => ddlog_std::Result::Err { + err: format!("{}", e), + }, + } +} + +pub fn join(url: &Url, other: &String) -> ddlog_std::Result { + match url.url.join(other.as_str()) { + Ok(url) => ddlog_std::Result::Ok { res: Url { url } }, + Err(e) => ddlog_std::Result::Err { + err: format!("{}", e), + }, + } +} + +pub fn url_to_string(url: &Url) -> String { + format!("{}", url.url) +} +pub fn scheme(url: &Url) -> String { + url.url.scheme().to_string() +} +pub fn has_authority(url: &Url) -> bool { + url.url.has_authority() +} +pub fn cannot_be_a_base(url: &Url) -> bool { + url.url.cannot_be_a_base() +} +pub fn username(url: &Url) -> String { + url.url.username().to_string() +} +pub fn password(url: &Url) -> ddlog_std::Option { + url.url.password().map(|x| x.to_string()).into() +} +pub fn has_host(url: &Url) -> bool { + url.url.has_host() +} +pub fn host_str(url: &Url) -> ddlog_std::Option { + url.url.host_str().map(|x| x.to_string()).into() +} +pub fn domain(url: &Url) -> ddlog_std::Option { + url.url.domain().map(|x| x.to_string()).into() +} +pub fn port(url: &Url) -> ddlog_std::Option { + url.url.port().into() +} +pub fn port_or_known_default(url: &Url) -> ddlog_std::Option { + url.url.port_or_known_default().into() +} +pub fn path(url: &Url) -> String { + url.url.path().to_string() +} +pub fn query(url: &Url) -> ddlog_std::Option { + url.url.query().map(|x| x.to_string()).into() +} +pub fn fragment(url: &Url) -> ddlog_std::Option { + url.url.fragment().map(|x| x.to_string()).into() +} From b5905b8d0e1d917636bc1e9431ce9d5add2afb86 Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Fri, 12 Nov 2021 11:38:34 -0600 Subject: [PATCH 2/3] Narrowed dependencies and refactoring --- rust/template/Cargo.toml | 38 +- rust/template/cmd_parser/Cargo.toml | 36 +- rust/template/ddlog_derive/Cargo.toml | 38 +- rust/template/ddlog_profiler/Cargo.toml | 15 +- .../template/ddlog_profiler/src/debug_info.rs | 131 ++-- rust/template/ddlog_profiler/src/lib.rs | 49 +- .../ddlog_profiler/src/profile_statistics.rs | 22 +- rust/template/differential_datalog/Cargo.toml | 29 +- .../differential_datalog/src/ddlog.rs | 58 +- .../src/ddval/ddval_convert.rs | 660 +++++++++--------- .../differential_datalog/src/ddval/ddvalue.rs | 367 +++++----- .../differential_datalog/src/ddval/mod.rs | 237 ++++--- rust/template/differential_datalog/src/lib.rs | 3 +- .../src/program/arrange.rs | 442 ++++++------ .../differential_datalog/src/program/mod.rs | 142 ++-- .../src/program/worker.rs | 51 +- .../differential_datalog/src/replay.rs | 11 +- .../differential_datalog/src/utils.rs | 16 + .../differential_datalog_test/Cargo.toml | 14 +- .../template/differential_datalog_test/lib.rs | 28 +- rust/template/ovsdb/Cargo.toml | 36 +- rust/template/src/inventory.rs | 234 +++---- rust/template/src/lib.rs | 94 ++- rust/template/types/lib.rs | 152 ++-- src/Language/DifferentialDatalog/Compile.hs | 30 +- test/datalog_tests/ovn.rs | 72 +- 26 files changed, 1552 insertions(+), 1453 deletions(-) create mode 100644 rust/template/differential_datalog/src/utils.rs diff --git a/rust/template/Cargo.toml b/rust/template/Cargo.toml index 5e935e1d6..9c4aa0ede 100644 --- a/rust/template/Cargo.toml +++ b/rust/template/Cargo.toml @@ -16,21 +16,19 @@ c_api = ["differential_datalog/c_api"] checked_weights = ["differential_datalog/checked_weights"] [dependencies] -abomonation = "0.7" +abomonation = "0.7.3" ordered-float = { version = "2.0.0", features = ["serde"] } -cpuprofiler = { version = "0.0", optional = true } -#differential-dataflow = "0.11.0" +cpuprofiler = { version = "0.0.4", optional = true } differential-dataflow = { git = "https://github.com/ddlog-dev/differential-dataflow", branch = "ddlog-4" } -#timely = "0.11" timely = { git = "https://github.com/ddlog-dev/timely-dataflow", branch = "ddlog-4", default-features = false } -fnv = "1.0.2" +xxhash-rust = { version = "0.8.2", features = ["xxh3"] } once_cell = "1.8.0" -libc = "0.2" -num-traits = "0.2" -num = { version = "0.3", features = ["serde"] } +libc = "0.2.107" +num-traits = "0.2.14" +num = { version = "0.4.0", features = ["serde"] } rustop = { version = "1.1.1", optional = true } -serde = { version = "1.0", features = ["derive"] } -erased-serde = "0.3" +serde = { version = "1.0.130", features = ["derive"] } +erased-serde = "0.3.16" crossbeam-channel = "0.5.0" enum-primitive-derive = "0.2.1" triomphe = "0.1.3" @@ -41,19 +39,19 @@ phf = { version = "0.10.0", features = ["macros"] } # libraries: flatbuffers "2.0.0" <-> FlatBuffers "2.0.0". flatbuffers = { version = "2.0.0", optional = true } -[dependencies.differential_datalog] -path = "./differential_datalog" + [dependencies.differential_datalog] + path = "./differential_datalog" -[dependencies.ddlog_profiler] -path = "./ddlog_profiler" + [dependencies.ddlog_profiler] + path = "./ddlog_profiler" -[dependencies.cmd_parser] -path = "./cmd_parser" -optional = true + [dependencies.cmd_parser] + path = "./cmd_parser" + optional = true -[dependencies.ddlog_ovsdb_adapter] -path = "./ovsdb" -optional = true + [dependencies.ddlog_ovsdb_adapter] + path = "./ovsdb" + optional = true [target.'cfg(not(windows))'.build-dependencies] libtool = "0.1" diff --git a/rust/template/cmd_parser/Cargo.toml b/rust/template/cmd_parser/Cargo.toml index 57ea6317a..e8b616576 100644 --- a/rust/template/cmd_parser/Cargo.toml +++ b/rust/template/cmd_parser/Cargo.toml @@ -1,18 +1,18 @@ -[package] -name = "cmd_parser" -version = "0.1.0" -edition = "2018" - -[dependencies.differential_datalog] -path = "../differential_datalog" - -[dependencies] -libc = "0.2.42" -ordered-float = { version = "2.0.0", features = ["serde"] } -nom = "4.0" -num = "0.3" -rustyline = "1.0.0" - -[lib] -name = "cmd_parser" -path = "lib.rs" +[package] +name = "cmd_parser" +version = "0.1.0" +edition = "2018" + +[dependencies.differential_datalog] +path = "../differential_datalog" + +[dependencies] +libc = "0.2.107" +ordered-float = { version = "2.8.0", features = ["serde"] } +nom = "4.0" +num = "0.4.0" +rustyline = "1.0.0" + +[lib] +name = "cmd_parser" +path = "lib.rs" diff --git a/rust/template/ddlog_derive/Cargo.toml b/rust/template/ddlog_derive/Cargo.toml index 85d30ab97..f25c2ec59 100644 --- a/rust/template/ddlog_derive/Cargo.toml +++ b/rust/template/ddlog_derive/Cargo.toml @@ -1,19 +1,19 @@ -[package] -name = "ddlog_derive" -version = "0.1.0" -edition = "2018" -license = "MIT" - -[lib] -proc-macro = true - -[dependencies] -syn = "1.0.48" -quote = "1.0.8" -proc-macro2 = "1.0.24" - -[dev-dependencies] -trybuild = "1.0.38" -differential_datalog = { path = "../differential_datalog" } -serde = { version = "1.0", features = ["derive"] } -num = "0.3" +[package] +name = "ddlog_derive" +version = "0.1.0" +edition = "2018" +license = "MIT" + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0.48" +quote = "1.0.8" +proc-macro2 = "1.0.24" + +[dev-dependencies] +trybuild = "1.0.38" +differential_datalog = { path = "../differential_datalog" } +serde = { version = "1.0", features = ["derive"] } +num = "0.4.0" diff --git a/rust/template/ddlog_profiler/Cargo.toml b/rust/template/ddlog_profiler/Cargo.toml index 02eab720c..14fe2b74b 100644 --- a/rust/template/ddlog_profiler/Cargo.toml +++ b/rust/template/ddlog_profiler/Cargo.toml @@ -3,16 +3,13 @@ name = "ddlog_profiler" version = "0.1.0" edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +csv = "1.1.6" +xxhash-rust = { version = "0.8.2", features = ["xxh3"] } +serde = { version = "1.0.130", features = ["derive"] } +serde_json = "1.0.69" +once_cell = "1.8.0" +sequence_trie = "0.3" timely = { git = "https://github.com/ddlog-dev/timely-dataflow", branch = "ddlog-4", default-features = false } differential-dataflow = { git = "https://github.com/ddlog-dev/differential-dataflow", branch = "ddlog-4" } - -csv = "1.1" -fnv = "1.0.2" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.64" -once_cell = "1.8.0" -sequence_trie = "0.3" diff --git a/rust/template/ddlog_profiler/src/debug_info.rs b/rust/template/ddlog_profiler/src/debug_info.rs index 19795130c..545f6a5de 100644 --- a/rust/template/ddlog_profiler/src/debug_info.rs +++ b/rust/template/ddlog_profiler/src/debug_info.rs @@ -657,32 +657,39 @@ impl OperatorDebugInfo { /// More detailed description. pub fn description(&self) -> String { match self { - Self::Dataflow => "".to_string(), - Self::Builtin { .. } => "".to_string(), - Self::Input { .. } => "".to_string(), - Self::Filter { .. } => "".to_string(), - Self::Map { .. } => "".to_string(), - Self::FilterMap { .. } => "".to_string(), - Self::Head { .. } => "".to_string(), - Self::Flatmap { .. } => "".to_string(), - Self::FixedPoint { .. } => "".to_string(), + Self::Dataflow + | Self::Builtin { .. } + | Self::Input { .. } + | Self::Filter { .. } + | Self::Map { .. } + | Self::FilterMap { .. } + | Self::Head { .. } + | Self::Flatmap { .. } + | Self::FixedPoint { .. } + | Self::StreamXForm { .. } + | Self::JoinWithEnabled { .. } + | Self::ConsolidateOutput { .. } + | Self::DelayedRelation { .. } + | Self::InspectOutput { .. } + | Self::ProbeOutput { .. } + | Self::ConcatenateRelation { .. } + | Self::DistinctRelation { .. } + | Self::Inspect { .. } + | Self::Arrange { .. } + | Self::StreamArrange { .. } + | Self::GroupBy { .. } + | Self::Differentiate { .. } + | Self::ApplyTransformer { .. } + | Self::RelEnterRecursiveScope { .. } + | Self::ArrEnterRecursiveScope { .. } + | Self::RelLeaveRecursiveScope { .. } => String::new(), + Self::RecursiveComponent { rels, .. } => format!("Recursive program fragment consisting of relations {}", Self::code(&rels.as_slice().join(", "))), - Self::StreamXForm { .. } => "".to_string(), - Self::JoinWithEnabled { .. } => "".to_string(), - Self::ConsolidateOutput { .. } => "".to_string(), - Self::DelayedRelation { .. } => "".to_string(), - Self::InspectOutput { .. } => "".to_string(), - Self::ProbeOutput { .. } => "".to_string(), Self::ProbeIndex { rel, arr, indexes, .. } if indexes.len() == 1 => format!("Probe arrangement of {} by {} used to construct index {}", Self::code(rel), Self::code(arr), Self::code(&indexes[0])), Self::ProbeIndex { rel, arr, indexes, .. } => format!("Probe arrangement of {} by {} used to construct indexes {}", Self::code(rel), Self::code(arr), Self::code(&indexes.as_slice().join(", "))), - Self::ConcatenateRelation { .. } => "".to_string(), - Self::DistinctRelation { .. } => "".to_string(), Self::ArrangeRelationNonRec { rel, arr, .. } => format!("Arrange relation {} using pattern {}", Self::code(rel), Self::code(arr)), Self::ArrangeRelationRecInner { rel, arr, .. } => format!("Arrange relation {} using pattern {} inside recursive scope", Self::code(rel), Self::code(arr)), Self::ArrangeRelationRecOuter { rel, arr, .. } => format!("Arrange relation {} using pattern {}", Self::code(rel), Self::code(arr)), - Self::Inspect { .. } => "".to_string(), - Self::Arrange { .. } => "".to_string(), - Self::StreamArrange { .. } => "".to_string(), Self::Join { rel, arr, .. } => format!("Join rule prefix with relation {} arranged using pattern {}", Self::code(rel), Self::code(arr)), Self::Semijoin { rel, arr, .. } => format!("Semijoin rule prefix with relation {} arranged using pattern {}", Self::code(rel), Self::code(arr)), Self::Antijoin { rel, arr, .. } => format!("Antijon rule prefix with relation {} arranged using pattern {}", Self::code(rel), Self::code(arr)), @@ -690,55 +697,51 @@ impl OperatorDebugInfo { Self::ArrStreamSemijoin { stream, arr, .. } => format!("Semijoin rule prefix with stream {} arranged using pattern {}", Self::code(stream), Self::code(arr)), Self::StreamArrJoin { arr1, rel2, arr2, .. } => format!("Join streaming rule prefix arranged by {} with relation {} arranged using pattern {}", Self::code(arr1), Self::code(rel2), Self::code(arr2)), Self::StreamArrSemijoin { arr1, rel2, arr2, .. } => format!("Semijoin streaming rule prefix arranged by {} with relation {} arranged using pattern {}", Self::code(arr1), Self::code(rel2), Self::code(arr2)), - Self::GroupBy { .. } => "".to_string(), - Self::Differentiate { .. } => "".to_string(), - Self::ApplyTransformer { .. } => "".to_string(), - Self::RelEnterRecursiveScope { .. } => "".to_string(), - Self::ArrEnterRecursiveScope { .. } => "".to_string(), - Self::RelLeaveRecursiveScope { .. } => "".to_string(), } } pub fn source_pos(&self) -> &[SourcePosition] { match self { - Self::Dataflow => &[], - Self::Builtin { .. } => &[], - Self::Input { source_pos, .. } => slice::from_ref(source_pos), - Self::Filter { source_pos } => slice::from_ref(source_pos), - Self::FilterMap { source_pos } => slice::from_ref(source_pos), - Self::Head { source_pos } => slice::from_ref(source_pos), - Self::Flatmap { source_pos } => slice::from_ref(source_pos), - Self::Map { source_pos } => slice::from_ref(source_pos), - Self::FixedPoint { source_pos, .. } => slice::from_ref(source_pos), - Self::RecursiveComponent { .. } => &[], - Self::StreamXForm { source_pos } => slice::from_ref(source_pos), - Self::JoinWithEnabled { used_at, .. } => used_at, - Self::ConsolidateOutput { source_pos, .. } => slice::from_ref(source_pos), - Self::DelayedRelation { used_at, .. } => used_at, - Self::InspectOutput { source_pos, .. } => slice::from_ref(source_pos), - Self::ProbeIndex { used_at, .. } => used_at, - Self::ProbeOutput { source_pos, .. } => slice::from_ref(source_pos), - Self::ConcatenateRelation { source_pos, .. } => slice::from_ref(source_pos), - Self::DistinctRelation { source_pos, .. } => slice::from_ref(source_pos), - Self::ArrangeRelationNonRec { used_at, .. } => used_at, - Self::ArrangeRelationRecInner { used_at, .. } => used_at, - Self::ArrangeRelationRecOuter { used_at, .. } => used_at, - Self::Inspect { source_pos } => slice::from_ref(source_pos), - Self::Arrange { source_pos, .. } => slice::from_ref(source_pos), - Self::StreamArrange { source_pos, .. } => slice::from_ref(source_pos), - Self::Join { source_pos, .. } => slice::from_ref(source_pos), - Self::Semijoin { source_pos, .. } => slice::from_ref(source_pos), - Self::Antijoin { source_pos, .. } => slice::from_ref(source_pos), - Self::ArrStreamJoin { source_pos, .. } => slice::from_ref(source_pos), - Self::ArrStreamSemijoin { source_pos, .. } => slice::from_ref(source_pos), - Self::StreamArrJoin { source_pos, .. } => slice::from_ref(source_pos), - Self::StreamArrSemijoin { source_pos, .. } => slice::from_ref(source_pos), - Self::GroupBy { source_pos } => slice::from_ref(source_pos), - Self::Differentiate { source_pos, .. } => slice::from_ref(source_pos), - Self::ApplyTransformer { source_pos, .. } => slice::from_ref(source_pos), - Self::RelEnterRecursiveScope { .. } => &[], - Self::ArrEnterRecursiveScope { .. } => &[], - Self::RelLeaveRecursiveScope { .. } => &[], + Self::Dataflow + | Self::Builtin { .. } + | Self::RecursiveComponent { .. } + | Self::RelEnterRecursiveScope { .. } + | Self::ArrEnterRecursiveScope { .. } + | Self::RelLeaveRecursiveScope { .. } => &[], + + Self::JoinWithEnabled { used_at, .. } + | Self::DelayedRelation { used_at, .. } + | Self::ProbeIndex { used_at, .. } + | Self::ArrangeRelationNonRec { used_at, .. } + | Self::ArrangeRelationRecInner { used_at, .. } + | Self::ArrangeRelationRecOuter { used_at, .. } => used_at, + + Self::Input { source_pos, .. } + | Self::Filter { source_pos } + | Self::FilterMap { source_pos } + | Self::Head { source_pos } + | Self::Flatmap { source_pos } + | Self::Map { source_pos } + | Self::FixedPoint { source_pos, .. } + | Self::StreamXForm { source_pos } + | Self::ConsolidateOutput { source_pos, .. } + | Self::InspectOutput { source_pos, .. } + | Self::ProbeOutput { source_pos, .. } + | Self::ConcatenateRelation { source_pos, .. } + | Self::DistinctRelation { source_pos, .. } + | Self::Inspect { source_pos } + | Self::Arrange { source_pos, .. } + | Self::StreamArrange { source_pos, .. } + | Self::Join { source_pos, .. } + | Self::Semijoin { source_pos, .. } + | Self::Antijoin { source_pos, .. } + | Self::ArrStreamJoin { source_pos, .. } + | Self::ArrStreamSemijoin { source_pos, .. } + | Self::StreamArrJoin { source_pos, .. } + | Self::StreamArrSemijoin { source_pos, .. } + | Self::GroupBy { source_pos } + | Self::Differentiate { source_pos, .. } + | Self::ApplyTransformer { source_pos, .. } => slice::from_ref(source_pos), } } } diff --git a/rust/template/ddlog_profiler/src/lib.rs b/rust/template/ddlog_profiler/src/lib.rs index 7710b24f4..3e46842c1 100644 --- a/rust/template/ddlog_profiler/src/lib.rs +++ b/rust/template/ddlog_profiler/src/lib.rs @@ -5,13 +5,12 @@ mod profile_statistics; mod source_code; pub use debug_info::{ArrangementDebugInfo, OperatorDebugInfo, RuleDebugInfo}; -use source_code::LinesIterator; pub use source_code::{DDlogSourceCode, SourceFile, SourcePosition}; use profile_statistics::Statistics; +use source_code::LinesIterator; use differential_dataflow::logging::DifferentialEvent; -use fnv::FnvHashMap; use once_cell::sync::Lazy; use sequence_trie::SequenceTrie; use serde::Serialize; @@ -20,7 +19,9 @@ use std::{ cell::RefCell, cmp::max, collections::{HashMap, HashSet}, - fs, include_str, + fs, + hash::BuildHasherDefault, + include_str, iter::FromIterator, path::PathBuf, sync::Mutex, @@ -28,6 +29,7 @@ use std::{ time::Instant, }; use timely::logging::{OperatesEvent, ScheduleEvent, StartStop, TimelyEvent}; +use xxhash_rust::xxh3::Xxh3; const PROFILER_UI_HTML: &str = include_str!("../profiler_ui/ui.html"); const PROFILER_UI_CSS: &str = include_str!("../profiler_ui/ui.css"); @@ -140,9 +142,11 @@ pub enum ProfileDump { }, } +type XxHasher = BuildHasherDefault; + // Indexed representation of `source_code` that // supports fast lookup by `SourcePosition`. -type IndexedSourceCode = HashMap<&'static str, Vec<&'static str>>; +type IndexedSourceCode = HashMap<&'static str, Vec<&'static str>, XxHasher>; #[derive(Debug)] pub struct Profile { @@ -152,16 +156,16 @@ pub struct Profile { // Instant when the profiling session was started. start_time: Instant, addresses: SequenceTrie, - op_address: FnvHashMap>, + op_address: HashMap, XxHasher>, /// Map operator id to its detailed debug info. - debug_info: FnvHashMap, + debug_info: HashMap, /// Short name of the op only. - short_names: FnvHashMap, - sizes: FnvHashMap, - peak_sizes: FnvHashMap, - changes: FnvHashMap, - starts: FnvHashMap<(usize, usize), Duration>, - durations: FnvHashMap, + short_names: HashMap, + sizes: HashMap, + peak_sizes: HashMap, + changes: HashMap, + starts: HashMap<(usize, usize), Duration, XxHasher>, + durations: HashMap, // Initialization creates a file timely_stats: Option, // Keep track of whether we already tried initializing timely_stats, this avoids us @@ -172,25 +176,26 @@ pub struct Profile { impl Profile { pub fn new(source_code: &'static DDlogSourceCode, profile_directory: PathBuf) -> Profile { - let indexed_source_code: HashMap<&'static str, Vec<&'static str>> = source_code + let indexed_source_code = source_code .code .iter() .map(|source_file| (source_file.filename, source_file.contents.lines().collect())) .collect(); + Profile { source_code, indexed_source_code, profile_directory, start_time: Instant::now(), addresses: SequenceTrie::new(), - op_address: FnvHashMap::default(), - debug_info: FnvHashMap::default(), - short_names: FnvHashMap::default(), - sizes: FnvHashMap::default(), - peak_sizes: FnvHashMap::default(), - changes: FnvHashMap::default(), - starts: FnvHashMap::default(), - durations: FnvHashMap::default(), + op_address: HashMap::default(), + debug_info: HashMap::default(), + short_names: HashMap::default(), + sizes: HashMap::default(), + peak_sizes: HashMap::default(), + changes: HashMap::default(), + starts: HashMap::default(), + durations: HashMap::default(), timely_stats: None, stats_init: false, } @@ -260,7 +265,7 @@ impl Profile { Some(cpu_profile) } - fn size_profile(&self, sizes: &FnvHashMap) -> Vec { + fn size_profile(&self, sizes: &HashMap) -> Vec { let mut size_vec: Vec<(usize, isize)> = sizes.clone().into_iter().collect(); size_vec.sort_by(|(_, sz1), (_, sz2)| sz1.cmp(sz2).reverse()); let children = size_vec diff --git a/rust/template/ddlog_profiler/src/profile_statistics.rs b/rust/template/ddlog_profiler/src/profile_statistics.rs index 5bf67ad20..6b92e5def 100644 --- a/rust/template/ddlog_profiler/src/profile_statistics.rs +++ b/rust/template/ddlog_profiler/src/profile_statistics.rs @@ -1,16 +1,16 @@ -use std::collections::HashMap; -use std::hash::Hash; -use std::time::Duration; - use csv::Writer; use serde::{Deserialize, Serialize}; - -use fnv::FnvHashMap; -use std::fmt; -use std::fmt::Debug; -use std::fs::File; +use std::time::Duration; +use std::{ + collections::HashMap, + fmt::{self, Debug}, + fs::File, + hash::Hash, +}; use timely::logging::{ParkEvent, StartStop, TimelyEvent}; +use crate::XxHasher; + /// Possible events fields for CSV event_type column. #[derive(Serialize, Deserialize, Debug, Clone)] enum CSVEventType { @@ -238,8 +238,8 @@ impl Statistics { timestamp: Duration, worker_index: usize, data: &TimelyEvent, - addresses: &FnvHashMap>, - names: &FnvHashMap, + addresses: &HashMap, XxHasher>, + names: &HashMap, ) { match data { TimelyEvent::GuardedMessage(g) => { diff --git a/rust/template/differential_datalog/Cargo.toml b/rust/template/differential_datalog/Cargo.toml index 106fd156c..751f34282 100644 --- a/rust/template/differential_datalog/Cargo.toml +++ b/rust/template/differential_datalog/Cargo.toml @@ -13,27 +13,26 @@ c_api = [] checked_weights = [] [dependencies] -#differential-dataflow = "0.11.0" -differential-dataflow = { git = "https://github.com/ddlog-dev/differential-dataflow", branch = "ddlog-4" } -dogsdogsdogs = { git = "https://github.com/ddlog-dev/differential-dataflow", branch = "ddlog-4" } -#timely = "0.11" -timely = { git = "https://github.com/ddlog-dev/timely-dataflow", branch = "ddlog-4", default-features = false } -ddshow-sink = { git = "https://github.com/ddlog-dev/ddshow", branch = "ddlog-4" } -abomonation = "0.7" +ddshow-sink = { git = "https://github.com/ddlog-dev/ddshow", branch = "ddlog-4" } +abomonation = "0.7.3" abomonation_derive = "0.5.0" -ordered-float = { version = "2.0.0", features = ["serde"] } -fnv = "1.0.2" -libc = "0.2" -num = { version = "0.3", features = ["serde"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.64" -erased-serde = "0.3" -crossbeam-channel = "0.5.0" +ordered-float = { version = "2.8.0", features = ["serde"] } +xxhash-rust = { version = "0.8.2", features = ["xxh3"] } +libc = "0.2.107" +num = { version = "0.4.0", features = ["serde"] } +serde = { version = "1.0.130", features = ["derive"] } +serde_json = "1.0.69" +erased-serde = "0.3.16" +crossbeam-channel = "0.5.1" triomphe = "0.1.3" dyn-clone = "1.0.4" once_cell = "1.8.0" ddlog_profiler = { path = "../ddlog_profiler" } +dogsdogsdogs = { git = "https://github.com/ddlog-dev/differential-dataflow", branch = "ddlog-4" } +differential-dataflow = { git = "https://github.com/ddlog-dev/differential-dataflow", branch = "ddlog-4" } +timely = { git = "https://github.com/ddlog-dev/timely-dataflow", branch = "ddlog-4", default-features = false } + [dev-dependencies] byteorder = "1.4.2" getopts = "0.2.21" diff --git a/rust/template/differential_datalog/src/ddlog.rs b/rust/template/differential_datalog/src/ddlog.rs index e66a02d25..c79b59b35 100644 --- a/rust/template/differential_datalog/src/ddlog.rs +++ b/rust/template/differential_datalog/src/ddlog.rs @@ -1,27 +1,29 @@ //! Traits that encapsulate a running DDlog program. +use crate::{ + ddval::{Any, DDValue}, + program::{ArrId, IdxId, RelId, Update}, + record::{Record, RelIdentifier, UpdCmd}, + utils::XxHashMap, + valmap::DeltaMap, +}; +use ddlog_profiler::{CpuProfile, SizeProfileRecord}; use dyn_clone::DynClone; -use fnv::FnvHashMap; -use std::any::TypeId; -use std::collections::btree_set::BTreeSet; -use std::collections::BTreeMap; -#[cfg(feature = "c_api")] -use std::ffi::CStr; -use std::io; -use std::iter::Iterator; -use std::ops::Deref; -use std::sync::Arc as StdArc; +use std::{ + any::TypeId, + collections::{btree_set::BTreeSet, BTreeMap}, + io, + iter::Iterator, + ops::Deref, + sync::Arc as StdArc, +}; use triomphe::Arc; -use crate::ddval::{Any, DDValue}; -use crate::program::RelId; -use crate::program::Update; -use crate::program::{ArrId, IdxId}; -use crate::record::UpdCmd; -use crate::record::{Record, RelIdentifier}; -use crate::valmap::DeltaMap; +#[cfg(feature = "c_api")] +use std::ffi::CStr; -use ddlog_profiler::{CpuProfile, SizeProfileRecord}; +/// A map of relation ids to their relation's name +pub type RelationNameMap = XxHashMap; /// Convert relation and index names to and from numeric id's. pub trait DDlogInventory: DynClone { @@ -54,7 +56,7 @@ pub trait DDlogInventory: DynClone { #[cfg(feature = "c_api")] fn get_index_cname(&self, index_id: IdxId) -> Result<&'static CStr, String>; - fn input_relation_ids(&self) -> &'static FnvHashMap; + fn input_relation_ids(&self) -> &'static RelationNameMap; fn index_from_record(&self, index: IdxId, key: &Record) -> Result; @@ -117,7 +119,7 @@ where self.deref().get_index_cname(index_id) } - fn input_relation_ids(&self) -> &'static FnvHashMap { + fn input_relation_ids(&self) -> &'static RelationNameMap { self.deref().input_relation_ids() } @@ -190,7 +192,7 @@ where self.deref().get_index_cname(index_id) } - fn input_relation_ids(&self) -> &'static FnvHashMap { + fn input_relation_ids(&self) -> &'static RelationNameMap { self.deref().input_relation_ids() } @@ -263,7 +265,7 @@ where self.deref().get_index_cname(index_id) } - fn input_relation_ids(&self) -> &'static FnvHashMap { + fn input_relation_ids(&self) -> &'static RelationNameMap { self.deref().input_relation_ids() } @@ -349,7 +351,7 @@ pub trait DDlogProfiling { /// Controls recording of the number of insertions and deletions per /// arrangement. Unlike the arrangement size profile, which tracks the - /// number of records in each arrangment, this feature tracks the amount + /// number of records in each arrangement, this feature tracks the amount /// of churn. For example adding one record and deleting one record will /// show up as two changes in the change profile (but will cancel out in /// the size profile). @@ -423,7 +425,10 @@ pub trait DDlogDynamic { fn transaction_rollback(&self) -> Result<(), String>; /// Apply a set of updates. - fn apply_updates_dynamic(&self, upds: &mut dyn Iterator) -> Result<(), String>; + fn apply_updates_dynamic( + &self, + updates: &mut dyn Iterator, + ) -> Result<(), String>; fn clear_relation(&self, table: RelId) -> Result<(), String>; @@ -445,7 +450,10 @@ pub trait DDlog: DDlogDynamic { fn transaction_commit_dump_changes(&self) -> Result, String>; /// Apply a set of updates. - fn apply_updates(&self, upds: &mut dyn Iterator>) -> Result<(), String>; + fn apply_updates( + &self, + updates: &mut dyn Iterator>, + ) -> Result<(), String>; /// Query index. Returns all values associated with the given key in the index. fn query_index(&self, index: IdxId, key: DDValue) -> Result, String>; diff --git a/rust/template/differential_datalog/src/ddval/ddval_convert.rs b/rust/template/differential_datalog/src/ddval/ddval_convert.rs index e6cf37b98..749aaf90a 100644 --- a/rust/template/differential_datalog/src/ddval/ddval_convert.rs +++ b/rust/template/differential_datalog/src/ddval/ddval_convert.rs @@ -1,316 +1,344 @@ -use crate::{ - ddval::{DDVal, DDValMethods, DDValue}, - record::{IntoRecord, Mutator, Record}, -}; -use std::{ - any::{Any, TypeId}, - cmp::Ordering, - fmt::{self, Debug, Display, Formatter}, - hash::{Hash, Hasher}, - hint::unreachable_unchecked, - mem::{self, align_of, size_of, ManuallyDrop}, -}; -use triomphe::Arc; - -/// Trait to convert `DDVal` into concrete value type and back. -#[allow(clippy::upper_case_acronyms)] -pub trait DDValConvert: Sized { - /// Extract reference to concrete type from `&DDVal`. - /// - /// # Safety - /// - /// `value` **must** be the same type as the type the `DDVal` was created with - /// - unsafe fn from_ddval_ref(value: &DDVal) -> &Self; - - /// Converts an `&DDValue` into a reference of the given type - /// - /// Returns `None` if the type given is not the same as the type the `DDValue` - /// was created with - /// - fn try_from_ddvalue_ref(value: &DDValue) -> Option<&Self> - where - Self: 'static, - { - let value_type = (value.vtable.type_id)(&value.val); - if value_type == TypeId::of::() { - // Safety: The type we're turning the value into is the same as the one - // it was created with - Some(unsafe { Self::from_ddval_ref(&value.val) }) - } else { - None - } - } - - /// Converts an `&DDValue` into a reference of the given type - /// - /// # Panics - /// - /// Panics if the type given is not the same as the type the `DDValue` - /// was created with - /// - fn from_ddvalue_ref(value: &DDValue) -> &Self - where - Self: 'static, - { - Self::try_from_ddvalue_ref(value) - .expect("attempted to convert a DDValue into the incorrect type") - } - - /// Converts a [`DDValue`] into a reference of the given type without - /// checking that the type given and the [`DDValue`]'s internal types - /// are the same - /// - /// # Safety - /// - /// The type given must be the same as the [`DDValue`]'s internal type - /// - unsafe fn from_ddvalue_ref_unchecked(value: &DDValue) -> &Self - where - Self: 'static, - { - let value_type = (value.vtable.type_id)(&value.val); - debug_assert_eq!(value_type, TypeId::of::()); - - Self::try_from_ddvalue_ref(value).unwrap_or_else(|| unreachable_unchecked()) - } - - /// Extracts concrete value contained in `value`. - /// - /// # Safety - /// - /// `value` **must** be the same type as the type the `DDValue` was created with - /// - unsafe fn from_ddval(value: DDVal) -> Self; - - /// Converts a `DDValue` into a the given type - /// - /// Returns `None` if the type given is not the same as the type the `DDValue` - /// was created with - /// - fn try_from_ddvalue(value: DDValue) -> Option - where - Self: 'static, - { - let value_type = (value.vtable.type_id)(&value.val); - if value_type == TypeId::of::() { - // Safety: The type we're turning the value into is the same as the one - // it was created with - Some(unsafe { Self::from_ddval(value.into_ddval()) }) - } else { - None - } - } - - /// Converts a `DDValue` into the given type - /// - /// # Panics - /// - /// Panics if the type given is not the same as the type the `DDValue` - /// was created with - /// - fn from_ddvalue(value: DDValue) -> Self - where - Self: 'static, - { - Self::try_from_ddvalue(value) - .expect("attempted to convert a DDValue into the incorrect type") - } - - /// Converts a [`DDValue`] into the given type without checking that the - /// type given and the [`DDValue`]'s internal types are the same - /// - /// # Safety - /// - /// The type given must be the same as the [`DDValue`]'s internal type - /// - unsafe fn from_ddvalue_unchecked(value: DDValue) -> Self - where - Self: 'static, - { - let value_type = (value.vtable.type_id)(&value.val); - debug_assert_eq!(value_type, TypeId::of::()); - - Self::try_from_ddvalue(value).unwrap_or_else(|| unreachable_unchecked()) - } - - /// Convert a value to a `DDVal`, erasing its original type. - /// - /// This is a safe conversion that cannot fail. - fn into_ddval(self) -> DDVal; - - /// Creates a `DDValue` from the current value - fn ddvalue(&self) -> DDValue; - - /// Converts the current value into a `DDValue` - fn into_ddvalue(self) -> DDValue; - - /// The vtable containing all `DDValue` methods for the current type - const VTABLE: DDValMethods; -} - -/// Implement `DDValConvert` for all types that satisfy its type constraints -impl DDValConvert for T -where - T: Any - + Clone - + Debug - + IntoRecord - + Eq - + PartialEq - + Ord - + PartialOrd - + Hash - + Send - + Sync - + erased_serde::Serialize - + 'static, - Record: Mutator, -{ - unsafe fn from_ddval_ref(value: &DDVal) -> &Self { - let fits_in_usize = - size_of::() <= size_of::() && align_of::() <= align_of::(); - - if fits_in_usize { - &*<*const usize>::cast::(&value.v) - } else { - &*(value.v as *const Self) - } - } - - unsafe fn from_ddval(value: DDVal) -> Self { - let fits_in_usize = - size_of::() <= size_of::() && align_of::() <= align_of::(); - - if fits_in_usize { - <*const usize>::cast::(&value.v).read() - } else { - let arc = Arc::from_raw(value.v as *const Self); - - // If the `Arc` is uniquely held then simply take the inner value, - // otherwise clone it out - Arc::try_unwrap(arc).unwrap_or_else(|arc| Self::clone(&*arc)) - } - } - - fn into_ddval(self) -> DDVal { - let fits_in_usize = - size_of::() <= size_of::() && align_of::() <= align_of::(); - - // The size and alignment of the `T` must be less than or equal to a - // `usize`'s, otherwise we store it within an `Arc` - if fits_in_usize { - let mut v: usize = 0; - unsafe { <*mut usize>::cast::(&mut v).write(self) }; - - DDVal { v } - } else { - DDVal { - v: Arc::into_raw(Arc::new(self)) as usize, - } - } - } - - fn ddvalue(&self) -> DDValue { - DDValConvert::into_ddvalue(self.clone()) - } - - fn into_ddvalue(self) -> DDValue { - DDValue::new(self.into_ddval(), &Self::VTABLE) - } - - const VTABLE: DDValMethods = { - let clone = |this: &DDVal| -> DDVal { - let fits_in_usize = size_of::() <= size_of::() - && align_of::() <= align_of::(); - - if fits_in_usize { - unsafe { ::from_ddval_ref(this) }.clone().into_ddval() - } else { - let arc = unsafe { ManuallyDrop::new(Arc::from_raw(this.v as *const Self)) }; - - DDVal { - v: Arc::into_raw(Arc::clone(&arc)) as usize, - } - } - }; - - let into_record = - |this: DDVal| -> Record { unsafe { ::from_ddval(this) }.into_record() }; - - let eq: unsafe fn(&DDVal, &DDVal) -> bool = - |this, other| unsafe { ::from_ddval_ref(this).eq(::from_ddval_ref(other)) }; - - let partial_cmp: unsafe fn(&DDVal, &DDVal) -> Option = |this, other| unsafe { - ::from_ddval_ref(this).partial_cmp(::from_ddval_ref(other)) - }; - - let cmp: unsafe fn(&DDVal, &DDVal) -> Ordering = |this, other| unsafe { - ::from_ddval_ref(this).cmp(::from_ddval_ref(other)) - }; - - let hash = |this: &DDVal, mut state: &mut dyn Hasher| { - Hash::hash(unsafe { ::from_ddval_ref(this) }, &mut state); - }; - - let mutate = |this: &mut DDVal, record: &Record| -> Result<(), String> { - let mut clone = unsafe { ::from_ddval_ref(this) }.clone(); - Mutator::mutate(record, &mut clone)?; - *this = clone.into_ddval(); - - Ok(()) - }; - - let fmt_debug = |this: &DDVal, f: &mut Formatter| -> Result<(), fmt::Error> { - Debug::fmt(unsafe { ::from_ddval_ref(this) }, f) - }; - - let fmt_display = |this: &DDVal, f: &mut Formatter| -> Result<(), fmt::Error> { - Display::fmt( - &unsafe { ::from_ddval_ref(this) } - .clone() - .into_record(), - f, - ) - }; - - let drop = |this: &mut DDVal| { - let fits_in_usize = size_of::() <= size_of::() - && align_of::() <= align_of::(); - - if fits_in_usize { - // Allow the inner value's Drop impl to run automatically - let _val = unsafe { <*const usize>::cast::(&this.v).read() }; - } else { - let arc = unsafe { Arc::from_raw(this.v as *const Self) }; - mem::drop(arc); - } - }; - - let ddval_serialize: fn(&DDVal) -> &dyn erased_serde::Serialize = - |this| unsafe { ::from_ddval_ref(this) as &dyn erased_serde::Serialize }; - - let type_id = |_this: &DDVal| -> TypeId { TypeId::of::() }; - - DDValMethods { - clone, - into_record, - eq, - partial_cmp, - cmp, - hash, - mutate, - fmt_debug, - fmt_display, - drop, - ddval_serialize, - type_id, - } - }; -} - -impl Default for DDValue { - fn default() -> Self { - ().into_ddvalue() - } -} +use crate::{ + ddval::{DDVal, DDValMethods, DDValue}, + record::{IntoRecord, Mutator, Record}, +}; +use std::{ + any::{Any, TypeId}, + cmp::Ordering, + fmt::{self, Debug, Display, Formatter}, + hash::{Hash, Hasher}, + hint::unreachable_unchecked, + mem::{self, align_of, needs_drop, size_of, ManuallyDrop}, +}; +use triomphe::Arc; + +/// Trait to convert `DDVal` into concrete value type and back. +#[allow(clippy::upper_case_acronyms)] +pub trait DDValConvert: Sized { + /// Extract reference to concrete type from `&DDVal`. + /// + /// # Safety + /// + /// `value` **must** be the same type as the type the `DDVal` was created with + /// + unsafe fn from_ddval_ref(value: &DDVal) -> &Self; + + /// Converts an `&DDValue` into a reference of the given type + /// + /// Returns `None` if the type given is not the same as the type the `DDValue` + /// was created with + /// + #[inline] + fn try_from_ddvalue_ref(value: &DDValue) -> Option<&Self> + where + Self: 'static, + { + let value_type = (value.vtable.type_id)(&value.val); + if value_type == TypeId::of::() { + // Safety: The type we're turning the value into is the same as the one + // it was created with + Some(unsafe { Self::from_ddval_ref(&value.val) }) + } else { + None + } + } + + /// Converts an `&DDValue` into a reference of the given type + /// + /// # Panics + /// + /// Panics if the type given is not the same as the type the `DDValue` + /// was created with + /// + #[inline] + fn from_ddvalue_ref(value: &DDValue) -> &Self + where + Self: 'static, + { + Self::try_from_ddvalue_ref(value) + .expect("attempted to convert a DDValue into the incorrect type") + } + + /// Converts a [`DDValue`] into a reference of the given type without + /// checking that the type given and the [`DDValue`]'s internal types + /// are the same + /// + /// # Safety + /// + /// The type given must be the same as the [`DDValue`]'s internal type + /// + #[inline] + unsafe fn from_ddvalue_ref_unchecked(value: &DDValue) -> &Self + where + Self: 'static, + { + let value_type = (value.vtable.type_id)(&value.val); + debug_assert_eq!(value_type, TypeId::of::()); + + Self::try_from_ddvalue_ref(value).unwrap_or_else(|| unreachable_unchecked()) + } + + /// Extracts concrete value contained in `value`. + /// + /// # Safety + /// + /// `value` **must** be the same type as the type the `DDValue` was created with + /// + unsafe fn from_ddval(value: DDVal) -> Self; + + /// Converts a `DDValue` into a the given type + /// + /// Returns `None` if the type given is not the same as the type the `DDValue` + /// was created with + /// + #[inline] + fn try_from_ddvalue(value: DDValue) -> Result + where + Self: 'static, + { + let value_type = (value.vtable.type_id)(&value.val); + if value_type == TypeId::of::() { + // Safety: The type we're turning the value into is the same as the one + // it was created with + Ok(unsafe { Self::from_ddval(value.into_ddval()) }) + } else { + Err(value) + } + } + + /// Converts a `DDValue` into the given type + /// + /// # Panics + /// + /// Panics if the type given is not the same as the type the `DDValue` + /// was created with + /// + #[inline] + fn from_ddvalue(value: DDValue) -> Self + where + Self: 'static, + { + Self::try_from_ddvalue(value) + .expect("attempted to convert a DDValue into the incorrect type") + } + + /// Converts a [`DDValue`] into the given type without checking that the + /// type given and the [`DDValue`]'s internal types are the same + /// + /// # Safety + /// + /// The type given must be the same as the [`DDValue`]'s internal type + /// + #[inline] + unsafe fn from_ddvalue_unchecked(value: DDValue) -> Self + where + Self: 'static, + { + let value_type = (value.vtable.type_id)(&value.val); + debug_assert_eq!(value_type, TypeId::of::()); + + Self::try_from_ddvalue(value).unwrap_or_else(|_| unreachable_unchecked()) + } + + /// Convert a value to a `DDVal`, erasing its original type. + /// + /// This is a safe conversion that cannot fail. + fn into_ddval(self) -> DDVal; + + /// Creates a `DDValue` from the current value + fn ddvalue(&self) -> DDValue; + + /// Converts the current value into a `DDValue` + fn into_ddvalue(self) -> DDValue; + + /// The vtable containing all `DDValue` methods for the current type + const VTABLE: DDValMethods; + + /// Will be `true` if the current type can fit within a [`usize`] + const FITS_IN_USIZE: bool = + size_of::() <= size_of::() && align_of::() <= align_of::(); +} + +/// Implement `DDValConvert` for all types that satisfy its type constraints +impl DDValConvert for T +where + T: Any + + Clone + + Debug + + IntoRecord + + Eq + + PartialEq + + Ord + + PartialOrd + + Hash + + Send + + Sync + + erased_serde::Serialize + + 'static, + Record: Mutator, +{ + #[inline] + unsafe fn from_ddval_ref(value: &DDVal) -> &Self { + if Self::FITS_IN_USIZE { + &*<*const usize>::cast::(&value.v) + } else { + &*(value.v as *const Self) + } + } + + #[inline] + unsafe fn from_ddval(value: DDVal) -> Self { + if Self::FITS_IN_USIZE { + <*const usize>::cast::(&value.v).read() + } else { + let arc = Arc::from_raw(value.v as *const Self); + + // If the `Arc` is uniquely held then simply take the inner value, + // otherwise clone it out + Arc::try_unwrap(arc).unwrap_or_else(|arc| Self::clone(&*arc)) + } + } + + #[inline] + fn into_ddval(self) -> DDVal { + // The size and alignment of the `T` must be less than or equal to a + // `usize`'s, otherwise we store it within an `Arc` + if Self::FITS_IN_USIZE { + let mut v: usize = 0; + unsafe { <*mut usize>::cast::(&mut v).write(self) }; + + DDVal { v } + } else { + DDVal { + v: Arc::into_raw(Arc::new(self)) as usize, + } + } + } + + #[inline] + fn ddvalue(&self) -> DDValue { + DDValConvert::into_ddvalue(self.clone()) + } + + #[inline] + fn into_ddvalue(self) -> DDValue { + DDValue::new(self.into_ddval(), &Self::VTABLE) + } + + const VTABLE: DDValMethods = { + let clone = |this: &DDVal| -> DDVal { + if Self::FITS_IN_USIZE { + // Safety: The caller promises to ensure that Self + // is the current type of the given value + let value = unsafe { Self::from_ddval_ref(this) }; + + value.clone().into_ddval() + } else { + // Safety: `this.v` is a pointer created by `Arc::into_raw()` + let arc = unsafe { ManuallyDrop::new(Arc::from_raw(this.v as *const Self)) }; + + DDVal { + v: Arc::into_raw(Arc::clone(&arc)) as usize, + } + } + }; + + let into_record = + |this: DDVal| -> Record { unsafe { Self::from_ddval(this) }.into_record() }; + + let eq: unsafe fn(&DDVal, &DDVal) -> bool = |this, other| unsafe { + // Safety: The caller promises to ensure that `this` and `other` have the same type + let (this, other) = (Self::from_ddval_ref(this), Self::from_ddval_ref(other)); + + this.eq(other) + }; + + let partial_cmp: unsafe fn(&DDVal, &DDVal) -> Option = |this, other| unsafe { + // Safety: The caller promises to ensure that `this` and `other` have the same type + let (this, other) = (Self::from_ddval_ref(this), Self::from_ddval_ref(other)); + + this.partial_cmp(other) + }; + + let cmp: unsafe fn(&DDVal, &DDVal) -> Ordering = |this, other| unsafe { + // Safety: The caller promises to ensure that `this` and `other` have the same type + let (this, other) = (Self::from_ddval_ref(this), Self::from_ddval_ref(other)); + + this.cmp(other) + }; + + let hash = |this: &DDVal, mut state: &mut dyn Hasher| { + // Safety: The caller promises to ensure that Self + // is the current type of the given value + let value = unsafe { Self::from_ddval_ref(this) }; + + Hash::hash(value, &mut state); + }; + + let mutate = |this: &mut DDVal, record: &Record| -> Result<(), String> { + // Safety: The caller promises to ensure that Self + // is the current type of the given value + let mut clone = unsafe { Self::from_ddval_ref(this) }.clone(); + Mutator::mutate(record, &mut clone)?; + *this = clone.into_ddval(); + + Ok(()) + }; + + let fmt_debug = |this: &DDVal, f: &mut Formatter| -> Result<(), fmt::Error> { + // Safety: The caller promises to ensure that Self + // is the current type of the given value + let value = unsafe { Self::from_ddval_ref(this) }; + + Debug::fmt(value, f) + }; + + let fmt_display = |this: &DDVal, f: &mut Formatter| -> Result<(), fmt::Error> { + // Safety: The caller promises to ensure that Self + // is the current type of the given value + let value = unsafe { Self::from_ddval_ref(this) }; + + Display::fmt(&value.clone().into_record(), f) + }; + + let drop = |this: &mut DDVal| { + if Self::FITS_IN_USIZE { + // If the current type needs dropping, do it. Otherwise, we can statically elide it + if needs_drop::() { + // Allow the inner value's Drop impl to run automatically + let _val = unsafe { <*const usize>::cast::(&this.v).read() }; + } + } else { + let arc = unsafe { Arc::from_raw(this.v as *const Self) }; + mem::drop(arc); + } + }; + + let ddval_serialize: fn(&DDVal) -> &dyn erased_serde::Serialize = + |this| unsafe { Self::from_ddval_ref(this) as &dyn erased_serde::Serialize }; + + let type_id = |_this: &DDVal| -> TypeId { TypeId::of::() }; + + DDValMethods { + clone, + into_record, + eq, + partial_cmp, + cmp, + hash, + mutate, + fmt_debug, + fmt_display, + drop, + ddval_serialize, + type_id, + } + }; +} + +impl Default for DDValue { + #[inline] + fn default() -> Self { + ().into_ddvalue() + } +} diff --git a/rust/template/differential_datalog/src/ddval/ddvalue.rs b/rust/template/differential_datalog/src/ddval/ddvalue.rs index fa224c14b..373031562 100644 --- a/rust/template/differential_datalog/src/ddval/ddvalue.rs +++ b/rust/template/differential_datalog/src/ddval/ddvalue.rs @@ -1,180 +1,187 @@ -use crate::{ - ddval::{DDVal, DDValMethods}, - record::{FromRecord, IntoRecord, Mutator, Record}, -}; -use abomonation::Abomonation; -use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; -use std::{ - any::TypeId, - cmp::Ordering, - fmt::{self, Debug, Display, Formatter}, - hash::{Hash, Hasher}, -}; - -/// DDValue: this type is stored in all DD collections. -/// It consists of value and associated vtable. -pub struct DDValue { - pub(super) val: DDVal, - pub(super) vtable: &'static DDValMethods, -} - -impl Drop for DDValue { - fn drop(&mut self) { - (self.vtable.drop)(&mut self.val); - } -} - -impl DDValue { - pub fn new(val: DDVal, vtable: &'static DDValMethods) -> DDValue { - DDValue { val, vtable } - } - - pub fn into_ddval(self) -> DDVal { - let res = DDVal { v: self.val.v }; - std::mem::forget(self); - - res - } - - pub fn type_id(&self) -> TypeId { - (self.vtable.type_id)(&self.val) - } - - pub fn safe_cmp(&self, other: &DDValue) -> Ordering { - match (self.vtable.type_id)(&self.val).cmp(&(other.vtable.type_id)(&other.val)) { - Ordering::Equal => unsafe { (self.vtable.cmp)(&self.val, &other.val) }, - ord => ord, - } - } - - pub fn safe_eq(&self, other: &DDValue) -> bool { - ((self.vtable.type_id)(&self.val) == (other.vtable.type_id)(&other.val)) - && (unsafe { (self.vtable.eq)(&self.val, &other.val) }) - } -} - -impl Mutator for Record { - fn mutate(&self, x: &mut DDValue) -> Result<(), String> { - (x.vtable.mutate)(&mut x.val, self) - } -} - -impl IntoRecord for DDValue { - fn into_record(self) -> Record { - (self.vtable.into_record)(self.into_ddval()) - } -} - -impl FromRecord for DDValue { - fn from_record(_val: &Record) -> Result { - Err("'DDValue::from_record': not implemented".to_string()) - } -} - -impl Abomonation for DDValue { - unsafe fn entomb(&self, _write: &mut W) -> std::io::Result<()> { - panic!("'DDValue::entomb': not implemented") - } - - unsafe fn exhume<'a, 'b>(&'a mut self, _bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { - panic!("'DDValue::exhume': not implemented") - } - - fn extent(&self) -> usize { - panic!("'DDValue::extent': not implemented") - } -} - -/// `Serialize` implementation simply forwards the `serialize` operation to the -/// inner object. -impl Serialize for DDValue { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - erased_serde::serialize((self.vtable.ddval_serialize)(&self.val), serializer) - } -} - -/// Bogus `Deserialize` implementation, necessary to use `DDValue` type in DDlog -/// programs. We cannot provide a proper `Deserialize` implementation for `DDValue`, -/// as there is no object to forward `deserialize` to. -impl<'de> Deserialize<'de> for DDValue { - fn deserialize(_deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Err(D::Error::custom("cannot deserialize 'DDValue' type")) - } -} - -impl Display for DDValue { - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - (self.vtable.fmt_display)(&self.val, f) - } -} - -impl Debug for DDValue { - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - (self.vtable.fmt_debug)(&self.val, f) - } -} - -impl PartialOrd for DDValue { - fn partial_cmp(&self, other: &DDValue) -> Option { - /* Safety: The types of both values are the same. - * This should be enforced by the DDlog type checker. */ - debug_assert_eq!( - (self.vtable.type_id)(&self.val), - (other.vtable.type_id)(&other.val), - "DDValue::partial_cmp: attempted to compare two values of different types" - ); - unsafe { (self.vtable.partial_cmp)(&self.val, &other.val) } - } -} - -impl PartialEq for DDValue { - fn eq(&self, other: &Self) -> bool { - /* Safety: The types of both values are the same. - * This should be enforced by the DDlog type checker. */ - debug_assert_eq!( - (self.vtable.type_id)(&self.val), - (other.vtable.type_id)(&other.val), - "DDValue::eq: attempted to compare two values of different types" - ); - unsafe { (self.vtable.eq)(&self.val, &other.val) } - } -} - -impl Eq for DDValue {} - -impl Ord for DDValue { - fn cmp(&self, other: &Self) -> Ordering { - /* Safety: The types of both values are the same. - * This should be enforced by the DDlog type checker. */ - debug_assert_eq!( - (self.vtable.type_id)(&self.val), - (other.vtable.type_id)(&other.val), - "DDValue::cmp: attempted to compare two values of different types" - ); - unsafe { (self.vtable.cmp)(&self.val, &other.val) } - } -} - -impl Clone for DDValue { - fn clone(&self) -> Self { - DDValue { - val: (self.vtable.clone)(&self.val), - vtable: self.vtable, - } - } -} - -impl Hash for DDValue { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - (self.vtable.hash)(&self.val, state) - } -} +use crate::{ + ddval::{DDVal, DDValMethods}, + record::{FromRecord, IntoRecord, Mutator, Record}, +}; +use abomonation::Abomonation; +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + any::TypeId, + cmp::Ordering, + fmt::{self, Debug, Display, Formatter}, + hash::{Hash, Hasher}, +}; + +/// DDValue: this type is stored in all DD collections. +/// It consists of value and associated vtable. +pub struct DDValue { + pub(super) val: DDVal, + pub(super) vtable: &'static DDValMethods, +} + +impl Drop for DDValue { + #[inline] + fn drop(&mut self) { + unsafe { (self.vtable.drop)(&mut self.val) } + } +} + +impl DDValue { + #[inline] + pub fn new(val: DDVal, vtable: &'static DDValMethods) -> DDValue { + DDValue { val, vtable } + } + + #[inline] + pub fn into_ddval(self) -> DDVal { + let res = DDVal { v: self.val.v }; + std::mem::forget(self); + + res + } + + #[inline] + pub fn type_id(&self) -> TypeId { + (self.vtable.type_id)(&self.val) + } + + #[inline] + pub fn safe_cmp(&self, other: &DDValue) -> Ordering { + match (self.vtable.type_id)(&self.val).cmp(&(other.vtable.type_id)(&other.val)) { + Ordering::Equal => unsafe { (self.vtable.cmp)(&self.val, &other.val) }, + ord => ord, + } + } + + #[inline] + pub fn safe_eq(&self, other: &DDValue) -> bool { + ((self.vtable.type_id)(&self.val) == (other.vtable.type_id)(&other.val)) + && (unsafe { (self.vtable.eq)(&self.val, &other.val) }) + } +} + +impl Mutator for Record { + #[inline] + fn mutate(&self, x: &mut DDValue) -> Result<(), String> { + (x.vtable.mutate)(&mut x.val, self) + } +} + +impl IntoRecord for DDValue { + fn into_record(self) -> Record { + (self.vtable.into_record)(self.into_ddval()) + } +} + +impl FromRecord for DDValue { + fn from_record(_val: &Record) -> Result { + Err("'DDValue::from_record': not implemented".to_string()) + } +} + +impl Abomonation for DDValue { + unsafe fn entomb(&self, _write: &mut W) -> std::io::Result<()> { + panic!("'DDValue::entomb': not implemented") + } + + unsafe fn exhume<'a, 'b>(&'a mut self, _bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + panic!("'DDValue::exhume': not implemented") + } + + fn extent(&self) -> usize { + panic!("'DDValue::extent': not implemented") + } +} + +/// `Serialize` implementation simply forwards the `serialize` operation to the +/// inner object. +impl Serialize for DDValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + erased_serde::serialize((self.vtable.ddval_serialize)(&self.val), serializer) + } +} + +/// Bogus `Deserialize` implementation, necessary to use `DDValue` type in DDlog +/// programs. We cannot provide a proper `Deserialize` implementation for `DDValue`, +/// as there is no object to forward `deserialize` to. +impl<'de> Deserialize<'de> for DDValue { + fn deserialize(_deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Err(D::Error::custom("cannot deserialize 'DDValue' type")) + } +} + +impl Display for DDValue { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + (self.vtable.fmt_display)(&self.val, f) + } +} + +impl Debug for DDValue { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + (self.vtable.fmt_debug)(&self.val, f) + } +} + +impl PartialOrd for DDValue { + fn partial_cmp(&self, other: &DDValue) -> Option { + /* Safety: The types of both values are the same. + * This should be enforced by the DDlog type checker. */ + debug_assert_eq!( + (self.vtable.type_id)(&self.val), + (other.vtable.type_id)(&other.val), + "DDValue::partial_cmp: attempted to compare two values of different types" + ); + unsafe { (self.vtable.partial_cmp)(&self.val, &other.val) } + } +} + +impl PartialEq for DDValue { + fn eq(&self, other: &Self) -> bool { + /* Safety: The types of both values are the same. + * This should be enforced by the DDlog type checker. */ + debug_assert_eq!( + (self.vtable.type_id)(&self.val), + (other.vtable.type_id)(&other.val), + "DDValue::eq: attempted to compare two values of different types" + ); + unsafe { (self.vtable.eq)(&self.val, &other.val) } + } +} + +impl Eq for DDValue {} + +impl Ord for DDValue { + fn cmp(&self, other: &Self) -> Ordering { + /* Safety: The types of both values are the same. + * This should be enforced by the DDlog type checker. */ + debug_assert_eq!( + (self.vtable.type_id)(&self.val), + (other.vtable.type_id)(&other.val), + "DDValue::cmp: attempted to compare two values of different types" + ); + unsafe { (self.vtable.cmp)(&self.val, &other.val) } + } +} + +impl Clone for DDValue { + fn clone(&self) -> Self { + DDValue { + val: (self.vtable.clone)(&self.val), + vtable: self.vtable, + } + } +} + +impl Hash for DDValue { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + (self.vtable.hash)(&self.val, state) + } +} diff --git a/rust/template/differential_datalog/src/ddval/mod.rs b/rust/template/differential_datalog/src/ddval/mod.rs index 59a90c8d1..1587c2d21 100644 --- a/rust/template/differential_datalog/src/ddval/mod.rs +++ b/rust/template/differential_datalog/src/ddval/mod.rs @@ -1,99 +1,138 @@ -//! DDValue: Generic value type stored in all differential-dataflow relations. -//! -//! Rationale: Differential dataflow allows the user to assign an arbitrary user-defined type to -//! each collection. It relies on Rust's static dispatch mechanism to specialize its internal -//! machinery for each user-defined type. Unfortunately, beyond very simple programs this leads to -//! extremely long compilation times. One workaround that we used to rely on is to declare a -//! single enum type with a variant per concrete type used in at least one relation. This make -//! compilation feasible, but still very slow (~6 minutes for a simple DDlog program and ~10 -//! minutes for complex programs). -//! -//! Another alternative we implement here is to use a fixed value type that does not depend on -//! a concrete DDlog program and rely on dynamic dispatch to forward operations that DD expects -//! all values to implement (comparison, hashing, etc.) to their concrete implementations. This -//! way this crate (differential-datalog) can be compiled all the way to binary code separately -//! from the DDlog program using it and does not need to be re-compiled when the DDlog program -//! changes. Thus, the only part that must be re-compiled on changes to the DDlog code is the -//! auto-generated crate that declares concrete value types and rules. This is much faster than -//! re-compiling both crates together. -//! -//! The next design decision is how to implement dynamic dispatch. Rust trait objects is an -//! obvious choice, with value type being declared as `Box`. However, this proved -//! suboptimal in our experiments, as this design requires a dynamic memory allocation per value, -//! no matter how small. Furthermore, cloning a value (which DD does a lot, e.g., during -//! compaction) requires another allocation. -//! -//! We improve over this naive design in two ways. First, we use `Arc` instead of `Box`, which -//! introduces extra space overhead to store the reference count, but avoids memory allocation due -//! to cloning and shares the same heap allocation across multiple copies of the value. Second, we -//! store small objects <=`usize` bytes inline instead of wrapping them in an Arc to avoid dynamic -//! memory allocation for such objects altogether. Unfortunately, Rust's dynamic dispatch -//! mechanism does not support this, so we roll our own instead, with the following `DDValue` -//! declaration: -//! -//! ``` -//! use differential_datalog::ddval::*; -//! pub struct DDValue { -//! val: DDVal, -//! vtable: &'static DDValMethods, -//! } -//! ``` -//! -//! where `DDVal` is a `usize` that stores either an `Arc` or `T` (where `T` is the actual type -//! of value stored in the DDlog relation), and `DDValMethods` is a virtual table of methods that -//! must be implemented for all DD values. -//! -//! This design still requires a separate heap allocation for each value >8 bytes, which slows -//! things down quite a bit. Nevertheless, it has the same performance as our earlier -//! implementation using static dispatch and at least in some benchmarks uses less memory. The -//! only way to improve things further I can think of is to somehow co-design this with DD to use -//! DD's knowledge of the context where a value is being created to, e.g., allocate blocks of -//! values when possible. -//! - -#[macro_use] -mod ddval_convert; -mod any; -mod ddvalue; - -pub use any::{Any, AnyDeserializeSeed}; -pub use ddval_convert::DDValConvert; -pub use ddvalue::DDValue; - -use crate::record::Record; -use std::{ - any::TypeId, - cmp::Ordering, - fmt::{Error, Formatter}, - hash::Hasher, -}; - -/// Type-erased representation of a value. Can store the actual value or a pointer to it. -/// This could be just a `usize`, but we wrap it in a struct as we don't want it to implement -/// `Copy`. -pub struct DDVal { - pub v: usize, -} - -/// vtable of methods to be implemented by every value stored in DD. -pub struct DDValMethods { - pub clone: fn(this: &DDVal) -> DDVal, - pub into_record: fn(this: DDVal) -> Record, - - /// Safety: The types of the values contained in `this` and `other` must be the same - pub eq: unsafe fn(this: &DDVal, other: &DDVal) -> bool, - - /// Safety: The types of the values contained in `this` and `other` must be the same - pub partial_cmp: unsafe fn(this: &DDVal, other: &DDVal) -> Option, - - /// Safety: The types of the values contained in `this` and `other` must be the same - pub cmp: unsafe fn(this: &DDVal, other: &DDVal) -> Ordering, - - pub hash: fn(this: &DDVal, state: &mut dyn Hasher), - pub mutate: fn(this: &mut DDVal, record: &Record) -> Result<(), String>, - pub fmt_debug: fn(this: &DDVal, f: &mut Formatter) -> Result<(), Error>, - pub fmt_display: fn(this: &DDVal, f: &mut Formatter) -> Result<(), Error>, - pub drop: fn(this: &mut DDVal), - pub ddval_serialize: fn(this: &DDVal) -> &dyn erased_serde::Serialize, - pub type_id: fn(this: &DDVal) -> TypeId, -} +//! DDValue: Generic value type stored in all differential-dataflow relations. +//! +//! Rationale: Differential dataflow allows the user to assign an arbitrary user-defined type to +//! each collection. It relies on Rust's static dispatch mechanism to specialize its internal +//! machinery for each user-defined type. Unfortunately, beyond very simple programs this leads to +//! extremely long compilation times. One workaround that we used to rely on is to declare a +//! single enum type with a variant per concrete type used in at least one relation. This make +//! compilation feasible, but still very slow (~6 minutes for a simple DDlog program and ~10 +//! minutes for complex programs). +//! +//! Another alternative we implement here is to use a fixed value type that does not depend on +//! a concrete DDlog program and rely on dynamic dispatch to forward operations that DD expects +//! all values to implement (comparison, hashing, etc.) to their concrete implementations. This +//! way this crate (differential-datalog) can be compiled all the way to binary code separately +//! from the DDlog program using it and does not need to be re-compiled when the DDlog program +//! changes. Thus, the only part that must be re-compiled on changes to the DDlog code is the +//! auto-generated crate that declares concrete value types and rules. This is much faster than +//! re-compiling both crates together. +//! +//! The next design decision is how to implement dynamic dispatch. Rust trait objects is an +//! obvious choice, with value type being declared as `Box`. However, this proved +//! suboptimal in our experiments, as this design requires a dynamic memory allocation per value, +//! no matter how small. Furthermore, cloning a value (which DD does a lot, e.g., during +//! compaction) requires another allocation. +//! +//! We improve over this naive design in two ways. First, we use `Arc` instead of `Box`, which +//! introduces extra space overhead to store the reference count, but avoids memory allocation due +//! to cloning and shares the same heap allocation across multiple copies of the value. Second, we +//! store small objects <=`usize` bytes inline instead of wrapping them in an Arc to avoid dynamic +//! memory allocation for such objects altogether. Unfortunately, Rust's dynamic dispatch +//! mechanism does not support this, so we roll our own instead, with the following `DDValue` +//! declaration: +//! +//! ``` +//! use differential_datalog::ddval::*; +//! pub struct DDValue { +//! val: DDVal, +//! vtable: &'static DDValMethods, +//! } +//! ``` +//! +//! where `DDVal` is a `usize` that stores either an `Arc` or `T` (where `T` is the actual type +//! of value stored in the DDlog relation), and `DDValMethods` is a virtual table of methods that +//! must be implemented for all DD values. +//! +//! This design still requires a separate heap allocation for each value >8 bytes, which slows +//! things down quite a bit. Nevertheless, it has the same performance as our earlier +//! implementation using static dispatch and at least in some benchmarks uses less memory. The +//! only way to improve things further I can think of is to somehow co-design this with DD to use +//! DD's knowledge of the context where a value is being created to, e.g., allocate blocks of +//! values when possible. +//! + +#[macro_use] +mod ddval_convert; +mod any; +mod ddvalue; + +pub use any::{Any, AnyDeserializeSeed}; +pub use ddval_convert::DDValConvert; +pub use ddvalue::DDValue; + +use crate::record::Record; +use std::{ + any::TypeId, + cmp::Ordering, + fmt::{Error, Formatter}, + hash::Hasher, +}; + +/// Type-erased representation of a value. Can store the actual value or a pointer to it. +/// This could be just a `usize`, but we wrap it in a struct as we don't want it to implement +/// `Copy`. +pub struct DDVal { + pub v: usize, +} + +/// VTable of methods to be implemented by every value stored in DD. +pub struct DDValMethods { + /// Clones the current value, creating a new [`DDVal`] + pub clone: fn(this: &DDVal) -> DDVal, + /// Converts the current value into a [`Record] + pub into_record: fn(this: DDVal) -> Record, + + /// The [`Eq`] implementation for two values of the same concrete type + /// + /// # Safety + /// + /// The types of the values contained in `this` and `other` must be the same + /// + pub eq: unsafe fn(this: &DDVal, other: &DDVal) -> bool, + + /// The [`PartialOrd`] implementation for two values of the same concrete type + /// + /// # Safety + /// + /// The types of the values contained in `this` and `other` must be the same + /// + pub partial_cmp: unsafe fn(this: &DDVal, other: &DDVal) -> Option, + + /// The [`Ord`] implementation for two values of the same concrete type + /// + /// # Safety + /// + /// The types of the values contained in `this` and `other` must be the same + /// + pub cmp: unsafe fn(this: &DDVal, other: &DDVal) -> Ordering, + + /// Hashes a value to the given hasher using the concrete type's + /// [`Hash`] implementation + /// + /// Note: Hash implementations should be deterministic + /// + /// [`Hash`]: std::hash::Hash + pub hash: fn(this: &DDVal, state: &mut dyn Hasher), + + /// Mutates the current value with the given [`Record`] + pub mutate: fn(this: &mut DDVal, record: &Record) -> Result<(), String>, + + /// The concrete type's [`Debug`] implementation + /// + /// [`Debug`]: std::fmt::Debug + pub fmt_debug: fn(this: &DDVal, f: &mut Formatter) -> Result<(), Error>, + + /// Formats the current value using [`Record`]'s [`Display`] implementation + /// + /// [`Display`]: std::fmt::Display + pub fmt_display: fn(this: &DDVal, f: &mut Formatter) -> Result<(), Error>, + + /// Drops the current value + pub drop: unsafe fn(this: &mut DDVal), + + /// Serializes the current value + pub ddval_serialize: fn(this: &DDVal) -> &dyn erased_serde::Serialize, + + /// Gets the [`TypeId`] of the current value + pub type_id: fn(this: &DDVal) -> TypeId, +} diff --git a/rust/template/differential_datalog/src/lib.rs b/rust/template/differential_datalog/src/lib.rs index 38cf7703c..c60c8aa5b 100644 --- a/rust/template/differential_datalog/src/lib.rs +++ b/rust/template/differential_datalog/src/lib.rs @@ -11,6 +11,7 @@ mod ddlog; pub mod flatbuf; mod render; pub mod replay; +pub mod utils; mod valmap; mod variable; @@ -27,7 +28,7 @@ mod test_record; pub use callback::Callback; pub use ddlog::{ AnyDeserialize, AnyDeserializeFunc, D3log, D3logLocalizer, D3logLocationId, DDlog, DDlogDump, - DDlogDynamic, DDlogInventory, DDlogProfiling, + DDlogDynamic, DDlogInventory, DDlogProfiling, RelationNameMap, }; pub use replay::CommandRecorder; pub use triomphe; diff --git a/rust/template/differential_datalog/src/program/arrange.rs b/rust/template/differential_datalog/src/program/arrange.rs index d0e9894e1..3ed055766 100644 --- a/rust/template/differential_datalog/src/program/arrange.rs +++ b/rust/template/differential_datalog/src/program/arrange.rs @@ -1,221 +1,221 @@ -//! Relation arrangements and transformations - -use crate::{ - ddval::DDValue, - program::{ArrId, TKeyAgent, TKeyEnter, TValAgent, TValEnter, Weight}, -}; -use differential_dataflow::{ - difference::{Diff, Monoid}, - hashable::Hashable, - lattice::Lattice, - operators::{ - arrange::arrangement::{ArrangeBySelf, Arranged}, - Consolidate, JoinCore, Reduce, - }, - trace::{wrappers::enter::TraceEnter, BatchReader, Cursor, TraceReader}, - Collection, Data, ExchangeData, -}; -use fnv::FnvHashMap; -use std::ops::{Add, Mul, Neg}; -use timely::{ - dataflow::scopes::{Child, Scope}, - progress::{timestamp::Refines, Timestamp}, -}; - -/// An arrangement originating either in the current scope or a higher one -#[derive(Clone)] -pub enum ArrangementFlavor -where - S: Scope, - S::Timestamp: Lattice + Refines, - T: Lattice + Timestamp, -{ - /// An arrangement created within the current scope, usually created by - /// [`Arrangement::enter_region()`] or made directly from the output of - /// [`ArrangeBySelf::arrange_by_self()`] or similar methods - Local(Arrangement, TKeyAgent>), - /// An arrangement imported from a higher scope, usually created by [`Arrangement::enter()`] - Foreign(Arrangement, TKeyEnter>), -} - -/// A single [arrangement](Arranged), either arranged as a map of keys to values or as a set of keys -#[derive(Clone)] -pub enum Arrangement -where - S: Scope, - S::Timestamp: Lattice, - Map: TraceReader + Clone + 'static, - Map::Batch: BatchReader + Clone + 'static, - Map::Cursor: Cursor, - Set: TraceReader + Clone + 'static, - Set::Batch: BatchReader + Clone + 'static, - Set::Cursor: Cursor, -{ - /// An [arrangement](Arranged) of keys to associated values - Map(Arranged), - /// An [arrangement](Arranged) of keys - Set(Arranged), -} - -impl Arrangement -where - S: Scope, - S::Timestamp: Lattice + Ord, - Map: TraceReader + Clone + 'static, - Map::Batch: BatchReader + Clone + 'static, - Map::Cursor: Cursor, - Set: TraceReader + Clone + 'static, - Set::Batch: BatchReader + Clone + 'static, - Set::Cursor: Cursor, -{ - /// Brings an arranged collection out of a nested scope, see [`Arranged::enter()`] - pub fn enter<'a, TInner>( - &self, - inner: &Child<'a, S, TInner>, - ) -> Arrangement, R, TraceEnter, TraceEnter> - where - R: 'static, - TInner: Refines + Lattice + Timestamp + Clone + 'static, - { - match self { - Self::Map(arr) => Arrangement::Map(arr.enter(inner)), - Self::Set(arr) => Arrangement::Set(arr.enter(inner)), - } - } - - /// Brings an arranged collection into a nested region, see [`Arranged::enter_region()`] - pub fn enter_region<'a>( - &self, - region: &Child<'a, S, S::Timestamp>, - ) -> Arrangement, R, Map, Set> - where - R: 'static, - { - match self { - Self::Map(arr) => Arrangement::Map(arr.enter_region(region)), - Self::Set(arr) => Arrangement::Set(arr.enter_region(region)), - } - } -} - -impl<'a, S, R, Map, Set> Arrangement, R, Map, Set> -where - S: Scope, - S::Timestamp: Lattice + Ord, - Map: TraceReader + Clone + 'static, - Map::Batch: BatchReader + Clone + 'static, - Map::Cursor: Cursor, - Set: TraceReader + Clone + 'static, - Set::Batch: BatchReader + Clone + 'static, - Set::Cursor: Cursor, -{ - /// Brings an arranged collection out of a nested region, see [`Arranged::leave_region()`] - pub fn leave_region(&self) -> Arrangement { - match self { - Self::Map(arr) => Arrangement::Map(arr.leave_region()), - Self::Set(arr) => Arrangement::Set(arr.leave_region()), - } - } -} - -pub(super) struct Arrangements<'a, S, T> -where - S: Scope, - S::Timestamp: Lattice + Refines, - T: Lattice + Timestamp, -{ - pub(super) arrangements: &'a FnvHashMap>, -} - -impl<'a, S, T> Arrangements<'a, S, T> -where - S: Scope, - S::Timestamp: Lattice + Refines, - T: Lattice + Timestamp, -{ - pub(super) fn lookup_arr(&self, arrid: ArrId) -> ArrangementFlavor { - self.arrangements - .get(&arrid) - .cloned() - .unwrap_or_else(|| panic!("mk_rule: unknown arrangement {:?}", arrid)) - } -} - -// Versions of semijoin and antijoin operators that take arrangement instead of collection. -fn semijoin_arranged( - arranged: &Arranged, - other: &Arranged, -) -> Collection>::Output> -where - G: Scope, - G::Timestamp: Lattice + Ord, - T1: TraceReader + Clone + 'static, - T1::Batch: BatchReader, - T1::Cursor: Cursor, - T2: TraceReader + Clone + 'static, - T2::Batch: BatchReader, - T2::Cursor: Cursor, - K: Data + Hashable, - V: Data, - R2: Diff, - R1: Diff + Mul, - >::Output: Diff, -{ - arranged.join_core(other, |k, v, _| Some((k.clone(), v.clone()))) -} - -pub(super) fn antijoin_arranged( - arranged: &Arranged, - other: &Arranged, -) -> Collection -where - G: Scope, - G::Timestamp: Lattice + Ord, - T1: TraceReader + Clone + 'static, - T1::Batch: BatchReader, - T1::Cursor: Cursor, - T2: TraceReader + Clone + 'static, - T2::Batch: BatchReader, - T2::Cursor: Cursor, - K: Data + Hashable, - V: Data, - R2: Diff, - R1: Diff + Mul, -{ - arranged - .as_collection(|k, v| (k.clone(), v.clone())) - .concat(&semijoin_arranged(arranged, other).negate()) -} - -/// An alternative implementation of `distinct`. -/// -/// The implementation of `distinct` in differential dataflow maintains both its input and output -/// arrangements. This implementation, suggested by @frankmcsherry instead uses a single -/// arrangement that produces the number of "surplus" records, which are then subtracted from the -/// input to get an output with distinct records. This has the advantage that for keys that are -/// already distinct, there is no additional memory used in the output (nothing to subtract). It -/// has the downside that if the input changes a lot, the output may have more changes (to track -/// the input changes) than if it just recorded distinct records (which is pretty stable). -pub fn diff_distinct(collection: &Collection) -> Collection -where - G: Scope, - G::Timestamp: Lattice, - D: ExchangeData + Hashable, - R: Monoid + ExchangeData + Neg + Add + From, -{ - collection - .concat( - // For each value with weight w != 1, compute an adjustment record with the same value and - // weight (1-w) - &collection - .arrange_by_self() - .reduce(|_, src, dst| { - // If the input weight is 1, don't produce a surplus record. - if src[0].1 != R::from(1) { - dst.push(((), R::from(1) + src[0].1.clone().neg())) - } - }) - .map(|x| x.0), - ) - .consolidate() -} +//! Relation arrangements and transformations + +use crate::{ + ddval::DDValue, + program::{ArrId, TKeyAgent, TKeyEnter, TValAgent, TValEnter, Weight}, + utils::XxHashMap, +}; +use differential_dataflow::{ + difference::{Diff, Monoid}, + hashable::Hashable, + lattice::Lattice, + operators::{ + arrange::arrangement::{ArrangeBySelf, Arranged}, + Consolidate, JoinCore, Reduce, + }, + trace::{wrappers::enter::TraceEnter, BatchReader, Cursor, TraceReader}, + Collection, Data, ExchangeData, +}; +use std::ops::{Add, Mul, Neg}; +use timely::{ + dataflow::scopes::{Child, Scope}, + progress::{timestamp::Refines, Timestamp}, +}; + +/// An arrangement originating either in the current scope or a higher one +#[derive(Clone)] +pub enum ArrangementFlavor +where + S: Scope, + S::Timestamp: Lattice + Refines, + T: Lattice + Timestamp, +{ + /// An arrangement created within the current scope, usually created by + /// [`Arrangement::enter_region()`] or made directly from the output of + /// [`ArrangeBySelf::arrange_by_self()`] or similar methods + Local(Arrangement, TKeyAgent>), + /// An arrangement imported from a higher scope, usually created by [`Arrangement::enter()`] + Foreign(Arrangement, TKeyEnter>), +} + +/// A single [arrangement](Arranged), either arranged as a map of keys to values or as a set of keys +#[derive(Clone)] +pub enum Arrangement +where + S: Scope, + S::Timestamp: Lattice, + Map: TraceReader + Clone + 'static, + Map::Batch: BatchReader + Clone + 'static, + Map::Cursor: Cursor, + Set: TraceReader + Clone + 'static, + Set::Batch: BatchReader + Clone + 'static, + Set::Cursor: Cursor, +{ + /// An [arrangement](Arranged) of keys to associated values + Map(Arranged), + /// An [arrangement](Arranged) of keys + Set(Arranged), +} + +impl Arrangement +where + S: Scope, + S::Timestamp: Lattice + Ord, + Map: TraceReader + Clone + 'static, + Map::Batch: BatchReader + Clone + 'static, + Map::Cursor: Cursor, + Set: TraceReader + Clone + 'static, + Set::Batch: BatchReader + Clone + 'static, + Set::Cursor: Cursor, +{ + /// Brings an arranged collection out of a nested scope, see [`Arranged::enter()`] + pub fn enter<'a, TInner>( + &self, + inner: &Child<'a, S, TInner>, + ) -> Arrangement, R, TraceEnter, TraceEnter> + where + R: 'static, + TInner: Refines + Lattice + Timestamp + Clone + 'static, + { + match self { + Self::Map(arr) => Arrangement::Map(arr.enter(inner)), + Self::Set(arr) => Arrangement::Set(arr.enter(inner)), + } + } + + /// Brings an arranged collection into a nested region, see [`Arranged::enter_region()`] + pub fn enter_region<'a>( + &self, + region: &Child<'a, S, S::Timestamp>, + ) -> Arrangement, R, Map, Set> + where + R: 'static, + { + match self { + Self::Map(arr) => Arrangement::Map(arr.enter_region(region)), + Self::Set(arr) => Arrangement::Set(arr.enter_region(region)), + } + } +} + +impl<'a, S, R, Map, Set> Arrangement, R, Map, Set> +where + S: Scope, + S::Timestamp: Lattice + Ord, + Map: TraceReader + Clone + 'static, + Map::Batch: BatchReader + Clone + 'static, + Map::Cursor: Cursor, + Set: TraceReader + Clone + 'static, + Set::Batch: BatchReader + Clone + 'static, + Set::Cursor: Cursor, +{ + /// Brings an arranged collection out of a nested region, see [`Arranged::leave_region()`] + pub fn leave_region(&self) -> Arrangement { + match self { + Self::Map(arr) => Arrangement::Map(arr.leave_region()), + Self::Set(arr) => Arrangement::Set(arr.leave_region()), + } + } +} + +pub(super) struct Arrangements<'a, S, T> +where + S: Scope, + S::Timestamp: Lattice + Refines, + T: Lattice + Timestamp, +{ + pub(super) arrangements: &'a XxHashMap>, +} + +impl<'a, S, T> Arrangements<'a, S, T> +where + S: Scope, + S::Timestamp: Lattice + Refines, + T: Lattice + Timestamp, +{ + pub(super) fn lookup_arr(&self, arrid: ArrId) -> ArrangementFlavor { + self.arrangements + .get(&arrid) + .cloned() + .unwrap_or_else(|| panic!("mk_rule: unknown arrangement {:?}", arrid)) + } +} + +// Versions of semijoin and antijoin operators that take arrangement instead of collection. +fn semijoin_arranged( + arranged: &Arranged, + other: &Arranged, +) -> Collection>::Output> +where + G: Scope, + G::Timestamp: Lattice + Ord, + T1: TraceReader + Clone + 'static, + T1::Batch: BatchReader, + T1::Cursor: Cursor, + T2: TraceReader + Clone + 'static, + T2::Batch: BatchReader, + T2::Cursor: Cursor, + K: Data + Hashable, + V: Data, + R2: Diff, + R1: Diff + Mul, + >::Output: Diff, +{ + arranged.join_core(other, |k, v, _| Some((k.clone(), v.clone()))) +} + +pub(super) fn antijoin_arranged( + arranged: &Arranged, + other: &Arranged, +) -> Collection +where + G: Scope, + G::Timestamp: Lattice + Ord, + T1: TraceReader + Clone + 'static, + T1::Batch: BatchReader, + T1::Cursor: Cursor, + T2: TraceReader + Clone + 'static, + T2::Batch: BatchReader, + T2::Cursor: Cursor, + K: Data + Hashable, + V: Data, + R2: Diff, + R1: Diff + Mul, +{ + arranged + .as_collection(|k, v| (k.clone(), v.clone())) + .concat(&semijoin_arranged(arranged, other).negate()) +} + +/// An alternative implementation of `distinct`. +/// +/// The implementation of `distinct` in differential dataflow maintains both its input and output +/// arrangements. This implementation, suggested by @frankmcsherry instead uses a single +/// arrangement that produces the number of "surplus" records, which are then subtracted from the +/// input to get an output with distinct records. This has the advantage that for keys that are +/// already distinct, there is no additional memory used in the output (nothing to subtract). It +/// has the downside that if the input changes a lot, the output may have more changes (to track +/// the input changes) than if it just recorded distinct records (which is pretty stable). +pub fn diff_distinct(collection: &Collection) -> Collection +where + G: Scope, + G::Timestamp: Lattice, + D: ExchangeData + Hashable, + R: Monoid + ExchangeData + Neg + Add + From, +{ + collection + .concat( + // For each value with weight w != 1, compute an adjustment record with the same value and + // weight (1-w) + &collection + .arrange_by_self() + .reduce(|_, src, dst| { + // If the input weight is 1, don't produce a surplus record. + if src[0].1 != R::from(1) { + dst.push(((), R::from(1) + src[0].1.clone().neg())) + } + }) + .map(|x| x.0), + ) + .consolidate() +} diff --git a/rust/template/differential_datalog/src/program/mod.rs b/rust/template/differential_datalog/src/program/mod.rs index 58e693490..13f7b60a5 100644 --- a/rust/template/differential_datalog/src/program/mod.rs +++ b/rust/template/differential_datalog/src/program/mod.rs @@ -25,6 +25,7 @@ use crate::{ arrange_by::{ArrangeBy, ArrangementKind}, RenderContext, }, + utils::{XxHashMap, XxHashSet}, }; use abomonation_derive::Abomonation; pub use arrange::diff_distinct; @@ -38,7 +39,6 @@ use ddlog_profiler::{ with_prof_context, ArrangementDebugInfo, DDlogSourceCode, OperatorDebugInfo, ProfMsg, Profile, RuleDebugInfo, SourcePosition, }; -use fnv::{FnvHashMap, FnvHashSet}; use num::{One, Zero}; use std::{ any::Any, @@ -47,7 +47,7 @@ use std::{ collections::{hash_map, BTreeSet}, fmt::{self, Debug, Formatter}, iter::{self, Cycle, Skip}, - ops::{Add, AddAssign, Mul, Neg, Range}, + ops::{Add, AddAssign, Deref, Mul, Neg, Range}, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, @@ -273,7 +273,7 @@ pub struct Program { } type TransformerMap<'a> = - FnvHashMap, TS>, DDValue, Weight>>; + XxHashMap, TS>, DDValue, Weight>>; /// Represents a dataflow fragment implemented outside of DDlog directly in differential-dataflow. /// @@ -546,65 +546,39 @@ pub enum XFormArrangement { } impl XFormArrangement { - pub(super) fn dependencies(&self) -> FnvHashSet { + pub(super) fn dependencies(&self) -> XxHashSet { match self { - XFormArrangement::FlatMap { next, .. } => match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }, - XFormArrangement::FilterMap { next, .. } => match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }, - XFormArrangement::Aggregate { next, .. } => match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }, - XFormArrangement::Join { + Self::FlatMap { next, .. } + | Self::FilterMap { next, .. } + | Self::Aggregate { next, .. } => next + .deref() + .as_ref() + .map_or_else(Default::default, XFormCollection::dependencies), + + Self::Join { arrangement, next, .. - } => { - let mut deps = match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }; - deps.insert(Dep::Arr(*arrangement)); - deps } - XFormArrangement::Semijoin { + | Self::Semijoin { arrangement, next, .. - } => { - let mut deps = match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }; - deps.insert(Dep::Arr(*arrangement)); - deps } - XFormArrangement::Antijoin { + | Self::Antijoin { arrangement, next, .. } => { - let mut deps = match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }; - deps.insert(Dep::Arr(*arrangement)); - deps - } - XFormArrangement::StreamJoin { rel, next, .. } => { - let mut deps = match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }; - deps.insert(Dep::Rel(*rel)); - deps - } - XFormArrangement::StreamSemijoin { rel, next, .. } => { - let mut deps = match **next { - None => FnvHashSet::default(), - Some(ref n) => n.dependencies(), - }; - deps.insert(Dep::Rel(*rel)); - deps + let mut dependencies = next + .deref() + .as_ref() + .map_or_else(Default::default, XFormCollection::dependencies); + dependencies.insert(Dep::Arr(*arrangement)); + dependencies + } + + Self::StreamJoin { rel, next, .. } | Self::StreamSemijoin { rel, next, .. } => { + let mut dependencies = next + .deref() + .as_ref() + .map_or_else(Default::default, XFormCollection::dependencies); + dependencies.insert(Dep::Rel(*rel)); + dependencies } } } @@ -715,38 +689,38 @@ pub enum XFormCollection { } impl XFormCollection { - pub fn dependencies(&self) -> FnvHashSet { + pub fn dependencies(&self) -> XxHashSet { match self { XFormCollection::Arrange { next, .. } => next.dependencies(), XFormCollection::Differentiate { next, .. } => match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }, XFormCollection::Map { next, .. } => match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }, XFormCollection::FlatMap { next, .. } => match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }, XFormCollection::Filter { next, .. } => match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }, XFormCollection::FilterMap { next, .. } => match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }, XFormCollection::Inspect { next, .. } => match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }, XFormCollection::StreamJoin { arrangement, next, .. } => { let mut deps = match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }; deps.insert(Dep::Arr(*arrangement)); @@ -756,7 +730,7 @@ impl XFormCollection { arrangement, next, .. } => { let mut deps = match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }; deps.insert(Dep::Arr(*arrangement)); @@ -764,11 +738,11 @@ impl XFormCollection { } XFormCollection::StreamXForm { xform, next, .. } => { let deps1 = match **xform { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref x) => x.dependencies(), }; let deps2 = match **next { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref n) => n.dependencies(), }; deps1.union(&deps2).cloned().collect() @@ -794,11 +768,11 @@ pub enum Rule { } impl Rule { - fn dependencies(&self) -> FnvHashSet { + fn dependencies(&self) -> XxHashSet { match self { Rule::CollectionRule { rel, xform, .. } => { let mut deps = match xform { - None => FnvHashSet::default(), + None => XxHashSet::default(), Some(ref x) => x.dependencies(), }; deps.insert(Dep::Rel(*rel)); @@ -935,16 +909,16 @@ impl Arrangement { } /// Set relation content. -pub type ValSet = FnvHashSet; +pub type ValSet = XxHashSet; /// Multiset relation content. pub type ValMSet = DeltaSet; /// Indexed relation content. -pub type IndexedValSet = FnvHashMap; +pub type IndexedValSet = XxHashMap; /// Relation delta -pub type DeltaSet = FnvHashMap; +pub type DeltaSet = XxHashMap; /// Runtime representation of a datalog program. /// @@ -961,7 +935,7 @@ pub struct RunningProgram { /// deadlocks when one of the workers has died, but `recv` blocks instead /// of failing, since the channel is still considered alive. reply_recv: Vec>, - relations: FnvHashMap, + relations: XxHashMap, worker_guards: Option>>, transaction_in_progress: bool, need_to_flush: bool, @@ -989,7 +963,7 @@ impl Debug for RunningProgram { .field("reply_recv", &self.reply_recv) .field( "relations", - &(&self.relations as *const FnvHashMap), + &(&self.relations as *const XxHashMap), ) .field("transaction_in_progress", &self.transaction_in_progress) .field("need_to_flush", &self.need_to_flush) @@ -1151,7 +1125,7 @@ impl Program { ) .map_err(|err| format!("Failed to start timely computation: {:?}", err))?; - let mut rels = FnvHashMap::default(); + let mut rels = XxHashMap::default(); for relid in self.input_relations() { let rel = self.get_relation(relid); if rel.input { @@ -1160,7 +1134,7 @@ impl Program { rels.insert( relid, RelationInstance::Stream { - delta: FnvHashMap::default(), + delta: XxHashMap::default(), }, ); } @@ -1168,8 +1142,8 @@ impl Program { rels.insert( relid, RelationInstance::Multiset { - elements: FnvHashMap::default(), - delta: FnvHashMap::default(), + elements: XxHashMap::default(), + delta: XxHashMap::default(), }, ); } @@ -1178,8 +1152,8 @@ impl Program { rels.insert( relid, RelationInstance::Flat { - elements: FnvHashSet::default(), - delta: FnvHashMap::default(), + elements: XxHashSet::default(), + delta: XxHashMap::default(), }, ); } @@ -1188,8 +1162,8 @@ impl Program { relid, RelationInstance::Indexed { key_func: f, - elements: FnvHashMap::default(), - delta: FnvHashMap::default(), + elements: XxHashMap::default(), + delta: XxHashMap::default(), }, ); } @@ -1306,11 +1280,11 @@ impl Program { } /// Return all relations required to compute rels, excluding recursive dependencies on rels - fn dependencies<'a, R>(rels: R) -> FnvHashSet + fn dependencies<'a, R>(rels: R) -> XxHashSet where R: Iterator + Clone + 'a, { - let mut result = FnvHashSet::default(); + let mut result = XxHashSet::default(); for rel in rels.clone() { for rule in &rel.rules { result = result.union(&rule.dependencies()).cloned().collect(); @@ -1525,7 +1499,7 @@ impl Program { d_col, &*xform, &Arrangements { - arrangements: &FnvHashMap::default(), + arrangements: &XxHashMap::default(), }, dummy_lookup_collection, ); diff --git a/rust/template/differential_datalog/src/program/worker.rs b/rust/template/differential_datalog/src/program/worker.rs index 5039513f8..3bbc16036 100644 --- a/rust/template/differential_datalog/src/program/worker.rs +++ b/rust/template/differential_datalog/src/program/worker.rs @@ -7,6 +7,7 @@ use crate::{ Reply, TKeyAgent, TValAgent, Update, Weight, TS, }, render::RenderContext, + utils::XxHashMap, variable::Variable, }; use crossbeam_channel::{Receiver, Sender}; @@ -30,7 +31,6 @@ use differential_dataflow::{ Collection, }; use dogsdogsdogs::operators::lookup_map; -use fnv::{FnvBuildHasher, FnvHashMap}; use num::{one, zero, One, Zero}; use std::{ borrow::Cow, @@ -57,7 +57,7 @@ use triomphe::Arc as ThinArc; // Handles to objects involved in managing the progress of the dataflow. struct SessionData { // Input sessions for program relations. - sessions: FnvHashMap>, + sessions: XxHashMap>, // Input session for the special `Enabled` relation (see detailed comment in // `session_dataflow()`). enabled_session: InputSession, @@ -76,7 +76,7 @@ struct SessionData { >, } -type DelayedVarMap = FnvHashMap< +type DelayedVarMap = XxHashMap< RelId, ( DelayedRelation, @@ -217,7 +217,7 @@ impl<'a> DDlogWorker<'a> { /// Applies updates to the current worker's input sessions fn batch_update( &self, - sessions: &mut FnvHashMap>, + sessions: &mut XxHashMap>, updates: Vec>, timestamp: TS, ) -> Result<(), String> { @@ -579,19 +579,16 @@ impl<'a> DDlogWorker<'a> { self.worker.dataflow::( |outer: &mut Child, TS>| -> Result<_, String> { - let mut sessions: FnvHashMap> = - FnvHashMap::default(); - let mut collections: FnvHashMap< + let mut sessions: XxHashMap> = + XxHashMap::default(); + let mut collections: XxHashMap< RelId, Collection, TS>, DDValue, Weight>, - > = HashMap::with_capacity_and_hasher( - program.nodes.len(), - FnvBuildHasher::default(), - ); - let mut arrangements: FnvHashMap< + > = HashMap::with_capacity_and_hasher(program.nodes.len(), Default::default()); + let mut arrangements: XxHashMap< ArrId, Arrangement<_, Weight, TValAgent, TKeyAgent>, - > = FnvHashMap::default(); + > = XxHashMap::default(); // Create an `Enabled` relation used to enforce the dataflow termination in the // presence of delayed relations. A delayed relation can potentially generate an @@ -785,9 +782,9 @@ fn render_relation( scope: &mut S, program: &Program, render_context: &RenderContext, - sessions: &mut FnvHashMap>, - collections: &mut FnvHashMap>, - arrangements: &mut FnvHashMap, TKeyAgent>>, + sessions: &mut XxHashMap>, + collections: &mut XxHashMap>, + arrangements: &mut XxHashMap, TKeyAgent>>, delayed_vars: &DelayedVarMap, ) where S: Scope, @@ -806,7 +803,7 @@ fn render_relation( collection }; - let entered_arrangements: FnvHashMap<_, ArrangementFlavor<_, TS>> = arrangements + let entered_arrangements: XxHashMap<_, ArrangementFlavor<_, TS>> = arrangements .iter() .map(|(&arr_id, arr)| (arr_id, ArrangementFlavor::Local(arr.clone()))) .collect(); @@ -890,12 +887,12 @@ fn render_scc<'a>( scope: &mut Child<'a, Worker, TS>, program: &Program, render_context: &RenderContext, - sessions: &mut FnvHashMap>, - collections: &mut FnvHashMap< + sessions: &mut XxHashMap>, + collections: &mut XxHashMap< RelId, Collection, TS>, DDValue, Weight>, >, - arrangements: &mut FnvHashMap< + arrangements: &mut XxHashMap< ArrId, Arrangement, TS>, Weight, TValAgent, TKeyAgent>, >, @@ -931,11 +928,11 @@ fn render_scc<'a>( scope.scoped("recursive component", |inner| -> Result<_, String> { // create variables for relations defined in the Scc. let mut vars = - HashMap::with_capacity_and_hasher(rels.len(), FnvBuildHasher::default()); + XxHashMap::with_capacity_and_hasher(rels.len(), Default::default()); // arrangements created inside the nested scope - let mut local_arrangements = FnvHashMap::default(); + let mut local_arrangements = XxHashMap::default(); // arrangements entered from global scope - let mut inner_arrangements = FnvHashMap::default(); + let mut inner_arrangements = XxHashMap::default(); for r in rels.iter() { let rel = collections.get(&r.rel.id).ok_or_else(|| { @@ -986,10 +983,8 @@ fn render_scc<'a>( Program::dependencies(rels.iter().map(|relation| &relation.rel)); // collections entered from global scope - let mut inner_collections = HashMap::with_capacity_and_hasher( - dependencies.len(), - FnvBuildHasher::default(), - ); + let mut inner_collections = + XxHashMap::with_capacity_and_hasher(dependencies.len(), Default::default()); for dep in dependencies { match dep { @@ -1062,7 +1057,7 @@ fn render_scc<'a>( // bring new relations back to the outer scope let mut new_collections = - HashMap::with_capacity_and_hasher(rels.len(), FnvBuildHasher::default()); + XxHashMap::with_capacity_and_hasher(rels.len(), Default::default()); for rel in rels { let var = vars.get(&rel.rel.id).ok_or_else(|| { format!("no variable found for relation ID {}", rel.rel.id) diff --git a/rust/template/differential_datalog/src/replay.rs b/rust/template/differential_datalog/src/replay.rs index 68f295ec4..d248c2cb5 100644 --- a/rust/template/differential_datalog/src/replay.rs +++ b/rust/template/differential_datalog/src/replay.rs @@ -416,12 +416,11 @@ where #[cfg(test)] mod tests { - use fnv::FnvHashMap; - + use super::*; use crate::program::ArrId; + use std::{any::TypeId, collections::HashMap, hash::BuildHasherDefault}; + use xxhash_rust::xxh3::Xxh3; - use super::*; - use std::any::TypeId; #[cfg(feature = "c_api")] use std::ffi::CStr; @@ -464,7 +463,9 @@ mod tests { unimplemented!() } - fn input_relation_ids(&self) -> &'static FnvHashMap { + fn input_relation_ids( + &self, + ) -> &'static HashMap> { unimplemented!() } diff --git a/rust/template/differential_datalog/src/utils.rs b/rust/template/differential_datalog/src/utils.rs new file mode 100644 index 000000000..74abadb4a --- /dev/null +++ b/rust/template/differential_datalog/src/utils.rs @@ -0,0 +1,16 @@ +//! Various utilities + +use std::{ + collections::{HashMap, HashSet}, + hash::BuildHasherDefault, +}; +use xxhash_rust::xxh3::Xxh3; + +/// The default [`Xxh3`] hasher +pub type Xxh3Hasher = BuildHasherDefault; + +/// A [`HashMap`] backed by the [`Xxh3`] hashing algorithm +pub type XxHashMap = HashMap; + +/// A [`HashSet`] backed by the [`Xxh3`] hashing algorithm +pub type XxHashSet = HashSet; diff --git a/rust/template/differential_datalog_test/Cargo.toml b/rust/template/differential_datalog_test/Cargo.toml index cfd46584e..e0ff18688 100644 --- a/rust/template/differential_datalog_test/Cargo.toml +++ b/rust/template/differential_datalog_test/Cargo.toml @@ -8,16 +8,16 @@ name = "differential_datalog_test" path = "lib.rs" [dependencies] -fnv = "1.0.7" +num = "0.4.0" abomonation = "0.7.3" -serde_json = "1.0.60" -erased-serde = "0.3.13" +serde_json = "1.0.69" +erased-serde = "0.3.16" abomonation_derive = "0.5.0" -datalog_example = { path = "../", default-features = false } ddlog_derive = { path = "../ddlog_derive" } -num = { version = "0.3" } -serde = { version = "1.0.125", features = ["derive"] } -differential_datalog = { path = "../differential_datalog" } ddlog_profiler = { path = "../ddlog_profiler" } +serde = { version = "1.0.130", features = ["derive"] } +xxhash-rust = { version = "0.8.2", features = ["xxh3"] } +differential_datalog = { path = "../differential_datalog" } +datalog_example = { path = "../", default-features = false } timely = { git = "https://github.com/ddlog-dev/timely-dataflow", branch = "ddlog-4", default-features = false } differential-dataflow = { git = "https://github.com/ddlog-dev/differential-dataflow", branch = "ddlog-4", default-features = false } diff --git a/rust/template/differential_datalog_test/lib.rs b/rust/template/differential_datalog_test/lib.rs index 9ee21acb7..c6824e792 100644 --- a/rust/template/differential_datalog_test/lib.rs +++ b/rust/template/differential_datalog_test/lib.rs @@ -6,23 +6,23 @@ //! factored in a separate crate. #![cfg_attr(not(test), allow(dead_code))] -use std::borrow::Cow; -use std::collections::btree_map::{BTreeMap, Entry}; -use std::collections::btree_set::BTreeSet; -use std::sync::{Arc, Mutex}; +use std::{ + borrow::Cow, + collections::{ + btree_map::{BTreeMap, Entry}, + btree_set::BTreeSet, + }, + sync::{Arc, Mutex}, +}; use ddlog_profiler::{ ArrangementDebugInfo, DDlogSourceCode, OperatorDebugInfo, RuleDebugInfo, SourcePosition, }; -use differential_datalog::{ddval::*, program::config::Config, program::*}; -use fnv::FnvHashMap; +use differential_datalog::{ddval::*, program::config::Config, program::*, utils::XxHashMap}; use num::One; -use timely::communication::Allocator; -use timely::dataflow::scopes::*; -use timely::worker::Worker; +use timely::{communication::Allocator, dataflow::scopes::*, worker::Worker}; -use differential_dataflow::operators::Join; -use differential_dataflow::Collection; +use differential_dataflow::{operators::Join, Collection}; pub mod test_value; use test_value::*; @@ -667,7 +667,7 @@ fn test_join(nthreads: usize) { }; type CollectionMap<'a> = - FnvHashMap, TS>, DDValue, Weight>>; + XxHashMap, TS>, DDValue, Weight>>; fn join_transformer() -> Box Fn(&mut CollectionMap<'a>)> { Box::new(|collections| { @@ -1997,7 +1997,7 @@ fn conversion_lossless() { let val = boolean.clone().into_ddvalue(); assert_eq!(Some(&boolean), Bool::try_from_ddvalue_ref(&val)); - assert_eq!(Some(boolean.clone()), Bool::try_from_ddvalue(val.clone())); + assert_eq!(Ok(boolean.clone()), Bool::try_from_ddvalue(val.clone())); assert_eq!(&boolean, Bool::from_ddvalue_ref(&val)); assert_eq!(boolean, Bool::from_ddvalue(val)); } @@ -2007,7 +2007,7 @@ fn checked_conversions() { let val = Bool(true).into_ddvalue(); assert!(Empty::try_from_ddvalue_ref(&val).is_none()); - assert!(Empty::try_from_ddvalue(val).is_none()); + assert!(Empty::try_from_ddvalue(val).is_err()); } #[test] diff --git a/rust/template/ovsdb/Cargo.toml b/rust/template/ovsdb/Cargo.toml index 743cc8ef0..aa3fb072d 100644 --- a/rust/template/ovsdb/Cargo.toml +++ b/rust/template/ovsdb/Cargo.toml @@ -1,19 +1,17 @@ -[package] -name = "ddlog_ovsdb_adapter" -version = "0.1.0" -edition = "2018" - -[dependencies.differential_datalog] -path = "../differential_datalog" - -[dependencies] -num = "0.3" -serde_json = "1.0" - -[dependencies.uuid] -version = "0.8" - -[lib] -name = "ddlog_ovsdb_adapter" -path = "lib.rs" -crate-type = ["staticlib", "rlib"] +[package] +name = "ddlog_ovsdb_adapter" +version = "0.1.0" +edition = "2018" + +[lib] +name = "ddlog_ovsdb_adapter" +path = "lib.rs" +crate-type = ["staticlib", "rlib"] + +[dependencies] +num = "0.4.0" +uuid = "0.8.2" +serde_json = "1.0.69" + + [dependencies.differential_datalog] + path = "../differential_datalog" diff --git a/rust/template/src/inventory.rs b/rust/template/src/inventory.rs index eaaf4ca2d..1cd966d20 100644 --- a/rust/template/src/inventory.rs +++ b/rust/template/src/inventory.rs @@ -1,117 +1,117 @@ -use crate::{ - d3log_localize_val, idxkey_from_record, indexes2arrid, indexid2name, rel_name2orig_name, - relid2name, relkey_from_record, relval_from_record, Indexes, Relations, - RAW_INPUT_RELATION_ID_MAP, -}; -#[cfg(feature = "c_api")] -use crate::{indexid2cname, rel_name2orig_cname, relid2cname}; -use differential_datalog::{ - ddval::DDValue, - program::{ArrId, IdxId, RelId}, - record::{Record, RelIdentifier}, - D3logLocalizer, D3logLocationId, DDlogInventory, -}; -use fnv::FnvHashMap; -#[cfg(feature = "c_api")] -use std::ffi::CStr; -use std::{any::TypeId, convert::TryFrom}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Inventory; - -impl DDlogInventory for Inventory { - fn get_table_id(&self, table_name: &str) -> Result { - Relations::try_from(table_name).map_or_else( - |()| Err(format!("unknown relation {}", table_name)), - |rel| Ok(rel as RelId), - ) - } - - fn get_table_name(&self, table_id: RelId) -> Result<&'static str, String> { - relid2name(table_id).ok_or_else(|| format!("unknown relation {}", table_id)) - } - - fn get_table_original_name(&self, table_name: &str) -> Result<&'static str, String> { - rel_name2orig_name(table_name).ok_or_else(|| format!("unknown relation {}", table_name)) - } - - #[cfg(feature = "c_api")] - fn get_table_original_cname(&self, table_name: &str) -> Result<&'static CStr, String> { - rel_name2orig_cname(table_name).ok_or_else(|| format!("unknown relation {}", table_name)) - } - - #[cfg(feature = "c_api")] - fn get_table_cname(&self, table_id: RelId) -> Result<&'static CStr, String> { - relid2cname(table_id).ok_or_else(|| format!("unknown relation {}", table_id)) - } - - fn get_index_id(&self, index_name: &str) -> Result { - Indexes::try_from(index_name).map_or_else( - |()| Err(format!("unknown index {}", index_name)), - |idx| Ok(idx as IdxId), - ) - } - - fn get_index_name(&self, index_id: IdxId) -> Result<&'static str, String> { - indexid2name(index_id).ok_or_else(|| format!("unknown index {}", index_id)) - } - - #[cfg(feature = "c_api")] - fn get_index_cname(&self, index_id: IdxId) -> Result<&'static CStr, String> { - indexid2cname(index_id).ok_or_else(|| format!("unknown index {}", index_id)) - } - - fn input_relation_ids(&self) -> &'static FnvHashMap { - &*RAW_INPUT_RELATION_ID_MAP - } - - fn index_from_record(&self, index_id: IdxId, key: &Record) -> Result { - let index = - Indexes::try_from(index_id).map_err(|_| format!("Unknown index {}", index_id))?; - idxkey_from_record(index, key) - } - - fn relation_type_id(&self, relation: RelId) -> Option { - Relations::try_from(relation) - .map(|relation| relation.type_id()) - .ok() - } - - fn relation_value_from_record( - &self, - relation: &RelIdentifier, - record: &Record, - ) -> Result<(RelId, DDValue), String> { - let relation = Relations::try_from(relation) - .map_err(|_| format!("Unknown relation {:?}", relation))?; - relval_from_record(relation, record).map(|value| (relation as RelId, value)) - } - - fn relation_key_from_record( - &self, - relation: &RelIdentifier, - record: &Record, - ) -> Result<(RelId, DDValue), String> { - let relation = Relations::try_from(relation) - .map_err(|_| format!("Unknown relation {:?}", relation))?; - relkey_from_record(relation, record).map(|key| (relation as RelId, key)) - } - - fn index_to_arrangement_id(&self, index: IdxId) -> Option { - let index = Indexes::try_from(index).ok()?; - Some(indexes2arrid(index)) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct D3logInventory; - -impl D3logLocalizer for D3logInventory { - fn localize_value( - &self, - relation: RelId, - value: DDValue, - ) -> Result<(Option, RelId, DDValue), DDValue> { - d3log_localize_val(relation, value) - } -} +use crate::{ + d3log_localize_val, idxkey_from_record, indexes2arrid, indexid2name, rel_name2orig_name, + relid2name, relkey_from_record, relval_from_record, Indexes, Relations, + RAW_INPUT_RELATION_ID_MAP, +}; +#[cfg(feature = "c_api")] +use crate::{indexid2cname, rel_name2orig_cname, relid2cname}; +use differential_datalog::{ + ddval::DDValue, + program::{ArrId, IdxId, RelId}, + record::{Record, RelIdentifier}, + D3logLocalizer, D3logLocationId, DDlogInventory, RelationNameMap, +}; +#[cfg(feature = "c_api")] +use std::ffi::CStr; +use std::{any::TypeId, collections::HashMap, convert::TryFrom, hash::BuildHasherDefault}; +use xxhash_rust::xxh3::Xxh3; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Inventory; + +impl DDlogInventory for Inventory { + fn get_table_id(&self, table_name: &str) -> Result { + Relations::try_from(table_name).map_or_else( + |()| Err(format!("unknown relation {}", table_name)), + |rel| Ok(rel as RelId), + ) + } + + fn get_table_name(&self, table_id: RelId) -> Result<&'static str, String> { + relid2name(table_id).ok_or_else(|| format!("unknown relation {}", table_id)) + } + + fn get_table_original_name(&self, table_name: &str) -> Result<&'static str, String> { + rel_name2orig_name(table_name).ok_or_else(|| format!("unknown relation {}", table_name)) + } + + #[cfg(feature = "c_api")] + fn get_table_original_cname(&self, table_name: &str) -> Result<&'static CStr, String> { + rel_name2orig_cname(table_name).ok_or_else(|| format!("unknown relation {}", table_name)) + } + + #[cfg(feature = "c_api")] + fn get_table_cname(&self, table_id: RelId) -> Result<&'static CStr, String> { + relid2cname(table_id).ok_or_else(|| format!("unknown relation {}", table_id)) + } + + fn get_index_id(&self, index_name: &str) -> Result { + Indexes::try_from(index_name).map_or_else( + |()| Err(format!("unknown index {}", index_name)), + |idx| Ok(idx as IdxId), + ) + } + + fn get_index_name(&self, index_id: IdxId) -> Result<&'static str, String> { + indexid2name(index_id).ok_or_else(|| format!("unknown index {}", index_id)) + } + + #[cfg(feature = "c_api")] + fn get_index_cname(&self, index_id: IdxId) -> Result<&'static CStr, String> { + indexid2cname(index_id).ok_or_else(|| format!("unknown index {}", index_id)) + } + + fn input_relation_ids(&self) -> &'static RelationNameMap { + &*RAW_INPUT_RELATION_ID_MAP + } + + fn index_from_record(&self, index_id: IdxId, key: &Record) -> Result { + let index = + Indexes::try_from(index_id).map_err(|_| format!("Unknown index {}", index_id))?; + idxkey_from_record(index, key) + } + + fn relation_type_id(&self, relation: RelId) -> Option { + Relations::try_from(relation) + .map(|relation| relation.type_id()) + .ok() + } + + fn relation_value_from_record( + &self, + relation: &RelIdentifier, + record: &Record, + ) -> Result<(RelId, DDValue), String> { + let relation = Relations::try_from(relation) + .map_err(|_| format!("Unknown relation {:?}", relation))?; + relval_from_record(relation, record).map(|value| (relation as RelId, value)) + } + + fn relation_key_from_record( + &self, + relation: &RelIdentifier, + record: &Record, + ) -> Result<(RelId, DDValue), String> { + let relation = Relations::try_from(relation) + .map_err(|_| format!("Unknown relation {:?}", relation))?; + relkey_from_record(relation, record).map(|key| (relation as RelId, key)) + } + + fn index_to_arrangement_id(&self, index: IdxId) -> Option { + let index = Indexes::try_from(index).ok()?; + Some(indexes2arrid(index)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct D3logInventory; + +impl D3logLocalizer for D3logInventory { + fn localize_value( + &self, + relation: RelId, + value: DDValue, + ) -> Result<(Option, RelId, DDValue), DDValue> { + d3log_localize_val(relation, value) + } +} diff --git a/rust/template/src/lib.rs b/rust/template/src/lib.rs index a138d83cd..ac6a64f54 100644 --- a/rust/template/src/lib.rs +++ b/rust/template/src/lib.rs @@ -51,14 +51,10 @@ use num_traits::cast::FromPrimitive; use num_traits::identities::One; use once_cell::sync::Lazy; -use fnv::{FnvBuildHasher, FnvHashMap}; use phf::phf_map; +use xxhash_rust::xxh3::Xxh3; -use serde::ser::SerializeTuple; -use serde::Deserialize; -use serde::Deserializer; -use serde::Serialize; -use serde::Serializer; +use serde::{ser::SerializeTuple, Deserialize, Deserializer, Serialize, Serializer}; // This import is only needed to convince the OS X compiler to export // `extern C` functions declared in ddlog_log.rs in the generated lib. @@ -131,19 +127,41 @@ macro_rules! impl_trait_d3log { }; ( $(($out_rel:expr, $in_rel:expr, $typ:ty)),+ ) => { - pub static D3LOG_CONVERTER_MAP: ::once_cell::sync::Lazy<::std::collections::HashMapResult<(Option, program::RelId, DDValue), DDValue>>> = ::once_cell::sync::Lazy::new(|| { - let mut m = ::std::collections::HashMap::new(); - $( - m.insert($out_rel, { fn __f(val: DDValue) -> Result<(Option, program::RelId, DDValue), DDValue> { - if let Some(::ddlog_std::tuple2(loc_id, inner_val)) = <::ddlog_std::tuple2, $typ>>::try_from_ddvalue_ref(&val) { - Ok((::ddlog_std::std2option(*loc_id), $in_rel, (*inner_val).clone().into_ddvalue())) - } else { - Err(val) - } - } __f as fn(DDValue)->Result<(Option, program::RelId, DDValue), DDValue>}); - )* - m + pub static D3LOG_CONVERTER_MAP: ::once_cell::sync::Lazy< + ::std::collections::HashMap< + program::RelId, + fn(DDValue) -> ::std::result::Result<(Option, program::RelId, DDValue), DDValue> + ::std::hash::BuildHasherDefault<::xxhash_rust::xxh3::Xxh3>, + > + > = ::once_cell::sync::Lazy::new(|| { + use ::differential_datalog::ddval::DDValConvert; + + // Sums up all of the given relations so that we can pre-allocate the map + const TOTAL_RELATIONS: usize = [(::std::stringify!($out_rel),)+].len(); + + let mut conversions = ::std::collections::HashMap::with_capacity_and_hasher( + TOTAL_RELATIONS, + ::std::default::Default::default(), + ); + + $({ + fn convert(value: DDValue) -> Result<(Option, program::RelId, DDValue), DDValue> { + let ::ddlog_std::tuple2(loc_id, inner_val) = + <::ddlog_std::tuple2<::ddlog_std::Option, $typ>>::try_from_ddvalue(value)?; + + Ok(( + ::ddlog_std::Option::from(*loc_id), + $in_rel, + (*inner_val).clone().into_ddvalue(), + )) + } + + conversions.insert($out_rel, convert as _); + })* + + conversions }); + fn d3log_localize_val(relid: program::RelId, val: DDValue) -> Result<(Option, program::RelId, DDValue), DDValue> { if let Some(f) = D3LOG_CONVERTER_MAP.get(&relid) { f(val) @@ -155,11 +173,15 @@ macro_rules! impl_trait_d3log { } static RAW_RELATION_ID_MAP: ::once_cell::sync::Lazy< - ::fnv::FnvHashMap<::differential_datalog::program::RelId, &'static ::core::primitive::str>, + ::std::collections::HashMap< + ::differential_datalog::program::RelId, + &'static ::core::primitive::str, + ::std::hash::BuildHasherDefault, + >, > = ::once_cell::sync::Lazy::new(|| { - let mut map = ::fnv::FnvHashMap::with_capacity_and_hasher( + let mut map = ::std::collections::HashMap::with_capacity_and_hasher( crate::RELIDMAP.len(), - ::fnv::FnvBuildHasher::default(), + ::std::default::Default::default(), ); for (&relation, &name) in crate::RELIDMAP.iter() { @@ -170,11 +192,15 @@ static RAW_RELATION_ID_MAP: ::once_cell::sync::Lazy< }); static RAW_INPUT_RELATION_ID_MAP: ::once_cell::sync::Lazy< - ::fnv::FnvHashMap<::differential_datalog::program::RelId, &'static ::core::primitive::str>, + ::std::collections::HashMap< + ::differential_datalog::program::RelId, + &'static ::core::primitive::str, + ::std::hash::BuildHasherDefault, + >, > = ::once_cell::sync::Lazy::new(|| { - let mut map = ::fnv::FnvHashMap::with_capacity_and_hasher( + let mut map = ::std::collections::HashMap::with_capacity_and_hasher( crate::INPUT_RELIDMAP.len(), - ::fnv::FnvBuildHasher::default(), + ::std::default::Default::default(), ); for (&relation, &name) in crate::INPUT_RELIDMAP.iter() { @@ -319,6 +345,7 @@ pub unsafe extern "C" fn ddlog_run_with_config( // Note: This is `triomphe::Arc`, *not* `std::sync::Arc` Arc::into_raw(Arc::new(hddlog)) } + Err(err) => { HDDlog::print_err(print_err, &format!("HDDlog::new() failed: {}", err)); ptr::null() @@ -338,6 +365,7 @@ pub unsafe extern "C" fn ddlog_run( num_timely_workers: workers, ..Default::default() }; + ddlog_run_with_config(&config, do_store, print_err, init_state) } @@ -462,11 +490,15 @@ pub fn rel_name2orig_cname(_tname: &str) -> Option<&'static ::std::ffi::CStr> { ::std::panic!("rel_name2orig_cname not implemented") } -pub static RELIDMAP: Lazy> = Lazy::new(FnvHashMap::default); -pub static INPUT_RELIDMAP: Lazy> = - Lazy::new(FnvHashMap::default); -pub static OUTPUT_RELIDMAP: Lazy> = - Lazy::new(FnvHashMap::default); +pub static RELIDMAP: Lazy< + ::std::collections::HashMap>, +> = Lazy::new(|| ::std::collections::HashMap::with_hasher(::std::default::Default::default())); +pub static INPUT_RELIDMAP: Lazy< + ::std::collections::HashMap>, +> = Lazy::new(|| ::std::collections::HashMap::with_hasher(::std::default::Default::default())); +pub static OUTPUT_RELIDMAP: Lazy< + ::std::collections::HashMap>, +> = Lazy::new(|| ::std::collections::HashMap::with_hasher(::std::default::Default::default())); pub fn indexid2name(_iid: program::IdxId) -> Option<&'static str> { ::std::panic!("indexid2name not implemented") @@ -480,7 +512,9 @@ pub fn indexes2arrid(idx: Indexes) -> program::ArrId { ::std::panic!("indexes2arrid not implemented") } -pub static IDXIDMAP: Lazy> = Lazy::new(FnvHashMap::default); +pub static IDXIDMAP: Lazy< + ::std::collections::HashMap>, +> = Lazy::new(|| ::std::collections::HashMap::with_hasher(::std::default::Default::default())); impl_trait_d3log!(); diff --git a/rust/template/types/lib.rs b/rust/template/types/lib.rs index d045d0044..62e27fe89 100644 --- a/rust/template/types/lib.rs +++ b/rust/template/types/lib.rs @@ -1,77 +1,75 @@ -#![allow( - path_statements, - unused_imports, - non_snake_case, - non_camel_case_types, - non_upper_case_globals, - unused_parens, - non_shorthand_field_patterns, - dead_code, - overflowing_literals, - unreachable_patterns, - unused_variables, - clippy::missing_safety_doc, - clippy::match_single_binding, - clippy::ptr_arg, - clippy::redundant_closure, - clippy::needless_lifetimes, - clippy::borrowed_box, - clippy::map_clone, - clippy::toplevel_ref_arg, - clippy::double_parens, - clippy::collapsible_if, - clippy::clone_on_copy, - clippy::unused_unit, - clippy::deref_addrof, - clippy::clone_on_copy, - clippy::needless_return, - clippy::op_ref, - clippy::match_like_matches_macro, - clippy::comparison_chain, - clippy::len_zero, - clippy::extra_unused_lifetimes -)] - -use ::num::One; -use ::std::ops::Deref; - -use ::differential_dataflow::collection; -use ::timely::communication; -use ::timely::dataflow::scopes; -use ::timely::worker; - -use ::ddlog_derive::{FromRecord, IntoRecord, Mutator}; -use ::differential_datalog::ddval::DDValConvert; -use ::differential_datalog::program; -use ::differential_datalog::program::TupleTS; -use ::differential_datalog::program::XFormArrangement; -use ::differential_datalog::program::XFormCollection; -use ::differential_datalog::program::Weight; -use ::differential_datalog::record::FromRecord; -use ::differential_datalog::record::FromRecordInner; -use ::differential_datalog::record::IntoRecord; -use ::differential_datalog::record::Mutator; -use ::differential_datalog::record::MutatorInner; -use ::serde::Deserialize; -use ::serde::Serialize; - - -// `usize` and `isize` are builtin Rust types; we therefore declare an alias to DDlog's `usize` and -// `isize`. -pub type std_usize = u64; -pub type std_isize = i64; - -/*- !!!!!!!!!!!!!!!!!!!! -*/ -// Don't edit this line -// Code below this point is needed to test-compile template -// code and is not part of the template. - -/* Import bits of DDlog runtime required by `differential_datalog_test` and the `main_crate` test. */ -#[path = "../../../lib/ddlog_rt.rs"] -pub mod ddlog_rt; - -#[path = "../../../lib/ddlog_bigint.rs"] -pub mod ddlog_bigint; - -#[path = "../../../lib/ddlog_log.rs"] -pub mod ddlog_log; +#![allow( + path_statements, + unused_imports, + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + unused_parens, + non_shorthand_field_patterns, + dead_code, + overflowing_literals, + unreachable_patterns, + unused_variables, + clippy::missing_safety_doc, + clippy::match_single_binding, + clippy::ptr_arg, + clippy::redundant_closure, + clippy::needless_lifetimes, + clippy::borrowed_box, + clippy::map_clone, + clippy::toplevel_ref_arg, + clippy::double_parens, + clippy::collapsible_if, + clippy::clone_on_copy, + clippy::unused_unit, + clippy::deref_addrof, + clippy::clone_on_copy, + clippy::needless_return, + clippy::op_ref, + clippy::match_like_matches_macro, + clippy::comparison_chain, + clippy::len_zero, + clippy::extra_unused_lifetimes +)] + +use std::ops::Deref; + +use differential_dataflow::collection; +use timely::communication; +use timely::dataflow::scopes; +use timely::worker; + +use ddlog_derive::{FromRecord, IntoRecord, Mutator}; +use differential_datalog::ddval::DDValConvert; +use differential_datalog::program; +use differential_datalog::program::TupleTS; +use differential_datalog::program::Weight; +use differential_datalog::program::XFormArrangement; +use differential_datalog::program::XFormCollection; +use differential_datalog::record::FromRecord; +use differential_datalog::record::FromRecordInner; +use differential_datalog::record::IntoRecord; +use differential_datalog::record::Mutator; +use differential_datalog::record::MutatorInner; +use serde::Deserialize; +use serde::Serialize; + +// `usize` and `isize` are builtin Rust types; we therefore declare an alias to DDlog's `usize` and +// `isize`. +pub type std_usize = u64; +pub type std_isize = i64; + +/*- !!!!!!!!!!!!!!!!!!!! -*/ +// Don't edit this line +// Code below this point is needed to test-compile template +// code and is not part of the template. + +/* Import bits of DDlog runtime required by `differential_datalog_test` and the `main_crate` test. */ +#[path = "../../../lib/ddlog_rt.rs"] +pub mod ddlog_rt; + +#[path = "../../../lib/ddlog_bigint.rs"] +pub mod ddlog_bigint; + +#[path = "../../../lib/ddlog_log.rs"] +pub mod ddlog_log; diff --git a/src/Language/DifferentialDatalog/Compile.hs b/src/Language/DifferentialDatalog/Compile.hs index 14eca84e4..0c30e4663 100644 --- a/src/Language/DifferentialDatalog/Compile.hs +++ b/src/Language/DifferentialDatalog/Compile.hs @@ -231,6 +231,7 @@ rustLibFiles = , ("differential_datalog/src/api/update_handler.rs" , $(embedFile "rust/template/differential_datalog/src/api/update_handler.rs")) , ("differential_datalog/src/flatbuf/mod.rs" , $(embedFile "rust/template/differential_datalog/src/flatbuf/mod.rs")) , ("differential_datalog/src/dataflow/map.rs" , $(embedFile "rust/template/differential_datalog/src/dataflow/map.rs")) + , ("differential_datalog/src/utils.rs" , $(embedFile "rust/template/differential_datalog/src/utils.rs")) , ("differential_datalog_test/Cargo.toml" , $(embedFile "rust/template/differential_datalog_test/Cargo.toml")) , ("differential_datalog_test/lib.rs" , $(embedFile "rust/template/differential_datalog_test/lib.rs")) , ("differential_datalog_test/test_value.rs" , $(embedFile "rust/template/differential_datalog_test/test_value.rs")) @@ -785,18 +786,10 @@ mkCargoToml rs_code crate crate_id = "ddlog_profiler = { path = \"" <> pp root <> "../ddlog_profiler\" }" $$ "ddlog_derive = { path = \"" <> pp root <> "../ddlog_derive\" }" $$ "abomonation = \"0.7\"" $$ - "ordered-float = { version = \"2.0.0\", features = [\"serde\"] }" $$ - "fnv = \"1.0.2\"" $$ - "twox-hash = \"1.6.0\"" $$ - "once_cell = \"1.4.1\"" $$ - "libc = \"0.2\"" $$ - "time = { version = \"0.2\", features = [\"serde\"] }" $$ - "serde_json = \"1.0\"" $$ "serde = { version = \"1.0\", features = [\"derive\"] }" $$ - "num = \"0.3\"" $$ "erased-serde = \"0.3\"" $$ - --"differential-dataflow = \"0.11.0\"" $$ - --"timely = \"0.11\"" $$ + "ordered-float = \"2.8.0\"" $$ + "once_cell = \"1.8.0\"" $$ "differential-dataflow = { git = \"https://github.com/ddlog-dev/differential-dataflow\", branch = \"ddlog-4\" }" $$ "timely = { git = \"https://github.com/ddlog-dev/timely-dataflow\", branch = \"ddlog-4\", default-features = false }" $$ "" $$ @@ -1725,15 +1718,15 @@ data LazyStatic = LazyStatic { c_api :: Bool } --- Creates a static variable holding a `FnvHashMap` pre-allocated and pre-filled with the supplied values +-- Creates a static variable holding an `XxHashMap` pre-allocated and pre-filled with the supplied values -- -- The generated code will take roughly this form: -- ```rust -- /// {documentation} -- #[cfg(feature = "c_api")] // only if c_api is true --- pub static {static name}: ::once_cell::sync::Lazy<::fnv::FnvHashMap<{key}, {value}>> = +-- pub static {static name}: ::once_cell::sync::Lazy<::differential_datalog::utils::XxHashMap<{key}, {value}>> = -- ::once_cell::sync::Lazy::new(|| { --- let mut map = ::fnv::FnvHashMap::with_capacity_and_hasher({length elements}, ::fnv::FnvBuildHasher::default()); +-- let mut map = ::differential_datalog::utils::XxHashMap::with_capacity_and_hasher({length elements}, ::std::default::Default::default()); -- // For each element -- map.insert({key}, {value}); -- @@ -1744,11 +1737,14 @@ createLazyStatic :: LazyStatic -> Doc createLazyStatic lazy_static = doc_comment $$ (if (c_api lazy_static) then "#[cfg(feature = \"c_api\")]\n" else "") - <> "pub static" <+> static_name <> ": ::once_cell::sync::Lazy<::fnv::FnvHashMap<" <> key_type <> "," <+> value_type <> ">> =" + <> "pub static" <+> static_name <> ": ::once_cell::sync::Lazy<::differential_datalog::utils::XxHashMap<" <> key_type <> "," <+> value_type <> ">> =" $$ " ::once_cell::sync::Lazy::new(|| {" -- Pre-allocate the HashMap, maps using a hasher other than `RandomState` can't use `with_capacity()`, so we - -- use `with_capacity_and_hasher()`, giving it our pre-allocation capacity and a default hasher provided by fnv - $$ " let mut map = ::fnv::FnvHashMap::with_capacity_and_hasher(" <> (int map_len) <> ", ::fnv::FnvBuildHasher::default());" + -- use `with_capacity_and_hasher()`, giving it our pre-allocation capacity and a default hasher provided by xxhash + $$ " let mut map = ::differential_datalog::utils::XxHashMap::with_capacity_and_hasher(" + $$ (nest' $ int map_len) <> "," + $$ " ::std::default::Default::default()," + $$ " );" $$ (nest' . nest' $ vcat entries) $$ " map" $$ " });" @@ -1905,7 +1901,7 @@ compileApplyNode d Apply{..} idx = ApplyNode applyModule idx xformer_name (mkSourcePos transPos) $ "pub fn __apply_" <> pp idx <+> "() -> Box<" $$ " dyn for<'a> Fn(" - $$ " &mut ::fnv::FnvHashMap<" + $$ " &mut ::differential_datalog::utils::XxHashMap<" $$ " program::RelId," $$ " collection::Collection<" $$ " scopes::Child<'a, worker::Worker, program::TS>," diff --git a/test/datalog_tests/ovn.rs b/test/datalog_tests/ovn.rs index 88cf62284..da75350cb 100644 --- a/test/datalog_tests/ovn.rs +++ b/test/datalog_tests/ovn.rs @@ -1,35 +1,37 @@ -use fnv::FnvHashSet; - -pub type Set = FnvHashSet; - -pub fn build_dhcp_netmask(_cidr: &String) -> String { - "not implemented: build_dhcp_netmask".to_owned() -} -pub fn eth_addr_from_string(_str: &String) -> ddlog_std::Option { - ddlog_std::Option::None -} -pub fn extract_ips(_str: &String) -> Set { - FnvHashSet::default() -} - -pub fn extract_mac(_str: &String) -> ddlog_std::tuple2 { - ddlog_std::tuple2(0, "extract_mac not implemented".to_owned()) -} -pub fn extract_subnets(_str: &String) -> Set { - FnvHashSet::default() -} - -pub fn in6_generate_lla(_mac: &mac_addr_t) -> ip6_addr_t { - 0 -} -pub fn ip_address_and_port_from_lb_key(_key: &String) -> ip_port_t { - ip_port_t{ip: "0.0.0.0".to_owned(), port: ddlog_std::Option::None} -} -pub fn ip_parse(_str: &String) -> ddlog_std::Option { - ddlog_std::Option::None -} - -pub fn ipv6_string_mapped(_addr: &ip6_addr_t) -> String { - "not implemented: ipv6_string_mapped".to_owned() -} - +use differential_datalog::XxHashSet; + +pub type Set = XxHashSet; + +pub fn build_dhcp_netmask(_cidr: &String) -> String { + "not implemented: build_dhcp_netmask".to_owned() +} +pub fn eth_addr_from_string(_str: &String) -> ddlog_std::Option { + ddlog_std::Option::None +} +pub fn extract_ips(_str: &String) -> Set { + XxHashSet::default() +} + +pub fn extract_mac(_str: &String) -> ddlog_std::tuple2 { + ddlog_std::tuple2(0, "extract_mac not implemented".to_owned()) +} +pub fn extract_subnets(_str: &String) -> Set { + XxHashSet::default() +} + +pub fn in6_generate_lla(_mac: &mac_addr_t) -> ip6_addr_t { + 0 +} +pub fn ip_address_and_port_from_lb_key(_key: &String) -> ip_port_t { + ip_port_t { + ip: "0.0.0.0".to_owned(), + port: ddlog_std::Option::None, + } +} +pub fn ip_parse(_str: &String) -> ddlog_std::Option { + ddlog_std::Option::None +} + +pub fn ipv6_string_mapped(_addr: &ip6_addr_t) -> String { + "not implemented: ipv6_string_mapped".to_owned() +} From c12380f16cbb70e405d5175cb8b83243ab6c8143 Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Mon, 15 Nov 2021 13:38:21 -0600 Subject: [PATCH 3/3] Added missing tilde --- rust/template/differential_datalog/src/ddval/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/template/differential_datalog/src/ddval/mod.rs b/rust/template/differential_datalog/src/ddval/mod.rs index 1587c2d21..8dd00cf59 100644 --- a/rust/template/differential_datalog/src/ddval/mod.rs +++ b/rust/template/differential_datalog/src/ddval/mod.rs @@ -79,7 +79,7 @@ pub struct DDVal { pub struct DDValMethods { /// Clones the current value, creating a new [`DDVal`] pub clone: fn(this: &DDVal) -> DDVal, - /// Converts the current value into a [`Record] + /// Converts the current value into a [`Record`] pub into_record: fn(this: DDVal) -> Record, /// The [`Eq`] implementation for two values of the same concrete type