|
| 1 | +use std::sync::atomic::{self, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize}; |
| 2 | + |
| 3 | +use once_cell::sync::Lazy; |
| 4 | +use serde::Serialize; |
| 5 | +use serde_json::{json, Value}; |
| 6 | + |
| 7 | +use crate::internal; |
| 8 | + |
| 9 | +use super::AntithesisLocationInfo; |
| 10 | + |
| 11 | +pub struct Guard<const MAX: bool, T: AtomicMinMax> { |
| 12 | + mark: T::Atomic, |
| 13 | +} |
| 14 | + |
| 15 | +trait Extremal { |
| 16 | + const MIN: Self; |
| 17 | + const MAX: Self; |
| 18 | +} |
| 19 | + |
| 20 | +impl<const MAX: bool, T: AtomicMinMax> Guard<MAX, T> |
| 21 | +where T::Atomic: Extremal { |
| 22 | + pub const fn init() -> Self { |
| 23 | + let mark = if MAX { T::Atomic::MIN } else { T::Atomic::MAX }; |
| 24 | + Self { mark } |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +trait AtomicMinMax { |
| 29 | + type Atomic; |
| 30 | + fn fetch_min(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self; |
| 31 | + fn fetch_max(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self; |
| 32 | +} |
| 33 | + |
| 34 | +impl<const MAX: bool, T: AtomicMinMax + PartialOrd + Copy> Guard<MAX, T> { |
| 35 | + pub fn should_emit(&self, new: T) -> bool { |
| 36 | + if MAX { |
| 37 | + let max = T::fetch_max(&self.mark, new, atomic::Ordering::SeqCst); |
| 38 | + !(max >= new) |
| 39 | + } else { |
| 40 | + let min = T::fetch_min(&self.mark, new, atomic::Ordering::SeqCst); |
| 41 | + !(min <= new) |
| 42 | + } |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +pub trait Diff { |
| 47 | + type Output; |
| 48 | + |
| 49 | + fn diff(&self, other: Self) -> Self::Output; |
| 50 | +} |
| 51 | + |
| 52 | +macro_rules! impl_extremal { |
| 53 | + ($($t:ty)*) => {$( |
| 54 | + impl Extremal for $t { |
| 55 | + const MIN: $t = <$t>::MIN; |
| 56 | + const MAX: $t = <$t>::MAX; |
| 57 | + } |
| 58 | + )*} |
| 59 | +} |
| 60 | + |
| 61 | +impl_extremal! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } |
| 62 | + |
| 63 | +macro_rules! impl_extremal_atomic { |
| 64 | + ($(($t:ty, $raw_t:ty))*) => {$( |
| 65 | + impl Extremal for $t { |
| 66 | + const MIN: $t = <$t>::new(<$raw_t>::MIN); |
| 67 | + const MAX: $t = <$t>::new(<$raw_t>::MAX); |
| 68 | + } |
| 69 | + )*} |
| 70 | +} |
| 71 | + |
| 72 | +impl_extremal_atomic! { (AtomicUsize, usize) (AtomicU8, u8) (AtomicU16, u16) (AtomicU32, u32) (AtomicU64, u64) (AtomicIsize, isize) (AtomicI8, i8) (AtomicI16, i16) (AtomicI32, i32) (AtomicI64, i64) } |
| 73 | + |
| 74 | +impl Extremal for AtomicF32 { |
| 75 | + const MIN: Self = AtomicF32(AtomicU32::new(0xff800000)); |
| 76 | + const MAX: Self = AtomicF32(AtomicU32::new(0x7f800000)); |
| 77 | +} |
| 78 | + |
| 79 | +impl Extremal for AtomicF64 { |
| 80 | + const MIN: Self = AtomicF64(AtomicU64::new(0xfff0000000000000)); |
| 81 | + const MAX: Self = AtomicF64(AtomicU64::new(0x7ff0000000000000)); |
| 82 | +} |
| 83 | + |
| 84 | +macro_rules! impl_atomic_min_max { |
| 85 | + ($(($t:ty, $atomic_t:ty))*) => {$( |
| 86 | + impl AtomicMinMax for $t { |
| 87 | + type Atomic = $atomic_t; |
| 88 | + |
| 89 | + fn fetch_min(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self { |
| 90 | + current.fetch_min(other, ordering) |
| 91 | + } |
| 92 | + |
| 93 | + fn fetch_max(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self { |
| 94 | + current.fetch_max(other, ordering) |
| 95 | + } |
| 96 | + } |
| 97 | + )*}; |
| 98 | +} |
| 99 | + |
| 100 | +impl_atomic_min_max! { (usize, AtomicUsize) (u8, AtomicU8) (u16, AtomicU16) (u32, AtomicU32) (u64, AtomicU64) (isize, AtomicIsize) (i8, AtomicI8) (i16, AtomicI16) (i32, AtomicI32) (i64, AtomicI64) } |
| 101 | + |
| 102 | +macro_rules! impl_atomic_min_max_float { |
| 103 | + ($(($t:ty, $atomic_t:ident, $store_t:ty))*) => {$( |
| 104 | + struct $atomic_t($store_t); |
| 105 | + |
| 106 | + impl AtomicMinMax for $t { |
| 107 | + type Atomic = $atomic_t; |
| 108 | + |
| 109 | + fn fetch_min(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self { |
| 110 | + <$t>::from_bits(current.0.fetch_update(ordering, ordering, |x| Some(<$t>::from_bits(x).min(other).to_bits())).unwrap()) |
| 111 | + } |
| 112 | + |
| 113 | + fn fetch_max(current: &Self::Atomic, other: Self, ordering: atomic::Ordering) -> Self { |
| 114 | + <$t>::from_bits(current.0.fetch_update(ordering, ordering, |x| Some(<$t>::from_bits(x).max(other).to_bits())).unwrap()) |
| 115 | + } |
| 116 | + } |
| 117 | + )*}; |
| 118 | +} |
| 119 | + |
| 120 | +impl_atomic_min_max_float! { (f32, AtomicF32, AtomicU32) (f64, AtomicF64, AtomicU64)} |
| 121 | + |
| 122 | +macro_rules! impl_diff_unsigned { |
| 123 | + ($($t:ty)*) => {$( |
| 124 | + impl Diff for $t { |
| 125 | + type Output = f64; |
| 126 | + |
| 127 | + fn diff(&self, other: Self) -> Self::Output { |
| 128 | + if *self < other { |
| 129 | + -((other - self) as f64) |
| 130 | + } else { |
| 131 | + (self - other) as f64 |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + )*}; |
| 136 | +} |
| 137 | + |
| 138 | +impl_diff_unsigned! { usize u8 u16 u32 u64 u128 } |
| 139 | + |
| 140 | +macro_rules! impl_diff_signed { |
| 141 | + ($(($t:ty, $unsigned_t:ty))*) => {$( |
| 142 | + impl Diff for $t { |
| 143 | + type Output = f64; |
| 144 | + |
| 145 | + fn diff(&self, other: Self) -> Self::Output { |
| 146 | + if *self < other { |
| 147 | + -((other as $unsigned_t).wrapping_sub(*self as $unsigned_t) as f64) |
| 148 | + } else { |
| 149 | + (*self as $unsigned_t).wrapping_sub(other as $unsigned_t) as f64 |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | + )*}; |
| 154 | +} |
| 155 | + |
| 156 | +impl_diff_signed! { (isize, usize) (i8, u8) (i16, u16) (i32, u32) (i64, u64) (i128, u128) } |
| 157 | + |
| 158 | +macro_rules! impl_diff_float { |
| 159 | + ($($t:ty)*) => {$( |
| 160 | + impl Diff for $t { |
| 161 | + type Output = f64; |
| 162 | + |
| 163 | + fn diff(&self, other: Self) -> Self::Output { |
| 164 | + (self - other) as f64 |
| 165 | + } |
| 166 | + } |
| 167 | + )*}; |
| 168 | +} |
| 169 | + |
| 170 | +impl_diff_float! { f32 f64 } |
| 171 | + |
| 172 | +#[derive(Copy, Clone, Serialize)] |
| 173 | +#[serde(rename_all(serialize = "lowercase"))] |
| 174 | +pub enum GuidanceType { |
| 175 | + Numeric, |
| 176 | + Boolean, |
| 177 | + JSON, |
| 178 | +} |
| 179 | + |
| 180 | +#[derive(Serialize)] |
| 181 | +struct GuidanceInfo { |
| 182 | + guidance_type: GuidanceType, |
| 183 | + message: String, |
| 184 | + id: String, |
| 185 | + location: AntithesisLocationInfo, |
| 186 | + maximize: bool, |
| 187 | + guidance_data: Value, |
| 188 | + hit: bool, |
| 189 | +} |
| 190 | + |
| 191 | +pub struct GuidanceCatalogInfo { |
| 192 | + pub guidance_type: GuidanceType, |
| 193 | + pub message: &'static str, |
| 194 | + pub id: &'static str, |
| 195 | + pub class: &'static str, |
| 196 | + pub function: &'static Lazy<&'static str>, |
| 197 | + pub file: &'static str, |
| 198 | + pub begin_line: u32, |
| 199 | + pub begin_column: u32, |
| 200 | + pub maximize: bool, |
| 201 | +} |
| 202 | + |
| 203 | +pub fn guidance_impl( |
| 204 | + guidance_type: GuidanceType, |
| 205 | + message: String, |
| 206 | + id: String, |
| 207 | + class: String, |
| 208 | + function: String, |
| 209 | + file: String, |
| 210 | + begin_line: u32, |
| 211 | + begin_column: u32, |
| 212 | + maximize: bool, |
| 213 | + guidance_data: Value, |
| 214 | + hit: bool, |
| 215 | +) { |
| 216 | + let location = AntithesisLocationInfo { class, function, file, begin_line, begin_column }; |
| 217 | + let guidance = GuidanceInfo { |
| 218 | + guidance_type, message, id, location, maximize, guidance_data, hit |
| 219 | + }; |
| 220 | + |
| 221 | + internal::dispatch_output(&json!({ "antithesis_guidance": guidance })); |
| 222 | +} |
0 commit comments