38 Coding Style
Patrick Vogler edited this page 2024-02-15 11:06:11 +00:00

Headline

Coding Style

Abstract

This document discusses practices and style for programmers working on the BigWhoop compression library. The Guidelines are based on the C Coding Style recommended by the GNOME Developer Documentation and should be followed to promote a consistent, readable, and maintainable code base. The Guide offers solutions to common C, python and cmake programming issues and illustrates through examples of code

Content

The Single Most Important Rule

Above all, the following rule from the GNOME developer documentation should always be followed.

The single most important rule when writing code is this: check the surrounding code and try to imitate it.

As a maintainer it is dismaying to receive a patch that is obviously in a different coding style to the surrounding code. This is disrespectful, like someone tromping into a spotlessly-clean house with muddy shoes.

So, whatever this document recommends, if there is already written code and you are contributing to it, keep its current style consistent even if it is not your favorite style.

Most importantly, do not make your first contribution to a project a change in the coding style to suit your taste. That is incredibly disrespectful.

Formatting

Keep the length of source code lines to 100 characters or less to ensure maximum readability on most modern monitors with a reasonable font size. Longer lines of code are more difficult to read and understand. Too many indentations should be interpreted as an indication that code restructuring is required.

Indentation

Each new level is indented by 2 spaces, braces go on a line by themselves, and they are indented as well:

while (c = *str++)
  {
    if ((c >= 97) && (c <= 122))
      {
        c = c - 32;
      }
    hash = (hash * 33) ^ c;
  }

Resist the urge to reindent everything, or to use an inconsisent style. Make sure that your preferred editor respects the indentation rules so your contribution is respectful of the code's customs.

Line Continuation

Continuation lines should align wrapped elements with the first argument (excluding reference operators) or use a hanging indent. When using a hanging indent, there should be no arguments on the first line and all subsequent "hanging" lines should be indented with 2 spaces in relation to the calling command:

Aligned Along First Deliminator

# Correct
error = initialize_tagtree (&prec_control->tag_inclusion,
                             prec_control->numCbX,
                             prec_control->numCbY,
                             prec_control->numCbZ,
                             prec_control->numCbTS)
exit (error);

# Wrong
error = initialize_tagtree (&prec_control->tag_inclusion,
                            prec_control->numCbX,
                            prec_control->numCbY,
                            prec_control->numCbZ,
                            prec_control->numCbTS)
exit (error);

Hanging Indent

# Correct
error = initialize_tagtree (
          &prec_control->tag_inclusion,
           prec_control->numCbX,
           prec_control->numCbY,
           prec_control->numCbZ,
           prec_control->numCbTS);
exit (error);

# Wrong
error = initialize_tagtree (
  &prec_control->tag_inclusion,
   prec_control->numCbX,
   prec_control->numCbY,
   prec_control->numCbZ,
   prec_control->numCbTS);
exit (error);

Forumlas always break before binary operations with the new lines aligned to the first operand in the mathematical formulation:

# Correct
prec_control->numCodeblocks_a = (uint64) (prec_control->numCbX
                                        * prec_control->numCbY
                                        * prec_control->numCbZ
                                        * prec_control->numCbTS);

stream->L = stream->L 
          + stream->Lmax
          + stream->t;
          
# Wrong
prec_control->numCodeblocks_a = (uint64) (prec_control->numCbX *
                                          prec_control->numCbY *
                                          prec_control->numCbZ *
                                          prec_control->numCbTS);

stream->L = stream->L +
            stream->Lmax +
            stream->t;

Braces

Curly braces for function definitions should rest on a new line and should not add an indentation level:

uint64
bytes_used (bwc_stream const *const stream)
{
  if (stream->T == 0xFF)
      return stream->L + 1;
  else
      return stream->L;
}

A new block should always be placed on a new indentation level:

f = fopen('file');
{
  if (stream->T == 0xFF)
      return stream->L + 1;
  else
      return stream->L;
}
fclose(f);

Curly braces should not be used for single statement blocks

if (stream->T == 0xFF)
    return stream->L + 1;
else
    return stream->L;

unless one of the following 4 exceptions applies:

1. If either side of an if…else statement has braces

if (stream->T == 0xFF)
  {
    stream->L = stream->L + stream->t
    return stream->L + 1;
  }
else
  {
    return stream->L;
  }

2. If a single statement covers multiple lines

if (stream->T == 0xFF)
  {
    stream->L = stream->L 
              + stream->Lmax
              + stream->t;
  }
else
  {
    return stream->L;
  }

3. If the condition is composed of many lines

if (stream->L <= stream->Lmax
    stream->T == 0xFF &&
    stream->t == 8)
  {
    return stream->L + 1;
  }
