390 lines
13 KiB
Plaintext
390 lines
13 KiB
Plaintext
|
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.
|
||
|
|