This document describes the coding conventions used in Orion-LD and its supporting k-libraries (kjson, kalloc, kbase, ktrace, khash). Use this guide when contributing code to maintain consistency.
- File Organization
- Header Files
- Source Files
- Naming Conventions
- Formatting
- Comments
- Memory Management
- Error Handling
- Logging and Tracing
- Common Patterns
- K-Libs Specifics
- Code organized by functionality under
src/lib/orionld/andsrc/app/orionld/ - Subdirectories for features:
contextCache/,dbModel/,mhd/,q/,mongoc/, etc. - Each function in its own module (source file and its header file)
- E.g.,
X.cppcontains the functionX()) - Helper functions only used by X() are static in
X.c.
- E.g.,
- Descriptive camelCase:
orionldContextCacheInit.cpp,dbModelAttributePublishedAtLookup.cpp - Header and source files paired:
functionName.h+functionName.cpp
The guard name is REPONAME_FILENAME_H_ — the repo/library name as prefix, then the filename (camelCase uppercased, no underscores inserted), then _H_ with trailing underscore.
For k-libs:
#ifndef KJSON_KJPARSE_H_
#define KJSON_KJPARSE_H_
// content
#endif // KJSON_KJPARSE_H_For Orion-LD (includes directory path from repo root):
#ifndef SRC_LIB_ORIONLD_CONTEXTCACHE_ORIONLDCONTEXTCACHE_H_
#define SRC_LIB_ORIONLD_CONTEXTCACHE_ORIONLDCONTEXTCACHE_H_
// content
#endif // SRC_LIB_ORIONLD_CONTEXTCACHE_ORIONLDCONTEXTCACHE_H_The repo prefix avoids collisions between identically named headers across libraries (e.g., KBASE_VERSION_H_, KJSON_VERSION_H_, KHASH_VERSION_H_).
Every .c and .h file in a k-lib must have the Apache 2.0 file header. Blank comment lines use // (with trailing space).
For .c files, the header goes at the very top:
//
// FILE kaAlloc.c
//
// AUTHOR Ken Zangelin
//
// Copyright 2019 Ken Zangelin
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with theLicense.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <stdlib.h> // callocFor .h files, the include guard comes first, then the header:
#ifndef KALLOC_KAALLOC_H_
#define KALLOC_KAALLOC_H_
//
// FILE kaAlloc.h
//
// AUTHOR Ken Zangelin
//
// Copyright 2019 Ken Zangelin
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with theLicense.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "kalloc/KAlloc.h" // KAlloc
// -----------------------------------------------------------------------------
//
// kaAlloc -
//
extern char* kaAlloc(KAlloc* kaP, unsigned long long size);
#endif // KALLOC_KAALLOC_H_/*
*
* Copyright 20XX FIWARE Foundation e.V.
*
* This file is part of Orion-LD Context Broker.
*
* [License text - GNU AGPL v3]
*
* Author: Ken Zangelin
*/
#ifndef INCLUDE_GUARD
#define INCLUDE_GUARD
#include <system_headers.h> // what's used
extern "C"
{
#include "kjson/KjNode.h" // KjNode
#include "ktrace/kTrace.h" // KT_*
}
#include "orionld/types/Type.h" // Type
// -----------------------------------------------------------------------------
//
// Declarations
//
extern Type globalVar;
extern void functionName(void);
#endif // INCLUDE_GUARD- System headers (
<stdio.h>,<string.h>). - C library headers wrapped in
extern "C" { }(kjson, kalloc, ktrace, etc.). - Third party headers.
- Internal library headers, in order from most "basic" to less basic.
- Own interface last (in .c files, not needed in C++ headers).
Each include has a trailing comment explaining what's used:
#include "kjson/kjLookup.h" // kjLookup
#include "orionld/common/orionldState.h" // orionldState/*
*
* Copyright 20XX FIWARE Foundation e.V.
*
* [License text]
*
* Author: Ken Zangelin
*/
#include <system.h> // what's used
extern "C"
{
#include "kjson/KjNode.h" // KjNode
}
#include "orionld/common/orionldState.h" // orionldState
#include "orionld/module/thisFile.h" // Own interface
// -----------------------------------------------------------------------------
//
// functionName -
//
ReturnType functionName(ParamType paramP)
{
// implementation
}// -----------------------------------------------------------------------------
//
// functionName - brief description
//
// PARAMETERS
// paramP - description of parameter
// outP - output parameter description
//
// RETURN VALUE
// Description of what is returned
//
// NOTE
// Any additional notes about usage
//| Type | Pattern | Example |
|---|---|---|
| Public | moduleName + Action |
orionldContextCacheInit() |
| Static/Internal | lowercase start | attributeToSimplified() |
| K-libs | prefix + camelCase | kjObject(), kaAlloc(), ktInit() |
| Type | Pattern | Example |
|---|---|---|
| Pointers | suffix with P |
contextP, nodeP, entityP |
| Arrays/Vectors | suffix with V |
attrV[], connectionV |
| Loop counters | ix, i, j |
for (int ix = 0; ...) |
| Indices | suffix with Ix |
orionldContextCacheSlotIx |
| Globals | descriptive, module-prefixed | orionldContextCacheArray |
| Type | Pattern | Example |
|---|---|---|
| Structs | CamelCase | OrionldContext, PgConnectionPool |
| Enums | CamelCase | OrionldTraceLevels, KjValueType |
| Enum values | Prefix + CamelCase | KtResponse, KjString, SsPing |
| Typedefs (k-libs) | K + name | KBool, KjNode, KAlloc |
| Type | Pattern | Example |
|---|---|---|
| Constants | ALL_CAPS | ORIONLD_CONTEXT_CACHE_SIZE |
| Function-like | ALL_CAPS | KJSON_ALLOC_CHECK(), KT_T() |
- 2 spaces per level (no tabs)
- Consistent throughout
The * belongs to the type, not the variable. The pointer is part of the type:
// Correct — * pegged to the type
KjNode* nodeP = NULL;
char* name = "entity";
int* countsP = NULL;
// Wrong — * pegged to the variable
KjNode *nodeP;
char *name;Never declare multiple variables on the same line. This eliminates the int *a, b ambiguity entirely:
// Correct
int count = 0;
int total = 0;
char* name = NULL;
// Wrong — multiple variables on one line
int count, total;
int *a, b; // b is not a pointer — this is whyNo space between a function name and (. Always a space between a keyword and (:
// Functions — no space before (
kaAlloc(&kaP, size);
kjLookup(entityP, "id");
strcmp(a, b);
// Keywords — space before (
if (condition)
for (int ix = 0; ix < len; ix++)
while (running)
switch (type)No space inside the parentheses, one space after:
int* xP = (int*) vP;
char* buf = (char*) calloc(1, size);
float f = (float) intValue;// Opening brace on same line for functions
void functionName(void)
{
// body
}
// Control structures: brace on new line
if (condition)
{
// body
}
else
{
// body
}
// Single statement body: NEVER use braces
if (condition)
singleStatement();
if (ptr == NULL)
return NULL;
for (int ix = 0; ix < items; ix++)
process(ix);
while (retries < 10)
retry();
if (condition)
doThis();
else
doThat();Align related declarations for readability:
extern sem_t orionldContextCacheSem;
extern OrionldContext* orionldContextCacheArray[1000];
extern int orionldContextCacheSlots;
extern int orionldContextCacheSlotIx;Align inline comments:
KjNode* idP = kjLookup(entityP, "id"); // Entity ID
KjNode* typeP = kjLookup(entityP, "type"); // Entity Type- Generally under 120 characters
- Break long lines at logical points
Use // comments exclusively. Never use /* */ block comments — they're ugly and don't nest. // comments have been standard C since C99 (1999).
Always leave at least 2 spaces before a trailing // comment:
char* buf = kaAlloc(&kaP, size); // Allocate from request pool
int len = strlen(name); // Cache lengthWrong:
char* buf = kaAlloc(&kaP, size); // Too close
char* buf = kaAlloc(&kaP, size);// No space at all// -----------------------------------------------------------------------------
//
// sectionName - description
//There must be exactly 3 blank lines before every // ----- separator block. This applies everywhere: after the license header, after #include blocks, and between functions.
#include "module/lastInclude.h" // lastThing
// -----------------------------------------------------------------------------
//
// firstFunction -
//
int firstFunction(void)
{
return 0;
}
// -----------------------------------------------------------------------------
//
// secondFunction -
//
void secondFunction(void)
{
// ...
}This visual spacing makes it easy to distinguish function boundaries when scrolling through source code.
//
// Explanation spanning
// multiple lines
//- Redundant comments that just repeat the code
- Commented-out code (delete it, use git history)
- kaAlloc - Primary allocator (request-scoped, fast)
- kaStrdup - String duplication with kaAlloc
- malloc - When kaAlloc is unavailable or for long-lived allocations
// Preferred: use kalloc
char* buf = kaAlloc(&orionldState.kalloc, size);
// String duplication
char* copy = kaStrdup(&orionldState.kalloc, original);
// Conditional allocation (prefer kalloc, fall back to malloc)
if (size < orionldState.kalloc.bytesLeft)
buf = kaAlloc(&orionldState.kalloc, size);
else
{
buf = (char*) malloc(size);
orionldStateDelayedFreeEnqueue(buf); // Ensure cleanup
}
// Buffer growth: use exponential, not linear
int newSize = oldSize * 2; // Good
int newSize = oldSize + 512; // Avoid - causes O(n²)KjNode* nodeP = kjObject(orionldState.kjsonP, NULL);
KJSON_ALLOC_CHECK(nodeP); // Returns/exits on NULL- Functions return
NULLor-1on failure - Use error enums:
OrionldResponseErrorType,KjStatus,KaStatus
// Simple error with return
if (ptr == NULL)
return NULL;
// Error with trace
if (ptr == NULL)
KT_RE(NULL, "Failed to allocate buffer");
// Error with orionld error response (error info saved to be later returned in a HTTP response)
if (invalid)
{
orionldError(OrionldBadRequestData, "Invalid parameter", paramName, 400);
return false;
}
// Sequential checks (common pattern - to dave lines and ease readibility)
if (idP == NULL) KT_RVE("No 'id' in entity");
if (typeP == NULL) KT_RVE("No 'type' in entity");// Explicit comparison preferred
if (ptr == NULL)
if (ptr != NULL)
// Conditional assignment with NULL check
KjNode* valueP = (objectP != NULL)? kjLookup(objectP, "value") : NULL;| Macro | Purpose | Example |
|---|---|---|
KT_T(level, ...) |
Trace (debug) | KT_T(KtRequest, "Processing %s", url) |
KT_V(...) |
Verbose | KT_V("Detail info") |
KT_W(...) |
Warning | KT_W("Unexpected value") |
KT_E(...) |
Error | KT_E("Failed to connect") |
KT_RE(ret, ...) |
Error + return | KT_RE(NULL, "Allocation failed") |
KT_RVE(...) |
Error + return void | KT_RVE("Invalid state") |
KT_X(code, ...) |
Fatal exit | KT_X(1, "Out of memory") |
KT_TREE(node, label, level) |
Dump JSON tree | KT_TREE(entityP, "Entity", KtSR) |
Defined in orionld/common/traceLevels.h:
KtRequest(200) - Request processingKtResponse(201) - Response generationKtMongoc(500) - MongoDB operationsKtNotification(1100) - NotificationsKtContext(1700) - Context operations
PERFORMANCE(renderStart);
// ... code to measure ...
PERFORMANCE(renderEnd);extern "C"
{
#include "kjson/KjNode.h" // KjNode
#include "kalloc/kaAlloc.h" // kaAlloc
}static OrionLdRestService services[] =
{
{ HTTP_GET, "/ngsi-ld/v1/entities", getEntities },
{ HTTP_POST, "/ngsi-ld/v1/entities", postEntities },
{ HTTP_NOVERB, "", NULL } // Terminator
};switch (nodeP->type)
{
case KjString: handleString(nodeP); break;
case KjInt: handleInt(nodeP); break;
case KjFloat: handleFloat(nodeP); break;
case KjObject: handleObject(nodeP); break;
case KjArray: handleArray(nodeP); break;
default: handleDefault(nodeP); break;
}for (KjNode* attrP = entityP->value.firstChildP; attrP != NULL; attrP = attrP->next)
{
// process attribute
}// Use strcmp for string comparison
if (strcmp(attrP->name, "id") == 0)
continue;
// Multiple comparisons
if (strcmp(name, "type") == 0) continue;
if (strcmp(name, "id") == 0) continue;
if (strcmp(name, "createdAt") == 0) continue;
if (strcmp(name, "modifiedAt") == 0) continue;| Library | Prefix | Example |
|---|---|---|
| kjson | kj |
kjObject(), kjParse(), kjRender() |
| kalloc | ka |
kaAlloc(), kaStrdup(), kaRealloc() |
| kbase | k |
kTimeGet(), kStringSplit() |
| ktrace | kt |
ktInit(), ktOut() |
| khash | khash |
khashTableCreate(), khashItemLookup() |
- No C++ features in k-lib code
- Use
extern "C"when including from C++
// Return NULL on allocation failure
KjNode* nodeP = kjObject(kjP, name);
if (nodeP == NULL)
KLOG_RE(NULL, "allocation failed");
// Status enums
typedef enum KjStatus { KjsOk, KjsBadParam, KjsAllocError, ... } KjStatus;
typedef enum KaStatus { KasOk, KasAllocError, ... } KaStatus;// Prefer kalloc, fall back to malloc
char* buf = (kjP != NULL) ? kaAlloc(kjP->kallocP, size) : malloc(size);NAMING:
Functions: orionldModuleAction(), kjAction()
Pointers: variableP
Arrays: variableV[]
Indices: variableIx
Macros: ALL_CAPS_WITH_UNDERSCORES
Types: CamelCase, KjNode, OrionldContext
FORMATTING:
Indent: 2 spaces
Braces: New line for functions and multi-line if/for/while; no braces for single-statement bodies
Pointers: KjNode* nodeP (star pegged to type, one variable per line)
Parens: func() (no space), if () (space)
Casts: (char*) ptr (no space inside, space after)
Comments: // only (no /* */), at least 2 spaces before //
Blank lines: 3 blank lines before every // ----- separator
Line length: ~120 chars max
MEMORY:
Primary: kaAlloc(&orionldState.kalloc, size)
Strings: kaStrdup(&orionldState.kalloc, str)
Growth: size * 2 (exponential, not linear)
Check: KJSON_ALLOC_CHECK(ptr)
ERRORS:
Return: KT_RE(NULL, "message") or KT_RVE("message")
Report: orionldError(type, title, detail, status)
Log: KT_E("error"), KT_W("warning"), KT_T(level, "trace")
INCLUDES:
Order: system -> extern "C" { k-libs } -> internal -> own interface
Comment: #include "file.h" // WhatIsUsed