Skip to content

Code Style Guide

This guide defines the coding standards for the EmbSec Kit project. Consistent code style improves readability, maintainability, and collaboration.

General Principles

  1. Clarity over cleverness: Write code that is easy to understand
  2. Consistency: Follow existing patterns in the codebase
  3. Documentation: Comment complex logic and document public APIs
  4. Testing: Write tests for new functionality
  5. Security: Consider security implications of all code changes

C Code Style

We use LLVM style with some modifications as defined in .clang-format:

Formatting Rules

  • Indentation: 4 spaces (no tabs)
  • Line length: 80 characters maximum
  • Braces: Linux kernel style (opening brace on same line for functions)
  • Pointer alignment: Right-aligned (char *str, not char* str)

Automatic Formatting

Use clang-format to automatically format your code:

# Format a single file
clang-format -i src/myfile.c

# Format all C files
find . -name "*.c" -o -name "*.h" | xargs clang-format -i

Naming Conventions

Functions

// Public API functions: embsec_module_action()
void embsec_uart_init(void);
int embsec_gpio_read(uint8_t pin);

// Internal functions: module_action()
static void uart_configure_baudrate(uint32_t baud);
static int gpio_validate_pin(uint8_t pin);

Variables

// Local variables: snake_case
int byte_count = 0;
char *user_input = NULL;

// Global variables: g_snake_case
volatile uint32_t g_system_ticks = 0;
static char g_buffer[256];

// Constants: UPPER_SNAKE_CASE
#define MAX_BUFFER_SIZE 1024
#define UART_BAUDRATE   115200

Types

// Structs: snake_case with _t suffix
typedef struct {
    uint32_t address;
    size_t   size;
} memory_region_t;

// Enums: snake_case with _e suffix
typedef enum {
    GPIO_MODE_INPUT,
    GPIO_MODE_OUTPUT,
    GPIO_MODE_ALTERNATE
} gpio_mode_e;

Code Organization

File Structure

/*
 * filename.c - Brief description
 *
 * Detailed description of the file's purpose
 *
 * Author: Name <email>
 * SPDX-License-Identifier: MIT
 */

// 1. System includes
#include <stdint.h>
#include <string.h>

// 2. Project includes
#include "embsec/embsec.h"
#include "embsec/uart.h"

// 3. Local includes
#include "internal.h"

// 4. Macros and constants
#define BUFFER_SIZE 256

// 5. Type definitions
typedef struct {
    // ...
} local_state_t;

// 6. Static variables
static local_state_t g_state;

// 7. Static function declarations
static void helper_function(void);

// 8. Public function implementations
void embsec_public_function(void)
{
    // Implementation
}

// 9. Static function implementations
static void helper_function(void)
{
    // Implementation
}

Documentation

Function Documentation

/**
 * @brief Initialize the UART peripheral
 * 
 * @param baud Baud rate (e.g., 115200)
 * @param config Configuration flags
 * 
 * @return 0 on success, negative error code on failure
 * 
 * @note This function must be called before any other UART operations
 */
int embsec_uart_init(uint32_t baud, uint32_t config);

Inline Comments

// Single-line comments for simple explanations
x = (y + 1) * 2;  // Account for header byte

/*
 * Multi-line comments for complex explanations
 * that require more detail or context
 */
if (complex_condition) {
    // Explain why this is necessary
    perform_special_operation();
}

Python Code Style

For Python code (tests, scripts), follow PEP 8:

Key Rules

  • Indentation: 4 spaces
  • Line length: 79 characters (72 for docstrings)
  • Naming: snake_case for functions/variables, PascalCase for classes
  • Imports: Group and sort imports (standard, third-party, local)

Example

#!/usr/bin/env python3
"""
Module docstring describing the purpose.
"""

import os
import sys
from typing import List, Optional

import pytest
from serial import Serial

from embsec_test import TestFramework


class LabTest:
    """Test class for lab exercises."""

    def __init__(self, port: str) -> None:
        """Initialize test with serial port."""
        self.port = port
        self.serial: Optional[Serial] = None

    def run_exploit(self, payload: bytes) -> bool:
        """
        Run exploit with given payload.

        Args:
            payload: Exploit payload bytes

        Returns:
            True if exploit succeeded
        """
        # Implementation
        pass

CMake Style

Formatting

  • Use lowercase for commands: add_library(), not ADD_LIBRARY()
  • Indent with 2 spaces
  • One argument per line for multi-argument commands

Example

# Define library
add_library(embsec-sdk STATIC
  src/uart.c
  src/gpio.c
  src/timer.c
)

# Set properties
target_include_directories(embsec-sdk
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
  PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/src
)

# Conditional logic
if(BUILD_TESTING)
  add_subdirectory(tests)
endif()

Shell Scripts

Style Rules

  • Use #!/usr/bin/env bash shebang
  • Set safety flags: set -euo pipefail
  • Use "${variable}" for variable expansion
  • Check command existence before use
  • Provide help text with -h/--help

Example

#!/usr/bin/env bash
#
# Script description
#
set -euo pipefail

# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly DEFAULT_BUILD_DIR="build"

# Functions
usage() {
    cat <<EOF
Usage: $(basename "$0") [OPTIONS]

Options:
    -b, --build-dir DIR    Build directory (default: ${DEFAULT_BUILD_DIR})
    -v, --verbose         Enable verbose output
    -h, --help           Show this help message
EOF
}

# Main
main() {
    local build_dir="${DEFAULT_BUILD_DIR}"
    local verbose=0

    # Parse arguments
    while [[ $# -gt 0 ]]; do
        case $1 in
            -b|--build-dir)
                build_dir="$2"
                shift 2
                ;;
            -v|--verbose)
                verbose=1
                shift
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            *)
                echo "Error: Unknown option: $1" >&2
                usage
                exit 1
                ;;
        esac
    done

    # Implementation
}

main "$@"

Git Commit Messages

Format

type(scope): brief description

Longer explanation of the change, why it was needed,
and any important implementation details.

Fixes: #123

Types

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting, etc.)
  • refactor: Code refactoring
  • test: Test additions or changes
  • build: Build system changes
  • ci: CI/CD configuration changes
  • chore: Other maintenance tasks

Examples

feat(sdk): add SPI peripheral support

Implement SPI driver with support for master mode operation.
Includes DMA support for efficient data transfers.

- Add embsec_spi_init() and configuration functions
- Implement interrupt-driven and DMA transfer modes  
- Add comprehensive test coverage

Fixes: #45

Code Review Checklist

Before submitting a merge request, ensure:

  • Code follows the style guide
  • All tests pass (make test)
  • New code has appropriate tests
  • Documentation is updated
  • No compiler warnings
  • Performance impact evaluated
  • Backwards compatibility maintained

Tools and Automation

Pre-commit Hooks

Install pre-commit hooks to automatically check style:

# Install pre-commit
pip install pre-commit

# Install hooks
pre-commit install

# Run manually
pre-commit run --all-files

Editor Configuration

VS Code

{
    "C_Cpp.clang_format_style": "file",
    "editor.formatOnSave": true,
    "files.insertFinalNewline": true,
    "files.trimTrailingWhitespace": true
}

Vim

" Use clang-format
autocmd FileType c,cpp nnoremap <buffer> <leader>f :!clang-format -i %<CR>

" Basic settings
set expandtab
set shiftwidth=4
set softtabstop=4

Questions?

If you have questions about code style:

  1. Check existing code for examples
  2. Refer to the .clang-format configuration
  3. Ask in your merge request for clarification