Elvelt

Standard Library

`empl_runtime.h` — runtime utilities

empl_runtime.h — runtime utilities

The empl_runtime.h header (164 lines, MPL_Standard/System/empl_runtime.h) provides the runtime infrastructure that all language frontends use through their adapters. It declares:

  • mpl_os — OS / platform info
  • mpl_crypto_random_uuid() — RFC 4122 v4 UUID
  • mpl_crypto_random_bytes(n) — hex-encoded random bytes
  • mpl_iso8601_to_time_t(s) — ISO 8601 → Unix time
  • mpl_process_exit(code), mpl_process_cwd(), mpl_process_chdir(path), mpl_process_nextTick(fn) — process helpers

It is intentionally language-agnostic and free of mpl_value / JS interop dependencies (so it can be included by empl_core.h and also by standalone tests).

Includes

// empl_runtime.h:18
#include <cstdlib>
#include <functional>
#include <string>
#include <chrono>
#include <cstdio>
#include <random>
#include <sstream>
#include <iomanip>

Plus platform-conditional includes:

// empl_runtime.h:27
#if defined(_WIN32)
  #ifndef MPL_PLATFORM_WINDOWS
    #define MPL_PLATFORM_WINDOWS 1
  #endif
#else
  #ifndef MPL_PLATFORM_POSIX
    #define MPL_PLATFORM_POSIX 1
  #endif
  #include <unistd.h>
  #if defined(__APPLE__)
    #ifndef MPL_PLATFORM_DARWIN
      #define MPL_PLATFORM_DARWIN 1
    #endif
  #endif
#endif

The MPL_PLATFORM_* macros can be used in downstream code for conditional compilation.


mpl_os — OS / platform info

// empl_runtime.h:44
struct mpl_os {
    static std::string platform();
    static std::string arch();
    static std::string hostname();
    static std::string homedir();
};

All four methods are static. The struct is a namespace-like container; do not instantiate.

platform()

// empl_runtime.h:45
static std::string platform() {
#if defined(MPL_PLATFORM_WINDOWS)
    return "win32";
#elif defined(MPL_PLATFORM_DARWIN)
    return "darwin";
#else
    return "linux";
#endif
}

Returns one of "win32", "darwin", "linux". Matches the Environment.GetPlatform() stdlib function (which lives in mpl::System::Environment).

Source mapping:

Source Call
JS process.platform mpl_os::platform()
Python sys.platform mpl_os::platform()
Ruby RUBY_PLATFORM mpl_os::platform()

arch()

// empl_runtime.h:54
static std::string arch() {
#if defined(__aarch64__) || defined(_M_ARM64)
    return "arm64";
#elif defined(__x86_64) || defined(_M_X64)
    return "x64";
#else
    return std::string(sizeof(void*) == 8 ? "x64" : "x86");
#endif
}

Returns "arm64", "x64", or "x86" (32-bit fallback).

Source mapping:

Source Call
JS process.arch mpl_os::arch()
Python platform.machine() mpl_os::arch()
Go runtime.GOARCH mpl_os::arch()

hostname()

// empl_runtime.h:63
static std::string hostname() {
    char buf[256] = {0};
#if defined(MPL_PLATFORM_WINDOWS)
    // Minimal fallback: not as feature-complete as gethostname
    return std::string("windows-host");
#else
    if (gethostname(buf, sizeof(buf) - 1) == 0) {
        return std::string(buf);
    }
    return std::string("unknown");
#endif
}

Returns the system hostname. POSIX uses gethostname(2). Windows fallback returns the placeholder "windows-host" (Windows gethostname requires winsock linkage which is avoided here for header simplicity).

Source mapping:

Source Call
JS os.hostname() mpl_os::hostname()
Python socket.gethostname() mpl_os::hostname()
Go os.Hostname() mpl_os::hostname()

homedir()

// empl_runtime.h:75
static std::string homedir() {
#if defined(MPL_PLATFORM_WINDOWS)
    const char* drive = std::getenv("HOMEDRIVE");
    const char* path = std::getenv("HOMEPATH");
    if (drive && path) return std::string(drive) + path;
    return std::string("C:/Users/Default");
#else
    const char* home = std::getenv("HOME");
    if (home && *home) return std::string(home);
    return std::string("/tmp");
#endif
}

Returns the user's home directory. Windows combines HOMEDRIVE + HOMEPATH. POSIX uses HOME.

Source mapping:

Source Call
JS os.homedir() mpl_os::homedir()
Python os.path.expanduser("~") mpl_os::homedir()
Go os.UserHomeDir() mpl_os::homedir()

Crypto helpers

mpl_crypto_random_uuid()

// empl_runtime.h:90
inline std::string mpl_crypto_random_uuid();

Returns a 36-character RFC 4122 v4 UUID string (lowercase hex, hyphenated), e.g. "f47ac10b-58cc-4372-a567-0e02b2c3d479".

