Difference between revisions of "Kernel Normal Form"

From Embedded Xinu
Jump to navigation Jump to search
(→‎Example: Updated example)
(Introduction seemed very wordy)
 
(13 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{Update}}
+
This page describes major aspects of the preferred style for kernel source files in the [[Xinu]] operating system source tree.
  
== Tabs ==
+
== Comments ==
* Use tabs to indent code.
+
 
* Use spaces to position comments that appear to the right of code.
+
=== General ===
 +
 
 +
Generally, code should be well-commented.  However, comments should only mention things that are ''not'' already obvious.
 +
 
 +
[http://www.stack.nl/~dimitri/doxygen/ Doxygen] is used to automatically generate documentation from comments.  Comments beginning with 2 asterisks are recognized by Doxygen; other comments are not.  Doxygen-style comments should generally be used to document files as well as any functions, variables, definitions, and structures that are meant to be an external interface--- that is, not internal to a file or component.  "Regular" comments should be used in other cases, such as explaining the code.
 +
 
 +
The following shows the main forms of comments:
 +
 
 +
<source lang="c">
 +
 
 +
/*
 +
* Immensely important comments look like this.
 +
*/
 +
 
 +
/* Typical single-line comments look like this. */
 +
 
 +
/**
 +
* Multi-line comments that should appear in the autogenerated documentation
 +
* should look like this.
 +
*/
 +
 
 +
/** Single-line comments to appear in documentation look like this. */
 +
/**< or they look like this, if appearing on the same line as code. */
 +
</source>
 +
 
 +
The first form of single-line comments must be placed above the code that it pertains to, whereas the second form may be placed either above the code or on the same line as the code it describes (if it fits).
 +
 
 +
=== Files ===
 +
 
 +
At the top of all source files, there should be a file comment, followed by a copyright comment, like the following:
 +
 
 +
<source lang="c">
 +
/**
 +
* @file somefile.c
 +
* This is a description of the file.
 +
*/
 +
/* Embedded Xinu, Copyright (C) 2013.  All rights reserved. */
 +
</source>
 +
 
 +
The double asterisks opening the comment as well as the @file declaration are essential, since they inform Doxygen that the comment documents the file itself.  Although not strictly necessary, please do include the filename in the @file declaration.
  
== Braces ==
+
Previous releases used a @provides tag to declare all publicly visible symbols from each file.  This is no longer done because Doxygen automatically determines what functions and variables are in each file.
* For functions and control blocks, the brace goes on its own line.
 
* Control blocks can be compressed to
 
  
== Spaces ==
+
Also, previous releases used the Subversion $Id$ keyword in each file, but these are no longer used because they duplicate the purpose of version control systems.
* Spaces around operators are recommended, around <code>=</code> are required.
 
* The parenthesis following a keyword is always prefixed by a space.
 
* Following a function call or declaration, there is no space between the name and the opening parenthesis.
 
  
== Function Naming ==
+
The copyright comment is not intended to be parsed by Doxygen, so it must only use one asterisk. If changes are made to any file with a copyright year prior to the current year, then, legally speaking, the current year must be added to the copyright comment if those changes are legally significant for copyright purposes. However, note that the code does not use the lengthy 20+-line copyright statements used in many other projects, as the authors believe these clutter up the files and the license is already made clear in the COPYING file.
* Function names use Java style [http://en.wikipedia.org/wiki/LowerCamelCase lowerCamelCase], following Java conventions on capitalization edge cases.
 
* Avoid unnecessary abbreviation in function names as reasonable.
 
  
== Global Variables ==
+
== Preprocessor ==
* Global tables are named after the related structure with the suffix "tab", such as <code>devicetab</code>, <code>processtab</code>, etc.
 
  
== Types ==
+
As a precaution against multiple definitions every include file should protect itself against redefining its material.
To avoid machine type size issues, the <code>int</code> and other #define types are deprecated.  Instead, all code should use: <code>long</code>, <code>ulong</code>, <code>short</code>, <code>ushort</code>, <code>char</code>, <code>uchar</code>.
 
  
Pointers which are used soley as references to memory locations should be declared of type <code>void *</code>.
+
<source lang="c">
 +
#ifndef _INCLUDE_H_
 +
#define _INCLUDE_H_
 +
[...]
 +
#endif                          /* _INCLUDE_H_ */
 +
</source>
  
== Comments ==
+
After the opening comment block and copyright of a file, include one blank line and begin including non-local header files.
* The top of any file should contain a comment with the file name and major public functions (as well as the copyright once it is determined)
+
 
* Functions should follow normal javadoc (doxygen in our case) style
+
<source lang="c">
* Comments should be follow /* ... */ style and vary depending on line size
+
#include <kernel.h>
 +
#include <device.h>
 +
#include <memory.h>
 +
#include <string.h>
 +
</source>
 +
 
 +
Following that, if there are local header files include a blank line and then continue including files.
 +
 
 +
<source lang="c">
 +
#include "local.h"
 +
</source>
 +
 
 +
Macro definitions should be in ALL CAPS unless it goes against a standard.  This includes macros that are used in lieu of a function.  If the macro consumes multiple lines, align the backslashes one space to the right of the longest line.  Any final statement-terminating semicolon should not appear in the macro, rather it will be supplied by the invocation of the macro to allow easier parsing of the code by humans and editors alike.
 +
 
 +
<source lang="c">
 +
#define MACRO(x, y)      \
 +
    (x) = (x) + 5 * (y); \
 +
    (x) /= 3
 +
</source>
 +
 
 +
When using conditional directives such as <code>#if</code> or <code>#ifdef</code>, it is recommended to place a comment following the matching <code>#else</code> or <code>#endif</code> to make the reader have an easier time discerning where conditionally compiled code begins and ends.
 +
 
 +
<source lang="c">
 +
#ifdef MIPS
 +
/* MIPS specific code goes here. */
 +
#else                            /* not MIPS */
 +
/* generic code goes here. */
 +
#endif                          /* MIPS */
 +
</source>
 +
 
 +
== Structs and Typedefs ==
 +
 
 +
Structures should have logically named members with a comment describing what each member is for.  Structures do not have to have a typedef, but if they do have one it should be inline with the structure definition.
 +
 
 +
<source lang="c">
 +
typedef struct dentry
 +
{
 +
    int major;                  /**< major device number          */
 +
    int minor;                  /**< minor device number          */
 +
    void *csr;                  /**< control and status registers */
 +
    [...]
 +
} device;
 +
</source>
 +
 
 +
== Functions ==
 +
 
 +
Functions used in more than one file are "global" and ''must'' have a prototype in a header file.
 +
 
 +
Functions used in only one file are "local" and must be declared with the <code>static</code> modifier.  This prevents namespace pollution and lets the compiler possibly inline the function.  If local functions are used before being defined, a prototype must be placed towards the top of the file.
 +
 
 +
Global functions must be documented by Doxygen using a comment similar to the following:
 +
 
 +
<source lang="c">
 +
/**
 +
* The main function of the program will parse the input for the arguments
 +
* passed.
 +
* @param argc  number of arguments passed to function
 +
* @param argv  array of char *s containing passed arguments
 +
* @param func  pointer to function that takes two int parameters
 +
* @param offset offset into char * array to read
 +
* @param length length to read at offset
 +
* @return zero on successful completion, non-zero if unsuccessful.
 +
*/
 +
int foo(int argc, char **argv, devcall (*func)(int, int), int offset,
 +
        int length)
 +
{
 +
    /* well written code. */
 +
}
 +
</source>
 +
 
 +
Note that Doxygen comments for global functions should focus on what a developer would need to know to call the function.  They should generally ''not'' discuss implementation details.
 +
 
 +
Local functions need not be documented as formally.  However, they may have regular (not Doxygen) comments that help explain the code.
  
== Example ==
+
== Spacing ==
  
It should be noted that since HTML does not allow the tab character, this example should reflect someone who has their tab stop set to 4 spaces. USE THE \t CHARACTER.
+
Languages keywords (such as <code>if</code>, <code>while</code>, <code>for</code>, <code>switch</code>) all have one space following their use.  This helps differentiate keywords from function calls.  Braces (<code>{</code> and <code>}</code>) should always be used in control statements.  The use of brackets in all cases helps minimize the risk of bugs occurring when adding new lines to a statement.
  
/**
+
<source lang="c">
  * @file    exampleFile.c
+
for (i = 0; i < length; i++)
  * @provides exampleFile, fileLoaded, getIO
+
{
  * While only an example this portion should contain general information
+
    a = i + 1;
  * about the importance of this file.
+
    b *= a;
  *
+
}
  * $Id$
 
  */
 
/* Embedded XINU, Copyright (C) 2007.  All rights reserved. */
 
 
   
 
   
#include <kernel.h>
+
if (NULL != value)
#include <proc.h>
+
{
#include <queue.h>
+
    *value = new_value;
 +
}
 
   
 
   
/* End of file marker */
+
while (TRUE)
#define FILE_END 0x255          /**< Magic number to designate end-of-file */
+
{
+
    /* Do nothing. */
/*
+
}
  * Processor Registers
+
</source>
  */
+
 
#define zero, $0                /**< hardwired zero                        */
+
Avoid declarations within new statement blocks when possible, certain versions of compilers may not recognize them for what they are.
  #define a0,  $4                /**< first passed argument                */
+
 
#define s0,  $16              /**< callee saved                          */
+
Indentations are done using 4 spaces per level. If a conditional statement wraps around place the operator at the beginning of the next line (lining up with first variable above).
+
 
extern ulong getData(int *register);
+
<source lang="c">
extern ulong data_register;
+
while (count > 30 && TRUE == this_variable_is_true
+
      && NULL != value)
/**
+
{
  * Load an example file into XINU.
+
    /* Do something. */
  * @param fd      descriptor of file to load
+
}
  * @param timeout how long to wait before error.
+
 
  * @return OK for success, SYSERR for failure to load
+
if (foo)
  */
+
{
devcall exampleFile(ushort fd, ulong timeout)
+
    /* foo case. */
{
+
}
    ulong counter; /* number of cycles we've been waiting  */
+
else if (bar)
    ulong data;    /* 64-bits for storing a section of data */
+
{
+
    /* bar case. */
    for ( counter = 0; counter < timeout; counter++ )
+
}
    {
+
else
        data = getIO();
+
{
+
    /* else case. */
        /* TODO: perform operation on data */
+
}
+
</source>
        /* We have finished reading the file */
+
 
        if ( loadXinu(fd) == OK )
+
Switch statements should be formatted with each case lining up with the braces as follows:
        { return OK; }
+
 
    }
+
<source lang="c">
+
switch (test)
    return SYSERR;
+
{
  }
+
case 0:
   
+
case 1:
  /**
+
    /* Process. */
  * Search for the end of the file
+
    break;
  * @param fd file descriptor to finish reading
+
default:
  * @return OK if complete, SYSERR if incomplete
+
    /* Normal case. */
  */
+
    break;
local fileLoaded(ushort fd)
+
}
{
+
</source>
    /* check for end of file marker */
+
 
    if ( filetab[fd].buffer == FILE_END )
+
There should be no spaces after function names.  Commas should be followed by a space. Typically there are only spaces with more complex statements. Code readability is king. Binary operators should be padded with a space on either side.
    {
+
 
        /* other code if needed */
+
<source lang="c">
        return OK;
+
error = function(a1, a2);
    }
+
if ((OK != error) && (5 < error))
    else
+
{
    {
+
    exit(error);
        /* other code if needed */
+
}
        return SYSERR;
+
</source>
    }
+
 
}
+
Unary operators do not require a space.
+
 
  /**
+
In cases where operator precedence is unclear, always error on the side of including additional parentheses.
  * Read data from the s0 register through an assembly function.
+
 
  * @return data from register s0
+
== Miscellaneous ==
  */
+
 
ulong getIO()
+
It is permissible to declare multiple variables on one line, but do not initialize variables until everything has been declared.
{
+
 
    switch (data_register)
+
<source lang="c">
    {
+
struct foo one, *two;
    case S0: case S1:
+
int three, four, five;
        return getData(s0);
+
 
    case A0: case A1: case A2: case A3:
+
five = 5;
        return getData(a0);
+
four = four();
    default:
+
</source>
        return SYSERR;
+
 
    }
+
Type casts and <code>sizeof</code> should not be followed by a space. <code>sizeof</code> should always be written with parentheses.
}
+
 
 +
<source lang="c">
 +
a = (ushort)sizeof(struct memblock);
 +
</source>
 +
 
 +
Committed code should never produce warnings or errors.
 +
 
 +
Function names should use [[w:CamelCase|lowerCamelCase]].  Avoid unnecessary abbreviation in function names as reasonable.
 +
 
 +
Pointers which are used solely as references to memory locations (and not to a structure or array of a specific type) should be declared of type <code>void *</code>.

Latest revision as of 21:45, 11 September 2013

This page describes major aspects of the preferred style for kernel source files in the Xinu operating system source tree.

Comments

General

Generally, code should be well-commented. However, comments should only mention things that are not already obvious.

Doxygen is used to automatically generate documentation from comments. Comments beginning with 2 asterisks are recognized by Doxygen; other comments are not. Doxygen-style comments should generally be used to document files as well as any functions, variables, definitions, and structures that are meant to be an external interface--- that is, not internal to a file or component. "Regular" comments should be used in other cases, such as explaining the code.

The following shows the main forms of comments:

/*
 * Immensely important comments look like this.
 */

/* Typical single-line comments look like this. */

/**
 * Multi-line comments that should appear in the autogenerated documentation
 * should look like this.
 */

/** Single-line comments to appear in documentation look like this. */
/**< or they look like this, if appearing on the same line as code. */

The first form of single-line comments must be placed above the code that it pertains to, whereas the second form may be placed either above the code or on the same line as the code it describes (if it fits).

Files

At the top of all source files, there should be a file comment, followed by a copyright comment, like the following:

/**
 * @file somefile.c
 * This is a description of the file.
 */
/* Embedded Xinu, Copyright (C) 2013.  All rights reserved. */

The double asterisks opening the comment as well as the @file declaration are essential, since they inform Doxygen that the comment documents the file itself. Although not strictly necessary, please do include the filename in the @file declaration.

Previous releases used a @provides tag to declare all publicly visible symbols from each file. This is no longer done because Doxygen automatically determines what functions and variables are in each file.

Also, previous releases used the Subversion $Id$ keyword in each file, but these are no longer used because they duplicate the purpose of version control systems.

The copyright comment is not intended to be parsed by Doxygen, so it must only use one asterisk. If changes are made to any file with a copyright year prior to the current year, then, legally speaking, the current year must be added to the copyright comment if those changes are legally significant for copyright purposes. However, note that the code does not use the lengthy 20+-line copyright statements used in many other projects, as the authors believe these clutter up the files and the license is already made clear in the COPYING file.

Preprocessor

As a precaution against multiple definitions every include file should protect itself against redefining its material.

#ifndef _INCLUDE_H_
#define _INCLUDE_H_
[...]
#endif                           /* _INCLUDE_H_ */

After the opening comment block and copyright of a file, include one blank line and begin including non-local header files.

#include <kernel.h>
#include <device.h>
#include <memory.h>
#include <string.h>

Following that, if there are local header files include a blank line and then continue including files.

#include "local.h"

Macro definitions should be in ALL CAPS unless it goes against a standard. This includes macros that are used in lieu of a function. If the macro consumes multiple lines, align the backslashes one space to the right of the longest line. Any final statement-terminating semicolon should not appear in the macro, rather it will be supplied by the invocation of the macro to allow easier parsing of the code by humans and editors alike.

#define MACRO(x, y)      \
    (x) = (x) + 5 * (y); \
    (x) /= 3

When using conditional directives such as #if or #ifdef, it is recommended to place a comment following the matching #else or #endif to make the reader have an easier time discerning where conditionally compiled code begins and ends.

#ifdef MIPS
/* MIPS specific code goes here. */
#else                            /* not MIPS */
/* generic code goes here. */
#endif                           /* MIPS */

Structs and Typedefs

Structures should have logically named members with a comment describing what each member is for. Structures do not have to have a typedef, but if they do have one it should be inline with the structure definition.

typedef struct dentry
{
    int major;                   /**< major device number          */
    int minor;                   /**< minor device number          */
    void *csr;                   /**< control and status registers */
    [...]
} device;

Functions

Functions used in more than one file are "global" and must have a prototype in a header file.

Functions used in only one file are "local" and must be declared with the static modifier. This prevents namespace pollution and lets the compiler possibly inline the function. If local functions are used before being defined, a prototype must be placed towards the top of the file.

Global functions must be documented by Doxygen using a comment similar to the following:

/**
 * The main function of the program will parse the input for the arguments
 * passed.
 * @param argc   number of arguments passed to function
 * @param argv   array of char *s containing passed arguments
 * @param func   pointer to function that takes two int parameters
 * @param offset offset into char * array to read
 * @param length length to read at offset
 * @return zero on successful completion, non-zero if unsuccessful.
 */
int foo(int argc, char **argv, devcall (*func)(int, int), int offset,
        int length)
{
    /* well written code. */
}

Note that Doxygen comments for global functions should focus on what a developer would need to know to call the function. They should generally not discuss implementation details.

Local functions need not be documented as formally. However, they may have regular (not Doxygen) comments that help explain the code.

Spacing

Languages keywords (such as if, while, for, switch) all have one space following their use. This helps differentiate keywords from function calls. Braces ({ and }) should always be used in control statements. The use of brackets in all cases helps minimize the risk of bugs occurring when adding new lines to a statement.

for (i = 0; i < length; i++)
{
    a = i + 1;
    b *= a;
}
 
if (NULL != value)
{
    *value = new_value;
}
 
while (TRUE)
{
    /* Do nothing. */
}

Avoid declarations within new statement blocks when possible, certain versions of compilers may not recognize them for what they are.

Indentations are done using 4 spaces per level. If a conditional statement wraps around place the operator at the beginning of the next line (lining up with first variable above).

while (count > 30 && TRUE == this_variable_is_true
       && NULL != value)
{
    /* Do something. */
}

if (foo)
{
    /* foo case. */
}
else if (bar)
{
    /* bar case. */
}
else
{
    /* else case. */
}

Switch statements should be formatted with each case lining up with the braces as follows:

switch (test)
{
case 0:
case 1:
    /* Process. */
    break;
default:
    /* Normal case. */
    break;
}

There should be no spaces after function names. Commas should be followed by a space. Typically there are only spaces with more complex statements. Code readability is king. Binary operators should be padded with a space on either side.

error = function(a1, a2);
if ((OK != error) && (5 < error))
{
    exit(error);
}

Unary operators do not require a space.

In cases where operator precedence is unclear, always error on the side of including additional parentheses.

Miscellaneous

It is permissible to declare multiple variables on one line, but do not initialize variables until everything has been declared.

struct foo one, *two;
int three, four, five;

five = 5;
four = four();

Type casts and sizeof should not be followed by a space. sizeof should always be written with parentheses.

a = (ushort)sizeof(struct memblock);

Committed code should never produce warnings or errors.

Function names should use lowerCamelCase. Avoid unnecessary abbreviation in function names as reasonable.

Pointers which are used solely as references to memory locations (and not to a structure or array of a specific type) should be declared of type void *.