Cosm Programmers Guide
Version: p2-2008-09-06
Overview
The purpose of this document is to establish common programming methods for those developing code compatable with the Cosm libraries.
Development Cycle
Since we use SVN the development cycle is continuous. Coders should make sure they are familiar with SVN.
Coordination
_Any_ questions at all, ask. Before implementing any additional low level functions inform me, so we can make sure there isn't any overlap. I intend to be familiar with every line of code, so if you need any "major" functionality let me know, someone else may already be working on it. Also, if you're going to do anything "neat" be sure and inform me first, just so more than one person knows what you're doing.
Use of Other Code and Libraries
All code must be original and your own writing, you can't cut-n-paste from _any_ other projects or code libraries due to license problems (and other code won't follow the Cosm standards anyway). We've made sure there are no license problems with the couple of libraries that are eventually needed, and even then they will be completely separated from the main code.
Simplicity
Simplicity and clarity in the code are more important than weird optimization tricks. Especially since all the real work is done in the cores. All code interfaces are designed to be as simple as possible.
Thread Safe Code
All Cosm routines must be fully thread safe. This is not optional. If you don't know how to use locks correctly please go read up, there are many sources on the net on using locks. Keep in mind also that 64 and 128bit data types may not be atomic.
Portability
Straight ANSI C + GNU Assembly. Period.
For numerous reasons of portability and readability Cosm will be written in
straight ANSI C. The ASM optimizations should be done in GNU-ASM for
portability to all OS variants of a given chip. C++ Is nice, but has caused
a great deal of strange porting problems in the past requiring the addition
of many #define and #ifdef statements. Pointers are of unknown length so
care must be taken not to do pointer math. The non-portable types int/long
etc. should only be used as return values for pass/fail/error functions.
Testing
The Cosm_TestModule() functions must test all the functions in a file, causing all cases to be taken within the functions and all outcomes and errors to be generated. Cosm will also use the "well tested, infrequent release" philosophy, not "rapid release, let the user debug it" one. All code should always be kept in a state where it can be compiled without errors/warnings. Never submit code that you can't compile. The Cosm_TestModule() code will also serve as the example code, so it must be complete and correct.
Comments
Comments, comments, comments. In general anything more complex then a for( ... ) loop should have a comment in the code every few lines. Function definitions should also be documented (in the header file) with the parameters, return values, and error conditions. Any { } block longer then ~30 (1/2 page) lines should have a comment at the closing }
Header Files
Header files for a given C file should contain all defines, structs, and function prototypes for the code. Functions should appear in the .c file in the same order they appear in the .h file, the 'helper' functions (Cosm_*) after main interface functions (Cosm*). Defines should be used liberally, and constants directly in the code where the meaning isn't obvious (read: everywhere) should not be used. Any other non-Cosm header files that are needed for the .c file should be included at the top of the .c, not in the .h which should only include it's own information. Only if a .h is needed for the .h to parse should it be included from the .h.
Headers also contain the programmer documentation for a function, what it does, parameters, and expected return values or errors. No one should ever have to find and then dig around .c files to be able to use a function, and so any documentation should be in the header. All things will eventually be documented in non-programmer language in other formats.
Structure Alignment
All structures that will be written to the network or files, or used in an array MUST be properly aligned, padding if needed. This means that u64 and u128 data types must fall on 8 byte boundries, u32 on 4 byte boundries, and u16s on 2 byte boundries. The length of the structure must also be aligned for the biggest type, so if a struct has a u64, it has to be a multiple of 8 bytes long, etc. Becasue of this only platform specific structures that never leave memory (like a mutex) are immune to this requirement. The 2 defines PACKED_STRUCT_BEGIN and PACKED_STRUCT_END exist to insert the correct compiler commands to make a structure fully packed. Wrap any network or disk structure in these defines. See the sample code below for examples.
Version Control
Version numbers for any code or files must be incremented when any change is made. Problems caused by a non-incremented version number can be quite difficult to track. Luckily, Git does a wonderful job of this. Git also does a great job of tracking changes and history. Make sure your messages on commited code are descriptive. Do no use the top of a .c file for the history, it quickly takes over the entire file.
#ifdef Usage
System specific #ifdef are only allowed in CPU/OS level libraries. The only #ifdefs in the utility layer of code should be those provided by the CPU/OS layer for endian, 64biness, etc. When doing any test for CPU/OS type, use globally defined OS_TYPE and CPU_TYPE to switch off of e.g. #if ( OS_TYPE == OS_MACOSX ) - this is more readable to people not familiar with each compiler/os/cpu combination, and is less likely to lead to confusion. Also, preprocessore '#'s go in column 1, and never get indented due to some compilers being picky about the standard.
Naming Conventions
All names should make sense and be long enough for the code to be self documenting
Source File Names
Related code and header files should have the same base filename,
e.g. file.c and file.h.
Functions
All functions should begin with a capital letter and each word within a
function name should be capitalized, e.g. ReadConfig( char * filename );
Library Functions
All user level functions should begin with "Cosm", the module pneumonic, and
each word within a function name should be capitalized, e.g. CosmFoo( ... );
All low level functions should begin with "Cosm_" and be named in a
similar manner e.g. Cosm_Bar( ... );
Local Variables and Function Arguments
Loop variables can be one letter, e.g. u32 x, y, z; anything else should be
descriptive and lower case. "_" should be used between words in local
variables. e.g. u64 word_count;
Global Variables
Globals are variables that need an "extern" and you have to go running
around files trying to find the definitions/usage. So don't even think
about using them. No global variables exist in Cosm APIs, and only a couple
are used for global initialization and shutdown. Users will need them however,
so when we must use then, they start with "__" to avoid any chance of name
collisions.
Structures
Structures should be named in all upper case with "cosm_" as a prefix e.g.
struct cosm_PACKET_HEADER {...}; This may seem a bit odd until you realize it
makes typing struct names very easy. This sets them apart from defines and
macros. Structure members should be lower case like local variables.
Defines
Defines are in all upper case as per C standard e.g.
#define COSM_FOO 30
In general #if defined(...) is prefered over #ifdef since there is no
#elifdef, so this is more consistent overall.
Macros
Macros are in all upper case beginning with "_COSM_" e.g.
#define _COSM_BAR( x ) Bar( x, 42 ); return;
Types and enums
All lowercase no underscores. e.g. u32, utf8char, cosmtime. Usage
makes them clearly different from variables. The enum values should
be named like defines, uppercase with "_".
Code Style
Liberal Spacing
DontCrunchTheCodeAllTogether( void ); e.g. for( i = 0 ; i < 42 ; i += 3 ) Each
statement should be on it's own line. Statements that are long should be
wrapped to the next line at the 78th column or less so that printing is
possible. Any logic more then one line (for, while, if-else) should use a
set of { } for clarity. funtions( x ) and macros( 3 ) do not have a space
between the function name and firt paren. Typecasts (ascii *) also do not have
the extra spaces to that they can be distinguished from parameters.
Indenting
2 spaces for every level of logic, and 2 additional spaces when a line is
wrapped. Tabs should never be used, as they are editor dependant.
When wrapping a line with a "||" or any other logical operator, the operator
should start the second line, not end the first.
Width
Keep everything within the standard 78 columns, wrap lines when needed. Some
of the code will need to be printed, snail-mailed, or OCR'd into other
countries. There is one important exception to this, in the html documentation
for functions the width of example code must be 70 columns or less, due how
man pages are displayed.
Example:
-- blah.h --
#ifndef COSM_BLAH_H #define COSM_BLAH_H /* include everything we need */ #include "cputypes.h" #include "cosm/os_io.h" #define COSM_FOO_ERROR -3 typedef struct invalid_STRUCT { u32 a; u64 b; /* not on 8 byte boundry, cannot be networked or filed */ u16 c; /* length isn't a multiple of 8, cannot be arrayed */ } invalid_STRUCT; PACKED_STRUCT_BEGIN typedef struct valid_STRUCT { u32 a; u16 c; u16 pad; /* padding */ u64 b; /* correctly aligned, correct length, etc. */ } valid_STRUCT; PACKED_STRUCT_END s32 Cosm_TestBlah( void ); #endif
-- blah.c --
#include "blah.h" s32 Cosm_TestBlah( void ) { s32 error; /* run all tests this is a multi-line comment */ if ( ( /* test a fails */ ) || ( /* line wraping */ ) ) { error = COSM_FOO_ERROR; } else /* this is the else format */ { error = COSM_PASS; } /* foo failed - this is single line comment */ return error; } int CosmBlah( void ) { u128 apple; s32 e; /* use the macro to set apple */ _COSM_SET128( apple, 0123456701234567, 89ABCDEF89ABCDEF ) CosmPrint( "A is for Apple = 0x%032Z\n", apple ); if ( foo = Cosm_TestBlah() ); { CosmPrint( "Test Blah Failed = %i\n", foo ); return COSM_FAIL; } return COSM_PASS; }
indent
The unix program indent strips all whitespace and mangles comments
but what comes close is an indent.pro file in docs/indent.pro.
Since indent removes most all whitespace (a fundamental conflict with
readability) this should only be used in absolute emergencies on extremely
bad 3rd party code.
General Conventions
Readability
Code should be "readable" in a way that flows when read/spoken. This makes it
easier to comprehend code when reading it for the first time, such as when
hunting for a bug. This involves boith good variable naming, as well as the
other formating and conventioned mentioned in this document. If the code
does not read well when written, it will not read well later.
Return Values
When a pass/fail needs to be returned, zero should be pass, nonzero a failure.
COSM_PASS and COSM_FAIL exist for this reason, so use them.
This is so that all code can be of the general form
if ( COSM_PASS != ( error = test() ) ) { /* deal with the problem and recover */ }
When pointers are returned, NULL is failure, anything else should be a considered valid.
if ( NULL == ( pointer = test() ) ) { /* deal with the problem and recover */ }
When return is used, do not use the ()'s, it's not a function call.
int, char, short
int, char, short etc. are not used in the APIs. All parameters are of
the universal cputypes.h formats. int can be used internally (with care) for
any temp variables, for/while loops, system calls, etc.
Parameter order
When a function takes parameters, they should be (destination, source) similar
to ( a = b ).
u64 and s64's
When using non-zero u64/s64 constants in code, use the "L" suffix.
e.g. 42L, -1L. Do not use a lowercase "L", as it looks just like a "1"(one)
in most fonts.
u8 u16 u32 u64 u128 s8 s16 s32 s64 s128 f32 f64 f128 ascii utf8 utf8char cosmtime cosmid
Program names
Program names end in ".exe" reguardless of platform. Some platforms
require this, the rest dont care. This way a program's name is always fixed.
Credit Where Credit is Due
A master CREDITS(.txt) file will be maintained where a list of names of the
people that made a significant contribution to writing the code. People will
be divided into categories as implementers, porters, and miscellaneous.
Common Pitfalls
Error Code Checking
All function error codes must be checked and acted on accordingly.
Network Buffers and Memory Overruns
Buffer overrun and bad memory allocation should always be checked for.
Use of Uncleared Structs
Structures need to be either CosmMemeAlloc'd or CosmMemSet to zero before use.
Structures on the local stack will have random values when the function
is entered.
Pointer Math
Never cast a pointer to a u32 or u64 to do pointer math. You do not know if
the pointers will be 32 or 64bits wide on the destination chip, and there is
no good reason to do pointer math outside of the ASM tweaked (and therefore
CPU specific) cores.
NULL Pointers
Check for NULL pointers before all operations. Be especially careful of
library functions i.e. memcpy() as many machines have libraries that have
problems with NULLs.
Typecasting
Most things should be typecast as normal for ANSI C. However,
s64, u64, s128, and u128 cannot be typecast because they are structures on
some systems. For these types macro/functions are provided in os_math.
Memory Leaks
Always Make sure any memory allocation has a matching memory free.
© Mithral Inc.
1995-2024.
All Rights Reserved.
Mithral® and Cosm® are trademarks of
Mithral Inc.