1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/*
23
24 Data generators for fuzzing test data in a reproducible way.
25
26*/
27#include <SDL3/SDL_test.h>
28
29#include <float.h> /* Needed for FLT_MAX and DBL_EPSILON */
30#include <limits.h> /* Needed for UCHAR_MAX, etc. */
31
32/**
33 * Counter for fuzzer invocations
34 */
35static int fuzzerInvocationCounter = 0;
36
37/**
38 * Context for shared random number generator
39 */
40static Uint64 rndContext;
41
42/*
43 * Note: doxygen documentation markup for functions is in the header file.
44 */
45
46void SDLTest_FuzzerInit(Uint64 execKey)
47{
48 rndContext = execKey;
49 fuzzerInvocationCounter = 0;
50}
51
52int SDLTest_GetFuzzerInvocationCount(void)
53{
54 return fuzzerInvocationCounter;
55}
56
57Uint8 SDLTest_RandomUint8(void)
58{
59 fuzzerInvocationCounter++;
60
61 return (Uint8)(SDL_rand_bits_r(&rndContext) >> 24);
62}
63
64Sint8 SDLTest_RandomSint8(void)
65{
66 fuzzerInvocationCounter++;
67
68 return (Sint8)(SDL_rand_bits_r(&rndContext) >> 24);
69}
70
71Uint16 SDLTest_RandomUint16(void)
72{
73 fuzzerInvocationCounter++;
74
75 return (Uint16)(SDL_rand_bits_r(&rndContext) >> 16);
76}
77
78Sint16 SDLTest_RandomSint16(void)
79{
80 fuzzerInvocationCounter++;
81
82 return (Sint16)(SDL_rand_bits_r(&rndContext) >> 16);
83}
84
85Uint32 SDLTest_RandomUint32(void)
86{
87 fuzzerInvocationCounter++;
88
89 return SDL_rand_bits_r(&rndContext);
90}
91
92Sint32 SDLTest_RandomSint32(void)
93{
94 fuzzerInvocationCounter++;
95
96 return (Sint32)SDL_rand_bits_r(&rndContext);
97}
98
99Uint64 SDLTest_RandomUint64(void)
100{
101 union
102 {
103 Uint64 v64;
104 Uint32 v32[2];
105 } value;
106
107 fuzzerInvocationCounter++;
108
109 value.v32[0] = SDLTest_RandomUint32();
110 value.v32[1] = SDLTest_RandomUint32();
111
112 return value.v64;
113}
114
115Sint64 SDLTest_RandomSint64(void)
116{
117 union
118 {
119 Uint64 v64;
120 Uint32 v32[2];
121 } value;
122
123 fuzzerInvocationCounter++;
124
125 value.v32[0] = SDLTest_RandomUint32();
126 value.v32[1] = SDLTest_RandomUint32();
127
128 return (Sint64)value.v64;
129}
130
131Sint32 SDLTest_RandomIntegerInRange(Sint32 min, Sint32 max)
132{
133 fuzzerInvocationCounter++;
134
135 if (min == max) {
136 return min;
137 }
138
139 if (min > max) {
140 Sint32 temp = min;
141 min = max;
142 max = temp;
143 }
144
145 Uint64 range = (Sint64)max - (Sint64)min;
146 if (range < SDL_MAX_SINT32) {
147 return min + SDL_rand_r(&rndContext, (Sint32) range + 1);
148 } else {
149 Uint64 add = SDL_rand_bits_r(&rndContext) | ((Uint64) SDL_rand_bits_r(&rndContext) << 32);
150 return (Sint32) (min + (Sint64) (add % (range + 1)));
151 }
152}
153
154/**
155 * Generates a unsigned boundary value between the given boundaries.
156 * Boundary values are inclusive. See the examples below.
157 * If boundary2 < boundary1, the values are swapped.
158 * If boundary1 == boundary2, value of boundary1 will be returned
159 *
160 * Generating boundary values for Uint8:
161 * BoundaryValues(UINT8_MAX, 10, 20, True) -> [10,11,19,20]
162 * BoundaryValues(UINT8_MAX, 10, 20, False) -> [9,21]
163 * BoundaryValues(UINT8_MAX, 0, 15, True) -> [0, 1, 14, 15]
164 * BoundaryValues(UINT8_MAX, 0, 15, False) -> [16]
165 * BoundaryValues(UINT8_MAX, 0, 0xFF, False) -> [0], error set
166 *
167 * Generator works the same for other types of unsigned integers.
168 *
169 * \param maxValue The biggest value that is acceptable for this data type.
170 * For instance, for Uint8 -> 255, Uint16 -> 65536 etc.
171 * \param boundary1 defines lower boundary
172 * \param boundary2 defines upper boundary
173 * \param validDomain Generate only for valid domain (for the data type)
174 *
175 * \returns Returns a random boundary value for the domain or 0 in case of error
176 */
177static Uint64 SDLTest_GenerateUnsignedBoundaryValues(const Uint64 maxValue, Uint64 boundary1, Uint64 boundary2, bool validDomain)
178{
179 Uint64 b1, b2;
180 Uint64 delta;
181 Uint64 tempBuf[4];
182 Uint8 index;
183
184 /* Maybe swap */
185 if (boundary1 > boundary2) {
186 b1 = boundary2;
187 b2 = boundary1;
188 } else {
189 b1 = boundary1;
190 b2 = boundary2;
191 }
192
193 index = 0;
194 if (validDomain == true) {
195 if (b1 == b2) {
196 return b1;
197 }
198
199 /* Generate up to 4 values within bounds */
200 delta = b2 - b1;
201 if (delta < 4) {
202 do {
203 tempBuf[index] = b1 + index;
204 index++;
205 } while (index < delta);
206 } else {
207 tempBuf[index] = b1;
208 index++;
209 tempBuf[index] = b1 + 1;
210 index++;
211 tempBuf[index] = b2 - 1;
212 index++;
213 tempBuf[index] = b2;
214 index++;
215 }
216 } else {
217 /* Generate up to 2 values outside of bounds */
218 if (b1 > 0) {
219 tempBuf[index] = b1 - 1;
220 index++;
221 }
222
223 if (b2 < maxValue) {
224 tempBuf[index] = b2 + 1;
225 index++;
226 }
227 }
228
229 if (index == 0) {
230 /* There are no valid boundaries */
231 SDL_Unsupported();
232 return 0;
233 }
234
235 return tempBuf[SDLTest_RandomUint8() % index];
236}
237
238Uint8 SDLTest_RandomUint8BoundaryValue(Uint8 boundary1, Uint8 boundary2, bool validDomain)
239{
240 /* max value for Uint8 */
241 const Uint64 maxValue = UCHAR_MAX;
242 return (Uint8)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
243 (Uint64)boundary1, (Uint64)boundary2,
244 validDomain);
245}
246
247Uint16 SDLTest_RandomUint16BoundaryValue(Uint16 boundary1, Uint16 boundary2, bool validDomain)
248{
249 /* max value for Uint16 */
250 const Uint64 maxValue = USHRT_MAX;
251 return (Uint16)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
252 (Uint64)boundary1, (Uint64)boundary2,
253 validDomain);
254}
255
256Uint32 SDLTest_RandomUint32BoundaryValue(Uint32 boundary1, Uint32 boundary2, bool validDomain)
257{
258/* max value for Uint32 */
259#if ((ULONG_MAX) == (UINT_MAX))
260 const Uint64 maxValue = ULONG_MAX;
261#else
262 const Uint64 maxValue = UINT_MAX;
263#endif
264 return (Uint32)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
265 (Uint64)boundary1, (Uint64)boundary2,
266 validDomain);
267}
268
269Uint64 SDLTest_RandomUint64BoundaryValue(Uint64 boundary1, Uint64 boundary2, bool validDomain)
270{
271 /* max value for Uint64 */
272 const Uint64 maxValue = UINT64_MAX;
273 return SDLTest_GenerateUnsignedBoundaryValues(maxValue,
274 boundary1, boundary2,
275 validDomain);
276}
277
278/**
279 * Generates a signed boundary value between the given boundaries.
280 * Boundary values are inclusive. See the examples below.
281 * If boundary2 < boundary1, the values are swapped.
282 * If boundary1 == boundary2, value of boundary1 will be returned
283 *
284 * Generating boundary values for Sint8:
285 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, True) -> [-10,-9,19,20]
286 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, False) -> [-11,21]
287 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -30, -15, True) -> [-30, -29, -16, -15]
288 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 15, False) -> [16]
289 * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 127, False) -> [0], error set
290 *
291 * Generator works the same for other types of signed integers.
292 *
293 * \param minValue The smallest value that is acceptable for this data type.
294 * For instance, for Uint8 -> -127, etc.
295 * \param maxValue The biggest value that is acceptable for this data type.
296 * For instance, for Uint8 -> 127, etc.
297 * \param boundary1 defines lower boundary
298 * \param boundary2 defines upper boundary
299 * \param validDomain Generate only for valid domain (for the data type)
300 *
301 * \returns Returns a random boundary value for the domain or 0 in case of error
302 */
303static Sint64 SDLTest_GenerateSignedBoundaryValues(const Sint64 minValue, const Sint64 maxValue, Sint64 boundary1, Sint64 boundary2, bool validDomain)
304{
305 Sint64 b1, b2;
306 Sint64 delta;
307 Sint64 tempBuf[4];
308 Uint8 index;
309
310 /* Maybe swap */
311 if (boundary1 > boundary2) {
312 b1 = boundary2;
313 b2 = boundary1;
314 } else {
315 b1 = boundary1;
316 b2 = boundary2;
317 }
318
319 index = 0;
320 if (validDomain == true) {
321 if (b1 == b2) {
322 return b1;
323 }
324
325 /* Generate up to 4 values within bounds */
326 delta = b2 - b1;
327 if (delta < 4) {
328 do {
329 tempBuf[index] = b1 + index;
330 index++;
331 } while (index < delta);
332 } else {
333 tempBuf[index] = b1;
334 index++;
335 tempBuf[index] = b1 + 1;
336 index++;
337 tempBuf[index] = b2 - 1;
338 index++;
339 tempBuf[index] = b2;
340 index++;
341 }
342 } else {
343 /* Generate up to 2 values outside of bounds */
344 if (b1 > minValue) {
345 tempBuf[index] = b1 - 1;
346 index++;
347 }
348
349 if (b2 < maxValue) {
350 tempBuf[index] = b2 + 1;
351 index++;
352 }
353 }
354
355 if (index == 0) {
356 /* There are no valid boundaries */
357 SDL_Unsupported();
358 return minValue;
359 }
360
361 return tempBuf[SDLTest_RandomUint8() % index];
362}
363
364Sint8 SDLTest_RandomSint8BoundaryValue(Sint8 boundary1, Sint8 boundary2, bool validDomain)
365{
366 /* min & max values for Sint8 */
367 const Sint64 maxValue = SCHAR_MAX;
368 const Sint64 minValue = SCHAR_MIN;
369 return (Sint8)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
370 (Sint64)boundary1, (Sint64)boundary2,
371 validDomain);
372}
373
374Sint16 SDLTest_RandomSint16BoundaryValue(Sint16 boundary1, Sint16 boundary2, bool validDomain)
375{
376 /* min & max values for Sint16 */
377 const Sint64 maxValue = SHRT_MAX;
378 const Sint64 minValue = SHRT_MIN;
379 return (Sint16)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
380 (Sint64)boundary1, (Sint64)boundary2,
381 validDomain);
382}
383
384Sint32 SDLTest_RandomSint32BoundaryValue(Sint32 boundary1, Sint32 boundary2, bool validDomain)
385{
386/* min & max values for Sint32 */
387#if ((ULONG_MAX) == (UINT_MAX))
388 const Sint64 maxValue = LONG_MAX;
389 const Sint64 minValue = LONG_MIN;
390#else
391 const Sint64 maxValue = INT_MAX;
392 const Sint64 minValue = INT_MIN;
393#endif
394 return (Sint32)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
395 (Sint64)boundary1, (Sint64)boundary2,
396 validDomain);
397}
398
399Sint64 SDLTest_RandomSint64BoundaryValue(Sint64 boundary1, Sint64 boundary2, bool validDomain)
400{
401 /* min & max values for Sint64 */
402 const Sint64 maxValue = INT64_MAX;
403 const Sint64 minValue = INT64_MIN;
404 return SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
405 boundary1, boundary2,
406 validDomain);
407}
408
409float SDLTest_RandomUnitFloat(void)
410{
411 return SDL_randf_r(&rndContext);
412}
413
414float SDLTest_RandomFloat(void)
415{
416 union
417 {
418 float f;
419 Uint32 v32;
420 } value;
421
422 do {
423 value.v32 = SDLTest_RandomUint32();
424 } while (SDL_isnanf(value.f) || SDL_isinff(value.f));
425
426 return value.f;
427}
428
429double SDLTest_RandomUnitDouble(void)
430{
431 return (double)(SDLTest_RandomUint64() >> (64-53)) * 0x1.0p-53;
432}
433
434double SDLTest_RandomDouble(void)
435{
436 union
437 {
438 double d;
439 Uint64 v64;
440 } value;
441
442 do {
443 value.v64 = SDLTest_RandomUint64();
444 } while (SDL_isnan(value.d) || SDL_isinf(value.d));
445
446 return value.d;
447}
448
449char *SDLTest_RandomAsciiString(void)
450{
451 return SDLTest_RandomAsciiStringWithMaximumLength(255);
452}
453
454char *SDLTest_RandomAsciiStringWithMaximumLength(int maxLength)
455{
456 int size;
457
458 if (maxLength < 1) {
459 SDL_InvalidParamError("maxLength");
460 return NULL;
461 }
462
463 size = (SDLTest_RandomUint32() % (maxLength + 1));
464 if (size == 0) {
465 size = 1;
466 }
467 return SDLTest_RandomAsciiStringOfSize(size);
468}
469
470char *SDLTest_RandomAsciiStringOfSize(int size)
471{
472 char *string;
473 int counter;
474
475 if (size < 1) {
476 SDL_InvalidParamError("size");
477 return NULL;
478 }
479
480 string = (char *)SDL_malloc((size + 1) * sizeof(char));
481 if (!string) {
482 return NULL;
483 }
484
485 for (counter = 0; counter < size; ++counter) {
486 string[counter] = (char)SDLTest_RandomIntegerInRange(32, 126);
487 }
488
489 string[counter] = '\0';
490
491 fuzzerInvocationCounter++;
492
493 return string;
494}
495