Datalight Coding Style | |
====================== | |
This is a description of the Datalight Coding Style intended for third parties | |
who want to contribute code to Reliance Edge. This document is derived from the | |
DDSS Coding Guidelines, but only contains a subset of the content which is most | |
likely to be relevant to third party contributors. | |
Reliance Edge complies with the MISRA-C:2012 coding guidelines, which includes | |
many rules that affect coding style. Unfortunately the MISRA-C:2012 document is | |
not freely available, and is much too long to be effectively summarized, but if | |
you are familiar with the rules, adhere to them. A few important rules of | |
thumb: avoid the goto and continue keywords; avoid using more than one break | |
in a loop; and avoid having more than one return from a function (single point | |
of exit); default cases in every switch statement; avoid recursion; and make | |
generous use of parentheses. Outside of the file system driver, in tests and | |
host tools, the MISRA-C rules are relaxed. | |
Beyond MISRA-C, Datalight has a standard coding style. Most aspects of this | |
style are matters of preference, but when contributing code to Datalight an | |
effort should be made to use this style for the sake of consistency. | |
Below is an example function, which illustrates several key points of Datalight | |
Coding Style: | |
/** @brief One-sentence description of what this function does. | |
Additional description. | |
@param ulFirstParameter Description of the parameter. | |
@param pszPointer Description of the parameter. | |
@return Describe the return value. | |
@retval true Optional description of specific return value. | |
@retval false Optional description of specific return value. | |
*/ | |
bool ExampleFunction( | |
uint32_t ulFirstParameter, | |
char *pszPointer) | |
{ | |
bool fStatus = true; | |
/* This is a single-line comment. | |
*/ | |
if(ulFirstParameter > 0U) | |
{ | |
/* This is a multi-line comment. Filler text: Lorem ipsum dolor sit | |
amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt | |
ut labore et dolore magna aliqua. | |
*/ | |
FunctionCall(); | |
while(fStatus) | |
{ | |
fStatus = AnotherFunction(ulFirstParameter, pszPointer); | |
} | |
} | |
return fStatus; | |
} | |
Tab Stop Conventions | |
-------------------- | |
In all C code (.c/.h), use a tab width of four spaces, and use soft tabs (in | |
other words, tabs are expanded to spaces). In Makefiles, use hard tabs and a | |
tab width of 8. | |
Naming | |
------ | |
Datalight uses CamelCase for functions and variables. Type names are generally | |
UPPERCASE, except for standard types like uint32_t. Preprocessor macros are | |
UPPERCASE, with words separated by underscores (for example, INODE_INVALID). | |
Doxygen Documentation | |
--------------------- | |
Doxygen is used to document functions (including static functions), along with | |
types, structures, files, etc. For Doxygen tags, use '@' instead of a backslash | |
(thus "@param" not "\param"). | |
Function Declarations | |
--------------------- | |
Multi-line function declarations are preferred, as they tend to be more | |
readable. Use the following form: | |
static bool ExampleFunctionDeclaration( | |
uint32_t ulFirstParameter, | |
char *pszPointer, | |
uint8_t **ppbBuffer) | |
{ | |
uint16_t uLocalVar; /* descriptive comment */ | |
uint8_t *pbBuffer = NULL; /* descriptive comment */ | |
Function body... | |
} | |
The following guidelines should be used: | |
- Align both the data-type and the variable names, for parameters and locals, at | |
the same level if practical. | |
- For pointer types, the '*' belongs to the variable name---it's not part of the | |
data-type, so keep it with the variable name. | |
- If useful, single line comments may be used to describe local variables (not | |
a requirement). | |
- For functions with no parameters, the "void" declaration does not need to be | |
on a separate line. | |
- Generally each variable should be declared on a separate line. This promotes | |
readability, and facilitates having a comment for each variable. | |
Function declarations should be spaced apart by two blank lines between the | |
closing brace which ends a function and the Doxygen comment which starts the | |
next. | |
Curly Braces | |
------------ | |
Datalight lines up all curly braces vertically. As per MISRA-C, curly braces | |
are never omitted, even if the braces contain only a single statement. | |
For consistency, even structure declarations and initializations should use the | |
same style, with the curly braces lined up vertically. One exception is for | |
structure initializations where both the opening and closing curly braces can | |
fit on the same line. If so, do it. | |
Code Comments | |
------------- | |
Datalight uses the standard C style /* comments */. C++ style comments (//) are | |
never used. The Datalight standard comment style is shown below. This style | |
applies to all general comments within the code. | |
/* This is a single-line comment. | |
*/ | |
if(ulFirstParameter > 0U) | |
{ | |
/* This is a multi-line comment. Filler text: Lorem ipsum dolor sit amet, | |
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore | |
et dolore magna aliqua. | |
*/ | |
while(fStatus) | |
{ | |
} | |
} | |
Note the characteristics: | |
- The /* and */ align with the natural 4 character indentation. | |
- The comment text is exactly indented another 4 characters. | |
- The comment text starts on the same line as the opening /*. | |
- The terminating */ is on its own line. | |
- There is usually a single blank line preceding the comment, however if the | |
preceding line is an opening curly brace, then an extra blank line is not | |
necessary. | |
- There is usually no blank line after the comment, but rather the closing */ | |
"attaches" the comment to the code about which the comment refers. | |
- These comments should always fit with the standard 80 character margin. | |
Comments where the /* and */ are on the same line may be used in a few places: | |
- For variable or parameter descriptions, where the comment fits on the same | |
line as the declaration. | |
- For structure member declarations, where the comment fits on the same line as | |
the declaration. | |
- For macros or preprocessor logic, where the comment fits on the same line. | |
It is OK for such comments to exceed the 80 character margin by a small amount, | |
if necessary, as this sometimes promotes code readability. | |
Indentation Style | |
----------------- | |
The general paradigm used in Datalight code is that curly braces line up | |
vertically, and everything in between them is indented. This should include all | |
comments, labels, and preprocessor symbols. The only things which are aligned | |
at the left-most columns are: | |
- Symbols, variables, declarations, and preprocessor logic which are at the | |
module-scope (outside of a function) | |
- Comments which are outside of a function | |
- Function declarations | |
- Function open and closing curly braces | |
Typically comments are always lined up directly with the code to which they | |
apply. | |
Labels (when used; gotos are disallowed in driver code) are lined up two | |
characters to the left of the code they reside in, to make them stand out, while | |
as the same time, still remaining subservient to the level of curly braces in | |
which they reside. For example: | |
bool ExampleLabelUsage(void) | |
{ | |
MutexLock(); | |
Lots of complicated code... | |
Unlock: | |
MutexUnlock(); | |
return fSuccess; | |
} | |
Preprocessor logic, such as controlling features which are conditionally | |
compiled in or out, should not disrupt the flow of the code, but rather should | |
be indented in similar fashion to the code it controls, but positioned two | |
characters to the left. For example, consider the following code snippet. The | |
preprocessor conditions are both indented relative to the outer curly braces, | |
but do not disrupt the normal code flow. | |
int32_t red_statvfs( | |
const char *pszVolume, | |
REDSTATFS *pStatvfs) | |
{ | |
REDSTATUS ret; | |
ret = PosixEnter(); | |
if(ret == 0) | |
{ | |
uint8_t bVolNum; | |
ret = RedPathSplit(pszVolume, &bVolNum, NULL); | |
#if REDCONF_VOLUME_COUNT > 1U | |
if(ret == 0) | |
{ | |
ret = RedCoreVolSetCurrent(bVolNum); | |
} | |
#endif | |
if(ret == 0) | |
{ | |
ret = RedCoreVolStat(pStatvfs); | |
} | |
PosixLeave(); | |
} | |
return PosixReturn(ret); | |
} | |
Note that, like anything else between curly brackets, the contents of a switch | |
statement are indented: | |
switch(ulSignature) | |
{ | |
case META_SIG_MASTER: | |
fValid = (uFlags == BFLAG_META_MASTER); | |
break; | |
case META_SIG_IMAP: | |
fValid = (uFlags == BFLAG_META_IMAP); | |
break; | |
case META_SIG_INODE: | |
fValid = (uFlags == BFLAG_META_INODE); | |
break; | |
case META_SIG_DINDIR: | |
fValid = (uFlags == BFLAG_META_DINDIR); | |
break; | |
case META_SIG_INDIR: | |
fValid = (uFlags == BFLAG_META_INDIR); | |
break; | |
default: | |
fValid = false; | |
break; | |
} | |
Maximum Line Length | |
------------------- | |
The maximum line length for code need not be rigidly limited to the traditional | |
80 characters. Nevertheless the line lengths should be kept reasonable. | |
Anything longer than 100 to 120 characters should probably be broken up. The | |
most important consideration is readability---fitting on the screen is important | |
for readability, but equally important is facilitating an easy understanding of | |
the logical code flow. | |
There are a few exceptions on both sides of the issue. Generally comments | |
should be limited to 80 characters always. Some lines of code may exceed the | |
120 character length by a large margin, if it makes the code more understandable | |
and maintainable. This is especially true when dealing with code that generates | |
output which needs to be lined up. | |
Regardless of everything else, no lines should exceed 250 characters because | |
some editors cannot handle anything larger. | |
Maximum Display Output Line Length | |
---------------------------------- | |
Any code which displays TTY style output, whether on a screen or a terminal, | |
should be constructed so the output is readable and wraps properly on an 80 | |
character wide display. This primarily applies to the "standard" output from | |
various tests and tools as well as syntax output for those tests and tools; | |
debug output can violate this rule. | |
Preprocessor Notation | |
--------------------- | |
Don't use preprocessor notation where the # is separated from the keyword by one | |
or more white spaces. For example, don't do: | |
#ifndef SYMBOL1 | |
# define SYMBOL1 | |
#endif | |
Instead, do: | |
#ifndef SYMBOL1 | |
#define SYMBOL1 | |
#endif | |
Hexadecimal Notation | |
-------------------- | |
Use uppercase for any alphabetic hexadecimal digits, and lower case for the | |
notational element. For example: | |
#define HEXNUM 0x123abd /* Bad */ | |
#define HEXNUM 0X123ABD /* Bad */ | |
#define HEXNUM 0x123ABD /* Good */ | |
Hungarian Notation | |
------------------ | |
Datalight uses Hungarian notation. The following type prefixes are used: | |
Type Prefix | Meaning | |
----------- | ------- | |
c | char | |
uc | unsigned char | |
i | int | |
n | unsigned int or size_t | |
b | uint8_t | |
u | uint16_t | |
ul | uint32_t | |
ull | uint64_t | |
sz | array of char that will be null-terminated | |
f | bool | |
h | A handle | |
fn | A function (always used with the "p" modifier) | |
There is no official Hungarian for int8_t, int16_t, int32_t, or int64_t, | |
although some code uses unofficial variants (like "ll" for int64_t). | |
The following modifiers may be used in combination with the type prefixes | |
defined above, or in combination with other types: | |
Modifier | Meaning | |
-------- | ------- | |
a | An array | |
p | A pointer | |
g | A global variable | |
Notes: | |
- There is no standard Hungarian for structure declarations, however the use of | |
the "a" and "p" modifiers is completely appropriate (and expected). | |
- For those data types which do not have any standard defined Hungarian prefix, | |
using none is preferable to misusing another prefix which would lead to | |
confusion. | |
- The "p" pointer modifier must be used such that a variable which is a pointer | |
to a pointer uses multiple "p" prefixes. A general rule-of-thumb is that the | |
variable name should have the same number of "p" prefixes as the declaration | |
has asterisks. This allows pointer expressions to be easily decoded using | |
cancellation. | |
Variable Scope | |
-------------- | |
Declare a variable in the narrowest scope in which it is meaningful. | |
Unnecessarily declaring all variables at the beginning of a function, where they | |
may be physically far from where they are actually used, makes the code harder | |
to maintain. | |
When multiple blocks of code share a variable, but not its value, declare the | |
variable separately for each code block. | |
For example, if two separate blocks contain loops indexed by a variable ulIndex | |
declare it separately in each block rather than declaring it once in a wider | |
scope and using it in both places. | |
Using distinct declarations in the two blocks allows the compiler to check for | |
failure to initialize the variable in the second block. If there is a single | |
declaration, the (now meaningless) value left over from the first block can be | |
used erroneously in the second block. | |