Coding Standards¶
Language & Compiler¶
| Item | Standard |
|---|---|
| Language | C11 (-std=c11) |
| Compiler | GCC ≥ 9.0 |
| Warnings | -Wall -Wextra -Werror (zero warnings policy) |
| Optimization | -O3 for production, -O0 -g for debug |
| Sanitizers | -fsanitize=address,undefined during development |
Formatting¶
Indentation & Braces¶
/* K&R style braces, 4-space indentation */
static inline int function_name(int param1, const char *param2)
{
if (param1 < 0) {
return -EINVAL;
}
for (int i = 0; i < param1; i++) {
do_something(i);
}
return 0;
}
Naming Conventions¶
| Element | Convention | Example |
|---|---|---|
| Functions | module_verb_noun | raw_socket_open, ring_buffer_push |
| Types (struct) | module_noun_t | raw_socket_t, uart_config_t |
| Types (enum) | module_noun_t | uart_parity_t, gpio_edge_t |
| Enum values | MODULE_UPPER_CASE | UART_PARITY_NONE, GPIO_EDGE_RISING |
| Constants | MODULE_UPPER_CASE | REACTOR_MAX_EVENTS, ETH_HDR_SIZE |
| Local variables | snake_case | frame_len, slot_ptr |
| Struct members | snake_case | if_index, baud_rate |
Line Length¶
- Maximum 90 characters per line
- Break long function calls at parameter boundaries:
Design Patterns¶
Header-Only Implementation¶
All protocol modules use static inline functions in the .h file:
/* Good: zero-cost abstraction, inlineable */
static inline int module_operation(module_t *ctx, int param)
{
/* Implementation */
return 0;
}
Rationale: Eliminates function call overhead in the hot path. The compiler can inline and optimize across module boundaries.
Error Handling¶
Return -errno on failure (never positive error codes):
static inline int uart_open(uart_t *uart, const char *device,
const uart_config_t *config)
{
uart->fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (uart->fd < 0) {
return -errno; /* ← return negative errno */
}
return 0; /* ← success = 0 */
}
Caller pattern:
int rc = uart_open(&uart, "/dev/ttyS0", &cfg);
if (rc < 0) {
fprintf(stderr, "Failed: %s\n", strerror(-rc));
return rc;
}
Zero Dynamic Allocation¶
All memory must be pre-allocated by the caller:
/* Good: caller provides storage */
static uint8_t ring_storage[4096 * 64] __attribute__((aligned(64)));
ring_buffer_init(&rb, ring_storage, 4096, 64);
/* Bad: internal malloc */
rb->buffer = malloc(capacity * slot_size); /* NEVER do this */
Resource Cleanup¶
Every _open() has a matching _close(). On failure during open, clean up partial state:
static inline int module_open(module_t *m)
{
m->fd = open(...);
if (m->fd < 0) return -errno;
if (ioctl(m->fd, ...) < 0) {
int err = errno;
close(m->fd); /* ← clean up partial state */
return -err;
}
return 0;
}
Packed Structures for Wire Formats¶
Use __attribute__((packed)) for protocol frame structures:
typedef struct __attribute__((packed)) {
uint8_t dst_mac[6];
uint8_t src_mac[6];
uint16_t ethertype;
uint8_t payload[];
} eth_frame_t;
Atomic Operations¶
Use C11 <stdatomic.h> with explicit memory ordering:
/* Never use default (sequential consistency) in hot paths */
/* Bad: */ atomic_store(&head, value);
/* Good: */ atomic_store_explicit(&head, value, memory_order_release);
Documentation Requirements¶
Header File Documentation¶
Every .h file must include:
- Module name and purpose Single-line description
- Protocol specification Wire format, timing, state machine
- ASCII diagram Memory layout, frame structure, or architecture
- Compilation command Exact GCC invocation
- Runtime prerequisites Permissions, kernel modules, hardware
Function Documentation¶
/**
* Brief one-line description.
*
* Longer description if the function has non-obvious behavior,
* side effects, or thread-safety considerations.
*
* @param name Description of parameter
* @param other Description with constraints (e.g., "must be power of 2")
* @return What the return value means (success/failure codes)
*/
static inline int module_function(type *name, type other);
Concurrency Rules¶
- Single-threaded modules (reactor, raw_socket): No atomics needed. Document "NOT thread-safe" in header.
- SPSC modules (ring_buffer, spsc_queue): Use acquire/release ordering. No locks.
- MPMC modules (memory_pool): Use CAS with ABA prevention. Document retry semantics.
- Never use mutexes in the data path. Use lockless structures or partition work per-thread.
Cache Alignment¶
/* Align hot atomic variables to cache-line boundaries */
alignas(64) _Atomic uint64_t head;
uint8_t _padding[64 - sizeof(_Atomic uint64_t)];
/* Align buffers for DMA and vectorization */
static uint8_t buffer[SIZE] __attribute__((aligned(64)));
Build Flags Reference¶
| Flag | Purpose |
|---|---|
-Wall -Wextra -Werror | Zero tolerance for warnings |
-O3 | Full optimization (inlining, vectorization) |
-std=c11 | C11 standard (atomics, alignas) |
-march=native | CPU-specific optimizations (for benchmarks) |
-flto | Link-time optimization (cross-TU inlining) |
-fno-strict-aliasing | Safe for type-punning (wire protocol parsing) |
-fsanitize=address | ASan for development builds |
-fsanitize=thread | TSan for concurrency debugging |
-DNDEBUG | Disable assertions in release |