TrustPeer Coding Guidelines

This document is meant for all TrustPeer developers. It describes simple rules that every coder should follow (most importants first):

1. each public item (function/variable/constant/macro) that can be used by other developpers should be be declared in a declaration-only file (.h file for C language) with comments that describe the exact contract between the user and the coder: 

- The preconditions are the conditions that the user should respect (coherency of call parameters, value ranges, etc.)
- The postconditions are the ones that the coder should respect (no side effects except the ones documented, exact description of the results of a function call, exact range of the return values, error values, etc.)

- in addition to "normal" error return values, the "programming error" ERRPROG return value can be used (by pre/invariant/post assertions) (#define ERRPROG 1), see §2.
- a null return value (0,NULL) must be returned when no error happened (yes, it means return values are used only for errors, just like the Unix processes do)

- The above rules also apply for each non-trivial private item

2. each non-trivial function/macro code should contain assertions returning the ERRPROG value, using one of our macros (an "errprog.h" macro works like g_return_val_if_fail(expr,val) )

- Assertions that try to check all the preconditions of the .h
- Assertions that try to check all the postconditions of the .h
- Assertions that try to check all the internal conditions of the algorithm used (each non-trivial block or loop should have Invariant assertions)
- Gnu Nana provides predicate logic math tools to check math conditions, but only gcc supports them

- Assertions and ERRPROG value should not be used for "normal" error values that are part of a contract. Use plain return values instead.

3. each contract should be tested by a regression test:

- For each contract, a test function/macro should be written that will check for most possible input values if the postconditions are respected at the end (including error values)
- This test will also try to trigger the preconditions assertions for most impossible input values to see if they are detected

-All the regression tests should be called by a cron job. They should be called too when a bug can not be found easily

4. each caller should take error values into account:

-Each time a function/macro is called, the first thing to do will always be to check for a (non null) return error value and act accordingly (most of the times it means returning a non null error value to the caller's caller).
-If no error can ever be returned by a call then a comment should indicate this besides the call (used mostly when the function called has a void return)
-You should use "Exceptions that must be caught" when your langage supports them (Java). In other languages, you should always use the return value of a function to return errors. You should never use void as a return value (except for proved simple functions named v_yournamehere() ).

-In languages like C, if you don't check a call for a (non null) return value, it simply means your code is flawed.

5. when using a language with pointers/unchecked array indices, you should assert (or return an error value) before each instruction that uses the content they point to. :

-Macros can be written to facilitate these checks
-Make sure there is a documented way to find where the array ends: ANSI strings (char arrays) use a null element, i.e. the number 0
-When using raw data from the outside (keyboard/network/files...) you should never use directly functions that can trigger overflows (like ANSI scanf and string.h functions)
-In addition, you should use memory usage testing libraries like Valgrind

6. you should send version numbers in the beginning of each protocol or when writing persistent datas

-When writing a persistent data structure (file, database, ...) you should always begin by the version number of this structure, and all the compatible version numbers. (it's very useful when a given version of the program wants to read data written by another version)
-In the same way, each protocol between processes A and B should start with A giving the version # of the protocol it wants to use, and B replying OK or an earlier version #. Then A reply OK or an earlier version #, and so on till OK or Error if no common version # can be found.

7. you should never use global variables

- they can be modified by every function without knowing which has a bug
- they are not threadsafe

8. when using private functions/variables/constants, you must specify to the compiler that they are private

-The C language uses the keyword "static" at the beginning of the declaration of such private functions/variables/constants that are not inside a function.
-Obviously, such private items should never be part of a .h file !
-You should never use "static" inside a function since it is not threadsafe

-If you forget to use "static" when building libraries, then several libraries can have the same name for their private items, producing problems...

9. when writing a function declaration, you should use "const" for each read-only input parameter

-This is also useful for plain variables (non pointers) since some compilers will issue warnings when you try to use them as return values

10. you should use new blocks {} each time you need new resources or temporary variables

- at the end of each block, make sure you release (free) all resources no more needed: you can add comments for each resource
- add a comment after each closing } /* comment */ to know which { it is associated with




(THINGS TO ADD TO THIS PAGE: {} matching, examples for each rule, ...)