ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
types.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file types.h
6 * @brief The primitive types every other file is written in, and the one place <stdint.h> and
7 * <stddef.h> appear.
8 *
9 * A library that targets xtensa, riscv, arm and c2000 cannot spend a type it has not established.
10 * `uint8_t` is optional in C11 - a conforming implementation omits it when it has no 8-bit type,
11 * which is exactly the c2000 case, where a byte is 16 bits wide. Naming the widths once here makes
12 * that one file to port and one place to look.
13 *
14 * **Widths are stated, never inherited.** `size_t` is the type this file exists to replace at an
15 * offset or a length. Its width is whatever the target's pointer happens to be, so the same
16 * expression is 32-bit index math on a device and 64-bit on the host that tests it - different
17 * emitted code from the same source, which is the one thing this library does not accept.
18 * ::proto_idx is a stated width the build can override, so the arithmetic has the same shape
19 * everywhere.
20 *
21 * **Narrow values are carried in the register width.** On every ISA in the target list an operation
22 * narrower than the register costs an extra mask or sign-extend to keep the unused half honest, so
23 * the cheapest 16-bit index on a 32-bit part is a 32-bit one truncated at the boundary, exactly as
24 * a 32-bit index on a 64-bit host is. ::proto_word is that register width.
25 *
26 * @author Douglas Quigg (dstroy0)
27 * @date 2026
28 */
29
30#ifndef PROTOCORE_TYPES_H
31#define PROTOCORE_TYPES_H
32
33#include <assert.h> // C11 spells static_assert here; C++ has it built in
34#include <stddef.h> // size_t, and the one place it enters the library
35#include <stdint.h>
36
37/// @name Fixed-width integers
38/// The exact-width names, aliased once. A port that lacks one of these replaces it here.
39/// @{
40typedef uint8_t proto_u8;
41typedef uint16_t proto_u16;
42typedef uint32_t proto_u32;
43typedef uint64_t proto_u64;
44typedef int8_t proto_i8;
45typedef int16_t proto_i16;
46typedef int32_t proto_i32;
47typedef int64_t proto_i64;
48/// @}
49
50/**
51 * @brief The truth value.
52 *
53 * Each language's own boolean, so this needs no header and cannot collide with a vendor `bool`
54 * macro - which several SDKs in the target list define, and which is why `<stdbool.h>` is not
55 * included here. Both spellings are one byte and both normalize any nonzero to 1, so a value
56 * crossing between the library and a caller compares the same on either side.
57 *
58 * The C++ arm is not conversion scaffolding: this type reaches the public surface through
59 * protocore.h, and the sketches that surface is written for are compiled as C++.
60 */
61#ifdef __cplusplus
62typedef bool proto_bool;
63#else
64typedef _Bool proto_bool;
65#endif
66
67#define PROTO_TRUE ((proto_bool)1) ///< the true value, spelled so a caller never writes a bare 1
68#define PROTO_FALSE ((proto_bool)0) ///< the false value
69
70/**
71 * @brief Cross-thread access to a ProtoCore field declared `_Atomic`: a slot state, a run flag.
72 *
73 * Every read is an acquire load and every write a release store, so the ordering is stated at each
74 * access rather than left to the default. MMgr keeps its ring atomics inside memoria_anularis and
75 * exposes none, so the fields ProtoCore owns itself are reached through these two.
76 *
77 * `atomic_load_explicit` and `atomic_store_explicit` are `<stdatomic.h>`, which a file using these
78 * includes itself: this header also reaches C++ sketches, where that header is not C11's.
79 */
80#define PROTO_ATOMIC_LOAD(p) atomic_load_explicit((p), memory_order_acquire)
81#define PROTO_ATOMIC_STORE(p, v) atomic_store_explicit((p), (v), memory_order_release) ///< release store of @p v
82
83/**
84 * @brief Give a header's declarations C linkage, so their symbol names carry no parameter types.
85 *
86 * Wraps the declarations between them in `extern "C"` under a C++ compiler, and expands to nothing
87 * under a C one. The sketches, ESP-IDF app code and Unity suites that call this library are C++;
88 * src/ is C.
89 */
90#ifdef __cplusplus
91#define PROTOCORE_BEGIN_DECLS \
92 extern "C" \
93 {
94#define PROTOCORE_END_DECLS }
95#else
96#define PROTOCORE_BEGIN_DECLS
97#define PROTOCORE_END_DECLS
98#endif
99
100/**
101 * @brief Give an enum the narrowest type its values fit in. Carried by every enum in this library.
102 *
103 * C11 leaves an enum's underlying type to the implementation, and the target list does not agree:
104 * xtensa, riscv32 and the host give an int, while arm-none-eabi defaults to the narrow form. An enum
105 * declared inside a struct is BSS, so without this the same connection slot is one size on an ESP32
106 * and another on a Cortex-M, and the footprint stops being a single number that can be computed
107 * before flashing. C23 spells the same thing `enum E : proto_u8`; until every compiler in the list
108 * is C23, the attribute is what states it.
109 *
110 * TI's compiler takes `--small_enum` on the command line and has no attribute for it, so this
111 * expands to nothing there and the assert below fails rather than the width being wrong quietly.
112 */
113#if defined(__GNUC__) || defined(__clang__)
114#define PROTO_ENUM_PACKED __attribute__((packed))
115#else
116#define PROTO_ENUM_PACKED
117#endif
118
119/**
120 * @brief The natural register width, as a type.
121 *
122 * What an index or a count is carried in while it is being worked on. Narrower arithmetic is not
123 * cheaper on these parts: it costs the mask that keeps the unused half correct.
124 */
125#if PROTO_WORD_BITS == 64
126typedef proto_u64 proto_word;
127#elif PROTO_WORD_BITS == 32
128typedef proto_u32 proto_word;
129#elif PROTO_WORD_BITS == 16
130typedef proto_u16 proto_word;
131#else
132#error "PROTO_WORD_BITS must be 16, 32 or 64 - see protocore_config.h"
133#endif
134
135/**
136 * @brief Every offset, length and capacity in this library. Never `size_t`.
137 *
138 * Stated at ::PROTO_INDEX_BITS so an offset is the same width in the device build and in the host
139 * test that proves it. 32 bits addresses far more than any pool this library reserves; a target
140 * whose every buffer is under 64 KB can set 16 and pay one narrower register per index.
141 */
142#if PROTO_INDEX_BITS == 32
143typedef proto_u32 proto_idx;
144#elif PROTO_INDEX_BITS == 16
145typedef proto_u16 proto_idx;
146#else
147#error "PROTO_INDEX_BITS must be 16 or 32 - see protocore_config.h"
148#endif
149
150// The knobs above say what the widths should be; these check the target actually provides them.
151// A platform missing an exact-width type fails here, naming itself, rather than at the first
152// expression that assumed it.
153//
154// `static_assert` rather than C11's `_Static_assert`: this header is reached from the sketches
155// compiled as C++, where the underscored spelling is not a keyword. C11's <assert.h> defines the
156// unprefixed name and C++ has it built in, so one spelling is correct on both sides.
157static_assert(sizeof(proto_u8) == 1, "proto_u8 must be exactly 8 bits: this target has no 8-bit type");
158static_assert(sizeof(proto_u16) * 8u == 16u, "proto_u16 must be exactly 16 bits");
159static_assert(sizeof(proto_u32) * 8u == 32u, "proto_u32 must be exactly 32 bits");
160static_assert(sizeof(proto_u64) * 8u == 64u, "proto_u64 must be exactly 64 bits");
161static_assert(sizeof(proto_word) * 8u == PROTO_WORD_BITS, "proto_word must be exactly PROTO_WORD_BITS wide");
162static_assert(sizeof(proto_idx) * 8u == PROTO_INDEX_BITS, "proto_idx must be exactly PROTO_INDEX_BITS wide");
163static_assert(sizeof(proto_idx) <= sizeof(proto_word), "an index must fit the register it is carried in");
164
165// ::PROTO_ENUM_PACKED is a toolchain feature rather than a per-declaration one, so proving it is
166// honored once proves it for every enum that carries it. An enum needing more than a byte still
167// widens on its own: the attribute asks for the narrowest type the values fit, not for one byte.
173static_assert(sizeof(proto_enum_probe) == 1,
174 "PROTO_ENUM_PACKED is not honored here, so no enum keeps its declared width (TI: pass --small_enum)");
175
176#endif // PROTOCORE_TYPES_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTO_INDEX_BITS
Bits in every offset, length and capacity the library declares (protocore_idx).
Definition platform.h:78
#define PROTO_WORD_BITS
The target's natural register width, in bits.
Definition platform.h:66
int32_t proto_i32
Definition types.h:46
uint8_t proto_u8
Definition types.h:40
int64_t proto_i64
Definition types.h:47
int8_t proto_i8
Definition types.h:44
_Bool proto_bool
The truth value.
Definition types.h:64
enum PROTO_ENUM_PACKED proto_enum_probe
The natural register width, as a type.
uint16_t proto_u16
Definition types.h:41
@ PROTO_ENUM_PROBE_MIN
Definition types.h:170
@ PROTO_ENUM_PROBE_MAX
Definition types.h:171
uint64_t proto_u64
Definition types.h:43
int16_t proto_i16
Definition types.h:45
uint32_t proto_u32
Definition types.h:42