published: Thu, 15 Nov 2018 03:54:06 +0100

C Style

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Naming Conventions

Enums, macros and global constants must be UPPER_CASE. Functions, struct members and variables must be snake_case. Structs should be snake_case but may be CamelCase if there are many structs but must be consistent throughout the codebase.

Source files must use the .c extension, header files must use the .h extension and all files must use snake_case.

Formatting

Spaces on either side of any operator must be used, except hypens after an opening parenthesis. Indentation shall be 4 spaces and there must not be any tabs in any source or header file.

Where possible use conformant array parameters. Function opening and closing braces must be on their own line.

Define variables which are used throughout a block at the beginning of that block. Exceptions include when a function is considerably long, and a variable is only used from midway (or further) through the function; in this case those variables can be defined at the top of a 'logical block' (basically whatever 'looks right').

Braces should be on the same line as the statement, with spaces on the outside of parenthesis. Statement blocks with only a single body line may omit braces but the body must still be on a new line, indented as usual (though this isn't required if it is more readable with braces).

Only C++ style comments shall be used placed above relevant lines, not on the same line. Comment blocks must use empty comments in order to denote that a space is still intended as part of the same comment block and they are not multiple separate comments. The reason for C++ comments over C style comments is that there is less variance on how you may use C++ comments which leads to a more consistent overall style.

The bool type (from the stdbool.h header) should be used instead of using integers when requiring true/false only values.

Small example

#include <pthread.h>

// Static look-up table enabling byte-wise computation of CRC32 values.
// 256 values for representing all the single byte values 0x00-0xFF.
static uint32_t crc32_lut[256] = {0};

// Any functions writing to the look-up table should use the mutex to
// ensure that no multiple writes occur across threads. There is also
// pthread_once_t to make sure that the generation code is run at least
// once before the table is used.
static pthread_mutex_t crc32_lut_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t crc32_lut_gen = PTHREAD_ONCE_INIT;

// Generate CRC32 look-up table using reverse polynomial
//
// $x^{32}+x^{26}+x^{23}+x^{22}+x^{16}+x^{12}+
//  x^{11}+x^{10}+x^8+x^7+x^5+x^4+x^2+x+1$
//
// 1 is defined as the MSB, so if you set x equal to 1 then you get
// the following number:
// `1110 1101 1011 1000 1000 0011 0010 0000'
// `0xEDB88320'
void crc32_gen_lut()
{
    // Lock thread for writing look-up table.
    pthread_mutex_lock(&crc32_lut_mutex);
    for (uint16_t i = 0; i <= 0xFF; i++) {
        uint32_t crc = i;
        // If crc is even, XOR polynomial. The reverse polynomial
        // indicates that we are doing right shifts.

        // This code is functionally equivalent to:
        // for (uint8_t j = 0; j < 8; j++) {
        //     if (crc & 1)
        //         crc = (crc >> 1);
        //     else
        //         crc = (crc >> 1) ^ 0xEDB88320;
        // }
        for (uint8_t j = 0; j < 8; j++)
            crc = (crc >> 1) ^ (-(crc & 1) & 0xEDB88320);
        crc32_lut[i] = crc;
    }
    // Unlock mutex to release ownership of look-up table
    pthread_mutex_unlock(&crc32_lut_mutex);
}

// Calculate and return a CRC32 value
uint32_t crc32_(uint32_t crc, const void *data, size_t size)
{
    // Generate table if it has not already been generated. You could also
    // check if crc32_lut[1] is equal to the reverse polynomial we are
    // using, but this wouldn't be thread-safe.
    pthread_once(&crc32_lut_gen, crc32_gen_lut);
    // Don't want the table to be changed as we are using it.
    pthread_mutex_lock(&crc32_lut_mutex);
    crc = ~crc;

    while (size--)
        crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *(uint8_t *)data++];

    pthread_mutex_unlock(&crc32_lut_mutex);
    return ~crc;
}