Implementation uses std::mt19937_64 seeded from std::random_device, then sets the version (4) and variant (10xx) bits per RFC 4122.

Source mapping:

Source Call
JS crypto.randomUUID() mpl_crypto_random_uuid()
Python uuid.uuid4() mpl_crypto_random_uuid()
Ruby SecureRandom.uuid mpl_crypto_random_uuid()

Security note. This uses std::mt19937_64 (Mersenne Twister), which is not a cryptographic PRNG. It is adequate for IDs but should NOT be used for session tokens, key material, or other security-sensitive purposes. For security-sensitive randomness, use OpenSSL RAND_bytes (available via empl_pqc.h infrastructure or direct OpenSSL include).

mpl_crypto_random_bytes(n)

// empl_runtime.h:113
inline std::string mpl_crypto_random_bytes(long long n);

Returns n random bytes as a hex-encoded string of length 2n. Negative n returns empty string.

Implementation uses std::mt19937_64 (same caveat as above — non-cryptographic).

Source mapping:

Source Call
JS crypto.getRandomValues(buf) (with hex encoding) mpl_crypto_random_bytes(n)
Python secrets.token_hex(n) mpl_crypto_random_bytes(n)
Go crypto/rand (with hex encoding) mpl_crypto_random_bytes(n)

ISO 8601 parser

mpl_iso8601_to_time_t(s)

// empl_runtime.h:130
inline long long mpl_iso8601_to_time_t(const std::string& s);

// empl_runtime.h:155 (overload for mpl_value)
inline long long mpl_iso8601_to_time_t(const mpl_value& v);

Best-effort parse of an ISO 8601 date string into Unix seconds (time_t). Returns -1 on parse failure.

Accepted formats:

Format Example
YYYY-MM-DD "2026-06-23"
YYYY-MM-DDTHH:MM:SSZ "2026-06-23T12:34:56Z" (the T…Z suffix is parsed but only the date portion is used for the time_t)

Validation rules:

  • Year must be in [1970, 9999].
  • Month must be in [1, 12].
  • Day must be in [1, 31] (no month-aware validation).
  • Returns -1 for empty input or unparseable strings.

Platform handling:

  • POSIX: uses timegm (UTC time).
  • Windows: uses _mkgmtime (the Windows equivalent).

Source mapping:

Source Call
JS Date.parse(s).getTime() / 1000 mpl_iso8601_to_time_t(s)
Python datetime.fromisoformat(s).timestamp() mpl_iso8601_to_time_t(s)
Go time.Parse(time.RFC3339, s) mpl_iso8601_to_time_t(s)

Process helpers (Phase 3)

mpl_process_exit(code)

// empl_runtime.h:160
inline void mpl_process_exit(int code) { std::exit(code); }

Terminates the process with the given exit code. Equivalent to Process.Exit(code) in the stdlib namespace.

mpl_process_cwd()

// empl_runtime.h:161
inline std::string mpl_process_cwd() { return "/"; }

Stub. Returns "/" always. The real implementation requires hooking main(int argc, char** argv) to capture the initial CWD. Use Environment.GetCurrentDirectory() (which wraps std::filesystem::current_path) instead.

mpl_process_chdir(path)

// empl_runtime.h:162
inline void mpl_process_chdir(const std::string&) {}

Stub. Empty body. Use Environment.SetCurrentDirectory(path) instead.

mpl_process_nextTick(fn)

// empl_runtime.h:163
inline void mpl_process_nextTick(std::function<void()> fn) { mpl_queue_microtask(std::move(fn)); }

Queues fn onto the EMPL microtask queue (defined in empl.h). Equivalent to JS process.nextTick(fn) — runs fn after the current operation completes but before the event loop continues.


Threading / async model

empl_runtime.h itself does not introduce threading primitives — those are inherited from empl_core.h (microtask queue, mutex, condition variable) and empl.h (event loop, microtask CV).

The mpl_crypto_random_uuid() and mpl_crypto_random_bytes() functions use thread_local std::mt19937_64 for thread-safety.


mpl_value overload

// empl_runtime.h:155
inline long long mpl_iso8601_to_time_t(const mpl_value& v) {
    return mpl_iso8601_to_time_t(mpl_to_string(v));
}

The mpl_value overload exists because generated code passes mpl_value (a std::variant) to stdlib functions. The overload stringifies via mpl_to_string and delegates to the std::string version. This is the canonical pattern for any mpl_runtime function that accepts a string — always provide BOTH overloads.


See also

  • pqc.md — post-quantum crypto (separate header, more advanced)
  • cpp-impl.md — C++ stdlib implementation
  • modules.md — full module list
  • headers.md — header hierarchy
  • empl.h lines 86-189 — mpl_throw_*, mpl_strict_mode_flag, mpl_microtask_queue, mpl_run_event_loop (the upstream infrastructure that mpl_process_nextTick builds on)