Level A

Level A is the small freestanding vocabulary used before FALL talks about operating systems, boards, files, sockets, or devices. This page teaches only two Level A subjects: borrowed views and deterministic bundles.

What A View Means

A view is a pointer plus a size. It does not allocate memory, it does not copy the data, and it does not keep the original object alive. The caller is responsible for making sure the referenced storage still exists while the view is used.

a::u8 bytes[128]{};

a::view<a::u8> writable{bytes};
a::view<const a::u8> first_packet = writable.first(32);

a::view<T> stores the element count at runtime. It is useful when the buffer size is known only by the code path that passes it in.

a::view<T>

Use a::view<T> when a function needs to read or write a continuous range without owning it.

void clear(a::view<a::u8> out) noexcept {
    for (a::usize i = 0; i < out.size(); ++i) {
        out[i] = 0;
    }
}

a::u8 packet[64]{};
clear(a::view<a::u8>{packet});

Useful operations include size(), empty(), data(), first(n), last(n), drop(n), slice(pos, count), and bytes(). Slicing clamps to the available range instead of allocating a new object.

A view of const char from a string literal excludes the final zero terminator from its logical size.

constexpr a::view<const char> text{"fall"};
static_assert(text.size() == 4);

a::view_n<T, N>

a::view_n<T, N> is also non-owning, but the element count is part of the type. Use it when the exact size is part of the contract.

a::u8 block_storage[32]{};
a::view_n<a::u8, 32> block{block_storage};

block.at<0>() = 0xff;

at<I>() checks the index at compile time. Constructing view_n from a dynamic view is fail-safe: the result is valid only when the dynamic view has exactly N elements.

a::view<a::u8> dynamic{block_storage, 16};
a::view_n<a::u8, 32> exact{dynamic};

if (!exact.dynamic_view()) {
    // dynamic.size() was not 32
}

a::zview<T>

a::zview<T> is a borrowed zero-terminated sequence. Its logical size excludes the terminator, and explicit pointer plus length construction accepts only storage where data()[size()] == T{}.

constexpr a::zview<const char> name{"config.bin"};
static_assert(name.size() == 10);

const char raw[] = {'o', 'k', '\0'};
a::zview<const char> valid{raw, 2};

Use this when the backend needs a C-style zero-terminated string but application code still wants an explicit length-aware type.

a::zview_n<T, N>

a::zview_n<T, N> combines both ideas: the sequence is zero-terminated, and the non-terminator count is encoded in the type.

constexpr a::zview_n<const char, 4> word{"fall"};
static_assert(word.len() == 4);

a::view<const char> without_terminator = word.unterminated_view();

This is useful for small protocol names, fixed file names, or compile-time checked strings passed to code that must still see the zero terminator.

View Rules

Why Bundles Exist

Embedded and freestanding code often cannot rely on a heap. a::bundle lets an application declare the storage it needs at compile time and materialize it as one object with normal C++ layout and alignment.

A bundle is not a map, registry, allocator, or runtime object store. It is typed storage chosen by the application.

Declaring A Bundle

A bundle specification contains a permanent resource list. Each resource has a type and, for arrays and buffers, a size. Tags give resources compile-time names.

struct PeerState final {
    a::u64 id;
    a::u32 last_seen_tick;
};

struct NetworkState final {
    a::usize connected_peers;
};

struct peer_table final {};
struct receive_packet final {};
struct send_packet final {};

struct NetworkBundle final {
    using permanent = a::resources<
        a::array<PeerState, 128, peer_table>,
        a::object<NetworkState>,
        a::buffer<receive_packet, 64 * 1024>,
        a::buffer<send_packet, 64 * 1024>
    >;
};

static a::bundle<NetworkBundle> network{};

Using A Bundle

Resources are retrieved by tag and kind. Arrays and buffers return fixed-size views. Objects return references.

a::view_n<PeerState, 128> peers = network.array<peer_table>();
a::view_n<a::u8, 64 * 1024> receive = network.buffer<receive_packet>();
NetworkState& state = network.object<NetworkState>();

peers.at<0>().id = 42;
receive.at<0>() = 0xff;
state.connected_peers = 1;

If you request an unknown tag or ask for a buffer as an object, the program fails at compile time. Duplicate tags in one bundle region are also rejected.

Bundle Benefits

static_assert(a::bundle<NetworkBundle>::size_bytes() >= 128 * sizeof(PeerState));
static_assert(a::bundle<NetworkBundle>::alignment_bytes() >= alignof(NetworkState));

Bundle Costs