lain/
types.rs

1use num_traits::Bounded;
2use std::fmt::Debug;
3
4#[cfg(feature = "serde_support")]
5use serde::{Deserialize, Serialize};
6
7/// Represents an enum that can contain unsafe values.
8///
9/// These are enums which may potentially be used as indices, offsets, or used in some other
10/// calculation. This wrapper type exists since the Rust compiler makes strong assumptions about how
11/// enums are used, and if you attempt to unsafely (either through a union or pointers) set the
12/// value of an enum to an indiscriminant value, you will regularly hit issues with illegal
13/// instructions being executed while in debug mode. See, Rust will emit certain LLVM IR code like
14/// `unreachable;` to give LLVM certain hints. The problem is that Rust believes (and rightfully so)
15/// that enums have discrete values *unless* they are programmed to contain custom discriminant
16/// values. So if you have ane num like:
17/// ```
18/// enum MyEnum {
19///     Foo = 1,
20///     Bar,
21///     Baz, // ...
22/// }
23/// ```
24///
25/// Rust expects in some scenarios that *all* possible values have been accounted for so the
26/// following is emitted:
27///
28/// ```compile_fail
29/// let my_enum_instance = MyEnum::Foo;
30/// match my_enum_instance {
31///     MyEnum::Foo | MyEnum::Bar | MyEnum::Baz => println!("All possibilities accounted for :)"), // your code
32///     _ => unreachable(), // the compiler will insert this branch in some scenarios
33/// }
34/// ```
35///
36/// But what if you set the value of your instance to something other than 1, 2, or 3 via `unsafe`
37/// code? That `unreachable()` block is hit *in debug builds only* and suddenly your code doesn't
38/// work. In release mode, sometimes the `_` (default) path is actually used to hold the first item
39/// of the enum, so your "all other values" code path *actually* represents a very real value.
40///
41/// **TL;DR** Rust makes too many assumptions about how enums are used to make doing unsafe things
42/// with them worthwhile. This wrapper enum works around that.
43#[derive(Debug, Clone, Copy)]
44#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
45pub enum UnsafeEnum<T, I> {
46    Valid(T),
47    Invalid(I),
48}
49
50impl<T, I> Default for UnsafeEnum<T, I>
51where
52    T: Default,
53{
54    fn default() -> Self {
55        UnsafeEnum::Valid(Default::default())
56    }
57}
58
59impl<E, T> crate::traits::ToPrimitive for UnsafeEnum<E, T>
60where
61    E: crate::traits::ToPrimitive<Output = T>,
62    T: Copy,
63{
64    type Output = T;
65
66    fn to_primitive(&self) -> T {
67        match self {
68            UnsafeEnum::Valid(ref e) => e.to_primitive(),
69            UnsafeEnum::Invalid(n) => *n,
70        }
71    }
72}
73
74// TODO: Clean up this string interface. This isn't the cleanest
75/// Wrapper around `String` that provides mutation methods appropriate for UTF-8 encoded Strings
76#[derive(Debug, Default, Clone)]
77pub struct Utf8String {
78    pub(crate) inner: Vec<Utf8Char>,
79}
80
81impl Utf8String {
82    pub fn new(s: &str) -> Self {
83        Utf8String {
84            inner: s.chars().map(|c| Utf8Char(c)).collect(),
85        }
86    }
87}
88
89/// Wrapper around `String` that provides mutation methods appropriate for ASCII encoded Strings
90#[derive(Debug, Default, Clone)]
91pub struct AsciiString {
92    pub(crate) inner: Vec<AsciiChar>,
93}
94
95impl AsciiString {
96    pub fn new(s: &str) -> Self {
97        AsciiString {
98            inner: s.chars().map(|c| AsciiChar(c)).collect(),
99        }
100    }
101}
102
103/// Represents a UTF-8 character.
104#[derive(Default, Debug, Clone)]
105pub(crate) struct Utf8Char(pub(crate) char);
106
107/// Represents an ASCII character.
108#[derive(Default, Debug, Clone)]
109pub(crate) struct AsciiChar(pub(crate) char);
110
111/// Data structure holding constraints that the [NewFuzzed::new_fuzzed][lain::traits::NewFuzzed::new_fuzzed] or
112/// [Mutatable::mutate][lain::traits::Mutatable::mutate] methods should try to respect.
113#[derive(Debug, Default, Clone)]
114pub struct Constraints<T: Bounded + Debug> {
115    /// The contextual "min" bound
116    pub min: Option<T>,
117    /// The contextual "max" bound (**not** inclusive)
118    pub max: Option<T>,
119    /// Which direction to weigh the RNG towards
120    pub weighted: Weighted,
121    /// The space allotted for dynamically-sized objects
122    pub max_size: Option<usize>,
123    pub base_object_size_accounted_for: bool,
124}
125
126impl<T: Bounded + Debug> Constraints<T> {
127    pub fn new() -> Constraints<T> {
128        Constraints {
129            min: None,
130            max: None,
131            weighted: Weighted::None,
132            max_size: None,
133            base_object_size_accounted_for: false,
134        }
135    }
136
137    pub fn min<'a>(&'a mut self, min: T) -> &'a mut Constraints<T> {
138        self.min = Some(min);
139        self
140    }
141
142    pub fn max<'a>(&'a mut self, max: T) -> &'a mut Constraints<T> {
143        self.max = Some(max);
144        self
145    }
146
147    pub fn weighted<'a>(&'a mut self, weighted: Weighted) -> &'a mut Constraints<T> {
148        self.weighted = weighted;
149        self
150    }
151
152    pub fn max_size<'a>(&'a mut self, max_size: usize) -> &'a mut Constraints<T> {
153        self.max_size = Some(max_size);
154        self
155    }
156
157    pub fn account_for_base_object_size<'a, U: crate::traits::SerializedSize>(
158        &'a mut self,
159    ) -> &'a mut Constraints<T> {
160        if !self.base_object_size_accounted_for {
161            if let Some(ref mut max_size) = self.max_size {
162                if U::max_default_object_size() > *max_size {
163                    panic!("minimum base object size is larger than the desired maximum output size (required at least: 0x{:X}, max: 0x{:X}). Check to ensure your output buffer for this object is larger enough", U::max_default_object_size(), *max_size);
164                }
165
166                *max_size = max_size.saturating_sub(U::max_default_object_size());
167            }
168
169            self.base_object_size_accounted_for = true;
170        }
171
172        self
173    }
174
175    pub fn set_base_size_accounted_for<'a>(&'a mut self) -> &'a mut Constraints<T> {
176        self.base_object_size_accounted_for = true;
177        self
178    }
179}
180
181/// Which direction to weigh ranges towards (min bound, upper bound, or none).
182#[derive(Debug, PartialEq, Clone, Copy)]
183pub enum Weighted {
184    None,
185    Min,
186    Max,
187}
188
189impl Default for Weighted {
190    fn default() -> Self {
191        Weighted::None
192    }
193}