Skip to content

Random API

The random module exposes an opaque deterministic pseudo-random number generator (PRNG).

Type

typedef struct GrimoireRandom_t GrimoireRandom_t;
#define GrimoireRandom GrimoireRandom_t*
  • GrimoireRandom is a pointer handle.
  • The internal state layout is intentionally hidden.

Lifecycle Functions

GrimoireRandom_CreateNew

GRIMOIRE_API GrimoireRandom GrimoireRandom_CreateNew();

Creates a new instance with a non-deterministic seed source.

  • Returns: a valid GrimoireRandom or NULL on allocation/initialization failure.

GrimoireRandom_CreateSeed

GRIMOIRE_API GrimoireRandom GrimoireRandom_CreateSeed(hash_t seed);

Creates a deterministic instance.

  • Parameters: seed deterministic 32-bit seed (hash_t).
  • Returns: a valid GrimoireRandom or NULL on failure.

GrimoireRandom_Destroy

GRIMOIRE_API void GrimoireRandom_Destroy(GrimoireRandom random);

Releases resources owned by the instance.

  • Safe with NULL.
  • Must be called exactly once for each successful Create* result.

Sampling Functions

GrimoireRandom_Next

GRIMOIRE_API int32_t GrimoireRandom_Next(GrimoireRandom random);

Returns a pseudo-random signed 32-bit integer.

  • Interval: [0, INT32_MAX)

GrimoireRandom_NextRange

GRIMOIRE_API int32_t GrimoireRandom_NextRange(GrimoireRandom random, int32_t min, int32_t max);

Returns a pseudo-random integer in [min, max).

  • Precondition: min < max.

GrimoireRandom_NextMax

GRIMOIRE_API int32_t GrimoireRandom_NextMax(GrimoireRandom random, int32_t max);

Equivalent to GrimoireRandom_NextRange(random, 0, max).

  • Precondition: max > 0.

GrimoireRandom_NextDouble

GRIMOIRE_API double GrimoireRandom_NextDouble(GrimoireRandom random);

Returns a uniform floating-point value in [0.0, 1.0).

GrimoireRandom_NextBytes

GRIMOIRE_API void GrimoireRandom_NextBytes(GrimoireRandom random, void* buffer, size_t length);

Fills length bytes at buffer with pseudo-random data.

  • buffer must point to writable memory of at least length bytes.

State and Cloning Functions

GrimoireRandom_Serialize

GRIMOIRE_API void GrimoireRandom_Serialize(GrimoireRandom random, uint8_t* buffer);

Serializes current state into a caller-provided byte buffer.

  • Buffer size for this release: 228 bytes.
  • Serialized layout is implementation-defined.

GrimoireRandom_Deserialize

GRIMOIRE_API GrimoireRandom GrimoireRandom_Deserialize(const uint8_t* buffer);

Creates a new instance from serialized state.

  • Input buffer must come from GrimoireRandom_Serialize.
  • Returns: cloned state as new handle or NULL on failure.

GrimoireRandom_CloneInto

GRIMOIRE_API void GrimoireRandom_CloneInto(const GrimoireRandom source, GrimoireRandom destination);

Copies state from source into an existing destination instance.

GrimoireRandom_Clone

GRIMOIRE_API GrimoireRandom GrimoireRandom_Clone(const GrimoireRandom source);

Allocates and returns a new state-identical clone.

Determinism and Reproducibility

  • Identical seed and call sequence produce identical outputs.
  • Cloned or deserialized states continue sequence from the same point.
  • Mixing deterministic and non-deterministic creation in one path will break reproducibility.

Ownership and Safety Checklist

  • Always check Create*, Clone, and Deserialize for NULL.
  • Destroy every created handle.
  • Validate range inputs before calling NextRange.
  • Keep serialized state buffers version-paired with this release.
  • Do not use for cryptographic purposes.

Example: Checkpoint and Resume Stream

#include <stddef.h>
#include <grimoire-pcg/random.h>

enum { GRIMOIRE_RANDOM_STATE_SIZE = 228 };

typedef struct RngCheckpoint
{
    uint32_t version;
    uint32_t size;
    uint8_t state[GRIMOIRE_RANDOM_STATE_SIZE];
} RngCheckpoint;

static bool checkpoint_save(GrimoireRandom rng, RngCheckpoint* outCp)
{
    if (!rng || !outCp) {
        return false;
    }

    outCp->version = 1;
    outCp->size = GRIMOIRE_RANDOM_STATE_SIZE;
    GrimoireRandom_Serialize(rng, outCp->state);
    return true;
}

static GrimoireRandom checkpoint_load(const RngCheckpoint* cp)
{
    if (!cp || cp->version != 1 || cp->size != GRIMOIRE_RANDOM_STATE_SIZE) {
        return NULL;
    }

    return GrimoireRandom_Deserialize(cp->state);
}

void sample_resume(void)
{
    RngCheckpoint cp;

    GrimoireRandom a = GrimoireRandom_CreateSeed(42);
    if (!a) return;

    (void)GrimoireRandom_Next(a);
    if (!checkpoint_save(a, &cp)) {
        GrimoireRandom_Destroy(a);
        return;
    }

    GrimoireRandom b = checkpoint_load(&cp);
    if (b) {
        int32_t v1 = GrimoireRandom_Next(a);
        int32_t v2 = GrimoireRandom_Next(b);
        (void)v1;
        (void)v2;
        GrimoireRandom_Destroy(b);
    }

    GrimoireRandom_Destroy(a);
}

Example: Branching a Deterministic Stream

#include <grimoire-pcg/random.h>

void generate_two_paths(hash_t seed)
{
    GrimoireRandom base = GrimoireRandom_CreateSeed(seed);
    if (!base) return;

    GrimoireRandom branchA = GrimoireRandom_Clone(base);
    GrimoireRandom branchB = GrimoireRandom_Clone(base);

    if (branchA && branchB) {
        int32_t a0 = GrimoireRandom_NextRange(branchA, 0, 100);
        int32_t b0 = GrimoireRandom_NextRange(branchB, 0, 100);
        (void)a0;
        (void)b0;
    }

    GrimoireRandom_Destroy(branchB);
    GrimoireRandom_Destroy(branchA);
    GrimoireRandom_Destroy(base);
}