ajhahn.de
← Flash
Flash 79 lines
// mem — flibc's freestanding C-ABI `mem*` providers, ported to Flash from the
// hand-written Zig (the first `flibc` library leaf after the `tokenize` and
// `readline` cores).
//
// flibc payloads link without compiler-rt, so the C runtime helpers LLVM emits
// for certain loop idioms are not supplied. Once a payload exercises a real
// `#memcpy`-backed builder or a bare NUL scan, LLVM's loop-idiom recognizer
// lowers it to a `memcpy` / `strlen` call and the link fails on an undefined
// symbol. These three byte loops, named exactly `memcpy` / `memset` / `strlen`,
// close that loop: the idiom recognizer refuses to rewrite a recognized idiom
// into a call to the very function being compiled, so the loops are
// self-recursion-safe.
//
// First port to exercise a many-item pointer's mutability across the family: a
// many-item pointer is const-pointee by default in Flash, exactly like a slice
// (`[]T`) and a single pointer (`*T`). The copy sources read through bare `[*]u8`
// / `[*]u64` and the NUL scan through `[*:0]u8`; the writable destination carries
// `mut` (`[*]mut u8` / `[*]mut u64`), the one-word marker the slice and
// single-pointer forms already use. The single-item `*anyopaque` / `*mut
// anyopaque` distinction is the same: `*T` is a const pointee, `*mut T` a mutable
// one. The `///` doc comments are carried through verbatim; the core lowers to
// Zig whose token stream matches the reference modulo Flash's canonical layout
// (mandatory braces, dropped `//` comments).

/// memset(dst, c, n) — fill `n` bytes of `dst` with byte `c`. Byte
/// granular; the C ABI returns `dst`.
export fn memset(dst [*]mut u8, c i32, n_in u64) [*]mut u8 {
    var n = n_in
    var p = dst
    const byte u8 = #truncate(#as(u32, #bitCast(c)))
    while n != 0 {
        p[0] = byte
        p += 1
        n -= 1
    }
    return dst
}

/// memcpy(dst, src, bytes) — copy `bytes` bytes from `src` to `dst`
/// (non-overlapping). Copies 8 bytes at a time when both operands are
/// 8-aligned, then drains the tail byte-wise. The C ABI returns `dst`.
export fn memcpy(dst *mut anyopaque, src *anyopaque, bytes u64) *mut anyopaque {
    var d [*]mut u8 = #ptrCast(dst)
    var s [*]u8 = #ptrCast(src)
    var n = bytes

    if #intFromPtr(d) % 8 == 0 && #intFromPtr(s) % 8 == 0 {
        var d64 [*]mut u64 = #ptrCast(#alignCast(d))
        var s64 [*]u64 = #ptrCast(#alignCast(s))
        while n >= 8 {
            d64[0] = s64[0]
            d64 += 1
            s64 += 1
            n -= 8
        }
        d = #ptrCast(d64)
        s = #ptrCast(s64)
    }

    while n > 0 {
        d[0] = s[0]
        d += 1
        s += 1
        n -= 1
    }
    return dst
}

/// strlen(s) — length of the NUL-terminated string at `s`, excluding the
/// terminator. The lone scan the idiom recognizer would otherwise route
/// to an external `strlen`; defining it here closes the loop.
export fn strlen(s [*:0]u8) u64 {
    var n u64 = 0
    while s[n] != 0 {
        n += 1
    }
    return n
}