1use antithesis_sdk::random::random_choice;
4use rand::distr::Distribution;
5use rand::{Rng, RngExt};
6
7use crate::rand::Boundary;
8
9#[derive(Clone, Copy, Debug)]
11pub enum Vibe {
12 Clean,
14 Feral,
16}
17
18#[must_use]
20pub fn sample_vibe() -> Vibe {
21 match random_choice(&[Vibe::Clean, Vibe::Feral]) {
22 Some(Vibe::Feral) => Vibe::Feral,
23 _ => Vibe::Clean,
24 }
25}
26
27pub(crate) const NAME_SEPARATORS: &[u8] = b"._- ";
29
30pub(crate) const COMPLIANT_WORD: &[&[u8]] = &[
32 b"adp",
33 b"dogstatsd",
34 b"requests",
35 b"latency",
36 b"errors",
37 b"count",
38 b"total",
39 b"bytes",
40 b"queue",
41 b"workers",
42];
43
44pub(crate) const ABERRANT_WORD: &[&[u8]] = &[
47 b"",
48 b" ",
49 b"\t",
50 b"\0",
51 b"a:b",
52 b"a|b",
53 b"a,b",
54 b"#hash",
55 b"@at",
56 b"_sc",
57 b"_e{1,1}",
58 b"\x80",
59 b"\xc3",
60 b"\xed\xa0\x80",
61 b"\xc0\x80",
62 b"\xff\xfe",
63 b"emoji\xf0\x9f\x92\xa9",
64];
65
66pub(crate) const ABERRANT_VALUES: &[&[u8]] = &[
69 b"0",
70 b"-0",
71 b"inf",
72 b"-inf",
73 b"+inf",
74 b"nan",
75 b"infinity",
76 b"1e999999",
77 b"-1e999999",
78 b"0x1p4",
79 b"1_000",
80 b".",
81 b"+",
82 b"-",
83 b"1.",
84 b".5",
85 b"1:2:3:4:5",
86 b"00000000000000000000000000000000000000000000000000000001.5",
87 b"3.141592653589793115997963468544185161590576171875000000000000000000000000",
88 "\u{221e}".as_bytes(),
89 "-\u{221e}".as_bytes(),
90 "\u{ff11}\u{ff12}\u{ff13}".as_bytes(),
91 "\u{0664}\u{0662}".as_bytes(),
92];
93
94pub(crate) const COMPLIANT_TS: &[&[u8]] = &[b"1700000000", b"1", b"1609459200"];
96
97const COMPLIANT_TAG_KEYS: &[&[u8]] = &[b"env", b"service", b"region", b"version", b"team", b"shard"];
101const ABERRANT_TAG_KEYS: &[&[u8]] = &[b"", b" ", b":", b",", b"#", b"\0", b"\x80"];
102const COMPLIANT_TAG_VALUES: &[&[u8]] = &[
103 b"prod",
104 b"staging",
105 b"adp",
106 b"us-east-1",
107 b"eu-west-1",
108 b"1.2.3",
109 b"web01",
110 b"0",
111];
112const ABERRANT_TAG_VALUES: &[&[u8]] = &[b"", b",", b"|", b":", b"\xff", b"\xed\xa0\x80", b"a,b"];
113
114#[derive(Clone, Copy)]
116enum Form {
117 Compact,
118 Expanded,
119}
120
121pub(crate) fn extend_choice(buf: &mut Vec<u8>, vibe: Vibe, compliant: &[&[u8]], aberrant: &[&[u8]]) {
124 let pools: &[&[&[u8]]] = match vibe {
125 Vibe::Clean => &[compliant],
126 Vibe::Feral => &[compliant, aberrant],
127 };
128 if let Some(&pool) = random_choice(pools) {
129 if let Some(&item) = random_choice(pool) {
130 buf.extend_from_slice(item);
131 }
132 }
133}
134
135const ELEMENT_COUNTS_CLEAN: &[u8] = &[1, 1, 2, 2, 3, 3, 4, 5, 6];
137
138const ELEMENT_COUNTS_FERAL: &[u8] = &[1, 1, 2, 2, 3, 3, 4, 5, 6, 0, 127, 255];
140
141fn sample_count<R: Rng + ?Sized>(rng: &mut R, vibe: Vibe) -> u8 {
142 let counts = match vibe {
143 Vibe::Clean => ELEMENT_COUNTS_CLEAN,
144 Vibe::Feral => ELEMENT_COUNTS_FERAL,
145 };
146 counts[rng.random_range(0..counts.len())]
147}
148
149pub(crate) fn write_segments<R: Rng + ?Sized>(
152 rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe, compliant: &[&[u8]], aberrant: &[&[u8]], separators: &[u8],
153) {
154 let count = sample_count(rng, vibe);
155 for i in 0..count {
156 if i > 0 {
157 if let Some(&sep) = random_choice(separators) {
158 buf.push(sep);
159 }
160 }
161 extend_choice(buf, vibe, compliant, aberrant);
162 }
163}
164
165pub(crate) fn write_words<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe) {
167 write_segments(rng, buf, vibe, COMPLIANT_WORD, ABERRANT_WORD, NAME_SEPARATORS);
168}
169
170pub(crate) fn write_field(buf: &mut Vec<u8>, vibe: Vibe, prefix: &[u8], compliant: &[&[u8]], aberrant: &[&[u8]]) {
172 buf.push(b'|');
173 buf.extend_from_slice(prefix);
174 extend_choice(buf, vibe, compliant, aberrant);
175}
176
177pub(crate) fn write_tags<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>, vibe: Vibe) {
182 let count = sample_count(rng, vibe);
183 for t in 0..count {
184 if t == 0 {
185 buf.extend_from_slice(b"|#");
186 } else {
187 buf.push(b',');
188 }
189 write_segments(rng, buf, vibe, COMPLIANT_TAG_KEYS, ABERRANT_TAG_KEYS, NAME_SEPARATORS);
190 buf.push(b':');
191 write_segments(
192 rng,
193 buf,
194 vibe,
195 COMPLIANT_TAG_VALUES,
196 ABERRANT_TAG_VALUES,
197 NAME_SEPARATORS,
198 );
199 }
200}
201
202pub(crate) fn write_number<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>, digits: &[u8]) {
205 match random_choice(&[Form::Compact, Form::Expanded]) {
206 Some(Form::Expanded) => {
207 let (sign, rest) = match digits.first() {
208 Some(&(b'-' | b'+')) => (&digits[..1], &digits[1..]),
209 _ => (&digits[..0], digits),
210 };
211 buf.extend_from_slice(sign);
212 pad_zeros(rng, buf);
213 buf.extend_from_slice(rest);
214 let fractional = rest.contains(&b'.') && !rest.iter().any(|&c| c == b'e' || c == b'E');
215 if fractional {
216 pad_zeros(rng, buf);
217 }
218 }
219 _ => buf.extend_from_slice(digits),
220 }
221}
222
223fn pad_zeros<R: Rng + ?Sized>(rng: &mut R, buf: &mut Vec<u8>) {
225 let zeros = usize::from(Boundary::<u8>::new().sample(rng));
226 buf.resize(buf.len() + zeros, b'0');
227}