Base components in the lib-common
C library enhancements
printf
printf
: the lib-common overloads printf with our own implementation that
provides optimizations for the most used cases. It also has some improvements
over the standard printf
implementation:
-
It can be used in signal handlers.
-
It overloads the
%p
as done in the Linux kernel. This means that%p
(and%*p
) also consume the alphanumeric characters that directly follow thep
and use them as a way to change the interpretation of the passed parameter. As a consequence, you must make sure your%p
is followed by a non-alphanumeric character in order to print a pointer address.
The following extensions are supported:
-
%*pM
putslen
raw bytes of memory from the given pointer. Its most common use-case is a faster%.*s
:printf("%.*s", len, str)
printsMIN(len, strlen(str))
bytes, whileprintf("%*pM", len, str)
always putslen
bytes without trying to interpret those bytes (and thus can also put\0
).%*pM
when directly followed by alphanumeric character will additionally print those characters. -
%*pX
and%*px
are very similar to%*pM
, but instead of putting raw memory, they encode the data in hexadecimal. The%*pX
puts uppercase characters while the%*px
variant puts lowercase characters. Both output2 * len
characters and consumelen
bytes from the provided pointer. -
%pL
puts the content of the pointedlstr_t
object. -
%*pS
is an extension brought by IOP that allows to put IOP class instances in JSON. -
%*pV
used with VALUE_FMT_ARG to print aplatform.Value
. -
%*pI
used with ID_FMT_ARG to print aplatform.Id
.
printf("%ptoto", ptr); /* Invalid, since "toto" is not a valid
* modifier */
printf("%p toto", ptr); /* output something like:
* 0x7efe129cb0f00 toto */
printf("%*pM", 4, "12345"); /* output 1234 */
printf("%*pMtoto", 4, "12345"); /* output 1234toto */
printf("%*pXtoto", 4, "12345"); /* output 31323334toto */
printf("%*pX world", 5, "Hello"); /* output 48656C6C6F world */
lstr_t toto = LSTR("toto");
printf("%pL", &toto); /* output toto */
char data[] = { 0xde, 0xad, 0xbe, 0xef };
printf("%*px", sizeof(data), data); /* output deadbeef */
printf("%*pX", sizeof(data), data); /* output DEADBEEF */
foo__bar__t bar;
iop_init(foo__bar, &bar);
bar.i1 = 1;
bar.i2 = 2;
printf("%*pS", IOP_OBJ_FMT_ARG(&bar));
/* outputs {"_class":"foo.bar","i1":1,"i2":2} */
printf("%*pS", IOP_ST_FMT_ARG(toto__toto, &toto));
/* outputs {"tata":1, "tutu":2} */
Basic macros
The lib-common provides several macros that improve our code readability.
-
countof()
: returns the number of elements in a static array.
static int a[] = { 1, 2, 3, 4 };
for (int i = 0; i < countof(a); i++) {
...
}
-
CMP
: compares two numerical variables. This macro avoids having to rewrite the integer comparison logic each time we sort entries. Thanks to the ternary operator extension of gcc it can be easily chained to compare several fields of a structure.
struct foo_t {
int a;
int b;
};
/* foo_t structures are sorted by increasing value of 'a' and, for each 'a'
* by increasing value of 'b' */
int foo_cmp(const struct foo_t *s1, const struct foo_t *s2)
{
return CMP(s1->a, s2->a) ?: CMP(s1->b, s2->b);
}
-
get_unaligned_*
andput_unaligned_*
: These macros must be used whenever you are reading value for a byte stream that may not be properly aligned to CPU word boundaries. -
There are many more macros like
SWAP
,DIV_ROUND_UP
,ROUND_UP
,TOSTR
,MIN
,MIN3
,MAX
,MAX3
…
Error management
The lib-common comes with macros that will help making the error management as unobtrusive as possible.
-
RETHROW*
: This macro family is used to propagate error status. It works as long as the code conforms to our error reporting convention and the function does not have to perform cleanup before returning.RETHROW
variants depend on the type of caller and on the type of the callee. In order to remember whichRETHROW
to use, keep in mind thatN
stands for "numerical" andP
for "pointer", thenNP
means "converting numerical value to pointer".-
RETHROW
: rethrow integer error code -
RETHROW_P
: rethrow NULL pointer -
RETHROW_NP
: throw a NULL pointer if callee returned a negative return code -
RETHROW_PN
: throw a negative return code if the callee returned the NULL pointer
-
int foo(void);
byte *bar(int s);
int baz(void)
{
int s;
byte *data;
/* Assign s to the return value of foo, but filter out error cases
* before by rethrowing the error code.
*/
s = RETHROW(foo());
/* Fetch the data, but return -1 if bar fails.
*/
data = RETHROW_PN(bar(s));
return data[0];
}
-
THROW_ERR_IF
/THROW_ERR_UNLESS
andTHROW_NULL_IF
/THROW_NULL_UNLESS
: those macros can be used to return an error in a function. -
expect()
: the expect function takes a condition and returns the result of the evaluation of the condition. In development mode, it willabort()
the execution of the program if the condition is evaluated tofalse
. This macros is a complement/replacement forassert()
with the following differences:-
Code put in
assert()
is never executed in production mode, while code put in anexpect()
is always executed. -
expect()
returns the result of the evaluation of the condition whileassert()
is a statement, and as such has no return value.expect()
also generates a.debug
file containing the current backtrace.
-
if (expect(pos >= 0)) {
do_something();
} else {
handle_error();
}