aboutsummaryrefslogtreecommitdiff
path: root/stdlib
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-11-03 16:10:03 -0500
committerBruce Hill <bruce@bruce-hill.com>2024-11-03 16:10:03 -0500
commit7ccb7a8a9b8f10d30fd8fca01e849dcad354a855 (patch)
tree1f1bf3c3617f790f80598fa82b8fadafc576e853 /stdlib
parent39a58bc129fd9461d54b837bc1650c4c650aa333 (diff)
Use an RNG parameter for array:random(), array:shuffle(),
array:shuffled()
Diffstat (limited to 'stdlib')
-rw-r--r--stdlib/arrays.c35
-rw-r--r--stdlib/arrays.h8
-rw-r--r--stdlib/integers.c12
-rw-r--r--stdlib/integers.h1
4 files changed, 37 insertions, 19 deletions
diff --git a/stdlib/arrays.c b/stdlib/arrays.c
index 6b61f5b5..552fb4cb 100644
--- a/stdlib/arrays.c
+++ b/stdlib/arrays.c
@@ -249,36 +249,51 @@ public Array_t Array$sorted(Array_t arr, Closure_t comparison, int64_t padded_it
return arr;
}
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wstack-protector"
-public void Array$shuffle(Array_t *arr, int64_t padded_item_size)
+static uint64_t random_range(Closure_t rng, uint64_t upper_bound)
+{
+ if (upper_bound < 2)
+ return 0;
+
+ // This approach is taken from arc4random_uniform()
+ uint64_t min = -upper_bound % upper_bound;
+ uint64_t r;
+ for (;;) {
+ r = ((uint64_t(*)(void*))rng.fn)(rng.userdata);
+ if (r >= min)
+ break;
+ }
+
+ return r % upper_bound;
+}
+
+public void Array$shuffle(Array_t *arr, Closure_t rng, int64_t padded_item_size)
{
if (arr->data_refcount != 0 || (int64_t)arr->stride != padded_item_size)
Array$compact(arr, padded_item_size);
char tmp[padded_item_size];
for (int64_t i = arr->length-1; i > 1; i--) {
- int64_t j = arc4random_uniform(i+1);
+ int64_t j = (int64_t)random_range(rng, (uint64_t)(i+1));
memcpy(tmp, arr->data + i*padded_item_size, (size_t)padded_item_size);
memcpy((void*)arr->data + i*padded_item_size, arr->data + j*padded_item_size, (size_t)padded_item_size);
memcpy((void*)arr->data + j*padded_item_size, tmp, (size_t)padded_item_size);
}
}
-#pragma GCC diagnostic pop
-public Array_t Array$shuffled(Array_t arr, int64_t padded_item_size)
+public Array_t Array$shuffled(Array_t arr, Closure_t rng, int64_t padded_item_size)
{
Array$compact(&arr, padded_item_size);
- Array$shuffle(&arr, padded_item_size);
+ Array$shuffle(&arr, rng, padded_item_size);
return arr;
}
-public void *Array$random(Array_t arr)
+public void *Array$random(Array_t arr, Closure_t rng)
{
if (arr.length == 0)
return NULL; // fail("Cannot get a random item from an empty array!");
- int64_t index = arc4random_uniform(arr.length);
- return arr.data + arr.stride*index;
+
+ uint64_t index = random_range(rng, (uint64_t)arr.length);
+ return arr.data + arr.stride*(int64_t)index;
}
public Table_t Array$counts(Array_t arr, const TypeInfo_t *type)
diff --git a/stdlib/arrays.h b/stdlib/arrays.h
index 03f00d49..5d452d30 100644
--- a/stdlib/arrays.h
+++ b/stdlib/arrays.h
@@ -70,10 +70,10 @@ Int_t Array$find(Array_t arr, void *item, const TypeInfo_t *type);
Int_t Array$first(Array_t arr, Closure_t predicate);
void Array$sort(Array_t *arr, Closure_t comparison, int64_t padded_item_size);
Array_t Array$sorted(Array_t arr, Closure_t comparison, int64_t padded_item_size);
-void Array$shuffle(Array_t *arr, int64_t padded_item_size);
-Array_t Array$shuffled(Array_t arr, int64_t padded_item_size);
-void *Array$random(Array_t arr);
-#define Array$random_value(arr, t) ({ Array_t _arr = arr; if (_arr.length == 0) fail("Cannot get a random value from an empty array!"); *(t*)Array$random(_arr); })
+void Array$shuffle(Array_t *arr, Closure_t rng, int64_t padded_item_size);
+Array_t Array$shuffled(Array_t arr, Closure_t rng, int64_t padded_item_size);
+void *Array$random(Array_t arr, Closure_t rng);
+#define Array$random_value(arr, rng, t) ({ Array_t _arr = arr; if (_arr.length == 0) fail("Cannot get a random value from an empty array!"); *(t*)Array$random(_arr, rng); })
Array_t Array$sample(Array_t arr, Int_t n, Array_t weights, int64_t padded_item_size);
Table_t Array$counts(Array_t arr, const TypeInfo_t *type);
void Array$clear(Array_t *array);
diff --git a/stdlib/integers.c b/stdlib/integers.c
index f604aa53..8d305daf 100644
--- a/stdlib/integers.c
+++ b/stdlib/integers.c
@@ -440,14 +440,16 @@ public const TypeInfo_t Int$info = {
} \
return bit_array; \
} \
+ public c_type KindOfInt ## $full_random(void) { \
+ c_type r; \
+ arc4random_buf(&r, sizeof(r)); \
+ return r; \
+ } \
public c_type KindOfInt ## $random(c_type min, c_type max) { \
if (min > max) fail("Random minimum value (%ld) is larger than the maximum value (%ld)", min, max); \
if (min == max) return min; \
- if (min == min_val && max == max_val) { \
- c_type r; \
- arc4random_buf(&r, sizeof(r)); \
- return r; \
- } \
+ if (min == min_val && max == max_val) \
+ return KindOfInt ## $full_random(); \
uint64_t range = (uint64_t)max - (uint64_t)min + 1; \
uint64_t min_r = -range % range; \
uint64_t r; \
diff --git a/stdlib/integers.h b/stdlib/integers.h
index e7b5b0e1..04699162 100644
--- a/stdlib/integers.h
+++ b/stdlib/integers.h
@@ -35,6 +35,7 @@
Text_t type_name ## $octal(c_type i, Int_t digits, bool prefix); \
Array_t type_name ## $bits(c_type x); \
c_type type_name ## $random(c_type min, c_type max); \
+ c_type type_name ## $full_random(void); \
to_attr Range_t type_name ## $to(c_type from, c_type to); \
PUREFUNC Optional ## type_name ## _t type_name ## $from_text(Text_t text); \
MACROLIKE PUREFUNC c_type type_name ## $clamped(c_type x, c_type min, c_type max) { \