else
  {
    return stream->L;
  }

4. Nested if, in which case the block should be placed on the outermost if

if (stream->L <= stream->Lmax)
  {
    if (stream->T == 0xFF)
      return stream->L + 1;
    else
      return stream->L;
  }
else
  {
    stream->L++;
  }

The closing parenthesis in multiline constructs must be placed at the end of the last line of the construct, as in:

# Correct
DWT_5X3_FILTER[2][5]  = {{DWT_5X3_H1,  DWT_5X3_H0,  0,          0, 0}, 
                         {DWT_5X3_G2,  DWT_5X3_G1,  DWT_5X3_G0, 0, 0}};

Whitespace

Always put a space before an opening parenthesis but never after. For multiple opening parenthesis only a single space in front of the first parenthesis should be used:

while (c = *str++)
  {
    if ((c >= 97) && (c <= 122))
      {
        c = c - 32;
      }
    hash = (hash * 33) ^ c;
  }

# Wrong
while(c = *str++)
  {
    if((c >= 97) && (c <= 122))
      {
        c = c - 32;
      }
    hash =(hash * 33) ^ c;
  }
  
# Wrong
while (c = *str++)
  {
    if ( (c >= 97) && (c <= 122))
      {
        c = c - 32;
      }
    hash = (hash * 33) ^ c;
  }

When declaring a structure type use newlines to separate logical sections of the structure:

  typedef struct
  {
    uint16                      CSsgc;                    // Flag signaling user control variable.
    uchar                       resilience;               // Flag signalling error resilience.

    uint64                      tileSizeX,  tileSizeY;    // Spatial tile size.
    uint64                      tileSizeZ,  tileSizeTS;   // Temporal tile size.

    uint64                      numTilesX,  numTilesY;    // Spatial number of tiles.
    uint64                      numTilesZ,  numTilesTS;   // Temporal number of tiles.
    uint64                      numTiles;                 // Global number of tiles.
    
  } bwc_gl_ctrl;

Do not eliminate whitespace and newlines just because something would fit on a single line:

# Wrong
if (stream->T == 0xFF) return stream->L + 1; else return stream->L;

Do eliminate trailing whitespace on any line, preferably as a separate patch or commit. Never use empty lines at the beginning or at the end of a file.

Conditions

Boolean values should not be checked for equality to make the code more readable:

if (error)
  return 1;

if (!error)
  return 0;

The check for 0 should refer to the specific way the 0 is used: 0 for a numeric value, '\0' for the end of a string, or NULL for a pointer. In this way, the variable type can be derived implicitly by reading the comparison. For the FALSE boolean we refer to the rule about boolean equations.

if (buffer == NULL)
    return NULL;

if (get_bit (stream) == 0)
    node->value++;

if (bwc->info.file_ext [0] != '\0')
    return 0;

Functions

The following general rules should be followed when defining a function in BigWhoop:

  • Function name must be lowercase, optionally separated with underscore _ character
  • Functions private to a translation unit need to be declared before they are called
  • All private functions of a translation unit must appear before the public functions in their respective block, which is identified by the block header private functions:
  • All public functions of a translation unit must appear in their respective block, identified by the public functions block header:

Functions should be declared by placing the returned value on a separate line from the function name:

static void
free_tile (bwc_field *const field)
{
  
}

The argument list must be broken into a new line for each argument, with the argument names right aligned, taking into account pointers and const qualifiers:

static uchar
initialize_subband (bwc_field            *const  field, 
                    bwc_parameter        *const  parameter,
                    bwc_resolution       *const  resolution,
                    bwc_subband          *const  subband, 
                    int32          const         level, 
                    int16          const         highband)
{
  
}

This alignment holds when calling the function:

initialize_subband (field,
                   parameter, 
                   resolution, 
                   &resolution->subband[m], 
                   r, 
                   l);

For header files, the function prototypes must be vertically aligned in six columns and separated by the function delimiter:

  uchar        bwc_open_file              (bwc_field                    *const  field,
                                           char                  const  *const  filename,
                                           char                  const  *const  mode);
  //==========|==========================|======================|======|=======|====================
  uchar        bwc_load_file              (bwc_field                    *const  field,
                                           char                  const  *const  filename);
  //==========|==========================|======================|======|=======|====================
  uchar        bwc_compress               (bwc_field                    *const  field,
                                           char                         *const  rate_control);
  //==========|==========================|======================|======|=======|====================
  uchar        bwc_decompress             (bwc_field                    *const  field, 
                                           uint8                 const          layer);

Each column is marked by the function delimiter with a | with each element in the column right aligned to the column seperator. The delimiter must precede every function prototype except the first.

Variables

Structures, enumerations, typedefs

Comments

Syntactic Conventions

Names