std1.0.0-beta-7 SafeC docs

Source collections/slice.h

View in the repository

// SafeC Standard Library — Slice<T>: a bounds-checked view of T elements
//
//   int arr[8];
//   struct Slice<int> s = std::Slice::from(arr[0..8]);
//   int third = s.get(2UL).default(0);
//   struct Slice<int> mid = s.sub(2UL, 5UL);   // elements 2,3,4
//   []int raw = s.as_slice();                  // back to the built-in slice
//
// The language's own []T is the buffer type std's APIs take and return;
// Slice<T> is a library view on top of it with checked get/set/sub, and
// the way to view a heap array from 'new T[n]' (Slice::of(first, n): the
// caller vouches for n) or allocate one (Slice::alloc). A Slice doesn't own
// its elements unless it came from Slice::alloc() (then free() releases
// them). The arr_* helpers below work on a []T directly.
#pragma once
#include <std/mem.h>
#include <std/panic.h>

namespace std {

generic<T>
struct Slice {
    ?&T           ptr;   // first element; null for an empty slice
    unsigned long len;   // element count

    static struct Slice<T> from([]T s);                      // view of a built-in slice
    static struct Slice<T> of(&T first, unsigned long len);  // view of len elements at first (a 'new T[len]')
    static struct Slice<T> alloc(unsigned long len);         // zeroed heap slice; empty on OOM

    int             in_bounds(unsigned long idx) const;
    ?&T             get(unsigned long idx) const;     // null if out of range
    &T              at(unsigned long idx) const;      // panics if out of range
    int             set(unsigned long idx, T val);    // 0 if out of range
    struct Slice<T> sub(unsigned long start, unsigned long end) const; // [start, end); empty if invalid
    unsigned long   length() const;
    int             is_empty() const;
    []T             as_slice() const;                  // the same elements as a built-in slice
    void            free();                            // only for Slice::alloc() slices
};

generic<T>
struct Slice<T> Slice::from([]T s) {
    struct Slice<T> v;
    v.ptr = null;
    v.len = 0UL;
    if (s.len != 0UL) { unsafe { v.ptr = (?&T)s.ptr; } v.len = s.len; }
    return v;
}

generic<T>
[]T Slice::as_slice() const {
    unsafe { return ((T*)self.ptr)[0..(self.ptr.is_null() ? 0UL : self.len)]; }
}

generic<T>
struct Slice<T> Slice::of(&T first, unsigned long len) {
    struct Slice<T> s;
    s.ptr = first;
    s.len = len;
    return s;
}

// Zero-initialised heap slice of 'len' elements; empty (null) on OOM.
generic<T>
struct Slice<T> Slice::alloc(unsigned long len) {
    struct Slice<T> s;
    s.ptr = null;
    s.len = 0UL;
    ?&T p = null;
    unsafe { p = (?&T)alloc_zeroed_raw_(checked_mul_size(len, sizeof(T))); }
    if (!p.is_null()) { s.ptr = p; s.len = len; }
    return s;
}

generic<T>
int Slice::in_bounds(unsigned long idx) const { return idx < self.len; }

generic<T>
?&T Slice::get(unsigned long idx) const {
    if (idx >= self.len) return null;
    match (self.ptr) {
        case some(p): unsafe { return (?&T)&p[idx]; }
        case null: { }
    }
    return null;
}

generic<T>
&T Slice::at(unsigned long idx) const {
    return expect_ref(self.get(idx), "Slice::at: index out of range");
}

generic<T>
int Slice::set(unsigned long idx, T val) {
    match (self.get(idx)) {
        case some(e): { *e = val; return 1; }
        case null: { }
    }
    return 0;
}

generic<T>
struct Slice<T> Slice::sub(unsigned long start, unsigned long end) const {
    struct Slice<T> out;
    out.ptr = null;
    out.len = 0UL;
    if (start < end && end <= self.len) {
        out.ptr = self.get(start);
        out.len = end - start;
    }
    return out;
}

generic<T>
unsigned long Slice::length() const { return self.len; }

generic<T>
int Slice::is_empty() const { return self.len == 0UL; }

generic<T>
void Slice::free() {
    unsafe { dealloc((?&heap T)self.ptr); }
    self.ptr = null;
    self.len = 0UL;
}

// ── Helpers on a built-in slice ([]T) ───────────────────────────────────
// s[i] already traps when out of range; arr_at / arr_set are the
// non-panicking forms.

generic<T>
?&T arr_at([]T s, unsigned long idx) {
    if (idx >= s.len) return null;
    return &s[idx];
}

generic<T>
int arr_set([]T s, unsigned long idx, T val) {
    if (idx >= s.len) return 0;
    s[idx] = val;
    return 1;
}

generic<T>
void arr_fill([]T s, T val) {
    unsigned long i = 0UL;
    while (i < s.len) { s[i] = val; i = i + 1UL; }
}

// Copies min(dst.len, src.len) elements; returns that count.
generic<T>
unsigned long arr_copy([]T dst, []const T src) {
    unsigned long n = dst.len < src.len ? dst.len : src.len;
    unsigned long i = 0UL;
    while (i < n) { dst[i] = src[i]; i = i + 1UL; }
    return n;
}

// Smallest / largest element by '<'; null for an empty slice.
generic<T>
?T arr_min([]const T s) {
    if (s.len == 0UL) return null;
    T best = s[0];
    unsigned long i = 1UL;
    while (i < s.len) { if (s[i] < best) best = s[i]; i = i + 1UL; }
    return best;
}

generic<T>
?T arr_max([]const T s) {
    if (s.len == 0UL) return null;
    T best = s[0];
    unsigned long i = 1UL;
    while (i < s.len) { if (s[i] > best) best = s[i]; i = i + 1UL; }
    return best;
}

generic<T>
void arr_reverse([]T s) {
    if (s.len < 2UL) return;
    unsigned long lo = 0UL;
    unsigned long hi = s.len - 1UL;
    while (lo < hi) {
        T tmp = s[lo];
        s[lo] = s[hi];
        s[hi] = tmp;
        lo = lo + 1UL;
        hi = hi - 1UL;
    }
}

} // namespace std